Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3204f13
chore(dev): dev environment on @data-fair/lib-dev and zellij
BatLeDev Oct 28, 2026
ec0e14a
fix(deps): in-range updates clear the npm audit and image findings
BatLeDev Oct 28, 2026
bf46e82
chore(deps): drop dotenv-cli, declare the root imports of the tests
BatLeDev Oct 28, 2026
d591890
fix(deps): @data-fair/frame 0.19
BatLeDev Oct 28, 2026
51f04da
ci: current action majors, no third-party version action
BatLeDev Oct 28, 2026
41a084f
build(docker): node 24.22.1 everywhere
BatLeDev Oct 29, 2026
abdcde9
chore: shared tooling of the df-dev pilots
BatLeDev Oct 29, 2026
63e249c
ci: start the vite UI for the api tests, logs on failure
BatLeDev Oct 29, 2026
f0dbbe3
test(e2e): warm the subscribe-webhooks route
BatLeDev Oct 29, 2026
bf08fd7
test: internal secret through the x-secret-key header
BatLeDev Oct 29, 2026
634f0c7
chore(dev): send-notifications script on the df-dev ports
BatLeDev Oct 29, 2026
ba08565
chore(dev): leftovers of the df-dev migration
BatLeDev Oct 29, 2026
b36f15c
fix(events): next page of the events list skipped older events with a…
BatLeDev Oct 29, 2026
9d4aa61
test(events): pagination over an older event with a greater id
BatLeDev Oct 29, 2026
2bdb335
test(webhooks): hook servers on OS-picked ports, no fixed port
BatLeDev Oct 29, 2026
e5331ce
chore: light pre-push gate of the df-dev pilots
BatLeDev Oct 29, 2026
a721ba8
test: polls instead of fixed sleeps
BatLeDev Oct 29, 2026
bc6c72d
test(webhooks): the retry decision is unit-tested, no api test seeds …
BatLeDev Oct 29, 2026
93f3d71
test: playwright standard of the df-dev pilots
BatLeDev Oct 29, 2026
c2e1bd5
fix(ui): no vite dependency discovery in dev
BatLeDev Oct 29, 2026
678fc43
test: specs in tests/features/<feature>/ as in the df-dev pilots
BatLeDev Oct 29, 2026
3393f23
ci: run the e2e tests too
BatLeDev Oct 29, 2026
1a0352b
chore: relics of the branch found before the PR
BatLeDev Oct 29, 2026
dd194a4
ci: actions/cache v5 and actions/upload-artifact v7
BatLeDev Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .claude/skills/check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ Run all quality checks for this project in sequence. Stop and fix any issues bef

1. `npm run lint-fix`
2. `npm run build-types`
3. `npm run check-types`
4. `npm run test`
3. `npm -w ui run build` (generates the `ui/dts` files vue-tsc needs)
4. `npm run check-types`
5. `npm run test`
2 changes: 1 addition & 1 deletion .github/workflows/releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- id: get_version
uses: battila7/get-version-action@v2
run: echo "major=$(echo "${GITHUB_REF_NAME#v}" | cut -d. -f1)" >> "$GITHUB_OUTPUT"

- name: Send Keel notifications to koumoul.com
run: |
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/reuse-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

- name: Login to Github container registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand All @@ -27,7 +27,7 @@ jobs:

- name: Prepare docker tags for main image
id: docker_meta_main
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
tags: |
Expand All @@ -39,7 +39,7 @@ jobs:

- name: Build and push main image
id: docker_build_main
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
push: true
Expand Down
81 changes: 65 additions & 16 deletions .github/workflows/reuse-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,92 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@v4
- uses: actions/setup-node@v3
uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: 24
node-version-file: .nvmrc
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint and type-check
run: npm run lint && npm run build-types && npm run check-types && npm -w ui run build
# vue-tsc needs the .d.ts files generated by the UI build
- name: Lint, build and type-check
run: npm run lint && npm run build-types && npm -w ui run build && npm run check-types

# only the browser binaries are cached, keyed on the installed playwright version;
# the system packages they need are installed on every run
- name: Read the playwright version
id: playwright
run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"

- name: Cache playwright browsers
id: playwright-cache
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}

- name: Install playwright browser
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium

- name: Install playwright system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium

- name: Init env
run: bash dev/init-env.sh
run: ./node_modules/.bin/df-dev init-env # local bin only: a bare npx would fetch an unrelated "df-dev" from the registry if the dependency were missing

# the runner checks out a detached HEAD, so init-env derives DEV_HOST from the
# the runner checks out a detached HEAD, so DEV_HOST is derived from the
# literal "HEAD"; force a plain lowercase host so simple-directory's site url
# matches the (lowercased) request host
- name: Use localhost as DEV_HOST in CI
run: sed -i 's/^DEV_HOST=.*/DEV_HOST=localhost/' .env

- name: Start dev dependencies
run: docker compose up -d --wait
run: docker compose --profile '*' up -d --wait

- name: Start dev API
run: NODE_ENV=development NODE_CONFIG_DIR=./api/config node api/index.ts &
# the pane commands of .zellij.kdl, without nodemon
- name: Start dev API and UI
run: |
mkdir -p dev/logs
set -a; source .env; set +a
(cd api && NODE_ENV=development node index.ts > ../dev/logs/api.log 2>&1 &)
(cd ui && NODE_CONFIG_DIR=../api/config/ ../node_modules/.bin/vite --port $DEV_UI_PORT > ../dev/logs/ui.log 2>&1 &)

- name: Wait for API to be ready
- name: Wait for the dev API and UI to be ready
run: |
set -a; source .env; set +a
for i in $(seq 1 30); do
curl -sf http://localhost:$(grep DEV_API_PORT .env | cut -d= -f2)/api/ping && break
if curl -sf "http://localhost:${DEV_API_PORT}/api/ping" >/dev/null \
&& curl -sf "http://localhost:${DEV_UI_PORT}/events/" >/dev/null; then
exit 0
fi
sleep 2
done
echo "dev API or UI did not become ready" >&2
tail -n 50 dev/logs/*.log
exit 1

# one job while the whole suite stays well under ~10 min (tooling.md "CI test jobs")
- name: Run tests
run: npm run test-unit && npm run test-api
run: npm test

- name: Upload test results
if: failure()
uses: actions/upload-artifact@v7
with:
name: test-results
path: |
test-results/
dev/logs/
retention-days: 7

- name: Docker compose logs on failure
if: failure()
run: docker compose --profile '*' logs

- name: Audit
run: npm audit --omit=dev --audit-level=critical
run: npm audit --omit=dev --audit-level=critical
2 changes: 1 addition & 1 deletion .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1 +1 @@
npx --no-install commitlint --edit ""
npx --no -- commitlint --edit "$1"
2 changes: 1 addition & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1 +1 @@
npm run quality
bash dev/pre-push.sh
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24
24.22.1
38 changes: 15 additions & 23 deletions .zellij.kdl
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
// Dev environment of events, see @data-fair/lib-dev. Agents: npx --no df-dev status | logs <pane> | restart <pane>
layout {
pane {
split_direction "vertical"
pane name="events" borderless=true {
command "bash"
args "-ic" "nvm use > /dev/null 2>&1 && bash"
}
pane name="deps" {
command "bash"
args "-ic" "npm run dev-deps"
}
pane split_direction="vertical" {
pane name="events" borderless=true
pane name="status" command="./node_modules/.bin/df-status" {
args "--start-deps"
}
pane {
split_direction "vertical"
pane name="ui" {
command "bash"
args "-ic" "nvm use > /dev/null 2>&1 && npm -w ui run dev"
}
pane name="api" {
command "bash"
args "-ic" "nvm use > /dev/null 2>&1 && npm -w api run dev"
}
}
pane split_direction="vertical" {
pane name="ui" command="bash" {
args "-c" "cd ui && FORCE_COLOR=1 NODE_CONFIG_DIR=../api/config/ exec ../node_modules/.bin/df-mem-cap vite --port $DEV_UI_PORT"
}
pane size=2 borderless=true {
command "bash"
args "-ic" "echo -n -e \"Dev server available at \\e[1;96mhttp://$DEV_HOST:$NGINX_PORT\\033[0m\""
pane name="api" command="bash" {
args "-c" "./node_modules/.bin/df-dev wait-deps && cd api && FORCE_COLOR=1 NODE_ENV=development DEBUG='upgrade,webpush' exec ../node_modules/.bin/nodemon -e js,ts,json index.ts"
}
}
pane name="url" size=1 borderless=true command="bash" {
args "-c" "echo -n -e \"Dev server available at \\e[1;96mhttp://$DEV_HOST:$NGINX_PORT/events/dev\\e[0m\"; exec sleep infinity"
}
}
71 changes: 42 additions & 29 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,61 @@ Types are mostly managed from JSON schemas (for example @api/types/subscription/

## Dev environment

Development processes (dev-api, dev-ui, docker compose services) are managed by the user, not by you. **Never attempt to start, stop, restart, or kill any dev process or container.**
The user starts the dev environment of a checkout with `npm run dev-zellij` (zellij >= 0.44, session named after the checkout directory). Agents never start or kill the session, and act on processes and containers only through `df-dev`:

### Checking status
```bash
npx --no df-dev status # containers (health, memory), panes (running / exited + code, readiness)
npx --no df-dev logs api -n 100 # last lines of a pane: api, ui
npx --no df-dev restart api # Ctrl+C, rerun, then wait for /api/ping
npx --no df-dev up maildev # optional container (compose profile), added to the running env
```

Run `bash dev/status.sh` to see which services are up or down. This probes all known endpoints and reports their status. Always use this when:
- A test fails with a connection error
- You suspect a service might be down
- Before reporting an environment issue to the user
- Read the logs before restarting: nodemon (api) and vite (ui) already reload on file changes. Restart only a pane that exited, hangs, or needs new env.
- If `restart` answers "not ready", report to the user with the logs instead of retrying.
- Never use `docker compose restart`, `kill`, `zellij action`, nor `npm ci` / `npm install` while the env runs.
- Always `npx --no df-dev`, never a bare `npx df-dev`.
- Optional containers: `maildev` (notification mails sent through simple-directory). The user can start it with the env: `npm run dev-zellij -- --services maildev`, or later with `npx --no df-dev up maildev`.

### Consulting logs
Each git worktree gets its own `.env` (random ports + `<branch>.localhost` host). From the main checkout:

Log files are in `dev/logs/`:
- `dev/logs/dev-api.log` — API server (nodemon). Check this for startup failures or runtime errors.
- `dev/logs/dev-ui.log` — UI dev server (vite).
- `dev/logs/docker-compose.log` — All docker compose services (nginx, simple-directory, mongo).
```bash
npx --no df-worktree feat-xyz # ../events_feat-xyz: npm ci + df-dev setup (.env, types, UI build)
npx --no df-worktree-delete feat-xyz # containers, volumes, zellij session, then the worktree
```

Use `tail -n 50 dev/logs/<file>` to see recent output, or `grep -i error dev/logs/<file>` to find errors.
Branch names: letters, digits, `.`, `_`, `-`, at most 60 characters, no `/`.

### When something is down

If a service is down, do not try to fix the infrastructure. Instead:
1. Run `bash dev/status.sh` to identify what's down.
2. Check relevant logs in `dev/logs/` for errors.
3. Report the problem clearly to the user with the status output and any relevant log lines.

Port assignments are in `.env`. Do not modify them.
Port assignments are in `.env` (generated by `npx --no df-dev setup`). Do not modify them.

## Testing

Run tests: `npm run test`
Run specific tests: `npm run test tests/events.api.spec.ts`
Run tests: `npm run test` (`npm run test-api-fast` stops at the first api failure while iterating)
Run specific tests: `npm run test tests/features/events/events.api.spec.ts`

If a test fails with a connection error, run `bash dev/status.sh` to diagnose, then stop and ask the user for help.
On a failure the api pane logs are attached to the trace (`npx playwright show-trace test-results/<test>/trace.zip`); per-test durations of the last run are in `dev/logs/test-timings.json`. Keep the 30 s test timeout (hooks included): a heavy spec calls `test.slow()`. e2e sessions come from simple-directory's API (@tests/fixtures/login.ts), not its login form.

Test users are defined in @dev/resources/users.json and organizations in @dev/resources/organizations.json. Modify these as little as possible, but if you do you need to force reload of the simple-directory container `docker compose restart simple-directory`.
If a test fails with a connection error, run `npx --no df-dev status` and `npx --no df-dev logs api` to diagnose, then stop and ask the user for help.

Test users are defined in @dev/resources/users.json and organizations in @dev/resources/organizations.json. Modify these as little as possible, but if you do you need to ask the user to reload the simple-directory container.

Scope tests use the non-human identity `test1-scope-nhi` (admin of `test1`, declared in @dev/resources/users.json) through `nhiSession(scope?)` in @tests/support/nhi.ts: it returns cookie and bearer clients for a session limited to the requested scope. It needs the simple-directory `master` image (scoped NHI token exchange, `MANAGE_NHIS`).

Tests are separated in playwright projects: unit (pure functions), api (stateful API endpoints through HTTP) and e2e (UI with playwright browser instrumentation). When working on the e2e part you can use subagents `playwright-test-generator` and `playwright-test-healer`.
Specs live in `tests/features/<feature>/` (`<name>.unit|api|e2e.spec.ts`); setups, fixtures and support helpers stay at the root of `tests/`. Tests are separated in playwright projects: unit (pure functions), api (stateful API endpoints through HTTP) and e2e (UI with playwright browser instrumentation). When working on the e2e part you can use subagents `playwright-test-generator` and `playwright-test-healer`.

In case of failures you might find error contexts in @test-results.

Webhook targets: `startHookServer(handler)` in @tests/support/hook-server.ts listens on a port picked by the OS (never a fixed port: worktrees running their tests at the same time would receive each other's deliveries); `closedPortUrl` is a target where nothing listens.

Quality gates:

| When | What |
|---|---|
| commit (`.husky/pre-commit`) | `npm run lint` |
| push (`.husky/pre-push` → `dev/pre-push.sh`) | type check, unit tests, the `@smoke` api/e2e tests, and the api/e2e spec files changed since `origin/main` |
| pull request, release tag (`reuse-quality.yml`) | lint, types, UI build, audit, and the whole unit/api/e2e suite |

`@smoke` (`test('...', { tag: '@smoke' }, ...)`) marks a few fast tests over the core flows (event push, subscription and notification, webhook creation, scoped session, subscriptions and notifications pages). Tag a test only if it is fast and covers a path many features depend on. `npm run quality` runs the full gate locally.

### Debugging e2e failures

When e2e tests fail, follow this order:
Expand All @@ -69,15 +82,15 @@ events is scope-aware (`session.init(..., { scopeAware: true })`): a session who
- Every new route that reads the session gets a row in `scopedOperations` and `requireOperationScope('<operationId>')` as its first handler. Without one it stays refused to scoped sessions, as push registrations and admin routes are on purpose.
- A new websocket channel needs a case in `channelScopes`, otherwise scoped sessions cannot subscribe to it.
- `GET /api/api-docs.json` (@api/contract/api-docs.ts, built from the same table) is the document data-fair merges into the platform document: keep it independent of the request origin (data-fair fetches it over the private url) and keep each accepted scope as its own `security` alternative. Edit the table, not the document.
- Tests: @tests/scopes.unit.spec.ts and @tests/scopes.api.spec.ts, with `nhiSession` (see Testing).
- Tests: @tests/features/scopes/scopes.unit.spec.ts and @tests/features/scopes/scopes.api.spec.ts, with `nhiSession` (see Testing).

## Code patterns

When working on this project, read the following files on a need-to-know basis to understand conventions:

- API route pattern: @api/src/subscriptions/router.ts
- Vue page pattern: @ui/src/pages/
- Unit test pattern: @tests/subscriptions.unit.spec.ts
- API test pattern: @tests/subscriptions.api.spec.ts
- E2E test pattern: @tests/subscriptions.e2e.spec.ts
- Unit test pattern: @tests/features/subscriptions/subscriptions.unit.spec.ts
- API test pattern: @tests/features/subscriptions/subscriptions.api.spec.ts
- E2E test pattern: @tests/features/subscriptions/subscriptions.e2e.spec.ts
- Type generation from JSON schemas: @api/types/subscription/schema.js
13 changes: 8 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@

- A Javascript/Typescript IDE with [Vue.js](https://vuejs.org/) and [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) support.
- A recent [Docker](https://docs.docker.com/engine/install/) installation.
- [Node.js v22+](https://nodejs.org/)
- [Node.js v24.22.1+](https://nodejs.org/)

## Install dependencies

Install npm dependencies for all workspaces:
Install npm dependencies for all workspaces, then generate `.env` (ports), the types and the UI build:
```
npm install
npm ci
npx --no df-dev setup
```

Pull images at first and then once in a while:

```bash
ddocker compose pull
docker compose --profile '*' pull
```

## Run the development servers
Expand All @@ -25,10 +26,12 @@ The UI is a [nuxt](https://nuxt.com/) project.

The API is a small [https://expressjs.com](Express) server.

The recommended way to run the development servers is to use [zellij](https://zellij.dev/):
The development servers run in a [zellij](https://zellij.dev/) (>= 0.44) session, see `@data-fair/lib-dev`:

```
npm run dev-zellij
# with optional containers
npm run dev-zellij -- --services maildev
```

To send notifications in the dev environment you can edit en run [send-notifications.ts](dev/scripts/send-notifications.ts):
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
##########################
FROM node:24.21.0-alpine3.24 AS base
FROM node:24.22.1-alpine3.24@sha256:1200d815de24f8dcca745fbf7651d166034a35adbcad8860a8538ffff44f1420 AS base

# pick up alpine security fixes published after the base image was built
RUN apk upgrade --no-cache
Expand Down Expand Up @@ -79,4 +79,4 @@ EXPOSE 8080
EXPOSE 9090
USER node
WORKDIR /app/api
CMD ["node", "--max-http-header-size", "64000", "--experimental-strip-types", "index.ts"]
CMD ["node", "--max-http-header-size", "64000", "index.ts"]
8 changes: 4 additions & 4 deletions api/config/development.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import dotenv from 'dotenv'
dotenv.config({ path: import.meta.resolve('../../.env').replace('file://', '') })

if (!process.env.DEV_API_PORT) throw new Error('missing DEV_API_PORT env variable, use "dotenv -- npm run dev" or configure .env file')
if (!process.env.DEV_API_PORT) throw new Error('missing DEV_API_PORT env variable, run "npx --no df-dev setup"')

export default {
port: process.env.DEV_API_PORT,
Expand All @@ -12,8 +12,8 @@ export default {
port: 9092
},
secretKeys: {
identities: 'SECRET_IDENTITIES',
events: 'SECRET_EVENTS',
sendMails: 'SECRET_SENDMAILS'
identities: 'secret-identities',
events: 'secret-events',
sendMails: 'secret-sendmails'
}
}
Loading
Loading