@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.
- package/README.md +1 -0
- package/dist/atoms/index.d.mts +1 -1
- package/dist/atoms/index.mjs +1 -1
- package/dist/{atoms-BSwVl736.mjs → atoms-C-zXJjaz.mjs} +106 -10
- package/dist/{atoms-BSwVl736.mjs.map → atoms-C-zXJjaz.mjs.map} +1 -1
- package/dist/default-BoC3JEsO.mjs +6 -0
- package/dist/default-BoC3JEsO.mjs.map +1 -0
- package/dist/{index-EC-BnGsx.d.mts → index-BniwXaIC.d.mts} +3 -1
- package/dist/{index-EC-BnGsx.d.mts.map → index-BniwXaIC.d.mts.map} +1 -1
- package/dist/{index-Bypr00UG.d.mts → index-BwQLFs9R.d.mts} +362 -17
- package/dist/index-BwQLFs9R.d.mts.map +1 -0
- package/dist/{index-BP3l3mje.d.mts → index-D1eMaWbY.d.mts} +52 -5
- package/dist/index-D1eMaWbY.d.mts.map +1 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +4 -4
- package/dist/manifests.mjs +1 -1
- package/dist/orchestrator/index.d.mts +2 -2
- package/dist/orchestrator/index.mjs +2 -2
- package/dist/{orchestrator-qkFBKFgJ.mjs → orchestrator-BN2nT9FZ.mjs} +317 -25
- package/dist/orchestrator-BN2nT9FZ.mjs.map +1 -0
- package/dist/standalone.mjs +423 -33
- package/dist/tokens/index.d.mts +1 -1
- package/dist/tokens/index.mjs +1 -1
- package/dist/{tokens-C0j4iCrs.mjs → tokens-CbH2s1jV.mjs} +6 -4
- package/dist/tokens-CbH2s1jV.mjs.map +1 -0
- package/package.json +5 -4
- package/dist/default-W5MwYLBc.mjs +0 -6
- package/dist/default-W5MwYLBc.mjs.map +0 -1
- package/dist/index-BP3l3mje.d.mts.map +0 -1
- package/dist/index-Bypr00UG.d.mts.map +0 -1
- package/dist/orchestrator-qkFBKFgJ.mjs.map +0 -1
- 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-
|
|
2
|
-
import { r as tokensCss } from "./tokens-
|
|
3
|
-
import { t as default_default } from "./default-
|
|
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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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-
|
|
4069
|
+
//# sourceMappingURL=orchestrator-BN2nT9FZ.mjs.map
|