@zitadel/components 0.1.0-alpha.14 → 0.1.0-alpha.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +1 -0
  2. package/dist/atoms/index.d.mts +1 -1
  3. package/dist/atoms/index.mjs +1 -1
  4. package/dist/{atoms-BSwVl736.mjs → atoms-C-zXJjaz.mjs} +106 -10
  5. package/dist/{atoms-BSwVl736.mjs.map → atoms-C-zXJjaz.mjs.map} +1 -1
  6. package/dist/default-BoC3JEsO.mjs +6 -0
  7. package/dist/default-BoC3JEsO.mjs.map +1 -0
  8. package/dist/{index-EC-BnGsx.d.mts → index-BniwXaIC.d.mts} +3 -1
  9. package/dist/{index-EC-BnGsx.d.mts.map → index-BniwXaIC.d.mts.map} +1 -1
  10. package/dist/{index-Bypr00UG.d.mts → index-BwQLFs9R.d.mts} +362 -17
  11. package/dist/index-BwQLFs9R.d.mts.map +1 -0
  12. package/dist/{index-BP3l3mje.d.mts → index-D1eMaWbY.d.mts} +52 -5
  13. package/dist/index-D1eMaWbY.d.mts.map +1 -0
  14. package/dist/index.d.mts +4 -4
  15. package/dist/index.mjs +4 -4
  16. package/dist/manifests.mjs +1 -1
  17. package/dist/orchestrator/index.d.mts +2 -2
  18. package/dist/orchestrator/index.mjs +2 -2
  19. package/dist/{orchestrator-qkFBKFgJ.mjs → orchestrator-BN2nT9FZ.mjs} +317 -25
  20. package/dist/orchestrator-BN2nT9FZ.mjs.map +1 -0
  21. package/dist/standalone.mjs +423 -33
  22. package/dist/tokens/index.d.mts +1 -1
  23. package/dist/tokens/index.mjs +1 -1
  24. package/dist/{tokens-C0j4iCrs.mjs → tokens-CbH2s1jV.mjs} +6 -4
  25. package/dist/tokens-CbH2s1jV.mjs.map +1 -0
  26. package/package.json +5 -4
  27. package/dist/default-W5MwYLBc.mjs +0 -6
  28. package/dist/default-W5MwYLBc.mjs.map +0 -1
  29. package/dist/index-BP3l3mje.d.mts.map +0 -1
  30. package/dist/index-Bypr00UG.d.mts.map +0 -1
  31. package/dist/orchestrator-qkFBKFgJ.mjs.map +0 -1
  32. package/dist/tokens-C0j4iCrs.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { C as baseHostStyles, S as focusVisibleStyles, T as emit, w as t, x as __decorate } from "./atoms-BSwVl736.mjs";
2
- import { r as tokensCss } from "./tokens-C0j4iCrs.mjs";
3
- import { t as default_default } from "./default-W5MwYLBc.mjs";
1
+ import { C as baseHostStyles, S as focusVisibleStyles, T as emit, w as t, x as __decorate } from "./atoms-C-zXJjaz.mjs";
2
+ import { r as tokensCss } from "./tokens-CbH2s1jV.mjs";
3
+ import { t as default_default } from "./default-BoC3JEsO.mjs";
4
4
  import { manifestRegistry } from "./manifests.mjs";
5
5
  import { LitElement, css, html, nothing } from "lit";
6
6
  import { customElement, property, state } from "lit/decorators.js";
@@ -97,6 +97,37 @@ function safeJsonParse(text) {
97
97
  return { raw: text };
98
98
  }
99
99
  }
100
+ /**
101
+ * Extracts the server's `{code, message, details}` envelope from an
102
+ * {@link ApiError} into a display string. Falls back to the fetch layer's
103
+ * `"METHOD url returned N"` when the body isn't shaped like an envelope, so
104
+ * transport-level failures (HTML from a proxy, empty 5xx) still say
105
+ * something.
106
+ */
107
+ function apiErrorMessage(error) {
108
+ const body = error.body;
109
+ if (!isRecord(body)) return error.message;
110
+ const serverMessage = typeof body.message === "string" ? body.message : void 0;
111
+ const detail = pickDetailString(body.details);
112
+ if (!serverMessage) return error.message;
113
+ return detail ? `${serverMessage}: ${detail}` : serverMessage;
114
+ }
115
+ /**
116
+ * Reads the innermost human-readable string out of the spec's error
117
+ * `details` field. Handles both `details: "..."` and the nested
118
+ * `details: { details: "..." }` shape the platform emits for validation
119
+ * failures.
120
+ */
121
+ function pickDetailString(details) {
122
+ if (typeof details === "string") return details;
123
+ if (isRecord(details)) {
124
+ if (typeof details.details === "string") return details.details;
125
+ if (typeof details.message === "string") return details.message;
126
+ }
127
+ }
128
+ function isRecord(value) {
129
+ return typeof value === "object" && value !== null && !Array.isArray(value);
130
+ }
100
131
  //#endregion
101
132
  //#region src/orchestrator/api-client.ts
102
133
  const apiRequestInit = { credentials: "include" };
@@ -133,7 +164,7 @@ async function exchangeSession(api, body, params) {
133
164
  * orchestrator's signed-in surfaces to render the user's identity.
134
165
  */
135
166
  async function getSession$1(api) {
136
- return await api.getMySession(apiRequestInit);
167
+ return api.getMySession(apiRequestInit);
137
168
  }
138
169
  /**
139
170
  * Revokes the current session (`DELETE /sessions/me`) with credentials. The
@@ -237,7 +268,9 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
237
268
  getMySession: () => getMySession,
238
269
  getMyUser: () => getMyUser,
239
270
  getOpenIDConfiguration: () => getOpenIDConfiguration,
271
+ getPatchProjectUrl: () => getPatchProjectUrl,
240
272
  getProject: () => getProject,
273
+ getQueryProjectsUrl: () => getQueryProjectsUrl,
241
274
  getReady: () => getReady,
242
275
  getRevokeMySessionUrl: () => getRevokeMySessionUrl,
243
276
  getRevokeSessionUrl: () => getRevokeSessionUrl,
@@ -259,6 +292,8 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
259
292
  listSchemas: () => listSchemas,
260
293
  listSessions: () => listSessions,
261
294
  listUsers: () => listUsers,
295
+ patchProject: () => patchProject,
296
+ queryProjects: () => queryProjects,
262
297
  revokeMySession: () => revokeMySession,
263
298
  revokeSession: () => revokeSession,
264
299
  revokeToken: () => revokeToken,
@@ -797,6 +832,23 @@ const createProject = async (createProjectBody, options) => {
797
832
  body: JSON.stringify(createProjectBody)
798
833
  });
799
834
  };
835
+ const getQueryProjectsUrl = () => {
836
+ return `${getProxyPath()}/projects/query`;
837
+ };
838
+ /**
839
+ * @summary Query projects
840
+ */
841
+ const queryProjects = async (queryProjectsBody, options) => {
842
+ return customFetch(getQueryProjectsUrl(), {
843
+ ...options,
844
+ method: "POST",
845
+ headers: {
846
+ "Content-Type": "application/json",
847
+ ...options?.headers
848
+ },
849
+ body: JSON.stringify(queryProjectsBody)
850
+ });
851
+ };
800
852
  const getGetProjectUrl = (projectId) => {
801
853
  return `${getProxyPath()}/projects/${projectId}`;
802
854
  };
@@ -811,6 +863,25 @@ const getProject = async (projectId, options) => {
811
863
  method: "GET"
812
864
  });
813
865
  };
866
+ const getPatchProjectUrl = (projectId) => {
867
+ return `${getProxyPath()}/projects/${projectId}`;
868
+ };
869
+ /**
870
+ * Updates the state of a project.
871
+
872
+ * @summary Update project
873
+ */
874
+ const patchProject = async (projectId, patchProjectBody, options) => {
875
+ return customFetch(getPatchProjectUrl(projectId), {
876
+ ...options,
877
+ method: "PATCH",
878
+ headers: {
879
+ "Content-Type": "application/json",
880
+ ...options?.headers
881
+ },
882
+ body: JSON.stringify(patchProjectBody)
883
+ });
884
+ };
814
885
  const getCreateSessionUrl = () => {
815
886
  return `${getProxyPath()}/sessions`;
816
887
  };
@@ -1815,6 +1886,21 @@ function lookup(locale, key) {
1815
1886
  * - Partials are loaded from an in-memory map; no filesystem access.
1816
1887
  */
1817
1888
  const TEMPLATE_NAMES$1 = { default: "default" };
1889
+ /**
1890
+ * Maps `error.*` text keys to a field name (Figma inline-error
1891
+ * annotations). Shared by the template filters (`fieldError` routes these
1892
+ * inline, `formLevelError` suppresses their banner) and by
1893
+ * {@link localiseFlowErrorKeys}, which must downgrade them to a banner
1894
+ * message when the step doesn't render the mapped field.
1895
+ */
1896
+ const fieldErrorKeys = {
1897
+ "error.email_required": "email",
1898
+ "error.email_invalid": "email",
1899
+ "error.email_exists": "email",
1900
+ "error.password_required": "password",
1901
+ "error.password_incorrect": "password",
1902
+ "error.invalid_credentials": "password"
1903
+ };
1818
1904
  function createLiquidEngine(options) {
1819
1905
  const engine = new Liquid({
1820
1906
  templates: {
@@ -1831,7 +1917,7 @@ function createLiquidEngine(options) {
1831
1917
  engine.registerFilter("raw", (value) => stringify(value));
1832
1918
  engine.registerFilter("t", function tFilter(key, ...args) {
1833
1919
  const lookupKey = stringify(key);
1834
- return interpolate(options.locale[lookupKey] ?? lookupKey, args.map(stringify));
1920
+ return interpolate(options.locale[lookupKey] ?? injectedKeyFallback(options.locale, lookupKey) ?? lookupKey, args.map(stringify));
1835
1921
  });
1836
1922
  /** Resolves `{text_key}.placeholder` — empty when undefined (not the raw key). */
1837
1923
  engine.registerFilter("fieldPlaceholder", (textKey) => {
@@ -1859,15 +1945,6 @@ function createLiquidEngine(options) {
1859
1945
  };
1860
1946
  });
1861
1947
  });
1862
- /** Maps `error.*` text keys to a field name (Figma inline-error annotations). */
1863
- const fieldErrorKeys = {
1864
- "error.email_required": "email",
1865
- "error.email_invalid": "email",
1866
- "error.email_exists": "email",
1867
- "error.password_required": "password",
1868
- "error.password_incorrect": "password",
1869
- "error.invalid_credentials": "password"
1870
- };
1871
1948
  /** Resolves `{text_key}.title` for form-level `<zl-alert heading>`. */
1872
1949
  engine.registerFilter("alertHeading", (textKey) => {
1873
1950
  const lookupKey = `${stringify(textKey)}.title`;
@@ -1901,6 +1978,105 @@ function createLiquidEngine(options) {
1901
1978
  });
1902
1979
  return engine;
1903
1980
  }
1981
+ /**
1982
+ * Fallbacks for text keys the flow engine derives from tenant-chosen step
1983
+ * names (`<step>.action.back` for the injected back action — see
1984
+ * `internal/domain/flow_state_machine.go` `buildStep`). Step names are open,
1985
+ * so no dictionary can enumerate them; a missing step-specific key falls back
1986
+ * to its generic entry instead of leaking the raw key into the UI.
1987
+ */
1988
+ const INJECTED_KEY_FALLBACKS = [{
1989
+ suffix: ".action.back",
1990
+ fallback: "action.back"
1991
+ }];
1992
+ function injectedKeyFallback(locale, key) {
1993
+ for (const { suffix, fallback } of INJECTED_KEY_FALLBACKS) if (key.endsWith(suffix)) return locale[fallback];
1994
+ }
1995
+ /**
1996
+ * Rule-suffix fallbacks for the server's field-validation keys
1997
+ * (`error.<field>_<rule>` — see `FlowFieldValidationError.TextKey` in
1998
+ * `internal/domain/flow_field_resolver.go`). Field names come from the
1999
+ * tenant's user schema, so no catalog can enumerate the specific keys;
2000
+ * a miss resolves to the rule's generic entry, interpolated with the
2001
+ * field's label (`{0}`). The server spells the format rule `_invalid`
2002
+ * (the catalog's existing convention, e.g. `error.email_invalid`), so
2003
+ * that suffix takes the format wording; `_unknown_field` (a submitted
2004
+ * name that is not a step field) takes the catch-all.
2005
+ */
2006
+ const FLOW_ERROR_RULE_FALLBACKS = [
2007
+ {
2008
+ suffix: "_required",
2009
+ generic: "error.field_required"
2010
+ },
2011
+ {
2012
+ suffix: "_min_length",
2013
+ generic: "error.field_min_length"
2014
+ },
2015
+ {
2016
+ suffix: "_max_length",
2017
+ generic: "error.field_max_length"
2018
+ },
2019
+ {
2020
+ suffix: "_format",
2021
+ generic: "error.field_format"
2022
+ },
2023
+ {
2024
+ suffix: "_invalid",
2025
+ generic: "error.field_format"
2026
+ },
2027
+ {
2028
+ suffix: "_unknown_field",
2029
+ generic: "error.field_invalid"
2030
+ }
2031
+ ];
2032
+ const FLOW_ERROR_CATCH_ALL_KEY = "error.field_invalid";
2033
+ /**
2034
+ * Localises a `step.error` payload of field-validation text keys.
2035
+ * Returns `null` unless EVERY `"; "`-joined segment is an `error.*` key
2036
+ * — the caller keeps other payloads (outcome names, `auth_attempt.*`
2037
+ * diagnostics) verbatim.
2038
+ *
2039
+ * Keys the locale knows pass through as `text_key` entries: the template
2040
+ * localises them via `| t`, and `fieldErrorKeys` routes the known ones
2041
+ * inline to their field. Unknown keys with a recognised rule suffix are
2042
+ * pre-localised here from their generic {@link FLOW_ERROR_RULE_FALLBACKS}
2043
+ * entry; unknown keys without one pass through as `text_key` (matching
2044
+ * `| t`'s behaviour for non-validation keys such as
2045
+ * `error.sign_in_server`, which localises via `.title`/`.body`).
2046
+ */
2047
+ function localiseFlowErrorKeys(raw, ctx) {
2048
+ const segments = raw.split("; ");
2049
+ if (!segments.every((segment) => segment.startsWith("error."))) return null;
2050
+ return segments.map((key) => localiseFlowErrorKey(key, ctx));
2051
+ }
2052
+ function localiseFlowErrorKey(key, ctx) {
2053
+ const inlineField = fieldErrorKeys[key];
2054
+ const orphanedInline = inlineField !== void 0 && ctx.fields !== void 0 && !ctx.fields.includes(inlineField);
2055
+ if (!orphanedInline && ctx.locale[key] !== void 0) return { text_key: key };
2056
+ for (const { suffix, generic } of FLOW_ERROR_RULE_FALLBACKS) {
2057
+ if (!key.endsWith(suffix)) continue;
2058
+ const field = key.slice(6, key.length - suffix.length);
2059
+ if (field === "") break;
2060
+ return { message: capitaliseFirst(interpolate(ctx.locale[generic] ?? ctx.locale[FLOW_ERROR_CATCH_ALL_KEY] ?? "Please check {0}.", [fieldLabel(ctx, field)])) };
2061
+ }
2062
+ if (orphanedInline && ctx.locale[key] !== void 0) return { message: ctx.locale[key] };
2063
+ return { text_key: key };
2064
+ }
2065
+ /** `<step>.field.<name>` from the locale, else a humanised field name. */
2066
+ function fieldLabel(ctx, field) {
2067
+ return ctx.locale[`${ctx.stepName}.field.${field}`] ?? humaniseFieldName(field);
2068
+ }
2069
+ /**
2070
+ * `x-auth-methods#password` → "password", `givenName` → "given name",
2071
+ * `date_of_birth` → "date of birth".
2072
+ */
2073
+ function humaniseFieldName(field) {
2074
+ return (field.includes("#") ? field.split("#").pop() : field).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_.-]+/g, " ").trim().toLowerCase();
2075
+ }
2076
+ /** Messages may open with a lowercase field label; sentence-case them. */
2077
+ function capitaliseFirst(text) {
2078
+ return text.length > 0 ? text[0]?.toUpperCase() + text.slice(1) : text;
2079
+ }
1904
2080
  function stringify(value) {
1905
2081
  if (value == null) return "";
1906
2082
  if (typeof value === "string") return value;
@@ -1911,6 +2087,11 @@ function stringify(value) {
1911
2087
  return String(value);
1912
2088
  }
1913
2089
  }
2090
+ /**
2091
+ * Positional `{n}` substitution shared by the `| t` filter and the
2092
+ * pre-localising fallback in {@link localiseFlowErrorKeys}, so both
2093
+ * produce byte-identical output for the same template and args.
2094
+ */
1914
2095
  function interpolate(template, args) {
1915
2096
  if (args.length === 0) return template;
1916
2097
  return template.replace(/\{(\d+)\}/g, (match, index) => {
@@ -2028,6 +2209,10 @@ const en = {
2028
2209
  "signed-in.continue": "Continue",
2029
2210
  "signed-in.logout": "Sign out",
2030
2211
  "passkey-login.title": "Sign in with your passkey",
2212
+ "passkey-first.title": "Sign in",
2213
+ "passkey-first.description": "Use your passkey to continue",
2214
+ "passkey-first.action.passkey": "Continue with passkey",
2215
+ "passkey-first.action.email_fallback": "Use email instead",
2031
2216
  "recover.title": "Check your email",
2032
2217
  "recover.description": "We sent a password reset link to your email address.",
2033
2218
  "recover.action.back": "Back to sign in",
@@ -2035,8 +2220,11 @@ const en = {
2035
2220
  "submit.signin": "Sign in",
2036
2221
  "action.forgot_password": "Forgot password?",
2037
2222
  "action.cancel": "Cancel",
2223
+ "action.back": "Back",
2038
2224
  "sso.redirect.title": "Redirecting to your provider…",
2039
- "error.passkey_cancelled": "Passkey setup was cancelled",
2225
+ "passkey.pending.status": "Waiting for your passkey…",
2226
+ "error.passkey_cancelled": "The passkey prompt was closed before completing.",
2227
+ "error.passkey_timeout": "The passkey request timed out. Please try again.",
2040
2228
  "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
2041
2229
  "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
2042
2230
  "error.passkey_unsupported": "This device does not support passkeys",
@@ -2049,6 +2237,11 @@ const en = {
2049
2237
  /** Figma sign-in error `6602:180268` — inline on password field. */
2050
2238
  "error.invalid_credentials": "Wrong email or password.",
2051
2239
  "error.required": "This field is required.",
2240
+ "error.field_required": "{0} is required.",
2241
+ "error.field_format": "Please enter a valid {0}.",
2242
+ "error.field_min_length": "{0} is too short.",
2243
+ "error.field_max_length": "{0} is too long.",
2244
+ "error.field_invalid": "Please check {0}.",
2052
2245
  "error.sign_in_server.title": "We couldn't complete your sign in.",
2053
2246
  "error.sign_in_server.body": "Please try again in a few minutes"
2054
2247
  };
@@ -2130,6 +2323,10 @@ const de = {
2130
2323
  "signed-in.continue": "Weiter",
2131
2324
  "signed-in.logout": "Abmelden",
2132
2325
  "passkey-login.title": "Mit Passkey anmelden",
2326
+ "passkey-first.title": "Anmelden",
2327
+ "passkey-first.description": "Verwende deinen Passkey, um fortzufahren",
2328
+ "passkey-first.action.passkey": "Weiter mit Passkey",
2329
+ "passkey-first.action.email_fallback": "Stattdessen E-Mail verwenden",
2133
2330
  "recover.title": "E-Mail prüfen",
2134
2331
  "recover.description": "Wir haben einen Link zum Zurücksetzen des Passworts an deine E-Mail-Adresse gesendet.",
2135
2332
  "recover.action.back": "Zurück zur Anmeldung",
@@ -2137,8 +2334,11 @@ const de = {
2137
2334
  "submit.signin": "Anmelden",
2138
2335
  "action.forgot_password": "Passwort vergessen?",
2139
2336
  "action.cancel": "Abbrechen",
2337
+ "action.back": "Zurück",
2140
2338
  "sso.redirect.title": "Weiterleitung zum Anbieter…",
2141
- "error.passkey_cancelled": "Passkey-Einrichtung wurde abgebrochen",
2339
+ "passkey.pending.status": "Warten auf deinen Passkey…",
2340
+ "error.passkey_cancelled": "Die Passkey-Abfrage wurde vorzeitig geschlossen.",
2341
+ "error.passkey_timeout": "Die Passkey-Anfrage hat zu lange gedauert. Bitte versuche es erneut.",
2142
2342
  "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
2143
2343
  "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
2144
2344
  "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
@@ -2150,6 +2350,11 @@ const de = {
2150
2350
  "error.email_exists": "Ein Konto mit dieser E-Mail-Adresse existiert bereits.",
2151
2351
  "error.invalid_credentials": "Falsche E-Mail oder falsches Passwort.",
2152
2352
  "error.required": "Dieses Feld ist erforderlich.",
2353
+ "error.field_required": "{0} ist erforderlich.",
2354
+ "error.field_format": "Bitte gib einen gültigen Wert für {0} ein.",
2355
+ "error.field_min_length": "{0} ist zu kurz.",
2356
+ "error.field_max_length": "{0} ist zu lang.",
2357
+ "error.field_invalid": "Bitte überprüfe {0}.",
2153
2358
  "error.sign_in_server.title": "Anmeldung konnte nicht abgeschlossen werden.",
2154
2359
  "error.sign_in_server.body": "Bitte versuche es in einigen Minuten erneut"
2155
2360
  };
@@ -2231,6 +2436,10 @@ const it = {
2231
2436
  "signed-in.continue": "Continua",
2232
2437
  "signed-in.logout": "Esci",
2233
2438
  "passkey-login.title": "Accedi con la tua passkey",
2439
+ "passkey-first.title": "Accedi",
2440
+ "passkey-first.description": "Usa la tua passkey per continuare",
2441
+ "passkey-first.action.passkey": "Continua con passkey",
2442
+ "passkey-first.action.email_fallback": "Usa invece l'email",
2234
2443
  "recover.title": "Controlla la tua e-mail",
2235
2444
  "recover.description": "Abbiamo inviato un link per reimpostare la password al tuo indirizzo e-mail.",
2236
2445
  "recover.action.back": "Torna all'accesso",
@@ -2238,8 +2447,11 @@ const it = {
2238
2447
  "submit.signin": "Accedi",
2239
2448
  "action.forgot_password": "Password dimenticata?",
2240
2449
  "action.cancel": "Annulla",
2450
+ "action.back": "Indietro",
2241
2451
  "sso.redirect.title": "Reindirizzamento al provider…",
2242
- "error.passkey_cancelled": "La configurazione della passkey è stata annullata",
2452
+ "passkey.pending.status": "In attesa della tua passkey",
2453
+ "error.passkey_cancelled": "La richiesta della passkey è stata chiusa prima del completamento.",
2454
+ "error.passkey_timeout": "La richiesta della passkey è scaduta. Riprova.",
2243
2455
  "error.passkey_not_registered": "Questa passkey non è registrata. Accedi con e-mail e password.",
2244
2456
  "error.passkey_setup_failed": "La registrazione della passkey non è stata completata. Riprova.",
2245
2457
  "error.passkey_unsupported": "Questo dispositivo non supporta le passkey",
@@ -2251,6 +2463,11 @@ const it = {
2251
2463
  "error.email_exists": "Esiste già un account con questo indirizzo e-mail.",
2252
2464
  "error.invalid_credentials": "E-mail o password errata.",
2253
2465
  "error.required": "Questo campo è obbligatorio.",
2466
+ "error.field_required": "{0} è obbligatorio.",
2467
+ "error.field_format": "Inserisci un valore valido per {0}.",
2468
+ "error.field_min_length": "{0} è troppo corto.",
2469
+ "error.field_max_length": "{0} è troppo lungo.",
2470
+ "error.field_invalid": "Controlla {0}.",
2254
2471
  "error.sign_in_server.title": "Non è stato possibile completare l'accesso.",
2255
2472
  "error.sign_in_server.body": "Riprova tra qualche minuto"
2256
2473
  };
@@ -2484,6 +2701,13 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2484
2701
  set purpose(value) {
2485
2702
  this.#_purpose_accessor_storage = value;
2486
2703
  }
2704
+ #_flowName_accessor_storage = "";
2705
+ get flowName() {
2706
+ return this.#_flowName_accessor_storage;
2707
+ }
2708
+ set flowName(value) {
2709
+ this.#_flowName_accessor_storage = value;
2710
+ }
2487
2711
  #_project_accessor_storage;
2488
2712
  get project() {
2489
2713
  return this.#_project_accessor_storage;
@@ -2591,6 +2815,8 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2591
2815
  sheet.replaceSync(layout_chrome_default);
2592
2816
  root.adoptedStyleSheets = [...existing, sheet];
2593
2817
  root.addEventListener("zl-input", this.handleAtomInput);
2818
+ root.addEventListener("zl-change", this.handleAtomEdited);
2819
+ root.addEventListener("zl-dismiss", this.handleAlertDismiss);
2594
2820
  root.addEventListener("zl-submit", this.handleAtomSubmit);
2595
2821
  root.addEventListener("click", this.handleDelegatedAction);
2596
2822
  root.addEventListener("submit", this.handleFormSubmit);
@@ -2729,17 +2955,32 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2729
2955
  if (!cfg.projectId) throw new Error("<zitadel-login> requires a project id (the `project-id` attribute, `configureZitadel({ projectId })`, or a `project` handle) to start a flow.");
2730
2956
  wire = await startFlow(api, {
2731
2957
  project_id: cfg.projectId,
2732
- purpose: this.purpose
2958
+ purpose: this.purpose,
2959
+ ...this.flowName ? { flow_definition_name: this.flowName } : {}
2733
2960
  });
2734
2961
  }
2735
2962
  this.applyResponse(wire);
2736
2963
  } catch (error) {
2737
- this.handleTransportError(error);
2964
+ this.handleTransportError(this.describeFlowSelectionError(error));
2738
2965
  } finally {
2739
2966
  this.loading = false;
2740
2967
  }
2741
2968
  }
2969
+ /**
2970
+ * When a `flow-name` lookup fails, the server's envelope only says
2971
+ * "not found" / "purpose mismatch" — it cannot know the name came from
2972
+ * an attribute. Rewrap those two codes with the attribute and the fix;
2973
+ * every other error passes through untouched.
2974
+ */
2975
+ describeFlowSelectionError(error) {
2976
+ if (!this.flowName || !(error instanceof ApiError)) return error;
2977
+ const code = typeof error.body === "object" && error.body !== null && "code" in error.body ? String(error.body.code) : "";
2978
+ if (code === "flowdef.not_found") return /* @__PURE__ */ new Error(`<zitadel-login> flow-name="${this.flowName}" does not match any active flow definition in this project. Check the \`name\` in your flow file and that it has been applied (\`zitadel apply\`).`);
2979
+ if (code === "flowdef.purpose_mismatch") return /* @__PURE__ */ new Error(`<zitadel-login> flow-name="${this.flowName}" matched a flow definition that does not serve purpose "${this.purpose}".`);
2980
+ return error;
2981
+ }
2742
2982
  applyResponse(wire) {
2983
+ this.stepErrorDismissed = false;
2743
2984
  this.response = wire;
2744
2985
  const { branding, issues } = validateBranding(wire.branding);
2745
2986
  this.branding = branding;
@@ -2793,7 +3034,12 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2793
3034
  }
2794
3035
  renderStep(step, engine) {
2795
3036
  const tenantSource = typeof this.branding?.liquid_template === "string" && this.branding.liquid_template.length > 0 ? this.branding.liquid_template : null;
2796
- const errors = step.error ? step.error.startsWith("error.") ? [{ text_key: step.error }] : [{ message: step.error }] : [];
3037
+ const rawErrors = step.error ? localiseFlowErrorKeys(step.error, {
3038
+ locale: this.resolveLocale(),
3039
+ stepName: step.name ?? "",
3040
+ fields: (step.fields ?? []).map((field) => field.name)
3041
+ }) ?? [{ message: step.error }] : [];
3042
+ const errors = this.loading && this.stepErrorDismissed ? [] : rawErrors;
2797
3043
  const fields = step.fields ?? [];
2798
3044
  const actions = step.actions ?? [];
2799
3045
  const context = {
@@ -2887,6 +3133,17 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2887
3133
  if (native && native.value !== field.value) return native.value;
2888
3134
  return typeof field.value === "string" ? field.value : void 0;
2889
3135
  }
3136
+ /**
3137
+ * True once the user retired the current step error by editing a field or
3138
+ * dismissing the alert. Deliberately NON-reactive: consulting reactive
3139
+ * state in `renderStep` would change its output string on the first
3140
+ * post-dismiss keystroke, and `unsafeHTML` would rebuild the whole step
3141
+ * subtree — wiping typed values (`hydrateStepAfterRender` only re-applies
3142
+ * them on response changes) and reconnecting atoms. Reset on every new
3143
+ * response; consulted only by the `loading` re-render, which rebuilds
3144
+ * anyway.
3145
+ */
3146
+ stepErrorDismissed = false;
2890
3147
  handleAtomInput = (event) => {
2891
3148
  if (!event.detail) return;
2892
3149
  const { name, value } = event.detail;
@@ -2896,11 +3153,41 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2896
3153
  [name]: value
2897
3154
  };
2898
3155
  this.syncFieldElementValue(name, value);
3156
+ this.clearStaleErrors(name);
2899
3157
  emit(this, "zitadel-flow-input", {
2900
3158
  name,
2901
3159
  value
2902
3160
  });
2903
3161
  };
3162
+ /** `zl-change` from <zl-checkbox>/<zl-select>: only error clearing. */
3163
+ handleAtomEdited = (event) => {
3164
+ const name = event.detail?.name;
3165
+ if (name) this.clearStaleErrors(name);
3166
+ };
3167
+ /** Explicit dismiss of the step-error alert (it removes itself). */
3168
+ handleAlertDismiss = (event) => {
3169
+ if (event.target?.matches?.("zl-alert[data-zl-step-error]")) this.stepErrorDismissed = true;
3170
+ };
3171
+ /**
3172
+ * Retire the current step error after the user edits `fieldName`:
3173
+ * remove the form-level alert(s) and clear the edited field's inline
3174
+ * error — other fields' inline errors stay until they are edited.
3175
+ * Imperative on purpose; see {@link stepErrorDismissed}.
3176
+ */
3177
+ clearStaleErrors(fieldName) {
3178
+ if (!this.response?.step.error) return;
3179
+ const root = this.shadowRoot;
3180
+ if (!root) return;
3181
+ if (!this.stepErrorDismissed) {
3182
+ this.stepErrorDismissed = true;
3183
+ for (const alert of root.querySelectorAll("zl-alert[data-zl-step-error]")) alert.remove();
3184
+ }
3185
+ for (const field of root.querySelectorAll("zl-field")) {
3186
+ if (field.getAttribute("name") !== fieldName || !field.invalid) continue;
3187
+ field.invalid = false;
3188
+ field.error = "";
3189
+ }
3190
+ }
2904
3191
  syncFieldElementValue(name, value) {
2905
3192
  const root = this.shadowRoot;
2906
3193
  if (!root) return;
@@ -2960,9 +3247,10 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2960
3247
  */
2961
3248
  handlePasskeyError = (event) => {
2962
3249
  if (!this.response) return;
2963
- const { error: message, aborted } = event.detail;
2964
- const errorKey = aborted ? "error.passkey_cancelled" : "error.passkey_failed";
3250
+ const { error: message, aborted, timed_out: timedOut } = event.detail;
3251
+ const errorKey = timedOut ? "error.passkey_timeout" : aborted ? "error.passkey_cancelled" : "error.passkey_failed";
2965
3252
  if (this.response.step.error === errorKey) return;
3253
+ this.stepErrorDismissed = false;
2966
3254
  const { challenge: _dropped, ...stepWithoutChallenge } = this.response.step;
2967
3255
  this.response = {
2968
3256
  ...this.response,
@@ -2971,7 +3259,7 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2971
3259
  error: errorKey
2972
3260
  }
2973
3261
  };
2974
- console.warn(`[zitadel-login] passkey ceremony ${aborted ? "cancelled" : "failed"}: ${message}`);
3262
+ console.warn(`[zitadel-login] passkey ceremony ${timedOut ? "timed out" : aborted ? "cancelled" : "failed"}: ${message}`);
2975
3263
  };
2976
3264
  findPrimaryAction() {
2977
3265
  const root = this.shadowRoot;
@@ -3013,13 +3301,17 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
3013
3301
  }
3014
3302
  }
3015
3303
  handleTransportError(error) {
3016
- const message = error instanceof Error ? error.message : "Unexpected error contacting the Flow API.";
3304
+ const message = error instanceof ApiError ? apiErrorMessage(error) : error instanceof Error ? error.message : "Unexpected error contacting the Flow API.";
3017
3305
  this.startupError = message;
3018
3306
  console.error("[zitadel-login]", error);
3019
3307
  emit(this, "zitadel-flow-error", { message });
3020
3308
  }
3021
3309
  };
3022
3310
  __decorate([property({ type: String })], ZitadelLogin.prototype, "purpose", null);
3311
+ __decorate([property({
3312
+ type: String,
3313
+ attribute: "flow-name"
3314
+ })], ZitadelLogin.prototype, "flowName", null);
3023
3315
  __decorate([property({ attribute: false })], ZitadelLogin.prototype, "project", null);
3024
3316
  __decorate([property({
3025
3317
  type: String,
@@ -3774,4 +4066,4 @@ ZitadelSession = __decorate([customElement("zitadel-session")], ZitadelSession);
3774
4066
  //#endregion
3775
4067
  export { submitStep as S, applyBrandingTokens as _, layout_chrome_default as a, getCurrentStep as b, it as c, TEMPLATE_NAMES as d, MANDATORY_GATES_MARKER as f, validateBranding as g, applyFontUrl as h, ThemeController as i, de as l, patchMandatoryGates as m, ZitadelLogout as n, createSanitiser as o, mandatoryGatesMarkerComment as p, ZitadelLogin as r, builtinLocales as s, ZitadelSession as t, en as u, buildBrandingStylesheet as v, startFlow as x, resolveTheme as y };
3776
4068
 
3777
- //# sourceMappingURL=orchestrator-qkFBKFgJ.mjs.map
4069
+ //# sourceMappingURL=orchestrator-BN2nT9FZ.mjs.map