@gjsify/cli 0.26.0 → 0.26.1
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 +47 -47
- package/lib/commands/affected.js +146 -15
- package/lib/utils/build-cache.js +7 -2
- package/lib/utils/dir-link.d.ts +58 -0
- package/lib/utils/dir-link.js +126 -0
- package/lib/utils/dlx-cache.d.ts +30 -3
- package/lib/utils/dlx-cache.js +61 -7
- package/lib/utils/install-packument-cache.d.ts +23 -0
- package/lib/utils/install-packument-cache.js +45 -10
- package/package.json +17 -17
|
@@ -13,19 +13,22 @@
|
|
|
13
13
|
//
|
|
14
14
|
// Layout:
|
|
15
15
|
//
|
|
16
|
-
// $XDG_CACHE_HOME/gjsify/metadata/
|
|
16
|
+
// $XDG_CACHE_HOME/gjsify/metadata/v2/<shard>/<encoded-registry>,<encoded-name>.json
|
|
17
17
|
//
|
|
18
18
|
// The cache is keyed by (registry, name) — NOT name alone — so switching the
|
|
19
19
|
// registry for a scope can never serve a packument from the wrong source on a
|
|
20
20
|
// coincidental ETag match. `<shard>` is a 2-hex FNV-1a digest of the key, a
|
|
21
21
|
// directory-fan-out step so the leaf dir never grows unbounded (same rationale
|
|
22
|
-
// as the tarball cache's hex sharding). `
|
|
22
|
+
// as the tarball cache's hex sharding). `v2` is a layout version.
|
|
23
23
|
//
|
|
24
24
|
// Disabled with `GJSIFY_PACKUMENT_CACHE=0` (or `false`). Honours
|
|
25
25
|
// `XDG_CACHE_HOME` like the tarball + dlx caches.
|
|
26
26
|
import { join } from 'node:path';
|
|
27
27
|
import { atomicWrite, gjsifyCacheRoot, readCacheFile } from './install-cache-fs.js';
|
|
28
|
-
|
|
28
|
+
// v2: the separator changed from the Win32-reserved `|` to `,` (see `pathFor`),
|
|
29
|
+
// so every v1 entry is unreachable rather than merely stale — a new root retires
|
|
30
|
+
// them instead of leaving orphans under the old one.
|
|
31
|
+
const CACHE_LAYOUT_VERSION = 'v2';
|
|
29
32
|
/** `true` unless `GJSIFY_PACKUMENT_CACHE` is `0` / `false` / empty. */
|
|
30
33
|
function isEnabled() {
|
|
31
34
|
const flag = process.env.GJSIFY_PACKUMENT_CACHE;
|
|
@@ -34,7 +37,7 @@ function isEnabled() {
|
|
|
34
37
|
const trimmed = flag.trim();
|
|
35
38
|
return !(trimmed === '0' || trimmed === 'false' || trimmed === '');
|
|
36
39
|
}
|
|
37
|
-
/** Root of the packument cache: `$XDG_CACHE_HOME/gjsify/metadata/
|
|
40
|
+
/** Root of the packument cache: `$XDG_CACHE_HOME/gjsify/metadata/v2`. */
|
|
38
41
|
function cacheRoot() {
|
|
39
42
|
return gjsifyCacheRoot('metadata', CACHE_LAYOUT_VERSION);
|
|
40
43
|
}
|
|
@@ -47,15 +50,47 @@ function shardFor(key) {
|
|
|
47
50
|
}
|
|
48
51
|
return ((h >>> 0) & 0xff).toString(16).padStart(2, '0');
|
|
49
52
|
}
|
|
50
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* One path part, made safe for EVERY filesystem we run on.
|
|
55
|
+
*
|
|
56
|
+
* `encodeURIComponent` alone is not enough: it deliberately leaves `*` (and
|
|
57
|
+
* `!'()`) unescaped, and `*` is one of Win32's reserved filename characters.
|
|
58
|
+
* A scoped package never contains one today, but a registry URL can, and the
|
|
59
|
+
* failure mode is the silent one described on {@link pathFor}.
|
|
60
|
+
*/
|
|
61
|
+
function encodePart(part) {
|
|
62
|
+
return encodeURIComponent(part).replace(/\*/g, '%2A');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The cache key for a (registry, name) pair — exported so the filename rule
|
|
66
|
+
* below is a PROPERTY a test can assert, not a claim in a comment.
|
|
67
|
+
*
|
|
68
|
+
* THE SEPARATOR IS NOT COSMETIC. It used to be `|`, producing names like
|
|
69
|
+
* `corgi|https%3A%2F%2Fregistry.npmjs.org%2F|%40gjsify%2Fnode-gi.json` — and `|`
|
|
70
|
+
* is a Win32 RESERVED filename character, so every write failed with `EINVAL`.
|
|
71
|
+
* Nothing reported it: `atomicWrite` is best-effort on purpose (a read-only
|
|
72
|
+
* cache volume must not break an install) and `readCacheFile` reads an
|
|
73
|
+
* unreadable path as a MISS. So on Windows this cache was never broken loudly —
|
|
74
|
+
* it was silently DEAD, and the `If-None-Match` → `304` revalidation that is the
|
|
75
|
+
* entire reason this file exists never engaged once.
|
|
76
|
+
*
|
|
77
|
+
* `,` is legal on Win32 AND escaped by `encodeURIComponent`, which is what keeps
|
|
78
|
+
* a flat two-part name injective: an encoded part cannot contain the separator,
|
|
79
|
+
* so exactly one `(registry, name)` pair maps to any given filename.
|
|
80
|
+
*
|
|
81
|
+
* The shard key and the filename are derived from the SAME encoded parts. They
|
|
82
|
+
* used to disagree — the key hashed the raw pair while the name used the encoded
|
|
83
|
+
* one — which is how a reserved character in the name survived review of a
|
|
84
|
+
* function whose other half looked correct.
|
|
85
|
+
*/
|
|
86
|
+
export function packumentCacheKey(registry, name) {
|
|
87
|
+
return `${encodePart(registry)},${encodePart(name)}`;
|
|
88
|
+
}
|
|
51
89
|
function pathFor(registry, name) {
|
|
52
90
|
if (!isEnabled())
|
|
53
91
|
return null;
|
|
54
|
-
const key =
|
|
55
|
-
|
|
56
|
-
// while staying a stable, reversible, single path segment.
|
|
57
|
-
const file = `${encodeURIComponent(registry)}|${encodeURIComponent(name)}.json`;
|
|
58
|
-
return join(cacheRoot(), shardFor(key), file);
|
|
92
|
+
const key = packumentCacheKey(registry, name);
|
|
93
|
+
return join(cacheRoot(), shardFor(key), `${key}.json`);
|
|
59
94
|
}
|
|
60
95
|
/**
|
|
61
96
|
* Read a cached packument + its ETag for `(registry, name)`. Returns `null` on
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -130,19 +130,19 @@
|
|
|
130
130
|
"cli"
|
|
131
131
|
],
|
|
132
132
|
"dependencies": {
|
|
133
|
-
"@gjsify/buffer": "^0.26.
|
|
134
|
-
"@gjsify/create-app": "^0.26.
|
|
135
|
-
"@gjsify/node-globals": "^0.26.
|
|
136
|
-
"@gjsify/node-polyfills": "^0.26.
|
|
137
|
-
"@gjsify/npm-registry": "^0.26.
|
|
138
|
-
"@gjsify/resolve-npm": "^0.26.
|
|
139
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.26.
|
|
140
|
-
"@gjsify/rolldown-plugin-pnp": "^0.26.
|
|
141
|
-
"@gjsify/semver": "^0.26.
|
|
142
|
-
"@gjsify/tar": "^0.26.
|
|
143
|
-
"@gjsify/tsc": "^0.26.
|
|
144
|
-
"@gjsify/web-polyfills": "^0.26.
|
|
145
|
-
"@gjsify/workspace": "^0.26.
|
|
133
|
+
"@gjsify/buffer": "^0.26.1",
|
|
134
|
+
"@gjsify/create-app": "^0.26.1",
|
|
135
|
+
"@gjsify/node-globals": "^0.26.1",
|
|
136
|
+
"@gjsify/node-polyfills": "^0.26.1",
|
|
137
|
+
"@gjsify/npm-registry": "^0.26.1",
|
|
138
|
+
"@gjsify/resolve-npm": "^0.26.1",
|
|
139
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.26.1",
|
|
140
|
+
"@gjsify/rolldown-plugin-pnp": "^0.26.1",
|
|
141
|
+
"@gjsify/semver": "^0.26.1",
|
|
142
|
+
"@gjsify/tar": "^0.26.1",
|
|
143
|
+
"@gjsify/tsc": "^0.26.1",
|
|
144
|
+
"@gjsify/web-polyfills": "^0.26.1",
|
|
145
|
+
"@gjsify/workspace": "^0.26.1",
|
|
146
146
|
"cosmiconfig": "^9.0.2",
|
|
147
147
|
"get-tsconfig": "^4.14.0",
|
|
148
148
|
"pkg-types": "^2.3.1",
|
|
@@ -150,13 +150,13 @@
|
|
|
150
150
|
"yargs": "^18.0.0"
|
|
151
151
|
},
|
|
152
152
|
"devDependencies": {
|
|
153
|
-
"@gjsify/unit": "^0.26.
|
|
153
|
+
"@gjsify/unit": "^0.26.1",
|
|
154
154
|
"@types/yargs": "^17.0.35",
|
|
155
155
|
"typescript": "^6.0.3"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
|
-
"@gjsify/oxfmt-native": "^0.26.
|
|
159
|
-
"@gjsify/rolldown-native": "^0.26.
|
|
158
|
+
"@gjsify/oxfmt-native": "^0.26.1",
|
|
159
|
+
"@gjsify/rolldown-native": "^0.26.1"
|
|
160
160
|
},
|
|
161
161
|
"peerDependenciesMeta": {
|
|
162
162
|
"@gjsify/oxfmt-native": {
|