@openclaw/fs-safe 0.10.0 → 0.12.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 +114 -0
- package/LICENSE +1 -0
- package/README.md +39 -6
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +3 -2
- package/dist/advanced.d.ts +5 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +5 -1
- package/dist/archive-durability.d.ts +6 -6
- package/dist/archive-durability.d.ts.map +1 -1
- package/dist/archive-durability.js +1 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +8 -8
- package/dist/archive-gzip-tail.d.ts +1 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +16 -9
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +4 -2
- package/dist/archive-merge.d.ts +5 -1
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +15 -12
- package/dist/archive-native.js +4 -4
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +54 -42
- package/dist/archive-staging.d.ts +6 -3
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +42 -22
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +7 -6
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +16 -13
- package/dist/archive-zip-admission.d.ts +1 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +48 -12
- package/dist/archive-zip-loader.d.ts +6 -0
- package/dist/archive-zip-loader.d.ts.map +1 -0
- package/dist/archive-zip-loader.js +38 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +26 -9
- package/dist/archive-zip-preflight.d.ts +2 -3
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +2 -34
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +12 -10
- package/dist/bounded-read.d.ts +5 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +18 -11
- package/dist/copy-file-input.d.ts +0 -1
- package/dist/copy-file-input.d.ts.map +1 -1
- package/dist/copy-file-input.js +4 -26
- package/dist/copy-tree-portable.d.ts.map +1 -1
- package/dist/copy-tree-portable.js +57 -26
- package/dist/copy.d.ts +1 -1
- package/dist/copy.d.ts.map +1 -1
- package/dist/copy.js +3 -1
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +5 -3
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +5 -4
- package/dist/directory-guard.d.ts +11 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +53 -11
- package/dist/durability.d.ts +1 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +1 -1
- package/dist/file-handle-transfer.d.ts +14 -0
- package/dist/file-handle-transfer.d.ts.map +1 -0
- package/dist/file-handle-transfer.js +64 -0
- package/dist/file-hash.d.ts +3 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +99 -32
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +8 -4
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +7 -5
- package/dist/file-store-path.d.ts +3 -0
- package/dist/file-store-path.d.ts.map +1 -0
- package/dist/file-store-path.js +27 -0
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +15 -5
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +56 -44
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +2 -18
- package/dist/filename.d.ts +1 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +32 -14
- package/dist/guarded-mkdir.d.ts +2 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +52 -16
- package/dist/guest-dispatch-python.d.ts +2 -0
- package/dist/guest-dispatch-python.d.ts.map +1 -0
- package/dist/guest-dispatch-python.js +117 -0
- package/dist/guest-native-python.d.ts +4 -0
- package/dist/guest-native-python.d.ts.map +1 -0
- package/dist/guest-native-python.js +135 -0
- package/dist/guest.d.ts +9 -0
- package/dist/guest.d.ts.map +1 -0
- package/dist/guest.js +421 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/install-path.d.ts +6 -0
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +16 -2
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue-ownership.d.ts +2 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +47 -3
- package/dist/json-durable-queue-read.d.ts +6 -0
- package/dist/json-durable-queue-read.d.ts.map +1 -0
- package/dist/json-durable-queue-read.js +59 -0
- package/dist/json-durable-queue.d.ts +1 -1
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +43 -84
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +2 -1
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +2 -1
- package/dist/move-path-stage.d.ts.map +1 -1
- package/dist/move-path-stage.js +2 -1
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +4 -3
- package/dist/mutation-authority.d.ts +1 -0
- package/dist/mutation-authority.d.ts.map +1 -1
- package/dist/mutation-authority.js +4 -4
- package/dist/native-binding.d.ts +14 -2
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +3 -2
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +2 -1
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +5 -4
- package/dist/output.d.ts +2 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +2 -0
- package/dist/overwrite-file-handle.d.ts +8 -0
- package/dist/overwrite-file-handle.d.ts.map +1 -0
- package/dist/overwrite-file-handle.js +42 -0
- package/dist/path-case.d.ts +7 -0
- package/dist/path-case.d.ts.map +1 -0
- package/dist/path-case.js +136 -0
- package/dist/path-scope-lexical.d.ts +14 -0
- package/dist/path-scope-lexical.d.ts.map +1 -0
- package/dist/path-scope-lexical.js +27 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +22 -3
- package/dist/permissions-windows.d.ts +1 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +48 -6
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +3 -1
- package/dist/pinned-write.d.ts +2 -2
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +3 -1
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +6 -4
- package/dist/realpath.d.ts +4 -0
- package/dist/realpath.d.ts.map +1 -0
- package/dist/realpath.js +43 -0
- package/dist/recursive-mkdir-path.d.ts +3 -0
- package/dist/recursive-mkdir-path.d.ts.map +1 -0
- package/dist/recursive-mkdir-path.js +8 -0
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +2 -1
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +23 -31
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +7 -12
- package/dist/replace-file-mode.d.ts +3 -0
- package/dist/replace-file-mode.d.ts.map +1 -0
- package/dist/replace-file-mode.js +10 -0
- package/dist/replace-file.d.ts +1 -0
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +14 -8
- package/dist/root-boundary.d.ts +25 -0
- package/dist/root-boundary.d.ts.map +1 -0
- package/dist/root-boundary.js +177 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +40 -13
- package/dist/root-create-input.d.ts +10 -0
- package/dist/root-create-input.d.ts.map +1 -0
- package/dist/root-create-input.js +80 -0
- package/dist/root-directory-list.d.ts +3 -1
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +31 -5
- package/dist/root-entries.d.ts +11 -0
- package/dist/root-entries.d.ts.map +1 -0
- package/dist/root-entries.js +61 -0
- package/dist/root-errors.d.ts +5 -5
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +13 -12
- package/dist/root-impl.d.ts +13 -3
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +93 -70
- package/dist/root-move-preflight.d.ts +8 -0
- package/dist/root-move-preflight.d.ts.map +1 -0
- package/dist/root-move-preflight.js +16 -0
- package/dist/root-options.d.ts +12 -1
- package/dist/root-options.d.ts.map +1 -1
- package/dist/root-path-existing.d.ts +2 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +14 -5
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +3 -2
- package/dist/root-path.d.ts +2 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +54 -26
- package/dist/root-paths.d.ts +2 -6
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +24 -32
- package/dist/root-remove.d.ts +5 -0
- package/dist/root-remove.d.ts.map +1 -0
- package/dist/root-remove.js +286 -0
- package/dist/root-symlink-policy.d.ts +2 -1
- package/dist/root-symlink-policy.d.ts.map +1 -1
- package/dist/root-symlink-policy.js +2 -2
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +2 -1
- package/dist/root-write-mode.d.ts +2 -0
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +21 -7
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +12 -3
- package/dist/root.d.ts +2 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +3 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +2 -1
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +2 -1
- package/dist/secure-file-windows.d.ts +10 -0
- package/dist/secure-file-windows.d.ts.map +1 -0
- package/dist/secure-file-windows.js +186 -0
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +27 -7
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +2 -1
- package/dist/sibling-staged-file.d.ts +2 -0
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +49 -8
- package/dist/sibling-temp.d.ts +2 -0
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +8 -5
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +16 -5
- package/dist/sidecar-lock-policy.d.ts +2 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +17 -0
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -3
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +4 -3
- package/dist/temp-target.d.ts +14 -12
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +15 -7
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +7 -5
- package/dist/unicode-path.d.ts +3 -0
- package/dist/unicode-path.d.ts.map +1 -0
- package/dist/unicode-path.js +13 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +14 -12
- package/dist/write-file-handle.d.ts +1 -0
- package/dist/write-file-handle.d.ts.map +1 -1
- package/dist/write-file-handle.js +3 -2
- package/docs/advanced.md +7 -1
- package/docs/archive.md +31 -5
- package/docs/atomic.md +17 -1
- package/docs/config.md +1 -0
- package/docs/contributing.md +33 -2
- package/docs/copy.md +75 -6
- package/docs/directory-identity.md +85 -0
- package/docs/durability.md +40 -3
- package/docs/entries.md +109 -0
- package/docs/errors.md +3 -3
- package/docs/file-store.md +21 -0
- package/docs/filename.md +9 -2
- package/docs/guest.md +146 -0
- package/docs/in-place-write.md +81 -0
- package/docs/index.md +2 -0
- package/docs/install-path.md +59 -13
- package/docs/install.md +34 -0
- package/docs/native-helper.md +14 -5
- package/docs/native.md +18 -2
- package/docs/output.md +32 -6
- package/docs/path-case.md +64 -0
- package/docs/path-scope.md +1 -1
- package/docs/path.md +1 -1
- package/docs/permissions.md +37 -3
- package/docs/public-api.md +36 -2
- package/docs/root.md +33 -3
- package/docs/secure-file.md +17 -14
- package/docs/security-model.md +1 -1
- package/docs/sidecar-lock.md +12 -3
- package/docs/store.md +22 -1
- package/docs/temp.md +39 -6
- package/docs/types.md +1 -1
- package/docs/writing.md +153 -3
- package/package.json +14 -8
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
|
+
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
+
/** Internal: the caller has admitted these unchanged bytes and their physical count. */
|
|
4
|
+
export async function loadAdmittedZipArchive(buffer, entryCount) {
|
|
5
|
+
const JSZip = await importOptionalJsZip();
|
|
6
|
+
let archive;
|
|
7
|
+
try {
|
|
8
|
+
archive = await JSZip.loadAsync(buffer);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
throw new ArchiveFormatError(`invalid ZIP archive: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
12
|
+
}
|
|
13
|
+
const names = Object.keys(archive.files);
|
|
14
|
+
if (names.length !== entryCount) {
|
|
15
|
+
throw new ArchiveSecurityError("entry-path", "zip archive contains duplicate or colliding entry names");
|
|
16
|
+
}
|
|
17
|
+
for (const name of names)
|
|
18
|
+
validateArchiveEntryPath(name);
|
|
19
|
+
return archive;
|
|
20
|
+
}
|
|
21
|
+
async function importOptionalJsZip() {
|
|
22
|
+
try {
|
|
23
|
+
const module = await import("jszip");
|
|
24
|
+
const candidate = typeof module === "function" ? module : module.default;
|
|
25
|
+
if ((typeof candidate !== "object" && typeof candidate !== "function") ||
|
|
26
|
+
candidate === null ||
|
|
27
|
+
typeof candidate.loadAsync !== "function") {
|
|
28
|
+
throw new Error('Optional archive dependency "jszip" does not expose loadAsync().');
|
|
29
|
+
}
|
|
30
|
+
return candidate;
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
throw missingOptionalArchiveDependencyError("jszip", err);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function missingOptionalArchiveDependencyError(packageName, cause) {
|
|
37
|
+
return new Error(`Optional archive dependency "${packageName}" is not installed. Install it to use ZIP archive helpers from @openclaw/fs-safe/archive.`, { cause });
|
|
38
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"
|
|
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;AA+BD,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,MAAM,GAAG,SAAS,CA8CrB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { types } from "node:util";
|
|
1
2
|
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
3
|
import { stripArchivePath, validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
4
|
import { updateCrc32 } from "./archive-crc32.js";
|
|
@@ -31,9 +32,10 @@ export function zipExtraFields(bytes) {
|
|
|
31
32
|
}
|
|
32
33
|
return critical;
|
|
33
34
|
}
|
|
35
|
+
const utf8Decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
34
36
|
function utf8(bytes) {
|
|
35
37
|
try {
|
|
36
|
-
return
|
|
38
|
+
return utf8Decoder.decode(bytes);
|
|
37
39
|
}
|
|
38
40
|
catch {
|
|
39
41
|
return zipFormat("invalid UTF-8 name");
|
|
@@ -42,10 +44,10 @@ function utf8(bytes) {
|
|
|
42
44
|
function key(name) {
|
|
43
45
|
return stripArchivePath(name, 0) ?? "";
|
|
44
46
|
}
|
|
45
|
-
function originalName(name, flags) {
|
|
47
|
+
function originalName(name, rawName, flags) {
|
|
46
48
|
// ASCII path syntax is encoding-independent. Do not reinterpret legacy bytes
|
|
47
49
|
// as UTF-8 (native ZIP uses CP437); the selected decoder still validates its name.
|
|
48
|
-
validateArchiveEntryPath(
|
|
50
|
+
validateArchiveEntryPath(rawName.replace(/[\x80-\xff]/g, "_"));
|
|
49
51
|
if (!(flags & 0x800))
|
|
50
52
|
return undefined;
|
|
51
53
|
const decoded = utf8(name);
|
|
@@ -68,15 +70,30 @@ export function admitZipNames(params) {
|
|
|
68
70
|
const { central, local, flags, centralExtra, localExtra, seen } = params;
|
|
69
71
|
if (!central.length || !local.length)
|
|
70
72
|
zipFormat("empty entry name");
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
// Reuse only within this synchronous call; shared backing bytes can change
|
|
74
|
+
// concurrently even though admission does not yield while checking names.
|
|
75
|
+
const sameName = !types.isSharedArrayBuffer(central.buffer) &&
|
|
76
|
+
!types.isSharedArrayBuffer(local.buffer) && central.equals(local);
|
|
77
|
+
const centralRaw = central.toString("latin1");
|
|
78
|
+
const localRaw = sameName ? centralRaw : local.toString("latin1");
|
|
79
|
+
const centralUtf8 = originalName(central, centralRaw, flags);
|
|
80
|
+
const localUtf8 = sameName ? centralUtf8 : originalName(local, localRaw, flags);
|
|
73
81
|
const centralUnicode = unicodeName(central, centralExtra);
|
|
74
|
-
const
|
|
75
|
-
|
|
82
|
+
const centralField = centralExtra.get(0x7075);
|
|
83
|
+
const localField = localExtra.get(0x7075);
|
|
84
|
+
// Only immutable identical fields share their CRC-bound name admission.
|
|
85
|
+
const sameUnicode = (!centralField || !types.isSharedArrayBuffer(centralField.buffer)) &&
|
|
86
|
+
(!localField || !types.isSharedArrayBuffer(localField.buffer)) &&
|
|
87
|
+
(centralField === localField ||
|
|
88
|
+
(centralField !== undefined && localField !== undefined && centralField.equals(localField)));
|
|
89
|
+
const localUnicode = sameName && sameUnicode ? centralUnicode : unicodeName(local, localExtra);
|
|
90
|
+
const centralKey = key(centralRaw);
|
|
91
|
+
if (!sameName && centralKey !== key(localRaw)) {
|
|
76
92
|
zipFormat("central and local names disagree");
|
|
77
93
|
}
|
|
78
94
|
const interpretations = [centralUtf8, localUtf8, centralUnicode, localUnicode].filter((value) => value !== undefined);
|
|
79
|
-
|
|
95
|
+
const interpretationKey = interpretations.length ? key(interpretations[0]) : undefined;
|
|
96
|
+
if (interpretations.some((value) => value !== interpretations[0] && key(value) !== interpretationKey)) {
|
|
80
97
|
zipFormat("conflicting Unicode name interpretations");
|
|
81
98
|
}
|
|
82
99
|
// JSZip checks the central Unicode field against the local name. A slash-only
|
|
@@ -88,7 +105,7 @@ export function admitZipNames(params) {
|
|
|
88
105
|
if (!centralUnicode && localUnicode && key(localUnicode) !== key(local.toString("utf8"))) {
|
|
89
106
|
zipFormat("local-only Unicode override changes the name");
|
|
90
107
|
}
|
|
91
|
-
const identities = new Set([
|
|
108
|
+
const identities = new Set([centralKey]);
|
|
92
109
|
if (centralUnicode !== undefined)
|
|
93
110
|
identities.add(key(Buffer.from(centralUnicode).toString("latin1")));
|
|
94
111
|
if ([...identities].some((identity) => seen.has(identity))) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type ArchiveExtractLimits } from "./archive-limits.js";
|
|
2
|
+
import { type ZipArchiveWithFiles } from "./archive-zip-loader.js";
|
|
2
3
|
export { readZipCentralDirectoryEntryCount } from "./archive-zip-count.js";
|
|
3
|
-
export type ZipArchiveWithFiles
|
|
4
|
-
files: Record<string, unknown>;
|
|
5
|
-
};
|
|
4
|
+
export type { ZipArchiveWithFiles } from "./archive-zip-loader.js";
|
|
6
5
|
export declare function loadZipArchiveWithPreflight(buffer: Buffer | Uint8Array, limits?: ArchiveExtractLimits): Promise<ZipArchiveWithFiles>;
|
|
7
6
|
//# sourceMappingURL=archive-zip-preflight.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-preflight.d.ts","sourceRoot":"","sources":["../src/archive-zip-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-zip-preflight.d.ts","sourceRoot":"","sources":["../src/archive-zip-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,iCAAiC,EAAE,MAAM,wBAAwB,CAAC;AAC3E,YAAY,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAO9B"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, resolveExtractLimits, } from "./archive-limits.js";
|
|
2
|
-
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
3
2
|
import { admitZipBuffer } from "./archive-zip-admission.js";
|
|
4
|
-
import {
|
|
3
|
+
import { loadAdmittedZipArchive } from "./archive-zip-loader.js";
|
|
5
4
|
export { readZipCentralDirectoryEntryCount } from "./archive-zip-count.js";
|
|
6
5
|
export async function loadZipArchiveWithPreflight(buffer, limits) {
|
|
7
6
|
const resolvedLimits = resolveExtractLimits(limits);
|
|
@@ -9,36 +8,5 @@ export async function loadZipArchiveWithPreflight(buffer, limits) {
|
|
|
9
8
|
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ARCHIVE_SIZE_EXCEEDS_LIMIT);
|
|
10
9
|
}
|
|
11
10
|
const entryCount = admitZipBuffer(buffer, resolvedLimits);
|
|
12
|
-
|
|
13
|
-
let archive;
|
|
14
|
-
try {
|
|
15
|
-
archive = await JSZip.loadAsync(buffer);
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
throw new ArchiveFormatError(`invalid ZIP archive: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
|
|
19
|
-
}
|
|
20
|
-
if (Object.keys(archive.files).length !== entryCount) {
|
|
21
|
-
throw new ArchiveSecurityError("entry-path", "zip archive contains duplicate or colliding entry names");
|
|
22
|
-
}
|
|
23
|
-
for (const name of Object.keys(archive.files))
|
|
24
|
-
validateArchiveEntryPath(name);
|
|
25
|
-
return archive;
|
|
26
|
-
}
|
|
27
|
-
async function importOptionalJsZip() {
|
|
28
|
-
try {
|
|
29
|
-
const module = await import("jszip");
|
|
30
|
-
const candidate = typeof module === "function" ? module : module.default;
|
|
31
|
-
if ((typeof candidate !== "object" && typeof candidate !== "function") ||
|
|
32
|
-
candidate === null ||
|
|
33
|
-
typeof candidate.loadAsync !== "function") {
|
|
34
|
-
throw new Error('Optional archive dependency "jszip" does not expose loadAsync().');
|
|
35
|
-
}
|
|
36
|
-
return candidate;
|
|
37
|
-
}
|
|
38
|
-
catch (err) {
|
|
39
|
-
throw missingOptionalArchiveDependencyError("jszip", err);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function missingOptionalArchiveDependencyError(packageName, cause) {
|
|
43
|
-
return new Error(`Optional archive dependency "${packageName}" is not installed. Install it to use ZIP archive helpers from @openclaw/fs-safe/archive.`, { cause });
|
|
11
|
+
return await loadAdmittedZipArchive(buffer, entryCount);
|
|
44
12
|
}
|
package/dist/archive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CjF"}
|
package/dist/archive.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createTarEntryPlanner } from "./archive-tar.js";
|
|
2
2
|
import { inspectTar, replayTar } from "./archive-tar-stream.js";
|
|
3
3
|
import { runPinnedWriteHelper } from "./pinned-write.js";
|
|
4
|
-
import
|
|
4
|
+
import { constants as fsConstants } from "node:fs";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
@@ -10,7 +10,7 @@ import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchiveP
|
|
|
10
10
|
import { createPipelineTimeoutError, waitForDeadline, withExtractionDeadline, } from "./archive-deadline.js";
|
|
11
11
|
import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, createExtractBudgetTransform, resolveExtractLimits, resolveTarMeterLimits, } from "./archive-limits.js";
|
|
12
12
|
import { assertPortableArchiveKind, resolveArchiveKind } from "./archive-kind.js";
|
|
13
|
-
import {
|
|
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
16
|
import { isZipSymlinkEntry, zipEntryDeclaredSize, zipEntryMode, } from "./archive-zip-entry.js";
|
|
@@ -22,6 +22,7 @@ import { stageArchiveFileForExtraction } from "./archive-input.js";
|
|
|
22
22
|
import { getNativeBinding } from "./native.js";
|
|
23
23
|
import { resolveArchiveFilteredEntryPolicy, shouldExtractArchiveEntry, } from "./archive-policy.js";
|
|
24
24
|
import { writeSiblingTempFile } from "./sibling-temp.js";
|
|
25
|
+
import { realpathSync } from "./realpath.js";
|
|
25
26
|
export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
|
|
26
27
|
export { resolveArchiveKind, resolvePackedRootDir } from "./archive-kind.js";
|
|
27
28
|
export { readArchiveEntry } from "./archive-read.js";
|
|
@@ -122,7 +123,8 @@ async function extractZip(params) {
|
|
|
122
123
|
deadline: params.deadline,
|
|
123
124
|
});
|
|
124
125
|
try {
|
|
125
|
-
const
|
|
126
|
+
const destinationGuard = await prepareArchiveDestinationGuard(params.destDir);
|
|
127
|
+
const destinationRealDir = destinationGuard.realPath;
|
|
126
128
|
params.deadline.check();
|
|
127
129
|
const buffer = await fs.readFile(stagedArchive.path, { signal: params.deadline.signal });
|
|
128
130
|
params.deadline.check();
|
|
@@ -136,7 +138,7 @@ async function extractZip(params) {
|
|
|
136
138
|
await withStagedArchiveDestination({
|
|
137
139
|
destinationRealDir,
|
|
138
140
|
run: async (stagingDir) => {
|
|
139
|
-
const stagingRealDir =
|
|
141
|
+
const stagingRealDir = realpathSync.native(stagingDir);
|
|
140
142
|
const acceptedEntries = [];
|
|
141
143
|
for (const entry of entries) {
|
|
142
144
|
params.deadline.check();
|
|
@@ -189,8 +191,7 @@ async function extractZip(params) {
|
|
|
189
191
|
entries: acceptedEntries,
|
|
190
192
|
durable: params.durable,
|
|
191
193
|
sourceDir: stagingRealDir,
|
|
192
|
-
|
|
193
|
-
destinationRealDir,
|
|
194
|
+
destinationGuard,
|
|
194
195
|
deadline: params.deadline,
|
|
195
196
|
});
|
|
196
197
|
params.deadline.check();
|
|
@@ -248,9 +249,10 @@ async function extractWasmTar(params) {
|
|
|
248
249
|
await inspectTar({ archivePath: params.archivePath, limits: tarLimits, signal: deadline.signal,
|
|
249
250
|
onMember: (entry) => { manifest.push(entry); } });
|
|
250
251
|
deadline.check();
|
|
251
|
-
const
|
|
252
|
+
const destinationGuard = await prepareArchiveDestinationGuard(options.destDir);
|
|
253
|
+
const destinationRealDir = destinationGuard.realPath;
|
|
252
254
|
await withStagedArchiveDestination({ destinationRealDir, run: async (stagingPath) => {
|
|
253
|
-
const stagingDir =
|
|
255
|
+
const stagingDir = realpathSync.native(stagingPath);
|
|
254
256
|
const planEntry = createTarEntryPlanner({ ...options, rootDir: destinationRealDir, limits: params.limits });
|
|
255
257
|
const accepted = manifest.flatMap((entry) => {
|
|
256
258
|
deadline.check();
|
|
@@ -273,8 +275,8 @@ async function extractWasmTar(params) {
|
|
|
273
275
|
},
|
|
274
276
|
});
|
|
275
277
|
deadline.check();
|
|
276
|
-
await mergePlannedArchiveIntoDestination({ entries: accepted, sourceDir: stagingDir,
|
|
277
|
-
|
|
278
|
+
await mergePlannedArchiveIntoDestination({ entries: accepted, sourceDir: stagingDir, destinationGuard,
|
|
279
|
+
deadline, durable: options.durable });
|
|
278
280
|
deadline.check();
|
|
279
281
|
} });
|
|
280
282
|
}
|
package/dist/bounded-read.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export declare function readBoundedAsync(maxBytes: number, readChunk: (scratch:
|
|
|
15
15
|
export declare function readFileHandleBounded(handle: ReadableFileHandle, maxBytes: number): Promise<Buffer>;
|
|
16
16
|
/** Async bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
17
17
|
export declare function readFileDescriptorBounded(fd: number, maxBytes: number): Promise<Buffer>;
|
|
18
|
+
export declare function readBoundedSync(maxBytes: number, readChunk: (scratch: Buffer, length: number) => number, options?: {
|
|
19
|
+
observeRegularFileSize?: () => number | undefined;
|
|
20
|
+
initialSize?: number;
|
|
21
|
+
createLimitError?: () => Error;
|
|
22
|
+
}): Buffer;
|
|
18
23
|
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
19
24
|
export declare function readFileDescriptorBoundedSync(fd: number, maxBytes: number): Buffer;
|
|
20
25
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounded-read.d.ts","sourceRoot":"","sources":["../src/bounded-read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,KAAK,kBAAkB,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AA8CnD,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAC/D,OAAO,GAAE;IACP,sBAAsB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,KAAK,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACf,GACL,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAMjB;AAyBD,oFAAoF;AACpF,wBAAsB,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK7F;AAED,mFAAmF;AACnF,wBAAgB,6BAA6B,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"bounded-read.d.ts","sourceRoot":"","sources":["../src/bounded-read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,KAAK,kBAAkB,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AA8CnD,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAC/D,OAAO,GAAE;IACP,sBAAsB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,KAAK,CAAC;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACf,GACL,OAAO,CAAC,MAAM,CAAC,CAuBjB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAMjB;AAyBD,oFAAoF;AACpF,wBAAsB,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK7F;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,EACtD,OAAO,GAAE;IACP,sBAAsB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,KAAK,CAAC;CAC3B,GACL,MAAM,CAoBR;AAED,mFAAmF;AACnF,wBAAgB,6BAA6B,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMlF"}
|
package/dist/bounded-read.js
CHANGED
|
@@ -54,7 +54,7 @@ export async function readBoundedAsync(maxBytes, readChunk, options = {}) {
|
|
|
54
54
|
// initial read; virtual files can report zero or stale sizes thereafter.
|
|
55
55
|
const currentSize = bytesRead < buffer.length ? observeRegularFileSize?.() : undefined;
|
|
56
56
|
total = addReadBytes(total, bytesRead, maxBytes, createLimitError);
|
|
57
|
-
if (currentSize !== undefined && bytesRead >= currentSize)
|
|
57
|
+
if (currentSize !== undefined && currentSize > 0 && bytesRead >= currentSize)
|
|
58
58
|
return finishReadBuffer(buffer, total, allocate);
|
|
59
59
|
}
|
|
60
60
|
while (true) {
|
|
@@ -108,29 +108,36 @@ export async function readFileDescriptorBounded(fd, maxBytes) {
|
|
|
108
108
|
return await readDescriptorChunk(fd, scratch, length);
|
|
109
109
|
}, { observeRegularFileSize: () => regularFileSize(fd) });
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
export function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
111
|
+
export function readBoundedSync(maxBytes, readChunk, options = {}) {
|
|
113
112
|
normalizeMaxBytes(maxBytes);
|
|
114
113
|
let total = 0;
|
|
115
|
-
|
|
116
|
-
const size =
|
|
114
|
+
const { observeRegularFileSize, createLimitError } = options;
|
|
115
|
+
const size = options.initialSize ?? observeRegularFileSize?.();
|
|
117
116
|
let buffer = size === undefined ? createScratchBuffer(maxBytes) : createInitialBuffer(maxBytes, size);
|
|
118
117
|
if (size !== undefined) {
|
|
119
|
-
const bytesRead =
|
|
118
|
+
const bytesRead = readChunk(buffer, buffer.length);
|
|
120
119
|
if (bytesRead === 0)
|
|
121
120
|
return finishReadBuffer(buffer, 0);
|
|
122
|
-
const currentSize = bytesRead < buffer.length ?
|
|
123
|
-
total = addReadBytes(total, bytesRead, maxBytes);
|
|
124
|
-
if (currentSize !== undefined && bytesRead >= currentSize)
|
|
121
|
+
const currentSize = bytesRead < buffer.length ? observeRegularFileSize?.() : undefined;
|
|
122
|
+
total = addReadBytes(total, bytesRead, maxBytes, createLimitError);
|
|
123
|
+
if (currentSize !== undefined && currentSize > 0 && bytesRead >= currentSize)
|
|
125
124
|
return finishReadBuffer(buffer, total);
|
|
126
125
|
}
|
|
127
126
|
while (true) {
|
|
128
127
|
if (total === buffer.length)
|
|
129
128
|
buffer = growReadBuffer(buffer, maxBytes);
|
|
130
129
|
const remaining = buffer.subarray(total);
|
|
131
|
-
const bytesRead =
|
|
130
|
+
const bytesRead = readChunk(remaining, remaining.length);
|
|
132
131
|
if (bytesRead === 0)
|
|
133
132
|
return finishReadBuffer(buffer, total);
|
|
134
|
-
total = addReadBytes(total, bytesRead, maxBytes);
|
|
133
|
+
total = addReadBytes(total, bytesRead, maxBytes, createLimitError);
|
|
135
134
|
}
|
|
136
135
|
}
|
|
136
|
+
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
137
|
+
export function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
138
|
+
return readBoundedSync(maxBytes, (buffer, length) => fs.readSync(fd, buffer, 0, length, null), {
|
|
139
|
+
// Small budgets already bound the first allocation without a size lookup.
|
|
140
|
+
initialSize: maxBytes <= READ_CHUNK_BYTES ? maxBytes : undefined,
|
|
141
|
+
observeRegularFileSize: () => regularFileSize(fd),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -16,7 +16,6 @@ export declare function assertCopySourceCurrent(source: {
|
|
|
16
16
|
handle: FileHandle;
|
|
17
17
|
realPath: string;
|
|
18
18
|
}, identity: BigIntStats): Promise<void>;
|
|
19
|
-
export declare function copyFileChunks(input: CopyFileInput, maxBytes?: number): AsyncGenerator<Buffer>;
|
|
20
19
|
export declare function writeCopyFileToFd(fd: number, input: CopyFileInput, maxBytes?: number, assertBeforeMutation?: () => void): Promise<void>;
|
|
21
20
|
export declare function createNativeCopyFile(native: NativeBinding, input: CopyFileInput, parentFd: number, basename: string, maxBytes: number | undefined, sync: boolean): Promise<NativeFileCopyResult | undefined>;
|
|
22
21
|
export declare function assertNativeCopyCompleted(input: CopyFileInput, copied?: NativeFileCopyResult): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copy-file-input.d.ts","sourceRoot":"","sources":["../src/copy-file-input.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIhE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,aAAa,CAM5E;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAChD,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,
|
|
1
|
+
{"version":3,"file":"copy-file-input.d.ts","sourceRoot":"","sources":["../src/copy-file-input.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIhE,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,aAAa,CAM5E;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAChD,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,aAAa,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,oBAAoB,CAAC,EAAE,MAAM,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CA8B3C;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAWnG"}
|
package/dist/copy-file-input.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolveCopyCloneMode } from "./copy-policy.js";
|
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { getNativeBinding } from "./native.js";
|
|
5
5
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
6
|
-
import {
|
|
6
|
+
import { transferFileHandle } from "./file-handle-transfer.js";
|
|
7
7
|
export function resolveFileCopyCloneMode(mode) {
|
|
8
8
|
const clone = resolveCopyCloneMode(mode, "never");
|
|
9
9
|
if (clone === "always" && !getNativeBinding()?.copyFileExclusive) {
|
|
@@ -21,32 +21,10 @@ export async function assertCopySourceCurrent(source, identity) {
|
|
|
21
21
|
return current;
|
|
22
22
|
}, identity);
|
|
23
23
|
}
|
|
24
|
-
export async function* copyFileChunks(input, maxBytes = Infinity) {
|
|
25
|
-
const buffer = Buffer.allocUnsafe(Math.min(256 * 1024, Math.max(64 * 1024, input.size)));
|
|
26
|
-
let position = 0;
|
|
27
|
-
while (true) {
|
|
28
|
-
input.signal?.throwIfAborted();
|
|
29
|
-
const { bytesRead } = await input.handle.read(buffer, 0, Math.min(buffer.length, maxBytes - position + 1), position);
|
|
30
|
-
input.signal?.throwIfAborted();
|
|
31
|
-
if (bytesRead > maxBytes - position) {
|
|
32
|
-
throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
|
|
33
|
-
}
|
|
34
|
-
if (bytesRead === 0)
|
|
35
|
-
return;
|
|
36
|
-
position += bytesRead;
|
|
37
|
-
// The consumer finishes writing before the next iteration reuses this buffer.
|
|
38
|
-
yield buffer.subarray(0, bytesRead);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
24
|
export async function writeCopyFileToFd(fd, input, maxBytes, assertBeforeMutation) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
input.signal?.throwIfAborted();
|
|
46
|
-
assertBeforeMutation?.();
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
}
|
|
25
|
+
await transferFileHandle(input.handle, fd, {
|
|
26
|
+
maxBytes, sizeHint: input.size, signal: input.signal, assertBeforeMutation,
|
|
27
|
+
});
|
|
50
28
|
}
|
|
51
29
|
export async function createNativeCopyFile(native, input, parentFd, basename, maxBytes, sync) {
|
|
52
30
|
input.signal?.throwIfAborted();
|
|
@@ -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;AAQ/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,CAiNf"}
|
|
@@ -4,6 +4,11 @@ import fsp, {} from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { assertStagedDirectoryCurrent, exactIdentityMatches, openStagedDirectory, } from "./staged-directory.js";
|
|
7
|
+
function timestampSeconds(nanoseconds) {
|
|
8
|
+
// Dates discard sub-millisecond precision; negative numbers mean "now" in
|
|
9
|
+
// Node's utimes API. Numeric strings retain fractional, pre-epoch timestamps.
|
|
10
|
+
return String(Number(nanoseconds / 1000000000n) + Number(nanoseconds % 1000000000n) / 1e9);
|
|
11
|
+
}
|
|
7
12
|
/** Byte-copy adapter for the shared immutable-source, caller-owned namespace contract. */
|
|
8
13
|
export async function copyOwnedTree(source, destination, options) {
|
|
9
14
|
options.signal?.throwIfAborted();
|
|
@@ -13,11 +18,9 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
13
18
|
const abort = () => cancellation.abort(options.signal?.reason);
|
|
14
19
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
15
20
|
const pending = new Set();
|
|
21
|
+
const finishing = new Set();
|
|
16
22
|
const buffers = [];
|
|
17
|
-
|
|
18
|
-
await Promise.all(pending);
|
|
19
|
-
};
|
|
20
|
-
async function schedule(operation) {
|
|
23
|
+
async function schedule(operation, children) {
|
|
21
24
|
signal.throwIfAborted();
|
|
22
25
|
const task = operation()
|
|
23
26
|
.catch((error) => {
|
|
@@ -25,8 +28,10 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
25
28
|
})
|
|
26
29
|
.finally(() => {
|
|
27
30
|
pending.delete(task);
|
|
31
|
+
children.delete(task);
|
|
28
32
|
});
|
|
29
33
|
pending.add(task);
|
|
34
|
+
children.add(task);
|
|
30
35
|
if (pending.size >= options.concurrency)
|
|
31
36
|
await Promise.race(pending);
|
|
32
37
|
signal.throwIfAborted();
|
|
@@ -82,7 +87,7 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
82
87
|
signal.throwIfAborted();
|
|
83
88
|
if (process.platform !== "win32")
|
|
84
89
|
await output.chmod(Number(stat.mode & 4095n));
|
|
85
|
-
await output.utimes(stat.
|
|
90
|
+
await output.utimes(timestampSeconds(stat.atimeNs), timestampSeconds(stat.mtimeNs));
|
|
86
91
|
}
|
|
87
92
|
catch (error) {
|
|
88
93
|
cancellation.abort(error);
|
|
@@ -97,13 +102,14 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
|
-
async function copyDirectory(from, to) {
|
|
105
|
+
async function copyDirectory(from, to, parentChildren) {
|
|
101
106
|
signal?.throwIfAborted();
|
|
102
107
|
const stat = await fsp.lstat(from, { bigint: true });
|
|
103
108
|
// Exclusive admission prevents merging a pre-existing directory, including
|
|
104
109
|
// any destination left by an unsuccessful native clone.
|
|
105
110
|
await fsp.mkdir(to, { mode: 0o700 });
|
|
106
111
|
const target = openStagedDirectory(to);
|
|
112
|
+
const children = new Set();
|
|
107
113
|
let original;
|
|
108
114
|
try {
|
|
109
115
|
original = openStagedDirectory(from);
|
|
@@ -118,10 +124,10 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
118
124
|
const childTarget = path.join(to, entry.name);
|
|
119
125
|
const child = await fsp.lstat(childSource, { bigint: true });
|
|
120
126
|
if (child.isDirectory()) {
|
|
121
|
-
await copyDirectory(childSource, childTarget);
|
|
127
|
+
await copyDirectory(childSource, childTarget, children);
|
|
122
128
|
}
|
|
123
129
|
else if (child.isFile()) {
|
|
124
|
-
await schedule(() => copyFile(childSource, childTarget, child));
|
|
130
|
+
await schedule(() => copyFile(childSource, childTarget, child), children);
|
|
125
131
|
}
|
|
126
132
|
else if (child.isSymbolicLink()) {
|
|
127
133
|
const link = process.platform === "win32"
|
|
@@ -152,40 +158,65 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
152
158
|
throw new FsSafeError("not-file", "tree copying does not support special files");
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
|
-
await settle();
|
|
156
|
-
signal.throwIfAborted();
|
|
157
|
-
assertStagedDirectoryCurrent(original.receipt);
|
|
158
|
-
assertStagedDirectoryCurrent(target.receipt);
|
|
159
|
-
if (process.platform !== "win32")
|
|
160
|
-
fs.fchmodSync(target.fd, Number(stat.mode & 4095n));
|
|
161
|
-
await fsp.utimes(to, stat.atime, stat.mtime);
|
|
162
|
-
assertStagedDirectoryCurrent(target.receipt);
|
|
163
161
|
}
|
|
164
162
|
catch (error) {
|
|
165
163
|
cancellation.abort(error);
|
|
166
164
|
throw error;
|
|
167
165
|
}
|
|
168
166
|
finally {
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
167
|
+
// Traversal can advance into siblings while this directory's own files
|
|
168
|
+
// settle. Children retain their parents and finish metadata bottom-up.
|
|
169
|
+
const completion = (async () => {
|
|
170
|
+
try {
|
|
171
|
+
await Promise.all(children);
|
|
172
|
+
signal.throwIfAborted();
|
|
173
|
+
if (original) {
|
|
174
|
+
assertStagedDirectoryCurrent(original.receipt);
|
|
175
|
+
assertStagedDirectoryCurrent(target.receipt);
|
|
176
|
+
if (process.platform !== "win32")
|
|
177
|
+
fs.fchmodSync(target.fd, Number(stat.mode & 4095n));
|
|
178
|
+
await fsp.utimes(to, timestampSeconds(stat.atimeNs), timestampSeconds(stat.mtimeNs));
|
|
179
|
+
assertStagedDirectoryCurrent(target.receipt);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
try {
|
|
184
|
+
if (original)
|
|
185
|
+
fs.closeSync(original.fd);
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
fs.closeSync(target.fd);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
})()
|
|
192
|
+
.catch((error) => {
|
|
193
|
+
cancellation.abort(error);
|
|
194
|
+
})
|
|
195
|
+
.finally(() => {
|
|
196
|
+
finishing.delete(completion);
|
|
197
|
+
parentChildren?.delete(completion);
|
|
198
|
+
});
|
|
199
|
+
finishing.add(completion);
|
|
200
|
+
parentChildren?.add(completion);
|
|
201
|
+
// Bound traversed-but-unsettled directories as well as file workers;
|
|
202
|
+
// ancestors stay open along the current traversal path.
|
|
203
|
+
if (finishing.size >= options.concurrency)
|
|
204
|
+
await Promise.race(finishing);
|
|
179
205
|
}
|
|
206
|
+
signal.throwIfAborted();
|
|
180
207
|
}
|
|
181
208
|
try {
|
|
182
209
|
await copyDirectory(source.receipt.realPath, destination);
|
|
210
|
+
await Promise.all(finishing);
|
|
211
|
+
signal.throwIfAborted();
|
|
183
212
|
}
|
|
184
213
|
catch (error) {
|
|
214
|
+
await Promise.all(finishing);
|
|
185
215
|
options.signal?.throwIfAborted();
|
|
186
216
|
throw error;
|
|
187
217
|
}
|
|
188
218
|
finally {
|
|
219
|
+
await Promise.all(finishing);
|
|
189
220
|
options.signal?.removeEventListener("abort", abort);
|
|
190
221
|
}
|
|
191
222
|
}
|
package/dist/copy.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type CopyCloneMode } from "./copy-policy.js";
|
|
2
2
|
export { readCloneFileMetadata, type CloneFileMetadata } from "./clone-metadata.js";
|
|
3
3
|
export type { CopyCloneMode } from "./copy-policy.js";
|
|
4
|
-
export type TreeCloneBackend = "apfs" | "btrfs" | "refs" | "xfs";
|
|
4
|
+
export type TreeCloneBackend = "apfs" | "btrfs" | "refs" | "xfs" | "zfs";
|
|
5
5
|
export type CopyTreeOptions = {
|
|
6
6
|
clone?: CopyCloneMode;
|
|
7
7
|
signal?: AbortSignal;
|
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,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;AAEF,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAS/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
|
@@ -104,7 +104,9 @@ async function materializeTree(source, destination, options, policy) {
|
|
|
104
104
|
await copyOwnedTree(original, target, {
|
|
105
105
|
signal: options.signal,
|
|
106
106
|
concurrency: options.concurrency ?? (process.platform === "win32" ? 4 : 1),
|
|
107
|
-
copyFileContents: process.platform === "win32"
|
|
107
|
+
copyFileContents: process.platform === "win32" || process.platform === "linux"
|
|
108
|
+
? native?.copyFileContents
|
|
109
|
+
: undefined,
|
|
108
110
|
});
|
|
109
111
|
options.signal?.throwIfAborted();
|
|
110
112
|
assertStagedDirectoryCurrent(parent.receipt);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-path.d.ts","sourceRoot":"","sources":["../src/device-path.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,0BAA0B,GAClC,cAAc,GACd,UAAU,GACV,gBAAgB,CAAC;AAErB,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,CAAC;AAcF,eAAO,MAAM,6BAA6B,EAAE,WAAW,CAAC,MAAM,CAgC5D,CAAC;
|
|
1
|
+
{"version":3,"file":"device-path.d.ts","sourceRoot":"","sources":["../src/device-path.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,0BAA0B,GAClC,cAAc,GACd,UAAU,GACV,gBAAgB,CAAC;AAErB,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,CAAC;AAcF,eAAO,MAAM,6BAA6B,EAAE,WAAW,CAAC,MAAM,CAgC5D,CAAC;AA2EH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjE;AAcD,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,2BAAgC,GACxC,yBAAyB,GAAG,SAAS,CAWvC;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAET;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,2BAA2B,GACpC,IAAI,CAON"}
|
package/dist/device-path.js
CHANGED
|
@@ -96,9 +96,11 @@ function matchPosixDeviceReadPath(filePath, cwd) {
|
|
|
96
96
|
}
|
|
97
97
|
function normalizeWindowsDeviceBaseName(filePath) {
|
|
98
98
|
const normalized = trimTrailingWindowsSeparators(filePath.replace(/\//g, "\\"));
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
const
|
|
99
|
+
const start = normalized.lastIndexOf("\\") + 1;
|
|
100
|
+
const stream = normalized.indexOf(":", start);
|
|
101
|
+
const extension = normalized.indexOf(".", start);
|
|
102
|
+
const end = Math.min(stream < 0 ? normalized.length : stream, extension < 0 ? normalized.length : extension);
|
|
103
|
+
const stem = normalized.slice(start, end);
|
|
102
104
|
return trimTrailingWindowsIgnoredChars(stem).toUpperCase();
|
|
103
105
|
}
|
|
104
106
|
export function isWindowsReservedDeviceName(name) {
|