gutterpress 0.11.10-alpha.2 → 0.11.10-alpha.6
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 +24 -46
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +10 -6
- package/dist/{build-dfpvvhys.js → build-npxq1tvm.js} +7 -6
- package/dist/cdp-jg95s674.js +15 -0
- package/dist/cdp-smdx6mr3.js +16 -0
- package/dist/{cli-d8rdk1y7.js → cli-30j93gzt.js} +11 -335
- package/dist/{cli-pfczdbyd.js → cli-5qr3z0qa.js} +10 -404
- package/dist/{cli-c41yr7he.js → cli-7fkhgtfn.js} +3 -1
- package/dist/{cli-k2hwrp7s.js → cli-97bdz5k0.js} +1 -4
- package/dist/cli-kkzka31p.js +261 -0
- package/dist/{cli-p679pvce.js → cli-vbzkkcbv.js} +2 -2
- package/dist/{cli-4xksazfr.js → cli-z8ye91f0.js} +283 -1142
- package/dist/cli.js +12 -14
- package/dist/{doctor-hheax5yc.js → doctor-ykr6gtpv.js} +3 -3
- package/dist/engine/shared/cdp.d.ts +23 -19
- package/dist/engine-3gwctsty.js +24 -0
- package/dist/engine-mwshptgf.js +25 -0
- package/dist/{ext-q5024j6v.js → ext-bna9nr3v.js} +7 -6
- package/dist/{index-1a4rx87v.js → index-322fxk25.js} +344 -1130
- package/dist/{index-kqftehgy.js → index-dzp0hc2c.js} +4 -2
- package/dist/{index-fnhak0nh.js → index-qf7y4d85.js} +10 -404
- package/dist/index-r4wvy5v3.js +267 -0
- package/dist/{index-t43k5rq1.js → index-rzm1phg0.js} +23 -356
- package/dist/index.d.ts +0 -1
- package/dist/index.js +12 -87
- package/dist/lib/build-error.d.ts +4 -5
- package/dist/lib/build-preflight.d.ts +1 -35
- package/dist/lib/build-runner.d.ts +27 -44
- package/dist/lib/chromium.d.ts +5 -1
- package/dist/lib/engine.d.ts +3 -18
- package/dist/lib/extension-manager.d.ts +2 -2
- package/dist/lib/extension-manifest.d.ts +1 -1
- package/dist/lib/markdown/assemble.d.ts +2 -9
- package/dist/lib/markdown/index.d.ts +2 -4
- package/dist/lib/markdown/plugins.d.ts +0 -3
- package/dist/lib/npm-plugin-installer.d.ts +6 -1
- package/dist/lib/plugin-vendor.d.ts +10 -78
- package/dist/lib/source-provider.d.ts +1 -1
- package/dist/lib/validation-exec.d.ts +5 -6
- package/dist/{new-6xm4tdxb.js → new-0hv3nwqj.js} +9 -8
- package/dist/{preflight-ezt8rvg3.js → preflight-pk0k25fm.js} +7 -6
- package/dist/preview/file-watcher.d.ts +4 -43
- package/dist/preview/http-server.d.ts +1 -3
- package/dist/{preview-hehs46dd.js → preview-8b1fn59h.js} +7 -6
- package/dist/{preview-shell-gqnc5xxe.js → preview-shell-tqndvhbx.js} +5 -7
- package/dist/{publish-ha505ejq.js → publish-ba5h76tz.js} +7 -6
- package/dist/render.d.ts +1 -1
- package/dist/render.js +1 -9
- package/dist/{source-provider-ta09wegv.js → source-provider-pzmz0br9.js} +1 -1
- package/dist/{source-provider-7c2ww3bz.js → source-provider-qbthnmqw.js} +1 -1
- package/dist/{validate-je56vdbj.js → validate-4rq2r5b3.js} +7 -6
- package/package.json +1 -4
- package/dist/audit-v9594a72.js +0 -69
- package/dist/engine-qtfrfbpk.js +0 -41
- package/dist/engine-trte2tg2.js +0 -40
- package/dist/lib/browser-pool.d.ts +0 -35
- package/dist/lib/lint-runner.d.ts +0 -29
- package/dist/lint-tbywt66a.js +0 -68
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAssetPath,
|
|
3
3
|
isIgnoredMarginBoxProperty,
|
|
4
|
-
launchChromium,
|
|
5
4
|
marginBoxAlign,
|
|
6
5
|
marginBoxRectPt
|
|
7
|
-
} from "./index-
|
|
6
|
+
} from "./index-r4wvy5v3.js";
|
|
7
|
+
import {
|
|
8
|
+
launchChromium
|
|
9
|
+
} from "./index-rzm1phg0.js";
|
|
8
10
|
|
|
9
11
|
// src/engine/compiler/build.ts
|
|
10
12
|
import { readFile } from "node:fs/promises";
|
|
@@ -199,42 +199,14 @@ function formatData(data, clean) {
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
// src/lib/plugin-vendor.ts
|
|
202
|
-
import {
|
|
203
|
-
import { createReadStream } from "node:fs";
|
|
204
|
-
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
|
|
202
|
+
import { lstat, readFile, realpath } from "node:fs/promises";
|
|
205
203
|
import path2 from "node:path";
|
|
206
204
|
import { exports as resolveExports } from "resolve.exports";
|
|
207
|
-
import { satisfies, validRange } from "semver";
|
|
208
|
-
|
|
209
|
-
// src/lib/npm-registry.ts
|
|
210
|
-
var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
211
|
-
var ENV_OVERRIDE = "GUTTERPRESS_NPM_REGISTRY";
|
|
212
|
-
function npmRegistryUrl() {
|
|
213
|
-
const configured = process.env[ENV_OVERRIDE]?.trim();
|
|
214
|
-
if (!configured)
|
|
215
|
-
return DEFAULT_NPM_REGISTRY;
|
|
216
|
-
let scheme;
|
|
217
|
-
try {
|
|
218
|
-
scheme = new URL(configured).protocol;
|
|
219
|
-
} catch {
|
|
220
|
-
throw new Error(`Invalid ${ENV_OVERRIDE} URL "${configured}".`);
|
|
221
|
-
}
|
|
222
|
-
if (scheme !== "http:" && scheme !== "https:") {
|
|
223
|
-
throw new Error(`${ENV_OVERRIDE} must be an http(s) URL — got "${scheme}".`);
|
|
224
|
-
}
|
|
225
|
-
return configured.replace(/\/+$/, "");
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/lib/plugin-vendor.ts
|
|
229
205
|
var PLUGINS_DIR = "plugins";
|
|
230
206
|
var VENDORED_NPM_DIR = "npm";
|
|
231
|
-
var VENDOR_RECEIPT_FILE = ".gutterpress-install.json";
|
|
232
|
-
var VENDOR_RECEIPT_VERSION = 2;
|
|
233
207
|
var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
|
|
234
208
|
var EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
235
209
|
var WINDOWS_RESERVED = /^(con|prn|aux|nul|clock\$|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\..*)?$/i;
|
|
236
|
-
var MAX_VERIFIED_FILES = 50000;
|
|
237
|
-
var MAX_VERIFIED_BYTES = 512 * 1024 * 1024;
|
|
238
210
|
function isValidNpmPackageName(name) {
|
|
239
211
|
if (!name || name.length > 214 || name !== name.toLowerCase())
|
|
240
212
|
return false;
|
|
@@ -287,10 +259,6 @@ function vendoredNpmPluginPackageDir(installRoot, name) {
|
|
|
287
259
|
function toPosixPath(value) {
|
|
288
260
|
return value.split(path2.sep).join("/");
|
|
289
261
|
}
|
|
290
|
-
function packageEntryFormat(manifest, relativeEntry) {
|
|
291
|
-
const extension = path2.posix.extname(relativeEntry);
|
|
292
|
-
return extension === ".cjs" || extension === ".json" || extension !== ".mjs" && manifest.type !== "module" ? "commonjs" : "module";
|
|
293
|
-
}
|
|
294
262
|
function assertWindowsSafeRelativePath(relative) {
|
|
295
263
|
if (!relative || relative.includes("\\") || relative.startsWith("/") || /^[a-zA-Z]:/.test(relative)) {
|
|
296
264
|
throw new Error(`Unsafe package path: ${relative || "(empty)"}`);
|
|
@@ -309,12 +277,6 @@ function assertWindowsSafeRelativePath(relative) {
|
|
|
309
277
|
function windowsPathKey(relative) {
|
|
310
278
|
return relative.normalize("NFKC").toLowerCase();
|
|
311
279
|
}
|
|
312
|
-
function safeReceiptPath(relative, label) {
|
|
313
|
-
if (typeof relative !== "string")
|
|
314
|
-
throw new Error(`${label} must be a string.`);
|
|
315
|
-
assertWindowsSafeRelativePath(relative);
|
|
316
|
-
return relative;
|
|
317
|
-
}
|
|
318
280
|
function isContained(root, candidate) {
|
|
319
281
|
const relative = path2.relative(root, candidate);
|
|
320
282
|
return relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative);
|
|
@@ -322,38 +284,6 @@ function isContained(root, candidate) {
|
|
|
322
284
|
function object(value) {
|
|
323
285
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
324
286
|
}
|
|
325
|
-
function stringMap(value) {
|
|
326
|
-
const map = object(value);
|
|
327
|
-
if (!map)
|
|
328
|
-
return {};
|
|
329
|
-
const result = {};
|
|
330
|
-
for (const [name, selector] of Object.entries(map)) {
|
|
331
|
-
if (typeof selector === "string" && selector.trim())
|
|
332
|
-
result[name] = selector.trim();
|
|
333
|
-
}
|
|
334
|
-
return result;
|
|
335
|
-
}
|
|
336
|
-
function dependencyDeclarations(manifest) {
|
|
337
|
-
const required = stringMap(manifest.dependencies);
|
|
338
|
-
const optional = stringMap(manifest.optionalDependencies);
|
|
339
|
-
for (const name of Object.keys(optional))
|
|
340
|
-
delete required[name];
|
|
341
|
-
const peers = stringMap(manifest.peerDependencies);
|
|
342
|
-
const peerMeta = object(manifest.peerDependenciesMeta) ?? {};
|
|
343
|
-
const optionalPeers = {};
|
|
344
|
-
for (const [name, selector] of Object.entries(peers)) {
|
|
345
|
-
if (name in required || name in optional)
|
|
346
|
-
continue;
|
|
347
|
-
if (object(peerMeta[name])?.optional === true)
|
|
348
|
-
optionalPeers[name] = selector;
|
|
349
|
-
else
|
|
350
|
-
required[name] = selector;
|
|
351
|
-
}
|
|
352
|
-
return { required, optional, optionalPeers };
|
|
353
|
-
}
|
|
354
|
-
function unsupportedRegistrySelector(selector) {
|
|
355
|
-
return /^(?:file:|git\+|https?:|ssh:|github:|workspace:|link:|npm:)/i.test(selector);
|
|
356
|
-
}
|
|
357
287
|
async function readPackageJson(packageDir) {
|
|
358
288
|
const packageJsonPath = path2.join(packageDir, "package.json");
|
|
359
289
|
const info = await lstat(packageJsonPath);
|
|
@@ -365,26 +295,6 @@ async function readPackageJson(packageDir) {
|
|
|
365
295
|
throw new Error(`Package metadata is not an object: ${packageJsonPath}`);
|
|
366
296
|
return parsed;
|
|
367
297
|
}
|
|
368
|
-
async function verifyReceiptEntry(installRoot, packagePath, entry, label) {
|
|
369
|
-
if (entry === undefined)
|
|
370
|
-
return;
|
|
371
|
-
const relative = safeReceiptPath(entry, label);
|
|
372
|
-
if (!relative.startsWith(`${packagePath}/`)) {
|
|
373
|
-
throw new Error(`${label} is outside its package.`);
|
|
374
|
-
}
|
|
375
|
-
const packageDir = path2.resolve(installRoot, ...packagePath.split("/"));
|
|
376
|
-
const entryPath = path2.resolve(installRoot, ...relative.split("/"));
|
|
377
|
-
const [realPackageDir, realEntry] = await Promise.all([
|
|
378
|
-
realpath(packageDir),
|
|
379
|
-
realpath(entryPath)
|
|
380
|
-
]);
|
|
381
|
-
if (!isContained(realPackageDir, realEntry))
|
|
382
|
-
throw new Error(`${label} escapes its package.`);
|
|
383
|
-
const info = await lstat(realEntry);
|
|
384
|
-
if (!info.isFile() || info.isSymbolicLink())
|
|
385
|
-
throw new Error(`${label} is not a regular file.`);
|
|
386
|
-
return realEntry;
|
|
387
|
-
}
|
|
388
298
|
function packageResolutionTargets(manifest, subpath, condition) {
|
|
389
299
|
if (manifest.exports !== undefined) {
|
|
390
300
|
try {
|
|
@@ -452,112 +362,16 @@ async function resolveEntryCandidate(packageDir, resolution) {
|
|
|
452
362
|
}
|
|
453
363
|
return null;
|
|
454
364
|
}
|
|
455
|
-
async function resolvePackageEntry(packageDir, packageJson
|
|
365
|
+
async function resolvePackageEntry(packageDir, packageJson) {
|
|
456
366
|
const manifest = packageJson ?? await readPackageJson(packageDir);
|
|
457
|
-
for (const
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
throw new Error("Package has no contained JavaScript entry point for ESM import.");
|
|
463
|
-
}
|
|
464
|
-
async function listTreeEntries(root) {
|
|
465
|
-
const out = [];
|
|
466
|
-
async function visit(dir) {
|
|
467
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
468
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
469
|
-
for (const entry of entries) {
|
|
470
|
-
const absolute = path2.join(dir, entry.name);
|
|
471
|
-
const relative = toPosixPath(path2.relative(root, absolute));
|
|
472
|
-
if (relative === VENDOR_RECEIPT_FILE)
|
|
473
|
-
continue;
|
|
474
|
-
if (entry.isSymbolicLink())
|
|
475
|
-
throw new Error(`Vendor tree contains a symbolic link: ${relative}`);
|
|
476
|
-
if (entry.isDirectory()) {
|
|
477
|
-
out.push({ relative, directory: true });
|
|
478
|
-
await visit(absolute);
|
|
479
|
-
} else if (entry.isFile())
|
|
480
|
-
out.push({ relative, directory: false });
|
|
481
|
-
else
|
|
482
|
-
throw new Error(`Vendor tree contains an unsupported filesystem entry: ${relative}`);
|
|
367
|
+
for (const condition of ["import", "require"]) {
|
|
368
|
+
for (const target of packageResolutionTargets(manifest, "", condition)) {
|
|
369
|
+
const resolved = await resolveEntryCandidate(packageDir, target);
|
|
370
|
+
if (resolved)
|
|
371
|
+
return resolved;
|
|
483
372
|
}
|
|
484
373
|
}
|
|
485
|
-
|
|
486
|
-
return out;
|
|
487
|
-
}
|
|
488
|
-
async function computeVendorTreeDigest(root) {
|
|
489
|
-
const entries = await listTreeEntries(root);
|
|
490
|
-
const files = entries.filter((entry) => !entry.directory);
|
|
491
|
-
if (files.length > MAX_VERIFIED_FILES) {
|
|
492
|
-
throw new Error(`Vendor tree contains more than ${MAX_VERIFIED_FILES} files.`);
|
|
493
|
-
}
|
|
494
|
-
const seen = new Set;
|
|
495
|
-
const hash = createHash("sha256");
|
|
496
|
-
let bytes = 0;
|
|
497
|
-
for (const entry of entries) {
|
|
498
|
-
const { relative } = entry;
|
|
499
|
-
assertWindowsSafeRelativePath(relative);
|
|
500
|
-
const key = windowsPathKey(relative);
|
|
501
|
-
if (seen.has(key))
|
|
502
|
-
throw new Error(`Vendor tree has a Windows-colliding path: ${relative}`);
|
|
503
|
-
seen.add(key);
|
|
504
|
-
if (entry.directory)
|
|
505
|
-
continue;
|
|
506
|
-
const absolute = path2.join(root, ...relative.split("/"));
|
|
507
|
-
const info = await lstat(absolute);
|
|
508
|
-
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1) {
|
|
509
|
-
throw new Error(`Vendor tree file is not an independent regular file: ${relative}`);
|
|
510
|
-
}
|
|
511
|
-
bytes += info.size;
|
|
512
|
-
if (bytes > MAX_VERIFIED_BYTES) {
|
|
513
|
-
throw new Error(`Vendor tree exceeds ${Math.round(MAX_VERIFIED_BYTES / 1024 / 1024)}MB.`);
|
|
514
|
-
}
|
|
515
|
-
hash.update(`${Buffer.byteLength(relative)}:${relative}:${info.size}\x00`);
|
|
516
|
-
for await (const chunk of createReadStream(absolute))
|
|
517
|
-
hash.update(chunk);
|
|
518
|
-
}
|
|
519
|
-
return { digest: hash.digest("hex"), files: files.length, bytes };
|
|
520
|
-
}
|
|
521
|
-
async function assertVendorPackageOwnership(root, packagePaths) {
|
|
522
|
-
const packages = packagePaths.map((relative) => ({
|
|
523
|
-
relative,
|
|
524
|
-
key: windowsPathKey(relative),
|
|
525
|
-
parts: relative.split("/").length
|
|
526
|
-
})).sort((a, b) => b.key.length - a.key.length);
|
|
527
|
-
for (const entry of await listTreeEntries(root)) {
|
|
528
|
-
const actualParts = entry.relative.split("/");
|
|
529
|
-
for (const part of actualParts) {
|
|
530
|
-
if (part.normalize("NFKC").toLowerCase() === "node_modules" && part !== "node_modules") {
|
|
531
|
-
throw new Error(`Vendor tree contains a non-canonical node_modules path: ${entry.relative}`);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
const key = windowsPathKey(entry.relative);
|
|
535
|
-
const structural = packages.some((pkg) => pkg.key === key || pkg.key.startsWith(`${key}/`));
|
|
536
|
-
if (entry.directory && structural)
|
|
537
|
-
continue;
|
|
538
|
-
const owner = packages.find((pkg) => key.startsWith(`${pkg.key}/`));
|
|
539
|
-
if (!owner)
|
|
540
|
-
throw new Error(`Vendor tree path is not owned by a receipt package: ${entry.relative}`);
|
|
541
|
-
const packageRelative = actualParts.slice(owner.parts);
|
|
542
|
-
const nodeModulesAt = packageRelative.findIndex((part) => part.normalize("NFKC").toLowerCase() === "node_modules");
|
|
543
|
-
if (nodeModulesAt < 0)
|
|
544
|
-
continue;
|
|
545
|
-
const generatedEmptyDirectory = entry.directory && nodeModulesAt === 0 && (packageRelative.length === 1 || packageRelative.length === 2 && packageRelative[1].startsWith("@"));
|
|
546
|
-
if (!generatedEmptyDirectory) {
|
|
547
|
-
throw new Error(`Package contains bundled node_modules content: ${entry.relative}`);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
async function recomputeReceiptEntry(packageDir, packagePath, manifest, condition) {
|
|
552
|
-
try {
|
|
553
|
-
const entry = await resolvePackageEntry(packageDir, manifest, condition);
|
|
554
|
-
return `${packagePath}/${entry}`;
|
|
555
|
-
} catch (error) {
|
|
556
|
-
if (error instanceof Error && error.message.includes("no contained JavaScript entry point")) {
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
throw error;
|
|
560
|
-
}
|
|
374
|
+
throw new Error("Package has no contained JavaScript entry point.");
|
|
561
375
|
}
|
|
562
376
|
async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
|
|
563
377
|
const requestedProjectRoot = path2.resolve(projectDir);
|
|
@@ -582,214 +396,6 @@ async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expect
|
|
|
582
396
|
}
|
|
583
397
|
return installRoot;
|
|
584
398
|
}
|
|
585
|
-
function reconcileReceiptGraph(receipt, packages, manifests) {
|
|
586
|
-
if (!Array.isArray(receipt.skipped))
|
|
587
|
-
throw new Error("Vendor receipt skipped list is invalid.");
|
|
588
|
-
const declarations = new Map;
|
|
589
|
-
for (const [packagePath, manifest] of manifests) {
|
|
590
|
-
declarations.set(packagePath, dependencyDeclarations(manifest));
|
|
591
|
-
}
|
|
592
|
-
const skippedByEdge = new Map;
|
|
593
|
-
for (const skipped of receipt.skipped) {
|
|
594
|
-
if (!packages.has(skipped.from) || !isValidNpmPackageName(skipped.name) || typeof skipped.selector !== "string" || !["optional", "optional-peer"].includes(skipped.kind) || typeof skipped.reason !== "string") {
|
|
595
|
-
throw new Error("Vendor receipt contains an invalid skipped dependency.");
|
|
596
|
-
}
|
|
597
|
-
const declared = declarations.get(skipped.from);
|
|
598
|
-
const expectedSelector = skipped.kind === "optional" ? declared.optional[skipped.name] : declared.optionalPeers[skipped.name];
|
|
599
|
-
if (expectedSelector !== skipped.selector) {
|
|
600
|
-
throw new Error(`Skipped dependency is not declared as ${skipped.kind}: ${skipped.name}`);
|
|
601
|
-
}
|
|
602
|
-
const key = `${skipped.from}\x00${skipped.name}`;
|
|
603
|
-
if (skippedByEdge.has(key))
|
|
604
|
-
throw new Error(`Duplicate skipped dependency: ${skipped.name}`);
|
|
605
|
-
skippedByEdge.set(key, skipped);
|
|
606
|
-
}
|
|
607
|
-
for (const pkg of receipt.packages) {
|
|
608
|
-
if (!object(pkg.dependencies))
|
|
609
|
-
throw new Error(`Invalid dependency map for ${pkg.name}.`);
|
|
610
|
-
const declared = declarations.get(pkg.path);
|
|
611
|
-
for (const [dependency, target] of Object.entries(pkg.dependencies)) {
|
|
612
|
-
const targetPackage = packages.get(target);
|
|
613
|
-
if (!isValidNpmPackageName(dependency) || !targetPackage || targetPackage.name !== dependency) {
|
|
614
|
-
throw new Error(`Broken dependency edge ${pkg.name} -> ${dependency}.`);
|
|
615
|
-
}
|
|
616
|
-
const selector = declared.required[dependency] ?? declared.optional[dependency];
|
|
617
|
-
if (!selector)
|
|
618
|
-
throw new Error(`Undeclared dependency edge ${pkg.name} -> ${dependency}.`);
|
|
619
|
-
if (unsupportedRegistrySelector(selector)) {
|
|
620
|
-
throw new Error(`Unsupported selector on dependency edge ${pkg.name} -> ${dependency}.`);
|
|
621
|
-
}
|
|
622
|
-
const range = validRange(selector);
|
|
623
|
-
if (range && !satisfies(targetPackage.version, range)) {
|
|
624
|
-
throw new Error(`Dependency edge ${pkg.name} -> ${dependency}@${targetPackage.version} does not satisfy ${selector}.`);
|
|
625
|
-
}
|
|
626
|
-
if (skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
|
|
627
|
-
throw new Error(`Dependency is both installed and skipped: ${pkg.name} -> ${dependency}.`);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
for (const dependency of Object.keys(declared.required)) {
|
|
631
|
-
if (!(dependency in pkg.dependencies)) {
|
|
632
|
-
throw new Error(`Missing required dependency edge ${pkg.name} -> ${dependency}.`);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
for (const dependency of Object.keys(declared.optional)) {
|
|
636
|
-
if (!(dependency in pkg.dependencies) && !skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
|
|
637
|
-
throw new Error(`Optional dependency is neither installed nor skipped: ${pkg.name} -> ${dependency}.`);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
for (const dependency of Object.keys(declared.optionalPeers)) {
|
|
641
|
-
if (!skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
|
|
642
|
-
throw new Error(`Optional peer dependency is not recorded as skipped: ${pkg.name} -> ${dependency}.`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
async function verifyVendoredPlugin(projectDir, expectedName, expectedVersion) {
|
|
648
|
-
try {
|
|
649
|
-
const installRoot = await resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion);
|
|
650
|
-
if (!installRoot)
|
|
651
|
-
return null;
|
|
652
|
-
const receiptPath = path2.join(installRoot, VENDOR_RECEIPT_FILE);
|
|
653
|
-
let raw;
|
|
654
|
-
try {
|
|
655
|
-
const info = await lstat(receiptPath);
|
|
656
|
-
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.size > 1024 * 1024) {
|
|
657
|
-
throw new Error("Vendor receipt is not a small regular file.");
|
|
658
|
-
}
|
|
659
|
-
raw = await readFile(receiptPath, "utf8");
|
|
660
|
-
} catch (error) {
|
|
661
|
-
if (error.code === "ENOENT")
|
|
662
|
-
return null;
|
|
663
|
-
throw error;
|
|
664
|
-
}
|
|
665
|
-
const receipt = JSON.parse(raw);
|
|
666
|
-
if (receipt.schemaVersion !== VENDOR_RECEIPT_VERSION) {
|
|
667
|
-
throw new Error(`Unsupported vendor receipt schema ${String(receipt.schemaVersion)}.`);
|
|
668
|
-
}
|
|
669
|
-
if (receipt.root?.name !== expectedName || receipt.root?.version !== expectedVersion) {
|
|
670
|
-
throw new Error("Vendor receipt identity does not match the manifest entry.");
|
|
671
|
-
}
|
|
672
|
-
if (!Array.isArray(receipt.packages) || receipt.packages.length === 0) {
|
|
673
|
-
throw new Error("Vendor receipt has no package graph.");
|
|
674
|
-
}
|
|
675
|
-
const tree = await computeVendorTreeDigest(installRoot);
|
|
676
|
-
if (receipt.tree?.algorithm !== "sha256" || receipt.tree.digest !== tree.digest || receipt.tree.files !== tree.files || receipt.tree.bytes !== tree.bytes) {
|
|
677
|
-
throw new Error("Vendor tree hash does not match its receipt.");
|
|
678
|
-
}
|
|
679
|
-
const packages = new Map;
|
|
680
|
-
const manifests = new Map;
|
|
681
|
-
const verifiedPackages = [];
|
|
682
|
-
const pathKeys = new Set;
|
|
683
|
-
for (const pkg of receipt.packages) {
|
|
684
|
-
const relative = safeReceiptPath(pkg.path, "Package receipt path");
|
|
685
|
-
const key = windowsPathKey(relative);
|
|
686
|
-
if (pathKeys.has(key))
|
|
687
|
-
throw new Error(`Duplicate package receipt path: ${relative}`);
|
|
688
|
-
pathKeys.add(key);
|
|
689
|
-
if (!isValidNpmPackageName(pkg.name) || !isExactNpmVersion(pkg.version)) {
|
|
690
|
-
throw new Error(`Invalid package identity in receipt: ${pkg.name}@${pkg.version}`);
|
|
691
|
-
}
|
|
692
|
-
const expectedSuffix = `node_modules/${pkg.name}`;
|
|
693
|
-
if (relative !== expectedSuffix && !relative.endsWith(`/${expectedSuffix}`)) {
|
|
694
|
-
throw new Error(`Package receipt path does not match ${pkg.name}: ${relative}`);
|
|
695
|
-
}
|
|
696
|
-
if (typeof pkg.integrity !== "string" || !/^(?:sha512|sha384|sha256|sha1)-(?:hex-)?[A-Za-z0-9+/=]+$/.test(pkg.integrity) || typeof pkg.legacySha1 !== "boolean" || pkg.legacySha1 !== pkg.integrity.startsWith("sha1-")) {
|
|
697
|
-
throw new Error(`Package receipt is missing provenance: ${pkg.name}@${pkg.version}`);
|
|
698
|
-
}
|
|
699
|
-
const tarball = new URL(pkg.tarball);
|
|
700
|
-
if (tarball.origin !== new URL(npmRegistryUrl()).origin || tarball.username || tarball.password) {
|
|
701
|
-
throw new Error(`Package receipt has an invalid tarball URL: ${pkg.name}@${pkg.version}`);
|
|
702
|
-
}
|
|
703
|
-
const packageDir = path2.resolve(installRoot, ...relative.split("/"));
|
|
704
|
-
if (!isContained(installRoot, packageDir))
|
|
705
|
-
throw new Error(`Package escapes vendor root: ${relative}`);
|
|
706
|
-
const realPackageDir2 = await realpath(packageDir);
|
|
707
|
-
if (!isContained(installRoot, realPackageDir2)) {
|
|
708
|
-
throw new Error(`Package resolves outside the vendor root: ${relative}`);
|
|
709
|
-
}
|
|
710
|
-
const manifest = await readPackageJson(packageDir);
|
|
711
|
-
if (manifest.name !== pkg.name || manifest.version !== pkg.version) {
|
|
712
|
-
throw new Error(`Installed package identity changed: ${pkg.name}@${pkg.version}`);
|
|
713
|
-
}
|
|
714
|
-
const [expectedEntry, expectedRequireEntry] = await Promise.all([
|
|
715
|
-
recomputeReceiptEntry(packageDir, relative, manifest, "import"),
|
|
716
|
-
recomputeReceiptEntry(packageDir, relative, manifest, "require")
|
|
717
|
-
]);
|
|
718
|
-
if (pkg.entry !== expectedEntry || pkg.requireEntry !== expectedRequireEntry) {
|
|
719
|
-
throw new Error(`Recorded package entries do not match package.json: ${pkg.name}@${pkg.version}`);
|
|
720
|
-
}
|
|
721
|
-
packages.set(relative, pkg);
|
|
722
|
-
manifests.set(relative, manifest);
|
|
723
|
-
verifiedPackages.push({
|
|
724
|
-
path: relative,
|
|
725
|
-
name: pkg.name,
|
|
726
|
-
packageDir: realPackageDir2,
|
|
727
|
-
manifest,
|
|
728
|
-
entryPath: await verifyReceiptEntry(installRoot, relative, pkg.entry, "Package import entry"),
|
|
729
|
-
requireEntryPath: await verifyReceiptEntry(installRoot, relative, pkg.requireEntry, "Package require entry"),
|
|
730
|
-
dependencies: pkg.dependencies
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
await assertVendorPackageOwnership(installRoot, receipt.packages.map((pkg) => pkg.path));
|
|
734
|
-
reconcileReceiptGraph(receipt, packages, manifests);
|
|
735
|
-
const rootPackagePath = safeReceiptPath(receipt.root.packagePath, "Root package path");
|
|
736
|
-
const rootPackage = packages.get(rootPackagePath);
|
|
737
|
-
if (!rootPackage || rootPackage.name !== expectedName || rootPackage.version !== expectedVersion) {
|
|
738
|
-
throw new Error("Vendor receipt root package is missing.");
|
|
739
|
-
}
|
|
740
|
-
const reachablePackages = new Set([rootPackagePath]);
|
|
741
|
-
const pendingPackages = [rootPackagePath];
|
|
742
|
-
while (pendingPackages.length > 0) {
|
|
743
|
-
const current = packages.get(pendingPackages.pop());
|
|
744
|
-
for (const target of Object.values(current.dependencies)) {
|
|
745
|
-
if (reachablePackages.has(target))
|
|
746
|
-
continue;
|
|
747
|
-
reachablePackages.add(target);
|
|
748
|
-
pendingPackages.push(target);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
if (reachablePackages.size !== receipt.packages.length) {
|
|
752
|
-
throw new Error("Vendor receipt contains a package that is not reachable from its root.");
|
|
753
|
-
}
|
|
754
|
-
const rootManifest = manifests.get(rootPackagePath);
|
|
755
|
-
if (receipt.root.format !== "module" && receipt.root.format !== "commonjs") {
|
|
756
|
-
throw new Error("Vendor receipt root format is invalid.");
|
|
757
|
-
}
|
|
758
|
-
const entryRelative = safeReceiptPath(receipt.root.entry, "Plugin entry path");
|
|
759
|
-
if (!entryRelative.startsWith(`${rootPackagePath}/`)) {
|
|
760
|
-
throw new Error("Plugin entry is outside its package.");
|
|
761
|
-
}
|
|
762
|
-
const expectedRootEntry = receipt.root.format === "commonjs" ? rootPackage.requireEntry ?? rootPackage.entry : rootPackage.entry;
|
|
763
|
-
if (entryRelative !== expectedRootEntry) {
|
|
764
|
-
throw new Error("Plugin entry does not match the package graph entry.");
|
|
765
|
-
}
|
|
766
|
-
if (packageEntryFormat(rootManifest, entryRelative) !== receipt.root.format) {
|
|
767
|
-
throw new Error("Plugin entry format does not match its package metadata.");
|
|
768
|
-
}
|
|
769
|
-
const entryPath = path2.resolve(installRoot, ...entryRelative.split("/"));
|
|
770
|
-
const rootPackageDir = path2.resolve(installRoot, ...rootPackagePath.split("/"));
|
|
771
|
-
const [realPackageDir, realEntry] = await Promise.all([
|
|
772
|
-
realpath(rootPackageDir),
|
|
773
|
-
realpath(entryPath)
|
|
774
|
-
]);
|
|
775
|
-
if (!isContained(installRoot, realEntry) || !isContained(realPackageDir, realEntry)) {
|
|
776
|
-
throw new Error("Plugin entry resolves outside the verified vendor tree.");
|
|
777
|
-
}
|
|
778
|
-
const entryInfo = await lstat(realEntry);
|
|
779
|
-
if (!entryInfo.isFile() || entryInfo.isSymbolicLink()) {
|
|
780
|
-
throw new Error("Plugin entry is not a regular file.");
|
|
781
|
-
}
|
|
782
|
-
return {
|
|
783
|
-
installRoot,
|
|
784
|
-
entryPath: realEntry,
|
|
785
|
-
format: receipt.root.format,
|
|
786
|
-
packages: verifiedPackages,
|
|
787
|
-
receipt
|
|
788
|
-
};
|
|
789
|
-
} catch (error) {
|
|
790
|
-
throw new Error(`Vendored plugin "${expectedName}@${expectedVersion}" failed verification: ` + `${error instanceof Error ? error.message : String(error)} Reinstall it before building.`, { cause: error });
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
399
|
|
|
794
400
|
// src/lib/source-provider.ts
|
|
795
401
|
var noopLogger = {
|
|
@@ -1154,7 +760,7 @@ async function countUnversionedChanges(projectDir) {
|
|
|
1154
760
|
const appWritten = `${bookPrefix}${PLUGINS_DIR}/${VENDORED_NPM_DIR}/`;
|
|
1155
761
|
return withRepoLock(dir, async () => {
|
|
1156
762
|
const { adds, removes } = await listWorkdirChanges(dir);
|
|
1157
|
-
const mine = new Set([...adds, ...removes].filter((f) => f.startsWith(bookPrefix) && !f.startsWith(appWritten)
|
|
763
|
+
const mine = new Set([...adds, ...removes].filter((f) => f.startsWith(bookPrefix) && !f.startsWith(appWritten)));
|
|
1158
764
|
return { changedFiles: mine.size, stale: gitFs.existsSync(snapshotStagingMarkerPath(dir)) };
|
|
1159
765
|
});
|
|
1160
766
|
}
|
|
@@ -1330,4 +936,4 @@ async function switchBranch(options) {
|
|
|
1330
936
|
});
|
|
1331
937
|
}
|
|
1332
938
|
|
|
1333
|
-
export {
|
|
939
|
+
export { PLUGINS_DIR, VENDORED_NPM_DIR, isValidNpmPackageName, parseNpmPluginSpec, isExactNpmVersion, vendoredNpmPluginRoot, vendoredNpmPluginPackageDir, toPosixPath, assertWindowsSafeRelativePath, windowsPathKey, packageResolutionTargets, safePackageTarget, resolvePackageEntry, resolveVendoredPluginInstallRoot, gitFs, errorLogData, resolveLogger, gitScopeFor, HISTORY_PAGE_LIMIT, withRepoLock, whenGitIdle, __repoLockQueueSizeForTests, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, countUnversionedChanges, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup, SWITCH_BRANCH_SNAPSHOT_MESSAGE, listLocalBranches, switchBranch };
|