@openclaw/fs-safe 0.13.1 → 0.15.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/CHANGELOG.md +64 -1
- package/README.md +10 -4
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -1
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +113 -46
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +4 -4
- package/dist/archive-zip-directory.d.ts +4 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +2 -0
- package/dist/archive-zip-entry.d.ts +6 -2
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-entry.js +23 -8
- package/dist/archive-zip-integrity.d.ts.map +1 -1
- package/dist/archive-zip-integrity.js +3 -4
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +107 -31
- package/dist/archive-zip-names.d.ts +1 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +6 -0
- package/dist/archive.js +6 -5
- package/dist/bounded-read-stream.d.ts +0 -1
- package/dist/bounded-read-stream.d.ts.map +1 -1
- package/dist/bounded-read-stream.js +0 -6
- package/dist/copy-publication.d.ts +6 -0
- package/dist/copy-publication.d.ts.map +1 -1
- package/dist/copy-publication.js +3 -0
- package/dist/copy-tree-portable.d.ts.map +1 -1
- package/dist/copy-tree-portable.js +44 -24
- package/dist/copy.d.ts.map +1 -1
- package/dist/copy.js +29 -11
- package/dist/directory-mode-owner.js +5 -5
- package/dist/file-handle-transfer.d.ts +2 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +57 -2
- package/dist/file-identity.d.ts.map +1 -1
- package/dist/file-identity.js +18 -4
- package/dist/file-lock-sync-admission.d.ts +19 -0
- package/dist/file-lock-sync-admission.d.ts.map +1 -0
- package/dist/file-lock-sync-admission.js +93 -0
- package/dist/file-lock-sync-root-acquire.d.ts +4 -0
- package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
- package/dist/file-lock-sync-root-acquire.js +370 -0
- package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
- package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
- package/dist/file-lock-sync-root-arbitration.js +66 -0
- package/dist/file-lock-sync-root-held.d.ts +34 -0
- package/dist/file-lock-sync-root-held.d.ts.map +1 -0
- package/dist/file-lock-sync-root-held.js +393 -0
- package/dist/file-lock-sync-root-io.d.ts +44 -0
- package/dist/file-lock-sync-root-io.d.ts.map +1 -0
- package/dist/file-lock-sync-root-io.js +209 -0
- package/dist/file-lock-sync-root-mutation.d.ts +17 -0
- package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
- package/dist/file-lock-sync-root-mutation.js +277 -0
- package/dist/file-lock-sync-root-options.d.ts +20 -0
- package/dist/file-lock-sync-root-options.d.ts.map +1 -0
- package/dist/file-lock-sync-root-options.js +58 -0
- package/dist/file-lock-sync-root-registration.d.ts +2 -0
- package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
- package/dist/file-lock-sync-root-registration.js +90 -0
- package/dist/file-lock-sync-root.d.ts +36 -0
- package/dist/file-lock-sync-root.d.ts.map +1 -0
- package/dist/file-lock-sync-root.js +361 -0
- package/dist/file-lock-sync-stale-admission.d.ts +24 -0
- package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
- package/dist/file-lock-sync-stale-admission.js +205 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +245 -205
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +5 -1
- package/dist/file-store-sync-directory.d.ts.map +1 -1
- package/dist/file-store-sync-directory.js +14 -7
- package/dist/file-store-sync-write.js +3 -3
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +47 -12
- package/dist/guest-dispatch-python.js +1 -1
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +22 -15
- package/dist/json-durable-queue-ownership.d.ts +0 -1
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +0 -6
- package/dist/native-binding.d.ts +8 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-parent-admission.d.ts +3 -2
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +24 -5
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +7 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +202 -172
- package/dist/native-policy-directory-observation.d.ts +12 -0
- package/dist/native-policy-directory-observation.d.ts.map +1 -0
- package/dist/native-policy-directory-observation.js +61 -0
- package/dist/native-policy-parent-windows.d.ts +14 -0
- package/dist/native-policy-parent-windows.d.ts.map +1 -0
- package/dist/native-policy-parent-windows.js +200 -0
- package/dist/native-rename-outcome.d.ts +4 -0
- package/dist/native-rename-outcome.d.ts.map +1 -0
- package/dist/native-rename-outcome.js +8 -0
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +42 -40
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +12 -8
- package/dist/path-prefix.d.ts.map +1 -1
- package/dist/path-prefix.js +30 -8
- package/dist/path-suffix-aliases.d.ts +2 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -1
- package/dist/path-suffix-aliases.js +25 -17
- package/dist/permission-exec.d.ts +2 -0
- package/dist/permission-exec.d.ts.map +1 -1
- package/dist/permission-exec.js +150 -21
- package/dist/permissions-windows.js +1 -1
- package/dist/pinned-mutation-admission.d.ts.map +1 -1
- package/dist/pinned-mutation-admission.js +23 -29
- package/dist/pinned-mutation-observation.d.ts +7 -2
- package/dist/pinned-mutation-observation.d.ts.map +1 -1
- package/dist/pinned-mutation-observation.js +81 -18
- package/dist/pinned-mutation-shared-route.d.ts +1 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
- package/dist/pinned-mutation-shared-route.js +1 -1
- package/dist/pinned-write-types.d.ts +2 -0
- package/dist/pinned-write-types.d.ts.map +1 -1
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +75 -121
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +1 -15
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +256 -18
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +62 -70
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -12
- package/dist/replace-file-temp-owner.d.ts +5 -2
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +65 -30
- package/dist/replace-file.js +6 -6
- package/dist/retained-directory-replacement.d.ts +26 -0
- package/dist/retained-directory-replacement.d.ts.map +1 -0
- package/dist/retained-directory-replacement.js +193 -0
- package/dist/root-boundary.d.ts +1 -0
- package/dist/root-boundary.d.ts.map +1 -1
- package/dist/root-boundary.js +4 -0
- package/dist/root-context.d.ts +0 -8
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +0 -3
- package/dist/root-create-input.d.ts +6 -0
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +5 -1
- package/dist/root-directory-creation.d.ts.map +1 -1
- package/dist/root-directory-creation.js +5 -4
- package/dist/root-directory-list.d.ts +1 -0
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +20 -11
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +2 -2
- package/dist/root-path-errors.d.ts +1 -0
- package/dist/root-path-errors.d.ts.map +1 -1
- package/dist/root-path-errors.js +11 -2
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +12 -37
- package/dist/root-path.js +1 -13
- package/dist/root-remove.d.ts +1 -0
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +4 -0
- package/dist/root-walk.d.ts +1 -1
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +17 -2
- package/dist/root-write-admission.d.ts +0 -2
- package/dist/root-write-admission.d.ts.map +1 -1
- package/dist/root-write-admission.js +1 -15
- package/dist/root-write-compatibility.d.ts +1 -2
- package/dist/root-write-compatibility.d.ts.map +1 -1
- package/dist/root-write-compatibility.js +17 -68
- package/dist/root-write-complete-parent.d.ts.map +1 -1
- package/dist/root-write-complete-parent.js +10 -24
- package/dist/root-write-lock-binding.d.ts +15 -0
- package/dist/root-write-lock-binding.d.ts.map +1 -0
- package/dist/root-write-lock-binding.js +162 -0
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +29 -42
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +2 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +3 -24
- package/dist/secret-read-policy.d.ts +6 -2
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +26 -2
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +23 -12
- package/dist/sidecar-lock-acquire.d.ts +2 -28
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +288 -199
- package/dist/sidecar-lock-admission-context.d.ts +19 -0
- package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
- package/dist/sidecar-lock-admission-context.js +60 -0
- package/dist/sidecar-lock-admission-parser.d.ts +43 -0
- package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
- package/dist/sidecar-lock-admission-parser.js +113 -0
- package/dist/sidecar-lock-admission.d.ts +35 -0
- package/dist/sidecar-lock-admission.d.ts.map +1 -0
- package/dist/sidecar-lock-admission.js +7 -0
- package/dist/sidecar-lock-reclaim.d.ts +9 -4
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +80 -25
- package/dist/sidecar-lock-stale-admission.d.ts +39 -0
- package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
- package/dist/sidecar-lock-stale-admission.js +232 -0
- package/dist/sidecar-lock-target.d.ts +8 -0
- package/dist/sidecar-lock-target.d.ts.map +1 -0
- package/dist/sidecar-lock-target.js +55 -0
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +100 -16
- package/dist/staged-directory.d.ts +5 -2
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +37 -1
- package/dist/temp-workspace-admission.d.ts.map +1 -1
- package/dist/temp-workspace-admission.js +28 -4
- package/dist/temp-workspace-descriptor.d.ts.map +1 -1
- package/dist/temp-workspace-descriptor.js +9 -27
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +8 -8
- package/dist/walk.d.ts +5 -1
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +19 -6
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +2 -2
- package/docs/advanced.md +1 -1
- package/docs/archive.md +36 -9
- package/docs/atomic.md +85 -8
- package/docs/copy.md +35 -0
- package/docs/file-store.md +19 -0
- package/docs/guest.md +1 -1
- package/docs/json-store.md +5 -0
- package/docs/native-helper.md +10 -3
- package/docs/native.md +8 -0
- package/docs/output.md +6 -0
- package/docs/path-prefix.md +10 -0
- package/docs/path-suffix-aliases.md +51 -6
- package/docs/permissions.md +13 -0
- package/docs/public-api.md +3 -2
- package/docs/root.md +22 -3
- package/docs/sidecar-lock.md +109 -4
- package/docs/staged-file.md +7 -3
- package/docs/temp.md +20 -3
- package/docs/walk.md +67 -1
- package/docs/writing.md +11 -1
- package/package.json +10 -10
- package/dist/darwin-acl.d.ts +0 -4
- package/dist/darwin-acl.d.ts.map +0 -1
- package/dist/darwin-acl.js +0 -24
|
@@ -1,57 +1,133 @@
|
|
|
1
1
|
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
2
|
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
-
import {
|
|
3
|
+
import { registerAdmittedZipEntry } from "./archive-zip-entry.js";
|
|
4
4
|
import { zipPathKey } from "./archive-zip-names.js";
|
|
5
|
+
function disagreement() {
|
|
6
|
+
throw new ArchiveFormatError("ZIP decoder disagrees with admitted directory metadata");
|
|
7
|
+
}
|
|
8
|
+
function collision() {
|
|
9
|
+
throw new ArchiveSecurityError("entry-path", "zip archive contains duplicate or colliding entry names");
|
|
10
|
+
}
|
|
11
|
+
function matchesData(value, physical) {
|
|
12
|
+
if (!value || typeof value !== "object")
|
|
13
|
+
return false;
|
|
14
|
+
const data = value;
|
|
15
|
+
return Number.isInteger(data.crc32) && data.crc32 >= -0x80000000 && data.crc32 <= 0xffffffff &&
|
|
16
|
+
(data.crc32 >>> 0) === physical.crc32 && data.uncompressedSize === physical.size &&
|
|
17
|
+
data.compressedSize === physical.compressedSize && typeof data.getContentWorker === "function" &&
|
|
18
|
+
data.compression?.magic === String.fromCharCode(physical.compressionMethod & 255, physical.compressionMethod >>> 8);
|
|
19
|
+
}
|
|
5
20
|
/** Internal: the caller has admitted these unchanged bytes and their metadata. */
|
|
6
21
|
export async function loadAdmittedZipArchive(buffer, admitted) {
|
|
22
|
+
const physicalByPath = new Map();
|
|
23
|
+
for (const entry of admitted) {
|
|
24
|
+
if (physicalByPath.has(entry.portableKey))
|
|
25
|
+
collision();
|
|
26
|
+
physicalByPath.set(entry.portableKey, entry);
|
|
27
|
+
}
|
|
7
28
|
const JSZip = await importOptionalJsZip();
|
|
8
|
-
|
|
29
|
+
const archive = new JSZip();
|
|
30
|
+
const originalFile = archive.file;
|
|
31
|
+
const originalDescriptor = Object.getOwnPropertyDescriptor(archive, "file");
|
|
32
|
+
const inserted = new Map();
|
|
33
|
+
const remaining = new Map(physicalByPath);
|
|
34
|
+
// JSZip's fileAdd can misclassify UNIX type bits as directories, rename an
|
|
35
|
+
// entry, and erase its payload. Intercept only this private instance's load.
|
|
36
|
+
Object.defineProperty(archive, "file", { configurable: true, writable: true,
|
|
37
|
+
value: function (...args) {
|
|
38
|
+
if (args.length === 1)
|
|
39
|
+
return originalFile.apply(this, args);
|
|
40
|
+
const [name, data, options] = args;
|
|
41
|
+
if (this !== archive || args.length !== 3 || typeof name !== "string" || !options || typeof options !== "object")
|
|
42
|
+
disagreement();
|
|
43
|
+
validateArchiveEntryPath(name);
|
|
44
|
+
const key = zipPathKey(name);
|
|
45
|
+
const physical = remaining.get(key);
|
|
46
|
+
if (!physical) {
|
|
47
|
+
if (physicalByPath.has(key))
|
|
48
|
+
collision();
|
|
49
|
+
disagreement();
|
|
50
|
+
}
|
|
51
|
+
const fields = options;
|
|
52
|
+
const unixMode = physical.creatorSystem === 3 ? physical.externalAttributes >>> 16 : null;
|
|
53
|
+
const dosMode = physical.creatorSystem === 0 ? physical.externalAttributes & 0x3f : null;
|
|
54
|
+
const decoderDirectory = Boolean(physical.externalAttributes & 0x10) || physical.portableDirectory;
|
|
55
|
+
if (physical.index !== inserted.size || fields.unixPermissions !== unixMode || fields.dosPermissions !== dosMode ||
|
|
56
|
+
fields.dir !== decoderDirectory || !matchesData(data, physical))
|
|
57
|
+
disagreement();
|
|
58
|
+
const result = originalFile.call(this, name, data, {
|
|
59
|
+
...fields, dir: false, unixPermissions: null, dosPermissions: null, createFolders: false,
|
|
60
|
+
});
|
|
61
|
+
const entry = archive.files[name];
|
|
62
|
+
if (!entry || typeof entry !== "object" || inserted.has(entry))
|
|
63
|
+
disagreement();
|
|
64
|
+
// fileAdd also substitutes an empty promise for declared-zero contents.
|
|
65
|
+
// Retain the original decoder object so size/CRC checks still read bytes.
|
|
66
|
+
entry._data = data;
|
|
67
|
+
inserted.set(entry, { physical, data });
|
|
68
|
+
remaining.delete(key);
|
|
69
|
+
return result;
|
|
70
|
+
},
|
|
71
|
+
});
|
|
9
72
|
try {
|
|
10
|
-
|
|
73
|
+
const loaded = await archive.loadAsync(buffer, { createFolders: false });
|
|
74
|
+
if (loaded !== archive)
|
|
75
|
+
disagreement();
|
|
11
76
|
}
|
|
12
77
|
catch (error) {
|
|
78
|
+
if (error instanceof ArchiveFormatError || error instanceof ArchiveSecurityError)
|
|
79
|
+
throw error;
|
|
13
80
|
throw new ArchiveFormatError(`invalid ZIP archive: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
14
81
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Use the admitted portable interpretation, including legacy UTF-8 decoding.
|
|
21
|
-
const physicalByPath = new Map();
|
|
22
|
-
for (const entry of admitted) {
|
|
23
|
-
physicalByPath.set(entry.portableKey, entry);
|
|
82
|
+
finally {
|
|
83
|
+
if (originalDescriptor)
|
|
84
|
+
Object.defineProperty(archive, "file", originalDescriptor);
|
|
85
|
+
else
|
|
86
|
+
Reflect.deleteProperty(archive, "file");
|
|
24
87
|
}
|
|
88
|
+
const names = Object.keys(archive.files);
|
|
89
|
+
if (names.length !== admitted.length)
|
|
90
|
+
collision();
|
|
91
|
+
const normalized = [];
|
|
25
92
|
for (const name of names) {
|
|
26
93
|
validateArchiveEntryPath(name);
|
|
27
|
-
const key = zipPathKey(name);
|
|
28
|
-
const physical = physicalByPath.get(key);
|
|
29
94
|
const entry = archive.files[name];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
physicalByPath.delete(key);
|
|
95
|
+
if (!entry || typeof entry.name !== "string")
|
|
96
|
+
disagreement();
|
|
97
|
+
validateArchiveEntryPath(entry.name);
|
|
98
|
+
const insertion = inserted.get(entry);
|
|
99
|
+
if (!insertion || zipPathKey(name) !== insertion.physical.portableKey || zipPathKey(entry.name) !== insertion.physical.portableKey ||
|
|
100
|
+
entry.dir !== false || entry.unixPermissions !== null || entry.dosPermissions !== null ||
|
|
101
|
+
entry._data !== insertion.data || !matchesData(entry._data, insertion.physical))
|
|
102
|
+
disagreement();
|
|
103
|
+
normalized[insertion.physical.index] = [name, entry, insertion.physical];
|
|
104
|
+
inserted.delete(entry);
|
|
41
105
|
}
|
|
42
|
-
if (
|
|
43
|
-
|
|
106
|
+
if (remaining.size || inserted.size)
|
|
107
|
+
disagreement();
|
|
108
|
+
const files = Object.create(null);
|
|
109
|
+
for (const [name, entry, physical] of normalized) {
|
|
110
|
+
const appendSlash = physical.kind === "directory" && !name.endsWith("/");
|
|
111
|
+
const normalizedName = appendSlash ? `${name}/` : name;
|
|
112
|
+
if (Object.hasOwn(files, normalizedName))
|
|
113
|
+
collision();
|
|
114
|
+
if (appendSlash) {
|
|
115
|
+
entry.name = `${entry.name}/`;
|
|
116
|
+
}
|
|
117
|
+
files[normalizedName] = entry;
|
|
118
|
+
registerAdmittedZipEntry(entry, physical);
|
|
44
119
|
}
|
|
120
|
+
archive.files = files;
|
|
45
121
|
return archive;
|
|
46
122
|
}
|
|
47
123
|
async function importOptionalJsZip() {
|
|
48
124
|
try {
|
|
49
125
|
const module = await import("jszip");
|
|
50
126
|
const candidate = typeof module === "function" ? module : module.default;
|
|
51
|
-
if (
|
|
52
|
-
candidate
|
|
53
|
-
typeof candidate.loadAsync !== "function") {
|
|
54
|
-
throw new Error('Optional archive dependency "jszip" does not expose loadAsync().');
|
|
127
|
+
if (typeof candidate !== "function" ||
|
|
128
|
+
typeof candidate.prototype?.file !== "function" ||
|
|
129
|
+
typeof candidate.prototype?.loadAsync !== "function") {
|
|
130
|
+
throw new Error('Optional archive dependency "jszip" does not expose its constructor, file(), and loadAsync().');
|
|
55
131
|
}
|
|
56
132
|
return candidate;
|
|
57
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE;AAUD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAoBD,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE;AAUD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAoBD,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAuEzF"}
|
|
@@ -130,9 +130,15 @@ export function admitZipNames(params) {
|
|
|
130
130
|
// Unflagged legacy names can decode differently and need their own key.
|
|
131
131
|
const portableKey = portablePath === centralRaw ? centralKey
|
|
132
132
|
: interpretations.includes(portablePath) ? interpretationKey : zipPathKey(portablePath);
|
|
133
|
+
// JSZip prioritizes flagged local UTF-8 and checks a central Unicode field
|
|
134
|
+
// against local bytes. Canonically equal names can end in different separators.
|
|
135
|
+
const decoderPath = flags & 0x800 ? localUtf8
|
|
136
|
+
: centralUnicode !== undefined && (sameName || centralField.readUInt32LE(1) === updateCrc32(0, local))
|
|
137
|
+
? centralUnicode : local.toString("utf8");
|
|
133
138
|
return {
|
|
134
139
|
path,
|
|
135
140
|
portableKey,
|
|
141
|
+
portableDirectory: decoderPath.endsWith("/"),
|
|
136
142
|
directory,
|
|
137
143
|
};
|
|
138
144
|
}
|
package/dist/archive.js
CHANGED
|
@@ -13,7 +13,7 @@ import { assertPortableArchiveKind, resolveArchiveKind } from "./archive-kind.js
|
|
|
13
13
|
import { prepareArchiveDestinationGuard, preparePrivateArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
|
|
14
14
|
import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
|
|
15
15
|
import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
|
|
16
|
-
import {
|
|
16
|
+
import { zipEntryKind, zipEntryDeclaredSize, zipEntryMode, } from "./archive-zip-entry.js";
|
|
17
17
|
import { createZipIntegrityTransform, normalizeZipIntegrityError, } from "./archive-zip-integrity.js";
|
|
18
18
|
import { FsSafeError } from "./errors.js";
|
|
19
19
|
import { ArchiveSecurityError } from "./archive-errors.js";
|
|
@@ -95,7 +95,7 @@ async function writeZipFileEntry(params) {
|
|
|
95
95
|
}
|
|
96
96
|
params.deadline.check();
|
|
97
97
|
if (!handleClosedByStream) {
|
|
98
|
-
await tempHandle.close()
|
|
98
|
+
await tempHandle.close();
|
|
99
99
|
handleClosedByStream = true;
|
|
100
100
|
}
|
|
101
101
|
tempHandle = null;
|
|
@@ -153,8 +153,7 @@ async function extractZip(params) {
|
|
|
153
153
|
}
|
|
154
154
|
assertArchiveEntryPathComponentsWithinLimit(output.relPath, limits);
|
|
155
155
|
trackOutputPath(output.relPath, entry.name);
|
|
156
|
-
const
|
|
157
|
-
const entryKind = isSymlink ? "symlink" : entry.dir ? "directory" : "file";
|
|
156
|
+
const entryKind = zipEntryKind(entry);
|
|
158
157
|
const entrySize = zipEntryDeclaredSize(entry);
|
|
159
158
|
if (!shouldExtractArchiveEntry({
|
|
160
159
|
filter: params.entryFilter,
|
|
@@ -163,9 +162,11 @@ async function extractZip(params) {
|
|
|
163
162
|
})) {
|
|
164
163
|
continue;
|
|
165
164
|
}
|
|
166
|
-
if (
|
|
165
|
+
if (entryKind === "symlink") {
|
|
167
166
|
throw new ArchiveSecurityError("entry-link", `zip entry is a link: ${entry.name}`);
|
|
168
167
|
}
|
|
168
|
+
if (entryKind === "other")
|
|
169
|
+
continue;
|
|
169
170
|
const mode = zipEntryMode(entry, params.entryModes);
|
|
170
171
|
acceptedEntries.push({ path: output.relPath, kind: entry.dir ? "directory" : "file", mode });
|
|
171
172
|
await preparePrivateArchiveOutputPath({
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { Transform } from "node:stream";
|
|
2
|
-
export declare function createMaxBytesTransform(maxBytes: number): Transform;
|
|
3
2
|
export declare function createByteLimitTransform(maxBytes: number, overflowError: (bytes: number) => Error): Transform;
|
|
4
3
|
//# sourceMappingURL=bounded-read-stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,GACtC,SAAS,CAaX"}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { Transform } from "node:stream";
|
|
2
|
-
import { normalizeMaxBytes } from "./byte-budget.js";
|
|
3
|
-
import { FsSafeError } from "./errors.js";
|
|
4
|
-
export function createMaxBytesTransform(maxBytes) {
|
|
5
|
-
normalizeMaxBytes(maxBytes);
|
|
6
|
-
return createByteLimitTransform(maxBytes, (bytes) => new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
|
|
7
|
-
}
|
|
8
2
|
export function createByteLimitTransform(maxBytes, overflowError) {
|
|
9
3
|
let bytes = 0;
|
|
10
4
|
return new Transform({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BigIntStats } from "node:fs";
|
|
1
2
|
import type { PinnedWriteParams, PublishedWriteIdentity } from "./pinned-write.js";
|
|
2
3
|
export type RootCopyPublicationReceipt = Readonly<{
|
|
3
4
|
path: string;
|
|
@@ -9,7 +10,12 @@ export declare function createCopyPublicationObserver(path: string, notify?: (re
|
|
|
9
10
|
rethrowObserverFailure(error: unknown): void;
|
|
10
11
|
};
|
|
11
12
|
export declare const onCopyPublication: unique symbol;
|
|
13
|
+
export declare const onCopySourceAdmission: unique symbol;
|
|
12
14
|
export type CopyPublicationOptions = {
|
|
13
15
|
[onCopyPublication]?: PinnedWriteParams["verifyPublished"];
|
|
16
|
+
[onCopySourceAdmission]?: (identity: BigIntStats, realPath: string) => {
|
|
17
|
+
mode: number;
|
|
18
|
+
verify(): void;
|
|
19
|
+
};
|
|
14
20
|
};
|
|
15
21
|
//# sourceMappingURL=copy-publication.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-publication.d.ts","sourceRoot":"","sources":["../src/copy-publication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGnF,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI;IAIpD,WAAW,WAAW,sBAAsB,GAAG,IAAI;IAcnD,sBAAsB,QAAQ,OAAO,GAAG,IAAI;EAO/C;AAID,eAAO,MAAM,iBAAiB,eAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"copy-publication.d.ts","sourceRoot":"","sources":["../src/copy-publication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGnF,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI;IAIpD,WAAW,WAAW,sBAAsB,GAAG,IAAI;IAcnD,sBAAsB,QAAQ,OAAO,GAAG,IAAI;EAO/C;AAID,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAG7D,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AACrE,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,iBAAiB,CAAC,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC3D,CAAC,qBAAqB,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,KAAK;QACrE,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,IAAI,IAAI,CAAC;KAChB,CAAC;CACH,CAAC"}
|
package/dist/copy-publication.js
CHANGED
|
@@ -28,3 +28,6 @@ export function createCopyPublicationObserver(path, notify) {
|
|
|
28
28
|
// Internal composition hook; the descriptor is borrowed until the callback returns.
|
|
29
29
|
// Kept off RootCopyOptions and all public package exports.
|
|
30
30
|
export const onCopyPublication = Symbol("onCopyPublication");
|
|
31
|
+
// Reuse the copy's admitted descriptor identity before any bytes are staged.
|
|
32
|
+
// Internal callers can bind prior observations and select its publication mode.
|
|
33
|
+
export const onCopySourceAdmission = Symbol("onCopySourceAdmission");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-tree-portable.d.ts","sourceRoot":"","sources":["../src/copy-tree-portable.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAGL,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"copy-tree-portable.d.ts","sourceRoot":"","sources":["../src/copy-tree-portable.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAGL,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAU/B,0FAA0F;AAC1F,wBAAsB,aAAa,CACjC,MAAM,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAC9C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;IACP,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CACtD,GACA,OAAO,CAAC,IAAI,CAAC,CAoOf"}
|
|
@@ -15,16 +15,21 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
15
15
|
const cancellation = new AbortController();
|
|
16
16
|
const signal = cancellation.signal;
|
|
17
17
|
setMaxListeners(options.concurrency, signal);
|
|
18
|
-
const abort = () => cancellation.abort(options.signal?.reason);
|
|
19
|
-
options.signal?.addEventListener("abort", abort, { once: true });
|
|
20
18
|
const pending = new Set();
|
|
21
19
|
const finishing = new Set();
|
|
22
20
|
const buffers = [];
|
|
21
|
+
let failure = undefined;
|
|
22
|
+
function recordFailure(value) {
|
|
23
|
+
failure ??= { value };
|
|
24
|
+
cancellation.abort(failure);
|
|
25
|
+
}
|
|
26
|
+
const abort = () => recordFailure(options.signal?.reason);
|
|
27
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
23
28
|
async function schedule(operation, children) {
|
|
24
29
|
signal.throwIfAborted();
|
|
25
30
|
const task = operation()
|
|
26
31
|
.catch((error) => {
|
|
27
|
-
|
|
32
|
+
recordFailure(error);
|
|
28
33
|
})
|
|
29
34
|
.finally(() => {
|
|
30
35
|
pending.delete(task);
|
|
@@ -65,13 +70,13 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
65
70
|
try {
|
|
66
71
|
let position = 0;
|
|
67
72
|
while (true) {
|
|
68
|
-
signal
|
|
73
|
+
signal.throwIfAborted();
|
|
69
74
|
const { bytesRead } = await input.read(buffer, 0, buffer.length, position);
|
|
70
75
|
if (bytesRead === 0)
|
|
71
76
|
break;
|
|
72
77
|
let written = 0;
|
|
73
78
|
while (written < bytesRead) {
|
|
74
|
-
signal
|
|
79
|
+
signal.throwIfAborted();
|
|
75
80
|
const { bytesWritten } = await output.write(buffer, written, bytesRead - written, position + written);
|
|
76
81
|
if (bytesWritten === 0)
|
|
77
82
|
throw new Error("copy write made no progress");
|
|
@@ -90,20 +95,25 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
90
95
|
await output.utimes(timestampSeconds(stat.atimeNs), timestampSeconds(stat.mtimeNs));
|
|
91
96
|
}
|
|
92
97
|
catch (error) {
|
|
93
|
-
|
|
94
|
-
throw error;
|
|
98
|
+
recordFailure(error);
|
|
95
99
|
}
|
|
96
|
-
|
|
100
|
+
if (output) {
|
|
97
101
|
try {
|
|
98
|
-
await output
|
|
102
|
+
await output.close();
|
|
99
103
|
}
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
catch (error) {
|
|
105
|
+
recordFailure(error);
|
|
102
106
|
}
|
|
103
107
|
}
|
|
108
|
+
try {
|
|
109
|
+
await input.close();
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
recordFailure(error);
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
async function copyDirectory(from, to, parentChildren) {
|
|
106
|
-
signal
|
|
116
|
+
signal.throwIfAborted();
|
|
107
117
|
const stat = await fsp.lstat(from, { bigint: true });
|
|
108
118
|
// Exclusive admission prevents merging a pre-existing directory, including
|
|
109
119
|
// any destination left by an unsuccessful native clone.
|
|
@@ -117,7 +127,7 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
117
127
|
throw new FsSafeError("path-mismatch", "copy source directory changed while opening");
|
|
118
128
|
}
|
|
119
129
|
for (const entry of await fsp.readdir(from, { withFileTypes: true })) {
|
|
120
|
-
signal
|
|
130
|
+
signal.throwIfAborted();
|
|
121
131
|
assertStagedDirectoryCurrent(target.receipt);
|
|
122
132
|
assertStagedDirectoryCurrent(original.receipt);
|
|
123
133
|
const childSource = path.join(from, entry.name);
|
|
@@ -151,7 +161,7 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
151
161
|
throw error;
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
|
-
signal
|
|
164
|
+
signal.throwIfAborted();
|
|
155
165
|
await fsp.symlink(link, childTarget, type);
|
|
156
166
|
}
|
|
157
167
|
else {
|
|
@@ -160,7 +170,7 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
160
170
|
}
|
|
161
171
|
}
|
|
162
172
|
catch (error) {
|
|
163
|
-
|
|
173
|
+
recordFailure(error);
|
|
164
174
|
throw error;
|
|
165
175
|
}
|
|
166
176
|
finally {
|
|
@@ -179,18 +189,26 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
179
189
|
assertStagedDirectoryCurrent(target.receipt);
|
|
180
190
|
}
|
|
181
191
|
}
|
|
182
|
-
|
|
192
|
+
catch (error) {
|
|
193
|
+
recordFailure(error);
|
|
194
|
+
}
|
|
195
|
+
if (original) {
|
|
183
196
|
try {
|
|
184
|
-
|
|
185
|
-
fs.closeSync(original.fd);
|
|
197
|
+
fs.closeSync(original.fd);
|
|
186
198
|
}
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
catch (error) {
|
|
200
|
+
recordFailure(error);
|
|
189
201
|
}
|
|
190
202
|
}
|
|
203
|
+
try {
|
|
204
|
+
fs.closeSync(target.fd);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
recordFailure(error);
|
|
208
|
+
}
|
|
191
209
|
})()
|
|
192
210
|
.catch((error) => {
|
|
193
|
-
|
|
211
|
+
recordFailure(error);
|
|
194
212
|
})
|
|
195
213
|
.finally(() => {
|
|
196
214
|
finishing.delete(completion);
|
|
@@ -211,12 +229,14 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
211
229
|
signal.throwIfAborted();
|
|
212
230
|
}
|
|
213
231
|
catch (error) {
|
|
214
|
-
|
|
215
|
-
options.signal?.throwIfAborted();
|
|
216
|
-
throw error;
|
|
232
|
+
recordFailure(error);
|
|
217
233
|
}
|
|
218
234
|
finally {
|
|
219
235
|
await Promise.all(finishing);
|
|
220
236
|
options.signal?.removeEventListener("abort", abort);
|
|
221
237
|
}
|
|
238
|
+
if (failure)
|
|
239
|
+
throw failure.value;
|
|
240
|
+
options.signal?.throwIfAborted();
|
|
241
|
+
signal.throwIfAborted();
|
|
222
242
|
}
|
package/dist/copy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../src/copy.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAK5E,OAAO,EAAE,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACpF,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;
|
|
1
|
+
{"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../src/copy.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAK5E,OAAO,EAAE,qBAAqB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACpF,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAiBF,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAc/E;AAED,0FAA0F;AAC1F,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAM,GAC5C,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,mGAAmG;AACnG,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
package/dist/copy.js
CHANGED
|
@@ -7,18 +7,36 @@ import { FsSafeError } from "./errors.js";
|
|
|
7
7
|
import { getNativeBinding, requireNativeBinding } from "./native.js";
|
|
8
8
|
import { assertStagedDirectoryCurrent, openStagedDirectory } from "./staged-directory.js";
|
|
9
9
|
export { readCloneFileMetadata } from "./clone-metadata.js";
|
|
10
|
+
function firstFailure(current, value) {
|
|
11
|
+
return current ?? { value };
|
|
12
|
+
}
|
|
13
|
+
function closeDescriptor(fd, failure) {
|
|
14
|
+
try {
|
|
15
|
+
fs.closeSync(fd);
|
|
16
|
+
return failure;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return firstFailure(failure, error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
10
22
|
/** Inspect an existing real directory without creating probe files. */
|
|
11
23
|
export function probeTreeClone(parentPath) {
|
|
12
24
|
const native = getNativeBinding();
|
|
13
25
|
if (!native)
|
|
14
26
|
return undefined;
|
|
15
27
|
const parent = openStagedDirectory(assertAbsolutePathInput(parentPath));
|
|
28
|
+
let backend;
|
|
29
|
+
let failure;
|
|
16
30
|
try {
|
|
17
|
-
|
|
31
|
+
backend = native.probeTreeClone(parent.fd) ?? undefined;
|
|
18
32
|
}
|
|
19
|
-
|
|
20
|
-
|
|
33
|
+
catch (error) {
|
|
34
|
+
failure = firstFailure(failure, error);
|
|
21
35
|
}
|
|
36
|
+
failure = closeDescriptor(parent.fd, failure);
|
|
37
|
+
if (failure)
|
|
38
|
+
throw failure.value;
|
|
39
|
+
return backend;
|
|
22
40
|
}
|
|
23
41
|
/** Create an empty clone source: a Btrfs subvolume or an ordinary supported directory. */
|
|
24
42
|
export async function createCloneSource(destination, options = {}) {
|
|
@@ -56,6 +74,7 @@ async function materializeTree(source, destination, options, policy) {
|
|
|
56
74
|
throw new FsSafeError("invalid-path", "clone destination must name a child directory");
|
|
57
75
|
const parent = openStagedDirectory(path.dirname(target));
|
|
58
76
|
let original;
|
|
77
|
+
let failure;
|
|
59
78
|
try {
|
|
60
79
|
const supported = native?.probeTreeClone(parent.fd);
|
|
61
80
|
if (!supported && policy === "always") {
|
|
@@ -113,13 +132,12 @@ async function materializeTree(source, destination, options, policy) {
|
|
|
113
132
|
assertStagedDirectoryCurrent(original.receipt);
|
|
114
133
|
}
|
|
115
134
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (original)
|
|
119
|
-
fs.closeSync(original.fd);
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
fs.closeSync(parent.fd);
|
|
123
|
-
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
failure = firstFailure(failure, error);
|
|
124
137
|
}
|
|
138
|
+
if (original)
|
|
139
|
+
failure = closeDescriptor(original.fd, failure);
|
|
140
|
+
failure = closeDescriptor(parent.fd, failure);
|
|
141
|
+
if (failure)
|
|
142
|
+
throw failure.value;
|
|
125
143
|
}
|
|
@@ -43,10 +43,10 @@ export function ownDirectoryMode(params) {
|
|
|
43
43
|
// Hooks/ancestor checks can yield; recheck the original named association.
|
|
44
44
|
await params.inspect();
|
|
45
45
|
checks.check?.();
|
|
46
|
-
let
|
|
46
|
+
let dispatchDeadlineFailure;
|
|
47
47
|
if (currentMode !== mode) {
|
|
48
|
+
checks.check?.();
|
|
48
49
|
try {
|
|
49
|
-
checks.check?.();
|
|
50
50
|
await params.chmod(mode);
|
|
51
51
|
}
|
|
52
52
|
catch (error) {
|
|
@@ -58,13 +58,13 @@ export function ownDirectoryMode(params) {
|
|
|
58
58
|
checks.check?.();
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
|
-
|
|
61
|
+
dispatchDeadlineFailure = { error };
|
|
62
62
|
}
|
|
63
63
|
await params.verifyChmod?.();
|
|
64
64
|
}
|
|
65
65
|
const finalMode = await params.inspect();
|
|
66
|
-
if (
|
|
67
|
-
throw
|
|
66
|
+
if (dispatchDeadlineFailure)
|
|
67
|
+
throw dispatchDeadlineFailure.error;
|
|
68
68
|
checks.check?.();
|
|
69
69
|
if (!params.ignoreChmodError && finalMode !== mode) {
|
|
70
70
|
throw new FsSafeError("path-mismatch", "directory final mode could not be verified");
|
|
@@ -5,6 +5,8 @@ export type CopyFileHandleOptions = {
|
|
|
5
5
|
onChunk?: (chunk: Uint8Array) => void;
|
|
6
6
|
assertBeforeMutation?: () => void;
|
|
7
7
|
};
|
|
8
|
+
/** Copies caller-owned regular-file descriptors without moving either cursor. */
|
|
9
|
+
export declare function copyFileDescriptorSync(sourceFd: number, targetFd: number, options?: CopyFileHandleOptions): number;
|
|
8
10
|
/** Copies caller-owned regular files from offset zero without moving either cursor. */
|
|
9
11
|
export declare function copyFileHandle(source: FileHandle, target: FileHandle, options?: CopyFileHandleOptions): Promise<number>;
|
|
10
12
|
export declare function transferFileHandle(source: FileHandle, target: FileHandle | number, options: CopyFileHandleOptions & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAQF,iFAAiF;AACjF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,MAAM,CA6CR;AAgDD,uFAAuF;AACvF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAGD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
|