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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +27 -0
  2. package/defaults/README-branding.md +36 -2
  3. package/defaults/README-flows.md +21 -4
  4. package/defaults/README-schemas.md +2 -2
  5. package/defaults/branding/hero/login.liquid +4 -2
  6. package/defaults/branding/split/login.liquid +5 -0
  7. package/defaults/branding/split-right/login.liquid +5 -0
  8. package/defaults/default-human-user.json +2 -4
  9. package/defaults/default-login.json +20 -0
  10. package/defaults/presets/passkey-first/human-user.json +2 -4
  11. package/defaults/presets/passkey-first/login.json +30 -0
  12. package/dist/branding-url.d.mts +14 -0
  13. package/dist/branding-url.d.mts.map +1 -0
  14. package/dist/branding-url.mjs +23 -0
  15. package/dist/branding-url.mjs.map +1 -0
  16. package/dist/{defaults-CXTnFMHj.mjs → defaults-eiGHyI5Y.mjs} +130 -77
  17. package/dist/{defaults-CXTnFMHj.mjs.map → defaults-eiGHyI5Y.mjs.map} +1 -1
  18. package/dist/defaults.mjs +1 -1
  19. package/dist/index.d.mts +2 -1
  20. package/dist/index.mjs +4 -3
  21. package/dist/{meta-schemas-B4YiHAX_.mjs → meta-schemas-CSc18JqO.mjs} +27 -34
  22. package/dist/{meta-schemas-B4YiHAX_.mjs.map → meta-schemas-CSc18JqO.mjs.map} +1 -1
  23. package/dist/meta-schemas.mjs +1 -1
  24. package/dist/normalize.d.mts +11 -9
  25. package/dist/normalize.d.mts.map +1 -1
  26. package/dist/normalize.mjs +24 -17
  27. package/dist/normalize.mjs.map +1 -1
  28. package/dist/schemas.d.mts.map +1 -1
  29. package/dist/schemas.mjs +8 -6
  30. package/dist/schemas.mjs.map +1 -1
  31. package/dist/validate.mjs +70 -11
  32. package/dist/validate.mjs.map +1 -1
  33. package/meta-schemas/auth-method.json +2 -7
  34. package/meta-schemas/branding.json +8 -16
  35. package/meta-schemas/flow-definition.json +20 -1
  36. package/meta-schemas/user-property.json +2 -23
  37. package/package.json +7 -2
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # @zitadel/config
2
+
3
+ Versioned Zitadel local config schemas and defaults — the contract behind the
4
+ `.zitadel/` directory that `zitadel setup` scaffolds into your project.
5
+
6
+ ## What's in here
7
+
8
+ | Import | What it carries |
9
+ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
10
+ | `@zitadel/config/schemas` | Zod schemas for the local config files (`schemaConfigSchema`, `flowConfigSchema`, `brandingConfigSchema`) |
11
+ | `@zitadel/config/branding-url` | Canonical loopback HTTP URL predicate shared by config and browser render gates |
12
+ | `@zitadel/config/validate` | Validation entry points the CLI runs on `plan`/`apply` |
13
+ | `@zitadel/config/normalize` | Normalization used to compare local files against server state |
14
+ | `@zitadel/config/defaults` | The versioned default user schema, login flow, and branding designs |
15
+ | `@zitadel/config/meta-schemas` | The JSON meta-schemas the defaults pin |
16
+ | `@zitadel/config/template` | Liquid template helpers |
17
+ | `@zitadel/config/defaults/default-login.json` | The default login flow — the authority for flow step shape |
18
+ | `@zitadel/config/defaults/default-human-user.json` | The default user schema |
19
+
20
+ ## The customer-facing part
21
+
22
+ `zitadel setup` copies the defaults into your repo as
23
+ `.zitadel/{schemas,flows,branding}/`, each with a README explaining how to
24
+ edit it (`defaults/README-schemas.md`, `README-flows.md`,
25
+ `README-branding.md` in this package become those files). Those files are
26
+ yours to edit; `zitadel plan` / `zitadel apply` reconcile them with the
27
+ server, and edits publish new revisions rather than mutating state in place.
@@ -6,7 +6,7 @@ and asset URLs) plus `login.liquid`, the LiquidJS template the
6
6
 
7
7
  ## Workflow
8
8
 
9
- 1. Edit `login.liquid` (and `branding.json` for logo/font/hero URLs).
9
+ 1. Edit `login.liquid` (and `branding.json` for logo/hero URLs).
10
10
  2. `zitadel plan` — validates the template (LiquidJS parse, banned patterns,
11
11
  the required `{% mandatory_gates %}` tag) and shows the pending revision.
12
12
  3. `zitadel apply` — publishes an immutable branding revision. The login UI
@@ -27,6 +27,40 @@ by re-applying an earlier template.
27
27
 
28
28
  ## Make it yours
29
29
 
30
+ - Brand asset URLs go in `branding.json` and must use `https://`. Each template
31
+ decides which fields it renders: `centered` and `hero` use `logo_url`; `split`
32
+ and `split-right` use both `logo_url` and `hero_url`; `minimal` uses neither
33
+ until you add them to `login.liquid`. The `hero` design's brand pane is
34
+ editable markup rather than a `hero_url` background. Custom fonts are not
35
+ configurable here yet; load them from the embedding page.
36
+
37
+ ```json
38
+ {
39
+ "$schema": "../meta/branding.json",
40
+ "layout": "split",
41
+ "liquid_template_file": "./login.liquid",
42
+ "logo_url": "https://example.com/logo.svg",
43
+ "hero_url": "https://example.com/hero.jpg"
44
+ }
45
+ ```
46
+
47
+ A well-formed URL that serves nothing is the one branding mistake nothing
48
+ else catches — it passes validation, publishes a revision, and then renders
49
+ as an invisible image. So `zitadel plan` probes each asset URL and warns
50
+ (never fails) when it is unreachable, bodyless, or not an image, and the
51
+ login UI hides an asset that fails to load, restoring the shipped design's
52
+ no-asset content rather than leaving a gap. If the host is only
53
+ reachable from where the login page renders — or you are offline — set
54
+ `ZITADEL_SKIP_ASSET_PROBE=1` to skip the check. The probe contacts public
55
+ HTTPS destinations only and re-checks redirects; loopback/private/internal
56
+ targets are left to the browser-side fallback instead of being requested by
57
+ the machine running `plan`.
58
+
59
+ `layout` is the degrade preset (`centered` or `split`), **not** the complete
60
+ design catalog. All named designs (`centered`, `split`, `split-right`, `hero`,
61
+ `minimal`) are delivered as templates and map onto those two values. Switch
62
+ designs with `zitadel branding eject --design <name>`, don't edit `layout`.
63
+
30
64
  - In the split-family designs (`split`, `split-right`, `hero`) the
31
65
  `.zl-split__brand` pane is yours: structural HTML plus inline `style=""`
32
66
  attributes are allowed; `button`, `input`, and `form` tags are stripped —
@@ -38,7 +72,7 @@ by re-applying an earlier template.
38
72
  - The split chrome is tunable from your template root's `style` attribute
39
73
  (`--zl-split-columns`, `--zl-split-align`, `--zl-split-brand-mobile`), and
40
74
  `zl-split--right` on the wrapper mirrors the panes. Knob reference:
41
- `docs/design/branding/templates.md` in the Zitadel repo.
75
+ [`docs/design/branding/templates.md`](https://github.com/zitadel/nextgen/blob/main/docs/design/branding/templates.md).
42
76
  - The "Secured with Zitadel" attribution is licence-gated and on by default.
43
77
  - Back-navigation: the engine injects a `kind: "back"` action on steps
44
78
  that can return to their predecessor, and the browser's back gesture
@@ -37,8 +37,25 @@ Typical edits:
37
37
  tokens).
38
38
  - **Add or remove a step** — extend `steps[]`.
39
39
  - **Rewire transitions** — edit `steps[].transitions` to point at a
40
- different next step, or use `action: switch` / `pivot` to jump to
41
- another flow.
40
+ different next step. Cross-flow jumps (`action: switch` / `pivot`)
41
+ validate in a definition but are **not yet executed by the runtime** —
42
+ submitting through one fails with a 400 (`code: "flow.unsupported"`);
43
+ keep transitions within one flow, using a purpose switch (next bullet)
44
+ for login ↔ register navigation.
45
+ - **Offer a purpose switch in the card** — pair a `kind: navigate`
46
+ action with a transition that declares a local `purpose`. The default
47
+ login flow ships this in both directions:
48
+
49
+ ```json
50
+ "register": { "target": "register", "purpose": "register" }
51
+ ```
52
+
53
+ Navigation skips field validation (an empty email box can't block the
54
+ link), and the declared purpose is what makes the landing leg run with
55
+ the right semantics — register creates the user instead of trying to
56
+ verify one. Rules: the purpose must be one this flow's `purposes`
57
+ serves, the target must be that purpose's entry step, and `purpose`
58
+ never combines with `action` (which targets another flow instead).
42
59
  - **Add another flow** — drop a new JSON file with its own `purposes`
43
60
  and a distinct `name` (e.g. a per-team login). See
44
61
  [Multiple flows](#multiple-flows) for how it gets selected at runtime.
@@ -70,8 +87,8 @@ experiment can't silently take over `/login` — scope it or pin
70
87
 
71
88
  ## Presets
72
89
 
73
- `zitadel setup` scaffolds this folder from a preset (`--preset
74
- password-first` or `--preset passkey-first`). The passkey-first flow
90
+ `zitadel setup` scaffolds this folder from a preset: `password-first` (the
91
+ default root files) or `--preset passkey-first`. The passkey-first flow
75
92
  enters login on a fields-less passkey step with an email → password
76
93
  fallback path. The preset only decides the starting point — edit
77
94
  anything here afterwards.
@@ -28,8 +28,8 @@ Here's a simplified example:
28
28
  "firstName"
29
29
  ],
30
30
  "x-auth-methods": {
31
- "password": { "enabled": true, "position": 1 },
32
- "passkey": { "enabled": true, "position": 2 }
31
+ "password": { "enabled": true },
32
+ "passkey": { "enabled": true }
33
33
  }
34
34
  }
35
35
  ```
@@ -19,7 +19,8 @@
19
19
  <nav class="zl-hero__nav">
20
20
  <span class="zl-hero__brand">
21
21
  {% if branding.logo_url %}
22
- <img class="zl-hero__brand-logo" src="{{ branding.logo_url }}" alt="" />
22
+ <img class="zl-hero__brand-logo" src="{{ branding.logo_url }}" alt="" data-zl-asset-fallback />
23
+ <span class="zl-hero__brand-mark" aria-hidden="true" hidden data-zl-asset-fallback-content></span>
23
24
  {% else %}
24
25
  <span class="zl-hero__brand-mark" aria-hidden="true"></span>
25
26
  {% endif %}
@@ -54,7 +55,8 @@
54
55
  </aside>
55
56
  <section class="zl-split__form">
56
57
  {% if branding.logo_url %}
57
- <img class="zl-split__compact" src="{{ branding.logo_url }}" alt="" />
58
+ <img class="zl-split__compact" src="{{ branding.logo_url }}" alt="" data-zl-asset-fallback />
59
+ <p class="zl-split__compact zl-hero__compact-brand" hidden data-zl-asset-fallback-content>Your brand</p>
58
60
  {% else %}
59
61
  <p class="zl-split__compact zl-hero__compact-brand">Your brand</p>
60
62
  {% endif %}
@@ -7,6 +7,11 @@
7
7
  {% if branding.hero_url %}
8
8
  <img class="zl-split__hero" src="{{ branding.hero_url }}" alt="" />
9
9
  {% endif %}
10
+ {% unless branding.logo_url or branding.hero_url %}
11
+ {% comment %} Until branding.json names an asset, a decorative panel
12
+ keeps the split composition readable. {% endcomment %}
13
+ <div class="zl-split__placeholder" aria-hidden="true"></div>
14
+ {% endunless %}
10
15
  </aside>
11
16
  <section class="zl-split__form">
12
17
  {% if branding.logo_url %}
@@ -7,6 +7,11 @@
7
7
  {% if branding.hero_url %}
8
8
  <img class="zl-split__hero" src="{{ branding.hero_url }}" alt="" />
9
9
  {% endif %}
10
+ {% unless branding.logo_url or branding.hero_url %}
11
+ {% comment %} Until branding.json names an asset, a decorative panel
12
+ keeps the split composition readable. {% endcomment %}
13
+ <div class="zl-split__placeholder" aria-hidden="true"></div>
14
+ {% endunless %}
10
15
  </aside>
11
16
  <section class="zl-split__form">
12
17
  {% if branding.logo_url %}
@@ -9,12 +9,10 @@
9
9
  "description": "The default editable schema for human users.",
10
10
  "x-auth-methods": {
11
11
  "password": {
12
- "enabled": true,
13
- "position": 1
12
+ "enabled": true
14
13
  },
15
14
  "passkey": {
16
- "enabled": true,
17
- "position": 2
15
+ "enabled": true
18
16
  }
19
17
  },
20
18
  "required": [
@@ -25,6 +25,12 @@
25
25
  "kind": "passkey",
26
26
  "primary": false,
27
27
  "text_key": "identifier.action.passkey"
28
+ },
29
+ {
30
+ "name": "register",
31
+ "kind": "navigate",
32
+ "primary": false,
33
+ "text_key": "identifier.action.register.link"
28
34
  }
29
35
  ],
30
36
  "transitions": {
@@ -36,6 +42,10 @@
36
42
  },
37
43
  "user_not_found": {
38
44
  "target": "register"
45
+ },
46
+ "register": {
47
+ "target": "register",
48
+ "purpose": "register"
39
49
  }
40
50
  }
41
51
  },
@@ -84,6 +94,12 @@
84
94
  "kind": "passkey_register",
85
95
  "primary": false,
86
96
  "text_key": "register.action.passkey"
97
+ },
98
+ {
99
+ "name": "sign_in",
100
+ "kind": "navigate",
101
+ "primary": false,
102
+ "text_key": "register.action.sign_in.link"
87
103
  }
88
104
  ],
89
105
  "transitions": {
@@ -95,6 +111,10 @@
95
111
  },
96
112
  "user_already_exists": {
97
113
  "target": "password"
114
+ },
115
+ "sign_in": {
116
+ "target": "identifier",
117
+ "purpose": "login"
98
118
  }
99
119
  }
100
120
  },
@@ -9,12 +9,10 @@
9
9
  "description": "The default editable schema for human users (passkey-first sign-in).",
10
10
  "x-auth-methods": {
11
11
  "passkey": {
12
- "enabled": true,
13
- "position": 1
12
+ "enabled": true
14
13
  },
15
14
  "password": {
16
- "enabled": true,
17
- "position": 2
15
+ "enabled": true
18
16
  }
19
17
  },
20
18
  "required": [
@@ -23,6 +23,12 @@
23
23
  "kind": "navigate",
24
24
  "primary": false,
25
25
  "text_key": "passkey-first.action.email_fallback"
26
+ },
27
+ {
28
+ "name": "register",
29
+ "kind": "navigate",
30
+ "primary": false,
31
+ "text_key": "identifier.action.register.link"
26
32
  }
27
33
  ],
28
34
  "transitions": {
@@ -34,6 +40,10 @@
34
40
  },
35
41
  "user_not_found": {
36
42
  "target": "register"
43
+ },
44
+ "register": {
45
+ "target": "register",
46
+ "purpose": "register"
37
47
  }
38
48
  }
39
49
  },
@@ -54,6 +64,12 @@
54
64
  "kind": "passkey",
55
65
  "primary": false,
56
66
  "text_key": "identifier.action.passkey"
67
+ },
68
+ {
69
+ "name": "register",
70
+ "kind": "navigate",
71
+ "primary": false,
72
+ "text_key": "identifier.action.register.link"
57
73
  }
58
74
  ],
59
75
  "transitions": {
@@ -65,6 +81,10 @@
65
81
  },
66
82
  "user_not_found": {
67
83
  "target": "register"
84
+ },
85
+ "register": {
86
+ "target": "register",
87
+ "purpose": "register"
68
88
  }
69
89
  }
70
90
  },
@@ -113,6 +133,12 @@
113
133
  "kind": "submit",
114
134
  "primary": false,
115
135
  "text_key": "register.action.password"
136
+ },
137
+ {
138
+ "name": "sign_in",
139
+ "kind": "navigate",
140
+ "primary": false,
141
+ "text_key": "register.action.sign_in.link"
116
142
  }
117
143
  ],
118
144
  "transitions": {
@@ -124,6 +150,10 @@
124
150
  },
125
151
  "user_already_exists": {
126
152
  "target": "password"
153
+ },
154
+ "sign_in": {
155
+ "target": "passkey-first",
156
+ "purpose": "login"
127
157
  }
128
158
  }
129
159
  },
@@ -0,0 +1,14 @@
1
+ //#region src/branding-url.d.ts
2
+ /**
3
+ * Reports whether value is an absolute HTTP URL using one of the canonical
4
+ * loopback spellings shared by the config and component gates.
5
+ *
6
+ * The raw-pattern check deliberately runs before WHATWG URL normalisation so
7
+ * shorthand, integer, hexadecimal, leading-zero, and userinfo forms cannot
8
+ * become a loopback host in one JavaScript layer while the Go save gate rejects
9
+ * them.
10
+ */
11
+ declare function isCanonicalLoopbackHttpUrl(value: string): boolean;
12
+ //#endregion
13
+ export { isCanonicalLoopbackHttpUrl };
14
+ //# sourceMappingURL=branding-url.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branding-url.d.mts","names":[],"sources":["../src/branding-url.ts"],"mappings":";;AAYA;;;;;;;;iBAAgB,0BAAA,CAA2B,KAAA"}
@@ -0,0 +1,23 @@
1
+ //#region src/branding-url.ts
2
+ const CANONICAL_LOOPBACK_HTTP_URL = /^http:\/\/(?:localhost|127\.(?:0|[1-9][0-9]{0,2})\.(?:0|[1-9][0-9]{0,2})\.(?:0|[1-9][0-9]{0,2})|\[::1\])(?::[0-9]+)?(?:[/?#]|$)/i;
3
+ /**
4
+ * Reports whether value is an absolute HTTP URL using one of the canonical
5
+ * loopback spellings shared by the config and component gates.
6
+ *
7
+ * The raw-pattern check deliberately runs before WHATWG URL normalisation so
8
+ * shorthand, integer, hexadecimal, leading-zero, and userinfo forms cannot
9
+ * become a loopback host in one JavaScript layer while the Go save gate rejects
10
+ * them.
11
+ */
12
+ function isCanonicalLoopbackHttpUrl(value) {
13
+ if (!CANONICAL_LOOPBACK_HTTP_URL.test(value)) return false;
14
+ try {
15
+ return new URL(value).protocol === "http:";
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+ //#endregion
21
+ export { isCanonicalLoopbackHttpUrl };
22
+
23
+ //# sourceMappingURL=branding-url.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branding-url.mjs","names":[],"sources":["../src/branding-url.ts"],"sourcesContent":["const CANONICAL_LOOPBACK_HTTP_URL =\n /^http:\\/\\/(?:localhost|127\\.(?:0|[1-9][0-9]{0,2})\\.(?:0|[1-9][0-9]{0,2})\\.(?:0|[1-9][0-9]{0,2})|\\[::1\\])(?::[0-9]+)?(?:[/?#]|$)/i;\n\n/**\n * Reports whether value is an absolute HTTP URL using one of the canonical\n * loopback spellings shared by the config and component gates.\n *\n * The raw-pattern check deliberately runs before WHATWG URL normalisation so\n * shorthand, integer, hexadecimal, leading-zero, and userinfo forms cannot\n * become a loopback host in one JavaScript layer while the Go save gate rejects\n * them.\n */\nexport function isCanonicalLoopbackHttpUrl(value: string): boolean {\n if (!CANONICAL_LOOPBACK_HTTP_URL.test(value)) return false;\n try {\n return new URL(value).protocol === \"http:\";\n } catch {\n return false;\n }\n}\n"],"mappings":";AAAA,MAAM,8BACJ;;;;;;;;;;AAWF,SAAgB,2BAA2B,OAAwB;AACjE,KAAI,CAAC,4BAA4B,KAAK,MAAM,CAAE,QAAO;AACrD,KAAI;AACF,SAAO,IAAI,IAAI,MAAM,CAAC,aAAa;SAC7B;AACN,SAAO"}
@@ -11,14 +11,8 @@ var default_human_user_default = {
11
11
  type: "object",
12
12
  description: "The default editable schema for human users.",
13
13
  "x-auth-methods": {
14
- "password": {
15
- "enabled": true,
16
- "position": 1
17
- },
18
- "passkey": {
19
- "enabled": true,
20
- "position": 2
21
- }
14
+ "password": { "enabled": true },
15
+ "passkey": { "enabled": true }
22
16
  },
23
17
  required: ["email"],
24
18
  properties: { "email": {
@@ -43,21 +37,34 @@ var default_login_default = {
43
37
  {
44
38
  "name": "identifier",
45
39
  "fields": ["email"],
46
- "actions": [{
47
- "name": "submit",
48
- "kind": "submit",
49
- "primary": true,
50
- "text_key": "identifier.action.continue"
51
- }, {
52
- "name": "passkey",
53
- "kind": "passkey",
54
- "primary": false,
55
- "text_key": "identifier.action.passkey"
56
- }],
40
+ "actions": [
41
+ {
42
+ "name": "submit",
43
+ "kind": "submit",
44
+ "primary": true,
45
+ "text_key": "identifier.action.continue"
46
+ },
47
+ {
48
+ "name": "passkey",
49
+ "kind": "passkey",
50
+ "primary": false,
51
+ "text_key": "identifier.action.passkey"
52
+ },
53
+ {
54
+ "name": "register",
55
+ "kind": "navigate",
56
+ "primary": false,
57
+ "text_key": "identifier.action.register.link"
58
+ }
59
+ ],
57
60
  "transitions": {
58
61
  "submit": { "target": "password" },
59
62
  "passkey": { "target": "done" },
60
- "user_not_found": { "target": "register" }
63
+ "user_not_found": { "target": "register" },
64
+ "register": {
65
+ "target": "register",
66
+ "purpose": "register"
67
+ }
61
68
  }
62
69
  },
63
70
  {
@@ -82,21 +89,34 @@ var default_login_default = {
82
89
  {
83
90
  "name": "register",
84
91
  "fields": ["email"],
85
- "actions": [{
86
- "name": "submit",
87
- "kind": "submit",
88
- "primary": true,
89
- "text_key": "register.action.password"
90
- }, {
91
- "name": "passkey_register",
92
- "kind": "passkey_register",
93
- "primary": false,
94
- "text_key": "register.action.passkey"
95
- }],
92
+ "actions": [
93
+ {
94
+ "name": "submit",
95
+ "kind": "submit",
96
+ "primary": true,
97
+ "text_key": "register.action.password"
98
+ },
99
+ {
100
+ "name": "passkey_register",
101
+ "kind": "passkey_register",
102
+ "primary": false,
103
+ "text_key": "register.action.passkey"
104
+ },
105
+ {
106
+ "name": "sign_in",
107
+ "kind": "navigate",
108
+ "primary": false,
109
+ "text_key": "register.action.sign_in.link"
110
+ }
111
+ ],
96
112
  "transitions": {
97
113
  "submit": { "target": "register-password" },
98
114
  "passkey_register": { "target": "done" },
99
- "user_already_exists": { "target": "password" }
115
+ "user_already_exists": { "target": "password" },
116
+ "sign_in": {
117
+ "target": "identifier",
118
+ "purpose": "login"
119
+ }
100
120
  }
101
121
  },
102
122
  {
@@ -132,14 +152,8 @@ var human_user_default = {
132
152
  type: "object",
133
153
  description: "The default editable schema for human users (passkey-first sign-in).",
134
154
  "x-auth-methods": {
135
- "passkey": {
136
- "enabled": true,
137
- "position": 1
138
- },
139
- "password": {
140
- "enabled": true,
141
- "position": 2
142
- }
155
+ "passkey": { "enabled": true },
156
+ "password": { "enabled": true }
143
157
  },
144
158
  required: ["email"],
145
159
  properties: { "email": {
@@ -164,41 +178,67 @@ var login_default = {
164
178
  {
165
179
  "name": "passkey-first",
166
180
  "fields": [],
167
- "actions": [{
168
- "name": "passkey",
169
- "kind": "passkey",
170
- "primary": true,
171
- "text_key": "passkey-first.action.passkey"
172
- }, {
173
- "name": "email_fallback",
174
- "kind": "navigate",
175
- "primary": false,
176
- "text_key": "passkey-first.action.email_fallback"
177
- }],
181
+ "actions": [
182
+ {
183
+ "name": "passkey",
184
+ "kind": "passkey",
185
+ "primary": true,
186
+ "text_key": "passkey-first.action.passkey"
187
+ },
188
+ {
189
+ "name": "email_fallback",
190
+ "kind": "navigate",
191
+ "primary": false,
192
+ "text_key": "passkey-first.action.email_fallback"
193
+ },
194
+ {
195
+ "name": "register",
196
+ "kind": "navigate",
197
+ "primary": false,
198
+ "text_key": "identifier.action.register.link"
199
+ }
200
+ ],
178
201
  "transitions": {
179
202
  "passkey": { "target": "done" },
180
203
  "email_fallback": { "target": "identifier" },
181
- "user_not_found": { "target": "register" }
204
+ "user_not_found": { "target": "register" },
205
+ "register": {
206
+ "target": "register",
207
+ "purpose": "register"
208
+ }
182
209
  }
183
210
  },
184
211
  {
185
212
  "name": "identifier",
186
213
  "fields": ["email"],
187
- "actions": [{
188
- "name": "submit",
189
- "kind": "submit",
190
- "primary": true,
191
- "text_key": "identifier.action.continue"
192
- }, {
193
- "name": "passkey",
194
- "kind": "passkey",
195
- "primary": false,
196
- "text_key": "identifier.action.passkey"
197
- }],
214
+ "actions": [
215
+ {
216
+ "name": "submit",
217
+ "kind": "submit",
218
+ "primary": true,
219
+ "text_key": "identifier.action.continue"
220
+ },
221
+ {
222
+ "name": "passkey",
223
+ "kind": "passkey",
224
+ "primary": false,
225
+ "text_key": "identifier.action.passkey"
226
+ },
227
+ {
228
+ "name": "register",
229
+ "kind": "navigate",
230
+ "primary": false,
231
+ "text_key": "identifier.action.register.link"
232
+ }
233
+ ],
198
234
  "transitions": {
199
235
  "submit": { "target": "password" },
200
236
  "passkey": { "target": "done" },
201
- "user_not_found": { "target": "register" }
237
+ "user_not_found": { "target": "register" },
238
+ "register": {
239
+ "target": "register",
240
+ "purpose": "register"
241
+ }
202
242
  }
203
243
  },
204
244
  {
@@ -223,21 +263,34 @@ var login_default = {
223
263
  {
224
264
  "name": "register",
225
265
  "fields": ["email"],
226
- "actions": [{
227
- "name": "passkey_register",
228
- "kind": "passkey_register",
229
- "primary": true,
230
- "text_key": "register.action.passkey"
231
- }, {
232
- "name": "submit",
233
- "kind": "submit",
234
- "primary": false,
235
- "text_key": "register.action.password"
236
- }],
266
+ "actions": [
267
+ {
268
+ "name": "passkey_register",
269
+ "kind": "passkey_register",
270
+ "primary": true,
271
+ "text_key": "register.action.passkey"
272
+ },
273
+ {
274
+ "name": "submit",
275
+ "kind": "submit",
276
+ "primary": false,
277
+ "text_key": "register.action.password"
278
+ },
279
+ {
280
+ "name": "sign_in",
281
+ "kind": "navigate",
282
+ "primary": false,
283
+ "text_key": "register.action.sign_in.link"
284
+ }
285
+ ],
237
286
  "transitions": {
238
287
  "passkey_register": { "target": "done" },
239
288
  "submit": { "target": "register-password" },
240
- "user_already_exists": { "target": "password" }
289
+ "user_already_exists": { "target": "password" },
290
+ "sign_in": {
291
+ "target": "passkey-first",
292
+ "purpose": "login"
293
+ }
241
294
  }
242
295
  },
243
296
  {
@@ -513,4 +566,4 @@ function trimTrailingSlash(value) {
513
566
  //#endregion
514
567
  export { brandingReadmeContent as _, DEFAULT_BUILTIN_SCHEMA_BASE as a, DEFAULT_SCHEMA_CONFIG_PATH as c, SETUP_PRESETS as d, SETUP_USE_CASES as f, getDefaultLoginFlow as g, getDefaultHumanUserSchema as h, DEFAULT_BRANDING_TEMPLATE_PATH as i, DEFAULT_SETUP_PRESET as l, getDefaultBrandingConfig as m, DEFAULT_BRANDING_CONFIG_PATH as n, DEFAULT_FLOW_CONFIG_PATH as o, defaultHumanUserSchemaUrl as p, DEFAULT_BRANDING_DESIGN as r, DEFAULT_FLOW_SCHEMA_URI as s, BRANDING_DESIGNS as t, DEFAULT_SETUP_USE_CASE as u, flowsReadmeContent as v, schemasReadmeContent as y };
515
568
 
516
- //# sourceMappingURL=defaults-CXTnFMHj.mjs.map
569
+ //# sourceMappingURL=defaults-eiGHyI5Y.mjs.map