@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.
- package/README.md +40 -0
- package/bin/cli-router.js +90 -0
- package/bin/cli.js +25 -4
- package/dist/cli/src/deploy/index.js +81 -1
- package/dist/cli/src/deploy-backend/context.js +62 -0
- package/dist/cli/src/deploy-backend/index.js +196 -0
- package/dist/cli/src/deploy-backend/outcome.js +85 -0
- package/dist/cli/src/deploy-backend/wrangler-config.js +271 -0
- package/dist/cli/src/utils/bin.js +23 -0
- package/dist/cli/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/deploy/index.d.ts +32 -0
- package/dist/types/src/deploy/index.d.ts.map +1 -1
- package/dist/types/src/deploy-backend/context.d.ts +29 -0
- package/dist/types/src/deploy-backend/context.d.ts.map +1 -0
- package/dist/types/src/deploy-backend/index.d.ts +54 -0
- package/dist/types/src/deploy-backend/index.d.ts.map +1 -0
- package/dist/types/src/deploy-backend/outcome.d.ts +63 -0
- package/dist/types/src/deploy-backend/outcome.d.ts.map +1 -0
- package/dist/types/src/deploy-backend/wrangler-config.d.ts +106 -0
- package/dist/types/src/deploy-backend/wrangler-config.d.ts.map +1 -0
- package/dist/types/src/utils/bin.d.ts +24 -0
- package/dist/types/src/utils/bin.d.ts.map +1 -0
- package/package.json +10 -2
|
@@ -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
|
|
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.
|
|
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"
|