@juuno-sdk/cli 4.0.2 → 4.1.0

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.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Reading a deploy response, and saying what happened.
3
+ *
4
+ * Two functions, split so the decision is separable from the printing:
5
+ * `describeOutcome` is pure and decides, `report` performs. Kept apart from the
6
+ * command because the distinction they draw is a safety property rather than
7
+ * presentation — a 404 means the platform refused before anything reached
8
+ * Cloudflare, so nothing is live and the CLI can fail loudly; any other failure
9
+ * may have uploaded the script and then failed a later step, so the server's own
10
+ * message is the only accurate account of what happened and is passed through
11
+ * untouched.
12
+ *
13
+ * Getting that backwards would either hide a live half-deploy behind a tidy
14
+ * error, or tell a developer to go and register something that already exists.
15
+ */
16
+ /** The subset of the deploy endpoint's JSON this command reads. */
17
+ export interface DeployResponse {
18
+ success?: boolean;
19
+ message?: string;
20
+ error?: string;
21
+ errors?: unknown;
22
+ schema_version?: string | number | null;
23
+ }
24
+ export type DeployOutcome =
25
+ /** Deployed. `lines` is what to print, in order. */
26
+ {
27
+ kind: 'success';
28
+ lines: string[];
29
+ }
30
+ /**
31
+ * No backend is registered for this script. Nothing was uploaded, so this is
32
+ * safe to raise as an ordinary error with a fix attached.
33
+ */
34
+ | {
35
+ kind: 'unregistered';
36
+ message: string;
37
+ }
38
+ /**
39
+ * Something else went wrong, possibly after the upload. Reported rather than
40
+ * thrown, so the server's account survives verbatim instead of being wrapped.
41
+ */
42
+ | {
43
+ kind: 'failed';
44
+ message: string;
45
+ details?: string;
46
+ };
47
+ /**
48
+ * What the response means, given the modules that were sent.
49
+ *
50
+ * A body of `{ success: false }` on a 200 counts as a failure — the endpoint
51
+ * reports step failures in the body, so the HTTP status alone is not the answer.
52
+ */
53
+ export declare function describeOutcome(status: number, body: DeployResponse, moduleNames: string[], api: string): DeployOutcome;
54
+ /**
55
+ * Turn an outcome into output and an exit code.
56
+ *
57
+ * Only `unregistered` throws, and only because nothing was uploaded: the caller
58
+ * wraps a throw as a one-line `❌ …`, which is the right shape for "this was
59
+ * refused, here is the fix". A failure that may have half-landed must not be
60
+ * wrapped, so it is written straight to stderr and the exit code is set by hand.
61
+ */
62
+ export declare function report(outcome: DeployOutcome): void;
63
+ //# sourceMappingURL=outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../../../src/deploy-backend/outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,MAAM,aAAa;AACvB,oDAAoD;AAClD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE;AACtC;;;GAGG;GACD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC3C;;;GAGG;GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,cAAc,EACpB,WAAW,EAAE,MAAM,EAAE,EACrB,GAAG,EAAE,MAAM,GACV,aAAa,CA+Cf;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAmBnD"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Pure helpers for a Workers-for-Platforms deploy: building the payload from a
3
+ * worker's wrangler.jsonc, and resolving where to send it.
4
+ *
5
+ * Kept free of I/O so the part that fails silently — the metadata — is directly
6
+ * testable. See wrangler-config.test.ts.
7
+ */
8
+ /** A wrangler.jsonc, as much of it as this deploy path cares about. */
9
+ export interface WranglerConfig {
10
+ name?: string;
11
+ main?: string;
12
+ compatibility_date?: string;
13
+ compatibility_flags?: string[];
14
+ vars?: Record<string, unknown>;
15
+ kv_namespaces?: ({
16
+ binding: string;
17
+ } & Record<string, unknown>)[];
18
+ d1_databases?: ({
19
+ binding: string;
20
+ } & Record<string, unknown>)[];
21
+ env?: Record<string, WranglerConfig>;
22
+ [key: string]: unknown;
23
+ }
24
+ export interface DeployBinding {
25
+ type: 'kv_namespace' | 'd1' | 'plain_text';
26
+ name: string;
27
+ text?: string;
28
+ }
29
+ export interface DeployMetadata {
30
+ main_module: string;
31
+ compatibility_date: string;
32
+ compatibility_flags?: string[];
33
+ bindings?: DeployBinding[];
34
+ }
35
+ /**
36
+ * Normalise the API base, and refuse to put the developer token on a cleartext
37
+ * connection.
38
+ *
39
+ * Loopback stays exempt so a local Laravel over http still works. Anything else
40
+ * must be https, because the next thing the caller does with this value is send
41
+ * a bearer token to it.
42
+ */
43
+ export declare function resolveApiUrl(value: string): string;
44
+ /**
45
+ * Strip comments and trailing commas from JSONC so JSON.parse accepts it.
46
+ *
47
+ * String-aware: a `//` or block-comment opener inside a quoted value is left
48
+ * alone, as is an escaped quote. Written by hand rather than pulling in a parser
49
+ * because this is the only JSONC we read and the input is our own.
50
+ *
51
+ * The complexity is suppressed rather than refactored. This is a character
52
+ * scanner: four mutually-exclusive states over one cursor, sharing `out`, `i`
53
+ * and three flags. Extracting the branches means threading all of that through
54
+ * helpers or hoisting it into an object, which trades a shape every reader
55
+ * recognises for one nobody does — and a state machine you have to reassemble
56
+ * across functions is exactly where an off-by-one hides. The 31 cases in
57
+ * `__tests__/wrangler-config.test.ts` are what keeps it honest instead.
58
+ */
59
+ export declare function stripJsonc(source: string): string;
60
+ export declare function parseWranglerConfig(source: string): WranglerConfig;
61
+ /**
62
+ * Flatten a wrangler config for one environment.
63
+ *
64
+ * Inherited keys come from the top level unless the environment overrides them;
65
+ * the binding keys in NOT_INHERITED come from the environment alone, matching
66
+ * wrangler's own rule.
67
+ */
68
+ export declare function resolveEnvConfig(config: WranglerConfig, env: string): WranglerConfig;
69
+ /**
70
+ * The entry module's name as it appears in the dry-run output. wrangler compiles
71
+ * `./src/index.ts` to `dist/index.js`, so the extension is normalised.
72
+ */
73
+ export declare function entryModuleName(main: string): string;
74
+ /**
75
+ * Build the `metadata` part for the platform deploy endpoint.
76
+ *
77
+ * Store bindings are emitted **by name only**. The platform provisions the
78
+ * store and fills the id — sending one would make it adopt an existing store
79
+ * instead, and nothing would report that it had. Note the two are not
80
+ * symmetric: KV's id field is `namespace_id`, D1's is `id`, and neither is set
81
+ * here.
82
+ */
83
+ export declare function buildMetadata(resolved: WranglerConfig): DeployMetadata;
84
+ /**
85
+ * The tenant script name wrangler will have built for this environment.
86
+ *
87
+ * Read off the raw config rather than the resolved one because the rule is not
88
+ * inheritance: an env that declares its own `name` uses it verbatim, and one
89
+ * that does not gets `<top-level-name>-<env>`. The merged config cannot tell the
90
+ * two apart, so reading it would address the upload to `google-reviews` for a
91
+ * bundle wrangler built as `google-reviews-prod` — refused as unregistered, or
92
+ * worse, landing on whichever tenant already holds that name.
93
+ */
94
+ export declare function scriptName(config: WranglerConfig, env: string): string;
95
+ /** Files in the dry-run output that belong in the upload. */
96
+ export declare function isModuleFile(filename: string): boolean;
97
+ /**
98
+ * The Content-Type of a module part, which is how Cloudflare decides what kind
99
+ * of module it is — not a formality. `application/javascript+module` makes it an
100
+ * ES module and `application/wasm` a CompiledWasm one, so labelling a `.wasm`
101
+ * as JavaScript would have Cloudflare try to parse the binary as source and
102
+ * reject the upload. isModuleFile() already accepts `.wasm`, so the two have to
103
+ * agree about what that means.
104
+ */
105
+ export declare function moduleContentType(filename: string): string;
106
+ //# sourceMappingURL=wrangler-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrangler-config.d.ts","sourceRoot":"","sources":["../../../../src/deploy-backend/wrangler-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAI/B,aAAa,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,YAAY,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,YAAY,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B;AAID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBnD;AAoDD;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmEjD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAElE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,MAAM,GACV,cAAc,CAqBhB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc,CAwDtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAYtE;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAItD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D"}
@@ -0,0 +1,24 @@
1
+ import crossSpawn from 'cross-spawn';
2
+ /**
3
+ * Spawning a package-manager binary, portably.
4
+ *
5
+ * `npx`, `pnpm` and `yarn` are shell scripts on POSIX and `.cmd` shims on
6
+ * Windows, and neither Node nor libuv will run a `.cmd` from `spawn` without a
7
+ * shell — `CreateProcess` cannot execute one, and since the fix for
8
+ * CVE-2024-27980 Node refuses rather than silently routing through cmd.exe.
9
+ * Naming the shim (`npx.cmd`) fixes PATH resolution and not the spawn.
10
+ *
11
+ * Doing it correctly means invoking `cmd.exe /d /s /c` with the command and
12
+ * every argument escaped for cmd's parser, plus a second round of escaping for
13
+ * shims under `node_modules/.bin`, and `windowsVerbatimArguments` so Node does
14
+ * not re-quote what was just escaped. `shell: true` is the wrong shortcut: it
15
+ * hands the whole line to cmd.exe to re-parse, so a worker directory containing
16
+ * `&` or `^` becomes a command.
17
+ *
18
+ * cross-spawn already does all of it, is the ecosystem's answer to this exact
19
+ * problem, and was already in the tree transitively. Preferred over a local
20
+ * reimplementation, which is where the escaping bugs live.
21
+ */
22
+ export declare const spawn: typeof crossSpawn;
23
+ export declare const spawnSync: typeof import("node:child_process").spawnSync;
24
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../../../src/utils/bin.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,KAAK,mBAAa,CAAC;AAChC,eAAO,MAAM,SAAS,+CAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juuno-sdk/cli",
3
- "version": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "Official CLI for Juuno external app development: deploy, list and inspect apps",
5
5
  "private": false,
6
6
  "type": "module",
@@ -19,15 +19,19 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
+ "cross-spawn": "^7.0.6",
22
23
  "inquirer": "^14.0.2",
23
24
  "jszip": "^3.10.1",
24
25
  "semver": "^7.8.5"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@tsconfig/node22": "^22.0.5",
29
+ "@types/cross-spawn": "^6.0.6",
28
30
  "@types/node": "^26.2.0",
29
31
  "@types/semver": "^7.8.0",
30
32
  "@vue/tsconfig": "^0.9.1",
33
+ "eslint": "^10.8.1",
34
+ "oxlint": "^1.77.0",
31
35
  "typescript": "^5.9.3",
32
36
  "vitest": "^4.1.10",
33
37
  "@juuno/eslint-config": "0.0.1"
@@ -49,7 +53,11 @@
49
53
  "dev": "pnpm build && node ./bin/cli.js",
50
54
  "build": "pnpm build:cli",
51
55
  "build:cli": "tsc --project tsconfig.build.json",
52
- "type-check": "tsc --project tsconfig.build.json --noEmit",
56
+ "type-check": "tsc --project tsconfig.vitest.json",
57
+ "lint": "pnpm lint:oxlint && pnpm lint:eslint",
58
+ "lint:oxlint": "oxlint",
59
+ "lint:eslint": "eslint .",
60
+ "lint:fix": "eslint . --fix",
53
61
  "test": "pnpm test:unit",
54
62
  "test:unit": "vitest run",
55
63
  "test:unit:watch": "vitest"