@openclaw/fs-safe 0.7.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/README.md +5 -1
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +9 -10
- package/dist/archive-merge.d.ts +18 -0
- package/dist/archive-merge.d.ts.map +1 -0
- package/dist/archive-merge.js +157 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +25 -5
- package/dist/archive-policy.d.ts +1 -1
- package/dist/archive-policy.d.ts.map +1 -1
- package/dist/archive-policy.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +3 -2
- package/dist/archive-staging.d.ts +13 -8
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +44 -192
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +9 -5
- package/dist/archive-tar-runtime.d.ts +1 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -1
- package/dist/archive-zip-admission.d.ts +2 -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 +8 -1
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +6 -2
- package/dist/archive-zip-entry.d.ts +1 -1
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-names.d.ts +1 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +1 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +17 -25
- package/dist/directory-guard.d.ts +17 -5
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +8 -6
- package/dist/directory-mode-node.d.ts +8 -0
- package/dist/directory-mode-node.d.ts.map +1 -0
- package/dist/directory-mode-node.js +93 -0
- package/dist/directory-mode-owner.d.ts +21 -0
- package/dist/directory-mode-owner.d.ts.map +1 -0
- package/dist/directory-mode-owner.js +73 -0
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -0
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +13 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +8 -11
- package/dist/filename.d.ts +6 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +36 -0
- package/dist/guarded-mkdir.d.ts +2 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +2 -2
- package/dist/guarded-mutation.d.ts +2 -2
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/json-document-store.d.ts +3 -0
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +2 -0
- package/dist/json-durable-queue-ownership.d.ts +3 -1
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +7 -4
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +25 -28
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +18 -56
- package/dist/move-path-cleanup.d.ts +43 -0
- package/dist/move-path-cleanup.d.ts.map +1 -0
- package/dist/move-path-cleanup.js +159 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +30 -84
- package/dist/native-operations.d.ts +2 -4
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +7 -9
- package/dist/native-pinned-write-windows.d.ts +2 -2
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +5 -5
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +14 -4
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/opened-file-failure.d.ts +1 -0
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +4 -0
- package/dist/output-sibling.d.ts.map +1 -1
- package/dist/output-sibling.js +9 -3
- package/dist/pinned-write.d.ts +2 -2
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +32 -13
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +7 -3
- package/dist/regular-file.d.ts +1 -1
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +35 -6
- package/dist/replace-file-descriptor.d.ts +9 -1
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +58 -10
- package/dist/replace-file-temp-owner.d.ts +7 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +29 -0
- package/dist/replace-file.d.ts +1 -0
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +12 -38
- package/dist/root-context.d.ts +4 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +19 -0
- package/dist/root-impl.d.ts +43 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +13 -11
- package/dist/root-path-symlink.d.ts +6 -2
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +7 -6
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +6 -2
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +4 -0
- package/dist/root-write-verification.d.ts +3 -2
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +6 -0
- package/dist/secret-file.d.ts +9 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +136 -74
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +9 -3
- package/dist/sidecar-lock-acquire.d.ts +1 -0
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +24 -5
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +6 -4
- package/dist/sidecar-lock-types.d.ts +7 -0
- package/dist/sidecar-lock-types.d.ts.map +1 -1
- package/dist/sidecar-lock.d.ts +2 -0
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +44 -3
- package/dist/write-open-flags.d.ts +5 -0
- package/dist/write-open-flags.d.ts.map +1 -0
- package/dist/write-open-flags.js +35 -0
- package/docs/advanced.md +1 -1
- package/docs/archive.md +56 -7
- package/docs/atomic.md +8 -2
- package/docs/contributing.md +13 -0
- package/docs/durability.md +3 -2
- package/docs/errors.md +3 -1
- package/docs/file-store.md +38 -8
- package/docs/local-roots.md +4 -3
- package/docs/output.md +4 -1
- package/docs/private-file-store.md +11 -2
- package/docs/reading.md +3 -1
- package/docs/regular-file.md +16 -2
- package/docs/root.md +4 -2
- package/docs/secret-file.md +37 -2
- package/docs/sidecar-lock.md +33 -5
- package/docs/store.md +5 -1
- package/docs/temp.md +19 -1
- package/docs/writing.md +24 -3
- package/package.json +14 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAUlD,MAAM,MAAM,mBAAmB,GAAG;
|
|
1
|
+
{"version":3,"file":"directory-guard.d.ts","sourceRoot":"","sources":["../src/directory-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAUlD,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,KAAK,GAAG,WAAW,GAAG,KAAK,IAAI;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,mBAAmB,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;AAC7H,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACnH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAStH,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5F;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAMxE;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAQxE;AAED,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACxH,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAqBzJ,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAcpB;AAGD,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAM7H"}
|
package/dist/directory-guard.js
CHANGED
|
@@ -6,15 +6,17 @@ import { sameFileIdentity } from "./file-identity.js";
|
|
|
6
6
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
7
7
|
import { isNotFoundPathError } from "./path.js";
|
|
8
8
|
import { directoryComponentNotDirectoryError } from "./root-errors.js";
|
|
9
|
-
export async function createAsyncDirectoryGuard(dir) {
|
|
10
|
-
const stat = await fs.lstat(dir);
|
|
9
|
+
export async function createAsyncDirectoryGuard(dir, options) {
|
|
10
|
+
const stat = options?.bigint ? await inspectDirectoryIdentity(dir) : await fs.lstat(dir);
|
|
11
11
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
12
12
|
throw directoryComponentNotDirectoryError();
|
|
13
13
|
}
|
|
14
14
|
return { dir, realPath: await fs.realpath(dir), stat };
|
|
15
15
|
}
|
|
16
16
|
export async function assertAsyncDirectoryGuard(guard) {
|
|
17
|
-
const stat =
|
|
17
|
+
const stat = typeof guard.stat.dev === "bigint" && typeof guard.stat.ino === "bigint"
|
|
18
|
+
? await inspectDirectoryIdentity(guard.dir, { dev: guard.stat.dev, ino: guard.stat.ino })
|
|
19
|
+
: await fs.lstat(guard.dir);
|
|
18
20
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
19
21
|
throw directoryComponentNotDirectoryError();
|
|
20
22
|
}
|
|
@@ -38,12 +40,12 @@ export function assertSyncDirectoryGuard(guard) {
|
|
|
38
40
|
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
export async function createNearestExistingDirectoryGuard(rootReal, targetPath) {
|
|
43
|
+
export async function createNearestExistingDirectoryGuard(rootReal, targetPath, options = { bigint: false }) {
|
|
42
44
|
let current = path.resolve(targetPath);
|
|
43
45
|
const root = path.resolve(rootReal);
|
|
44
46
|
while (current !== root) {
|
|
45
47
|
try {
|
|
46
|
-
return await createAsyncDirectoryGuard(current);
|
|
48
|
+
return await createAsyncDirectoryGuard(current, options);
|
|
47
49
|
}
|
|
48
50
|
catch (error) {
|
|
49
51
|
if (!isNotFoundPathError(error)) {
|
|
@@ -52,7 +54,7 @@ export async function createNearestExistingDirectoryGuard(rootReal, targetPath)
|
|
|
52
54
|
current = path.dirname(current);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
|
-
return await createAsyncDirectoryGuard(root);
|
|
57
|
+
return await createAsyncDirectoryGuard(root, options);
|
|
56
58
|
}
|
|
57
59
|
export function createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
|
|
58
60
|
let current = path.resolve(targetPath);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import { type DirectoryModeOwner } from "./directory-mode-owner.js";
|
|
3
|
+
/** Real Node only: injected filesystem adapters must retain descriptor-chmod semantics. */
|
|
4
|
+
export declare function pinNodeDirectoryForMode(dirPath: string, options?: {
|
|
5
|
+
expectedIdentity?: BigIntStats;
|
|
6
|
+
ownerUid?: number;
|
|
7
|
+
}): Promise<DirectoryModeOwner>;
|
|
8
|
+
//# sourceMappingURL=directory-mode-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAKtD,OAAO,EAA0C,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAW5G,2FAA2F;AAC3F,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,OAAO,CAAC,kBAAkB,CAAC,CAsE7B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { inspectDirectoryIdentity } from "./directory-guard.js";
|
|
5
|
+
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
6
|
+
import { assertOwnedDirectory, ownDirectoryMode } from "./directory-mode-owner.js";
|
|
7
|
+
function searchOnlyFlags() {
|
|
8
|
+
if (process.arch !== "x64" && process.arch !== "arm64")
|
|
9
|
+
return undefined;
|
|
10
|
+
// Darwin SDK O_SEARCH = O_EXEC (0x40000000) | O_DIRECTORY, on x64/arm64.
|
|
11
|
+
if (process.platform === "darwin")
|
|
12
|
+
return { flags: 0x40000000, proc: false };
|
|
13
|
+
// Linux x86-64/aarch64 UAPI O_PATH = 010000000. Not a usable fchmod fd.
|
|
14
|
+
if (process.platform === "linux")
|
|
15
|
+
return { flags: 0x200000, proc: true };
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
/** Real Node only: injected filesystem adapters must retain descriptor-chmod semantics. */
|
|
19
|
+
export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
20
|
+
const { ownerUid } = options;
|
|
21
|
+
const assertOwner = (stat) => {
|
|
22
|
+
if (ownerUid !== undefined && stat.uid !== BigInt(ownerUid)) {
|
|
23
|
+
throw new FsSafeError("not-owned", "directory mode target must retain its expected owner");
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const expected = await inspectDirectoryIdentity(dirPath, options.expectedIdentity);
|
|
27
|
+
assertOwner(expected);
|
|
28
|
+
if (process.platform === "win32") {
|
|
29
|
+
// POSIX mode enforcement is unsupported; retain strict path identity checks.
|
|
30
|
+
return ownDirectoryMode({
|
|
31
|
+
inspect: async () => { assertOwner(await inspectDirectoryIdentity(dirPath, expected)); return 0; },
|
|
32
|
+
chmod: async () => undefined, close: async () => undefined, ignoreChmodError: true,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if ([constants.O_DIRECTORY, constants.O_NOFOLLOW, constants.O_NONBLOCK, constants.O_RDONLY]
|
|
36
|
+
.some((flag) => typeof flag !== "number")) {
|
|
37
|
+
throw new FsSafeError("helper-unavailable", "no-follow directory mode descriptors are unavailable");
|
|
38
|
+
}
|
|
39
|
+
const flags = constants.O_DIRECTORY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
40
|
+
let proc = false;
|
|
41
|
+
const handle = await fs.open(dirPath, constants.O_RDONLY | flags).catch(async (error) => {
|
|
42
|
+
if (error.code !== "EACCES")
|
|
43
|
+
throw error;
|
|
44
|
+
const route = searchOnlyFlags();
|
|
45
|
+
if (!route)
|
|
46
|
+
throw error;
|
|
47
|
+
proc = route.proc;
|
|
48
|
+
return await fs.open(dirPath, route.flags | flags);
|
|
49
|
+
});
|
|
50
|
+
try {
|
|
51
|
+
const inspect = async () => {
|
|
52
|
+
const opened = await inspectFileIdentity(() => handle.stat({ bigint: true }), expected);
|
|
53
|
+
assertOwnedDirectory(expected, opened);
|
|
54
|
+
assertOwner(opened);
|
|
55
|
+
await inspectDirectoryIdentity(dirPath, expected);
|
|
56
|
+
return Number(opened.mode & 4095n);
|
|
57
|
+
};
|
|
58
|
+
const procPath = `/proc/self/fd/${handle.fd}`;
|
|
59
|
+
const assertProcAuthority = async () => {
|
|
60
|
+
// Authenticate the fd namespace, not the followed target's filesystem.
|
|
61
|
+
// This trusts host mount-namespace integrity, not privileged mount replacement.
|
|
62
|
+
const namespace = await fs.statfs("/proc/self/fd", { bigint: true });
|
|
63
|
+
if (namespace.type !== 0x9fa0n) {
|
|
64
|
+
throw new FsSafeError("path-mismatch", "directory mode requires a trusted procfs fd namespace");
|
|
65
|
+
}
|
|
66
|
+
const opened = await inspectFileIdentity(() => handle.stat({ bigint: true }), expected);
|
|
67
|
+
const followed = await inspectFileIdentity(() => fs.stat(procPath, { bigint: true }), expected);
|
|
68
|
+
assertOwnedDirectory(opened, followed);
|
|
69
|
+
assertOwner(opened);
|
|
70
|
+
assertOwner(followed);
|
|
71
|
+
};
|
|
72
|
+
const owner = ownDirectoryMode({
|
|
73
|
+
inspect,
|
|
74
|
+
prepareChmod: proc ? assertProcAuthority : undefined,
|
|
75
|
+
verifyChmod: proc ? assertProcAuthority : undefined,
|
|
76
|
+
async chmod(mode) {
|
|
77
|
+
if (proc) {
|
|
78
|
+
await fs.chmod(procPath, mode);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await handle.chmod(mode);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
close: () => handle.close(),
|
|
85
|
+
});
|
|
86
|
+
await owner.verify();
|
|
87
|
+
return owner;
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
await handle.close();
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BigIntStats, Stats } from "node:fs";
|
|
2
|
+
export type DirectoryModeChecks = {
|
|
3
|
+
check?: () => void;
|
|
4
|
+
beforeChmod?: () => Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
export type DirectoryModeOwner = {
|
|
7
|
+
verify(check?: () => void): Promise<void>;
|
|
8
|
+
apply(mode: number, checks?: DirectoryModeChecks): Promise<void>;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
export declare function assertOwnedDirectory(expected: Stats | BigIntStats, actual: Stats | BigIntStats): void;
|
|
12
|
+
/** Serializes use and close: even a queued path-based fd operation retains its descriptor. */
|
|
13
|
+
export declare function ownDirectoryMode(params: {
|
|
14
|
+
inspect: () => Promise<number>;
|
|
15
|
+
chmod: (mode: number) => Promise<void>;
|
|
16
|
+
prepareChmod?: () => Promise<void>;
|
|
17
|
+
verifyChmod?: () => Promise<void>;
|
|
18
|
+
close: () => Promise<void>;
|
|
19
|
+
ignoreChmodError?: boolean;
|
|
20
|
+
}): DirectoryModeOwner;
|
|
21
|
+
//# sourceMappingURL=directory-mode-owner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-mode-owner.d.ts","sourceRoot":"","sources":["../src/directory-mode-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAIlD,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,GAAG,WAAW,EAAE,MAAM,EAAE,KAAK,GAAG,WAAW,GAAG,IAAI,CAOrG;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,kBAAkB,CAoDrB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { FsSafeError } from "./errors.js";
|
|
2
|
+
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
3
|
+
export function assertOwnedDirectory(expected, actual) {
|
|
4
|
+
if (actual.isSymbolicLink() || !actual.isDirectory()) {
|
|
5
|
+
throw new FsSafeError("not-file", "directory mode target must be a real directory");
|
|
6
|
+
}
|
|
7
|
+
if (!sameFileIdentityForCleanup(expected, actual)) {
|
|
8
|
+
throw new FsSafeError("path-mismatch", "directory changed before its mode could be applied");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Serializes use and close: even a queued path-based fd operation retains its descriptor. */
|
|
12
|
+
export function ownDirectoryMode(params) {
|
|
13
|
+
let pending = Promise.resolve();
|
|
14
|
+
let closing;
|
|
15
|
+
const enqueue = (run) => {
|
|
16
|
+
if (closing)
|
|
17
|
+
return Promise.reject(new FsSafeError("path-mismatch", "directory mode owner is closed"));
|
|
18
|
+
const operation = pending.then(run);
|
|
19
|
+
pending = operation.catch(() => undefined);
|
|
20
|
+
return operation;
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
verify: (check) => enqueue(async () => {
|
|
24
|
+
check?.();
|
|
25
|
+
await params.inspect();
|
|
26
|
+
check?.();
|
|
27
|
+
}),
|
|
28
|
+
apply: (mode, checks = {}) => enqueue(async () => {
|
|
29
|
+
checks.check?.();
|
|
30
|
+
const currentMode = await params.inspect();
|
|
31
|
+
checks.check?.();
|
|
32
|
+
if (currentMode !== mode) {
|
|
33
|
+
await params.prepareChmod?.();
|
|
34
|
+
checks.check?.();
|
|
35
|
+
}
|
|
36
|
+
await checks.beforeChmod?.();
|
|
37
|
+
checks.check?.();
|
|
38
|
+
// Hooks/ancestor checks can yield; recheck the original named association.
|
|
39
|
+
await params.inspect();
|
|
40
|
+
checks.check?.();
|
|
41
|
+
let dispatchDeadlineError;
|
|
42
|
+
if (currentMode !== mode) {
|
|
43
|
+
try {
|
|
44
|
+
checks.check?.();
|
|
45
|
+
await params.chmod(mode);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (!params.ignoreChmodError)
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
// Expiry cannot release the fd while post-dispatch verification is pending.
|
|
52
|
+
try {
|
|
53
|
+
checks.check?.();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
dispatchDeadlineError = error;
|
|
57
|
+
}
|
|
58
|
+
await params.verifyChmod?.();
|
|
59
|
+
}
|
|
60
|
+
const finalMode = await params.inspect();
|
|
61
|
+
if (dispatchDeadlineError)
|
|
62
|
+
throw dispatchDeadlineError;
|
|
63
|
+
checks.check?.();
|
|
64
|
+
if (!params.ignoreChmodError && finalMode !== mode) {
|
|
65
|
+
throw new FsSafeError("path-mismatch", "directory final mode could not be verified");
|
|
66
|
+
}
|
|
67
|
+
}),
|
|
68
|
+
close() {
|
|
69
|
+
closing ??= pending.then(params.close);
|
|
70
|
+
return closing;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type FailureKind = "identity" | "resolution" | `open:${string}` | `unlinked:${string}` | `changed:${string}`;
|
|
1
|
+
type FailureKind = "identity" | "resolution" | `open:${string}` | `exclusive-create:${string}` | `unlinked:${string}` | `changed:${string}`;
|
|
2
2
|
export declare function recordFileObservationFailure(error: unknown, kind: FailureKind): void;
|
|
3
3
|
export declare function isFileObservationFailure(error: unknown, kind: FailureKind): boolean;
|
|
4
4
|
export declare function fileObservation(): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-observation.d.ts","sourceRoot":"","sources":["../src/file-observation.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,MAAM,EAAE,GAAG,YAAY,MAAM,EAAE,GAAG,WAAW,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"file-observation.d.ts","sourceRoot":"","sources":["../src/file-observation.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,MAAM,EAAE,GAAG,oBAAoB,MAAM,EAAE,GAAG,YAAY,MAAM,EAAE,GAAG,WAAW,MAAM,EAAE,CAAC;AAG5I,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAMpF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAEnF;AAGD,wBAAgB,eAAe;IAG3B,GAAG,CAAC,CAAC,aAAa,MAAM,CAAC,GAAG,CAAC;IAC7B,GAAG,QAAQ,OAAO,QAAQ,WAAW,GAAG,OAAO;EAElD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Readable } from "node:stream";
|
|
2
2
|
import { type SyncDirectoryGuard } from "./directory-guard.js";
|
|
3
3
|
import { type Root } from "./root.js";
|
|
4
|
+
import { prepareSecretFileWrite } from "./secret-file.js";
|
|
4
5
|
export type SyncParentGuard = SyncDirectoryGuard;
|
|
5
6
|
export declare function ensureParentInRoot(scopedRoot: Root, relativePath: string, mode: number): Promise<void>;
|
|
6
7
|
export declare function openWritableStoreRoot(params: {
|
|
@@ -8,6 +9,7 @@ export declare function openWritableStoreRoot(params: {
|
|
|
8
9
|
dirMode: number;
|
|
9
10
|
maxBytes?: number;
|
|
10
11
|
}): Promise<Root>;
|
|
12
|
+
export declare function openPrivateStoreLockRoot(params: Parameters<typeof prepareSecretFileWrite>[0]): Promise<Root>;
|
|
11
13
|
export declare function writeStreamToTempSource(params: {
|
|
12
14
|
stream: Readable;
|
|
13
15
|
maxBytes?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAgDD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
|
|
1
|
+
{"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAgDD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
|
|
@@ -9,6 +9,9 @@ import { FsSafeError } from "./errors.js";
|
|
|
9
9
|
import { sameFileIdentity } from "./file-identity.js";
|
|
10
10
|
import { isPathInside, isPathRelativeEscape } from "./path.js";
|
|
11
11
|
import { resolveOpenedFileRealPathForHandle, root } from "./root.js";
|
|
12
|
+
import { ensureTrailingSep } from "./root-context.js";
|
|
13
|
+
import { RootHandle } from "./root-impl.js";
|
|
14
|
+
import { prepareSecretFileWrite } from "./secret-file.js";
|
|
12
15
|
import { resolveSecureTempRoot } from "./secure-temp-dir.js";
|
|
13
16
|
function parentRelativePath(relativePath) {
|
|
14
17
|
const parent = path.posix.dirname(relativePath);
|
|
@@ -28,6 +31,16 @@ export async function openWritableStoreRoot(params) {
|
|
|
28
31
|
await fs.chmod(params.rootDir, params.dirMode).catch(() => undefined);
|
|
29
32
|
return await root(params.rootDir, { hardlinks: "reject", maxBytes });
|
|
30
33
|
}
|
|
34
|
+
export async function openPrivateStoreLockRoot(params) {
|
|
35
|
+
const { parentGuard } = await prepareSecretFileWrite(params);
|
|
36
|
+
// Bind to the admitted parent, never resolve a replacement into a fresh capability.
|
|
37
|
+
return new RootHandle({
|
|
38
|
+
rootDir: parentGuard.dir,
|
|
39
|
+
rootReal: parentGuard.realPath,
|
|
40
|
+
rootWithSep: ensureTrailingSep(parentGuard.realPath),
|
|
41
|
+
rootIdentity: { dev: parentGuard.stat.dev, ino: parentGuard.stat.ino },
|
|
42
|
+
}, { hardlinks: "reject" });
|
|
43
|
+
}
|
|
31
44
|
async function chmodDirectoryInRootBestEffort(scopedRoot, relativePath, mode) {
|
|
32
45
|
const dirPath = await scopedRoot.resolve(relativePath);
|
|
33
46
|
const directoryFlag = "O_DIRECTORY" in syncFs.constants ? syncFs.constants.O_DIRECTORY : 0;
|
package/dist/file-store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,OAAO,EAA4B,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC7F,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"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"}
|
package/dist/file-store.js
CHANGED
|
@@ -6,31 +6,25 @@ import { readFileDescriptorBoundedSync } from "./bounded-read.js";
|
|
|
6
6
|
import { FsSafeError } from "./errors.js";
|
|
7
7
|
import { assertFileStoreMaxBytes } from "./file-store-limit.js";
|
|
8
8
|
import { pruneExpiredStoreEntries } from "./file-store-prune.js";
|
|
9
|
-
import { ensureParentInRoot, openWritableStoreRoot, writeStreamToTempSource, } from "./file-store-boundary.js";
|
|
9
|
+
import { ensureParentInRoot, openPrivateStoreLockRoot, openWritableStoreRoot, writeStreamToTempSource, } from "./file-store-boundary.js";
|
|
10
10
|
import { writeFileSyncAtomic } from "./file-store-sync-write.js";
|
|
11
11
|
import { createJsonStore } from "./json-document-store.js";
|
|
12
12
|
import { stringifyJsonDocument } from "./json-stringify.js";
|
|
13
|
-
import { isNotFoundPathError, resolveSafeRelativePath } from "./path.js";
|
|
13
|
+
import { isNotFoundPathError, resolveSafeRelativePath, splitSafeRelativePath } from "./path.js";
|
|
14
14
|
import { throwFsSafeReadError } from "./read-error.js";
|
|
15
15
|
import { root } from "./root.js";
|
|
16
16
|
import { DEFAULT_ROOT_MAX_BYTES } from "./root-impl.js";
|
|
17
17
|
import { readRegularFile } from "./regular-file.js";
|
|
18
18
|
import { matchRootFileOpenFailure, openRootFileSync } from "./root-file.js";
|
|
19
|
-
import { assertNoDriveRelativePathSegments } from "./safe-path-segment.js";
|
|
20
19
|
import { writeSecretFileAtomic } from "./secret-file.js";
|
|
21
20
|
function assertRelativePath(relativePath) {
|
|
22
21
|
const raw = relativePath.trim();
|
|
23
22
|
if (!raw || raw !== relativePath) {
|
|
24
23
|
throw new FsSafeError("invalid-path", "store key must be non-empty and unpadded");
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
path.posix.isAbsolute(raw) || path.win32.isAbsolute(raw) || raw.startsWith("//");
|
|
30
|
-
if (delegated)
|
|
31
|
-
return raw;
|
|
32
|
-
if (segments.every((segment) => segment.length === 0 || segment === ".") ||
|
|
33
|
-
segments.some((segment) => segment.length === 0 || segment === ".") ||
|
|
25
|
+
const segments = splitSafeRelativePath(raw);
|
|
26
|
+
if (segments.length === 0 ||
|
|
27
|
+
segments.join("/") !== raw ||
|
|
34
28
|
raw.normalize("NFC") !== raw ||
|
|
35
29
|
segments.some((segment) => /[ .]$/u.test(segment))) {
|
|
36
30
|
throw new FsSafeError("invalid-path", "store key must use one canonical relative spelling");
|
|
@@ -272,6 +266,9 @@ export function fileStore(options) {
|
|
|
272
266
|
const filePath = resolveStorePath(rootDir, relativePath);
|
|
273
267
|
return createJsonStore({
|
|
274
268
|
filePath,
|
|
269
|
+
...(privateMode ? {
|
|
270
|
+
prepareLock: () => openPrivateStoreLockRoot({ rootDir, filePath, mode, dirMode }),
|
|
271
|
+
} : {}),
|
|
275
272
|
readIfExists: async () => {
|
|
276
273
|
try {
|
|
277
274
|
return await (await openRoot()).readJson(assertRelativePath(relativePath));
|
package/dist/filename.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/** Keeps short names exact and trims only the filename tail of a composite temp name. */
|
|
2
|
+
export declare function fitFileNameToPortableComponent(params: {
|
|
3
|
+
prefix: string;
|
|
4
|
+
fileName: string;
|
|
5
|
+
suffix: string;
|
|
6
|
+
}): string;
|
|
1
7
|
export declare function sanitizeUntrustedFileName(fileName: string, fallbackName: string): string;
|
|
2
8
|
//# sourceMappingURL=filename.d.ts.map
|
package/dist/filename.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../src/filename.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../src/filename.ts"],"names":[],"mappings":"AAqCA,yFAAyF;AACzF,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CA8BT;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAgCxF"}
|
package/dist/filename.js
CHANGED
|
@@ -22,6 +22,42 @@ function suffixWindowsReservedDeviceName(fileName) {
|
|
|
22
22
|
}
|
|
23
23
|
return `${baseName}_${fileName.slice(baseNameEnd)}`;
|
|
24
24
|
}
|
|
25
|
+
const PORTABLE_FILE_NAME_BYTES = 255;
|
|
26
|
+
function normalizedFileNameBytes(value) {
|
|
27
|
+
return Math.max(Buffer.byteLength(value.normalize("NFC"), "utf8"), Buffer.byteLength(value.normalize("NFD"), "utf8"));
|
|
28
|
+
}
|
|
29
|
+
/** Keeps short names exact and trims only the filename tail of a composite temp name. */
|
|
30
|
+
export function fitFileNameToPortableComponent(params) {
|
|
31
|
+
const complete = `${params.prefix}${params.fileName}${params.suffix}`;
|
|
32
|
+
if (normalizedFileNameBytes(complete) <= PORTABLE_FILE_NAME_BYTES) {
|
|
33
|
+
return params.fileName;
|
|
34
|
+
}
|
|
35
|
+
if (normalizedFileNameBytes(`${params.prefix}${params.suffix}`) > PORTABLE_FILE_NAME_BYTES) {
|
|
36
|
+
// Preserve the existing OS error for a caller-supplied prefix that cannot fit.
|
|
37
|
+
return params.fileName;
|
|
38
|
+
}
|
|
39
|
+
const extension = path.extname(params.fileName);
|
|
40
|
+
const preserveExtension = normalizedFileNameBytes(`${params.prefix}${extension}${params.suffix}`) <=
|
|
41
|
+
PORTABLE_FILE_NAME_BYTES;
|
|
42
|
+
const tailSuffix = preserveExtension ? extension : "";
|
|
43
|
+
const stem = preserveExtension
|
|
44
|
+
? params.fileName.slice(0, params.fileName.length - extension.length)
|
|
45
|
+
: params.fileName;
|
|
46
|
+
const codePoints = Array.from(stem);
|
|
47
|
+
let low = 0;
|
|
48
|
+
let high = codePoints.length;
|
|
49
|
+
while (low < high) {
|
|
50
|
+
const length = Math.ceil((low + high) / 2);
|
|
51
|
+
const candidate = `${params.prefix}${codePoints.slice(0, length).join("")}${tailSuffix}${params.suffix}`;
|
|
52
|
+
if (normalizedFileNameBytes(candidate) <= PORTABLE_FILE_NAME_BYTES) {
|
|
53
|
+
low = length;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
high = length - 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return `${codePoints.slice(0, low).join("")}${tailSuffix}`;
|
|
60
|
+
}
|
|
25
61
|
export function sanitizeUntrustedFileName(fileName, fallbackName) {
|
|
26
62
|
const trimmed = typeof fileName === "string" ? fileName.trim() : "";
|
|
27
63
|
if (!trimmed) {
|
package/dist/guarded-mkdir.d.ts
CHANGED
|
@@ -8,5 +8,7 @@ export declare function mkdirPathComponentsWithGuards(params: {
|
|
|
8
8
|
rootReal: string;
|
|
9
9
|
targetPath: string;
|
|
10
10
|
beforeComponent?: (componentPath: string) => Promise<void> | void;
|
|
11
|
+
mode?: number;
|
|
12
|
+
rejectSymlinks?: boolean;
|
|
11
13
|
}): Promise<string>;
|
|
12
14
|
//# sourceMappingURL=guarded-mkdir.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guarded-mkdir.d.ts","sourceRoot":"","sources":["../src/guarded-mkdir.ts"],"names":[],"mappings":"AAyBA;;;;;GAKG;AACH,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"guarded-mkdir.d.ts","sourceRoot":"","sources":["../src/guarded-mkdir.ts"],"names":[],"mappings":"AAyBA;;;;;GAKG;AACH,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAuDlB"}
|
package/dist/guarded-mkdir.js
CHANGED
|
@@ -42,7 +42,7 @@ export async function mkdirPathComponentsWithGuards(params) {
|
|
|
42
42
|
await assertAsyncDirectoryGuard(parentGuard);
|
|
43
43
|
await params.beforeComponent?.(next);
|
|
44
44
|
try {
|
|
45
|
-
await fs.mkdir(next);
|
|
45
|
+
await fs.mkdir(next, { mode: params.mode });
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
48
48
|
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
|
|
@@ -50,7 +50,7 @@ export async function mkdirPathComponentsWithGuards(params) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
const stat = await fs.lstat(next);
|
|
53
|
-
if (!stat.isSymbolicLink() && !stat.isDirectory()) {
|
|
53
|
+
if ((params.rejectSymlinks && stat.isSymbolicLink()) || (!stat.isSymbolicLink() && !stat.isDirectory())) {
|
|
54
54
|
throw directoryComponentNotDirectoryError();
|
|
55
55
|
}
|
|
56
56
|
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export declare function withAsyncDirectoryGuards<T>(guards: readonly
|
|
1
|
+
import { type AnyAsyncDirectoryGuard, type SyncDirectoryGuard } from "./directory-guard.js";
|
|
2
|
+
export declare function withAsyncDirectoryGuards<T>(guards: readonly AnyAsyncDirectoryGuard[], mutate: () => Promise<T>, options?: {
|
|
3
3
|
verifyAfter?: boolean;
|
|
4
4
|
onPostGuardFailure?: (result: T, error: unknown) => Promise<void> | void;
|
|
5
5
|
}): Promise<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guarded-mutation.d.ts","sourceRoot":"","sources":["../src/guarded-mutation.ts"],"names":[],"mappings":"AAGA,OAAO,EAOL,KAAK,
|
|
1
|
+
{"version":3,"file":"guarded-mutation.d.ts","sourceRoot":"","sources":["../src/guarded-mutation.ts"],"names":[],"mappings":"AAGA,OAAO,EAOL,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAE9B,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,MAAM,EAAE,SAAS,sBAAsB,EAAE,EACzC,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACxB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACrE,GACL,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,MAAM,EAAE,MAAM,CAAC,EACf,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,CAAC,CAWH;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAchB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,IAAI,CAUP;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,IAAI,CAWP"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FileLockRetryOptions } from "./file-lock.js";
|
|
2
|
+
import type { Root } from "./root.js";
|
|
2
3
|
import type { SidecarLockStaleRecovery } from "./sidecar-lock.js";
|
|
3
4
|
export type JsonStoreLockOptions = {
|
|
4
5
|
staleMs?: number;
|
|
@@ -22,6 +23,8 @@ export type JsonStore<T> = {
|
|
|
22
23
|
};
|
|
23
24
|
export type JsonStoreAdapter<T> = {
|
|
24
25
|
filePath: string;
|
|
26
|
+
/** Internal admission before lock I/O, within the serialized mutation. */
|
|
27
|
+
prepareLock?: () => Promise<Root>;
|
|
25
28
|
readIfExists(): Promise<T | undefined>;
|
|
26
29
|
readRequired(): Promise<T>;
|
|
27
30
|
write(value: T, options?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-document-store.d.ts","sourceRoot":"","sources":["../src/json-document-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"json-document-store.d.ts","sourceRoot":"","sources":["../src/json-document-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AASlE,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,oBAAoB,CAAC;IAC7B,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,IAAI,IAAI,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC/B,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChC,YAAY,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACxE,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,YAAY,IAAI,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACvC,YAAY,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE,CAAC;AA2BF,wBAAgB,eAAe,CAAC,CAAC,EAC/B,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC5B,OAAO,GAAE,oBAAyB,GACjC,SAAS,CAAC,CAAC,CAAC,CAmFd"}
|
|
@@ -55,7 +55,9 @@ export function createJsonStore(adapter, options = {}) {
|
|
|
55
55
|
if (!locks || !lockOptions) {
|
|
56
56
|
return await run();
|
|
57
57
|
}
|
|
58
|
+
const lockRoot = adapter.prepareLock ? await adapter.prepareLock() : undefined;
|
|
58
59
|
return await locks.withLock({
|
|
60
|
+
...(lockRoot ? { lockRoot } : {}),
|
|
59
61
|
targetPath: adapter.filePath,
|
|
60
62
|
staleMs: lockOptions.staleMs,
|
|
61
63
|
timeoutMs: lockOptions.timeoutMs,
|
|
@@ -4,7 +4,9 @@ export type DurableQueueEntryPathsLike = {
|
|
|
4
4
|
processingPath?: string;
|
|
5
5
|
};
|
|
6
6
|
export declare function durableQueueProcessingPath(paths: DurableQueueEntryPathsLike): string;
|
|
7
|
-
export declare function claimDurableQueueEntry(paths: DurableQueueEntryPathsLike
|
|
7
|
+
export declare function claimDurableQueueEntry(paths: DurableQueueEntryPathsLike, options?: {
|
|
8
|
+
skipUnowned?: boolean;
|
|
9
|
+
}): Promise<string | null>;
|
|
8
10
|
export declare function completeDeliveredQueueEntry(paths: DurableQueueEntryPathsLike): Promise<boolean>;
|
|
9
11
|
export declare function acknowledgeDurableQueueEntry(paths: DurableQueueEntryPathsLike): Promise<void>;
|
|
10
12
|
export declare function moveDurableQueueEntryToFailed(params: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-durable-queue-ownership.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-ownership.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAQF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAKpF;
|
|
1
|
+
{"version":3,"file":"json-durable-queue-ownership.d.ts","sourceRoot":"","sources":["../src/json-durable-queue-ownership.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAQF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,CAKpF;AAwED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,0BAA0B,EACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE;IAC1D,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDhB"}
|
|
@@ -40,7 +40,7 @@ async function regularQueueFileIdentity(filePath) {
|
|
|
40
40
|
async function withQueueEntryLock(paths, run) {
|
|
41
41
|
return await serializePathWrite(paths.jsonPath, async () => await withQueueTransferLock(path.resolve(paths.jsonPath), run));
|
|
42
42
|
}
|
|
43
|
-
async function claimDurableQueueEntryUnlocked(paths) {
|
|
43
|
+
async function claimDurableQueueEntryUnlocked(paths, options = {}) {
|
|
44
44
|
const processingPath = durableQueueProcessingPath(paths);
|
|
45
45
|
await recoverDurableQueueRetirement({ jsonPath: paths.jsonPath, processingPath });
|
|
46
46
|
const existingProcessing = await regularQueueFileIdentity(processingPath);
|
|
@@ -58,6 +58,9 @@ async function claimDurableQueueEntryUnlocked(paths) {
|
|
|
58
58
|
if (!pending || pending.isSymbolicLink() || !pending.isFile())
|
|
59
59
|
return null;
|
|
60
60
|
if (pending.nlink > 1n || !sameFileIdentityForCleanup(pending, pending)) {
|
|
61
|
+
// Batch admission may skip unowned input, never a failed owned transition.
|
|
62
|
+
if (options.skipUnowned)
|
|
63
|
+
return null;
|
|
61
64
|
throw new FsSafeError("path-mismatch", "queue entry is not exclusively owned");
|
|
62
65
|
}
|
|
63
66
|
try {
|
|
@@ -79,10 +82,10 @@ async function claimDurableQueueEntryUnlocked(paths) {
|
|
|
79
82
|
await retireDurableQueueSource({ jsonPath: paths.jsonPath, processingPath });
|
|
80
83
|
return processingPath;
|
|
81
84
|
}
|
|
82
|
-
export async function claimDurableQueueEntry(paths) {
|
|
85
|
+
export async function claimDurableQueueEntry(paths, options = {}) {
|
|
83
86
|
if (!(await lstatOrNull(path.dirname(paths.jsonPath))))
|
|
84
87
|
return null;
|
|
85
|
-
return await withQueueEntryLock(paths, async () => await claimDurableQueueEntryUnlocked(paths));
|
|
88
|
+
return await withQueueEntryLock(paths, async () => await claimDurableQueueEntryUnlocked(paths, options));
|
|
86
89
|
}
|
|
87
90
|
export async function completeDeliveredQueueEntry(paths) {
|
|
88
91
|
if (!(await lstatOrNull(path.dirname(paths.deliveredPath))))
|
|
@@ -110,8 +113,8 @@ export async function acknowledgeDurableQueueEntry(paths) {
|
|
|
110
113
|
if (!processing) {
|
|
111
114
|
if (delivered) {
|
|
112
115
|
await fs.unlink(paths.deliveredPath);
|
|
113
|
-
await syncDirectory(path.dirname(paths.deliveredPath));
|
|
114
116
|
}
|
|
117
|
+
await syncDirectory(path.dirname(paths.deliveredPath));
|
|
115
118
|
if (await regularQueueFileIdentity(paths.jsonPath)) {
|
|
116
119
|
throw new FsSafeError("path-mismatch", "queue acknowledgement requires a processing claim");
|
|
117
120
|
}
|
|
@@ -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":"AAkBA,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;AAOF,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AAY3E,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAO5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AAgLD,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,CAQhB;AA6DD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CASZ;AAED,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,CAsBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CA8Dd;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,CAShB"}
|