Skip to content

Commit 1457332

Browse files
committed
fix(cli): guard missing project id and fail fast on absent package.json
Throw a clear error in the Vite and Angular dev proxies when the project id is missing (ZITADEL_PROJECT_ID / zitadel.json "project") instead of forwarding a "Bearer sk_undefined" token and 401-ing. Make ensureDevScript fail fast when package.json is absent rather than fabricating a manifest add-dep then assumes. Correct the reclaim docstring to include the package.json dev-script edit, and drop the non-null assertions in the Angular test (oxlint no-non-null-assertion).
1 parent 2fdf884 commit 1457332

5 files changed

Lines changed: 43 additions & 24 deletions

File tree

apps/cli/src/lib/orca/patchers/rule/angular/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ZitadelError } from "../../../../errors";
12
import { isObject, parseJsonObject, stableStringify } from "../../../../json";
23
import { npmDistTagForCliVersion } from "../../../../public-cli";
34
import type { FileOp } from "../file-writer/types";
@@ -16,11 +17,16 @@ const SDK_DEPENDENCY = "@zitadel/sdk-angular";
1617
* patching a project that already wires its own `dev` leaves it untouched.
1718
*/
1819
function ensureDevScript(source: string | undefined): string {
19-
const pkg: Record<string, unknown> = source ? parseJsonObject(source, "package.json") : {};
20+
if (source === undefined) {
21+
throw new ZitadelError("E_VALIDATION", "package.json is required to add the dev script", {
22+
hint: "Run setup from a project that has a package.json.",
23+
});
24+
}
25+
const pkg = parseJsonObject(source, "package.json");
2026
const scripts = isObject(pkg.scripts) ? pkg.scripts : undefined;
2127
// Leave an existing dev script untouched, returning the source unchanged so
2228
// the edit op skips the file instead of reformatting the user's package.json.
23-
if (source !== undefined && scripts?.dev !== undefined) {
29+
if (scripts?.dev !== undefined) {
2430
return source;
2531
}
2632
pkg.scripts = { ...(scripts ?? {}), dev: "ng serve" };

apps/cli/src/lib/orca/patchers/rule/angular/templates.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ export function proxyConfTemplate(): string {
7070
const { readFileSync } = require("node:fs");
7171
7272
const config = JSON.parse(readFileSync("zitadel.json", "utf8"));
73+
if (!config.project) {
74+
throw new Error("zitadel.json is missing \\"project\\"; re-run zitadel setup.");
75+
}
7376
const bearer = \`Bearer sk_\${config.project}\`;
7477
7578
function setBearer(proxyReq) {

apps/cli/src/lib/orca/patchers/rule/reclaim.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import type { FileOp, ScaffoldPlan } from "./file-writer/types";
44
/**
55
* The subset of a patcher plan's operations that `doctor --fix` re-applies:
66
* env merges, gitignore entries, dependency additions, marker-bearing managed
7-
* files (framework routes/middleware), and config edits (the `/__nextgen` dev
8-
* proxy merged into `vite.config`/`nuxt.config`/`angular.json`). The `edit`
9-
* transforms are idempotent — they re-add their block only when it is missing —
10-
* so replaying one restores a removed proxy without disturbing the rest of the
11-
* config. Deliberately excludes the unmarked `.zitadel/` resource writes and
12-
* `zitadel.json` — those are user-editable and synced by `apply`, so `--fix`
13-
* must not clobber them.
7+
* files (framework routes/middleware), and the `edit` transforms — the
8+
* `/__nextgen` dev proxy merged into `vite.config`/`nuxt.config`/`angular.json`
9+
* and the Angular `dev` script added to `package.json`. Every `edit` transform
10+
* is idempotent and only adds what is missing (an existing value is left as-is,
11+
* the transform returning the source unchanged), so replaying one restores a
12+
* removed managed block without clobbering the user's own edits. Deliberately
13+
* excludes the unmarked `.zitadel/` resource writes and `zitadel.json` — those
14+
* are user-editable and synced by `apply`, so `--fix` must not clobber them.
1415
*
1516
* Pure: filters a freshly-allocated list; the input plan is not mutated.
1617
*/

apps/cli/src/lib/orca/patchers/rule/vite-support.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ function proxyEntryCode(server: string): string {
2222
changeOrigin: false,
2323
rewrite: (path) => path.replace(/^\\${PROXY_PATH}/, "").replace(/^(?!\\/)/, "/"),
2424
configure: (proxy) => {
25-
const bearer = \`Bearer sk_\${loadEnv("development", dirname(fileURLToPath(import.meta.url)), "ZITADEL_").ZITADEL_PROJECT_ID}\`;
25+
const projectId = loadEnv("development", dirname(fileURLToPath(import.meta.url)), "ZITADEL_").ZITADEL_PROJECT_ID;
26+
if (!projectId) {
27+
throw new Error("ZITADEL_PROJECT_ID is not set; add it to .env.local (zitadel setup writes it).");
28+
}
29+
const bearer = \`Bearer sk_\${projectId}\`;
2630
proxy.on("proxyReq", (proxyReq) => {
2731
proxyReq.setHeader("authorization", bearer);
2832
});

apps/cli/tests/unit/lib/orca/patchers/rule/angular/index.test.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ function writeContents(plan: ScaffoldPlan, path: string): string | undefined {
3333
return op?.contents;
3434
}
3535

36+
function packageJsonEdit(): (source: string | undefined) => string {
37+
const op = new AngularPatcher()
38+
.plan(ctx())
39+
.ops.find(
40+
(candidate): candidate is Extract<FileOp, { kind: "edit" }> =>
41+
candidate.kind === "edit" && candidate.path === "package.json",
42+
);
43+
if (!op) {
44+
throw new Error("expected a package.json edit op in the Angular plan");
45+
}
46+
return op.edit;
47+
}
48+
3649
describe("AngularPatcher.plan", () => {
3750
it("writes the managed root component, template, and proxy config", () => {
3851
const plan = new AngularPatcher().plan(ctx());
@@ -44,28 +57,20 @@ describe("AngularPatcher.plan", () => {
4457
});
4558

4659
it("adds a `dev` npm script so `npm run dev` works like the other frameworks", () => {
47-
const edit = new AngularPatcher()
48-
.plan(ctx())
49-
.ops.find(
50-
(op): op is Extract<FileOp, { kind: "edit" }> =>
51-
op.kind === "edit" && op.path === "package.json",
52-
);
53-
const result = JSON.parse(edit!.edit(`{ "scripts": { "start": "ng serve" } }`));
60+
const result = JSON.parse(packageJsonEdit()(`{ "scripts": { "start": "ng serve" } }`));
5461
expect(result.scripts.dev).toBe("ng serve");
5562
expect(result.scripts.start).toBe("ng serve");
5663
});
5764

5865
it("preserves an existing `dev` script instead of overwriting it", () => {
59-
const edit = new AngularPatcher()
60-
.plan(ctx())
61-
.ops.find(
62-
(op): op is Extract<FileOp, { kind: "edit" }> =>
63-
op.kind === "edit" && op.path === "package.json",
64-
);
65-
const result = JSON.parse(edit!.edit(`{ "scripts": { "dev": "ng serve --hmr" } }`));
66+
const result = JSON.parse(packageJsonEdit()(`{ "scripts": { "dev": "ng serve --hmr" } }`));
6667
expect(result.scripts.dev).toBe("ng serve --hmr");
6768
});
6869

70+
it("fails fast when package.json is absent instead of fabricating one", () => {
71+
expect(() => packageJsonEdit()(undefined)).toThrowError(/package\.json is required/);
72+
});
73+
6974
it("adds the SDK dependency at the CLI's prerelease tag", () => {
7075
const dep = new AngularPatcher()
7176
.plan(ctx())

0 commit comments

Comments
 (0)