zyrk 0.3.7 → 0.3.9
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/VERSION +1 -1
- package/bin/zyrk.js +1 -1
- package/dist/cli/main.d.ts +54 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +473 -0
- package/dist/cli/npm-delegate.d.ts +38 -0
- package/dist/cli/npm-delegate.d.ts.map +1 -0
- package/{src/cli/npm-delegate.ts → dist/cli/npm-delegate.js} +26 -55
- package/dist/cli/parse.d.ts +40 -0
- package/dist/cli/parse.d.ts.map +1 -0
- package/dist/cli/parse.js +78 -0
- package/dist/commands/dependencies.d.ts +12 -0
- package/dist/commands/dependencies.d.ts.map +1 -0
- package/dist/commands/dependencies.js +118 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +506 -0
- package/dist/commands/maintenance.d.ts +27 -0
- package/dist/commands/maintenance.d.ts.map +1 -0
- package/dist/commands/maintenance.js +198 -0
- package/dist/commands/patch.d.ts +38 -0
- package/dist/commands/patch.d.ts.map +1 -0
- package/dist/commands/patch.js +336 -0
- package/dist/commands/project.d.ts +15 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +396 -0
- package/dist/commands/store.d.ts +10 -0
- package/dist/commands/store.d.ts.map +1 -0
- package/dist/commands/store.js +551 -0
- package/dist/config/paths.d.ts +37 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +66 -0
- package/dist/dlx/create.d.ts +23 -0
- package/dist/dlx/create.d.ts.map +1 -0
- package/dist/dlx/create.js +59 -0
- package/dist/dlx/run.d.ts +54 -0
- package/dist/dlx/run.d.ts.map +1 -0
- package/dist/dlx/run.js +274 -0
- package/dist/errors.d.ts +68 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +111 -0
- package/dist/extract/tarball.d.ts +82 -0
- package/dist/extract/tarball.d.ts.map +1 -0
- package/dist/extract/tarball.js +645 -0
- package/dist/fetch/all.d.ts +48 -0
- package/dist/fetch/all.d.ts.map +1 -0
- package/dist/fetch/all.js +127 -0
- package/dist/fetch/package.d.ts +45 -0
- package/dist/fetch/package.d.ts.map +1 -0
- package/dist/fetch/package.js +124 -0
- package/dist/fetch/registry.d.ts +109 -0
- package/dist/fetch/registry.d.ts.map +1 -0
- package/dist/fetch/registry.js +418 -0
- package/dist/install/import-npm.d.ts +4 -0
- package/dist/install/import-npm.d.ts.map +1 -0
- package/dist/install/import-npm.js +147 -0
- package/dist/install/lock.d.ts +8 -0
- package/dist/install/lock.d.ts.map +1 -0
- package/dist/install/lock.js +146 -0
- package/dist/install/metadata.d.ts +19 -0
- package/dist/install/metadata.d.ts.map +1 -0
- package/dist/install/metadata.js +79 -0
- package/dist/install/registry.d.ts +8 -0
- package/dist/install/registry.d.ts.map +1 -0
- package/dist/install/registry.js +59 -0
- package/dist/install/run.d.ts +32 -0
- package/dist/install/run.d.ts.map +1 -0
- package/dist/install/run.js +545 -0
- package/dist/lockfile/fingerprints.d.ts +35 -0
- package/dist/lockfile/fingerprints.d.ts.map +1 -0
- package/dist/lockfile/fingerprints.js +56 -0
- package/dist/lockfile/read.d.ts +27 -0
- package/dist/lockfile/read.d.ts.map +1 -0
- package/dist/lockfile/read.js +126 -0
- package/dist/lockfile/types.d.ts +74 -0
- package/dist/lockfile/types.d.ts.map +1 -0
- package/dist/lockfile/types.js +17 -0
- package/dist/lockfile/write.d.ts +39 -0
- package/dist/lockfile/write.d.ts.map +1 -0
- package/dist/lockfile/write.js +180 -0
- package/dist/log.d.ts +21 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +79 -0
- package/dist/project/projection.d.ts +50 -0
- package/dist/project/projection.d.ts.map +1 -0
- package/dist/project/projection.js +276 -0
- package/dist/resolve/resolver.d.ts +75 -0
- package/dist/resolve/resolver.d.ts.map +1 -0
- package/dist/resolve/resolver.js +533 -0
- package/dist/resolve/spec.d.ts +26 -0
- package/dist/resolve/spec.d.ts.map +1 -0
- package/dist/resolve/spec.js +87 -0
- package/dist/store/db.d.ts +49 -0
- package/dist/store/db.d.ts.map +1 -0
- package/{src/store/db.ts → dist/store/db.js} +106 -206
- package/dist/store/graph.d.ts +60 -0
- package/dist/store/graph.d.ts.map +1 -0
- package/dist/store/graph.js +115 -0
- package/dist/store/layout.d.ts +71 -0
- package/dist/store/layout.d.ts.map +1 -0
- package/dist/store/layout.js +282 -0
- package/dist/store/manifests.d.ts +78 -0
- package/dist/store/manifests.d.ts.map +1 -0
- package/dist/store/manifests.js +238 -0
- package/dist/store/objects.d.ts +56 -0
- package/dist/store/objects.d.ts.map +1 -0
- package/dist/store/objects.js +157 -0
- package/dist/store/realize.d.ts +38 -0
- package/dist/store/realize.d.ts.map +1 -0
- package/dist/store/realize.js +227 -0
- package/dist/store/source.d.ts +42 -0
- package/dist/store/source.d.ts.map +1 -0
- package/dist/store/source.js +169 -0
- package/dist/store/txn.d.ts +37 -0
- package/dist/store/txn.d.ts.map +1 -0
- package/dist/store/txn.js +97 -0
- package/dist/store/verify.d.ts +40 -0
- package/dist/store/verify.d.ts.map +1 -0
- package/dist/store/verify.js +396 -0
- package/dist/util/fsx.d.ts +54 -0
- package/dist/util/fsx.d.ts.map +1 -0
- package/dist/util/fsx.js +259 -0
- package/dist/util/hash.d.ts +48 -0
- package/dist/util/hash.d.ts.map +1 -0
- package/dist/util/hash.js +80 -0
- package/dist/util/ids.d.ts +19 -0
- package/dist/util/ids.d.ts.map +1 -0
- package/dist/util/ids.js +38 -0
- package/dist/util/integrity.d.ts +97 -0
- package/dist/util/integrity.d.ts.map +1 -0
- package/dist/util/integrity.js +192 -0
- package/dist/util/json.d.ts +14 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +78 -0
- package/dist/util/locks.d.ts +40 -0
- package/dist/util/locks.d.ts.map +1 -0
- package/dist/util/locks.js +186 -0
- package/dist/util/proc.d.ts +32 -0
- package/dist/util/proc.d.ts.map +1 -0
- package/dist/util/proc.js +84 -0
- package/dist/version.d.ts +27 -0
- package/dist/version.d.ts.map +1 -0
- package/{src/version.ts → dist/version.js} +0 -8
- package/package.json +5 -3
- package/src/cli/main.ts +0 -468
- package/src/cli/parse.ts +0 -106
- package/src/commands/dependencies.ts +0 -154
- package/src/commands/deploy.ts +0 -777
- package/src/commands/maintenance.ts +0 -260
- package/src/commands/patch.ts +0 -408
- package/src/commands/project.ts +0 -598
- package/src/commands/store.ts +0 -689
- package/src/config/paths.ts +0 -119
- package/src/dlx/create.ts +0 -69
- package/src/dlx/run.ts +0 -389
- package/src/errors.ts +0 -142
- package/src/extract/tarball.ts +0 -868
- package/src/fetch/all.ts +0 -206
- package/src/fetch/package.ts +0 -181
- package/src/fetch/registry.ts +0 -615
- package/src/install/import-npm.ts +0 -177
- package/src/install/lock.ts +0 -176
- package/src/install/metadata.ts +0 -122
- package/src/install/registry.ts +0 -88
- package/src/install/run.ts +0 -760
- package/src/lockfile/fingerprints.ts +0 -74
- package/src/lockfile/read.ts +0 -198
- package/src/lockfile/types.ts +0 -75
- package/src/lockfile/write.ts +0 -257
- package/src/log.ts +0 -100
- package/src/project/projection.ts +0 -390
- package/src/resolve/resolver.ts +0 -757
- package/src/resolve/spec.ts +0 -131
- package/src/store/graph.ts +0 -175
- package/src/store/layout.ts +0 -431
- package/src/store/manifests.ts +0 -347
- package/src/store/objects.ts +0 -229
- package/src/store/realize.ts +0 -322
- package/src/store/source.ts +0 -249
- package/src/store/txn.ts +0 -118
- package/src/store/verify.ts +0 -433
- package/src/types/vendor.d.ts +0 -120
- package/src/util/fsx.ts +0 -289
- package/src/util/hash.ts +0 -117
- package/src/util/ids.ts +0 -42
- package/src/util/integrity.ts +0 -269
- package/src/util/json.ts +0 -89
- package/src/util/locks.ts +0 -266
- package/src/util/proc.ts +0 -108
package/src/store/layout.ts
DELETED
|
@@ -1,431 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { ZyrkError } from "../errors.ts";
|
|
4
|
-
import { resolveStoreRoot } from "../config/paths.ts";
|
|
5
|
-
import {
|
|
6
|
-
ensureDirWithMode,
|
|
7
|
-
isErrno,
|
|
8
|
-
pathExists,
|
|
9
|
-
readJsonFile,
|
|
10
|
-
writeFileAtomic,
|
|
11
|
-
} from "../util/fsx.ts";
|
|
12
|
-
import {
|
|
13
|
-
CLI_VERSION,
|
|
14
|
-
CONTEXT_CANONICALIZATION_VERSION,
|
|
15
|
-
EXTRACTION_POLICY_VERSION,
|
|
16
|
-
OBJECT_KEY_SCHEME,
|
|
17
|
-
SOURCE_MANIFEST_SCHEMA_VERSION,
|
|
18
|
-
STORE_FORMAT_FILE,
|
|
19
|
-
STORE_FORMAT_VERSION,
|
|
20
|
-
} from "../version.ts";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The managed store: one verified package store per OS user (SPEC 7).
|
|
24
|
-
*
|
|
25
|
-
* Layout follows SPEC 7.3. Path length is bounded because every
|
|
26
|
-
* content-derived directory name is a fixed-length digest-based identifier
|
|
27
|
-
* and package names never become path components.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
export type StorePaths = {
|
|
31
|
-
root: string;
|
|
32
|
-
formatFile: string;
|
|
33
|
-
objectsDir: string;
|
|
34
|
-
sourcesDir: string;
|
|
35
|
-
realizationsDir: string;
|
|
36
|
-
graphsDir: string;
|
|
37
|
-
metadataDir: string;
|
|
38
|
-
journalDir: string;
|
|
39
|
-
locksDir: string;
|
|
40
|
-
stagingDir: string;
|
|
41
|
-
quarantineDir: string;
|
|
42
|
-
trashDir: string;
|
|
43
|
-
pinsDir: string;
|
|
44
|
-
dbFile: string;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export function storePaths(root: string): StorePaths {
|
|
48
|
-
const metadataDir = path.join(root, "metadata");
|
|
49
|
-
return {
|
|
50
|
-
root,
|
|
51
|
-
formatFile: path.join(root, STORE_FORMAT_FILE),
|
|
52
|
-
objectsDir: path.join(root, "objects"),
|
|
53
|
-
sourcesDir: path.join(root, "sources"),
|
|
54
|
-
realizationsDir: path.join(root, "realizations"),
|
|
55
|
-
graphsDir: path.join(root, "graphs"),
|
|
56
|
-
metadataDir,
|
|
57
|
-
journalDir: path.join(metadataDir, "journal"),
|
|
58
|
-
locksDir: path.join(root, "locks"),
|
|
59
|
-
stagingDir: path.join(root, "staging"),
|
|
60
|
-
quarantineDir: path.join(root, "quarantine"),
|
|
61
|
-
trashDir: path.join(root, "trash"),
|
|
62
|
-
pinsDir: path.join(root, "pins"),
|
|
63
|
-
dbFile: path.join(metadataDir, "state.sqlite"),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export type StoreFormatMarker = {
|
|
68
|
-
storeFormatVersion: number;
|
|
69
|
-
objectKeyScheme: string;
|
|
70
|
-
sourceManifestSchemaVersion: number;
|
|
71
|
-
extractionPolicyVersion: number;
|
|
72
|
-
contextCanonicalizationVersion: number;
|
|
73
|
-
createdAt: string;
|
|
74
|
-
tool: { name: string; version: string };
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export type OpenStoreOptions = {
|
|
78
|
-
/** Explicit root; defaults to the platform location or ZYRK_STORE_DIR. */
|
|
79
|
-
root?: string;
|
|
80
|
-
/** Create and initialize the store if it does not exist. */
|
|
81
|
-
create?: boolean;
|
|
82
|
-
/**
|
|
83
|
-
* Test-only escape hatch for permission checks. Normal operation must never
|
|
84
|
-
* pass this: a group/world-writable store is a blocking diagnostic
|
|
85
|
-
* (SPEC 7.2).
|
|
86
|
-
*/
|
|
87
|
-
allowUnsafePermissions?: boolean;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export class Store {
|
|
91
|
-
readonly root: string;
|
|
92
|
-
readonly paths: StorePaths;
|
|
93
|
-
/** undefined when the store has not been initialized yet. */
|
|
94
|
-
readonly format: StoreFormatMarker | undefined;
|
|
95
|
-
|
|
96
|
-
private constructor(root: string, format: StoreFormatMarker | undefined) {
|
|
97
|
-
this.root = root;
|
|
98
|
-
this.paths = storePaths(root);
|
|
99
|
-
this.format = format;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
get initialized(): boolean {
|
|
103
|
-
return this.format !== undefined;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
static async find(options: { root?: string } = {}): Promise<string> {
|
|
107
|
-
return options.root ?? resolveStoreRoot().root;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
static async open(options: OpenStoreOptions = {}): Promise<Store> {
|
|
111
|
-
const root = await canonicalizeRoot(
|
|
112
|
-
path.resolve(options.root ?? resolveStoreRoot().root),
|
|
113
|
-
);
|
|
114
|
-
const exists = await pathExists(root);
|
|
115
|
-
if (!exists) {
|
|
116
|
-
if (options.create !== true) {
|
|
117
|
-
return new Store(root, undefined);
|
|
118
|
-
}
|
|
119
|
-
await Store.initialize(root, options.allowUnsafePermissions === true);
|
|
120
|
-
} else {
|
|
121
|
-
await Store.checkSafety(root, options.allowUnsafePermissions === true);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const paths = storePaths(root);
|
|
125
|
-
const markerPath = paths.formatFile;
|
|
126
|
-
if (!(await pathExists(markerPath))) {
|
|
127
|
-
if (options.create === true) {
|
|
128
|
-
await Store.initialize(root, options.allowUnsafePermissions === true);
|
|
129
|
-
} else {
|
|
130
|
-
return new Store(root, undefined);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const marker = await readStoreFormatMarker(markerPath);
|
|
135
|
-
validateStoreFormatMarker(marker, markerPath);
|
|
136
|
-
return new Store(root, marker);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Throw unless the store is initialized and usable. */
|
|
140
|
-
requireInitialized(): StoreFormatMarker {
|
|
141
|
-
if (this.format === undefined) {
|
|
142
|
-
throw new ZyrkError(
|
|
143
|
-
"ZYRK_STORE_UNSAFE",
|
|
144
|
-
`the zyrk store at ${this.root} is not initialized`,
|
|
145
|
-
{
|
|
146
|
-
detail: { storeRoot: this.root },
|
|
147
|
-
remedy:
|
|
148
|
-
"run an install or fetch command to initialize the store, or set ZYRK_STORE_DIR to an existing initialized store",
|
|
149
|
-
},
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
return this.format;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
private static async initialize(
|
|
156
|
-
root: string,
|
|
157
|
-
allowUnsafePermissions: boolean,
|
|
158
|
-
): Promise<void> {
|
|
159
|
-
if (!(await pathExists(root))) {
|
|
160
|
-
await ensureDirWithMode(root, 0o700);
|
|
161
|
-
}
|
|
162
|
-
// An existing directory is checked, never silently chmodded: quietly
|
|
163
|
-
// changing the permissions of a directory the user pointed at (a home
|
|
164
|
-
// directory, a shared mount) is worse than refusing with a diagnostic
|
|
165
|
-
// (SPEC 7.2).
|
|
166
|
-
await Store.checkSafety(root, allowUnsafePermissions);
|
|
167
|
-
|
|
168
|
-
const paths = storePaths(root);
|
|
169
|
-
for (const dir of [
|
|
170
|
-
paths.objectsDir,
|
|
171
|
-
paths.sourcesDir,
|
|
172
|
-
paths.realizationsDir,
|
|
173
|
-
paths.graphsDir,
|
|
174
|
-
paths.metadataDir,
|
|
175
|
-
paths.journalDir,
|
|
176
|
-
paths.locksDir,
|
|
177
|
-
paths.stagingDir,
|
|
178
|
-
paths.quarantineDir,
|
|
179
|
-
paths.trashDir,
|
|
180
|
-
paths.pinsDir,
|
|
181
|
-
]) {
|
|
182
|
-
await ensureDirWithMode(dir, 0o700);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (!(await pathExists(paths.formatFile))) {
|
|
186
|
-
const marker: StoreFormatMarker = {
|
|
187
|
-
storeFormatVersion: STORE_FORMAT_VERSION,
|
|
188
|
-
objectKeyScheme: OBJECT_KEY_SCHEME,
|
|
189
|
-
sourceManifestSchemaVersion: SOURCE_MANIFEST_SCHEMA_VERSION,
|
|
190
|
-
extractionPolicyVersion: EXTRACTION_POLICY_VERSION,
|
|
191
|
-
contextCanonicalizationVersion: CONTEXT_CANONICALIZATION_VERSION,
|
|
192
|
-
createdAt: new Date().toISOString(),
|
|
193
|
-
tool: { name: "zyrk", version: CLI_VERSION },
|
|
194
|
-
};
|
|
195
|
-
await writeFileAtomic(
|
|
196
|
-
paths.formatFile,
|
|
197
|
-
`${JSON.stringify(marker, null, 2)}\n`,
|
|
198
|
-
{ mode: 0o644 },
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Blocking diagnostics for account isolation (SPEC 7.2): the store must be
|
|
205
|
-
* user-owned and not group/world writable. Never fixes ownership
|
|
206
|
-
* automatically; a root-owned store gets a repair explanation instead.
|
|
207
|
-
*/
|
|
208
|
-
private static async checkSafety(
|
|
209
|
-
root: string,
|
|
210
|
-
allowUnsafePermissions: boolean,
|
|
211
|
-
): Promise<void> {
|
|
212
|
-
const resolved = await fs.realpath(root);
|
|
213
|
-
const stat = await fs.stat(resolved);
|
|
214
|
-
if (!stat.isDirectory()) {
|
|
215
|
-
throw new ZyrkError(
|
|
216
|
-
"ZYRK_STORE_UNSAFE",
|
|
217
|
-
`store path is not a directory: ${root}`,
|
|
218
|
-
{
|
|
219
|
-
detail: { storeRoot: root },
|
|
220
|
-
remedy:
|
|
221
|
-
"choose a directory for the store, or remove the conflicting file",
|
|
222
|
-
},
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (process.platform === "win32") {
|
|
227
|
-
// Windows ACL verification arrives with Windows platform support
|
|
228
|
-
// (SPEC 27); until then the store is not advertised as supported there.
|
|
229
|
-
throw new ZyrkError(
|
|
230
|
-
"ZYRK_PLATFORM_UNSUPPORTED",
|
|
231
|
-
"Windows store permission checks are not implemented yet",
|
|
232
|
-
{
|
|
233
|
-
detail: { storeRoot: root, platform: process.platform },
|
|
234
|
-
remedy:
|
|
235
|
-
"run on Linux for now; Windows support is gated on its platform fixtures",
|
|
236
|
-
},
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (allowUnsafePermissions) return;
|
|
241
|
-
|
|
242
|
-
const uid = process.getuid?.();
|
|
243
|
-
if (uid !== undefined && stat.uid !== uid) {
|
|
244
|
-
throw new ZyrkError(
|
|
245
|
-
"ZYRK_STORE_UNSAFE",
|
|
246
|
-
`store is owned by another user (uid ${stat.uid}, running as ${uid}): ${root}`,
|
|
247
|
-
{
|
|
248
|
-
detail: { storeRoot: root, ownerUid: stat.uid, runningUid: uid },
|
|
249
|
-
remedy:
|
|
250
|
-
"if this store was created by root, move it under your own account or point ZYRK_STORE_DIR at a user-owned location; zyrk never rewrites ownership recursively",
|
|
251
|
-
},
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
if (uid !== undefined && stat.uid === 0 && uid !== 0) {
|
|
255
|
-
throw new ZyrkError("ZYRK_STORE_UNSAFE", `store is root-owned: ${root}`, {
|
|
256
|
-
detail: { storeRoot: root, ownerUid: stat.uid },
|
|
257
|
-
remedy:
|
|
258
|
-
"create a fresh store under your account instead of reusing a root-owned one",
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const mode = stat.mode & 0o777;
|
|
263
|
-
if ((mode & 0o022) !== 0) {
|
|
264
|
-
throw new ZyrkError(
|
|
265
|
-
"ZYRK_STORE_UNSAFE",
|
|
266
|
-
`store is group- or world-writable (mode ${mode.toString(8)}): ${root}`,
|
|
267
|
-
{
|
|
268
|
-
detail: { storeRoot: root, mode: mode.toString(8) },
|
|
269
|
-
remedy: `chmod go-w '${root}' (and its subdirectories) so other accounts cannot modify shared package content`,
|
|
270
|
-
},
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Every managed boundary zyrk writes or reuses through gets the same
|
|
275
|
-
// ownership, type, and mode checks as the root, evaluated with lstat so
|
|
276
|
-
// a substituted symlink is rejected instead of followed (S02). Missing
|
|
277
|
-
// directories are created on demand with 0700; existing ones are never
|
|
278
|
-
// silently repaired.
|
|
279
|
-
const paths = storePaths(resolved);
|
|
280
|
-
for (const dir of [
|
|
281
|
-
paths.objectsDir,
|
|
282
|
-
paths.sourcesDir,
|
|
283
|
-
paths.realizationsDir,
|
|
284
|
-
paths.graphsDir,
|
|
285
|
-
paths.metadataDir,
|
|
286
|
-
paths.journalDir,
|
|
287
|
-
paths.locksDir,
|
|
288
|
-
paths.stagingDir,
|
|
289
|
-
paths.quarantineDir,
|
|
290
|
-
paths.trashDir,
|
|
291
|
-
paths.pinsDir,
|
|
292
|
-
]) {
|
|
293
|
-
await Store.checkManagedDir(dir, uid, allowUnsafePermissions);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
private static async checkManagedDir(
|
|
298
|
-
dir: string,
|
|
299
|
-
uid: number | undefined,
|
|
300
|
-
allowUnsafePermissions: boolean,
|
|
301
|
-
): Promise<void> {
|
|
302
|
-
let stat;
|
|
303
|
-
try {
|
|
304
|
-
stat = await fs.lstat(dir);
|
|
305
|
-
} catch (error) {
|
|
306
|
-
if (isErrno(error, "ENOENT")) return;
|
|
307
|
-
throw error;
|
|
308
|
-
}
|
|
309
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
310
|
-
throw new ZyrkError(
|
|
311
|
-
"ZYRK_STORE_UNSAFE",
|
|
312
|
-
`store directory is not a real directory: ${dir}`,
|
|
313
|
-
{
|
|
314
|
-
detail: { storeDir: dir },
|
|
315
|
-
remedy:
|
|
316
|
-
"remove the unexpected file or link and let zyrk recreate the managed directory",
|
|
317
|
-
},
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
if (allowUnsafePermissions) return;
|
|
321
|
-
if (uid !== undefined && stat.uid !== uid) {
|
|
322
|
-
throw new ZyrkError(
|
|
323
|
-
"ZYRK_STORE_UNSAFE",
|
|
324
|
-
`store directory is owned by another user (uid ${stat.uid}, running as ${uid}): ${dir}`,
|
|
325
|
-
{
|
|
326
|
-
detail: { storeDir: dir, ownerUid: stat.uid, runningUid: uid },
|
|
327
|
-
remedy:
|
|
328
|
-
"zyrk never rewrites ownership recursively; use a store your account owns",
|
|
329
|
-
},
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
if ((stat.mode & 0o777 & 0o022) !== 0) {
|
|
333
|
-
throw new ZyrkError(
|
|
334
|
-
"ZYRK_STORE_UNSAFE",
|
|
335
|
-
`store directory is group- or world-writable (mode ${(stat.mode & 0o777).toString(8)}): ${dir}`,
|
|
336
|
-
{
|
|
337
|
-
detail: { storeDir: dir, mode: (stat.mode & 0o777).toString(8) },
|
|
338
|
-
remedy: `chmod go-w '${dir}' so other accounts cannot modify shared package content`,
|
|
339
|
-
},
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Resolve symlinks deliberately so the store has one canonical identity
|
|
347
|
-
* for safety checks, paths, and records. A symlinked ZYRK_STORE_DIR is
|
|
348
|
-
* allowed; it always means its target. Missing leaves resolve through
|
|
349
|
-
* their nearest existing ancestor.
|
|
350
|
-
*/
|
|
351
|
-
async function canonicalizeRoot(root: string): Promise<string> {
|
|
352
|
-
try {
|
|
353
|
-
return await fs.realpath(root);
|
|
354
|
-
} catch (error) {
|
|
355
|
-
if (!isErrno(error, "ENOENT")) throw error;
|
|
356
|
-
// A dangling root symlink exists according to lstat but not realpath.
|
|
357
|
-
// Resolve its target before initialization so writes and safety checks
|
|
358
|
-
// use the same canonical directory as an already-existing root.
|
|
359
|
-
try {
|
|
360
|
-
if ((await fs.lstat(root)).isSymbolicLink()) {
|
|
361
|
-
return await canonicalizeRoot(
|
|
362
|
-
path.resolve(path.dirname(root), await fs.readlink(root)),
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
} catch (linkError) {
|
|
366
|
-
if (!isErrno(linkError, "ENOENT")) throw linkError;
|
|
367
|
-
}
|
|
368
|
-
const parent = path.dirname(root);
|
|
369
|
-
if (parent === root) throw error;
|
|
370
|
-
return path.join(await canonicalizeRoot(parent), path.basename(root));
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
async function readStoreFormatMarker(
|
|
375
|
-
markerPath: string,
|
|
376
|
-
): Promise<StoreFormatMarker> {
|
|
377
|
-
try {
|
|
378
|
-
return await readJsonFile<StoreFormatMarker>(markerPath);
|
|
379
|
-
} catch (error) {
|
|
380
|
-
throw new ZyrkError(
|
|
381
|
-
"ZYRK_STORE_CORRUPT",
|
|
382
|
-
`store format marker is unreadable: ${markerPath}`,
|
|
383
|
-
{
|
|
384
|
-
detail: { formatFile: markerPath },
|
|
385
|
-
remedy:
|
|
386
|
-
"inspect the marker file; if it is damaged, remove it and re-run an install to reinitialize format metadata",
|
|
387
|
-
cause: error,
|
|
388
|
-
},
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function validateStoreFormatMarker(
|
|
394
|
-
marker: StoreFormatMarker,
|
|
395
|
-
markerPath: string,
|
|
396
|
-
): void {
|
|
397
|
-
if (marker.storeFormatVersion !== STORE_FORMAT_VERSION) {
|
|
398
|
-
throw new ZyrkError(
|
|
399
|
-
"ZYRK_STORE_FORMAT_UNSUPPORTED",
|
|
400
|
-
`store format ${marker.storeFormatVersion} is not supported by this zyrk (expects ${STORE_FORMAT_VERSION})`,
|
|
401
|
-
{
|
|
402
|
-
detail: {
|
|
403
|
-
formatFile: markerPath,
|
|
404
|
-
storeFormatVersion: marker.storeFormatVersion,
|
|
405
|
-
supported: STORE_FORMAT_VERSION,
|
|
406
|
-
},
|
|
407
|
-
remedy:
|
|
408
|
-
"use a CLI matching this store format, or initialize a fresh store at a new location; if this store is newer, upgrade zyrk first",
|
|
409
|
-
},
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
if (marker.objectKeyScheme !== OBJECT_KEY_SCHEME) {
|
|
413
|
-
throw new ZyrkError(
|
|
414
|
-
"ZYRK_STORE_FORMAT_UNSUPPORTED",
|
|
415
|
-
`object key scheme ${marker.objectKeyScheme} is not supported`,
|
|
416
|
-
{
|
|
417
|
-
detail: {
|
|
418
|
-
formatFile: markerPath,
|
|
419
|
-
objectKeyScheme: marker.objectKeyScheme,
|
|
420
|
-
},
|
|
421
|
-
remedy:
|
|
422
|
-
"use a CLI that understands this store, or initialize a fresh store",
|
|
423
|
-
},
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
if (marker.extractionPolicyVersion !== EXTRACTION_POLICY_VERSION) {
|
|
427
|
-
// Extraction-rule changes are part of source identity (SPEC 8.1/13.4):
|
|
428
|
-
// existing manifests stay readable, but new fetches mint new identities.
|
|
429
|
-
// This is an informational condition, not an error.
|
|
430
|
-
}
|
|
431
|
-
}
|