@gjsify/cli 0.36.0 → 0.38.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/dist/cli.gjs.mjs +169 -169
- package/lib/actions/build.d.ts +7 -0
- package/lib/actions/build.js +18 -3
- package/lib/bundler-pick.js +61 -87
- package/lib/cli-fail.d.ts +4 -7
- package/lib/cli-fail.js +29 -55
- package/lib/commands/affected.js +161 -286
- package/lib/commands/install.d.ts +63 -66
- package/lib/commands/install.js +372 -536
- package/lib/config.d.ts +14 -0
- package/lib/config.js +18 -1
- package/lib/templates/install.mjs.tmpl +164 -75
- package/lib/types/cli-build-options.d.ts +33 -67
- package/lib/types/command.d.ts +9 -12
- package/lib/types/config-data.d.ts +137 -221
- package/lib/utils/auth-npmrc.d.ts +5 -10
- package/lib/utils/auth-npmrc.js +11 -21
- package/lib/utils/bin-shim.d.ts +40 -25
- package/lib/utils/bin-shim.js +51 -26
- package/lib/utils/clear-targets.d.ts +8 -11
- package/lib/utils/clear-targets.js +22 -29
- package/lib/utils/copy-targets.d.ts +13 -19
- package/lib/utils/copy-targets.js +31 -44
- package/lib/utils/dep-aggregation.d.ts +7 -15
- package/lib/utils/dep-aggregation.js +9 -22
- package/lib/utils/detect-native-packages.d.ts +89 -122
- package/lib/utils/detect-native-packages.js +119 -169
- package/lib/utils/dir-link.d.ts +29 -48
- package/lib/utils/dir-link.js +33 -56
- package/lib/utils/discover-showcases.d.ts +6 -7
- package/lib/utils/discover-showcases.js +11 -15
- package/lib/utils/dlx-cache.d.ts +29 -38
- package/lib/utils/dlx-cache.js +53 -63
- package/lib/utils/force-exit.d.ts +0 -3
- package/lib/utils/force-exit.js +13 -24
- package/lib/utils/gi-search-path.d.ts +16 -24
- package/lib/utils/gi-search-path.js +54 -82
- package/lib/utils/gjs-bundle-guard.d.ts +5 -13
- package/lib/utils/gjs-bundle-guard.js +35 -57
- package/lib/utils/gjs-source-escape.d.ts +2 -2
- package/lib/utils/gjs-source-escape.js +21 -39
- package/lib/utils/gjsify-shim.d.ts +17 -20
- package/lib/utils/gjsify-shim.js +80 -120
- package/lib/utils/html-entry.d.ts +8 -12
- package/lib/utils/html-entry.js +19 -28
- package/lib/utils/install-backend-native.d.ts +123 -161
- package/lib/utils/install-backend-native.js +536 -737
- package/lib/utils/install-backend.d.ts +42 -57
- package/lib/utils/install-backend.js +16 -21
- package/lib/utils/install-cache-fs.d.ts +10 -15
- package/lib/utils/install-cache-fs.js +18 -26
- package/lib/utils/install-lock.d.ts +5 -6
- package/lib/utils/install-lock.js +46 -65
- package/lib/utils/install-packument-cache.d.ts +27 -33
- package/lib/utils/install-packument-cache.js +44 -57
- package/lib/utils/install-progress.d.ts +2 -9
- package/lib/utils/install-progress.js +14 -39
- package/lib/utils/install-tarball-cache.d.ts +14 -20
- package/lib/utils/install-tarball-cache.js +41 -59
- package/lib/utils/library-output.d.ts +9 -22
- package/lib/utils/library-output.js +19 -37
- package/lib/utils/load-npmrc.d.ts +5 -6
- package/lib/utils/load-npmrc.js +18 -27
- package/lib/utils/node-bundle-guard.d.ts +4 -8
- package/lib/utils/node-bundle-guard.js +21 -35
- package/lib/utils/node-script.js +40 -53
- package/lib/utils/node-version.d.ts +5 -14
- package/lib/utils/node-version.js +15 -24
- package/lib/utils/normalize-bundler-options.d.ts +7 -11
- package/lib/utils/normalize-bundler-options.js +24 -42
- package/lib/utils/npm-oidc.d.ts +23 -37
- package/lib/utils/npm-oidc.js +28 -53
- package/lib/utils/npm-otp-cache.d.ts +4 -4
- package/lib/utils/npm-otp-cache.js +20 -25
- package/lib/utils/npm-otp.d.ts +20 -30
- package/lib/utils/npm-otp.js +32 -51
- package/lib/utils/onboard-probe.d.ts +7 -13
- package/lib/utils/onboard-probe.js +16 -27
- package/lib/utils/parse-spec.d.ts +3 -3
- package/lib/utils/parse-spec.js +4 -5
- package/lib/utils/pkg-json-edit.d.ts +7 -12
- package/lib/utils/pkg-json-edit.js +13 -26
- package/lib/utils/pkg-json.d.ts +11 -18
- package/lib/utils/pkg-json.js +19 -28
- package/lib/utils/prefixed-output.d.ts +3 -4
- package/lib/utils/prefixed-output.js +13 -19
- package/lib/utils/prompt.d.ts +3 -4
- package/lib/utils/prompt.js +14 -18
- package/lib/utils/publish-diagnose.d.ts +11 -16
- package/lib/utils/publish-diagnose.js +15 -28
- package/lib/utils/publish-headers.d.ts +7 -11
- package/lib/utils/publish-headers.js +36 -65
- package/lib/utils/publishable-packages.d.ts +4 -6
- package/lib/utils/publishable-packages.js +14 -18
- package/lib/utils/resolve-gjs-entry.d.ts +5 -8
- package/lib/utils/resolve-gjs-entry.js +10 -27
- package/lib/utils/resolve-npm-package.d.ts +6 -11
- package/lib/utils/resolve-npm-package.js +29 -63
- package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
- package/lib/utils/resolve-plugin-by-name.js +9 -14
- package/lib/utils/run-gjs.d.ts +16 -21
- package/lib/utils/run-gjs.js +31 -56
- package/lib/utils/run-lifecycle-script.d.ts +5 -9
- package/lib/utils/run-lifecycle-script.js +23 -42
- package/lib/utils/run-node.d.ts +21 -33
- package/lib/utils/run-node.js +43 -64
- package/lib/utils/runtimes.d.ts +25 -35
- package/lib/utils/runtimes.js +34 -46
- package/lib/utils/spawn.d.ts +32 -48
- package/lib/utils/spawn.js +51 -84
- package/lib/utils/stdin.d.ts +2 -2
- package/lib/utils/stdin.js +10 -25
- package/lib/utils/suggest.d.ts +6 -7
- package/lib/utils/suggest.js +8 -10
- package/lib/utils/system-gi.d.ts +36 -27
- package/lib/utils/system-gi.js +85 -114
- package/lib/utils/trust-registry.d.ts +4 -8
- package/lib/utils/trust-registry.js +7 -15
- package/lib/utils/win32-command.d.ts +8 -14
- package/lib/utils/win32-command.js +29 -43
- package/lib/utils/workspace-root.js +7 -12
- package/package.json +18 -18
package/lib/utils/prompt.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
// Interactive prompts for `gjsify login` / `gjsify trust`.
|
|
2
2
|
//
|
|
3
|
-
// On a TTY every prompt runs inside a single RAW-mode session (`runRawSession`)
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// line
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// resume/pause churn on the shared stdin singleton between two sequential
|
|
12
|
-
// prompts also intermittently dropped the line or resolved it empty.
|
|
13
|
-
// - Reading raw + handling `\r`/`\n` ourselves removes both failure modes.
|
|
3
|
+
// On a TTY every prompt runs inside a single RAW-mode session (`runRawSession`) reading
|
|
4
|
+
// key-by-key with manual echo and a guaranteed cooked-mode restore. It deliberately relies on
|
|
5
|
+
// neither the terminal's cooked line-discipline nor the shared `process.stdin` resume/pause
|
|
6
|
+
// cycle: a cooked read with the line discipline in an unexpected state (ICRNL off after a prior
|
|
7
|
+
// raw prompt) saw Enter arrive as a bare `\r` that never terminated the line, hanging the prompt
|
|
8
|
+
// showing `name^M`, and the resume/pause churn on the shared stdin singleton between sequential
|
|
9
|
+
// prompts intermittently dropped the line or resolved it empty. Handling `\r`/`\n` here removes
|
|
10
|
+
// both failure modes.
|
|
14
11
|
//
|
|
15
|
-
// Raw mode (via @gjsify/process → terminal-native)
|
|
16
|
-
//
|
|
17
|
-
//
|
|
12
|
+
// Raw mode (via @gjsify/process → terminal-native) also clears ISIG, so Ctrl-C arrives as a
|
|
13
|
+
// `\x03` keystroke handled below (restore + exit) rather than a SIGINT that would kill the
|
|
14
|
+
// process leaving the terminal in raw mode.
|
|
18
15
|
//
|
|
19
16
|
// Non-TTY stdin (piped input, CI) keeps a plain line read so
|
|
20
17
|
// `printf 'user\npass\n' | gjsify login` still works.
|
|
@@ -26,10 +23,9 @@ function isTtyStdin() {
|
|
|
26
23
|
return Boolean(stdin.isTTY) && typeof stdin.setRawMode === 'function';
|
|
27
24
|
}
|
|
28
25
|
/**
|
|
29
|
-
* Pure key handler for the raw-mode line editor — the single source of truth
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* passwords, and treats Ctrl-C as an interrupt rather than text.
|
|
26
|
+
* Pure key handler for the raw-mode line editor — the single source of truth for how a keystroke
|
|
27
|
+
* updates the buffer + echo. Pure (no I/O) so the rules are unit-testable: Enter is `\r` OR `\n`,
|
|
28
|
+
* passwords mask, and Ctrl-C is an interrupt rather than text.
|
|
33
29
|
*/
|
|
34
30
|
export function applyKey(buf, ch, mask) {
|
|
35
31
|
if (ch === '\r' || ch === '\n') {
|
|
@@ -3,36 +3,31 @@ export type Diagnose404Reason = 'dead-token' | 'live-token-404' | 'unknown';
|
|
|
3
3
|
export interface Diagnose404Result {
|
|
4
4
|
/** Discriminant — drives the JSON shape + exit-code path in the caller. */
|
|
5
5
|
reason: Diagnose404Reason;
|
|
6
|
-
/**
|
|
6
|
+
/** Set only when `reason === 'live-token-404'`. */
|
|
7
7
|
username?: string;
|
|
8
|
-
/** Multi-line
|
|
8
|
+
/** Multi-line hint, ready for stderr as-is. */
|
|
9
9
|
message: string;
|
|
10
10
|
}
|
|
11
11
|
interface Diagnose404Input {
|
|
12
12
|
/** Full package name including scope, e.g. `@gjsify/abort-controller`. */
|
|
13
13
|
packageName: string;
|
|
14
|
-
/** Pinned version we tried to publish. */
|
|
15
14
|
version: string;
|
|
16
|
-
/** Registry URL
|
|
15
|
+
/** Registry URL, with or without trailing slash. */
|
|
17
16
|
registry: string;
|
|
18
|
-
/**
|
|
17
|
+
/** Source of the Authorization header on the whoami probe. */
|
|
19
18
|
npmrc: NpmrcConfig | undefined;
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
|
-
* Probe `/-/whoami` to disambiguate the 404 cause. Best-effort:
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* Pure async I/O — no side effects, no process.exit, no console writes.
|
|
27
|
-
* The caller owns presentation (stderr vs stdout, plain vs JSON).
|
|
21
|
+
* Probe `/-/whoami` to disambiguate the 404 cause. Best-effort: a thrown error or
|
|
22
|
+
* non-2xx yields `reason: 'unknown'`, leaving the caller's generic error path
|
|
23
|
+
* untouched. No side effects and no writes — the caller owns presentation.
|
|
28
24
|
*/
|
|
29
25
|
export declare function diagnose404(input: Diagnose404Input): Promise<Diagnose404Result>;
|
|
30
26
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* keep the generic error path.
|
|
27
|
+
* Is the 404 body the "dead-token-or-missing-package" shape? npm's PUT answers
|
|
28
|
+
* with plain-text `Not Found` or an empty body; the JSON `{"error":"Not Found"}`
|
|
29
|
+
* form is accepted too. Any other 404 body — notably one carrying a structured npm
|
|
30
|
+
* error code — keeps the generic error path.
|
|
36
31
|
*/
|
|
37
32
|
export declare function is404DiagnosticCandidate(body: string): boolean;
|
|
38
33
|
export {};
|
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
// Dead-token vs new-package diagnostic for `gjsify publish`.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// A `404 Not Found` on the publish PUT is npm's shape for two very different
|
|
4
|
+
// causes, and guessing sends the user down the wrong path entirely: the
|
|
5
|
+
// `_authToken` has been revoked/expired (npm answers an unrecognised bearer with
|
|
6
|
+
// 404, not 401), or the scoped package genuinely is not on npm yet and needs the
|
|
7
|
+
// first-publish bootstrap from AGENTS.md.
|
|
5
8
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// user down the wrong path (Trusted-Publisher-bootstrap setup).
|
|
10
|
-
// 2. The scoped `@gjsify/<pkg>` genuinely doesn't exist on npm yet —
|
|
11
|
-
// the first-publish bootstrap step from AGENTS.md.
|
|
12
|
-
//
|
|
13
|
-
// `GET /-/whoami` with the SAME Authorization header disambiguates: a live
|
|
14
|
-
// token returns `{"username": "..."}`, a dead token returns `{}` (status 200
|
|
15
|
-
// in both cases — the empty body is npm's signal).
|
|
16
|
-
//
|
|
17
|
-
// `npm publish` from npm-cli handles dead tokens with `401 EOTP` + a clear
|
|
18
|
-
// "one-time-password or refresh the token" hint. The 404 path is npm's
|
|
19
|
-
// PUT-specific shape and `gjsify publish` previously just printed the
|
|
20
|
-
// opaque body. This helper closes the diagnostic gap.
|
|
9
|
+
// `GET /-/whoami` with the SAME Authorization header disambiguates: a live token
|
|
10
|
+
// returns `{"username": "…"}`, a dead one returns `{}` — status 200 for both, so
|
|
11
|
+
// the EMPTY BODY is the signal.
|
|
21
12
|
import { whoami } from '@gjsify/npm-registry';
|
|
22
13
|
/**
|
|
23
|
-
* Probe `/-/whoami` to disambiguate the 404 cause. Best-effort:
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* Pure async I/O — no side effects, no process.exit, no console writes.
|
|
28
|
-
* The caller owns presentation (stderr vs stdout, plain vs JSON).
|
|
14
|
+
* Probe `/-/whoami` to disambiguate the 404 cause. Best-effort: a thrown error or
|
|
15
|
+
* non-2xx yields `reason: 'unknown'`, leaving the caller's generic error path
|
|
16
|
+
* untouched. No side effects and no writes — the caller owns presentation.
|
|
29
17
|
*/
|
|
30
18
|
export async function diagnose404(input) {
|
|
31
19
|
const { packageName, version, registry, npmrc } = input;
|
|
@@ -85,11 +73,10 @@ function formatUnknown(name, version) {
|
|
|
85
73
|
return `gjsify publish: ${name}@${version} — 404 Not Found`;
|
|
86
74
|
}
|
|
87
75
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* keep the generic error path.
|
|
76
|
+
* Is the 404 body the "dead-token-or-missing-package" shape? npm's PUT answers
|
|
77
|
+
* with plain-text `Not Found` or an empty body; the JSON `{"error":"Not Found"}`
|
|
78
|
+
* form is accepted too. Any other 404 body — notably one carrying a structured npm
|
|
79
|
+
* error code — keeps the generic error path.
|
|
93
80
|
*/
|
|
94
81
|
export function is404DiagnosticCandidate(body) {
|
|
95
82
|
const trimmed = body.trim();
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { type NpmrcConfig } from '@gjsify/npm-registry';
|
|
2
2
|
/**
|
|
3
3
|
* Percent-encode a package name for the publish PUT path, matching
|
|
4
|
-
* npm-package-arg's `escapedName` convention
|
|
5
|
-
* `this.escapedName = name.replace('/', '%2f')`.
|
|
4
|
+
* npm-package-arg's `escapedName` convention.
|
|
6
5
|
*
|
|
7
6
|
* Scoped: `@scope/name` → `@scope%2fname` (literal `@`, lowercase `%2f`, the
|
|
8
7
|
* scope/name segments NOT otherwise re-encoded — npm only replaces the slash).
|
|
9
|
-
* Unscoped: returned unchanged
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* URL — `%2F` (uppercase) or a re-encoded `@` would miss the create route.
|
|
8
|
+
* Unscoped: returned unchanged. The registry is picky about this exact shape on
|
|
9
|
+
* the publish PUT URL — uppercase `%2F` or a re-encoded `@` misses the create
|
|
10
|
+
* route.
|
|
13
11
|
*/
|
|
14
12
|
export declare function escapePackageName(name: string): string;
|
|
15
13
|
export interface PublishHeaderOptions {
|
|
@@ -19,8 +17,7 @@ export interface PublishHeaderOptions {
|
|
|
19
17
|
otp?: string;
|
|
20
18
|
/**
|
|
21
19
|
* Override the `authorization` header (e.g. an OIDC Bearer token resolved
|
|
22
|
-
* after `buildHeaders` ran)
|
|
23
|
-
* resolution produced.
|
|
20
|
+
* after `buildHeaders` ran), replacing whatever npmrc auth resolution produced.
|
|
24
21
|
*/
|
|
25
22
|
authorization?: string;
|
|
26
23
|
/**
|
|
@@ -37,8 +34,7 @@ export interface PublishHeaderOptions {
|
|
|
37
34
|
* `npm-command: publish`, `npm-auth-type` (`web`, or `legacy` when `--otp`),
|
|
38
35
|
* `user-agent`, and `npm-otp` when an OTP is supplied.
|
|
39
36
|
*
|
|
40
|
-
* Notably
|
|
41
|
-
*
|
|
42
|
-
* is dropped to match.
|
|
37
|
+
* Notably NO `accept` header: `npm-registry-fetch` sends none on the publish PUT,
|
|
38
|
+
* and a wildcard here misses the create route.
|
|
43
39
|
*/
|
|
44
40
|
export declare function buildPublishHeaders(url: string, opts: PublishHeaderOptions): Record<string, string>;
|
|
@@ -3,48 +3,30 @@
|
|
|
3
3
|
// package correctly.
|
|
4
4
|
//
|
|
5
5
|
// Root cause this fixes: a first-publish of a never-before-published
|
|
6
|
-
// `@scope/<name>` via token auth
|
|
7
|
-
// `Not Found`, NOT a JSON npm error). That is a
|
|
8
|
-
// payload-validation failure — npm's write frontdoor keys the create path on
|
|
9
|
-
//
|
|
10
|
-
//
|
|
6
|
+
// `@scope/<name>` via token auth returned a bare `404 Not Found` (body
|
|
7
|
+
// `Not Found`, NOT a JSON npm error). That is a ROUTING miss, not a
|
|
8
|
+
// payload-validation failure — npm's write frontdoor keys the create path on the
|
|
9
|
+
// `npm-command` header, which gjsify was not sending. `npm publish` with the same
|
|
10
|
+
// ~/.npmrc token succeeds because libnpmpublish goes through
|
|
11
11
|
// `npm-registry-fetch`'s `getHeaders`, which always sets `npm-command`
|
|
12
|
-
// (+ `npm-auth-type`, + a real `user-agent`).
|
|
13
|
-
//
|
|
14
|
-
// Evidence (refs/npm-cli):
|
|
15
|
-
// - `lib/npm.js:404` `flat.npmCommand = this.command` → set for EVERY npm
|
|
16
|
-
// command; equals `"publish"` for `npm publish`.
|
|
17
|
-
// - `node_modules/npm-registry-fetch/lib/index.js:231-233`
|
|
18
|
-
// `if (opts.npmCommand) headers['npm-command'] = opts.npmCommand`
|
|
19
|
-
// and `:219-221` `if (opts.authType) headers['npm-auth-type'] = …`
|
|
20
|
-
// and `:215-217` `'user-agent': opts.userAgent`.
|
|
21
|
-
// - `workspaces/config/lib/definitions/definitions.js:282-290` — `auth-type`
|
|
22
|
-
// default `'web'`, flattened to `flatOptions.authType`.
|
|
23
|
-
// - `:1581-1587` — when `--otp` is set, npm forces `auth-type` to `'legacy'`
|
|
24
|
-
// before flattening, so an OTP publish sends `npm-auth-type: legacy`.
|
|
25
|
-
// - `node_modules/npm-package-arg/lib/npa.js:188`
|
|
26
|
-
// `this.escapedName = name.replace('/', '%2f')` — lowercase `%2f`,
|
|
27
|
-
// literal `@`. (gjsify's URL encoding already matches this — it was never
|
|
28
|
-
// the bug.)
|
|
12
|
+
// (+ `npm-auth-type`, + a real `user-agent`). Reference: `refs/npm-cli`.
|
|
29
13
|
//
|
|
30
14
|
// This module pulls in no `gi://` and no runtime fetch/Soup stack, so it is
|
|
31
|
-
// unit-testable on both Node and GJS. Its one filesystem touch is the
|
|
32
|
-
//
|
|
15
|
+
// unit-testable on both Node and GJS. Its one filesystem touch is the manifest
|
|
16
|
+
// walk backing the user-agent version — see `cliVersion()`.
|
|
33
17
|
import { readFileSync } from 'node:fs';
|
|
34
18
|
import { dirname } from 'node:path';
|
|
35
19
|
import { fileURLToPath } from 'node:url';
|
|
36
20
|
import { buildHeaders } from '@gjsify/npm-registry';
|
|
37
21
|
/**
|
|
38
22
|
* Percent-encode a package name for the publish PUT path, matching
|
|
39
|
-
* npm-package-arg's `escapedName` convention
|
|
40
|
-
* `this.escapedName = name.replace('/', '%2f')`.
|
|
23
|
+
* npm-package-arg's `escapedName` convention.
|
|
41
24
|
*
|
|
42
25
|
* Scoped: `@scope/name` → `@scope%2fname` (literal `@`, lowercase `%2f`, the
|
|
43
26
|
* scope/name segments NOT otherwise re-encoded — npm only replaces the slash).
|
|
44
|
-
* Unscoped: returned unchanged
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* URL — `%2F` (uppercase) or a re-encoded `@` would miss the create route.
|
|
27
|
+
* Unscoped: returned unchanged. The registry is picky about this exact shape on
|
|
28
|
+
* the publish PUT URL — uppercase `%2F` or a re-encoded `@` misses the create
|
|
29
|
+
* route.
|
|
48
30
|
*/
|
|
49
31
|
export function escapePackageName(name) {
|
|
50
32
|
return name.replace('/', '%2f');
|
|
@@ -57,34 +39,26 @@ export function escapePackageName(name) {
|
|
|
57
39
|
* `npm-command: publish`, `npm-auth-type` (`web`, or `legacy` when `--otp`),
|
|
58
40
|
* `user-agent`, and `npm-otp` when an OTP is supplied.
|
|
59
41
|
*
|
|
60
|
-
* Notably
|
|
61
|
-
*
|
|
62
|
-
* is dropped to match.
|
|
42
|
+
* Notably NO `accept` header: `npm-registry-fetch` sends none on the publish PUT,
|
|
43
|
+
* and a wildcard here misses the create route.
|
|
63
44
|
*/
|
|
64
45
|
export function buildPublishHeaders(url, opts) {
|
|
65
46
|
const headers = buildHeaders(url, { npmrc: opts.npmrc });
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// we keep it harmless and explicit by leaving it as buildHeaders set it.)
|
|
71
|
-
// content-type: application/json — npm-registry-fetch sets this for any
|
|
72
|
-
// non-string/non-stream/non-buffer body (index.js:63-67); our body is a
|
|
73
|
-
// JSON string, so we set it explicitly.
|
|
47
|
+
// `buildHeaders`' `accept-encoding: identity` default is left as it set it —
|
|
48
|
+
// the PUT body is JSON we generated, so the encoding header is irrelevant here.
|
|
49
|
+
// npm-registry-fetch sets this for any non-string/non-stream/non-buffer body;
|
|
50
|
+
// ours is a JSON string, so it is set explicitly.
|
|
74
51
|
headers['content-type'] = 'application/json';
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
52
|
+
// THE routing header: npm sets it for every command, and the registry's write
|
|
53
|
+
// frontdoor uses it to route the create-package PUT. Missing it = bare
|
|
54
|
+
// `404 Not Found` on a new package.
|
|
78
55
|
headers['npm-command'] = 'publish';
|
|
79
|
-
// npm
|
|
80
|
-
// (definitions.js:1583-1586). Sent by getHeaders (index.js:219-221).
|
|
56
|
+
// npm forces auth-type `legacy` whenever `--otp` is set, `web` otherwise.
|
|
81
57
|
headers['npm-auth-type'] = opts.otp ? 'legacy' : 'web';
|
|
82
|
-
// user-agent — a real npm-shaped UA (getHeaders index.js:215-217).
|
|
83
58
|
headers['user-agent'] = opts.userAgent ?? defaultUserAgent();
|
|
84
|
-
// npm-otp — only when an OTP is supplied (index.js:243-245).
|
|
85
59
|
if (opts.otp)
|
|
86
60
|
headers['npm-otp'] = opts.otp;
|
|
87
|
-
//
|
|
61
|
+
// OIDC token resolved after npmrc auth.
|
|
88
62
|
if (opts.authorization)
|
|
89
63
|
headers['authorization'] = opts.authorization;
|
|
90
64
|
return headers;
|
|
@@ -94,21 +68,18 @@ export function buildPublishHeaders(url, opts) {
|
|
|
94
68
|
* `@gjsify/cli` is a DUAL-ENTRY package and neither layer covers both entries:
|
|
95
69
|
*
|
|
96
70
|
* 1. **`__PACKAGE_VERSION__`** — the compile-time define
|
|
97
|
-
* (`package.json#gjsify.defineFromPackageJson`), the
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* define alone would therefore have left the Node entry — the one `npm
|
|
102
|
-
* install @gjsify/cli` actually runs — on a permanent placeholder.
|
|
71
|
+
* (`package.json#gjsify.defineFromPackageJson`). Zero I/O, but the bundler only
|
|
72
|
+
* writes `dist/cli.gjs.mjs`; the npm-wired Node bin under `lib/` comes from
|
|
73
|
+
* plain `tsc`, which knows nothing about defines. A define alone would leave the
|
|
74
|
+
* Node entry — the one `npm install @gjsify/cli` runs — on a permanent placeholder.
|
|
103
75
|
*
|
|
104
76
|
* 2. **an upward manifest walk** for the nearest `package.json` naming
|
|
105
77
|
* `@gjsify/cli`, covering `lib/`. Deliberately NOT a fixed
|
|
106
|
-
* `new URL('../../package.json', import.meta.url)` read: that resolves
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* `dist/`, and from an installed `node_modules/@gjsify/cli/**` alike.
|
|
78
|
+
* `new URL('../../package.json', import.meta.url)` read: that resolves correctly
|
|
79
|
+
* from `lib/utils/` but points one directory too high from `dist/`, and the
|
|
80
|
+
* build-time `inlineStaticReads` pass that would have frozen it is scoped to
|
|
81
|
+
* `node_modules` paths. The walk is depth-independent, so it works from `src/`,
|
|
82
|
+
* `lib/`, `dist/` and an installed `node_modules/@gjsify/cli/**` alike.
|
|
112
83
|
*
|
|
113
84
|
* `GJSIFY_CLI_PACKAGE_JSON` overrides both, matching `self-update.ts`.
|
|
114
85
|
*/
|
|
@@ -147,10 +118,10 @@ function walkForCliVersion() {
|
|
|
147
118
|
return '';
|
|
148
119
|
}
|
|
149
120
|
/**
|
|
150
|
-
* npm-CLI-shaped user-agent
|
|
151
|
-
* `
|
|
152
|
-
*
|
|
153
|
-
*
|
|
121
|
+
* npm-CLI-shaped user-agent, mirroring npm-registry-fetch's
|
|
122
|
+
* `npm@<v>/node@<v>+<arch> (<platform>)` with the gjsify CLI identity substituted.
|
|
123
|
+
* The registry does not validate the value; it must merely be a non-placeholder UA,
|
|
124
|
+
* which some WAF / CDN frontdoors require.
|
|
154
125
|
*/
|
|
155
126
|
function defaultUserAgent() {
|
|
156
127
|
const proc = globalThis.process;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { Workspace } from '@gjsify/workspace';
|
|
2
2
|
/**
|
|
3
3
|
* Package-group directories holding publishable packages that are NOT root
|
|
4
|
-
* workspaces.
|
|
5
|
-
*
|
|
6
|
-
* silently broken first release.
|
|
4
|
+
* workspaces. Extend this the moment a new such group appears — the list is the
|
|
5
|
+
* only thing standing between one and a silently broken first release.
|
|
7
6
|
*/
|
|
8
7
|
export declare const NON_WORKSPACE_PUBLISHABLE_DIRS: readonly [readonly ["packages", "node-gi"], readonly ["packages", "napi"]];
|
|
9
8
|
/**
|
|
10
9
|
* Publishable packages in {@link NON_WORKSPACE_PUBLISHABLE_DIRS}, shaped as
|
|
11
10
|
* `Workspace` entries so callers can treat them like any other package.
|
|
12
11
|
*
|
|
13
|
-
* Best-effort by design
|
|
14
|
-
*
|
|
15
|
-
* value is covering everything they can reach.
|
|
12
|
+
* Best-effort by design — a missing directory or malformed manifest is skipped,
|
|
13
|
+
* not thrown: this runs inside sweeps whose value is covering everything reachable.
|
|
16
14
|
*/
|
|
17
15
|
export declare function discoverNonWorkspacePublishables(cwd: string): Workspace[];
|
|
18
16
|
/**
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
// Publishable-package discovery shared by `gjsify trust` and `gjsify onboard`.
|
|
2
2
|
//
|
|
3
|
-
// `discoverWorkspaces` only sees ROOT WORKSPACES
|
|
3
|
+
// `discoverWorkspaces` only sees ROOT WORKSPACES, and some publishable packages
|
|
4
4
|
// deliberately are not: each is an independent native engine with its own CI
|
|
5
|
-
// workflow, kept out of the
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// done" on 2026-07-26 while `@gjsify/napi` was neither in the list nor
|
|
13
|
-
// published at all, because only `packages/node-gi/*` was carved out.
|
|
5
|
+
// workflow, kept out of the graph so a full `gjsify foreach` never tries to build
|
|
6
|
+
// it. They still publish through `release.yml`, so a sweep that reasons about
|
|
7
|
+
// "what do we publish" must include them — otherwise it silently under-reports and
|
|
8
|
+
// the package's first release fails the OIDC exchange with `404 — package not
|
|
9
|
+
// found`. Measured: `gjsify onboard` called 127 packages "already done" while
|
|
10
|
+
// `@gjsify/napi` was neither listed nor published, because only
|
|
11
|
+
// `packages/node-gi/*` had been carved out.
|
|
14
12
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
15
13
|
import { dirname, join } from 'node:path';
|
|
16
14
|
/**
|
|
17
15
|
* Package-group directories holding publishable packages that are NOT root
|
|
18
|
-
* workspaces.
|
|
19
|
-
*
|
|
20
|
-
* silently broken first release.
|
|
16
|
+
* workspaces. Extend this the moment a new such group appears — the list is the
|
|
17
|
+
* only thing standing between one and a silently broken first release.
|
|
21
18
|
*/
|
|
22
19
|
export const NON_WORKSPACE_PUBLISHABLE_DIRS = [
|
|
23
20
|
['packages', 'node-gi'], // @gjsify/node-gi + the @gjsify/gtk-runtime-* bundles
|
|
@@ -27,13 +24,12 @@ export const NON_WORKSPACE_PUBLISHABLE_DIRS = [
|
|
|
27
24
|
* Publishable packages in {@link NON_WORKSPACE_PUBLISHABLE_DIRS}, shaped as
|
|
28
25
|
* `Workspace` entries so callers can treat them like any other package.
|
|
29
26
|
*
|
|
30
|
-
* Best-effort by design
|
|
31
|
-
*
|
|
32
|
-
* value is covering everything they can reach.
|
|
27
|
+
* Best-effort by design — a missing directory or malformed manifest is skipped,
|
|
28
|
+
* not thrown: this runs inside sweeps whose value is covering everything reachable.
|
|
33
29
|
*/
|
|
34
30
|
export function discoverNonWorkspacePublishables(cwd) {
|
|
35
|
-
// Walk up to the repo root
|
|
36
|
-
//
|
|
31
|
+
// Walk up to the repo root, anchored on the FIRST configured group rather than
|
|
32
|
+
// on `packages/` itself, which also exists inside individual packages.
|
|
37
33
|
let root = cwd;
|
|
38
34
|
const anchor = NON_WORKSPACE_PUBLISHABLE_DIRS[0];
|
|
39
35
|
for (let i = 0; i < 8; i++) {
|
|
@@ -5,14 +5,11 @@ interface ResolvedEntry {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function resolveGjsEntry(pkgDir: string, binName: string | null): ResolvedEntry;
|
|
7
7
|
/**
|
|
8
|
-
* Resolve the `--app node` bundle
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `build:node → dist/<name>.node.mjs` convention; the GJS entry is
|
|
14
|
-
* `dist/<name>.gjs.js`, so the extension can differ).
|
|
15
|
-
* Hard-fails with an actionable hint when neither is found.
|
|
8
|
+
* Resolve the `--app node` bundle, which node/bun/deno all consume. An explicit
|
|
9
|
+
* `gjsify.example.node` wins; otherwise the node twin of the GJS entry is derived by
|
|
10
|
+
* swapping the `.gjs.<ext>` infix for `.node.<ext>` and probing `mjs`/`js`/`cjs` —
|
|
11
|
+
* the extension can differ, since `build:node` emits `dist/<name>.node.mjs` while the
|
|
12
|
+
* GJS entry is `dist/<name>.gjs.js`.
|
|
16
13
|
*/
|
|
17
14
|
export declare function resolveNodeEntry(pkgDir: string): string;
|
|
18
15
|
export {};
|
|
@@ -1,20 +1,8 @@
|
|
|
1
|
-
// Resolve the GJS entry point of an installed package
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// "gjsify": {
|
|
7
|
-
// "main": "dist/gjs.js",
|
|
8
|
-
// "bin": { "name-a": "dist/a.js", "name-b": "dist/b.js" }
|
|
9
|
-
// }
|
|
10
|
-
// }
|
|
11
|
-
//
|
|
12
|
-
// Resolution order:
|
|
13
|
-
// 1. user-supplied bin name + `gjsify.bin[name]` → that path
|
|
14
|
-
// 2. single-entry `gjsify.bin` → the only path
|
|
15
|
-
// 3. `gjsify.main` → that path
|
|
16
|
-
// 4. fallback: `package.json#main` → that path (advisory warning)
|
|
17
|
-
// 5. otherwise: hard-fail with a fix hint
|
|
1
|
+
// Resolve the GJS entry point of an installed package, per the `gjsify` field
|
|
2
|
+
// convention. In order: a requested `gjsify.bin[name]`, a single-entry
|
|
3
|
+
// `gjsify.bin`, `gjsify.main`, then `package.json#main` — the last flagged
|
|
4
|
+
// `fromFallback` so the caller can warn (`commands/dlx.ts` does). No entry at all,
|
|
5
|
+
// or several bins with none named, hard-fails with a fix hint.
|
|
18
6
|
import { readFileSync } from 'node:fs';
|
|
19
7
|
import { existsSync } from 'node:fs';
|
|
20
8
|
import { join, resolve } from 'node:path';
|
|
@@ -64,14 +52,11 @@ export function resolveGjsEntry(pkgDir, binName) {
|
|
|
64
52
|
return { bundlePath, binName: resolvedBin, fromFallback };
|
|
65
53
|
}
|
|
66
54
|
/**
|
|
67
|
-
* Resolve the `--app node` bundle
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* `build:node → dist/<name>.node.mjs` convention; the GJS entry is
|
|
73
|
-
* `dist/<name>.gjs.js`, so the extension can differ).
|
|
74
|
-
* Hard-fails with an actionable hint when neither is found.
|
|
55
|
+
* Resolve the `--app node` bundle, which node/bun/deno all consume. An explicit
|
|
56
|
+
* `gjsify.example.node` wins; otherwise the node twin of the GJS entry is derived by
|
|
57
|
+
* swapping the `.gjs.<ext>` infix for `.node.<ext>` and probing `mjs`/`js`/`cjs` —
|
|
58
|
+
* the extension can differ, since `build:node` emits `dist/<name>.node.mjs` while the
|
|
59
|
+
* GJS entry is `dist/<name>.gjs.js`.
|
|
75
60
|
*/
|
|
76
61
|
export function resolveNodeEntry(pkgDir) {
|
|
77
62
|
const pkgJsonPath = join(pkgDir, 'package.json');
|
|
@@ -79,7 +64,6 @@ export function resolveNodeEntry(pkgDir) {
|
|
|
79
64
|
throw new Error(`no package.json found at ${pkgDir}`);
|
|
80
65
|
}
|
|
81
66
|
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
82
|
-
// 1. Explicit declaration wins.
|
|
83
67
|
const explicit = pkg.gjsify?.example?.node;
|
|
84
68
|
if (explicit) {
|
|
85
69
|
const p = resolve(pkgDir, explicit);
|
|
@@ -88,7 +72,6 @@ export function resolveNodeEntry(pkgDir) {
|
|
|
88
72
|
}
|
|
89
73
|
return p;
|
|
90
74
|
}
|
|
91
|
-
// 2. Derive from the GJS entry.
|
|
92
75
|
const gjsEntry = pkg.gjsify?.main ?? (pkg.gjsify?.bin ? Object.values(pkg.gjsify.bin)[0] : undefined) ?? pkg.main;
|
|
93
76
|
if (!gjsEntry) {
|
|
94
77
|
throw new Error(`package "${pkg.name ?? pkgDir}" has no entry to derive a node bundle from — ` +
|
|
@@ -2,20 +2,15 @@ export interface ResolveNpmPackageOptions {
|
|
|
2
2
|
/** Anchor directory to search from (typically the caller's cwd). */
|
|
3
3
|
cwd?: string;
|
|
4
4
|
/**
|
|
5
|
-
* Bundle path or module URL to anchor the bundle-side createRequire
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* invoked from an unrelated cwd.
|
|
5
|
+
* Bundle path or module URL to anchor the bundle-side createRequire at. Pass
|
|
6
|
+
* `import.meta.url` from the call site so the bundle's own node_modules chain stays
|
|
7
|
+
* reachable even when the CLI is invoked from an unrelated cwd.
|
|
9
8
|
*/
|
|
10
9
|
bundleUrl?: string;
|
|
11
10
|
}
|
|
12
11
|
/**
|
|
13
|
-
* Resolve a bare npm specifier through
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Each anchor is tried in priority order — `GJSIFY_NODE_PATH` (highest)
|
|
18
|
-
* → caller cwd → workspace root → bundle URL → parent-dir walk from
|
|
19
|
-
* cwd. The first anchor whose `require.resolve()` succeeds wins.
|
|
12
|
+
* Resolve a bare npm specifier through the anchors listed above, returning the resolved
|
|
13
|
+
* module's absolute path or null when every anchor failed. Callers usually wrap the result in
|
|
14
|
+
* `pathToFileURL().href` before a dynamic `import(...)`.
|
|
20
15
|
*/
|
|
21
16
|
export declare function resolveNpmPackage(specifier: string, opts?: ResolveNpmPackageOptions): string | null;
|