@openclaw/fs-safe 0.8.6 → 0.9.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 +15 -0
- package/dist/archive-durability.d.ts +24 -0
- package/dist/archive-durability.d.ts.map +1 -0
- package/dist/archive-durability.js +180 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +2 -1
- package/dist/archive-input.js +4 -4
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +3 -2
- package/dist/archive-merge.d.ts +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +41 -8
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +1 -0
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +5 -4
- package/dist/archive-staging.d.ts +1 -1
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +24 -19
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/copy-publication.d.ts +6 -0
- package/dist/copy-publication.d.ts.map +1 -0
- package/dist/copy-publication.js +3 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +4 -4
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +5 -4
- package/dist/file-store-sync-write.d.ts +1 -0
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +9 -6
- package/dist/file-store.d.ts +4 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +10 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +1 -2
- package/dist/guarded-mutation.js +1 -1
- package/dist/install-path.js +7 -7
- 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 +1 -0
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue-ownership.js +3 -2
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +18 -13
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +4 -3
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +31 -18
- package/dist/opened-file-failure.d.ts +1 -1
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +3 -2
- package/dist/permissions.js +3 -3
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +9 -3
- package/dist/publish-file.js +21 -21
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +30 -17
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -5
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +8 -3
- package/dist/root-impl.d.ts +1 -1
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +16 -2
- package/dist/secret-file.d.ts +1 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- package/dist/secret-read-async.js +5 -5
- package/dist/secure-file.d.ts +1 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +19 -9
- package/dist/sibling-staged-file.js +7 -7
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +16 -7
- package/dist/sidecar-lock-acquire.d.ts +1 -1
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +4 -3
- package/dist/sidecar-lock-policy.js +2 -2
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +16 -4
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -1
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +9 -4
- package/dist/walk.js +2 -2
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -2
- package/docs/archive.md +35 -2
- package/docs/file-store.md +48 -3
- package/docs/json-store.md +10 -0
- package/docs/reading.md +6 -0
- package/docs/root.md +2 -2
- package/docs/secret-file.md +6 -0
- package/docs/sidecar-lock.md +2 -0
- package/docs/writing.md +5 -3
- package/package.json +11 -11
package/dist/archive-staging.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, } from "./directory-guard.js";
|
|
@@ -44,16 +45,16 @@ export async function assertDirectoryIdentityGuard(guard) {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
export async function prepareArchiveDestinationDir(destDir) {
|
|
47
|
-
const stat =
|
|
48
|
+
const stat = fsSync.lstatSync(destDir);
|
|
48
49
|
if (stat.isSymbolicLink()) {
|
|
49
50
|
throw new ArchiveSecurityError("destination-symlink", "archive destination is a symlink");
|
|
50
51
|
}
|
|
51
52
|
if (!stat.isDirectory()) {
|
|
52
53
|
throw new ArchiveSecurityError("destination-not-directory", "archive destination is not a directory");
|
|
53
54
|
}
|
|
54
|
-
const realPath =
|
|
55
|
-
const realStat =
|
|
56
|
-
const postStat =
|
|
55
|
+
const realPath = fsSync.realpathSync.native(destDir);
|
|
56
|
+
const realStat = fsSync.statSync(realPath);
|
|
57
|
+
const postStat = fsSync.lstatSync(destDir);
|
|
57
58
|
if (realStat.dev !== stat.dev ||
|
|
58
59
|
realStat.ino !== stat.ino ||
|
|
59
60
|
postStat.isSymbolicLink() ||
|
|
@@ -71,7 +72,7 @@ async function assertNoSymlinkTraversal(params) {
|
|
|
71
72
|
current = path.join(current, part);
|
|
72
73
|
let stat;
|
|
73
74
|
try {
|
|
74
|
-
stat =
|
|
75
|
+
stat = fsSync.lstatSync(current);
|
|
75
76
|
}
|
|
76
77
|
catch (err) {
|
|
77
78
|
if (isNotFoundPathError(err)) {
|
|
@@ -87,7 +88,7 @@ async function assertNoSymlinkTraversal(params) {
|
|
|
87
88
|
export async function assertResolvedInsideDestination(params) {
|
|
88
89
|
let resolved;
|
|
89
90
|
try {
|
|
90
|
-
resolved =
|
|
91
|
+
resolved = fsSync.realpathSync.native(params.targetPath);
|
|
91
92
|
}
|
|
92
93
|
catch (err) {
|
|
93
94
|
if (isNotFoundPathError(err)) {
|
|
@@ -113,10 +114,10 @@ async function mkdirArchiveOutput(params) {
|
|
|
113
114
|
export async function prepareArchiveOutputPath(params) {
|
|
114
115
|
await prepareOutputPath(params);
|
|
115
116
|
}
|
|
116
|
-
export async function preparePrivateArchiveOutputPath(params, assertGuards) {
|
|
117
|
-
await prepareOutputPath(params, assertGuards, true);
|
|
117
|
+
export async function preparePrivateArchiveOutputPath(params, assertGuards, destinationGuard) {
|
|
118
|
+
await prepareOutputPath(params, assertGuards, true, destinationGuard);
|
|
118
119
|
}
|
|
119
|
-
async function prepareOutputPath(params, assertGuards, privateWorkingMode = false) {
|
|
120
|
+
async function prepareOutputPath(params, assertGuards, privateWorkingMode = false, existingDestinationGuard) {
|
|
120
121
|
checkExtractionDeadline(params.deadline);
|
|
121
122
|
const targetRoot = privateWorkingMode ? {
|
|
122
123
|
async mkdir(relativePath) {
|
|
@@ -136,16 +137,24 @@ async function prepareOutputPath(params, assertGuards, privateWorkingMode = fals
|
|
|
136
137
|
mode: 0o700,
|
|
137
138
|
rejectSymlinks: true,
|
|
138
139
|
beforeComponent: async () => {
|
|
139
|
-
await
|
|
140
|
-
checkExtractionDeadline(params.deadline);
|
|
141
|
-
await assertGuards?.();
|
|
140
|
+
await assertOutputGuards();
|
|
142
141
|
checkExtractionDeadline(params.deadline);
|
|
143
142
|
},
|
|
144
143
|
});
|
|
145
144
|
},
|
|
146
145
|
} : await root(params.destinationRealDir);
|
|
147
146
|
checkExtractionDeadline(params.deadline);
|
|
148
|
-
const destinationGuard = await createDirectoryIdentityGuard(params.destinationRealDir);
|
|
147
|
+
const destinationGuard = existingDestinationGuard ?? await createDirectoryIdentityGuard(params.destinationRealDir);
|
|
148
|
+
const assertOutputGuards = async () => {
|
|
149
|
+
// The merge's callback verifies this same original destination guard.
|
|
150
|
+
if (existingDestinationGuard && assertGuards)
|
|
151
|
+
await assertGuards();
|
|
152
|
+
else {
|
|
153
|
+
await assertDirectoryIdentityGuard(destinationGuard);
|
|
154
|
+
checkExtractionDeadline(params.deadline);
|
|
155
|
+
await assertGuards?.();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
149
158
|
checkExtractionDeadline(params.deadline);
|
|
150
159
|
const relPath = params.relPath.split(path.sep).join(path.posix.sep);
|
|
151
160
|
await assertNoSymlinkTraversal({
|
|
@@ -158,9 +167,7 @@ async function prepareOutputPath(params, assertGuards, privateWorkingMode = fals
|
|
|
158
167
|
await getFsSafeTestHooks()?.beforeArchiveOutputMutation?.("mkdir", params.outPath);
|
|
159
168
|
checkExtractionDeadline(params.deadline);
|
|
160
169
|
await ownExtractionDestinationMutation(params.deadline, async () => {
|
|
161
|
-
await
|
|
162
|
-
checkExtractionDeadline(params.deadline);
|
|
163
|
-
await assertGuards?.();
|
|
170
|
+
await assertOutputGuards();
|
|
164
171
|
checkExtractionDeadline(params.deadline);
|
|
165
172
|
await mkdirArchiveOutput({
|
|
166
173
|
targetRoot,
|
|
@@ -184,9 +191,7 @@ async function prepareOutputPath(params, assertGuards, privateWorkingMode = fals
|
|
|
184
191
|
await getFsSafeTestHooks()?.beforeArchiveOutputMutation?.("mkdir", path.dirname(params.outPath));
|
|
185
192
|
checkExtractionDeadline(params.deadline);
|
|
186
193
|
await ownExtractionDestinationMutation(params.deadline, async () => {
|
|
187
|
-
await
|
|
188
|
-
checkExtractionDeadline(params.deadline);
|
|
189
|
-
await assertGuards?.();
|
|
194
|
+
await assertOutputGuards();
|
|
190
195
|
checkExtractionDeadline(params.deadline);
|
|
191
196
|
await mkdirArchiveOutput({
|
|
192
197
|
targetRoot,
|
package/dist/archive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA4DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CjF"}
|
package/dist/archive.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createTarEntryPlanner } from "./archive-tar.js";
|
|
2
2
|
import { inspectTar, replayTar } from "./archive-tar-stream.js";
|
|
3
3
|
import { runPinnedWriteHelper } from "./pinned-write.js";
|
|
4
|
-
import { constants as fsConstants } from "node:fs";
|
|
4
|
+
import fsSync, { constants as fsConstants } from "node:fs";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
@@ -77,6 +77,8 @@ async function writeZipFileEntry(params) {
|
|
|
77
77
|
dir: path.dirname(destinationPath),
|
|
78
78
|
chmodDir: false,
|
|
79
79
|
mode: 0o600,
|
|
80
|
+
syncTempFile: false,
|
|
81
|
+
syncParentDir: false,
|
|
80
82
|
writeTemp: async (tempPath) => {
|
|
81
83
|
tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, 0o600);
|
|
82
84
|
const writable = tempHandle.createWriteStream();
|
|
@@ -134,7 +136,7 @@ async function extractZip(params) {
|
|
|
134
136
|
await withStagedArchiveDestination({
|
|
135
137
|
destinationRealDir,
|
|
136
138
|
run: async (stagingDir) => {
|
|
137
|
-
const stagingRealDir =
|
|
139
|
+
const stagingRealDir = fsSync.realpathSync.native(stagingDir);
|
|
138
140
|
const acceptedEntries = [];
|
|
139
141
|
for (const entry of entries) {
|
|
140
142
|
params.deadline.check();
|
|
@@ -185,6 +187,7 @@ async function extractZip(params) {
|
|
|
185
187
|
params.deadline.check();
|
|
186
188
|
await mergePlannedArchiveIntoDestination({
|
|
187
189
|
entries: acceptedEntries,
|
|
190
|
+
durable: params.durable,
|
|
188
191
|
sourceDir: stagingRealDir,
|
|
189
192
|
destinationDir: params.destDir,
|
|
190
193
|
destinationRealDir,
|
|
@@ -212,6 +215,7 @@ export async function extractArchive(params) {
|
|
|
212
215
|
// getters and inherited policy must survive identically on every backend.
|
|
213
216
|
const options = {
|
|
214
217
|
archivePath: params.archivePath, destDir: params.destDir,
|
|
218
|
+
durable: params.durable,
|
|
215
219
|
stripComponents: params.stripComponents, limits,
|
|
216
220
|
entryModes: params.entryModes, entryFilter: params.entryFilter, onFiltered,
|
|
217
221
|
};
|
|
@@ -246,7 +250,7 @@ async function extractWasmTar(params) {
|
|
|
246
250
|
deadline.check();
|
|
247
251
|
const destinationRealDir = await prepareArchiveDestinationDir(options.destDir);
|
|
248
252
|
await withStagedArchiveDestination({ destinationRealDir, run: async (stagingPath) => {
|
|
249
|
-
const stagingDir =
|
|
253
|
+
const stagingDir = fsSync.realpathSync.native(stagingPath);
|
|
250
254
|
const planEntry = createTarEntryPlanner({ ...options, rootDir: destinationRealDir, limits: params.limits });
|
|
251
255
|
const accepted = manifest.flatMap((entry) => {
|
|
252
256
|
deadline.check();
|
|
@@ -262,6 +266,7 @@ async function extractWasmTar(params) {
|
|
|
262
266
|
if (member.kind === "file") {
|
|
263
267
|
await runPinnedWriteHelper({ rootPath: stagingDir, relativeParentPath: path.posix.dirname(member.path),
|
|
264
268
|
basename: path.posix.basename(member.path), mkdir: false, mode: 0o600, overwrite: false,
|
|
269
|
+
sync: false,
|
|
265
270
|
maxBytes: member.size, input: { kind: "stream", stream: Readable.from(payload) } });
|
|
266
271
|
}
|
|
267
272
|
deadline.check();
|
|
@@ -269,7 +274,7 @@ async function extractWasmTar(params) {
|
|
|
269
274
|
});
|
|
270
275
|
deadline.check();
|
|
271
276
|
await mergePlannedArchiveIntoDestination({ entries: accepted, sourceDir: stagingDir,
|
|
272
|
-
destinationDir: options.destDir, destinationRealDir, deadline });
|
|
277
|
+
destinationDir: options.destDir, destinationRealDir, deadline, durable: options.durable });
|
|
273
278
|
deadline.check();
|
|
274
279
|
} });
|
|
275
280
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PinnedWriteParams } from "./pinned-write.js";
|
|
2
|
+
export declare const onCopyPublication: unique symbol;
|
|
3
|
+
export type CopyPublicationOptions = {
|
|
4
|
+
[onCopyPublication]?: PinnedWriteParams["verifyPublished"];
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=copy-publication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-publication.d.ts","sourceRoot":"","sources":["../src/copy-publication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAC7D,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,iBAAiB,CAAC,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;CAC5D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"directory-mode-node.d.ts","sourceRoot":"","sources":["../src/directory-mode-node.ts"],"names":[],"mappings":"AAAA,OAAe,EAAa,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAK9D,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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { constants } from "node:fs";
|
|
1
|
+
import fsSync, { constants } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { inspectDirectoryIdentity } from "./directory-guard.js";
|
|
@@ -49,7 +49,7 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
49
49
|
});
|
|
50
50
|
try {
|
|
51
51
|
const inspect = async () => {
|
|
52
|
-
const opened = await inspectFileIdentity(() => handle.
|
|
52
|
+
const opened = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }), expected);
|
|
53
53
|
assertOwnedDirectory(expected, opened);
|
|
54
54
|
assertOwner(opened);
|
|
55
55
|
await inspectDirectoryIdentity(dirPath, expected);
|
|
@@ -63,8 +63,8 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
63
63
|
if (namespace.type !== 0x9fa0n) {
|
|
64
64
|
throw new FsSafeError("path-mismatch", "directory mode requires a trusted procfs fd namespace");
|
|
65
65
|
}
|
|
66
|
-
const opened = await inspectFileIdentity(() => handle.
|
|
67
|
-
const followed = await inspectFileIdentity(() =>
|
|
66
|
+
const opened = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }), expected);
|
|
67
|
+
const followed = await inspectFileIdentity(() => fsSync.statSync(procPath, { bigint: true }), expected);
|
|
68
68
|
assertOwnedDirectory(opened, followed);
|
|
69
69
|
assertOwner(opened);
|
|
70
70
|
assertOwner(followed);
|
package/dist/file-hash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAElD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,GACrD,OAAO,CAAC,gBAAgB,CAAC,CAoB3B;AA+CD,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAElF"}
|
package/dist/file-hash.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import fsSync from "node:fs";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import { FsSafeError } from "./errors.js";
|
|
4
5
|
import { getNativeBinding } from "./native.js";
|
|
5
6
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
6
7
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
7
8
|
export async function hashFileHandle(handle, native = getNativeBinding()) {
|
|
8
|
-
const stat =
|
|
9
|
+
const stat = fsSync.fstatSync(handle.fd);
|
|
9
10
|
if (!stat.isFile()) {
|
|
10
11
|
throw new FsSafeError("not-file", "SHA-256 input is not a regular file");
|
|
11
12
|
}
|
|
@@ -26,7 +27,7 @@ export async function hashFileHandle(handle, native = getNativeBinding()) {
|
|
|
26
27
|
}
|
|
27
28
|
async function hashPath(filePath) {
|
|
28
29
|
const before = await inspectFileIdentity(async () => {
|
|
29
|
-
const stat =
|
|
30
|
+
const stat = fsSync.lstatSync(filePath, { bigint: true });
|
|
30
31
|
if (stat.isSymbolicLink()) {
|
|
31
32
|
throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link");
|
|
32
33
|
}
|
|
@@ -49,14 +50,14 @@ async function hashPath(filePath) {
|
|
|
49
50
|
}
|
|
50
51
|
try {
|
|
51
52
|
const opened = await inspectFileIdentity(async () => {
|
|
52
|
-
const stat =
|
|
53
|
+
const stat = fsSync.fstatSync(handle.fd, { bigint: true });
|
|
53
54
|
if (!stat.isFile()) {
|
|
54
55
|
throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
|
|
55
56
|
}
|
|
56
57
|
return stat;
|
|
57
58
|
}, before);
|
|
58
59
|
await inspectFileIdentity(async () => {
|
|
59
|
-
const stat =
|
|
60
|
+
const stat = fsSync.lstatSync(filePath, { bigint: true });
|
|
60
61
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
61
62
|
throw new FsSafeError("path-mismatch", "SHA-256 path changed while opening");
|
|
62
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-store-sync-write.d.ts","sourceRoot":"","sources":["../src/file-store-sync-write.ts"],"names":[],"mappings":"AAwBA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"file-store-sync-write.d.ts","sourceRoot":"","sources":["../src/file-store-sync-write.ts"],"names":[],"mappings":"AAwBA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CA+GT"}
|
|
@@ -64,7 +64,8 @@ export function writeFileSyncAtomic(params) {
|
|
|
64
64
|
if (!opened.isFile() || opened.nlink > 1) {
|
|
65
65
|
throw new FsSafeError("path-mismatch", "store temp is not an owned regular file");
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
if (params.durable)
|
|
68
|
+
fs.fsyncSync(descriptor);
|
|
68
69
|
return opened;
|
|
69
70
|
}
|
|
70
71
|
finally {
|
|
@@ -105,11 +106,13 @@ export function writeFileSyncAtomic(params) {
|
|
|
105
106
|
}
|
|
106
107
|
if (parentGuard) {
|
|
107
108
|
assertSyncDirectoryGuard(parentGuard);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
if (params.durable) {
|
|
110
|
+
syncDirectorySync({
|
|
111
|
+
path: parentGuard.dir,
|
|
112
|
+
realPath: parentGuard.realPath,
|
|
113
|
+
identity: parentGuard.stat,
|
|
114
|
+
}, { label: "store parent" });
|
|
115
|
+
}
|
|
113
116
|
assertSyncDirectoryGuard(parentGuard);
|
|
114
117
|
}
|
|
115
118
|
return filePath;
|
package/dist/file-store.d.ts
CHANGED
|
@@ -6,11 +6,15 @@ import { type OpenResult, type ReadResult, type Root, type RootReadOptions } fro
|
|
|
6
6
|
export type FileStoreOptions = {
|
|
7
7
|
rootDir: string;
|
|
8
8
|
private?: boolean;
|
|
9
|
+
/** Default for writes, streams, and copies; true syncs file and parent where supported. */
|
|
10
|
+
durable?: boolean;
|
|
9
11
|
dirMode?: number;
|
|
10
12
|
mode?: number;
|
|
11
13
|
maxBytes?: number;
|
|
12
14
|
};
|
|
13
15
|
export type FileStoreWriteOptions = {
|
|
16
|
+
/** Override store durability; defaults to the store option, then true. */
|
|
17
|
+
durable?: boolean;
|
|
14
18
|
dirMode?: number;
|
|
15
19
|
mode?: number;
|
|
16
20
|
maxBytes?: number;
|
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;AAQnE,OAAO,EAAmB,KAAK,oBAAoB,EAAE,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAItG,OAAO,EAAQ,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAMpG,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAAG;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC;AAEnF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,CACH,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GAAG,UAAU,EACzB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,WAAW,
|
|
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
|
@@ -106,6 +106,7 @@ async function copyIntoRoot(params) {
|
|
|
106
106
|
});
|
|
107
107
|
await ensureParentInRoot(scopedRoot, relativePath, dirMode);
|
|
108
108
|
await scopedRoot.copyIn(relativePath, params.sourcePath, {
|
|
109
|
+
durable: params.durable,
|
|
109
110
|
maxBytes: params.maxBytes,
|
|
110
111
|
mkdir: false,
|
|
111
112
|
mode: params.mode ?? 0o600,
|
|
@@ -117,6 +118,7 @@ export function fileStore(options) {
|
|
|
117
118
|
const privateMode = options.private ?? false;
|
|
118
119
|
const dirMode = options.dirMode ?? 0o700;
|
|
119
120
|
const mode = options.mode ?? 0o600;
|
|
121
|
+
const durable = options.durable ?? true;
|
|
120
122
|
const maxBytes = normalizeMaxBytes(options.maxBytes);
|
|
121
123
|
async function openRoot() {
|
|
122
124
|
return await root(rootDir, { hardlinks: "reject", maxBytes });
|
|
@@ -132,6 +134,7 @@ export function fileStore(options) {
|
|
|
132
134
|
rootDir,
|
|
133
135
|
filePath: destination,
|
|
134
136
|
content,
|
|
137
|
+
durable: writeOptions?.durable ?? durable,
|
|
135
138
|
dirMode: writeOptions?.dirMode ?? dirMode,
|
|
136
139
|
mode: writeOptions?.mode ?? mode,
|
|
137
140
|
});
|
|
@@ -147,6 +150,7 @@ export function fileStore(options) {
|
|
|
147
150
|
await scopedRoot.write(safeRelativePath, content, {
|
|
148
151
|
mkdir: false,
|
|
149
152
|
mode: writeOptions?.mode ?? mode,
|
|
153
|
+
durable: writeOptions?.durable ?? durable,
|
|
150
154
|
});
|
|
151
155
|
return destination;
|
|
152
156
|
}
|
|
@@ -173,6 +177,7 @@ export function fileStore(options) {
|
|
|
173
177
|
rootDir,
|
|
174
178
|
filePath: destination,
|
|
175
179
|
content: Buffer.concat(chunks),
|
|
180
|
+
durable: writeOptions?.durable ?? durable,
|
|
176
181
|
dirMode: writeOptions?.dirMode ?? dirMode,
|
|
177
182
|
mode: writeOptions?.mode ?? mode,
|
|
178
183
|
});
|
|
@@ -188,6 +193,7 @@ export function fileStore(options) {
|
|
|
188
193
|
rootDir,
|
|
189
194
|
relativePath: safeRelativePath,
|
|
190
195
|
sourcePath: staged.path,
|
|
196
|
+
durable: writeOptions?.durable ?? durable,
|
|
191
197
|
maxBytes: limit,
|
|
192
198
|
mode: writeOptions?.mode ?? mode,
|
|
193
199
|
tempPrefix: writeOptions?.tempPrefix,
|
|
@@ -212,6 +218,7 @@ export function fileStore(options) {
|
|
|
212
218
|
rootDir,
|
|
213
219
|
relativePath,
|
|
214
220
|
sourcePath,
|
|
221
|
+
durable: writeOptions?.durable ?? durable,
|
|
215
222
|
dirMode: writeOptions?.dirMode ?? dirMode,
|
|
216
223
|
maxBytes: configuredLimit,
|
|
217
224
|
mode: writeOptions?.mode ?? mode,
|
|
@@ -283,7 +290,7 @@ export function fileStore(options) {
|
|
|
283
290
|
readRequired: async () => await (await openRoot()).readJson(assertRelativePath(relativePath)),
|
|
284
291
|
write: async (value, options) => {
|
|
285
292
|
const json = stringifyJsonDocument(value, null, 2);
|
|
286
|
-
await write(relativePath, options?.trailingNewline === false ? json : `${json}\n
|
|
293
|
+
await write(relativePath, options?.trailingNewline === false ? json : `${json}\n`, { durable: options?.durable });
|
|
287
294
|
},
|
|
288
295
|
}, jsonOptions);
|
|
289
296
|
},
|
|
@@ -297,6 +304,7 @@ export function fileStoreSync(options) {
|
|
|
297
304
|
const privateMode = options.private ?? false;
|
|
298
305
|
const dirMode = options.dirMode ?? 0o700;
|
|
299
306
|
const mode = options.mode ?? 0o600;
|
|
307
|
+
const durable = options.durable ?? true;
|
|
300
308
|
const maxBytes = normalizeMaxBytes(options.maxBytes);
|
|
301
309
|
function write(relativePath, data, writeOptions) {
|
|
302
310
|
const destination = resolveStorePath(rootDir, relativePath);
|
|
@@ -308,6 +316,7 @@ export function fileStoreSync(options) {
|
|
|
308
316
|
filePath: destination,
|
|
309
317
|
content,
|
|
310
318
|
privateMode,
|
|
319
|
+
durable: writeOptions?.durable ?? durable,
|
|
311
320
|
dirMode: writeOptions?.dirMode ?? dirMode,
|
|
312
321
|
mode: writeOptions?.mode ?? mode,
|
|
313
322
|
});
|
package/dist/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOxD"}
|
package/dist/fs.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import fsp from "node:fs/promises";
|
|
3
2
|
/**
|
|
4
3
|
* Returns true when `fs.stat()` can stat the path.
|
|
5
4
|
*
|
|
@@ -8,7 +7,7 @@ import fsp from "node:fs/promises";
|
|
|
8
7
|
*/
|
|
9
8
|
export async function pathExists(filePath) {
|
|
10
9
|
try {
|
|
11
|
-
|
|
10
|
+
fs.statSync(filePath);
|
|
12
11
|
return true;
|
|
13
12
|
}
|
|
14
13
|
catch {
|
package/dist/guarded-mutation.js
CHANGED
|
@@ -49,7 +49,7 @@ export async function guardedRename(params) {
|
|
|
49
49
|
: await createAsyncDirectoryGuard(path.dirname(params.to));
|
|
50
50
|
await withAsyncDirectoryGuards([sourceGuard, targetGuard], async () => {
|
|
51
51
|
if (params.onSourceInspected) {
|
|
52
|
-
params.onSourceInspected(
|
|
52
|
+
params.onSourceInspected(fsSync.lstatSync(params.from, { bigint: true }));
|
|
53
53
|
}
|
|
54
54
|
// Authority must survive all awaited guards; do not yield before rename dispatch.
|
|
55
55
|
params.assertBeforeRename?.();
|
package/dist/install-path.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import
|
|
2
|
+
import fsSync from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { isPathInside } from "./path.js";
|
|
5
5
|
export function safeDirName(input) {
|
|
@@ -49,9 +49,9 @@ export async function assertCanonicalPathWithinBase(params) {
|
|
|
49
49
|
if (!isPathInside(baseDir, candidatePath)) {
|
|
50
50
|
throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
|
|
51
51
|
}
|
|
52
|
-
const baseLstat =
|
|
52
|
+
const baseLstat = fsSync.lstatSync(baseDir);
|
|
53
53
|
if (baseLstat.isSymbolicLink()) {
|
|
54
|
-
const baseStat =
|
|
54
|
+
const baseStat = fsSync.statSync(baseDir);
|
|
55
55
|
if (!baseStat.isDirectory()) {
|
|
56
56
|
throw new Error(`Invalid ${params.boundaryLabel}: base directory must resolve to a directory`);
|
|
57
57
|
}
|
|
@@ -59,15 +59,15 @@ export async function assertCanonicalPathWithinBase(params) {
|
|
|
59
59
|
else if (!baseLstat.isDirectory()) {
|
|
60
60
|
throw new Error(`Invalid ${params.boundaryLabel}: base directory must be a directory`);
|
|
61
61
|
}
|
|
62
|
-
const baseRealPath =
|
|
62
|
+
const baseRealPath = fsSync.realpathSync.native(baseDir);
|
|
63
63
|
const validateDirectory = async (dirPath) => {
|
|
64
64
|
const resolvedDirPath = path.resolve(dirPath);
|
|
65
|
-
const dirLstat =
|
|
65
|
+
const dirLstat = fsSync.lstatSync(dirPath);
|
|
66
66
|
if (dirLstat.isSymbolicLink()) {
|
|
67
67
|
if (resolvedDirPath !== baseDir) {
|
|
68
68
|
throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
|
|
69
69
|
}
|
|
70
|
-
const dirStat =
|
|
70
|
+
const dirStat = fsSync.statSync(dirPath);
|
|
71
71
|
if (!dirStat.isDirectory()) {
|
|
72
72
|
throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
|
|
73
73
|
}
|
|
@@ -75,7 +75,7 @@ export async function assertCanonicalPathWithinBase(params) {
|
|
|
75
75
|
else if (!dirLstat.isDirectory()) {
|
|
76
76
|
throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
|
|
77
77
|
}
|
|
78
|
-
const dirRealPath =
|
|
78
|
+
const dirRealPath = fsSync.realpathSync.native(dirPath);
|
|
79
79
|
if (!isPathInside(baseRealPath, dirRealPath)) {
|
|
80
80
|
throw new Error(`Invalid path: must stay within ${params.boundaryLabel}`);
|
|
81
81
|
}
|
|
@@ -9,6 +9,8 @@ export type JsonStoreLockOptions = {
|
|
|
9
9
|
managerKey?: string;
|
|
10
10
|
};
|
|
11
11
|
export type JsonFileStoreOptions = {
|
|
12
|
+
/** Overrides the parent store's durability for every JSON mutation. */
|
|
13
|
+
durable?: boolean;
|
|
12
14
|
trailingNewline?: boolean;
|
|
13
15
|
lock?: boolean | JsonStoreLockOptions;
|
|
14
16
|
};
|
|
@@ -29,6 +31,7 @@ export type JsonStoreAdapter<T> = {
|
|
|
29
31
|
readRequired(): Promise<T>;
|
|
30
32
|
write(value: T, options?: {
|
|
31
33
|
trailingNewline?: boolean;
|
|
34
|
+
durable?: boolean;
|
|
32
35
|
}): Promise<void>;
|
|
33
36
|
};
|
|
34
37
|
export declare function createJsonStore<T>(adapter: JsonStoreAdapter<T>, options?: JsonFileStoreOptions): JsonStore<T>;
|
|
@@ -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;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;
|
|
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,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,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,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F,CAAC;AA2BF,wBAAgB,eAAe,CAAC,CAAC,EAC/B,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC5B,OAAO,GAAE,oBAAyB,GACjC,SAAS,CAAC,CAAC,CAAC,CAoFd"}
|
|
@@ -38,6 +38,7 @@ export function createJsonStore(adapter, options = {}) {
|
|
|
38
38
|
async function write(value) {
|
|
39
39
|
await adapter.write(value, {
|
|
40
40
|
trailingNewline: options.trailingNewline ?? true,
|
|
41
|
+
durable: options.durable,
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
async function withSerializedMutation(run) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fsSync from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { syncDirectory } from "./directory-durability.js";
|
|
4
4
|
import { isPathRelativeEscape } from "./path.js";
|
|
5
5
|
export async function syncQueueDirectoryCreation(dir, validationBase) {
|
|
6
|
-
const baseReal =
|
|
7
|
-
const targetReal =
|
|
6
|
+
const baseReal = fsSync.realpathSync.native(validationBase);
|
|
7
|
+
const targetReal = fsSync.realpathSync.native(dir);
|
|
8
8
|
const relative = path.relative(baseReal, targetReal);
|
|
9
9
|
if (isPathRelativeEscape(relative)) {
|
|
10
10
|
throw new Error(`durable queue directory escapes validation base: ${dir}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { withSidecarLock } from "./sidecar-lock.js";
|
|
3
|
+
import fsSync, {} from "node:fs";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { syncDirectory } from "./directory-durability.js";
|
|
@@ -163,7 +164,7 @@ export function getErrorCode(error) {
|
|
|
163
164
|
}
|
|
164
165
|
async function lstatOrNull(filePath) {
|
|
165
166
|
try {
|
|
166
|
-
return
|
|
167
|
+
return fsSync.lstatSync(filePath, { bigint: true });
|
|
167
168
|
}
|
|
168
169
|
catch (error) {
|
|
169
170
|
if (getErrorCode(error) === "ENOENT")
|
|
@@ -205,7 +206,7 @@ async function ensureRetirementRoot(jsonPath) {
|
|
|
205
206
|
if (getErrorCode(error) !== "EEXIST")
|
|
206
207
|
throw error;
|
|
207
208
|
});
|
|
208
|
-
const root =
|
|
209
|
+
const root = fsSync.lstatSync(rootPath, { bigint: true });
|
|
209
210
|
assertDirectory(root, "queue retirement root");
|
|
210
211
|
await syncDirectory(parentPath);
|
|
211
212
|
return rootPath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAmBA,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;AAM3E,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;
|
|
1
|
+
{"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAmBA,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;AAM3E,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;AAiLD,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"}
|