@pipelex/sdk 0.47.0 → 0.48.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/README.md +9 -7
- package/dist/artifacts.d.ts +4 -3
- package/dist/artifacts.js +20 -14
- package/dist/artifacts.js.map +1 -1
- package/dist/cli/script.d.ts +1 -1
- package/dist/cli/source.d.ts +8 -7
- package/dist/cli/source.js +11 -5
- package/dist/cli/source.js.map +1 -1
- package/dist/client.d.ts +326 -213
- package/dist/client.js +829 -517
- package/dist/client.js.map +1 -1
- package/dist/codegen-check.d.ts +2 -3
- package/dist/codegen-check.js +2 -3
- package/dist/codegen-check.js.map +1 -1
- package/dist/errors.d.ts +5 -2
- package/dist/errors.js +5 -2
- package/dist/errors.js.map +1 -1
- package/dist/hooks/bundle-gather.d.ts +47 -40
- package/dist/hooks/bundle-gather.js +121 -219
- package/dist/hooks/bundle-gather.js.map +1 -1
- package/dist/hooks/check-core.d.ts +70 -6
- package/dist/hooks/check-core.js +188 -29
- package/dist/hooks/check-core.js.map +1 -1
- package/dist/hooks/claude-mthds-check.d.ts +19 -5
- package/dist/hooks/claude-mthds-check.js +20 -53
- package/dist/hooks/claude-mthds-check.js.map +1 -1
- package/dist/hooks/secret-guard.d.ts +38 -0
- package/dist/hooks/secret-guard.js +134 -0
- package/dist/hooks/secret-guard.js.map +1 -0
- package/dist/hooks/validate-stage.d.ts +29 -0
- package/dist/hooks/validate-stage.js +66 -0
- package/dist/hooks/validate-stage.js.map +1 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/method-trees.d.ts +220 -74
- package/dist/method-trees.js +250 -37
- package/dist/method-trees.js.map +1 -1
- package/dist/models.d.ts +162 -59
- package/dist/node-host/node.d.ts +33 -0
- package/dist/node-host/node.js +27 -0
- package/dist/node-host/node.js.map +1 -0
- package/dist/node-host/unavailable.d.ts +8 -0
- package/dist/node-host/unavailable.js +8 -0
- package/dist/node-host/unavailable.js.map +1 -0
- package/dist/node-host.d.ts +53 -0
- package/dist/node-host.js +72 -0
- package/dist/node-host.js.map +1 -0
- package/dist/prepare-inputs.d.ts +46 -27
- package/dist/prepare-inputs.js +123 -42
- package/dist/prepare-inputs.js.map +1 -1
- package/dist/product-models.d.ts +14 -16
- package/dist/upload.d.ts +4 -9
- package/dist/upload.js +13 -16
- package/dist/upload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +44 -21
- package/docs/cli.md +1 -1
- package/docs/crate-routes.md +43 -21
- package/docs/errors.md +5 -5
- package/docs/hook-bundle.md +27 -1
- package/docs/input-preparation.md +13 -13
- package/docs/method-trees.md +120 -22
- package/docs/run-results.md +5 -2
- package/package.json +8 -2
- package/dist/bundle-resources.d.ts +0 -62
- package/dist/bundle-resources.js +0 -165
- package/dist/bundle-resources.js.map +0 -1
- package/docs/bundle-resources.md +0 -107
|
@@ -1,58 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `POST /v1/validate` around an edited `.mthds` file.
|
|
2
|
+
* The method tree the validate stage sends to `POST /v1/validate` around an edited `.mthds` file.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* It is the tree a run of the method would send: the edited file's directory listed by the `mthds`
|
|
5
|
+
* package's lister, under the MTHDS standard's membership rule and Manifest Discovery, so it is
|
|
6
|
+
* rooted at the nearest `METHODS.toml` at or above that directory, else at the directory itself,
|
|
7
|
+
* and holds every file the rule keeps — every bundle of the method, and every file a step reads,
|
|
8
|
+
* such as a `PipeDocGen` step's `template_file`, under its tree path. The client sends it inline
|
|
9
|
+
* when small and by hash otherwise, uploading the files the API lacks.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* Screened, because the listing reads no `.gitignore` above the tree's root, so a repository's
|
|
12
|
+
* own ignore lines protect nothing in it: every file is read once, before any request, and one
|
|
13
|
+
* that looks like a secret by the workshop's rule, as `secret-guard.ts` refines it, stays home.
|
|
14
|
+
* The tree sent is the rest, over the bytes screened, never read again. A bundle or the manifest
|
|
15
|
+
* is read by every validation, so a tree without one is not the method: one that looks like a
|
|
16
|
+
* secret refuses the whole tree, and nothing is sent. Any other file is left out and the method
|
|
17
|
+
* validated without it, which only a step reading that file can notice, as a missing file the
|
|
18
|
+
* caller is told about.
|
|
19
|
+
*
|
|
20
|
+
* Capped, because the hook should not send a large tree on every edit, and because a tree it
|
|
21
|
+
* cannot list whole would risk a wrong verdict: on any of the caps, on a tree the standard refuses
|
|
22
|
+
* (a symbolic link or a special file in it, an unreadable directory), on a file that changed since
|
|
23
|
+
* it was listed, and on an edited file the tree does not hold (one the membership rule keeps out),
|
|
24
|
+
* the caller treats validate as unavailable rather than risk a wrong block.
|
|
17
25
|
*/
|
|
18
|
-
import type {
|
|
26
|
+
import type { MethodTreeSource } from "mthds/protocol";
|
|
27
|
+
import type { WithheldFile } from "./check-core.js";
|
|
28
|
+
/** The hook's own bounds on the tree it sends, whatever the deployment would take. */
|
|
19
29
|
export interface GatherCaps {
|
|
30
|
+
/** How many files the tree may hold. */
|
|
20
31
|
maxFiles: number;
|
|
32
|
+
/** How large one file of it may be, in bytes. */
|
|
33
|
+
maxFileBytes: number;
|
|
34
|
+
/** How large all its files together may be, in bytes. */
|
|
21
35
|
maxTotalBytes: number;
|
|
22
|
-
/** How many template files the gather sends at most; the server's default limit when omitted. */
|
|
23
|
-
maxResourceFiles?: number;
|
|
24
|
-
/** How large one template file may be; the server's default limit when omitted. */
|
|
25
|
-
maxResourceFileBytes?: number;
|
|
26
|
-
/** How many bytes of template files the gather sends at most; the server's default when omitted. */
|
|
27
|
-
maxResourceBytes?: number;
|
|
28
36
|
}
|
|
29
37
|
export declare const DEFAULT_GATHER_CAPS: GatherCaps;
|
|
30
38
|
/**
|
|
31
|
-
* The gathered
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
39
|
+
* The gathered tree and the files it leaves home, or why there is none to send: `secret` for a
|
|
40
|
+
* bundle or the manifest that looks like a secret, with the tree's root and every file of the
|
|
41
|
+
* tree that does, `overflow` for a tree past one of the caps, `unreadable` for a directory the
|
|
42
|
+
* standard's lister refuses to list or a file that changed since it was listed, and `outside` for
|
|
43
|
+
* an edited file the tree does not hold. A withheld file's path, and the root, are spelled as the
|
|
44
|
+
* caller spelled the edited file's directory whenever that spelling reaches the same root.
|
|
37
45
|
*/
|
|
38
46
|
export type GatherResult = {
|
|
39
47
|
ok: true;
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
tree: MethodTreeSource;
|
|
49
|
+
withheld: WithheldFile[];
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
reason: "secret";
|
|
53
|
+
root: string;
|
|
54
|
+
withheld: WithheldFile[];
|
|
42
55
|
} | {
|
|
43
56
|
ok: false;
|
|
44
|
-
reason: "overflow" | "unreadable" | "
|
|
57
|
+
reason: "overflow" | "unreadable" | "outside";
|
|
45
58
|
};
|
|
46
59
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* the
|
|
50
|
-
*
|
|
51
|
-
*/
|
|
52
|
-
export declare function gatherBundle(editedFilePath: string, caps?: GatherCaps): GatherResult;
|
|
53
|
-
/**
|
|
54
|
-
* The paths of the template files the steps of one `.mthds` file name: each
|
|
55
|
-
* `PipeDocGen` pipe's `template_file`. A file that does not parse names none, the
|
|
56
|
-
* server giving its verdict on it.
|
|
60
|
+
* List the method tree around the edited file, its directory under the standard's membership
|
|
61
|
+
* rule and the hook's caps, then read and screen every file of it. The files are read here, after
|
|
62
|
+
* the format write-back the hook made before this call, and the tree returned serves the bytes
|
|
63
|
+
* read, so what was screened is what is sent.
|
|
57
64
|
*/
|
|
58
|
-
export declare function
|
|
65
|
+
export declare function gatherMethodTree(editedFilePath: string, caps?: GatherCaps): Promise<GatherResult>;
|
|
@@ -1,252 +1,154 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `POST /v1/validate` around an edited `.mthds` file.
|
|
2
|
+
* The method tree the validate stage sends to `POST /v1/validate` around an edited `.mthds` file.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* It is the tree a run of the method would send: the edited file's directory listed by the `mthds`
|
|
5
|
+
* package's lister, under the MTHDS standard's membership rule and Manifest Discovery, so it is
|
|
6
|
+
* rooted at the nearest `METHODS.toml` at or above that directory, else at the directory itself,
|
|
7
|
+
* and holds every file the rule keeps — every bundle of the method, and every file a step reads,
|
|
8
|
+
* such as a `PipeDocGen` step's `template_file`, under its tree path. The client sends it inline
|
|
9
|
+
* when small and by hash otherwise, uploading the files the API lacks.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* Screened, because the listing reads no `.gitignore` above the tree's root, so a repository's
|
|
12
|
+
* own ignore lines protect nothing in it: every file is read once, before any request, and one
|
|
13
|
+
* that looks like a secret by the workshop's rule, as `secret-guard.ts` refines it, stays home.
|
|
14
|
+
* The tree sent is the rest, over the bytes screened, never read again. A bundle or the manifest
|
|
15
|
+
* is read by every validation, so a tree without one is not the method: one that looks like a
|
|
16
|
+
* secret refuses the whole tree, and nothing is sent. Any other file is left out and the method
|
|
17
|
+
* validated without it, which only a step reading that file can notice, as a missing file the
|
|
18
|
+
* caller is told about.
|
|
19
|
+
*
|
|
20
|
+
* Capped, because the hook should not send a large tree on every edit, and because a tree it
|
|
21
|
+
* cannot list whole would risk a wrong verdict: on any of the caps, on a tree the standard refuses
|
|
22
|
+
* (a symbolic link or a special file in it, an unreadable directory), on a file that changed since
|
|
23
|
+
* it was listed, and on an edited file the tree does not hold (one the membership rule keeps out),
|
|
24
|
+
* the caller treats validate as unavailable rather than risk a wrong block.
|
|
17
25
|
*/
|
|
18
|
-
import {
|
|
19
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
/** Mirror of pipelex's `scan_config.excluded_dirs` (dot-dirs are skipped wholesale). */
|
|
23
|
-
const EXCLUDED_DIRS = new Set([
|
|
24
|
-
"venv",
|
|
25
|
-
"env",
|
|
26
|
-
"virtualenv",
|
|
27
|
-
"__pycache__",
|
|
28
|
-
"node_modules",
|
|
29
|
-
"results",
|
|
30
|
-
]);
|
|
31
|
-
/** The server's default bounds on the files a request sends with its bundle. */
|
|
32
|
-
const DEFAULT_MAX_RESOURCE_FILES = 32;
|
|
33
|
-
const DEFAULT_MAX_RESOURCE_FILE_BYTES = 5 * 1024 * 1024;
|
|
34
|
-
const DEFAULT_MAX_RESOURCE_BYTES = 10 * 1024 * 1024;
|
|
26
|
+
import { realpathSync } from "node:fs";
|
|
27
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
28
|
+
import { listDirectoryTree, treeDigestOf } from "../method-trees.js";
|
|
29
|
+
import { secretFindings } from "./secret-guard.js";
|
|
35
30
|
export const DEFAULT_GATHER_CAPS = {
|
|
36
|
-
maxFiles:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
maxResourceFileBytes: DEFAULT_MAX_RESOURCE_FILE_BYTES,
|
|
40
|
-
maxResourceBytes: DEFAULT_MAX_RESOURCE_BYTES,
|
|
31
|
+
maxFiles: 100,
|
|
32
|
+
maxFileBytes: 5 * 1024 * 1024,
|
|
33
|
+
maxTotalBytes: 12 * 1024 * 1024,
|
|
41
34
|
};
|
|
42
35
|
/**
|
|
43
|
-
*
|
|
44
|
-
* the
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* bundle. The caps guard the opposite failure (over-supply), never this one.
|
|
48
|
-
*/
|
|
49
|
-
function walkMthdsFiles(dir, collected) {
|
|
50
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
51
|
-
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
52
|
-
if (entry.name.startsWith(".") || EXCLUDED_DIRS.has(entry.name)) {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
const full = join(dir, entry.name);
|
|
56
|
-
if (entry.isDirectory()) {
|
|
57
|
-
walkMthdsFiles(full, collected);
|
|
58
|
-
}
|
|
59
|
-
else if (entry.isFile() && entry.name.endsWith(".mthds")) {
|
|
60
|
-
collected.push(full);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Gather the `.mthds` files under the edited file's parent directory, the
|
|
66
|
-
* edited file first (absent any `main_pipe`, the server's primary blueprint is
|
|
67
|
-
* the first content — make that the file being edited). Contents are read
|
|
68
|
-
* fresh from disk, so a format write-back is already reflected.
|
|
36
|
+
* List the method tree around the edited file, its directory under the standard's membership
|
|
37
|
+
* rule and the hook's caps, then read and screen every file of it. The files are read here, after
|
|
38
|
+
* the format write-back the hook made before this call, and the tree returned serves the bytes
|
|
39
|
+
* read, so what was screened is what is sent.
|
|
69
40
|
*/
|
|
70
|
-
export function
|
|
41
|
+
export async function gatherMethodTree(editedFilePath, caps = DEFAULT_GATHER_CAPS) {
|
|
71
42
|
const editedAbs = resolve(editedFilePath);
|
|
72
|
-
|
|
73
|
-
const paths = [];
|
|
43
|
+
let tree;
|
|
74
44
|
try {
|
|
75
|
-
|
|
45
|
+
tree = await listDirectoryTree(dirname(editedAbs), {
|
|
46
|
+
maxFileCount: caps.maxFiles,
|
|
47
|
+
maxFileSize: caps.maxFileBytes,
|
|
48
|
+
maxTotalSize: caps.maxTotalBytes,
|
|
49
|
+
}, "validate");
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return { ok: false, reason: isLimitExceeded(err) ? "overflow" : "unreadable" };
|
|
53
|
+
}
|
|
54
|
+
const root = tree.root;
|
|
55
|
+
const treePath = root === null ? undefined : treePathOf(editedAbs, root);
|
|
56
|
+
if (root === null || treePath === undefined || !tree.files.some((f) => f.path === treePath)) {
|
|
57
|
+
return { ok: false, reason: "outside" };
|
|
58
|
+
}
|
|
59
|
+
// The tree's reader checks each file against the hash it was listed with.
|
|
60
|
+
const bytesByPath = new Map();
|
|
61
|
+
try {
|
|
62
|
+
for (const file of tree.files) {
|
|
63
|
+
bytesByPath.set(file.path, await tree.readFile(file.path));
|
|
64
|
+
}
|
|
76
65
|
}
|
|
77
66
|
catch {
|
|
78
67
|
return { ok: false, reason: "unreadable" };
|
|
79
68
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return { ok: false, reason: "unreadable" };
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
const gathered = gatherTemplateFiles(parentDir, ordered, files, caps);
|
|
106
|
-
if (!gathered.ok)
|
|
107
|
-
return gathered;
|
|
108
|
-
return { ok: true, files, resources: gathered.resources };
|
|
69
|
+
const findings = secretFindings(tree.files.map((file) => ({ path: file.path, bytes: bytesByPath.get(file.path) })));
|
|
70
|
+
const shownRoot = shownRootOf(editedAbs, root);
|
|
71
|
+
const withheld = findings.map((finding) => ({
|
|
72
|
+
path: join(shownRoot, ...finding.path.split("/")),
|
|
73
|
+
reason: finding.reason,
|
|
74
|
+
}));
|
|
75
|
+
if (findings.some((finding) => isReadByEveryValidation(finding.path))) {
|
|
76
|
+
// The files to fix first lead, so a note naming only the first few still names them.
|
|
77
|
+
const firsts = findings.map((finding) => isReadByEveryValidation(finding.path));
|
|
78
|
+
const ordered = [
|
|
79
|
+
...withheld.filter((_, index) => firsts[index]),
|
|
80
|
+
...withheld.filter((_, index) => !firsts[index]),
|
|
81
|
+
];
|
|
82
|
+
return { ok: false, reason: "secret", root: shownRoot, withheld: ordered };
|
|
83
|
+
}
|
|
84
|
+
// A withheld file's bytes leave the map, so the tree sent cannot serve them.
|
|
85
|
+
for (const finding of findings)
|
|
86
|
+
bytesByPath.delete(finding.path);
|
|
87
|
+
const files = tree.files.filter((file) => bytesByPath.has(file.path));
|
|
88
|
+
return { ok: true, tree: await treeOver(files, bytesByPath), withheld };
|
|
109
89
|
}
|
|
110
90
|
/**
|
|
111
|
-
* The
|
|
112
|
-
*
|
|
113
|
-
*
|
|
91
|
+
* The tree's root as the caller spells it, the way the hook's output names the files of the
|
|
92
|
+
* tree: the root reached from the edited file's directory as the caller wrote it, by the same
|
|
93
|
+
* steps that lead from that directory's real path to the root, when it is the same directory,
|
|
94
|
+
* and the root's real path, the lister's, otherwise. A directory reached through a link above
|
|
95
|
+
* the root is so named through the link, while a link pointing into the tree, whose steps up
|
|
96
|
+
* leave the tree, falls back to the real path.
|
|
114
97
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
98
|
+
function shownRootOf(editedAbs, root) {
|
|
99
|
+
const editedDir = dirname(editedAbs);
|
|
117
100
|
try {
|
|
118
|
-
|
|
101
|
+
const candidate = resolve(editedDir, relative(realpathSync(editedDir), root));
|
|
102
|
+
return realpathSync(candidate) === root ? candidate : root;
|
|
119
103
|
}
|
|
120
104
|
catch {
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
const pipes = isRecord(document) ? document["pipe"] : undefined;
|
|
124
|
-
if (!isRecord(pipes))
|
|
125
|
-
return [];
|
|
126
|
-
const named = [];
|
|
127
|
-
for (const pipe of Object.values(pipes)) {
|
|
128
|
-
if (isRecord(pipe) &&
|
|
129
|
-
pipe["type"] === "PipeDocGen" &&
|
|
130
|
-
typeof pipe["template_file"] === "string") {
|
|
131
|
-
named.push(pipe["template_file"]);
|
|
132
|
-
}
|
|
105
|
+
return root;
|
|
133
106
|
}
|
|
134
|
-
|
|
107
|
+
}
|
|
108
|
+
/** Whether every validation reads the file whole: a bundle, or the manifest at the root. */
|
|
109
|
+
function isReadByEveryValidation(treePath) {
|
|
110
|
+
return treePath.endsWith(".mthds") || treePath === "METHODS.toml";
|
|
135
111
|
}
|
|
136
112
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* file that is not there beside any `.mthds` file naming it, for the server to say
|
|
141
|
-
* the step's file was not supplied.
|
|
142
|
-
*
|
|
143
|
-
* Validate is unavailable rather than a wrong verdict when the request cannot carry
|
|
144
|
-
* what loading the bundle from disk would find: a file that leads out of the
|
|
145
|
-
* bundle's directory through a link is never read (`unreadable`), and one path that
|
|
146
|
-
* names different files beside different `.mthds` files, or a file beside one and
|
|
147
|
-
* nothing beside another, is a `conflict`. Every size is checked against the caps
|
|
148
|
-
* before any file is read, the bytes compared to tell two files apart included, so
|
|
149
|
-
* the hook never holds more than the caps allow (`overflow`).
|
|
113
|
+
* The tree of `files`, the listing's own entries, serving the bytes already read, which hold
|
|
114
|
+
* those files' bytes alone, with the digest of that listing: the listed tree's digest when
|
|
115
|
+
* nothing was left out.
|
|
150
116
|
*/
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
// Each path a step names, once, with the file it names beside the first `.mthds` file
|
|
163
|
-
// naming it, or `null` when there is none there.
|
|
164
|
-
const located = new Map();
|
|
165
|
-
let fileCount = 0;
|
|
166
|
-
let totalBytes = 0;
|
|
167
|
-
for (const [index, mthdsPath] of paths.entries()) {
|
|
168
|
-
for (const named of templateFilesNamedBy(files[index].content)) {
|
|
169
|
-
if (bundleResourcePathRefusal(named) !== undefined)
|
|
170
|
-
continue;
|
|
171
|
-
const key = canonicalBundleResourcePath(named);
|
|
172
|
-
const found = locateRegularFile(join(dirname(mthdsPath), key));
|
|
173
|
-
if (found !== null) {
|
|
174
|
-
if (!found.real.startsWith(root + sep))
|
|
175
|
-
return { ok: false, reason: "unreadable" };
|
|
176
|
-
if (found.size > maxFileBytes)
|
|
177
|
-
return { ok: false, reason: "overflow" };
|
|
178
|
-
}
|
|
179
|
-
if (!located.has(key)) {
|
|
180
|
-
located.set(key, found);
|
|
181
|
-
if (found === null)
|
|
182
|
-
continue;
|
|
183
|
-
fileCount += 1;
|
|
184
|
-
if (fileCount > maxFiles)
|
|
185
|
-
return { ok: false, reason: "overflow" };
|
|
186
|
-
totalBytes += found.size;
|
|
187
|
-
if (totalBytes > maxBytes)
|
|
188
|
-
return { ok: false, reason: "overflow" };
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
const known = located.get(key);
|
|
192
|
-
if (known === null || found === null) {
|
|
193
|
-
if (known !== found)
|
|
194
|
-
return { ok: false, reason: "conflict" };
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (!sameFileContent(known, found))
|
|
198
|
-
return { ok: false, reason: "conflict" };
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const resources = Object.create(null);
|
|
202
|
-
let readBytes = 0;
|
|
203
|
-
for (const [key, found] of located) {
|
|
204
|
-
if (found === null)
|
|
205
|
-
continue;
|
|
206
|
-
try {
|
|
207
|
-
const bytes = readFileSync(found.real);
|
|
208
|
-
// A file that grew since it was measured is held to the caps as read.
|
|
209
|
-
readBytes += bytes.length;
|
|
210
|
-
if (bytes.length > maxFileBytes || readBytes > maxBytes) {
|
|
211
|
-
return { ok: false, reason: "overflow" };
|
|
212
|
-
}
|
|
213
|
-
resources[key] = bytes;
|
|
214
|
-
}
|
|
215
|
-
catch {
|
|
216
|
-
return { ok: false, reason: "unreadable" };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return { ok: true, resources };
|
|
117
|
+
async function treeOver(files, bytesByPath) {
|
|
118
|
+
return {
|
|
119
|
+
files,
|
|
120
|
+
digest: await treeDigestOf(files),
|
|
121
|
+
readFile: async (treePath) => {
|
|
122
|
+
const bytes = bytesByPath.get(treePath);
|
|
123
|
+
if (bytes === undefined)
|
|
124
|
+
throw new Error(`The tree holds no file '${treePath}'.`);
|
|
125
|
+
return bytes;
|
|
126
|
+
},
|
|
127
|
+
};
|
|
220
128
|
}
|
|
221
|
-
/** The
|
|
222
|
-
function
|
|
129
|
+
/** The edited file's tree path under `root`, or `undefined` when it lies outside it. */
|
|
130
|
+
function treePathOf(editedAbs, root) {
|
|
131
|
+
let real;
|
|
223
132
|
try {
|
|
224
|
-
|
|
225
|
-
const stats = statSync(real);
|
|
226
|
-
return stats.isFile() ? { real, size: stats.size } : null;
|
|
133
|
+
real = realpathSync(editedAbs);
|
|
227
134
|
}
|
|
228
135
|
catch {
|
|
229
|
-
return
|
|
136
|
+
return undefined;
|
|
230
137
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
function sameFileContent(left, right) {
|
|
238
|
-
if (left.real === right.real)
|
|
239
|
-
return true;
|
|
240
|
-
if (left.size !== right.size)
|
|
241
|
-
return false;
|
|
242
|
-
try {
|
|
243
|
-
return Buffer.compare(readFileSync(left.real), readFileSync(right.real)) === 0;
|
|
244
|
-
}
|
|
245
|
-
catch {
|
|
246
|
-
return false;
|
|
138
|
+
const relativePath = relative(root, real);
|
|
139
|
+
if (relativePath === "" ||
|
|
140
|
+
relativePath === ".." ||
|
|
141
|
+
relativePath.startsWith(`..${sep}`) ||
|
|
142
|
+
isAbsolute(relativePath)) {
|
|
143
|
+
return undefined;
|
|
247
144
|
}
|
|
145
|
+
return relativePath.split(sep).join("/").normalize("NFC");
|
|
248
146
|
}
|
|
249
|
-
|
|
250
|
-
|
|
147
|
+
/** Whether a listing failed on one of the caps: the lister's `limit_exceeded`. */
|
|
148
|
+
function isLimitExceeded(err) {
|
|
149
|
+
const cause = err instanceof Error ? err.cause : undefined;
|
|
150
|
+
return (typeof cause === "object" &&
|
|
151
|
+
cause !== null &&
|
|
152
|
+
cause.code === "limit_exceeded");
|
|
251
153
|
}
|
|
252
154
|
//# sourceMappingURL=bundle-gather.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-gather.js","sourceRoot":"","sources":["../../src/hooks/bundle-gather.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"bundle-gather.js","sourceRoot":"","sources":["../../src/hooks/bundle-gather.ts"],"names":[],"mappings":"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"}
|