diff --git a/AGENTS.md b/AGENTS.md index f5c5bc7..79935fc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,6 +83,11 @@ one home, linking to it rather than restating (credentials, env vars, rotation s - Node 24 runs `.ts` directly (type stripping). Keep all TypeScript **erasable** (`erasableSyntaxOnly` is on): no `enum`, `namespace`, parameter properties, or decorators. Import local modules with their `.ts` extension. +- **No `.mjs`.** Write modules as `.ts` (Prio 1) — even standalone scripts run in bare + `node:24` containers (the e2e mock servers, `examples/shifts-upstream/server.ts`): Node + strips types and detects ESM from syntax, no package.json needed. If a file genuinely + must be plain JavaScript, use `.js` (Prio 2); `"type": "module"` is already set in both + `package.json`s, so `.js` is ESM. - **No build step** and no compiled artifacts — do not add a bundler or `tsc` emit. - Before finishing a change, run the typecheck and tests above; both must pass. - Tests use the built-in `node --test` runner — no test framework dependency. diff --git a/README.md b/README.md index 60af7ff..4c765d8 100644 --- a/README.md +++ b/README.md @@ -680,9 +680,9 @@ docker compose -f compose.yml -f e2e-tests/compose.oauth.yml down -v **Full browser flow** (`full-flow.spec.ts`) — the real Playwright UI against the live stack: the themed **password login** and a **mocked-SSO** login (an in-network mock OIDC provider, -`e2e-tests/mock-oidc.mjs`), **menu filtering by role**, the **users/groups/roles** admin CRUD, a +`e2e-tests/mock-oidc.ts`), **menu filtering by role**, the **users/groups/roles** admin CRUD, a permission-gated **plugin page**, and **logout**. Because the themed form posts straight to -Kratos and cookies are host-scoped, a tiny same-origin gateway (`e2e-tests/proxy.mjs`) fronts web + +Kratos and cookies are host-scoped, a tiny same-origin gateway (`e2e-tests/proxy.ts`) fronts web + Kratos on one host (`ory/kratos/e2e-proxy.yml` points Kratos at it) — exactly as a production reverse proxy would. @@ -932,7 +932,7 @@ ory/ Ory service config (kratos/: identity schema, kratos.yml, o plugins/ Drop-in plugin folders (scanned at /app/plugins; bind-mount or bake in). Ships scheduling/ — the reference plugin (list/form over an upstream + permission-gated nav) you copy examples/ Non-app helpers; shifts-upstream/ is the dev mock backend the reference plugin reads/writes (stand-in for your real service) docs/ Reference docs (plugin-contract.md — the authoritative plugin API) -e2e-tests/ Playwright E2E: visual.spec (design system, Ory-free) + auth-refresh.spec (token timeout/re-mint) + oauth-login.spec (OAuth2 login + consent) + full-flow.spec (browser UI: password/SSO login, menu-by-role, admin CRUD, plugin page, logout) + devstack-login.spec (regression: login works from the banner's localhost URL and 127.0.0.1 is canonicalised, on the plain `docker compose up` topology); proxy.mjs (same-origin gateway) + mock-oidc.mjs (mock SSO provider) back full-flow. e2e-tests/Dockerfile + e2e-tests/compose.{visual,auth,oauth,full,devstack}.yml run them +e2e-tests/ Playwright E2E: visual.spec (design system, Ory-free) + auth-refresh.spec (token timeout/re-mint) + oauth-login.spec (OAuth2 login + consent) + full-flow.spec (browser UI: password/SSO login, menu-by-role, admin CRUD, plugin page, logout) + devstack-login.spec (regression: login works from the banner's localhost URL and 127.0.0.1 is canonicalised, on the plain `docker compose up` topology); proxy.ts (same-origin gateway) + mock-oidc.ts (mock SSO provider) back full-flow. e2e-tests/Dockerfile + e2e-tests/compose.{visual,auth,oauth,full,devstack}.yml run them ci.sh The full CI gate: typecheck → unit tests → every E2E suite, each on a fresh, always-torn-down stack (`bash ci.sh`) ``` diff --git a/compose.override.yml b/compose.override.yml index 033727e..55c54ae 100644 --- a/compose.override.yml +++ b/compose.override.yml @@ -25,7 +25,7 @@ services: # the real backend instead. Uses the pinned app image so there's nothing extra to build/pull. shifts-upstream: image: node:24.16.0-alpine3.24 - command: node /srv/server.mjs + command: node /srv/server.ts restart: unless-stopped volumes: - ./examples/shifts-upstream:/srv:ro diff --git a/e2e-tests/compose.full.yml b/e2e-tests/compose.full.yml index 0f29950..e8bfed7 100644 --- a/e2e-tests/compose.full.yml +++ b/e2e-tests/compose.full.yml @@ -1,6 +1,6 @@ # Full browser E2E — the real Playwright UI flow against the live stack: password + # mocked-SSO login, menu filtering by role, users/groups/roles CRUD, a plugin page, logout. A tiny -# same-origin gateway (proxy, e2e-tests/proxy.mjs) fronts web + Kratos on one host so the browser's cookies +# same-origin gateway (proxy, e2e-tests/proxy.ts) fronts web + Kratos on one host so the browser's cookies # round-trip (ory/kratos/e2e-proxy.yml points Kratos at it); a mock OIDC provider backs the SSO test. # docker compose -f compose.yml -f e2e-tests/compose.full.yml run --build --rm e2e # docker compose -f compose.yml -f e2e-tests/compose.full.yml down -v # tear down after @@ -42,9 +42,9 @@ services: # The reference plugin's upstream (examples/shifts-upstream) so /scheduling/shifts shows real rows. shifts-upstream: image: node:24.16.0-alpine3.24 - command: ["node", "/server.mjs"] + command: ["node", "/server.ts"] volumes: - - ./examples/shifts-upstream/server.mjs:/server.mjs:ro + - ./examples/shifts-upstream/server.ts:/server.ts:ro healthcheck: test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:4000/shifts"] interval: 2s @@ -55,22 +55,22 @@ services: # verifies via its jwks. Reachable as the same host (mock-oidc:9000) by both the browser and Kratos. mock-oidc: image: node:24.16.0-alpine3.24 - command: ["node", "/mock-oidc.mjs"] + command: ["node", "/mock-oidc.ts"] environment: ISSUER: http://mock-oidc:9000 SSO_EMAIL: sso-user@plainpages.local volumes: - - ./e2e-tests/mock-oidc.mjs:/mock-oidc.mjs:ro + - ./e2e-tests/mock-oidc.ts:/mock-oidc.ts:ro healthcheck: test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9000/.well-known/openid-configuration"] interval: 2s timeout: 4s retries: 15 - # Same-origin gateway: Kratos-owned paths → kratos, everything else → web (e2e-tests/proxy.mjs). + # Same-origin gateway: Kratos-owned paths → kratos, everything else → web (e2e-tests/proxy.ts). proxy: image: node:24.16.0-alpine3.24 - command: ["node", "/proxy.mjs"] + command: ["node", "/proxy.ts"] depends_on: web: condition: service_healthy @@ -78,7 +78,7 @@ services: KRATOS_URL: http://kratos:4433 WEB_URL: http://web:3000 volumes: - - ./e2e-tests/proxy.mjs:/proxy.mjs:ro + - ./e2e-tests/proxy.ts:/proxy.ts:ro healthcheck: test: ["CMD", "wget", "-q", "-O", "-", "http://localhost/public/css/styles.css"] interval: 2s diff --git a/e2e-tests/mock-oidc.mjs b/e2e-tests/mock-oidc.ts similarity index 100% rename from e2e-tests/mock-oidc.mjs rename to e2e-tests/mock-oidc.ts diff --git a/e2e-tests/proxy.mjs b/e2e-tests/proxy.ts similarity index 100% rename from e2e-tests/proxy.mjs rename to e2e-tests/proxy.ts diff --git a/examples/shifts-upstream/server.mjs b/examples/shifts-upstream/server.ts similarity index 100% rename from examples/shifts-upstream/server.mjs rename to examples/shifts-upstream/server.ts diff --git a/ory/kratos/e2e-proxy.yml b/ory/kratos/e2e-proxy.yml index 76ae361..1f16273 100644 --- a/ory/kratos/e2e-proxy.yml +++ b/ory/kratos/e2e-proxy.yml @@ -1,6 +1,6 @@ # Browser-E2E overlay (e2e-tests/compose.full.yml) — merged after kratos.yml via a second `-c`. The # full-flow suite drives the real browser, so web + Kratos must share one origin (the `proxy` -# gateway, e2e-tests/proxy.mjs). Point Kratos' public base_url and every self-service URL at that host so +# gateway, e2e-tests/proxy.ts). Point Kratos' public base_url and every self-service URL at that host so # the flow action, the session cookie, and the after-login redirect all stay same-origin as the # browser sees them. The normal (10m) tokenizer TTL from kratos.yml is kept — no re-mint mid-test. serve: