@openclaw/fs-safe 0.9.0 → 0.11.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 +134 -0
- package/LICENSE +1 -0
- package/README.md +52 -4
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +3 -2
- package/dist/advanced.d.ts +5 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +5 -0
- package/dist/archive-crc32.d.ts.map +1 -1
- package/dist/archive-crc32.js +6 -1
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +3 -4
- 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 +4 -5
- package/dist/archive-gzip-tail.d.ts +3 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +23 -3
- 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 +16 -13
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +7 -6
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +83 -74
- 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 +11 -4
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +23 -10
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +18 -14
- 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 +13 -8
- 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 +12 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +82 -45
- package/dist/clone-metadata.d.ts +19 -0
- package/dist/clone-metadata.d.ts.map +1 -0
- package/dist/clone-metadata.js +32 -0
- package/dist/copy-file-input.d.ts +22 -0
- package/dist/copy-file-input.d.ts.map +1 -0
- package/dist/copy-file-input.js +69 -0
- package/dist/copy-policy.d.ts +3 -0
- package/dist/copy-policy.d.ts.map +1 -0
- package/dist/copy-policy.js +8 -0
- package/dist/copy-publication.d.ts +10 -1
- package/dist/copy-publication.d.ts.map +1 -1
- package/dist/copy-publication.js +27 -0
- package/dist/copy-tree-portable.d.ts +9 -0
- package/dist/copy-tree-portable.d.ts.map +1 -0
- package/dist/copy-tree-portable.js +222 -0
- package/dist/copy.d.ts +16 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +125 -0
- 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/error-detail.d.ts.map +1 -1
- package/dist/error-detail.js +4 -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 +9 -2
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +135 -39
- 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 +6 -4
- 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.map +1 -1
- package/dist/filename.js +4 -13
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +4 -2
- package/dist/guarded-mutation.d.ts +2 -0
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +8 -4
- 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 +413 -0
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +9 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +5 -8
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +19 -18
- 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 +24 -25
- package/dist/move-path-stage.d.ts +8 -0
- package/dist/move-path-stage.d.ts.map +1 -0
- package/dist/move-path-stage.js +56 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +30 -34
- package/dist/mutation-authority.d.ts +9 -0
- package/dist/mutation-authority.d.ts.map +1 -0
- package/dist/mutation-authority.js +36 -0
- package/dist/native-binding.d.ts +29 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts +1 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +11 -5
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +19 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +3 -1
- package/dist/native-staged-file.d.ts +3 -3
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +35 -8
- 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.d.ts.map +1 -1
- package/dist/path.js +5 -2
- package/dist/permissions-windows.d.ts +1 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +85 -53
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +3 -1
- package/dist/pinned-operation.js +1 -1
- package/dist/pinned-write.d.ts +7 -3
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +51 -30
- package/dist/positional-read.d.ts +9 -0
- package/dist/positional-read.d.ts.map +1 -0
- package/dist/positional-read.js +36 -0
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +6 -4
- package/dist/publish-copy-stage.d.ts +13 -0
- package/dist/publish-copy-stage.d.ts.map +1 -0
- package/dist/publish-copy-stage.js +47 -0
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +6 -5
- 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-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +6 -2
- 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-context.d.ts +3 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +53 -28
- 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 +26 -0
- package/dist/root-directory-list.d.ts.map +1 -0
- package/dist/root-directory-list.js +219 -0
- 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 +6 -5
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +16 -12
- package/dist/root-file.d.ts +2 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +12 -4
- package/dist/root-impl.d.ts +17 -51
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +358 -234
- package/dist/root-options.d.ts +77 -0
- package/dist/root-options.d.ts.map +1 -0
- package/dist/root-options.js +18 -0
- package/dist/root-path-existing.d.ts +5 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +59 -3
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +3 -2
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +74 -162
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +13 -9
- 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 +14 -0
- package/dist/root-symlink-policy.d.ts.map +1 -0
- package/dist/root-symlink-policy.js +34 -0
- package/dist/root-walk.d.ts +5 -4
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +99 -50
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +3 -5
- package/dist/root.d.ts +4 -1
- package/dist/root.d.ts.map +1 -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.d.ts.map +1 -1
- package/dist/secure-file.js +25 -8
- 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 +1 -0
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +42 -8
- package/dist/sibling-temp.d.ts +2 -0
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +6 -4
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +17 -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 +20 -1
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +4 -3
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +3 -2
- package/dist/temp-target.d.ts +14 -12
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +12 -6
- package/dist/timing.d.ts +1 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +25 -6
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +9 -7
- 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 +9 -28
- package/dist/windows-owner.d.ts +9 -12
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +24 -58
- package/dist/write-file-handle.d.ts +7 -0
- package/dist/write-file-handle.d.ts.map +1 -0
- package/dist/write-file-handle.js +26 -0
- package/docs/advanced.md +22 -4
- package/docs/archive.md +44 -9
- package/docs/atomic.md +24 -1
- package/docs/config.md +1 -0
- package/docs/contributing.md +36 -1
- package/docs/copy.md +155 -0
- package/docs/directory-identity.md +85 -0
- package/docs/durability.md +53 -3
- package/docs/entries.md +109 -0
- package/docs/errors.md +4 -4
- package/docs/file-store.md +15 -0
- package/docs/guest.md +141 -0
- package/docs/in-place-write.md +81 -0
- package/docs/index.md +2 -0
- package/docs/install.md +31 -0
- package/docs/local-roots.md +8 -1
- package/docs/native-helper.md +10 -3
- package/docs/native.md +18 -1
- package/docs/output.md +32 -6
- package/docs/path-case.md +64 -0
- package/docs/path-scope.md +1 -1
- package/docs/permissions.md +29 -10
- package/docs/positional-read.md +63 -0
- package/docs/public-api.md +31 -2
- package/docs/root.md +196 -6
- package/docs/secure-file.md +2 -0
- package/docs/security-model.md +14 -0
- package/docs/sidecar-lock.md +12 -3
- package/docs/temp.md +35 -6
- package/docs/timing.md +2 -0
- package/docs/types.md +19 -12
- package/docs/walk.md +54 -5
- package/docs/writing.md +173 -5
- package/package.json +19 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realpath.d.ts","sourceRoot":"","sources":["../src/realpath.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY,WAAyB,MAAM;oBACtC,MAAM;CACtB,CAAC"}
|
package/dist/realpath.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
import { getSystemErrorName } from "node:util";
|
|
4
|
+
import { FsSafeError } from "./errors.js";
|
|
5
|
+
import { getNativeBinding } from "./native.js";
|
|
6
|
+
import { getFsSafeNativeConfig } from "./native-config.js";
|
|
7
|
+
const runtimePlatform = process.versions.bun ? platform() : undefined;
|
|
8
|
+
const bunPosix = runtimePlatform === "darwin" || runtimePlatform === "linux" ? runtimePlatform : undefined;
|
|
9
|
+
function resolve(input, native) {
|
|
10
|
+
if (bunPosix) {
|
|
11
|
+
// Bun 1.4.2 normalizes native path components and opens the leaf for reads.
|
|
12
|
+
// The Rust OS resolver preserves permissions, locks and physical spelling.
|
|
13
|
+
// Reuse our N-API addon so this also works with JIT disabled; Bun's FFI
|
|
14
|
+
// bridge requires JIT and would duplicate native loading and memory handling.
|
|
15
|
+
// Remove this routing once the Bun baseline includes oven-sh/bun#42374.
|
|
16
|
+
if (input.includes("\0")) {
|
|
17
|
+
throw Object.assign(new TypeError("realpath input must not contain null bytes"), {
|
|
18
|
+
code: "ERR_INVALID_ARG_VALUE",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const binding = getNativeBinding();
|
|
22
|
+
if (binding?.canonicalizePath) {
|
|
23
|
+
// Ordinary resolution normalizes both the input and each expanded symlink.
|
|
24
|
+
const result = binding.canonicalizePath(input, !native);
|
|
25
|
+
if (result.path !== undefined)
|
|
26
|
+
return result.path;
|
|
27
|
+
const errno = -(result.errno);
|
|
28
|
+
const code = getSystemErrorName(errno);
|
|
29
|
+
throw Object.assign(new Error(`${code}: realpath '${input}'`), {
|
|
30
|
+
code, errno, syscall: "realpath", path: input,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (getFsSafeNativeConfig().mode === "require") {
|
|
34
|
+
throw new FsSafeError("helper-unavailable", "native fs-safe canonicalization is unavailable");
|
|
35
|
+
}
|
|
36
|
+
// The workaround belongs to the same optional addon, so "off" disables it
|
|
37
|
+
// too. With off or a missing addon in auto, Bun's upstream limits still apply.
|
|
38
|
+
}
|
|
39
|
+
return native ? fs.realpathSync.native(input) : fs.realpathSync(input);
|
|
40
|
+
}
|
|
41
|
+
export const realpathSync = Object.assign((input) => resolve(input, false), {
|
|
42
|
+
native: (input) => resolve(input, true),
|
|
43
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recursive-mkdir-path.d.ts","sourceRoot":"","sources":["../src/recursive-mkdir-path.ts"],"names":[],"mappings":"AAKA,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG5D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { platform } from "node:os";
|
|
2
|
+
import { absolutePathWithRawSegments } from "./root-path-existing.js";
|
|
3
|
+
const needsAbsolutePath = Boolean(process.versions.bun) && platform() === "win32";
|
|
4
|
+
/** Bun's Windows recursive mkdir cannot inspect existing relative dot components. */
|
|
5
|
+
export function recursiveMkdirPath(directory) {
|
|
6
|
+
// Preserve junction/parent traversal; path.resolve would erase those components.
|
|
7
|
+
return needsAbsolutePath ? absolutePathWithRawSegments(directory) : directory;
|
|
8
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-directory.d.ts","sourceRoot":"","sources":["../src/replace-directory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"replace-directory.d.ts","sourceRoot":"","sources":["../src/replace-directory.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { guardedRename, guardedRm } from "./guarded-mutation.js";
|
|
5
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
5
6
|
import { assertSafePathPrefix } from "./safe-path-segment.js";
|
|
6
7
|
import { serializePathWrite } from "./write-queue.js";
|
|
7
8
|
export async function replaceDirectoryAtomic(options) {
|
|
@@ -16,7 +17,7 @@ async function replaceDirectoryAtomicUnserialized(options, targetDir) {
|
|
|
16
17
|
const backupPrefix = assertSafePathPrefix(options.backupPrefix ?? ".fs-safe-dir-backup-", { label: "atomic directory backup prefix" });
|
|
17
18
|
const backupDir = path.join(parentDir, `${backupPrefix}${process.pid}-${randomUUID()}`);
|
|
18
19
|
let backupCreated = false;
|
|
19
|
-
await fs.mkdir(parentDir, { recursive: true });
|
|
20
|
+
await fs.mkdir(recursiveMkdirPath(parentDir), { recursive: true });
|
|
20
21
|
try {
|
|
21
22
|
await guardedRename({ from: targetDir, to: backupDir });
|
|
22
23
|
backupCreated = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAQ/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;AAyFF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,IAAI,CAwBN;AA+HD,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DhB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAkEP"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import syncFs, {} from "node:fs";
|
|
2
2
|
import fs, {} from "node:fs/promises";
|
|
3
|
+
import { readBoundedAsync, readBoundedSync } from "./bounded-read.js";
|
|
3
4
|
import { FsSafeError } from "./errors.js";
|
|
4
5
|
import { sameFileIdentity } from "./file-identity.js";
|
|
5
6
|
import { readOwnedCopySource, readOwnedCopySourceSync } from "./replace-file-copy-source.js";
|
|
@@ -9,7 +10,6 @@ const NOFOLLOW = SUPPORTS_NOFOLLOW ? syncFs.constants.O_NOFOLLOW : 0;
|
|
|
9
10
|
const OPEN_READ_FLAGS = resolveReadOpenFlags();
|
|
10
11
|
const OPEN_READ_WRITE_FLAGS = syncFs.constants.O_RDWR | NOFOLLOW;
|
|
11
12
|
const OPEN_WRITE_EXCLUSIVE_FLAGS = syncFs.constants.O_WRONLY | syncFs.constants.O_CREAT | syncFs.constants.O_EXCL | NOFOLLOW;
|
|
12
|
-
const READ_CHUNK_BYTES = 64 * 1024;
|
|
13
13
|
function notFound(error) {
|
|
14
14
|
return error.code === "ENOENT";
|
|
15
15
|
}
|
|
@@ -136,37 +136,27 @@ export function assertDestinationHardlinkPolicySync(fsModule, dest, policy) {
|
|
|
136
136
|
fsModule.closeSync(fd);
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
function restoreReadOptions(stat, maxBytes) {
|
|
140
|
+
return {
|
|
141
|
+
initialSize: Number.isSafeInteger(stat.size) && stat.size >= 0 ? stat.size : undefined,
|
|
142
|
+
createLimitError: () => new FsSafeError("too-large", `Atomic replace restore snapshot exceeds maxRestoreBytes (${maxBytes})`),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
async function readRestoreSnapshot(handle, maxBytes, stat) {
|
|
141
146
|
let position = 0;
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
145
|
-
if (bytesRead === 0)
|
|
146
|
-
break;
|
|
147
|
+
return await readBoundedAsync(maxBytes, async (buffer, length) => {
|
|
148
|
+
const { bytesRead } = await handle.read(buffer, 0, length, position);
|
|
147
149
|
position += bytesRead;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
chunks.push(buffer.subarray(0, bytesRead));
|
|
152
|
-
}
|
|
153
|
-
return Buffer.concat(chunks, position);
|
|
150
|
+
return bytesRead;
|
|
151
|
+
}, restoreReadOptions(stat, maxBytes));
|
|
154
152
|
}
|
|
155
|
-
function
|
|
156
|
-
const chunks = [];
|
|
153
|
+
function readRestoreSnapshotSync(fsModule, fd, maxBytes, stat) {
|
|
157
154
|
let position = 0;
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const bytesRead = fsModule.readSync(fd, buffer, 0, buffer.length, position);
|
|
161
|
-
if (bytesRead === 0)
|
|
162
|
-
break;
|
|
155
|
+
return readBoundedSync(maxBytes, (buffer, length) => {
|
|
156
|
+
const bytesRead = fsModule.readSync(fd, buffer, 0, length, position);
|
|
163
157
|
position += bytesRead;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
chunks.push(buffer.subarray(0, bytesRead));
|
|
168
|
-
}
|
|
169
|
-
return Buffer.concat(chunks, position);
|
|
158
|
+
return bytesRead;
|
|
159
|
+
}, restoreReadOptions(stat, maxBytes));
|
|
170
160
|
}
|
|
171
161
|
async function writeAll(handle, data) {
|
|
172
162
|
await handle.truncate(0);
|
|
@@ -201,8 +191,9 @@ function restoreFailure(writeError, cleanup, restoreError) {
|
|
|
201
191
|
: `Atomic copy fallback failed and original restoration failed: ${primary.message}`, { cause, details });
|
|
202
192
|
}
|
|
203
193
|
async function replacePinnedWithRestore(fsModule, handle, replacement, maxRestoreBytes, replacementMode) {
|
|
204
|
-
const
|
|
205
|
-
const
|
|
194
|
+
const originalStat = fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
|
|
195
|
+
const originalMode = originalStat.mode;
|
|
196
|
+
const original = await readRestoreSnapshot(handle, maxRestoreBytes, originalStat);
|
|
206
197
|
try {
|
|
207
198
|
await writeAll(handle, replacement);
|
|
208
199
|
await handle.chmod(replacementMode);
|
|
@@ -224,8 +215,9 @@ async function replacePinnedWithRestore(fsModule, handle, replacement, maxRestor
|
|
|
224
215
|
}
|
|
225
216
|
}
|
|
226
217
|
function replacePinnedWithRestoreSync(fsModule, fd, replacement, maxRestoreBytes, replacementMode, fchmodSync) {
|
|
227
|
-
const
|
|
228
|
-
const
|
|
218
|
+
const originalStat = fsModule.fstatSync(fd);
|
|
219
|
+
const originalMode = originalStat.mode;
|
|
220
|
+
const original = readRestoreSnapshotSync(fsModule, fd, maxRestoreBytes, originalStat);
|
|
229
221
|
try {
|
|
230
222
|
writeAllSync(fsModule, fd, replacement);
|
|
231
223
|
fchmodSync?.(fd, replacementMode);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-copy-source.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-source.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-file-copy-source.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-source.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAQnD,KAAK,qBAAqB,GAAG,IAAI,CAAC,cAAc,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AACvF,KAAK,oBAAoB,GAAG,IAAI,CAC9B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAClE,CAAC;AAqDF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,qBAAqB,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BjD;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA8BxC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import syncFs, {} from "node:fs";
|
|
2
2
|
import fs, {} from "node:fs/promises";
|
|
3
|
+
import { readBoundedSync } from "./bounded-read.js";
|
|
3
4
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
4
5
|
import { FsSafeError } from "./errors.js";
|
|
5
6
|
import { sameFileIdentity } from "./file-identity.js";
|
|
6
7
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
7
8
|
const OPEN_READ_FLAGS = resolveReadOpenFlags();
|
|
8
|
-
const READ_CHUNK_BYTES = 64 * 1024;
|
|
9
9
|
function assertSourcePreview(source, src) {
|
|
10
10
|
if (source.isSymbolicLink()) {
|
|
11
11
|
throw new FsSafeError("symlink", `Refusing copy fallback from non-file source: ${src}`);
|
|
@@ -44,18 +44,13 @@ function openSourceSync(fsModule, src) {
|
|
|
44
44
|
throw error;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
function readAllSync(fsModule, fd) {
|
|
48
|
-
const chunks = [];
|
|
47
|
+
function readAllSync(fsModule, fd, size) {
|
|
49
48
|
let position = 0;
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
const bytesRead = fsModule.readSync(fd, buffer, 0, buffer.length, position);
|
|
53
|
-
if (bytesRead === 0)
|
|
54
|
-
break;
|
|
49
|
+
return readBoundedSync(Infinity, (buffer, length) => {
|
|
50
|
+
const bytesRead = fsModule.readSync(fd, buffer, 0, length, position);
|
|
55
51
|
position += bytesRead;
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
return Buffer.concat(chunks, position);
|
|
52
|
+
return bytesRead;
|
|
53
|
+
}, { initialSize: Number.isSafeInteger(size) && size >= 0 ? size : undefined });
|
|
59
54
|
}
|
|
60
55
|
export async function readOwnedCopySource(params) {
|
|
61
56
|
assertSourcePreview(params.fsModule === fs
|
|
@@ -99,7 +94,7 @@ export function readOwnedCopySourceSync(params) {
|
|
|
99
94
|
if (opened.nlink !== 1) {
|
|
100
95
|
throw new FsSafeError("hardlink", `Hardlinked copy fallback source not allowed: ${params.src}`);
|
|
101
96
|
}
|
|
102
|
-
return { replacement: readAllSync(params.fsModule, fd), mode: opened.mode };
|
|
97
|
+
return { replacement: readAllSync(params.fsModule, fd, opened.size), mode: opened.mode };
|
|
103
98
|
}
|
|
104
99
|
finally {
|
|
105
100
|
try {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replace-file-mode.d.ts","sourceRoot":"","sources":["../src/replace-file-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAQ5D"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
export function inheritedRegularFileMode(stat) {
|
|
3
|
+
if (stat.isSymbolicLink()) {
|
|
4
|
+
throw new FsSafeError("symlink", "atomic replace destination must not be a symbolic link");
|
|
5
|
+
}
|
|
6
|
+
if (!stat.isFile()) {
|
|
7
|
+
throw new FsSafeError("not-file", "atomic replace destination must be a regular file");
|
|
8
|
+
}
|
|
9
|
+
return stat.mode & 0o777;
|
|
10
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAgEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,
|
|
1
|
+
{"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAgEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAkDN;IAED,WAAW,IAAI,IAAI,CAGlB;IAED,MAAM,CAAC,MAAM,EAAE;QACb,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,IAAI,CAkCP;CACF"}
|
|
@@ -319,7 +319,9 @@ export class SyncAtomicTempOwner {
|
|
|
319
319
|
if (sha256Hex(fsModule.readFileSync(publishedFd)) !== expectedHash) {
|
|
320
320
|
throw new FsSafeError("path-mismatch", `Atomic replace published content changed: ${pathname}`);
|
|
321
321
|
}
|
|
322
|
-
|
|
322
|
+
const previousFd = this.#fd;
|
|
323
|
+
this.#fd = undefined;
|
|
324
|
+
fsModule.closeSync(previousFd);
|
|
323
325
|
this.#fd = publishedFd;
|
|
324
326
|
this.#identity = identity;
|
|
325
327
|
publishedFd = undefined;
|
|
@@ -359,8 +361,10 @@ export class SyncAtomicTempOwner {
|
|
|
359
361
|
if (cleanupComplete)
|
|
360
362
|
this.#unregister();
|
|
361
363
|
if (this.#fd !== undefined) {
|
|
364
|
+
const fd = this.#fd;
|
|
365
|
+
this.#fd = undefined;
|
|
362
366
|
try {
|
|
363
|
-
params.fsModule.closeSync(
|
|
367
|
+
params.fsModule.closeSync(fd);
|
|
364
368
|
}
|
|
365
369
|
catch (closeError) {
|
|
366
370
|
deferredError = deferredError
|
package/dist/replace-file.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ type ReplaceFileAtomicBaseOptions = {
|
|
|
19
19
|
content: string | Uint8Array;
|
|
20
20
|
dirMode?: number;
|
|
21
21
|
mode?: number;
|
|
22
|
+
/** Inherit only rwx bits from an existing non-symlink regular file. */
|
|
22
23
|
preserveExistingMode?: boolean;
|
|
23
24
|
tempPrefix?: string;
|
|
24
25
|
renameMaxRetries?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-file.d.ts","sourceRoot":"","sources":["../src/replace-file.ts"],"names":[],"mappings":"AACA,OAAO,MAAwC,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-file.d.ts","sourceRoot":"","sources":["../src/replace-file.ts"],"names":[],"mappings":"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"}
|
package/dist/replace-file.js
CHANGED
|
@@ -2,8 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import syncFs, {} from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
5
6
|
import { assertDestinationHardlinkPolicy, assertDestinationHardlinkPolicySync, copyFallbackReplace, copyFallbackReplaceSync, } from "./replace-file-copy-fallback.js";
|
|
6
7
|
import { applyDirectoryMode, applyDirectoryModeSync, syncDirectoryBestEffort, syncDirectoryBestEffortSync, writeTempFile, writeTempFileSync, } from "./replace-file-descriptor.js";
|
|
8
|
+
import { inheritedRegularFileMode } from "./replace-file-mode.js";
|
|
7
9
|
import { atomicExpectedContentHash, validateRenameIdentity, withAtomicRenameIdentityLock, withAtomicRenameIdentityLockSync, } from "./replace-file-rename-policy.js";
|
|
8
10
|
import { AsyncAtomicTempOwner, SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
|
|
9
11
|
import { assertSafePathPrefix } from "./safe-path-segment.js";
|
|
@@ -107,7 +109,7 @@ async function resolveMode(options) {
|
|
|
107
109
|
const fsModule = options.fileSystem?.promises ?? fs;
|
|
108
110
|
let stat;
|
|
109
111
|
try {
|
|
110
|
-
stat = fsModule === fs ? syncFs.
|
|
112
|
+
stat = fsModule === fs ? syncFs.lstatSync(options.filePath) : await fsModule.lstat(options.filePath);
|
|
111
113
|
}
|
|
112
114
|
catch (error) {
|
|
113
115
|
if (error.code === "ENOENT") {
|
|
@@ -115,7 +117,7 @@ async function resolveMode(options) {
|
|
|
115
117
|
}
|
|
116
118
|
throw error;
|
|
117
119
|
}
|
|
118
|
-
return stat ? stat
|
|
120
|
+
return stat ? inheritedRegularFileMode(stat) : defaultMode;
|
|
119
121
|
}
|
|
120
122
|
function resolveModeSync(options) {
|
|
121
123
|
const defaultMode = options.mode ?? 0o600;
|
|
@@ -125,14 +127,14 @@ function resolveModeSync(options) {
|
|
|
125
127
|
const fsModule = options.fileSystem ?? syncFs;
|
|
126
128
|
let stat;
|
|
127
129
|
try {
|
|
128
|
-
stat = fsModule.
|
|
130
|
+
stat = fsModule.lstatSync(options.filePath);
|
|
129
131
|
}
|
|
130
132
|
catch (error) {
|
|
131
133
|
if (error.code !== "ENOENT") {
|
|
132
134
|
throw error;
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
|
-
return stat ? stat
|
|
137
|
+
return stat ? inheritedRegularFileMode(stat) : defaultMode;
|
|
136
138
|
}
|
|
137
139
|
function missingFchmodSyncError() {
|
|
138
140
|
return new TypeError("fileSystem.fchmodSync is required when mode, dirMode, or preserveExistingMode is specified");
|
|
@@ -150,7 +152,9 @@ export async function replaceFileAtomicWithDirectorySync(options, syncParent) {
|
|
|
150
152
|
if (options.renameIdentity !== "verify-content-with-lock") {
|
|
151
153
|
return await replaceFileAtomicUnserialized(options, syncParent);
|
|
152
154
|
}
|
|
153
|
-
|
|
155
|
+
const fsModule = options.fileSystem?.promises ?? fs;
|
|
156
|
+
const dir = path.dirname(filePath);
|
|
157
|
+
await fsModule.mkdir(fsModule === fs ? recursiveMkdirPath(dir) : dir, {
|
|
154
158
|
recursive: true,
|
|
155
159
|
mode: options.dirMode ?? 0o700,
|
|
156
160
|
});
|
|
@@ -168,7 +172,7 @@ async function replaceFileAtomicUnserialized(options, syncParent) {
|
|
|
168
172
|
const tempOwner = new AsyncAtomicTempOwner(tempPath);
|
|
169
173
|
let originalError;
|
|
170
174
|
try {
|
|
171
|
-
await fsModule.mkdir(dir, { recursive: true, mode: dirMode });
|
|
175
|
+
await fsModule.mkdir(fsModule === fs ? recursiveMkdirPath(dir) : dir, { recursive: true, mode: dirMode });
|
|
172
176
|
await applyDirectoryMode({ fsModule, dirPath: dir, mode: dirMode });
|
|
173
177
|
tempOwner.start();
|
|
174
178
|
tempOwner.adopt(await writeTempFile({
|
|
@@ -243,7 +247,9 @@ export function replaceFileAtomicSync(options) {
|
|
|
243
247
|
if (options.renameIdentity !== "verify-content-with-lock") {
|
|
244
248
|
return replaceFileAtomicSyncUnserialized(options);
|
|
245
249
|
}
|
|
246
|
-
|
|
250
|
+
const fsModule = options.fileSystem ?? syncFs;
|
|
251
|
+
const dir = path.dirname(filePath);
|
|
252
|
+
fsModule.mkdirSync(fsModule === syncFs ? recursiveMkdirPath(dir) : dir, {
|
|
247
253
|
recursive: true,
|
|
248
254
|
mode: options.dirMode ?? 0o700,
|
|
249
255
|
});
|
|
@@ -267,7 +273,7 @@ function replaceFileAtomicSyncUnserialized(options) {
|
|
|
267
273
|
const tempOwner = new SyncAtomicTempOwner(tempPath);
|
|
268
274
|
let originalError;
|
|
269
275
|
try {
|
|
270
|
-
fsModule.mkdirSync(dir, { recursive: true, mode: dirMode });
|
|
276
|
+
fsModule.mkdirSync(fsModule === syncFs ? recursiveMkdirPath(dir) : dir, { recursive: true, mode: dirMode });
|
|
271
277
|
applyDirectoryModeSync({ fsModule, dirPath: dir, mode: dirMode, fchmodSync });
|
|
272
278
|
tempOwner.start();
|
|
273
279
|
tempOwner.adopt(writeTempFileSync({
|
package/dist/root-context.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare function resolvePathInRoot(root: RootContext, relativePath: strin
|
|
|
22
22
|
allowFinalSymlink?: boolean;
|
|
23
23
|
rejectUnsafeDeviceReads?: boolean;
|
|
24
24
|
rejectSymlinks?: boolean;
|
|
25
|
+
rejectFinalSymlink?: boolean;
|
|
26
|
+
resolveCanonical?: boolean;
|
|
27
|
+
rejectAmbiguousParents?: boolean;
|
|
25
28
|
}): Promise<{
|
|
26
29
|
rootReal: string;
|
|
27
30
|
rootWithSep: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-context.d.ts","sourceRoot":"","sources":["../src/root-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-context.d.ts","sourceRoot":"","sources":["../src/root-context.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAAW,MAAM,WACM,CAAC;AAEtD,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAEtE;AAED,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAKzE;AAMD,wBAAsB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAetF;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4B9E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBhF;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhF;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE;IACR,cAAc,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAAC;IACpD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDtE;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAEvE"}
|
package/dist/root-context.js
CHANGED
|
@@ -4,11 +4,12 @@ import path from "node:path";
|
|
|
4
4
|
import { inspectDirectoryIdentity } from "./directory-guard.js";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { sameFileIdentity } from "./file-identity.js";
|
|
7
|
-
import { expandHomePrefix } from "./home-dir.js";
|
|
8
7
|
import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, } from "./path.js";
|
|
9
8
|
import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath } from "./root-path.js";
|
|
10
|
-
import { outsideWorkspaceError } from "./root-errors.js";
|
|
9
|
+
import { outsideWorkspaceError, rootPathChangedError } from "./root-errors.js";
|
|
11
10
|
import { isDriveRelativePath } from "./safe-path-segment.js";
|
|
11
|
+
import { realpathSync } from "./realpath.js";
|
|
12
|
+
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
12
13
|
export const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
|
|
13
14
|
export function assertValidRootRelativePath(relativePath) {
|
|
14
15
|
assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
|
|
@@ -20,30 +21,39 @@ export function assertValidRootDestinationPath(relativePath) {
|
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
let cachedHomePath;
|
|
24
|
+
const POSIX_PARENT_COMPONENT = /(?:^|\/)\.\.(?:\/|$)/;
|
|
25
|
+
const WINDOWS_PARENT_COMPONENT = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
|
|
23
26
|
export async function expandRelativePathWithHome(relativePath) {
|
|
27
|
+
const homeOnly = relativePath === "~";
|
|
28
|
+
const homePrefix = relativePath.startsWith("~/") || (path.sep === "\\" && relativePath.startsWith("~\\"));
|
|
29
|
+
if (!homeOnly && !homePrefix)
|
|
30
|
+
return relativePath;
|
|
24
31
|
const rawHome = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
25
32
|
if (cachedHomePath?.raw !== rawHome) {
|
|
26
33
|
let realHome = rawHome;
|
|
27
34
|
try {
|
|
28
|
-
realHome =
|
|
35
|
+
realHome = realpathSync.native(rawHome);
|
|
29
36
|
}
|
|
30
37
|
catch {
|
|
31
38
|
// If the home dir cannot be canonicalized, keep lexical expansion behavior.
|
|
32
39
|
}
|
|
33
40
|
cachedHomePath = { raw: rawHome, real: realHome };
|
|
34
41
|
}
|
|
35
|
-
return
|
|
42
|
+
return homeOnly ? cachedHomePath.real : `${ensureTrailingSep(cachedHomePath.real)}${relativePath.slice(2)}`;
|
|
36
43
|
}
|
|
37
44
|
export async function resolveRootContext(rootDir) {
|
|
38
45
|
assertNoNulPathInput(rootDir, "root dir contains a NUL byte");
|
|
46
|
+
const lexicalRoot = path.resolve(rootDir);
|
|
39
47
|
let rootReal;
|
|
40
48
|
let rootIdentity;
|
|
41
49
|
try {
|
|
42
|
-
rootReal =
|
|
43
|
-
const rootStat =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
rootReal = realpathSync.native(rootDir);
|
|
51
|
+
const rootStat = await inspectFileIdentity(() => {
|
|
52
|
+
const stat = fs.statSync(rootReal, { bigint: true });
|
|
53
|
+
if (!stat.isDirectory())
|
|
54
|
+
throw new FsSafeError("invalid-path", "root dir is not a directory");
|
|
55
|
+
return stat;
|
|
56
|
+
});
|
|
47
57
|
rootIdentity = { dev: rootStat.dev, ino: rootStat.ino };
|
|
48
58
|
}
|
|
49
59
|
catch (err) {
|
|
@@ -56,7 +66,7 @@ export async function resolveRootContext(rootDir) {
|
|
|
56
66
|
throw err;
|
|
57
67
|
}
|
|
58
68
|
return {
|
|
59
|
-
rootDir:
|
|
69
|
+
rootDir: lexicalRoot,
|
|
60
70
|
rootIdentity,
|
|
61
71
|
rootReal,
|
|
62
72
|
rootWithSep: ensureTrailingSep(rootReal),
|
|
@@ -64,17 +74,22 @@ export async function resolveRootContext(rootDir) {
|
|
|
64
74
|
}
|
|
65
75
|
export function rootRelativeReadPath(root, filePath) {
|
|
66
76
|
const absoluteInput = path.isAbsolute(filePath);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
if (!absoluteInput)
|
|
78
|
+
return filePath;
|
|
79
|
+
const raw = process.platform === "win32" ? filePath.replaceAll("/", path.sep) : filePath;
|
|
80
|
+
for (const base of [root.rootDir, root.rootReal]) {
|
|
81
|
+
const prefix = ensureTrailingSep(base);
|
|
82
|
+
const matches = process.platform === "win32"
|
|
83
|
+
? raw.slice(0, prefix.length).toLowerCase() === prefix.toLowerCase()
|
|
84
|
+
: raw.startsWith(prefix);
|
|
85
|
+
if (matches) {
|
|
86
|
+
let start = prefix.length;
|
|
87
|
+
while (raw[start] === path.sep)
|
|
88
|
+
start += 1;
|
|
89
|
+
return raw.slice(start);
|
|
90
|
+
}
|
|
76
91
|
}
|
|
77
|
-
return
|
|
92
|
+
return raw;
|
|
78
93
|
}
|
|
79
94
|
export async function assertRootIdentityCurrent(root) {
|
|
80
95
|
let current;
|
|
@@ -86,22 +101,20 @@ export async function assertRootIdentityCurrent(root) {
|
|
|
86
101
|
current = fs.lstatSync(root.rootReal);
|
|
87
102
|
}
|
|
88
103
|
catch (error) {
|
|
89
|
-
throw
|
|
90
|
-
cause: error instanceof Error ? error : undefined,
|
|
91
|
-
});
|
|
104
|
+
throw rootPathChangedError(error instanceof Error ? error : undefined);
|
|
92
105
|
}
|
|
93
106
|
if (current.isSymbolicLink() ||
|
|
94
107
|
!current.isDirectory() ||
|
|
95
108
|
!sameFileIdentity(current, root.rootIdentity)) {
|
|
96
|
-
throw
|
|
109
|
+
throw rootPathChangedError();
|
|
97
110
|
}
|
|
98
111
|
}
|
|
99
112
|
export async function resolvePathInRoot(root, relativePath, options) {
|
|
100
113
|
assertValidRootRelativePath(relativePath);
|
|
101
114
|
await assertRootIdentityCurrent(root);
|
|
102
115
|
const expanded = await expandRelativePathWithHome(relativePath);
|
|
103
|
-
|
|
104
|
-
if (!isPathInside(root.rootWithSep, resolved)) {
|
|
116
|
+
let resolved = path.resolve(root.rootWithSep, expanded);
|
|
117
|
+
if (!options?.resolveCanonical && !isPathInside(root.rootWithSep, resolved)) {
|
|
105
118
|
throw outsideWorkspaceError();
|
|
106
119
|
}
|
|
107
120
|
if (options?.rejectUnsafeDeviceReads === true) {
|
|
@@ -111,14 +124,26 @@ export async function resolvePathInRoot(root, relativePath, options) {
|
|
|
111
124
|
? expanded
|
|
112
125
|
: `${root.rootWithSep}${expanded}`;
|
|
113
126
|
try {
|
|
114
|
-
|
|
127
|
+
const resolution = {
|
|
115
128
|
absolutePath: rawAbsolutePath,
|
|
116
129
|
rootPath: root.rootReal,
|
|
117
130
|
rootCanonicalPath: root.rootReal,
|
|
118
131
|
boundaryLabel: "root",
|
|
119
132
|
policy: options?.allowFinalSymlink ? ROOT_PATH_ALIAS_POLICIES.unlinkTarget : undefined,
|
|
120
133
|
rejectSymlinks: options?.rejectSymlinks,
|
|
121
|
-
|
|
134
|
+
rejectFinalSymlink: options?.rejectFinalSymlink,
|
|
135
|
+
};
|
|
136
|
+
const checked = await resolveRootPath(resolution);
|
|
137
|
+
const parentComponent = process.platform === "win32" ? WINDOWS_PARENT_COMPONENT : POSIX_PARENT_COMPONENT;
|
|
138
|
+
if (options?.rejectAmbiguousParents && parentComponent.test(expanded) &&
|
|
139
|
+
path.relative(checked.canonicalPath, resolved) !== "") {
|
|
140
|
+
const normalized = await resolveRootPath({ ...resolution, absolutePath: resolved });
|
|
141
|
+
if (path.relative(checked.canonicalPath, normalized.canonicalPath) !== "") {
|
|
142
|
+
throw new FsSafeError("path-alias", "parent traversal resolves differently through a symlink");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (options?.resolveCanonical)
|
|
146
|
+
resolved = checked.canonicalPath;
|
|
122
147
|
}
|
|
123
148
|
catch (error) {
|
|
124
149
|
if (error instanceof FsSafeError && error.code === "symlink") {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PinnedWriteInput } from "./pinned-write.js";
|
|
2
|
+
import type { RootCreateOptions, RootCreateStreamOptions, RootWriteOptions } from "./root-options.js";
|
|
3
|
+
export type RootWriteParams = RootWriteOptions & RootCreateStreamOptions & {
|
|
4
|
+
relativePath: string;
|
|
5
|
+
data: string | Buffer | AsyncIterable<Uint8Array>;
|
|
6
|
+
};
|
|
7
|
+
export declare function rethrowCreateInputError(error: unknown): never;
|
|
8
|
+
export declare function createInputOptions(data: RootWriteParams["data"], options: RootCreateOptions & RootCreateStreamOptions, defaultMaxBytes?: number): RootCreateOptions & RootCreateStreamOptions;
|
|
9
|
+
export declare function rootWriteInput(params: RootWriteParams): PinnedWriteInput;
|
|
10
|
+
//# sourceMappingURL=root-create-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-create-input.d.ts","sourceRoot":"","sources":["../src/root-create-input.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEtG,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,uBAAuB,GAAG;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;CACnD,CAAC;AAQF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAG7D;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,OAAO,EAAE,iBAAiB,GAAG,uBAAuB,EACpD,eAAe,CAAC,EAAE,MAAM,GACvB,iBAAiB,GAAG,uBAAuB,CAe7C;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAwCxE"}
|