@zitadel/components 0.0.0 → 0.1.0-alpha.2

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 (80) hide show
  1. package/README.md +41 -36
  2. package/dist/atoms/index.d.mts +1 -1
  3. package/dist/atoms/index.mjs +1 -1
  4. package/dist/atoms/zl-alert.d.ts.map +1 -1
  5. package/dist/atoms/zl-button.d.ts +7 -1
  6. package/dist/atoms/zl-button.d.ts.map +1 -1
  7. package/dist/atoms/zl-card.d.ts +7 -0
  8. package/dist/atoms/zl-card.d.ts.map +1 -1
  9. package/dist/atoms/zl-field.d.ts +14 -1
  10. package/dist/atoms/zl-field.d.ts.map +1 -1
  11. package/dist/atoms/zl-icon.d.ts.map +1 -1
  12. package/dist/atoms/zl-page-shell.d.ts +6 -0
  13. package/dist/atoms/zl-page-shell.d.ts.map +1 -1
  14. package/dist/atoms/zl-passkey.d.ts +6 -1
  15. package/dist/atoms/zl-passkey.d.ts.map +1 -1
  16. package/dist/atoms/zl-pill.d.ts +0 -1
  17. package/dist/atoms/zl-pill.d.ts.map +1 -1
  18. package/dist/{atoms-CmOTltTH.mjs → atoms-eOxuoGDq.mjs} +135 -78
  19. package/dist/atoms-eOxuoGDq.mjs.map +1 -0
  20. package/dist/default-DP74t2io.mjs +6 -0
  21. package/dist/default-DP74t2io.mjs.map +1 -0
  22. package/dist/{index-bZEvwHtq.d.mts → index-DjBsBWdg.d.mts} +99 -98
  23. package/dist/index-DjBsBWdg.d.mts.map +1 -0
  24. package/dist/{index-DIUFZjXw.d.mts → index-DmtnqUIY.d.mts} +1237 -574
  25. package/dist/index-DmtnqUIY.d.mts.map +1 -0
  26. package/dist/{index-DaOSCm0T.d.mts → index-EJengPPu.d.mts} +1 -1
  27. package/dist/{index-DaOSCm0T.d.mts.map → index-EJengPPu.d.mts.map} +1 -1
  28. package/dist/index.d.mts +6 -5
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.mjs +4 -3
  33. package/dist/internal/emit.d.ts +12 -0
  34. package/dist/internal/emit.d.ts.map +1 -0
  35. package/dist/internal/escape-html.d.ts +10 -0
  36. package/dist/internal/escape-html.d.ts.map +1 -0
  37. package/dist/{manifest-DCC2FxCE.d.mts → manifest-DN5Al_Ur.d.mts} +1 -1
  38. package/dist/{manifest-DCC2FxCE.d.mts.map → manifest-DN5Al_Ur.d.mts.map} +1 -1
  39. package/dist/manifests.d.mts +1 -1
  40. package/dist/manifests.mjs +1 -1
  41. package/dist/orchestrator/index.d.mts +3 -2
  42. package/dist/orchestrator/index.d.ts +3 -5
  43. package/dist/orchestrator/index.d.ts.map +1 -1
  44. package/dist/orchestrator/index.mjs +3 -2
  45. package/dist/orchestrator/liquid.d.ts +3 -0
  46. package/dist/orchestrator/liquid.d.ts.map +1 -1
  47. package/dist/orchestrator/locales/de.d.ts +7 -0
  48. package/dist/orchestrator/locales/de.d.ts.map +1 -1
  49. package/dist/orchestrator/locales/en.d.ts +6 -0
  50. package/dist/orchestrator/locales/en.d.ts.map +1 -1
  51. package/dist/orchestrator/locales/index.d.ts +1 -0
  52. package/dist/orchestrator/locales/index.d.ts.map +1 -1
  53. package/dist/orchestrator/locales/it.d.ts +14 -0
  54. package/dist/orchestrator/locales/it.d.ts.map +1 -0
  55. package/dist/orchestrator/resolve-api.d.ts +37 -0
  56. package/dist/orchestrator/resolve-api.d.ts.map +1 -0
  57. package/dist/orchestrator/zitadel-login.d.ts +30 -2
  58. package/dist/orchestrator/zitadel-login.d.ts.map +1 -1
  59. package/dist/orchestrator/zitadel-logout.d.ts +33 -5
  60. package/dist/orchestrator/zitadel-logout.d.ts.map +1 -1
  61. package/dist/{orchestrator-CNKo2WQG.mjs → orchestrator-Dds72XrU.mjs} +587 -619
  62. package/dist/orchestrator-Dds72XrU.mjs.map +1 -0
  63. package/dist/standalone.mjs +12002 -0
  64. package/dist/tokens/index.d.mts +1 -1
  65. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  66. package/package.json +9 -3
  67. package/dist/atoms-CmOTltTH.mjs.map +0 -1
  68. package/dist/index-7FDHEpv5.d.mts +0 -996
  69. package/dist/index-7FDHEpv5.d.mts.map +0 -1
  70. package/dist/index-DIUFZjXw.d.mts.map +0 -1
  71. package/dist/index-bZEvwHtq.d.mts.map +0 -1
  72. package/dist/orchestrator/templates/auth-form.liquid.d.ts +0 -14
  73. package/dist/orchestrator/templates/auth-form.liquid.d.ts.map +0 -1
  74. package/dist/orchestrator/templates/default.liquid.d.ts +0 -23
  75. package/dist/orchestrator/templates/default.liquid.d.ts.map +0 -1
  76. package/dist/orchestrator/templates/passkey-upsell.liquid.d.ts +0 -15
  77. package/dist/orchestrator/templates/passkey-upsell.liquid.d.ts.map +0 -1
  78. package/dist/orchestrator/templates/signed-in.liquid.d.ts +0 -20
  79. package/dist/orchestrator/templates/signed-in.liquid.d.ts.map +0 -1
  80. package/dist/orchestrator-CNKo2WQG.mjs.map +0 -1
@@ -1,20 +1,12 @@
1
- import { _ as __decorate, b as t, v as focusVisibleStyles, y as baseHostStyles } from "./atoms-CmOTltTH.mjs";
1
+ import { _ as __decorate, b as t, v as focusVisibleStyles, x as emit, y as baseHostStyles } from "./atoms-eOxuoGDq.mjs";
2
2
  import { r as tokensCss } from "./tokens-TXmurodq.mjs";
3
+ import { t as default_default } from "./default-DP74t2io.mjs";
3
4
  import { manifestRegistry } from "./manifests.mjs";
4
5
  import { LitElement, css, html, nothing } from "lit";
5
6
  import { customElement, property, state } from "lit/decorators.js";
6
7
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
7
8
  import { Liquid } from "liquidjs";
8
9
  import DOMPurify from "dompurify";
9
- //#region ../api/dist/runtime/base-url.mjs
10
- let proxyPath = "";
11
- function getProxyPath() {
12
- return proxyPath;
13
- }
14
- function setProxyPath(path) {
15
- proxyPath = path;
16
- }
17
- //#endregion
18
10
  //#region ../api/dist/runtime/auth.mjs
19
11
  /**
20
12
  * Module-global bearer token used by the orval-generated client's
@@ -34,18 +26,6 @@ function setApiAuthToken(token) {
34
26
  apiAuthToken = token;
35
27
  }
36
28
  //#endregion
37
- //#region ../api/dist/chunk-CfYAbeIz.mjs
38
- var __defProp = Object.defineProperty;
39
- var __exportAll = (all, no_symbols) => {
40
- let target = {};
41
- for (var name in all) __defProp(target, name, {
42
- get: all[name],
43
- enumerable: true
44
- });
45
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
46
- return target;
47
- };
48
- //#endregion
49
29
  //#region ../api/dist/runtime/fetch.mjs
50
30
  /**
51
31
  * Framework-neutral failure type the orval-generated client throws on
@@ -118,6 +98,57 @@ function safeJsonParse(text) {
118
98
  }
119
99
  }
120
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
121
152
  //#region ../api/dist/generated/endpoints/zitadelNextGen.mjs
122
153
  /**
123
154
  * Generated by orval v8.10.0 🍺
@@ -165,6 +196,7 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
165
196
  getGetKeysUrl: () => getGetKeysUrl,
166
197
  getGetLiveUrl: () => getGetLiveUrl,
167
198
  getGetMySessionUrl: () => getGetMySessionUrl,
199
+ getGetMyUserUrl: () => getGetMyUserUrl,
168
200
  getGetOpenIDConfigurationUrl: () => getGetOpenIDConfigurationUrl,
169
201
  getGetProjectUrl: () => getGetProjectUrl,
170
202
  getGetReadyUrl: () => getGetReadyUrl,
@@ -183,6 +215,7 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
183
215
  getListUsersUrl: () => getListUsersUrl,
184
216
  getLive: () => getLive,
185
217
  getMySession: () => getMySession,
218
+ getMyUser: () => getMyUser,
186
219
  getOpenIDConfiguration: () => getOpenIDConfiguration,
187
220
  getProject: () => getProject,
188
221
  getReady: () => getReady,
@@ -191,6 +224,7 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
191
224
  getRevokeTokenUrl: () => getRevokeTokenUrl,
192
225
  getSchemaById: () => getSchemaById,
193
226
  getSession: () => getSession,
227
+ getSetUserPasswordUrl: () => getSetUserPasswordUrl,
194
228
  getSubmitFlowEventUrl: () => getSubmitFlowEventUrl,
195
229
  getSubmitFlowStepUrl: () => getSubmitFlowStepUrl,
196
230
  getTeam: () => getTeam,
@@ -207,6 +241,7 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
207
241
  revokeMySession: () => revokeMySession,
208
242
  revokeSession: () => revokeSession,
209
243
  revokeToken: () => revokeToken,
244
+ setUserPassword: () => setUserPassword,
210
245
  submitFlowEvent: () => submitFlowEvent,
211
246
  submitFlowStep: () => submitFlowStep,
212
247
  updateFlowDefinition: () => updateFlowDefinition,
@@ -411,7 +446,7 @@ const getCreateUserUrl = (params) => {
411
446
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
412
447
  });
413
448
  const stringifiedParams = normalizedParams.toString();
414
- return stringifiedParams.length > 0 ? `${getProxyPath()}/user?${stringifiedParams}` : `${getProxyPath()}/user`;
449
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
415
450
  };
416
451
  /**
417
452
  * @summary Create user
@@ -433,7 +468,7 @@ const getListUsersUrl = (params) => {
433
468
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
434
469
  });
435
470
  const stringifiedParams = normalizedParams.toString();
436
- return stringifiedParams.length > 0 ? `${getProxyPath()}/user?${stringifiedParams}` : `${getProxyPath()}/user`;
471
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
437
472
  };
438
473
  /**
439
474
  * @summary List users
@@ -450,7 +485,7 @@ const getGetUserByIDUrl = (userId, params) => {
450
485
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
451
486
  });
452
487
  const stringifiedParams = normalizedParams.toString();
453
- return stringifiedParams.length > 0 ? `${getProxyPath()}/user/${userId}?${stringifiedParams}` : `${getProxyPath()}/user/${userId}`;
488
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}?${stringifiedParams}` : `${getProxyPath()}/users/${userId}`;
454
489
  };
455
490
  /**
456
491
  * @summary Get user by ID
@@ -461,6 +496,40 @@ const getUserByID = async (userId, params, options) => {
461
496
  method: "GET"
462
497
  });
463
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
+ };
464
533
  const getCreateFlowUrl = () => {
465
534
  return `${getProxyPath()}/flow`;
466
535
  };
@@ -985,28 +1054,38 @@ const listFlowDefinitions = async (params, options) => {
985
1054
  method: "GET"
986
1055
  });
987
1056
  };
988
- const getGetFlowDefinitionUrl = (id) => {
989
- return `${getProxyPath()}/flow_definitions/${id}`;
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}`;
990
1064
  };
991
1065
  /**
992
1066
  * Get a flow definition by id
993
1067
  * @summary Get a flow definition by id
994
1068
  */
995
- const getFlowDefinition = async (id, options) => {
996
- return customFetch(getGetFlowDefinitionUrl(id), {
1069
+ const getFlowDefinition = async (id, params, options) => {
1070
+ return customFetch(getGetFlowDefinitionUrl(id, params), {
997
1071
  ...options,
998
1072
  method: "GET"
999
1073
  });
1000
1074
  };
1001
- const getUpdateFlowDefinitionUrl = (id) => {
1002
- return `${getProxyPath()}/flow_definitions/${id}`;
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}`;
1003
1082
  };
1004
1083
  /**
1005
1084
  * Update a flow definition by id
1006
1085
  * @summary Update a flow definition by id
1007
1086
  */
1008
- const updateFlowDefinition = async (id, updateFlowDefinitionBody, options) => {
1009
- return customFetch(getUpdateFlowDefinitionUrl(id), {
1087
+ const updateFlowDefinition = async (id, updateFlowDefinitionBody, params, options) => {
1088
+ return customFetch(getUpdateFlowDefinitionUrl(id, params), {
1010
1089
  ...options,
1011
1090
  method: "PATCH",
1012
1091
  headers: {
@@ -1016,15 +1095,20 @@ const updateFlowDefinition = async (id, updateFlowDefinitionBody, options) => {
1016
1095
  body: JSON.stringify(updateFlowDefinitionBody)
1017
1096
  });
1018
1097
  };
1019
- const getDeleteFlowDefinitionUrl = (id) => {
1020
- return `${getProxyPath()}/flow_definitions/${id}`;
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}`;
1021
1105
  };
1022
1106
  /**
1023
1107
  * Delete a flow definition by id
1024
1108
  * @summary Delete a flow definition by id
1025
1109
  */
1026
- const deleteFlowDefinition = async (id, options) => {
1027
- return customFetch(getDeleteFlowDefinitionUrl(id), {
1110
+ const deleteFlowDefinition = async (id, params, options) => {
1111
+ return customFetch(getDeleteFlowDefinitionUrl(id, params), {
1028
1112
  ...options,
1029
1113
  method: "DELETE"
1030
1114
  });
@@ -1122,10 +1206,29 @@ function createApi(apiBase) {
1122
1206
  }
1123
1207
  //#endregion
1124
1208
  //#region ../api/dist/runtime/config.mjs
1125
- let currentProject = null;
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
+ }
1126
1226
  /**
1127
1227
  * Per-project API client cache. Ensures `getApi(project)` returns the
1128
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.
1129
1232
  */
1130
1233
  const apiCache = /* @__PURE__ */ new WeakMap();
1131
1234
  /**
@@ -1150,37 +1253,82 @@ function getApi(project) {
1150
1253
  * has not been called yet.
1151
1254
  */
1152
1255
  function getZitadelConfig() {
1153
- return currentProject;
1256
+ return readSlot();
1154
1257
  }
1155
1258
  //#endregion
1156
- //#region src/orchestrator/api-client.ts
1157
- const apiRequestInit = { credentials: "include" };
1158
- async function startFlow(api, input) {
1159
- return api.createFlow(input, apiRequestInit);
1160
- }
1161
- async function submitStep(api, id, body) {
1162
- try {
1163
- return await api.submitFlowStep(id, body, apiRequestInit);
1164
- } catch (error) {
1165
- if (error instanceof ApiError && error.status === 400 && isFlowResponse(error.body)) return error.body;
1166
- throw error;
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);
1167
1300
  }
1168
- }
1169
- function isFlowResponse(body) {
1170
- return typeof body === "object" && body !== null && "step" in body;
1171
- }
1172
- async function getCurrentStep(api, id) {
1173
- return api.getFlowStep(id, apiRequestInit);
1301
+ return project;
1174
1302
  }
1175
1303
  /**
1176
- * Exchange a terminal-flow `handoff_token` for an authenticated session.
1177
- * The server sets the `__nextgen_session` HttpOnly cookie on success.
1304
+ * Resolves the effective SDK project handle and its typed API client for an
1305
+ * orchestrator element. Precedence, highest first:
1178
1306
  *
1179
- * Uses the generated `exchangeHandoff` client which handles the
1180
- * `project_id` query parameter via {@link ExchangeHandoffParams}.
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>`).
1181
1324
  */
1182
- async function exchangeSession(api, body, params) {
1183
- return api.exchangeHandoff(body, params, apiRequestInit);
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
+ };
1184
1332
  }
1185
1333
  //#endregion
1186
1334
  //#region src/orchestrator/branding-to-tokens.ts
@@ -1450,6 +1598,19 @@ function applyFontUrl(shadowRoot, fontUrl) {
1450
1598
  shadowRoot.prepend(link);
1451
1599
  }
1452
1600
  //#endregion
1601
+ //#region src/internal/escape-html.ts
1602
+ /**
1603
+ * Escapes the five HTML-significant characters so a string is safe to
1604
+ * interpolate into either element text or a double-quoted attribute value.
1605
+ *
1606
+ * Used for the orchestrator-owned attribution markup, which is appended after
1607
+ * the Liquid output has already been sanitised — so it can't rely on the
1608
+ * DOMPurify pass and must escape its own (tenant-supplied) values.
1609
+ */
1610
+ function escapeHtml(value) {
1611
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1612
+ }
1613
+ //#endregion
1453
1614
  //#region src/orchestrator/mandatory-gates.ts
1454
1615
  const MANDATORY_GATES_MARKER = "ZL_MANDATORY_GATES";
1455
1616
  const mandatoryGatesMarkerComment = `<!--${MANDATORY_GATES_MARKER}-->`;
@@ -1519,458 +1680,6 @@ function lookup(locale, key) {
1519
1680
  return locale[key] ?? key;
1520
1681
  }
1521
1682
  //#endregion
1522
- //#region src/orchestrator/templates/auth-form.liquid.ts
1523
- /**
1524
- * `auth-form` partial — Sign in and Sign up screens (Figma screens file).
1525
- *
1526
- * Sign up 2xl frame: `6593:141741` (card `6593:141743`, attribution `6593:141767`).
1527
- * Sign in 2xl frame: `6593:141983`, card `6593:141985`, stack `6593:141989`
1528
- * (email + password + forgot link + CTAs on one step).
1529
- * Sign-in errors: wrong credentials `6602:180268`, server alert `6594:125237`.
1530
- *
1531
- * Figma `Field+CTAs` stacks fields + CTAs in one column at 16px (`spacing-03`).
1532
- * Primary/secondary are 48px buttons; register / sign_in are body copy with a
1533
- * purple text link (24px row) — not `<zl-button hierarchy="text">`.
1534
- */
1535
- const authFormTemplate = String.raw`
1536
- <zl-page-shell data-zl-template-root>
1537
- {% if branding.logo_url %}
1538
- <img slot="header" class="zl-card-logo" src="{{ branding.logo_url }}" alt="" />
1539
- {% endif %}
1540
- <zl-card>
1541
- <h1 slot="header" class="zl-card-title">{{ step.texts.title_key | t: identity.display_name }}</h1>
1542
- {% if step.texts.description_key %}
1543
- <p slot="header" class="zl-card-subtitle">{{ step.texts.description_key | t: identity.display_name }}</p>
1544
- {% endif %}
1545
-
1546
- {% if errors and errors.size > 0 %}
1547
- {% for err in errors %}
1548
- {% if err | formLevelError %}
1549
- {% assign alert_heading = err.text_key | alertHeading %}
1550
- {% assign alert_body = err.text_key | alertBody %}
1551
- <zl-alert severity="error"{% if alert_heading != "" %} heading="{{ alert_heading | t }}"{% endif %}>
1552
- {% if alert_body != "" %}{{ alert_body | t }}{% elsif err.text_key %}{{ err.text_key | t }}{% else %}{{ err.message }}{% endif %}
1553
- </zl-alert>
1554
- {% endif %}
1555
- {% endfor %}
1556
- {% endif %}
1557
-
1558
- {% for entry in fields %}
1559
- {% assign field_placeholder = entry[1].text_key | fieldPlaceholder %}
1560
- {% assign field_help = entry[1].text_key | fieldHelp %}
1561
- {% assign field_err = entry[0] | fieldError: errors %}
1562
- {% assign autocomplete_val = '' %}
1563
- {% if entry[0] == 'email' %}
1564
- {% assign autocomplete_val = 'email' %}
1565
- {% elsif entry[1].type == 'password' and step.name == 'register' %}
1566
- {% assign autocomplete_val = 'new-password' %}
1567
- {% elsif entry[1].type == 'password' %}
1568
- {% assign autocomplete_val = 'current-password' %}
1569
- {% endif %}
1570
- <zl-field
1571
- name="{{ entry[0] }}"
1572
- label="{{ entry[1].text_key | t }}"
1573
- type="{{ entry[1].type }}"
1574
- value="{{ entry[1].value }}"
1575
- {% if autocomplete_val != '' %}autocomplete="{{ autocomplete_val }}"{% endif %}
1576
- {% if entry[1].required %}required{% endif %}
1577
- {% if field_placeholder != "" %}placeholder="{{ field_placeholder }}"{% endif %}
1578
- {% if field_err != "" %}invalid error="{{ field_err }}"{% endif %}
1579
- >
1580
- {% if field_help != "" %}
1581
- <span slot="help">{{ field_help }}</span>
1582
- {% endif %}
1583
- </zl-field>
1584
- {% endfor %}
1585
-
1586
- {% if fields.email and fields.password and step.name == 'identifier' %}
1587
- <p class="zl-card-forgot">
1588
- <a href="#" class="zl-card-forgot__link" data-action="recover">{{ 'action.forgot_password' | t }}</a>
1589
- </p>
1590
- {% endif %}
1591
-
1592
- {% for entry in actions %}
1593
- {% if entry[1].primary %}
1594
- <zl-button
1595
- hierarchy="primary"
1596
- size="medium"
1597
- type="submit"
1598
- block
1599
- action="{{ entry[0] }}"
1600
- label="{{ entry[1].text_key | t }}"
1601
- {% if loading %}loading{% endif %}
1602
- ></zl-button>
1603
- {% endif %}
1604
- {% endfor %}
1605
-
1606
- {% if actions.passkey %}
1607
- <zl-button
1608
- hierarchy="secondary"
1609
- size="medium"
1610
- block
1611
- action="passkey"
1612
- label="{{ actions.passkey.text_key | t }}"
1613
- ></zl-button>
1614
- {% endif %}
1615
-
1616
- {% if actions.register %}
1617
- <p class="zl-card-nav">
1618
- {{ 'identifier.action.register.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="register">{{ 'identifier.action.register.link' | t }}</a>
1619
- </p>
1620
- {% endif %}
1621
-
1622
- {% if actions.sign_in %}
1623
- <p class="zl-card-nav">
1624
- {{ 'register.action.sign_in.lead' | t }}<a href="#" class="zl-card-nav__link" data-action="sign_in">{{ 'register.action.sign_in.link' | t }}</a>
1625
- </p>
1626
- {% endif %}
1627
-
1628
- {% if challenge and challenge.method == "passkey" %}
1629
- <zl-passkey
1630
- ceremony="{{ challenge.options.ceremony | default: 'authenticate' }}"
1631
- challenge-id="{{ challenge.challenge_id }}"
1632
- options='{{ challenge.options | json }}'
1633
- ></zl-passkey>
1634
- {% endif %}
1635
-
1636
- {% mandatory_gates %}
1637
- </zl-card>
1638
- </zl-page-shell>
1639
- `;
1640
- //#endregion
1641
- //#region src/orchestrator/templates/default.liquid.ts
1642
- /**
1643
- * Bundled `default.liquid` master template.
1644
- *
1645
- * Renders the four target Figma auth screens (Sign in, Sign up, Passkey
1646
- * upsell, Signed in) by including the appropriate partial based on
1647
- * `step.name`. Tenant-specific layouts (`branding.layout`) replace the
1648
- * outer chrome only — the per-step partials stay shared so atom output
1649
- * tracks the design system without each tenant re-implementing it.
1650
- *
1651
- * Spec: docs/design/branding/templates.md.
1652
- */
1653
- const defaultTemplate = String.raw`
1654
- {%- if step.name == 'signed-in' or step.complete == 'show' -%}
1655
- {%- include 'signed-in' -%}
1656
- {%- elsif step.name == 'passkey' or step.name == 'passkey-upsell' or step.name == 'passkey-setup' or step.name == 'passkey-login' -%}
1657
- {%- include 'passkey-upsell' -%}
1658
- {%- else -%}
1659
- {%- include 'auth-form' -%}
1660
- {%- endif -%}
1661
- `;
1662
- /**
1663
- * Layout chrome injected as an `adoptedStyleSheet` on the orchestrator's
1664
- * shadow root. Provides the page background and the attribution-footer
1665
- * positioning that lives *outside* any template — the rest of the visual
1666
- * surface is owned by the `<zl-page-shell>` / `<zl-card>` atoms the
1667
- * templates render. Token references resolve against the
1668
- * `@zitadel/design-tokens` CSS variables that are present on
1669
- * `:host` via the branding stylesheet.
1670
- */
1671
- const layoutChromeCss = `
1672
- :host {
1673
- display: block;
1674
- min-height: 100%;
1675
- background: var(--zl-color-surface-default-black, #0f0f11);
1676
- color: var(--zl-color-text-primary-white, #f4f4f6);
1677
- font-family: var(--zl-font-family-sans, system-ui, sans-serif);
1678
- }
1679
- .zl-mount {
1680
- display: flex;
1681
- flex-direction: column;
1682
- min-height: 100vh;
1683
- background: inherit;
1684
- color: inherit;
1685
- }
1686
- .zl-mount > zl-page-shell,
1687
- .zl-mount > [data-zl-template-root] {
1688
- flex: 1 1 auto;
1689
- }
1690
- .zl-attribution {
1691
- display: flex;
1692
- justify-content: center;
1693
- align-items: center;
1694
- background: transparent;
1695
- }
1696
- .zl-attribution[hidden] {
1697
- display: none;
1698
- }
1699
- /* Card header typography — Figma "Desktop/Headings/Small" + "Body Small
1700
- Regular" pattern shared by sign-in (6593:141740), passkey upsell
1701
- (6594:89142 / 6594:12796), and signed-in (6596:132845). Templates
1702
- slot raw <h1>/<p> elements into <zl-card>, which keeps them in the
1703
- light DOM where the orchestrator's stylesheet can reach them — the
1704
- atom intentionally doesn't dictate typography. */
1705
- .zl-card-title {
1706
- margin: 0;
1707
- font-family: var(--zl-font-family-heading, var(--zl-font-family-sans, system-ui, sans-serif));
1708
- font-size: 2rem;
1709
- font-weight: 400;
1710
- line-height: 2.5rem;
1711
- letter-spacing: -0.02em;
1712
- color: var(--zl-color-text-primary-white, #f4f4f6);
1713
- text-align: left;
1714
- }
1715
- .zl-card-subtitle {
1716
- margin: 0;
1717
- font-family: var(--zl-font-family-sans, system-ui, sans-serif);
1718
- font-size: 0.875rem;
1719
- line-height: 1.25rem;
1720
- font-weight: 400;
1721
- color: var(--zl-color-text-primary-white, #f4f4f6);
1722
- }
1723
- /* Figma 6593:141989 / 6594:12882 — left-aligned in Field+CTAs (x=0, items-start). */
1724
- .zl-card-nav {
1725
- margin: 0;
1726
- font-family: var(--zl-font-family-sans, system-ui, sans-serif);
1727
- font-size: 1rem;
1728
- line-height: 1.5rem;
1729
- font-weight: 400;
1730
- color: var(--zl-color-text-primary-white, #f4f4f6);
1731
- text-align: left;
1732
- }
1733
- .zl-card-nav__link {
1734
- display: inline;
1735
- margin: 0;
1736
- padding: 0;
1737
- border: none;
1738
- background: transparent;
1739
- font: inherit;
1740
- line-height: inherit;
1741
- color: var(--zl-color-icon-default-purple, #bba5e4);
1742
- text-decoration: none;
1743
- cursor: pointer;
1744
- }
1745
- .zl-card-nav__link:hover {
1746
- text-decoration: underline;
1747
- }
1748
- .zl-card-nav__link:focus-visible {
1749
- outline: var(--zl-focus-width, 2px) solid var(--zl-focus-color, #f4f4f6);
1750
- outline-offset: var(--zl-focus-offset, 2px);
1751
- }
1752
- /* Figma 6593:141989 — forgot password row (left-aligned, before CTAs). */
1753
- .zl-card-forgot {
1754
- margin: 0;
1755
- text-align: left;
1756
- }
1757
- .zl-card-forgot__link {
1758
- display: inline;
1759
- margin: 0;
1760
- padding: 0;
1761
- border: none;
1762
- background: transparent;
1763
- font-family: var(--zl-font-family-sans, system-ui, sans-serif);
1764
- font-size: 1rem;
1765
- line-height: 1.5rem;
1766
- font-weight: 400;
1767
- color: var(--zl-color-icon-default-purple, #bba5e4);
1768
- text-decoration: none;
1769
- cursor: pointer;
1770
- }
1771
- .zl-card-forgot__link:hover {
1772
- text-decoration: underline;
1773
- }
1774
- .zl-card-forgot__link:focus-visible {
1775
- outline: var(--zl-focus-width, 2px) solid var(--zl-focus-color, #f4f4f6);
1776
- outline-offset: var(--zl-focus-offset, 2px);
1777
- }
1778
- `;
1779
- //#endregion
1780
- //#region src/orchestrator/templates/passkey-upsell.liquid.ts
1781
- /**
1782
- * `passkey-upsell` partial — third Figma target screen (file
1783
- * `xkvBjkOJ8ENuHdTGZHXezK`, node `6594:630`).
1784
- *
1785
- * Differs from `auth-form`:
1786
- * - heading + subtitle live in a compact card (gap 24px instead of 32px),
1787
- * - primary "Set up passkey" CTA carries a trailing 24px passkey icon
1788
- * (Figma slots the icon to the right of the label, not above it),
1789
- * - secondary "Skip for now" uses the dark secondary hierarchy, not the
1790
- * `text` hierarchy — the Figma button has a solid #252528 background.
1791
- *
1792
- * Server-side errors land in a `<zl-alert>` the same way as the auth form.
1793
- */
1794
- const passkeyUpsellTemplate = String.raw`
1795
- <zl-page-shell data-zl-template-root>
1796
- {% if branding.logo_url %}
1797
- <img slot="header" class="zl-card-logo" src="{{ branding.logo_url }}" alt="" />
1798
- {% endif %}
1799
- <zl-card compact>
1800
- <h1 slot="header" class="zl-card-title">{{ step.texts.title_key | t: identity.display_name }}</h1>
1801
- <p slot="header" class="zl-card-subtitle">{{ 'passkey-upsell.description.line1' | t }}</p>
1802
- <p slot="header" class="zl-card-subtitle">{{ 'passkey-upsell.description.line2' | t }}</p>
1803
-
1804
- {% if errors and errors.size > 0 %}
1805
- {% for err in errors %}
1806
- {% if err | formLevelError %}
1807
- <zl-alert severity="error">
1808
- {% if err.text_key %}{{ err.text_key | t }}{% else %}{{ err.message }}{% endif %}
1809
- </zl-alert>
1810
- {% endif %}
1811
- {% endfor %}
1812
- {% endif %}
1813
-
1814
- {% for entry in actions %}
1815
- {% if entry[1].primary %}
1816
- <zl-button
1817
- slot="footer"
1818
- hierarchy="primary"
1819
- size="medium"
1820
- type="submit"
1821
- block
1822
- action="{{ entry[0] }}"
1823
- {% if loading %}loading disabled{% endif %}
1824
- >
1825
- {{ entry[1].text_key | t }}
1826
- <zl-icon slot="trailing" name="passkey" size="24"></zl-icon>
1827
- </zl-button>
1828
- {% endif %}
1829
- {% endfor %}
1830
-
1831
- {% for entry in actions %}
1832
- {% unless entry[1].primary %}
1833
- <zl-button
1834
- slot="footer"
1835
- hierarchy="secondary"
1836
- size="medium"
1837
- type="button"
1838
- block
1839
- action="{{ entry[0] }}"
1840
- label="{{ entry[1].text_key | t }}"
1841
- ></zl-button>
1842
- {% endunless %}
1843
- {% endfor %}
1844
-
1845
- {% mandatory_gates %}
1846
-
1847
- {% if challenge and challenge.method == "passkey" %}
1848
- <zl-passkey
1849
- ceremony="{{ challenge.options.ceremony | default: 'register' }}"
1850
- challenge-id="{{ challenge.challenge_id }}"
1851
- options='{{ challenge.options | json }}'
1852
- ></zl-passkey>
1853
- {% endif %}
1854
- </zl-card>
1855
- </zl-page-shell>
1856
- `;
1857
- //#endregion
1858
- //#region src/orchestrator/templates/signed-in.liquid.ts
1859
- /**
1860
- * `signed-in` partial — fourth Figma target screen (file
1861
- * `xkvBjkOJ8ENuHdTGZHXezK`, node `6596:132844`).
1862
- *
1863
- * Figma layout:
1864
- * - heading "You're signed in as" (left-aligned, full width)
1865
- * - body line with the authenticated email address (Arimo Regular 16/24)
1866
- * - "Continue" primary CTA
1867
- * - "Logout" secondary CTA
1868
- *
1869
- * No avatar / identity hero glyph: the Figma frame is intentionally a
1870
- * pure modal layover and dropping the avatar keeps the orchestrator
1871
- * default in lockstep with the spec.
1872
- *
1873
- * Rendered when the flow returns a terminal step (`complete: "show"`)
1874
- * and no `post-sign-in-url` is configured, so the user can confirm the
1875
- * identity before the host SPA takes over.
1876
- */
1877
- const signedInTemplate = String.raw`
1878
- <zl-page-shell data-zl-template-root>
1879
- {% if branding.logo_url %}
1880
- <img slot="header" class="zl-card-logo" src="{{ branding.logo_url }}" alt="" />
1881
- {% endif %}
1882
- <zl-card compact>
1883
- <h1 slot="header" class="zl-card-title">
1884
- {{ step.texts.title_key | t: identity.display_name }}
1885
- </h1>
1886
- {% if identity.email_address or identity.display_name %}
1887
- <p slot="header" class="zl-card-subtitle">
1888
- {{ identity.email_address | default: identity.display_name }}
1889
- </p>
1890
- {% endif %}
1891
-
1892
- {% if errors and errors.size > 0 %}
1893
- {% for err in errors %}
1894
- <zl-alert severity="error">{{ err.text_key | t }}</zl-alert>
1895
- {% endfor %}
1896
- {% endif %}
1897
-
1898
- {% if actions and actions.size > 0 %}
1899
- {% for entry in actions %}
1900
- {% if entry[1].primary %}
1901
- <zl-button
1902
- slot="footer"
1903
- hierarchy="primary"
1904
- size="medium"
1905
- type="submit"
1906
- block
1907
- action="{{ entry[0] }}"
1908
- label="{{ entry[1].text_key | t }}"
1909
- {% if loading %}loading{% endif %}
1910
- ></zl-button>
1911
- {% endif %}
1912
- {% endfor %}
1913
-
1914
- {% for entry in actions %}
1915
- {% unless entry[1].primary %}
1916
- <zl-button
1917
- slot="footer"
1918
- hierarchy="secondary"
1919
- size="medium"
1920
- block
1921
- action="{{ entry[0] }}"
1922
- label="{{ entry[1].text_key | t }}"
1923
- ></zl-button>
1924
- {% endunless %}
1925
- {% endfor %}
1926
- {% else %}
1927
- {% comment %}
1928
- Fallback when the API ships a terminal step with no actions —
1929
- matches Figma node 6596:132844 which always renders Continue
1930
- + Logout. The orchestrator wires these to its built-in
1931
- continue / logout handlers.
1932
- {% endcomment %}
1933
- <zl-button
1934
- slot="footer"
1935
- hierarchy="primary"
1936
- size="medium"
1937
- type="submit"
1938
- block
1939
- action="continue"
1940
- label="{{ 'signed-in.continue' | t }}"
1941
- ></zl-button>
1942
- <zl-button
1943
- slot="footer"
1944
- hierarchy="secondary"
1945
- size="medium"
1946
- block
1947
- action="logout"
1948
- label="{{ 'signed-in.logout' | t }}"
1949
- ></zl-button>
1950
- {% endif %}
1951
-
1952
- {% mandatory_gates %}
1953
- </zl-card>
1954
- </zl-page-shell>
1955
- `;
1956
- //#endregion
1957
- //#region src/orchestrator/template-names.ts
1958
- /**
1959
- * Names of the bundled Liquid templates the `<zitadel-login>` orchestrator
1960
- * registers.
1961
- *
1962
- * This lives in its own module — separate from `liquid.ts` — so the public
1963
- * barrel can re-export `TEMPLATE_NAMES` without dragging LiquidJS' `Liquid`
1964
- * type (and its Node ambient-type requirements) into the published
1965
- * declaration surface. See `liquid.ts` / the orchestrator barrel.
1966
- */
1967
- const TEMPLATE_NAMES = {
1968
- default: "default",
1969
- authForm: "auth-form",
1970
- passkeyUpsell: "passkey-upsell",
1971
- signedIn: "signed-in"
1972
- };
1973
- //#endregion
1974
1683
  //#region src/orchestrator/liquid.ts
1975
1684
  /**
1976
1685
  * LiquidJS engine factory for the `<zitadel-login>` orchestrator.
@@ -1987,13 +1696,11 @@ const TEMPLATE_NAMES = {
1987
1696
  * escapes (Layer 1 of the security pipeline).
1988
1697
  * - Partials are loaded from an in-memory map; no filesystem access.
1989
1698
  */
1699
+ const TEMPLATE_NAMES$1 = { default: "default" };
1990
1700
  function createLiquidEngine(options) {
1991
1701
  const engine = new Liquid({
1992
1702
  templates: {
1993
- [TEMPLATE_NAMES.default]: defaultTemplate,
1994
- [TEMPLATE_NAMES.authForm]: authFormTemplate,
1995
- [TEMPLATE_NAMES.passkeyUpsell]: passkeyUpsellTemplate,
1996
- [TEMPLATE_NAMES.signedIn]: signedInTemplate,
1703
+ [TEMPLATE_NAMES$1.default]: default_default,
1997
1704
  ...options.templates ?? {}
1998
1705
  },
1999
1706
  cache: true,
@@ -2077,6 +1784,23 @@ function interpolate(template, args) {
2077
1784
  });
2078
1785
  }
2079
1786
  //#endregion
1787
+ //#region src/orchestrator/template-names.ts
1788
+ /**
1789
+ * Names of the bundled Liquid templates the `<zitadel-login>` orchestrator
1790
+ * registers.
1791
+ *
1792
+ * This lives in its own module — separate from `liquid.ts` — so the public
1793
+ * barrel can re-export `TEMPLATE_NAMES` without dragging LiquidJS' `Liquid`
1794
+ * type (and its Node ambient-type requirements) into the published
1795
+ * declaration surface. See `liquid.ts` / the orchestrator barrel.
1796
+ */
1797
+ const TEMPLATE_NAMES = {
1798
+ default: "default",
1799
+ authForm: "auth-form",
1800
+ passkeyUpsell: "passkey-upsell",
1801
+ signedIn: "signed-in"
1802
+ };
1803
+ //#endregion
2080
1804
  //#region src/orchestrator/locales/en.ts
2081
1805
  /**
2082
1806
  * Minimal English locale dictionary.
@@ -2085,53 +1809,100 @@ function interpolate(template, args) {
2085
1809
  * to the raw key (matches the spec in
2086
1810
  * `docs/design/flowengine/flow-engine-guide.md`).
2087
1811
  *
1812
+ * Text keys are auto-generated by the server from the flow definition:
1813
+ * - Step titles: `<step>.title`
1814
+ * - Step descriptions: `<step>.description`
1815
+ * - Field labels: `<step>.field.<field>`
1816
+ * - Action labels: `<step>.action.<action>`
1817
+ *
2088
1818
  * Copy aligned to Figma screens file `xkvBjkOJ8ENuHdTGZHXezK` (May 2026).
2089
1819
  * MVP only — multi-locale support is deferred.
2090
1820
  */
2091
1821
  const en = {
2092
1822
  "identifier.title": "Sign in",
1823
+ "identifier.description": "Enter your email to continue",
2093
1824
  "identifier.field.email": "Work email",
2094
1825
  "identifier.field.email.placeholder": "you@company.com",
2095
- "identifier.field.password": "Password",
1826
+ "identifier.action.submit": "Continue",
1827
+ "identifier.action.continue": "Continue",
2096
1828
  "identifier.action.passkey": "Sign in with a passkey",
2097
1829
  "identifier.action.register.lead": "Don't have an account? ",
2098
1830
  "identifier.action.register.link": "Sign up",
1831
+ "authenticate.title": "Sign in",
1832
+ "authenticate.description": "Enter your password",
1833
+ "authenticate.field.password": "Password",
1834
+ "authenticate.action.submit": "Sign in",
1835
+ "choose-register.title": "Create your account",
1836
+ "choose-register.description": "Choose how you'd like to sign up",
1837
+ "choose-register.action.register-with-password": "Sign up with password",
1838
+ "choose-register.action.register-with-passkey": "Sign up with passkey",
1839
+ "collect-credentials.title": "Create your account",
1840
+ "collect-credentials.description": "Set up your email and password",
1841
+ "collect-credentials.field.email": "Work email",
1842
+ "collect-credentials.field.email.placeholder": "you@company.com",
1843
+ "collect-credentials.field.password": "Password",
1844
+ "collect-credentials.field.password.help": "At least 8 characters, including a symbol and number.",
1845
+ "collect-credentials.action.submit": "Sign up",
1846
+ "register-password.title": "Create your password",
1847
+ "register-password.description": "Choose a secure password for your account",
1848
+ "register-password.field.password": "Password",
1849
+ "register-password.field.password.help": "At least 8 characters, including a symbol and number.",
1850
+ "register-password.action.submit": "Sign up",
1851
+ "passkey-upsell.title": "Sign in faster next time",
1852
+ "passkey-upsell.description": "No password needed ever again.",
1853
+ "passkey-upsell.description.line1": "No password needed ever again.",
1854
+ "passkey-upsell.description.line2": "Sign in with Face ID, Touch ID, or PIN.",
1855
+ "passkey-upsell.action.passkey_register": "Set up passkey",
1856
+ "passkey-upsell.action.skip": "Skip for now",
1857
+ "passkey-upsell.action.setup": "Set up passkey",
1858
+ "collect-passkey-email.title": "Create your account",
1859
+ "collect-passkey-email.description": "Enter your email to set up a passkey",
1860
+ "collect-passkey-email.field.email": "Work email",
1861
+ "collect-passkey-email.field.email.placeholder": "you@company.com",
1862
+ "collect-passkey-email.action.submit": "Continue",
1863
+ "passkey-enroll.title": "Set up your passkey",
1864
+ "passkey-enroll.description": "Use Face ID, Touch ID, or your device PIN to create a passkey.",
1865
+ "passkey-enroll.action.passkey_register": "Set up passkey",
1866
+ "done.title": "You're signed in as",
1867
+ "done.description": "",
2099
1868
  "password.title": "Sign in",
1869
+ "password.description": "Enter your password",
2100
1870
  "password.field.password": "Password",
1871
+ "password.action.signin": "Sign in",
2101
1872
  "password.action.passkey": "Sign in with a passkey",
2102
1873
  "password.action.register.lead": "Don't have an account? ",
2103
1874
  "password.action.register.link": "Sign up",
2104
1875
  "register.title": "Create your account",
1876
+ "register.description": "Enter your email to get started",
2105
1877
  "register.field.email": "Work email",
2106
1878
  "register.field.email.placeholder": "you@company.com",
2107
1879
  "register.field.password": "Password",
2108
1880
  "register.field.password.help": "At least 8 characters, including a symbol and number.",
1881
+ "register.field.givenName": "Given name",
1882
+ "register.field.familyName": "Family name",
1883
+ "register.field.dateOfBirth": "Date of birth",
1884
+ "register.action.password": "Continue with password",
1885
+ "register.action.passkey": "Register with a passkey",
2109
1886
  "register.action.submit": "Sign up",
2110
1887
  "register.action.sign_in.lead": "Already have an account? ",
2111
1888
  "register.action.sign_in.link": "Sign in",
2112
- "passkey-upsell.title": "Sign in faster next time",
2113
- "passkey-upsell.description.line1": "No password needed ever again.",
2114
- "passkey-upsell.description.line2": "Sign in with Face ID, Touch ID, or PIN.",
2115
- "passkey-upsell.action.setup": "Set up passkey",
2116
- "passkey-upsell.action.skip": "Skip for now",
2117
- /** Figma `6594:630` setup-passkey annotations — form-level `<zl-alert>`. */
2118
- "error.passkey_cancelled": "Passkey setup was cancelled",
2119
- "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
2120
- "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
2121
- "error.passkey_unsupported": "This device does not support passkeys",
2122
- "error.passkey_failed": "Something went wrong. Please try again.",
2123
1889
  "complete.title": "You're signed in as",
2124
1890
  "signed-in.continue": "Continue",
2125
1891
  "signed-in.logout": "Logout",
1892
+ "passkey-login.title": "Sign in with your passkey",
2126
1893
  "recover.title": "Check your email",
2127
1894
  "recover.description": "We sent a password reset link to your email address.",
2128
1895
  "recover.action.back": "Back to sign in",
2129
- "passkey-login.title": "Sign in with your passkey",
2130
1896
  "submit.continue": "Continue",
2131
1897
  "submit.signin": "Sign in",
2132
1898
  "action.forgot_password": "Forgot password?",
2133
1899
  "action.cancel": "Cancel",
2134
1900
  "sso.redirect.title": "Redirecting to your provider…",
1901
+ "error.passkey_cancelled": "Passkey setup was cancelled",
1902
+ "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
1903
+ "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
1904
+ "error.passkey_unsupported": "This device does not support passkeys",
1905
+ "error.passkey_failed": "Something went wrong. Please try again.",
2135
1906
  "error.email_required": "Please enter an email address",
2136
1907
  "error.email_invalid": "Please enter a valid email",
2137
1908
  "error.password_required": "Please enter a password",
@@ -2147,47 +1918,86 @@ const en = {
2147
1918
  //#region src/orchestrator/locales/de.ts
2148
1919
  const de = {
2149
1920
  "identifier.title": "Anmelden",
1921
+ "identifier.description": "Gib deine E-Mail-Adresse ein, um fortzufahren",
2150
1922
  "identifier.field.email": "E-Mail",
2151
1923
  "identifier.field.email.placeholder": "du@unternehmen.com",
2152
- "identifier.field.password": "Passwort",
1924
+ "identifier.action.submit": "Weiter",
1925
+ "identifier.action.continue": "Weiter",
2153
1926
  "identifier.action.passkey": "Mit Passkey anmelden",
2154
1927
  "identifier.action.register.lead": "Noch kein Konto? ",
2155
1928
  "identifier.action.register.link": "Registrieren",
1929
+ "authenticate.title": "Anmelden",
1930
+ "authenticate.description": "Gib dein Passwort ein",
1931
+ "authenticate.field.password": "Passwort",
1932
+ "authenticate.action.submit": "Anmelden",
1933
+ "choose-register.title": "Konto erstellen",
1934
+ "choose-register.description": "Wähle, wie du dich registrieren möchtest",
1935
+ "choose-register.action.register-with-password": "Mit Passwort registrieren",
1936
+ "choose-register.action.register-with-passkey": "Mit Passkey registrieren",
1937
+ "collect-credentials.title": "Konto erstellen",
1938
+ "collect-credentials.description": "Richte E-Mail und Passwort ein",
1939
+ "collect-credentials.field.email": "E-Mail",
1940
+ "collect-credentials.field.email.placeholder": "du@unternehmen.com",
1941
+ "collect-credentials.field.password": "Passwort",
1942
+ "collect-credentials.field.password.help": "Mindestens 8 Zeichen, einschließlich eines Sonderzeichens und einer Zahl.",
1943
+ "collect-credentials.action.submit": "Registrieren",
1944
+ "register-password.title": "Passwort erstellen",
1945
+ "register-password.description": "Wähle ein sicheres Passwort für dein Konto",
1946
+ "register-password.field.password": "Passwort",
1947
+ "register-password.field.password.help": "Mindestens 8 Zeichen, einschließlich eines Sonderzeichens und einer Zahl.",
1948
+ "register-password.action.submit": "Registrieren",
1949
+ "passkey-upsell.title": "Nächstes Mal schneller anmelden",
1950
+ "passkey-upsell.description": "Kein Passwort mehr nötig.",
1951
+ "passkey-upsell.description.line1": "Kein Passwort mehr nötig.",
1952
+ "passkey-upsell.description.line2": "Melde dich mit Face ID, Touch ID oder PIN an.",
1953
+ "passkey-upsell.action.passkey_register": "Passkey einrichten",
1954
+ "passkey-upsell.action.skip": "Vorerst überspringen",
1955
+ "passkey-upsell.action.setup": "Passkey einrichten",
1956
+ "collect-passkey-email.title": "Konto erstellen",
1957
+ "collect-passkey-email.description": "Gib deine E-Mail-Adresse ein, um einen Passkey einzurichten",
1958
+ "collect-passkey-email.field.email": "E-Mail",
1959
+ "collect-passkey-email.field.email.placeholder": "du@unternehmen.com",
1960
+ "collect-passkey-email.action.submit": "Weiter",
1961
+ "passkey-enroll.title": "Passkey einrichten",
1962
+ "passkey-enroll.description": "Verwende Face ID, Touch ID oder die Geräte-PIN, um einen Passkey zu erstellen.",
1963
+ "passkey-enroll.action.passkey_register": "Passkey einrichten",
1964
+ "done.title": "Du bist angemeldet als",
1965
+ "done.description": "",
2156
1966
  "password.title": "Anmelden",
1967
+ "password.description": "Gib dein Passwort ein",
2157
1968
  "password.field.password": "Passwort",
1969
+ "password.action.signin": "Anmelden",
2158
1970
  "password.action.passkey": "Mit Passkey anmelden",
2159
1971
  "password.action.register.lead": "Noch kein Konto? ",
2160
1972
  "password.action.register.link": "Registrieren",
2161
1973
  "register.title": "Konto erstellen",
1974
+ "register.description": "Gib deine E-Mail-Adresse ein, um loszulegen",
2162
1975
  "register.field.email": "E-Mail",
2163
1976
  "register.field.email.placeholder": "du@unternehmen.com",
2164
1977
  "register.field.password": "Passwort",
2165
1978
  "register.field.password.help": "Mindestens 8 Zeichen, einschließlich eines Sonderzeichens und einer Zahl.",
1979
+ "register.action.password": "Mit Passwort fortfahren",
1980
+ "register.action.passkey": "Mit Passkey registrieren",
2166
1981
  "register.action.submit": "Registrieren",
2167
1982
  "register.action.sign_in.lead": "Bereits ein Konto? ",
2168
1983
  "register.action.sign_in.link": "Anmelden",
2169
- "passkey-upsell.title": "Nächstes Mal schneller anmelden",
2170
- "passkey-upsell.description.line1": "Kein Passwort mehr nötig.",
2171
- "passkey-upsell.description.line2": "Melde dich mit Face ID, Touch ID oder PIN an.",
2172
- "passkey-upsell.action.setup": "Passkey einrichten",
2173
- "passkey-upsell.action.skip": "Vorerst überspringen",
2174
- "error.passkey_cancelled": "Passkey-Einrichtung wurde abgebrochen",
2175
- "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
2176
- "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
2177
- "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
2178
- "error.passkey_failed": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
2179
1984
  "complete.title": "Du bist angemeldet als",
2180
1985
  "signed-in.continue": "Weiter",
2181
1986
  "signed-in.logout": "Abmelden",
1987
+ "passkey-login.title": "Mit Passkey anmelden",
2182
1988
  "recover.title": "E-Mail prüfen",
2183
1989
  "recover.description": "Wir haben einen Link zum Zurücksetzen des Passworts an deine E-Mail-Adresse gesendet.",
2184
1990
  "recover.action.back": "Zurück zur Anmeldung",
2185
- "passkey-login.title": "Mit Passkey anmelden",
2186
1991
  "submit.continue": "Weiter",
2187
1992
  "submit.signin": "Anmelden",
2188
1993
  "action.forgot_password": "Passwort vergessen?",
2189
1994
  "action.cancel": "Abbrechen",
2190
1995
  "sso.redirect.title": "Weiterleitung zum Anbieter…",
1996
+ "error.passkey_cancelled": "Passkey-Einrichtung wurde abgebrochen",
1997
+ "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
1998
+ "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
1999
+ "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
2000
+ "error.passkey_failed": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
2191
2001
  "error.email_required": "Bitte gib eine E-Mail-Adresse ein",
2192
2002
  "error.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein",
2193
2003
  "error.password_required": "Bitte gib ein Passwort ein",
@@ -2199,10 +2009,105 @@ const de = {
2199
2009
  "error.sign_in_server.body": "Bitte versuche es in einigen Minuten erneut"
2200
2010
  };
2201
2011
  //#endregion
2012
+ //#region src/orchestrator/locales/it.ts
2013
+ const it = {
2014
+ "identifier.title": "Accedi",
2015
+ "identifier.description": "Inserisci la tua e-mail per continuare",
2016
+ "identifier.field.email": "E-mail aziendale",
2017
+ "identifier.field.email.placeholder": "tu@azienda.com",
2018
+ "identifier.action.submit": "Continua",
2019
+ "identifier.action.continue": "Continua",
2020
+ "identifier.action.passkey": "Accedi con passkey",
2021
+ "identifier.action.register.lead": "Non hai un account? ",
2022
+ "identifier.action.register.link": "Registrati",
2023
+ "authenticate.title": "Accedi",
2024
+ "authenticate.description": "Inserisci la tua password",
2025
+ "authenticate.field.password": "Password",
2026
+ "authenticate.action.submit": "Accedi",
2027
+ "choose-register.title": "Crea il tuo account",
2028
+ "choose-register.description": "Scegli come registrarti",
2029
+ "choose-register.action.register-with-password": "Registrati con password",
2030
+ "choose-register.action.register-with-passkey": "Registrati con passkey",
2031
+ "collect-credentials.title": "Crea il tuo account",
2032
+ "collect-credentials.description": "Configura e-mail e password",
2033
+ "collect-credentials.field.email": "E-mail aziendale",
2034
+ "collect-credentials.field.email.placeholder": "tu@azienda.com",
2035
+ "collect-credentials.field.password": "Password",
2036
+ "collect-credentials.field.password.help": "Almeno 8 caratteri, incluso un simbolo e un numero.",
2037
+ "collect-credentials.action.submit": "Registrati",
2038
+ "register-password.title": "Crea la tua password",
2039
+ "register-password.description": "Scegli una password sicura per il tuo account",
2040
+ "register-password.field.password": "Password",
2041
+ "register-password.field.password.help": "Almeno 8 caratteri, incluso un simbolo e un numero.",
2042
+ "register-password.action.submit": "Registrati",
2043
+ "passkey-upsell.title": "Accedi più velocemente la prossima volta",
2044
+ "passkey-upsell.description": "Nessuna password necessaria.",
2045
+ "passkey-upsell.description.line1": "Nessuna password necessaria.",
2046
+ "passkey-upsell.description.line2": "Accedi con Face ID, Touch ID o PIN.",
2047
+ "passkey-upsell.action.passkey_register": "Configura passkey",
2048
+ "passkey-upsell.action.skip": "Salta per ora",
2049
+ "passkey-upsell.action.setup": "Configura passkey",
2050
+ "collect-passkey-email.title": "Crea il tuo account",
2051
+ "collect-passkey-email.description": "Inserisci la tua e-mail per configurare una passkey",
2052
+ "collect-passkey-email.field.email": "E-mail aziendale",
2053
+ "collect-passkey-email.field.email.placeholder": "tu@azienda.com",
2054
+ "collect-passkey-email.action.submit": "Continua",
2055
+ "passkey-enroll.title": "Configura la tua passkey",
2056
+ "passkey-enroll.description": "Usa Face ID, Touch ID o il PIN del dispositivo per creare una passkey.",
2057
+ "passkey-enroll.action.passkey_register": "Configura passkey",
2058
+ "done.title": "Sei connesso come",
2059
+ "done.description": "",
2060
+ "password.title": "Accedi",
2061
+ "password.description": "Inserisci la tua password",
2062
+ "password.field.password": "Password",
2063
+ "password.action.signin": "Accedi",
2064
+ "password.action.passkey": "Accedi con passkey",
2065
+ "password.action.register.lead": "Non hai un account? ",
2066
+ "password.action.register.link": "Registrati",
2067
+ "register.title": "Crea il tuo account",
2068
+ "register.description": "Inserisci la tua e-mail per iniziare",
2069
+ "register.field.email": "E-mail aziendale",
2070
+ "register.field.email.placeholder": "tu@azienda.com",
2071
+ "register.field.password": "Password",
2072
+ "register.field.password.help": "Almeno 8 caratteri, incluso un simbolo e un numero.",
2073
+ "register.action.password": "Continua con password",
2074
+ "register.action.passkey": "Registrati con passkey",
2075
+ "register.action.submit": "Registrati",
2076
+ "register.action.sign_in.lead": "Hai già un account? ",
2077
+ "register.action.sign_in.link": "Accedi",
2078
+ "complete.title": "Sei connesso come",
2079
+ "signed-in.continue": "Continua",
2080
+ "signed-in.logout": "Esci",
2081
+ "passkey-login.title": "Accedi con la tua passkey",
2082
+ "recover.title": "Controlla la tua e-mail",
2083
+ "recover.description": "Abbiamo inviato un link per reimpostare la password al tuo indirizzo e-mail.",
2084
+ "recover.action.back": "Torna all'accesso",
2085
+ "submit.continue": "Continua",
2086
+ "submit.signin": "Accedi",
2087
+ "action.forgot_password": "Password dimenticata?",
2088
+ "action.cancel": "Annulla",
2089
+ "sso.redirect.title": "Reindirizzamento al provider…",
2090
+ "error.passkey_cancelled": "La configurazione della passkey è stata annullata",
2091
+ "error.passkey_not_registered": "Questa passkey non è registrata. Accedi con e-mail e password.",
2092
+ "error.passkey_setup_failed": "La registrazione della passkey non è stata completata. Riprova.",
2093
+ "error.passkey_unsupported": "Questo dispositivo non supporta le passkey",
2094
+ "error.passkey_failed": "Qualcosa è andato storto. Riprova.",
2095
+ "error.email_required": "Inserisci un indirizzo e-mail",
2096
+ "error.email_invalid": "Inserisci un indirizzo e-mail valido",
2097
+ "error.password_required": "Inserisci una password",
2098
+ "error.password_incorrect": "E-mail o password errata.",
2099
+ "error.email_exists": "Esiste già un account con questo indirizzo e-mail.",
2100
+ "error.invalid_credentials": "E-mail o password errata.",
2101
+ "error.required": "Questo campo è obbligatorio.",
2102
+ "error.sign_in_server.title": "Non è stato possibile completare l'accesso.",
2103
+ "error.sign_in_server.body": "Riprova tra qualche minuto"
2104
+ };
2105
+ //#endregion
2202
2106
  //#region src/orchestrator/locales/index.ts
2203
2107
  const builtinLocales = {
2204
2108
  en,
2205
- de
2109
+ de,
2110
+ it
2206
2111
  };
2207
2112
  //#endregion
2208
2113
  //#region ../shared-component-styles/dist/attribution-markup.mjs
@@ -2349,6 +2254,9 @@ function createSanitiser() {
2349
2254
  };
2350
2255
  }
2351
2256
  //#endregion
2257
+ //#region src/orchestrator/templates/layout-chrome.css?inline
2258
+ 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";
2259
+ //#endregion
2352
2260
  //#region src/orchestrator/theme-controller.ts
2353
2261
  const DARK_QUERY = "(prefers-color-scheme: dark)";
2354
2262
  var ThemeController = class {
@@ -2430,6 +2338,27 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2430
2338
  set project(value) {
2431
2339
  this.#_project_accessor_storage = value;
2432
2340
  }
2341
+ #_projectId_accessor_storage = "";
2342
+ get projectId() {
2343
+ return this.#_projectId_accessor_storage;
2344
+ }
2345
+ set projectId(value) {
2346
+ this.#_projectId_accessor_storage = value;
2347
+ }
2348
+ #_proxyPath_accessor_storage = "";
2349
+ get proxyPath() {
2350
+ return this.#_proxyPath_accessor_storage;
2351
+ }
2352
+ set proxyPath(value) {
2353
+ this.#_proxyPath_accessor_storage = value;
2354
+ }
2355
+ #_url_accessor_storage = "";
2356
+ get url() {
2357
+ return this.#_url_accessor_storage;
2358
+ }
2359
+ set url(value) {
2360
+ this.#_url_accessor_storage = value;
2361
+ }
2433
2362
  #_postSignInUrl_accessor_storage = "";
2434
2363
  get postSignInUrl() {
2435
2364
  return this.#_postSignInUrl_accessor_storage;
@@ -2506,7 +2435,7 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2506
2435
  if (root instanceof ShadowRoot) {
2507
2436
  const existing = Array.isArray(root.adoptedStyleSheets) ? root.adoptedStyleSheets : [];
2508
2437
  const sheet = new CSSStyleSheet();
2509
- sheet.replaceSync(layoutChromeCss);
2438
+ sheet.replaceSync(layout_chrome_default);
2510
2439
  root.adoptedStyleSheets = [...existing, sheet];
2511
2440
  root.addEventListener("zl-input", this.handleAtomInput);
2512
2441
  root.addEventListener("zl-submit", this.handleAtomSubmit);
@@ -2535,9 +2464,12 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2535
2464
  * resolved language is used as the base; entries from the `locales` map (if
2536
2465
  * set) are spread on top so partial overrides work without importing and
2537
2466
  * spreading the full base dictionary.
2467
+ *
2468
+ * Priority: explicit `lang` attr → `navigator.language` (user preference)
2469
+ * → `document.documentElement.lang` (page default) → English fallback.
2538
2470
  */
2539
2471
  resolveLocale() {
2540
- const primary = ((this.lang || (typeof document !== "undefined" ? document.documentElement.lang : "") || (typeof navigator !== "undefined" ? navigator.language : "")).split("-")[0] ?? "").toLowerCase();
2472
+ const primary = ((this.lang || (typeof navigator !== "undefined" ? navigator.language : "") || (typeof document !== "undefined" ? document.documentElement.lang : "")).split("-")[0] ?? "").toLowerCase();
2541
2473
  const builtin = builtinLocales[primary] ?? en;
2542
2474
  const custom = this.locales?.[primary];
2543
2475
  return custom ? {
@@ -2559,10 +2491,21 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2559
2491
  }
2560
2492
  updated(changed) {
2561
2493
  if (!changed.has("response")) return;
2562
- requestAnimationFrame(() => {
2563
- this.applyValuesToFields();
2564
- this.moveFocusToFirstField();
2565
- });
2494
+ this.hydrateStepAfterRender();
2495
+ }
2496
+ /**
2497
+ * Apply captured values and move focus once the new step has fully
2498
+ * rendered. This commit produces the step's `zl-field`/`zl-button` atoms,
2499
+ * but those render their own shadow DOM on a later microtask — so await
2500
+ * this element's update *and* the child atoms' first render before touching
2501
+ * them, rather than guessing a frame with `requestAnimationFrame`.
2502
+ */
2503
+ async hydrateStepAfterRender() {
2504
+ await this.updateComplete;
2505
+ const atoms = this.shadowRoot?.querySelectorAll("zl-field, zl-button");
2506
+ if (atoms) await Promise.all(Array.from(atoms).map((atom) => atom.updateComplete));
2507
+ this.applyValuesToFields();
2508
+ this.moveFocusToFirstField();
2566
2509
  }
2567
2510
  render() {
2568
2511
  if (this.startupError) return html`<form class="zl-mount" novalidate>
@@ -2586,6 +2529,10 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2586
2529
  * Figma sign-in frame where the pill sits 24px below the card, both
2587
2530
  * centred on the page). It can't be a sibling of the page-shell because
2588
2531
  * the page-shell already occupies the full viewport height.
2532
+ *
2533
+ * This markup is appended AFTER `renderStep` has sanitised the Liquid
2534
+ * output, so it is not run through DOMPurify. It is orchestrator-owned and
2535
+ * any tenant-supplied values (`custom_link`) are escaped via `escapeHtml`.
2589
2536
  */
2590
2537
  injectAttribution(rendered) {
2591
2538
  const html = this.renderAttributionHtml();
@@ -2605,23 +2552,28 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2605
2552
  const show = attribution?.show_zitadel !== false;
2606
2553
  const custom = attribution?.custom_link;
2607
2554
  if (!show && !custom) return "";
2608
- if (custom) return `<div slot="footer" part="attribution" class="zl-attribution"><zl-pill tone="neutral" href="${String(custom.href).replace(/"/g, "&quot;")}">${String(custom.label).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</zl-pill></div>`;
2555
+ 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>`;
2609
2556
  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>`;
2610
2557
  }
2558
+ /** Declarative config read from this element's attributes. */
2559
+ get projectAttrs() {
2560
+ return {
2561
+ projectId: this.projectId,
2562
+ proxyPath: this.proxyPath,
2563
+ url: this.url
2564
+ };
2565
+ }
2611
2566
  async startFlow() {
2612
- const cfg = this.project ?? getZitadelConfig();
2613
- const projectId = cfg?.projectId || "";
2614
- if (!cfg) throw new Error("<zitadel-login> requires a `config` prop (from configureZitadel()) or configureZitadel() must be called before use.");
2615
- const api = getApi(cfg);
2616
2567
  this.loading = true;
2617
2568
  this.startupError = null;
2618
2569
  try {
2570
+ const { project: cfg, api } = resolveApi(this.project, this.projectAttrs, "<zitadel-login>");
2619
2571
  let wire;
2620
2572
  if (this.resumeFlowId) wire = await getCurrentStep(api, this.resumeFlowId);
2621
2573
  else {
2622
- if (!projectId) throw new Error("<zitadel-login> requires a `project-id` attribute (or configureZitadel()) to start a flow.");
2574
+ 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.");
2623
2575
  wire = await startFlow(api, {
2624
- project_id: projectId,
2576
+ project_id: cfg.projectId,
2625
2577
  purpose: this.purpose
2626
2578
  });
2627
2579
  }
@@ -2659,16 +2611,12 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2659
2611
  async maybeCompleteFlow(response) {
2660
2612
  const behavior = response.step.complete;
2661
2613
  if (!behavior) return;
2662
- this.dispatchEvent(new CustomEvent("zitadel-flow-complete", {
2663
- bubbles: true,
2664
- composed: true,
2665
- detail: {
2666
- behavior,
2667
- redirect_uri: response.redirect_uri,
2668
- handoff_token: response.handoff_token,
2669
- handoff_token_expires_at: response.handoff_token_expires_at
2670
- }
2671
- }));
2614
+ emit(this, "zitadel-flow-complete", {
2615
+ behavior,
2616
+ redirect_uri: response.redirect_uri,
2617
+ handoff_token: response.handoff_token,
2618
+ handoff_token_expires_at: response.handoff_token_expires_at
2619
+ });
2672
2620
  if (typeof window === "undefined") return;
2673
2621
  if (behavior === "redirect" && response.redirect_uri) {
2674
2622
  window.location.assign(response.redirect_uri);
@@ -2678,9 +2626,8 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2678
2626
  if (behavior === "show" && handoffToken && this.postSignInUrl) {
2679
2627
  this.loading = true;
2680
2628
  try {
2681
- const cfg = this.project ?? getZitadelConfig();
2682
- if (!cfg) throw new Error("<zitadel-login> config is required for exchange.");
2683
- await exchangeSession(getApi(cfg), { handoff_token: handoffToken }, { project_id: cfg.projectId });
2629
+ const { project: cfg, api } = resolveApi(this.project, this.projectAttrs, "<zitadel-login>");
2630
+ await exchangeSession(api, { handoff_token: handoffToken }, { project_id: cfg.projectId });
2684
2631
  window.location.assign(this.postSignInUrl);
2685
2632
  } catch (error) {
2686
2633
  this.handleTransportError(error);
@@ -2766,14 +2713,10 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2766
2713
  ...this.formValues,
2767
2714
  [name]: value
2768
2715
  };
2769
- this.dispatchEvent(new CustomEvent("zitadel-flow-input", {
2770
- bubbles: true,
2771
- composed: true,
2772
- detail: {
2773
- name,
2774
- value
2775
- }
2776
- }));
2716
+ emit(this, "zitadel-flow-input", {
2717
+ name,
2718
+ value
2719
+ });
2777
2720
  };
2778
2721
  handleAtomSubmit = (event) => {
2779
2722
  if (this.loading) return;
@@ -2802,7 +2745,7 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2802
2745
  handlePasskeyResult = (event) => {
2803
2746
  if (this.loading) return;
2804
2747
  const { challenge_id, method, proof } = event.detail;
2805
- this.submit("submit", {
2748
+ this.submit(method, {
2806
2749
  challenge_id,
2807
2750
  method,
2808
2751
  proof
@@ -2844,10 +2787,8 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2844
2787
  moveFocusToFirstField() {
2845
2788
  const root = this.shadowRoot;
2846
2789
  if (!root) return;
2847
- requestAnimationFrame(() => {
2848
- const focusables = root.querySelectorAll("zl-field, zl-button");
2849
- Array.from(focusables).find((el) => !el.hasAttribute("disabled"))?.focus();
2850
- });
2790
+ const focusables = root.querySelectorAll("zl-field, zl-button");
2791
+ Array.from(focusables).find((el) => !el.hasAttribute("disabled"))?.focus();
2851
2792
  }
2852
2793
  async submit(action, challengeResponse) {
2853
2794
  if (!this.response || this.loading) return;
@@ -2866,15 +2807,10 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2866
2807
  fields,
2867
2808
  ...challengeResponse ? { challenge_response: challengeResponse } : {}
2868
2809
  };
2869
- const cfg = this.project ?? getZitadelConfig();
2870
- if (!cfg) throw new Error("<zitadel-login> config is required for submit.");
2871
- const wire = await submitStep(getApi(cfg), id, body);
2810
+ const { api } = resolveApi(this.project, this.projectAttrs, "<zitadel-login>");
2811
+ const wire = await submitStep(api, id, body);
2872
2812
  this.applyResponse(wire);
2873
- this.dispatchEvent(new CustomEvent("zitadel-flow-step", {
2874
- bubbles: true,
2875
- composed: true,
2876
- detail: { step: wire.step }
2877
- }));
2813
+ emit(this, "zitadel-flow-step", { step: wire.step });
2878
2814
  } catch (error) {
2879
2815
  this.handleTransportError(error);
2880
2816
  } finally {
@@ -2885,15 +2821,20 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2885
2821
  const message = error instanceof Error ? error.message : "Unexpected error contacting the Flow API.";
2886
2822
  this.startupError = message;
2887
2823
  console.error("[zitadel-login]", error);
2888
- this.dispatchEvent(new CustomEvent("zitadel-flow-error", {
2889
- bubbles: true,
2890
- composed: true,
2891
- detail: { message }
2892
- }));
2824
+ emit(this, "zitadel-flow-error", { message });
2893
2825
  }
2894
2826
  };
2895
2827
  __decorate([property({ type: String })], ZitadelLogin.prototype, "purpose", null);
2896
2828
  __decorate([property({ attribute: false })], ZitadelLogin.prototype, "project", null);
2829
+ __decorate([property({
2830
+ type: String,
2831
+ attribute: "project-id"
2832
+ })], ZitadelLogin.prototype, "projectId", null);
2833
+ __decorate([property({
2834
+ type: String,
2835
+ attribute: "proxy-path"
2836
+ })], ZitadelLogin.prototype, "proxyPath", null);
2837
+ __decorate([property({ type: String })], ZitadelLogin.prototype, "url", null);
2897
2838
  __decorate([property({
2898
2839
  type: String,
2899
2840
  attribute: "post-sign-in-url"
@@ -3064,6 +3005,27 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
3064
3005
  set project(value) {
3065
3006
  this.#_project_accessor_storage = value;
3066
3007
  }
3008
+ #_projectId_accessor_storage = "";
3009
+ get projectId() {
3010
+ return this.#_projectId_accessor_storage;
3011
+ }
3012
+ set projectId(value) {
3013
+ this.#_projectId_accessor_storage = value;
3014
+ }
3015
+ #_proxyPath_accessor_storage = "";
3016
+ get proxyPath() {
3017
+ return this.#_proxyPath_accessor_storage;
3018
+ }
3019
+ set proxyPath(value) {
3020
+ this.#_proxyPath_accessor_storage = value;
3021
+ }
3022
+ #_url_accessor_storage = "";
3023
+ get url() {
3024
+ return this.#_url_accessor_storage;
3025
+ }
3026
+ set url(value) {
3027
+ this.#_url_accessor_storage = value;
3028
+ }
3067
3029
  #_postSignOutUrl_accessor_storage = "";
3068
3030
  get postSignOutUrl() {
3069
3031
  return this.#_postSignOutUrl_accessor_storage;
@@ -3190,22 +3152,25 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
3190
3152
  this.errorMessage = "";
3191
3153
  }
3192
3154
  /**
3193
- * Calls the typed `endSession` operation (`GET /auth/end-session`) with
3194
- * `credentials: "include"`. The server clears the session cookie via
3195
- * `Set-Cookie: Max-Age=0`; on success this element fires `zitadel-signout`
3196
- * and optionally navigates to `postSignOutUrl`.
3155
+ * Calls `DELETE /sessions/me` (`revokeMySession`) with `credentials: "include"`.
3156
+ * The server validates the `__nextgen_session` cookie, deletes the session, and
3157
+ * clears the cookie via `Set-Cookie: Max-Age=0`. On success this element fires
3158
+ * `zitadel-signout` and optionally navigates to `postSignOutUrl`.
3197
3159
  */
3160
+ /** Declarative config read from this element's attributes. */
3161
+ get projectAttrs() {
3162
+ return {
3163
+ projectId: this.projectId,
3164
+ proxyPath: this.proxyPath,
3165
+ url: this.url
3166
+ };
3167
+ }
3198
3168
  async doLogout() {
3199
3169
  this.loading = true;
3200
3170
  this.errorMessage = "";
3201
- const params = {
3202
- ...this.clientId ? { client_id: this.clientId } : {},
3203
- ...this.postSignOutUrl ? { post_logout_redirect_uri: this.postSignOutUrl } : {}
3204
- };
3205
3171
  try {
3206
- const cfg = this.project ?? getZitadelConfig();
3207
- if (!cfg) throw new Error("<zitadel-logout> requires a `config` prop (from configureZitadel()).");
3208
- await getApi(cfg).endSession(params, { credentials: "include" });
3172
+ const { api } = resolveApi(this.project, this.projectAttrs, "<zitadel-logout>");
3173
+ await api.revokeMySession({ credentials: "include" });
3209
3174
  } catch (error) {
3210
3175
  const message = error instanceof Error ? error.message : "";
3211
3176
  this.errorMessage = message || "Sign-out failed. Please try again.";
@@ -3214,15 +3179,10 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
3214
3179
  }
3215
3180
  this.open = false;
3216
3181
  this.loading = false;
3217
- const detail = {
3182
+ emit(this, "zitadel-signout", {
3218
3183
  name: this.displayName,
3219
3184
  email: this.displayEmail
3220
- };
3221
- this.dispatchEvent(new CustomEvent("zitadel-signout", {
3222
- bubbles: true,
3223
- composed: true,
3224
- detail
3225
- }));
3185
+ });
3226
3186
  if (this.postSignOutUrl && typeof window !== "undefined") window.location.href = this.postSignOutUrl;
3227
3187
  }
3228
3188
  /**
@@ -3236,9 +3196,8 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
3236
3196
  ...this.clientId ? { client_id: this.clientId } : {},
3237
3197
  ...this.postSignOutUrl ? { post_logout_redirect_uri: this.postSignOutUrl } : {}
3238
3198
  };
3239
- const cfg = this.project ?? getZitadelConfig();
3240
- if (!cfg) throw new Error("<zitadel-logout> requires a `config` prop (from configureZitadel()).");
3241
- return getApi(cfg).getEndSessionUrl(params);
3199
+ const { api } = resolveApi(this.project, this.projectAttrs, "<zitadel-logout>");
3200
+ return api.getEndSessionUrl(params);
3242
3201
  }
3243
3202
  handleSignOutClick(event) {
3244
3203
  event.preventDefault();
@@ -3303,6 +3262,15 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
3303
3262
  }
3304
3263
  };
3305
3264
  __decorate([property({ attribute: false })], ZitadelLogout.prototype, "project", null);
3265
+ __decorate([property({
3266
+ type: String,
3267
+ attribute: "project-id"
3268
+ })], ZitadelLogout.prototype, "projectId", null);
3269
+ __decorate([property({
3270
+ type: String,
3271
+ attribute: "proxy-path"
3272
+ })], ZitadelLogout.prototype, "proxyPath", null);
3273
+ __decorate([property({ type: String })], ZitadelLogout.prototype, "url", null);
3306
3274
  __decorate([property({
3307
3275
  type: String,
3308
3276
  attribute: "post-sign-out-url"
@@ -3331,6 +3299,6 @@ function fillTemplateTokens(fragment, name, email, initial) {
3331
3299
  }
3332
3300
  }
3333
3301
  //#endregion
3334
- export { startFlow as C, getCurrentStep as S, applyFontUrl as _, builtinLocales as a, buildBrandingStylesheet as b, TEMPLATE_NAMES as c, defaultTemplate as d, layoutChromeCss as f, patchMandatoryGates as g, mandatoryGatesMarkerComment as h, createSanitiser as i, signedInTemplate as l, MANDATORY_GATES_MARKER as m, ZitadelLogin as n, de as o, authFormTemplate as p, ThemeController as r, en as s, ZitadelLogout as t, passkeyUpsellTemplate as u, validateBranding as v, submitStep as w, resolveTheme as x, applyBrandingTokens as y };
3302
+ 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 };
3335
3303
 
3336
- //# sourceMappingURL=orchestrator-CNKo2WQG.mjs.map
3304
+ //# sourceMappingURL=orchestrator-Dds72XrU.mjs.map