@zitadel/components 0.1.0-alpha.9 → 1.0.0-alpha.20

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.
Files changed (44) hide show
  1. package/README.md +135 -72
  2. package/dist/atoms/index.d.mts +2 -2
  3. package/dist/atoms/index.mjs +2 -2
  4. package/dist/atoms-DSxjEHca.mjs +2268 -0
  5. package/dist/atoms-DSxjEHca.mjs.map +1 -0
  6. package/dist/default-Dnjq9iBt.mjs +6 -0
  7. package/dist/default-Dnjq9iBt.mjs.map +1 -0
  8. package/dist/index-BPgWdWx7.d.mts +14642 -0
  9. package/dist/index-BPgWdWx7.d.mts.map +1 -0
  10. package/dist/index-BlxTxXK8.d.mts +449 -0
  11. package/dist/index-BlxTxXK8.d.mts.map +1 -0
  12. package/dist/index-CaScNIBy.d.mts +1597 -0
  13. package/dist/index-CaScNIBy.d.mts.map +1 -0
  14. package/dist/index.d.mts +22 -8
  15. package/dist/index.d.mts.map +1 -1
  16. package/dist/index.mjs +5 -5
  17. package/dist/jsx.d.ts +67 -0
  18. package/dist/manifests.d.mts.map +1 -1
  19. package/dist/manifests.mjs +4 -2
  20. package/dist/manifests.mjs.map +1 -1
  21. package/dist/orchestrator/index.d.mts +3 -3
  22. package/dist/orchestrator/index.mjs +3 -3
  23. package/dist/orchestrator-ixQm2RIC.mjs +4998 -0
  24. package/dist/orchestrator-ixQm2RIC.mjs.map +1 -0
  25. package/dist/standalone.mjs +9140 -5849
  26. package/dist/tokens/index.d.mts +1 -1
  27. package/dist/tokens/index.mjs +1 -1
  28. package/dist/tokens-T4N9VJz3.mjs +452 -0
  29. package/dist/tokens-T4N9VJz3.mjs.map +1 -0
  30. package/package.json +13 -12
  31. package/dist/atoms-B25wiDqb.mjs +0 -1464
  32. package/dist/atoms-B25wiDqb.mjs.map +0 -1
  33. package/dist/default-Cpl78hQN.mjs +0 -6
  34. package/dist/default-Cpl78hQN.mjs.map +0 -1
  35. package/dist/index-DB_JTtln.d.mts +0 -1000
  36. package/dist/index-DB_JTtln.d.mts.map +0 -1
  37. package/dist/index-DVDCrfkv.d.mts +0 -7396
  38. package/dist/index-DVDCrfkv.d.mts.map +0 -1
  39. package/dist/index-EJengPPu.d.mts +0 -249
  40. package/dist/index-EJengPPu.d.mts.map +0 -1
  41. package/dist/orchestrator-CW9zixuw.mjs +0 -3362
  42. package/dist/orchestrator-CW9zixuw.mjs.map +0 -1
  43. package/dist/tokens-BQ_augxi.mjs +0 -244
  44. package/dist/tokens-BQ_augxi.mjs.map +0 -1
@@ -1,3362 +0,0 @@
1
- import { _ as __decorate, b as t, v as focusVisibleStyles, x as emit, y as baseHostStyles } from "./atoms-B25wiDqb.mjs";
2
- import { r as tokensCss } from "./tokens-BQ_augxi.mjs";
3
- import { t as default_default } from "./default-Cpl78hQN.mjs";
4
- import { manifestRegistry } from "./manifests.mjs";
5
- import { LitElement, css, html, nothing } from "lit";
6
- import { customElement, property, state } from "lit/decorators.js";
7
- import { unsafeHTML } from "lit/directives/unsafe-html.js";
8
- import { Liquid } from "liquidjs";
9
- import DOMPurify from "dompurify";
10
- //#region ../api/dist/runtime/auth.mjs
11
- /**
12
- * Module-global bearer token used by the orval-generated client's
13
- * custom fetch. Set once at command boot (the same lifecycle pattern
14
- * as `base-url.ts`); every generated request reads from here.
15
- *
16
- * Keeping the token here means the CLI doesn't have to thread an
17
- * `Authorization` header through every generated call site; orval's
18
- * `mutator` wires `runtime/fetch.ts` in, and that file pulls the token
19
- * from this module.
20
- */
21
- let apiAuthToken;
22
- function getApiAuthToken() {
23
- return apiAuthToken;
24
- }
25
- function setApiAuthToken(token) {
26
- apiAuthToken = token;
27
- }
28
- //#endregion
29
- //#region ../api/dist/runtime/fetch.mjs
30
- /**
31
- * Framework-neutral failure type the orval-generated client throws on
32
- * any non-2xx response. Carries the HTTP status, the parsed error body
33
- * (the spec's `{code, message, details?}` envelope when the server
34
- * returned one), and the request URL — enough for callers to map to
35
- * their own error taxonomy without re-implementing the fetch layer.
36
- *
37
- * Callers that don't care about the distinction can catch `ApiError`
38
- * generically; callers that do (the CLI's `toZitadelError`) read
39
- * `status` and decide.
40
- */
41
- var ApiError = class extends Error {
42
- status;
43
- url;
44
- body;
45
- constructor(status, url, body, message) {
46
- super(message);
47
- this.name = "ApiError";
48
- this.status = status;
49
- this.url = url;
50
- this.body = body;
51
- }
52
- };
53
- /**
54
- * The orval `mutator` for the fetch client. Every generated operation
55
- * routes its request through this function instead of the global
56
- * `fetch`. We pin three concerns here so generated call sites stay
57
- * focused on the shape of one HTTP call:
58
- *
59
- * - bearer auth — read from `runtime/auth.ts` and attached automatically;
60
- * - non-2xx → throw — orval's stock client parses the body regardless
61
- * of status, so callers would have to inspect every response. Throw
62
- * `ApiError` on `!res.ok` so failures interrupt control flow;
63
- * - body parsing — return the parsed JSON typed as `T` (the operation's
64
- * return type, threaded through by orval), or `undefined` for the
65
- * spec's `204`/`205`/`304` no-body responses.
66
- */
67
- async function customFetch(url, options) {
68
- const token = getApiAuthToken();
69
- const headers = new Headers(options.headers);
70
- if (token && !headers.has("authorization")) headers.set("authorization", `Bearer ${token}`);
71
- const res = await fetch(url, {
72
- ...options,
73
- headers
74
- });
75
- const rawBody = [
76
- 204,
77
- 205,
78
- 304
79
- ].includes(res.status) ? "" : await res.text();
80
- const parsed = rawBody ? safeJsonParse(rawBody) : void 0;
81
- if (!res.ok) {
82
- const message = `${options.method ?? "GET"} ${url} returned ${res.status}`;
83
- throw new ApiError(res.status, url, parsed, message);
84
- }
85
- return parsed;
86
- }
87
- /**
88
- * `JSON.parse` wrapped so a non-JSON body (e.g. an HTML 502 from a
89
- * proxy) becomes `{ raw: "<text>" }` instead of throwing inside
90
- * `customFetch`. The thrown `ApiError` then carries something useful
91
- * for the user to see.
92
- */
93
- function safeJsonParse(text) {
94
- try {
95
- return JSON.parse(text);
96
- } catch {
97
- return { raw: text };
98
- }
99
- }
100
- //#endregion
101
- //#region src/orchestrator/api-client.ts
102
- const apiRequestInit = { credentials: "include" };
103
- async function startFlow(api, input) {
104
- return api.createFlow(input, apiRequestInit);
105
- }
106
- async function submitStep(api, id, body) {
107
- try {
108
- return await api.submitFlowStep(id, body, apiRequestInit);
109
- } catch (error) {
110
- if (error instanceof ApiError && error.status === 400 && isFlowResponse(error.body)) return error.body;
111
- throw error;
112
- }
113
- }
114
- function isFlowResponse(body) {
115
- return typeof body === "object" && body !== null && "step" in body;
116
- }
117
- async function getCurrentStep(api, id) {
118
- return api.getFlowStep(id, apiRequestInit);
119
- }
120
- /**
121
- * Exchange a terminal-flow `handoff_token` for an authenticated session.
122
- * The server sets the `__nextgen_session` HttpOnly cookie on success.
123
- *
124
- * Uses the generated `exchangeHandoff` client which handles the
125
- * `project_id` query parameter via {@link ExchangeHandoffParams}.
126
- */
127
- async function exchangeSession(api, body, params) {
128
- return api.exchangeHandoff(body, params, apiRequestInit);
129
- }
130
- //#endregion
131
- //#region ../api/dist/runtime/base-url.mjs
132
- let proxyPath = "";
133
- function getProxyPath() {
134
- return proxyPath;
135
- }
136
- function setProxyPath(path) {
137
- proxyPath = path;
138
- }
139
- //#endregion
140
- //#region ../api/dist/chunk-CfYAbeIz.mjs
141
- var __defProp = Object.defineProperty;
142
- var __exportAll = (all, no_symbols) => {
143
- let target = {};
144
- for (var name in all) __defProp(target, name, {
145
- get: all[name],
146
- enumerable: true
147
- });
148
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
149
- return target;
150
- };
151
- //#endregion
152
- //#region ../api/dist/generated/endpoints/zitadelNextGen.mjs
153
- /**
154
- * Generated by orval v8.10.0 🍺
155
- * Do not edit manually.
156
- * Zitadel NextGen
157
- * This is the next generation of the Zitadel identity platform.
158
- * OpenAPI spec version: 0.0.1
159
- */
160
- var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
161
- authorizeDevice: () => authorizeDevice,
162
- authorizeGet: () => authorizeGet,
163
- createAuthAttempt: () => createAuthAttempt,
164
- createFlow: () => createFlow,
165
- createFlowDefinition: () => createFlowDefinition,
166
- createHandoff: () => createHandoff,
167
- createProject: () => createProject,
168
- createSchema: () => createSchema,
169
- createSession: () => createSession,
170
- createTeam: () => createTeam,
171
- createUser: () => createUser,
172
- deleteFlowDefinition: () => deleteFlowDefinition,
173
- endSession: () => endSession,
174
- exchangeHandoff: () => exchangeHandoff,
175
- getAuthAttempt: () => getAuthAttempt,
176
- getAuthorizeDeviceUrl: () => getAuthorizeDeviceUrl,
177
- getAuthorizeGetUrl: () => getAuthorizeGetUrl,
178
- getCreateAuthAttemptUrl: () => getCreateAuthAttemptUrl,
179
- getCreateFlowDefinitionUrl: () => getCreateFlowDefinitionUrl,
180
- getCreateFlowUrl: () => getCreateFlowUrl,
181
- getCreateHandoffUrl: () => getCreateHandoffUrl,
182
- getCreateProjectUrl: () => getCreateProjectUrl,
183
- getCreateSchemaUrl: () => getCreateSchemaUrl,
184
- getCreateSessionUrl: () => getCreateSessionUrl,
185
- getCreateTeamUrl: () => getCreateTeamUrl,
186
- getCreateUserUrl: () => getCreateUserUrl,
187
- getDeleteFlowDefinitionUrl: () => getDeleteFlowDefinitionUrl,
188
- getEndSessionUrl: () => getEndSessionUrl,
189
- getExchangeHandoffUrl: () => getExchangeHandoffUrl,
190
- getFlowDefinition: () => getFlowDefinition,
191
- getFlowStep: () => getFlowStep,
192
- getGetAuthAttemptUrl: () => getGetAuthAttemptUrl,
193
- getGetFlowDefinitionUrl: () => getGetFlowDefinitionUrl,
194
- getGetFlowStepUrl: () => getGetFlowStepUrl,
195
- getGetHealthUrl: () => getGetHealthUrl,
196
- getGetKeysUrl: () => getGetKeysUrl,
197
- getGetLiveUrl: () => getGetLiveUrl,
198
- getGetMySessionUrl: () => getGetMySessionUrl,
199
- getGetMyUserUrl: () => getGetMyUserUrl,
200
- getGetOpenIDConfigurationUrl: () => getGetOpenIDConfigurationUrl,
201
- getGetProjectUrl: () => getGetProjectUrl,
202
- getGetReadyUrl: () => getGetReadyUrl,
203
- getGetSchemaByIdUrl: () => getGetSchemaByIdUrl,
204
- getGetSessionUrl: () => getGetSessionUrl,
205
- getGetTeamUrl: () => getGetTeamUrl,
206
- getGetTokenUrl: () => getGetTokenUrl,
207
- getGetUserByIDUrl: () => getGetUserByIDUrl,
208
- getGetUserInfoUrl: () => getGetUserInfoUrl,
209
- getHealth: () => getHealth,
210
- getIntrospectUrl: () => getIntrospectUrl,
211
- getIssueChallengeUrl: () => getIssueChallengeUrl,
212
- getKeys: () => getKeys,
213
- getListFlowDefinitionsUrl: () => getListFlowDefinitionsUrl,
214
- getListSessionsUrl: () => getListSessionsUrl,
215
- getListUsersUrl: () => getListUsersUrl,
216
- getLive: () => getLive,
217
- getMySession: () => getMySession,
218
- getMyUser: () => getMyUser,
219
- getOpenIDConfiguration: () => getOpenIDConfiguration,
220
- getProject: () => getProject,
221
- getReady: () => getReady,
222
- getRevokeMySessionUrl: () => getRevokeMySessionUrl,
223
- getRevokeSessionUrl: () => getRevokeSessionUrl,
224
- getRevokeTokenUrl: () => getRevokeTokenUrl,
225
- getSchemaById: () => getSchemaById,
226
- getSession: () => getSession,
227
- getSetUserPasswordUrl: () => getSetUserPasswordUrl,
228
- getSubmitFlowEventUrl: () => getSubmitFlowEventUrl,
229
- getSubmitFlowStepUrl: () => getSubmitFlowStepUrl,
230
- getTeam: () => getTeam,
231
- getToken: () => getToken,
232
- getUpdateFlowDefinitionUrl: () => getUpdateFlowDefinitionUrl,
233
- getUserByID: () => getUserByID,
234
- getUserInfo: () => getUserInfo,
235
- getVerifyChallengeProofUrl: () => getVerifyChallengeProofUrl,
236
- introspect: () => introspect,
237
- issueChallenge: () => issueChallenge,
238
- listFlowDefinitions: () => listFlowDefinitions,
239
- listSessions: () => listSessions,
240
- listUsers: () => listUsers,
241
- revokeMySession: () => revokeMySession,
242
- revokeSession: () => revokeSession,
243
- revokeToken: () => revokeToken,
244
- setUserPassword: () => setUserPassword,
245
- submitFlowEvent: () => submitFlowEvent,
246
- submitFlowStep: () => submitFlowStep,
247
- updateFlowDefinition: () => updateFlowDefinition,
248
- verifyChallengeProof: () => verifyChallengeProof
249
- });
250
- const getGetOpenIDConfigurationUrl = () => {
251
- return `${getProxyPath()}/.well-known/openid-configuration`;
252
- };
253
- /**
254
- * Retrieve the OpenID Connect configuration
255
- * @summary Get OpenID Connect configuration
256
- */
257
- const getOpenIDConfiguration = async (options) => {
258
- return customFetch(getGetOpenIDConfigurationUrl(), {
259
- ...options,
260
- method: "GET"
261
- });
262
- };
263
- const getAuthorizeGetUrl = (params) => {
264
- const normalizedParams = new URLSearchParams();
265
- Object.entries(params || {}).forEach(([key, value]) => {
266
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
267
- });
268
- const stringifiedParams = normalizedParams.toString();
269
- return stringifiedParams.length > 0 ? `${getProxyPath()}/auth/authorize?${stringifiedParams}` : `${getProxyPath()}/auth/authorize`;
270
- };
271
- /**
272
- * @summary Authorize a user
273
- */
274
- const authorizeGet = async (params, options) => {
275
- return customFetch(getAuthorizeGetUrl(params), {
276
- ...options,
277
- method: "GET"
278
- });
279
- };
280
- const getAuthorizeDeviceUrl = (params) => {
281
- const normalizedParams = new URLSearchParams();
282
- Object.entries(params || {}).forEach(([key, value]) => {
283
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
284
- });
285
- const stringifiedParams = normalizedParams.toString();
286
- return stringifiedParams.length > 0 ? `${getProxyPath()}/auth/device-authorization?${stringifiedParams}` : `${getProxyPath()}/auth/device-authorization`;
287
- };
288
- /**
289
- * @summary Authorize a device
290
- */
291
- const authorizeDevice = async (params, options) => {
292
- return customFetch(getAuthorizeDeviceUrl(params), {
293
- ...options,
294
- method: "GET"
295
- });
296
- };
297
- const getEndSessionUrl = (params) => {
298
- const normalizedParams = new URLSearchParams();
299
- Object.entries(params || {}).forEach(([key, value]) => {
300
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
301
- });
302
- const stringifiedParams = normalizedParams.toString();
303
- return stringifiedParams.length > 0 ? `${getProxyPath()}/auth/end-session?${stringifiedParams}` : `${getProxyPath()}/auth/end-session`;
304
- };
305
- /**
306
- * @summary End a session
307
- */
308
- const endSession = async (params, options) => {
309
- return customFetch(getEndSessionUrl(params), {
310
- ...options,
311
- method: "GET"
312
- });
313
- };
314
- const getIntrospectUrl = () => {
315
- return `${getProxyPath()}/auth/introspect`;
316
- };
317
- /**
318
- * @summary Introspect a token
319
- */
320
- const introspect = async (introspectBody, options) => {
321
- const formUrlEncoded = new URLSearchParams();
322
- formUrlEncoded.append(`token`, introspectBody.token);
323
- if (introspectBody.token_type_hint !== void 0) formUrlEncoded.append(`token_type_hint`, introspectBody.token_type_hint);
324
- return customFetch(getIntrospectUrl(), {
325
- ...options,
326
- method: "POST",
327
- headers: {
328
- "Content-Type": "application/x-www-form-urlencoded",
329
- ...options?.headers
330
- },
331
- body: formUrlEncoded
332
- });
333
- };
334
- const getGetKeysUrl = () => {
335
- return `${getProxyPath()}/auth/keys`;
336
- };
337
- /**
338
- * @summary Get public keys
339
- */
340
- const getKeys = async (options) => {
341
- return customFetch(getGetKeysUrl(), {
342
- ...options,
343
- method: "GET"
344
- });
345
- };
346
- const getRevokeTokenUrl = () => {
347
- return `${getProxyPath()}/auth/revoke`;
348
- };
349
- /**
350
- * @summary Revoke an access token or refresh token
351
- */
352
- const revokeToken = async (revokeTokenBody, options) => {
353
- const formUrlEncoded = new URLSearchParams();
354
- if (revokeTokenBody.token !== void 0) formUrlEncoded.append(`token`, revokeTokenBody.token);
355
- if (revokeTokenBody.token_type_hint !== void 0) formUrlEncoded.append(`token_type_hint`, revokeTokenBody.token_type_hint);
356
- return customFetch(getRevokeTokenUrl(), {
357
- ...options,
358
- method: "POST",
359
- headers: {
360
- "Content-Type": "application/x-www-form-urlencoded",
361
- ...options?.headers
362
- },
363
- body: formUrlEncoded
364
- });
365
- };
366
- const getGetTokenUrl = () => {
367
- return `${getProxyPath()}/auth/token`;
368
- };
369
- /**
370
- * @summary Get accesstoken
371
- */
372
- const getToken = async (getTokenBody, options) => {
373
- const formUrlEncoded = new URLSearchParams();
374
- if (getTokenBody.code !== void 0) formUrlEncoded.append(`code`, getTokenBody.code);
375
- if (getTokenBody.client_assertion !== void 0) formUrlEncoded.append(`client_assertion`, getTokenBody.client_assertion);
376
- if (getTokenBody.client_assertion_type !== void 0) formUrlEncoded.append(`client_assertion_type`, getTokenBody.client_assertion_type);
377
- if (getTokenBody.client_id !== void 0) formUrlEncoded.append(`client_id`, getTokenBody.client_id);
378
- if (getTokenBody.client_secret !== void 0) formUrlEncoded.append(`client_secret`, getTokenBody.client_secret);
379
- if (getTokenBody.code_verifier !== void 0) formUrlEncoded.append(`code_verifier`, getTokenBody.code_verifier);
380
- if (getTokenBody.grant_type !== void 0) formUrlEncoded.append(`grant_type`, getTokenBody.grant_type);
381
- if (getTokenBody.redirect_uri !== void 0) formUrlEncoded.append(`redirect_uri`, getTokenBody.redirect_uri);
382
- return customFetch(getGetTokenUrl(), {
383
- ...options,
384
- method: "POST",
385
- headers: {
386
- "Content-Type": "application/x-www-form-urlencoded",
387
- ...options?.headers
388
- },
389
- body: formUrlEncoded
390
- });
391
- };
392
- const getGetUserInfoUrl = () => {
393
- return `${getProxyPath()}/auth/userinfo`;
394
- };
395
- /**
396
- * @summary Get user info
397
- */
398
- const getUserInfo = async (options) => {
399
- return customFetch(getGetUserInfoUrl(), {
400
- ...options,
401
- method: "GET"
402
- });
403
- };
404
- const getGetHealthUrl = () => {
405
- return `${getProxyPath()}/healthz`;
406
- };
407
- /**
408
- * Check whether the server is healthy
409
- * @summary Check server health
410
- */
411
- const getHealth = async (options) => {
412
- return customFetch(getGetHealthUrl(), {
413
- ...options,
414
- method: "GET"
415
- });
416
- };
417
- const getGetLiveUrl = () => {
418
- return `${getProxyPath()}/livez`;
419
- };
420
- /**
421
- * Check whether the server is started
422
- * @summary Check server liveness
423
- */
424
- const getLive = async (options) => {
425
- return customFetch(getGetLiveUrl(), {
426
- ...options,
427
- method: "GET"
428
- });
429
- };
430
- const getGetReadyUrl = () => {
431
- return `${getProxyPath()}/readyz`;
432
- };
433
- /**
434
- * Check whether the server is ready to accept requests
435
- * @summary Check server readiness
436
- */
437
- const getReady = async (options) => {
438
- return customFetch(getGetReadyUrl(), {
439
- ...options,
440
- method: "GET"
441
- });
442
- };
443
- const getCreateUserUrl = (params) => {
444
- const normalizedParams = new URLSearchParams();
445
- Object.entries(params || {}).forEach(([key, value]) => {
446
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
447
- });
448
- const stringifiedParams = normalizedParams.toString();
449
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
450
- };
451
- /**
452
- * @summary Create user
453
- */
454
- const createUser = async (createUserBody, params, options) => {
455
- return customFetch(getCreateUserUrl(params), {
456
- ...options,
457
- method: "POST",
458
- headers: {
459
- "Content-Type": "application/json",
460
- ...options?.headers
461
- },
462
- body: JSON.stringify(createUserBody)
463
- });
464
- };
465
- const getListUsersUrl = (params) => {
466
- const normalizedParams = new URLSearchParams();
467
- Object.entries(params || {}).forEach(([key, value]) => {
468
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
469
- });
470
- const stringifiedParams = normalizedParams.toString();
471
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
472
- };
473
- /**
474
- * @summary List users
475
- */
476
- const listUsers = async (params, options) => {
477
- return customFetch(getListUsersUrl(params), {
478
- ...options,
479
- method: "GET"
480
- });
481
- };
482
- const getGetUserByIDUrl = (userId, params) => {
483
- const normalizedParams = new URLSearchParams();
484
- Object.entries(params || {}).forEach(([key, value]) => {
485
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
486
- });
487
- const stringifiedParams = normalizedParams.toString();
488
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}?${stringifiedParams}` : `${getProxyPath()}/users/${userId}`;
489
- };
490
- /**
491
- * @summary Get user by ID
492
- */
493
- const getUserByID = async (userId, params, options) => {
494
- return customFetch(getGetUserByIDUrl(userId, params), {
495
- ...options,
496
- method: "GET"
497
- });
498
- };
499
- const getSetUserPasswordUrl = (userId, params) => {
500
- const normalizedParams = new URLSearchParams();
501
- Object.entries(params || {}).forEach(([key, value]) => {
502
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
503
- });
504
- const stringifiedParams = normalizedParams.toString();
505
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}/password?${stringifiedParams}` : `${getProxyPath()}/users/${userId}/password`;
506
- };
507
- /**
508
- * @summary Set user password
509
- */
510
- const setUserPassword = async (userId, setUserPasswordBody, params, options) => {
511
- return customFetch(getSetUserPasswordUrl(userId, params), {
512
- ...options,
513
- method: "PUT",
514
- headers: {
515
- "Content-Type": "application/json",
516
- ...options?.headers
517
- },
518
- body: JSON.stringify(setUserPasswordBody)
519
- });
520
- };
521
- const getGetMyUserUrl = () => {
522
- return `${getProxyPath()}/users/me`;
523
- };
524
- /**
525
- * @summary Get my user information
526
- */
527
- const getMyUser = async (options) => {
528
- return customFetch(getGetMyUserUrl(), {
529
- ...options,
530
- method: "GET"
531
- });
532
- };
533
- const getCreateFlowUrl = () => {
534
- return `${getProxyPath()}/flow`;
535
- };
536
- /**
537
- * Resolves a flow definition based on purpose + audience context and returns
538
- the first capability step. Creates a new session implicitly unless
539
- `session_id` is provided (for step-up / reauth on an existing session).
540
-
541
- The response contains an `id` field — the flow handle. Use it as the path
542
- parameter for all subsequent `/flow/{id}/submit` and `/flow/{id}/event` calls.
543
-
544
- The response also sets an encrypted `HttpOnly` cookie (`_zflow`) containing
545
- the flow's orchestration state (current step, collected data, history).
546
- The server is stateless between requests — all flow state lives in this
547
- cookie. The browser sends it automatically on subsequent requests.
548
-
549
- * @summary Start a new flow
550
- */
551
- const createFlow = async (createFlowBody, options) => {
552
- return customFetch(getCreateFlowUrl(), {
553
- ...options,
554
- method: "POST",
555
- headers: {
556
- "Content-Type": "application/json",
557
- ...options?.headers
558
- },
559
- body: JSON.stringify(createFlowBody)
560
- });
561
- };
562
- const getGetFlowStepUrl = (id) => {
563
- return `${getProxyPath()}/flow/${id}`;
564
- };
565
- /**
566
- * Returns the current capability step without advancing the state machine.
567
- Useful for page reloads or re-rendering after a network error.
568
-
569
- * @summary Get current step (re-render)
570
- */
571
- const getFlowStep = async (id, options) => {
572
- return customFetch(getGetFlowStepUrl(id), {
573
- ...options,
574
- method: "GET"
575
- });
576
- };
577
- const getSubmitFlowStepUrl = (id) => {
578
- return `${getProxyPath()}/flow/${id}/submit`;
579
- };
580
- /**
581
- * Submits user input for the current step. The server validates,
582
- processes (e.g., verifies a credential), advances the state machine
583
- through any invisible steps, and returns the next visible step.
584
-
585
- The response sets an updated encrypted `HttpOnly` cookie (`_zflow`)
586
- with the new flow state. The server is stateless — all orchestration
587
- state is carried in this cookie between requests.
588
-
589
- **Important:** The `id` in the response may differ from the `id` used in
590
- the request. This happens when a flow pivots (pushes a new flow onto the
591
- stack) or when a stacked flow completes (auto-pops to the parent flow).
592
- Always use the `id` from the latest response for the next request.
593
-
594
- ## Flow completion
595
-
596
- When `step.type` is `complete`, the flow is terminal. The `step.behavior`
597
- field tells the frontend what to do:
598
-
599
- | `behavior` | Action |
600
- |------------- |------------------------------------------------------------|
601
- | `redirect` | Navigate to `redirect_uri` (OIDC/SAML auth request done). |
602
- | `show` | Render the step as a success screen (e.g., registration). |
603
-
604
- A `complete` step is only returned when the **entire flow stack** is done.
605
- If a stacked flow (e.g., recovery pivoted from login) finishes, the server
606
- auto-pops to the parent flow and returns the parent's next step — the
607
- frontend never sees a `complete` for intermediate flows.
608
-
609
- * @summary Submit step data and advance
610
- */
611
- const submitFlowStep = async (id, submitFlowStepBody, options) => {
612
- return customFetch(getSubmitFlowStepUrl(id), {
613
- ...options,
614
- method: "POST",
615
- headers: {
616
- "Content-Type": "application/json",
617
- ...options?.headers
618
- },
619
- body: JSON.stringify(submitFlowStepBody)
620
- });
621
- };
622
- const getSubmitFlowEventUrl = (id) => {
623
- return `${getProxyPath()}/flow/${id}/event`;
624
- };
625
- /**
626
- * Submits telemetry or fingerprint data from the frontend.
627
- Does not advance the state machine. Used for risk evaluation.
628
-
629
- * @summary Submit client-side event
630
- */
631
- const submitFlowEvent = async (id, submitFlowEventBody, options) => {
632
- return customFetch(getSubmitFlowEventUrl(id), {
633
- ...options,
634
- method: "POST",
635
- headers: {
636
- "Content-Type": "application/json",
637
- ...options?.headers
638
- },
639
- body: JSON.stringify(submitFlowEventBody)
640
- });
641
- };
642
- const getCreateAuthAttemptUrl = () => {
643
- return `${getProxyPath()}/auth_attempts`;
644
- };
645
- /**
646
- * Starts a new authentication attempt. This is the entry point for the auth_attempts state machine.
647
-
648
- An attempt is an ephemeral (15-minute TTL) state machine that drives a single authentication round.
649
- It accepts factor challenges, verifies proofs, and completes into a session or handoff token.
650
-
651
- Accepts a project_id and challenge_nonce (from POST /bootstrap/challenge). For step-up re-auth,
652
- also include session_id to add factors to an existing session.
653
-
654
- * @summary Create a new authentication attempt
655
- */
656
- const createAuthAttempt = async (createAuthAttemptBody, options) => {
657
- return customFetch(getCreateAuthAttemptUrl(), {
658
- ...options,
659
- method: "POST",
660
- headers: {
661
- "Content-Type": "application/json",
662
- ...options?.headers
663
- },
664
- body: JSON.stringify(createAuthAttemptBody)
665
- });
666
- };
667
- const getGetAuthAttemptUrl = (attemptId) => {
668
- return `${getProxyPath()}/auth_attempts/${attemptId}`;
669
- };
670
- /**
671
- * Polls the current state of an authentication attempt.
672
-
673
- Returns the attempt's state, available factors for the next challenge,
674
- challenges issued so far, and any errors preventing progress.
675
-
676
- Use this for polling during long-running factor verifications (e.g., waiting for
677
- a federated IdP callback or a device flow).
678
-
679
- * @summary Get authentication attempt state
680
- */
681
- const getAuthAttempt = async (attemptId, options) => {
682
- return customFetch(getGetAuthAttemptUrl(attemptId), {
683
- ...options,
684
- method: "GET"
685
- });
686
- };
687
- const getIssueChallengeUrl = (attemptId) => {
688
- return `${getProxyPath()}/auth_attempts/${attemptId}/challenges`;
689
- };
690
- /**
691
- * Issues a single-factor verification challenge within an auth attempt.
692
-
693
- This advances the authentication state machine by requesting a specific factor method
694
- (password, passkey, TOTP, OTP via SMS, etc.). The server responds with challenge details
695
- including method, metadata, and any UI hints. The client then verifies the proof
696
- by calling POST /auth_attempts/{attempt_id}/challenges/{challenge_id}/verify.
697
-
698
- * @summary Issue a factor challenge
699
- */
700
- const issueChallenge = async (attemptId, issueChallengeBody, options) => {
701
- return customFetch(getIssueChallengeUrl(attemptId), {
702
- ...options,
703
- method: "POST",
704
- headers: {
705
- "Content-Type": "application/json",
706
- ...options?.headers
707
- },
708
- body: JSON.stringify(issueChallengeBody)
709
- });
710
- };
711
- const getVerifyChallengeProofUrl = (attemptId, challengeId) => {
712
- return `${getProxyPath()}/auth_attempts/${attemptId}/challenges/${challengeId}/verify`;
713
- };
714
- /**
715
- * Submits a proof (credential, code, assertion) to verify a factor challenge.
716
-
717
- The proof format depends on the challenge method. For example:
718
- - `password` method: { password: "…" }
719
- - `totp` method: { totp: { code: "123456" } }
720
- - `passkey` method: { passkey: { assertion: "…" } }
721
- - `recovery_code` method: { recovery_code: "…" }
722
-
723
- On successful verification, the factor is written to the auth attempt.
724
- The attempt moves to the next pending challenge or completes if all required factors are verified.
725
-
726
- * @summary Verify a factor proof
727
- */
728
- const verifyChallengeProof = async (attemptId, challengeId, verifyChallengeProofBody, options) => {
729
- return customFetch(getVerifyChallengeProofUrl(attemptId, challengeId), {
730
- ...options,
731
- method: "POST",
732
- headers: {
733
- "Content-Type": "application/json",
734
- ...options?.headers
735
- },
736
- body: JSON.stringify(verifyChallengeProofBody)
737
- });
738
- };
739
- const getCreateHandoffUrl = (attemptId) => {
740
- return `${getProxyPath()}/auth_attempts/${attemptId}/handoff`;
741
- };
742
- /**
743
- * Completes the authentication attempt and mints a `handoff_token`.
744
-
745
- Call this after all required factors have been verified and the attempt is in `completed` state.
746
- The handoff token is short-lived (≤60 seconds) and must be exchanged at
747
- POST /sessions/exchange to receive the final session and session_token.
748
-
749
- The handoff token is:
750
- - Single-use (atomic exchange, no retry)
751
- - Audience-bound (requires matching project key for exchange)
752
- - Idempotency-safe within a 5-minute window (see conventions)
753
-
754
- * @summary Complete authentication and create handoff token
755
- */
756
- const createHandoff = async (attemptId, options) => {
757
- return customFetch(getCreateHandoffUrl(attemptId), {
758
- ...options,
759
- method: "POST"
760
- });
761
- };
762
- const getCreateProjectUrl = () => {
763
- return `${getProxyPath()}/projects`;
764
- };
765
- /**
766
- * @summary Create project
767
- */
768
- const createProject = async (createProjectBody, options) => {
769
- return customFetch(getCreateProjectUrl(), {
770
- ...options,
771
- method: "POST",
772
- headers: {
773
- "Content-Type": "application/json",
774
- ...options?.headers
775
- },
776
- body: JSON.stringify(createProjectBody)
777
- });
778
- };
779
- const getGetProjectUrl = (projectId) => {
780
- return `${getProxyPath()}/projects/${projectId}`;
781
- };
782
- /**
783
- * Returns the current state of a project.
784
-
785
- * @summary Get project
786
- */
787
- const getProject = async (projectId, options) => {
788
- return customFetch(getGetProjectUrl(projectId), {
789
- ...options,
790
- method: "GET"
791
- });
792
- };
793
- const getCreateSessionUrl = () => {
794
- return `${getProxyPath()}/sessions`;
795
- };
796
- /**
797
- * Creates an anonymous session shell with no user and no factors (`state: building`).
798
-
799
- This is optional — an `auth_attempt` will create a session implicitly if none is provided.
800
- Use this explicitly when you want to:
801
- - Pre-allocate a `session_id` before the user is known, so device/telemetry signals
802
- can be correlated with the eventual authenticated session from the start.
803
- - Track anonymous state (bot detection, device fingerprint) that survives until authentication.
804
-
805
- The returned `session_token` authorises GET and DELETE on this session.
806
- It is superseded when a handoff exchange completes — clients must replace it at that point.
807
-
808
- Anonymous sessions expire aggressively (10-minute TTL). The TTL resets to the configured
809
- full session TTL when the first authentication factor is written via a completing `auth_attempt`.
810
-
811
- * @summary Create an anonymous session shell
812
- */
813
- const createSession = async (createSessionBody, options) => {
814
- return customFetch(getCreateSessionUrl(), {
815
- ...options,
816
- method: "POST",
817
- headers: {
818
- "Content-Type": "application/json",
819
- ...options?.headers
820
- },
821
- body: JSON.stringify(createSessionBody)
822
- });
823
- };
824
- const getListSessionsUrl = (params) => {
825
- const normalizedParams = new URLSearchParams();
826
- Object.entries(params || {}).forEach(([key, value]) => {
827
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
828
- });
829
- const stringifiedParams = normalizedParams.toString();
830
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions?${stringifiedParams}` : `${getProxyPath()}/sessions`;
831
- };
832
- /**
833
- * Returns a paginated list of sessions for a project.
834
- Requires a project service key (OAuth2 client credentials).
835
-
836
- * @summary List sessions
837
- */
838
- const listSessions = async (params, options) => {
839
- return customFetch(getListSessionsUrl(params), {
840
- ...options,
841
- method: "GET"
842
- });
843
- };
844
- const getExchangeHandoffUrl = (params) => {
845
- const normalizedParams = new URLSearchParams();
846
- Object.entries(params || {}).forEach(([key, value]) => {
847
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
848
- });
849
- const stringifiedParams = normalizedParams.toString();
850
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/exchange?${stringifiedParams}` : `${getProxyPath()}/sessions/exchange`;
851
- };
852
- /**
853
- * Consumes a one-time `handoff_token` minted by `POST /auth_attempts/{id}/handoff`
854
- and returns the resulting session and a `session_token`.
855
-
856
- The server resolves the originating `auth_attempt` from the token and then:
857
-
858
- | Originating auth_attempt | Outcome |
859
- |---|---|
860
- | No `session_id` | A new authenticated session is **created**. |
861
- | `session_id` points to an anonymous shell | Existing session is **upgraded** — user and factors written in, TTL reset to full session TTL. |
862
- | `session_id` points to an active session (step-up) | Existing session is **upgraded** — new factors merged, `assurance_levels[]` expanded. |
863
-
864
- The response shape is identical in all three cases.
865
-
866
- The `session_token` supersedes any previously issued `session_token` for the same session.
867
- Clients must replace their stored token at this point.
868
-
869
- Requires a project service key (OAuth2 client credentials).
870
-
871
- * @summary Exchange handoff token for a session
872
- */
873
- const exchangeHandoff = async (exchangeHandoffBody, params, options) => {
874
- return customFetch(getExchangeHandoffUrl(params), {
875
- ...options,
876
- method: "POST",
877
- headers: {
878
- "Content-Type": "application/json",
879
- ...options?.headers
880
- },
881
- body: JSON.stringify(exchangeHandoffBody)
882
- });
883
- };
884
- const getGetSessionUrl = (sessionId, params) => {
885
- const normalizedParams = new URLSearchParams();
886
- Object.entries(params || {}).forEach(([key, value]) => {
887
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
888
- });
889
- const stringifiedParams = normalizedParams.toString();
890
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/${sessionId}?${stringifiedParams}` : `${getProxyPath()}/sessions/${sessionId}`;
891
- };
892
- /**
893
- * Returns the current state of a session including its factors and all currently
894
- satisfied assurance levels.
895
-
896
- `assurance_levels[]` may shrink over time as factor freshness windows expire,
897
- without the session itself expiring. Use step-up authentication (a new `auth_attempt`
898
- against the same `session_id`) to restore a dropped assurance level.
899
-
900
- * @summary Get session state
901
- */
902
- const getSession = async (sessionId, params, options) => {
903
- return customFetch(getGetSessionUrl(sessionId, params), {
904
- ...options,
905
- method: "GET"
906
- });
907
- };
908
- const getRevokeSessionUrl = (sessionId, params) => {
909
- const normalizedParams = new URLSearchParams();
910
- Object.entries(params || {}).forEach(([key, value]) => {
911
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
912
- });
913
- const stringifiedParams = normalizedParams.toString();
914
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/${sessionId}?${stringifiedParams}` : `${getProxyPath()}/sessions/${sessionId}`;
915
- };
916
- /**
917
- * Revokes the session immediately (`state: revoked`). This is the logout operation.
918
-
919
- The session_token issued at creation (or superseded by a handoff exchange) is required.
920
- After revocation, any tokens derived from this session are invalidated.
921
-
922
- * @summary Revoke session
923
- */
924
- const revokeSession = async (sessionId, params, options) => {
925
- return customFetch(getRevokeSessionUrl(sessionId, params), {
926
- ...options,
927
- method: "DELETE"
928
- });
929
- };
930
- const getGetMySessionUrl = () => {
931
- return `${getProxyPath()}/sessions/me`;
932
- };
933
- /**
934
- * Returns the current state of the current session including its factors and all currently
935
- satisfied assurance levels.
936
-
937
- `assurance_levels[]` may shrink over time as factor freshness windows expire,
938
- without the session itself expiring. Use step-up authentication (a new `auth_attempt`
939
- against the same `session_id`) to restore a dropped assurance level.
940
-
941
- * @summary Get my session state
942
- */
943
- const getMySession = async (options) => {
944
- return customFetch(getGetMySessionUrl(), {
945
- ...options,
946
- method: "GET"
947
- });
948
- };
949
- const getRevokeMySessionUrl = () => {
950
- return `${getProxyPath()}/sessions/me`;
951
- };
952
- /**
953
- * Revokes the session immediately (`state: revoked`). This is the logout operation.
954
-
955
- The __nextgen_session cookie issued at creation (or superseded by a handoff exchange) is required.
956
- After revocation, any tokens derived from this session are invalidated including the cookie itself, which is cleared in the response.
957
-
958
- * @summary Revoke my session
959
- */
960
- const revokeMySession = async (options) => {
961
- return customFetch(getRevokeMySessionUrl(), {
962
- ...options,
963
- method: "DELETE"
964
- });
965
- };
966
- const getCreateSchemaUrl = (params) => {
967
- const normalizedParams = new URLSearchParams();
968
- Object.entries(params || {}).forEach(([key, value]) => {
969
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
970
- });
971
- const stringifiedParams = normalizedParams.toString();
972
- return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas?${stringifiedParams}` : `${getProxyPath()}/schemas`;
973
- };
974
- /**
975
- * Create a new schema. The schema definition must include a unique $id field,
976
- which will be used to identify the schema in future requests. The $id must
977
- be a valid URI and should ideally point to the location where the schema
978
- can be accessed.
979
-
980
- The schema can either be a concrete schema, e.g. a user schema, or a
981
- schema-url which will be resolved by the server.
982
-
983
- * @summary Create new schema
984
- */
985
- const createSchema = async (createSchemaBody, params, options) => {
986
- return customFetch(getCreateSchemaUrl(params), {
987
- ...options,
988
- method: "POST",
989
- headers: {
990
- "Content-Type": "application/json",
991
- ...options?.headers
992
- },
993
- body: JSON.stringify(createSchemaBody)
994
- });
995
- };
996
- const getGetSchemaByIdUrl = (id, params) => {
997
- const normalizedParams = new URLSearchParams();
998
- Object.entries(params || {}).forEach(([key, value]) => {
999
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1000
- });
1001
- const stringifiedParams = normalizedParams.toString();
1002
- return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas/${id}?${stringifiedParams}` : `${getProxyPath()}/schemas/${id}`;
1003
- };
1004
- /**
1005
- * Get a schema by its ID. This will return the default revision of the schema.
1006
- * @summary Get schema by ID
1007
- */
1008
- const getSchemaById = async (id, params, options) => {
1009
- return customFetch(getGetSchemaByIdUrl(id, params), {
1010
- ...options,
1011
- method: "GET"
1012
- });
1013
- };
1014
- const getCreateFlowDefinitionUrl = () => {
1015
- return `${getProxyPath()}/flow_definitions`;
1016
- };
1017
- /**
1018
- * Creates a new flow definition.
1019
- Flow definitions are templates that define the sequence of steps (capabilities)
1020
- for a particular user journey (e.g., registration, login, password reset).
1021
-
1022
- Flow definitions are created based on the flow meta schema, which includes the flow's purpose, audience, and the steps involved.
1023
-
1024
- * @summary Create a new flow definition
1025
- */
1026
- const createFlowDefinition = async (createFlowDefinitionBody, options) => {
1027
- return customFetch(getCreateFlowDefinitionUrl(), {
1028
- ...options,
1029
- method: "POST",
1030
- headers: {
1031
- "Content-Type": "application/json",
1032
- ...options?.headers
1033
- },
1034
- body: JSON.stringify(createFlowDefinitionBody)
1035
- });
1036
- };
1037
- const getListFlowDefinitionsUrl = (params) => {
1038
- const normalizedParams = new URLSearchParams();
1039
- Object.entries(params || {}).forEach(([key, value]) => {
1040
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1041
- });
1042
- const stringifiedParams = normalizedParams.toString();
1043
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions?${stringifiedParams}` : `${getProxyPath()}/flow_definitions`;
1044
- };
1045
- /**
1046
- * Retrieves a list of all flow definitions.
1047
- This endpoint can be used to view existing flow definitions and their configurations.
1048
-
1049
- * @summary List flow definitions
1050
- */
1051
- const listFlowDefinitions = async (params, options) => {
1052
- return customFetch(getListFlowDefinitionsUrl(params), {
1053
- ...options,
1054
- method: "GET"
1055
- });
1056
- };
1057
- const getGetFlowDefinitionUrl = (id, params) => {
1058
- const normalizedParams = new URLSearchParams();
1059
- Object.entries(params || {}).forEach(([key, value]) => {
1060
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1061
- });
1062
- const stringifiedParams = normalizedParams.toString();
1063
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions/${id}?${stringifiedParams}` : `${getProxyPath()}/flow_definitions/${id}`;
1064
- };
1065
- /**
1066
- * Get a flow definition by id
1067
- * @summary Get a flow definition by id
1068
- */
1069
- const getFlowDefinition = async (id, params, options) => {
1070
- return customFetch(getGetFlowDefinitionUrl(id, params), {
1071
- ...options,
1072
- method: "GET"
1073
- });
1074
- };
1075
- const getUpdateFlowDefinitionUrl = (id, params) => {
1076
- const normalizedParams = new URLSearchParams();
1077
- Object.entries(params || {}).forEach(([key, value]) => {
1078
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1079
- });
1080
- const stringifiedParams = normalizedParams.toString();
1081
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions/${id}?${stringifiedParams}` : `${getProxyPath()}/flow_definitions/${id}`;
1082
- };
1083
- /**
1084
- * Update a flow definition by id
1085
- * @summary Update a flow definition by id
1086
- */
1087
- const updateFlowDefinition = async (id, updateFlowDefinitionBody, params, options) => {
1088
- return customFetch(getUpdateFlowDefinitionUrl(id, params), {
1089
- ...options,
1090
- method: "PATCH",
1091
- headers: {
1092
- "Content-Type": "application/json",
1093
- ...options?.headers
1094
- },
1095
- body: JSON.stringify(updateFlowDefinitionBody)
1096
- });
1097
- };
1098
- const getDeleteFlowDefinitionUrl = (id, params) => {
1099
- const normalizedParams = new URLSearchParams();
1100
- Object.entries(params || {}).forEach(([key, value]) => {
1101
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1102
- });
1103
- const stringifiedParams = normalizedParams.toString();
1104
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions/${id}?${stringifiedParams}` : `${getProxyPath()}/flow_definitions/${id}`;
1105
- };
1106
- /**
1107
- * Delete a flow definition by id
1108
- * @summary Delete a flow definition by id
1109
- */
1110
- const deleteFlowDefinition = async (id, params, options) => {
1111
- return customFetch(getDeleteFlowDefinitionUrl(id, params), {
1112
- ...options,
1113
- method: "DELETE"
1114
- });
1115
- };
1116
- const getCreateTeamUrl = (params) => {
1117
- const normalizedParams = new URLSearchParams();
1118
- Object.entries(params || {}).forEach(([key, value]) => {
1119
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1120
- });
1121
- const stringifiedParams = normalizedParams.toString();
1122
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams?${stringifiedParams}` : `${getProxyPath()}/teams`;
1123
- };
1124
- /**
1125
- * @summary Create team
1126
- */
1127
- const createTeam = async (params, createTeamBody, options) => {
1128
- return customFetch(getCreateTeamUrl(params), {
1129
- ...options,
1130
- method: "POST",
1131
- headers: {
1132
- "Content-Type": "application/json",
1133
- ...options?.headers
1134
- },
1135
- body: JSON.stringify(createTeamBody)
1136
- });
1137
- };
1138
- const getGetTeamUrl = (teamId, params) => {
1139
- const normalizedParams = new URLSearchParams();
1140
- Object.entries(params || {}).forEach(([key, value]) => {
1141
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1142
- });
1143
- const stringifiedParams = normalizedParams.toString();
1144
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams/${teamId}?${stringifiedParams}` : `${getProxyPath()}/teams/${teamId}`;
1145
- };
1146
- /**
1147
- * Returns the current state of a team.
1148
-
1149
- * @summary Get team
1150
- */
1151
- const getTeam = async (teamId, params, options) => {
1152
- return customFetch(getGetTeamUrl(teamId, params), {
1153
- ...options,
1154
- method: "GET"
1155
- });
1156
- };
1157
- //#endregion
1158
- //#region ../api/dist/runtime/api-factory.mjs
1159
- /**
1160
- * Factory for the typed Zitadel client every consumer reaches for.
1161
- *
1162
- * Wraps the orval-generated endpoints in a {@link Proxy} so each
1163
- * generated function sees the right base URL and (optionally) the
1164
- * right bearer token at call time — without the caller having to
1165
- * thread either through every operation, and without exposing the
1166
- * module-globals (`setProxyPath` / `setApiAuthToken`) as part of the
1167
- * public API.
1168
- *
1169
- * Per-instance isolation: multiple clients can coexist in one process
1170
- * (different servers, different tokens). Their Proxy `get` traps set
1171
- * the globals synchronously before invoking the underlying generated
1172
- * function, which reads them at the top of `customFetch` before any
1173
- * `await`. Two clients running interleaved within a single JS turn
1174
- * are safe; two clients running in parallel across awaits could
1175
- * clobber each other — not a pattern any current consumer uses.
1176
- */
1177
- /**
1178
- * Build a typed Zitadel client pre-bound to a base URL and (optionally)
1179
- * a bearer token. Every method on the returned object mirrors a
1180
- * generated orval function:
1181
- *
1182
- * const client = createZitadelClient({ baseUrl, token });
1183
- * await client.createProject({ previewOrigins: [] });
1184
- * await client.createSchema(body, { project_id });
1185
- */
1186
- function createZitadelClient(opts) {
1187
- const baseUrl = opts.baseUrl.replace(/\/+$/, "");
1188
- return new Proxy(zitadelNextGen_exports, { get(target, prop, receiver) {
1189
- const value = Reflect.get(target, prop, receiver);
1190
- if (typeof value !== "function") return value;
1191
- return (...args) => {
1192
- setProxyPath(baseUrl);
1193
- setApiAuthToken(opts.token);
1194
- return value(...args);
1195
- };
1196
- } });
1197
- }
1198
- /**
1199
- * Legacy entry point: builds a client with only the base URL set, no
1200
- * token. Components and SDKs that don't carry an auth token (browser
1201
- * flows authenticated by platform cookies) still consume this name.
1202
- * Equivalent to `createZitadelClient({ baseUrl: apiBase })`.
1203
- */
1204
- function createApi(apiBase) {
1205
- return createZitadelClient({ baseUrl: apiBase });
1206
- }
1207
- //#endregion
1208
- //#region ../api/dist/runtime/config.mjs
1209
- /**
1210
- * Slot for the configured project, stored on `globalThis` under a
1211
- * {@link Symbol.for} key. Every copy of this module evaluated in the
1212
- * same JS realm resolves the symbol through the global symbol registry
1213
- * to the same identity, so they all read and write a single slot —
1214
- * needed because the standalone components bundle inlines its own copy
1215
- * of this file, and dual-package hazards / monorepo duplicates can load
1216
- * a second copy alongside the app's. With a module-local `let` each
1217
- * instance kept its own singleton and `configureZitadel()` calls in one
1218
- * were invisible to `getZitadelConfig()` calls in another. Sharing is
1219
- * realm-scoped — separate realms (iframes, Node `vm` contexts, worker
1220
- * threads) have their own registries and are not unified by this.
1221
- */
1222
- const PROJECT_SLOT = Symbol.for("@zitadel/api/config:currentProject");
1223
- function readSlot() {
1224
- return globalThis[PROJECT_SLOT] ?? null;
1225
- }
1226
- /**
1227
- * Per-project API client cache. Ensures `getApi(project)` returns the
1228
- * same instance for the same project handle — no re-wrapping on every call.
1229
- * Module-local on purpose: keyed by the shared frozen `ZitadelProject`
1230
- * object identity, so cross-instance reads still resolve through the
1231
- * same key and at worst memoize once per module instance.
1232
- */
1233
- const apiCache = /* @__PURE__ */ new WeakMap();
1234
- /**
1235
- * Returns a typed API client for the given project, with the base URL
1236
- * pre-bound. Cached per project handle — safe to call multiple times.
1237
- *
1238
- * ```ts
1239
- * const project = configureZitadel({ ... });
1240
- * export const api = getApi(project);
1241
- * ```
1242
- */
1243
- function getApi(project) {
1244
- let api = apiCache.get(project);
1245
- if (!api) {
1246
- api = createApi(project.proxyPath);
1247
- apiCache.set(project, api);
1248
- }
1249
- return api;
1250
- }
1251
- /**
1252
- * Returns the current project handle, or `null` if {@link configureZitadel}
1253
- * has not been called yet.
1254
- */
1255
- function getZitadelConfig() {
1256
- return readSlot();
1257
- }
1258
- //#endregion
1259
- //#region src/orchestrator/resolve-api.ts
1260
- /** Matches the `configureZitadel()` default so attribute and JS config agree. */
1261
- const DEFAULT_PROXY_PATH = "/__nextgen";
1262
- /**
1263
- * Cache of project handles synthesized from attributes, keyed by their value
1264
- * tuple. `getApi()` caches its client in a `WeakMap` keyed by the project's
1265
- * identity, so the same attribute values must yield the *same* frozen object
1266
- * across the several `resolveApi()` calls a single flow makes — otherwise
1267
- * every call misses the cache and re-wraps a fresh client. A real page uses a
1268
- * handful of distinct configs at most; the FIFO bound below is just a guard so
1269
- * a pathological page that churns through unique configs can't leak memory.
1270
- */
1271
- const MAX_SYNTHETIC_PROJECTS = 32;
1272
- const syntheticProjects = /* @__PURE__ */ new Map();
1273
- /**
1274
- * Builds a `ZitadelProject` from declarative attributes, or `undefined` when
1275
- * no `project-id` was given (nothing to configure). Mirrors
1276
- * `configureZitadel()`'s proxy-path defaulting so the two config paths are
1277
- * interchangeable.
1278
- */
1279
- function projectFromAttrs({ projectId, proxyPath, url }) {
1280
- if (!projectId) return void 0;
1281
- const resolvedProxyPath = proxyPath || DEFAULT_PROXY_PATH;
1282
- const resolvedUrl = url || void 0;
1283
- const key = JSON.stringify([
1284
- projectId,
1285
- resolvedProxyPath,
1286
- resolvedUrl ?? ""
1287
- ]);
1288
- let project = syntheticProjects.get(key);
1289
- if (!project) {
1290
- project = Object.freeze({
1291
- projectId,
1292
- proxyPath: resolvedProxyPath,
1293
- url: resolvedUrl
1294
- });
1295
- if (syntheticProjects.size >= MAX_SYNTHETIC_PROJECTS) {
1296
- const oldest = syntheticProjects.keys().next().value;
1297
- if (oldest !== void 0) syntheticProjects.delete(oldest);
1298
- }
1299
- syntheticProjects.set(key, project);
1300
- }
1301
- return project;
1302
- }
1303
- /**
1304
- * Resolves the effective SDK project handle and its typed API client for an
1305
- * orchestrator element. Precedence, highest first:
1306
- *
1307
- * 1. the element's `project` property (an SDK handle set from JS / a framework),
1308
- * 2. the global handle from `configureZitadel()`,
1309
- * 3. the `project-id` / `proxy-path` / `url` attributes set declaratively in HTML.
1310
- *
1311
- * Both JS paths (property and global) win over the declarative attributes, so a
1312
- * deliberate `configureZitadel()` is never silently overridden by fallback or
1313
- * stale HTML attributes; the attributes are the no-JS fallback.
1314
- *
1315
- * Both `<zitadel-login>` and `<zitadel-logout>` need the same resolution and
1316
- * the same "no config" failure, so it lives here rather than being copied
1317
- * into each element. Callers run this inside their try/catch so a missing
1318
- * configuration surfaces through the element's normal error path rather than
1319
- * as an unhandled rejection.
1320
- *
1321
- * @param project Optional per-element override handle.
1322
- * @param attrs Declarative config read from the element's attributes.
1323
- * @param element Tag name used in the thrown error (e.g. `<zitadel-login>`).
1324
- */
1325
- function resolveApi(project, attrs, element) {
1326
- const cfg = project ?? getZitadelConfig() ?? projectFromAttrs(attrs);
1327
- if (!cfg) throw new Error(`${element} requires a configured project: set the \`project-id\` attribute, set the \`project\` property, or call configureZitadel().`);
1328
- return {
1329
- project: cfg,
1330
- api: getApi(cfg)
1331
- };
1332
- }
1333
- //#endregion
1334
- //#region src/orchestrator/branding-to-tokens.ts
1335
- /**
1336
- * Branding -> CSS token bridge.
1337
- *
1338
- * Per `docs/design/branding/README.md` the orchestrator owns theming. Templates
1339
- * never emit `<style>` blocks; they only structure HTML. This module translates
1340
- * the `Branding` JSON into a CSSStyleSheet of `:host { --zl-* }` declarations
1341
- * and applies it via `shadowRoot.adoptedStyleSheets`.
1342
- *
1343
- * Dark-mode overrides are emitted as `:host([data-theme="dark"]) { ... }`
1344
- * (matching `docs/design/branding/tokens.md`). Resolution between
1345
- * `light | dark | auto` happens in `<zitadel-login>`.
1346
- *
1347
- * Base layer: the design-tokens package ships the full `--zl-*` set as both a
1348
- * `.css` file (for host pages) and a `tokensCss` string (for shadow roots and
1349
- * SSR). We adopt the string into every orchestrator shadow root so atoms
1350
- * paint correctly even when the host page didn't `@import` tokens.css — the
1351
- * orchestrator is meant to drop into any page.
1352
- */
1353
- const RADIUS_MAP = {
1354
- none: "0",
1355
- sm: "0.25rem",
1356
- md: "0.5rem",
1357
- lg: "0.75rem",
1358
- full: "9999px"
1359
- };
1360
- const DENSITY_MAP = {
1361
- compact: {
1362
- "--zl-spacing-03": "0.75rem",
1363
- "--zl-spacing-05": "1.5rem"
1364
- },
1365
- regular: {},
1366
- comfortable: {
1367
- "--zl-spacing-03": "1.25rem",
1368
- "--zl-spacing-05": "2.25rem"
1369
- }
1370
- };
1371
- const PALETTE_MAP = {
1372
- primary: ["--zl-color-surface-default-white"],
1373
- on_primary: ["--zl-color-text-button-default"],
1374
- background: ["--zl-color-surface-default-black"],
1375
- surface: ["--zl-color-surface-default-primary-gray"],
1376
- muted: ["--zl-color-surface-default-secondary-gray"],
1377
- border: ["--zl-color-border-default-gray-200", "--zl-color-border-default-gray-100"],
1378
- text: ["--zl-color-text-primary-white"],
1379
- text_muted: ["--zl-color-text-secondary-gray"],
1380
- link: ["--zl-color-text-subtitle-pink"],
1381
- success: [
1382
- "--zl-color-text-success",
1383
- "--zl-color-border-success",
1384
- "--zl-color-icon-success"
1385
- ],
1386
- warning: ["--zl-color-text-subtitle-orange"],
1387
- error: [
1388
- "--zl-color-text-error",
1389
- "--zl-color-border-error",
1390
- "--zl-color-icon-error"
1391
- ]
1392
- };
1393
- /**
1394
- * Build a CSS string of `:host { --zl-* }` declarations from a Branding
1395
- * payload. Light values land on `:host`; dark overrides land on
1396
- * `:host([data-theme="dark"])`.
1397
- */
1398
- function buildBrandingStylesheet(branding, options = {}) {
1399
- const lightDecls = collectDeclarations(branding);
1400
- const darkPalette = branding?.theme?.dark?.palette;
1401
- const darkDecls = darkPalette ? mapPalette(darkPalette) : {};
1402
- const blocks = [];
1403
- if (Object.keys(lightDecls).length > 0) blocks.push(formatBlock(":host", lightDecls));
1404
- if (Object.keys(darkDecls).length > 0) blocks.push(formatBlock(":host([data-theme=\"dark\"])", darkDecls));
1405
- if (options.resolvedTheme === "dark" && darkPalette) {
1406
- const merged = {
1407
- ...lightDecls,
1408
- ...darkDecls
1409
- };
1410
- blocks.length = 0;
1411
- blocks.push(formatBlock(":host", merged));
1412
- }
1413
- return blocks.join("\n");
1414
- }
1415
- function collectDeclarations(branding) {
1416
- if (!branding) return {};
1417
- const decls = {};
1418
- Object.assign(decls, mapPalette(branding.palette));
1419
- Object.assign(decls, mapTypography(branding.typography));
1420
- Object.assign(decls, mapShape(branding.shape));
1421
- return decls;
1422
- }
1423
- function mapPalette(palette) {
1424
- if (!palette) return {};
1425
- const out = {};
1426
- for (const [key, varNames] of Object.entries(PALETTE_MAP)) {
1427
- const value = palette[key];
1428
- if (typeof value === "string" && value.length > 0) for (const varName of varNames) out[varName] = value;
1429
- }
1430
- return out;
1431
- }
1432
- function mapTypography(typography) {
1433
- if (!typography) return {};
1434
- const out = {};
1435
- if (typography.font_family) {
1436
- out["--zl-font-family-sans"] = typography.font_family;
1437
- out["--zl-font-family-heading"] = typography.font_family;
1438
- }
1439
- if (typography.font_family_mono) out["--zl-font-family-mono"] = typography.font_family_mono;
1440
- const scale = clamp(typography.scale ?? 1, .75, 1.25);
1441
- if (scale !== 1) out["--zl-font-scale"] = `${scale}`;
1442
- return out;
1443
- }
1444
- function mapShape(shape) {
1445
- if (!shape) return {};
1446
- const out = {};
1447
- if (shape.radius && RADIUS_MAP[shape.radius]) {
1448
- const radius = RADIUS_MAP[shape.radius];
1449
- out["--zl-radius-s"] = radius;
1450
- out["--zl-radius-m"] = radius === "0" ? "0" : `calc(${radius} * 1.5)`;
1451
- out["--zl-radius-l"] = radius === "0" ? "0" : `calc(${radius} * 2)`;
1452
- }
1453
- if (shape.density) Object.assign(out, DENSITY_MAP[shape.density]);
1454
- return out;
1455
- }
1456
- function formatBlock(selector, decls) {
1457
- return `${selector} {\n${Object.entries(decls).map(([prop, value]) => ` ${prop}: ${value};`).join("\n")}\n}`;
1458
- }
1459
- function clamp(value, min, max) {
1460
- if (Number.isNaN(value)) return min;
1461
- return Math.min(max, Math.max(min, value));
1462
- }
1463
- let baseTokenSheet;
1464
- function getBaseTokenSheet() {
1465
- if (typeof CSSStyleSheet === "undefined") return void 0;
1466
- if (!baseTokenSheet) {
1467
- baseTokenSheet = new CSSStyleSheet();
1468
- baseTokenSheet.replaceSync(tokensCss.replaceAll(":root,\n[data-theme=\"dark\"]", ":host,\n:host([data-theme=\"dark\"])").replaceAll("[data-theme=\"light\"]", ":host([data-theme=\"light\"])"));
1469
- }
1470
- return baseTokenSheet;
1471
- }
1472
- /**
1473
- * Adopt the design-system base token layer onto a `ShadowRoot`. Safe to call
1474
- * many times — the underlying constructable sheet is shared across every
1475
- * orchestrator instance and de-duplicated in the adopted list.
1476
- */
1477
- function applyBaseTokens(shadowRoot) {
1478
- const sheet = getBaseTokenSheet();
1479
- if (!sheet) return;
1480
- const existing = Array.isArray(shadowRoot.adoptedStyleSheets) ? shadowRoot.adoptedStyleSheets : [];
1481
- if (existing.includes(sheet)) return;
1482
- try {
1483
- shadowRoot.adoptedStyleSheets = [sheet, ...existing];
1484
- } catch {}
1485
- }
1486
- /**
1487
- * Apply a branding payload as a `--zl-*` overrides layer on top of the base
1488
- * token sheet. Subsequent calls replace the previous override sheet so a new
1489
- * branding payload paints cleanly without leaking older declarations.
1490
- *
1491
- * Callers should run `applyBaseTokens(shadowRoot)` first (once per shadow
1492
- * root) so the base values exist before branding patches them.
1493
- */
1494
- function applyBrandingTokens(shadowRoot, branding, resolvedTheme) {
1495
- const css = buildBrandingStylesheet(branding, { resolvedTheme });
1496
- if (typeof CSSStyleSheet === "undefined") return;
1497
- const sheet = new CSSStyleSheet();
1498
- sheet.replaceSync(css);
1499
- const previous = shadowRoot.__zlTokenSheet;
1500
- shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets.filter((s) => s !== previous), sheet];
1501
- shadowRoot.__zlTokenSheet = sheet;
1502
- }
1503
- function resolveTheme(branding) {
1504
- const mode = branding?.theme?.mode ?? "dark";
1505
- if (mode === "light") return "light";
1506
- if (mode === "dark") return "dark";
1507
- if (typeof matchMedia === "function") return matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
1508
- return "dark";
1509
- }
1510
- //#endregion
1511
- //#region ../api/dist/generated/model/index.mjs
1512
- const CreateFlow201BrandingLayout = {
1513
- centered: "centered",
1514
- split: "split"
1515
- };
1516
- //#endregion
1517
- //#region src/orchestrator/branding-validator.ts
1518
- /**
1519
- * Lightweight paint-time validator for `Branding` payloads.
1520
- *
1521
- * Per `docs/design/branding/schema.md` §Shape invariants:
1522
- *
1523
- * 1. Every referenced URL is https.
1524
- * 2. `layout` is in the documented enum.
1525
- * 3. `liquid_template` shape checks happen elsewhere (security pipeline runs
1526
- * server-side; structural validator is deferred until the full atom set
1527
- * lands).
1528
- *
1529
- * Failures are non-fatal: we collect issues, log a dev-build warning, and
1530
- * return a sanitised payload with the offending fields stripped (set to
1531
- * `undefined`) so the orchestrator falls back to the bundled defaults.
1532
- */
1533
- const VALID_LAYOUTS = new Set(Object.values(CreateFlow201BrandingLayout));
1534
- function validateBranding(input) {
1535
- if (!input) return {
1536
- branding: void 0,
1537
- issues: []
1538
- };
1539
- const issues = [];
1540
- const out = { ...input };
1541
- if (out.layout && !VALID_LAYOUTS.has(out.layout)) {
1542
- issues.push(`Unknown layout "${out.layout}" — falling back to "centered".`);
1543
- out.layout = CreateFlow201BrandingLayout.centered;
1544
- }
1545
- out.logo_url = sanitiseUrl(out.logo_url, "logo_url", issues);
1546
- out.font_url = sanitiseUrl(out.font_url, "font_url", issues);
1547
- out.hero_url = sanitiseUrl(out.hero_url, "hero_url", issues);
1548
- if (out.assets) out.assets = {
1549
- logo_dark: sanitiseUrl(out.assets.logo_dark, "assets.logo_dark", issues),
1550
- favicon: sanitiseUrl(out.assets.favicon, "assets.favicon", issues),
1551
- background_image: sanitiseUrl(out.assets.background_image, "assets.background_image", issues)
1552
- };
1553
- return {
1554
- branding: out,
1555
- issues
1556
- };
1557
- }
1558
- function sanitiseUrl(value, field, issues) {
1559
- if (value == null || value === "") return;
1560
- try {
1561
- const parsed = new URL(value);
1562
- if (parsed.protocol !== "https:") {
1563
- issues.push(`${field} must use https (got "${parsed.protocol}") — dropped.`);
1564
- return;
1565
- }
1566
- return parsed.toString();
1567
- } catch {
1568
- issues.push(`${field} is not a valid URL — dropped.`);
1569
- return;
1570
- }
1571
- }
1572
- //#endregion
1573
- //#region src/orchestrator/font-loader.ts
1574
- /**
1575
- * Inject `branding.font_url` as `<link rel="stylesheet">` into the host
1576
- * document's `<head>`. Per `docs/design/branding/templates.md`:
1577
- *
1578
- * "The `font_url` stylesheet is injected by the orchestrator; the template
1579
- * does not emit the `<link>` tag itself."
1580
- *
1581
- * The link must live at document level, not inside the shadow root:
1582
- * browsers ignore `@font-face` rules declared in shadow-tree stylesheets,
1583
- * so a shadow-scoped link would never register the font faces and every
1584
- * branded font silently falls back to the system stack. `font-family`
1585
- * references inside the shadow tree resolve against document-level faces.
1586
- *
1587
- * Idempotent: calling with the same URL is a no-op; calling with a new URL
1588
- * replaces the previous link. Calling with `null`/`undefined` removes any
1589
- * previously injected link.
1590
- */
1591
- const LINK_ID = "zl-font-link";
1592
- function applyFontUrl(shadowRoot, fontUrl) {
1593
- const ownerDocument = shadowRoot.ownerDocument ?? document;
1594
- const existing = ownerDocument.head.querySelector(`link#${LINK_ID}`);
1595
- if (!fontUrl) {
1596
- existing?.remove();
1597
- return;
1598
- }
1599
- const link = existing ?? ownerDocument.createElement("link");
1600
- link.id = LINK_ID;
1601
- link.rel = "stylesheet";
1602
- if (link.href !== fontUrl) link.href = fontUrl;
1603
- if (!existing) ownerDocument.head.appendChild(link);
1604
- }
1605
- //#endregion
1606
- //#region src/internal/escape-html.ts
1607
- /**
1608
- * Escapes the five HTML-significant characters so a string is safe to
1609
- * interpolate into either element text or a double-quoted attribute value.
1610
- *
1611
- * Used for the orchestrator-owned attribution markup, which is appended after
1612
- * the Liquid output has already been sanitised — so it can't rely on the
1613
- * DOMPurify pass and must escape its own (tenant-supplied) values.
1614
- */
1615
- function escapeHtml(value) {
1616
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1617
- }
1618
- //#endregion
1619
- //#region src/orchestrator/mandatory-gates.ts
1620
- const MANDATORY_GATES_MARKER = "ZL_MANDATORY_GATES";
1621
- const mandatoryGatesMarkerComment = `<!--${MANDATORY_GATES_MARKER}-->`;
1622
- function patchMandatoryGates(html, step, locale) {
1623
- const template = document.createElement("template");
1624
- template.innerHTML = html;
1625
- const fragment = template.content;
1626
- const additions = collectMissingAtoms(fragment, step, locale);
1627
- const marker = findMarkerComment(fragment);
1628
- if (marker?.parentNode) {
1629
- for (const node of additions) marker.parentNode.insertBefore(node, marker);
1630
- marker.remove();
1631
- } else for (const node of additions) fragment.appendChild(node);
1632
- return template.innerHTML;
1633
- }
1634
- function collectMissingAtoms(fragment, step, locale) {
1635
- const additions = [];
1636
- if (step.fields) for (const field of step.fields) {
1637
- if (!field.required) continue;
1638
- if (hasFieldFor(fragment, field.name)) continue;
1639
- additions.push(buildField(field.name, field.text_key, field.type, locale, field.required));
1640
- }
1641
- if (step.actions && !hasPrimaryButton(fragment)) {
1642
- const primary = step.actions.find((action) => action.primary);
1643
- if (primary) additions.push(buildSubmit(primary.name, primary.text_key, locale));
1644
- }
1645
- return additions;
1646
- }
1647
- function hasPrimaryButton(fragment) {
1648
- return Boolean(fragment.querySelector("zl-button[hierarchy=\"primary\"]"));
1649
- }
1650
- function hasFieldFor(fragment, name) {
1651
- for (const field of fragment.querySelectorAll("zl-field")) if (field.getAttribute("name") === name) return true;
1652
- return false;
1653
- }
1654
- function findMarkerComment(fragment) {
1655
- const walker = (fragment.ownerDocument ?? document).createTreeWalker(fragment, NodeFilter.SHOW_COMMENT);
1656
- let node = walker.nextNode();
1657
- while (node) {
1658
- if (node.nodeValue?.trim() === "ZL_MANDATORY_GATES") return node;
1659
- node = walker.nextNode();
1660
- }
1661
- return null;
1662
- }
1663
- function buildField(name, textKey, type, locale, required) {
1664
- const el = document.createElement("zl-field");
1665
- el.setAttribute("name", name);
1666
- el.setAttribute("label", lookup(locale, textKey ?? name));
1667
- el.setAttribute("type", type);
1668
- if (required) el.setAttribute("required", "");
1669
- return el;
1670
- }
1671
- function buildSubmit(name, textKey, locale) {
1672
- const el = document.createElement("zl-button");
1673
- el.setAttribute("hierarchy", "primary");
1674
- el.setAttribute("size", "medium");
1675
- el.setAttribute("type", "submit");
1676
- el.setAttribute("block", "");
1677
- el.setAttribute("action", name);
1678
- el.setAttribute("label", lookup(locale, textKey ?? "submit.continue"));
1679
- return el;
1680
- }
1681
- function lookup(locale, key) {
1682
- return locale[key] ?? key;
1683
- }
1684
- //#endregion
1685
- //#region src/orchestrator/liquid.ts
1686
- /**
1687
- * LiquidJS engine factory for the `<zitadel-login>` orchestrator.
1688
- *
1689
- * Configures the engine per the security pipeline in
1690
- * `docs/design/flowengine/template-security.md` and registers the
1691
- * `| t` filter and `{% mandatory_gates %}` tag from
1692
- * `docs/design/branding/templates.md`.
1693
- *
1694
- * Hard rules:
1695
- *
1696
- * - `{{ }}` output is HTML-escaped (LiquidJS default since v10).
1697
- * - The `| raw` filter is overridden to a no-op string passthrough that still
1698
- * escapes (Layer 1 of the security pipeline).
1699
- * - Partials are loaded from an in-memory map; no filesystem access.
1700
- */
1701
- const TEMPLATE_NAMES$1 = { default: "default" };
1702
- function createLiquidEngine(options) {
1703
- const engine = new Liquid({
1704
- templates: {
1705
- [TEMPLATE_NAMES$1.default]: default_default,
1706
- ...options.templates ?? {}
1707
- },
1708
- cache: true,
1709
- jsTruthy: true,
1710
- relativeReference: false,
1711
- strictFilters: false,
1712
- strictVariables: false,
1713
- outputEscape: "escape"
1714
- });
1715
- engine.registerFilter("raw", (value) => stringify(value));
1716
- engine.registerFilter("t", function tFilter(key, ...args) {
1717
- const lookupKey = stringify(key);
1718
- return interpolate(options.locale[lookupKey] ?? lookupKey, args.map(stringify));
1719
- });
1720
- /** Resolves `{text_key}.placeholder` — empty when undefined (not the raw key). */
1721
- engine.registerFilter("fieldPlaceholder", (textKey) => {
1722
- const lookupKey = `${stringify(textKey)}.placeholder`;
1723
- return options.locale[lookupKey] ?? "";
1724
- });
1725
- /** Resolves `{text_key}.help` — empty when undefined. */
1726
- engine.registerFilter("fieldHelp", (textKey) => {
1727
- const lookupKey = `${stringify(textKey)}.help`;
1728
- return options.locale[lookupKey] ?? "";
1729
- });
1730
- /** Maps `error.*` text keys to a field name (Figma inline-error annotations). */
1731
- const fieldErrorKeys = {
1732
- "error.email_required": "email",
1733
- "error.email_invalid": "email",
1734
- "error.email_exists": "email",
1735
- "error.password_required": "password",
1736
- "error.password_incorrect": "password",
1737
- "error.invalid_credentials": "password"
1738
- };
1739
- /** Resolves `{text_key}.title` for form-level `<zl-alert heading>`. */
1740
- engine.registerFilter("alertHeading", (textKey) => {
1741
- const lookupKey = `${stringify(textKey)}.title`;
1742
- return options.locale[lookupKey] ?? "";
1743
- });
1744
- /** Resolves `{text_key}.body` for form-level `<zl-alert>` message slot. */
1745
- engine.registerFilter("alertBody", (textKey) => {
1746
- const lookupKey = `${stringify(textKey)}.body`;
1747
- return options.locale[lookupKey] ?? "";
1748
- });
1749
- /** Localized inline error for `fieldName`, or empty when none applies. */
1750
- engine.registerFilter("fieldError", (fieldName, errors) => {
1751
- const name = stringify(fieldName);
1752
- if (!Array.isArray(errors)) return "";
1753
- for (const item of errors) {
1754
- const key = item.text_key ?? "";
1755
- if (fieldErrorKeys[key] === name) return options.locale[key] ?? key;
1756
- }
1757
- return "";
1758
- });
1759
- /** True when the error should render as `<zl-alert>`, not on a field. */
1760
- engine.registerFilter("formLevelError", (err) => {
1761
- const key = err?.text_key ?? "";
1762
- return key === "" || !(key in fieldErrorKeys);
1763
- });
1764
- engine.registerTag("mandatory_gates", {
1765
- parse() {},
1766
- render() {
1767
- return mandatoryGatesMarkerComment;
1768
- }
1769
- });
1770
- return engine;
1771
- }
1772
- function stringify(value) {
1773
- if (value == null) return "";
1774
- if (typeof value === "string") return value;
1775
- if (typeof value === "number" || typeof value === "boolean") return String(value);
1776
- try {
1777
- return JSON.stringify(value);
1778
- } catch {
1779
- return String(value);
1780
- }
1781
- }
1782
- function interpolate(template, args) {
1783
- if (args.length === 0) return template;
1784
- return template.replace(/\{(\d+)\}/g, (match, index) => {
1785
- return args[Number(index)] ?? match;
1786
- });
1787
- }
1788
- //#endregion
1789
- //#region src/orchestrator/template-names.ts
1790
- /**
1791
- * Names of the bundled Liquid templates the `<zitadel-login>` orchestrator
1792
- * registers.
1793
- *
1794
- * This lives in its own module — separate from `liquid.ts` — so the public
1795
- * barrel can re-export `TEMPLATE_NAMES` without dragging LiquidJS' `Liquid`
1796
- * type (and its Node ambient-type requirements) into the published
1797
- * declaration surface. See `liquid.ts` / the orchestrator barrel.
1798
- */
1799
- const TEMPLATE_NAMES = {
1800
- default: "default",
1801
- authForm: "auth-form",
1802
- passkeyUpsell: "passkey-upsell",
1803
- signedIn: "signed-in"
1804
- };
1805
- //#endregion
1806
- //#region src/orchestrator/locales/en.ts
1807
- /**
1808
- * Minimal English locale dictionary.
1809
- *
1810
- * The `| t` filter looks up `text_key` strings here. Missing keys fall through
1811
- * to the raw key (matches the spec in
1812
- * `docs/design/flowengine/flow-engine-guide.md`).
1813
- *
1814
- * Text keys are auto-generated by the server from the flow definition:
1815
- * - Step titles: `<step>.title`
1816
- * - Step descriptions: `<step>.description`
1817
- * - Field labels: `<step>.field.<field>`
1818
- * - Action labels: `<step>.action.<action>`
1819
- *
1820
- * Copy aligned to Figma screens file `xkvBjkOJ8ENuHdTGZHXezK` (May 2026).
1821
- * MVP only — multi-locale support is deferred.
1822
- */
1823
- const en = {
1824
- "identifier.title": "Sign in",
1825
- "identifier.description": "Enter your email to continue",
1826
- "identifier.field.email": "Work email",
1827
- "identifier.field.email.placeholder": "you@company.com",
1828
- "identifier.field.password": "Password",
1829
- "identifier.action.submit": "Sign in",
1830
- "identifier.action.continue": "Sign in",
1831
- "identifier.action.passkey": "Sign in with a passkey",
1832
- "identifier.action.register.lead": "Don't have an account? ",
1833
- "identifier.action.register.link": "Sign up",
1834
- "authenticate.title": "Sign in",
1835
- "authenticate.description": "Enter your password",
1836
- "authenticate.field.password": "Password",
1837
- "authenticate.action.submit": "Sign in",
1838
- "choose-register.title": "Create your account",
1839
- "choose-register.description": "Choose how you'd like to sign up",
1840
- "choose-register.action.register-with-password": "Sign up with password",
1841
- "choose-register.action.register-with-passkey": "Sign up with passkey",
1842
- "collect-credentials.title": "Create your account",
1843
- "collect-credentials.description": "Set up your email and password",
1844
- "collect-credentials.field.email": "Work email",
1845
- "collect-credentials.field.email.placeholder": "you@company.com",
1846
- "collect-credentials.field.password": "Password",
1847
- "collect-credentials.field.password.help": "At least 8 characters, including a symbol and number.",
1848
- "collect-credentials.action.submit": "Sign up",
1849
- "register-password.title": "Create your password",
1850
- "register-password.description": "Choose a secure password for your account",
1851
- "register-password.field.password": "Password",
1852
- "register-password.field.password.help": "At least 8 characters, including a symbol and number.",
1853
- "register-password.action.submit": "Sign up",
1854
- "passkey-upsell.title": "Sign in faster next time",
1855
- "passkey-upsell.description": "No password needed ever again.",
1856
- "passkey-upsell.description.line1": "No password needed ever again.",
1857
- "passkey-upsell.description.line2": "Sign in with Face ID, Touch ID, or PIN.",
1858
- "passkey-upsell.action.passkey_register": "Set up passkey",
1859
- "passkey-upsell.action.skip": "Skip for now",
1860
- "passkey-upsell.action.setup": "Set up passkey",
1861
- "collect-passkey-email.title": "Create your account",
1862
- "collect-passkey-email.description": "Enter your email to set up a passkey",
1863
- "collect-passkey-email.field.email": "Work email",
1864
- "collect-passkey-email.field.email.placeholder": "you@company.com",
1865
- "collect-passkey-email.action.submit": "Continue",
1866
- "passkey-enroll.title": "Set up your passkey",
1867
- "passkey-enroll.description": "Use Face ID, Touch ID, or your device PIN to create a passkey.",
1868
- "passkey-enroll.action.passkey_register": "Set up passkey",
1869
- "done.title": "You're signed in as",
1870
- "done.description": "",
1871
- "password.title": "Sign in",
1872
- "password.description": "Enter your password",
1873
- "password.field.password": "Password",
1874
- "password.action.signin": "Sign in",
1875
- "password.action.passkey": "Sign in with a passkey",
1876
- "password.action.register.lead": "Don't have an account? ",
1877
- "password.action.register.link": "Sign up",
1878
- "register.title": "Create your account",
1879
- "register.description": "",
1880
- "register.field.email": "Work email",
1881
- "register.field.email.placeholder": "you@company.com",
1882
- "register.field.password": "Password",
1883
- "register.field.password.help": "At least 8 characters, including a symbol and number.",
1884
- "register.field.givenName": "Given name",
1885
- "register.field.familyName": "Family name",
1886
- "register.field.dateOfBirth": "Date of birth",
1887
- "register.field.dateOfBirth.placeholder": "YYYY-MM-DD",
1888
- "register.field.dateOfBirth.help": "Use YYYY-MM-DD.",
1889
- "register.action.password": "Continue with password",
1890
- "register.action.passkey": "Continue with a passkey",
1891
- "register.action.submit": "Sign up",
1892
- "register.action.sign_in.lead": "Already have an account? ",
1893
- "register.action.sign_in.link": "Sign in",
1894
- "complete.title": "You're signed in as",
1895
- "signed-in.continue": "Continue",
1896
- "signed-in.logout": "Logout",
1897
- "passkey-login.title": "Sign in with your passkey",
1898
- "recover.title": "Check your email",
1899
- "recover.description": "We sent a password reset link to your email address.",
1900
- "recover.action.back": "Back to sign in",
1901
- "submit.continue": "Continue",
1902
- "submit.signin": "Sign in",
1903
- "action.forgot_password": "Forgot password?",
1904
- "action.cancel": "Cancel",
1905
- "sso.redirect.title": "Redirecting to your provider…",
1906
- "error.passkey_cancelled": "Passkey setup was cancelled",
1907
- "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
1908
- "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
1909
- "error.passkey_unsupported": "This device does not support passkeys",
1910
- "error.passkey_failed": "Something went wrong. Please try again.",
1911
- "error.email_required": "Please enter an email address",
1912
- "error.email_invalid": "Please enter a valid email",
1913
- "error.password_required": "Please enter a password",
1914
- "error.password_incorrect": "Wrong email or password.",
1915
- "error.email_exists": "An account with this email already exists.",
1916
- /** Figma sign-in error `6602:180268` — inline on password field. */
1917
- "error.invalid_credentials": "Wrong email or password.",
1918
- "error.required": "This field is required.",
1919
- "error.sign_in_server.title": "We couldn't complete your sign in.",
1920
- "error.sign_in_server.body": "Please try again in a few minutes"
1921
- };
1922
- //#endregion
1923
- //#region src/orchestrator/locales/de.ts
1924
- const de = {
1925
- "identifier.title": "Anmelden",
1926
- "identifier.description": "Gib deine E-Mail-Adresse ein, um fortzufahren",
1927
- "identifier.field.email": "E-Mail",
1928
- "identifier.field.email.placeholder": "du@unternehmen.com",
1929
- "identifier.field.password": "Passwort",
1930
- "identifier.action.submit": "Anmelden",
1931
- "identifier.action.continue": "Anmelden",
1932
- "identifier.action.passkey": "Mit Passkey anmelden",
1933
- "identifier.action.register.lead": "Noch kein Konto? ",
1934
- "identifier.action.register.link": "Registrieren",
1935
- "authenticate.title": "Anmelden",
1936
- "authenticate.description": "Gib dein Passwort ein",
1937
- "authenticate.field.password": "Passwort",
1938
- "authenticate.action.submit": "Anmelden",
1939
- "choose-register.title": "Konto erstellen",
1940
- "choose-register.description": "Wähle, wie du dich registrieren möchtest",
1941
- "choose-register.action.register-with-password": "Mit Passwort registrieren",
1942
- "choose-register.action.register-with-passkey": "Mit Passkey registrieren",
1943
- "collect-credentials.title": "Konto erstellen",
1944
- "collect-credentials.description": "Richte E-Mail und Passwort ein",
1945
- "collect-credentials.field.email": "E-Mail",
1946
- "collect-credentials.field.email.placeholder": "du@unternehmen.com",
1947
- "collect-credentials.field.password": "Passwort",
1948
- "collect-credentials.field.password.help": "Mindestens 8 Zeichen, einschließlich eines Sonderzeichens und einer Zahl.",
1949
- "collect-credentials.action.submit": "Registrieren",
1950
- "register-password.title": "Passwort erstellen",
1951
- "register-password.description": "Wähle ein sicheres Passwort für dein Konto",
1952
- "register-password.field.password": "Passwort",
1953
- "register-password.field.password.help": "Mindestens 8 Zeichen, einschließlich eines Sonderzeichens und einer Zahl.",
1954
- "register-password.action.submit": "Registrieren",
1955
- "passkey-upsell.title": "Nächstes Mal schneller anmelden",
1956
- "passkey-upsell.description": "Kein Passwort mehr nötig.",
1957
- "passkey-upsell.description.line1": "Kein Passwort mehr nötig.",
1958
- "passkey-upsell.description.line2": "Melde dich mit Face ID, Touch ID oder PIN an.",
1959
- "passkey-upsell.action.passkey_register": "Passkey einrichten",
1960
- "passkey-upsell.action.skip": "Vorerst überspringen",
1961
- "passkey-upsell.action.setup": "Passkey einrichten",
1962
- "collect-passkey-email.title": "Konto erstellen",
1963
- "collect-passkey-email.description": "Gib deine E-Mail-Adresse ein, um einen Passkey einzurichten",
1964
- "collect-passkey-email.field.email": "E-Mail",
1965
- "collect-passkey-email.field.email.placeholder": "du@unternehmen.com",
1966
- "collect-passkey-email.action.submit": "Weiter",
1967
- "passkey-enroll.title": "Passkey einrichten",
1968
- "passkey-enroll.description": "Verwende Face ID, Touch ID oder die Geräte-PIN, um einen Passkey zu erstellen.",
1969
- "passkey-enroll.action.passkey_register": "Passkey einrichten",
1970
- "done.title": "Du bist angemeldet als",
1971
- "done.description": "",
1972
- "password.title": "Anmelden",
1973
- "password.description": "Gib dein Passwort ein",
1974
- "password.field.password": "Passwort",
1975
- "password.action.signin": "Anmelden",
1976
- "password.action.passkey": "Mit Passkey anmelden",
1977
- "password.action.register.lead": "Noch kein Konto? ",
1978
- "password.action.register.link": "Registrieren",
1979
- "register.title": "Konto erstellen",
1980
- "register.description": "",
1981
- "register.field.email": "E-Mail",
1982
- "register.field.email.placeholder": "du@unternehmen.com",
1983
- "register.field.password": "Passwort",
1984
- "register.field.password.help": "Mindestens 8 Zeichen, einschließlich eines Sonderzeichens und einer Zahl.",
1985
- "register.field.givenName": "Vorname",
1986
- "register.field.familyName": "Nachname",
1987
- "register.field.dateOfBirth": "Geburtsdatum",
1988
- "register.field.dateOfBirth.placeholder": "JJJJ-MM-TT",
1989
- "register.field.dateOfBirth.help": "Verwende JJJJ-MM-TT.",
1990
- "register.action.password": "Mit Passwort fortfahren",
1991
- "register.action.passkey": "Weiter mit Passkey",
1992
- "register.action.submit": "Registrieren",
1993
- "register.action.sign_in.lead": "Bereits ein Konto? ",
1994
- "register.action.sign_in.link": "Anmelden",
1995
- "complete.title": "Du bist angemeldet als",
1996
- "signed-in.continue": "Weiter",
1997
- "signed-in.logout": "Abmelden",
1998
- "passkey-login.title": "Mit Passkey anmelden",
1999
- "recover.title": "E-Mail prüfen",
2000
- "recover.description": "Wir haben einen Link zum Zurücksetzen des Passworts an deine E-Mail-Adresse gesendet.",
2001
- "recover.action.back": "Zurück zur Anmeldung",
2002
- "submit.continue": "Weiter",
2003
- "submit.signin": "Anmelden",
2004
- "action.forgot_password": "Passwort vergessen?",
2005
- "action.cancel": "Abbrechen",
2006
- "sso.redirect.title": "Weiterleitung zum Anbieter…",
2007
- "error.passkey_cancelled": "Passkey-Einrichtung wurde abgebrochen",
2008
- "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
2009
- "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
2010
- "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
2011
- "error.passkey_failed": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
2012
- "error.email_required": "Bitte gib eine E-Mail-Adresse ein",
2013
- "error.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein",
2014
- "error.password_required": "Bitte gib ein Passwort ein",
2015
- "error.password_incorrect": "Falsche E-Mail oder falsches Passwort.",
2016
- "error.email_exists": "Ein Konto mit dieser E-Mail-Adresse existiert bereits.",
2017
- "error.invalid_credentials": "Falsche E-Mail oder falsches Passwort.",
2018
- "error.required": "Dieses Feld ist erforderlich.",
2019
- "error.sign_in_server.title": "Anmeldung konnte nicht abgeschlossen werden.",
2020
- "error.sign_in_server.body": "Bitte versuche es in einigen Minuten erneut"
2021
- };
2022
- //#endregion
2023
- //#region src/orchestrator/locales/it.ts
2024
- const it = {
2025
- "identifier.title": "Accedi",
2026
- "identifier.description": "Inserisci la tua e-mail per continuare",
2027
- "identifier.field.email": "E-mail aziendale",
2028
- "identifier.field.email.placeholder": "tu@azienda.com",
2029
- "identifier.field.password": "Password",
2030
- "identifier.action.submit": "Accedi",
2031
- "identifier.action.continue": "Accedi",
2032
- "identifier.action.passkey": "Accedi con passkey",
2033
- "identifier.action.register.lead": "Non hai un account? ",
2034
- "identifier.action.register.link": "Registrati",
2035
- "authenticate.title": "Accedi",
2036
- "authenticate.description": "Inserisci la tua password",
2037
- "authenticate.field.password": "Password",
2038
- "authenticate.action.submit": "Accedi",
2039
- "choose-register.title": "Crea il tuo account",
2040
- "choose-register.description": "Scegli come registrarti",
2041
- "choose-register.action.register-with-password": "Registrati con password",
2042
- "choose-register.action.register-with-passkey": "Registrati con passkey",
2043
- "collect-credentials.title": "Crea il tuo account",
2044
- "collect-credentials.description": "Configura e-mail e password",
2045
- "collect-credentials.field.email": "E-mail aziendale",
2046
- "collect-credentials.field.email.placeholder": "tu@azienda.com",
2047
- "collect-credentials.field.password": "Password",
2048
- "collect-credentials.field.password.help": "Almeno 8 caratteri, incluso un simbolo e un numero.",
2049
- "collect-credentials.action.submit": "Registrati",
2050
- "register-password.title": "Crea la tua password",
2051
- "register-password.description": "Scegli una password sicura per il tuo account",
2052
- "register-password.field.password": "Password",
2053
- "register-password.field.password.help": "Almeno 8 caratteri, incluso un simbolo e un numero.",
2054
- "register-password.action.submit": "Registrati",
2055
- "passkey-upsell.title": "Accedi più velocemente la prossima volta",
2056
- "passkey-upsell.description": "Nessuna password necessaria.",
2057
- "passkey-upsell.description.line1": "Nessuna password necessaria.",
2058
- "passkey-upsell.description.line2": "Accedi con Face ID, Touch ID o PIN.",
2059
- "passkey-upsell.action.passkey_register": "Configura passkey",
2060
- "passkey-upsell.action.skip": "Salta per ora",
2061
- "passkey-upsell.action.setup": "Configura passkey",
2062
- "collect-passkey-email.title": "Crea il tuo account",
2063
- "collect-passkey-email.description": "Inserisci la tua e-mail per configurare una passkey",
2064
- "collect-passkey-email.field.email": "E-mail aziendale",
2065
- "collect-passkey-email.field.email.placeholder": "tu@azienda.com",
2066
- "collect-passkey-email.action.submit": "Continua",
2067
- "passkey-enroll.title": "Configura la tua passkey",
2068
- "passkey-enroll.description": "Usa Face ID, Touch ID o il PIN del dispositivo per creare una passkey.",
2069
- "passkey-enroll.action.passkey_register": "Configura passkey",
2070
- "done.title": "Sei connesso come",
2071
- "done.description": "",
2072
- "password.title": "Accedi",
2073
- "password.description": "Inserisci la tua password",
2074
- "password.field.password": "Password",
2075
- "password.action.signin": "Accedi",
2076
- "password.action.passkey": "Accedi con passkey",
2077
- "password.action.register.lead": "Non hai un account? ",
2078
- "password.action.register.link": "Registrati",
2079
- "register.title": "Crea il tuo account",
2080
- "register.description": "",
2081
- "register.field.email": "E-mail aziendale",
2082
- "register.field.email.placeholder": "tu@azienda.com",
2083
- "register.field.password": "Password",
2084
- "register.field.password.help": "Almeno 8 caratteri, incluso un simbolo e un numero.",
2085
- "register.field.givenName": "Nome",
2086
- "register.field.familyName": "Cognome",
2087
- "register.field.dateOfBirth": "Data di nascita",
2088
- "register.field.dateOfBirth.placeholder": "AAAA-MM-GG",
2089
- "register.field.dateOfBirth.help": "Usa AAAA-MM-GG.",
2090
- "register.action.password": "Continua con password",
2091
- "register.action.passkey": "Continua con passkey",
2092
- "register.action.submit": "Registrati",
2093
- "register.action.sign_in.lead": "Hai già un account? ",
2094
- "register.action.sign_in.link": "Accedi",
2095
- "complete.title": "Sei connesso come",
2096
- "signed-in.continue": "Continua",
2097
- "signed-in.logout": "Esci",
2098
- "passkey-login.title": "Accedi con la tua passkey",
2099
- "recover.title": "Controlla la tua e-mail",
2100
- "recover.description": "Abbiamo inviato un link per reimpostare la password al tuo indirizzo e-mail.",
2101
- "recover.action.back": "Torna all'accesso",
2102
- "submit.continue": "Continua",
2103
- "submit.signin": "Accedi",
2104
- "action.forgot_password": "Password dimenticata?",
2105
- "action.cancel": "Annulla",
2106
- "sso.redirect.title": "Reindirizzamento al provider…",
2107
- "error.passkey_cancelled": "La configurazione della passkey è stata annullata",
2108
- "error.passkey_not_registered": "Questa passkey non è registrata. Accedi con e-mail e password.",
2109
- "error.passkey_setup_failed": "La registrazione della passkey non è stata completata. Riprova.",
2110
- "error.passkey_unsupported": "Questo dispositivo non supporta le passkey",
2111
- "error.passkey_failed": "Qualcosa è andato storto. Riprova.",
2112
- "error.email_required": "Inserisci un indirizzo e-mail",
2113
- "error.email_invalid": "Inserisci un indirizzo e-mail valido",
2114
- "error.password_required": "Inserisci una password",
2115
- "error.password_incorrect": "E-mail o password errata.",
2116
- "error.email_exists": "Esiste già un account con questo indirizzo e-mail.",
2117
- "error.invalid_credentials": "E-mail o password errata.",
2118
- "error.required": "Questo campo è obbligatorio.",
2119
- "error.sign_in_server.title": "Non è stato possibile completare l'accesso.",
2120
- "error.sign_in_server.body": "Riprova tra qualche minuto"
2121
- };
2122
- //#endregion
2123
- //#region src/orchestrator/locales/index.ts
2124
- const builtinLocales = {
2125
- en,
2126
- de,
2127
- it
2128
- };
2129
- //#endregion
2130
- //#region ../shared-component-styles/dist/attribution-markup.mjs
2131
- /**
2132
- * Figma sign-in attribution chip (`6593:141767`): "Secured with" + 65×16 logotype.
2133
- * Shared by Lit orchestrator HTML, dev playground, and paired React playgrounds.
2134
- */
2135
- const ZITADEL_ATTRIBUTION_LOGOTYPE_SVG = "<svg class=\"zr-pill__logotype-svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 67.3501 15.1111\" width=\"65\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M2.04839 4.265C2.06224 4.1284 2.1748 3.98591 2.32756 3.91161L8.98593 0.0494262C9.227 -0.0678242 9.45274 0.0334116 9.43089 0.248972V4.52681C9.41705 4.66341 9.30448 4.80589 9.15172 4.88019L2.49335 8.66426C2.25228 8.78151 2.02654 8.68027 2.04839 8.46471V4.265Z\"/><path d=\"M14.0086 5.25359C14.12 5.33388 14.1871 5.50261 14.1751 5.67205V13.2643C14.1561 13.5317 13.9555 13.6766 13.7798 13.5499L10.1005 11.4468C9.98908 11.3666 9.92196 11.1978 9.934 11.0284V3.32695C9.95299 3.05956 10.1535 2.91467 10.3293 3.04138L14.0086 5.25359Z\"/><path d=\"M7.37428 15.0817C7.24906 15.138 7.06938 15.1118 6.92865 15.0167L0.183465 11.1405C-0.038611 10.9903 -0.0638098 10.7442 0.133796 10.6554L3.76278 8.53833C3.888 8.48203 4.06767 8.50827 4.2084 8.60341L10.9046 12.5341C11.1267 12.6843 11.1557 12.9125 10.9543 13.0193L7.37428 15.0817Z\"/><path d=\"M65.6687 13.1043V2.00077H67.3501V13.1043H65.6687Z\"/><path d=\"M60.4255 13.2947C59.6535 13.2947 58.9715 13.1202 58.3793 12.7712C57.7871 12.4222 57.3218 11.9358 56.9834 11.3119C56.6556 10.688 56.4917 9.97418 56.4917 9.1705C56.4917 8.33509 56.6556 7.61073 56.9834 6.99739C57.3112 6.38405 57.7712 5.90819 58.3634 5.56979C58.9662 5.22083 59.6588 5.04634 60.4414 5.04634C61.2027 5.04634 61.869 5.21554 62.44 5.55393C63.011 5.88175 63.4552 6.33118 63.7724 6.90222C64.1002 7.47326 64.2642 8.12359 64.2642 8.85325C64.2642 9.0013 64.2589 9.1282 64.2483 9.23395C64.2483 9.33969 64.2377 9.46659 64.2166 9.61464H58.1731C58.2048 10.0588 58.3158 10.4553 58.5062 10.8043C58.6965 11.1427 58.9556 11.4124 59.2834 11.6133C59.6112 11.8036 59.9866 11.8988 60.4096 11.8988C60.8644 11.8988 61.2503 11.7983 61.5676 11.5974C61.8954 11.3859 62.1386 11.0898 62.2972 10.7091H64.0421C63.9046 11.185 63.672 11.6186 63.3441 12.0098C63.0269 12.4011 62.6251 12.7131 62.1386 12.9457C61.6522 13.1784 61.0811 13.2947 60.4255 13.2947ZM58.1889 8.42497H62.551C62.5299 7.81163 62.3131 7.3305 61.9007 6.98153C61.4883 6.62199 60.986 6.44222 60.3938 6.44222C59.865 6.44222 59.3839 6.60613 58.9503 6.93394C58.5168 7.26176 58.263 7.75876 58.1889 8.42497Z\"/><path d=\"M50.5389 13.2947C49.7563 13.2947 49.0796 13.1096 48.5085 12.7395C47.9481 12.3588 47.5145 11.8565 47.2078 11.2326C46.9117 10.6087 46.7637 9.91073 46.7637 9.13877C46.7637 8.37739 46.9117 7.69004 47.2078 7.0767C47.5145 6.45279 47.9481 5.96106 48.5085 5.60152C49.0796 5.2314 49.7563 5.04634 50.5389 5.04634C51.1205 5.04634 51.6492 5.16795 52.1251 5.41117C52.601 5.64382 52.9869 5.97692 53.283 6.41049V2.00077H54.9803V13.1043H53.3465L53.283 11.8829C53.0292 12.2954 52.6644 12.6337 52.1886 12.8981C51.7127 13.1625 51.1628 13.2947 50.5389 13.2947ZM50.872 11.8195C51.369 11.8195 51.7973 11.7032 52.1568 11.4705C52.5164 11.2379 52.7913 10.9206 52.9817 10.5188C53.172 10.1169 53.2672 9.66751 53.2672 9.1705C53.2672 8.59946 53.1561 8.1183 52.9341 7.72704C52.7226 7.33577 52.4318 7.0344 52.0617 6.82291C51.7021 6.61141 51.3003 6.50567 50.8561 6.50567C50.4014 6.50567 49.9943 6.6167 49.6347 6.83877C49.2752 7.06084 48.9897 7.36749 48.7782 7.75876C48.5773 8.15003 48.4768 8.61532 48.4768 9.15463C48.4768 9.68337 48.5825 10.1487 48.794 10.5505C49.0055 10.9524 49.2911 11.2643 49.6506 11.4864C50.0101 11.7085 50.4173 11.8195 50.872 11.8195Z\"/><path d=\"M40.7882 13.2947C40.2806 13.2947 39.8153 13.2048 39.3923 13.025C38.9693 12.8347 38.6309 12.5597 38.3771 12.2002C38.1233 11.8406 37.9965 11.4071 37.9965 10.8995C37.9965 10.2967 38.1445 9.81556 38.4406 9.45602C38.7473 9.0859 39.1544 8.81624 39.662 8.64704C40.1801 8.46727 40.7459 8.37739 41.3592 8.37739H43.3579C43.3579 7.92267 43.2839 7.55786 43.1358 7.28291C42.9878 6.99739 42.7868 6.79118 42.533 6.66429C42.2792 6.52681 41.9832 6.45808 41.6448 6.45808C41.2006 6.45808 40.8094 6.56383 40.471 6.77532C40.1431 6.97624 39.9369 7.29876 39.8523 7.7429H38.1551C38.2079 7.17186 38.3983 6.68544 38.7261 6.28359C39.0645 5.88175 39.4928 5.57508 40.011 5.36358C40.5291 5.15209 41.079 5.04634 41.6606 5.04634C42.422 5.04634 43.0512 5.1891 43.5482 5.47462C44.0558 5.74957 44.4312 6.13555 44.6744 6.63256C44.9282 7.12958 45.0551 7.69531 45.0551 8.3298V10.7409C45.0657 11.0475 45.1292 11.2749 45.2455 11.4229C45.3724 11.5604 45.5945 11.6397 45.9117 11.6609V13.1043C45.4887 13.1043 45.1186 13.0567 44.8013 12.9616C44.4947 12.8664 44.2409 12.7236 44.04 12.5333C43.839 12.3324 43.6804 12.0786 43.5641 11.7719C43.3103 12.1949 42.9402 12.5544 42.4537 12.8505C41.9673 13.1466 41.4121 13.2947 40.7882 13.2947ZM41.1848 11.9147C41.6183 11.9147 41.999 11.8089 42.3268 11.5974C42.6547 11.3753 42.9084 11.0898 43.0882 10.7409C43.268 10.3813 43.3579 9.99533 43.3579 9.58291V9.55119H41.4861C41.1795 9.55119 40.8887 9.5882 40.6137 9.66223C40.3493 9.73625 40.1326 9.86315 39.9634 10.0429C39.8047 10.2121 39.7254 10.45 39.7254 10.7567C39.7254 11.1586 39.8682 11.4547 40.1537 11.645C40.4392 11.8248 40.7829 11.9147 41.1848 11.9147Z\"/><path d=\"M35.5197 13.1043C34.7372 13.1043 34.1344 12.914 33.7114 12.5333C33.2884 12.1526 33.0769 11.5287 33.0769 10.6615V6.64842H31.8873V5.23669H33.0769V3.3332H34.7583V5.23669H36.757V6.64842H34.7583V10.6615C34.7583 11.0634 34.8482 11.333 35.028 11.4705C35.2078 11.608 35.488 11.6767 35.8687 11.6767H36.757V13.1043H35.5197Z\"/><path d=\"M29.6602 3.85668C29.3536 3.85668 29.0892 3.7509 28.8671 3.53941C28.6556 3.31734 28.5499 3.04768 28.5499 2.73043C28.5499 2.42376 28.6556 2.16468 28.8671 1.95319C29.0892 1.74169 29.3536 1.63594 29.6602 1.63594C29.9775 1.63594 30.2419 1.74169 30.4533 1.95319C30.6754 2.16468 30.7865 2.42376 30.7865 2.73043C30.7865 3.04768 30.6754 3.31734 30.4533 3.53941C30.2419 3.7509 29.9775 3.85668 29.6602 3.85668ZM28.8195 13.1043V5.23669H30.5009V13.1043H28.8195Z\"/><path d=\"M19.8276 13.1043V11.8036L25.1098 3.42837H19.8911V2.00077H27.0608V3.30147L21.7628 11.6609H27.1084V13.1043H19.8276Z\"/></svg>";
2136
- /** Inner markup for the default Zitadel attribution pill (label + logotype). */
2137
- function zitadelAttributionPillInnerHtml() {
2138
- return `Secured with <span class="zr-pill__logotype">${ZITADEL_ATTRIBUTION_LOGOTYPE_SVG}</span>`;
2139
- }
2140
- //#endregion
2141
- //#region src/orchestrator/sanitiser.ts
2142
- /**
2143
- * Layer 2 of the template security pipeline (`docs/design/flowengine/template-security.md`).
2144
- *
2145
- * Wraps DOMPurify with the configuration the auth-UI templates need:
2146
- *
2147
- * - Allow every `<zl-*>` custom element registered in the manifest registry,
2148
- * plus the union of attributes declared on each manifest.
2149
- * - Allow common HTML structural / typographic tags used by `default.liquid`.
2150
- * - Strip `on*` event handlers and any `<script>` / `<style>` tags (theming is
2151
- * orchestrator-owned via `adoptedStyleSheets`).
2152
- *
2153
- * Runs after Liquid finishes rendering and before the HTML is handed to Lit's
2154
- * `unsafeHTML` directive.
2155
- */
2156
- /**
2157
- * Standard HTML tags that templates may use as structural chrome around
2158
- * `<zl-*>` atoms. Anything outside this list and the manifest registry is
2159
- * stripped at sanitise-time.
2160
- */
2161
- const STRUCTURAL_TAGS = [
2162
- "div",
2163
- "span",
2164
- "p",
2165
- "small",
2166
- "strong",
2167
- "em",
2168
- "br",
2169
- "hr",
2170
- "h1",
2171
- "h2",
2172
- "h3",
2173
- "h4",
2174
- "h5",
2175
- "h6",
2176
- "ul",
2177
- "ol",
2178
- "li",
2179
- "a",
2180
- "img",
2181
- "section",
2182
- "header",
2183
- "footer",
2184
- "main",
2185
- "nav",
2186
- "aside",
2187
- "article",
2188
- "figure",
2189
- "figcaption",
2190
- "label",
2191
- "fieldset",
2192
- "legend"
2193
- ];
2194
- /**
2195
- * Attributes templates may carry on structural tags. Per-atom attributes are
2196
- * derived from the manifest registry below — manifests are the single source
2197
- * of truth for "what attributes does `<zl-field>` accept?".
2198
- */
2199
- const COMMON_ATTRS = [
2200
- "id",
2201
- "class",
2202
- "role",
2203
- "slot",
2204
- "tabindex",
2205
- "lang",
2206
- "dir",
2207
- "alt",
2208
- "title",
2209
- "src",
2210
- "srcset",
2211
- "sizes",
2212
- "href",
2213
- "target",
2214
- "rel",
2215
- "style",
2216
- "name",
2217
- "for",
2218
- "data-testid",
2219
- "data-theme",
2220
- "hidden"
2221
- ];
2222
- const CUSTOM_TAG_PATTERN = /^zl-[a-z0-9-]+$/;
2223
- function buildAllowedTagList() {
2224
- const set = new Set(STRUCTURAL_TAGS);
2225
- for (const manifest of manifestRegistry) set.add(manifest.tag);
2226
- return [...set];
2227
- }
2228
- function buildAllowedAttrList() {
2229
- const set = new Set(COMMON_ATTRS);
2230
- for (const manifest of manifestRegistry) for (const attr of manifest.attrs) set.add(attr);
2231
- return [...set];
2232
- }
2233
- /**
2234
- * Returns a sanitiser bound to the current manifest registry. Callers should
2235
- * cache the result; rebuilding is cheap but allocates a fresh DOMPurify config.
2236
- */
2237
- function createSanitiser() {
2238
- const config = {
2239
- USE_PROFILES: { html: true },
2240
- ADD_TAGS: buildAllowedTagList(),
2241
- ADD_ATTR: buildAllowedAttrList(),
2242
- FORBID_TAGS: [
2243
- "script",
2244
- "style",
2245
- "iframe",
2246
- "object",
2247
- "embed",
2248
- "form",
2249
- "input",
2250
- "button"
2251
- ],
2252
- FORBID_ATTR: [
2253
- "onerror",
2254
- "onclick",
2255
- "onload",
2256
- "onfocus",
2257
- "onblur"
2258
- ],
2259
- CUSTOM_ELEMENT_HANDLING: {
2260
- tagNameCheck: CUSTOM_TAG_PATTERN,
2261
- attributeNameCheck: /^(?:[a-z][a-z0-9-]*)$/,
2262
- allowCustomizedBuiltInElements: false
2263
- },
2264
- KEEP_CONTENT: true,
2265
- ALLOW_DATA_ATTR: true,
2266
- ALLOW_ARIA_ATTR: true,
2267
- RETURN_TRUSTED_TYPE: false
2268
- };
2269
- return function sanitise(html) {
2270
- const result = DOMPurify.sanitize(html, config);
2271
- return typeof result === "string" ? result : String(result);
2272
- };
2273
- }
2274
- //#endregion
2275
- //#region src/orchestrator/templates/layout-chrome.css?inline
2276
- var layout_chrome_default = ":host {\n background: var(--zl-color-surface-default-black, #0f0f11);\n min-height: 100%;\n color: var(--zl-color-text-primary-white, #f4f4f6);\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n display: block;\n}\n\n.zl-mount {\n background: inherit;\n min-height: 100vh;\n color: inherit;\n flex-direction: column;\n display: flex;\n}\n\n.zl-mount > zl-page-shell, .zl-mount > [data-zl-template-root] {\n flex: auto;\n}\n\n.zl-attribution {\n background: none;\n justify-content: center;\n align-items: center;\n display: flex;\n}\n\n.zl-attribution[hidden] {\n display: none;\n}\n\n.zl-card-title {\n font-family: var(--zl-font-family-heading, var(--zl-font-family-sans, system-ui, sans-serif));\n letter-spacing: -.02em;\n color: var(--zl-color-text-primary-white, #f4f4f6);\n text-align: left;\n margin: 0;\n font-size: 2rem;\n font-weight: 400;\n line-height: 2.5rem;\n}\n\n.zl-card-subtitle {\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n color: var(--zl-color-text-primary-white, #f4f4f6);\n margin: 0;\n font-size: .875rem;\n font-weight: 400;\n line-height: 1.25rem;\n}\n\n.zl-card-nav {\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n color: var(--zl-color-text-primary-white, #f4f4f6);\n text-align: left;\n margin: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5rem;\n}\n\n.zl-card-nav__link {\n font: inherit;\n line-height: inherit;\n color: var(--zl-color-icon-default-purple, #bba5e4);\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: none;\n display: inline;\n}\n\n.zl-card-nav__link:hover {\n text-decoration: underline;\n}\n\n.zl-card-nav__link:focus-visible {\n outline: var(--zl-focus-width, 2px) solid var(--zl-focus-color, #f4f4f6);\n outline-offset: var(--zl-focus-offset, 2px);\n}\n\n.zl-card-forgot {\n text-align: left;\n margin: 0;\n}\n\n.zl-card-forgot__link {\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n color: var(--zl-color-icon-default-purple, #bba5e4);\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5rem;\n text-decoration: none;\n display: inline;\n}\n\n.zl-card-forgot__link:hover {\n text-decoration: underline;\n}\n\n.zl-card-forgot__link:focus-visible {\n outline: var(--zl-focus-width, 2px) solid var(--zl-focus-color, #f4f4f6);\n outline-offset: var(--zl-focus-offset, 2px);\n}\n";
2277
- //#endregion
2278
- //#region src/orchestrator/theme-controller.ts
2279
- const DARK_QUERY = "(prefers-color-scheme: dark)";
2280
- var ThemeController = class {
2281
- host;
2282
- branding;
2283
- mediaQuery = null;
2284
- _theme = "dark";
2285
- constructor(host) {
2286
- this.host = host;
2287
- host.addController(this);
2288
- }
2289
- get theme() {
2290
- return this._theme;
2291
- }
2292
- setBranding(branding) {
2293
- this.branding = branding;
2294
- this.refresh();
2295
- }
2296
- hostConnected() {
2297
- this.refresh();
2298
- }
2299
- hostDisconnected() {
2300
- this.detach();
2301
- }
2302
- refresh() {
2303
- const mode = this.branding?.theme?.mode ?? "dark";
2304
- if (mode === "auto") {
2305
- this.attach();
2306
- this.update(this.mediaQuery?.matches === false ? "light" : "dark");
2307
- return;
2308
- }
2309
- this.detach();
2310
- this.update(mode === "light" ? "light" : "dark");
2311
- }
2312
- attach() {
2313
- if (this.mediaQuery || typeof matchMedia !== "function") return;
2314
- this.mediaQuery = matchMedia(DARK_QUERY);
2315
- this.mediaQuery.addEventListener("change", this.onMediaChange);
2316
- }
2317
- detach() {
2318
- if (!this.mediaQuery) return;
2319
- this.mediaQuery.removeEventListener("change", this.onMediaChange);
2320
- this.mediaQuery = null;
2321
- }
2322
- onMediaChange = (event) => {
2323
- this.update(event.matches ? "dark" : "light");
2324
- };
2325
- update(next) {
2326
- if (this._theme === next) return;
2327
- this._theme = next;
2328
- this.host.requestUpdate();
2329
- }
2330
- };
2331
- //#endregion
2332
- //#region src/orchestrator/zitadel-login.ts
2333
- let ZitadelLogin = class ZitadelLogin extends LitElement {
2334
- static shadowRootOptions = {
2335
- ...LitElement.shadowRootOptions,
2336
- delegatesFocus: true
2337
- };
2338
- static styles = css`
2339
- :host {
2340
- display: block;
2341
- width: 100%;
2342
- min-height: 100vh;
2343
- }
2344
- `;
2345
- #_purpose_accessor_storage = "login";
2346
- get purpose() {
2347
- return this.#_purpose_accessor_storage;
2348
- }
2349
- set purpose(value) {
2350
- this.#_purpose_accessor_storage = value;
2351
- }
2352
- #_project_accessor_storage;
2353
- get project() {
2354
- return this.#_project_accessor_storage;
2355
- }
2356
- set project(value) {
2357
- this.#_project_accessor_storage = value;
2358
- }
2359
- #_projectId_accessor_storage = "";
2360
- get projectId() {
2361
- return this.#_projectId_accessor_storage;
2362
- }
2363
- set projectId(value) {
2364
- this.#_projectId_accessor_storage = value;
2365
- }
2366
- #_proxyPath_accessor_storage = "";
2367
- get proxyPath() {
2368
- return this.#_proxyPath_accessor_storage;
2369
- }
2370
- set proxyPath(value) {
2371
- this.#_proxyPath_accessor_storage = value;
2372
- }
2373
- #_url_accessor_storage = "";
2374
- get url() {
2375
- return this.#_url_accessor_storage;
2376
- }
2377
- set url(value) {
2378
- this.#_url_accessor_storage = value;
2379
- }
2380
- #_postSignInUrl_accessor_storage = "";
2381
- get postSignInUrl() {
2382
- return this.#_postSignInUrl_accessor_storage;
2383
- }
2384
- set postSignInUrl(value) {
2385
- this.#_postSignInUrl_accessor_storage = value;
2386
- }
2387
- #_resumeFlowId_accessor_storage = "";
2388
- get resumeFlowId() {
2389
- return this.#_resumeFlowId_accessor_storage;
2390
- }
2391
- set resumeFlowId(value) {
2392
- this.#_resumeFlowId_accessor_storage = value;
2393
- }
2394
- #_lang_accessor_storage = "";
2395
- get lang() {
2396
- return this.#_lang_accessor_storage;
2397
- }
2398
- set lang(value) {
2399
- this.#_lang_accessor_storage = value;
2400
- }
2401
- #_locales_accessor_storage;
2402
- get locales() {
2403
- return this.#_locales_accessor_storage;
2404
- }
2405
- set locales(value) {
2406
- this.#_locales_accessor_storage = value;
2407
- }
2408
- #_response_accessor_storage = null;
2409
- get response() {
2410
- return this.#_response_accessor_storage;
2411
- }
2412
- set response(value) {
2413
- this.#_response_accessor_storage = value;
2414
- }
2415
- #_branding_accessor_storage = void 0;
2416
- get branding() {
2417
- return this.#_branding_accessor_storage;
2418
- }
2419
- set branding(value) {
2420
- this.#_branding_accessor_storage = value;
2421
- }
2422
- #_loading_accessor_storage = false;
2423
- get loading() {
2424
- return this.#_loading_accessor_storage;
2425
- }
2426
- set loading(value) {
2427
- this.#_loading_accessor_storage = value;
2428
- }
2429
- #_startupError_accessor_storage = null;
2430
- get startupError() {
2431
- return this.#_startupError_accessor_storage;
2432
- }
2433
- set startupError(value) {
2434
- this.#_startupError_accessor_storage = value;
2435
- }
2436
- #_formValues_accessor_storage = {};
2437
- get formValues() {
2438
- return this.#_formValues_accessor_storage;
2439
- }
2440
- set formValues(value) {
2441
- this.#_formValues_accessor_storage = value;
2442
- }
2443
- themeController = new ThemeController(this);
2444
- engine = null;
2445
- sanitise = createSanitiser();
2446
- /**
2447
- * Cached compiled tenant template, keyed by source string. Re-rendering on
2448
- * every `formValues` change otherwise re-parses the same template.
2449
- */
2450
- tenantTemplateCache = null;
2451
- createRenderRoot() {
2452
- const root = super.createRenderRoot();
2453
- if (root instanceof ShadowRoot) {
2454
- const existing = Array.isArray(root.adoptedStyleSheets) ? root.adoptedStyleSheets : [];
2455
- const sheet = new CSSStyleSheet();
2456
- sheet.replaceSync(layout_chrome_default);
2457
- root.adoptedStyleSheets = [...existing, sheet];
2458
- root.addEventListener("zl-input", this.handleAtomInput);
2459
- root.addEventListener("zl-submit", this.handleAtomSubmit);
2460
- root.addEventListener("click", this.handleDelegatedAction);
2461
- root.addEventListener("submit", this.handleFormSubmit);
2462
- root.addEventListener("zl-passkey-result", this.handlePasskeyResult);
2463
- root.addEventListener("zl-passkey-error", this.handlePasskeyError);
2464
- }
2465
- return root;
2466
- }
2467
- /**
2468
- * Start the flow after the first render rather than in `connectedCallback`.
2469
- * Frameworks that wrap web components (e.g. `@lit/react` in the console)
2470
- * attach the element first and then assign object properties (`branding`,
2471
- * `locale`) via setters. `connectedCallback` runs synchronously on attach
2472
- * — before any setters from a wrapper's effects/refs — so reading those
2473
- * properties there sees stale defaults. `firstUpdated` runs after Lit's
2474
- * first render, by which time setters from the wrapping framework have
2475
- * fired.
2476
- */
2477
- firstUpdated() {
2478
- queueMicrotask(() => void this.startFlow());
2479
- }
2480
- /**
2481
- * Resolves the effective locale dictionary. The built-in dictionary for the
2482
- * resolved language is used as the base; entries from the `locales` map (if
2483
- * set) are spread on top so partial overrides work without importing and
2484
- * spreading the full base dictionary.
2485
- *
2486
- * Priority: explicit `lang` attr → `navigator.language` (user preference)
2487
- * → `document.documentElement.lang` (page default) → English fallback.
2488
- */
2489
- resolveLocale() {
2490
- const primary = ((this.lang || (typeof navigator !== "undefined" ? navigator.language : "") || (typeof document !== "undefined" ? document.documentElement.lang : "")).split("-")[0] ?? "").toLowerCase();
2491
- const builtin = builtinLocales[primary] ?? en;
2492
- const custom = this.locales?.[primary];
2493
- return custom ? {
2494
- ...builtin,
2495
- ...custom
2496
- } : builtin;
2497
- }
2498
- willUpdate(changed) {
2499
- if (!this.engine || changed.has("locales") || changed.has("lang")) this.engine = createLiquidEngine({ locale: this.resolveLocale() });
2500
- const root = this.shadowRoot;
2501
- if (root) {
2502
- applyBaseTokens(root);
2503
- applyBrandingTokens(root, this.branding, this.themeController.theme);
2504
- applyFontUrl(root, this.branding?.font_url ?? null);
2505
- }
2506
- this.dataset.theme = this.themeController.theme;
2507
- this.toggleAttribute("data-theme-dark", this.themeController.theme === "dark");
2508
- this.setAttribute("aria-busy", this.loading ? "true" : "false");
2509
- }
2510
- updated(changed) {
2511
- if (!changed.has("response")) return;
2512
- this.hydrateStepAfterRender();
2513
- }
2514
- /**
2515
- * Apply captured values and move focus once the new step has fully
2516
- * rendered. This commit produces the step's `zl-field`/`zl-button` atoms,
2517
- * but those render their own shadow DOM on a later microtask — so await
2518
- * this element's update *and* the child atoms' first render before touching
2519
- * them, rather than guessing a frame with `requestAnimationFrame`.
2520
- */
2521
- async hydrateStepAfterRender() {
2522
- await this.updateComplete;
2523
- const atoms = this.shadowRoot?.querySelectorAll("zl-field, zl-button");
2524
- if (atoms) await Promise.all(Array.from(atoms).map((atom) => atom.updateComplete));
2525
- this.applyValuesToFields();
2526
- this.moveFocusToFirstField();
2527
- }
2528
- render() {
2529
- if (this.startupError) return html`<form class="zl-mount" novalidate>
2530
- <zl-alert severity="error">${this.startupError}</zl-alert>
2531
- </form>`;
2532
- if (!this.response || !this.engine) return html`<slot name="loader"></slot>`;
2533
- const rendered = this.injectAttribution(this.renderStep(this.response.step, this.engine));
2534
- return html`<form
2535
- class="zl-mount"
2536
- part="form"
2537
- novalidate
2538
- aria-busy=${this.loading ? "true" : "false"}
2539
- >
2540
- ${unsafeHTML(rendered)}
2541
- </form>`;
2542
- }
2543
- /**
2544
- * Inject the attribution badge into the rendered template's
2545
- * `<zl-page-shell>` footer slot. The attribution must live INSIDE the
2546
- * page-shell so it sits within the 100vh viewport rhythm (matching the
2547
- * Figma sign-in frame where the pill sits 24px below the card, both
2548
- * centred on the page). It can't be a sibling of the page-shell because
2549
- * the page-shell already occupies the full viewport height.
2550
- *
2551
- * This markup is appended AFTER `renderStep` has sanitised the Liquid
2552
- * output, so it is not run through DOMPurify. It is orchestrator-owned and
2553
- * any tenant-supplied values (`custom_link`) are escaped via `escapeHtml`.
2554
- */
2555
- injectAttribution(rendered) {
2556
- const html = this.renderAttributionHtml();
2557
- if (!html) return rendered;
2558
- if (rendered.includes("</zl-page-shell>")) return rendered.replace("</zl-page-shell>", `${html}</zl-page-shell>`);
2559
- return rendered + html;
2560
- }
2561
- /**
2562
- * "Secured with Zitadel" attribution chrome injected into every
2563
- * template's page-shell footer slot. Controlled by
2564
- * `branding.attribution.show_zitadel` — defaults to `true` for
2565
- * community / OSS deployments. Licensed tenants can suppress the badge
2566
- * entirely or swap it for a `custom_link` value.
2567
- */
2568
- renderAttributionHtml() {
2569
- const attribution = this.branding?.attribution;
2570
- const show = attribution?.show_zitadel !== false;
2571
- const custom = attribution?.custom_link;
2572
- if (!show && !custom) return "";
2573
- if (custom) return `<div slot="footer" part="attribution" class="zl-attribution"><zl-pill tone="neutral" href="${escapeHtml(String(custom.href))}">${escapeHtml(String(custom.label))}</zl-pill></div>`;
2574
- return `<div slot="footer" part="attribution" class="zl-attribution"><zl-pill tone="neutral" href="https://zitadel.com" part="attribution-pill" aria-label="Secured with Zitadel">${zitadelAttributionPillInnerHtml()}</zl-pill></div>`;
2575
- }
2576
- /** Declarative config read from this element's attributes. */
2577
- get projectAttrs() {
2578
- return {
2579
- projectId: this.projectId,
2580
- proxyPath: this.proxyPath,
2581
- url: this.url
2582
- };
2583
- }
2584
- async startFlow() {
2585
- this.loading = true;
2586
- this.startupError = null;
2587
- try {
2588
- const { project: cfg, api } = resolveApi(this.project, this.projectAttrs, "<zitadel-login>");
2589
- let wire;
2590
- if (this.resumeFlowId) wire = await getCurrentStep(api, this.resumeFlowId);
2591
- else {
2592
- if (!cfg.projectId) throw new Error("<zitadel-login> requires a project id (the `project-id` attribute, `configureZitadel({ projectId })`, or a `project` handle) to start a flow.");
2593
- wire = await startFlow(api, {
2594
- project_id: cfg.projectId,
2595
- purpose: this.purpose
2596
- });
2597
- }
2598
- this.applyResponse(wire);
2599
- } catch (error) {
2600
- this.handleTransportError(error);
2601
- } finally {
2602
- this.loading = false;
2603
- }
2604
- }
2605
- applyResponse(wire) {
2606
- this.response = wire;
2607
- const { branding, issues } = validateBranding(wire.branding);
2608
- this.branding = branding;
2609
- this.themeController.setBranding(branding);
2610
- if (issues.length > 0) console.warn("[zitadel-login] branding payload has issues:", issues);
2611
- this.formValues = {
2612
- ...collectInitialValues(wire.step),
2613
- ...this.formValues
2614
- };
2615
- this.maybeCompleteFlow(wire);
2616
- }
2617
- /**
2618
- * Acts on terminal flow steps. The wire surfaces two kinds of completion:
2619
- *
2620
- * - `step.complete === "redirect"` — navigate the browser to
2621
- * `response.redirect_uri` (OIDC/SAML `auth_request_id` resolved). This
2622
- * takes precedence over `post-sign-in-url`.
2623
- * - `step.complete === "show"` — when `post-sign-in-url` is set, exchange
2624
- * the `handoff_token` for a session cookie and navigate there.
2625
- *
2626
- * `zitadel-flow-complete` is always emitted so hosts with custom post-sign-in
2627
- * flows can handle the handoff themselves when `post-sign-in-url` is omitted.
2628
- */
2629
- async maybeCompleteFlow(response) {
2630
- const behavior = response.step.complete;
2631
- if (!behavior) return;
2632
- emit(this, "zitadel-flow-complete", {
2633
- behavior,
2634
- redirect_uri: response.redirect_uri,
2635
- handoff_token: response.handoff_token,
2636
- handoff_token_expires_at: response.handoff_token_expires_at
2637
- });
2638
- if (typeof window === "undefined") return;
2639
- if (behavior === "redirect" && response.redirect_uri) {
2640
- window.location.assign(response.redirect_uri);
2641
- return;
2642
- }
2643
- const handoffToken = response.handoff_token;
2644
- if (behavior === "show" && handoffToken && this.postSignInUrl) {
2645
- this.loading = true;
2646
- try {
2647
- const { project: cfg, api } = resolveApi(this.project, this.projectAttrs, "<zitadel-login>");
2648
- await exchangeSession(api, { handoff_token: handoffToken }, { project_id: cfg.projectId });
2649
- window.location.assign(this.postSignInUrl);
2650
- } catch (error) {
2651
- this.handleTransportError(error);
2652
- } finally {
2653
- this.loading = false;
2654
- }
2655
- }
2656
- }
2657
- renderStep(step, engine) {
2658
- const tenantSource = typeof this.branding?.liquid_template === "string" && this.branding.liquid_template.length > 0 ? this.branding.liquid_template : null;
2659
- const errors = step.error ? step.error.startsWith("error.") ? [{ text_key: step.error }] : [{ message: step.error }] : [];
2660
- const fields = step.fields ?? [];
2661
- const actions = step.actions ?? [];
2662
- const context = {
2663
- step: {
2664
- name: step.name,
2665
- complete: step.complete,
2666
- texts: step.texts ?? {}
2667
- },
2668
- fields,
2669
- actions,
2670
- gates: step.gates ?? {},
2671
- sso_providers: step.sso_providers ?? [],
2672
- challenge: step.challenge ?? null,
2673
- messages: [],
2674
- identity: this.deriveIdentity(),
2675
- errors,
2676
- branding: this.branding ?? {},
2677
- loading: this.loading
2678
- };
2679
- let raw;
2680
- try {
2681
- if (tenantSource) {
2682
- const compiled = this.tenantTemplateCache?.source === tenantSource ? this.tenantTemplateCache.template : engine.parse(tenantSource);
2683
- this.tenantTemplateCache = {
2684
- source: tenantSource,
2685
- template: compiled
2686
- };
2687
- raw = engine.renderSync(compiled, context);
2688
- } else raw = engine.renderFileSync(TEMPLATE_NAMES.default, context);
2689
- } catch (error) {
2690
- console.error("[zitadel-login] Liquid render failed:", error);
2691
- try {
2692
- raw = engine.renderFileSync(TEMPLATE_NAMES.default, context);
2693
- } catch {
2694
- return `<zl-alert severity="error">We couldn't render this step.</zl-alert>`;
2695
- }
2696
- }
2697
- const patched = patchMandatoryGates(raw, step, this.resolveLocale());
2698
- return this.sanitise(patched);
2699
- }
2700
- /**
2701
- * Build a `FlowIdentity` from the orchestrator's captured form values so
2702
- * the signed-in template can greet the user by email without the API
2703
- * having to round-trip identity claims. Email comes from the
2704
- * identifier step; display name composes from `given_name` /
2705
- * `family_name` when the register step ran.
2706
- */
2707
- deriveIdentity() {
2708
- const email = this.formValues.email?.trim();
2709
- const display = [this.formValues.given_name?.trim(), this.formValues.family_name?.trim()].filter(Boolean).join(" ").trim();
2710
- if (!email && !display) return null;
2711
- return {
2712
- ...email ? { email_address: email } : {},
2713
- ...display ? { display_name: display } : email ? { display_name: email } : {}
2714
- };
2715
- }
2716
- applyValuesToFields() {
2717
- const root = this.shadowRoot;
2718
- if (!root) return;
2719
- const fields = root.querySelectorAll("zl-field");
2720
- if (fields.length === 0) return;
2721
- for (const field of fields) {
2722
- const name = field.getAttribute("name");
2723
- if (!name || !(name in this.formValues)) continue;
2724
- const next = this.formValues[name];
2725
- if (field.value !== next) field.value = next;
2726
- }
2727
- }
2728
- captureValuesFromFields() {
2729
- const root = this.shadowRoot;
2730
- if (!root) return this.formValues;
2731
- const fields = root.querySelectorAll("zl-field");
2732
- if (fields.length === 0) return this.formValues;
2733
- const next = { ...this.formValues };
2734
- let changed = false;
2735
- for (const field of fields) {
2736
- const name = field.getAttribute("name");
2737
- const value = this.currentFieldValue(field);
2738
- if (!name || value === void 0) continue;
2739
- if (field.value !== value) field.value = value;
2740
- if (next[name] !== value) {
2741
- next[name] = value;
2742
- changed = true;
2743
- }
2744
- }
2745
- if (changed) this.formValues = next;
2746
- return next;
2747
- }
2748
- currentFieldValue(field) {
2749
- const native = field.shadowRoot?.querySelector("input");
2750
- if (native && native.value !== field.value) return native.value;
2751
- return typeof field.value === "string" ? field.value : void 0;
2752
- }
2753
- handleAtomInput = (event) => {
2754
- if (!event.detail) return;
2755
- const { name, value } = event.detail;
2756
- if (!name) return;
2757
- this.formValues = {
2758
- ...this.formValues,
2759
- [name]: value
2760
- };
2761
- this.syncFieldElementValue(name, value);
2762
- emit(this, "zitadel-flow-input", {
2763
- name,
2764
- value
2765
- });
2766
- };
2767
- syncFieldElementValue(name, value) {
2768
- const root = this.shadowRoot;
2769
- if (!root) return;
2770
- const fields = root.querySelectorAll("zl-field");
2771
- for (const field of fields) {
2772
- if (field.getAttribute("name") !== name) continue;
2773
- if (field.value !== value) field.value = value;
2774
- const native = field.shadowRoot?.querySelector("input");
2775
- if (native && native.value !== value) native.value = value;
2776
- }
2777
- }
2778
- handleAtomSubmit = (event) => {
2779
- if (this.loading) return;
2780
- this.submit(event.detail?.action ?? null);
2781
- };
2782
- /** Secondary navigation rows (`data-action` on `.zl-card-nav__link`). */
2783
- handleDelegatedAction = (event) => {
2784
- const target = event.target?.closest("[data-action]");
2785
- if (!target || target.closest("zl-button") || this.loading) return;
2786
- const action = target.getAttribute("data-action");
2787
- if (!action) return;
2788
- event.preventDefault();
2789
- this.submit(action);
2790
- };
2791
- handleFormSubmit = (event) => {
2792
- event.preventDefault();
2793
- if (this.loading) return;
2794
- const action = event.submitter?.getAttribute?.("action") ?? null ?? this.findPrimaryAction();
2795
- this.submit(action);
2796
- };
2797
- /**
2798
- * Handle a successful WebAuthn ceremony. Auto-submit with the proof
2799
- * as `challenge_response` so the flow advances without extra user
2800
- * interaction — the ceremony IS the factor verification (ADR 013).
2801
- */
2802
- handlePasskeyResult = (event) => {
2803
- if (this.loading) return;
2804
- const { challenge_id, method, proof } = event.detail;
2805
- this.submit(method, {
2806
- challenge_id,
2807
- method,
2808
- proof
2809
- });
2810
- };
2811
- /**
2812
- * Handle a WebAuthn ceremony error. Re-render the current step with
2813
- * an error message so the user sees feedback and can retry or skip.
2814
- *
2815
- * Guard: if the step already carries the same error key, skip the update.
2816
- * Mutating `this.response` triggers `unsafeHTML` to replace the DOM tree,
2817
- * which reconnects a fresh `<zl-passkey>` that immediately re-starts the
2818
- * ceremony — creating an infinite loop. The guard breaks the cycle.
2819
- *
2820
- * We also strip the `challenge` from the step so the template does not
2821
- * render a new `<zl-passkey>` on re-render. Without this, the first
2822
- * cancel would trigger a second ceremony (the guard prevents a third).
2823
- */
2824
- handlePasskeyError = (event) => {
2825
- if (!this.response) return;
2826
- const { error: message, aborted } = event.detail;
2827
- const errorKey = aborted ? "error.passkey_cancelled" : "error.passkey_failed";
2828
- if (this.response.step.error === errorKey) return;
2829
- const { challenge: _dropped, ...stepWithoutChallenge } = this.response.step;
2830
- this.response = {
2831
- ...this.response,
2832
- step: {
2833
- ...stepWithoutChallenge,
2834
- error: errorKey
2835
- }
2836
- };
2837
- console.warn(`[zitadel-login] passkey ceremony ${aborted ? "cancelled" : "failed"}: ${message}`);
2838
- };
2839
- findPrimaryAction() {
2840
- const root = this.shadowRoot;
2841
- if (!root) return null;
2842
- return (root.querySelector("zl-button[hierarchy=\"primary\"][type=\"submit\"]") ?? root.querySelector("zl-button[hierarchy=\"primary\"]"))?.getAttribute("action") || null;
2843
- }
2844
- moveFocusToFirstField() {
2845
- const root = this.shadowRoot;
2846
- if (!root) return;
2847
- const focusables = root.querySelectorAll("zl-field, zl-button");
2848
- Array.from(focusables).find((el) => !el.hasAttribute("disabled"))?.focus();
2849
- }
2850
- async submit(action, challengeResponse) {
2851
- if (!this.response || this.loading) return;
2852
- const { id, session_token } = this.response;
2853
- this.loading = true;
2854
- try {
2855
- const formValues = this.captureValuesFromFields();
2856
- const stepFields = this.response.step.fields ?? [];
2857
- const fields = {};
2858
- for (const f of stepFields) {
2859
- const value = formValues[f.name];
2860
- if (value !== void 0) fields[f.name] = value;
2861
- }
2862
- const body = {
2863
- session_token,
2864
- action: action ?? "submit",
2865
- fields,
2866
- ...challengeResponse ? { challenge_response: challengeResponse } : {}
2867
- };
2868
- const { api } = resolveApi(this.project, this.projectAttrs, "<zitadel-login>");
2869
- const wire = await submitStep(api, id, body);
2870
- this.applyResponse(wire);
2871
- emit(this, "zitadel-flow-step", { step: wire.step });
2872
- } catch (error) {
2873
- this.handleTransportError(error);
2874
- } finally {
2875
- this.loading = false;
2876
- }
2877
- }
2878
- handleTransportError(error) {
2879
- const message = error instanceof Error ? error.message : "Unexpected error contacting the Flow API.";
2880
- this.startupError = message;
2881
- console.error("[zitadel-login]", error);
2882
- emit(this, "zitadel-flow-error", { message });
2883
- }
2884
- };
2885
- __decorate([property({ type: String })], ZitadelLogin.prototype, "purpose", null);
2886
- __decorate([property({ attribute: false })], ZitadelLogin.prototype, "project", null);
2887
- __decorate([property({
2888
- type: String,
2889
- attribute: "project-id"
2890
- })], ZitadelLogin.prototype, "projectId", null);
2891
- __decorate([property({
2892
- type: String,
2893
- attribute: "proxy-path"
2894
- })], ZitadelLogin.prototype, "proxyPath", null);
2895
- __decorate([property({ type: String })], ZitadelLogin.prototype, "url", null);
2896
- __decorate([property({
2897
- type: String,
2898
- attribute: "post-sign-in-url"
2899
- })], ZitadelLogin.prototype, "postSignInUrl", null);
2900
- __decorate([property({
2901
- type: String,
2902
- attribute: "resume-flow-id"
2903
- })], ZitadelLogin.prototype, "resumeFlowId", null);
2904
- __decorate([property({ type: String })], ZitadelLogin.prototype, "lang", null);
2905
- __decorate([property({ attribute: false })], ZitadelLogin.prototype, "locales", null);
2906
- __decorate([state()], ZitadelLogin.prototype, "response", null);
2907
- __decorate([state()], ZitadelLogin.prototype, "branding", null);
2908
- __decorate([state()], ZitadelLogin.prototype, "loading", null);
2909
- __decorate([state()], ZitadelLogin.prototype, "startupError", null);
2910
- __decorate([state()], ZitadelLogin.prototype, "formValues", null);
2911
- ZitadelLogin = __decorate([customElement("zitadel-login")], ZitadelLogin);
2912
- function collectInitialValues(step) {
2913
- const values = {};
2914
- if (!step.fields) return values;
2915
- for (const field of step.fields) values[field.name] = typeof field.value === "string" ? field.value : "";
2916
- return values;
2917
- }
2918
- //#endregion
2919
- //#region src/orchestrator/zitadel-logout.ts
2920
- const DISPLAY_COOKIE_NAME = "__nextgen_display";
2921
- let ZitadelLogout = class ZitadelLogout extends LitElement {
2922
- static styles = [baseHostStyles, css`
2923
- :host {
2924
- display: inline-block;
2925
- position: relative;
2926
- }
2927
-
2928
- .trigger {
2929
- all: unset;
2930
- cursor: pointer;
2931
- width: 2.5rem;
2932
- height: 2.5rem;
2933
- border-radius: 9999px;
2934
- background: ${t.color.surface.defaultWhite};
2935
- color: ${t.color.text.buttonDefault};
2936
- font-size: 0.875rem;
2937
- font-weight: 600;
2938
- display: inline-flex;
2939
- align-items: center;
2940
- justify-content: center;
2941
- letter-spacing: 0.02em;
2942
- user-select: none;
2943
- transition: box-shadow ${t.motion.duration.fast} ${t.motion.easing.standard};
2944
- }
2945
- .trigger:focus-visible {
2946
- ${focusVisibleStyles};
2947
- }
2948
- .trigger[aria-expanded="true"] {
2949
- box-shadow: 0 0 0 2px ${t.focus.color};
2950
- }
2951
-
2952
- .dropdown {
2953
- position: absolute;
2954
- top: calc(100% + ${t.spacing["02"]});
2955
- right: 0;
2956
- width: 14rem;
2957
- background: ${t.color.surface.defaultPrimaryGray};
2958
- border: 1px solid ${t.color.border.defaultGray100};
2959
- border-radius: ${t.radius.m};
2960
- box-shadow: 0 12px 32px rgba(0, 0, 0, 0.32);
2961
- z-index: 9999;
2962
- overflow: hidden;
2963
- }
2964
-
2965
- .preview {
2966
- display: flex;
2967
- align-items: center;
2968
- gap: ${t.spacing["03"]};
2969
- padding: ${t.spacing["03"]};
2970
- border-bottom: 1px solid ${t.color.border.defaultGray100};
2971
- }
2972
- .preview-avatar {
2973
- flex-shrink: 0;
2974
- width: 2.5rem;
2975
- height: 2.5rem;
2976
- border-radius: 9999px;
2977
- background: ${t.color.surface.defaultWhite};
2978
- color: ${t.color.text.buttonDefault};
2979
- font-size: 0.875rem;
2980
- font-weight: 600;
2981
- display: inline-flex;
2982
- align-items: center;
2983
- justify-content: center;
2984
- }
2985
- .preview-info {
2986
- min-width: 0;
2987
- flex: 1;
2988
- }
2989
- .preview-name {
2990
- font-size: 0.875rem;
2991
- font-weight: 600;
2992
- color: ${t.color.text.primaryWhite};
2993
- white-space: nowrap;
2994
- overflow: hidden;
2995
- text-overflow: ellipsis;
2996
- }
2997
- .preview-email {
2998
- font-size: 0.75rem;
2999
- color: ${t.color.text.secondaryGray};
3000
- white-space: nowrap;
3001
- overflow: hidden;
3002
- text-overflow: ellipsis;
3003
- margin-top: 2px;
3004
- }
3005
-
3006
- .actions {
3007
- padding: ${t.spacing["02"]};
3008
- }
3009
- .signout-btn {
3010
- all: unset;
3011
- cursor: pointer;
3012
- display: flex;
3013
- align-items: center;
3014
- gap: ${t.spacing["02"]};
3015
- width: 100%;
3016
- padding: ${t.spacing["02"]} ${t.spacing["03"]};
3017
- border-radius: ${t.radius.s};
3018
- color: ${t.color.text.error};
3019
- font-size: 0.875rem;
3020
- font-weight: 500;
3021
- box-sizing: border-box;
3022
- }
3023
- .signout-btn:hover:not([disabled]) {
3024
- background: color-mix(in srgb, ${t.color.text.error} 12%, transparent);
3025
- }
3026
- .signout-btn:focus-visible {
3027
- ${focusVisibleStyles};
3028
- }
3029
- .signout-btn[disabled] {
3030
- cursor: not-allowed;
3031
- opacity: 0.6;
3032
- }
3033
- .signout-btn svg {
3034
- flex-shrink: 0;
3035
- }
3036
-
3037
- .spinner {
3038
- width: 1em;
3039
- height: 1em;
3040
- border-radius: 9999px;
3041
- border: 2px solid currentColor;
3042
- border-top-color: transparent;
3043
- animation: zl-logout-spin 600ms linear infinite;
3044
- }
3045
- @keyframes zl-logout-spin {
3046
- to {
3047
- transform: rotate(360deg);
3048
- }
3049
- }
3050
-
3051
- .error-bar {
3052
- padding: ${t.spacing["02"]} ${t.spacing["03"]};
3053
- font-size: 0.75rem;
3054
- color: ${t.color.text.error};
3055
- background: color-mix(in srgb, ${t.color.text.error} 12%, transparent);
3056
- border-top: 1px solid ${t.color.border.defaultGray100};
3057
- }
3058
- `];
3059
- #_project_accessor_storage;
3060
- get project() {
3061
- return this.#_project_accessor_storage;
3062
- }
3063
- set project(value) {
3064
- this.#_project_accessor_storage = value;
3065
- }
3066
- #_projectId_accessor_storage = "";
3067
- get projectId() {
3068
- return this.#_projectId_accessor_storage;
3069
- }
3070
- set projectId(value) {
3071
- this.#_projectId_accessor_storage = value;
3072
- }
3073
- #_proxyPath_accessor_storage = "";
3074
- get proxyPath() {
3075
- return this.#_proxyPath_accessor_storage;
3076
- }
3077
- set proxyPath(value) {
3078
- this.#_proxyPath_accessor_storage = value;
3079
- }
3080
- #_url_accessor_storage = "";
3081
- get url() {
3082
- return this.#_url_accessor_storage;
3083
- }
3084
- set url(value) {
3085
- this.#_url_accessor_storage = value;
3086
- }
3087
- #_postSignOutUrl_accessor_storage = "";
3088
- get postSignOutUrl() {
3089
- return this.#_postSignOutUrl_accessor_storage;
3090
- }
3091
- set postSignOutUrl(value) {
3092
- this.#_postSignOutUrl_accessor_storage = value;
3093
- }
3094
- #_clientId_accessor_storage = "";
3095
- get clientId() {
3096
- return this.#_clientId_accessor_storage;
3097
- }
3098
- set clientId(value) {
3099
- this.#_clientId_accessor_storage = value;
3100
- }
3101
- #_displayName_accessor_storage = "";
3102
- get displayName() {
3103
- return this.#_displayName_accessor_storage;
3104
- }
3105
- set displayName(value) {
3106
- this.#_displayName_accessor_storage = value;
3107
- }
3108
- #_displayEmail_accessor_storage = "";
3109
- get displayEmail() {
3110
- return this.#_displayEmail_accessor_storage;
3111
- }
3112
- set displayEmail(value) {
3113
- this.#_displayEmail_accessor_storage = value;
3114
- }
3115
- #_open_accessor_storage = false;
3116
- get open() {
3117
- return this.#_open_accessor_storage;
3118
- }
3119
- set open(value) {
3120
- this.#_open_accessor_storage = value;
3121
- }
3122
- #_loading_accessor_storage = false;
3123
- get loading() {
3124
- return this.#_loading_accessor_storage;
3125
- }
3126
- set loading(value) {
3127
- this.#_loading_accessor_storage = value;
3128
- }
3129
- #_errorMessage_accessor_storage = "";
3130
- get errorMessage() {
3131
- return this.#_errorMessage_accessor_storage;
3132
- }
3133
- set errorMessage(value) {
3134
- this.#_errorMessage_accessor_storage = value;
3135
- }
3136
- templateMode = false;
3137
- connectedCallback() {
3138
- super.connectedCallback();
3139
- this.dataset.theme = "dark";
3140
- this.readDisplayCookie();
3141
- const tmpl = this.querySelector("template");
3142
- if (tmpl instanceof HTMLTemplateElement) {
3143
- this.templateMode = true;
3144
- this.renderTemplate(tmpl);
3145
- }
3146
- document.addEventListener("click", this.handleDocumentClick);
3147
- document.addEventListener("keydown", this.handleDocumentKeydown);
3148
- }
3149
- disconnectedCallback() {
3150
- super.disconnectedCallback();
3151
- document.removeEventListener("click", this.handleDocumentClick);
3152
- document.removeEventListener("keydown", this.handleDocumentKeydown);
3153
- }
3154
- updated() {
3155
- const root = this.shadowRoot;
3156
- if (root && !this.templateMode) applyBaseTokens(root);
3157
- }
3158
- /**
3159
- * Decodes the `__nextgen_display` cookie (base64-encoded JSON, set by the
3160
- * auth backend on sign-in) and populates `displayName` / `displayEmail`.
3161
- * A missing or malformed cookie is intentionally non-fatal — the dropdown
3162
- * still renders, just with empty values.
3163
- */
3164
- readDisplayCookie() {
3165
- if (typeof document === "undefined") return;
3166
- const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${DISPLAY_COOKIE_NAME}=([^;]+)`));
3167
- if (!match || !match[1]) return;
3168
- try {
3169
- const data = JSON.parse(atob(match[1]));
3170
- this.displayName = typeof data.name === "string" ? data.name : "";
3171
- this.displayEmail = typeof data.email === "string" ? data.email : "";
3172
- } catch {}
3173
- }
3174
- get initial() {
3175
- const source = this.displayName || this.displayEmail;
3176
- return source ? source.charAt(0).toUpperCase() : "?";
3177
- }
3178
- /**
3179
- * Clones a consumer-supplied `<template>` into the light DOM, fills the
3180
- * `{{name}}`, `{{email}}`, and `{{initial}}` tokens via a TreeWalker, and
3181
- * wires every element with `data-action="logout"` to trigger sign-out.
3182
- * Light-DOM mounting is deliberate so the consumer's existing CSS applies.
3183
- */
3184
- renderTemplate(tmpl) {
3185
- const clone = tmpl.content.cloneNode(true);
3186
- fillTemplateTokens(clone, this.displayName, this.displayEmail, this.initial);
3187
- const container = document.createElement("span");
3188
- container.appendChild(clone);
3189
- this.appendChild(container);
3190
- container.querySelectorAll("[data-action=\"logout\"]").forEach((el) => {
3191
- el.addEventListener("click", (event) => {
3192
- event.preventDefault();
3193
- this.doLogout();
3194
- });
3195
- });
3196
- }
3197
- handleDocumentClick = (event) => {
3198
- if (!this.open) return;
3199
- if (event.composedPath().includes(this)) return;
3200
- this.open = false;
3201
- };
3202
- handleDocumentKeydown = (event) => {
3203
- if (!this.open) return;
3204
- if (event.key !== "Escape") return;
3205
- this.open = false;
3206
- this.shadowRoot?.querySelector(".trigger")?.focus();
3207
- };
3208
- toggleOpen() {
3209
- this.open = !this.open;
3210
- this.errorMessage = "";
3211
- }
3212
- /**
3213
- * Calls `DELETE /sessions/me` (`revokeMySession`) with `credentials: "include"`.
3214
- * The server validates the `__nextgen_session` cookie, deletes the session, and
3215
- * clears the cookie via `Set-Cookie: Max-Age=0`. On success this element fires
3216
- * `zitadel-signout` and optionally navigates to `postSignOutUrl`.
3217
- */
3218
- /** Declarative config read from this element's attributes. */
3219
- get projectAttrs() {
3220
- return {
3221
- projectId: this.projectId,
3222
- proxyPath: this.proxyPath,
3223
- url: this.url
3224
- };
3225
- }
3226
- async doLogout() {
3227
- this.loading = true;
3228
- this.errorMessage = "";
3229
- try {
3230
- const { api } = resolveApi(this.project, this.projectAttrs, "<zitadel-logout>");
3231
- await api.revokeMySession({ credentials: "include" });
3232
- } catch (error) {
3233
- const message = error instanceof Error ? error.message : "";
3234
- this.errorMessage = message || "Sign-out failed. Please try again.";
3235
- this.loading = false;
3236
- return;
3237
- }
3238
- this.open = false;
3239
- this.loading = false;
3240
- emit(this, "zitadel-signout", {
3241
- name: this.displayName,
3242
- email: this.displayEmail
3243
- });
3244
- if (this.postSignOutUrl && typeof window !== "undefined") window.location.href = this.postSignOutUrl;
3245
- }
3246
- /**
3247
- * Returns the absolute URL the end-session request will hit. Useful for
3248
- * test assertions and for consumers that prefer to navigate the browser
3249
- * directly (instead of fetching) so the OIDC session-end redirect is
3250
- * driven by the user agent.
3251
- */
3252
- getEndSessionUrl() {
3253
- const params = {
3254
- ...this.clientId ? { client_id: this.clientId } : {},
3255
- ...this.postSignOutUrl ? { post_logout_redirect_uri: this.postSignOutUrl } : {}
3256
- };
3257
- const { api } = resolveApi(this.project, this.projectAttrs, "<zitadel-logout>");
3258
- return api.getEndSessionUrl(params);
3259
- }
3260
- handleSignOutClick(event) {
3261
- event.preventDefault();
3262
- this.doLogout();
3263
- }
3264
- render() {
3265
- if (this.templateMode) return nothing;
3266
- return html`
3267
- <button
3268
- class="trigger"
3269
- type="button"
3270
- aria-label=${this.open ? "Close user menu" : "Open user menu"}
3271
- aria-expanded=${this.open ? "true" : "false"}
3272
- aria-haspopup="dialog"
3273
- @click=${this.toggleOpen}
3274
- >
3275
- ${this.initial}
3276
- </button>
3277
-
3278
- ${this.open ? html`
3279
- <div class="dropdown" role="dialog" aria-label="User menu">
3280
- <div class="preview">
3281
- <div class="preview-avatar" aria-hidden="true">${this.initial}</div>
3282
- <div class="preview-info">
3283
- <div class="preview-name">${this.displayName || this.displayEmail}</div>
3284
- ${this.displayName ? html`<div class="preview-email">${this.displayEmail}</div>` : nothing}
3285
- </div>
3286
- </div>
3287
-
3288
- <div class="actions">
3289
- <button
3290
- class="signout-btn"
3291
- type="button"
3292
- ?disabled=${this.loading}
3293
- @click=${this.handleSignOutClick}
3294
- >
3295
- ${this.loading ? html`<span class="spinner" aria-hidden="true"></span>` : html`
3296
- <svg
3297
- width="14"
3298
- height="14"
3299
- viewBox="0 0 24 24"
3300
- fill="none"
3301
- stroke="currentColor"
3302
- stroke-width="2"
3303
- stroke-linecap="round"
3304
- stroke-linejoin="round"
3305
- aria-hidden="true"
3306
- >
3307
- <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
3308
- <polyline points="16 17 21 12 16 7" />
3309
- <line x1="21" y1="12" x2="9" y2="12" />
3310
- </svg>
3311
- `}
3312
- <span>${this.loading ? "Signing out…" : "Sign out"}</span>
3313
- </button>
3314
- </div>
3315
-
3316
- ${this.errorMessage ? html`<div class="error-bar" role="alert">${this.errorMessage}</div>` : nothing}
3317
- </div>
3318
- ` : nothing}
3319
- `;
3320
- }
3321
- };
3322
- __decorate([property({ attribute: false })], ZitadelLogout.prototype, "project", null);
3323
- __decorate([property({
3324
- type: String,
3325
- attribute: "project-id"
3326
- })], ZitadelLogout.prototype, "projectId", null);
3327
- __decorate([property({
3328
- type: String,
3329
- attribute: "proxy-path"
3330
- })], ZitadelLogout.prototype, "proxyPath", null);
3331
- __decorate([property({ type: String })], ZitadelLogout.prototype, "url", null);
3332
- __decorate([property({
3333
- type: String,
3334
- attribute: "post-sign-out-url"
3335
- })], ZitadelLogout.prototype, "postSignOutUrl", null);
3336
- __decorate([property({
3337
- type: String,
3338
- attribute: "client-id"
3339
- })], ZitadelLogout.prototype, "clientId", null);
3340
- __decorate([state()], ZitadelLogout.prototype, "displayName", null);
3341
- __decorate([state()], ZitadelLogout.prototype, "displayEmail", null);
3342
- __decorate([state()], ZitadelLogout.prototype, "open", null);
3343
- __decorate([state()], ZitadelLogout.prototype, "loading", null);
3344
- __decorate([state()], ZitadelLogout.prototype, "errorMessage", null);
3345
- ZitadelLogout = __decorate([customElement("zitadel-logout")], ZitadelLogout);
3346
- /**
3347
- * Substitutes `{{name}}`, `{{email}}`, and `{{initial}}` placeholders inside
3348
- * a fragment's text nodes. Walking text nodes (rather than running a regex
3349
- * over `outerHTML`) keeps attributes and structural markup untouched.
3350
- */
3351
- function fillTemplateTokens(fragment, name, email, initial) {
3352
- const walker = document.createTreeWalker(fragment, NodeFilter.SHOW_TEXT);
3353
- let node = walker.nextNode();
3354
- while (node) {
3355
- if (node.textContent) node.textContent = node.textContent.replace(/\{\{name\}\}/g, name).replace(/\{\{email\}\}/g, email).replace(/\{\{initial\}\}/g, initial);
3356
- node = walker.nextNode();
3357
- }
3358
- }
3359
- //#endregion
3360
- export { buildBrandingStylesheet as _, createSanitiser as a, startFlow as b, de as c, MANDATORY_GATES_MARKER as d, mandatoryGatesMarkerComment as f, applyBrandingTokens as g, validateBranding as h, layout_chrome_default as i, en as l, applyFontUrl as m, ZitadelLogin as n, builtinLocales as o, patchMandatoryGates as p, ThemeController as r, it as s, ZitadelLogout as t, TEMPLATE_NAMES as u, resolveTheme as v, submitStep as x, getCurrentStep as y };
3361
-
3362
- //# sourceMappingURL=orchestrator-CW9zixuw.mjs.map