@openclaw/fs-safe 0.12.0 → 0.13.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 +61 -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/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 +14 -0
- package/dist/native-binding.d.ts.map +1 -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-parent-admission.d.ts +31 -0
- package/dist/native-parent-admission.d.ts.map +1 -0
- package/dist/native-parent-admission.js +124 -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 +0 -3
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +309 -40
- 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 +33 -29
- 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 +26 -9
- 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/install-path.js
CHANGED
|
@@ -3,6 +3,7 @@ import fsSync from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { isPathInside } from "./path.js";
|
|
5
5
|
import { realpathSync } from "./realpath.js";
|
|
6
|
+
import { hasWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
6
7
|
export function safeDirName(input) {
|
|
7
8
|
const trimmed = input.trim();
|
|
8
9
|
if (!trimmed) {
|
|
@@ -40,10 +41,24 @@ export function safePathSegmentHashedV2(input) {
|
|
|
40
41
|
return `id-v2-${hash}`;
|
|
41
42
|
}
|
|
42
43
|
export function resolveSafeInstallDir(params) {
|
|
44
|
+
const baseDir = params.baseDir;
|
|
45
|
+
if (hasWindowsPathAlias(baseDir, "filesystem")) {
|
|
46
|
+
return { ok: false, error: params.invalidNameMessage };
|
|
47
|
+
}
|
|
43
48
|
const encodedName = (params.nameEncoder ?? safeDirName)(params.id);
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
if (hasWindowsPathAlias(encodedName, "relative")) {
|
|
50
|
+
return { ok: false, error: params.invalidNameMessage };
|
|
51
|
+
}
|
|
52
|
+
const targetDir = path.join(baseDir, encodedName);
|
|
53
|
+
if (hasWindowsPathAlias(targetDir, "filesystem")) {
|
|
54
|
+
return { ok: false, error: params.invalidNameMessage };
|
|
55
|
+
}
|
|
56
|
+
const resolvedBase = resolvePathPreservingWindowsRoot(baseDir);
|
|
46
57
|
const resolvedTarget = path.resolve(targetDir);
|
|
58
|
+
if (hasWindowsPathAlias(resolvedBase, "filesystem") ||
|
|
59
|
+
hasWindowsPathAlias(resolvedTarget, "filesystem")) {
|
|
60
|
+
return { ok: false, error: params.invalidNameMessage };
|
|
61
|
+
}
|
|
47
62
|
const relative = path.relative(resolvedBase, resolvedTarget);
|
|
48
63
|
if (!relative ||
|
|
49
64
|
relative === ".." ||
|
|
@@ -54,40 +69,58 @@ export function resolveSafeInstallDir(params) {
|
|
|
54
69
|
return { ok: true, path: resolvedTarget };
|
|
55
70
|
}
|
|
56
71
|
export async function assertCanonicalPathWithinBase(params) {
|
|
57
|
-
const
|
|
58
|
-
const
|
|
72
|
+
const baseDirInput = params.baseDir;
|
|
73
|
+
const candidatePathInput = params.candidatePath;
|
|
74
|
+
const boundaryLabel = params.boundaryLabel;
|
|
75
|
+
const invalidPath = () => new Error(`Invalid path: must stay within ${boundaryLabel}`);
|
|
76
|
+
const assertAdmittedPath = (value) => {
|
|
77
|
+
if (hasWindowsPathAlias(value, "filesystem"))
|
|
78
|
+
throw invalidPath();
|
|
79
|
+
};
|
|
80
|
+
assertAdmittedPath(baseDirInput);
|
|
81
|
+
assertAdmittedPath(candidatePathInput);
|
|
82
|
+
const baseDir = resolvePathPreservingWindowsRoot(baseDirInput);
|
|
83
|
+
const candidatePath = resolvePathPreservingWindowsRoot(candidatePathInput);
|
|
84
|
+
assertAdmittedPath(baseDir);
|
|
85
|
+
assertAdmittedPath(candidatePath);
|
|
59
86
|
if (!isPathInside(baseDir, candidatePath)) {
|
|
60
|
-
throw
|
|
87
|
+
throw invalidPath();
|
|
61
88
|
}
|
|
62
|
-
const
|
|
89
|
+
const baseOperationPath = pathForWindowsFilesystem(baseDir);
|
|
90
|
+
const baseLstat = fsSync.lstatSync(baseOperationPath);
|
|
63
91
|
if (baseLstat.isSymbolicLink()) {
|
|
64
|
-
const baseStat = fsSync.statSync(
|
|
92
|
+
const baseStat = fsSync.statSync(baseOperationPath);
|
|
65
93
|
if (!baseStat.isDirectory()) {
|
|
66
|
-
throw new Error(`Invalid ${
|
|
94
|
+
throw new Error(`Invalid ${boundaryLabel}: base directory must resolve to a directory`);
|
|
67
95
|
}
|
|
68
96
|
}
|
|
69
97
|
else if (!baseLstat.isDirectory()) {
|
|
70
|
-
throw new Error(`Invalid ${
|
|
98
|
+
throw new Error(`Invalid ${boundaryLabel}: base directory must be a directory`);
|
|
71
99
|
}
|
|
72
|
-
const baseRealPath = realpathSync.native(
|
|
100
|
+
const baseRealPath = realpathSync.native(baseOperationPath);
|
|
101
|
+
assertAdmittedPath(baseRealPath);
|
|
73
102
|
const validateDirectory = async (dirPath) => {
|
|
74
|
-
|
|
75
|
-
const
|
|
103
|
+
assertAdmittedPath(dirPath);
|
|
104
|
+
const resolvedDirPath = resolvePathPreservingWindowsRoot(dirPath);
|
|
105
|
+
assertAdmittedPath(resolvedDirPath);
|
|
106
|
+
const operationPath = pathForWindowsFilesystem(dirPath);
|
|
107
|
+
const dirLstat = fsSync.lstatSync(operationPath);
|
|
76
108
|
if (dirLstat.isSymbolicLink()) {
|
|
77
109
|
if (resolvedDirPath !== baseDir) {
|
|
78
|
-
throw new Error(`Invalid path: must stay within ${
|
|
110
|
+
throw new Error(`Invalid path: must stay within ${boundaryLabel}`);
|
|
79
111
|
}
|
|
80
|
-
const dirStat = fsSync.statSync(
|
|
112
|
+
const dirStat = fsSync.statSync(operationPath);
|
|
81
113
|
if (!dirStat.isDirectory()) {
|
|
82
|
-
throw new Error(`Invalid path: must stay within ${
|
|
114
|
+
throw new Error(`Invalid path: must stay within ${boundaryLabel}`);
|
|
83
115
|
}
|
|
84
116
|
}
|
|
85
117
|
else if (!dirLstat.isDirectory()) {
|
|
86
|
-
throw new Error(`Invalid path: must stay within ${
|
|
118
|
+
throw new Error(`Invalid path: must stay within ${boundaryLabel}`);
|
|
87
119
|
}
|
|
88
|
-
const dirRealPath = realpathSync.native(
|
|
120
|
+
const dirRealPath = realpathSync.native(operationPath);
|
|
121
|
+
assertAdmittedPath(dirRealPath);
|
|
89
122
|
if (!isPathInside(baseRealPath, dirRealPath)) {
|
|
90
|
-
throw
|
|
123
|
+
throw invalidPath();
|
|
91
124
|
}
|
|
92
125
|
};
|
|
93
126
|
try {
|
|
@@ -100,5 +133,7 @@ export async function assertCanonicalPathWithinBase(params) {
|
|
|
100
133
|
throw err;
|
|
101
134
|
}
|
|
102
135
|
}
|
|
103
|
-
|
|
136
|
+
const candidateParent = path.dirname(candidatePath);
|
|
137
|
+
assertAdmittedPath(candidateParent);
|
|
138
|
+
await validateDirectory(candidateParent);
|
|
104
139
|
}
|
|
@@ -4,6 +4,12 @@ export type DurableQueueEntryPathsLike = {
|
|
|
4
4
|
deliveredPath: string;
|
|
5
5
|
processingPath?: string;
|
|
6
6
|
};
|
|
7
|
+
export type ValidatedDurableQueueEntryPaths = Readonly<{
|
|
8
|
+
jsonPath: string;
|
|
9
|
+
deliveredPath: string;
|
|
10
|
+
processingPath: string;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function validateDurableQueueEntryPaths(paths: DurableQueueEntryPathsLike): ValidatedDurableQueueEntryPaths;
|
|
7
13
|
export declare function durableQueueProcessingPath(paths: DurableQueueEntryPathsLike): string;
|
|
8
14
|
export declare function claimDurableQueueEntry(paths: DurableQueueEntryPathsLike, options?: {
|
|
9
15
|
skipUnowned?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-durable-queue-ownership.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-ownership.ts"],"names":[],"mappings":"AAEA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"json-durable-queue-ownership.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-ownership.ts"],"names":[],"mappings":"AAEA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAUnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC;AAUH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,0BAA0B,GAChC,+BAA+B,CAkBjC;AAiBD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAIpF;AA0ED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,0BAA0B,EACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAGD,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,KAAK,EAAE,0BAA0B,EACjC,QAAQ,EAAE,WAAW,EACrB,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACnC,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxE,OAAO,CAAC,CAAC,CAAC,CAmCZ;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,OAAO,CAAC,CAclB;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDhB;AAKD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI1D"}
|
|
@@ -8,12 +8,48 @@ import { FsSafeError } from "./errors.js";
|
|
|
8
8
|
import { sameFileIdentityForCleanup, sha256Hex } from "./file-identity.js";
|
|
9
9
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
10
10
|
import { serializePathWrite } from "./write-queue.js";
|
|
11
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
12
|
+
const validatedDurableQueueEntryPaths = new WeakSet();
|
|
13
|
+
function assertQueuePathString(value, label) {
|
|
14
|
+
if (typeof value !== "string") {
|
|
15
|
+
throw new FsSafeError("invalid-path", `${label} must be a string`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function validateDurableQueueEntryPaths(paths) {
|
|
19
|
+
const jsonPath = paths.jsonPath;
|
|
20
|
+
const deliveredPath = paths.deliveredPath;
|
|
21
|
+
const requestedProcessingPath = paths.processingPath;
|
|
22
|
+
assertQueuePathString(jsonPath, "queue JSON path");
|
|
23
|
+
assertQueuePathString(deliveredPath, "queue delivered path");
|
|
24
|
+
if (requestedProcessingPath !== undefined) {
|
|
25
|
+
assertQueuePathString(requestedProcessingPath, "queue processing path");
|
|
26
|
+
}
|
|
27
|
+
assertNoWindowsPathAlias(jsonPath);
|
|
28
|
+
assertNoWindowsPathAlias(deliveredPath);
|
|
29
|
+
if (requestedProcessingPath !== undefined) {
|
|
30
|
+
assertNoWindowsPathAlias(requestedProcessingPath);
|
|
31
|
+
}
|
|
32
|
+
const processingPath = requestedProcessingPath || processingPathFromJsonPath(jsonPath);
|
|
33
|
+
const validated = Object.freeze({ deliveredPath, jsonPath, processingPath });
|
|
34
|
+
validatedDurableQueueEntryPaths.add(validated);
|
|
35
|
+
return validated;
|
|
36
|
+
}
|
|
37
|
+
function ownDurableQueueEntryPaths(paths) {
|
|
38
|
+
if (validatedDurableQueueEntryPaths.has(paths)) {
|
|
39
|
+
return paths;
|
|
40
|
+
}
|
|
41
|
+
return validateDurableQueueEntryPaths(paths);
|
|
42
|
+
}
|
|
43
|
+
function processingPathFromJsonPath(jsonPath) {
|
|
44
|
+
return jsonPath.endsWith(".json")
|
|
45
|
+
? `${jsonPath.slice(0, -".json".length)}.processing`
|
|
46
|
+
: `${jsonPath}.processing`;
|
|
47
|
+
}
|
|
11
48
|
export function durableQueueProcessingPath(paths) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
: `${paths.jsonPath}.processing`;
|
|
49
|
+
const processingPath = paths.processingPath;
|
|
50
|
+
if (processingPath)
|
|
51
|
+
return processingPath;
|
|
52
|
+
return processingPathFromJsonPath(paths.jsonPath);
|
|
17
53
|
}
|
|
18
54
|
async function regularQueueFileIdentity(filePath) {
|
|
19
55
|
const identity = await lstatOrNull(filePath);
|
|
@@ -25,10 +61,12 @@ async function regularQueueFileIdentity(filePath) {
|
|
|
25
61
|
return identity;
|
|
26
62
|
}
|
|
27
63
|
async function withQueueEntryLock(paths, run) {
|
|
28
|
-
|
|
64
|
+
const resolvedJsonPath = path.resolve(paths.jsonPath);
|
|
65
|
+
assertNoWindowsPathAlias(resolvedJsonPath);
|
|
66
|
+
return await serializePathWrite(paths.jsonPath, async () => await withQueueTransferLock(resolvedJsonPath, run));
|
|
29
67
|
}
|
|
30
68
|
async function claimDurableQueueEntryUnlocked(paths, options = {}) {
|
|
31
|
-
const processingPath =
|
|
69
|
+
const processingPath = paths.processingPath;
|
|
32
70
|
await recoverDurableQueueRetirement({ jsonPath: paths.jsonPath, processingPath });
|
|
33
71
|
const existingProcessing = await regularQueueFileIdentity(processingPath);
|
|
34
72
|
if (existingProcessing) {
|
|
@@ -80,14 +118,16 @@ async function claimDurableQueueEntryUnlocked(paths, options = {}) {
|
|
|
80
118
|
return processingPath;
|
|
81
119
|
}
|
|
82
120
|
export async function claimDurableQueueEntry(paths, options = {}) {
|
|
83
|
-
|
|
121
|
+
const validatedPaths = ownDurableQueueEntryPaths(paths);
|
|
122
|
+
if (!(await lstatOrNull(path.dirname(validatedPaths.jsonPath))))
|
|
84
123
|
return null;
|
|
85
|
-
return await withQueueEntryLock(
|
|
124
|
+
return await withQueueEntryLock(validatedPaths, async () => await claimDurableQueueEntryUnlocked(validatedPaths, options));
|
|
86
125
|
}
|
|
87
126
|
// The caller retains its read pin until this owner releases it or migration settles.
|
|
88
127
|
export async function migrateDurableQueueEntry(paths, expected, releaseReadPin, run) {
|
|
89
|
-
|
|
90
|
-
|
|
128
|
+
const validatedPaths = ownDurableQueueEntryPaths(paths);
|
|
129
|
+
return await withQueueEntryLock(validatedPaths, async () => {
|
|
130
|
+
const processingPath = validatedPaths.processingPath;
|
|
91
131
|
const assertCurrent = async () => {
|
|
92
132
|
try {
|
|
93
133
|
await inspectFileIdentity(() => {
|
|
@@ -118,83 +158,90 @@ export async function migrateDurableQueueEntry(paths, expected, releaseReadPin,
|
|
|
118
158
|
});
|
|
119
159
|
}
|
|
120
160
|
export async function completeDeliveredQueueEntry(paths) {
|
|
121
|
-
|
|
161
|
+
const validatedPaths = ownDurableQueueEntryPaths(paths);
|
|
162
|
+
if (!(await lstatOrNull(path.dirname(validatedPaths.deliveredPath))))
|
|
122
163
|
return false;
|
|
123
|
-
return await withQueueEntryLock(
|
|
124
|
-
if (!(await regularQueueFileIdentity(
|
|
164
|
+
return await withQueueEntryLock(validatedPaths, async () => {
|
|
165
|
+
if (!(await regularQueueFileIdentity(validatedPaths.deliveredPath)))
|
|
125
166
|
return false;
|
|
126
|
-
if (await regularQueueFileIdentity(
|
|
127
|
-
await fs.unlink(
|
|
128
|
-
await syncDirectory(path.dirname(
|
|
167
|
+
if (await regularQueueFileIdentity(validatedPaths.processingPath)) {
|
|
168
|
+
await fs.unlink(validatedPaths.deliveredPath);
|
|
169
|
+
await syncDirectory(path.dirname(validatedPaths.deliveredPath));
|
|
129
170
|
return false;
|
|
130
171
|
}
|
|
131
|
-
await fs.unlink(
|
|
132
|
-
await syncDirectory(path.dirname(
|
|
172
|
+
await fs.unlink(validatedPaths.deliveredPath);
|
|
173
|
+
await syncDirectory(path.dirname(validatedPaths.deliveredPath));
|
|
133
174
|
return true;
|
|
134
175
|
});
|
|
135
176
|
}
|
|
136
177
|
export async function acknowledgeDurableQueueEntry(paths) {
|
|
137
|
-
|
|
178
|
+
const validatedPaths = ownDurableQueueEntryPaths(paths);
|
|
179
|
+
if (!(await lstatOrNull(path.dirname(validatedPaths.jsonPath))))
|
|
138
180
|
return;
|
|
139
|
-
await withQueueEntryLock(
|
|
140
|
-
const processingPath =
|
|
181
|
+
await withQueueEntryLock(validatedPaths, async () => {
|
|
182
|
+
const processingPath = validatedPaths.processingPath;
|
|
141
183
|
const processing = await regularQueueFileIdentity(processingPath);
|
|
142
|
-
const delivered = await regularQueueFileIdentity(
|
|
184
|
+
const delivered = await regularQueueFileIdentity(validatedPaths.deliveredPath);
|
|
143
185
|
if (!processing) {
|
|
144
186
|
if (delivered) {
|
|
145
|
-
await fs.unlink(
|
|
187
|
+
await fs.unlink(validatedPaths.deliveredPath);
|
|
146
188
|
}
|
|
147
|
-
await syncDirectory(path.dirname(
|
|
148
|
-
if (await regularQueueFileIdentity(
|
|
189
|
+
await syncDirectory(path.dirname(validatedPaths.deliveredPath));
|
|
190
|
+
if (await regularQueueFileIdentity(validatedPaths.jsonPath)) {
|
|
149
191
|
throw new FsSafeError("path-mismatch", "queue acknowledgement requires a processing claim");
|
|
150
192
|
}
|
|
151
193
|
return;
|
|
152
194
|
}
|
|
153
195
|
if (delivered)
|
|
154
|
-
await fs.unlink(
|
|
155
|
-
await fs.rename(processingPath,
|
|
156
|
-
await syncDirectory(path.dirname(
|
|
157
|
-
await fs.unlink(
|
|
158
|
-
await syncDirectory(path.dirname(
|
|
196
|
+
await fs.unlink(validatedPaths.deliveredPath);
|
|
197
|
+
await fs.rename(processingPath, validatedPaths.deliveredPath);
|
|
198
|
+
await syncDirectory(path.dirname(validatedPaths.deliveredPath));
|
|
199
|
+
await fs.unlink(validatedPaths.deliveredPath);
|
|
200
|
+
await syncDirectory(path.dirname(validatedPaths.deliveredPath));
|
|
159
201
|
});
|
|
160
202
|
}
|
|
161
203
|
export async function moveDurableQueueEntryToFailed(params) {
|
|
162
|
-
|
|
163
|
-
|
|
204
|
+
const pathsInput = params.paths;
|
|
205
|
+
const paths = ownDurableQueueEntryPaths(pathsInput);
|
|
206
|
+
const failedPath = params.failedPath;
|
|
207
|
+
assertQueuePathString(failedPath, "failed queue path");
|
|
208
|
+
assertNoWindowsPathAlias(failedPath);
|
|
209
|
+
await withQueueEntryLock(paths, async () => {
|
|
210
|
+
const processingPath = paths.processingPath;
|
|
164
211
|
const processing = await regularQueueFileIdentity(processingPath);
|
|
165
|
-
const existingFailed = await regularQueueFileIdentity(
|
|
212
|
+
const existingFailed = await regularQueueFileIdentity(failedPath);
|
|
166
213
|
if (!processing && existingFailed) {
|
|
167
|
-
await syncDirectory(path.dirname(
|
|
214
|
+
await syncDirectory(path.dirname(failedPath));
|
|
168
215
|
await syncDirectory(path.dirname(processingPath));
|
|
169
|
-
const recoveredFailed = await regularQueueFileIdentity(
|
|
216
|
+
const recoveredFailed = await regularQueueFileIdentity(failedPath);
|
|
170
217
|
if (!recoveredFailed ||
|
|
171
218
|
!sameFileIdentityForCleanup(existingFailed, recoveredFailed)) {
|
|
172
219
|
throw new FsSafeError("path-mismatch", "failed queue destination changed during recovery");
|
|
173
220
|
}
|
|
174
|
-
if (await regularQueueFileIdentity(
|
|
221
|
+
if (await regularQueueFileIdentity(paths.jsonPath)) {
|
|
175
222
|
throw new FsSafeError("already-exists", "failed queue destination already exists");
|
|
176
223
|
}
|
|
177
224
|
return;
|
|
178
225
|
}
|
|
179
226
|
const sourcePath = processing
|
|
180
227
|
? processingPath
|
|
181
|
-
: await claimDurableQueueEntryUnlocked(
|
|
228
|
+
: await claimDurableQueueEntryUnlocked(paths);
|
|
182
229
|
if (!sourcePath) {
|
|
183
230
|
throw Object.assign(new Error("queue entry does not exist"), { code: "ENOENT" });
|
|
184
231
|
}
|
|
185
232
|
const source = await regularQueueFileIdentity(sourcePath);
|
|
186
|
-
const failed = await regularQueueFileIdentity(
|
|
233
|
+
const failed = await regularQueueFileIdentity(failedPath);
|
|
187
234
|
if (failed) {
|
|
188
235
|
if (source && sameFileIdentityForCleanup(source, failed)) {
|
|
189
|
-
await syncDirectory(path.dirname(
|
|
236
|
+
await syncDirectory(path.dirname(failedPath));
|
|
190
237
|
await fs.unlink(sourcePath);
|
|
191
238
|
await syncDirectory(path.dirname(sourcePath));
|
|
192
239
|
return;
|
|
193
240
|
}
|
|
194
241
|
throw new FsSafeError("already-exists", "failed queue destination already exists");
|
|
195
242
|
}
|
|
196
|
-
await fs.link(sourcePath,
|
|
197
|
-
await syncDirectory(path.dirname(
|
|
243
|
+
await fs.link(sourcePath, failedPath);
|
|
244
|
+
await syncDirectory(path.dirname(failedPath));
|
|
198
245
|
await fs.unlink(sourcePath);
|
|
199
246
|
await syncDirectory(path.dirname(sourcePath));
|
|
200
247
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type QueueValidationRoot = {
|
|
2
|
+
path: string;
|
|
3
|
+
allowSymlinkBase: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function resolveQueueFilesystemPath(value: string): string;
|
|
6
|
+
export declare function queueValidationRoots(queueDir: string, failedDir: string): Promise<{
|
|
7
|
+
queueRoot: QueueValidationRoot;
|
|
8
|
+
failedRoot: QueueValidationRoot;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function queueValidationRoot(dir: string): QueueValidationRoot;
|
|
11
|
+
//# sourceMappingURL=json-durable-queue-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-durable-queue-paths.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-paths.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhE;AAqBD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,SAAS,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAU9E;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAKpE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
3
|
+
export function resolveQueueFilesystemPath(value) {
|
|
4
|
+
const resolved = resolvePathPreservingWindowsRoot(value);
|
|
5
|
+
assertNoWindowsPathAlias(resolved);
|
|
6
|
+
return resolved;
|
|
7
|
+
}
|
|
8
|
+
function commonPathAncestor(paths) {
|
|
9
|
+
const resolved = paths.map(resolveQueueFilesystemPath);
|
|
10
|
+
const root = path.parse(resolved[0] ?? process.cwd()).root;
|
|
11
|
+
const parts = resolved.map((entry) => path.relative(root, entry).split(path.sep));
|
|
12
|
+
const common = [];
|
|
13
|
+
for (let index = 0; parts.every((part) => index < part.length); index++) {
|
|
14
|
+
const segment = parts[0]?.[index];
|
|
15
|
+
if (!segment || !parts.every((part) => part[index] === segment))
|
|
16
|
+
break;
|
|
17
|
+
common.push(segment);
|
|
18
|
+
}
|
|
19
|
+
return path.join(root, ...common);
|
|
20
|
+
}
|
|
21
|
+
function samePathRoot(paths) {
|
|
22
|
+
const roots = paths.map((entry) => path.parse(resolveQueueFilesystemPath(entry)).root);
|
|
23
|
+
const first = process.platform === "win32" ? roots[0]?.toLowerCase() : roots[0];
|
|
24
|
+
return roots.every((root) => (process.platform === "win32" ? root.toLowerCase() : root) === first);
|
|
25
|
+
}
|
|
26
|
+
export async function queueValidationRoots(queueDir, failedDir) {
|
|
27
|
+
if (samePathRoot([queueDir, failedDir])) {
|
|
28
|
+
const common = commonPathAncestor([queueDir, failedDir]);
|
|
29
|
+
const root = queueValidationRoot(common);
|
|
30
|
+
return { failedRoot: root, queueRoot: root };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
failedRoot: queueValidationRoot(failedDir),
|
|
34
|
+
queueRoot: queueValidationRoot(queueDir),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function queueValidationRoot(dir) {
|
|
38
|
+
return {
|
|
39
|
+
path: path.parse(resolveQueueFilesystemPath(dir)).root,
|
|
40
|
+
allowSymlinkBase: process.platform === "darwin",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-durable-queue-read.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-read.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"json-durable-queue-read.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-read.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAO/C,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AA8B3E,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,CAAC,EAClD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9B,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxF,OAAO,CAAC,CAAC,CAAC,CA+BZ"}
|
|
@@ -3,6 +3,7 @@ import { normalizeMaxBytes } from "./byte-budget.js";
|
|
|
3
3
|
import { readBoundedAsync } from "./bounded-read.js";
|
|
4
4
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
5
5
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
6
|
+
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
6
7
|
export const DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES = 16 * 1024 * 1024;
|
|
7
8
|
async function inspectQueueEntry(inspect, maxBytes, expected) {
|
|
8
9
|
let inspectionFailed = false;
|
|
@@ -36,6 +37,7 @@ export async function withJsonDurableQueueEntry(filePath, options, run) {
|
|
|
36
37
|
const maxBytes = normalizeMaxBytes(options.maxBytes, {
|
|
37
38
|
defaultValue: DEFAULT_JSON_DURABLE_QUEUE_ENTRY_MAX_BYTES,
|
|
38
39
|
});
|
|
40
|
+
assertNoWindowsPathAlias(filePath);
|
|
39
41
|
const inspectPath = () => fs.lstatSync(filePath, { bigint: true });
|
|
40
42
|
const initialStat = await inspectQueueEntry(inspectPath, maxBytes);
|
|
41
43
|
const handle = await fs.promises.open(filePath, resolveReadOpenFlags());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,0CAA0C,EAAE,MAAM,8BAA8B,CAAC;AAE1F,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAQ5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhB;AAwID,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhB;AAmBD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAgBD,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAwBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CA2Ed;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB"}
|