@zitadel/cli 0.1.0-alpha.16 → 0.1.0-alpha.18
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 +145 -107
- package/SKILLS.md +98 -13
- package/dist/branding-BXj028-W.mjs +79 -0
- package/dist/branding-BXj028-W.mjs.map +1 -0
- package/dist/claim-state-BTGInAaN.mjs +66 -0
- package/dist/claim-state-BTGInAaN.mjs.map +1 -0
- package/dist/commands/apply.mjs +12 -7
- package/dist/commands/apply.mjs.map +1 -1
- package/dist/commands/branding/eject.mjs +125 -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 +477 -38
- package/dist/commands/doctor.mjs.map +1 -1
- package/dist/commands/eject.mjs +3 -3
- package/dist/commands/logs.mjs +2 -2
- package/dist/commands/plan.mjs +7 -5
- package/dist/commands/plan.mjs.map +1 -1
- package/dist/commands/reset.mjs +2 -2
- package/dist/commands/schemas/list.mjs +14 -10
- package/dist/commands/schemas/list.mjs.map +1 -1
- package/dist/commands/setup.mjs +331 -120
- package/dist/commands/setup.mjs.map +1 -1
- package/dist/commands/start.mjs +3 -4
- package/dist/commands/start.mjs.map +1 -1
- package/dist/commands/status.mjs +21 -13
- package/dist/commands/status.mjs.map +1 -1
- package/dist/commands/stop.mjs +3 -3
- package/dist/commands/stop.mjs.map +1 -1
- package/dist/{docker-BbX0yOpg.mjs → docker-Ciz9N6uI.mjs} +2 -2
- package/dist/{docker-BbX0yOpg.mjs.map → docker-Ciz9N6uI.mjs.map} +1 -1
- package/dist/{environment-ABj1IWZt.mjs → environment-CuHKxtIO.mjs} +1 -1
- package/dist/{environment-ABj1IWZt.mjs.map → environment-CuHKxtIO.mjs.map} +1 -1
- package/dist/{journey-guidance-E_3_UwGt.mjs → journey-guidance-CtQUfWar.mjs} +14 -3
- package/dist/{journey-guidance-E_3_UwGt.mjs.map → journey-guidance-CtQUfWar.mjs.map} +1 -1
- package/dist/{oclif-SjHLB_XK.mjs → oclif-CL6-M13m.mjs} +220 -43
- package/dist/oclif-CL6-M13m.mjs.map +1 -0
- package/dist/{orca-_TmrJQMw.mjs → orca-BWB4zOdw.mjs} +749 -192
- package/dist/orca-BWB4zOdw.mjs.map +1 -0
- package/dist/{ports-XujDtA47.mjs → ports-Cu2Y7aXQ.mjs} +25 -20
- package/dist/ports-Cu2Y7aXQ.mjs.map +1 -0
- package/dist/{processes-p2PPbKGG.mjs → processes-Bn2WngOi.mjs} +1 -1
- package/dist/{processes-p2PPbKGG.mjs.map → processes-Bn2WngOi.mjs.map} +1 -1
- package/dist/{project-DXc7q4wN.mjs → project-yejJSfdJ.mjs} +57 -4
- package/dist/project-yejJSfdJ.mjs.map +1 -0
- package/dist/scaffold-manifest-CKNFLX9T.mjs +81 -0
- package/dist/scaffold-manifest-CKNFLX9T.mjs.map +1 -0
- package/dist/{sync-Bu_iLIbb.mjs → sync-Cx2_4rXW.mjs} +178 -52
- package/dist/sync-Cx2_4rXW.mjs.map +1 -0
- package/dist/user-schema-W1OsXdpc.mjs +91 -0
- package/dist/user-schema-W1OsXdpc.mjs.map +1 -0
- package/oclif.manifest.json +227 -5
- package/package.json +7 -5
- package/dist/docker-guidance-3UZESTfb.mjs +0 -21
- package/dist/docker-guidance-3UZESTfb.mjs.map +0 -1
- package/dist/oclif-SjHLB_XK.mjs.map +0 -1
- package/dist/orca-_TmrJQMw.mjs.map +0 -1
- package/dist/ports-XujDtA47.mjs.map +0 -1
- package/dist/project-DXc7q4wN.mjs.map +0 -1
- package/dist/sync-Bu_iLIbb.mjs.map +0 -1
- package/dist/user-schema-DDz5-lX5.mjs +0 -13
- package/dist/user-schema-DDz5-lX5.mjs.map +0 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { E as ZitadelError } from "./oclif-CL6-M13m.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-BXj028-W.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branding-BXj028-W.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 { A as serverKind, k as publicCliCommand } from "./oclif-CL6-M13m.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-BTGInAaN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-state-BTGInAaN.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,7 +1,7 @@
|
|
|
1
|
-
import { t as BaseCommand } from "../oclif-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as environmentSchema } from "../environment-
|
|
1
|
+
import { k as publicCliCommand, t as BaseCommand } from "../oclif-CL6-M13m.mjs";
|
|
2
|
+
import { l as readZitadelSecret } from "../project-yejJSfdJ.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-Cx2_4rXW.mjs";
|
|
4
|
+
import { t as environmentSchema } from "../environment-CuHKxtIO.mjs";
|
|
5
5
|
import { Flags } from "@oclif/core";
|
|
6
6
|
import { createZitadelClient } from "@zitadel/api/client";
|
|
7
7
|
import { consola as consola$1 } from "consola";
|
|
@@ -35,17 +35,21 @@ var Apply = class Apply extends BaseCommand {
|
|
|
35
35
|
token: secret.project_secret
|
|
36
36
|
}),
|
|
37
37
|
projectId: secret.project_id,
|
|
38
|
-
env
|
|
38
|
+
env,
|
|
39
|
+
cwd
|
|
39
40
|
});
|
|
40
41
|
if (!dryRun) {
|
|
41
42
|
consola$1.start("Syncing schemas and flows to Zitadel");
|
|
42
|
-
const { filesUpdated } = await runSyncLoop(cwd, syncers);
|
|
43
|
+
const { filesUpdated, applied } = await runSyncLoop(cwd, syncers);
|
|
43
44
|
consola$1.success("Sync complete");
|
|
44
45
|
return this.emit({
|
|
45
46
|
status: "ok",
|
|
46
47
|
data: {
|
|
47
48
|
synced: true,
|
|
48
|
-
|
|
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)]
|
|
49
53
|
}
|
|
50
54
|
});
|
|
51
55
|
}
|
|
@@ -64,6 +68,7 @@ var Apply = class Apply extends BaseCommand {
|
|
|
64
68
|
status: "ok",
|
|
65
69
|
data: {
|
|
66
70
|
...summary,
|
|
71
|
+
changes: enumeratePlanResources(plan),
|
|
67
72
|
warnings: collectPlanWarnings(plan)
|
|
68
73
|
},
|
|
69
74
|
pretty: renderPlan(plan, isTTY)
|
|
@@ -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 {\n buildSyncPlan,\n collectPlanWarnings,\n makeSyncers,\n renderPlan,\n runSyncLoop,\n summarizePlan,\n} from \"../lib/sync\";\nimport { readZitadelSecret } from \"../lib/project\";\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 });\n\n if (!dryRun) {\n consola.start(\"Syncing schemas and flows to Zitadel\");\n const { filesUpdated } = await runSyncLoop(cwd, syncers);\n consola.success(\"Sync complete\");\n return this.emit({
|
|
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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { E as ZitadelError, T as stableStringify, k as publicCliCommand, t as BaseCommand } from "../../oclif-CL6-M13m.mjs";
|
|
2
|
+
import { t as hasZitadelConfig } from "../../project-yejJSfdJ.mjs";
|
|
3
|
+
import { t as BRANDING_DIR } from "../../branding-BXj028-W.mjs";
|
|
4
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { cancel, isCancel, select } from "@clack/prompts";
|
|
7
|
+
import { Flags } from "@oclif/core";
|
|
8
|
+
import { BRANDING_DESIGNS, DEFAULT_BRANDING_CONFIG_PATH, DEFAULT_BRANDING_DESIGN, DEFAULT_BRANDING_TEMPLATE_PATH, brandingReadmeContent, getDefaultBrandingConfig } from "@zitadel/config/defaults";
|
|
9
|
+
import { consola as consola$1 } from "consola";
|
|
10
|
+
import { BRANDING_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles } from "@zitadel/config/meta-schemas";
|
|
11
|
+
//#region src/commands/branding/eject.ts
|
|
12
|
+
/**
|
|
13
|
+
* The `branding eject` command — take ownership of the login template.
|
|
14
|
+
*
|
|
15
|
+
* Scaffolds `.zitadel/branding/` from a shipped design: the `branding.json`
|
|
16
|
+
* descriptor, the `login.liquid` template it references, a README, and the
|
|
17
|
+
* branding dialect meta-schema (for projects set up before it existed).
|
|
18
|
+
* Purely local — nothing is uploaded; the first `zitadel apply` publishes
|
|
19
|
+
* revision 1. Ejecting the template is the last rung of the branding
|
|
20
|
+
* override ladder (docs/design/branding/override-ladder.md); unrelated to
|
|
21
|
+
* `zitadel eject`, which removes the Zitadel integration.
|
|
22
|
+
*/
|
|
23
|
+
var BrandingEject = class BrandingEject extends BaseCommand {
|
|
24
|
+
static description = "Take ownership of the login template: scaffold .zitadel/branding/ from a shipped design.";
|
|
25
|
+
static flags = { design: Flags.string({
|
|
26
|
+
description: `Design to start from (default: ${DEFAULT_BRANDING_DESIGN}).`,
|
|
27
|
+
options: [...BRANDING_DESIGNS]
|
|
28
|
+
}) };
|
|
29
|
+
async run() {
|
|
30
|
+
const { flags } = await this.parse(BrandingEject);
|
|
31
|
+
await this.toMeta(flags);
|
|
32
|
+
const { cwd, force, nonInteractive } = this.meta;
|
|
33
|
+
if (!await hasZitadelConfig(cwd)) throw new ZitadelError("E_VALIDATION", "This directory is not a Zitadel project", {
|
|
34
|
+
hint: "Run setup first; branding files live under .zitadel/.",
|
|
35
|
+
nextCommands: [publicCliCommand("setup", this.meta.cliVersion)]
|
|
36
|
+
});
|
|
37
|
+
const design = flags.design ?? await this.promptDesign(nonInteractive);
|
|
38
|
+
this.recordTelemetry({
|
|
39
|
+
design,
|
|
40
|
+
force
|
|
41
|
+
});
|
|
42
|
+
const { branding, template } = getDefaultBrandingConfig(design);
|
|
43
|
+
const filesWritten = [];
|
|
44
|
+
const write = async (relPath, content, overwrite) => {
|
|
45
|
+
const dest = join(cwd, relPath);
|
|
46
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
47
|
+
try {
|
|
48
|
+
await writeFile(dest, content, overwrite ? void 0 : { flag: "wx" });
|
|
49
|
+
filesWritten.push(relPath);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (isErrno(error, "EEXIST")) throw new ZitadelError("E_CONFLICT", `${relPath} already exists`, { hint: "Rerun with --force to replace it, or edit the existing file directly." });
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
await write(DEFAULT_BRANDING_CONFIG_PATH, `${stableStringify({
|
|
56
|
+
$schema: BRANDING_FILE_SCHEMA_REF,
|
|
57
|
+
...branding
|
|
58
|
+
})}\n`, force);
|
|
59
|
+
await write(DEFAULT_BRANDING_TEMPLATE_PATH, template, force);
|
|
60
|
+
await write(join(BRANDING_DIR, "README.md"), brandingReadmeContent(), false).catch(swallowConflict);
|
|
61
|
+
const brandingMeta = metaSchemaFiles().find((f) => f.name === "branding.json");
|
|
62
|
+
if (brandingMeta) await write(join(META_SCHEMA_DIR, brandingMeta.name), `${stableStringify(brandingMeta.body)}\n`, false).catch(swallowConflict);
|
|
63
|
+
for (const file of filesWritten) consola$1.info(`Wrote ${file}`);
|
|
64
|
+
consola$1.success(`Ejected the ${design} design into ${BRANDING_DIR}/`);
|
|
65
|
+
return this.emit({
|
|
66
|
+
status: "ok",
|
|
67
|
+
data: {
|
|
68
|
+
design,
|
|
69
|
+
files_written: filesWritten,
|
|
70
|
+
next_actions: [`Edit ${DEFAULT_BRANDING_TEMPLATE_PATH} — the login UI is yours now.`, "Plan validates the template and shows the pending revision; apply publishes it."],
|
|
71
|
+
next_commands: [publicCliCommand("plan", this.meta.cliVersion), publicCliCommand("apply", this.meta.cliVersion)]
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async promptDesign(nonInteractive) {
|
|
76
|
+
if (nonInteractive) return DEFAULT_BRANDING_DESIGN;
|
|
77
|
+
const choice = await select({
|
|
78
|
+
message: "Which design do you want to start from?",
|
|
79
|
+
initialValue: DEFAULT_BRANDING_DESIGN,
|
|
80
|
+
options: [
|
|
81
|
+
{
|
|
82
|
+
value: "centered",
|
|
83
|
+
label: "Centered",
|
|
84
|
+
hint: "the bundled default, card centred on page"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
value: "split",
|
|
88
|
+
label: "Split",
|
|
89
|
+
hint: "brand panel left, form right"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
value: "split-right",
|
|
93
|
+
label: "Split (reversed)",
|
|
94
|
+
hint: "form left, brand panel right"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
value: "hero",
|
|
98
|
+
label: "Hero",
|
|
99
|
+
hint: "landing-style brand pane left, form right"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
value: "minimal",
|
|
103
|
+
label: "Minimal",
|
|
104
|
+
hint: "no card chrome, fields straight on the page"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
if (isCancel(choice)) {
|
|
109
|
+
cancel("Eject cancelled.");
|
|
110
|
+
throw new ZitadelError("E_VALIDATION", "Eject cancelled by user");
|
|
111
|
+
}
|
|
112
|
+
return choice;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
function swallowConflict(error) {
|
|
116
|
+
if (error instanceof ZitadelError && error.code === "E_CONFLICT") return;
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
function isErrno(error, code) {
|
|
120
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { BrandingEject as default };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=eject.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eject.mjs","names":[],"sources":["../../../src/commands/branding/eject.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cancel, isCancel, select } from \"@clack/prompts\";\nimport { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport {\n BRANDING_DESIGNS,\n DEFAULT_BRANDING_CONFIG_PATH,\n DEFAULT_BRANDING_DESIGN,\n DEFAULT_BRANDING_TEMPLATE_PATH,\n brandingReadmeContent,\n getDefaultBrandingConfig,\n} from \"@zitadel/config/defaults\";\nimport { BRANDING_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles } from \"@zitadel/config/meta-schemas\";\n\nimport { BRANDING_DIR } from \"../../lib/branding\";\nimport { ZitadelError } from \"../../lib/errors\";\nimport { stableStringify } from \"../../lib/json\";\nimport { BaseCommand, type JsonEnvelope } from \"../../lib/oclif\";\nimport { hasZitadelConfig } from \"../../lib/project\";\nimport { publicCliCommand } from \"../../lib/public-cli\";\n\n/**\n * The `branding eject` command — take ownership of the login template.\n *\n * Scaffolds `.zitadel/branding/` from a shipped design: the `branding.json`\n * descriptor, the `login.liquid` template it references, a README, and the\n * branding dialect meta-schema (for projects set up before it existed).\n * Purely local — nothing is uploaded; the first `zitadel apply` publishes\n * revision 1. Ejecting the template is the last rung of the branding\n * override ladder (docs/design/branding/override-ladder.md); unrelated to\n * `zitadel eject`, which removes the Zitadel integration.\n */\nexport default class BrandingEject extends BaseCommand {\n static override description =\n \"Take ownership of the login template: scaffold .zitadel/branding/ from a shipped design.\";\n static override flags = {\n design: Flags.string({\n description: `Design to start from (default: ${DEFAULT_BRANDING_DESIGN}).`,\n options: [...BRANDING_DESIGNS],\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(BrandingEject);\n await this.toMeta(flags);\n const { cwd, force, nonInteractive } = this.meta;\n\n if (!(await hasZitadelConfig(cwd))) {\n throw new ZitadelError(\"E_VALIDATION\", \"This directory is not a Zitadel project\", {\n hint: \"Run setup first; branding files live under .zitadel/.\",\n nextCommands: [publicCliCommand(\"setup\", this.meta.cliVersion)],\n });\n }\n\n const design = flags.design ?? (await this.promptDesign(nonInteractive));\n this.recordTelemetry({ design, force });\n const { branding, template } = getDefaultBrandingConfig(design);\n\n const filesWritten: string[] = [];\n const write = async (relPath: string, content: string, overwrite: boolean): Promise<void> => {\n const dest = join(cwd, relPath);\n await mkdir(dirname(dest), { recursive: true });\n try {\n await writeFile(dest, content, overwrite ? undefined : { flag: \"wx\" });\n filesWritten.push(relPath);\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) {\n throw new ZitadelError(\"E_CONFLICT\", `${relPath} already exists`, {\n hint: \"Rerun with --force to replace it, or edit the existing file directly.\",\n });\n }\n throw error;\n }\n };\n\n await write(\n DEFAULT_BRANDING_CONFIG_PATH,\n `${stableStringify({ $schema: BRANDING_FILE_SCHEMA_REF, ...branding })}\\n`,\n force,\n );\n await write(DEFAULT_BRANDING_TEMPLATE_PATH, template, force);\n\n // Never overwrite a hand-edited README; setups from before the branding\n // dialect also miss the meta-schema, so materialize it when absent.\n await write(join(BRANDING_DIR, \"README.md\"), brandingReadmeContent(), false).catch(\n swallowConflict,\n );\n const brandingMeta = metaSchemaFiles().find((f) => f.name === \"branding.json\");\n if (brandingMeta) {\n await write(\n join(META_SCHEMA_DIR, brandingMeta.name),\n `${stableStringify(brandingMeta.body)}\\n`,\n false,\n ).catch(swallowConflict);\n }\n\n for (const file of filesWritten) {\n consola.info(`Wrote ${file}`);\n }\n consola.success(`Ejected the ${design} design into ${BRANDING_DIR}/`);\n\n return this.emit({\n status: \"ok\",\n data: {\n design,\n files_written: filesWritten,\n next_actions: [\n `Edit ${DEFAULT_BRANDING_TEMPLATE_PATH} — the login UI is yours now.`,\n \"Plan validates the template and shows the pending revision; apply publishes it.\",\n ],\n next_commands: [\n publicCliCommand(\"plan\", this.meta.cliVersion),\n publicCliCommand(\"apply\", this.meta.cliVersion),\n ],\n },\n });\n }\n\n private async promptDesign(nonInteractive: boolean): Promise<string> {\n if (nonInteractive) {\n return DEFAULT_BRANDING_DESIGN;\n }\n const choice = await select({\n message: \"Which design do you want to start from?\",\n initialValue: DEFAULT_BRANDING_DESIGN as string,\n options: [\n { value: \"centered\", label: \"Centered\", hint: \"the bundled default, card centred on page\" },\n { value: \"split\", label: \"Split\", hint: \"brand panel left, form right\" },\n { value: \"split-right\", label: \"Split (reversed)\", hint: \"form left, brand panel right\" },\n { value: \"hero\", label: \"Hero\", hint: \"landing-style brand pane left, form right\" },\n { value: \"minimal\", label: \"Minimal\", hint: \"no card chrome, fields straight on the page\" },\n ],\n });\n if (isCancel(choice)) {\n cancel(\"Eject cancelled.\");\n throw new ZitadelError(\"E_VALIDATION\", \"Eject cancelled by user\");\n }\n return choice;\n }\n}\n\nfunction swallowConflict(error: unknown): void {\n if (error instanceof ZitadelError && error.code === \"E_CONFLICT\") {\n return;\n }\n throw error;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === code\n );\n}\n"],"mappings":"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"}
|