@zitadel/components 0.1.0-alpha.18 → 0.1.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
- import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-5-wYIl88.mjs";
2
- import { r as tokensCss } from "./tokens-BV-PPbQd.mjs";
1
+ import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-B6QmUAzo.mjs";
2
+ import { r as tokensCss } from "./tokens-DSnSf1mf.mjs";
3
3
  import { manifestRegistry } from "./manifests.mjs";
4
4
  import { t as default_default } from "./default-Bnjy2dJB.mjs";
5
5
  import { LitElement, css, html, nothing } from "lit";
6
6
  import { customElement, property, state } from "lit/decorators.js";
7
7
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
8
+ import { isCanonicalLoopbackHttpUrl } from "@zitadel/config/branding-url";
8
9
  import { MANDATORY_GATES_TAG } from "@zitadel/config/template";
9
10
  import { Liquid } from "liquidjs";
10
11
  import DOMPurify from "dompurify";
@@ -130,6 +131,30 @@ function isRecord(value) {
130
131
  return typeof value === "object" && value !== null && !Array.isArray(value);
131
132
  }
132
133
  //#endregion
134
+ //#region ../shared-component-styles/dist/attribution-markup.mjs
135
+ /**
136
+ * Figma sign-in attribution chip (`6593:141767`): "Secured with" + 65×16 logotype.
137
+ * Shared by Lit orchestrator HTML, dev playground, and paired React playgrounds.
138
+ */
139
+ 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>";
140
+ /** Inner markup for the default Zitadel attribution pill (label + logotype). */
141
+ function zitadelAttributionPillInnerHtml() {
142
+ return `Secured with <span class="zr-pill__logotype">${ZITADEL_ATTRIBUTION_LOGOTYPE_SVG}</span>`;
143
+ }
144
+ //#endregion
145
+ //#region src/internal/escape-html.ts
146
+ /**
147
+ * Escapes the five HTML-significant characters so a string is safe to
148
+ * interpolate into either element text or a double-quoted attribute value.
149
+ *
150
+ * Used for the orchestrator-owned attribution markup, which is appended after
151
+ * the Liquid output has already been sanitised — so it can't rely on the
152
+ * DOMPurify pass and must escape its own (tenant-supplied) values.
153
+ */
154
+ function escapeHtml(value) {
155
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
156
+ }
157
+ //#endregion
133
158
  //#region src/orchestrator/api-client.ts
134
159
  const apiRequestInit = { credentials: "include" };
135
160
  async function startFlow(api, input) {
@@ -175,2283 +200,2342 @@ async function revokeSession$1(api) {
175
200
  await api.revokeMySession(apiRequestInit);
176
201
  }
177
202
  //#endregion
178
- //#region ../api/dist/runtime/base-url.mjs
179
- let proxyPath = "";
180
- function getProxyPath() {
181
- return proxyPath;
182
- }
183
- function setProxyPath(path) {
184
- proxyPath = path;
185
- }
186
- //#endregion
187
- //#region ../api/dist/chunk-CfYAbeIz.mjs
188
- var __defProp = Object.defineProperty;
189
- var __exportAll = (all, no_symbols) => {
190
- let target = {};
191
- for (var name in all) __defProp(target, name, {
192
- get: all[name],
193
- enumerable: true
194
- });
195
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
196
- return target;
197
- };
198
- //#endregion
199
- //#region ../api/dist/generated/endpoints/zitadelNextGen.mjs
200
- /**
201
- * Generated by orval v8.10.0 🍺
202
- * Do not edit manually.
203
- * Zitadel NextGen
204
- * This is the next generation of the Zitadel identity platform.
205
- * OpenAPI spec version: 0.0.1
206
- */
207
- var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
208
- completeClaim: () => completeClaim,
209
- createAuthAttempt: () => createAuthAttempt,
210
- createBranding: () => createBranding,
211
- createFlow: () => createFlow,
212
- createFlowDefinition: () => createFlowDefinition,
213
- createHandoff: () => createHandoff,
214
- createProject: () => createProject,
215
- createSchema: () => createSchema,
216
- createSession: () => createSession,
217
- createTeam: () => createTeam,
218
- createUser: () => createUser,
219
- deleteFlowDefinition: () => deleteFlowDefinition,
220
- deleteTeam: () => deleteTeam,
221
- deleteUserByID: () => deleteUserByID,
222
- exchangeHandoff: () => exchangeHandoff,
223
- getAuthAttempt: () => getAuthAttempt,
224
- getBrandingById: () => getBrandingById,
225
- getClaimStatus: () => getClaimStatus,
226
- getCompleteClaimUrl: () => getCompleteClaimUrl,
227
- getCreateAuthAttemptUrl: () => getCreateAuthAttemptUrl,
228
- getCreateBrandingUrl: () => getCreateBrandingUrl,
229
- getCreateFlowDefinitionUrl: () => getCreateFlowDefinitionUrl,
230
- getCreateFlowUrl: () => getCreateFlowUrl,
231
- getCreateHandoffUrl: () => getCreateHandoffUrl,
232
- getCreateProjectUrl: () => getCreateProjectUrl,
233
- getCreateSchemaUrl: () => getCreateSchemaUrl,
234
- getCreateSessionUrl: () => getCreateSessionUrl,
235
- getCreateTeamUrl: () => getCreateTeamUrl,
236
- getCreateUserUrl: () => getCreateUserUrl,
237
- getDeleteFlowDefinitionUrl: () => getDeleteFlowDefinitionUrl,
238
- getDeleteTeamUrl: () => getDeleteTeamUrl,
239
- getDeleteUserByIDUrl: () => getDeleteUserByIDUrl,
240
- getExchangeHandoffUrl: () => getExchangeHandoffUrl,
241
- getFlowDefinition: () => getFlowDefinition,
242
- getFlowStep: () => getFlowStep,
243
- getGetAuthAttemptUrl: () => getGetAuthAttemptUrl,
244
- getGetBrandingByIdUrl: () => getGetBrandingByIdUrl,
245
- getGetClaimStatusUrl: () => getGetClaimStatusUrl,
246
- getGetFlowDefinitionUrl: () => getGetFlowDefinitionUrl,
247
- getGetFlowStepUrl: () => getGetFlowStepUrl,
248
- getGetHealthUrl: () => getGetHealthUrl,
249
- getGetLiveUrl: () => getGetLiveUrl,
250
- getGetMySessionUrl: () => getGetMySessionUrl,
251
- getGetMyUserUrl: () => getGetMyUserUrl,
252
- getGetProjectUrl: () => getGetProjectUrl,
253
- getGetReadyUrl: () => getGetReadyUrl,
254
- getGetSchemaByIdUrl: () => getGetSchemaByIdUrl,
255
- getGetSessionUrl: () => getGetSessionUrl,
256
- getGetTeamUrl: () => getGetTeamUrl,
257
- getGetUserByIDUrl: () => getGetUserByIDUrl,
258
- getHealth: () => getHealth,
259
- getInitClaimUrl: () => getInitClaimUrl,
260
- getIssueChallengeUrl: () => getIssueChallengeUrl,
261
- getListBrandingUrl: () => getListBrandingUrl,
262
- getListFlowDefinitionsUrl: () => getListFlowDefinitionsUrl,
263
- getListSchemasUrl: () => getListSchemasUrl,
264
- getListUserPasskeysUrl: () => getListUserPasskeysUrl,
265
- getListUserTeamsUrl: () => getListUserTeamsUrl,
266
- getListUsersUrl: () => getListUsersUrl,
267
- getLive: () => getLive,
268
- getMySession: () => getMySession,
269
- getMyUser: () => getMyUser,
270
- getPatchProjectUrl: () => getPatchProjectUrl,
271
- getProject: () => getProject,
272
- getQueryProjectsUrl: () => getQueryProjectsUrl,
273
- getQuerySessionsUrl: () => getQuerySessionsUrl,
274
- getQueryTeamsUrl: () => getQueryTeamsUrl,
275
- getReady: () => getReady,
276
- getRevokeMySessionUrl: () => getRevokeMySessionUrl,
277
- getRevokeSessionUrl: () => getRevokeSessionUrl,
278
- getSchemaById: () => getSchemaById,
279
- getSession: () => getSession,
280
- getSetUserPasswordUrl: () => getSetUserPasswordUrl,
281
- getSubmitFlowStepUrl: () => getSubmitFlowStepUrl,
282
- getTeam: () => getTeam,
283
- getUpdateFlowDefinitionUrl: () => getUpdateFlowDefinitionUrl,
284
- getUpdateTeamUrl: () => getUpdateTeamUrl,
285
- getUserByID: () => getUserByID,
286
- getVerifyChallengeProofUrl: () => getVerifyChallengeProofUrl,
287
- initClaim: () => initClaim,
288
- issueChallenge: () => issueChallenge,
289
- listBranding: () => listBranding,
290
- listFlowDefinitions: () => listFlowDefinitions,
291
- listSchemas: () => listSchemas,
292
- listUserPasskeys: () => listUserPasskeys,
293
- listUserTeams: () => listUserTeams,
294
- listUsers: () => listUsers,
295
- patchProject: () => patchProject,
296
- queryProjects: () => queryProjects,
297
- querySessions: () => querySessions,
298
- queryTeams: () => queryTeams,
299
- revokeMySession: () => revokeMySession,
300
- revokeSession: () => revokeSession,
301
- setUserPassword: () => setUserPassword,
302
- submitFlowStep: () => submitFlowStep,
303
- updateFlowDefinition: () => updateFlowDefinition,
304
- updateTeam: () => updateTeam,
305
- verifyChallengeProof: () => verifyChallengeProof
306
- });
307
- const getGetHealthUrl = () => {
308
- return `${getProxyPath()}/healthz`;
309
- };
203
+ //#region src/orchestrator/asset-fallback.ts
310
204
  /**
311
- * Check whether the server is healthy
312
- * @summary Check server health
205
+ * Broken-branding-asset degradation.
206
+ *
207
+ * A `logo_url` / `hero_url` that is well-formed but unreachable clears every
208
+ * gate on the way in — the CLI's zod shape check, the server's save gate —
209
+ * and then renders as a 0×0 `<img>`: a silent hole where the brand should
210
+ * be, with nothing in the console and nothing in `plan` output. The split
211
+ * designs are the worst case, because their `.zl-split__placeholder` is
212
+ * keyed on *no asset being configured*, so a configured-but-dead asset
213
+ * leaves the whole brand pane blank.
214
+ *
215
+ * The templates cannot fix this themselves. They go through DOMPurify
216
+ * (`sanitiser.ts`), which strips `onerror` along with every other inline
217
+ * handler, so the recovery has to be orchestrator-side: arm a real `error`
218
+ * listener on each rendered image after every commit, hide what fails, and
219
+ * put the placeholder back when that empties a brand pane.
313
220
  */
314
- const getHealth = async (options) => {
315
- return customFetch(getGetHealthUrl(), {
316
- ...options,
317
- method: "GET"
318
- });
319
- };
320
- const getGetLiveUrl = () => {
321
- return `${getProxyPath()}/livez`;
322
- };
221
+ /** Marks an image whose `error` listener is already attached. */
222
+ const ARMED_ATTR = "data-zl-asset-armed";
223
+ /** Marks an image that failed to load; `layout-chrome.css` hides it. */
224
+ const BROKEN_ATTR = "data-zl-asset-broken";
225
+ const BRAND_PANE_SELECTOR = ".zl-split__brand";
226
+ const PLACEHOLDER_CLASS = "zl-split__placeholder";
227
+ const AUTHORED_FALLBACK_ATTR = "data-zl-asset-fallback";
228
+ const AUTHORED_FALLBACK_CONTENT_ATTR = "data-zl-asset-fallback-content";
323
229
  /**
324
- * Check whether the server is started
325
- * @summary Check server liveness
230
+ * Arm every not-yet-armed `<img>` under `root`, and repair anything already
231
+ * broken. Idempotent: safe to call on every Lit commit, since `unsafeHTML`
232
+ * only re-parses when the rendered string actually changes and re-parsed
233
+ * nodes arrive without the marker attribute.
326
234
  */
327
- const getLive = async (options) => {
328
- return customFetch(getGetLiveUrl(), {
329
- ...options,
330
- method: "GET"
331
- });
332
- };
333
- const getGetReadyUrl = () => {
334
- return `${getProxyPath()}/readyz`;
335
- };
235
+ function armAssetFallbacks(root) {
236
+ let repairNeeded = false;
237
+ for (const img of root.querySelectorAll("img")) {
238
+ if (img.hasAttribute(ARMED_ATTR)) continue;
239
+ img.setAttribute(ARMED_ATTR, "");
240
+ if (img.getAttribute("src") && img.complete && img.naturalWidth === 0) {
241
+ markBroken(img);
242
+ repairNeeded = true;
243
+ continue;
244
+ }
245
+ img.addEventListener("error", () => {
246
+ markBroken(img);
247
+ repairBrandPanes(root);
248
+ }, { once: true });
249
+ }
250
+ if (repairNeeded) repairBrandPanes(root);
251
+ }
252
+ function markBroken(img) {
253
+ img.setAttribute(BROKEN_ATTR, "");
254
+ revealAuthoredFallback(img);
255
+ console.warn(`[zitadel-login] branding asset failed to load, hiding it: ${img.getAttribute("src") ?? ""}`);
256
+ }
336
257
  /**
337
- * Check whether the server is ready to accept requests
338
- * @summary Check server readiness
258
+ * Some shipped templates have a richer no-asset branch than the generic split
259
+ * placeholder (the hero design's brand mark and compact text). They render that
260
+ * branch hidden beside the image and opt in with data attributes, so the
261
+ * orchestrator can reveal the exact authored fallback without knowing template
262
+ * copy or structure.
339
263
  */
340
- const getReady = async (options) => {
341
- return customFetch(getGetReadyUrl(), {
342
- ...options,
343
- method: "GET"
344
- });
345
- };
346
- const getCreateUserUrl = (params) => {
347
- const normalizedParams = new URLSearchParams();
348
- Object.entries(params || {}).forEach(([key, value]) => {
349
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
350
- });
351
- const stringifiedParams = normalizedParams.toString();
352
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
353
- };
264
+ function revealAuthoredFallback(img) {
265
+ if (!img.hasAttribute(AUTHORED_FALLBACK_ATTR)) return;
266
+ const fallback = img.nextElementSibling;
267
+ if (fallback?.hasAttribute(AUTHORED_FALLBACK_CONTENT_ATTR)) fallback.removeAttribute("hidden");
268
+ }
354
269
  /**
355
- * @summary Create user
270
+ * Restore `.zl-split__placeholder` in any split brand pane whose only content
271
+ * was assets that all failed. Deliberately narrow: a pane is only repaired
272
+ * when it actually holds a broken image, so a pane an author left empty on
273
+ * purpose — or a hero design whose landing copy still renders — is never
274
+ * given decoration it did not ask for.
356
275
  */
357
- const createUser = async (createUserBody, params, options) => {
358
- return customFetch(getCreateUserUrl(params), {
359
- ...options,
360
- method: "POST",
361
- headers: {
362
- "Content-Type": "application/json",
363
- ...options?.headers
364
- },
365
- body: JSON.stringify(createUserBody)
366
- });
367
- };
368
- const getListUsersUrl = (params) => {
369
- const normalizedParams = new URLSearchParams();
370
- Object.entries(params || {}).forEach(([key, value]) => {
371
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
372
- });
373
- const stringifiedParams = normalizedParams.toString();
374
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
276
+ function repairBrandPanes(root) {
277
+ for (const pane of root.querySelectorAll(BRAND_PANE_SELECTOR)) {
278
+ if (pane.querySelector(`.${PLACEHOLDER_CLASS}`)) continue;
279
+ const children = Array.from(pane.children);
280
+ if (!children.some(isBrokenImage)) continue;
281
+ if (children.some((child) => !isBrokenImage(child))) continue;
282
+ const placeholder = pane.ownerDocument.createElement("div");
283
+ placeholder.className = PLACEHOLDER_CLASS;
284
+ placeholder.setAttribute("aria-hidden", "true");
285
+ pane.append(placeholder);
286
+ }
287
+ }
288
+ function isBrokenImage(node) {
289
+ return node.tagName === "IMG" && node.hasAttribute(BROKEN_ATTR);
290
+ }
291
+ //#endregion
292
+ //#region ../api/dist/generated/model/index.mjs
293
+ const CreateFlow201BrandingLayout = {
294
+ centered: "centered",
295
+ split: "split"
375
296
  };
297
+ //#endregion
298
+ //#region src/orchestrator/branding-validator.ts
376
299
  /**
377
- * @summary List users
300
+ * Lightweight paint-time validator for `Branding` payloads.
301
+ *
302
+ * Per `docs/design/branding/schema.md` §Shape invariants:
303
+ *
304
+ * 1. Every referenced URL is https, except logo/hero assets may use canonical
305
+ * loopback HTTP while the component itself renders on a loopback HTTP
306
+ * origin.
307
+ * 2. `layout` is in the documented enum.
308
+ * 3. `liquid_template` shape checks happen elsewhere (security pipeline runs
309
+ * server-side; structural validator is deferred until the full atom set
310
+ * lands).
311
+ *
312
+ * Failures are non-fatal: we collect issues, log a dev-build warning, and
313
+ * return a sanitised payload with the offending fields stripped (set to
314
+ * `undefined`) so the orchestrator falls back to the bundled defaults.
378
315
  */
379
- const listUsers = async (params, options) => {
380
- return customFetch(getListUsersUrl(params), {
381
- ...options,
382
- method: "GET"
383
- });
384
- };
385
- const getGetUserByIDUrl = (userId, params) => {
386
- const normalizedParams = new URLSearchParams();
387
- Object.entries(params || {}).forEach(([key, value]) => {
388
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
389
- });
390
- const stringifiedParams = normalizedParams.toString();
391
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}?${stringifiedParams}` : `${getProxyPath()}/users/${userId}`;
392
- };
316
+ const VALID_LAYOUTS = new Set(Object.values(CreateFlow201BrandingLayout));
317
+ function validateBranding(input, context = {}) {
318
+ if (!input) return {
319
+ branding: void 0,
320
+ issues: []
321
+ };
322
+ const issues = [];
323
+ const out = { ...input };
324
+ const allowLoopbackHttp = context.renderingOrigin !== void 0 && isCanonicalLoopbackHttpUrl(context.renderingOrigin);
325
+ if (out.layout && !VALID_LAYOUTS.has(out.layout)) {
326
+ issues.push(`Unknown layout "${out.layout}" — falling back to "centered".`);
327
+ out.layout = CreateFlow201BrandingLayout.centered;
328
+ }
329
+ out.logo_url = sanitiseUrl(out.logo_url, "logo_url", issues, { allowed: allowLoopbackHttp });
330
+ out.font_url = sanitiseUrl(out.font_url, "font_url", issues);
331
+ out.hero_url = sanitiseUrl(out.hero_url, "hero_url", issues, { allowed: allowLoopbackHttp });
332
+ if (out.assets) out.assets = {
333
+ logo_dark: sanitiseUrl(out.assets.logo_dark, "assets.logo_dark", issues),
334
+ favicon: sanitiseUrl(out.assets.favicon, "assets.favicon", issues),
335
+ background_image: sanitiseUrl(out.assets.background_image, "assets.background_image", issues)
336
+ };
337
+ return {
338
+ branding: out,
339
+ issues
340
+ };
341
+ }
342
+ function sanitiseUrl(value, field, issues, loopbackHttp) {
343
+ if (value == null || value === "") return;
344
+ try {
345
+ const parsed = new URL(value);
346
+ if (parsed.protocol === "http:" && loopbackHttp?.allowed && isCanonicalLoopbackHttpUrl(value)) return parsed.toString();
347
+ if (parsed.protocol !== "https:") {
348
+ const loopbackHint = loopbackHttp ? "; canonical loopback http is allowed only on loopback development pages" : "";
349
+ issues.push(`${field} must use https (got "${parsed.protocol}"${loopbackHint}) — dropped.`);
350
+ return;
351
+ }
352
+ return parsed.toString();
353
+ } catch {
354
+ issues.push(`${field} is not a valid URL — dropped.`);
355
+ return;
356
+ }
357
+ }
358
+ //#endregion
359
+ //#region src/orchestrator/exportparts.ts
393
360
  /**
394
- * @summary Get user by ID
361
+ * Forwards atom-internal CSS Shadow Parts through the orchestrator boundary.
362
+ *
363
+ * Atoms stamp `part="..."` on their internals and declare the public set in
364
+ * their manifest (`parts`). Those parts are visible one shadow boundary up —
365
+ * inside the orchestrator's shadow root — but not to the embedding page,
366
+ * because `::part()` pierces a single boundary unless each host in the chain
367
+ * re-exports (`exportparts`). Templates never author the forwarding (the
368
+ * sanitiser strips `exportparts`); the orchestrator stamps it after every
369
+ * render commit so the mapping stays canonical for template-rendered and
370
+ * gate-patched atoms alike.
371
+ *
372
+ * Exported names are mechanical, collision-free, and public contract
373
+ * (override-ladder Tier 2, same stability rules as token names):
374
+ * `<atom>-<part>` with the `zl-` prefix dropped — `zl-field`'s `input` part
375
+ * becomes `zitadel-login::part(field-input)` on the host page. Manifests are
376
+ * the canonical part catalogue; this module derives, never curates.
395
377
  */
396
- const getUserByID = async (userId, params, options) => {
397
- return customFetch(getGetUserByIDUrl(userId, params), {
398
- ...options,
399
- method: "GET"
400
- });
401
- };
402
- const getDeleteUserByIDUrl = (userId, params) => {
403
- const normalizedParams = new URLSearchParams();
404
- Object.entries(params || {}).forEach(([key, value]) => {
405
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
406
- });
407
- const stringifiedParams = normalizedParams.toString();
408
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}?${stringifiedParams}` : `${getProxyPath()}/users/${userId}`;
409
- };
378
+ const exportpartsByTag = new Map(manifestRegistry.filter((manifest) => manifest.parts.length > 0).map((manifest) => {
379
+ const short = manifest.tag.replace(/^zl-/, "");
380
+ const value = manifest.parts.map((part) => `${part}: ${short}-${part}`).join(", ");
381
+ return [manifest.tag, value];
382
+ }));
410
383
  /**
411
- * @summary Delete user by ID
384
+ * Stamps `exportparts` onto every known atom in `scope`. Idempotent — call
385
+ * after each render commit: `unsafeHTML` re-parses replace previously
386
+ * stamped nodes, and the mandatory-gates patcher appends atoms after
387
+ * sanitisation.
412
388
  */
413
- const deleteUserByID = async (userId, params, options) => {
414
- return customFetch(getDeleteUserByIDUrl(userId, params), {
415
- ...options,
416
- method: "DELETE"
417
- });
418
- };
419
- const getListUserPasskeysUrl = (userId, params) => {
420
- const normalizedParams = new URLSearchParams();
421
- Object.entries(params || {}).forEach(([key, value]) => {
422
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
423
- });
424
- const stringifiedParams = normalizedParams.toString();
425
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}/passkeys?${stringifiedParams}` : `${getProxyPath()}/users/${userId}/passkeys`;
426
- };
389
+ function stampExportparts(scope) {
390
+ for (const [tag, value] of exportpartsByTag) for (const el of scope.querySelectorAll(tag)) if (el.getAttribute("exportparts") !== value) el.setAttribute("exportparts", value);
391
+ }
392
+ //#endregion
393
+ //#region src/orchestrator/mandatory-gates.ts
394
+ const MANDATORY_GATES_MARKER = "ZL_MANDATORY_GATES";
395
+ /** Every atom tag that participates as a named form field. */
396
+ const FIELD_ATOM_TAGS = "zl-field, zl-select, zl-checkbox";
397
+ const mandatoryGatesMarkerComment = `<!--${MANDATORY_GATES_MARKER}-->`;
398
+ function patchMandatoryGates(html, step, locale) {
399
+ const template = document.createElement("template");
400
+ template.innerHTML = html;
401
+ const fragment = template.content;
402
+ const additions = collectMissingAtoms(fragment, step, locale);
403
+ const marker = findMarkerComment(fragment);
404
+ if (marker?.parentNode) {
405
+ for (const node of additions) marker.parentNode.insertBefore(node, marker);
406
+ marker.remove();
407
+ } else for (const node of additions) fragment.appendChild(node);
408
+ return template.innerHTML;
409
+ }
410
+ function collectMissingAtoms(fragment, step, locale) {
411
+ const additions = [];
412
+ if (step.fields) for (const field of step.fields) {
413
+ if (!field.required) continue;
414
+ if (hasFieldFor(fragment, field.name)) continue;
415
+ additions.push(buildField(field.name, field.text_key, field.type, locale, field.required));
416
+ }
417
+ if (step.actions && !hasPrimaryButton(fragment)) {
418
+ const primary = step.actions.find((action) => action.primary);
419
+ if (primary) additions.push(buildSubmit(primary.name, primary.text_key, locale));
420
+ }
421
+ return additions;
422
+ }
423
+ function hasPrimaryButton(fragment) {
424
+ return Boolean(fragment.querySelector("zl-button[hierarchy=\"primary\"]"));
425
+ }
426
+ function hasFieldFor(fragment, name) {
427
+ for (const field of fragment.querySelectorAll(FIELD_ATOM_TAGS)) if (field.getAttribute("name") === name) return true;
428
+ return false;
429
+ }
430
+ function findMarkerComment(fragment) {
431
+ const walker = (fragment.ownerDocument ?? document).createTreeWalker(fragment, NodeFilter.SHOW_COMMENT);
432
+ let node = walker.nextNode();
433
+ while (node) {
434
+ if (node.nodeValue?.trim() === "ZL_MANDATORY_GATES") return node;
435
+ node = walker.nextNode();
436
+ }
437
+ return null;
438
+ }
439
+ function buildField(name, textKey, type, locale, required) {
440
+ const el = document.createElement("zl-field");
441
+ el.setAttribute("name", name);
442
+ el.setAttribute("label", lookup(locale, textKey ?? name));
443
+ el.setAttribute("type", type);
444
+ if (required) el.setAttribute("required", "");
445
+ return el;
446
+ }
447
+ function buildSubmit(name, textKey, locale) {
448
+ const el = document.createElement("zl-button");
449
+ el.setAttribute("hierarchy", "primary");
450
+ el.setAttribute("size", "medium");
451
+ el.setAttribute("type", "submit");
452
+ el.setAttribute("block", "");
453
+ el.setAttribute("action", name);
454
+ el.setAttribute("label", lookup(locale, textKey ?? "submit.continue"));
455
+ return el;
456
+ }
457
+ function lookup(locale, key) {
458
+ return locale[key] ?? key;
459
+ }
460
+ //#endregion
461
+ //#region src/orchestrator/liquid.ts
427
462
  /**
428
- * @summary List user passkeys
463
+ * LiquidJS engine factory for the `<zitadel-login>` orchestrator.
464
+ *
465
+ * Configures the engine per the security pipeline in
466
+ * `docs/design/flowengine/template-security.md` and registers the
467
+ * `| t` filter and `{% mandatory_gates %}` tag from
468
+ * `docs/design/branding/templates.md`.
469
+ *
470
+ * Hard rules:
471
+ *
472
+ * - `{{ }}` output is HTML-escaped (LiquidJS default since v10).
473
+ * - The `| raw` filter is overridden to a no-op string passthrough that still
474
+ * escapes (Layer 1 of the security pipeline).
475
+ * - Partials are loaded from an in-memory map; no filesystem access.
429
476
  */
430
- const listUserPasskeys = async (userId, params, options) => {
431
- return customFetch(getListUserPasskeysUrl(userId, params), {
432
- ...options,
433
- method: "GET"
434
- });
435
- };
436
- const getSetUserPasswordUrl = (userId, params) => {
437
- const normalizedParams = new URLSearchParams();
438
- Object.entries(params || {}).forEach(([key, value]) => {
439
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
440
- });
441
- const stringifiedParams = normalizedParams.toString();
442
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}/password?${stringifiedParams}` : `${getProxyPath()}/users/${userId}/password`;
443
- };
477
+ const TEMPLATE_NAMES$1 = { default: "default" };
444
478
  /**
445
- * @summary Set user password
479
+ * Maps catalog `error.*` keys with no `_<rule>` suffix (so
480
+ * {@link localiseFlowErrorKeys} can't derive the field from the key itself)
481
+ * to their field name — the credential errors with bespoke catalog copy
482
+ * (`error.email_exists`, `error.invalid_credentials`, …). Rule-suffixed keys
483
+ * (`error.<field>_<rule>`) derive their field from the key. Consumed only by
484
+ * {@link localiseFlowErrorKeys}, which stamps the resolved `field` onto the
485
+ * error; the template filters (`fieldError` / `formLevelError`) then route by
486
+ * that `field`, and an error whose field the step omits is downgraded to a
487
+ * fieldless banner message.
446
488
  */
447
- const setUserPassword = async (userId, setUserPasswordBody, params, options) => {
448
- return customFetch(getSetUserPasswordUrl(userId, params), {
449
- ...options,
450
- method: "PUT",
451
- headers: {
452
- "Content-Type": "application/json",
453
- ...options?.headers
489
+ const fieldErrorKeys = {
490
+ "error.email_required": "email",
491
+ "error.email_invalid": "email",
492
+ "error.email_exists": "email",
493
+ "error.password_required": "password",
494
+ "error.password_incorrect": "password",
495
+ "error.invalid_credentials": "password"
496
+ };
497
+ function createLiquidEngine(options) {
498
+ const engine = new Liquid({
499
+ templates: {
500
+ [TEMPLATE_NAMES$1.default]: default_default,
501
+ ...options.templates ?? {}
454
502
  },
455
- body: JSON.stringify(setUserPasswordBody)
503
+ cache: true,
504
+ jsTruthy: true,
505
+ relativeReference: false,
506
+ strictFilters: false,
507
+ strictVariables: false,
508
+ outputEscape: "escape"
456
509
  });
457
- };
458
- const getListUserTeamsUrl = (userId, params) => {
459
- const normalizedParams = new URLSearchParams();
460
- Object.entries(params || {}).forEach(([key, value]) => {
461
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
510
+ engine.registerFilter("raw", (value) => stringify(value));
511
+ const warnedMissingKeys = /* @__PURE__ */ new Set();
512
+ engine.registerFilter("t", function tFilter(key, ...args) {
513
+ const lookupKey = stringify(key);
514
+ let template = options.locale[lookupKey] ?? injectedKeyFallback(options.locale, lookupKey) ?? fieldLabelFallback(lookupKey);
515
+ if (template === void 0) {
516
+ template = lookupKey;
517
+ if (lookupKey !== "" && !warnedMissingKeys.has(lookupKey)) {
518
+ warnedMissingKeys.add(lookupKey);
519
+ console.warn(`[zitadel-login] missing text key "${lookupKey}" — rendering the raw key`);
520
+ }
521
+ }
522
+ return interpolate(template, args.map(stringify));
462
523
  });
463
- const stringifiedParams = normalizedParams.toString();
464
- return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}/teams?${stringifiedParams}` : `${getProxyPath()}/users/${userId}/teams`;
465
- };
466
- /**
467
- * The user's team roster, ordered by team name. Each entry carries the
468
- team's name, so a client renders a page without resolving ids one by one.
469
-
470
- This is the N:N roster and it is not lifecycle ownership: the single team
471
- that owns the user's lifecycle is reported as `metadata.lifecycle_owner_team_id`
472
- on the user read endpoints. Memberships the user was removed from are not
473
- returned.
474
-
475
- * @summary List the teams a user belongs to
476
- */
477
- const listUserTeams = async (userId, params, options) => {
478
- return customFetch(getListUserTeamsUrl(userId, params), {
479
- ...options,
480
- method: "GET"
524
+ /** Resolves `{text_key}.placeholder` — empty when undefined (not the raw key). */
525
+ engine.registerFilter("fieldPlaceholder", (textKey) => {
526
+ const lookupKey = `${stringify(textKey)}.placeholder`;
527
+ return options.locale[lookupKey] ?? "";
481
528
  });
482
- };
483
- const getGetMyUserUrl = () => {
484
- return `${getProxyPath()}/users/me`;
485
- };
486
- /**
487
- * @summary Get my user information
488
- */
489
- const getMyUser = async (options) => {
490
- return customFetch(getGetMyUserUrl(), {
491
- ...options,
492
- method: "GET"
529
+ /** Resolves `{text_key}.help` — empty when undefined. */
530
+ engine.registerFilter("fieldHelp", (textKey) => {
531
+ const lookupKey = `${stringify(textKey)}.help`;
532
+ return options.locale[lookupKey] ?? "";
493
533
  });
494
- };
495
- const getCreateFlowUrl = () => {
496
- return `${getProxyPath()}/flow`;
497
- };
498
- /**
499
- * Resolves a flow definition based on purpose + audience context and returns
500
- the first capability step. Creates a new session implicitly unless
501
- `session_id` is provided (for step-up / reauth on an existing session).
502
-
503
- The response contains an `id` field — the flow handle. Use it as the path
504
- parameter for all subsequent `/flow/{id}/submit` calls.
505
-
506
- The response also sets an encrypted `HttpOnly` cookie (`_zflow`) containing
507
- the flow's orchestration state (current step, collected data, history).
508
- The server is stateless between requests — all flow state lives in this
509
- cookie. The browser sends it automatically on subsequent requests.
510
-
511
- * @summary Start a new flow
512
- */
513
- const createFlow = async (createFlowBody, options) => {
514
- return customFetch(getCreateFlowUrl(), {
515
- ...options,
516
- method: "POST",
517
- headers: {
518
- "Content-Type": "application/json",
519
- ...options?.headers
520
- },
521
- body: JSON.stringify(createFlowBody)
534
+ /**
535
+ * Builds `<zl-select>` options from a field's closed `validation.enum`. The
536
+ * wire only carries the allowed values (strings), so the label defaults to the
537
+ * value — author the enum with display-ready text. Piped through `| json` into
538
+ * the element's `options` attribute.
539
+ */
540
+ engine.registerFilter("selectOptions", (values) => {
541
+ if (!Array.isArray(values)) return [];
542
+ return values.map((value) => {
543
+ const v = stringify(value);
544
+ return {
545
+ value: v,
546
+ label: v
547
+ };
548
+ });
522
549
  });
523
- };
524
- const getGetFlowStepUrl = (id) => {
525
- return `${getProxyPath()}/flow/${id}`;
526
- };
527
- /**
528
- * Returns the current capability step without advancing the state machine.
529
- Useful for page reloads or re-rendering after a network error.
530
-
531
- * @summary Get current step (re-render)
532
- */
533
- const getFlowStep = async (id, options) => {
534
- return customFetch(getGetFlowStepUrl(id), {
535
- ...options,
536
- method: "GET"
550
+ /** Resolves `{text_key}.title` for form-level `<zl-alert heading>`. */
551
+ engine.registerFilter("alertHeading", (textKey) => {
552
+ const lookupKey = `${stringify(textKey)}.title`;
553
+ return options.locale[lookupKey] ?? "";
537
554
  });
538
- };
539
- const getSubmitFlowStepUrl = (id) => {
540
- return `${getProxyPath()}/flow/${id}/submit`;
541
- };
542
- /**
543
- * Submits user input for the current step. The server validates,
544
- processes (e.g., verifies a credential), advances the state machine
545
- through any invisible steps, and returns the next visible step.
546
-
547
- The response sets an updated encrypted `HttpOnly` cookie (`_zflow`)
548
- with the new flow state. The server is stateless — all orchestration
549
- state is carried in this cookie between requests.
550
-
551
- **Important:** The `id` in the response may differ from the `id` used in
552
- the request. This happens when a flow pivots (pushes a new flow onto the
553
- stack) or when a stacked flow completes (auto-pops to the parent flow).
554
- Always use the `id` from the latest response for the next request.
555
-
556
- ## Flow completion
557
-
558
- When `step.type` is `complete`, the flow is terminal. The `step.behavior`
559
- field tells the frontend what to do:
560
-
561
- | `behavior` | Action |
562
- |------------- |------------------------------------------------------------|
563
- | `redirect` | Navigate to `redirect_uri` (OIDC/SAML auth request done). |
564
- | `show` | Render the step as a success screen (e.g., registration). |
565
-
566
- A `complete` step is only returned when the **entire flow stack** is done.
567
- If a stacked flow (e.g., recovery pivoted from login) finishes, the server
568
- auto-pops to the parent flow and returns the parent's next step — the
569
- frontend never sees a `complete` for intermediate flows.
570
-
571
- * @summary Submit step data and advance
572
- */
573
- const submitFlowStep = async (id, submitFlowStepBody, options) => {
574
- return customFetch(getSubmitFlowStepUrl(id), {
575
- ...options,
576
- method: "POST",
577
- headers: {
578
- "Content-Type": "application/json",
579
- ...options?.headers
580
- },
581
- body: JSON.stringify(submitFlowStepBody)
555
+ /** Resolves `{text_key}.body` for form-level `<zl-alert>` message slot. */
556
+ engine.registerFilter("alertBody", (textKey) => {
557
+ const lookupKey = `${stringify(textKey)}.body`;
558
+ return options.locale[lookupKey] ?? "";
582
559
  });
583
- };
584
- const getCreateAuthAttemptUrl = () => {
585
- return `${getProxyPath()}/auth_attempts`;
586
- };
560
+ /** Localized inline error for `fieldName`, or empty when none applies. */
561
+ engine.registerFilter("fieldError", (fieldName, errors) => {
562
+ const name = stringify(fieldName);
563
+ if (!Array.isArray(errors)) return "";
564
+ for (const item of errors) {
565
+ const err = item;
566
+ if (err.field !== name) continue;
567
+ return err.text_key ? options.locale[err.text_key] ?? err.text_key : err.message ?? "";
568
+ }
569
+ return "";
570
+ });
571
+ /**
572
+ * Automation-hook token for a field name: strips the
573
+ * `x-auth-methods#` credential prefix so testids stay method-named
574
+ * (`zitadel-field-password`), while the `name` attribute keeps the
575
+ * raw wire key. See hookName for the contract.
576
+ */
577
+ engine.registerFilter("testid", (fieldName) => hookName(stringify(fieldName)));
578
+ /**
579
+ * True when the error should render as `<zl-alert>`, not on a field. A
580
+ * field-scoped error (`field` set by {@link localiseFlowErrorKeys} when the
581
+ * step renders that field) routes inline instead; everything else — engine
582
+ * errors, and orphaned field errors already downgraded to a fieldless
583
+ * message — falls to the banner.
584
+ */
585
+ engine.registerFilter("formLevelError", (err) => {
586
+ return !err?.field;
587
+ });
588
+ engine.registerTag(MANDATORY_GATES_TAG, {
589
+ parse() {},
590
+ render() {
591
+ return mandatoryGatesMarkerComment;
592
+ }
593
+ });
594
+ return engine;
595
+ }
587
596
  /**
588
- * Starts a new authentication attempt. This is the entry point for the auth_attempts state machine.
589
-
590
- An attempt is an ephemeral (15-minute TTL) state machine that drives a single authentication round.
591
- It accepts factor challenges, verifies proofs, and completes into a session or handoff token.
592
-
593
- Accepts a project_id and challenge_nonce (from POST /bootstrap/challenge). For step-up re-auth,
594
- also include session_id to add factors to an existing session.
595
-
596
- * @summary Create a new authentication attempt
597
+ * Fallbacks for text keys the flow engine derives from tenant-chosen step
598
+ * names (`<step>.action.back` for the injected back action — see
599
+ * `internal/domain/flow_state_machine.go` `buildStep`). Step names are open,
600
+ * so no dictionary can enumerate them; a missing step-specific key falls back
601
+ * to its generic entry instead of leaking the raw key into the UI.
597
602
  */
598
- const createAuthAttempt = async (createAuthAttemptBody, options) => {
599
- return customFetch(getCreateAuthAttemptUrl(), {
600
- ...options,
601
- method: "POST",
602
- headers: {
603
- "Content-Type": "application/json",
604
- ...options?.headers
605
- },
606
- body: JSON.stringify(createAuthAttemptBody)
607
- });
608
- };
609
- const getGetAuthAttemptUrl = (attemptId) => {
610
- return `${getProxyPath()}/auth_attempts/${attemptId}`;
611
- };
603
+ const INJECTED_KEY_FALLBACKS = [{
604
+ suffix: ".action.back",
605
+ fallback: "action.back"
606
+ }];
607
+ function injectedKeyFallback(locale, key) {
608
+ for (const { suffix, fallback } of INJECTED_KEY_FALLBACKS) if (key.endsWith(suffix)) return locale[fallback];
609
+ }
612
610
  /**
613
- * Polls the current state of an authentication attempt.
614
-
615
- Returns the attempt's state, available factors for the next challenge,
616
- challenges issued so far, and any errors preventing progress.
617
-
618
- Use this for polling during long-running factor verifications (e.g., waiting for
619
- a federated IdP callback or a device flow).
620
-
621
- * @summary Get authentication attempt state
611
+ * Fallback for field-label keys (`<step>.field.<name>` see
612
+ * `FlowField.TextKey` in `internal/domain/flow_field_resolver.go`). Both
613
+ * halves are tenant-chosen (step names and schema property names), so no
614
+ * catalog can enumerate the keys; a miss renders a humanised property name
615
+ * ("dateOfBirth" → "Date of birth") instead of leaking the raw key into the
616
+ * form. Sub-keys like `.placeholder`/`.help` are excluded they resolve
617
+ * through their own filters, which stay empty on a miss.
622
618
  */
623
- const getAuthAttempt = async (attemptId, options) => {
624
- return customFetch(getGetAuthAttemptUrl(attemptId), {
625
- ...options,
626
- method: "GET"
627
- });
628
- };
629
- const getIssueChallengeUrl = (attemptId) => {
630
- return `${getProxyPath()}/auth_attempts/${attemptId}/challenges`;
631
- };
619
+ function fieldLabelFallback(key) {
620
+ const index = key.lastIndexOf(".field.");
621
+ if (index === -1) return void 0;
622
+ const field = key.slice(index + 7);
623
+ if (field === "" || field.includes(".")) return void 0;
624
+ return capitaliseFirst(humaniseFieldName(field));
625
+ }
632
626
  /**
633
- * Issues a single-factor verification challenge within an auth attempt.
634
-
635
- This advances the authentication state machine by requesting a specific factor method
636
- (password, passkey, TOTP, OTP via SMS, etc.). The server responds with challenge details
637
- including method, metadata, and any UI hints. The client then verifies the proof
638
- by calling POST /auth_attempts/{attempt_id}/challenges/{challenge_id}/verify.
639
-
640
- * @summary Issue a factor challenge
627
+ * Rule-suffix fallbacks for the server's field-validation keys
628
+ * (`error.<field>_<rule>` — see `FlowFieldValidationError.TextKey` in
629
+ * `internal/domain/flow_field_resolver.go`). Field names come from the
630
+ * tenant's user schema, so no catalog can enumerate the specific keys;
631
+ * a miss resolves to the rule's generic entry, interpolated with the
632
+ * field's label (`{0}`). The server spells the format rule `_invalid`
633
+ * (the catalog's existing convention, e.g. `error.email_invalid`), so
634
+ * that suffix takes the format wording; `_unknown_field` (a submitted
635
+ * name that is not a step field) takes the catch-all.
641
636
  */
642
- const issueChallenge = async (attemptId, issueChallengeBody, options) => {
643
- return customFetch(getIssueChallengeUrl(attemptId), {
644
- ...options,
645
- method: "POST",
646
- headers: {
647
- "Content-Type": "application/json",
648
- ...options?.headers
649
- },
650
- body: JSON.stringify(issueChallengeBody)
651
- });
652
- };
653
- const getVerifyChallengeProofUrl = (attemptId, challengeId) => {
654
- return `${getProxyPath()}/auth_attempts/${attemptId}/challenges/${challengeId}/verify`;
655
- };
656
- /**
657
- * Submits a proof (credential, code, assertion) to verify a factor challenge.
658
-
659
- The proof format depends on the challenge method. For example:
660
- - `password` method: { password: "" }
661
- - `totp` method: { totp: { code: "123456" } }
662
- - `passkey` method: { passkey: { assertion: "…" } }
663
- - `recovery_code` method: { recovery_code: "…" }
664
-
665
- On successful verification, the factor is written to the auth attempt.
666
- The attempt moves to the next pending challenge or completes if all required factors are verified.
667
-
668
- * @summary Verify a factor proof
669
- */
670
- const verifyChallengeProof = async (attemptId, challengeId, verifyChallengeProofBody, options) => {
671
- return customFetch(getVerifyChallengeProofUrl(attemptId, challengeId), {
672
- ...options,
673
- method: "POST",
674
- headers: {
675
- "Content-Type": "application/json",
676
- ...options?.headers
677
- },
678
- body: JSON.stringify(verifyChallengeProofBody)
679
- });
680
- };
681
- const getCreateHandoffUrl = (attemptId) => {
682
- return `${getProxyPath()}/auth_attempts/${attemptId}/handoff`;
683
- };
684
- /**
685
- * Completes the authentication attempt and mints a `handoff_token`.
686
-
687
- Call this after all required factors have been verified and the attempt is in `completed` state.
688
- The handoff token is short-lived (≤60 seconds) and must be exchanged at
689
- POST /sessions/exchange to receive the final session and session_token.
690
-
691
- The handoff token is:
692
- - Single-use (atomic exchange, no retry)
693
- - Audience-bound (requires matching project key for exchange)
694
- - Idempotency-safe within a 5-minute window (see conventions)
695
-
696
- * @summary Complete authentication and create handoff token
697
- */
698
- const createHandoff = async (attemptId, options) => {
699
- return customFetch(getCreateHandoffUrl(attemptId), {
700
- ...options,
701
- method: "POST"
702
- });
703
- };
704
- const getCreateProjectUrl = () => {
705
- return `${getProxyPath()}/projects`;
706
- };
707
- /**
708
- * @summary Create project
709
- */
710
- const createProject = async (createProjectBody, options) => {
711
- return customFetch(getCreateProjectUrl(), {
712
- ...options,
713
- method: "POST",
714
- headers: {
715
- "Content-Type": "application/json",
716
- ...options?.headers
717
- },
718
- body: JSON.stringify(createProjectBody)
719
- });
720
- };
721
- const getQueryProjectsUrl = () => {
722
- return `${getProxyPath()}/projects/query`;
723
- };
637
+ const FLOW_ERROR_RULE_FALLBACKS = [
638
+ {
639
+ suffix: "_required",
640
+ generic: "error.field_required"
641
+ },
642
+ {
643
+ suffix: "_min_length",
644
+ generic: "error.field_min_length"
645
+ },
646
+ {
647
+ suffix: "_max_length",
648
+ generic: "error.field_max_length"
649
+ },
650
+ {
651
+ suffix: "_format",
652
+ generic: "error.field_format"
653
+ },
654
+ {
655
+ suffix: "_invalid",
656
+ generic: "error.field_format"
657
+ },
658
+ {
659
+ suffix: "_unknown_field",
660
+ generic: "error.field_invalid"
661
+ }
662
+ ];
663
+ const FLOW_ERROR_CATCH_ALL_KEY = "error.field_invalid";
724
664
  /**
725
- * @summary Query projects
665
+ * Localises a `step.error` payload of `error.*` catalog keys —
666
+ * field-validation violations and general engine failures (e.g.
667
+ * `error.invalid_credentials`, `error.passkey_invalid`) alike.
668
+ * Returns `null` unless EVERY `"; "`-joined segment is an `error.*` key
669
+ * — the caller keeps other payloads (outcome tokens such as
670
+ * `user_not_found`) verbatim.
671
+ *
672
+ * Keys the locale knows pass through as `text_key` entries: the template
673
+ * localises them via `| t`, and `fieldErrorKeys` routes the known ones
674
+ * inline to their field. Unknown keys with a recognised rule suffix are
675
+ * pre-localised here from their generic {@link FLOW_ERROR_RULE_FALLBACKS}
676
+ * entry; unknown keys without one pass through as `text_key` (matching
677
+ * `| t`'s behaviour for non-validation keys such as
678
+ * `error.sign_in_server`, which localises via `.title`/`.body`).
726
679
  */
727
- const queryProjects = async (queryProjectsBody, options) => {
728
- return customFetch(getQueryProjectsUrl(), {
729
- ...options,
730
- method: "POST",
731
- headers: {
732
- "Content-Type": "application/json",
733
- ...options?.headers
734
- },
735
- body: JSON.stringify(queryProjectsBody)
736
- });
737
- };
738
- const getGetProjectUrl = (projectId) => {
739
- return `${getProxyPath()}/projects/${projectId}`;
740
- };
680
+ function localiseFlowErrorKeys(raw, ctx) {
681
+ const segments = raw.split("; ");
682
+ if (!segments.every((segment) => segment.startsWith("error."))) return null;
683
+ return segments.map((key) => localiseFlowErrorKey(key, ctx));
684
+ }
685
+ function localiseFlowErrorKey(key, ctx) {
686
+ const inlineField = fieldErrorKeys[key];
687
+ const orphanedInline = inlineField !== void 0 && ctx.fields !== void 0 && !ctx.fields.includes(inlineField);
688
+ if (!orphanedInline && ctx.locale[key] !== void 0) return inlineField !== void 0 ? {
689
+ field: inlineField,
690
+ text_key: key
691
+ } : { text_key: key };
692
+ for (const { suffix, generic } of FLOW_ERROR_RULE_FALLBACKS) {
693
+ if (!key.endsWith(suffix)) continue;
694
+ const field = key.slice(6, key.length - suffix.length);
695
+ if (field === "") break;
696
+ const message = capitaliseFirst(interpolate(ctx.locale[generic] ?? ctx.locale[FLOW_ERROR_CATCH_ALL_KEY] ?? "Please check {0}.", [fieldLabel(ctx, field)]));
697
+ return ctx.fields !== void 0 && ctx.fields.includes(field) ? {
698
+ field,
699
+ message
700
+ } : { message };
701
+ }
702
+ if (orphanedInline && ctx.locale[key] !== void 0) return { message: ctx.locale[key] };
703
+ return { text_key: key };
704
+ }
705
+ /** `<step>.field.<name>` from the locale, else a humanised field name. */
706
+ function fieldLabel(ctx, field) {
707
+ return ctx.locale[`${ctx.stepName}.field.${field}`] ?? humaniseFieldName(field);
708
+ }
741
709
  /**
742
- * Returns the current state of a project.
743
-
744
- * @summary Get project
710
+ * `x-auth-methods#password` "password", `givenName` "given name",
711
+ * `date_of_birth` → "date of birth".
745
712
  */
746
- const getProject = async (projectId, options) => {
747
- return customFetch(getGetProjectUrl(projectId), {
748
- ...options,
749
- method: "GET"
750
- });
751
- };
752
- const getPatchProjectUrl = (projectId) => {
753
- return `${getProxyPath()}/projects/${projectId}`;
754
- };
713
+ function humaniseFieldName(field) {
714
+ return (field.includes("#") ? field.split("#").pop() : field).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_.-]+/g, " ").trim().toLowerCase();
715
+ }
716
+ /** Messages may open with a lowercase field label; sentence-case them. */
717
+ function capitaliseFirst(text) {
718
+ return text.length > 0 ? text[0]?.toUpperCase() + text.slice(1) : text;
719
+ }
720
+ function stringify(value) {
721
+ if (value == null) return "";
722
+ if (typeof value === "string") return value;
723
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
724
+ try {
725
+ return JSON.stringify(value);
726
+ } catch {
727
+ return String(value);
728
+ }
729
+ }
755
730
  /**
756
- * Updates the state of a project.
757
-
758
- * @summary Update project
731
+ * Positional `{n}` substitution shared by the `| t` filter and the
732
+ * pre-localising fallback in {@link localiseFlowErrorKeys}, so both
733
+ * produce byte-identical output for the same template and args.
759
734
  */
760
- const patchProject = async (projectId, patchProjectBody, options) => {
761
- return customFetch(getPatchProjectUrl(projectId), {
762
- ...options,
763
- method: "PATCH",
764
- headers: {
765
- "Content-Type": "application/json",
766
- ...options?.headers
767
- },
768
- body: JSON.stringify(patchProjectBody)
735
+ function interpolate(template, args) {
736
+ if (args.length === 0) return template;
737
+ return template.replace(/\{(\d+)\}/g, (match, index) => {
738
+ return args[Number(index)] ?? match;
769
739
  });
770
- };
771
- const getInitClaimUrl = (projectId) => {
772
- return `${getProxyPath()}/projects/${projectId}/claim/init`;
773
- };
740
+ }
741
+ //#endregion
742
+ //#region src/orchestrator/locales/en.ts
774
743
  /**
775
- * Starts a claim challenge for an unclaimed project. Authenticated by the
776
- project secret, this mints a single-use, short-lived challenge and returns
777
- the `claim_url` the developer opens in a browser to complete the claim,
778
- together with the `challenge_id` the CLI polls with. The exact expiry is
779
- carried by `expires_at` on the response.
780
-
781
- * @summary Initialize a project claim
744
+ * Minimal English locale dictionary.
745
+ *
746
+ * The `| t` filter looks up `text_key` strings here. Missing keys fall through
747
+ * to the raw key (matches the spec in
748
+ * `docs/design/flowengine/flow-engine-guide.md`).
749
+ *
750
+ * Text keys are auto-generated by the server from the flow definition:
751
+ * - Step titles: `<step>.title`
752
+ * - Step descriptions: `<step>.description`
753
+ * - Field labels: `<step>.field.<field>`
754
+ * - Action labels: `<step>.action.<action>`
755
+ *
756
+ * Copy aligned to Figma screens file `xkvBjkOJ8ENuHdTGZHXezK` (May 2026).
757
+ * MVP only — multi-locale support is deferred.
782
758
  */
783
- const initClaim = async (projectId, options) => {
784
- return customFetch(getInitClaimUrl(projectId), {
785
- ...options,
786
- method: "POST"
787
- });
788
- };
789
- const getGetClaimStatusUrl = (projectId, params) => {
790
- const normalizedParams = new URLSearchParams();
791
- Object.entries(params || {}).forEach(([key, value]) => {
792
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
793
- });
794
- const stringifiedParams = normalizedParams.toString();
795
- return stringifiedParams.length > 0 ? `${getProxyPath()}/projects/${projectId}/claim/status?${stringifiedParams}` : `${getProxyPath()}/projects/${projectId}/claim/status`;
759
+ const en = {
760
+ "identifier.title": "Sign in",
761
+ "identifier.description": "Enter your email to continue",
762
+ "identifier.field.email": "Email",
763
+ "identifier.field.email.placeholder": "you@example.com",
764
+ "identifier.field.password": "Password",
765
+ "identifier.action.submit": "Sign in",
766
+ "identifier.action.continue": "Continue",
767
+ "identifier.action.passkey": "Sign in with a passkey",
768
+ "identifier.action.register.lead": "Don't have an account? ",
769
+ "identifier.action.register.link": "Sign up",
770
+ "authenticate.title": "Sign in",
771
+ "authenticate.description": "Enter your password",
772
+ "authenticate.field.password": "Password",
773
+ "authenticate.action.submit": "Sign in",
774
+ "choose-register.title": "Create your account",
775
+ "choose-register.description": "Choose how you'd like to sign up",
776
+ "choose-register.action.register-with-password": "Sign up with password",
777
+ "choose-register.action.register-with-passkey": "Sign up with passkey",
778
+ "collect-credentials.title": "Create your account",
779
+ "collect-credentials.description": "Set up your email and password",
780
+ "collect-credentials.field.email": "Email",
781
+ "collect-credentials.field.email.placeholder": "you@example.com",
782
+ "collect-credentials.field.password": "Password",
783
+ "collect-credentials.action.submit": "Sign up",
784
+ "register-password.title": "Create your password",
785
+ "register-password.description": "Choose a secure password for your account",
786
+ "register-password.field.password": "Password",
787
+ "register-password.action.submit": "Sign up",
788
+ "register-password.action.back": "Back",
789
+ "passkey-upsell.title": "Sign in faster next time",
790
+ "passkey-upsell.description": "No password needed ever again.",
791
+ "passkey-upsell.description.line1": "No password needed ever again.",
792
+ "passkey-upsell.description.line2": "Sign in with Face ID, Touch ID, or PIN.",
793
+ "passkey-upsell.action.passkey_register": "Set up passkey",
794
+ "passkey-upsell.action.skip": "Skip for now",
795
+ "passkey-upsell.action.setup": "Set up passkey",
796
+ "collect-passkey-email.title": "Create your account",
797
+ "collect-passkey-email.description": "Enter your email to set up a passkey",
798
+ "collect-passkey-email.field.email": "Email",
799
+ "collect-passkey-email.field.email.placeholder": "you@example.com",
800
+ "collect-passkey-email.action.submit": "Continue",
801
+ "passkey-enroll.title": "Set up your passkey",
802
+ "passkey-enroll.description": "Use Face ID, Touch ID, or your device PIN to create a passkey.",
803
+ "passkey-enroll.action.passkey_register": "Set up passkey",
804
+ "done.title": "You're signed in as",
805
+ "done.description": "",
806
+ "password.title": "Sign in",
807
+ "password.description": "Enter your password",
808
+ "password.field.password": "Password",
809
+ "password.action.signin": "Sign in",
810
+ "password.action.passkey": "Sign in with a passkey",
811
+ "password.action.back": "Back",
812
+ "password.action.register.lead": "Don't have an account? ",
813
+ "password.action.register.link": "Sign up",
814
+ "register.title": "Create your account",
815
+ "register.description": "",
816
+ "register.field.email": "Email",
817
+ "register.field.email.placeholder": "you@example.com",
818
+ "register.field.password": "Password",
819
+ "register.field.givenName": "Given name",
820
+ "register.field.familyName": "Family name",
821
+ "register.field.companyName": "Company name",
822
+ "register.field.dateOfBirth": "Date of birth",
823
+ "register.field.country": "Country",
824
+ "register.field.country.placeholder": "Select your country",
825
+ "register.field.terms": "I agree to the terms of service",
826
+ "register.action.password": "Continue with password",
827
+ "register.action.passkey": "Continue with a passkey",
828
+ "register.action.submit": "Sign up",
829
+ "register.action.back": "Back",
830
+ "register.action.sign_in.lead": "Already have an account? ",
831
+ "register.action.sign_in.link": "Sign in",
832
+ "complete.title": "You're signed in as",
833
+ "signed-in.continue": "Continue",
834
+ "signed-in.logout": "Sign out",
835
+ "passkey-login.title": "Sign in with your passkey",
836
+ "passkey-first.title": "Sign in",
837
+ "passkey-first.description": "Use your passkey to continue",
838
+ "passkey-first.action.passkey": "Continue with passkey",
839
+ "passkey-first.action.email_fallback": "Use email instead",
840
+ "recover.title": "Check your email",
841
+ "recover.description": "We sent a password reset link to your email address.",
842
+ "recover.action.back": "Back to sign in",
843
+ "submit.continue": "Continue",
844
+ "submit.signin": "Sign in",
845
+ "action.forgot_password": "Forgot password?",
846
+ "action.cancel": "Cancel",
847
+ "action.back": "Back",
848
+ "sso.redirect.title": "Redirecting to your provider…",
849
+ "passkey.pending.status": "Waiting for your passkey…",
850
+ "error.passkey_cancelled": "The passkey prompt was closed before completing.",
851
+ "error.passkey_timeout": "The passkey request timed out. Please try again.",
852
+ "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
853
+ "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
854
+ "error.passkey_unsupported": "This device does not support passkeys",
855
+ "error.passkey_failed": "Something went wrong. Please try again.",
856
+ "error.passkey_invalid": "This passkey could not be verified. Please try again.",
857
+ "error.passkey_registration_invalid": "The new passkey could not be verified. Please try registering it again.",
858
+ "error.email_required": "Please enter an email address",
859
+ "error.email_invalid": "Please enter a valid email",
860
+ "error.password_required": "Please enter a password",
861
+ "error.password_incorrect": "Wrong email or password.",
862
+ "error.email_exists": "An account with this email already exists.",
863
+ /** Figma sign-in error `6602:180268` — inline on password field. */
864
+ "error.invalid_credentials": "Wrong email or password.",
865
+ "error.required": "This field is required.",
866
+ "error.field_required": "{0} is required.",
867
+ "error.field_format": "Please enter a valid {0}.",
868
+ "error.field_min_length": "{0} is too short.",
869
+ "error.field_max_length": "{0} is too long.",
870
+ "error.field_invalid": "Please check {0}.",
871
+ "error.sign_in_server.title": "We couldn't complete your sign in.",
872
+ "error.sign_in_server.body": "Please try again in a few minutes"
873
+ };
874
+ //#endregion
875
+ //#region src/orchestrator/locales/de.ts
876
+ const de = {
877
+ "identifier.title": "Anmelden",
878
+ "identifier.description": "Gib deine E-Mail-Adresse ein, um fortzufahren",
879
+ "identifier.field.email": "E-Mail",
880
+ "identifier.field.email.placeholder": "du@beispiel.de",
881
+ "identifier.field.password": "Passwort",
882
+ "identifier.action.submit": "Anmelden",
883
+ "identifier.action.continue": "Weiter",
884
+ "identifier.action.passkey": "Mit Passkey anmelden",
885
+ "identifier.action.register.lead": "Noch kein Konto? ",
886
+ "identifier.action.register.link": "Registrieren",
887
+ "authenticate.title": "Anmelden",
888
+ "authenticate.description": "Gib dein Passwort ein",
889
+ "authenticate.field.password": "Passwort",
890
+ "authenticate.action.submit": "Anmelden",
891
+ "choose-register.title": "Konto erstellen",
892
+ "choose-register.description": "Wähle, wie du dich registrieren möchtest",
893
+ "choose-register.action.register-with-password": "Mit Passwort registrieren",
894
+ "choose-register.action.register-with-passkey": "Mit Passkey registrieren",
895
+ "collect-credentials.title": "Konto erstellen",
896
+ "collect-credentials.description": "Richte E-Mail und Passwort ein",
897
+ "collect-credentials.field.email": "E-Mail",
898
+ "collect-credentials.field.email.placeholder": "du@beispiel.de",
899
+ "collect-credentials.field.password": "Passwort",
900
+ "collect-credentials.action.submit": "Registrieren",
901
+ "register-password.title": "Passwort erstellen",
902
+ "register-password.description": "Wähle ein sicheres Passwort für dein Konto",
903
+ "register-password.field.password": "Passwort",
904
+ "register-password.action.submit": "Registrieren",
905
+ "register-password.action.back": "Zurück",
906
+ "passkey-upsell.title": "Nächstes Mal schneller anmelden",
907
+ "passkey-upsell.description": "Kein Passwort mehr nötig.",
908
+ "passkey-upsell.description.line1": "Kein Passwort mehr nötig.",
909
+ "passkey-upsell.description.line2": "Melde dich mit Face ID, Touch ID oder PIN an.",
910
+ "passkey-upsell.action.passkey_register": "Passkey einrichten",
911
+ "passkey-upsell.action.skip": "Vorerst überspringen",
912
+ "passkey-upsell.action.setup": "Passkey einrichten",
913
+ "collect-passkey-email.title": "Konto erstellen",
914
+ "collect-passkey-email.description": "Gib deine E-Mail-Adresse ein, um einen Passkey einzurichten",
915
+ "collect-passkey-email.field.email": "E-Mail",
916
+ "collect-passkey-email.field.email.placeholder": "du@beispiel.de",
917
+ "collect-passkey-email.action.submit": "Weiter",
918
+ "passkey-enroll.title": "Passkey einrichten",
919
+ "passkey-enroll.description": "Verwende Face ID, Touch ID oder die Geräte-PIN, um einen Passkey zu erstellen.",
920
+ "passkey-enroll.action.passkey_register": "Passkey einrichten",
921
+ "done.title": "Du bist angemeldet als",
922
+ "done.description": "",
923
+ "password.title": "Anmelden",
924
+ "password.description": "Gib dein Passwort ein",
925
+ "password.field.password": "Passwort",
926
+ "password.action.signin": "Anmelden",
927
+ "password.action.passkey": "Mit Passkey anmelden",
928
+ "password.action.back": "Zurück",
929
+ "password.action.register.lead": "Noch kein Konto? ",
930
+ "password.action.register.link": "Registrieren",
931
+ "register.title": "Konto erstellen",
932
+ "register.description": "",
933
+ "register.field.email": "E-Mail",
934
+ "register.field.email.placeholder": "du@beispiel.de",
935
+ "register.field.password": "Passwort",
936
+ "register.field.givenName": "Vorname",
937
+ "register.field.familyName": "Nachname",
938
+ "register.field.companyName": "Firmenname",
939
+ "register.field.dateOfBirth": "Geburtsdatum",
940
+ "register.field.country": "Land",
941
+ "register.field.country.placeholder": "Land auswählen",
942
+ "register.field.terms": "Ich stimme den Nutzungsbedingungen zu",
943
+ "register.action.password": "Mit Passwort fortfahren",
944
+ "register.action.passkey": "Weiter mit Passkey",
945
+ "register.action.submit": "Registrieren",
946
+ "register.action.back": "Zurück",
947
+ "register.action.sign_in.lead": "Bereits ein Konto? ",
948
+ "register.action.sign_in.link": "Anmelden",
949
+ "complete.title": "Du bist angemeldet als",
950
+ "signed-in.continue": "Weiter",
951
+ "signed-in.logout": "Abmelden",
952
+ "passkey-login.title": "Mit Passkey anmelden",
953
+ "passkey-first.title": "Anmelden",
954
+ "passkey-first.description": "Verwende deinen Passkey, um fortzufahren",
955
+ "passkey-first.action.passkey": "Weiter mit Passkey",
956
+ "passkey-first.action.email_fallback": "Stattdessen E-Mail verwenden",
957
+ "recover.title": "E-Mail prüfen",
958
+ "recover.description": "Wir haben einen Link zum Zurücksetzen des Passworts an deine E-Mail-Adresse gesendet.",
959
+ "recover.action.back": "Zurück zur Anmeldung",
960
+ "submit.continue": "Weiter",
961
+ "submit.signin": "Anmelden",
962
+ "action.forgot_password": "Passwort vergessen?",
963
+ "action.cancel": "Abbrechen",
964
+ "action.back": "Zurück",
965
+ "sso.redirect.title": "Weiterleitung zum Anbieter…",
966
+ "passkey.pending.status": "Warten auf deinen Passkey…",
967
+ "error.passkey_cancelled": "Die Passkey-Abfrage wurde vorzeitig geschlossen.",
968
+ "error.passkey_timeout": "Die Passkey-Anfrage hat zu lange gedauert. Bitte versuche es erneut.",
969
+ "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
970
+ "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
971
+ "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
972
+ "error.passkey_failed": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
973
+ "error.passkey_invalid": "Dieser Passkey konnte nicht bestätigt werden. Bitte versuche es erneut.",
974
+ "error.passkey_registration_invalid": "Der neue Passkey konnte nicht bestätigt werden. Bitte registriere ihn erneut.",
975
+ "error.email_required": "Bitte gib eine E-Mail-Adresse ein",
976
+ "error.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein",
977
+ "error.password_required": "Bitte gib ein Passwort ein",
978
+ "error.password_incorrect": "Falsche E-Mail oder falsches Passwort.",
979
+ "error.email_exists": "Ein Konto mit dieser E-Mail-Adresse existiert bereits.",
980
+ "error.invalid_credentials": "Falsche E-Mail oder falsches Passwort.",
981
+ "error.required": "Dieses Feld ist erforderlich.",
982
+ "error.field_required": "{0} ist erforderlich.",
983
+ "error.field_format": "Bitte gib einen gültigen Wert für {0} ein.",
984
+ "error.field_min_length": "{0} ist zu kurz.",
985
+ "error.field_max_length": "{0} ist zu lang.",
986
+ "error.field_invalid": "Bitte überprüfe {0}.",
987
+ "error.sign_in_server.title": "Anmeldung konnte nicht abgeschlossen werden.",
988
+ "error.sign_in_server.body": "Bitte versuche es in einigen Minuten erneut"
989
+ };
990
+ //#endregion
991
+ //#region src/orchestrator/locales/it.ts
992
+ const it = {
993
+ "identifier.title": "Accedi",
994
+ "identifier.description": "Inserisci la tua e-mail per continuare",
995
+ "identifier.field.email": "E-mail",
996
+ "identifier.field.email.placeholder": "tu@esempio.com",
997
+ "identifier.field.password": "Password",
998
+ "identifier.action.submit": "Accedi",
999
+ "identifier.action.continue": "Continua",
1000
+ "identifier.action.passkey": "Accedi con passkey",
1001
+ "identifier.action.register.lead": "Non hai un account? ",
1002
+ "identifier.action.register.link": "Registrati",
1003
+ "authenticate.title": "Accedi",
1004
+ "authenticate.description": "Inserisci la tua password",
1005
+ "authenticate.field.password": "Password",
1006
+ "authenticate.action.submit": "Accedi",
1007
+ "choose-register.title": "Crea il tuo account",
1008
+ "choose-register.description": "Scegli come registrarti",
1009
+ "choose-register.action.register-with-password": "Registrati con password",
1010
+ "choose-register.action.register-with-passkey": "Registrati con passkey",
1011
+ "collect-credentials.title": "Crea il tuo account",
1012
+ "collect-credentials.description": "Configura e-mail e password",
1013
+ "collect-credentials.field.email": "E-mail",
1014
+ "collect-credentials.field.email.placeholder": "tu@esempio.com",
1015
+ "collect-credentials.field.password": "Password",
1016
+ "collect-credentials.action.submit": "Registrati",
1017
+ "register-password.title": "Crea la tua password",
1018
+ "register-password.description": "Scegli una password sicura per il tuo account",
1019
+ "register-password.field.password": "Password",
1020
+ "register-password.action.submit": "Registrati",
1021
+ "register-password.action.back": "Indietro",
1022
+ "passkey-upsell.title": "Accedi più velocemente la prossima volta",
1023
+ "passkey-upsell.description": "Nessuna password necessaria.",
1024
+ "passkey-upsell.description.line1": "Nessuna password necessaria.",
1025
+ "passkey-upsell.description.line2": "Accedi con Face ID, Touch ID o PIN.",
1026
+ "passkey-upsell.action.passkey_register": "Configura passkey",
1027
+ "passkey-upsell.action.skip": "Salta per ora",
1028
+ "passkey-upsell.action.setup": "Configura passkey",
1029
+ "collect-passkey-email.title": "Crea il tuo account",
1030
+ "collect-passkey-email.description": "Inserisci la tua e-mail per configurare una passkey",
1031
+ "collect-passkey-email.field.email": "E-mail",
1032
+ "collect-passkey-email.field.email.placeholder": "tu@esempio.com",
1033
+ "collect-passkey-email.action.submit": "Continua",
1034
+ "passkey-enroll.title": "Configura la tua passkey",
1035
+ "passkey-enroll.description": "Usa Face ID, Touch ID o il PIN del dispositivo per creare una passkey.",
1036
+ "passkey-enroll.action.passkey_register": "Configura passkey",
1037
+ "done.title": "Sei connesso come",
1038
+ "done.description": "",
1039
+ "password.title": "Accedi",
1040
+ "password.description": "Inserisci la tua password",
1041
+ "password.field.password": "Password",
1042
+ "password.action.signin": "Accedi",
1043
+ "password.action.passkey": "Accedi con passkey",
1044
+ "password.action.back": "Indietro",
1045
+ "password.action.register.lead": "Non hai un account? ",
1046
+ "password.action.register.link": "Registrati",
1047
+ "register.title": "Crea il tuo account",
1048
+ "register.description": "",
1049
+ "register.field.email": "E-mail",
1050
+ "register.field.email.placeholder": "tu@esempio.com",
1051
+ "register.field.password": "Password",
1052
+ "register.field.givenName": "Nome",
1053
+ "register.field.familyName": "Cognome",
1054
+ "register.field.companyName": "Nome azienda",
1055
+ "register.field.dateOfBirth": "Data di nascita",
1056
+ "register.field.country": "Paese",
1057
+ "register.field.country.placeholder": "Seleziona il tuo paese",
1058
+ "register.field.terms": "Accetto i termini di servizio",
1059
+ "register.action.password": "Continua con password",
1060
+ "register.action.passkey": "Continua con passkey",
1061
+ "register.action.submit": "Registrati",
1062
+ "register.action.back": "Indietro",
1063
+ "register.action.sign_in.lead": "Hai già un account? ",
1064
+ "register.action.sign_in.link": "Accedi",
1065
+ "complete.title": "Sei connesso come",
1066
+ "signed-in.continue": "Continua",
1067
+ "signed-in.logout": "Esci",
1068
+ "passkey-login.title": "Accedi con la tua passkey",
1069
+ "passkey-first.title": "Accedi",
1070
+ "passkey-first.description": "Usa la tua passkey per continuare",
1071
+ "passkey-first.action.passkey": "Continua con passkey",
1072
+ "passkey-first.action.email_fallback": "Usa invece l'email",
1073
+ "recover.title": "Controlla la tua e-mail",
1074
+ "recover.description": "Abbiamo inviato un link per reimpostare la password al tuo indirizzo e-mail.",
1075
+ "recover.action.back": "Torna all'accesso",
1076
+ "submit.continue": "Continua",
1077
+ "submit.signin": "Accedi",
1078
+ "action.forgot_password": "Password dimenticata?",
1079
+ "action.cancel": "Annulla",
1080
+ "action.back": "Indietro",
1081
+ "sso.redirect.title": "Reindirizzamento al provider…",
1082
+ "passkey.pending.status": "In attesa della tua passkey…",
1083
+ "error.passkey_cancelled": "La richiesta della passkey è stata chiusa prima del completamento.",
1084
+ "error.passkey_timeout": "La richiesta della passkey è scaduta. Riprova.",
1085
+ "error.passkey_not_registered": "Questa passkey non è registrata. Accedi con e-mail e password.",
1086
+ "error.passkey_setup_failed": "La registrazione della passkey non è stata completata. Riprova.",
1087
+ "error.passkey_unsupported": "Questo dispositivo non supporta le passkey",
1088
+ "error.passkey_failed": "Qualcosa è andato storto. Riprova.",
1089
+ "error.passkey_invalid": "Non è stato possibile verificare questa passkey. Riprova.",
1090
+ "error.passkey_registration_invalid": "Non è stato possibile verificare la nuova passkey. Riprova a registrarla.",
1091
+ "error.email_required": "Inserisci un indirizzo e-mail",
1092
+ "error.email_invalid": "Inserisci un indirizzo e-mail valido",
1093
+ "error.password_required": "Inserisci una password",
1094
+ "error.password_incorrect": "E-mail o password errata.",
1095
+ "error.email_exists": "Esiste già un account con questo indirizzo e-mail.",
1096
+ "error.invalid_credentials": "E-mail o password errata.",
1097
+ "error.required": "Questo campo è obbligatorio.",
1098
+ "error.field_required": "{0} è obbligatorio.",
1099
+ "error.field_format": "Inserisci un valore valido per {0}.",
1100
+ "error.field_min_length": "{0} è troppo corto.",
1101
+ "error.field_max_length": "{0} è troppo lungo.",
1102
+ "error.field_invalid": "Controlla {0}.",
1103
+ "error.sign_in_server.title": "Non è stato possibile completare l'accesso.",
1104
+ "error.sign_in_server.body": "Riprova tra qualche minuto"
1105
+ };
1106
+ //#endregion
1107
+ //#region src/orchestrator/locales/business.ts
1108
+ const businessLocales = {
1109
+ en: {
1110
+ "identifier.field.email": "Work email",
1111
+ "identifier.field.email.placeholder": "you@company.com",
1112
+ "collect-credentials.field.email": "Work email",
1113
+ "collect-credentials.field.email.placeholder": "you@company.com",
1114
+ "collect-passkey-email.field.email": "Work email",
1115
+ "collect-passkey-email.field.email.placeholder": "you@company.com",
1116
+ "register.field.email": "Work email",
1117
+ "register.field.email.placeholder": "you@company.com"
1118
+ },
1119
+ de: {
1120
+ "identifier.field.email": "Geschäftliche E-Mail",
1121
+ "identifier.field.email.placeholder": "du@unternehmen.com",
1122
+ "collect-credentials.field.email": "Geschäftliche E-Mail",
1123
+ "collect-credentials.field.email.placeholder": "du@unternehmen.com",
1124
+ "collect-passkey-email.field.email": "Geschäftliche E-Mail",
1125
+ "collect-passkey-email.field.email.placeholder": "du@unternehmen.com",
1126
+ "register.field.email": "Geschäftliche E-Mail",
1127
+ "register.field.email.placeholder": "du@unternehmen.com"
1128
+ },
1129
+ it: {
1130
+ "identifier.field.email": "E-mail aziendale",
1131
+ "identifier.field.email.placeholder": "tu@azienda.com",
1132
+ "collect-credentials.field.email": "E-mail aziendale",
1133
+ "collect-credentials.field.email.placeholder": "tu@azienda.com",
1134
+ "collect-passkey-email.field.email": "E-mail aziendale",
1135
+ "collect-passkey-email.field.email.placeholder": "tu@azienda.com",
1136
+ "register.field.email": "E-mail aziendale",
1137
+ "register.field.email.placeholder": "tu@azienda.com"
1138
+ }
1139
+ };
1140
+ //#endregion
1141
+ //#region src/orchestrator/locales/index.ts
1142
+ const builtinLocales = {
1143
+ en,
1144
+ de,
1145
+ it
1146
+ };
1147
+ //#endregion
1148
+ //#region ../api/dist/runtime/base-url.mjs
1149
+ let proxyPath = "";
1150
+ function getProxyPath() {
1151
+ return proxyPath;
1152
+ }
1153
+ function setProxyPath(path) {
1154
+ proxyPath = path;
1155
+ }
1156
+ //#endregion
1157
+ //#region ../api/dist/chunk-CfYAbeIz.mjs
1158
+ var __defProp = Object.defineProperty;
1159
+ var __exportAll = (all, no_symbols) => {
1160
+ let target = {};
1161
+ for (var name in all) __defProp(target, name, {
1162
+ get: all[name],
1163
+ enumerable: true
1164
+ });
1165
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
1166
+ return target;
1167
+ };
1168
+ //#endregion
1169
+ //#region ../api/dist/generated/endpoints/zitadelNextGen.mjs
1170
+ /**
1171
+ * Generated by orval v8.10.0 🍺
1172
+ * Do not edit manually.
1173
+ * Zitadel NextGen
1174
+ * This is the next generation of the Zitadel identity platform.
1175
+ * OpenAPI spec version: 0.0.1
1176
+ */
1177
+ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
1178
+ completeClaim: () => completeClaim,
1179
+ createAuthAttempt: () => createAuthAttempt,
1180
+ createBranding: () => createBranding,
1181
+ createFlow: () => createFlow,
1182
+ createFlowDefinition: () => createFlowDefinition,
1183
+ createHandoff: () => createHandoff,
1184
+ createProject: () => createProject,
1185
+ createSchema: () => createSchema,
1186
+ createSession: () => createSession,
1187
+ createTeam: () => createTeam,
1188
+ createUser: () => createUser,
1189
+ deleteFlowDefinition: () => deleteFlowDefinition,
1190
+ deleteTeam: () => deleteTeam,
1191
+ deleteUserByID: () => deleteUserByID,
1192
+ exchangeHandoff: () => exchangeHandoff,
1193
+ getAuthAttempt: () => getAuthAttempt,
1194
+ getBrandingById: () => getBrandingById,
1195
+ getClaimStatus: () => getClaimStatus,
1196
+ getCompleteClaimUrl: () => getCompleteClaimUrl,
1197
+ getCreateAuthAttemptUrl: () => getCreateAuthAttemptUrl,
1198
+ getCreateBrandingUrl: () => getCreateBrandingUrl,
1199
+ getCreateFlowDefinitionUrl: () => getCreateFlowDefinitionUrl,
1200
+ getCreateFlowUrl: () => getCreateFlowUrl,
1201
+ getCreateHandoffUrl: () => getCreateHandoffUrl,
1202
+ getCreateProjectUrl: () => getCreateProjectUrl,
1203
+ getCreateSchemaUrl: () => getCreateSchemaUrl,
1204
+ getCreateSessionUrl: () => getCreateSessionUrl,
1205
+ getCreateTeamUrl: () => getCreateTeamUrl,
1206
+ getCreateUserUrl: () => getCreateUserUrl,
1207
+ getDeleteFlowDefinitionUrl: () => getDeleteFlowDefinitionUrl,
1208
+ getDeleteTeamUrl: () => getDeleteTeamUrl,
1209
+ getDeleteUserByIDUrl: () => getDeleteUserByIDUrl,
1210
+ getEvent: () => getEvent,
1211
+ getExchangeHandoffUrl: () => getExchangeHandoffUrl,
1212
+ getFlowDefinition: () => getFlowDefinition,
1213
+ getFlowStep: () => getFlowStep,
1214
+ getGetAuthAttemptUrl: () => getGetAuthAttemptUrl,
1215
+ getGetBrandingByIdUrl: () => getGetBrandingByIdUrl,
1216
+ getGetClaimStatusUrl: () => getGetClaimStatusUrl,
1217
+ getGetEventUrl: () => getGetEventUrl,
1218
+ getGetFlowDefinitionUrl: () => getGetFlowDefinitionUrl,
1219
+ getGetFlowStepUrl: () => getGetFlowStepUrl,
1220
+ getGetHealthUrl: () => getGetHealthUrl,
1221
+ getGetLiveUrl: () => getGetLiveUrl,
1222
+ getGetMySessionUrl: () => getGetMySessionUrl,
1223
+ getGetMyUserUrl: () => getGetMyUserUrl,
1224
+ getGetProjectUrl: () => getGetProjectUrl,
1225
+ getGetReadyUrl: () => getGetReadyUrl,
1226
+ getGetSchemaByIdUrl: () => getGetSchemaByIdUrl,
1227
+ getGetSessionUrl: () => getGetSessionUrl,
1228
+ getGetTeamUrl: () => getGetTeamUrl,
1229
+ getGetUserByIDUrl: () => getGetUserByIDUrl,
1230
+ getHealth: () => getHealth,
1231
+ getInitClaimUrl: () => getInitClaimUrl,
1232
+ getIssueChallengeUrl: () => getIssueChallengeUrl,
1233
+ getListBrandingUrl: () => getListBrandingUrl,
1234
+ getListEventsUrl: () => getListEventsUrl,
1235
+ getListFlowDefinitionsUrl: () => getListFlowDefinitionsUrl,
1236
+ getListSchemasUrl: () => getListSchemasUrl,
1237
+ getListUserPasskeysUrl: () => getListUserPasskeysUrl,
1238
+ getListUserTeamsUrl: () => getListUserTeamsUrl,
1239
+ getListUsersUrl: () => getListUsersUrl,
1240
+ getLive: () => getLive,
1241
+ getMySession: () => getMySession,
1242
+ getMyUser: () => getMyUser,
1243
+ getPatchProjectUrl: () => getPatchProjectUrl,
1244
+ getProject: () => getProject,
1245
+ getQueryProjectsUrl: () => getQueryProjectsUrl,
1246
+ getQuerySessionsUrl: () => getQuerySessionsUrl,
1247
+ getQueryTeamsUrl: () => getQueryTeamsUrl,
1248
+ getReady: () => getReady,
1249
+ getRevokeMySessionUrl: () => getRevokeMySessionUrl,
1250
+ getRevokeSessionUrl: () => getRevokeSessionUrl,
1251
+ getSchemaById: () => getSchemaById,
1252
+ getSession: () => getSession,
1253
+ getSetUserPasswordUrl: () => getSetUserPasswordUrl,
1254
+ getSubmitFlowStepUrl: () => getSubmitFlowStepUrl,
1255
+ getTeam: () => getTeam,
1256
+ getUpdateFlowDefinitionUrl: () => getUpdateFlowDefinitionUrl,
1257
+ getUpdateTeamUrl: () => getUpdateTeamUrl,
1258
+ getUserByID: () => getUserByID,
1259
+ getVerifyChallengeProofUrl: () => getVerifyChallengeProofUrl,
1260
+ initClaim: () => initClaim,
1261
+ issueChallenge: () => issueChallenge,
1262
+ listBranding: () => listBranding,
1263
+ listEvents: () => listEvents,
1264
+ listFlowDefinitions: () => listFlowDefinitions,
1265
+ listSchemas: () => listSchemas,
1266
+ listUserPasskeys: () => listUserPasskeys,
1267
+ listUserTeams: () => listUserTeams,
1268
+ listUsers: () => listUsers,
1269
+ patchProject: () => patchProject,
1270
+ queryProjects: () => queryProjects,
1271
+ querySessions: () => querySessions,
1272
+ queryTeams: () => queryTeams,
1273
+ revokeMySession: () => revokeMySession,
1274
+ revokeSession: () => revokeSession,
1275
+ setUserPassword: () => setUserPassword,
1276
+ submitFlowStep: () => submitFlowStep,
1277
+ updateFlowDefinition: () => updateFlowDefinition,
1278
+ updateTeam: () => updateTeam,
1279
+ verifyChallengeProof: () => verifyChallengeProof
1280
+ });
1281
+ const getGetHealthUrl = () => {
1282
+ return `${getProxyPath()}/healthz`;
796
1283
  };
797
1284
  /**
798
- * Polled by the CLI while a browser completes the claim. Authorized by the
799
- project secret that initiated the challenge. Returns `pending`, or
800
- `completed` with the owning team, the claim timestamp, and the dashboard
801
- URL once the browser leg has finished.
802
-
803
- * @summary Get claim status
1285
+ * Check whether the server is healthy
1286
+ * @summary Check server health
804
1287
  */
805
- const getClaimStatus = async (projectId, params, options) => {
806
- return customFetch(getGetClaimStatusUrl(projectId, params), {
1288
+ const getHealth = async (options) => {
1289
+ return customFetch(getGetHealthUrl(), {
807
1290
  ...options,
808
1291
  method: "GET"
809
1292
  });
810
1293
  };
811
- const getCompleteClaimUrl = (projectId) => {
812
- return `${getProxyPath()}/projects/${projectId}/claim/complete`;
1294
+ const getGetLiveUrl = () => {
1295
+ return `${getProxyPath()}/livez`;
813
1296
  };
814
1297
  /**
815
- * Called by the browser after the developer authenticates on the claim page.
816
- Authenticated by the `__nextgen_session` cookie, it attaches the project to
817
- the developer's personal team using the `challenge_id` from the claim URL as
818
- its single-use, browser-safe authorization.
819
-
820
- * @summary Complete a project claim
1298
+ * Check whether the server is started
1299
+ * @summary Check server liveness
821
1300
  */
822
- const completeClaim = async (projectId, completeClaimBody, options) => {
823
- return customFetch(getCompleteClaimUrl(projectId), {
1301
+ const getLive = async (options) => {
1302
+ return customFetch(getGetLiveUrl(), {
824
1303
  ...options,
825
- method: "POST",
826
- headers: {
827
- "Content-Type": "application/json",
828
- ...options?.headers
829
- },
830
- body: JSON.stringify(completeClaimBody)
1304
+ method: "GET"
831
1305
  });
832
1306
  };
833
- const getCreateSessionUrl = () => {
834
- return `${getProxyPath()}/sessions`;
1307
+ const getGetReadyUrl = () => {
1308
+ return `${getProxyPath()}/readyz`;
835
1309
  };
836
1310
  /**
837
- * Creates an anonymous session shell with no user and no factors (`state: building`).
838
-
839
- This is optional — an `auth_attempt` will create a session implicitly if none is provided.
840
- Use this explicitly when you want to:
841
- - Pre-allocate a `session_id` before the user is known, so device/telemetry signals
842
- can be correlated with the eventual authenticated session from the start.
843
- - Track anonymous state (bot detection, device fingerprint) that survives until authentication.
844
-
845
- Creating a session is an app-plane operation on the project credential
846
- (`session.write`). The returned `session_token` is a session credential for the
847
- end-user client, not a management scope: it is delivered as the
848
- `__nextgen_session` cookie and authorises the self-service operations
849
- `GET /sessions/me` and `DELETE /sessions/me` (`nextgenSession` scheme). The
850
- by-id operations `GET /sessions/{session_id}` and `DELETE /sessions/{session_id}`
851
- are operator endpoints and require `session.read` / `session.delete` instead.
852
-
853
- The `session_token` is superseded when a handoff exchange completes — clients must
854
- replace it at that point.
855
-
856
- Anonymous sessions expire aggressively (10-minute TTL). The TTL resets to the configured
857
- full session TTL when the first authentication factor is written via a completing `auth_attempt`.
858
-
859
- * @summary Create an anonymous session shell
1311
+ * Check whether the server is ready to accept requests
1312
+ * @summary Check server readiness
860
1313
  */
861
- const createSession = async (createSessionBody, options) => {
862
- return customFetch(getCreateSessionUrl(), {
1314
+ const getReady = async (options) => {
1315
+ return customFetch(getGetReadyUrl(), {
863
1316
  ...options,
864
- method: "POST",
865
- headers: {
866
- "Content-Type": "application/json",
867
- ...options?.headers
868
- },
869
- body: JSON.stringify(createSessionBody)
1317
+ method: "GET"
870
1318
  });
871
1319
  };
872
- const getQuerySessionsUrl = (params) => {
1320
+ const getCreateUserUrl = (params) => {
873
1321
  const normalizedParams = new URLSearchParams();
874
1322
  Object.entries(params || {}).forEach(([key, value]) => {
875
1323
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
876
1324
  });
877
1325
  const stringifiedParams = normalizedParams.toString();
878
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/query?${stringifiedParams}` : `${getProxyPath()}/sessions/query`;
1326
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
879
1327
  };
880
1328
  /**
881
- * Returns the sessions of a project, paginated with a cursor.
882
- Sessions of every lifecycle state are returned; each carries its `state`.
883
- Requires `session.read` permission.
884
-
885
- * @summary Query sessions
1329
+ * @summary Create user
886
1330
  */
887
- const querySessions = async (querySessionsBody, params, options) => {
888
- return customFetch(getQuerySessionsUrl(params), {
1331
+ const createUser = async (createUserBody, params, options) => {
1332
+ return customFetch(getCreateUserUrl(params), {
889
1333
  ...options,
890
1334
  method: "POST",
891
1335
  headers: {
892
1336
  "Content-Type": "application/json",
893
1337
  ...options?.headers
894
1338
  },
895
- body: JSON.stringify(querySessionsBody)
1339
+ body: JSON.stringify(createUserBody)
896
1340
  });
897
1341
  };
898
- const getExchangeHandoffUrl = (params) => {
1342
+ const getListUsersUrl = (params) => {
899
1343
  const normalizedParams = new URLSearchParams();
900
1344
  Object.entries(params || {}).forEach(([key, value]) => {
901
1345
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
902
1346
  });
903
1347
  const stringifiedParams = normalizedParams.toString();
904
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/exchange?${stringifiedParams}` : `${getProxyPath()}/sessions/exchange`;
1348
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users?${stringifiedParams}` : `${getProxyPath()}/users`;
905
1349
  };
906
1350
  /**
907
- * Consumes a one-time `handoff_token` minted by `POST /auth_attempts/{id}/handoff`
908
- and returns the resulting session and a `session_token`.
909
-
910
- The server resolves the originating `auth_attempt` from the token and then:
911
-
912
- | Originating auth_attempt | Outcome |
913
- |---|---|
914
- | No `session_id` | A new authenticated session is **created**. |
915
- | `session_id` points to an anonymous shell | Existing session is **upgraded** — user and factors written in, TTL reset to full session TTL. |
916
- | `session_id` points to an active session (step-up) | Existing session is **upgraded** — new factors merged, `assurance_levels[]` expanded. |
917
-
918
- The response shape is identical in all three cases.
919
-
920
- The `session_token` supersedes any previously issued `session_token` for the same session.
921
- Clients must replace their stored token at this point.
922
-
923
- Requires a project service key (OAuth2 client credentials).
924
-
925
- * @summary Exchange handoff token for a session
1351
+ * @summary List users
926
1352
  */
927
- const exchangeHandoff = async (exchangeHandoffBody, params, options) => {
928
- return customFetch(getExchangeHandoffUrl(params), {
1353
+ const listUsers = async (params, options) => {
1354
+ return customFetch(getListUsersUrl(params), {
929
1355
  ...options,
930
- method: "POST",
931
- headers: {
932
- "Content-Type": "application/json",
933
- ...options?.headers
934
- },
935
- body: JSON.stringify(exchangeHandoffBody)
1356
+ method: "GET"
936
1357
  });
937
1358
  };
938
- const getGetSessionUrl = (sessionId, params) => {
1359
+ const getGetUserByIDUrl = (userId, params) => {
939
1360
  const normalizedParams = new URLSearchParams();
940
1361
  Object.entries(params || {}).forEach(([key, value]) => {
941
1362
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
942
1363
  });
943
1364
  const stringifiedParams = normalizedParams.toString();
944
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/${sessionId}?${stringifiedParams}` : `${getProxyPath()}/sessions/${sessionId}`;
1365
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}?${stringifiedParams}` : `${getProxyPath()}/users/${userId}`;
945
1366
  };
946
1367
  /**
947
- * Returns the current state of a session including its factors and all currently
948
- satisfied assurance levels.
949
-
950
- `assurance_levels[]` may shrink over time as factor freshness windows expire,
951
- without the session itself expiring. Use step-up authentication (a new `auth_attempt`
952
- against the same `session_id`) to restore a dropped assurance level.
953
-
954
- * @summary Get session state
1368
+ * @summary Get user by ID
955
1369
  */
956
- const getSession = async (sessionId, params, options) => {
957
- return customFetch(getGetSessionUrl(sessionId, params), {
1370
+ const getUserByID = async (userId, params, options) => {
1371
+ return customFetch(getGetUserByIDUrl(userId, params), {
958
1372
  ...options,
959
1373
  method: "GET"
960
1374
  });
961
1375
  };
962
- const getRevokeSessionUrl = (sessionId, params) => {
963
- const normalizedParams = new URLSearchParams();
964
- Object.entries(params || {}).forEach(([key, value]) => {
965
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
966
- });
967
- const stringifiedParams = normalizedParams.toString();
968
- return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/${sessionId}?${stringifiedParams}` : `${getProxyPath()}/sessions/${sessionId}`;
1376
+ const getDeleteUserByIDUrl = (userId) => {
1377
+ return `${getProxyPath()}/users/${userId}`;
969
1378
  };
970
1379
  /**
971
- * Permanently deletes the session, terminating it immediately.
972
-
973
- This is the operator revoke path and requires the `session.delete` scope on a
974
- project-bound credential. End-user logout with the `__nextgen_session` cookie is
975
- `DELETE /sessions/me` (`nextgenSession` scheme).
976
-
977
- Idempotent: deleting a session that does not exist (or was already deleted)
978
- still returns 204. After deletion, any tokens derived from the session are
979
- invalidated.
980
-
981
- * @summary Revoke session
1380
+ * @summary Delete user by ID
982
1381
  */
983
- const revokeSession = async (sessionId, params, options) => {
984
- return customFetch(getRevokeSessionUrl(sessionId, params), {
1382
+ const deleteUserByID = async (userId, options) => {
1383
+ return customFetch(getDeleteUserByIDUrl(userId), {
985
1384
  ...options,
986
1385
  method: "DELETE"
987
1386
  });
988
1387
  };
989
- const getGetMySessionUrl = () => {
990
- return `${getProxyPath()}/sessions/me`;
991
- };
992
- /**
993
- * Returns the current state of the current session including its factors and all currently
994
- satisfied assurance levels.
995
-
996
- `assurance_levels[]` may shrink over time as factor freshness windows expire,
997
- without the session itself expiring. Use step-up authentication (a new `auth_attempt`
998
- against the same `session_id`) to restore a dropped assurance level.
999
-
1000
- * @summary Get my session state
1001
- */
1002
- const getMySession = async (options) => {
1003
- return customFetch(getGetMySessionUrl(), {
1004
- ...options,
1005
- method: "GET"
1388
+ const getListUserPasskeysUrl = (userId, params) => {
1389
+ const normalizedParams = new URLSearchParams();
1390
+ Object.entries(params || {}).forEach(([key, value]) => {
1391
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1006
1392
  });
1007
- };
1008
- const getRevokeMySessionUrl = () => {
1009
- return `${getProxyPath()}/sessions/me`;
1393
+ const stringifiedParams = normalizedParams.toString();
1394
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}/passkeys?${stringifiedParams}` : `${getProxyPath()}/users/${userId}/passkeys`;
1010
1395
  };
1011
1396
  /**
1012
- * Logs out by permanently deleting the session.
1013
-
1014
- The `__nextgen_session` cookie issued at creation (or superseded by a handoff
1015
- exchange) is required. Idempotent: if the session is already gone this still
1016
- returns 204. Any tokens derived from the session are invalidated, and the
1017
- cookie itself is cleared in the response.
1018
-
1019
- * @summary Revoke my session
1397
+ * @summary List user passkeys
1020
1398
  */
1021
- const revokeMySession = async (options) => {
1022
- return customFetch(getRevokeMySessionUrl(), {
1399
+ const listUserPasskeys = async (userId, params, options) => {
1400
+ return customFetch(getListUserPasskeysUrl(userId, params), {
1023
1401
  ...options,
1024
- method: "DELETE"
1402
+ method: "GET"
1025
1403
  });
1026
1404
  };
1027
- const getCreateSchemaUrl = (params) => {
1028
- const normalizedParams = new URLSearchParams();
1029
- Object.entries(params || {}).forEach(([key, value]) => {
1030
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1031
- });
1032
- const stringifiedParams = normalizedParams.toString();
1033
- return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas?${stringifiedParams}` : `${getProxyPath()}/schemas`;
1405
+ const getSetUserPasswordUrl = (userId) => {
1406
+ return `${getProxyPath()}/users/${userId}/password`;
1034
1407
  };
1035
1408
  /**
1036
- * Create a new schema. The optional `$id` field is the JSON Schema document
1037
- URI used to identify the schema in future requests (GitOps-stable identity,
1038
- not a free-form resource primary key). When `$id` is omitted, the server
1039
- generates a `sch_*` URL. When provided, `$id` must be unique within the
1040
- project and should ideally be a valid URI pointing at where the schema can
1041
- be accessed.
1042
-
1043
- The schema can either be a concrete schema, e.g. a user schema, or a
1044
- schema-url which will be resolved by the server.
1045
-
1046
- * @summary Create new schema
1409
+ * @summary Set user password
1047
1410
  */
1048
- const createSchema = async (createSchemaBody, params, options) => {
1049
- return customFetch(getCreateSchemaUrl(params), {
1411
+ const setUserPassword = async (userId, setUserPasswordBody, options) => {
1412
+ return customFetch(getSetUserPasswordUrl(userId), {
1050
1413
  ...options,
1051
- method: "POST",
1414
+ method: "PUT",
1052
1415
  headers: {
1053
1416
  "Content-Type": "application/json",
1054
1417
  ...options?.headers
1055
1418
  },
1056
- body: JSON.stringify(createSchemaBody)
1419
+ body: JSON.stringify(setUserPasswordBody)
1057
1420
  });
1058
1421
  };
1059
- const getListSchemasUrl = (params) => {
1422
+ const getListUserTeamsUrl = (userId, params) => {
1060
1423
  const normalizedParams = new URLSearchParams();
1061
1424
  Object.entries(params || {}).forEach(([key, value]) => {
1062
1425
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1063
1426
  });
1064
1427
  const stringifiedParams = normalizedParams.toString();
1065
- return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas?${stringifiedParams}` : `${getProxyPath()}/schemas`;
1428
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/users/${userId}/teams?${stringifiedParams}` : `${getProxyPath()}/users/${userId}/teams`;
1066
1429
  };
1067
1430
  /**
1068
- * Retrieve a list of all schemas available in the system. This endpoint
1069
- supports pagination and filtering based on schema attributes.
1431
+ * The user's team roster, ordered by team name. Each entry carries the
1432
+ team's name, so a client renders a page without resolving ids one by one.
1433
+
1434
+ This is the N:N roster and it is not lifecycle ownership: the single team
1435
+ that owns the user's lifecycle is reported as `metadata.lifecycle_owner_team_id`
1436
+ on the user read endpoints. Memberships the user was removed from are not
1437
+ returned.
1070
1438
 
1071
- * @summary List all schemas
1439
+ * @summary List the teams a user belongs to
1072
1440
  */
1073
- const listSchemas = async (params, options) => {
1074
- return customFetch(getListSchemasUrl(params), {
1441
+ const listUserTeams = async (userId, params, options) => {
1442
+ return customFetch(getListUserTeamsUrl(userId, params), {
1075
1443
  ...options,
1076
1444
  method: "GET"
1077
1445
  });
1078
1446
  };
1079
- const getGetSchemaByIdUrl = (id, params) => {
1080
- const normalizedParams = new URLSearchParams();
1081
- Object.entries(params || {}).forEach(([key, value]) => {
1082
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1083
- });
1084
- const stringifiedParams = normalizedParams.toString();
1085
- return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas/${id}?${stringifiedParams}` : `${getProxyPath()}/schemas/${id}`;
1447
+ const getGetMyUserUrl = () => {
1448
+ return `${getProxyPath()}/users/me`;
1086
1449
  };
1087
1450
  /**
1088
- * Get a schema by its ID. This will return the default revision of the schema.
1089
- * @summary Get schema by ID
1451
+ * @summary Get my user information
1090
1452
  */
1091
- const getSchemaById = async (id, params, options) => {
1092
- return customFetch(getGetSchemaByIdUrl(id, params), {
1453
+ const getMyUser = async (options) => {
1454
+ return customFetch(getGetMyUserUrl(), {
1093
1455
  ...options,
1094
1456
  method: "GET"
1095
1457
  });
1096
1458
  };
1097
- const getCreateFlowDefinitionUrl = () => {
1098
- return `${getProxyPath()}/flow_definitions`;
1459
+ const getCreateFlowUrl = () => {
1460
+ return `${getProxyPath()}/flow`;
1099
1461
  };
1100
1462
  /**
1101
- * Creates a new flow definition.
1102
- Flow definitions are templates that define the sequence of steps (capabilities)
1103
- for a particular user journey (e.g., registration, login, password reset).
1463
+ * Resolves a flow definition based on purpose + audience context and returns
1464
+ the first capability step. Creates a new session implicitly unless
1465
+ `session_id` is provided (for step-up / reauth on an existing session).
1104
1466
 
1105
- Flow definitions are created based on the flow definition schema, which includes the flow's purpose, audience, and the steps involved.
1467
+ The response contains an `id` field the flow handle. Use it as the path
1468
+ parameter for all subsequent `/flow/{id}/submit` calls.
1106
1469
 
1107
- * @summary Create a new flow definition
1470
+ The response also sets an encrypted `HttpOnly` cookie (`_zflow`) containing
1471
+ the flow's orchestration state (current step, collected data, history).
1472
+ The server is stateless between requests — all flow state lives in this
1473
+ cookie. The browser sends it automatically on subsequent requests.
1474
+
1475
+ * @summary Start a new flow
1108
1476
  */
1109
- const createFlowDefinition = async (createFlowDefinitionBody, options) => {
1110
- return customFetch(getCreateFlowDefinitionUrl(), {
1477
+ const createFlow = async (createFlowBody, options) => {
1478
+ return customFetch(getCreateFlowUrl(), {
1111
1479
  ...options,
1112
1480
  method: "POST",
1113
1481
  headers: {
1114
1482
  "Content-Type": "application/json",
1115
1483
  ...options?.headers
1116
1484
  },
1117
- body: JSON.stringify(createFlowDefinitionBody)
1485
+ body: JSON.stringify(createFlowBody)
1118
1486
  });
1119
1487
  };
1120
- const getListFlowDefinitionsUrl = (params) => {
1121
- const normalizedParams = new URLSearchParams();
1122
- Object.entries(params || {}).forEach(([key, value]) => {
1123
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1124
- });
1125
- const stringifiedParams = normalizedParams.toString();
1126
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions?${stringifiedParams}` : `${getProxyPath()}/flow_definitions`;
1488
+ const getGetFlowStepUrl = (id) => {
1489
+ return `${getProxyPath()}/flow/${id}`;
1127
1490
  };
1128
1491
  /**
1129
- * Retrieves a list of all flow definitions.
1130
- This endpoint can be used to view existing flow definitions and their configurations.
1492
+ * Returns the current capability step without advancing the state machine.
1493
+ Useful for page reloads or re-rendering after a network error.
1131
1494
 
1132
- * @summary List flow definitions
1495
+ * @summary Get current step (re-render)
1133
1496
  */
1134
- const listFlowDefinitions = async (params, options) => {
1135
- return customFetch(getListFlowDefinitionsUrl(params), {
1497
+ const getFlowStep = async (id, options) => {
1498
+ return customFetch(getGetFlowStepUrl(id), {
1136
1499
  ...options,
1137
1500
  method: "GET"
1138
1501
  });
1139
1502
  };
1140
- const getGetFlowDefinitionUrl = (id, params) => {
1141
- const normalizedParams = new URLSearchParams();
1142
- Object.entries(params || {}).forEach(([key, value]) => {
1143
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1144
- });
1145
- const stringifiedParams = normalizedParams.toString();
1146
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions/${id}?${stringifiedParams}` : `${getProxyPath()}/flow_definitions/${id}`;
1503
+ const getSubmitFlowStepUrl = (id) => {
1504
+ return `${getProxyPath()}/flow/${id}/submit`;
1147
1505
  };
1148
1506
  /**
1149
- * Get a flow definition by id
1150
- * @summary Get a flow definition by id
1507
+ * Submits user input for the current step. The server validates,
1508
+ processes (e.g., verifies a credential), advances the state machine
1509
+ through any invisible steps, and returns the next visible step.
1510
+
1511
+ The response sets an updated encrypted `HttpOnly` cookie (`_zflow`)
1512
+ with the new flow state. The server is stateless — all orchestration
1513
+ state is carried in this cookie between requests.
1514
+
1515
+ **Important:** The `id` in the response may differ from the `id` used in
1516
+ the request. This happens when a flow pivots (pushes a new flow onto the
1517
+ stack) or when a stacked flow completes (auto-pops to the parent flow).
1518
+ Always use the `id` from the latest response for the next request.
1519
+
1520
+ ## Flow completion
1521
+
1522
+ When `step.type` is `complete`, the flow is terminal. The `step.behavior`
1523
+ field tells the frontend what to do:
1524
+
1525
+ | `behavior` | Action |
1526
+ |------------- |------------------------------------------------------------|
1527
+ | `redirect` | Navigate to `redirect_uri` (OIDC/SAML auth request done). |
1528
+ | `show` | Render the step as a success screen (e.g., registration). |
1529
+
1530
+ A `complete` step is only returned when the **entire flow stack** is done.
1531
+ If a stacked flow (e.g., recovery pivoted from login) finishes, the server
1532
+ auto-pops to the parent flow and returns the parent's next step — the
1533
+ frontend never sees a `complete` for intermediate flows.
1534
+
1535
+ * @summary Submit step data and advance
1151
1536
  */
1152
- const getFlowDefinition = async (id, params, options) => {
1153
- return customFetch(getGetFlowDefinitionUrl(id, params), {
1537
+ const submitFlowStep = async (id, submitFlowStepBody, options) => {
1538
+ return customFetch(getSubmitFlowStepUrl(id), {
1154
1539
  ...options,
1155
- method: "GET"
1540
+ method: "POST",
1541
+ headers: {
1542
+ "Content-Type": "application/json",
1543
+ ...options?.headers
1544
+ },
1545
+ body: JSON.stringify(submitFlowStepBody)
1156
1546
  });
1157
1547
  };
1158
- const getUpdateFlowDefinitionUrl = (id, params) => {
1159
- const normalizedParams = new URLSearchParams();
1160
- Object.entries(params || {}).forEach(([key, value]) => {
1161
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1162
- });
1163
- const stringifiedParams = normalizedParams.toString();
1164
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions/${id}?${stringifiedParams}` : `${getProxyPath()}/flow_definitions/${id}`;
1548
+ const getCreateAuthAttemptUrl = () => {
1549
+ return `${getProxyPath()}/auth_attempts`;
1165
1550
  };
1166
1551
  /**
1167
- * Update a flow definition by id. This endpoint replaces the existing flow definition.
1168
- If `flow_definition.status` is omitted, the current status is preserved
1552
+ * Starts a new authentication attempt. This is the entry point for the auth_attempts state machine.
1169
1553
 
1170
- * @summary Update a flow definition by id
1554
+ An attempt is an ephemeral (15-minute TTL) state machine that drives a single authentication round.
1555
+ It accepts factor challenges, verifies proofs, and completes into a session or handoff token.
1556
+
1557
+ Accepts a project_id and challenge_nonce (from POST /bootstrap/challenge). For step-up re-auth,
1558
+ also include session_id to add factors to an existing session.
1559
+
1560
+ * @summary Create a new authentication attempt
1171
1561
  */
1172
- const updateFlowDefinition = async (id, updateFlowDefinitionBody, params, options) => {
1173
- return customFetch(getUpdateFlowDefinitionUrl(id, params), {
1562
+ const createAuthAttempt = async (createAuthAttemptBody, options) => {
1563
+ return customFetch(getCreateAuthAttemptUrl(), {
1174
1564
  ...options,
1175
- method: "PUT",
1565
+ method: "POST",
1176
1566
  headers: {
1177
1567
  "Content-Type": "application/json",
1178
1568
  ...options?.headers
1179
1569
  },
1180
- body: JSON.stringify(updateFlowDefinitionBody)
1570
+ body: JSON.stringify(createAuthAttemptBody)
1181
1571
  });
1182
1572
  };
1183
- const getDeleteFlowDefinitionUrl = (id, params) => {
1184
- const normalizedParams = new URLSearchParams();
1185
- Object.entries(params || {}).forEach(([key, value]) => {
1186
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1187
- });
1188
- const stringifiedParams = normalizedParams.toString();
1189
- return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions/${id}?${stringifiedParams}` : `${getProxyPath()}/flow_definitions/${id}`;
1573
+ const getGetAuthAttemptUrl = (attemptId) => {
1574
+ return `${getProxyPath()}/auth_attempts/${attemptId}`;
1190
1575
  };
1191
1576
  /**
1192
- * Delete a flow definition by id.
1193
- If the flow definition is currently being used by a flow, the deletion will fail.
1194
- If the flow definition is the last active flow definition for a given purpose, the deletion will fail to prevent disruption of new flows being started for that purpose.
1577
+ * Polls the current state of an authentication attempt.
1195
1578
 
1196
- * @summary Delete a flow definition by id
1579
+ Returns the attempt's state, available factors for the next challenge,
1580
+ challenges issued so far, and any errors preventing progress.
1581
+
1582
+ Use this for polling during long-running factor verifications (e.g., waiting for
1583
+ a federated IdP callback or a device flow).
1584
+
1585
+ * @summary Get authentication attempt state
1197
1586
  */
1198
- const deleteFlowDefinition = async (id, params, options) => {
1199
- return customFetch(getDeleteFlowDefinitionUrl(id, params), {
1587
+ const getAuthAttempt = async (attemptId, options) => {
1588
+ return customFetch(getGetAuthAttemptUrl(attemptId), {
1200
1589
  ...options,
1201
- method: "DELETE"
1590
+ method: "GET"
1202
1591
  });
1203
1592
  };
1204
- const getCreateTeamUrl = (params) => {
1205
- const normalizedParams = new URLSearchParams();
1206
- Object.entries(params || {}).forEach(([key, value]) => {
1207
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1208
- });
1209
- const stringifiedParams = normalizedParams.toString();
1210
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams?${stringifiedParams}` : `${getProxyPath()}/teams`;
1593
+ const getIssueChallengeUrl = (attemptId) => {
1594
+ return `${getProxyPath()}/auth_attempts/${attemptId}/challenges`;
1211
1595
  };
1212
1596
  /**
1213
- * @summary Create team
1597
+ * Issues a single-factor verification challenge within an auth attempt.
1598
+
1599
+ This advances the authentication state machine by requesting a specific factor method
1600
+ (password, passkey, TOTP, OTP via SMS, etc.). The server responds with challenge details
1601
+ including method, metadata, and any UI hints. The client then verifies the proof
1602
+ by calling POST /auth_attempts/{attempt_id}/challenges/{challenge_id}/verify.
1603
+
1604
+ * @summary Issue a factor challenge
1214
1605
  */
1215
- const createTeam = async (createTeamBody, params, options) => {
1216
- return customFetch(getCreateTeamUrl(params), {
1606
+ const issueChallenge = async (attemptId, issueChallengeBody, options) => {
1607
+ return customFetch(getIssueChallengeUrl(attemptId), {
1217
1608
  ...options,
1218
1609
  method: "POST",
1219
1610
  headers: {
1220
1611
  "Content-Type": "application/json",
1221
1612
  ...options?.headers
1222
1613
  },
1223
- body: JSON.stringify(createTeamBody)
1614
+ body: JSON.stringify(issueChallengeBody)
1224
1615
  });
1225
1616
  };
1226
- const getQueryTeamsUrl = (params) => {
1227
- const normalizedParams = new URLSearchParams();
1228
- Object.entries(params || {}).forEach(([key, value]) => {
1229
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1230
- });
1231
- const stringifiedParams = normalizedParams.toString();
1232
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams/query?${stringifiedParams}` : `${getProxyPath()}/teams/query`;
1617
+ const getVerifyChallengeProofUrl = (attemptId, challengeId) => {
1618
+ return `${getProxyPath()}/auth_attempts/${attemptId}/challenges/${challengeId}/verify`;
1233
1619
  };
1234
1620
  /**
1235
- * Returns the teams of a project, paginated with a cursor.
1236
- Teams of every lifecycle status are returned; each carries its `status`.
1621
+ * Submits a proof (credential, code, assertion) to verify a factor challenge.
1237
1622
 
1238
- * @summary Query teams
1623
+ The proof format depends on the challenge method. For example:
1624
+ - `password` method: { password: "…" }
1625
+ - `totp` method: { totp: { code: "123456" } }
1626
+ - `passkey` method: { passkey: { assertion: "…" } }
1627
+ - `recovery_code` method: { recovery_code: "…" }
1628
+
1629
+ On successful verification, the factor is written to the auth attempt.
1630
+ The attempt moves to the next pending challenge or completes if all required factors are verified.
1631
+
1632
+ * @summary Verify a factor proof
1239
1633
  */
1240
- const queryTeams = async (queryTeamsBody, params, options) => {
1241
- return customFetch(getQueryTeamsUrl(params), {
1634
+ const verifyChallengeProof = async (attemptId, challengeId, verifyChallengeProofBody, options) => {
1635
+ return customFetch(getVerifyChallengeProofUrl(attemptId, challengeId), {
1242
1636
  ...options,
1243
1637
  method: "POST",
1244
1638
  headers: {
1245
1639
  "Content-Type": "application/json",
1246
1640
  ...options?.headers
1247
1641
  },
1248
- body: JSON.stringify(queryTeamsBody)
1249
- });
1250
- };
1251
- const getGetTeamUrl = (teamId, params) => {
1252
- const normalizedParams = new URLSearchParams();
1253
- Object.entries(params || {}).forEach(([key, value]) => {
1254
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1255
- });
1256
- const stringifiedParams = normalizedParams.toString();
1257
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams/${teamId}?${stringifiedParams}` : `${getProxyPath()}/teams/${teamId}`;
1258
- };
1259
- /**
1260
- * Returns a Team by its id.
1261
-
1262
- * @summary Get team
1263
- */
1264
- const getTeam = async (teamId, params, options) => {
1265
- return customFetch(getGetTeamUrl(teamId, params), {
1266
- ...options,
1267
- method: "GET"
1642
+ body: JSON.stringify(verifyChallengeProofBody)
1268
1643
  });
1269
1644
  };
1270
- const getDeleteTeamUrl = (teamId, params) => {
1271
- const normalizedParams = new URLSearchParams();
1272
- Object.entries(params || {}).forEach(([key, value]) => {
1273
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1274
- });
1275
- const stringifiedParams = normalizedParams.toString();
1276
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams/${teamId}?${stringifiedParams}` : `${getProxyPath()}/teams/${teamId}`;
1645
+ const getCreateHandoffUrl = (attemptId) => {
1646
+ return `${getProxyPath()}/auth_attempts/${attemptId}/handoff`;
1277
1647
  };
1278
1648
  /**
1279
- * Deactivates the team.
1649
+ * Completes the authentication attempt and mints a `handoff_token`.
1280
1650
 
1281
- The team is tombstoned rather than erased: it stays readable through
1282
- getTeam with status `deactivated`. Its memberships are removed and the
1283
- users whose lifecycle it owns are deactivated with it.
1651
+ Call this after all required factors have been verified and the attempt is in `completed` state.
1652
+ The handoff token is short-lived (≤60 seconds) and must be exchanged at
1653
+ POST /sessions/exchange to receive the final session and session_token.
1284
1654
 
1285
- The request is idempotent. Deleting a team that is already deactivated
1286
- or doesn't exist succeeds without changing anything.
1655
+ The handoff token is:
1656
+ - Single-use (atomic exchange, no retry)
1657
+ - Audience-bound (requires matching project key for exchange)
1658
+ - Idempotency-safe within a 5-minute window (see conventions)
1287
1659
 
1288
- * @summary Delete team
1660
+ * @summary Complete authentication and create handoff token
1289
1661
  */
1290
- const deleteTeam = async (teamId, params, options) => {
1291
- return customFetch(getDeleteTeamUrl(teamId, params), {
1662
+ const createHandoff = async (attemptId, options) => {
1663
+ return customFetch(getCreateHandoffUrl(attemptId), {
1292
1664
  ...options,
1293
- method: "DELETE"
1665
+ method: "POST"
1294
1666
  });
1295
1667
  };
1296
- const getUpdateTeamUrl = (teamId, params) => {
1297
- const normalizedParams = new URLSearchParams();
1298
- Object.entries(params || {}).forEach(([key, value]) => {
1299
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1300
- });
1301
- const stringifiedParams = normalizedParams.toString();
1302
- return stringifiedParams.length > 0 ? `${getProxyPath()}/teams/${teamId}?${stringifiedParams}` : `${getProxyPath()}/teams/${teamId}`;
1668
+ const getCreateProjectUrl = () => {
1669
+ return `${getProxyPath()}/projects`;
1303
1670
  };
1304
1671
  /**
1305
- * Update team. Only active teams can be updated.
1306
-
1307
- * @summary Update team
1672
+ * @summary Create project
1308
1673
  */
1309
- const updateTeam = async (teamId, updateTeamBody, params, options) => {
1310
- return customFetch(getUpdateTeamUrl(teamId, params), {
1674
+ const createProject = async (createProjectBody, options) => {
1675
+ return customFetch(getCreateProjectUrl(), {
1311
1676
  ...options,
1312
- method: "PATCH",
1677
+ method: "POST",
1313
1678
  headers: {
1314
1679
  "Content-Type": "application/json",
1315
1680
  ...options?.headers
1316
1681
  },
1317
- body: JSON.stringify(updateTeamBody)
1682
+ body: JSON.stringify(createProjectBody)
1318
1683
  });
1319
1684
  };
1320
- const getCreateBrandingUrl = (params) => {
1321
- const normalizedParams = new URLSearchParams();
1322
- Object.entries(params || {}).forEach(([key, value]) => {
1323
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1685
+ const getQueryProjectsUrl = () => {
1686
+ return `${getProxyPath()}/projects/query`;
1687
+ };
1688
+ /**
1689
+ * @summary Query projects
1690
+ */
1691
+ const queryProjects = async (queryProjectsBody, options) => {
1692
+ return customFetch(getQueryProjectsUrl(), {
1693
+ ...options,
1694
+ method: "POST",
1695
+ headers: {
1696
+ "Content-Type": "application/json",
1697
+ ...options?.headers
1698
+ },
1699
+ body: JSON.stringify(queryProjectsBody)
1324
1700
  });
1325
- const stringifiedParams = normalizedParams.toString();
1326
- return stringifiedParams.length > 0 ? `${getProxyPath()}/branding?${stringifiedParams}` : `${getProxyPath()}/branding`;
1701
+ };
1702
+ const getGetProjectUrl = (projectId) => {
1703
+ return `${getProxyPath()}/projects/${projectId}`;
1327
1704
  };
1328
1705
  /**
1329
- * Publishes a new immutable branding revision for the project. Branding
1330
- revisions cannot be updated or deleted; every edit publishes a new
1331
- revision, and flow responses resolve the latest revision per project
1332
- (see ADR 040).
1706
+ * Returns the current state of a project.
1333
1707
 
1334
- The `liquid_template` is validated lexically on save (size, encoding,
1335
- banned patterns such as `<script>` tags, inline event handlers, and the
1336
- `| raw` filter). Authoritative LiquidJS validation runs at authoring
1337
- time via `zitadel plan` / `zitadel apply`.
1708
+ * @summary Get project
1709
+ */
1710
+ const getProject = async (projectId, options) => {
1711
+ return customFetch(getGetProjectUrl(projectId), {
1712
+ ...options,
1713
+ method: "GET"
1714
+ });
1715
+ };
1716
+ const getPatchProjectUrl = (projectId) => {
1717
+ return `${getProxyPath()}/projects/${projectId}`;
1718
+ };
1719
+ /**
1720
+ * Updates the state of a project.
1338
1721
 
1339
- * @summary Publish a new branding revision
1722
+ * @summary Update project
1340
1723
  */
1341
- const createBranding = async (createBrandingBody, params, options) => {
1342
- return customFetch(getCreateBrandingUrl(params), {
1724
+ const patchProject = async (projectId, patchProjectBody, options) => {
1725
+ return customFetch(getPatchProjectUrl(projectId), {
1343
1726
  ...options,
1344
- method: "POST",
1727
+ method: "PATCH",
1345
1728
  headers: {
1346
1729
  "Content-Type": "application/json",
1347
1730
  ...options?.headers
1348
1731
  },
1349
- body: JSON.stringify(createBrandingBody)
1732
+ body: JSON.stringify(patchProjectBody)
1350
1733
  });
1351
1734
  };
1352
- const getListBrandingUrl = (params) => {
1353
- const normalizedParams = new URLSearchParams();
1354
- Object.entries(params || {}).forEach(([key, value]) => {
1355
- if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1356
- });
1357
- const stringifiedParams = normalizedParams.toString();
1358
- return stringifiedParams.length > 0 ? `${getProxyPath()}/branding?${stringifiedParams}` : `${getProxyPath()}/branding`;
1735
+ const getInitClaimUrl = (projectId) => {
1736
+ return `${getProxyPath()}/projects/${projectId}/claim/init`;
1359
1737
  };
1360
1738
  /**
1361
- * Lists branding revisions for the project, newest first, capped at the
1362
- 100 most recent. The first entry is the revision flow responses
1363
- currently resolve. Deliberately unpaginated in v1 list endpoints
1364
- gain a real query mechanism together (ADR 031); advertising pagination
1365
- parameters the server ignores would be worse than none.
1739
+ * Starts a claim challenge for an unclaimed project. Authenticated by the
1740
+ project secret, this mints a single-use, short-lived challenge and returns
1741
+ the `claim_url` the developer opens in a browser to complete the claim,
1742
+ together with the `challenge_id` the CLI polls with. The exact expiry is
1743
+ carried by `expires_at` on the response.
1366
1744
 
1367
- * @summary List branding revisions
1745
+ * @summary Initialize a project claim
1368
1746
  */
1369
- const listBranding = async (params, options) => {
1370
- return customFetch(getListBrandingUrl(params), {
1747
+ const initClaim = async (projectId, options) => {
1748
+ return customFetch(getInitClaimUrl(projectId), {
1371
1749
  ...options,
1372
- method: "GET"
1750
+ method: "POST"
1373
1751
  });
1374
1752
  };
1375
- const getGetBrandingByIdUrl = (id, params) => {
1753
+ const getGetClaimStatusUrl = (projectId, params) => {
1376
1754
  const normalizedParams = new URLSearchParams();
1377
1755
  Object.entries(params || {}).forEach(([key, value]) => {
1378
1756
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1379
1757
  });
1380
1758
  const stringifiedParams = normalizedParams.toString();
1381
- return stringifiedParams.length > 0 ? `${getProxyPath()}/branding/${id}?${stringifiedParams}` : `${getProxyPath()}/branding/${id}`;
1759
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/projects/${projectId}/claim/status?${stringifiedParams}` : `${getProxyPath()}/projects/${projectId}/claim/status`;
1382
1760
  };
1383
1761
  /**
1384
- * Retrieves a single branding revision, including its stored configuration.
1385
- * @summary Get a branding revision by ID
1762
+ * Polled by the CLI while a browser completes the claim. Authorized by the
1763
+ project secret that initiated the challenge. Returns `pending`, or
1764
+ `completed` with the owning team, the claim timestamp, and the dashboard
1765
+ URL once the browser leg has finished.
1766
+
1767
+ * @summary Get claim status
1386
1768
  */
1387
- const getBrandingById = async (id, params, options) => {
1388
- return customFetch(getGetBrandingByIdUrl(id, params), {
1769
+ const getClaimStatus = async (projectId, params, options) => {
1770
+ return customFetch(getGetClaimStatusUrl(projectId, params), {
1389
1771
  ...options,
1390
1772
  method: "GET"
1391
1773
  });
1392
1774
  };
1393
- //#endregion
1394
- //#region ../api/dist/runtime/api-factory.mjs
1395
- /**
1396
- * Factory for the typed Zitadel client every consumer reaches for.
1397
- *
1398
- * Wraps the orval-generated endpoints in a {@link Proxy} so each
1399
- * generated function sees the right base URL and (optionally) the
1400
- * right bearer token at call time — without the caller having to
1401
- * thread either through every operation, and without exposing the
1402
- * module-globals (`setProxyPath` / `setApiAuthToken`) as part of the
1403
- * public API.
1404
- *
1405
- * Per-instance isolation: multiple clients can coexist in one process
1406
- * (different servers, different tokens). Their Proxy `get` traps set
1407
- * the globals synchronously before invoking the underlying generated
1408
- * function, which reads them at the top of `customFetch` before any
1409
- * `await`. Two clients running interleaved within a single JS turn
1410
- * are safe; two clients running in parallel across awaits could
1411
- * clobber each other — not a pattern any current consumer uses.
1412
- */
1413
- /**
1414
- * Build a typed Zitadel client pre-bound to a base URL and (optionally)
1415
- * a bearer token. Every method on the returned object mirrors a
1416
- * generated orval function:
1417
- *
1418
- * const client = createZitadelClient({ baseUrl, token });
1419
- * await client.createProject({ preview_origins: [] });
1420
- * await client.createSchema(body, { project_id });
1421
- */
1422
- function createZitadelClient(opts) {
1423
- let baseUrl = opts.baseUrl;
1424
- while (baseUrl.endsWith("/")) baseUrl = baseUrl.slice(0, -1);
1425
- return new Proxy(zitadelNextGen_exports, { get(target, prop, receiver) {
1426
- const value = Reflect.get(target, prop, receiver);
1427
- if (typeof value !== "function") return value;
1428
- return (...args) => {
1429
- setProxyPath(baseUrl);
1430
- setApiAuthToken(opts.token);
1431
- return value(...args);
1432
- };
1433
- } });
1434
- }
1435
- //#endregion
1436
- //#region ../api/dist/runtime/config.mjs
1437
- /**
1438
- * Slot for the configured project, stored on `globalThis` under a
1439
- * {@link Symbol.for} key. Every copy of this module evaluated in the
1440
- * same JS realm resolves the symbol through the global symbol registry
1441
- * to the same identity, so they all read and write a single slot —
1442
- * needed because the standalone components bundle inlines its own copy
1443
- * of this file, and dual-package hazards / monorepo duplicates can load
1444
- * a second copy alongside the app's. With a module-local `let` each
1445
- * instance kept its own singleton and `configureZitadel()` calls in one
1446
- * were invisible to `getZitadelConfig()` calls in another. Sharing is
1447
- * realm-scoped — separate realms (iframes, Node `vm` contexts, worker
1448
- * threads) have their own registries and are not unified by this.
1449
- */
1450
- const PROJECT_SLOT = Symbol.for("@zitadel/api/config:currentProject");
1451
- function readSlot() {
1452
- return globalThis[PROJECT_SLOT] ?? null;
1453
- }
1454
- /**
1455
- * Per-project API client cache. Ensures `getApi(project)` returns the
1456
- * same instance for the same project handle — no re-wrapping on every call.
1457
- * Module-local on purpose: keyed by the shared frozen `ZitadelProject`
1458
- * object identity, so cross-instance reads still resolve through the
1459
- * same key and at worst memoize once per module instance.
1460
- */
1461
- const apiCache = /* @__PURE__ */ new WeakMap();
1462
- /**
1463
- * Returns a typed API client for the given project, with the base URL
1464
- * pre-bound. Cached per project handle — safe to call multiple times.
1465
- *
1466
- * ```ts
1467
- * const project = configureZitadel({ ... });
1468
- * export const api = getApi(project);
1469
- * ```
1470
- */
1471
- function getApi(project) {
1472
- let api = apiCache.get(project);
1473
- if (!api) {
1474
- api = createZitadelClient({
1475
- baseUrl: project.proxyPath,
1476
- token: project.publishableKey
1477
- });
1478
- apiCache.set(project, api);
1479
- }
1480
- return api;
1481
- }
1482
- /**
1483
- * Returns the current project handle, or `null` if {@link configureZitadel}
1484
- * has not been called yet.
1485
- */
1486
- function getZitadelConfig() {
1487
- return readSlot();
1488
- }
1489
- //#endregion
1490
- //#region src/orchestrator/resolve-api.ts
1491
- /** Matches the `configureZitadel()` default so attribute and JS config agree. */
1492
- const DEFAULT_PROXY_PATH = "/__nextgen";
1493
- /**
1494
- * Cache of project handles synthesized from attributes, keyed by their value
1495
- * tuple. `getApi()` caches its client in a `WeakMap` keyed by the project's
1496
- * identity, so the same attribute values must yield the *same* frozen object
1497
- * across the several `resolveApi()` calls a single flow makes — otherwise
1498
- * every call misses the cache and re-wraps a fresh client. A real page uses a
1499
- * handful of distinct configs at most; the FIFO bound below is just a guard so
1500
- * a pathological page that churns through unique configs can't leak memory.
1501
- */
1502
- const MAX_SYNTHETIC_PROJECTS = 32;
1503
- const syntheticProjects = /* @__PURE__ */ new Map();
1504
- /**
1505
- * Builds a `ZitadelProject` from declarative attributes, or `undefined` when
1506
- * no `project-id` was given (nothing to configure). Mirrors
1507
- * `configureZitadel()`'s proxy-path defaulting so the two config paths are
1508
- * interchangeable.
1509
- */
1510
- function projectFromAttrs({ projectId, proxyPath, url }) {
1511
- if (!projectId) return void 0;
1512
- const resolvedProxyPath = proxyPath || DEFAULT_PROXY_PATH;
1513
- const resolvedUrl = url || void 0;
1514
- const key = JSON.stringify([
1515
- projectId,
1516
- resolvedProxyPath,
1517
- resolvedUrl ?? ""
1518
- ]);
1519
- let project = syntheticProjects.get(key);
1520
- if (!project) {
1521
- project = Object.freeze({
1522
- projectId,
1523
- proxyPath: resolvedProxyPath,
1524
- url: resolvedUrl
1525
- });
1526
- if (syntheticProjects.size >= MAX_SYNTHETIC_PROJECTS) {
1527
- const oldest = syntheticProjects.keys().next().value;
1528
- if (oldest !== void 0) syntheticProjects.delete(oldest);
1529
- }
1530
- syntheticProjects.set(key, project);
1531
- }
1532
- return project;
1533
- }
1775
+ const getCompleteClaimUrl = (projectId) => {
1776
+ return `${getProxyPath()}/projects/${projectId}/claim/complete`;
1777
+ };
1534
1778
  /**
1535
- * Resolves the effective SDK project handle and its typed API client for an
1536
- * orchestrator element. Precedence, highest first:
1537
- *
1538
- * 1. the element's `project` property (an SDK handle set from JS / a framework),
1539
- * 2. the global handle from `configureZitadel()`,
1540
- * 3. the `project-id` / `proxy-path` / `url` attributes set declaratively in HTML.
1541
- *
1542
- * Both JS paths (property and global) win over the declarative attributes, so a
1543
- * deliberate `configureZitadel()` is never silently overridden by fallback or
1544
- * stale HTML attributes; the attributes are the no-JS fallback.
1545
- *
1546
- * Both `<zitadel-login>` and `<zitadel-logout>` need the same resolution and
1547
- * the same "no config" failure, so it lives here rather than being copied
1548
- * into each element. Callers run this inside their try/catch so a missing
1549
- * configuration surfaces through the element's normal error path rather than
1550
- * as an unhandled rejection.
1551
- *
1552
- * @param project Optional per-element override handle.
1553
- * @param attrs Declarative config read from the element's attributes.
1554
- * @param element Tag name used in the thrown error (e.g. `<zitadel-login>`).
1779
+ * Called by the browser after the developer authenticates on the claim page.
1780
+ Authenticated by the `__nextgen_session` cookie, it attaches the project to
1781
+ the developer's personal team using the `challenge_id` from the claim URL as
1782
+ its single-use, browser-safe authorization.
1783
+
1784
+ * @summary Complete a project claim
1555
1785
  */
1556
- function resolveApi(project, attrs, element) {
1557
- const cfg = project ?? getZitadelConfig() ?? projectFromAttrs(attrs);
1558
- if (!cfg) throw new Error(`${element} requires a configured project: set the \`project-id\` attribute, set the \`project\` property, or call configureZitadel().`);
1559
- return {
1560
- project: cfg,
1561
- api: getApi(cfg)
1562
- };
1563
- }
1564
- //#endregion
1565
- //#region ../api/dist/generated/model/index.mjs
1566
- const CreateFlow201BrandingLayout = {
1567
- centered: "centered",
1568
- split: "split"
1786
+ const completeClaim = async (projectId, completeClaimBody, options) => {
1787
+ return customFetch(getCompleteClaimUrl(projectId), {
1788
+ ...options,
1789
+ method: "POST",
1790
+ headers: {
1791
+ "Content-Type": "application/json",
1792
+ ...options?.headers
1793
+ },
1794
+ body: JSON.stringify(completeClaimBody)
1795
+ });
1796
+ };
1797
+ const getCreateSessionUrl = () => {
1798
+ return `${getProxyPath()}/sessions`;
1569
1799
  };
1570
- //#endregion
1571
- //#region src/orchestrator/branding-validator.ts
1572
1800
  /**
1573
- * Lightweight paint-time validator for `Branding` payloads.
1574
- *
1575
- * Per `docs/design/branding/schema.md` §Shape invariants:
1576
- *
1577
- * 1. Every referenced URL is https.
1578
- * 2. `layout` is in the documented enum.
1579
- * 3. `liquid_template` shape checks happen elsewhere (security pipeline runs
1580
- * server-side; structural validator is deferred until the full atom set
1581
- * lands).
1582
- *
1583
- * Failures are non-fatal: we collect issues, log a dev-build warning, and
1584
- * return a sanitised payload with the offending fields stripped (set to
1585
- * `undefined`) so the orchestrator falls back to the bundled defaults.
1801
+ * Creates an anonymous session shell with no user and no factors (`state: building`).
1802
+
1803
+ This is optional — an `auth_attempt` will create a session implicitly if none is provided.
1804
+ Use this explicitly when you want to:
1805
+ - Pre-allocate a `session_id` before the user is known, so device/telemetry signals
1806
+ can be correlated with the eventual authenticated session from the start.
1807
+ - Track anonymous state (bot detection, device fingerprint) that survives until authentication.
1808
+
1809
+ Creating a session is an app-plane operation on the project credential
1810
+ (`session.write`). The returned `session_token` is a session credential for the
1811
+ end-user client, not a management scope: it is delivered as the
1812
+ `__nextgen_session` cookie and authorises the self-service operations
1813
+ `GET /sessions/me` and `DELETE /sessions/me` (`nextgenSession` scheme). The
1814
+ by-id operations `GET /sessions/{session_id}` and `DELETE /sessions/{session_id}`
1815
+ are operator endpoints and require `session.read` / `session.delete` instead.
1816
+
1817
+ The `session_token` is superseded when a handoff exchange completes — clients must
1818
+ replace it at that point.
1819
+
1820
+ Anonymous sessions expire aggressively (10-minute TTL). The TTL resets to the configured
1821
+ full session TTL when the first authentication factor is written via a completing `auth_attempt`.
1822
+
1823
+ * @summary Create an anonymous session shell
1586
1824
  */
1587
- const VALID_LAYOUTS = new Set(Object.values(CreateFlow201BrandingLayout));
1588
- function validateBranding(input) {
1589
- if (!input) return {
1590
- branding: void 0,
1591
- issues: []
1592
- };
1593
- const issues = [];
1594
- const out = { ...input };
1595
- if (out.layout && !VALID_LAYOUTS.has(out.layout)) {
1596
- issues.push(`Unknown layout "${out.layout}" — falling back to "centered".`);
1597
- out.layout = CreateFlow201BrandingLayout.centered;
1598
- }
1599
- out.logo_url = sanitiseUrl(out.logo_url, "logo_url", issues);
1600
- out.font_url = sanitiseUrl(out.font_url, "font_url", issues);
1601
- out.hero_url = sanitiseUrl(out.hero_url, "hero_url", issues);
1602
- if (out.assets) out.assets = {
1603
- logo_dark: sanitiseUrl(out.assets.logo_dark, "assets.logo_dark", issues),
1604
- favicon: sanitiseUrl(out.assets.favicon, "assets.favicon", issues),
1605
- background_image: sanitiseUrl(out.assets.background_image, "assets.background_image", issues)
1606
- };
1607
- return {
1608
- branding: out,
1609
- issues
1610
- };
1611
- }
1612
- function sanitiseUrl(value, field, issues) {
1613
- if (value == null || value === "") return;
1614
- try {
1615
- const parsed = new URL(value);
1616
- if (parsed.protocol !== "https:") {
1617
- issues.push(`${field} must use https (got "${parsed.protocol}") — dropped.`);
1618
- return;
1619
- }
1620
- return parsed.toString();
1621
- } catch {
1622
- issues.push(`${field} is not a valid URL — dropped.`);
1623
- return;
1624
- }
1625
- }
1626
- //#endregion
1627
- //#region src/orchestrator/exportparts.ts
1825
+ const createSession = async (createSessionBody, options) => {
1826
+ return customFetch(getCreateSessionUrl(), {
1827
+ ...options,
1828
+ method: "POST",
1829
+ headers: {
1830
+ "Content-Type": "application/json",
1831
+ ...options?.headers
1832
+ },
1833
+ body: JSON.stringify(createSessionBody)
1834
+ });
1835
+ };
1836
+ const getQuerySessionsUrl = (params) => {
1837
+ const normalizedParams = new URLSearchParams();
1838
+ Object.entries(params || {}).forEach(([key, value]) => {
1839
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1840
+ });
1841
+ const stringifiedParams = normalizedParams.toString();
1842
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/query?${stringifiedParams}` : `${getProxyPath()}/sessions/query`;
1843
+ };
1628
1844
  /**
1629
- * Forwards atom-internal CSS Shadow Parts through the orchestrator boundary.
1630
- *
1631
- * Atoms stamp `part="..."` on their internals and declare the public set in
1632
- * their manifest (`parts`). Those parts are visible one shadow boundary up —
1633
- * inside the orchestrator's shadow root — but not to the embedding page,
1634
- * because `::part()` pierces a single boundary unless each host in the chain
1635
- * re-exports (`exportparts`). Templates never author the forwarding (the
1636
- * sanitiser strips `exportparts`); the orchestrator stamps it after every
1637
- * render commit so the mapping stays canonical for template-rendered and
1638
- * gate-patched atoms alike.
1639
- *
1640
- * Exported names are mechanical, collision-free, and public contract
1641
- * (override-ladder Tier 2, same stability rules as token names):
1642
- * `<atom>-<part>` with the `zl-` prefix dropped — `zl-field`'s `input` part
1643
- * becomes `zitadel-login::part(field-input)` on the host page. Manifests are
1644
- * the canonical part catalogue; this module derives, never curates.
1845
+ * Returns the sessions of a project, paginated with a cursor.
1846
+ Sessions of every lifecycle state are returned; each carries its `state`.
1847
+ Requires `session.read` permission.
1848
+
1849
+ * @summary Query sessions
1645
1850
  */
1646
- const exportpartsByTag = new Map(manifestRegistry.filter((manifest) => manifest.parts.length > 0).map((manifest) => {
1647
- const short = manifest.tag.replace(/^zl-/, "");
1648
- const value = manifest.parts.map((part) => `${part}: ${short}-${part}`).join(", ");
1649
- return [manifest.tag, value];
1650
- }));
1851
+ const querySessions = async (querySessionsBody, params, options) => {
1852
+ return customFetch(getQuerySessionsUrl(params), {
1853
+ ...options,
1854
+ method: "POST",
1855
+ headers: {
1856
+ "Content-Type": "application/json",
1857
+ ...options?.headers
1858
+ },
1859
+ body: JSON.stringify(querySessionsBody)
1860
+ });
1861
+ };
1862
+ const getExchangeHandoffUrl = (params) => {
1863
+ const normalizedParams = new URLSearchParams();
1864
+ Object.entries(params || {}).forEach(([key, value]) => {
1865
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1866
+ });
1867
+ const stringifiedParams = normalizedParams.toString();
1868
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/sessions/exchange?${stringifiedParams}` : `${getProxyPath()}/sessions/exchange`;
1869
+ };
1651
1870
  /**
1652
- * Stamps `exportparts` onto every known atom in `scope`. Idempotent — call
1653
- * after each render commit: `unsafeHTML` re-parses replace previously
1654
- * stamped nodes, and the mandatory-gates patcher appends atoms after
1655
- * sanitisation.
1871
+ * Consumes a one-time `handoff_token` minted by `POST /auth_attempts/{id}/handoff`
1872
+ and returns the resulting session and a `session_token`.
1873
+
1874
+ The server resolves the originating `auth_attempt` from the token and then:
1875
+
1876
+ | Originating auth_attempt | Outcome |
1877
+ |---|---|
1878
+ | No `session_id` | A new authenticated session is **created**. |
1879
+ | `session_id` points to an anonymous shell | Existing session is **upgraded** — user and factors written in, TTL reset to full session TTL. |
1880
+ | `session_id` points to an active session (step-up) | Existing session is **upgraded** — new factors merged, `assurance_levels[]` expanded. |
1881
+
1882
+ The response shape is identical in all three cases.
1883
+
1884
+ The `session_token` supersedes any previously issued `session_token` for the same session.
1885
+ Clients must replace their stored token at this point.
1886
+
1887
+ Requires a project service key (OAuth2 client credentials).
1888
+
1889
+ * @summary Exchange handoff token for a session
1656
1890
  */
1657
- function stampExportparts(scope) {
1658
- for (const [tag, value] of exportpartsByTag) for (const el of scope.querySelectorAll(tag)) if (el.getAttribute("exportparts") !== value) el.setAttribute("exportparts", value);
1659
- }
1660
- //#endregion
1661
- //#region src/internal/escape-html.ts
1891
+ const exchangeHandoff = async (exchangeHandoffBody, params, options) => {
1892
+ return customFetch(getExchangeHandoffUrl(params), {
1893
+ ...options,
1894
+ method: "POST",
1895
+ headers: {
1896
+ "Content-Type": "application/json",
1897
+ ...options?.headers
1898
+ },
1899
+ body: JSON.stringify(exchangeHandoffBody)
1900
+ });
1901
+ };
1902
+ const getGetSessionUrl = (sessionId) => {
1903
+ return `${getProxyPath()}/sessions/${sessionId}`;
1904
+ };
1662
1905
  /**
1663
- * Escapes the five HTML-significant characters so a string is safe to
1664
- * interpolate into either element text or a double-quoted attribute value.
1665
- *
1666
- * Used for the orchestrator-owned attribution markup, which is appended after
1667
- * the Liquid output has already been sanitised so it can't rely on the
1668
- * DOMPurify pass and must escape its own (tenant-supplied) values.
1906
+ * Returns the current state of a session including its factors and all currently
1907
+ satisfied assurance levels.
1908
+
1909
+ `assurance_levels[]` may shrink over time as factor freshness windows expire,
1910
+ without the session itself expiring. Use step-up authentication (a new `auth_attempt`
1911
+ against the same `session_id`) to restore a dropped assurance level.
1912
+
1913
+ * @summary Get session state
1914
+ */
1915
+ const getSession = async (sessionId, options) => {
1916
+ return customFetch(getGetSessionUrl(sessionId), {
1917
+ ...options,
1918
+ method: "GET"
1919
+ });
1920
+ };
1921
+ const getRevokeSessionUrl = (sessionId) => {
1922
+ return `${getProxyPath()}/sessions/${sessionId}`;
1923
+ };
1924
+ /**
1925
+ * Permanently deletes the session, terminating it immediately.
1926
+
1927
+ This is the operator revoke path and requires the `session.delete` scope on a
1928
+ project-bound credential. End-user logout with the `__nextgen_session` cookie is
1929
+ `DELETE /sessions/me` (`nextgenSession` scheme).
1930
+
1931
+ Idempotent: deleting a session that does not exist (or was already deleted)
1932
+ still returns 204. After deletion, any tokens derived from the session are
1933
+ invalidated.
1934
+
1935
+ * @summary Revoke session
1669
1936
  */
1670
- function escapeHtml(value) {
1671
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1672
- }
1673
- //#endregion
1674
- //#region src/orchestrator/mandatory-gates.ts
1675
- const MANDATORY_GATES_MARKER = "ZL_MANDATORY_GATES";
1676
- /** Every atom tag that participates as a named form field. */
1677
- const FIELD_ATOM_TAGS = "zl-field, zl-select, zl-checkbox";
1678
- const mandatoryGatesMarkerComment = `<!--${MANDATORY_GATES_MARKER}-->`;
1679
- function patchMandatoryGates(html, step, locale) {
1680
- const template = document.createElement("template");
1681
- template.innerHTML = html;
1682
- const fragment = template.content;
1683
- const additions = collectMissingAtoms(fragment, step, locale);
1684
- const marker = findMarkerComment(fragment);
1685
- if (marker?.parentNode) {
1686
- for (const node of additions) marker.parentNode.insertBefore(node, marker);
1687
- marker.remove();
1688
- } else for (const node of additions) fragment.appendChild(node);
1689
- return template.innerHTML;
1690
- }
1691
- function collectMissingAtoms(fragment, step, locale) {
1692
- const additions = [];
1693
- if (step.fields) for (const field of step.fields) {
1694
- if (!field.required) continue;
1695
- if (hasFieldFor(fragment, field.name)) continue;
1696
- additions.push(buildField(field.name, field.text_key, field.type, locale, field.required));
1697
- }
1698
- if (step.actions && !hasPrimaryButton(fragment)) {
1699
- const primary = step.actions.find((action) => action.primary);
1700
- if (primary) additions.push(buildSubmit(primary.name, primary.text_key, locale));
1701
- }
1702
- return additions;
1703
- }
1704
- function hasPrimaryButton(fragment) {
1705
- return Boolean(fragment.querySelector("zl-button[hierarchy=\"primary\"]"));
1706
- }
1707
- function hasFieldFor(fragment, name) {
1708
- for (const field of fragment.querySelectorAll(FIELD_ATOM_TAGS)) if (field.getAttribute("name") === name) return true;
1709
- return false;
1710
- }
1711
- function findMarkerComment(fragment) {
1712
- const walker = (fragment.ownerDocument ?? document).createTreeWalker(fragment, NodeFilter.SHOW_COMMENT);
1713
- let node = walker.nextNode();
1714
- while (node) {
1715
- if (node.nodeValue?.trim() === "ZL_MANDATORY_GATES") return node;
1716
- node = walker.nextNode();
1717
- }
1718
- return null;
1719
- }
1720
- function buildField(name, textKey, type, locale, required) {
1721
- const el = document.createElement("zl-field");
1722
- el.setAttribute("name", name);
1723
- el.setAttribute("label", lookup(locale, textKey ?? name));
1724
- el.setAttribute("type", type);
1725
- if (required) el.setAttribute("required", "");
1726
- return el;
1727
- }
1728
- function buildSubmit(name, textKey, locale) {
1729
- const el = document.createElement("zl-button");
1730
- el.setAttribute("hierarchy", "primary");
1731
- el.setAttribute("size", "medium");
1732
- el.setAttribute("type", "submit");
1733
- el.setAttribute("block", "");
1734
- el.setAttribute("action", name);
1735
- el.setAttribute("label", lookup(locale, textKey ?? "submit.continue"));
1736
- return el;
1737
- }
1738
- function lookup(locale, key) {
1739
- return locale[key] ?? key;
1740
- }
1741
- //#endregion
1742
- //#region src/orchestrator/liquid.ts
1937
+ const revokeSession = async (sessionId, options) => {
1938
+ return customFetch(getRevokeSessionUrl(sessionId), {
1939
+ ...options,
1940
+ method: "DELETE"
1941
+ });
1942
+ };
1943
+ const getGetMySessionUrl = () => {
1944
+ return `${getProxyPath()}/sessions/me`;
1945
+ };
1743
1946
  /**
1744
- * LiquidJS engine factory for the `<zitadel-login>` orchestrator.
1745
- *
1746
- * Configures the engine per the security pipeline in
1747
- * `docs/design/flowengine/template-security.md` and registers the
1748
- * `| t` filter and `{% mandatory_gates %}` tag from
1749
- * `docs/design/branding/templates.md`.
1750
- *
1751
- * Hard rules:
1752
- *
1753
- * - `{{ }}` output is HTML-escaped (LiquidJS default since v10).
1754
- * - The `| raw` filter is overridden to a no-op string passthrough that still
1755
- * escapes (Layer 1 of the security pipeline).
1756
- * - Partials are loaded from an in-memory map; no filesystem access.
1947
+ * Returns the current state of the current session including its factors and all currently
1948
+ satisfied assurance levels.
1949
+
1950
+ `assurance_levels[]` may shrink over time as factor freshness windows expire,
1951
+ without the session itself expiring. Use step-up authentication (a new `auth_attempt`
1952
+ against the same `session_id`) to restore a dropped assurance level.
1953
+
1954
+ * @summary Get my session state
1757
1955
  */
1758
- const TEMPLATE_NAMES$1 = { default: "default" };
1956
+ const getMySession = async (options) => {
1957
+ return customFetch(getGetMySessionUrl(), {
1958
+ ...options,
1959
+ method: "GET"
1960
+ });
1961
+ };
1962
+ const getRevokeMySessionUrl = () => {
1963
+ return `${getProxyPath()}/sessions/me`;
1964
+ };
1759
1965
  /**
1760
- * Maps catalog `error.*` keys with no `_<rule>` suffix (so
1761
- * {@link localiseFlowErrorKeys} can't derive the field from the key itself)
1762
- * to their field name the credential errors with bespoke catalog copy
1763
- * (`error.email_exists`, `error.invalid_credentials`, …). Rule-suffixed keys
1764
- * (`error.<field>_<rule>`) derive their field from the key. Consumed only by
1765
- * {@link localiseFlowErrorKeys}, which stamps the resolved `field` onto the
1766
- * error; the template filters (`fieldError` / `formLevelError`) then route by
1767
- * that `field`, and an error whose field the step omits is downgraded to a
1768
- * fieldless banner message.
1966
+ * Logs out by permanently deleting the session.
1967
+
1968
+ The `__nextgen_session` cookie issued at creation (or superseded by a handoff
1969
+ exchange) is required. Idempotent: if the session is already gone this still
1970
+ returns 204. Any tokens derived from the session are invalidated, and the
1971
+ cookie itself is cleared in the response.
1972
+
1973
+ * @summary Revoke my session
1769
1974
  */
1770
- const fieldErrorKeys = {
1771
- "error.email_required": "email",
1772
- "error.email_invalid": "email",
1773
- "error.email_exists": "email",
1774
- "error.password_required": "password",
1775
- "error.password_incorrect": "password",
1776
- "error.invalid_credentials": "password"
1975
+ const revokeMySession = async (options) => {
1976
+ return customFetch(getRevokeMySessionUrl(), {
1977
+ ...options,
1978
+ method: "DELETE"
1979
+ });
1777
1980
  };
1778
- function createLiquidEngine(options) {
1779
- const engine = new Liquid({
1780
- templates: {
1781
- [TEMPLATE_NAMES$1.default]: default_default,
1782
- ...options.templates ?? {}
1783
- },
1784
- cache: true,
1785
- jsTruthy: true,
1786
- relativeReference: false,
1787
- strictFilters: false,
1788
- strictVariables: false,
1789
- outputEscape: "escape"
1981
+ const getCreateSchemaUrl = (params) => {
1982
+ const normalizedParams = new URLSearchParams();
1983
+ Object.entries(params || {}).forEach(([key, value]) => {
1984
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1790
1985
  });
1791
- engine.registerFilter("raw", (value) => stringify(value));
1792
- const warnedMissingKeys = /* @__PURE__ */ new Set();
1793
- engine.registerFilter("t", function tFilter(key, ...args) {
1794
- const lookupKey = stringify(key);
1795
- let template = options.locale[lookupKey] ?? injectedKeyFallback(options.locale, lookupKey) ?? fieldLabelFallback(lookupKey);
1796
- if (template === void 0) {
1797
- template = lookupKey;
1798
- if (lookupKey !== "" && !warnedMissingKeys.has(lookupKey)) {
1799
- warnedMissingKeys.add(lookupKey);
1800
- console.warn(`[zitadel-login] missing text key "${lookupKey}" — rendering the raw key`);
1801
- }
1802
- }
1803
- return interpolate(template, args.map(stringify));
1986
+ const stringifiedParams = normalizedParams.toString();
1987
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas?${stringifiedParams}` : `${getProxyPath()}/schemas`;
1988
+ };
1989
+ /**
1990
+ * Create a new schema. The optional `$id` field is the JSON Schema document
1991
+ URI used to identify the schema in future requests (GitOps-stable identity,
1992
+ not a free-form resource primary key). When `$id` is omitted, the server
1993
+ generates a `sch_*` URL. When provided, `$id` must be unique within the
1994
+ project and should ideally be a valid URI pointing at where the schema can
1995
+ be accessed.
1996
+
1997
+ The schema can either be a concrete schema, e.g. a user schema, or a
1998
+ schema-url which will be resolved by the server.
1999
+
2000
+ * @summary Create new schema
2001
+ */
2002
+ const createSchema = async (createSchemaBody, params, options) => {
2003
+ return customFetch(getCreateSchemaUrl(params), {
2004
+ ...options,
2005
+ method: "POST",
2006
+ headers: {
2007
+ "Content-Type": "application/json",
2008
+ ...options?.headers
2009
+ },
2010
+ body: JSON.stringify(createSchemaBody)
1804
2011
  });
1805
- /** Resolves `{text_key}.placeholder` — empty when undefined (not the raw key). */
1806
- engine.registerFilter("fieldPlaceholder", (textKey) => {
1807
- const lookupKey = `${stringify(textKey)}.placeholder`;
1808
- return options.locale[lookupKey] ?? "";
2012
+ };
2013
+ const getListSchemasUrl = (params) => {
2014
+ const normalizedParams = new URLSearchParams();
2015
+ Object.entries(params || {}).forEach(([key, value]) => {
2016
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1809
2017
  });
1810
- /** Resolves `{text_key}.help` — empty when undefined. */
1811
- engine.registerFilter("fieldHelp", (textKey) => {
1812
- const lookupKey = `${stringify(textKey)}.help`;
1813
- return options.locale[lookupKey] ?? "";
2018
+ const stringifiedParams = normalizedParams.toString();
2019
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas?${stringifiedParams}` : `${getProxyPath()}/schemas`;
2020
+ };
2021
+ /**
2022
+ * Retrieve a list of all schemas available in the system. This endpoint
2023
+ supports pagination and filtering based on schema attributes.
2024
+
2025
+ * @summary List all schemas
2026
+ */
2027
+ const listSchemas = async (params, options) => {
2028
+ return customFetch(getListSchemasUrl(params), {
2029
+ ...options,
2030
+ method: "GET"
1814
2031
  });
1815
- /**
1816
- * Builds `<zl-select>` options from a field's closed `validation.enum`. The
1817
- * wire only carries the allowed values (strings), so the label defaults to the
1818
- * value — author the enum with display-ready text. Piped through `| json` into
1819
- * the element's `options` attribute.
1820
- */
1821
- engine.registerFilter("selectOptions", (values) => {
1822
- if (!Array.isArray(values)) return [];
1823
- return values.map((value) => {
1824
- const v = stringify(value);
1825
- return {
1826
- value: v,
1827
- label: v
1828
- };
1829
- });
2032
+ };
2033
+ const getGetSchemaByIdUrl = (id, params) => {
2034
+ const normalizedParams = new URLSearchParams();
2035
+ Object.entries(params || {}).forEach(([key, value]) => {
2036
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1830
2037
  });
1831
- /** Resolves `{text_key}.title` for form-level `<zl-alert heading>`. */
1832
- engine.registerFilter("alertHeading", (textKey) => {
1833
- const lookupKey = `${stringify(textKey)}.title`;
1834
- return options.locale[lookupKey] ?? "";
2038
+ const stringifiedParams = normalizedParams.toString();
2039
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/schemas/${id}?${stringifiedParams}` : `${getProxyPath()}/schemas/${id}`;
2040
+ };
2041
+ /**
2042
+ * Get a schema by its ID. This will return the default revision of the schema.
2043
+ * @summary Get schema by ID
2044
+ */
2045
+ const getSchemaById = async (id, params, options) => {
2046
+ return customFetch(getGetSchemaByIdUrl(id, params), {
2047
+ ...options,
2048
+ method: "GET"
1835
2049
  });
1836
- /** Resolves `{text_key}.body` for form-level `<zl-alert>` message slot. */
1837
- engine.registerFilter("alertBody", (textKey) => {
1838
- const lookupKey = `${stringify(textKey)}.body`;
1839
- return options.locale[lookupKey] ?? "";
2050
+ };
2051
+ const getCreateFlowDefinitionUrl = () => {
2052
+ return `${getProxyPath()}/flow_definitions`;
2053
+ };
2054
+ /**
2055
+ * Creates a new flow definition.
2056
+ Flow definitions are templates that define the sequence of steps (capabilities)
2057
+ for a particular user journey (e.g., registration, login, password reset).
2058
+
2059
+ Flow definitions are created based on the flow definition schema, which includes the flow's purpose, audience, and the steps involved.
2060
+
2061
+ * @summary Create a new flow definition
2062
+ */
2063
+ const createFlowDefinition = async (createFlowDefinitionBody, options) => {
2064
+ return customFetch(getCreateFlowDefinitionUrl(), {
2065
+ ...options,
2066
+ method: "POST",
2067
+ headers: {
2068
+ "Content-Type": "application/json",
2069
+ ...options?.headers
2070
+ },
2071
+ body: JSON.stringify(createFlowDefinitionBody)
1840
2072
  });
1841
- /** Localized inline error for `fieldName`, or empty when none applies. */
1842
- engine.registerFilter("fieldError", (fieldName, errors) => {
1843
- const name = stringify(fieldName);
1844
- if (!Array.isArray(errors)) return "";
1845
- for (const item of errors) {
1846
- const err = item;
1847
- if (err.field !== name) continue;
1848
- return err.text_key ? options.locale[err.text_key] ?? err.text_key : err.message ?? "";
1849
- }
1850
- return "";
2073
+ };
2074
+ const getListFlowDefinitionsUrl = (params) => {
2075
+ const normalizedParams = new URLSearchParams();
2076
+ Object.entries(params || {}).forEach(([key, value]) => {
2077
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
1851
2078
  });
1852
- /**
1853
- * Automation-hook token for a field name: strips the
1854
- * `x-auth-methods#` credential prefix so testids stay method-named
1855
- * (`zitadel-field-password`), while the `name` attribute keeps the
1856
- * raw wire key. See hookName for the contract.
1857
- */
1858
- engine.registerFilter("testid", (fieldName) => hookName(stringify(fieldName)));
1859
- /**
1860
- * True when the error should render as `<zl-alert>`, not on a field. A
1861
- * field-scoped error (`field` set by {@link localiseFlowErrorKeys} when the
1862
- * step renders that field) routes inline instead; everything else — engine
1863
- * errors, and orphaned field errors already downgraded to a fieldless
1864
- * message — falls to the banner.
1865
- */
1866
- engine.registerFilter("formLevelError", (err) => {
1867
- return !err?.field;
2079
+ const stringifiedParams = normalizedParams.toString();
2080
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions?${stringifiedParams}` : `${getProxyPath()}/flow_definitions`;
2081
+ };
2082
+ /**
2083
+ * Retrieves a list of all flow definitions.
2084
+ This endpoint can be used to view existing flow definitions and their configurations.
2085
+
2086
+ * @summary List flow definitions
2087
+ */
2088
+ const listFlowDefinitions = async (params, options) => {
2089
+ return customFetch(getListFlowDefinitionsUrl(params), {
2090
+ ...options,
2091
+ method: "GET"
1868
2092
  });
1869
- engine.registerTag(MANDATORY_GATES_TAG, {
1870
- parse() {},
1871
- render() {
1872
- return mandatoryGatesMarkerComment;
1873
- }
2093
+ };
2094
+ const getGetFlowDefinitionUrl = (id) => {
2095
+ return `${getProxyPath()}/flow_definitions/${id}`;
2096
+ };
2097
+ /**
2098
+ * Get a flow definition by id
2099
+ * @summary Get a flow definition by id
2100
+ */
2101
+ const getFlowDefinition = async (id, options) => {
2102
+ return customFetch(getGetFlowDefinitionUrl(id), {
2103
+ ...options,
2104
+ method: "GET"
1874
2105
  });
1875
- return engine;
1876
- }
2106
+ };
2107
+ const getUpdateFlowDefinitionUrl = (id) => {
2108
+ return `${getProxyPath()}/flow_definitions/${id}`;
2109
+ };
1877
2110
  /**
1878
- * Fallbacks for text keys the flow engine derives from tenant-chosen step
1879
- * names (`<step>.action.back` for the injected back action — see
1880
- * `internal/domain/flow_state_machine.go` `buildStep`). Step names are open,
1881
- * so no dictionary can enumerate them; a missing step-specific key falls back
1882
- * to its generic entry instead of leaking the raw key into the UI.
2111
+ * Update a flow definition by id. This endpoint replaces the existing flow definition.
2112
+ If `flow_definition.status` is omitted, the current status is preserved
2113
+
2114
+ * @summary Update a flow definition by id
1883
2115
  */
1884
- const INJECTED_KEY_FALLBACKS = [{
1885
- suffix: ".action.back",
1886
- fallback: "action.back"
1887
- }];
1888
- function injectedKeyFallback(locale, key) {
1889
- for (const { suffix, fallback } of INJECTED_KEY_FALLBACKS) if (key.endsWith(suffix)) return locale[fallback];
1890
- }
2116
+ const updateFlowDefinition = async (id, updateFlowDefinitionBody, options) => {
2117
+ return customFetch(getUpdateFlowDefinitionUrl(id), {
2118
+ ...options,
2119
+ method: "PUT",
2120
+ headers: {
2121
+ "Content-Type": "application/json",
2122
+ ...options?.headers
2123
+ },
2124
+ body: JSON.stringify(updateFlowDefinitionBody)
2125
+ });
2126
+ };
2127
+ const getDeleteFlowDefinitionUrl = (id) => {
2128
+ return `${getProxyPath()}/flow_definitions/${id}`;
2129
+ };
1891
2130
  /**
1892
- * Fallback for field-label keys (`<step>.field.<name>` — see
1893
- * `FlowField.TextKey` in `internal/domain/flow_field_resolver.go`). Both
1894
- * halves are tenant-chosen (step names and schema property names), so no
1895
- * catalog can enumerate the keys; a miss renders a humanised property name
1896
- * ("dateOfBirth" "Date of birth") instead of leaking the raw key into the
1897
- * form. Sub-keys like `.placeholder`/`.help` are excluded — they resolve
1898
- * through their own filters, which stay empty on a miss.
2131
+ * Delete a flow definition by id.
2132
+ If the flow definition is currently being used by a flow, the deletion will fail.
2133
+ If the flow definition is the last active flow definition for a given purpose, the deletion will fail to prevent disruption of new flows being started for that purpose.
2134
+
2135
+ * @summary Delete a flow definition by id
1899
2136
  */
1900
- function fieldLabelFallback(key) {
1901
- const index = key.lastIndexOf(".field.");
1902
- if (index === -1) return void 0;
1903
- const field = key.slice(index + 7);
1904
- if (field === "" || field.includes(".")) return void 0;
1905
- return capitaliseFirst(humaniseFieldName(field));
1906
- }
2137
+ const deleteFlowDefinition = async (id, options) => {
2138
+ return customFetch(getDeleteFlowDefinitionUrl(id), {
2139
+ ...options,
2140
+ method: "DELETE"
2141
+ });
2142
+ };
2143
+ const getCreateTeamUrl = (params) => {
2144
+ const normalizedParams = new URLSearchParams();
2145
+ Object.entries(params || {}).forEach(([key, value]) => {
2146
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2147
+ });
2148
+ const stringifiedParams = normalizedParams.toString();
2149
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/teams?${stringifiedParams}` : `${getProxyPath()}/teams`;
2150
+ };
1907
2151
  /**
1908
- * Rule-suffix fallbacks for the server's field-validation keys
1909
- * (`error.<field>_<rule>` — see `FlowFieldValidationError.TextKey` in
1910
- * `internal/domain/flow_field_resolver.go`). Field names come from the
1911
- * tenant's user schema, so no catalog can enumerate the specific keys;
1912
- * a miss resolves to the rule's generic entry, interpolated with the
1913
- * field's label (`{0}`). The server spells the format rule `_invalid`
1914
- * (the catalog's existing convention, e.g. `error.email_invalid`), so
1915
- * that suffix takes the format wording; `_unknown_field` (a submitted
1916
- * name that is not a step field) takes the catch-all.
2152
+ * @summary Create team
1917
2153
  */
1918
- const FLOW_ERROR_RULE_FALLBACKS = [
1919
- {
1920
- suffix: "_required",
1921
- generic: "error.field_required"
1922
- },
1923
- {
1924
- suffix: "_min_length",
1925
- generic: "error.field_min_length"
1926
- },
1927
- {
1928
- suffix: "_max_length",
1929
- generic: "error.field_max_length"
1930
- },
1931
- {
1932
- suffix: "_format",
1933
- generic: "error.field_format"
1934
- },
1935
- {
1936
- suffix: "_invalid",
1937
- generic: "error.field_format"
1938
- },
1939
- {
1940
- suffix: "_unknown_field",
1941
- generic: "error.field_invalid"
1942
- }
1943
- ];
1944
- const FLOW_ERROR_CATCH_ALL_KEY = "error.field_invalid";
2154
+ const createTeam = async (createTeamBody, params, options) => {
2155
+ return customFetch(getCreateTeamUrl(params), {
2156
+ ...options,
2157
+ method: "POST",
2158
+ headers: {
2159
+ "Content-Type": "application/json",
2160
+ ...options?.headers
2161
+ },
2162
+ body: JSON.stringify(createTeamBody)
2163
+ });
2164
+ };
2165
+ const getQueryTeamsUrl = (params) => {
2166
+ const normalizedParams = new URLSearchParams();
2167
+ Object.entries(params || {}).forEach(([key, value]) => {
2168
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2169
+ });
2170
+ const stringifiedParams = normalizedParams.toString();
2171
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/teams/query?${stringifiedParams}` : `${getProxyPath()}/teams/query`;
2172
+ };
1945
2173
  /**
1946
- * Localises a `step.error` payload of `error.*` catalog keys
1947
- * field-validation violations and general engine failures (e.g.
1948
- * `error.invalid_credentials`, `error.passkey_invalid`) alike.
1949
- * Returns `null` unless EVERY `"; "`-joined segment is an `error.*` key
1950
- * — the caller keeps other payloads (outcome tokens such as
1951
- * `user_not_found`) verbatim.
1952
- *
1953
- * Keys the locale knows pass through as `text_key` entries: the template
1954
- * localises them via `| t`, and `fieldErrorKeys` routes the known ones
1955
- * inline to their field. Unknown keys with a recognised rule suffix are
1956
- * pre-localised here from their generic {@link FLOW_ERROR_RULE_FALLBACKS}
1957
- * entry; unknown keys without one pass through as `text_key` (matching
1958
- * `| t`'s behaviour for non-validation keys such as
1959
- * `error.sign_in_server`, which localises via `.title`/`.body`).
2174
+ * Returns the teams of a project, paginated with a cursor.
2175
+
2176
+ * @summary Query teams
1960
2177
  */
1961
- function localiseFlowErrorKeys(raw, ctx) {
1962
- const segments = raw.split("; ");
1963
- if (!segments.every((segment) => segment.startsWith("error."))) return null;
1964
- return segments.map((key) => localiseFlowErrorKey(key, ctx));
1965
- }
1966
- function localiseFlowErrorKey(key, ctx) {
1967
- const inlineField = fieldErrorKeys[key];
1968
- const orphanedInline = inlineField !== void 0 && ctx.fields !== void 0 && !ctx.fields.includes(inlineField);
1969
- if (!orphanedInline && ctx.locale[key] !== void 0) return inlineField !== void 0 ? {
1970
- field: inlineField,
1971
- text_key: key
1972
- } : { text_key: key };
1973
- for (const { suffix, generic } of FLOW_ERROR_RULE_FALLBACKS) {
1974
- if (!key.endsWith(suffix)) continue;
1975
- const field = key.slice(6, key.length - suffix.length);
1976
- if (field === "") break;
1977
- const message = capitaliseFirst(interpolate(ctx.locale[generic] ?? ctx.locale[FLOW_ERROR_CATCH_ALL_KEY] ?? "Please check {0}.", [fieldLabel(ctx, field)]));
1978
- return ctx.fields !== void 0 && ctx.fields.includes(field) ? {
1979
- field,
1980
- message
1981
- } : { message };
1982
- }
1983
- if (orphanedInline && ctx.locale[key] !== void 0) return { message: ctx.locale[key] };
1984
- return { text_key: key };
1985
- }
1986
- /** `<step>.field.<name>` from the locale, else a humanised field name. */
1987
- function fieldLabel(ctx, field) {
1988
- return ctx.locale[`${ctx.stepName}.field.${field}`] ?? humaniseFieldName(field);
1989
- }
2178
+ const queryTeams = async (queryTeamsBody, params, options) => {
2179
+ return customFetch(getQueryTeamsUrl(params), {
2180
+ ...options,
2181
+ method: "POST",
2182
+ headers: {
2183
+ "Content-Type": "application/json",
2184
+ ...options?.headers
2185
+ },
2186
+ body: JSON.stringify(queryTeamsBody)
2187
+ });
2188
+ };
2189
+ const getGetTeamUrl = (teamId) => {
2190
+ return `${getProxyPath()}/teams/${teamId}`;
2191
+ };
1990
2192
  /**
1991
- * `x-auth-methods#password` "password", `givenName` "given name",
1992
- * `date_of_birth` → "date of birth".
2193
+ * Returns a Team by its id.
2194
+
2195
+ * @summary Get team
1993
2196
  */
1994
- function humaniseFieldName(field) {
1995
- return (field.includes("#") ? field.split("#").pop() : field).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_.-]+/g, " ").trim().toLowerCase();
1996
- }
1997
- /** Messages may open with a lowercase field label; sentence-case them. */
1998
- function capitaliseFirst(text) {
1999
- return text.length > 0 ? text[0]?.toUpperCase() + text.slice(1) : text;
2000
- }
2001
- function stringify(value) {
2002
- if (value == null) return "";
2003
- if (typeof value === "string") return value;
2004
- if (typeof value === "number" || typeof value === "boolean") return String(value);
2005
- try {
2006
- return JSON.stringify(value);
2007
- } catch {
2008
- return String(value);
2009
- }
2010
- }
2197
+ const getTeam = async (teamId, options) => {
2198
+ return customFetch(getGetTeamUrl(teamId), {
2199
+ ...options,
2200
+ method: "GET"
2201
+ });
2202
+ };
2203
+ const getDeleteTeamUrl = (teamId) => {
2204
+ return `${getProxyPath()}/teams/${teamId}`;
2205
+ };
2011
2206
  /**
2012
- * Positional `{n}` substitution shared by the `| t` filter and the
2013
- * pre-localising fallback in {@link localiseFlowErrorKeys}, so both
2014
- * produce byte-identical output for the same template and args.
2207
+ * Deactivates the team.
2208
+
2209
+ The team is tombstoned rather than erased: it stays readable through
2210
+ getTeam with status `deactivated`. Its memberships are removed and the
2211
+ users whose lifecycle it owns are deactivated with it.
2212
+
2213
+ The request is idempotent. Deleting a team that is already deactivated
2214
+ or doesn't exist succeeds without changing anything.
2215
+
2216
+ * @summary Delete team
2015
2217
  */
2016
- function interpolate(template, args) {
2017
- if (args.length === 0) return template;
2018
- return template.replace(/\{(\d+)\}/g, (match, index) => {
2019
- return args[Number(index)] ?? match;
2218
+ const deleteTeam = async (teamId, options) => {
2219
+ return customFetch(getDeleteTeamUrl(teamId), {
2220
+ ...options,
2221
+ method: "DELETE"
2020
2222
  });
2021
- }
2022
- //#endregion
2023
- //#region src/orchestrator/template-names.ts
2223
+ };
2224
+ const getUpdateTeamUrl = (teamId) => {
2225
+ return `${getProxyPath()}/teams/${teamId}`;
2226
+ };
2024
2227
  /**
2025
- * Names of the bundled Liquid templates the `<zitadel-login>` orchestrator
2026
- * registers.
2027
- *
2028
- * This lives in its own module — separate from `liquid.ts` — so the public
2029
- * barrel can re-export `TEMPLATE_NAMES` without dragging LiquidJS' `Liquid`
2030
- * type (and its Node ambient-type requirements) into the published
2031
- * declaration surface. See `liquid.ts` / the orchestrator barrel.
2228
+ * Update team. Only active teams can be updated.
2229
+
2230
+ * @summary Update team
2032
2231
  */
2033
- const TEMPLATE_NAMES = {
2034
- default: "default",
2035
- authForm: "auth-form",
2036
- passkeyUpsell: "passkey-upsell",
2037
- signedIn: "signed-in"
2232
+ const updateTeam = async (teamId, updateTeamBody, options) => {
2233
+ return customFetch(getUpdateTeamUrl(teamId), {
2234
+ ...options,
2235
+ method: "PATCH",
2236
+ headers: {
2237
+ "Content-Type": "application/json",
2238
+ ...options?.headers
2239
+ },
2240
+ body: JSON.stringify(updateTeamBody)
2241
+ });
2242
+ };
2243
+ const getCreateBrandingUrl = (params) => {
2244
+ const normalizedParams = new URLSearchParams();
2245
+ Object.entries(params || {}).forEach(([key, value]) => {
2246
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2247
+ });
2248
+ const stringifiedParams = normalizedParams.toString();
2249
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/branding?${stringifiedParams}` : `${getProxyPath()}/branding`;
2038
2250
  };
2039
- //#endregion
2040
- //#region src/orchestrator/locales/en.ts
2041
2251
  /**
2042
- * Minimal English locale dictionary.
2043
- *
2044
- * The `| t` filter looks up `text_key` strings here. Missing keys fall through
2045
- * to the raw key (matches the spec in
2046
- * `docs/design/flowengine/flow-engine-guide.md`).
2047
- *
2048
- * Text keys are auto-generated by the server from the flow definition:
2049
- * - Step titles: `<step>.title`
2050
- * - Step descriptions: `<step>.description`
2051
- * - Field labels: `<step>.field.<field>`
2052
- * - Action labels: `<step>.action.<action>`
2053
- *
2054
- * Copy aligned to Figma screens file `xkvBjkOJ8ENuHdTGZHXezK` (May 2026).
2055
- * MVP only — multi-locale support is deferred.
2252
+ * Publishes a new immutable branding revision for the project. Branding
2253
+ revisions cannot be updated or deleted; every edit publishes a new
2254
+ revision, and flow responses resolve the latest revision per project
2255
+ (see ADR 040).
2256
+
2257
+ The `liquid_template` is validated lexically on save (size, encoding,
2258
+ banned patterns such as `<script>` tags, inline event handlers, and the
2259
+ `| raw` filter). Authoritative LiquidJS validation runs at authoring
2260
+ time via `zitadel plan` / `zitadel apply`.
2261
+
2262
+ * @summary Publish a new branding revision
2056
2263
  */
2057
- const en = {
2058
- "identifier.title": "Sign in",
2059
- "identifier.description": "Enter your email to continue",
2060
- "identifier.field.email": "Email",
2061
- "identifier.field.email.placeholder": "you@example.com",
2062
- "identifier.field.password": "Password",
2063
- "identifier.action.submit": "Sign in",
2064
- "identifier.action.continue": "Sign in",
2065
- "identifier.action.passkey": "Sign in with a passkey",
2066
- "identifier.action.register.lead": "Don't have an account? ",
2067
- "identifier.action.register.link": "Sign up",
2068
- "authenticate.title": "Sign in",
2069
- "authenticate.description": "Enter your password",
2070
- "authenticate.field.password": "Password",
2071
- "authenticate.action.submit": "Sign in",
2072
- "choose-register.title": "Create your account",
2073
- "choose-register.description": "Choose how you'd like to sign up",
2074
- "choose-register.action.register-with-password": "Sign up with password",
2075
- "choose-register.action.register-with-passkey": "Sign up with passkey",
2076
- "collect-credentials.title": "Create your account",
2077
- "collect-credentials.description": "Set up your email and password",
2078
- "collect-credentials.field.email": "Email",
2079
- "collect-credentials.field.email.placeholder": "you@example.com",
2080
- "collect-credentials.field.password": "Password",
2081
- "collect-credentials.action.submit": "Sign up",
2082
- "register-password.title": "Create your password",
2083
- "register-password.description": "Choose a secure password for your account",
2084
- "register-password.field.password": "Password",
2085
- "register-password.action.submit": "Sign up",
2086
- "register-password.action.back": "Back",
2087
- "passkey-upsell.title": "Sign in faster next time",
2088
- "passkey-upsell.description": "No password needed ever again.",
2089
- "passkey-upsell.description.line1": "No password needed ever again.",
2090
- "passkey-upsell.description.line2": "Sign in with Face ID, Touch ID, or PIN.",
2091
- "passkey-upsell.action.passkey_register": "Set up passkey",
2092
- "passkey-upsell.action.skip": "Skip for now",
2093
- "passkey-upsell.action.setup": "Set up passkey",
2094
- "collect-passkey-email.title": "Create your account",
2095
- "collect-passkey-email.description": "Enter your email to set up a passkey",
2096
- "collect-passkey-email.field.email": "Email",
2097
- "collect-passkey-email.field.email.placeholder": "you@example.com",
2098
- "collect-passkey-email.action.submit": "Continue",
2099
- "passkey-enroll.title": "Set up your passkey",
2100
- "passkey-enroll.description": "Use Face ID, Touch ID, or your device PIN to create a passkey.",
2101
- "passkey-enroll.action.passkey_register": "Set up passkey",
2102
- "done.title": "You're signed in as",
2103
- "done.description": "",
2104
- "password.title": "Sign in",
2105
- "password.description": "Enter your password",
2106
- "password.field.password": "Password",
2107
- "password.action.signin": "Sign in",
2108
- "password.action.passkey": "Sign in with a passkey",
2109
- "password.action.back": "Back",
2110
- "password.action.register.lead": "Don't have an account? ",
2111
- "password.action.register.link": "Sign up",
2112
- "register.title": "Create your account",
2113
- "register.description": "",
2114
- "register.field.email": "Email",
2115
- "register.field.email.placeholder": "you@example.com",
2116
- "register.field.password": "Password",
2117
- "register.field.givenName": "Given name",
2118
- "register.field.familyName": "Family name",
2119
- "register.field.companyName": "Company name",
2120
- "register.field.dateOfBirth": "Date of birth",
2121
- "register.field.country": "Country",
2122
- "register.field.country.placeholder": "Select your country",
2123
- "register.field.terms": "I agree to the terms of service",
2124
- "register.action.password": "Continue with password",
2125
- "register.action.passkey": "Continue with a passkey",
2126
- "register.action.submit": "Sign up",
2127
- "register.action.back": "Back",
2128
- "register.action.sign_in.lead": "Already have an account? ",
2129
- "register.action.sign_in.link": "Sign in",
2130
- "complete.title": "You're signed in as",
2131
- "signed-in.continue": "Continue",
2132
- "signed-in.logout": "Sign out",
2133
- "passkey-login.title": "Sign in with your passkey",
2134
- "passkey-first.title": "Sign in",
2135
- "passkey-first.description": "Use your passkey to continue",
2136
- "passkey-first.action.passkey": "Continue with passkey",
2137
- "passkey-first.action.email_fallback": "Use email instead",
2138
- "recover.title": "Check your email",
2139
- "recover.description": "We sent a password reset link to your email address.",
2140
- "recover.action.back": "Back to sign in",
2141
- "submit.continue": "Continue",
2142
- "submit.signin": "Sign in",
2143
- "action.forgot_password": "Forgot password?",
2144
- "action.cancel": "Cancel",
2145
- "action.back": "Back",
2146
- "sso.redirect.title": "Redirecting to your provider…",
2147
- "passkey.pending.status": "Waiting for your passkey…",
2148
- "error.passkey_cancelled": "The passkey prompt was closed before completing.",
2149
- "error.passkey_timeout": "The passkey request timed out. Please try again.",
2150
- "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
2151
- "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
2152
- "error.passkey_unsupported": "This device does not support passkeys",
2153
- "error.passkey_failed": "Something went wrong. Please try again.",
2154
- "error.passkey_invalid": "This passkey could not be verified. Please try again.",
2155
- "error.passkey_registration_invalid": "The new passkey could not be verified. Please try registering it again.",
2156
- "error.email_required": "Please enter an email address",
2157
- "error.email_invalid": "Please enter a valid email",
2158
- "error.password_required": "Please enter a password",
2159
- "error.password_incorrect": "Wrong email or password.",
2160
- "error.email_exists": "An account with this email already exists.",
2161
- /** Figma sign-in error `6602:180268` — inline on password field. */
2162
- "error.invalid_credentials": "Wrong email or password.",
2163
- "error.required": "This field is required.",
2164
- "error.field_required": "{0} is required.",
2165
- "error.field_format": "Please enter a valid {0}.",
2166
- "error.field_min_length": "{0} is too short.",
2167
- "error.field_max_length": "{0} is too long.",
2168
- "error.field_invalid": "Please check {0}.",
2169
- "error.sign_in_server.title": "We couldn't complete your sign in.",
2170
- "error.sign_in_server.body": "Please try again in a few minutes"
2264
+ const createBranding = async (createBrandingBody, params, options) => {
2265
+ return customFetch(getCreateBrandingUrl(params), {
2266
+ ...options,
2267
+ method: "POST",
2268
+ headers: {
2269
+ "Content-Type": "application/json",
2270
+ ...options?.headers
2271
+ },
2272
+ body: JSON.stringify(createBrandingBody)
2273
+ });
2274
+ };
2275
+ const getListBrandingUrl = (params) => {
2276
+ const normalizedParams = new URLSearchParams();
2277
+ Object.entries(params || {}).forEach(([key, value]) => {
2278
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2279
+ });
2280
+ const stringifiedParams = normalizedParams.toString();
2281
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/branding?${stringifiedParams}` : `${getProxyPath()}/branding`;
2282
+ };
2283
+ /**
2284
+ * Lists branding revisions for the project, newest first, capped at the
2285
+ 100 most recent. The first entry is the revision flow responses
2286
+ currently resolve. Deliberately unpaginated in v1 — list endpoints
2287
+ gain a real query mechanism together (ADR 031); advertising pagination
2288
+ parameters the server ignores would be worse than none.
2289
+
2290
+ * @summary List branding revisions
2291
+ */
2292
+ const listBranding = async (params, options) => {
2293
+ return customFetch(getListBrandingUrl(params), {
2294
+ ...options,
2295
+ method: "GET"
2296
+ });
2171
2297
  };
2172
- //#endregion
2173
- //#region src/orchestrator/locales/de.ts
2174
- const de = {
2175
- "identifier.title": "Anmelden",
2176
- "identifier.description": "Gib deine E-Mail-Adresse ein, um fortzufahren",
2177
- "identifier.field.email": "E-Mail",
2178
- "identifier.field.email.placeholder": "du@beispiel.de",
2179
- "identifier.field.password": "Passwort",
2180
- "identifier.action.submit": "Anmelden",
2181
- "identifier.action.continue": "Anmelden",
2182
- "identifier.action.passkey": "Mit Passkey anmelden",
2183
- "identifier.action.register.lead": "Noch kein Konto? ",
2184
- "identifier.action.register.link": "Registrieren",
2185
- "authenticate.title": "Anmelden",
2186
- "authenticate.description": "Gib dein Passwort ein",
2187
- "authenticate.field.password": "Passwort",
2188
- "authenticate.action.submit": "Anmelden",
2189
- "choose-register.title": "Konto erstellen",
2190
- "choose-register.description": "Wähle, wie du dich registrieren möchtest",
2191
- "choose-register.action.register-with-password": "Mit Passwort registrieren",
2192
- "choose-register.action.register-with-passkey": "Mit Passkey registrieren",
2193
- "collect-credentials.title": "Konto erstellen",
2194
- "collect-credentials.description": "Richte E-Mail und Passwort ein",
2195
- "collect-credentials.field.email": "E-Mail",
2196
- "collect-credentials.field.email.placeholder": "du@beispiel.de",
2197
- "collect-credentials.field.password": "Passwort",
2198
- "collect-credentials.action.submit": "Registrieren",
2199
- "register-password.title": "Passwort erstellen",
2200
- "register-password.description": "Wähle ein sicheres Passwort für dein Konto",
2201
- "register-password.field.password": "Passwort",
2202
- "register-password.action.submit": "Registrieren",
2203
- "register-password.action.back": "Zurück",
2204
- "passkey-upsell.title": "Nächstes Mal schneller anmelden",
2205
- "passkey-upsell.description": "Kein Passwort mehr nötig.",
2206
- "passkey-upsell.description.line1": "Kein Passwort mehr nötig.",
2207
- "passkey-upsell.description.line2": "Melde dich mit Face ID, Touch ID oder PIN an.",
2208
- "passkey-upsell.action.passkey_register": "Passkey einrichten",
2209
- "passkey-upsell.action.skip": "Vorerst überspringen",
2210
- "passkey-upsell.action.setup": "Passkey einrichten",
2211
- "collect-passkey-email.title": "Konto erstellen",
2212
- "collect-passkey-email.description": "Gib deine E-Mail-Adresse ein, um einen Passkey einzurichten",
2213
- "collect-passkey-email.field.email": "E-Mail",
2214
- "collect-passkey-email.field.email.placeholder": "du@beispiel.de",
2215
- "collect-passkey-email.action.submit": "Weiter",
2216
- "passkey-enroll.title": "Passkey einrichten",
2217
- "passkey-enroll.description": "Verwende Face ID, Touch ID oder die Geräte-PIN, um einen Passkey zu erstellen.",
2218
- "passkey-enroll.action.passkey_register": "Passkey einrichten",
2219
- "done.title": "Du bist angemeldet als",
2220
- "done.description": "",
2221
- "password.title": "Anmelden",
2222
- "password.description": "Gib dein Passwort ein",
2223
- "password.field.password": "Passwort",
2224
- "password.action.signin": "Anmelden",
2225
- "password.action.passkey": "Mit Passkey anmelden",
2226
- "password.action.back": "Zurück",
2227
- "password.action.register.lead": "Noch kein Konto? ",
2228
- "password.action.register.link": "Registrieren",
2229
- "register.title": "Konto erstellen",
2230
- "register.description": "",
2231
- "register.field.email": "E-Mail",
2232
- "register.field.email.placeholder": "du@beispiel.de",
2233
- "register.field.password": "Passwort",
2234
- "register.field.givenName": "Vorname",
2235
- "register.field.familyName": "Nachname",
2236
- "register.field.companyName": "Firmenname",
2237
- "register.field.dateOfBirth": "Geburtsdatum",
2238
- "register.field.country": "Land",
2239
- "register.field.country.placeholder": "Land auswählen",
2240
- "register.field.terms": "Ich stimme den Nutzungsbedingungen zu",
2241
- "register.action.password": "Mit Passwort fortfahren",
2242
- "register.action.passkey": "Weiter mit Passkey",
2243
- "register.action.submit": "Registrieren",
2244
- "register.action.back": "Zurück",
2245
- "register.action.sign_in.lead": "Bereits ein Konto? ",
2246
- "register.action.sign_in.link": "Anmelden",
2247
- "complete.title": "Du bist angemeldet als",
2248
- "signed-in.continue": "Weiter",
2249
- "signed-in.logout": "Abmelden",
2250
- "passkey-login.title": "Mit Passkey anmelden",
2251
- "passkey-first.title": "Anmelden",
2252
- "passkey-first.description": "Verwende deinen Passkey, um fortzufahren",
2253
- "passkey-first.action.passkey": "Weiter mit Passkey",
2254
- "passkey-first.action.email_fallback": "Stattdessen E-Mail verwenden",
2255
- "recover.title": "E-Mail prüfen",
2256
- "recover.description": "Wir haben einen Link zum Zurücksetzen des Passworts an deine E-Mail-Adresse gesendet.",
2257
- "recover.action.back": "Zurück zur Anmeldung",
2258
- "submit.continue": "Weiter",
2259
- "submit.signin": "Anmelden",
2260
- "action.forgot_password": "Passwort vergessen?",
2261
- "action.cancel": "Abbrechen",
2262
- "action.back": "Zurück",
2263
- "sso.redirect.title": "Weiterleitung zum Anbieter…",
2264
- "passkey.pending.status": "Warten auf deinen Passkey…",
2265
- "error.passkey_cancelled": "Die Passkey-Abfrage wurde vorzeitig geschlossen.",
2266
- "error.passkey_timeout": "Die Passkey-Anfrage hat zu lange gedauert. Bitte versuche es erneut.",
2267
- "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
2268
- "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
2269
- "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
2270
- "error.passkey_failed": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
2271
- "error.passkey_invalid": "Dieser Passkey konnte nicht bestätigt werden. Bitte versuche es erneut.",
2272
- "error.passkey_registration_invalid": "Der neue Passkey konnte nicht bestätigt werden. Bitte registriere ihn erneut.",
2273
- "error.email_required": "Bitte gib eine E-Mail-Adresse ein",
2274
- "error.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein",
2275
- "error.password_required": "Bitte gib ein Passwort ein",
2276
- "error.password_incorrect": "Falsche E-Mail oder falsches Passwort.",
2277
- "error.email_exists": "Ein Konto mit dieser E-Mail-Adresse existiert bereits.",
2278
- "error.invalid_credentials": "Falsche E-Mail oder falsches Passwort.",
2279
- "error.required": "Dieses Feld ist erforderlich.",
2280
- "error.field_required": "{0} ist erforderlich.",
2281
- "error.field_format": "Bitte gib einen gültigen Wert für {0} ein.",
2282
- "error.field_min_length": "{0} ist zu kurz.",
2283
- "error.field_max_length": "{0} ist zu lang.",
2284
- "error.field_invalid": "Bitte überprüfe {0}.",
2285
- "error.sign_in_server.title": "Anmeldung konnte nicht abgeschlossen werden.",
2286
- "error.sign_in_server.body": "Bitte versuche es in einigen Minuten erneut"
2298
+ const getGetBrandingByIdUrl = (id) => {
2299
+ return `${getProxyPath()}/branding/${id}`;
2287
2300
  };
2288
- //#endregion
2289
- //#region src/orchestrator/locales/it.ts
2290
- const it = {
2291
- "identifier.title": "Accedi",
2292
- "identifier.description": "Inserisci la tua e-mail per continuare",
2293
- "identifier.field.email": "E-mail",
2294
- "identifier.field.email.placeholder": "tu@esempio.com",
2295
- "identifier.field.password": "Password",
2296
- "identifier.action.submit": "Accedi",
2297
- "identifier.action.continue": "Accedi",
2298
- "identifier.action.passkey": "Accedi con passkey",
2299
- "identifier.action.register.lead": "Non hai un account? ",
2300
- "identifier.action.register.link": "Registrati",
2301
- "authenticate.title": "Accedi",
2302
- "authenticate.description": "Inserisci la tua password",
2303
- "authenticate.field.password": "Password",
2304
- "authenticate.action.submit": "Accedi",
2305
- "choose-register.title": "Crea il tuo account",
2306
- "choose-register.description": "Scegli come registrarti",
2307
- "choose-register.action.register-with-password": "Registrati con password",
2308
- "choose-register.action.register-with-passkey": "Registrati con passkey",
2309
- "collect-credentials.title": "Crea il tuo account",
2310
- "collect-credentials.description": "Configura e-mail e password",
2311
- "collect-credentials.field.email": "E-mail",
2312
- "collect-credentials.field.email.placeholder": "tu@esempio.com",
2313
- "collect-credentials.field.password": "Password",
2314
- "collect-credentials.action.submit": "Registrati",
2315
- "register-password.title": "Crea la tua password",
2316
- "register-password.description": "Scegli una password sicura per il tuo account",
2317
- "register-password.field.password": "Password",
2318
- "register-password.action.submit": "Registrati",
2319
- "register-password.action.back": "Indietro",
2320
- "passkey-upsell.title": "Accedi più velocemente la prossima volta",
2321
- "passkey-upsell.description": "Nessuna password necessaria.",
2322
- "passkey-upsell.description.line1": "Nessuna password necessaria.",
2323
- "passkey-upsell.description.line2": "Accedi con Face ID, Touch ID o PIN.",
2324
- "passkey-upsell.action.passkey_register": "Configura passkey",
2325
- "passkey-upsell.action.skip": "Salta per ora",
2326
- "passkey-upsell.action.setup": "Configura passkey",
2327
- "collect-passkey-email.title": "Crea il tuo account",
2328
- "collect-passkey-email.description": "Inserisci la tua e-mail per configurare una passkey",
2329
- "collect-passkey-email.field.email": "E-mail",
2330
- "collect-passkey-email.field.email.placeholder": "tu@esempio.com",
2331
- "collect-passkey-email.action.submit": "Continua",
2332
- "passkey-enroll.title": "Configura la tua passkey",
2333
- "passkey-enroll.description": "Usa Face ID, Touch ID o il PIN del dispositivo per creare una passkey.",
2334
- "passkey-enroll.action.passkey_register": "Configura passkey",
2335
- "done.title": "Sei connesso come",
2336
- "done.description": "",
2337
- "password.title": "Accedi",
2338
- "password.description": "Inserisci la tua password",
2339
- "password.field.password": "Password",
2340
- "password.action.signin": "Accedi",
2341
- "password.action.passkey": "Accedi con passkey",
2342
- "password.action.back": "Indietro",
2343
- "password.action.register.lead": "Non hai un account? ",
2344
- "password.action.register.link": "Registrati",
2345
- "register.title": "Crea il tuo account",
2346
- "register.description": "",
2347
- "register.field.email": "E-mail",
2348
- "register.field.email.placeholder": "tu@esempio.com",
2349
- "register.field.password": "Password",
2350
- "register.field.givenName": "Nome",
2351
- "register.field.familyName": "Cognome",
2352
- "register.field.companyName": "Nome azienda",
2353
- "register.field.dateOfBirth": "Data di nascita",
2354
- "register.field.country": "Paese",
2355
- "register.field.country.placeholder": "Seleziona il tuo paese",
2356
- "register.field.terms": "Accetto i termini di servizio",
2357
- "register.action.password": "Continua con password",
2358
- "register.action.passkey": "Continua con passkey",
2359
- "register.action.submit": "Registrati",
2360
- "register.action.back": "Indietro",
2361
- "register.action.sign_in.lead": "Hai già un account? ",
2362
- "register.action.sign_in.link": "Accedi",
2363
- "complete.title": "Sei connesso come",
2364
- "signed-in.continue": "Continua",
2365
- "signed-in.logout": "Esci",
2366
- "passkey-login.title": "Accedi con la tua passkey",
2367
- "passkey-first.title": "Accedi",
2368
- "passkey-first.description": "Usa la tua passkey per continuare",
2369
- "passkey-first.action.passkey": "Continua con passkey",
2370
- "passkey-first.action.email_fallback": "Usa invece l'email",
2371
- "recover.title": "Controlla la tua e-mail",
2372
- "recover.description": "Abbiamo inviato un link per reimpostare la password al tuo indirizzo e-mail.",
2373
- "recover.action.back": "Torna all'accesso",
2374
- "submit.continue": "Continua",
2375
- "submit.signin": "Accedi",
2376
- "action.forgot_password": "Password dimenticata?",
2377
- "action.cancel": "Annulla",
2378
- "action.back": "Indietro",
2379
- "sso.redirect.title": "Reindirizzamento al provider…",
2380
- "passkey.pending.status": "In attesa della tua passkey…",
2381
- "error.passkey_cancelled": "La richiesta della passkey è stata chiusa prima del completamento.",
2382
- "error.passkey_timeout": "La richiesta della passkey è scaduta. Riprova.",
2383
- "error.passkey_not_registered": "Questa passkey non è registrata. Accedi con e-mail e password.",
2384
- "error.passkey_setup_failed": "La registrazione della passkey non è stata completata. Riprova.",
2385
- "error.passkey_unsupported": "Questo dispositivo non supporta le passkey",
2386
- "error.passkey_failed": "Qualcosa è andato storto. Riprova.",
2387
- "error.passkey_invalid": "Non è stato possibile verificare questa passkey. Riprova.",
2388
- "error.passkey_registration_invalid": "Non è stato possibile verificare la nuova passkey. Riprova a registrarla.",
2389
- "error.email_required": "Inserisci un indirizzo e-mail",
2390
- "error.email_invalid": "Inserisci un indirizzo e-mail valido",
2391
- "error.password_required": "Inserisci una password",
2392
- "error.password_incorrect": "E-mail o password errata.",
2393
- "error.email_exists": "Esiste già un account con questo indirizzo e-mail.",
2394
- "error.invalid_credentials": "E-mail o password errata.",
2395
- "error.required": "Questo campo è obbligatorio.",
2396
- "error.field_required": "{0} è obbligatorio.",
2397
- "error.field_format": "Inserisci un valore valido per {0}.",
2398
- "error.field_min_length": "{0} è troppo corto.",
2399
- "error.field_max_length": "{0} è troppo lungo.",
2400
- "error.field_invalid": "Controlla {0}.",
2401
- "error.sign_in_server.title": "Non è stato possibile completare l'accesso.",
2402
- "error.sign_in_server.body": "Riprova tra qualche minuto"
2301
+ /**
2302
+ * Retrieves a single branding revision, including its stored configuration.
2303
+ * @summary Get a branding revision by ID
2304
+ */
2305
+ const getBrandingById = async (id, options) => {
2306
+ return customFetch(getGetBrandingByIdUrl(id), {
2307
+ ...options,
2308
+ method: "GET"
2309
+ });
2310
+ };
2311
+ const getListEventsUrl = (params) => {
2312
+ const normalizedParams = new URLSearchParams();
2313
+ Object.entries(params || {}).forEach(([key, value]) => {
2314
+ if (Array.isArray(value) && ["category", "event_type"].includes(key)) {
2315
+ value.forEach((v) => {
2316
+ normalizedParams.append(key, v === null ? "null" : v.toString());
2317
+ });
2318
+ return;
2319
+ }
2320
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2321
+ });
2322
+ const stringifiedParams = normalizedParams.toString();
2323
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/events?${stringifiedParams}` : `${getProxyPath()}/events`;
2324
+ };
2325
+ /**
2326
+ * Returns project-scoped audit events, newest-first by keyset on
2327
+ `(created_at, id)` (ADR 027 / ADR 049). Pass `order=asc` for oldest-first.
2328
+ Requires `events.read`.
2329
+
2330
+ Pre-claim projects return an empty list (events are stored but not
2331
+ visible until claim succeeds). Team-scoped credentials see only events
2332
+ whose emit-time `team_id` matches the credential team (enforced when
2333
+ team-scoped tokens exist).
2334
+
2335
+ Clients discriminate each item via `event_type` (OpenAPI Event oneOf) —
2336
+ see docs/design/api/events-catalog.md.
2337
+
2338
+ * @summary List events
2339
+ */
2340
+ const listEvents = async (params, options) => {
2341
+ return customFetch(getListEventsUrl(params), {
2342
+ ...options,
2343
+ method: "GET"
2344
+ });
2403
2345
  };
2404
- //#endregion
2405
- //#region src/orchestrator/locales/business.ts
2406
- const businessLocales = {
2407
- en: {
2408
- "identifier.field.email": "Work email",
2409
- "identifier.field.email.placeholder": "you@company.com",
2410
- "collect-credentials.field.email": "Work email",
2411
- "collect-credentials.field.email.placeholder": "you@company.com",
2412
- "collect-passkey-email.field.email": "Work email",
2413
- "collect-passkey-email.field.email.placeholder": "you@company.com",
2414
- "register.field.email": "Work email",
2415
- "register.field.email.placeholder": "you@company.com"
2416
- },
2417
- de: {
2418
- "identifier.field.email": "Geschäftliche E-Mail",
2419
- "identifier.field.email.placeholder": "du@unternehmen.com",
2420
- "collect-credentials.field.email": "Geschäftliche E-Mail",
2421
- "collect-credentials.field.email.placeholder": "du@unternehmen.com",
2422
- "collect-passkey-email.field.email": "Geschäftliche E-Mail",
2423
- "collect-passkey-email.field.email.placeholder": "du@unternehmen.com",
2424
- "register.field.email": "Geschäftliche E-Mail",
2425
- "register.field.email.placeholder": "du@unternehmen.com"
2426
- },
2427
- it: {
2428
- "identifier.field.email": "E-mail aziendale",
2429
- "identifier.field.email.placeholder": "tu@azienda.com",
2430
- "collect-credentials.field.email": "E-mail aziendale",
2431
- "collect-credentials.field.email.placeholder": "tu@azienda.com",
2432
- "collect-passkey-email.field.email": "E-mail aziendale",
2433
- "collect-passkey-email.field.email.placeholder": "tu@azienda.com",
2434
- "register.field.email": "E-mail aziendale",
2435
- "register.field.email.placeholder": "tu@azienda.com"
2436
- }
2346
+ const getGetEventUrl = (id, params) => {
2347
+ const normalizedParams = new URLSearchParams();
2348
+ Object.entries(params || {}).forEach(([key, value]) => {
2349
+ if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
2350
+ });
2351
+ const stringifiedParams = normalizedParams.toString();
2352
+ return stringifiedParams.length > 0 ? `${getProxyPath()}/events/${id}?${stringifiedParams}` : `${getProxyPath()}/events/${id}`;
2437
2353
  };
2438
- //#endregion
2439
- //#region src/orchestrator/locales/index.ts
2440
- const builtinLocales = {
2441
- en,
2442
- de,
2443
- it
2354
+ /**
2355
+ * Loads a single event by `(project_id, id)`. Requires `events.read`.
2356
+ Events are not registered in `resource_scope_index`; project scope is
2357
+ required on the query (ADR 049). Misses and cross-project ids return 404.
2358
+
2359
+ Pre-claim projects return 404 (stored events stay invisible until claim).
2360
+
2361
+ * @summary Get event by ID
2362
+ */
2363
+ const getEvent = async (id, params, options) => {
2364
+ return customFetch(getGetEventUrl(id, params), {
2365
+ ...options,
2366
+ method: "GET"
2367
+ });
2444
2368
  };
2445
2369
  //#endregion
2446
- //#region ../shared-component-styles/dist/attribution-markup.mjs
2370
+ //#region ../api/dist/runtime/api-factory.mjs
2447
2371
  /**
2448
- * Figma sign-in attribution chip (`6593:141767`): "Secured with" + 65×16 logotype.
2449
- * Shared by Lit orchestrator HTML, dev playground, and paired React playgrounds.
2372
+ * Factory for the typed Zitadel client every consumer reaches for.
2373
+ *
2374
+ * Wraps the orval-generated endpoints in a {@link Proxy} so each
2375
+ * generated function sees the right base URL and (optionally) the
2376
+ * right bearer token at call time — without the caller having to
2377
+ * thread either through every operation, and without exposing the
2378
+ * module-globals (`setProxyPath` / `setApiAuthToken`) as part of the
2379
+ * public API.
2380
+ *
2381
+ * Per-instance isolation: multiple clients can coexist in one process
2382
+ * (different servers, different tokens). Their Proxy `get` traps set
2383
+ * the globals synchronously before invoking the underlying generated
2384
+ * function, which reads them at the top of `customFetch` before any
2385
+ * `await`. Two clients running interleaved within a single JS turn
2386
+ * are safe; two clients running in parallel across awaits could
2387
+ * clobber each other — not a pattern any current consumer uses.
2450
2388
  */
2451
- 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>";
2452
- /** Inner markup for the default Zitadel attribution pill (label + logotype). */
2453
- function zitadelAttributionPillInnerHtml() {
2454
- return `Secured with <span class="zr-pill__logotype">${ZITADEL_ATTRIBUTION_LOGOTYPE_SVG}</span>`;
2389
+ /**
2390
+ * Build a typed Zitadel client pre-bound to a base URL and (optionally)
2391
+ * a bearer token. Every method on the returned object mirrors a
2392
+ * generated orval function:
2393
+ *
2394
+ * const client = createZitadelClient({ baseUrl, token });
2395
+ * await client.createProject({ preview_origins: [] });
2396
+ * await client.createSchema(body, { project_id });
2397
+ */
2398
+ function createZitadelClient(opts) {
2399
+ let baseUrl = opts.baseUrl;
2400
+ while (baseUrl.endsWith("/")) baseUrl = baseUrl.slice(0, -1);
2401
+ return new Proxy(zitadelNextGen_exports, { get(target, prop, receiver) {
2402
+ const value = Reflect.get(target, prop, receiver);
2403
+ if (typeof value !== "function") return value;
2404
+ return (...args) => {
2405
+ setProxyPath(baseUrl);
2406
+ setApiAuthToken(opts.token);
2407
+ return value(...args);
2408
+ };
2409
+ } });
2410
+ }
2411
+ //#endregion
2412
+ //#region ../api/dist/runtime/config.mjs
2413
+ /**
2414
+ * Slot for the configured project, stored on `globalThis` under a
2415
+ * {@link Symbol.for} key. Every copy of this module evaluated in the
2416
+ * same JS realm resolves the symbol through the global symbol registry
2417
+ * to the same identity, so they all read and write a single slot —
2418
+ * needed because the standalone components bundle inlines its own copy
2419
+ * of this file, and dual-package hazards / monorepo duplicates can load
2420
+ * a second copy alongside the app's. With a module-local `let` each
2421
+ * instance kept its own singleton and `configureZitadel()` calls in one
2422
+ * were invisible to `getZitadelConfig()` calls in another. Sharing is
2423
+ * realm-scoped — separate realms (iframes, Node `vm` contexts, worker
2424
+ * threads) have their own registries and are not unified by this.
2425
+ */
2426
+ const PROJECT_SLOT = Symbol.for("@zitadel/api/config:currentProject");
2427
+ function readSlot() {
2428
+ return globalThis[PROJECT_SLOT] ?? null;
2429
+ }
2430
+ /**
2431
+ * Per-project API client cache. Ensures `getApi(project)` returns the
2432
+ * same instance for the same project handle — no re-wrapping on every call.
2433
+ * Module-local on purpose: keyed by the shared frozen `ZitadelProject`
2434
+ * object identity, so cross-instance reads still resolve through the
2435
+ * same key and at worst memoize once per module instance.
2436
+ */
2437
+ const apiCache = /* @__PURE__ */ new WeakMap();
2438
+ /**
2439
+ * Returns a typed API client for the given project, with the base URL
2440
+ * pre-bound. Cached per project handle — safe to call multiple times.
2441
+ *
2442
+ * ```ts
2443
+ * const project = configureZitadel({ ... });
2444
+ * export const api = getApi(project);
2445
+ * ```
2446
+ */
2447
+ function getApi(project) {
2448
+ let api = apiCache.get(project);
2449
+ if (!api) {
2450
+ api = createZitadelClient({
2451
+ baseUrl: project.proxyPath,
2452
+ token: project.publishableKey
2453
+ });
2454
+ apiCache.set(project, api);
2455
+ }
2456
+ return api;
2457
+ }
2458
+ /**
2459
+ * Returns the current project handle, or `null` if {@link configureZitadel}
2460
+ * has not been called yet.
2461
+ */
2462
+ function getZitadelConfig() {
2463
+ return readSlot();
2464
+ }
2465
+ //#endregion
2466
+ //#region src/orchestrator/resolve-api.ts
2467
+ /** Matches the `configureZitadel()` default so attribute and JS config agree. */
2468
+ const DEFAULT_PROXY_PATH = "/__nextgen";
2469
+ /**
2470
+ * Cache of project handles synthesized from attributes, keyed by their value
2471
+ * tuple. `getApi()` caches its client in a `WeakMap` keyed by the project's
2472
+ * identity, so the same attribute values must yield the *same* frozen object
2473
+ * across the several `resolveApi()` calls a single flow makes — otherwise
2474
+ * every call misses the cache and re-wraps a fresh client. A real page uses a
2475
+ * handful of distinct configs at most; the FIFO bound below is just a guard so
2476
+ * a pathological page that churns through unique configs can't leak memory.
2477
+ */
2478
+ const MAX_SYNTHETIC_PROJECTS = 32;
2479
+ const syntheticProjects = /* @__PURE__ */ new Map();
2480
+ /**
2481
+ * Builds a `ZitadelProject` from declarative attributes, or `undefined` when
2482
+ * no `project-id` was given (nothing to configure). Mirrors
2483
+ * `configureZitadel()`'s proxy-path defaulting so the two config paths are
2484
+ * interchangeable.
2485
+ */
2486
+ function projectFromAttrs({ projectId, proxyPath, url }) {
2487
+ if (!projectId) return void 0;
2488
+ const resolvedProxyPath = proxyPath || DEFAULT_PROXY_PATH;
2489
+ const resolvedUrl = url || void 0;
2490
+ const key = JSON.stringify([
2491
+ projectId,
2492
+ resolvedProxyPath,
2493
+ resolvedUrl ?? ""
2494
+ ]);
2495
+ let project = syntheticProjects.get(key);
2496
+ if (!project) {
2497
+ project = Object.freeze({
2498
+ projectId,
2499
+ proxyPath: resolvedProxyPath,
2500
+ url: resolvedUrl
2501
+ });
2502
+ if (syntheticProjects.size >= MAX_SYNTHETIC_PROJECTS) {
2503
+ const oldest = syntheticProjects.keys().next().value;
2504
+ if (oldest !== void 0) syntheticProjects.delete(oldest);
2505
+ }
2506
+ syntheticProjects.set(key, project);
2507
+ }
2508
+ return project;
2509
+ }
2510
+ /**
2511
+ * Resolves the effective SDK project handle and its typed API client for an
2512
+ * orchestrator element. Precedence, highest first:
2513
+ *
2514
+ * 1. the element's `project` property (an SDK handle set from JS / a framework),
2515
+ * 2. the global handle from `configureZitadel()`,
2516
+ * 3. the `project-id` / `proxy-path` / `url` attributes set declaratively in HTML.
2517
+ *
2518
+ * Both JS paths (property and global) win over the declarative attributes, so a
2519
+ * deliberate `configureZitadel()` is never silently overridden by fallback or
2520
+ * stale HTML attributes; the attributes are the no-JS fallback.
2521
+ *
2522
+ * Both `<zitadel-login>` and `<zitadel-logout>` need the same resolution and
2523
+ * the same "no config" failure, so it lives here rather than being copied
2524
+ * into each element. Callers run this inside their try/catch so a missing
2525
+ * configuration surfaces through the element's normal error path rather than
2526
+ * as an unhandled rejection.
2527
+ *
2528
+ * @param project Optional per-element override handle.
2529
+ * @param attrs Declarative config read from the element's attributes.
2530
+ * @param element Tag name used in the thrown error (e.g. `<zitadel-login>`).
2531
+ */
2532
+ function resolveApi(project, attrs, element) {
2533
+ const cfg = project ?? getZitadelConfig() ?? projectFromAttrs(attrs);
2534
+ if (!cfg) throw new Error(`${element} requires a configured project: set the \`project-id\` attribute, set the \`project\` property, or call configureZitadel().`);
2535
+ return {
2536
+ project: cfg,
2537
+ api: getApi(cfg)
2538
+ };
2455
2539
  }
2456
2540
  //#endregion
2457
2541
  //#region src/orchestrator/sanitiser.ts
@@ -2626,15 +2710,15 @@ const DENSITY_MAP = {
2626
2710
  }
2627
2711
  };
2628
2712
  const PALETTE_MAP = {
2629
- primary: ["--zl-color-surface-default-white"],
2630
- on_primary: ["--zl-color-text-button-default"],
2713
+ primary: ["--zl-primary", "--zl-color-surface-default-white"],
2714
+ on_primary: ["--zl-primary-foreground", "--zl-color-text-button-default"],
2631
2715
  background: ["--zl-color-surface-default-black"],
2632
2716
  surface: ["--zl-color-surface-default-primary-gray"],
2633
2717
  muted: ["--zl-color-surface-default-secondary-gray"],
2634
2718
  border: ["--zl-color-border-default-gray-200", "--zl-color-border-default-gray-100"],
2635
2719
  text: ["--zl-color-text-primary-white"],
2636
2720
  text_muted: ["--zl-color-text-secondary-gray"],
2637
- link: ["--zl-color-text-subtitle-pink"],
2721
+ link: ["--zl-color-text-link"],
2638
2722
  success: [
2639
2723
  "--zl-color-text-success",
2640
2724
  "--zl-color-border-success",
@@ -2969,6 +3053,13 @@ var ZitadelSurface = class extends LitElement {
2969
3053
  set theme(value) {
2970
3054
  this.#_theme_accessor_storage = value;
2971
3055
  }
3056
+ #_suppressHeader_accessor_storage = false;
3057
+ get suppressHeader() {
3058
+ return this.#_suppressHeader_accessor_storage;
3059
+ }
3060
+ set suppressHeader(value) {
3061
+ this.#_suppressHeader_accessor_storage = value;
3062
+ }
2972
3063
  themeController = new ThemeController(this);
2973
3064
  /**
2974
3065
  * Resolve the active theme and commit it to this element's surface: adopt
@@ -2997,9 +3088,31 @@ __decorate([property({
2997
3088
  reflect: true
2998
3089
  })], ZitadelSurface.prototype, "variant", null);
2999
3090
  __decorate([property({ type: String })], ZitadelSurface.prototype, "theme", null);
3091
+ __decorate([property({
3092
+ type: Boolean,
3093
+ reflect: true,
3094
+ attribute: "suppress-header"
3095
+ })], ZitadelSurface.prototype, "suppressHeader", null);
3096
+ //#endregion
3097
+ //#region src/orchestrator/template-names.ts
3098
+ /**
3099
+ * Names of the bundled Liquid templates the `<zitadel-login>` orchestrator
3100
+ * registers.
3101
+ *
3102
+ * This lives in its own module — separate from `liquid.ts` — so the public
3103
+ * barrel can re-export `TEMPLATE_NAMES` without dragging LiquidJS' `Liquid`
3104
+ * type (and its Node ambient-type requirements) into the published
3105
+ * declaration surface. See `liquid.ts` / the orchestrator barrel.
3106
+ */
3107
+ const TEMPLATE_NAMES = {
3108
+ default: "default",
3109
+ authForm: "auth-form",
3110
+ passkeyUpsell: "passkey-upsell",
3111
+ signedIn: "signed-in"
3112
+ };
3000
3113
  //#endregion
3001
3114
  //#region src/orchestrator/templates/layout-chrome.css?inline
3002
- var layout_chrome_default = ":host {\n color: var(--zl-color-text-primary-white, #f4f4f6);\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n background: none;\n display: block;\n}\n\n:host([variant=\"page\"]) {\n background: var(--zl-color-surface-default-black, #0f0f11);\n min-height: 100%;\n}\n\n.zl-mount {\n min-height: var(--zl-page-min-height, auto);\n background: inherit;\n color: inherit;\n flex-direction: column;\n display: flex;\n container-type: inline-size;\n}\n\n:host([variant=\"page\"]) .zl-mount {\n min-height: var(--zl-page-min-height, 100vh);\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: 700;\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\n.zl-split {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n align-items: var(--zl-split-align, center);\n gap: var(--zl-spacing-05, 2rem);\n min-height: 100%;\n display: grid;\n}\n\n.zl-split__brand {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-04, 1.5rem);\n flex-direction: column;\n padding: 52px 16px;\n display: flex;\n}\n\n.zl-split__logo {\n max-width: min(16rem, 60%);\n}\n\n.zl-split__hero {\n border-radius: var(--zl-radius-m, .75rem);\n object-fit: cover;\n max-width: 100%;\n}\n\n.zl-split__form {\n justify-content: center;\n align-items: center;\n padding: 16px;\n display: flex;\n}\n\n.zl-split__compact {\n max-width: min(12rem, 60%);\n display: none;\n}\n\nimg.zl-split__compact {\n max-height: 2.5rem;\n}\n\nimg.zl-split__compact--hero {\n border-radius: var(--zl-radius-m, .75rem);\n object-fit: cover;\n width: 100%;\n max-width: 100%;\n max-height: 6rem;\n}\n\n.zl-split--right .zl-split__brand {\n order: 2;\n}\n\n.zl-split--right .zl-split__form {\n order: 1;\n}\n\n@container (width <= 48rem) {\n .zl-split {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .zl-split__brand {\n display: var(--zl-split-brand-mobile, none);\n }\n\n .zl-split__form {\n gap: var(--zl-spacing-04, 1.5rem);\n flex-direction: column;\n }\n\n .zl-split__compact {\n display: block;\n }\n}\n\n.zl-hero {\n text-align: left;\n width: 100%;\n max-width: 34rem;\n color: var(--zl-color-text-primary-white, #f4f4f6);\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n flex-direction: column;\n display: flex;\n}\n\n.zl-hero__nav {\n margin-bottom: var(--zl-spacing-05, 2rem);\n justify-content: space-between;\n align-items: center;\n display: flex;\n}\n\n.zl-hero__brand {\n align-items: center;\n gap: var(--zl-spacing-02, .5rem);\n letter-spacing: -.01em;\n font-size: 1.125rem;\n font-weight: 700;\n display: inline-flex;\n}\n\n.zl-hero__brand-logo {\n max-height: 1.75rem;\n}\n\n.zl-hero__brand-mark {\n border-radius: var(--zl-radius-s, .5rem);\n background: linear-gradient(135deg,\n var(--zl-color-icon-default-purple, #bba5e4),\n var(--zl-color-icon-default-pink, #f48aa0));\n width: 1.75rem;\n height: 1.75rem;\n display: inline-flex;\n}\n\n.zl-hero__compact-brand {\n overflow-wrap: anywhere;\n letter-spacing: -.01em;\n max-width: 100%;\n color: var(--zl-color-text-primary-white, #f4f4f6);\n margin: 0;\n font-size: 1.125rem;\n font-weight: 700;\n}\n\n.zl-hero__nav-links {\n gap: var(--zl-spacing-04, 1.5rem);\n color: var(--zl-color-text-secondary-gray, #bfbfcf);\n font-size: .9375rem;\n display: flex;\n}\n\n.zl-hero__nav-links a {\n color: inherit;\n text-decoration: none;\n}\n\n.zl-hero__nav-links a:hover {\n text-decoration: underline;\n}\n\n.zl-hero__eyebrow {\n margin: 0 0 var(--zl-spacing-03, 1rem);\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--zl-color-text-subtitle-purple, #bba5e4);\n font-size: .8125rem;\n font-weight: 600;\n}\n\n.zl-hero__headline {\n margin: 0 0 var(--zl-spacing-03, 1rem);\n font-family: var(--zl-font-family-heading, var(--zl-font-family-sans, system-ui, sans-serif));\n letter-spacing: -.02em;\n font-size: 2.75rem;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.zl-hero__sub {\n margin: 0 0 var(--zl-spacing-05, 2rem);\n max-width: 30rem;\n color: var(--zl-color-text-secondary-gray, #bfbfcf);\n font-size: 1.0625rem;\n line-height: 1.6;\n}\n\n.zl-hero__bullets {\n gap: var(--zl-spacing-02, .5rem);\n margin: 0 0 var(--zl-spacing-05, 2rem);\n flex-direction: column;\n padding: 0;\n list-style: none;\n display: flex;\n}\n\n.zl-hero__bullets li {\n align-items: center;\n gap: var(--zl-spacing-02, .5rem);\n font-size: 1rem;\n display: flex;\n}\n\n.zl-hero__bullets li:before {\n content: \"✓\";\n color: var(--zl-color-icon-default-purple, #bba5e4);\n font-weight: 700;\n}\n\n.zl-hero__footnote {\n padding-top: var(--zl-spacing-04, 1.5rem);\n border-top: 1px solid var(--zl-color-border-default-black, #252528);\n color: var(--zl-color-text-disabled, #686883);\n margin: 0;\n font-size: .8125rem;\n}\n\n.zl-hero__footnote strong {\n color: var(--zl-color-text-secondary-gray, #bfbfcf);\n font-weight: 600;\n}\n\n.zl-minimal {\n gap: var(--zl-spacing-04, 1.5rem);\n flex-direction: column;\n width: min(24rem, 100%);\n margin-inline: auto;\n display: flex;\n}\n\n:host([variant=\"page\"]) .zl-minimal {\n padding: 52px 16px;\n}\n";
3115
+ var layout_chrome_default = ":host {\n color: var(--zl-color-text-primary-white, #f4f4f6);\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n background: none;\n display: block;\n}\n\n:host([variant=\"page\"]) {\n background: var(--zl-color-surface-default-black, #0f0f11);\n min-height: 100%;\n}\n\nimg[data-zl-asset-broken] {\n display: none;\n}\n\n.zl-mount {\n min-height: var(--zl-page-min-height, auto);\n background: inherit;\n color: inherit;\n flex-direction: column;\n display: flex;\n container-type: inline-size;\n}\n\n:host([variant=\"page\"]) .zl-mount {\n min-height: var(--zl-page-min-height, 100vh);\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: 700;\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[data-suppress-header] .zl-card-title, [data-suppress-header] .zl-card-subtitle {\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n position: absolute;\n overflow: hidden;\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-text-link, 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-text-link, 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\n.zl-split {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n align-items: var(--zl-split-align, center);\n gap: var(--zl-spacing-05, 2rem);\n width: 100%;\n min-height: 100%;\n display: grid;\n}\n\n.zl-split__brand {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-04, 1.5rem);\n flex-direction: column;\n padding: 52px 16px;\n display: flex;\n}\n\n.zl-split__logo {\n max-width: min(16rem, 60%);\n max-height: var(--zl-split-logo-max-height, 16rem);\n}\n\n.zl-split__logo:has( + .zl-split__hero) {\n max-height: var(--zl-split-logo-max-height, 8rem);\n}\n\n.zl-split__logo:has( + .zl-split__hero) + .zl-split__hero {\n max-height: min(var(--zl-split-hero-max-height, 27.5rem),\n max(6rem,\n calc(100vh - 104px - 6.5rem - 2.5rem - var(--zl-spacing-04, 1.5rem) -\n var(--zl-spacing-04, 1.5rem) - var(--zl-split-logo-max-height, 8rem))));\n}\n\n.zl-split__hero {\n width: 100%;\n max-height: var(--zl-split-hero-max-height, min(27.5rem, 60vh));\n border-radius: var(--zl-radius-m, .75rem);\n object-fit: cover;\n}\n\n.zl-split__placeholder {\n aspect-ratio: 4 / 5;\n border-radius: var(--zl-radius-m, .75rem);\n background: radial-gradient(120% 120% at 18% 12%,\n var(--zl-color-icon-default-purple, #bba5e4) 0%,\n transparent 60%),\n radial-gradient(130% 130% at 85% 92%,\n var(--zl-color-icon-default-pink, #f48aa0) 0%,\n transparent 60%),\n var(--zl-color-surface-default-primary-gray, #252528);\n width: min(22rem, 85%);\n}\n\n.zl-split__form {\n justify-content: center;\n align-items: center;\n padding: 16px;\n display: flex;\n}\n\n.zl-split__compact {\n max-width: min(12rem, 60%);\n display: none;\n}\n\nimg.zl-split__compact {\n max-height: 2.5rem;\n}\n\nimg.zl-split__compact--hero {\n border-radius: var(--zl-radius-m, .75rem);\n object-fit: cover;\n width: 100%;\n max-width: 100%;\n max-height: 6rem;\n}\n\n.zl-split--right .zl-split__brand {\n order: 2;\n}\n\n.zl-split--right .zl-split__form {\n order: 1;\n}\n\nzl-page-shell:has(.zl-split) .zl-attribution {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n gap: var(--zl-spacing-05, 2rem);\n width: 100%;\n display: grid;\n}\n\nzl-page-shell:has(.zl-split) .zl-attribution > * {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) .zl-attribution > * {\n grid-column: 1;\n}\n\n@container (width <= 48rem) {\n .zl-split {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .zl-split__brand {\n display: var(--zl-split-brand-mobile, none);\n }\n\n .zl-split__form {\n gap: var(--zl-spacing-04, 1.5rem);\n flex-direction: column;\n }\n\n .zl-split__compact {\n display: block;\n }\n\n zl-page-shell:has(.zl-split) .zl-attribution {\n grid-template-columns: minmax(0, 1fr);\n }\n\n zl-page-shell:has(.zl-split) .zl-attribution > *, zl-page-shell:has(.zl-split--right) .zl-attribution > * {\n grid-column: 1;\n }\n}\n\n.zl-hero {\n text-align: left;\n width: 100%;\n max-width: 34rem;\n color: var(--zl-color-text-primary-white, #f4f4f6);\n font-family: var(--zl-font-family-sans, system-ui, sans-serif);\n flex-direction: column;\n display: flex;\n}\n\n.zl-hero__nav {\n margin-bottom: var(--zl-spacing-05, 2rem);\n justify-content: space-between;\n align-items: center;\n display: flex;\n}\n\n.zl-hero__brand {\n align-items: center;\n gap: var(--zl-spacing-02, .5rem);\n letter-spacing: -.01em;\n font-size: 1.125rem;\n font-weight: 700;\n display: inline-flex;\n}\n\n.zl-hero__brand-logo {\n max-height: 1.75rem;\n}\n\n.zl-hero__brand-mark {\n border-radius: var(--zl-radius-s, .5rem);\n background: linear-gradient(135deg,\n var(--zl-color-icon-default-purple, #bba5e4),\n var(--zl-color-icon-default-pink, #f48aa0));\n width: 1.75rem;\n height: 1.75rem;\n display: inline-flex;\n}\n\n.zl-hero__compact-brand {\n overflow-wrap: anywhere;\n letter-spacing: -.01em;\n max-width: 100%;\n color: var(--zl-color-text-primary-white, #f4f4f6);\n margin: 0;\n font-size: 1.125rem;\n font-weight: 700;\n}\n\n.zl-hero__nav-links {\n gap: var(--zl-spacing-04, 1.5rem);\n color: var(--zl-color-text-secondary-gray, #bfbfcf);\n font-size: .9375rem;\n display: flex;\n}\n\n.zl-hero__nav-links a {\n color: inherit;\n text-decoration: none;\n}\n\n.zl-hero__nav-links a:hover {\n text-decoration: underline;\n}\n\n.zl-hero__eyebrow {\n margin: 0 0 var(--zl-spacing-03, 1rem);\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--zl-color-text-subtitle-purple, #bba5e4);\n font-size: .8125rem;\n font-weight: 600;\n}\n\n.zl-hero__headline {\n margin: 0 0 var(--zl-spacing-03, 1rem);\n font-family: var(--zl-font-family-heading, var(--zl-font-family-sans, system-ui, sans-serif));\n letter-spacing: -.02em;\n font-size: 2.75rem;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.zl-hero__sub {\n margin: 0 0 var(--zl-spacing-05, 2rem);\n max-width: 30rem;\n color: var(--zl-color-text-secondary-gray, #bfbfcf);\n font-size: 1.0625rem;\n line-height: 1.6;\n}\n\n.zl-hero__bullets {\n gap: var(--zl-spacing-02, .5rem);\n margin: 0 0 var(--zl-spacing-05, 2rem);\n flex-direction: column;\n padding: 0;\n list-style: none;\n display: flex;\n}\n\n.zl-hero__bullets li {\n align-items: center;\n gap: var(--zl-spacing-02, .5rem);\n font-size: 1rem;\n display: flex;\n}\n\n.zl-hero__bullets li:before {\n content: \"✓\";\n color: var(--zl-color-icon-default-purple, #bba5e4);\n font-weight: 700;\n}\n\n.zl-hero__footnote {\n padding-top: var(--zl-spacing-04, 1.5rem);\n border-top: 1px solid var(--zl-color-border-default-black, #252528);\n color: var(--zl-color-text-disabled, #686883);\n margin: 0;\n font-size: .8125rem;\n}\n\n.zl-hero__footnote strong {\n color: var(--zl-color-text-secondary-gray, #bfbfcf);\n font-weight: 600;\n}\n\n.zl-minimal {\n gap: var(--zl-spacing-04, 1.5rem);\n flex-direction: column;\n width: min(24rem, 100%);\n margin-inline: auto;\n display: flex;\n}\n\n:host([variant=\"page\"]) .zl-minimal {\n padding: 52px 16px;\n}\n";
3003
3116
  //#endregion
3004
3117
  //#region src/orchestrator/zitadel-login.ts
3005
3118
  /** Narrow a rendered named element to the `formValue` field-atom contract. */
@@ -3213,8 +3326,13 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3213
3326
  updated(changed) {
3214
3327
  if (this.shadowRoot) {
3215
3328
  stampExportparts(this.shadowRoot);
3329
+ armAssetFallbacks(this.shadowRoot);
3216
3330
  const widget = this.variant !== "page";
3217
- for (const shell of this.shadowRoot.querySelectorAll("zl-page-shell")) shell.toggleAttribute("data-widget", widget);
3331
+ for (const shell of this.shadowRoot.querySelectorAll("zl-page-shell")) {
3332
+ shell.toggleAttribute("data-widget", widget);
3333
+ shell.toggleAttribute("data-suppress-header", this.suppressHeader);
3334
+ }
3335
+ for (const card of this.shadowRoot.querySelectorAll("zl-card")) card.toggleAttribute("data-suppress-header", this.suppressHeader);
3218
3336
  }
3219
3337
  const props = changed;
3220
3338
  if (!props.has("response")) return;
@@ -3222,10 +3340,10 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3222
3340
  }
3223
3341
  /**
3224
3342
  * Apply captured values and move focus once the new step has fully
3225
- * rendered. This commit produces the step's `zl-field`/`zl-button` atoms,
3226
- * but those render their own shadow DOM on a later microtask — so await
3227
- * this element's update *and* the child atoms' first render before touching
3228
- * them, rather than guessing a frame with `requestAnimationFrame`.
3343
+ * rendered. This commit produces the step's field/action atoms, but those
3344
+ * render their own shadow DOM on a later microtask — so await this element's
3345
+ * update *and* the child atoms' first render before touching them, rather
3346
+ * than guessing a frame with `requestAnimationFrame`.
3229
3347
  *
3230
3348
  * Focus on the *initial* response is page-mode-only: a dedicated login
3231
3349
  * route should focus its first field, but a widget embedded further down
@@ -3234,14 +3352,18 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3234
3352
  */
3235
3353
  async hydrateStepAfterRender(initial = false) {
3236
3354
  await this.updateComplete;
3237
- const atoms = this.shadowRoot?.querySelectorAll("zl-field, zl-button");
3355
+ const atoms = this.shadowRoot?.querySelectorAll("zl-field, zl-select, zl-checkbox, zl-button");
3238
3356
  if (atoms) await Promise.all(Array.from(atoms).map((atom) => atom.updateComplete));
3239
3357
  this.applyValuesToFields();
3240
- if (!initial || this.variant === "page") this.moveFocusToFirstField();
3358
+ if (!initial || this.variant === "page") this.moveFocusToFirstField(initial && this.variant === "page");
3241
3359
  }
3242
3360
  render() {
3243
3361
  if (this.startupError) return html`<form class="zl-mount" novalidate>
3244
- <zl-alert severity="error">${this.startupError}</zl-alert>
3362
+ <zl-page-shell>
3363
+ <zl-card>
3364
+ <zl-alert severity="error">${this.startupError}</zl-alert>
3365
+ </zl-card>
3366
+ </zl-page-shell>
3245
3367
  </form>`;
3246
3368
  if (!this.response || !this.engine) return html`<slot name="loader"></slot>`;
3247
3369
  const rendered = this.injectAttribution(this.renderStep(this.response.step, this.engine));
@@ -3334,7 +3456,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3334
3456
  applyResponse(wire) {
3335
3457
  this.stepErrorDismissed = false;
3336
3458
  this.response = wire;
3337
- const { branding, issues } = validateBranding(wire.branding);
3459
+ const { branding, issues } = validateBranding(wire.branding, { renderingOrigin: this.ownerDocument.location.origin });
3338
3460
  this.branding = branding;
3339
3461
  this.themeController.setBranding(branding);
3340
3462
  if (issues.length > 0) console.warn("[zitadel-login] branding payload has issues:", issues);
@@ -3694,11 +3816,17 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
3694
3816
  if (!root) return null;
3695
3817
  return (root.querySelector("zl-button[hierarchy=\"primary\"][type=\"submit\"]") ?? root.querySelector("zl-button[hierarchy=\"primary\"]"))?.getAttribute("action") || null;
3696
3818
  }
3697
- moveFocusToFirstField() {
3819
+ /**
3820
+ * On the initial paint, only a field earns focus: script-moved focus with
3821
+ * no prior interaction matches `:focus-visible`, so autofocusing a button
3822
+ * on a field-less step (passkey-first) paints a ring that reads as a
3823
+ * pre-selected state. Step swaps keep button focus — there the browser
3824
+ * derives the modality from the user's actual input.
3825
+ */
3826
+ moveFocusToFirstField(fieldsOnly = false) {
3698
3827
  const root = this.shadowRoot;
3699
3828
  if (!root) return;
3700
- const focusables = root.querySelectorAll("zl-field, zl-button");
3701
- Array.from(focusables).find((el) => !el.hasAttribute("disabled"))?.focus();
3829
+ (fieldsOnly ? this.fieldAtoms() : Array.from(root.querySelectorAll("zl-field, zl-select, zl-checkbox, zl-button"))).find((el) => !el.hasAttribute("disabled") && !el.hasAttribute("hidden") && el.getAttribute("type") !== "hidden")?.focus();
3702
3830
  }
3703
3831
  async submit(action, challengeResponse) {
3704
3832
  if (!this.response || this.loading) return;
@@ -4317,6 +4445,18 @@ let ZitadelSession = class ZitadelSession extends ZitadelSurface {
4317
4445
  line-height: 1.25rem;
4318
4446
  color: ${t.color.text.error};
4319
4447
  }
4448
+ /* suppress-header: visually hidden, kept in the accessibility tree. */
4449
+ .title.sr-only {
4450
+ position: absolute;
4451
+ width: 1px;
4452
+ height: 1px;
4453
+ margin: -1px;
4454
+ padding: 0;
4455
+ border: 0;
4456
+ overflow: hidden;
4457
+ clip-path: inset(50%);
4458
+ white-space: nowrap;
4459
+ }
4320
4460
  `];
4321
4461
  #_project_accessor_storage;
4322
4462
  get project() {
@@ -4480,9 +4620,13 @@ let ZitadelSession = class ZitadelSession extends ZitadelSurface {
4480
4620
  render() {
4481
4621
  return html`
4482
4622
  <zl-page-shell ?data-widget=${this.variant !== "page"}>
4483
- <zl-card>
4484
- <h1 slot="header" class="title">${this.heading}</h1>
4485
- ${this.identityLabel ? html`<p slot="header" class="identity">${this.identityLabel}</p>` : nothing}
4623
+ <zl-card ?data-suppress-header=${this.suppressHeader}>
4624
+ <h1 slot="header" class="title ${this.suppressHeader ? "sr-only" : ""}">
4625
+ ${this.heading}
4626
+ </h1>
4627
+ ${this.identityLabel ? html`<p slot=${this.suppressHeader ? nothing : "header"} class="identity">
4628
+ ${this.identityLabel}
4629
+ </p>` : nothing}
4486
4630
 
4487
4631
  <zl-button
4488
4632
  hierarchy="primary"
@@ -4526,6 +4670,6 @@ __decorate([state()], ZitadelSession.prototype, "loading", null);
4526
4670
  __decorate([state()], ZitadelSession.prototype, "errorMessage", null);
4527
4671
  ZitadelSession = __decorate([customElement("zitadel-session")], ZitadelSession);
4528
4672
  //#endregion
4529
- export { submitStep as C, startFlow as S, MANDATORY_GATES_MARKER as _, ThemeController as a, validateBranding as b, buildBrandingStylesheet as c, builtinLocales as d, businessLocales as f, TEMPLATE_NAMES as g, en as h, layout_chrome_default as i, resolveTheme as l, de as m, ZitadelLogout as n, applyFontUrl as o, it as p, ZitadelLogin as r, applyBrandingTokens as s, ZitadelSession as t, createSanitiser as u, mandatoryGatesMarkerComment as v, getCurrentStep as x, patchMandatoryGates as y };
4673
+ export { submitStep as C, startFlow as S, MANDATORY_GATES_MARKER as _, TEMPLATE_NAMES as a, validateBranding as b, applyBrandingTokens as c, createSanitiser as d, builtinLocales as f, en as g, de as h, layout_chrome_default as i, buildBrandingStylesheet as l, it as m, ZitadelLogout as n, ThemeController as o, businessLocales as p, ZitadelLogin as r, applyFontUrl as s, ZitadelSession as t, resolveTheme as u, mandatoryGatesMarkerComment as v, getCurrentStep as x, patchMandatoryGates as y };
4530
4674
 
4531
- //# sourceMappingURL=orchestrator-CctLkpmB.mjs.map
4675
+ //# sourceMappingURL=orchestrator-CsHP57zo.mjs.map