@zitadel/cli 0.1.0-alpha.8 → 1.0.0-alpha.20
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 +250 -62
- package/SKILLS.md +227 -38
- package/dist/branding-cygSBPkV.mjs +79 -0
- package/dist/branding-cygSBPkV.mjs.map +1 -0
- package/dist/claim-state-DlfD6Res.mjs +66 -0
- package/dist/claim-state-DlfD6Res.mjs.map +1 -0
- package/dist/commands/apply.mjs +27 -8
- package/dist/commands/apply.mjs.map +1 -1
- package/dist/commands/branding/eject.mjs +104 -0
- package/dist/commands/branding/eject.mjs.map +1 -0
- package/dist/commands/claim.mjs +402 -0
- package/dist/commands/claim.mjs.map +1 -0
- package/dist/commands/doctor.mjs +687 -55
- package/dist/commands/doctor.mjs.map +1 -1
- package/dist/commands/eject.mjs +18 -6
- package/dist/commands/eject.mjs.map +1 -1
- package/dist/commands/logs.mjs +2 -2
- package/dist/commands/plan.mjs +19 -6
- package/dist/commands/plan.mjs.map +1 -1
- package/dist/commands/reset.mjs +12 -4
- package/dist/commands/reset.mjs.map +1 -1
- package/dist/commands/schemas/list.mjs +146 -0
- package/dist/commands/schemas/list.mjs.map +1 -0
- package/dist/commands/setup.mjs +642 -287
- package/dist/commands/setup.mjs.map +1 -1
- package/dist/commands/start.mjs +73 -6
- package/dist/commands/start.mjs.map +1 -1
- package/dist/commands/status.mjs +52 -13
- package/dist/commands/status.mjs.map +1 -1
- package/dist/commands/stop.mjs +59 -6
- package/dist/commands/stop.mjs.map +1 -1
- package/dist/designs-Ckz18Dpo.mjs +38 -0
- package/dist/designs-Ckz18Dpo.mjs.map +1 -0
- package/dist/{docker-BA78SdC2.mjs → docker-DcRGTOJa.mjs} +61 -11
- package/dist/docker-DcRGTOJa.mjs.map +1 -0
- package/dist/environment-rjRVkJjW.mjs +17 -0
- package/dist/environment-rjRVkJjW.mjs.map +1 -0
- package/dist/journey-guidance-BGrOX_gT.mjs +40 -0
- package/dist/journey-guidance-BGrOX_gT.mjs.map +1 -0
- package/dist/oclif-CanO3zdt.mjs +1769 -0
- package/dist/oclif-CanO3zdt.mjs.map +1 -0
- package/dist/{orca-CfKDQRop.mjs → orca-BzQIQqzt.mjs} +1663 -303
- package/dist/orca-BzQIQqzt.mjs.map +1 -0
- package/dist/package-manager-DFdLcDx1.mjs +194 -0
- package/dist/package-manager-DFdLcDx1.mjs.map +1 -0
- package/dist/ports-BM20XIZb.mjs +116 -0
- package/dist/ports-BM20XIZb.mjs.map +1 -0
- package/dist/processes-DKHPkU8O.mjs +120 -0
- package/dist/processes-DKHPkU8O.mjs.map +1 -0
- package/dist/{project-CKAHtHML.mjs → project-CHhot85s.mjs} +57 -4
- package/dist/project-CHhot85s.mjs.map +1 -0
- package/dist/sync-BBlACKzT.mjs +1633 -0
- package/dist/sync-BBlACKzT.mjs.map +1 -0
- package/dist/user-schema-DTuOsdKE.mjs +91 -0
- package/dist/user-schema-DTuOsdKE.mjs.map +1 -0
- package/oclif.manifest.json +412 -5
- package/package.json +10 -5
- package/dist/docker-BA78SdC2.mjs.map +0 -1
- package/dist/docker-guidance-BvfpmsDj.mjs +0 -21
- package/dist/docker-guidance-BvfpmsDj.mjs.map +0 -1
- package/dist/oclif-VkCTGIEk.mjs +0 -818
- package/dist/oclif-VkCTGIEk.mjs.map +0 -1
- package/dist/orca-CfKDQRop.mjs.map +0 -1
- package/dist/project-CKAHtHML.mjs.map +0 -1
- package/dist/sync-B5lqgQO3.mjs +0 -733
- package/dist/sync-B5lqgQO3.mjs.map +0 -1
package/SKILLS.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Set up and manage Zitadel authentication in a local project with the
|
|
5
5
|
agent-friendly `zitadel` CLI. Use when the user wants to add login,
|
|
6
6
|
registration, or session handling, create a Zitadel project, scaffold auth
|
|
7
|
-
for a Next.js, React, Vue, Angular, or
|
|
7
|
+
for a Next.js, React, Vue, Angular, Nuxt, Solid, Svelte, or Qwik app, or plan and apply Zitadel
|
|
8
8
|
config changes from repo state.
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -20,6 +20,10 @@ parse the result rather than scraping human output.
|
|
|
20
20
|
- Always pass `--non-interactive --json`. The envelope is the contract.
|
|
21
21
|
- Add `--cwd <path>` when operating outside the current working directory.
|
|
22
22
|
- Never run interactive prompts; `--non-interactive` (and `--json`) disable them.
|
|
23
|
+
- The CLI sends anonymous usage telemetry by default. For automated/agent runs
|
|
24
|
+
that should stay silent, disable it with `--no-telemetry` (per invocation) or
|
|
25
|
+
`ZITADEL_TELEMETRY=0` / `DO_NOT_TRACK=1` (per environment); this also skips the
|
|
26
|
+
small end-of-command network flush.
|
|
23
27
|
- See `README.md` (its commands section is generated from the CLI's own
|
|
24
28
|
metadata) or run `zitadel <command> --help` for the full per-command flag list.
|
|
25
29
|
|
|
@@ -39,43 +43,203 @@ Each invocation prints one JSON object:
|
|
|
39
43
|
- `cli_version`, `command`, `source`: always present.
|
|
40
44
|
- On success: `data` with the command-specific payload.
|
|
41
45
|
- On a no-op: `reason` (e.g. `no-framework-detected`, `orphaned-config`).
|
|
42
|
-
- On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `
|
|
43
|
-
`message`.
|
|
46
|
+
- On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `E_NOT_FOUND`,
|
|
47
|
+
`E_CONFLICT`) and `message`.
|
|
44
48
|
- `next_commands`: the suggested follow-ups. Prefer these over free-text hints.
|
|
49
|
+
- `plan` and `apply` also emit `data.changes`: one row per touched resource
|
|
50
|
+
(`{kind, action, file, id?, previous_id?}`, action ∈ create/update/revision/
|
|
51
|
+
delete). Plan rows preview; apply rows report, with the resulting platform
|
|
52
|
+
ids. Use it to verify an edit did what you intended — `apply`'s
|
|
53
|
+
`files_updated` lists only local write-backs, not platform changes.
|
|
54
|
+
- `setup` emits `data.files`: one typed row per scaffolded artifact
|
|
55
|
+
(`{path, kind: file|dir, action: create|update}`), deduplicated. Use it to
|
|
56
|
+
see what setup created versus merged into (your `package.json` is an
|
|
57
|
+
`update`). `data.files_written` remains the flat list — deduplicated file
|
|
58
|
+
paths only, covering both scaffolded and `.zitadel/` resource files.
|
|
59
|
+
- `setup` also emits `data.design`: the starter login design it ejected and
|
|
60
|
+
published as branding revision 1, or `null` when the built-in template was
|
|
61
|
+
kept (no `.zitadel/branding/` files exist in that case). Use it to verify
|
|
62
|
+
the requested `--design` took effect without diffing the repo.
|
|
45
63
|
- `E_LOCAL_SERVER_NOT_RUNNING`: start the local runtime with
|
|
46
64
|
`npx @zitadel/cli@alpha start`, then retry with `--server local`.
|
|
65
|
+
- `E_NOT_FOUND`: an HTTP 404 from the target server. With the platform's
|
|
66
|
+
error envelope it names a missing resource (e.g. an unknown schema id);
|
|
67
|
+
without one the endpoint itself is missing — the `--server` value likely
|
|
68
|
+
points at something that is not a Zitadel platform API. Follow
|
|
69
|
+
`next_commands` (usually `start` + retry with `--server local`).
|
|
70
|
+
- `E_PORT_IN_USE`: the requested local runtime port already has a listener.
|
|
71
|
+
Stop that process, run `npx @zitadel/cli@alpha stop --all` for host-wide
|
|
72
|
+
CLI-managed local runtimes, or choose another `start --port`.
|
|
47
73
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
Capture stdout and stderr separately when scripting. Some terminals and agent
|
|
75
|
+
UIs display both streams together, but the machine contract is one parseable
|
|
76
|
+
JSON object on stdout; installer, audit, and package-manager progress belongs
|
|
77
|
+
on stderr.
|
|
78
|
+
|
|
79
|
+
Exit codes mirror the error class (3 = validation, 4 = network or not-found,
|
|
80
|
+
5 = conflict, 1 = auth, 2 = not-implemented). An unknown command is handled by
|
|
81
|
+
the CLI's help layer, not the envelope.
|
|
51
82
|
|
|
52
83
|
## Commands
|
|
53
84
|
|
|
54
85
|
- `setup` — create a Zitadel project and scaffold local auth (routes,
|
|
55
|
-
middleware, `.zitadel/**`, env templates).
|
|
56
|
-
and login flow
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
middleware, `.zitadel/**`, env templates). Setup writes the versioned local
|
|
87
|
+
default user schema and login flow into
|
|
88
|
+
`.zitadel/schemas/default-human-user.json` and
|
|
89
|
+
`.zitadel/flows/default-login.json`, uploads them through the schema and flow
|
|
90
|
+
APIs, then seeds `.zitadel/state.json` so `plan` is immediately empty. Agents
|
|
91
|
+
must pass `--framework` when scaffolding into a fresh directory; interactive
|
|
92
|
+
humans can omit it and choose from the prompt. Supported floors: Next.js 15+
|
|
93
|
+
and React 18+ — `setup` and `doctor` fail with `E_UNSUPPORTED_PROJECT_SHAPE`
|
|
94
|
+
below them instead of degrading silently (an unparseable version passes).
|
|
95
|
+
Flags:
|
|
96
|
+
`--framework next|react|vue|angular|nuxt|solid|svelte|qwik`, `--renderer
|
|
97
|
+
react` (selects the Next.js auth-page renderer; accepted for any framework
|
|
98
|
+
and recorded in `zitadel.json` branding, but only Next varies its generated
|
|
99
|
+
templates by it; the planned `web-component` renderer is not yet available
|
|
100
|
+
and is rejected if passed), `--dev-port` (dev-server port, also the issuer
|
|
63
101
|
origin registered with Zitadel — use distinct ports to run several scaffolded
|
|
64
|
-
apps side by side
|
|
102
|
+
apps side by side. The app must actually serve this port or the flow API
|
|
103
|
+
rejects its origin on the first submit, so setup makes the port explicit in
|
|
104
|
+
the app's own dev-server config: `server.port` + `strictPort` for Vite
|
|
105
|
+
frameworks, `serve.options.port` for Angular, and — because `next dev` and
|
|
106
|
+
`nuxt dev` take a port only from the command line — `--port` in the
|
|
107
|
+
`package.json` `dev` script for Next and Nuxt. On a pre-existing app that
|
|
108
|
+
means setup edits the `dev` script when it does not already name that port;
|
|
109
|
+
a script already on it is left untouched), `--preset password-first|passkey-first` (the sign-in
|
|
110
|
+
experience the scaffold starts from: `password-first` is the default —
|
|
111
|
+
email + password with passkey optional during registration; `passkey-first`
|
|
112
|
+
enters login on a one-tap passkey step with an email + password fallback;
|
|
113
|
+
recorded in `zitadel.json`), `--use-case minimal|consumer|business` (which
|
|
114
|
+
profile fields the scaffolded schema collects: `minimal` is the default —
|
|
115
|
+
email only; `consumer` adds given and family name; `business` also adds a
|
|
116
|
+
`companyName` attribute and overlays work-email copy on the generated auth
|
|
117
|
+
pages via the SDK's `businessLocales`; asked before `--preset` and recorded
|
|
118
|
+
in `zitadel.json`), `--design centered|split|split-right|hero|minimal`
|
|
119
|
+
(starter login design: ejects the design's template into
|
|
120
|
+
`.zitadel/branding/` and publishes it as branding revision 1 during setup;
|
|
121
|
+
the interactive wizard asks this as its final question with the built-in
|
|
122
|
+
template preselected — omit the flag in non-interactive runs to keep the
|
|
123
|
+
built-in template and no branding files), `--skip-install`.
|
|
124
|
+
On Next and Nuxt, the scaffolded auth/profile pages derive their embedding
|
|
125
|
+
posture from the app: a fresh scaffold (setup created the skeleton) pins
|
|
126
|
+
`variant="page"` full-page chrome, while a pre-existing app embeds
|
|
127
|
+
`variant="widget"` cards with `theme="auto"` in a layout-neutral wrapper.
|
|
128
|
+
`theme="auto"` follows the OS `prefers-color-scheme`, not the host app's
|
|
129
|
+
own theme — edit the generated page to set `theme="light"` or
|
|
130
|
+
`theme="dark"` when the app pins its scheme. Other frameworks always
|
|
131
|
+
scaffold the page posture. The chosen posture is recorded in the scaffold
|
|
132
|
+
manifest, `doctor --fix` restores managed pages in the recorded posture,
|
|
133
|
+
and editing the generated page is the supported way to change presentation
|
|
134
|
+
— there is no config knob.
|
|
135
|
+
Widget-posture embedding levers: host-page CSS sets `--zl-*` design-token
|
|
136
|
+
custom properties on the element to bridge the app's look through the
|
|
137
|
+
widget's shadow DOM (fonts `--zl-font-family-heading`/`-sans`, radii
|
|
138
|
+
`--zl-radius-*`, primary CTA `--zl-primary`/`--zl-primary-foreground`,
|
|
139
|
+
link color `--zl-link`); the `suppress-header` attribute
|
|
140
|
+
(wrapper prop `suppressHeader`) visually hides the widget's own heading
|
|
141
|
+
block when the page already carries one, keeping it in the accessibility
|
|
142
|
+
tree. Split-family designs collapse their brand pane by container width —
|
|
143
|
+
at card width they show the compact brand mark (`logo_url`, else
|
|
144
|
+
`hero_url`, from `.zitadel/branding/branding.json`; `hero` falls back to
|
|
145
|
+
editable text), and setup warns when a widget-posture app picks `split`
|
|
146
|
+
or `split-right`.
|
|
65
147
|
- `plan` — validate config and preview the sync diff without mutating anything.
|
|
66
148
|
- `apply` — validate and upload repo config to the platform.
|
|
149
|
+
- `plan` and `apply --dry-run` also emit `data.warnings`: non-blocking
|
|
150
|
+
findings as `{path, rule, message}`, the same text the human plan prints as
|
|
151
|
+
`# warning:` lines and `apply` prints through stderr. They never fail a run.
|
|
152
|
+
Two families exist today: flow-definition rules (`warn/…`, mirrored from the
|
|
153
|
+
server's validator) and branding asset reachability. `warn/asset-unreachable`
|
|
154
|
+
and `warn/asset-content-type` come from a bounded HEAD probe of
|
|
155
|
+
`logo_url` / `hero_url` — a URL that is well-formed but dead passes every
|
|
156
|
+
gate and then renders as a 0×0 image with nothing in the console, so the
|
|
157
|
+
probe is the only place it can be caught. It is advisory by design: the
|
|
158
|
+
machine planning is not necessarily the machine that renders the login page.
|
|
159
|
+
The probe only contacts public HTTPS destinations and re-checks every
|
|
160
|
+
redirect; loopback/private/internal targets stay inconclusive instead of
|
|
161
|
+
turning repo config into a network request from the planning host.
|
|
162
|
+
Set `ZITADEL_SKIP_ASSET_PROBE` to turn it off (offline, air-gapped CI, or a
|
|
163
|
+
CDN that only resolves from production) and `ZITADEL_ASSET_PROBE_TIMEOUT_MS`
|
|
164
|
+
to retune the per-URL budget (default 2500).
|
|
165
|
+
- In the human-readable plan, a multi-line field (branding's inlined
|
|
166
|
+
`liquid_template`) renders as `(<n> lines, sha256:…)` when it is created or
|
|
167
|
+
unchanged, and as a changed-line diff when it moved — not as one escaped
|
|
168
|
+
line. Read the file itself for full content.
|
|
169
|
+
- `schemas list` — inspect the revision history of a user-schema, filtered by
|
|
170
|
+
`--object-type` (e.g. `human-user`). Non-interactive/`--json` prints one row
|
|
171
|
+
per revision (newest first); interactive adds a picker that fetches and
|
|
172
|
+
pretty-prints the selected revision body.
|
|
67
173
|
- `doctor` — verify generated app files and local state once `zitadel.json`
|
|
68
|
-
exists. The
|
|
69
|
-
|
|
70
|
-
|
|
174
|
+
exists. The `managed-files` check compares the scaffolded app files against
|
|
175
|
+
the manifest setup recorded in `.zitadel/state.json`: a missing
|
|
176
|
+
infrastructure file (the request boundary, `custom-elements.d.ts`) fails,
|
|
177
|
+
a missing generated page warns, and files you edited (marker kept) or
|
|
178
|
+
replaced (marker removed) pass as `edited`/`adopted`. It also verifies the
|
|
179
|
+
managed config wirings (Vite/Nuxt proxy merges, Angular's `angular.json`
|
|
180
|
+
proxy and auth routes) through the patchers' idempotent transforms — a
|
|
181
|
+
detached or missing wiring config fails, an unverifiable one warns, and
|
|
182
|
+
`--fix` re-applies it. The Next/Nuxt `dev` script is verified the same way,
|
|
183
|
+
against the port recorded as the development issuer rather than the port the
|
|
184
|
+
script names today: a script moved off that port reports as an unapplied
|
|
185
|
+
config edit (a warning — a `dev` script is not the only way to choose a
|
|
186
|
+
port), and `--fix` restores the registered one. Boundary migrations converge: a pristine leftover
|
|
187
|
+
`middleware.ts` from a Next 15→16 upgrade is swapped for `proxy.ts`, while
|
|
188
|
+
an edited one is reported as a conflict instead of creating both (Next
|
|
189
|
+
rejects the pair). The default local
|
|
190
|
+
runtime is the `@zitadel/server` npm binary; Docker checks apply only when
|
|
191
|
+
using `--runtime docker` or `--image`. `--fix` restores missing managed
|
|
192
|
+
files and never replaces an existing scaffolded app file; additive repairs
|
|
193
|
+
(missing `.gitignore` entries, `.env.example` keys) still append to their
|
|
194
|
+
targets, and the SDK dependency is re-added only when absent — an existing
|
|
195
|
+
version pin is never rewritten. The `dependency-version` check warns when
|
|
196
|
+
an exactly-pinned `@zitadel/*` dependency does not match the CLI's own
|
|
197
|
+
version (the packages release as one train, and a floating
|
|
198
|
+
`npx @zitadel/cli@alpha` can run ahead of the app's pins); ranges,
|
|
199
|
+
dist-tags, and `file:`/`workspace:` specifiers express a deliberate choice
|
|
200
|
+
and are not compared. The repair — an exact-pin install command for the
|
|
201
|
+
project's detected package manager — is emitted in `data.next_commands`
|
|
202
|
+
and quoted in the warning message.
|
|
203
|
+
- `claim` — attach the project to a team so it becomes permanent. Mints a
|
|
204
|
+
short-lived link, opens it in a browser, and blocks until the developer
|
|
205
|
+
finishes signing in there, then records `claimed_at` and `team_id` in
|
|
206
|
+
`.zitadel/secret`. Nothing about the project changes: the issuer, users,
|
|
207
|
+
passkeys, and applications keep working, and the project secret is not
|
|
208
|
+
rotated. Re-running once the project belongs to a team is a clean
|
|
209
|
+
`status: "skipped"` with `reason: "already-claimed"`, so agents can retry
|
|
210
|
+
safely. The link is always printed before any browser opens, so a headless
|
|
211
|
+
machine, an SSH session, or `--no-open` needs no special handling — copy it
|
|
212
|
+
and open it anywhere. Links last 10 minutes; once one lapses the command
|
|
213
|
+
exits `E_VALIDATION` and points at a fresh run. `--dry-run` stops before
|
|
214
|
+
anything is minted and reports `status: "skipped"`, `reason: "dry-run"` —
|
|
215
|
+
there is nothing to preview, because a claim is decided in a browser.
|
|
216
|
+
Flags: `--no-open` (print the link instead of launching a browser),
|
|
217
|
+
`--timeout <seconds>` (stop waiting sooner than the link's own expiry).
|
|
218
|
+
`setup`, `status`, and `doctor` report whether a team is attached, reading
|
|
219
|
+
`claimed_at`/`team_id` from `.zitadel/secret` (no platform call). `status`
|
|
220
|
+
carries `data.project.claim` as `{"kind": "detached"}` or
|
|
221
|
+
`{"kind": "attached", "team_id": "team_01H…", "claimed_at": "2026-08-01T09:00:00.000Z"}`,
|
|
222
|
+
and `doctor` reports a
|
|
223
|
+
`claim` check. A project with no team is only ever a **warning**, never a
|
|
224
|
+
failure — it works exactly like one with a team, so `doctor` still exits 0
|
|
225
|
+
and `--fix` deliberately does nothing (a claim needs a human in a browser).
|
|
226
|
+
All three stay silent about teams when the project's `server` in
|
|
227
|
+
`zitadel.json` is local or self-hosted, where there is nothing to attach.
|
|
71
228
|
- `status` — summarize the local runtime and project state.
|
|
72
229
|
- `eject` (alias `uninstall`) — remove managed files and local Zitadel state;
|
|
73
230
|
requires `--force` when non-interactive.
|
|
74
231
|
- `start` — start the managed local Zitadel server and persist runtime metadata
|
|
75
|
-
under `.zitadel/local/runtime.json`.
|
|
76
|
-
the
|
|
232
|
+
under `.zitadel/local/runtime.json`. The binary runtime defaults to SQLite
|
|
233
|
+
under `.zitadel/local/nextgen-data/`. Runtime metadata reports the published
|
|
234
|
+
server package version; the repository contributor wrapper reports the
|
|
235
|
+
`dev+<short-commit>` source build it launched. That label names the revision
|
|
236
|
+
the binary was built from, which after a Moon cache hit can be an earlier
|
|
237
|
+
commit whose server sources are byte-identical. Use `--runtime docker` or
|
|
238
|
+
`--image` for the Docker backend.
|
|
77
239
|
- `stop` — stop the managed runtime while preserving
|
|
78
|
-
`.zitadel/local/nextgen-data`.
|
|
240
|
+
`.zitadel/local/nextgen-data`. Use `stop --all` to sweep all discovered
|
|
241
|
+
host-wide CLI-managed local runtime processes, including healthy runtimes
|
|
242
|
+
from other local projects; it does not kill arbitrary `/healthz` listeners.
|
|
79
243
|
- `logs` — print managed runtime logs; `--follow` streams in human mode.
|
|
80
244
|
- `reset` — stop/remove the managed runtime and delete local runtime data;
|
|
81
245
|
requires `--force` when non-interactive.
|
|
@@ -112,10 +276,10 @@ Do not treat a rendered login or registration form as completion.
|
|
|
112
276
|
|
|
113
277
|
`<zitadel-login>` and `<zitadel-logout>` are Lit elements with open shadow
|
|
114
278
|
roots. The stable automation hooks live inside nested shadow roots, so a flat
|
|
115
|
-
`document.querySelector('[data-testid="zitadel-
|
|
116
|
-
|
|
117
|
-
can target the hooks directly. Generic DOM-eval drivers should
|
|
118
|
-
roots recursively:
|
|
279
|
+
`document.querySelector('[data-testid="zitadel-input-email"]')` will not find
|
|
280
|
+
the native control. Browser drivers with shadow-DOM-aware locators, such as
|
|
281
|
+
Playwright, can target the hooks directly. Generic DOM-eval drivers should
|
|
282
|
+
pierce shadow roots recursively:
|
|
119
283
|
|
|
120
284
|
```js
|
|
121
285
|
function deepQuery(sel, root = document) {
|
|
@@ -131,22 +295,47 @@ function deepQuery(sel, root = document) {
|
|
|
131
295
|
}
|
|
132
296
|
```
|
|
133
297
|
|
|
134
|
-
Use `zitadel-field-email
|
|
135
|
-
`zitadel-action-submit
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
298
|
+
Use host hooks such as `zitadel-field-email`, `zitadel-field-password`, and
|
|
299
|
+
`zitadel-action-submit` when targeting the Lit atoms. Use native shadow-control
|
|
300
|
+
hooks such as `zitadel-input-email`, `zitadel-input-password`, and
|
|
301
|
+
`zitadel-action-submit-button` when filling or clicking the underlying input or
|
|
302
|
+
button. Hooks stay method-named even when the flow engine names a credential
|
|
303
|
+
field `x-auth-methods#<method>`; only the `name` attribute carries that raw
|
|
304
|
+
form key. Enter inside a field submits the step's primary action, but only for
|
|
305
|
+
key events that carry `key: "Enter"` — drivers whose synthesized key events
|
|
306
|
+
omit it (some CDP wrappers) should click `zitadel-action-submit` instead. For sign-out, open the user menu button if needed, then pierce to
|
|
307
|
+
`.signout-btn`; Playwright-style locators may use `zitadel-logout .signout-btn`.
|
|
308
|
+
The canonical component hook list lives in `packages/components/README.md`.
|
|
139
309
|
|
|
140
310
|
The checked-in automated regression path is `moon run workspace:journey`, which
|
|
141
311
|
exercises fresh-app setup plus registration, logout, and login across the
|
|
142
312
|
supported frameworks.
|
|
143
313
|
|
|
144
314
|
Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
|
|
145
|
-
then re-run `plan` and `apply`.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
315
|
+
then re-run `plan` and `apply`. Schema and flow files are synced from
|
|
316
|
+
`.zitadel/schemas/*.json` and `.zitadel/flows/*.json`. Login templates
|
|
317
|
+
(branding) are synced from `.zitadel/branding/`: a single `branding.json`
|
|
318
|
+
descriptor (layout, asset URLs) plus a sibling `login.liquid` LiquidJS
|
|
319
|
+
template referenced via `liquid_template_file`. Scaffold them with the
|
|
320
|
+
`branding eject` command (`--design centered|split|split-right|hero|minimal`,
|
|
321
|
+
interactive picker on a TTY) or at project creation with
|
|
322
|
+
`setup --design <name>`, which also publishes revision 1. Branding is
|
|
323
|
+
revisioned and immutable: every edit — including a `.liquid`-only edit —
|
|
324
|
+
plans as a `revise` and `apply` publishes a new revision; the login serves
|
|
325
|
+
the newest one. `plan` validates templates with the authoritative LiquidJS
|
|
326
|
+
validator (`E_VALIDATION` lists rule ids such as `no-script-tag` and
|
|
327
|
+
`mandatory-gates`; every template must keep a trailing
|
|
328
|
+
`{% mandatory_gates %}` tag). `font_url` is not writable yet; asset URLs
|
|
329
|
+
must be absolute `https://`. Keep exactly one descriptor in
|
|
330
|
+
`.zitadel/branding/` — extra `*.json` files there fail the scan.
|
|
331
|
+
Server-provisioned defaults remain a fallback for non-CLI project
|
|
332
|
+
creation, but CLI-created projects are authored from local files first. Flow create, read, list,
|
|
333
|
+
update, and delete are available, while the server enforces lifecycle rules
|
|
334
|
+
such as draft-only edits. Managed files carry a marker comment; `eject` removes only
|
|
335
|
+
files that still carry it, preserving anything the user replaced. For app-local
|
|
336
|
+
development, `--server local` resolves through `.zitadel/local/runtime.json` and
|
|
337
|
+
requires a healthy
|
|
338
|
+
`npx @zitadel/cli@alpha start` runtime. Runtime-only `.zitadel/local/**` state
|
|
339
|
+
does not block fresh same-directory scaffolding. `setup` installs dependencies
|
|
340
|
+
with the detected package manager by default; pass `--skip-install` when the
|
|
341
|
+
agent or host workflow will install dependencies separately.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { E as ZitadelError } from "./oclif-CanO3zdt.mjs";
|
|
2
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
//#region src/lib/branding/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Relative directory (from the project root) where local branding files live:
|
|
7
|
+
* the `branding.json` descriptor plus the sibling `.liquid` template it
|
|
8
|
+
* references. Owned here so commands, syncers, and tests share one source of
|
|
9
|
+
* truth; only `*.json` files are sync-discovered, the template rides along
|
|
10
|
+
* via `liquid_template_file`.
|
|
11
|
+
*/
|
|
12
|
+
const BRANDING_DIR = ".zitadel/branding";
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a descriptor's `liquid_template_file` reference to an absolute
|
|
15
|
+
* path. References are relative to the descriptor's directory (all
|
|
16
|
+
* descriptors live flat in {@link BRANDING_DIR}) and must stay inside the
|
|
17
|
+
* project — a reference escaping `cwd` is an error because apply would later
|
|
18
|
+
* write server state back to that path.
|
|
19
|
+
*/
|
|
20
|
+
function resolveTemplatePath(cwd, ref) {
|
|
21
|
+
const absolute = isAbsolute(ref) ? ref : resolve(join(cwd, BRANDING_DIR), ref);
|
|
22
|
+
const rel = relative(cwd, absolute);
|
|
23
|
+
if (rel.startsWith("..") || isAbsolute(rel)) throw new ZitadelError("E_VALIDATION", `liquid_template_file ${JSON.stringify(ref)} points outside the project`, { hint: `Keep the template next to its descriptor in ${BRANDING_DIR}/.` });
|
|
24
|
+
return absolute;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the template string a descriptor carries — the inline
|
|
28
|
+
* `liquid_template`, or the content of `liquid_template_file`. Returns
|
|
29
|
+
* undefined when the descriptor has neither (a legal descriptor that only
|
|
30
|
+
* sets layout/asset URLs).
|
|
31
|
+
*/
|
|
32
|
+
function readDescriptorTemplate(cwd, data) {
|
|
33
|
+
const descriptor = data;
|
|
34
|
+
if (typeof descriptor.liquid_template === "string") return descriptor.liquid_template;
|
|
35
|
+
if (typeof descriptor.liquid_template_file !== "string") return;
|
|
36
|
+
const path = resolveTemplatePath(cwd, descriptor.liquid_template_file);
|
|
37
|
+
try {
|
|
38
|
+
return readFileSync(path, "utf8");
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new ZitadelError("E_VALIDATION", `liquid_template_file ${JSON.stringify(descriptor.liquid_template_file)} cannot be read`, {
|
|
41
|
+
hint: "Create the template file or fix the reference; the `branding eject` command scaffolds a starting point.",
|
|
42
|
+
details: { cause: error instanceof Error ? error.message : String(error) }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Converts a local descriptor to the wire body of `POST /branding`: strips
|
|
48
|
+
* the editor `$schema` affordance and replaces `liquid_template_file` with
|
|
49
|
+
* the inlined template content. Non-throwing on a missing template file (the
|
|
50
|
+
* field is simply left out) — `validate` reports that case with a hint
|
|
51
|
+
* before any planning happens.
|
|
52
|
+
*/
|
|
53
|
+
function toBrandingWireBody(cwd, data) {
|
|
54
|
+
const { $schema, liquid_template_file, ...rest } = data;
|
|
55
|
+
const out = { ...rest };
|
|
56
|
+
if (typeof liquid_template_file === "string" && out.liquid_template === void 0) try {
|
|
57
|
+
out.liquid_template = readFileSync(resolveTemplatePath(cwd, liquid_template_file), "utf8");
|
|
58
|
+
} catch {}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Converts the server's canonical wire body back to the local descriptor
|
|
63
|
+
* form: when the local descriptor references a template file, the template
|
|
64
|
+
* string moves back out of the JSON into that reference. The caller writes
|
|
65
|
+
* the template content to the referenced file separately.
|
|
66
|
+
*/
|
|
67
|
+
function toLocalBrandingBody(canonicalWire, localData) {
|
|
68
|
+
const local = localData;
|
|
69
|
+
if (typeof local.liquid_template_file !== "string") return canonicalWire;
|
|
70
|
+
const { liquid_template, ...rest } = canonicalWire;
|
|
71
|
+
return {
|
|
72
|
+
...rest,
|
|
73
|
+
liquid_template_file: local.liquid_template_file
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { toLocalBrandingBody as a, toBrandingWireBody as i, readDescriptorTemplate as n, resolveTemplatePath as r, BRANDING_DIR as t };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=branding-cygSBPkV.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branding-cygSBPkV.mjs","names":[],"sources":["../src/lib/branding/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { isAbsolute, join, relative, resolve } from \"node:path\";\n\nimport { ZitadelError } from \"../errors\";\n\n/**\n * Relative directory (from the project root) where local branding files live:\n * the `branding.json` descriptor plus the sibling `.liquid` template it\n * references. Owned here so commands, syncers, and tests share one source of\n * truth; only `*.json` files are sync-discovered, the template rides along\n * via `liquid_template_file`.\n */\nexport const BRANDING_DIR = \".zitadel/branding\";\n\ntype BrandingDescriptor = {\n liquid_template?: string;\n liquid_template_file?: string;\n [key: string]: unknown;\n};\n\n/**\n * Resolves a descriptor's `liquid_template_file` reference to an absolute\n * path. References are relative to the descriptor's directory (all\n * descriptors live flat in {@link BRANDING_DIR}) and must stay inside the\n * project — a reference escaping `cwd` is an error because apply would later\n * write server state back to that path.\n */\nexport function resolveTemplatePath(cwd: string, ref: string): string {\n const absolute = isAbsolute(ref) ? ref : resolve(join(cwd, BRANDING_DIR), ref);\n const rel = relative(cwd, absolute);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new ZitadelError(\n \"E_VALIDATION\",\n `liquid_template_file ${JSON.stringify(ref)} points outside the project`,\n { hint: `Keep the template next to its descriptor in ${BRANDING_DIR}/.` },\n );\n }\n return absolute;\n}\n\n/**\n * Returns the template string a descriptor carries — the inline\n * `liquid_template`, or the content of `liquid_template_file`. Returns\n * undefined when the descriptor has neither (a legal descriptor that only\n * sets layout/asset URLs).\n */\nexport function readDescriptorTemplate(cwd: string, data: object): string | undefined {\n const descriptor = data as BrandingDescriptor;\n if (typeof descriptor.liquid_template === \"string\") {\n return descriptor.liquid_template;\n }\n if (typeof descriptor.liquid_template_file !== \"string\") {\n return undefined;\n }\n const path = resolveTemplatePath(cwd, descriptor.liquid_template_file);\n try {\n return readFileSync(path, \"utf8\");\n } catch (error) {\n throw new ZitadelError(\n \"E_VALIDATION\",\n `liquid_template_file ${JSON.stringify(descriptor.liquid_template_file)} cannot be read`,\n {\n hint: \"Create the template file or fix the reference; the `branding eject` command scaffolds a starting point.\",\n details: { cause: error instanceof Error ? error.message : String(error) },\n },\n );\n }\n}\n\n/**\n * Converts a local descriptor to the wire body of `POST /branding`: strips\n * the editor `$schema` affordance and replaces `liquid_template_file` with\n * the inlined template content. Non-throwing on a missing template file (the\n * field is simply left out) — `validate` reports that case with a hint\n * before any planning happens.\n */\nexport function toBrandingWireBody(cwd: string, data: object): object {\n const { $schema, liquid_template_file, ...rest } = data as BrandingDescriptor & {\n $schema?: string;\n };\n void $schema;\n const out: Record<string, unknown> = { ...rest };\n if (typeof liquid_template_file === \"string\" && out.liquid_template === undefined) {\n try {\n out.liquid_template = readFileSync(resolveTemplatePath(cwd, liquid_template_file), \"utf8\");\n } catch {\n // Reported by validate(); keep normalize/hashing total.\n }\n }\n return out;\n}\n\n/**\n * Converts the server's canonical wire body back to the local descriptor\n * form: when the local descriptor references a template file, the template\n * string moves back out of the JSON into that reference. The caller writes\n * the template content to the referenced file separately.\n */\nexport function toLocalBrandingBody(canonicalWire: object, localData: object): object {\n const local = localData as BrandingDescriptor;\n if (typeof local.liquid_template_file !== \"string\") {\n return canonicalWire;\n }\n const { liquid_template, ...rest } = canonicalWire as BrandingDescriptor;\n void liquid_template;\n return { ...rest, liquid_template_file: local.liquid_template_file };\n}\n"],"mappings":";;;;;;;;;;;AAYA,MAAa,eAAe;;;;;;;;AAe5B,SAAgB,oBAAoB,KAAa,KAAqB;CACpE,MAAM,WAAW,WAAW,IAAI,GAAG,MAAM,QAAQ,KAAK,KAAK,aAAa,EAAE,IAAI;CAC9E,MAAM,MAAM,SAAS,KAAK,SAAS;AACnC,KAAI,IAAI,WAAW,KAAK,IAAI,WAAW,IAAI,CACzC,OAAM,IAAI,aACR,gBACA,wBAAwB,KAAK,UAAU,IAAI,CAAC,8BAC5C,EAAE,MAAM,+CAA+C,aAAa,KAAK,CAC1E;AAEH,QAAO;;;;;;;;AAST,SAAgB,uBAAuB,KAAa,MAAkC;CACpF,MAAM,aAAa;AACnB,KAAI,OAAO,WAAW,oBAAoB,SACxC,QAAO,WAAW;AAEpB,KAAI,OAAO,WAAW,yBAAyB,SAC7C;CAEF,MAAM,OAAO,oBAAoB,KAAK,WAAW,qBAAqB;AACtE,KAAI;AACF,SAAO,aAAa,MAAM,OAAO;UAC1B,OAAO;AACd,QAAM,IAAI,aACR,gBACA,wBAAwB,KAAK,UAAU,WAAW,qBAAqB,CAAC,kBACxE;GACE,MAAM;GACN,SAAS,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE;GAC3E,CACF;;;;;;;;;;AAWL,SAAgB,mBAAmB,KAAa,MAAsB;CACpE,MAAM,EAAE,SAAS,sBAAsB,GAAG,SAAS;CAInD,MAAM,MAA+B,EAAE,GAAG,MAAM;AAChD,KAAI,OAAO,yBAAyB,YAAY,IAAI,oBAAoB,KAAA,EACtE,KAAI;AACF,MAAI,kBAAkB,aAAa,oBAAoB,KAAK,qBAAqB,EAAE,OAAO;SACpF;AAIV,QAAO;;;;;;;;AAST,SAAgB,oBAAoB,eAAuB,WAA2B;CACpF,MAAM,QAAQ;AACd,KAAI,OAAO,MAAM,yBAAyB,SACxC,QAAO;CAET,MAAM,EAAE,iBAAiB,GAAG,SAAS;AAErC,QAAO;EAAE,GAAG;EAAM,sBAAsB,MAAM;EAAsB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { M as serverKind, j as publicCliCommand } from "./oclif-CanO3zdt.mjs";
|
|
2
|
+
//#region src/lib/claim-state.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether the local record says a team owns this project.
|
|
5
|
+
*
|
|
6
|
+
* Both fields or neither: a half-written record is not an attachment, and
|
|
7
|
+
* treating it as one would hand callers a `team_id` with no idea when it
|
|
8
|
+
* landed. Shared with `zitadel claim`, which asks the same question before it
|
|
9
|
+
* decides to skip, so the two cannot drift as the record grows fields.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately separate from {@link claimState}: the command must answer this
|
|
12
|
+
* on any server, while the nudges only ask it on the cloud.
|
|
13
|
+
*/
|
|
14
|
+
function isAttached(secret) {
|
|
15
|
+
return Boolean(secret.claimed_at && secret.team_id);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Decides the attachment state from local state alone.
|
|
19
|
+
*
|
|
20
|
+
* **Why no network call.** The contract exposes no ambient claim-state read:
|
|
21
|
+
* `getClaimStatus` answers for one live challenge and rejects a secret that did
|
|
22
|
+
* not mint it, and claim attributes were deliberately kept off
|
|
23
|
+
* `GET /projects/{id}` because they belong to the grant, not the project. So
|
|
24
|
+
* `.zitadel/secret` is the only source, which is also why `status` and `doctor`
|
|
25
|
+
* stay fast and work offline. `zitadel claim` reaches the same conclusion the
|
|
26
|
+
* same way before it decides to skip.
|
|
27
|
+
*
|
|
28
|
+
* **Why cloud only.** Claiming attaches a project to a team on the platform.
|
|
29
|
+
* A local or self-hosted server has no such platform, and `--server local` is
|
|
30
|
+
* the documented dev loop, so an ungated nudge would advertise an impossible
|
|
31
|
+
* action for the entire life of a local project.
|
|
32
|
+
*
|
|
33
|
+
* The local record can be stale in one direction: a project claimed from
|
|
34
|
+
* another machine, or a `.zitadel/secret` restored from a backup, reads as
|
|
35
|
+
* `detached` here. That is tolerable because everything downstream is
|
|
36
|
+
* advisory, and `zitadel claim` itself turns the resulting `409` into a clean
|
|
37
|
+
* skip rather than an error.
|
|
38
|
+
*/
|
|
39
|
+
function claimState(input) {
|
|
40
|
+
if (serverKind.value(input.server) !== "cloud") return { kind: "not-applicable" };
|
|
41
|
+
if (isAttached(input.secret)) return {
|
|
42
|
+
kind: "attached",
|
|
43
|
+
team_id: input.secret.team_id,
|
|
44
|
+
claimed_at: input.secret.claimed_at
|
|
45
|
+
};
|
|
46
|
+
return { kind: "detached" };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The nudge for `next_actions`, quoting the command the way `journey-guidance`
|
|
50
|
+
* does.
|
|
51
|
+
*
|
|
52
|
+
* Deliberately says nothing about deletion: unattached projects are framed as
|
|
53
|
+
* temporary, but the epic's 14-day lifetime is not enforced anywhere yet, so
|
|
54
|
+
* promising it would be a lie the product cannot keep.
|
|
55
|
+
*/
|
|
56
|
+
function claimAction(cliVersion) {
|
|
57
|
+
return `This project is temporary until you attach it to a team: run ${publicCliCommand("claim", cliVersion)} to make it permanent. Nothing about the project changes, so users, passkeys, and the issuer keep working.`;
|
|
58
|
+
}
|
|
59
|
+
/** The command for `next_commands`. */
|
|
60
|
+
function claimCommand(cliVersion) {
|
|
61
|
+
return publicCliCommand("claim", cliVersion);
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { isAttached as i, claimCommand as n, claimState as r, claimAction as t };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=claim-state-DlfD6Res.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-state-DlfD6Res.mjs","names":[],"sources":["../src/lib/claim-state.ts"],"sourcesContent":["import { serverKind } from \"./oclif/server-kind\";\nimport type { ZitadelSecret } from \"./project\";\nimport { publicCliCommand } from \"./public-cli\";\n\n/**\n * Whether this project is attached to a team, as the CLI can tell locally.\n *\n * `not-applicable` is its own variant rather than a boolean flag on the caller\n * because \"there is nothing to attach here\" and \"nothing is attached yet\" want\n * opposite output: the first prints nothing at all, the second nudges.\n */\nexport type ClaimState =\n | { kind: \"attached\"; team_id: string; claimed_at: string }\n | { kind: \"detached\" }\n | { kind: \"not-applicable\" };\n\n/**\n * Whether the local record says a team owns this project.\n *\n * Both fields or neither: a half-written record is not an attachment, and\n * treating it as one would hand callers a `team_id` with no idea when it\n * landed. Shared with `zitadel claim`, which asks the same question before it\n * decides to skip, so the two cannot drift as the record grows fields.\n *\n * Deliberately separate from {@link claimState}: the command must answer this\n * on any server, while the nudges only ask it on the cloud.\n */\nexport function isAttached(\n secret: Pick<ZitadelSecret, \"claimed_at\" | \"team_id\">,\n): secret is { claimed_at: string; team_id: string } {\n return Boolean(secret.claimed_at && secret.team_id);\n}\n\n/**\n * Decides the attachment state from local state alone.\n *\n * **Why no network call.** The contract exposes no ambient claim-state read:\n * `getClaimStatus` answers for one live challenge and rejects a secret that did\n * not mint it, and claim attributes were deliberately kept off\n * `GET /projects/{id}` because they belong to the grant, not the project. So\n * `.zitadel/secret` is the only source, which is also why `status` and `doctor`\n * stay fast and work offline. `zitadel claim` reaches the same conclusion the\n * same way before it decides to skip.\n *\n * **Why cloud only.** Claiming attaches a project to a team on the platform.\n * A local or self-hosted server has no such platform, and `--server local` is\n * the documented dev loop, so an ungated nudge would advertise an impossible\n * action for the entire life of a local project.\n *\n * The local record can be stale in one direction: a project claimed from\n * another machine, or a `.zitadel/secret` restored from a backup, reads as\n * `detached` here. That is tolerable because everything downstream is\n * advisory, and `zitadel claim` itself turns the resulting `409` into a clean\n * skip rather than an error.\n */\nexport function claimState(input: {\n secret: Pick<ZitadelSecret, \"claimed_at\" | \"team_id\">;\n server: string;\n}): ClaimState {\n if (serverKind.value(input.server) !== \"cloud\") {\n return { kind: \"not-applicable\" };\n }\n if (isAttached(input.secret)) {\n return {\n kind: \"attached\",\n team_id: input.secret.team_id,\n claimed_at: input.secret.claimed_at,\n };\n }\n return { kind: \"detached\" };\n}\n\n/**\n * The nudge for `next_actions`, quoting the command the way `journey-guidance`\n * does.\n *\n * Deliberately says nothing about deletion: unattached projects are framed as\n * temporary, but the epic's 14-day lifetime is not enforced anywhere yet, so\n * promising it would be a lie the product cannot keep.\n */\nexport function claimAction(cliVersion: string): string {\n return (\n \"This project is temporary until you attach it to a team: run \" +\n `${publicCliCommand(\"claim\", cliVersion)} to make it permanent. ` +\n \"Nothing about the project changes, so users, passkeys, and the issuer keep working.\"\n );\n}\n\n/** The command for `next_commands`. */\nexport function claimCommand(cliVersion: string): string {\n return publicCliCommand(\"claim\", cliVersion);\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,SAAgB,WACd,QACmD;AACnD,QAAO,QAAQ,OAAO,cAAc,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;AAyBrD,SAAgB,WAAW,OAGZ;AACb,KAAI,WAAW,MAAM,MAAM,OAAO,KAAK,QACrC,QAAO,EAAE,MAAM,kBAAkB;AAEnC,KAAI,WAAW,MAAM,OAAO,CAC1B,QAAO;EACL,MAAM;EACN,SAAS,MAAM,OAAO;EACtB,YAAY,MAAM,OAAO;EAC1B;AAEH,QAAO,EAAE,MAAM,YAAY;;;;;;;;;;AAW7B,SAAgB,YAAY,YAA4B;AACtD,QACE,gEACG,iBAAiB,SAAS,WAAW,CAAC;;;AAM7C,SAAgB,aAAa,YAA4B;AACvD,QAAO,iBAAiB,SAAS,WAAW"}
|
package/dist/commands/apply.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { t as BaseCommand } from "../oclif-
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
1
|
+
import { j as publicCliCommand, t as BaseCommand } from "../oclif-CanO3zdt.mjs";
|
|
2
|
+
import { l as readZitadelSecret } from "../project-CHhot85s.mjs";
|
|
3
|
+
import { a as buildSyncPlan, i as summarizePlan, l as makeSyncers, n as enumeratePlanResources, r as renderPlan, s as runSyncLoop, t as collectPlanWarnings } from "../sync-BBlACKzT.mjs";
|
|
4
|
+
import { t as environmentSchema } from "../environment-rjRVkJjW.mjs";
|
|
4
5
|
import { Flags } from "@oclif/core";
|
|
5
6
|
import { createZitadelClient } from "@zitadel/api/client";
|
|
6
7
|
import { consola as consola$1 } from "consola";
|
|
@@ -34,24 +35,42 @@ var Apply = class Apply extends BaseCommand {
|
|
|
34
35
|
token: secret.project_secret
|
|
35
36
|
}),
|
|
36
37
|
projectId: secret.project_id,
|
|
37
|
-
env
|
|
38
|
+
env,
|
|
39
|
+
cwd
|
|
38
40
|
});
|
|
39
41
|
if (!dryRun) {
|
|
40
42
|
consola$1.start("Syncing schemas and flows to Zitadel");
|
|
41
|
-
await runSyncLoop(cwd, syncers);
|
|
43
|
+
const { filesUpdated, applied } = await runSyncLoop(cwd, syncers);
|
|
42
44
|
consola$1.success("Sync complete");
|
|
43
45
|
return this.emit({
|
|
44
46
|
status: "ok",
|
|
45
|
-
data: {
|
|
47
|
+
data: {
|
|
48
|
+
synced: true,
|
|
49
|
+
changes: applied,
|
|
50
|
+
files_updated: filesUpdated,
|
|
51
|
+
next_actions: applied.length === 0 ? ["Everything is already in sync — no changes were applied."] : ["Changes are live — reload your app to see them.", "Re-run plan to confirm local config and platform are in sync."],
|
|
52
|
+
next_commands: [publicCliCommand("plan", this.meta.cliVersion)]
|
|
53
|
+
}
|
|
46
54
|
});
|
|
47
55
|
}
|
|
48
56
|
consola$1.start("Building plan (dry run)");
|
|
49
57
|
const plan = await buildSyncPlan(cwd, syncers, true);
|
|
50
58
|
const summary = summarizePlan(plan);
|
|
51
|
-
|
|
59
|
+
this.recordTelemetry({
|
|
60
|
+
creates: summary.creates,
|
|
61
|
+
updates: summary.updates,
|
|
62
|
+
revisions: summary.revisions,
|
|
63
|
+
deletes: summary.deletes,
|
|
64
|
+
total: summary.total
|
|
65
|
+
});
|
|
66
|
+
consola$1.success(`Plan: ${summary.creates} create${summary.creates === 1 ? "" : "s"}, ${summary.updates} update${summary.updates === 1 ? "" : "s"}, ${summary.revisions} new revision${summary.revisions === 1 ? "" : "s"}, ${summary.deletes} delete${summary.deletes === 1 ? "" : "s"}`);
|
|
52
67
|
return this.emit({
|
|
53
68
|
status: "ok",
|
|
54
|
-
data:
|
|
69
|
+
data: {
|
|
70
|
+
...summary,
|
|
71
|
+
changes: enumeratePlanResources(plan),
|
|
72
|
+
warnings: collectPlanWarnings(plan)
|
|
73
|
+
},
|
|
55
74
|
pretty: renderPlan(plan, isTTY)
|
|
56
75
|
});
|
|
57
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.mjs","names":[],"sources":["../../src/commands/apply.ts"],"sourcesContent":["import { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport { createZitadelClient } from \"@zitadel/api/client\";\n\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { environmentSchema } from \"../lib/environment\";\nimport {
|
|
1
|
+
{"version":3,"file":"apply.mjs","names":[],"sources":["../../src/commands/apply.ts"],"sourcesContent":["import { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport { createZitadelClient } from \"@zitadel/api/client\";\n\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { environmentSchema } from \"../lib/environment\";\nimport {\n buildSyncPlan,\n collectPlanWarnings,\n enumeratePlanResources,\n makeSyncers,\n renderPlan,\n runSyncLoop,\n summarizePlan,\n} from \"../lib/sync\";\nimport { readZitadelSecret } from \"../lib/project\";\nimport { publicCliCommand } from \"../lib/public-cli\";\n\n/**\n * `zitadel apply` — validate and upload repo config to the platform.\n *\n * Runs the sync loop to convergence, or (with `--dry-run`) previews the diff\n * without mutating. All validation — structural shape and `${VAR}` / `*_env`\n * reference presence — happens inside the sync engine ({@link buildSyncPlan}),\n * so an invalid or under-configured file fails with `E_VALIDATION` before any\n * platform call.\n */\nexport default class Apply extends BaseCommand {\n static override description = \"Validate and upload repo config to the platform.\";\n static override flags = {\n environment: Flags.string({\n char: \"e\",\n description: \"Target environment (default: development).\",\n options: [...environmentSchema.options],\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Apply);\n await this.toMeta(flags);\n const { cwd, source, env, dryRun, isTTY } = this.meta;\n\n const secret = await readZitadelSecret(cwd);\n consola.info(`Project ${secret.project_id}`);\n consola.info(`Server ${source}`);\n const client = createZitadelClient({\n baseUrl: source,\n token: secret.project_secret,\n });\n const syncers = makeSyncers({\n client,\n projectId: secret.project_id,\n env,\n cwd,\n });\n\n if (!dryRun) {\n consola.start(\"Syncing schemas and flows to Zitadel\");\n const { filesUpdated, applied } = await runSyncLoop(cwd, syncers);\n consola.success(\"Sync complete\");\n return this.emit({\n status: \"ok\",\n data: {\n synced: true,\n // Platform resources this run touched (with resulting ids);\n // `files_updated` stays the local write-backs only.\n changes: applied,\n files_updated: filesUpdated,\n next_actions:\n applied.length === 0\n ? [\"Everything is already in sync — no changes were applied.\"]\n : [\n \"Changes are live — reload your app to see them.\",\n \"Re-run plan to confirm local config and platform are in sync.\",\n ],\n next_commands: [publicCliCommand(\"plan\", this.meta.cliVersion)],\n },\n });\n }\n\n consola.start(\"Building plan (dry run)\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n this.recordTelemetry({\n creates: summary.creates,\n updates: summary.updates,\n revisions: summary.revisions,\n deletes: summary.deletes,\n total: summary.total,\n });\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.revisions} new revision${summary.revisions === 1 ? \"\" : \"s\"}, ` +\n `${summary.deletes} delete${summary.deletes === 1 ? \"\" : \"s\"}`,\n );\n return this.emit({\n status: \"ok\",\n data: {\n ...summary,\n changes: enumeratePlanResources(plan),\n warnings: collectPlanWarnings(plan),\n },\n pretty: renderPlan(plan, isTTY),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,IAAqB,QAArB,MAAqB,cAAc,YAAY;CAC7C,OAAgB,cAAc;CAC9B,OAAgB,QAAQ,EACtB,aAAa,MAAM,OAAO;EACxB,MAAM;EACN,aAAa;EACb,SAAS,CAAC,GAAG,kBAAkB,QAAQ;EACxC,CAAC,EACH;CAED,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,MAAM;AACzC,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,QAAQ,KAAK,QAAQ,UAAU,KAAK;EAEjD,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,YAAQ,KAAK,aAAa,OAAO,aAAa;AAC9C,YAAQ,KAAK,aAAa,SAAS;EAKnC,MAAM,UAAU,YAAY;GAC1B,QALa,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CAEO;GACN,WAAW,OAAO;GAClB;GACA;GACD,CAAC;AAEF,MAAI,CAAC,QAAQ;AACX,aAAQ,MAAM,uCAAuC;GACrD,MAAM,EAAE,cAAc,YAAY,MAAM,YAAY,KAAK,QAAQ;AACjE,aAAQ,QAAQ,gBAAgB;AAChC,UAAO,KAAK,KAAK;IACf,QAAQ;IACR,MAAM;KACJ,QAAQ;KAGR,SAAS;KACT,eAAe;KACf,cACE,QAAQ,WAAW,IACf,CAAC,2DAA2D,GAC5D,CACE,mDACA,gEACD;KACP,eAAe,CAAC,iBAAiB,QAAQ,KAAK,KAAK,WAAW,CAAC;KAChE;IACF,CAAC;;AAGJ,YAAQ,MAAM,0BAA0B;EACxC,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,OAAK,gBAAgB;GACnB,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,OAAO,QAAQ;GAChB,CAAC;AACF,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,UAAU,eAAe,QAAQ,cAAc,IAAI,KAAK,IAAI,IACpE,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,MAC5D;AACD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IACJ,GAAG;IACH,SAAS,uBAAuB,KAAK;IACrC,UAAU,oBAAoB,KAAK;IACpC;GACD,QAAQ,WAAW,MAAM,MAAM;GAChC,CAAC"}
|