@zitadel/config 0.1.0-alpha.16 → 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.
package/defaults/README-flows.md
CHANGED
|
@@ -76,14 +76,6 @@ enters login on a fields-less passkey step with an email → password
|
|
|
76
76
|
fallback path. The preset only decides the starting point — edit
|
|
77
77
|
anything here afterwards.
|
|
78
78
|
|
|
79
|
-
## Presets
|
|
80
|
-
|
|
81
|
-
`zitadel setup` scaffolds this folder from a preset (`--preset
|
|
82
|
-
password-first` or `--preset passkey-first`). The passkey-first flow
|
|
83
|
-
enters login on a fields-less passkey step with an email → password
|
|
84
|
-
fallback path. The preset only decides the starting point — edit
|
|
85
|
-
anything here afterwards.
|
|
86
|
-
|
|
87
79
|
## Schema revisions
|
|
88
80
|
|
|
89
81
|
Editing a schema publishes a new immutable revision. When you `apply` a
|
package/dist/validate.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.mts","names":[],"sources":["../src/validate.ts"],"mappings":";;AA8BA;;;;;AAEA;;;;;;;;;;;;AAcA;;;;;;;;;;;KAhBY,sBAAA;AAAA,KAEA,mBAAA;EACV,QAAA,EAAU,sBAAA;EAEV,IAAA,EAAM,oBAAA;EAEN,OAAA;EAEA,IAAA;AAAA;;;;;cAOW,qBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAcD,oBAAA,gBAAoC,qBAAA;;cAGnC,iBAAA;;cAGA,aAAA;;cAUA,oBAAA,EAAsB,QAAA,CAAS,MAAA,SAAe,QAAA,CAAS,MAAA;;;;;;;;
|
|
1
|
+
{"version":3,"file":"validate.d.mts","names":[],"sources":["../src/validate.ts"],"mappings":";;AA8BA;;;;;AAEA;;;;;;;;;;;;AAcA;;;;;;;;;;;KAhBY,sBAAA;AAAA,KAEA,mBAAA;EACV,QAAA,EAAU,sBAAA;EAEV,IAAA,EAAM,oBAAA;EAEN,OAAA;EAEA,IAAA;AAAA;;;;;cAOW,qBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAcD,oBAAA,gBAAoC,qBAAA;;cAGnC,iBAAA;;cAGA,aAAA;;cAUA,oBAAA,EAAsB,QAAA,CAAS,MAAA,SAAe,QAAA,CAAS,MAAA;;;;;;;;iBAoCpD,sBAAA,CAAuB,IAAA,UAAc,MAAA,YAAkB,mBAAA"}
|
package/dist/validate.mjs
CHANGED
|
@@ -36,6 +36,11 @@ const PURPOSE_FLIP_TARGETS = {
|
|
|
36
36
|
login: { user_not_found: "register" },
|
|
37
37
|
register: { user_already_exists: "login" }
|
|
38
38
|
};
|
|
39
|
+
/** Mirrors `flipOutcomeImpacts` in flow_definition_validator.go (verbatim). */
|
|
40
|
+
const FLIP_OUTCOME_IMPACTS = {
|
|
41
|
+
user_not_found: "someone without an account gets stuck at sign-in instead of being routed to registration",
|
|
42
|
+
user_already_exists: "someone who already has an account gets stuck at registration instead of being routed to sign-in"
|
|
43
|
+
};
|
|
39
44
|
/** Action kinds a flow author may declare; `back` is engine-injected. */
|
|
40
45
|
const DECLARABLE_ACTION_KINDS = new Set([
|
|
41
46
|
"submit",
|
|
@@ -260,7 +265,7 @@ function validateFlipTableCoverage(def) {
|
|
|
260
265
|
if (entry === void 0) continue;
|
|
261
266
|
for (const [outcome, targetPurpose] of Object.entries(flipTargets)) {
|
|
262
267
|
if (!def.purposes.has(targetPurpose)) continue;
|
|
263
|
-
if (!entry.transitions.has(outcome)) issues.push(error("flip-table", `step ${q(entry.name)}: entry step for purpose ${q(purpose)} must wire ${q(outcome)} transition because ${q(targetPurpose)} is also a purpose`, entry.name));
|
|
268
|
+
if (!entry.transitions.has(outcome)) issues.push(error("flip-table", `step ${q(entry.name)}: entry step for purpose ${q(purpose)} must wire ${q(outcome)} transition because ${q(targetPurpose)} is also a purpose: without it, ${FLIP_OUTCOME_IMPACTS[outcome]}`, entry.name));
|
|
264
269
|
}
|
|
265
270
|
}
|
|
266
271
|
return issues;
|
package/dist/validate.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.mjs","names":[],"sources":["../src/validate.ts"],"sourcesContent":["/**\n * Plan-time port of the server's flow-definition validator\n * (`internal/domain/flow_definition_validator.go`). The CLI runs it in\n * `buildSyncPlan` so every rule the server enforces on apply speaks up\n * at plan time — with the same words — before anything mutates.\n *\n * Contract with the Go source:\n *\n * - Every rule id in {@link FLOW_VALIDATION_RULES} names the Go function\n * it ports (`goRef`); a drift-audit test asserts those functions still\n * exist and that the shared literals below still match.\n * - Error messages mirror the Go detail strings verbatim, so a user who\n * bypasses plan sees identical text from the server.\n * - Unlike the Go validator (fail-fast), this port collects every issue.\n * Later phases are gated on earlier phases being clean so one defect\n * does not cascade into noise (a BFS over duplicate step names is\n * ill-defined).\n *\n * Scope cuts (server/service-side, not portable): pivot/switch targets\n * must name an active flow definition in the same project (needs the\n * DB); the user schema itself is assumed meta-schema-valid.\n *\n * Lifecycle: this port is a local-first interim, not a second source of\n * truth to grow. Its designed successor is the server-side validate-only\n * bundle endpoint (zitadel/nextgen#449); once plan can ask the server,\n * this file, its drift audit, and the keep-in-sync comment on the Go\n * validator all go away. New rules land in Go first and are ported here\n * only until then.\n */\n\nexport type FlowValidationSeverity = \"error\" | \"warning\";\n\nexport type FlowValidationIssue = {\n severity: FlowValidationSeverity;\n /** Stable rule id from {@link FLOW_VALIDATION_RULES}. */\n rule: FlowValidationRuleId;\n /** Mirrors the Go `ErrFlowDefinitionInvalid` detail string where a Go rule exists. */\n message: string;\n /** Step the issue is anchored to, when applicable. */\n step?: string;\n};\n\n/**\n * Rule registry: rule id → the Go function it ports. `goRef: null` marks\n * CLI-only guidance with no server counterpart.\n */\nexport const FLOW_VALIDATION_RULES = {\n \"step-names\": { goRef: \"stepNamesMap\" },\n definition: { goRef: \"validateDefinition\" },\n steps: { goRef: \"validateSteps\" },\n graph: { goRef: \"validateGraph\" },\n cycles: { goRef: \"validateCycles\" },\n \"flip-table\": { goRef: \"validateFlipTableCoverage\" },\n \"schema/required-fields\": { goRef: \"validateRequiredUserSchemaFields\" },\n \"schema/fields-resolve\": { goRef: \"resolveAllStepFields\" },\n \"schema/passkey-actions\": { goRef: \"validatePasskeyActionsEnabled\" },\n \"schema/on-success-manifest\": { goRef: \"validateOnSuccessManifests\" },\n \"warn/password-without-identifier\": { goRef: null },\n} as const;\n\nexport type FlowValidationRuleId = keyof typeof FLOW_VALIDATION_RULES;\n\n/** Mirrors `reservedOutcomes` in flow_definition_validator.go. */\nexport const RESERVED_OUTCOMES = [\"user_not_found\", \"user_already_exists\", \"callback\"] as const;\n\n/** Mirrors the `FlowDefinitionPurpose` enum (snake transform) in flow_definition.go. */\nexport const FLOW_PURPOSES = [\n \"login\",\n \"register\",\n \"recovery\",\n \"profiling\",\n \"reauth\",\n \"link_account\",\n] as const;\n\n/** Mirrors `purposeFlipTargets` in flow_definition_validator.go. */\nexport const PURPOSE_FLIP_TARGETS: Readonly<Record<string, Readonly<Record<string, string>>>> = {\n login: { user_not_found: \"register\" },\n register: { user_already_exists: \"login\" },\n};\n\n/** Action kinds a flow author may declare; `back` is engine-injected. */\nconst DECLARABLE_ACTION_KINDS = new Set([\"submit\", \"passkey\", \"passkey_register\", \"navigate\"]);\n\n/** Mirrors `flowBackActionName` in flow_state_machine.go. */\nconst BACK_ACTION_NAME = \"back\";\n\n/** Mirrors `authMethodPrefix` in flow_definition.go. */\nconst AUTH_METHOD_PREFIX = \"x-auth-methods#\";\n\n/** Mirrors `ManifestForOnSuccess` in flow_on_success.go. */\nconst ON_SUCCESS_MANIFESTS: Readonly<Record<string, readonly FieldChallenge[]>> = {\n create_user: [\"identifier\", \"password\"],\n};\n\ntype FieldChallenge = \"identifier\" | \"password\";\n\n/**\n * Validate a flow-definition config body (the `.zitadel/flows/*.json`\n * content) against the rules the server enforces on apply. `schema` is\n * the content of the user schema the flow's `user_schema` pins; when\n * omitted, schema-dependent rules are skipped. Pure — collects all\n * issues, never throws.\n */\nexport function validateFlowDefinition(flow: object, schema?: object): FlowValidationIssue[] {\n const def = readFlow(flow);\n const issues: FlowValidationIssue[] = [];\n\n // Phase 1 — step names (everything else keys off them).\n const stepNames = new Set<string>();\n for (const step of def.steps) {\n if (stepNames.has(step.name)) {\n issues.push(error(\"step-names\", `duplicate step name ${q(step.name)}`, step.name));\n }\n stepNames.add(step.name);\n }\n\n // Phase 2 — purposes.\n if (def.purposes.size === 0) {\n issues.push(error(\"definition\", \"no purposes defined\"));\n }\n for (const [purpose, entryStep] of def.purposes) {\n if (!(FLOW_PURPOSES as readonly string[]).includes(purpose)) {\n issues.push(error(\"definition\", `'${purpose}' is not a valid purpose`));\n continue;\n }\n if (entryStep === \"\") {\n issues.push(error(\"definition\", `initial step for purpose '${purpose}' is empty`));\n continue;\n }\n if (!stepNames.has(entryStep)) {\n issues.push(\n error(\"definition\", `purpose ${q(purpose)} targets unknown entry-point step ${q(entryStep)}`),\n );\n }\n }\n\n // Phase 3 — per-step structure.\n for (const step of def.steps) {\n issues.push(...validateStep(step));\n }\n\n // Graph phases are ill-defined over broken structure — gate on 1–3.\n if (issues.length === 0) {\n issues.push(...validateGraph(def, stepNames));\n if (issues.length === 0) {\n issues.push(...validateCycles(def));\n }\n issues.push(...validateFlipTableCoverage(def));\n }\n\n // Schema-dependent phases: only meaningful over structurally sound\n // steps, and only when the paired schema content is available.\n if (schema !== undefined && issues.length === 0) {\n issues.push(...validateAgainstSchema(def, schema));\n }\n\n return issues;\n}\n\n// ── internal flow reading ────────────────────────────────────────────────────\n\ntype FlowStep = {\n name: string;\n fields: string[];\n actions: { name: string; kind: string }[];\n gateCount: number;\n ssoProviderCount: number;\n transitions: Map<string, { target: string; action: string | null }>;\n onSuccess: string | undefined;\n terminal: boolean;\n};\n\ntype FlowDef = {\n purposes: Map<string, string>;\n steps: FlowStep[];\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction str(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\n/**\n * Coerce raw parsed JSON into the internal shape. Callers usually run\n * the Zod config schema first; the defensive reads keep the standalone\n * API from crashing on malformed input (it reports rule violations on\n * whatever is readable instead).\n */\nfunction readFlow(flow: object): FlowDef {\n const raw = flow as Record<string, unknown>;\n\n const purposes = new Map<string, string>();\n if (isPlainObject(raw.purposes)) {\n for (const [purpose, entry] of Object.entries(raw.purposes)) {\n purposes.set(purpose, str(entry));\n }\n }\n\n const steps: FlowStep[] = [];\n if (Array.isArray(raw.steps)) {\n for (const value of raw.steps) {\n if (!isPlainObject(value)) continue;\n const transitions = new Map<string, { target: string; action: string | null }>();\n if (isPlainObject(value.transitions)) {\n for (const [key, t] of Object.entries(value.transitions)) {\n const entry = isPlainObject(t) ? t : {};\n transitions.set(key, {\n target: str(entry.target),\n action: typeof entry.action === \"string\" ? entry.action : null,\n });\n }\n }\n steps.push({\n name: str(value.name),\n fields: Array.isArray(value.fields) ? value.fields.map(str) : [],\n actions: Array.isArray(value.actions)\n ? value.actions\n .filter(isPlainObject)\n .map((a) => ({ name: str(a.name), kind: str(a.kind) }))\n : [],\n gateCount: isPlainObject(value.gates) ? Object.keys(value.gates).length : 0,\n ssoProviderCount: Array.isArray(value.sso_providers) ? value.sso_providers.length : 0,\n transitions,\n onSuccess: typeof value.on_success === \"string\" ? value.on_success : undefined,\n terminal: value.complete !== undefined && value.complete !== null,\n });\n }\n }\n\n return { purposes, steps };\n}\n\n/** Go `%q` — double-quoted string. */\nfunction q(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction error(rule: FlowValidationRuleId, message: string, step?: string): FlowValidationIssue {\n return step === undefined\n ? { severity: \"error\", rule, message }\n : { severity: \"error\", rule, message, step };\n}\n\n// ── phase 3: per-step structure (validateSteps + uniqueNonEmptyFields) ──────\n\nfunction validateStep(step: FlowStep): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n const name = step.name;\n\n if (step.terminal) {\n if (\n step.fields.length > 0 ||\n step.actions.length > 0 ||\n step.transitions.size > 0 ||\n step.gateCount > 0 ||\n step.ssoProviderCount > 0\n ) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)} is terminal (complete is set) but has fields, actions, transitions, gates, or sso_providers`,\n name,\n ),\n );\n }\n return issues;\n }\n\n const seenFields = new Set<string>();\n for (const field of step.fields) {\n if (field === \"\") {\n issues.push(error(\"steps\", `step ${q(name)}: field entry is empty`, name));\n continue;\n }\n if (seenFields.has(field)) {\n issues.push(error(\"steps\", `step ${q(name)}: duplicate field ${q(field)}`, name));\n }\n seenFields.add(field);\n }\n\n const actionNames = new Set<string>();\n for (const action of step.actions) {\n if (action.name === \"\") {\n issues.push(error(\"steps\", `step ${q(name)}: action has empty name`, name));\n continue;\n }\n if (actionNames.has(action.name)) {\n issues.push(error(\"steps\", `step ${q(name)}: duplicate action ${q(action.name)}`, name));\n continue;\n }\n if (action.kind === \"\" || (action.kind !== \"back\" && !DECLARABLE_ACTION_KINDS.has(action.kind))) {\n issues.push(error(\"steps\", `step ${q(name)}: action ${q(action.name)} has no kind`, name));\n } else if (action.kind === \"back\") {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)}: action ${q(action.name)} has kind=back, which is engine-injected and cannot be declared`,\n name,\n ),\n );\n }\n if (action.name === BACK_ACTION_NAME) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)}: action name ${q(action.name)} is reserved for engine-injected back navigation`,\n name,\n ),\n );\n }\n actionNames.add(action.name);\n }\n\n const hasCallback = step.transitions.has(\"callback\");\n if (\n step.fields.length === 0 &&\n step.actions.length === 0 &&\n step.ssoProviderCount === 0 &&\n step.gateCount === 0 &&\n !hasCallback\n ) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)} is non-terminal but has no fields, actions, sso_providers, gates, or transitions.callback`,\n name,\n ),\n );\n }\n\n if (step.ssoProviderCount > 0 && !hasCallback) {\n issues.push(\n error(\"steps\", `step ${q(name)}: has sso_providers but is missing transitions.callback`, name),\n );\n }\n\n for (const actionName of actionNames) {\n if (!step.transitions.has(actionName)) {\n issues.push(\n error(\"steps\", `step ${q(name)}: action ${q(actionName)} has no matching transition`, name),\n );\n }\n }\n\n for (const transitionKey of step.transitions.keys()) {\n if (!actionNames.has(transitionKey) && !(RESERVED_OUTCOMES as readonly string[]).includes(transitionKey)) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)}: transition key ${q(transitionKey)} is not an action name or reserved outcome (user_not_found, user_already_exists, callback)`,\n name,\n ),\n );\n }\n }\n\n return issues;\n}\n\n// ── phase 4: graph / cycles / flip table ─────────────────────────────────────\n\n/** A transition with no cross-flow action targets a step in this flow. */\nfunction isCurrentFlow(t: { action: string | null }): boolean {\n return t.action === null;\n}\n\n/** Adjacency over local (current-flow) transitions: step → target steps. */\nfunction localAdjacency(def: FlowDef): Map<string, string[]> {\n const adj = new Map<string, string[]>();\n for (const step of def.steps) {\n for (const t of step.transitions.values()) {\n if (isCurrentFlow(t)) {\n const targets = adj.get(step.name) ?? [];\n targets.push(t.target);\n adj.set(step.name, targets);\n }\n }\n }\n return adj;\n}\n\n/** Reverse adjacency over local transitions: target step → source steps. */\nfunction reverseAdjacency(def: FlowDef): Map<string, string[]> {\n const reverse = new Map<string, string[]>();\n for (const step of def.steps) {\n for (const t of step.transitions.values()) {\n if (isCurrentFlow(t)) {\n const sources = reverse.get(t.target) ?? [];\n sources.push(step.name);\n reverse.set(t.target, sources);\n }\n }\n }\n return reverse;\n}\n\n/** BFS from `start` over `adj`, accumulating into `visited`. */\nfunction bfs(start: string, adj: Map<string, string[]>, visited: Set<string>): void {\n const queue = [start];\n for (let head = 0; head < queue.length; head += 1) {\n const current = queue[head];\n if (current === undefined || visited.has(current)) continue;\n visited.add(current);\n queue.push(...(adj.get(current) ?? []));\n }\n}\n\nfunction validateGraph(def: FlowDef, stepNames: Set<string>): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n\n for (const step of def.steps) {\n for (const [key, t] of step.transitions) {\n if (isCurrentFlow(t)) {\n if (!stepNames.has(t.target)) {\n issues.push(\n error(\n \"graph\",\n `step ${q(step.name)}: transition ${q(key)} targets unknown step ${q(t.target)}`,\n step.name,\n ),\n );\n }\n } else if (t.action !== \"switch\" && t.action !== \"pivot\") {\n issues.push(\n error(\n \"graph\",\n `step ${q(step.name)}: transition ${q(key)} has invalid action ${q(t.action ?? \"\")}`,\n step.name,\n ),\n );\n }\n }\n }\n\n for (const step of def.steps) {\n if (!step.terminal && step.transitions.size === 0) {\n issues.push(\n error(\"graph\", `step ${q(step.name)} is non-terminal but has no outgoing transitions`, step.name),\n );\n }\n }\n\n const reachable = new Set<string>();\n const adj = localAdjacency(def);\n for (const entryStep of def.purposes.values()) {\n bfs(entryStep, adj, reachable);\n }\n for (const step of def.steps) {\n if (!reachable.has(step.name)) {\n issues.push(error(\"graph\", `step ${q(step.name)} is unreachable from any entry point`, step.name));\n }\n }\n\n return issues;\n}\n\nfunction validateCycles(def: FlowDef): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n const reverse = reverseAdjacency(def);\n\n // Seed: escape nodes (terminal, or a switch/pivot out); BFS backwards.\n const safe = new Set<string>();\n const queue: string[] = [];\n for (const step of def.steps) {\n const escapes =\n step.terminal || [...step.transitions.values()].some((t) => !isCurrentFlow(t));\n if (escapes) {\n safe.add(step.name);\n queue.push(step.name);\n }\n }\n for (let head = 0; head < queue.length; head += 1) {\n const current = queue[head];\n if (current === undefined) continue;\n for (const pred of reverse.get(current) ?? []) {\n if (!safe.has(pred)) {\n safe.add(pred);\n queue.push(pred);\n }\n }\n }\n\n for (const step of def.steps) {\n if (!step.terminal && !safe.has(step.name)) {\n issues.push(\n error(\n \"cycles\",\n `step ${q(step.name)} is trapped: no path to a terminal step or another flow`,\n step.name,\n ),\n );\n }\n }\n return issues;\n}\n\nfunction validateFlipTableCoverage(def: FlowDef): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n for (const [purpose, entryStepName] of def.purposes) {\n const flipTargets = PURPOSE_FLIP_TARGETS[purpose];\n if (flipTargets === undefined) continue;\n const entry = def.steps.find((s) => s.name === entryStepName);\n if (entry === undefined) continue;\n for (const [outcome, targetPurpose] of Object.entries(flipTargets)) {\n if (!def.purposes.has(targetPurpose)) continue;\n if (!entry.transitions.has(outcome)) {\n issues.push(\n error(\n \"flip-table\",\n `step ${q(entry.name)}: entry step for purpose ${q(purpose)} must wire ${q(outcome)} transition because ${q(targetPurpose)} is also a purpose`,\n entry.name,\n ),\n );\n }\n }\n }\n return issues;\n}\n\n// ── phase 5: schema-dependent rules ──────────────────────────────────────────\n\nfunction schemaProperties(schema: Record<string, unknown>): Record<string, unknown> | undefined {\n return isPlainObject(schema.properties) ? schema.properties : undefined;\n}\n\nfunction schemaRequired(schema: Record<string, unknown>): string[] {\n return Array.isArray(schema.required) ? schema.required.map(str).filter((n) => n !== \"\") : [];\n}\n\n/** Mirrors `xAuthMethodsReader.IsEnabled` in flow_field_resolver_schema.go. */\nfunction authMethodEnabled(schema: Record<string, unknown>, method: string): boolean {\n if (!isPlainObject(schema[\"x-auth-methods\"])) return false;\n const entry = schema[\"x-auth-methods\"][method];\n return isPlainObject(entry) && entry.enabled === true;\n}\n\n/**\n * Resolve one step field to its challenge, mirroring the subset of\n * `SchemaFieldResolver.Resolve` the validator needs. Returns either the\n * challenge (or null for none) or the Go-verbatim resolution error.\n */\nfunction resolveFieldChallenge(\n field: string,\n properties: Record<string, unknown>,\n schema: Record<string, unknown>,\n): { challenge: FieldChallenge | null } | { message: string } {\n if (field.startsWith(AUTH_METHOD_PREFIX)) {\n const method = field.slice(AUTH_METHOD_PREFIX.length);\n // Mirrors deriveAuthMethodType: only password is field-shaped today.\n if (method !== \"password\") {\n return { message: `unknown field type for ${q(field)}` };\n }\n if (!authMethodEnabled(schema, method)) {\n // Surfaced by resolveAllStepFields' enabled-method cross-check.\n return { message: `${q(method)} is not an enabled authentication method` };\n }\n return { challenge: \"password\" };\n }\n\n const property = properties[field];\n if (property === undefined) {\n return { message: `flow field: not a property in the user schema: ${q(field)}` };\n }\n if (!isPlainObject(property)) {\n return { challenge: null };\n }\n\n // Mirrors schemaReader.JSONType: the nullable idiom `[\"null\", X]`\n // reduces to X; any other multi-entry union is unsupported.\n const jsonType = property.type;\n if (Array.isArray(jsonType)) {\n const nonNull = jsonType.filter((t) => t !== \"null\");\n if (nonNull.length > 1) {\n return { message: `flow field: unsupported JSON type: [${jsonType.map(str).join(\" \")}]` };\n }\n }\n\n // Mirrors deriveUnique + deriveIdentifierChallenge: any recognized\n // non-empty x-unique scope makes the property an identifier.\n const unique = property[\"x-unique\"];\n if (unique === \"project\" || unique === \"team\") {\n return { challenge: \"identifier\" };\n }\n return { challenge: null };\n}\n\nfunction validateAgainstSchema(def: FlowDef, schema: object): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n const raw = schema as Record<string, unknown>;\n\n const properties = schemaProperties(raw);\n if (properties === undefined) {\n issues.push(error(\"schema/fields-resolve\", \"user schema has no properties\"));\n return issues;\n }\n\n // Required-fields coverage (validateRequiredUserSchemaFields).\n const collected = new Set<string>();\n for (const step of def.steps) {\n for (const field of step.fields) {\n collected.add(field);\n }\n }\n const missing = schemaRequired(raw)\n .filter((field) => !collected.has(field))\n .sort();\n if (missing.length > 0) {\n issues.push(\n error(\n \"schema/required-fields\",\n `required fields [${missing.join(\" \")}] in user schema are missing in the flow definition steps`,\n ),\n );\n }\n\n // Field resolution (resolveAllStepFields), collecting challenges for\n // the manifest and warning rules below.\n const challengesByStep = new Map<string, Set<FieldChallenge>>();\n let resolutionFailed = false;\n for (const step of def.steps) {\n const challenges = new Set<FieldChallenge>();\n for (const field of step.fields) {\n const resolved = resolveFieldChallenge(field, properties, raw);\n if (\"message\" in resolved) {\n issues.push(\n error(\"schema/fields-resolve\", `step ${q(step.name)}: ${resolved.message}`, step.name),\n );\n resolutionFailed = true;\n continue;\n }\n if (resolved.challenge !== null) {\n challenges.add(resolved.challenge);\n }\n }\n challengesByStep.set(step.name, challenges);\n }\n\n // Passkey enablement (validatePasskeyActionsEnabled): passkey is\n // action-shaped, not field-shaped, so the per-field enabled-method\n // check above never sees it.\n if (!authMethodEnabled(raw, \"passkey\")) {\n for (const step of def.steps) {\n for (const action of step.actions) {\n if (action.kind === \"passkey\" || action.kind === \"passkey_register\") {\n issues.push(\n error(\n \"schema/passkey-actions\",\n `step ${q(step.name)}: action ${q(action.name)} offers passkey but \"passkey\" is not an enabled authentication method`,\n step.name,\n ),\n );\n }\n }\n }\n }\n\n if (resolutionFailed) {\n return issues;\n }\n\n // on_success manifests (validateOnSuccessManifests): every kind the\n // mutation needs must be collected on the step or upstream.\n const reverse = reverseAdjacency(def);\n for (const step of def.steps) {\n if (step.onSuccess === undefined) continue;\n const manifest = ON_SUCCESS_MANIFESTS[step.onSuccess];\n if (manifest === undefined) continue;\n const upstream = new Set<string>();\n bfs(step.name, reverse, upstream);\n for (const kind of manifest) {\n const established = [...upstream].some((name) => challengesByStep.get(name)?.has(kind));\n if (!established) {\n issues.push(\n error(\n \"schema/on-success-manifest\",\n `step ${q(step.name)}: on_success ${step.onSuccess} requires ${q(kind)} to be collected upstream`,\n step.name,\n ),\n );\n }\n }\n }\n\n // CLI-only guidance (no Go counterpart): a password collected on the\n // login path with no identifier upstream leaves the engine unable to\n // resolve which user to challenge. Path-sensitive: walk forward from\n // the login entry and stop at any step that collects an identifier\n // (every route through it is covered), so an identifier on a\n // *different* route — e.g. only on the register-purpose chain into a\n // shared step — cannot mask a login route that never collects one.\n const loginEntry = def.purposes.get(\"login\");\n if (loginEntry !== undefined) {\n const adj = localAdjacency(def);\n const identifierFree = new Set<string>();\n const queue = [loginEntry];\n for (let head = 0; head < queue.length; head += 1) {\n const name = queue[head];\n if (name === undefined || identifierFree.has(name)) continue;\n if (challengesByStep.get(name)?.has(\"identifier\")) continue;\n identifierFree.add(name);\n queue.push(...(adj.get(name) ?? []));\n }\n for (const step of def.steps) {\n if (!identifierFree.has(step.name)) continue;\n if (!challengesByStep.get(step.name)?.has(\"password\")) continue;\n issues.push({\n severity: \"warning\",\n rule: \"warn/password-without-identifier\",\n message: `step ${q(step.name)} collects ${AUTH_METHOD_PREFIX}password on the login path but no upstream step collects an identifier field — the engine cannot resolve which user to challenge`,\n step: step.name,\n });\n }\n }\n\n return issues;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validate.mjs","names":[],"sources":["../src/validate.ts"],"sourcesContent":["/**\n * Plan-time port of the server's flow-definition validator\n * (`internal/domain/flow_definition_validator.go`). The CLI runs it in\n * `buildSyncPlan` so every rule the server enforces on apply speaks up\n * at plan time — with the same words — before anything mutates.\n *\n * Contract with the Go source:\n *\n * - Every rule id in {@link FLOW_VALIDATION_RULES} names the Go function\n * it ports (`goRef`); a drift-audit test asserts those functions still\n * exist and that the shared literals below still match.\n * - Error messages mirror the Go detail strings verbatim, so a user who\n * bypasses plan sees identical text from the server.\n * - Unlike the Go validator (fail-fast), this port collects every issue.\n * Later phases are gated on earlier phases being clean so one defect\n * does not cascade into noise (a BFS over duplicate step names is\n * ill-defined).\n *\n * Scope cuts (server/service-side, not portable): pivot/switch targets\n * must name an active flow definition in the same project (needs the\n * DB); the user schema itself is assumed meta-schema-valid.\n *\n * Lifecycle: this port is a local-first interim, not a second source of\n * truth to grow. Its designed successor is the server-side validate-only\n * bundle endpoint (zitadel/nextgen#449); once plan can ask the server,\n * this file, its drift audit, and the keep-in-sync comment on the Go\n * validator all go away. New rules land in Go first and are ported here\n * only until then.\n */\n\nexport type FlowValidationSeverity = \"error\" | \"warning\";\n\nexport type FlowValidationIssue = {\n severity: FlowValidationSeverity;\n /** Stable rule id from {@link FLOW_VALIDATION_RULES}. */\n rule: FlowValidationRuleId;\n /** Mirrors the Go `ErrFlowDefinitionInvalid` detail string where a Go rule exists. */\n message: string;\n /** Step the issue is anchored to, when applicable. */\n step?: string;\n};\n\n/**\n * Rule registry: rule id → the Go function it ports. `goRef: null` marks\n * CLI-only guidance with no server counterpart.\n */\nexport const FLOW_VALIDATION_RULES = {\n \"step-names\": { goRef: \"stepNamesMap\" },\n definition: { goRef: \"validateDefinition\" },\n steps: { goRef: \"validateSteps\" },\n graph: { goRef: \"validateGraph\" },\n cycles: { goRef: \"validateCycles\" },\n \"flip-table\": { goRef: \"validateFlipTableCoverage\" },\n \"schema/required-fields\": { goRef: \"validateRequiredUserSchemaFields\" },\n \"schema/fields-resolve\": { goRef: \"resolveAllStepFields\" },\n \"schema/passkey-actions\": { goRef: \"validatePasskeyActionsEnabled\" },\n \"schema/on-success-manifest\": { goRef: \"validateOnSuccessManifests\" },\n \"warn/password-without-identifier\": { goRef: null },\n} as const;\n\nexport type FlowValidationRuleId = keyof typeof FLOW_VALIDATION_RULES;\n\n/** Mirrors `reservedOutcomes` in flow_definition_validator.go. */\nexport const RESERVED_OUTCOMES = [\"user_not_found\", \"user_already_exists\", \"callback\"] as const;\n\n/** Mirrors the `FlowDefinitionPurpose` enum (snake transform) in flow_definition.go. */\nexport const FLOW_PURPOSES = [\n \"login\",\n \"register\",\n \"recovery\",\n \"profiling\",\n \"reauth\",\n \"link_account\",\n] as const;\n\n/** Mirrors `purposeFlipTargets` in flow_definition_validator.go. */\nexport const PURPOSE_FLIP_TARGETS: Readonly<Record<string, Readonly<Record<string, string>>>> = {\n login: { user_not_found: \"register\" },\n register: { user_already_exists: \"login\" },\n};\n\n/** Mirrors `flipOutcomeImpacts` in flow_definition_validator.go (verbatim). */\nconst FLIP_OUTCOME_IMPACTS: Readonly<Record<string, string>> = {\n user_not_found:\n \"someone without an account gets stuck at sign-in instead of being routed to registration\",\n user_already_exists:\n \"someone who already has an account gets stuck at registration instead of being routed to sign-in\",\n};\n\n/** Action kinds a flow author may declare; `back` is engine-injected. */\nconst DECLARABLE_ACTION_KINDS = new Set([\"submit\", \"passkey\", \"passkey_register\", \"navigate\"]);\n\n/** Mirrors `flowBackActionName` in flow_state_machine.go. */\nconst BACK_ACTION_NAME = \"back\";\n\n/** Mirrors `authMethodPrefix` in flow_definition.go. */\nconst AUTH_METHOD_PREFIX = \"x-auth-methods#\";\n\n/** Mirrors `ManifestForOnSuccess` in flow_on_success.go. */\nconst ON_SUCCESS_MANIFESTS: Readonly<Record<string, readonly FieldChallenge[]>> = {\n create_user: [\"identifier\", \"password\"],\n};\n\ntype FieldChallenge = \"identifier\" | \"password\";\n\n/**\n * Validate a flow-definition config body (the `.zitadel/flows/*.json`\n * content) against the rules the server enforces on apply. `schema` is\n * the content of the user schema the flow's `user_schema` pins; when\n * omitted, schema-dependent rules are skipped. Pure — collects all\n * issues, never throws.\n */\nexport function validateFlowDefinition(flow: object, schema?: object): FlowValidationIssue[] {\n const def = readFlow(flow);\n const issues: FlowValidationIssue[] = [];\n\n // Phase 1 — step names (everything else keys off them).\n const stepNames = new Set<string>();\n for (const step of def.steps) {\n if (stepNames.has(step.name)) {\n issues.push(error(\"step-names\", `duplicate step name ${q(step.name)}`, step.name));\n }\n stepNames.add(step.name);\n }\n\n // Phase 2 — purposes.\n if (def.purposes.size === 0) {\n issues.push(error(\"definition\", \"no purposes defined\"));\n }\n for (const [purpose, entryStep] of def.purposes) {\n if (!(FLOW_PURPOSES as readonly string[]).includes(purpose)) {\n issues.push(error(\"definition\", `'${purpose}' is not a valid purpose`));\n continue;\n }\n if (entryStep === \"\") {\n issues.push(error(\"definition\", `initial step for purpose '${purpose}' is empty`));\n continue;\n }\n if (!stepNames.has(entryStep)) {\n issues.push(\n error(\"definition\", `purpose ${q(purpose)} targets unknown entry-point step ${q(entryStep)}`),\n );\n }\n }\n\n // Phase 3 — per-step structure.\n for (const step of def.steps) {\n issues.push(...validateStep(step));\n }\n\n // Graph phases are ill-defined over broken structure — gate on 1–3.\n if (issues.length === 0) {\n issues.push(...validateGraph(def, stepNames));\n if (issues.length === 0) {\n issues.push(...validateCycles(def));\n }\n issues.push(...validateFlipTableCoverage(def));\n }\n\n // Schema-dependent phases: only meaningful over structurally sound\n // steps, and only when the paired schema content is available.\n if (schema !== undefined && issues.length === 0) {\n issues.push(...validateAgainstSchema(def, schema));\n }\n\n return issues;\n}\n\n// ── internal flow reading ────────────────────────────────────────────────────\n\ntype FlowStep = {\n name: string;\n fields: string[];\n actions: { name: string; kind: string }[];\n gateCount: number;\n ssoProviderCount: number;\n transitions: Map<string, { target: string; action: string | null }>;\n onSuccess: string | undefined;\n terminal: boolean;\n};\n\ntype FlowDef = {\n purposes: Map<string, string>;\n steps: FlowStep[];\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction str(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\n/**\n * Coerce raw parsed JSON into the internal shape. Callers usually run\n * the Zod config schema first; the defensive reads keep the standalone\n * API from crashing on malformed input (it reports rule violations on\n * whatever is readable instead).\n */\nfunction readFlow(flow: object): FlowDef {\n const raw = flow as Record<string, unknown>;\n\n const purposes = new Map<string, string>();\n if (isPlainObject(raw.purposes)) {\n for (const [purpose, entry] of Object.entries(raw.purposes)) {\n purposes.set(purpose, str(entry));\n }\n }\n\n const steps: FlowStep[] = [];\n if (Array.isArray(raw.steps)) {\n for (const value of raw.steps) {\n if (!isPlainObject(value)) continue;\n const transitions = new Map<string, { target: string; action: string | null }>();\n if (isPlainObject(value.transitions)) {\n for (const [key, t] of Object.entries(value.transitions)) {\n const entry = isPlainObject(t) ? t : {};\n transitions.set(key, {\n target: str(entry.target),\n action: typeof entry.action === \"string\" ? entry.action : null,\n });\n }\n }\n steps.push({\n name: str(value.name),\n fields: Array.isArray(value.fields) ? value.fields.map(str) : [],\n actions: Array.isArray(value.actions)\n ? value.actions\n .filter(isPlainObject)\n .map((a) => ({ name: str(a.name), kind: str(a.kind) }))\n : [],\n gateCount: isPlainObject(value.gates) ? Object.keys(value.gates).length : 0,\n ssoProviderCount: Array.isArray(value.sso_providers) ? value.sso_providers.length : 0,\n transitions,\n onSuccess: typeof value.on_success === \"string\" ? value.on_success : undefined,\n terminal: value.complete !== undefined && value.complete !== null,\n });\n }\n }\n\n return { purposes, steps };\n}\n\n/** Go `%q` — double-quoted string. */\nfunction q(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction error(rule: FlowValidationRuleId, message: string, step?: string): FlowValidationIssue {\n return step === undefined\n ? { severity: \"error\", rule, message }\n : { severity: \"error\", rule, message, step };\n}\n\n// ── phase 3: per-step structure (validateSteps + uniqueNonEmptyFields) ──────\n\nfunction validateStep(step: FlowStep): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n const name = step.name;\n\n if (step.terminal) {\n if (\n step.fields.length > 0 ||\n step.actions.length > 0 ||\n step.transitions.size > 0 ||\n step.gateCount > 0 ||\n step.ssoProviderCount > 0\n ) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)} is terminal (complete is set) but has fields, actions, transitions, gates, or sso_providers`,\n name,\n ),\n );\n }\n return issues;\n }\n\n const seenFields = new Set<string>();\n for (const field of step.fields) {\n if (field === \"\") {\n issues.push(error(\"steps\", `step ${q(name)}: field entry is empty`, name));\n continue;\n }\n if (seenFields.has(field)) {\n issues.push(error(\"steps\", `step ${q(name)}: duplicate field ${q(field)}`, name));\n }\n seenFields.add(field);\n }\n\n const actionNames = new Set<string>();\n for (const action of step.actions) {\n if (action.name === \"\") {\n issues.push(error(\"steps\", `step ${q(name)}: action has empty name`, name));\n continue;\n }\n if (actionNames.has(action.name)) {\n issues.push(error(\"steps\", `step ${q(name)}: duplicate action ${q(action.name)}`, name));\n continue;\n }\n if (action.kind === \"\" || (action.kind !== \"back\" && !DECLARABLE_ACTION_KINDS.has(action.kind))) {\n issues.push(error(\"steps\", `step ${q(name)}: action ${q(action.name)} has no kind`, name));\n } else if (action.kind === \"back\") {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)}: action ${q(action.name)} has kind=back, which is engine-injected and cannot be declared`,\n name,\n ),\n );\n }\n if (action.name === BACK_ACTION_NAME) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)}: action name ${q(action.name)} is reserved for engine-injected back navigation`,\n name,\n ),\n );\n }\n actionNames.add(action.name);\n }\n\n const hasCallback = step.transitions.has(\"callback\");\n if (\n step.fields.length === 0 &&\n step.actions.length === 0 &&\n step.ssoProviderCount === 0 &&\n step.gateCount === 0 &&\n !hasCallback\n ) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)} is non-terminal but has no fields, actions, sso_providers, gates, or transitions.callback`,\n name,\n ),\n );\n }\n\n if (step.ssoProviderCount > 0 && !hasCallback) {\n issues.push(\n error(\"steps\", `step ${q(name)}: has sso_providers but is missing transitions.callback`, name),\n );\n }\n\n for (const actionName of actionNames) {\n if (!step.transitions.has(actionName)) {\n issues.push(\n error(\"steps\", `step ${q(name)}: action ${q(actionName)} has no matching transition`, name),\n );\n }\n }\n\n for (const transitionKey of step.transitions.keys()) {\n if (!actionNames.has(transitionKey) && !(RESERVED_OUTCOMES as readonly string[]).includes(transitionKey)) {\n issues.push(\n error(\n \"steps\",\n `step ${q(name)}: transition key ${q(transitionKey)} is not an action name or reserved outcome (user_not_found, user_already_exists, callback)`,\n name,\n ),\n );\n }\n }\n\n return issues;\n}\n\n// ── phase 4: graph / cycles / flip table ─────────────────────────────────────\n\n/** A transition with no cross-flow action targets a step in this flow. */\nfunction isCurrentFlow(t: { action: string | null }): boolean {\n return t.action === null;\n}\n\n/** Adjacency over local (current-flow) transitions: step → target steps. */\nfunction localAdjacency(def: FlowDef): Map<string, string[]> {\n const adj = new Map<string, string[]>();\n for (const step of def.steps) {\n for (const t of step.transitions.values()) {\n if (isCurrentFlow(t)) {\n const targets = adj.get(step.name) ?? [];\n targets.push(t.target);\n adj.set(step.name, targets);\n }\n }\n }\n return adj;\n}\n\n/** Reverse adjacency over local transitions: target step → source steps. */\nfunction reverseAdjacency(def: FlowDef): Map<string, string[]> {\n const reverse = new Map<string, string[]>();\n for (const step of def.steps) {\n for (const t of step.transitions.values()) {\n if (isCurrentFlow(t)) {\n const sources = reverse.get(t.target) ?? [];\n sources.push(step.name);\n reverse.set(t.target, sources);\n }\n }\n }\n return reverse;\n}\n\n/** BFS from `start` over `adj`, accumulating into `visited`. */\nfunction bfs(start: string, adj: Map<string, string[]>, visited: Set<string>): void {\n const queue = [start];\n for (let head = 0; head < queue.length; head += 1) {\n const current = queue[head];\n if (current === undefined || visited.has(current)) continue;\n visited.add(current);\n queue.push(...(adj.get(current) ?? []));\n }\n}\n\nfunction validateGraph(def: FlowDef, stepNames: Set<string>): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n\n for (const step of def.steps) {\n for (const [key, t] of step.transitions) {\n if (isCurrentFlow(t)) {\n if (!stepNames.has(t.target)) {\n issues.push(\n error(\n \"graph\",\n `step ${q(step.name)}: transition ${q(key)} targets unknown step ${q(t.target)}`,\n step.name,\n ),\n );\n }\n } else if (t.action !== \"switch\" && t.action !== \"pivot\") {\n issues.push(\n error(\n \"graph\",\n `step ${q(step.name)}: transition ${q(key)} has invalid action ${q(t.action ?? \"\")}`,\n step.name,\n ),\n );\n }\n }\n }\n\n for (const step of def.steps) {\n if (!step.terminal && step.transitions.size === 0) {\n issues.push(\n error(\"graph\", `step ${q(step.name)} is non-terminal but has no outgoing transitions`, step.name),\n );\n }\n }\n\n const reachable = new Set<string>();\n const adj = localAdjacency(def);\n for (const entryStep of def.purposes.values()) {\n bfs(entryStep, adj, reachable);\n }\n for (const step of def.steps) {\n if (!reachable.has(step.name)) {\n issues.push(error(\"graph\", `step ${q(step.name)} is unreachable from any entry point`, step.name));\n }\n }\n\n return issues;\n}\n\nfunction validateCycles(def: FlowDef): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n const reverse = reverseAdjacency(def);\n\n // Seed: escape nodes (terminal, or a switch/pivot out); BFS backwards.\n const safe = new Set<string>();\n const queue: string[] = [];\n for (const step of def.steps) {\n const escapes =\n step.terminal || [...step.transitions.values()].some((t) => !isCurrentFlow(t));\n if (escapes) {\n safe.add(step.name);\n queue.push(step.name);\n }\n }\n for (let head = 0; head < queue.length; head += 1) {\n const current = queue[head];\n if (current === undefined) continue;\n for (const pred of reverse.get(current) ?? []) {\n if (!safe.has(pred)) {\n safe.add(pred);\n queue.push(pred);\n }\n }\n }\n\n for (const step of def.steps) {\n if (!step.terminal && !safe.has(step.name)) {\n issues.push(\n error(\n \"cycles\",\n `step ${q(step.name)} is trapped: no path to a terminal step or another flow`,\n step.name,\n ),\n );\n }\n }\n return issues;\n}\n\nfunction validateFlipTableCoverage(def: FlowDef): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n for (const [purpose, entryStepName] of def.purposes) {\n const flipTargets = PURPOSE_FLIP_TARGETS[purpose];\n if (flipTargets === undefined) continue;\n const entry = def.steps.find((s) => s.name === entryStepName);\n if (entry === undefined) continue;\n for (const [outcome, targetPurpose] of Object.entries(flipTargets)) {\n if (!def.purposes.has(targetPurpose)) continue;\n if (!entry.transitions.has(outcome)) {\n issues.push(\n error(\n \"flip-table\",\n `step ${q(entry.name)}: entry step for purpose ${q(purpose)} must wire ${q(outcome)} transition because ${q(targetPurpose)} is also a purpose: without it, ${FLIP_OUTCOME_IMPACTS[outcome]}`,\n entry.name,\n ),\n );\n }\n }\n }\n return issues;\n}\n\n// ── phase 5: schema-dependent rules ──────────────────────────────────────────\n\nfunction schemaProperties(schema: Record<string, unknown>): Record<string, unknown> | undefined {\n return isPlainObject(schema.properties) ? schema.properties : undefined;\n}\n\nfunction schemaRequired(schema: Record<string, unknown>): string[] {\n return Array.isArray(schema.required) ? schema.required.map(str).filter((n) => n !== \"\") : [];\n}\n\n/** Mirrors `xAuthMethodsReader.IsEnabled` in flow_field_resolver_schema.go. */\nfunction authMethodEnabled(schema: Record<string, unknown>, method: string): boolean {\n if (!isPlainObject(schema[\"x-auth-methods\"])) return false;\n const entry = schema[\"x-auth-methods\"][method];\n return isPlainObject(entry) && entry.enabled === true;\n}\n\n/**\n * Resolve one step field to its challenge, mirroring the subset of\n * `SchemaFieldResolver.Resolve` the validator needs. Returns either the\n * challenge (or null for none) or the Go-verbatim resolution error.\n */\nfunction resolveFieldChallenge(\n field: string,\n properties: Record<string, unknown>,\n schema: Record<string, unknown>,\n): { challenge: FieldChallenge | null } | { message: string } {\n if (field.startsWith(AUTH_METHOD_PREFIX)) {\n const method = field.slice(AUTH_METHOD_PREFIX.length);\n // Mirrors deriveAuthMethodType: only password is field-shaped today.\n if (method !== \"password\") {\n return { message: `unknown field type for ${q(field)}` };\n }\n if (!authMethodEnabled(schema, method)) {\n // Surfaced by resolveAllStepFields' enabled-method cross-check.\n return { message: `${q(method)} is not an enabled authentication method` };\n }\n return { challenge: \"password\" };\n }\n\n const property = properties[field];\n if (property === undefined) {\n return { message: `flow field: not a property in the user schema: ${q(field)}` };\n }\n if (!isPlainObject(property)) {\n return { challenge: null };\n }\n\n // Mirrors schemaReader.JSONType: the nullable idiom `[\"null\", X]`\n // reduces to X; any other multi-entry union is unsupported.\n const jsonType = property.type;\n if (Array.isArray(jsonType)) {\n const nonNull = jsonType.filter((t) => t !== \"null\");\n if (nonNull.length > 1) {\n return { message: `flow field: unsupported JSON type: [${jsonType.map(str).join(\" \")}]` };\n }\n }\n\n // Mirrors deriveUnique + deriveIdentifierChallenge: any recognized\n // non-empty x-unique scope makes the property an identifier.\n const unique = property[\"x-unique\"];\n if (unique === \"project\" || unique === \"team\") {\n return { challenge: \"identifier\" };\n }\n return { challenge: null };\n}\n\nfunction validateAgainstSchema(def: FlowDef, schema: object): FlowValidationIssue[] {\n const issues: FlowValidationIssue[] = [];\n const raw = schema as Record<string, unknown>;\n\n const properties = schemaProperties(raw);\n if (properties === undefined) {\n issues.push(error(\"schema/fields-resolve\", \"user schema has no properties\"));\n return issues;\n }\n\n // Required-fields coverage (validateRequiredUserSchemaFields).\n const collected = new Set<string>();\n for (const step of def.steps) {\n for (const field of step.fields) {\n collected.add(field);\n }\n }\n const missing = schemaRequired(raw)\n .filter((field) => !collected.has(field))\n .sort();\n if (missing.length > 0) {\n issues.push(\n error(\n \"schema/required-fields\",\n `required fields [${missing.join(\" \")}] in user schema are missing in the flow definition steps`,\n ),\n );\n }\n\n // Field resolution (resolveAllStepFields), collecting challenges for\n // the manifest and warning rules below.\n const challengesByStep = new Map<string, Set<FieldChallenge>>();\n let resolutionFailed = false;\n for (const step of def.steps) {\n const challenges = new Set<FieldChallenge>();\n for (const field of step.fields) {\n const resolved = resolveFieldChallenge(field, properties, raw);\n if (\"message\" in resolved) {\n issues.push(\n error(\"schema/fields-resolve\", `step ${q(step.name)}: ${resolved.message}`, step.name),\n );\n resolutionFailed = true;\n continue;\n }\n if (resolved.challenge !== null) {\n challenges.add(resolved.challenge);\n }\n }\n challengesByStep.set(step.name, challenges);\n }\n\n // Passkey enablement (validatePasskeyActionsEnabled): passkey is\n // action-shaped, not field-shaped, so the per-field enabled-method\n // check above never sees it.\n if (!authMethodEnabled(raw, \"passkey\")) {\n for (const step of def.steps) {\n for (const action of step.actions) {\n if (action.kind === \"passkey\" || action.kind === \"passkey_register\") {\n issues.push(\n error(\n \"schema/passkey-actions\",\n `step ${q(step.name)}: action ${q(action.name)} offers passkey but \"passkey\" is not an enabled authentication method`,\n step.name,\n ),\n );\n }\n }\n }\n }\n\n if (resolutionFailed) {\n return issues;\n }\n\n // on_success manifests (validateOnSuccessManifests): every kind the\n // mutation needs must be collected on the step or upstream.\n const reverse = reverseAdjacency(def);\n for (const step of def.steps) {\n if (step.onSuccess === undefined) continue;\n const manifest = ON_SUCCESS_MANIFESTS[step.onSuccess];\n if (manifest === undefined) continue;\n const upstream = new Set<string>();\n bfs(step.name, reverse, upstream);\n for (const kind of manifest) {\n const established = [...upstream].some((name) => challengesByStep.get(name)?.has(kind));\n if (!established) {\n issues.push(\n error(\n \"schema/on-success-manifest\",\n `step ${q(step.name)}: on_success ${step.onSuccess} requires ${q(kind)} to be collected upstream`,\n step.name,\n ),\n );\n }\n }\n }\n\n // CLI-only guidance (no Go counterpart): a password collected on the\n // login path with no identifier upstream leaves the engine unable to\n // resolve which user to challenge. Path-sensitive: walk forward from\n // the login entry and stop at any step that collects an identifier\n // (every route through it is covered), so an identifier on a\n // *different* route — e.g. only on the register-purpose chain into a\n // shared step — cannot mask a login route that never collects one.\n const loginEntry = def.purposes.get(\"login\");\n if (loginEntry !== undefined) {\n const adj = localAdjacency(def);\n const identifierFree = new Set<string>();\n const queue = [loginEntry];\n for (let head = 0; head < queue.length; head += 1) {\n const name = queue[head];\n if (name === undefined || identifierFree.has(name)) continue;\n if (challengesByStep.get(name)?.has(\"identifier\")) continue;\n identifierFree.add(name);\n queue.push(...(adj.get(name) ?? []));\n }\n for (const step of def.steps) {\n if (!identifierFree.has(step.name)) continue;\n if (!challengesByStep.get(step.name)?.has(\"password\")) continue;\n issues.push({\n severity: \"warning\",\n rule: \"warn/password-without-identifier\",\n message: `step ${q(step.name)} collects ${AUTH_METHOD_PREFIX}password on the login path but no upstream step collects an identifier field — the engine cannot resolve which user to challenge`,\n step: step.name,\n });\n }\n }\n\n return issues;\n}\n"],"mappings":";;;;;AA8CA,MAAa,wBAAwB;CACnC,cAAc,EAAE,OAAO,gBAAgB;CACvC,YAAY,EAAE,OAAO,sBAAsB;CAC3C,OAAO,EAAE,OAAO,iBAAiB;CACjC,OAAO,EAAE,OAAO,iBAAiB;CACjC,QAAQ,EAAE,OAAO,kBAAkB;CACnC,cAAc,EAAE,OAAO,6BAA6B;CACpD,0BAA0B,EAAE,OAAO,oCAAoC;CACvE,yBAAyB,EAAE,OAAO,wBAAwB;CAC1D,0BAA0B,EAAE,OAAO,iCAAiC;CACpE,8BAA8B,EAAE,OAAO,8BAA8B;CACrE,oCAAoC,EAAE,OAAO,MAAM;CACpD;;AAKD,MAAa,oBAAoB;CAAC;CAAkB;CAAuB;CAAW;;AAGtF,MAAa,gBAAgB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACD;;AAGD,MAAa,uBAAmF;CAC9F,OAAO,EAAE,gBAAgB,YAAY;CACrC,UAAU,EAAE,qBAAqB,SAAS;CAC3C;;AAGD,MAAM,uBAAyD;CAC7D,gBACE;CACF,qBACE;CACH;;AAGD,MAAM,0BAA0B,IAAI,IAAI;CAAC;CAAU;CAAW;CAAoB;CAAW,CAAC;;AAG9F,MAAM,mBAAmB;;AAGzB,MAAM,qBAAqB;;AAG3B,MAAM,uBAA4E,EAChF,aAAa,CAAC,cAAc,WAAW,EACxC;;;;;;;;AAWD,SAAgB,uBAAuB,MAAc,QAAwC;CAC3F,MAAM,MAAM,SAAS,KAAK;CAC1B,MAAM,SAAgC,EAAE;CAGxC,MAAM,4BAAY,IAAI,KAAa;AACnC,MAAK,MAAM,QAAQ,IAAI,OAAO;AAC5B,MAAI,UAAU,IAAI,KAAK,KAAK,CAC1B,QAAO,KAAK,MAAM,cAAc,uBAAuB,EAAE,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAEpF,YAAU,IAAI,KAAK,KAAK;;AAI1B,KAAI,IAAI,SAAS,SAAS,EACxB,QAAO,KAAK,MAAM,cAAc,sBAAsB,CAAC;AAEzD,MAAK,MAAM,CAAC,SAAS,cAAc,IAAI,UAAU;AAC/C,MAAI,CAAE,cAAoC,SAAS,QAAQ,EAAE;AAC3D,UAAO,KAAK,MAAM,cAAc,IAAI,QAAQ,0BAA0B,CAAC;AACvE;;AAEF,MAAI,cAAc,IAAI;AACpB,UAAO,KAAK,MAAM,cAAc,6BAA6B,QAAQ,YAAY,CAAC;AAClF;;AAEF,MAAI,CAAC,UAAU,IAAI,UAAU,CAC3B,QAAO,KACL,MAAM,cAAc,WAAW,EAAE,QAAQ,CAAC,oCAAoC,EAAE,UAAU,GAAG,CAC9F;;AAKL,MAAK,MAAM,QAAQ,IAAI,MACrB,QAAO,KAAK,GAAG,aAAa,KAAK,CAAC;AAIpC,KAAI,OAAO,WAAW,GAAG;AACvB,SAAO,KAAK,GAAG,cAAc,KAAK,UAAU,CAAC;AAC7C,MAAI,OAAO,WAAW,EACpB,QAAO,KAAK,GAAG,eAAe,IAAI,CAAC;AAErC,SAAO,KAAK,GAAG,0BAA0B,IAAI,CAAC;;AAKhD,KAAI,WAAW,KAAA,KAAa,OAAO,WAAW,EAC5C,QAAO,KAAK,GAAG,sBAAsB,KAAK,OAAO,CAAC;AAGpD,QAAO;;AAqBT,SAAS,cAAc,OAAkD;AACvE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,IAAI,OAAwB;AACnC,QAAO,OAAO,UAAU,WAAW,QAAQ;;;;;;;;AAS7C,SAAS,SAAS,MAAuB;CACvC,MAAM,MAAM;CAEZ,MAAM,2BAAW,IAAI,KAAqB;AAC1C,KAAI,cAAc,IAAI,SAAS,CAC7B,MAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,IAAI,SAAS,CACzD,UAAS,IAAI,SAAS,IAAI,MAAM,CAAC;CAIrC,MAAM,QAAoB,EAAE;AAC5B,KAAI,MAAM,QAAQ,IAAI,MAAM,CAC1B,MAAK,MAAM,SAAS,IAAI,OAAO;AAC7B,MAAI,CAAC,cAAc,MAAM,CAAE;EAC3B,MAAM,8BAAc,IAAI,KAAwD;AAChF,MAAI,cAAc,MAAM,YAAY,CAClC,MAAK,MAAM,CAAC,KAAK,MAAM,OAAO,QAAQ,MAAM,YAAY,EAAE;GACxD,MAAM,QAAQ,cAAc,EAAE,GAAG,IAAI,EAAE;AACvC,eAAY,IAAI,KAAK;IACnB,QAAQ,IAAI,MAAM,OAAO;IACzB,QAAQ,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;IAC3D,CAAC;;AAGN,QAAM,KAAK;GACT,MAAM,IAAI,MAAM,KAAK;GACrB,QAAQ,MAAM,QAAQ,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,IAAI,GAAG,EAAE;GAChE,SAAS,MAAM,QAAQ,MAAM,QAAQ,GACjC,MAAM,QACH,OAAO,cAAc,CACrB,KAAK,OAAO;IAAE,MAAM,IAAI,EAAE,KAAK;IAAE,MAAM,IAAI,EAAE,KAAK;IAAE,EAAE,GACzD,EAAE;GACN,WAAW,cAAc,MAAM,MAAM,GAAG,OAAO,KAAK,MAAM,MAAM,CAAC,SAAS;GAC1E,kBAAkB,MAAM,QAAQ,MAAM,cAAc,GAAG,MAAM,cAAc,SAAS;GACpF;GACA,WAAW,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa,KAAA;GACrE,UAAU,MAAM,aAAa,KAAA,KAAa,MAAM,aAAa;GAC9D,CAAC;;AAIN,QAAO;EAAE;EAAU;EAAO;;;AAI5B,SAAS,EAAE,OAAuB;AAChC,QAAO,KAAK,UAAU,MAAM;;AAG9B,SAAS,MAAM,MAA4B,SAAiB,MAAoC;AAC9F,QAAO,SAAS,KAAA,IACZ;EAAE,UAAU;EAAS;EAAM;EAAS,GACpC;EAAE,UAAU;EAAS;EAAM;EAAS;EAAM;;AAKhD,SAAS,aAAa,MAAuC;CAC3D,MAAM,SAAgC,EAAE;CACxC,MAAM,OAAO,KAAK;AAElB,KAAI,KAAK,UAAU;AACjB,MACE,KAAK,OAAO,SAAS,KACrB,KAAK,QAAQ,SAAS,KACtB,KAAK,YAAY,OAAO,KACxB,KAAK,YAAY,KACjB,KAAK,mBAAmB,EAExB,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,CAAC,+FAChB,KACD,CACF;AAEH,SAAO;;CAGT,MAAM,6BAAa,IAAI,KAAa;AACpC,MAAK,MAAM,SAAS,KAAK,QAAQ;AAC/B,MAAI,UAAU,IAAI;AAChB,UAAO,KAAK,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,yBAAyB,KAAK,CAAC;AAC1E;;AAEF,MAAI,WAAW,IAAI,MAAM,CACvB,QAAO,KAAK,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,oBAAoB,EAAE,MAAM,IAAI,KAAK,CAAC;AAEnF,aAAW,IAAI,MAAM;;CAGvB,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,MAAM,UAAU,KAAK,SAAS;AACjC,MAAI,OAAO,SAAS,IAAI;AACtB,UAAO,KAAK,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,0BAA0B,KAAK,CAAC;AAC3E;;AAEF,MAAI,YAAY,IAAI,OAAO,KAAK,EAAE;AAChC,UAAO,KAAK,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,qBAAqB,EAAE,OAAO,KAAK,IAAI,KAAK,CAAC;AACxF;;AAEF,MAAI,OAAO,SAAS,MAAO,OAAO,SAAS,UAAU,CAAC,wBAAwB,IAAI,OAAO,KAAK,CAC5F,QAAO,KAAK,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,OAAO,KAAK,CAAC,eAAe,KAAK,CAAC;WACjF,OAAO,SAAS,OACzB,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,OAAO,KAAK,CAAC,kEAC1C,KACD,CACF;AAEH,MAAI,OAAO,SAAS,iBAClB,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,CAAC,gBAAgB,EAAE,OAAO,KAAK,CAAC,mDAC/C,KACD,CACF;AAEH,cAAY,IAAI,OAAO,KAAK;;CAG9B,MAAM,cAAc,KAAK,YAAY,IAAI,WAAW;AACpD,KACE,KAAK,OAAO,WAAW,KACvB,KAAK,QAAQ,WAAW,KACxB,KAAK,qBAAqB,KAC1B,KAAK,cAAc,KACnB,CAAC,YAED,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,CAAC,6FAChB,KACD,CACF;AAGH,KAAI,KAAK,mBAAmB,KAAK,CAAC,YAChC,QAAO,KACL,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,0DAA0D,KAAK,CAC/F;AAGH,MAAK,MAAM,cAAc,YACvB,KAAI,CAAC,KAAK,YAAY,IAAI,WAAW,CACnC,QAAO,KACL,MAAM,SAAS,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,8BAA8B,KAAK,CAC5F;AAIL,MAAK,MAAM,iBAAiB,KAAK,YAAY,MAAM,CACjD,KAAI,CAAC,YAAY,IAAI,cAAc,IAAI,CAAE,kBAAwC,SAAS,cAAc,CACtG,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,CAAC,mBAAmB,EAAE,cAAc,CAAC,6FACpD,KACD,CACF;AAIL,QAAO;;;AAMT,SAAS,cAAc,GAAuC;AAC5D,QAAO,EAAE,WAAW;;;AAItB,SAAS,eAAe,KAAqC;CAC3D,MAAM,sBAAM,IAAI,KAAuB;AACvC,MAAK,MAAM,QAAQ,IAAI,MACrB,MAAK,MAAM,KAAK,KAAK,YAAY,QAAQ,CACvC,KAAI,cAAc,EAAE,EAAE;EACpB,MAAM,UAAU,IAAI,IAAI,KAAK,KAAK,IAAI,EAAE;AACxC,UAAQ,KAAK,EAAE,OAAO;AACtB,MAAI,IAAI,KAAK,MAAM,QAAQ;;AAIjC,QAAO;;;AAIT,SAAS,iBAAiB,KAAqC;CAC7D,MAAM,0BAAU,IAAI,KAAuB;AAC3C,MAAK,MAAM,QAAQ,IAAI,MACrB,MAAK,MAAM,KAAK,KAAK,YAAY,QAAQ,CACvC,KAAI,cAAc,EAAE,EAAE;EACpB,MAAM,UAAU,QAAQ,IAAI,EAAE,OAAO,IAAI,EAAE;AAC3C,UAAQ,KAAK,KAAK,KAAK;AACvB,UAAQ,IAAI,EAAE,QAAQ,QAAQ;;AAIpC,QAAO;;;AAIT,SAAS,IAAI,OAAe,KAA4B,SAA4B;CAClF,MAAM,QAAQ,CAAC,MAAM;AACrB,MAAK,IAAI,OAAO,GAAG,OAAO,MAAM,QAAQ,QAAQ,GAAG;EACjD,MAAM,UAAU,MAAM;AACtB,MAAI,YAAY,KAAA,KAAa,QAAQ,IAAI,QAAQ,CAAE;AACnD,UAAQ,IAAI,QAAQ;AACpB,QAAM,KAAK,GAAI,IAAI,IAAI,QAAQ,IAAI,EAAE,CAAE;;;AAI3C,SAAS,cAAc,KAAc,WAA+C;CAClF,MAAM,SAAgC,EAAE;AAExC,MAAK,MAAM,QAAQ,IAAI,MACrB,MAAK,MAAM,CAAC,KAAK,MAAM,KAAK,YAC1B,KAAI,cAAc,EAAE;MACd,CAAC,UAAU,IAAI,EAAE,OAAO,CAC1B,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,wBAAwB,EAAE,EAAE,OAAO,IAC9E,KAAK,KACN,CACF;YAEM,EAAE,WAAW,YAAY,EAAE,WAAW,QAC/C,QAAO,KACL,MACE,SACA,QAAQ,EAAE,KAAK,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,sBAAsB,EAAE,EAAE,UAAU,GAAG,IAClF,KAAK,KACN,CACF;AAKP,MAAK,MAAM,QAAQ,IAAI,MACrB,KAAI,CAAC,KAAK,YAAY,KAAK,YAAY,SAAS,EAC9C,QAAO,KACL,MAAM,SAAS,QAAQ,EAAE,KAAK,KAAK,CAAC,mDAAmD,KAAK,KAAK,CAClG;CAIL,MAAM,4BAAY,IAAI,KAAa;CACnC,MAAM,MAAM,eAAe,IAAI;AAC/B,MAAK,MAAM,aAAa,IAAI,SAAS,QAAQ,CAC3C,KAAI,WAAW,KAAK,UAAU;AAEhC,MAAK,MAAM,QAAQ,IAAI,MACrB,KAAI,CAAC,UAAU,IAAI,KAAK,KAAK,CAC3B,QAAO,KAAK,MAAM,SAAS,QAAQ,EAAE,KAAK,KAAK,CAAC,uCAAuC,KAAK,KAAK,CAAC;AAItG,QAAO;;AAGT,SAAS,eAAe,KAAqC;CAC3D,MAAM,SAAgC,EAAE;CACxC,MAAM,UAAU,iBAAiB,IAAI;CAGrC,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,QAAQ,IAAI,MAGrB,KADE,KAAK,YAAY,CAAC,GAAG,KAAK,YAAY,QAAQ,CAAC,CAAC,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC,EACnE;AACX,OAAK,IAAI,KAAK,KAAK;AACnB,QAAM,KAAK,KAAK,KAAK;;AAGzB,MAAK,IAAI,OAAO,GAAG,OAAO,MAAM,QAAQ,QAAQ,GAAG;EACjD,MAAM,UAAU,MAAM;AACtB,MAAI,YAAY,KAAA,EAAW;AAC3B,OAAK,MAAM,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE,CAC3C,KAAI,CAAC,KAAK,IAAI,KAAK,EAAE;AACnB,QAAK,IAAI,KAAK;AACd,SAAM,KAAK,KAAK;;;AAKtB,MAAK,MAAM,QAAQ,IAAI,MACrB,KAAI,CAAC,KAAK,YAAY,CAAC,KAAK,IAAI,KAAK,KAAK,CACxC,QAAO,KACL,MACE,UACA,QAAQ,EAAE,KAAK,KAAK,CAAC,0DACrB,KAAK,KACN,CACF;AAGL,QAAO;;AAGT,SAAS,0BAA0B,KAAqC;CACtE,MAAM,SAAgC,EAAE;AACxC,MAAK,MAAM,CAAC,SAAS,kBAAkB,IAAI,UAAU;EACnD,MAAM,cAAc,qBAAqB;AACzC,MAAI,gBAAgB,KAAA,EAAW;EAC/B,MAAM,QAAQ,IAAI,MAAM,MAAM,MAAM,EAAE,SAAS,cAAc;AAC7D,MAAI,UAAU,KAAA,EAAW;AACzB,OAAK,MAAM,CAAC,SAAS,kBAAkB,OAAO,QAAQ,YAAY,EAAE;AAClE,OAAI,CAAC,IAAI,SAAS,IAAI,cAAc,CAAE;AACtC,OAAI,CAAC,MAAM,YAAY,IAAI,QAAQ,CACjC,QAAO,KACL,MACE,cACA,QAAQ,EAAE,MAAM,KAAK,CAAC,2BAA2B,EAAE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,sBAAsB,EAAE,cAAc,CAAC,kCAAkC,qBAAqB,YAClL,MAAM,KACP,CACF;;;AAIP,QAAO;;AAKT,SAAS,iBAAiB,QAAsE;AAC9F,QAAO,cAAc,OAAO,WAAW,GAAG,OAAO,aAAa,KAAA;;AAGhE,SAAS,eAAe,QAA2C;AACjE,QAAO,MAAM,QAAQ,OAAO,SAAS,GAAG,OAAO,SAAS,IAAI,IAAI,CAAC,QAAQ,MAAM,MAAM,GAAG,GAAG,EAAE;;;AAI/F,SAAS,kBAAkB,QAAiC,QAAyB;AACnF,KAAI,CAAC,cAAc,OAAO,kBAAkB,CAAE,QAAO;CACrD,MAAM,QAAQ,OAAO,kBAAkB;AACvC,QAAO,cAAc,MAAM,IAAI,MAAM,YAAY;;;;;;;AAQnD,SAAS,sBACP,OACA,YACA,QAC4D;AAC5D,KAAI,MAAM,WAAW,mBAAmB,EAAE;EACxC,MAAM,SAAS,MAAM,MAAM,GAA0B;AAErD,MAAI,WAAW,WACb,QAAO,EAAE,SAAS,0BAA0B,EAAE,MAAM,IAAI;AAE1D,MAAI,CAAC,kBAAkB,QAAQ,OAAO,CAEpC,QAAO,EAAE,SAAS,GAAG,EAAE,OAAO,CAAC,2CAA2C;AAE5E,SAAO,EAAE,WAAW,YAAY;;CAGlC,MAAM,WAAW,WAAW;AAC5B,KAAI,aAAa,KAAA,EACf,QAAO,EAAE,SAAS,kDAAkD,EAAE,MAAM,IAAI;AAElF,KAAI,CAAC,cAAc,SAAS,CAC1B,QAAO,EAAE,WAAW,MAAM;CAK5B,MAAM,WAAW,SAAS;AAC1B,KAAI,MAAM,QAAQ,SAAS;MACT,SAAS,QAAQ,MAAM,MAAM,OAClC,CAAC,SAAS,EACnB,QAAO,EAAE,SAAS,uCAAuC,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI;;CAM7F,MAAM,SAAS,SAAS;AACxB,KAAI,WAAW,aAAa,WAAW,OACrC,QAAO,EAAE,WAAW,cAAc;AAEpC,QAAO,EAAE,WAAW,MAAM;;AAG5B,SAAS,sBAAsB,KAAc,QAAuC;CAClF,MAAM,SAAgC,EAAE;CACxC,MAAM,MAAM;CAEZ,MAAM,aAAa,iBAAiB,IAAI;AACxC,KAAI,eAAe,KAAA,GAAW;AAC5B,SAAO,KAAK,MAAM,yBAAyB,gCAAgC,CAAC;AAC5E,SAAO;;CAIT,MAAM,4BAAY,IAAI,KAAa;AACnC,MAAK,MAAM,QAAQ,IAAI,MACrB,MAAK,MAAM,SAAS,KAAK,OACvB,WAAU,IAAI,MAAM;CAGxB,MAAM,UAAU,eAAe,IAAI,CAChC,QAAQ,UAAU,CAAC,UAAU,IAAI,MAAM,CAAC,CACxC,MAAM;AACT,KAAI,QAAQ,SAAS,EACnB,QAAO,KACL,MACE,0BACA,oBAAoB,QAAQ,KAAK,IAAI,CAAC,2DACvC,CACF;CAKH,MAAM,mCAAmB,IAAI,KAAkC;CAC/D,IAAI,mBAAmB;AACvB,MAAK,MAAM,QAAQ,IAAI,OAAO;EAC5B,MAAM,6BAAa,IAAI,KAAqB;AAC5C,OAAK,MAAM,SAAS,KAAK,QAAQ;GAC/B,MAAM,WAAW,sBAAsB,OAAO,YAAY,IAAI;AAC9D,OAAI,aAAa,UAAU;AACzB,WAAO,KACL,MAAM,yBAAyB,QAAQ,EAAE,KAAK,KAAK,CAAC,IAAI,SAAS,WAAW,KAAK,KAAK,CACvF;AACD,uBAAmB;AACnB;;AAEF,OAAI,SAAS,cAAc,KACzB,YAAW,IAAI,SAAS,UAAU;;AAGtC,mBAAiB,IAAI,KAAK,MAAM,WAAW;;AAM7C,KAAI,CAAC,kBAAkB,KAAK,UAAU;OAC/B,MAAM,QAAQ,IAAI,MACrB,MAAK,MAAM,UAAU,KAAK,QACxB,KAAI,OAAO,SAAS,aAAa,OAAO,SAAS,mBAC/C,QAAO,KACL,MACE,0BACA,QAAQ,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,OAAO,KAAK,CAAC,wEAC/C,KAAK,KACN,CACF;;AAMT,KAAI,iBACF,QAAO;CAKT,MAAM,UAAU,iBAAiB,IAAI;AACrC,MAAK,MAAM,QAAQ,IAAI,OAAO;AAC5B,MAAI,KAAK,cAAc,KAAA,EAAW;EAClC,MAAM,WAAW,qBAAqB,KAAK;AAC3C,MAAI,aAAa,KAAA,EAAW;EAC5B,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAI,KAAK,MAAM,SAAS,SAAS;AACjC,OAAK,MAAM,QAAQ,SAEjB,KAAI,CADgB,CAAC,GAAG,SAAS,CAAC,MAAM,SAAS,iBAAiB,IAAI,KAAK,EAAE,IAAI,KAAK,CACtE,CACd,QAAO,KACL,MACE,8BACA,QAAQ,EAAE,KAAK,KAAK,CAAC,eAAe,KAAK,UAAU,YAAY,EAAE,KAAK,CAAC,4BACvE,KAAK,KACN,CACF;;CAYP,MAAM,aAAa,IAAI,SAAS,IAAI,QAAQ;AAC5C,KAAI,eAAe,KAAA,GAAW;EAC5B,MAAM,MAAM,eAAe,IAAI;EAC/B,MAAM,iCAAiB,IAAI,KAAa;EACxC,MAAM,QAAQ,CAAC,WAAW;AAC1B,OAAK,IAAI,OAAO,GAAG,OAAO,MAAM,QAAQ,QAAQ,GAAG;GACjD,MAAM,OAAO,MAAM;AACnB,OAAI,SAAS,KAAA,KAAa,eAAe,IAAI,KAAK,CAAE;AACpD,OAAI,iBAAiB,IAAI,KAAK,EAAE,IAAI,aAAa,CAAE;AACnD,kBAAe,IAAI,KAAK;AACxB,SAAM,KAAK,GAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAE;;AAEtC,OAAK,MAAM,QAAQ,IAAI,OAAO;AAC5B,OAAI,CAAC,eAAe,IAAI,KAAK,KAAK,CAAE;AACpC,OAAI,CAAC,iBAAiB,IAAI,KAAK,KAAK,EAAE,IAAI,WAAW,CAAE;AACvD,UAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,SAAS,QAAQ,EAAE,KAAK,KAAK,CAAC,YAAY,mBAAmB;IAC7D,MAAM,KAAK;IACZ,CAAC;;;AAIN,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zitadel/config",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.17",
|
|
4
4
|
"description": "Versioned Zitadel local config schemas and defaults",
|
|
5
5
|
"homepage": "https://github.com/zitadel/nextgen/tree/main/packages/config#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@zitadel/api": "0.1.0-alpha.
|
|
63
|
+
"@zitadel/api": "0.1.0-alpha.17"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"ajv": "^8.20.0",
|