@zitadel/components 0.1.0-alpha.15 → 0.1.0-alpha.17

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 (33) hide show
  1. package/README.md +5 -1
  2. package/dist/atoms/index.d.mts +1 -1
  3. package/dist/atoms/index.mjs +1 -1
  4. package/dist/{atoms-BSwVl736.mjs → atoms-KC7OJWXZ.mjs} +138 -12
  5. package/dist/atoms-KC7OJWXZ.mjs.map +1 -0
  6. package/dist/default-CfF7GoLv.mjs +6 -0
  7. package/dist/default-CfF7GoLv.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-DSZnfOJH.d.mts} +368 -17
  11. package/dist/index-DSZnfOJH.d.mts.map +1 -0
  12. package/dist/{index-BP3l3mje.d.mts → index-DjYJwejg.d.mts} +51 -4
  13. package/dist/index-DjYJwejg.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-Ty4uM5n_.mjs → orchestrator-BWqJw4Yl.mjs} +348 -25
  20. package/dist/orchestrator-BWqJw4Yl.mjs.map +1 -0
  21. package/dist/standalone.mjs +485 -34
  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 +3 -2
  27. package/dist/atoms-BSwVl736.mjs.map +0 -1
  28. package/dist/default-BmX2ZEbg.mjs +0 -6
  29. package/dist/default-BmX2ZEbg.mjs.map +0 -1
  30. package/dist/index-BP3l3mje.d.mts.map +0 -1
  31. package/dist/index-Bypr00UG.d.mts.map +0 -1
  32. package/dist/orchestrator-Ty4uM5n_.mjs.map +0 -1
  33. 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-BmX2ZEbg.mjs";
1
+ import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-KC7OJWXZ.mjs";
2
+ import { r as tokensCss } from "./tokens-CbH2s1jV.mjs";
3
+ import { t as default_default } from "./default-CfF7GoLv.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) ?? fieldLabelFallback(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`;
@@ -1888,6 +1965,13 @@ function createLiquidEngine(options) {
1888
1965
  }
1889
1966
  return "";
1890
1967
  });
1968
+ /**
1969
+ * Automation-hook token for a field name: strips the
1970
+ * `x-auth-methods#` credential prefix so testids stay method-named
1971
+ * (`zitadel-field-password`), while the `name` attribute keeps the
1972
+ * raw wire key. See hookName for the contract.
1973
+ */
1974
+ engine.registerFilter("testid", (fieldName) => hookName(stringify(fieldName)));
1891
1975
  /** True when the error should render as `<zl-alert>`, not on a field. */
1892
1976
  engine.registerFilter("formLevelError", (err) => {
1893
1977
  const key = err?.text_key ?? "";
@@ -1901,6 +1985,123 @@ function createLiquidEngine(options) {
1901
1985
  });
1902
1986
  return engine;
1903
1987
  }
1988
+ /**
1989
+ * Fallbacks for text keys the flow engine derives from tenant-chosen step
1990
+ * names (`<step>.action.back` for the injected back action — see
1991
+ * `internal/domain/flow_state_machine.go` `buildStep`). Step names are open,
1992
+ * so no dictionary can enumerate them; a missing step-specific key falls back
1993
+ * to its generic entry instead of leaking the raw key into the UI.
1994
+ */
1995
+ const INJECTED_KEY_FALLBACKS = [{
1996
+ suffix: ".action.back",
1997
+ fallback: "action.back"
1998
+ }];
1999
+ function injectedKeyFallback(locale, key) {
2000
+ for (const { suffix, fallback } of INJECTED_KEY_FALLBACKS) if (key.endsWith(suffix)) return locale[fallback];
2001
+ }
2002
+ /**
2003
+ * Fallback for field-label keys (`<step>.field.<name>` — see
2004
+ * `FlowField.TextKey` in `internal/domain/flow_field_resolver.go`). Both
2005
+ * halves are tenant-chosen (step names and schema property names), so no
2006
+ * catalog can enumerate the keys; a miss renders a humanised property name
2007
+ * ("dateOfBirth" → "Date of birth") instead of leaking the raw key into the
2008
+ * form. Sub-keys like `.placeholder`/`.help` are excluded — they resolve
2009
+ * through their own filters, which stay empty on a miss.
2010
+ */
2011
+ function fieldLabelFallback(key) {
2012
+ const index = key.lastIndexOf(".field.");
2013
+ if (index === -1) return void 0;
2014
+ const field = key.slice(index + 7);
2015
+ if (field === "" || field.includes(".")) return void 0;
2016
+ return capitaliseFirst(humaniseFieldName(field));
2017
+ }
2018
+ /**
2019
+ * Rule-suffix fallbacks for the server's field-validation keys
2020
+ * (`error.<field>_<rule>` — see `FlowFieldValidationError.TextKey` in
2021
+ * `internal/domain/flow_field_resolver.go`). Field names come from the
2022
+ * tenant's user schema, so no catalog can enumerate the specific keys;
2023
+ * a miss resolves to the rule's generic entry, interpolated with the
2024
+ * field's label (`{0}`). The server spells the format rule `_invalid`
2025
+ * (the catalog's existing convention, e.g. `error.email_invalid`), so
2026
+ * that suffix takes the format wording; `_unknown_field` (a submitted
2027
+ * name that is not a step field) takes the catch-all.
2028
+ */
2029
+ const FLOW_ERROR_RULE_FALLBACKS = [
2030
+ {
2031
+ suffix: "_required",
2032
+ generic: "error.field_required"
2033
+ },
2034
+ {
2035
+ suffix: "_min_length",
2036
+ generic: "error.field_min_length"
2037
+ },
2038
+ {
2039
+ suffix: "_max_length",
2040
+ generic: "error.field_max_length"
2041
+ },
2042
+ {
2043
+ suffix: "_format",
2044
+ generic: "error.field_format"
2045
+ },
2046
+ {
2047
+ suffix: "_invalid",
2048
+ generic: "error.field_format"
2049
+ },
2050
+ {
2051
+ suffix: "_unknown_field",
2052
+ generic: "error.field_invalid"
2053
+ }
2054
+ ];
2055
+ const FLOW_ERROR_CATCH_ALL_KEY = "error.field_invalid";
2056
+ /**
2057
+ * Localises a `step.error` payload of `error.*` catalog keys —
2058
+ * field-validation violations and general engine failures (e.g.
2059
+ * `error.invalid_credentials`, `error.passkey_invalid`) alike.
2060
+ * Returns `null` unless EVERY `"; "`-joined segment is an `error.*` key
2061
+ * — the caller keeps other payloads (outcome tokens such as
2062
+ * `user_not_found`) verbatim.
2063
+ *
2064
+ * Keys the locale knows pass through as `text_key` entries: the template
2065
+ * localises them via `| t`, and `fieldErrorKeys` routes the known ones
2066
+ * inline to their field. Unknown keys with a recognised rule suffix are
2067
+ * pre-localised here from their generic {@link FLOW_ERROR_RULE_FALLBACKS}
2068
+ * entry; unknown keys without one pass through as `text_key` (matching
2069
+ * `| t`'s behaviour for non-validation keys such as
2070
+ * `error.sign_in_server`, which localises via `.title`/`.body`).
2071
+ */
2072
+ function localiseFlowErrorKeys(raw, ctx) {
2073
+ const segments = raw.split("; ");
2074
+ if (!segments.every((segment) => segment.startsWith("error."))) return null;
2075
+ return segments.map((key) => localiseFlowErrorKey(key, ctx));
2076
+ }
2077
+ function localiseFlowErrorKey(key, ctx) {
2078
+ const inlineField = fieldErrorKeys[key];
2079
+ const orphanedInline = inlineField !== void 0 && ctx.fields !== void 0 && !ctx.fields.includes(inlineField);
2080
+ if (!orphanedInline && ctx.locale[key] !== void 0) return { text_key: key };
2081
+ for (const { suffix, generic } of FLOW_ERROR_RULE_FALLBACKS) {
2082
+ if (!key.endsWith(suffix)) continue;
2083
+ const field = key.slice(6, key.length - suffix.length);
2084
+ if (field === "") break;
2085
+ return { message: capitaliseFirst(interpolate(ctx.locale[generic] ?? ctx.locale[FLOW_ERROR_CATCH_ALL_KEY] ?? "Please check {0}.", [fieldLabel(ctx, field)])) };
2086
+ }
2087
+ if (orphanedInline && ctx.locale[key] !== void 0) return { message: ctx.locale[key] };
2088
+ return { text_key: key };
2089
+ }
2090
+ /** `<step>.field.<name>` from the locale, else a humanised field name. */
2091
+ function fieldLabel(ctx, field) {
2092
+ return ctx.locale[`${ctx.stepName}.field.${field}`] ?? humaniseFieldName(field);
2093
+ }
2094
+ /**
2095
+ * `x-auth-methods#password` → "password", `givenName` → "given name",
2096
+ * `date_of_birth` → "date of birth".
2097
+ */
2098
+ function humaniseFieldName(field) {
2099
+ return (field.includes("#") ? field.split("#").pop() : field).replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_.-]+/g, " ").trim().toLowerCase();
2100
+ }
2101
+ /** Messages may open with a lowercase field label; sentence-case them. */
2102
+ function capitaliseFirst(text) {
2103
+ return text.length > 0 ? text[0]?.toUpperCase() + text.slice(1) : text;
2104
+ }
1904
2105
  function stringify(value) {
1905
2106
  if (value == null) return "";
1906
2107
  if (typeof value === "string") return value;
@@ -1911,6 +2112,11 @@ function stringify(value) {
1911
2112
  return String(value);
1912
2113
  }
1913
2114
  }
2115
+ /**
2116
+ * Positional `{n}` substitution shared by the `| t` filter and the
2117
+ * pre-localising fallback in {@link localiseFlowErrorKeys}, so both
2118
+ * produce byte-identical output for the same template and args.
2119
+ */
1914
2120
  function interpolate(template, args) {
1915
2121
  if (args.length === 0) return template;
1916
2122
  return template.replace(/\{(\d+)\}/g, (match, index) => {
@@ -2028,6 +2234,10 @@ const en = {
2028
2234
  "signed-in.continue": "Continue",
2029
2235
  "signed-in.logout": "Sign out",
2030
2236
  "passkey-login.title": "Sign in with your passkey",
2237
+ "passkey-first.title": "Sign in",
2238
+ "passkey-first.description": "Use your passkey to continue",
2239
+ "passkey-first.action.passkey": "Continue with passkey",
2240
+ "passkey-first.action.email_fallback": "Use email instead",
2031
2241
  "recover.title": "Check your email",
2032
2242
  "recover.description": "We sent a password reset link to your email address.",
2033
2243
  "recover.action.back": "Back to sign in",
@@ -2035,12 +2245,17 @@ const en = {
2035
2245
  "submit.signin": "Sign in",
2036
2246
  "action.forgot_password": "Forgot password?",
2037
2247
  "action.cancel": "Cancel",
2248
+ "action.back": "Back",
2038
2249
  "sso.redirect.title": "Redirecting to your provider…",
2039
- "error.passkey_cancelled": "Passkey setup was cancelled",
2250
+ "passkey.pending.status": "Waiting for your passkey…",
2251
+ "error.passkey_cancelled": "The passkey prompt was closed before completing.",
2252
+ "error.passkey_timeout": "The passkey request timed out. Please try again.",
2040
2253
  "error.passkey_not_registered": "This passkey is not registered. Please sign in with your email and password.",
2041
2254
  "error.passkey_setup_failed": "Passkey registration did not complete. Please try again.",
2042
2255
  "error.passkey_unsupported": "This device does not support passkeys",
2043
2256
  "error.passkey_failed": "Something went wrong. Please try again.",
2257
+ "error.passkey_invalid": "This passkey could not be verified. Please try again.",
2258
+ "error.passkey_registration_invalid": "The new passkey could not be verified. Please try registering it again.",
2044
2259
  "error.email_required": "Please enter an email address",
2045
2260
  "error.email_invalid": "Please enter a valid email",
2046
2261
  "error.password_required": "Please enter a password",
@@ -2049,6 +2264,11 @@ const en = {
2049
2264
  /** Figma sign-in error `6602:180268` — inline on password field. */
2050
2265
  "error.invalid_credentials": "Wrong email or password.",
2051
2266
  "error.required": "This field is required.",
2267
+ "error.field_required": "{0} is required.",
2268
+ "error.field_format": "Please enter a valid {0}.",
2269
+ "error.field_min_length": "{0} is too short.",
2270
+ "error.field_max_length": "{0} is too long.",
2271
+ "error.field_invalid": "Please check {0}.",
2052
2272
  "error.sign_in_server.title": "We couldn't complete your sign in.",
2053
2273
  "error.sign_in_server.body": "Please try again in a few minutes"
2054
2274
  };
@@ -2130,6 +2350,10 @@ const de = {
2130
2350
  "signed-in.continue": "Weiter",
2131
2351
  "signed-in.logout": "Abmelden",
2132
2352
  "passkey-login.title": "Mit Passkey anmelden",
2353
+ "passkey-first.title": "Anmelden",
2354
+ "passkey-first.description": "Verwende deinen Passkey, um fortzufahren",
2355
+ "passkey-first.action.passkey": "Weiter mit Passkey",
2356
+ "passkey-first.action.email_fallback": "Stattdessen E-Mail verwenden",
2133
2357
  "recover.title": "E-Mail prüfen",
2134
2358
  "recover.description": "Wir haben einen Link zum Zurücksetzen des Passworts an deine E-Mail-Adresse gesendet.",
2135
2359
  "recover.action.back": "Zurück zur Anmeldung",
@@ -2137,12 +2361,17 @@ const de = {
2137
2361
  "submit.signin": "Anmelden",
2138
2362
  "action.forgot_password": "Passwort vergessen?",
2139
2363
  "action.cancel": "Abbrechen",
2364
+ "action.back": "Zurück",
2140
2365
  "sso.redirect.title": "Weiterleitung zum Anbieter…",
2141
- "error.passkey_cancelled": "Passkey-Einrichtung wurde abgebrochen",
2366
+ "passkey.pending.status": "Warten auf deinen Passkey…",
2367
+ "error.passkey_cancelled": "Die Passkey-Abfrage wurde vorzeitig geschlossen.",
2368
+ "error.passkey_timeout": "Die Passkey-Anfrage hat zu lange gedauert. Bitte versuche es erneut.",
2142
2369
  "error.passkey_not_registered": "Dieser Passkey ist nicht registriert. Bitte melde dich mit E-Mail und Passwort an.",
2143
2370
  "error.passkey_setup_failed": "Passkey-Registrierung wurde nicht abgeschlossen. Bitte versuche es erneut.",
2144
2371
  "error.passkey_unsupported": "Dieses Gerät unterstützt keine Passkeys",
2145
2372
  "error.passkey_failed": "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
2373
+ "error.passkey_invalid": "Dieser Passkey konnte nicht bestätigt werden. Bitte versuche es erneut.",
2374
+ "error.passkey_registration_invalid": "Der neue Passkey konnte nicht bestätigt werden. Bitte registriere ihn erneut.",
2146
2375
  "error.email_required": "Bitte gib eine E-Mail-Adresse ein",
2147
2376
  "error.email_invalid": "Bitte gib eine gültige E-Mail-Adresse ein",
2148
2377
  "error.password_required": "Bitte gib ein Passwort ein",
@@ -2150,6 +2379,11 @@ const de = {
2150
2379
  "error.email_exists": "Ein Konto mit dieser E-Mail-Adresse existiert bereits.",
2151
2380
  "error.invalid_credentials": "Falsche E-Mail oder falsches Passwort.",
2152
2381
  "error.required": "Dieses Feld ist erforderlich.",
2382
+ "error.field_required": "{0} ist erforderlich.",
2383
+ "error.field_format": "Bitte gib einen gültigen Wert für {0} ein.",
2384
+ "error.field_min_length": "{0} ist zu kurz.",
2385
+ "error.field_max_length": "{0} ist zu lang.",
2386
+ "error.field_invalid": "Bitte überprüfe {0}.",
2153
2387
  "error.sign_in_server.title": "Anmeldung konnte nicht abgeschlossen werden.",
2154
2388
  "error.sign_in_server.body": "Bitte versuche es in einigen Minuten erneut"
2155
2389
  };
@@ -2231,6 +2465,10 @@ const it = {
2231
2465
  "signed-in.continue": "Continua",
2232
2466
  "signed-in.logout": "Esci",
2233
2467
  "passkey-login.title": "Accedi con la tua passkey",
2468
+ "passkey-first.title": "Accedi",
2469
+ "passkey-first.description": "Usa la tua passkey per continuare",
2470
+ "passkey-first.action.passkey": "Continua con passkey",
2471
+ "passkey-first.action.email_fallback": "Usa invece l'email",
2234
2472
  "recover.title": "Controlla la tua e-mail",
2235
2473
  "recover.description": "Abbiamo inviato un link per reimpostare la password al tuo indirizzo e-mail.",
2236
2474
  "recover.action.back": "Torna all'accesso",
@@ -2238,12 +2476,17 @@ const it = {
2238
2476
  "submit.signin": "Accedi",
2239
2477
  "action.forgot_password": "Password dimenticata?",
2240
2478
  "action.cancel": "Annulla",
2479
+ "action.back": "Indietro",
2241
2480
  "sso.redirect.title": "Reindirizzamento al provider…",
2242
- "error.passkey_cancelled": "La configurazione della passkey è stata annullata",
2481
+ "passkey.pending.status": "In attesa della tua passkey",
2482
+ "error.passkey_cancelled": "La richiesta della passkey è stata chiusa prima del completamento.",
2483
+ "error.passkey_timeout": "La richiesta della passkey è scaduta. Riprova.",
2243
2484
  "error.passkey_not_registered": "Questa passkey non è registrata. Accedi con e-mail e password.",
2244
2485
  "error.passkey_setup_failed": "La registrazione della passkey non è stata completata. Riprova.",
2245
2486
  "error.passkey_unsupported": "Questo dispositivo non supporta le passkey",
2246
2487
  "error.passkey_failed": "Qualcosa è andato storto. Riprova.",
2488
+ "error.passkey_invalid": "Non è stato possibile verificare questa passkey. Riprova.",
2489
+ "error.passkey_registration_invalid": "Non è stato possibile verificare la nuova passkey. Riprova a registrarla.",
2247
2490
  "error.email_required": "Inserisci un indirizzo e-mail",
2248
2491
  "error.email_invalid": "Inserisci un indirizzo e-mail valido",
2249
2492
  "error.password_required": "Inserisci una password",
@@ -2251,6 +2494,11 @@ const it = {
2251
2494
  "error.email_exists": "Esiste già un account con questo indirizzo e-mail.",
2252
2495
  "error.invalid_credentials": "E-mail o password errata.",
2253
2496
  "error.required": "Questo campo è obbligatorio.",
2497
+ "error.field_required": "{0} è obbligatorio.",
2498
+ "error.field_format": "Inserisci un valore valido per {0}.",
2499
+ "error.field_min_length": "{0} è troppo corto.",
2500
+ "error.field_max_length": "{0} è troppo lungo.",
2501
+ "error.field_invalid": "Controlla {0}.",
2254
2502
  "error.sign_in_server.title": "Non è stato possibile completare l'accesso.",
2255
2503
  "error.sign_in_server.body": "Riprova tra qualche minuto"
2256
2504
  };
@@ -2484,6 +2732,13 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2484
2732
  set purpose(value) {
2485
2733
  this.#_purpose_accessor_storage = value;
2486
2734
  }
2735
+ #_flowName_accessor_storage = "";
2736
+ get flowName() {
2737
+ return this.#_flowName_accessor_storage;
2738
+ }
2739
+ set flowName(value) {
2740
+ this.#_flowName_accessor_storage = value;
2741
+ }
2487
2742
  #_project_accessor_storage;
2488
2743
  get project() {
2489
2744
  return this.#_project_accessor_storage;
@@ -2591,6 +2846,8 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2591
2846
  sheet.replaceSync(layout_chrome_default);
2592
2847
  root.adoptedStyleSheets = [...existing, sheet];
2593
2848
  root.addEventListener("zl-input", this.handleAtomInput);
2849
+ root.addEventListener("zl-change", this.handleAtomEdited);
2850
+ root.addEventListener("zl-dismiss", this.handleAlertDismiss);
2594
2851
  root.addEventListener("zl-submit", this.handleAtomSubmit);
2595
2852
  root.addEventListener("click", this.handleDelegatedAction);
2596
2853
  root.addEventListener("submit", this.handleFormSubmit);
@@ -2729,17 +2986,32 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2729
2986
  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
2987
  wire = await startFlow(api, {
2731
2988
  project_id: cfg.projectId,
2732
- purpose: this.purpose
2989
+ purpose: this.purpose,
2990
+ ...this.flowName ? { flow_definition_name: this.flowName } : {}
2733
2991
  });
2734
2992
  }
2735
2993
  this.applyResponse(wire);
2736
2994
  } catch (error) {
2737
- this.handleTransportError(error);
2995
+ this.handleTransportError(this.describeFlowSelectionError(error));
2738
2996
  } finally {
2739
2997
  this.loading = false;
2740
2998
  }
2741
2999
  }
3000
+ /**
3001
+ * When a `flow-name` lookup fails, the server's envelope only says
3002
+ * "not found" / "purpose mismatch" — it cannot know the name came from
3003
+ * an attribute. Rewrap those two codes with the attribute and the fix;
3004
+ * every other error passes through untouched.
3005
+ */
3006
+ describeFlowSelectionError(error) {
3007
+ if (!this.flowName || !(error instanceof ApiError)) return error;
3008
+ const code = typeof error.body === "object" && error.body !== null && "code" in error.body ? String(error.body.code) : "";
3009
+ 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\`).`);
3010
+ 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}".`);
3011
+ return error;
3012
+ }
2742
3013
  applyResponse(wire) {
3014
+ this.stepErrorDismissed = false;
2743
3015
  this.response = wire;
2744
3016
  const { branding, issues } = validateBranding(wire.branding);
2745
3017
  this.branding = branding;
@@ -2793,7 +3065,12 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2793
3065
  }
2794
3066
  renderStep(step, engine) {
2795
3067
  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 }] : [];
3068
+ const rawErrors = step.error ? localiseFlowErrorKeys(step.error, {
3069
+ locale: this.resolveLocale(),
3070
+ stepName: step.name ?? "",
3071
+ fields: (step.fields ?? []).map((field) => field.name)
3072
+ }) ?? [{ message: step.error }] : [];
3073
+ const errors = this.loading && this.stepErrorDismissed ? [] : rawErrors;
2797
3074
  const fields = step.fields ?? [];
2798
3075
  const actions = step.actions ?? [];
2799
3076
  const context = {
@@ -2887,6 +3164,17 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2887
3164
  if (native && native.value !== field.value) return native.value;
2888
3165
  return typeof field.value === "string" ? field.value : void 0;
2889
3166
  }
3167
+ /**
3168
+ * True once the user retired the current step error by editing a field or
3169
+ * dismissing the alert. Deliberately NON-reactive: consulting reactive
3170
+ * state in `renderStep` would change its output string on the first
3171
+ * post-dismiss keystroke, and `unsafeHTML` would rebuild the whole step
3172
+ * subtree — wiping typed values (`hydrateStepAfterRender` only re-applies
3173
+ * them on response changes) and reconnecting atoms. Reset on every new
3174
+ * response; consulted only by the `loading` re-render, which rebuilds
3175
+ * anyway.
3176
+ */
3177
+ stepErrorDismissed = false;
2890
3178
  handleAtomInput = (event) => {
2891
3179
  if (!event.detail) return;
2892
3180
  const { name, value } = event.detail;
@@ -2896,11 +3184,41 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2896
3184
  [name]: value
2897
3185
  };
2898
3186
  this.syncFieldElementValue(name, value);
3187
+ this.clearStaleErrors(name);
2899
3188
  emit(this, "zitadel-flow-input", {
2900
3189
  name,
2901
3190
  value
2902
3191
  });
2903
3192
  };
3193
+ /** `zl-change` from <zl-checkbox>/<zl-select>: only error clearing. */
3194
+ handleAtomEdited = (event) => {
3195
+ const name = event.detail?.name;
3196
+ if (name) this.clearStaleErrors(name);
3197
+ };
3198
+ /** Explicit dismiss of the step-error alert (it removes itself). */
3199
+ handleAlertDismiss = (event) => {
3200
+ if (event.target?.matches?.("zl-alert[data-zl-step-error]")) this.stepErrorDismissed = true;
3201
+ };
3202
+ /**
3203
+ * Retire the current step error after the user edits `fieldName`:
3204
+ * remove the form-level alert(s) and clear the edited field's inline
3205
+ * error — other fields' inline errors stay until they are edited.
3206
+ * Imperative on purpose; see {@link stepErrorDismissed}.
3207
+ */
3208
+ clearStaleErrors(fieldName) {
3209
+ if (!this.response?.step.error) return;
3210
+ const root = this.shadowRoot;
3211
+ if (!root) return;
3212
+ if (!this.stepErrorDismissed) {
3213
+ this.stepErrorDismissed = true;
3214
+ for (const alert of root.querySelectorAll("zl-alert[data-zl-step-error]")) alert.remove();
3215
+ }
3216
+ for (const field of root.querySelectorAll("zl-field")) {
3217
+ if (field.getAttribute("name") !== fieldName || !field.invalid) continue;
3218
+ field.invalid = false;
3219
+ field.error = "";
3220
+ }
3221
+ }
2904
3222
  syncFieldElementValue(name, value) {
2905
3223
  const root = this.shadowRoot;
2906
3224
  if (!root) return;
@@ -2960,9 +3278,10 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2960
3278
  */
2961
3279
  handlePasskeyError = (event) => {
2962
3280
  if (!this.response) return;
2963
- const { error: message, aborted } = event.detail;
2964
- const errorKey = aborted ? "error.passkey_cancelled" : "error.passkey_failed";
3281
+ const { error: message, aborted, timed_out: timedOut } = event.detail;
3282
+ const errorKey = timedOut ? "error.passkey_timeout" : aborted ? "error.passkey_cancelled" : "error.passkey_failed";
2965
3283
  if (this.response.step.error === errorKey) return;
3284
+ this.stepErrorDismissed = false;
2966
3285
  const { challenge: _dropped, ...stepWithoutChallenge } = this.response.step;
2967
3286
  this.response = {
2968
3287
  ...this.response,
@@ -2971,7 +3290,7 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
2971
3290
  error: errorKey
2972
3291
  }
2973
3292
  };
2974
- console.warn(`[zitadel-login] passkey ceremony ${aborted ? "cancelled" : "failed"}: ${message}`);
3293
+ console.warn(`[zitadel-login] passkey ceremony ${timedOut ? "timed out" : aborted ? "cancelled" : "failed"}: ${message}`);
2975
3294
  };
2976
3295
  findPrimaryAction() {
2977
3296
  const root = this.shadowRoot;
@@ -3013,13 +3332,17 @@ let ZitadelLogin = class ZitadelLogin extends LitElement {
3013
3332
  }
3014
3333
  }
3015
3334
  handleTransportError(error) {
3016
- const message = error instanceof Error ? error.message : "Unexpected error contacting the Flow API.";
3335
+ const message = error instanceof ApiError ? apiErrorMessage(error) : error instanceof Error ? error.message : "Unexpected error contacting the Flow API.";
3017
3336
  this.startupError = message;
3018
3337
  console.error("[zitadel-login]", error);
3019
3338
  emit(this, "zitadel-flow-error", { message });
3020
3339
  }
3021
3340
  };
3022
3341
  __decorate([property({ type: String })], ZitadelLogin.prototype, "purpose", null);
3342
+ __decorate([property({
3343
+ type: String,
3344
+ attribute: "flow-name"
3345
+ })], ZitadelLogin.prototype, "flowName", null);
3023
3346
  __decorate([property({ attribute: false })], ZitadelLogin.prototype, "project", null);
3024
3347
  __decorate([property({
3025
3348
  type: String,
@@ -3774,4 +4097,4 @@ ZitadelSession = __decorate([customElement("zitadel-session")], ZitadelSession);
3774
4097
  //#endregion
3775
4098
  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
4099
 
3777
- //# sourceMappingURL=orchestrator-Ty4uM5n_.mjs.map
4100
+ //# sourceMappingURL=orchestrator-BWqJw4Yl.mjs.map