@openclaw/fs-safe 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/README.md +12 -7
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +32 -11
- package/dist/advanced.d.ts +2 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +2 -0
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +6 -2
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +15 -2
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -19
- package/dist/archive-plan.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +18 -13
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +43 -10
- package/dist/archive-tar-inspect.d.ts.map +1 -1
- package/dist/archive-tar-inspect.js +4 -1
- 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 +2 -2
- package/dist/archive-zip-directory.d.ts +3 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +13 -2
- package/dist/archive-zip-loader.d.ts +3 -2
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +30 -4
- package/dist/archive-zip-manifest.d.ts +5 -0
- package/dist/archive-zip-manifest.d.ts.map +1 -0
- package/dist/archive-zip-manifest.js +22 -0
- package/dist/archive-zip-names.d.ts +6 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +35 -14
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +3 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/copy-file-input.d.ts +1 -1
- package/dist/copy-file-input.d.ts.map +1 -1
- package/dist/copy-file-input.js +2 -0
- package/dist/darwin-acl.d.ts +4 -0
- package/dist/darwin-acl.d.ts.map +1 -0
- package/dist/darwin-acl.js +24 -0
- package/dist/deny-mutations.d.ts.map +1 -1
- package/dist/deny-mutations.js +8 -2
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +67 -23
- package/dist/directory-entry-path.d.ts +3 -0
- package/dist/directory-entry-path.d.ts.map +1 -0
- package/dist/directory-entry-path.js +21 -0
- package/dist/directory-guard.d.ts +17 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +134 -48
- package/dist/directory-mode-node.d.ts +12 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +102 -4
- package/dist/effective-uid.d.ts +2 -0
- package/dist/effective-uid.d.ts.map +1 -0
- package/dist/effective-uid.js +25 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +98 -27
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +3 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +36 -6
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +29 -9
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +6 -2
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +20 -65
- package/dist/file-store-copy-source.d.ts +5 -0
- package/dist/file-store-copy-source.d.ts.map +1 -0
- package/dist/file-store-copy-source.js +31 -0
- package/dist/file-store-path.d.ts.map +1 -1
- package/dist/file-store-path.js +4 -1
- package/dist/file-store-sync-directory.d.ts +16 -0
- package/dist/file-store-sync-directory.d.ts.map +1 -0
- package/dist/file-store-sync-directory.js +349 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +11 -28
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +65 -20
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -2
- package/dist/guarded-mkdir.d.ts +8 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +176 -27
- package/dist/guest.d.ts.map +1 -1
- package/dist/guest.js +4 -1
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +73 -10
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +54 -19
- package/dist/json-durable-queue-ownership.d.ts +6 -0
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +89 -42
- package/dist/json-durable-queue-paths.d.ts +11 -0
- package/dist/json-durable-queue-paths.d.ts.map +1 -0
- package/dist/json-durable-queue-paths.js +42 -0
- package/dist/json-durable-queue-read.d.ts.map +1 -1
- package/dist/json-durable-queue-read.js +2 -0
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +50 -58
- package/dist/json-store.d.ts.map +1 -1
- package/dist/json-store.js +5 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +54 -22
- package/dist/local-file-access.d.ts.map +1 -1
- package/dist/local-file-access.js +4 -0
- package/dist/local-file-descriptor.d.ts +17 -0
- package/dist/local-file-descriptor.d.ts.map +1 -0
- package/dist/local-file-descriptor.js +84 -0
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +35 -7
- package/dist/move-path-cleanup.d.ts +2 -0
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +44 -18
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +31 -6
- package/dist/native-binding.d.ts +17 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-binding.js +8 -1
- package/dist/native-directory-observation.d.ts +17 -0
- package/dist/native-directory-observation.d.ts.map +1 -0
- package/dist/native-directory-observation.js +37 -0
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +6 -4
- package/dist/native-parent-admission.d.ts +32 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +127 -0
- package/dist/native-pinned-write-windows.d.ts +0 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +8 -9
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +314 -42
- package/dist/native-staged-file.d.ts +4 -4
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +20 -10
- package/dist/native.d.ts +1 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +7 -2
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +15 -5
- package/dist/overwrite-file-handle.d.ts.map +1 -1
- package/dist/overwrite-file-handle.js +5 -1
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +2 -0
- package/dist/path-policy.d.ts.map +1 -1
- package/dist/path-policy.js +7 -2
- package/dist/path-prefix.d.ts +7 -0
- package/dist/path-prefix.d.ts.map +1 -0
- package/dist/path-prefix.js +82 -0
- package/dist/path-scope-lexical.d.ts.map +1 -1
- package/dist/path-scope-lexical.js +18 -8
- package/dist/path-segment-route.d.ts +7 -0
- package/dist/path-segment-route.d.ts.map +1 -0
- package/dist/path-segment-route.js +24 -0
- package/dist/path-suffix-aliases.d.ts +10 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -0
- package/dist/path-suffix-aliases.js +386 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +16 -5
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +14 -3
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +37 -8
- package/dist/pinned-mutation-admission.d.ts +25 -0
- package/dist/pinned-mutation-admission.d.ts.map +1 -0
- package/dist/pinned-mutation-admission.js +425 -0
- package/dist/pinned-mutation-observation.d.ts +34 -0
- package/dist/pinned-mutation-observation.d.ts.map +1 -0
- package/dist/pinned-mutation-observation.js +142 -0
- package/dist/pinned-mutation-shared-route.d.ts +24 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -0
- package/dist/pinned-mutation-shared-route.js +70 -0
- package/dist/pinned-open.d.ts +6 -0
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +28 -10
- package/dist/pinned-write-types.d.ts +75 -0
- package/dist/pinned-write-types.d.ts.map +1 -0
- package/dist/pinned-write-types.js +1 -0
- package/dist/pinned-write.d.ts +7 -33
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +151 -17
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +2 -0
- package/dist/private-producer-handoff.d.ts +16 -0
- package/dist/private-producer-handoff.d.ts.map +1 -0
- package/dist/private-producer-handoff.js +272 -0
- package/dist/private-temp-workspace.d.ts +2 -39
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +183 -77
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +38 -32
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +56 -45
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +12 -5
- package/dist/replace-file-temp-owner.d.ts +1 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +12 -1
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +24 -24
- package/dist/root-boundary.d.ts +4 -0
- package/dist/root-boundary.d.ts.map +1 -1
- package/dist/root-boundary.js +6 -1
- package/dist/root-context.d.ts +12 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +57 -11
- package/dist/root-directory-creation.d.ts +13 -0
- package/dist/root-directory-creation.d.ts.map +1 -0
- package/dist/root-directory-creation.js +212 -0
- package/dist/root-directory-list.d.ts +16 -4
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +180 -39
- package/dist/root-directory.d.ts +23 -0
- package/dist/root-directory.d.ts.map +1 -0
- package/dist/root-directory.js +141 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +12 -1
- package/dist/root-file-final-admission.d.ts +21 -0
- package/dist/root-file-final-admission.d.ts.map +1 -0
- package/dist/root-file-final-admission.js +83 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +103 -24
- package/dist/root-impl.d.ts +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +289 -317
- package/dist/root-move-noreplace.d.ts +14 -0
- package/dist/root-move-noreplace.d.ts.map +1 -0
- package/dist/root-move-noreplace.js +202 -0
- package/dist/root-observed-path.d.ts +9 -0
- package/dist/root-observed-path.d.ts.map +1 -0
- package/dist/root-observed-path.js +95 -0
- package/dist/root-path-errors.d.ts +12 -0
- package/dist/root-path-errors.d.ts.map +1 -0
- package/dist/root-path-errors.js +13 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +53 -20
- package/dist/root-path-observation.d.ts +63 -0
- package/dist/root-path-observation.d.ts.map +1 -0
- package/dist/root-path-observation.js +180 -0
- package/dist/root-path-stat.d.ts +5 -0
- package/dist/root-path-stat.d.ts.map +1 -0
- package/dist/root-path-stat.js +101 -0
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +23 -4
- package/dist/root-path.d.ts +11 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +215 -53
- package/dist/root-paths-lexical.d.ts +9 -0
- package/dist/root-paths-lexical.d.ts.map +1 -0
- package/dist/root-paths-lexical.js +22 -0
- package/dist/root-paths.d.ts +3 -25
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +77 -154
- package/dist/root-read-admission.d.ts +26 -0
- package/dist/root-read-admission.d.ts.map +1 -0
- package/dist/root-read-admission.js +94 -0
- package/dist/root-remove-identity.d.ts +15 -0
- package/dist/root-remove-identity.d.ts.map +1 -0
- package/dist/root-remove-identity.js +89 -0
- package/dist/root-remove-receipt.d.ts +17 -0
- package/dist/root-remove-receipt.d.ts.map +1 -0
- package/dist/root-remove-receipt.js +37 -0
- package/dist/root-remove.d.ts +2 -1
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +172 -10
- package/dist/root-write-admission.d.ts +68 -0
- package/dist/root-write-admission.d.ts.map +1 -0
- package/dist/root-write-admission.js +322 -0
- package/dist/root-write-compatibility.d.ts +13 -0
- package/dist/root-write-compatibility.d.ts.map +1 -0
- package/dist/root-write-compatibility.js +74 -0
- package/dist/root-write-complete-parent.d.ts +42 -0
- package/dist/root-write-complete-parent.d.ts.map +1 -0
- package/dist/root-write-complete-parent.js +195 -0
- package/dist/root-write-publication.d.ts +24 -0
- package/dist/root-write-publication.d.ts.map +1 -0
- package/dist/root-write-publication.js +85 -0
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +6 -4
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +62 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +18 -13
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +5 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +92 -7
- package/dist/secure-temp-dir.d.ts +6 -3
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +121 -101
- package/dist/secure-temp-repair.d.ts +35 -0
- package/dist/secure-temp-repair.d.ts.map +1 -0
- package/dist/secure-temp-repair.js +106 -0
- package/dist/sibling-staged-file.d.ts +3 -2
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +179 -55
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +27 -13
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +44 -14
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +4 -2
- package/dist/staged-directory.d.ts +14 -5
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +54 -3
- package/dist/standalone-publication-path.d.ts +2 -0
- package/dist/standalone-publication-path.d.ts.map +1 -0
- package/dist/standalone-publication-path.js +6 -0
- package/dist/stat-observation.d.ts +11 -0
- package/dist/stat-observation.d.ts.map +1 -0
- package/dist/stat-observation.js +64 -0
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +39 -2
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +7 -2
- package/dist/temp-target.d.ts +2 -0
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +130 -3
- package/dist/temp-workspace-admission.d.ts +22 -0
- package/dist/temp-workspace-admission.d.ts.map +1 -0
- package/dist/temp-workspace-admission.js +374 -0
- package/dist/temp-workspace-child-admission.d.ts +13 -0
- package/dist/temp-workspace-child-admission.d.ts.map +1 -0
- package/dist/temp-workspace-child-admission.js +95 -0
- package/dist/temp-workspace-descriptor.d.ts +48 -0
- package/dist/temp-workspace-descriptor.d.ts.map +1 -0
- package/dist/temp-workspace-descriptor.js +363 -0
- package/dist/temp-workspace-identity.d.ts +15 -0
- package/dist/temp-workspace-identity.d.ts.map +1 -0
- package/dist/temp-workspace-identity.js +41 -0
- package/dist/temp-workspace-owner.d.ts +7 -6
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +121 -61
- package/dist/temp-workspace-permissions.d.ts +4 -0
- package/dist/temp-workspace-permissions.d.ts.map +1 -0
- package/dist/temp-workspace-permissions.js +32 -0
- package/dist/temp-workspace-types.d.ts +40 -0
- package/dist/temp-workspace-types.d.ts.map +1 -0
- package/dist/temp-workspace-types.js +1 -0
- package/dist/temp.d.ts +1 -1
- package/dist/temp.d.ts.map +1 -1
- package/dist/temp.js +1 -1
- package/dist/test-hooks.d.ts +7 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/text-atomic.d.ts.map +1 -1
- package/dist/text-atomic.js +3 -1
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +54 -8
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +9 -6
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +5 -0
- package/dist/windows-path-alias.d.ts +39 -0
- package/dist/windows-path-alias.d.ts.map +1 -0
- package/dist/windows-path-alias.js +153 -0
- package/docs/advanced.md +25 -0
- package/docs/archive.md +23 -2
- package/docs/atomic.md +27 -3
- package/docs/copy.md +3 -0
- package/docs/durability.md +14 -0
- package/docs/errors.md +14 -6
- package/docs/file-store.md +24 -3
- package/docs/filename.md +14 -7
- package/docs/install-path.md +2 -2
- package/docs/install.md +8 -7
- package/docs/json-store.md +5 -1
- package/docs/json.md +11 -0
- package/docs/mutation-policy-proof.md +65 -0
- package/docs/native-helper.md +41 -11
- package/docs/native.md +32 -8
- package/docs/output.md +33 -11
- package/docs/path-prefix.md +64 -0
- package/docs/path-suffix-aliases.md +159 -0
- package/docs/path.md +11 -0
- package/docs/private-file-store.md +14 -0
- package/docs/public-api.md +9 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +8 -1
- package/docs/root.md +18 -3
- package/docs/secret-file.md +3 -0
- package/docs/secure-file.md +6 -2
- package/docs/security-model.md +75 -8
- package/docs/sidecar-lock.md +28 -1
- package/docs/store.md +5 -1
- package/docs/temp.md +260 -36
- package/docs/test-hooks.md +14 -0
- package/docs/writing.md +38 -8
- package/package.json +10 -10
package/dist/output.js
CHANGED
|
@@ -7,6 +7,7 @@ import { fitFileNameToPortableComponent, sanitizeUntrustedFileName } from "./fil
|
|
|
7
7
|
import { isPathInside } from "./path.js";
|
|
8
8
|
import { root } from "./root.js";
|
|
9
9
|
import { tempFile } from "./temp-target.js";
|
|
10
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
10
11
|
const NON_PORTABLE_FILE_NAME_CHARACTERS = /[\u0000-\u001f\u007f-\u009f<>:"/\\|?*]/u;
|
|
11
12
|
function tempFileNameForTarget(targetPath, fallbackFileName) {
|
|
12
13
|
const fallback = sanitizeUntrustedFileName(fallbackFileName ?? "output.bin", "output.bin");
|
|
@@ -51,8 +52,12 @@ function assertFileTargetPath(targetPath) {
|
|
|
51
52
|
}
|
|
52
53
|
export async function writeExternalFileWithinRoot(options) {
|
|
53
54
|
const maxBytes = normalizeMaxBytes(options.maxBytes);
|
|
54
|
-
const
|
|
55
|
+
const rootDir = options.rootDir;
|
|
56
|
+
assertNoWindowsPathAlias(rootDir, "filesystem", "output root uses a Windows filesystem namespace alias");
|
|
55
57
|
const requestedTargetPath = options.path;
|
|
58
|
+
assertNoWindowsPathAlias(path.dirname(requestedTargetPath), "filesystem", "output target parent uses a Windows filesystem namespace alias");
|
|
59
|
+
const producerIsolation = options.producerIsolation;
|
|
60
|
+
const targetRoot = await root(rootDir);
|
|
56
61
|
if (requestedTargetPath.length === 0) {
|
|
57
62
|
throw new FsSafeError("invalid-path", "target path is required");
|
|
58
63
|
}
|
|
@@ -64,7 +69,9 @@ export async function writeExternalFileWithinRoot(options) {
|
|
|
64
69
|
});
|
|
65
70
|
assertFileTargetPath(rawTargetPath);
|
|
66
71
|
const targetPath = sanitizedTargetPath(rawTargetPath, options.fallbackFileName);
|
|
72
|
+
assertNoWindowsPathAlias(targetPath, "filesystem", "output target uses a Windows filesystem namespace alias");
|
|
67
73
|
const finalPath = await targetRoot.resolve(targetPath);
|
|
74
|
+
assertNoWindowsPathAlias(finalPath, "filesystem", "output target uses a Windows filesystem namespace alias");
|
|
68
75
|
if (options.staging === "sibling") {
|
|
69
76
|
const parentPath = path.dirname(targetPath);
|
|
70
77
|
if (parentPath !== ".") {
|
|
@@ -74,7 +81,7 @@ export async function writeExternalFileWithinRoot(options) {
|
|
|
74
81
|
const result = await writeExternalFileViaSibling({
|
|
75
82
|
finalPath: siblingFinalPath,
|
|
76
83
|
write: options.write,
|
|
77
|
-
producerIsolation
|
|
84
|
+
producerIsolation,
|
|
78
85
|
fallbackFileName: options.fallbackFileName,
|
|
79
86
|
maxBytes,
|
|
80
87
|
mode: options.mode,
|
|
@@ -99,7 +106,7 @@ export async function writeExternalFileWithinRoot(options) {
|
|
|
99
106
|
await staged.cleanup();
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
|
-
function
|
|
109
|
+
function buildSiblingTempName(targetPath, fallbackFileName) {
|
|
103
110
|
const prefix = `.fs-safe-output-${process.pid}-${randomUUID()}-`;
|
|
104
111
|
const suffix = ".part";
|
|
105
112
|
const safeTail = fitFileNameToPortableComponent({
|
|
@@ -107,12 +114,15 @@ function buildSiblingTempPath(targetPath, fallbackFileName) {
|
|
|
107
114
|
fileName: tempFileNameForTarget(targetPath, fallbackFileName),
|
|
108
115
|
suffix,
|
|
109
116
|
});
|
|
110
|
-
return
|
|
117
|
+
return `${prefix}${safeTail}${suffix}`;
|
|
111
118
|
}
|
|
112
119
|
async function writeExternalFileViaSibling(params) {
|
|
120
|
+
assertNoWindowsPathAlias(params.finalPath, "filesystem", "output target uses a Windows filesystem namespace alias");
|
|
113
121
|
const finalPath = path.resolve(params.finalPath);
|
|
122
|
+
assertNoWindowsPathAlias(finalPath, "filesystem", "output target uses a Windows filesystem namespace alias");
|
|
114
123
|
const { result } = await writeCallbackSibling({
|
|
115
|
-
|
|
124
|
+
tempDir: path.dirname(finalPath),
|
|
125
|
+
tempName: buildSiblingTempName(finalPath, params.fallbackFileName),
|
|
116
126
|
write: params.write,
|
|
117
127
|
producerIsolation: params.producerIsolation,
|
|
118
128
|
resolveFinalPath: () => finalPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overwrite-file-handle.d.ts","sourceRoot":"","sources":["../src/overwrite-file-handle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAEF,4FAA4F;AAC5F,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"overwrite-file-handle.d.ts","sourceRoot":"","sources":["../src/overwrite-file-handle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAEF,4FAA4F;AAC5F,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CA2Cf"}
|
|
@@ -4,6 +4,10 @@ import { readFileWindowFully } from "./positional-read.js";
|
|
|
4
4
|
import { writeAllToFile } from "./write-file-handle.js";
|
|
5
5
|
/** Overwrite a borrowed, non-append read/write handle with best-effort failure rollback. */
|
|
6
6
|
export async function overwriteFileHandle(handle, data, options = {}) {
|
|
7
|
+
// Snapshot caller-owned admission before the first await. Preserve the
|
|
8
|
+
// former method-call receiver while preventing a later getter mutation from
|
|
9
|
+
// replacing authority after rollback preparation.
|
|
10
|
+
const beforeWrite = options.beforeWrite;
|
|
7
11
|
const payload = data.subarray(0, data.byteLength);
|
|
8
12
|
const payloadBytes = payload.byteLength;
|
|
9
13
|
const stat = await handle.stat();
|
|
@@ -19,7 +23,7 @@ export async function overwriteFileHandle(handle, data, options = {}) {
|
|
|
19
23
|
if (await readFileWindowFully(handle, originalPrefix, 0) !== prefixBytes) {
|
|
20
24
|
throw new FsSafeError("read-failed", "file ended before overwrite rollback preparation completed");
|
|
21
25
|
}
|
|
22
|
-
assertSynchronousCallbackResult(
|
|
26
|
+
assertSynchronousCallbackResult(beforeWrite === undefined ? undefined : Reflect.apply(beforeWrite, options, []), "beforeWrite");
|
|
23
27
|
let prefixStarted = false;
|
|
24
28
|
try {
|
|
25
29
|
// Leave existing bytes intact until any additional storage has been written.
|
package/dist/owner-dacl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"owner-dacl.d.ts","sourceRoot":"","sources":["../src/owner-dacl.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,GACD;IACE,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC3B,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAmCvE"}
|
package/dist/owner-dacl.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { FsSafeError } from "./errors.js";
|
|
2
2
|
import { getNativeBinding } from "./native.js";
|
|
3
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
3
4
|
export function readOwnerAndDacl(targetPath) {
|
|
4
5
|
if (process.platform !== "win32") {
|
|
5
6
|
return { status: "unsupported-platform", platform: process.platform };
|
|
6
7
|
}
|
|
8
|
+
assertNoWindowsPathAlias(targetPath, "filesystem", "owner and DACL path uses a Windows filesystem namespace alias");
|
|
7
9
|
const native = getNativeBinding();
|
|
8
10
|
if (!native) {
|
|
9
11
|
throw new FsSafeError("helper-unavailable", "Windows owner and DACL facts require the matching optional native platform package; " +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-policy.d.ts","sourceRoot":"","sources":["../src/path-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"path-policy.d.ts","sourceRoot":"","sources":["../src/path-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAKxB,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD,eAAO,MAAM,mBAAmB;;;;;;;;;CAA2B,CAAC;AAE5D,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhB;AAED,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBhB"}
|
package/dist/path-policy.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath, } from "./root-path.js";
|
|
3
3
|
import { isNotFoundPathError } from "./path.js";
|
|
4
4
|
import { shortPath } from "./error-detail.js";
|
|
5
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
5
6
|
export const PATH_ALIAS_POLICIES = ROOT_PATH_ALIAS_POLICIES;
|
|
6
7
|
export async function assertNoPathAliasEscape(params) {
|
|
7
8
|
const resolved = await resolveRootPath({
|
|
@@ -25,9 +26,13 @@ export async function assertNoHardlinkedFinalPath(params) {
|
|
|
25
26
|
if (params.allowFinalHardlinkForUnlink) {
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
29
|
+
const filePath = params.filePath;
|
|
30
|
+
const root = params.root;
|
|
31
|
+
const boundaryLabel = params.boundaryLabel;
|
|
32
|
+
assertNoWindowsPathAlias(filePath);
|
|
28
33
|
let stat;
|
|
29
34
|
try {
|
|
30
|
-
stat = fs.statSync(
|
|
35
|
+
stat = fs.statSync(filePath);
|
|
31
36
|
}
|
|
32
37
|
catch (err) {
|
|
33
38
|
if (isNotFoundPathError(err)) {
|
|
@@ -39,6 +44,6 @@ export async function assertNoHardlinkedFinalPath(params) {
|
|
|
39
44
|
return;
|
|
40
45
|
}
|
|
41
46
|
if (stat.nlink > 1) {
|
|
42
|
-
throw new Error(`Hardlinked path is not allowed under ${
|
|
47
|
+
throw new Error(`Hardlinked path is not allowed under ${boundaryLabel} (${shortPath(root)}): ${shortPath(filePath)}`);
|
|
43
48
|
}
|
|
44
49
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ResolvedPathPrefix = {
|
|
2
|
+
readonly absolutePath: string;
|
|
3
|
+
readonly existingPath: string;
|
|
4
|
+
readonly unresolvedSegments: readonly string[];
|
|
5
|
+
};
|
|
6
|
+
export declare function resolvePathPrefixSync(input: string): ResolvedPathPrefix;
|
|
7
|
+
//# sourceMappingURL=path-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-prefix.d.ts","sourceRoot":"","sources":["../src/path-prefix.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;CAChD,CAAC;AAkBF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAsDvE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { formatErrorDetail } from "./error-detail.js";
|
|
4
|
+
import { assertNoNulPathInput, hasNodeErrorCode } from "./path.js";
|
|
5
|
+
import { realpathSync } from "./realpath.js";
|
|
6
|
+
import { absolutePathWithRawSegments } from "./root-path-existing.js";
|
|
7
|
+
import { pathForWindowsFilesystem } from "./windows-path-alias.js";
|
|
8
|
+
function rawRoot(absolutePath) {
|
|
9
|
+
if (path.sep === "\\" && /^\\\\[?.]\\UNC\\/i.test(absolutePath)) {
|
|
10
|
+
// Node parses only the namespace prefix as the root of an extended UNC path.
|
|
11
|
+
const shareRoot = path.parse(`\\\\${absolutePath.slice(8)}`).root;
|
|
12
|
+
return absolutePath.slice(0, shareRoot.length + 6);
|
|
13
|
+
}
|
|
14
|
+
return path.parse(absolutePath).root;
|
|
15
|
+
}
|
|
16
|
+
function resolutionError(code, absolutePath) {
|
|
17
|
+
const detail = code === "ELOOP" ? "too many symbolic links" : "not a directory";
|
|
18
|
+
return Object.assign(new Error(`${code}: ${detail}, resolvePathPrefix ${formatErrorDetail(absolutePath)}`), {
|
|
19
|
+
code, syscall: "resolvePathPrefix", path: absolutePath,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function resolvePathPrefixSync(input) {
|
|
23
|
+
assertNoNulPathInput(input);
|
|
24
|
+
const absolutePath = absolutePathWithRawSegments(input);
|
|
25
|
+
let resolved = rawRoot(absolutePath);
|
|
26
|
+
const remaining = absolutePath.slice(resolved.length).split(path.sep);
|
|
27
|
+
const visitedStates = new Set();
|
|
28
|
+
let symlinkHops = 0;
|
|
29
|
+
while (remaining.length > 0) {
|
|
30
|
+
const segment = remaining.shift();
|
|
31
|
+
if (segment === "")
|
|
32
|
+
continue;
|
|
33
|
+
if (segment === "." || segment === "..") {
|
|
34
|
+
const atRoot = resolved === rawRoot(resolved);
|
|
35
|
+
const lookup = segment === ".." && atRoot ? "." : segment;
|
|
36
|
+
// lstat(dir) does not test search permission, and realpath may erase dots.
|
|
37
|
+
const operationBase = pathForWindowsFilesystem(resolved);
|
|
38
|
+
fs.lstatSync(`${operationBase}${operationBase.endsWith(path.sep) ? "" : path.sep}${lookup}`, { bigint: true });
|
|
39
|
+
if (segment === ".." && !atRoot)
|
|
40
|
+
resolved = path.dirname(resolved);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const candidate = path.join(resolved, segment);
|
|
44
|
+
let stat;
|
|
45
|
+
try {
|
|
46
|
+
stat = fs.lstatSync(candidate, { bigint: true });
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (!hasNodeErrorCode(error, "ENOENT"))
|
|
50
|
+
throw error;
|
|
51
|
+
return {
|
|
52
|
+
absolutePath,
|
|
53
|
+
existingPath: realpathSync.native(pathForWindowsFilesystem(resolved)),
|
|
54
|
+
unresolvedSegments: [segment, ...remaining],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (!stat.isSymbolicLink()) {
|
|
58
|
+
if (!stat.isDirectory() && remaining.length > 0)
|
|
59
|
+
throw resolutionError("ENOTDIR", absolutePath);
|
|
60
|
+
resolved = candidate;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const state = JSON.stringify([String(stat.dev), String(stat.ino), candidate, remaining]);
|
|
64
|
+
if (symlinkHops >= 64 || visitedStates.has(state))
|
|
65
|
+
throw resolutionError("ELOOP", absolutePath);
|
|
66
|
+
visitedStates.add(state);
|
|
67
|
+
symlinkHops++;
|
|
68
|
+
const target = fs.readlinkSync(candidate);
|
|
69
|
+
const rawTarget = path.sep === "\\" ? target.replaceAll("/", "\\") : target;
|
|
70
|
+
if (path.isAbsolute(rawTarget)) {
|
|
71
|
+
const targetRoot = rawRoot(rawTarget);
|
|
72
|
+
// A rooted Windows link target uses the link's drive/share, not cwd's.
|
|
73
|
+
resolved = path.sep === "\\" && targetRoot === "\\" ? rawRoot(resolved) : targetRoot;
|
|
74
|
+
remaining.unshift(...rawTarget.slice(targetRoot.length).split(path.sep));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// A target's parent traversal applies after resolving its preceding links.
|
|
78
|
+
remaining.unshift(...rawTarget.split(path.sep));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { absolutePath, existingPath: realpathSync.native(pathForWindowsFilesystem(resolved)), unresolvedSegments: [] };
|
|
82
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-scope-lexical.d.ts","sourceRoot":"","sources":["../src/path-scope-lexical.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"path-scope-lexical.d.ts","sourceRoot":"","sources":["../src/path-scope-lexical.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAUF,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,2BAA2B,EACnC,IAAI,EAAE,MAAM,GACX;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA4B3D"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { isPathInside, isPathRelativeEscape } from "./path.js";
|
|
3
|
+
import { hasWindowsPathAlias, resolvePathFromBasePreservingWindowsRoot } from "./windows-path-alias.js";
|
|
3
4
|
function pathStaysWithinRoot(rootDir, candidatePath) {
|
|
4
5
|
if (process.platform !== "win32") {
|
|
5
6
|
return candidatePath !== rootDir && isPathInside(rootDir, candidatePath);
|
|
@@ -8,20 +9,29 @@ function pathStaysWithinRoot(rootDir, candidatePath) {
|
|
|
8
9
|
return Boolean(relative) && !isPathRelativeEscape(relative);
|
|
9
10
|
}
|
|
10
11
|
export function resolvePathWithinNormalizedRoot(params, root) {
|
|
11
|
-
const
|
|
12
|
+
const requestedPath = params.requestedPath;
|
|
13
|
+
const defaultFileName = params.defaultFileName;
|
|
14
|
+
const scopeLabel = params.scopeLabel;
|
|
15
|
+
if (hasWindowsPathAlias(params.rootDir, "filesystem") ||
|
|
16
|
+
hasWindowsPathAlias(root, "filesystem") ||
|
|
17
|
+
hasWindowsPathAlias(requestedPath, "filesystem") ||
|
|
18
|
+
(defaultFileName !== undefined && hasWindowsPathAlias(defaultFileName, "filesystem"))) {
|
|
19
|
+
return { ok: false, error: `Invalid path: must stay within ${scopeLabel}` };
|
|
20
|
+
}
|
|
21
|
+
const raw = requestedPath.trim();
|
|
12
22
|
if (!raw) {
|
|
13
|
-
if (!
|
|
23
|
+
if (!defaultFileName) {
|
|
14
24
|
return { ok: false, error: "path is required" };
|
|
15
25
|
}
|
|
16
|
-
const defaultPath =
|
|
17
|
-
if (!pathStaysWithinRoot(root, defaultPath)) {
|
|
18
|
-
return { ok: false, error: `Invalid path: must stay within ${
|
|
26
|
+
const defaultPath = resolvePathFromBasePreservingWindowsRoot(root, defaultFileName);
|
|
27
|
+
if (hasWindowsPathAlias(defaultPath, "filesystem") || !pathStaysWithinRoot(root, defaultPath)) {
|
|
28
|
+
return { ok: false, error: `Invalid path: must stay within ${scopeLabel}` };
|
|
19
29
|
}
|
|
20
30
|
return { ok: true, path: defaultPath };
|
|
21
31
|
}
|
|
22
|
-
const resolved =
|
|
23
|
-
if (!pathStaysWithinRoot(root, resolved)) {
|
|
24
|
-
return { ok: false, error: `Invalid path: must stay within ${
|
|
32
|
+
const resolved = resolvePathFromBasePreservingWindowsRoot(root, raw);
|
|
33
|
+
if (hasWindowsPathAlias(resolved, "filesystem") || !pathStaysWithinRoot(root, resolved)) {
|
|
34
|
+
return { ok: false, error: `Invalid path: must stay within ${scopeLabel}` };
|
|
25
35
|
}
|
|
26
36
|
return { ok: true, path: resolved };
|
|
27
37
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type PathSegmentRoute = Readonly<{
|
|
2
|
+
joined: string;
|
|
3
|
+
offsets: readonly number[];
|
|
4
|
+
}>;
|
|
5
|
+
export declare function createPathSegmentRoute(segments: readonly string[]): PathSegmentRoute;
|
|
6
|
+
export declare function joinPathSegmentRoute(base: string, route: PathSegmentRoute, offset: number, tail?: string): string;
|
|
7
|
+
//# sourceMappingURL=path-segment-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-segment-route.d.ts","sourceRoot":"","sources":["../src/path-segment-route.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,CAAC,CAAC;AAEH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAUpF;AAID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,GACZ,MAAM,CAMR"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function createPathSegmentRoute(segments) {
|
|
3
|
+
const offsets = new Array(segments.length + 1);
|
|
4
|
+
let cursor = 0;
|
|
5
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
6
|
+
offsets[index] = cursor;
|
|
7
|
+
cursor += segments[index].length;
|
|
8
|
+
if (index + 1 < segments.length)
|
|
9
|
+
cursor += path.sep.length;
|
|
10
|
+
}
|
|
11
|
+
offsets[segments.length] = cursor;
|
|
12
|
+
return Object.freeze({ joined: segments.join(path.sep), offsets: Object.freeze(offsets) });
|
|
13
|
+
}
|
|
14
|
+
// Select the immutable suffix by character offset so callers perform at most
|
|
15
|
+
// one path normalization without copying a shrinking segment array.
|
|
16
|
+
export function joinPathSegmentRoute(base, route, offset, tail) {
|
|
17
|
+
const start = route.offsets[offset];
|
|
18
|
+
if (start === undefined)
|
|
19
|
+
throw new RangeError("path segment offset is outside the route");
|
|
20
|
+
const suffix = route.joined.slice(start);
|
|
21
|
+
if (suffix)
|
|
22
|
+
return tail === undefined ? path.join(base, suffix) : path.join(base, suffix, tail);
|
|
23
|
+
return tail === undefined ? base : path.join(base, tail);
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ProbePathSuffixAliasesOptions = {
|
|
2
|
+
directory: string;
|
|
3
|
+
left: string;
|
|
4
|
+
right: string;
|
|
5
|
+
/** Synchronous caller policy for differing NFC pairs not equivalent under ASCII case folding. */
|
|
6
|
+
shouldProbeCaseVariants?: (leftNfc: string, rightNfc: string) => boolean;
|
|
7
|
+
};
|
|
8
|
+
/** Observe bounded missing suffix aliases; the caller owns caching and ambiguity policy. */
|
|
9
|
+
export declare function probePathSuffixAliasesSync(options: ProbePathSuffixAliasesOptions): boolean | undefined;
|
|
10
|
+
//# sourceMappingURL=path-suffix-aliases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-suffix-aliases.d.ts","sourceRoot":"","sources":["../src/path-suffix-aliases.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,uBAAuB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1E,CAAC;AA6UF,4FAA4F;AAC5F,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,6BAA6B,GAAG,OAAO,GAAG,SAAS,CA8CtG"}
|