@zitadel/testing 0.0.0 → 0.1.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @zitadel/testing
2
2
 
3
3
  Test-kit for **seeded ephemeral local Zitadel instances**: boot the real server
4
- (binary runtime + embedded Postgres, no Docker) from test code, create a
4
+ (binary runtime + SQLite by default, no Docker) from test code, create a
5
5
  project with the default login flow, and mint password users that can complete
6
6
  the real login journey immediately.
7
7
 
@@ -54,15 +54,12 @@ export default defineConfig({
54
54
 
55
55
  ```ts
56
56
  // my-login.spec.ts
57
- import { expect, test } from "@zitadel/testing/playwright";
57
+ import { expect, loginWithPassword, test } from "@zitadel/testing/playwright";
58
58
 
59
59
  test("user signs in with password", async ({ page, seed }) => {
60
60
  const user = await seed.user(); // unique email + password, loginable now
61
61
  await page.goto("/login");
62
- await page.getByLabel(/email/i).fill(user.email);
63
- await page.getByRole("button", { name: "Sign in", exact: true }).click();
64
- await page.getByLabel(/password/i).fill(user.password);
65
- await page.getByRole("button", { name: "Sign in", exact: true }).click();
62
+ await loginWithPassword(page, user); // drives the identifier → password steps
66
63
  await expect(page).toHaveURL(/\/admin/);
67
64
  });
68
65
  ```
@@ -73,6 +70,24 @@ apps; the console maps the same fields to `VITE_*`/`CONSOLE_*` names instead.
73
70
  The fixtures find the instance through `ZITADEL_TESTING_HANDSHAKE`, which
74
71
  `withZitadel()` points at its handshake file.
75
72
 
73
+ `app` is optional. Omit it when the instance itself serves the app — the
74
+ Zitadel binary embeds the console and hosted sign-in at `/ui/console/` and
75
+ `/ui/login/`, so a suite testing those surfaces has no second server to boot.
76
+ Only the instance entry is generated, and `appOrigin` must then be the
77
+ instance's own local origin:
78
+
79
+ ```ts
80
+ export default defineConfig({
81
+ testDir: "./e2e",
82
+ use: { baseURL: "http://localhost:8092" },
83
+ ...withZitadel({
84
+ configDir: import.meta.dirname,
85
+ port: 8092,
86
+ appOrigin: "http://localhost:8092", // the instance is the app server
87
+ }),
88
+ });
89
+ ```
90
+
76
91
  ### Start tests authenticated
77
92
 
78
93
  Most app tests don't want to re-test login. `authenticatedPage` seeds a user,
@@ -96,6 +111,65 @@ log in through the UI for those. `seed.identity()` complements registration
96
111
  specs: an unused email+password that creates nothing, so the flow under test
97
112
  must create the user.
98
113
 
114
+ ### Drive the login flow
115
+
116
+ Four ceremony helpers complete whole auth journeys against the
117
+ `<zitadel-login>` widget. They are built on the widget's documented
118
+ automation hooks (`zitadel-action-*`, `zitadel-field-*` / `zitadel-input-*`),
119
+ not on translated button texts, so they survive locale and copy changes:
120
+
121
+ ```ts
122
+ import {
123
+ loginWithPassword, // identifier → password (handles combined steps too)
124
+ loginWithPasskey, // identifier-first; or one-tap without an email
125
+ registerWithPassword, // unknown identifier → registration → password path
126
+ registerWithPasskey, // unknown identifier → registration → passkey ceremony
127
+ } from "@zitadel/testing/playwright";
128
+
129
+ await page.goto("/login");
130
+ await registerWithPassword(page, { email, password });
131
+ // assert your app's signed-in surface — the helpers never assert app state
132
+ ```
133
+
134
+ They assume the default flow vocabulary (`submit` / `passkey` /
135
+ `passkey_register` actions, `email` and password fields) and branch only on
136
+ what the flow renders: flows that require extra registration fields get them
137
+ via `profile: [{ field, value }]`, filled when present — a boolean value
138
+ drives a checkbox, a string matches a select option or fills a text-like
139
+ input. For custom flows or single steps, `flowAction(page, name)` /
140
+ `flowField(page, name)` return plain locators for the same hooks, with
141
+ `clickFlowAction` / `fillFlowField` as one-line wrappers. Broad fallbacks
142
+ (accessible names via `{ name }` / `{ label }`, the generic `data-action`
143
+ attribute) are scoped to the `<zitadel-login>` host element — so they never
144
+ match same-named controls in your app's own chrome, and they keep working
145
+ for custom templates that render no automation hooks.
146
+
147
+ ### Test passkey flows
148
+
149
+ `enableVirtualPasskey(page)` — also available as the on-demand `passkey`
150
+ fixture — attaches a CDP virtual authenticator to the page (a platform
151
+ authenticator with discoverable credentials and automatic user presence), so
152
+ the real registration and login ceremonies complete without an OS dialog:
153
+
154
+ ```ts
155
+ test("registers and signs in with a passkey", async ({ page, seed, passkey }) => {
156
+ const who = seed.identity();
157
+ await page.goto("/login");
158
+ await registerWithPasskey(page, { email: who.email });
159
+ await expect.poll(() => passkey.credentialCount()).toBe(1);
160
+ });
161
+ ```
162
+
163
+ Constraints, all inherent to WebAuthn/CDP: Chromium projects only (the CDP
164
+ WebAuthn domain exists nowhere else); the authenticator is bound to the page,
165
+ so register and sign back in on the same page (sign out by clearing cookies
166
+ instead of opening a fresh context); and serve the app on an origin WebAuthn
167
+ accepts as a relying-party ID — HTTPS on a real domain, or `http://localhost`
168
+ for local tests; raw IP origins like `127.0.0.1` are invalid. The default
169
+ `password-first` flow offers passkey registration at the registration-choice
170
+ step; boot `preset: "passkey-first"` for one-tap discoverable-credential
171
+ login flows.
172
+
99
173
  The two in-repo consumers are `apps/demo-next-e2e/playwright.real.config.mts`
100
174
  and `apps/console-e2e/playwright.real.config.mts` — run them with
101
175
  `moon run demo-next-e2e:e2e-real` / `moon run console-e2e:e2e-real`.
@@ -122,10 +196,10 @@ import {
122
196
  const z = await startLocalZitadel({
123
197
  port, // default: free port
124
198
  dir, // default: temp dir, removed on stop (caller dirs are kept)
125
- appOrigins, // registered as the project's previewOrigins
199
+ appOrigins, // registered as the project's preview_origins
126
200
  useCase, // "minimal" (default) | "consumer" | "business"
127
201
  preset, // "password-first" (default)
128
- serverBinary, // ZITADEL_SERVER_BINARY override (in-repo: dist/server/nextgen)
202
+ serverBinary, // ZITADEL_SERVER_BINARY override (path to a built server binary)
129
203
  keep, // keep the temp dir for debugging
130
204
  });
131
205
 
@@ -136,7 +210,7 @@ await z.seedUser({ email?, password?, attributes? }); // → { id, email, passwo
136
210
  await z.seedUsers(8, { email?, password?, attributes? }); // per-index templates
137
211
  z.identity(); // unused { email, password } — creates nothing
138
212
  await z.seedSession({ user? }); // → { user, sessionToken, expiresAt, cookie }
139
- await z.stop(); // stop server, reap embedded Postgres, remove owned temp dir
213
+ await z.stop(); // stop server and remove owned temp dir
140
214
  ```
141
215
 
142
216
  `connectZitadel(handle)` returns the same surface minus lifecycle — this is
@@ -144,23 +218,28 @@ what the Playwright fixtures use, and what a future remote-instance mode would
144
218
  build on.
145
219
 
146
220
  From `@zitadel/testing/playwright`: the `test`/`expect` fixtures (`seed.user()`
147
- per test, `zitadel` per worker), plus `withZitadel(options)` returning
148
- `{ webServer }` for the config, and `nextAppEnv`/`applyAppEnvTemplate` for the
149
- env-template mechanism described above.
221
+ per test, `zitadel` per worker, `passkey` on demand), the flow ceremonies
222
+ (`loginWithPassword`, `loginWithPasskey`, `registerWithPassword`,
223
+ `registerWithPasskey`) with their locator-level escape hatches
224
+ (`flowAction`/`flowField`, `clickFlowAction`/`fillFlowField`), plus
225
+ `withZitadel(options)` returning `{ webServer }` for the config,
226
+ `enableVirtualPasskey(page)` for non-fixture pages, and
227
+ `nextAppEnv`/`applyAppEnvTemplate` for the env-template mechanism described
228
+ above.
150
229
 
151
230
  ## How it works
152
231
 
153
232
  - **Lifecycle** shells out to `zitadel start/stop --json` (the CLI owns port
154
- preflight, the health wait, process-group stop, and embedded-Postgres
155
- reaping). Swapping this for direct library calls later will not change the
156
- public API.
233
+ preflight, the health wait, and process-group stop). Swapping this for direct
234
+ library calls later will not change the public API.
157
235
  - **Bootstrap** is the server-side half of `zitadel setup`, no files:
158
- `POST /projects` (unauthenticated; returns the `projectSecret` used as
236
+ `POST /projects` (unauthenticated; returns the `project_secret` used as
159
237
  bearer for everything else) → `POST /schemas` (server assigns the schema id)
160
238
  → `POST /flow_definitions` (default login flow pinned to that schema id).
161
239
  Templates come from `@zitadel/config/defaults`.
162
- - **Seeding** is `POST /users` (the body carries `$schema: <schema id>`) +
163
- `PUT /users/{id}/password` with `isChangeRequired: false`.
240
+ - **Seeding** is `POST /users` (the body names the schema in `schema` and puts
241
+ the schema-defined content under `attributes`) +
242
+ `PUT /users/{id}/password` with `is_change_required: false`.
164
243
 
165
244
  ## Parallelism model
166
245
 
@@ -169,13 +248,13 @@ project, so per-test `seed.user()` calls are isolation enough for login-flow
169
248
  tests, and tests run fully parallel against the shared instance
170
249
  (demonstrated by `demo-next-e2e:e2e-real`, 2 workers).
171
250
 
172
- Measured on an arm64 macBook (dev build, July 2026):
251
+ Typical timings with the SQLite local default (dated estimates from a July 2026 dev build — orders of magnitude, not a benchmark; remeasure locally before relying on them):
173
252
 
174
253
  | Operation | Time |
175
254
  | --- | --- |
176
- | Cold boot (fresh data dir: initdb + migrations + health) | ~20–27s |
177
- | Warm restart (existing data dir) | ~15s |
178
- | Stop incl. embedded-Postgres reap | ~12s |
255
+ | Cold boot (fresh data dir: SQLite migrate + health) | typically under a few seconds |
256
+ | Warm restart (existing data dir) | typically under a second |
257
+ | Stop | typically under a second |
179
258
  | Bootstrap (project + schema + flow) | ~100ms |
180
259
  | Seed one user (create + password) | ~50ms |
181
260
  | Full `e2e-real` suite (boot + Next dev + 2 browser tests) | ~25s |
@@ -199,10 +278,12 @@ Customer installs get the published server binary through `@zitadel/server`'s
199
278
  platform packages; the in-repo workspace carries no such binary, so the repo's
200
279
  own suites run `moon run server:build` and point the kit at the result via
201
280
  `ZITADEL_SERVER_BINARY` (the `withZitadel` option `zitadel.serverBinary` /
202
- `serverBinaryHint` exists for this). The in-repo moon tasks set
281
+ `serverBinaryHint` exists for this). Most in-repo moon tasks set
203
282
  `NEXTGEN_SERVER_LOGIN_ENABLED=false` / `NEXTGEN_SERVER_CONSOLE_ENABLED=false`
204
- the suites drive the app-embedded login, not the server-hosted `/ui/*`.
205
- Customer installs need none of this.
283
+ because they drive the app-embedded login, not the server-hosted `/ui/*`; the
284
+ exception is `console-e2e:e2e-embedded`, which keeps both surfaces on and
285
+ omits `app` — the binary-served `/ui/*` pages are its subject. Customer
286
+ installs need none of this.
206
287
 
207
288
  ## Known limitations
208
289
 
@@ -211,8 +292,8 @@ Customer installs need none of this.
211
292
  processes (Playwright workers) are unaffected.
212
293
  - macOS/Linux only for now. Not because of the port preflight (it degrades
213
294
  gracefully where `lsof` is missing) — the untested surface on Windows is the
214
- process-group stop and embedded-Postgres lifecycle. Revisit once the local
215
- runtime's SQLite default removes the Postgres component.
295
+ process-group stop for the local binary runtime. Revisit when Windows local
296
+ runtime support is a goal.
216
297
 
217
298
  ## Roadmap
218
299
 
@@ -238,19 +319,20 @@ on top, in intended order:
238
319
  `bootstrapProject({ baseUrl })` + `connectZitadel(handle)` already compose
239
320
  into this today; formalizing it means cleanup semantics and docs.
240
321
  5. **Vercel Sandbox runtime.** A publicly reachable ephemeral instance per
241
- preview deployment (e.g. `@zitadel/testing/vercel`), returning the same
242
- `InstanceHandle` so fixtures don't change. Gated on a spike: embedded
243
- Postgres on the Sandbox image, forwarded proto/host handling, secure
244
- cookies + issuer + handoff verification through `sandbox.domain()`,
245
- registering the preview URL as an allowed origin post-deploy
246
- (`PATCH /projects`), and cleanup that survives failed runs.
322
+ preview deployment (e.g. `@zitadel/testing/vercel`), returning the same
323
+ `InstanceHandle` so fixtures don't change. Gated on a spike: SQLite (or
324
+ another Sandbox-friendly store) on the Sandbox image, forwarded proto/host
325
+ handling, secure cookies + issuer + handoff verification through
326
+ `sandbox.domain()`, registering the preview URL as an allowed origin
327
+ post-deploy (`PATCH /projects`), and cleanup that survives failed runs.
247
328
  6. **Publishing.** Landed: the consumer journey installs the kit the way a
248
- customer would in CI, and the kit ships on the release train (release
249
- manifest + changeset `fixed` group), versioned in lockstep with
250
- `@zitadel/cli`. Remaining: the Windows decision (deferred until the SQLite
251
- local default removes the embedded-Postgres lifecycle) and the stability
252
- commitment when the train leaves alpha.
253
-
254
- Parked until server support exists: passkey seeding (pre-registered WebAuthn
255
- credentials). Independent refactor: extract `apps/cli/src/lib/local-server`
256
- into a shared package and swap the lifecycle shell-out for imports.
329
+ customer would in CI, and the kit ships on the release train (release
330
+ manifest + changeset `fixed` group), versioned in lockstep with
331
+ `@zitadel/cli`. Remaining: the Windows decision for the local binary
332
+ runtime and the stability commitment when the train leaves alpha.
333
+
334
+ Parked until server support exists: passkey *seeding* (pre-registered WebAuthn
335
+ credentials) UI-driven passkey ceremonies are covered today by
336
+ `enableVirtualPasskey`. Independent refactor: extract
337
+ `apps/cli/src/lib/local-server` into a shared package and swap the lifecycle
338
+ shell-out for imports.
@@ -16,9 +16,9 @@ interface InstanceHandle {
16
16
  */
17
17
  appOrigin?: string;
18
18
  /**
19
- * Server-assigned id of the seeded user schema. User documents must
20
- * reference it via their `$schema` field, so seeding needs it alongside the
21
- * project credential.
19
+ * Server-assigned id of the seeded user schema. A user names it in `schema`
20
+ * and puts the schema-defined content under `attributes`, so seeding needs
21
+ * it alongside the project credential.
22
22
  */
23
23
  schemaId: string;
24
24
  previewSecret?: string;
@@ -119,4 +119,4 @@ declare const nextAppEnv: AppEnvTemplate;
119
119
  declare function applyAppEnvTemplate(template: AppEnvTemplate, handle: InstanceHandle): Record<string, string>;
120
120
  //#endregion
121
121
  export { Identity as a, LocalZitadelRuntime as c, SeedUserInput as d, SeedUsersTemplate as f, ConnectedZitadel as i, MintedSession as l, SessionCookie as m, applyAppEnvTemplate as n, InstanceHandle as o, SeededUser as p, nextAppEnv as r, LocalZitadel as s, AppEnvTemplate as t, SeedSessionInput as u };
122
- //# sourceMappingURL=app-env-D3W0GYhA.d.mts.map
122
+ //# sourceMappingURL=app-env-Btmfcflb.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-env-D3W0GYhA.d.mts","names":[],"sources":["../src/types.ts","../src/app-env.ts"],"mappings":";;;;;AAMA;;UAAiB,cAAA;EACf,OAAA;EACA,SAAA;EACA,aAAA;EAAA;;;;;EAMA,SAAA;EAUe;;;;;EAJf,QAAA;EACA,aAAA;AAAA;AAAA,UAGe,aAAA;EACf,KAAA;EACA,QAAA;EAKyB;EAHzB,UAAA,GAAa,MAAA;AAAA;AAAA,UAGE,UAAA;EACf,EAAA;EACA,KAAA;EACA,QAAA;AAAA;AAIF;AAAA,UAAiB,QAAA;EACf,KAAA;EACA,QAAA;AAAA;AAIF;AAAA,UAAiB,aAAA;EACf,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA,EAAM,UAAA;EAJF;EAMJ,YAAA;EACA,SAAA;EACA,MAAA,EAAQ,aAAA;AAAA;AAAA,UAGO,gBAAA,SAAyB,aAAA;EAPlC;EASN,IAAA,GAAO,UAAA;EANP;EAQA,kBAAA;EAPQ;;;AAGV;;EAUE,MAAA;AAAA;AAAA,UAGe,iBAAA;EACf,KAAA,IAAS,KAAA;EACT,QAAA,IAAY,KAAA;EACZ,UAAA,IAAc,KAAA,aAAkB,MAAA;AAAA;AAAA,UAGjB,gBAAA;EACf,MAAA,EAAQ,cAAA;EAPO;EASf,GAAA,EAAK,aAAA;;EAEL,MAAA,EAAQ,MAAA;EACR,QAAA,CAAS,KAAA,GAAQ,aAAA,GAAgB,OAAA,CAAQ,UAAA;EAXhC;EAaT,SAAA,CAAU,KAAA,UAAe,QAAA,GAAW,iBAAA,GAAoB,OAAA,CAAQ,UAAA;EAZpD;EAcZ,QAAA,IAAY,QAAA;EAbE;;;;AAGhB;EAgBE,WAAA,CAAY,KAAA,GAAQ,gBAAA,GAAmB,OAAA,CAAQ,aAAA;AAAA;AAAA,UAGhC,mBAAA;EACf,IAAA;EACA,GAAA;EAfiB;EAiBjB,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,YAAA,SAAqB,gBAAA,EAAkB,eAAA;EACtD,OAAA,EAAS,mBAAA;EACT,IAAA,IAAQ,OAAA;AAAA;;;;;AAxGV;;;;KCEY,cAAA,GAAiB,MAAA,eAAqB,cAAA;;;;;;cAOrC,UAAA,EAAY,cAAA;AAAA,iBAMT,mBAAA,CACd,QAAA,EAAU,cAAA,EACV,MAAA,EAAQ,cAAA,GACP,MAAA"}
1
+ {"version":3,"file":"app-env-Btmfcflb.d.mts","names":[],"sources":["../src/types.ts","../src/app-env.ts"],"mappings":";;;;;AAMA;;UAAiB,cAAA;EACf,OAAA;EACA,SAAA;EACA,aAAA;EAAA;;;;;EAMA,SAAA;EAUe;;;;;EAJf,QAAA;EACA,aAAA;AAAA;AAAA,UAGe,aAAA;EACf,KAAA;EACA,QAAA;EAKyB;EAHzB,UAAA,GAAa,MAAA;AAAA;AAAA,UAGE,UAAA;EACf,EAAA;EACA,KAAA;EACA,QAAA;AAAA;AAIF;AAAA,UAAiB,QAAA;EACf,KAAA;EACA,QAAA;AAAA;AAIF;AAAA,UAAiB,aAAA;EACf,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA,EAAM,UAAA;EAJF;EAMJ,YAAA;EACA,SAAA;EACA,MAAA,EAAQ,aAAA;AAAA;AAAA,UAGO,gBAAA,SAAyB,aAAA;EAPlC;EASN,IAAA,GAAO,UAAA;EANP;EAQA,kBAAA;EAPQ;;;AAGV;;EAUE,MAAA;AAAA;AAAA,UAGe,iBAAA;EACf,KAAA,IAAS,KAAA;EACT,QAAA,IAAY,KAAA;EACZ,UAAA,IAAc,KAAA,aAAkB,MAAA;AAAA;AAAA,UAGjB,gBAAA;EACf,MAAA,EAAQ,cAAA;EAPO;EASf,GAAA,EAAK,aAAA;;EAEL,MAAA,EAAQ,MAAA;EACR,QAAA,CAAS,KAAA,GAAQ,aAAA,GAAgB,OAAA,CAAQ,UAAA;EAXhC;EAaT,SAAA,CAAU,KAAA,UAAe,QAAA,GAAW,iBAAA,GAAoB,OAAA,CAAQ,UAAA;EAZpD;EAcZ,QAAA,IAAY,QAAA;EAbE;;;;AAGhB;EAgBE,WAAA,CAAY,KAAA,GAAQ,gBAAA,GAAmB,OAAA,CAAQ,aAAA;AAAA;AAAA,UAGhC,mBAAA;EACf,IAAA;EACA,GAAA;EAfiB;EAiBjB,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,YAAA,SAAqB,gBAAA,EAAkB,eAAA;EACtD,OAAA,EAAS,mBAAA;EACT,IAAA,IAAQ,OAAA;AAAA;;;;;AAxGV;;;;KCEY,cAAA,GAAiB,MAAA,eAAqB,cAAA;;;;;;cAOrC,UAAA,EAAY,cAAA;AAAA,iBAMT,mBAAA,CACd,QAAA,EAAU,cAAA,EACV,MAAA,EAAQ,cAAA,GACP,MAAA"}
@@ -1,4 +1,4 @@
1
- const require_handshake = require("./handshake-CggSIoxF.cjs");
1
+ const require_handshake = require("./handshake-CRKcgkfN.cjs");
2
2
  const require_orchestration = require("./orchestration-D7QBgQ0l.cjs");
3
3
  let node_child_process = require("node:child_process");
4
4
  //#region src/app-runner.ts
@@ -1,4 +1,4 @@
1
- import { i as applyAppEnvTemplate, n as waitForHandshake } from "./handshake-BPtWruO8.mjs";
1
+ import { i as applyAppEnvTemplate, n as waitForHandshake } from "./handshake-ClzWvG8z.mjs";
2
2
  import { i as parseAppRunnerConfig, o as requireHandshakePath, t as APP_RUNNER_CONFIG_ENV } from "./orchestration-C640_1Lg.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  //#region src/app-runner.ts
@@ -53,13 +53,14 @@ async function waitForHandshake(path, timeoutMs = 6e4) {
53
53
  }
54
54
  }
55
55
  function validateHandle(value, source) {
56
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`handshake file ${source} is not an object`);
56
57
  const handle = value;
57
58
  for (const field of [
58
59
  "baseUrl",
59
60
  "projectId",
60
61
  "projectSecret",
61
62
  "schemaId"
62
- ]) if (typeof handle?.[field] !== "string" || handle[field].length === 0) throw new Error(`handshake file ${source} is missing "${field}"`);
63
+ ]) if (typeof handle[field] !== "string" || handle[field].length === 0) throw new Error(`handshake file ${source} is missing "${field}"`);
63
64
  if (!URL.canParse(handle.baseUrl)) throw new Error(`handshake file ${source} has a malformed "baseUrl": ${handle.baseUrl}`);
64
65
  return handle;
65
66
  }
@@ -95,4 +96,4 @@ Object.defineProperty(exports, "writeHandshake", {
95
96
  }
96
97
  });
97
98
 
98
- //# sourceMappingURL=handshake-CggSIoxF.cjs.map
99
+ //# sourceMappingURL=handshake-CRKcgkfN.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"handshake-CggSIoxF.cjs","names":[],"sources":["../src/app-env.ts","../src/handshake.ts"],"sourcesContent":["import type { InstanceHandle } from \"./types\";\n\n/**\n * Declarative mapping from an app's env var names to InstanceHandle fields.\n * A template (not a function) so it can cross process boundaries: the\n * Playwright config serializes it into the app-runner's environment, where it\n * is applied to the handle read from the handshake file.\n */\nexport type AppEnvTemplate = Record<string, keyof InstanceHandle>;\n\n/**\n * The env shape `@zitadel/sdk-next` apps read. Other frameworks pass their own\n * template — the console maps the same handle fields to `VITE_*`/`CONSOLE_*`\n * names, for example.\n */\nexport const nextAppEnv: AppEnvTemplate = {\n ZITADEL_URL: \"baseUrl\",\n NEXT_PUBLIC_ZITADEL_PROJECT_ID: \"projectId\",\n ZITADEL_PROJECT_SECRET: \"projectSecret\",\n};\n\nexport function applyAppEnvTemplate(\n template: AppEnvTemplate,\n handle: InstanceHandle,\n): Record<string, string> {\n const env: Record<string, string> = {};\n for (const [name, field] of Object.entries(template)) {\n const value = handle[field];\n if (typeof value !== \"string\" || value.length === 0) {\n // Fail instead of silently dropping the var: an app booted without one\n // of its env vars produces a much harder-to-read failure downstream.\n throw new Error(\n `app env template maps \"${name}\" to handle field \"${field}\", which the instance handle does not carry`,\n );\n }\n env[name] = value;\n }\n return env;\n}\n","import { readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\n\nimport type { InstanceHandle } from \"./types\";\n\n/**\n * The handshake file carries an InstanceHandle across process boundaries:\n * written by the script that boots + bootstraps the instance, read by\n * Playwright workers (fixtures) and the app dev-server wrapper.\n */\nexport async function writeHandshake(path: string, handle: InstanceHandle): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(handle, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport function readHandshakeSync(path: string): InstanceHandle {\n const contents = readFileSync(path, \"utf8\");\n let value: unknown;\n try {\n value = JSON.parse(contents);\n } catch (error) {\n throw new Error(`handshake file ${path} contains invalid JSON: ${(error as Error).message}`, {\n cause: error,\n });\n }\n return validateHandle(value, path);\n}\n\nexport async function waitForHandshake(path: string, timeoutMs = 60_000): Promise<InstanceHandle> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n return validateHandle(JSON.parse(await readFile(path, \"utf8\")), path);\n } catch (error) {\n if (Date.now() >= deadline) {\n throw new Error(\n `handshake file not readable within ${timeoutMs}ms: ${path} (${(error as Error).message})`,\n { cause: error },\n );\n }\n await sleep(250);\n }\n }\n}\n\nfunction validateHandle(value: unknown, source: string): InstanceHandle {\n const handle = value as Partial<InstanceHandle> | null;\n for (const field of [\"baseUrl\", \"projectId\", \"projectSecret\", \"schemaId\"] as const) {\n if (typeof handle?.[field] !== \"string\" || handle[field].length === 0) {\n throw new Error(`handshake file ${source} is missing \"${field}\"`);\n }\n }\n if (!URL.canParse(handle.baseUrl as string)) {\n throw new Error(`handshake file ${source} has a malformed \"baseUrl\": ${handle.baseUrl}`);\n }\n return handle as InstanceHandle;\n}\n"],"mappings":";;;;;;;;;;AAeA,MAAa,aAA6B;CACxC,aAAa;CACb,gCAAgC;CAChC,wBAAwB;CACzB;AAED,SAAgB,oBACd,UACA,QACwB;CACxB,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,EAAE;EACpD,MAAM,QAAQ,OAAO;AACrB,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAGhD,OAAM,IAAI,MACR,0BAA0B,KAAK,qBAAqB,MAAM,6CAC3D;AAEH,MAAI,QAAQ;;AAEd,QAAO;;;;;;;;;ACzBT,eAAsB,eAAe,MAAc,QAAuC;AACxF,QAAA,GAAA,iBAAA,QAAA,GAAA,UAAA,SAAoB,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,QAAA,GAAA,iBAAA,WAAgB,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAO,CAAC;;AAGhF,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,YAAA,GAAA,QAAA,cAAwB,MAAM,OAAO;CAC3C,IAAI;AACJ,KAAI;AACF,UAAQ,KAAK,MAAM,SAAS;UACrB,OAAO;AACd,QAAM,IAAI,MAAM,kBAAkB,KAAK,0BAA2B,MAAgB,WAAW,EAC3F,OAAO,OACR,CAAC;;AAEJ,QAAO,eAAe,OAAO,KAAK;;AAGpC,eAAsB,iBAAiB,MAAc,YAAY,KAAiC;CAChG,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,SACE,KAAI;AACF,SAAO,eAAe,KAAK,MAAM,OAAA,GAAA,iBAAA,UAAe,MAAM,OAAO,CAAC,EAAE,KAAK;UAC9D,OAAO;AACd,MAAI,KAAK,KAAK,IAAI,SAChB,OAAM,IAAI,MACR,sCAAsC,UAAU,MAAM,KAAK,IAAK,MAAgB,QAAQ,IACxF,EAAE,OAAO,OAAO,CACjB;AAEH,SAAA,GAAA,qBAAA,YAAY,IAAI;;;AAKtB,SAAS,eAAe,OAAgB,QAAgC;CACtE,MAAM,SAAS;AACf,MAAK,MAAM,SAAS;EAAC;EAAW;EAAa;EAAiB;EAAW,CACvE,KAAI,OAAO,SAAS,WAAW,YAAY,OAAO,OAAO,WAAW,EAClE,OAAM,IAAI,MAAM,kBAAkB,OAAO,eAAe,MAAM,GAAG;AAGrE,KAAI,CAAC,IAAI,SAAS,OAAO,QAAkB,CACzC,OAAM,IAAI,MAAM,kBAAkB,OAAO,8BAA8B,OAAO,UAAU;AAE1F,QAAO"}
1
+ {"version":3,"file":"handshake-CRKcgkfN.cjs","names":[],"sources":["../src/app-env.ts","../src/handshake.ts"],"sourcesContent":["import type { InstanceHandle } from \"./types\";\n\n/**\n * Declarative mapping from an app's env var names to InstanceHandle fields.\n * A template (not a function) so it can cross process boundaries: the\n * Playwright config serializes it into the app-runner's environment, where it\n * is applied to the handle read from the handshake file.\n */\nexport type AppEnvTemplate = Record<string, keyof InstanceHandle>;\n\n/**\n * The env shape `@zitadel/sdk-next` apps read. Other frameworks pass their own\n * template — the console maps the same handle fields to `VITE_*`/`CONSOLE_*`\n * names, for example.\n */\nexport const nextAppEnv: AppEnvTemplate = {\n ZITADEL_URL: \"baseUrl\",\n NEXT_PUBLIC_ZITADEL_PROJECT_ID: \"projectId\",\n ZITADEL_PROJECT_SECRET: \"projectSecret\",\n};\n\nexport function applyAppEnvTemplate(\n template: AppEnvTemplate,\n handle: InstanceHandle,\n): Record<string, string> {\n const env: Record<string, string> = {};\n for (const [name, field] of Object.entries(template)) {\n const value = handle[field];\n if (typeof value !== \"string\" || value.length === 0) {\n // Fail instead of silently dropping the var: an app booted without one\n // of its env vars produces a much harder-to-read failure downstream.\n throw new Error(\n `app env template maps \"${name}\" to handle field \"${field}\", which the instance handle does not carry`,\n );\n }\n env[name] = value;\n }\n return env;\n}\n","import { readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\n\nimport type { InstanceHandle } from \"./types\";\n\n/**\n * The handshake file carries an InstanceHandle across process boundaries:\n * written by the script that boots + bootstraps the instance, read by\n * Playwright workers (fixtures) and the app dev-server wrapper.\n */\nexport async function writeHandshake(path: string, handle: InstanceHandle): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(handle, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport function readHandshakeSync(path: string): InstanceHandle {\n const contents = readFileSync(path, \"utf8\");\n let value: unknown;\n try {\n value = JSON.parse(contents);\n } catch (error) {\n throw new Error(`handshake file ${path} contains invalid JSON: ${(error as Error).message}`, {\n cause: error,\n });\n }\n return validateHandle(value, path);\n}\n\nexport async function waitForHandshake(path: string, timeoutMs = 60_000): Promise<InstanceHandle> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n return validateHandle(JSON.parse(await readFile(path, \"utf8\")), path);\n } catch (error) {\n if (Date.now() >= deadline) {\n throw new Error(\n `handshake file not readable within ${timeoutMs}ms: ${path} (${(error as Error).message})`,\n { cause: error },\n );\n }\n await sleep(250);\n }\n }\n}\n\nfunction validateHandle(value: unknown, source: string): InstanceHandle {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(`handshake file ${source} is not an object`);\n }\n const handle = value as Partial<InstanceHandle>;\n for (const field of [\"baseUrl\", \"projectId\", \"projectSecret\", \"schemaId\"] as const) {\n if (typeof handle[field] !== \"string\" || handle[field].length === 0) {\n throw new Error(`handshake file ${source} is missing \"${field}\"`);\n }\n }\n if (!URL.canParse(handle.baseUrl as string)) {\n throw new Error(`handshake file ${source} has a malformed \"baseUrl\": ${handle.baseUrl}`);\n }\n return handle as InstanceHandle;\n}\n"],"mappings":";;;;;;;;;;AAeA,MAAa,aAA6B;CACxC,aAAa;CACb,gCAAgC;CAChC,wBAAwB;CACzB;AAED,SAAgB,oBACd,UACA,QACwB;CACxB,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,EAAE;EACpD,MAAM,QAAQ,OAAO;AACrB,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAGhD,OAAM,IAAI,MACR,0BAA0B,KAAK,qBAAqB,MAAM,6CAC3D;AAEH,MAAI,QAAQ;;AAEd,QAAO;;;;;;;;;ACzBT,eAAsB,eAAe,MAAc,QAAuC;AACxF,QAAA,GAAA,iBAAA,QAAA,GAAA,UAAA,SAAoB,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,QAAA,GAAA,iBAAA,WAAgB,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAO,CAAC;;AAGhF,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,YAAA,GAAA,QAAA,cAAwB,MAAM,OAAO;CAC3C,IAAI;AACJ,KAAI;AACF,UAAQ,KAAK,MAAM,SAAS;UACrB,OAAO;AACd,QAAM,IAAI,MAAM,kBAAkB,KAAK,0BAA2B,MAAgB,WAAW,EAC3F,OAAO,OACR,CAAC;;AAEJ,QAAO,eAAe,OAAO,KAAK;;AAGpC,eAAsB,iBAAiB,MAAc,YAAY,KAAiC;CAChG,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,SACE,KAAI;AACF,SAAO,eAAe,KAAK,MAAM,OAAA,GAAA,iBAAA,UAAe,MAAM,OAAO,CAAC,EAAE,KAAK;UAC9D,OAAO;AACd,MAAI,KAAK,KAAK,IAAI,SAChB,OAAM,IAAI,MACR,sCAAsC,UAAU,MAAM,KAAK,IAAK,MAAgB,QAAQ,IACxF,EAAE,OAAO,OAAO,CACjB;AAEH,SAAA,GAAA,qBAAA,YAAY,IAAI;;;AAKtB,SAAS,eAAe,OAAgB,QAAgC;AACtE,KAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,MAAM,CACrE,OAAM,IAAI,MAAM,kBAAkB,OAAO,mBAAmB;CAE9D,MAAM,SAAS;AACf,MAAK,MAAM,SAAS;EAAC;EAAW;EAAa;EAAiB;EAAW,CACvE,KAAI,OAAO,OAAO,WAAW,YAAY,OAAO,OAAO,WAAW,EAChE,OAAM,IAAI,MAAM,kBAAkB,OAAO,eAAe,MAAM,GAAG;AAGrE,KAAI,CAAC,IAAI,SAAS,OAAO,QAAkB,CACzC,OAAM,IAAI,MAAM,kBAAkB,OAAO,8BAA8B,OAAO,UAAU;AAE1F,QAAO"}
@@ -53,17 +53,18 @@ async function waitForHandshake(path, timeoutMs = 6e4) {
53
53
  }
54
54
  }
55
55
  function validateHandle(value, source) {
56
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`handshake file ${source} is not an object`);
56
57
  const handle = value;
57
58
  for (const field of [
58
59
  "baseUrl",
59
60
  "projectId",
60
61
  "projectSecret",
61
62
  "schemaId"
62
- ]) if (typeof handle?.[field] !== "string" || handle[field].length === 0) throw new Error(`handshake file ${source} is missing "${field}"`);
63
+ ]) if (typeof handle[field] !== "string" || handle[field].length === 0) throw new Error(`handshake file ${source} is missing "${field}"`);
63
64
  if (!URL.canParse(handle.baseUrl)) throw new Error(`handshake file ${source} has a malformed "baseUrl": ${handle.baseUrl}`);
64
65
  return handle;
65
66
  }
66
67
  //#endregion
67
68
  export { nextAppEnv as a, applyAppEnvTemplate as i, waitForHandshake as n, writeHandshake as r, readHandshakeSync as t };
68
69
 
69
- //# sourceMappingURL=handshake-BPtWruO8.mjs.map
70
+ //# sourceMappingURL=handshake-ClzWvG8z.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"handshake-BPtWruO8.mjs","names":["sleep"],"sources":["../src/app-env.ts","../src/handshake.ts"],"sourcesContent":["import type { InstanceHandle } from \"./types\";\n\n/**\n * Declarative mapping from an app's env var names to InstanceHandle fields.\n * A template (not a function) so it can cross process boundaries: the\n * Playwright config serializes it into the app-runner's environment, where it\n * is applied to the handle read from the handshake file.\n */\nexport type AppEnvTemplate = Record<string, keyof InstanceHandle>;\n\n/**\n * The env shape `@zitadel/sdk-next` apps read. Other frameworks pass their own\n * template — the console maps the same handle fields to `VITE_*`/`CONSOLE_*`\n * names, for example.\n */\nexport const nextAppEnv: AppEnvTemplate = {\n ZITADEL_URL: \"baseUrl\",\n NEXT_PUBLIC_ZITADEL_PROJECT_ID: \"projectId\",\n ZITADEL_PROJECT_SECRET: \"projectSecret\",\n};\n\nexport function applyAppEnvTemplate(\n template: AppEnvTemplate,\n handle: InstanceHandle,\n): Record<string, string> {\n const env: Record<string, string> = {};\n for (const [name, field] of Object.entries(template)) {\n const value = handle[field];\n if (typeof value !== \"string\" || value.length === 0) {\n // Fail instead of silently dropping the var: an app booted without one\n // of its env vars produces a much harder-to-read failure downstream.\n throw new Error(\n `app env template maps \"${name}\" to handle field \"${field}\", which the instance handle does not carry`,\n );\n }\n env[name] = value;\n }\n return env;\n}\n","import { readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\n\nimport type { InstanceHandle } from \"./types\";\n\n/**\n * The handshake file carries an InstanceHandle across process boundaries:\n * written by the script that boots + bootstraps the instance, read by\n * Playwright workers (fixtures) and the app dev-server wrapper.\n */\nexport async function writeHandshake(path: string, handle: InstanceHandle): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(handle, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport function readHandshakeSync(path: string): InstanceHandle {\n const contents = readFileSync(path, \"utf8\");\n let value: unknown;\n try {\n value = JSON.parse(contents);\n } catch (error) {\n throw new Error(`handshake file ${path} contains invalid JSON: ${(error as Error).message}`, {\n cause: error,\n });\n }\n return validateHandle(value, path);\n}\n\nexport async function waitForHandshake(path: string, timeoutMs = 60_000): Promise<InstanceHandle> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n return validateHandle(JSON.parse(await readFile(path, \"utf8\")), path);\n } catch (error) {\n if (Date.now() >= deadline) {\n throw new Error(\n `handshake file not readable within ${timeoutMs}ms: ${path} (${(error as Error).message})`,\n { cause: error },\n );\n }\n await sleep(250);\n }\n }\n}\n\nfunction validateHandle(value: unknown, source: string): InstanceHandle {\n const handle = value as Partial<InstanceHandle> | null;\n for (const field of [\"baseUrl\", \"projectId\", \"projectSecret\", \"schemaId\"] as const) {\n if (typeof handle?.[field] !== \"string\" || handle[field].length === 0) {\n throw new Error(`handshake file ${source} is missing \"${field}\"`);\n }\n }\n if (!URL.canParse(handle.baseUrl as string)) {\n throw new Error(`handshake file ${source} has a malformed \"baseUrl\": ${handle.baseUrl}`);\n }\n return handle as InstanceHandle;\n}\n"],"mappings":";;;;;;;;;;AAeA,MAAa,aAA6B;CACxC,aAAa;CACb,gCAAgC;CAChC,wBAAwB;CACzB;AAED,SAAgB,oBACd,UACA,QACwB;CACxB,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,EAAE;EACpD,MAAM,QAAQ,OAAO;AACrB,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAGhD,OAAM,IAAI,MACR,0BAA0B,KAAK,qBAAqB,MAAM,6CAC3D;AAEH,MAAI,QAAQ;;AAEd,QAAO;;;;;;;;;ACzBT,eAAsB,eAAe,MAAc,QAAuC;AACxF,OAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAM,UAAU,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAO,CAAC;;AAGhF,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,WAAW,aAAa,MAAM,OAAO;CAC3C,IAAI;AACJ,KAAI;AACF,UAAQ,KAAK,MAAM,SAAS;UACrB,OAAO;AACd,QAAM,IAAI,MAAM,kBAAkB,KAAK,0BAA2B,MAAgB,WAAW,EAC3F,OAAO,OACR,CAAC;;AAEJ,QAAO,eAAe,OAAO,KAAK;;AAGpC,eAAsB,iBAAiB,MAAc,YAAY,KAAiC;CAChG,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,SACE,KAAI;AACF,SAAO,eAAe,KAAK,MAAM,MAAM,SAAS,MAAM,OAAO,CAAC,EAAE,KAAK;UAC9D,OAAO;AACd,MAAI,KAAK,KAAK,IAAI,SAChB,OAAM,IAAI,MACR,sCAAsC,UAAU,MAAM,KAAK,IAAK,MAAgB,QAAQ,IACxF,EAAE,OAAO,OAAO,CACjB;AAEH,QAAMA,WAAM,IAAI;;;AAKtB,SAAS,eAAe,OAAgB,QAAgC;CACtE,MAAM,SAAS;AACf,MAAK,MAAM,SAAS;EAAC;EAAW;EAAa;EAAiB;EAAW,CACvE,KAAI,OAAO,SAAS,WAAW,YAAY,OAAO,OAAO,WAAW,EAClE,OAAM,IAAI,MAAM,kBAAkB,OAAO,eAAe,MAAM,GAAG;AAGrE,KAAI,CAAC,IAAI,SAAS,OAAO,QAAkB,CACzC,OAAM,IAAI,MAAM,kBAAkB,OAAO,8BAA8B,OAAO,UAAU;AAE1F,QAAO"}
1
+ {"version":3,"file":"handshake-ClzWvG8z.mjs","names":["sleep"],"sources":["../src/app-env.ts","../src/handshake.ts"],"sourcesContent":["import type { InstanceHandle } from \"./types\";\n\n/**\n * Declarative mapping from an app's env var names to InstanceHandle fields.\n * A template (not a function) so it can cross process boundaries: the\n * Playwright config serializes it into the app-runner's environment, where it\n * is applied to the handle read from the handshake file.\n */\nexport type AppEnvTemplate = Record<string, keyof InstanceHandle>;\n\n/**\n * The env shape `@zitadel/sdk-next` apps read. Other frameworks pass their own\n * template — the console maps the same handle fields to `VITE_*`/`CONSOLE_*`\n * names, for example.\n */\nexport const nextAppEnv: AppEnvTemplate = {\n ZITADEL_URL: \"baseUrl\",\n NEXT_PUBLIC_ZITADEL_PROJECT_ID: \"projectId\",\n ZITADEL_PROJECT_SECRET: \"projectSecret\",\n};\n\nexport function applyAppEnvTemplate(\n template: AppEnvTemplate,\n handle: InstanceHandle,\n): Record<string, string> {\n const env: Record<string, string> = {};\n for (const [name, field] of Object.entries(template)) {\n const value = handle[field];\n if (typeof value !== \"string\" || value.length === 0) {\n // Fail instead of silently dropping the var: an app booted without one\n // of its env vars produces a much harder-to-read failure downstream.\n throw new Error(\n `app env template maps \"${name}\" to handle field \"${field}\", which the instance handle does not carry`,\n );\n }\n env[name] = value;\n }\n return env;\n}\n","import { readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\n\nimport type { InstanceHandle } from \"./types\";\n\n/**\n * The handshake file carries an InstanceHandle across process boundaries:\n * written by the script that boots + bootstraps the instance, read by\n * Playwright workers (fixtures) and the app dev-server wrapper.\n */\nexport async function writeHandshake(path: string, handle: InstanceHandle): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(handle, null, 2)}\\n`, { mode: 0o600 });\n}\n\nexport function readHandshakeSync(path: string): InstanceHandle {\n const contents = readFileSync(path, \"utf8\");\n let value: unknown;\n try {\n value = JSON.parse(contents);\n } catch (error) {\n throw new Error(`handshake file ${path} contains invalid JSON: ${(error as Error).message}`, {\n cause: error,\n });\n }\n return validateHandle(value, path);\n}\n\nexport async function waitForHandshake(path: string, timeoutMs = 60_000): Promise<InstanceHandle> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n return validateHandle(JSON.parse(await readFile(path, \"utf8\")), path);\n } catch (error) {\n if (Date.now() >= deadline) {\n throw new Error(\n `handshake file not readable within ${timeoutMs}ms: ${path} (${(error as Error).message})`,\n { cause: error },\n );\n }\n await sleep(250);\n }\n }\n}\n\nfunction validateHandle(value: unknown, source: string): InstanceHandle {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(`handshake file ${source} is not an object`);\n }\n const handle = value as Partial<InstanceHandle>;\n for (const field of [\"baseUrl\", \"projectId\", \"projectSecret\", \"schemaId\"] as const) {\n if (typeof handle[field] !== \"string\" || handle[field].length === 0) {\n throw new Error(`handshake file ${source} is missing \"${field}\"`);\n }\n }\n if (!URL.canParse(handle.baseUrl as string)) {\n throw new Error(`handshake file ${source} has a malformed \"baseUrl\": ${handle.baseUrl}`);\n }\n return handle as InstanceHandle;\n}\n"],"mappings":";;;;;;;;;;AAeA,MAAa,aAA6B;CACxC,aAAa;CACb,gCAAgC;CAChC,wBAAwB;CACzB;AAED,SAAgB,oBACd,UACA,QACwB;CACxB,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,EAAE;EACpD,MAAM,QAAQ,OAAO;AACrB,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,EAGhD,OAAM,IAAI,MACR,0BAA0B,KAAK,qBAAqB,MAAM,6CAC3D;AAEH,MAAI,QAAQ;;AAEd,QAAO;;;;;;;;;ACzBT,eAAsB,eAAe,MAAc,QAAuC;AACxF,OAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAM,UAAU,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAO,CAAC;;AAGhF,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,WAAW,aAAa,MAAM,OAAO;CAC3C,IAAI;AACJ,KAAI;AACF,UAAQ,KAAK,MAAM,SAAS;UACrB,OAAO;AACd,QAAM,IAAI,MAAM,kBAAkB,KAAK,0BAA2B,MAAgB,WAAW,EAC3F,OAAO,OACR,CAAC;;AAEJ,QAAO,eAAe,OAAO,KAAK;;AAGpC,eAAsB,iBAAiB,MAAc,YAAY,KAAiC;CAChG,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,SACE,KAAI;AACF,SAAO,eAAe,KAAK,MAAM,MAAM,SAAS,MAAM,OAAO,CAAC,EAAE,KAAK;UAC9D,OAAO;AACd,MAAI,KAAK,KAAK,IAAI,SAChB,OAAM,IAAI,MACR,sCAAsC,UAAU,MAAM,KAAK,IAAK,MAAgB,QAAQ,IACxF,EAAE,OAAO,OAAO,CACjB;AAEH,QAAMA,WAAM,IAAI;;;AAKtB,SAAS,eAAe,OAAgB,QAAgC;AACtE,KAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,MAAM,CACrE,OAAM,IAAI,MAAM,kBAAkB,OAAO,mBAAmB;CAE9D,MAAM,SAAS;AACf,MAAK,MAAM,SAAS;EAAC;EAAW;EAAa;EAAiB;EAAW,CACvE,KAAI,OAAO,OAAO,WAAW,YAAY,OAAO,OAAO,WAAW,EAChE,OAAM,IAAI,MAAM,kBAAkB,OAAO,eAAe,MAAM,GAAG;AAGrE,KAAI,CAAC,IAAI,SAAS,OAAO,QAAkB,CACzC,OAAM,IAAI,MAAM,kBAAkB,OAAO,8BAA8B,OAAO,UAAU;AAE1F,QAAO"}
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_src = require("./src-BIL0PdSd.cjs");
3
- const require_handshake = require("./handshake-CggSIoxF.cjs");
2
+ const require_src = require("./src-I0KAh1zU.cjs");
3
+ const require_handshake = require("./handshake-CRKcgkfN.cjs");
4
4
  exports.SESSION_COOKIE_NAME = require_src.SESSION_COOKIE_NAME;
5
5
  exports.applyAppEnvTemplate = require_handshake.applyAppEnvTemplate;
6
6
  exports.bootLocalServer = require_src.bootLocalServer;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as Identity, c as LocalZitadelRuntime, d as SeedUserInput, f as SeedUsersTemplate, i as ConnectedZitadel, l as MintedSession, m as SessionCookie, n as applyAppEnvTemplate, o as InstanceHandle, p as SeededUser, r as nextAppEnv, s as LocalZitadel, t as AppEnvTemplate, u as SeedSessionInput } from "./app-env-D3W0GYhA.mjs";
1
+ import { a as Identity, c as LocalZitadelRuntime, d as SeedUserInput, f as SeedUsersTemplate, i as ConnectedZitadel, l as MintedSession, m as SessionCookie, n as applyAppEnvTemplate, o as InstanceHandle, p as SeededUser, r as nextAppEnv, s as LocalZitadel, t as AppEnvTemplate, u as SeedSessionInput } from "./app-env-Btmfcflb.mjs";
2
2
  import { ZitadelClient } from "@zitadel/api/client";
3
3
  import { SetupPreset, SetupUseCase } from "@zitadel/config/defaults";
4
4
 
@@ -54,9 +54,8 @@ interface BootedServer {
54
54
  /**
55
55
  * Boot an ephemeral local server by shelling out to `zitadel start` and parse
56
56
  * its JSON envelope. The CLI owns the subtle parts (port preflight, health
57
- * wait, process-group stop, embedded-Postgres reaping), so this module stays a
58
- * thin adapter; swapping it for direct library calls later must not change the
59
- * shape returned here.
57
+ * wait, process-group stop), so this module stays a thin adapter; swapping it
58
+ * for direct library calls later must not change the shape returned here.
60
59
  */
61
60
  declare function bootLocalServer(options?: BootServerOptions): Promise<BootedServer>;
62
61
  //#endregion
@@ -83,7 +82,7 @@ type StartLocalZitadelOptions = BootServerOptions & Omit<BootstrapProjectOptions
83
82
  */
84
83
  declare function connectZitadel(handle: InstanceHandle): ConnectedZitadel;
85
84
  /**
86
- * Boot an ephemeral local instance (binary runtime + embedded Postgres, no
85
+ * Boot an ephemeral local instance (binary runtime + SQLite by default, no
87
86
  * Docker) and bootstrap a project + default schema + login flow on it. The
88
87
  * result can seed loginable password users immediately.
89
88
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/bootstrap.ts","../src/lifecycle.ts","../src/handshake.ts","../src/session.ts","../src/index.ts"],"mappings":";;;;;UASiB,uBAAA;EACf,OAAA;EACA,WAAA;;AAFF;;;EAOE,UAAA;EACA,MAAA,GAAS,WAAA;EACT,OAAA,GAAU,YAAA;AAAA;AAAA,UAGK,mBAAA;EACf,SAAA;EACA,aAAA;EACA,aAAA;EACA,QAAA;EACA,MAAA;AAAA;;;;;;;iBAWoB,gBAAA,CACpB,OAAA,EAAS,uBAAA,GACR,OAAA,CAAQ,mBAAA;;;UCzBM,iBAAA;;EAEf,IAAA;;ADPF;;;ECYE,GAAA;EDXA;ECaA,YAAA;EDPA;ECSA,IAAA;EDRS;ECUT,MAAA;EACA,SAAA;AAAA;AAAA,UAGe,YAAA;EACf,OAAA;EACA,OAAA,EAAS,mBAAA;EACT,IAAA,IAAQ,OAAA;AAAA;;;;;;;;iBAUY,eAAA,CAAgB,OAAA,GAAS,iBAAA,GAAyB,OAAA,CAAQ,YAAA;;;;;;;ADnChF;iBEGsB,cAAA,CAAe,IAAA,UAAc,MAAA,EAAQ,cAAA,GAAiB,OAAA;AAAA,iBAK5D,iBAAA,CAAkB,IAAA,WAAe,cAAA;AAAA,iBAa3B,gBAAA,CAAiB,IAAA,UAAc,SAAA,YAAqB,OAAA,CAAQ,cAAA;;;;cCvBrE,mBAAA;;;KCED,wBAAA,GAA2B,iBAAA,GACrC,IAAA,CAAK,uBAAA;;AJDP;;;;iBIQgB,cAAA,CAAe,MAAA,EAAQ,cAAA,GAAiB,gBAAA;;;;;;iBA6BlC,iBAAA,CACpB,OAAA,GAAS,wBAAA,GACR,OAAA,CAAQ,YAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/bootstrap.ts","../src/lifecycle.ts","../src/handshake.ts","../src/session.ts","../src/index.ts"],"mappings":";;;;;UASiB,uBAAA;EACf,OAAA;EACA,WAAA;;AAFF;;;EAOE,UAAA;EACA,MAAA,GAAS,WAAA;EACT,OAAA,GAAU,YAAA;AAAA;AAAA,UAGK,mBAAA;EACf,SAAA;EACA,aAAA;EACA,aAAA;EACA,QAAA;EACA,MAAA;AAAA;;;;;;;iBAWoB,gBAAA,CACpB,OAAA,EAAS,uBAAA,GACR,OAAA,CAAQ,mBAAA;;;UCzBM,iBAAA;;EAEf,IAAA;;ADPF;;;ECYE,GAAA;EDXA;ECaA,YAAA;EDPA;ECSA,IAAA;EDRS;ECUT,MAAA;EACA,SAAA;AAAA;AAAA,UAGe,YAAA;EACf,OAAA;EACA,OAAA,EAAS,mBAAA;EACT,IAAA,IAAQ,OAAA;AAAA;;;;;;;iBASY,eAAA,CAAgB,OAAA,GAAS,iBAAA,GAAyB,OAAA,CAAQ,YAAA;;;;;;;ADlChF;iBEGsB,cAAA,CAAe,IAAA,UAAc,MAAA,EAAQ,cAAA,GAAiB,OAAA;AAAA,iBAK5D,iBAAA,CAAkB,IAAA,WAAe,cAAA;AAAA,iBAa3B,gBAAA,CAAiB,IAAA,UAAc,SAAA,YAAqB,OAAA,CAAQ,cAAA;;;;cCvBrE,mBAAA;;;KCED,wBAAA,GAA2B,iBAAA,GACrC,IAAA,CAAK,uBAAA;;AJDP;;;;iBIQgB,cAAA,CAAe,MAAA,EAAQ,cAAA,GAAiB,gBAAA;;;;;;iBA6BlC,iBAAA,CACpB,OAAA,GAAS,wBAAA,GACR,OAAA,CAAQ,YAAA"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as nextAppEnv, i as applyAppEnvTemplate, n as waitForHandshake, r as writeHandshake, t as readHandshakeSync } from "./handshake-BPtWruO8.mjs";
2
- import { a as bootstrapProject, i as bootLocalServer, n as startLocalZitadel, r as SESSION_COOKIE_NAME, t as connectZitadel } from "./src-DkG0V4A6.mjs";
1
+ import { a as nextAppEnv, i as applyAppEnvTemplate, n as waitForHandshake, r as writeHandshake, t as readHandshakeSync } from "./handshake-ClzWvG8z.mjs";
2
+ import { a as bootstrapProject, i as bootLocalServer, n as startLocalZitadel, r as SESSION_COOKIE_NAME, t as connectZitadel } from "./src-9dFjTIAw.mjs";
3
3
  export { SESSION_COOKIE_NAME, applyAppEnvTemplate, bootLocalServer, bootstrapProject, connectZitadel, nextAppEnv, readHandshakeSync, startLocalZitadel, waitForHandshake, writeHandshake };