@openclaw/feishu 2026.9.8 → 2026.10.1-beta.2
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/dist/.setup/{channel-DV4hM2Uf.mjs → channel-CX62T71S.mjs} +146 -245
- package/dist/.setup/{channel.runtime-CXQcH5zZ.mjs → channel.runtime-COiaq-Rv.mjs} +6 -12
- package/dist/.setup/{chat-D8MlFAZi.mjs → chat-BdyvImsY.mjs} +6 -9
- package/dist/.setup/{client-BYG-_IZl.mjs → client-BsBVWJzN.mjs} +1 -1
- package/dist/.setup/{doctor-contract-3XC0vbIw.mjs → doctor-contract-B1B9ogdd.mjs} +212 -27
- package/dist/.setup/{monitor-D_rsHBZ6.mjs → monitor-7DvwUs4Y.mjs} +5 -6
- package/dist/.setup/{monitor.account-DvvY7i6k.mjs → monitor.account-7xVu7XWY.mjs} +190 -393
- package/dist/.setup/{probe-_ViqSi0j.mjs → probe-B9o7iey6.mjs} +13 -39
- package/dist/.setup/{reply-delivery-result-COAjaukc.mjs → reply-delivery-result-Bt-BWBEA.mjs} +151 -297
- package/dist/.setup/{setup-api-C4S5I3ac.mjs → setup-api-DmzX9S01.mjs} +1 -1
- package/dist/.setup/{subagent-hooks-BaFoMu8W.mjs → subagent-hooks-CUXg0pC-.mjs} +5 -11
- package/dist/.setup/{thread-bindings-EdLXPXzw.mjs → thread-bindings-jW2x5VuN.mjs} +23 -41
- package/dist/api.js +234 -297
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +2 -2
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/subagent-hooks-api.js +1 -1
- package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
- package/node_modules/@openclaw/fs-safe/README.md +70 -379
- package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
- package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
- package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
- package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
- package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
- package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
- package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
- package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
- package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
- package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
- package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
- package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
- package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
- package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
- package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
- package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
- package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
- package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
- package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
- package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
- package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
- package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
- package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
- package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
- package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
- package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
- package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
- package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
- package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
- package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
- package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
- package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
- package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
- package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
- package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
- package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
- package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
- package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
- package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
- package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
- package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
- package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
- package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
- package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
- package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
- package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
- package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
- package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
- package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
- package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
- package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
- package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
- package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
- package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
- package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
- package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
- package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
- package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
- package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
- package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
- package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
- package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
- package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
- package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
- package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
- package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
- package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
- package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
- package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
- package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
- package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
- package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
- package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
- package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
- package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
- package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
- package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
- package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
- package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
- package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
- package/node_modules/@openclaw/fs-safe/package.json +10 -10
- package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
- package/package.json +5 -5
- package/skills/feishu-wiki/SKILL.md +1 -1
- package/dist/.setup/accounts-wRqItHug.mjs +0 -206
- package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
- package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
- package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
- package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
- package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
|
@@ -1,44 +1,29 @@
|
|
|
1
|
-
import { syncFileBestEffort, syncFileBestEffortSync } from "./file-sync.js";
|
|
2
1
|
import syncFs, {} from "node:fs";
|
|
3
|
-
import fs
|
|
2
|
+
import fs from "node:fs/promises";
|
|
4
3
|
import { FsSafeError } from "./errors.js";
|
|
5
4
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
5
|
import { ownDirectoryMode } from "./directory-mode-node.js";
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import { inspectAtomicIdentity, wait } from "./atomic-io.js";
|
|
7
|
+
function parentCloseFailure(primary, closeError) {
|
|
8
|
+
return primary ? new AggregateError([primary.error, closeError], "Atomic parent preparation and close failed") : closeError;
|
|
9
|
+
}
|
|
10
|
+
export function* syncDirectoryBestEffort(io, dirPath) {
|
|
11
|
+
let file;
|
|
9
12
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
file = yield* io.open(dirPath, "r");
|
|
14
|
+
yield* file.sync();
|
|
12
15
|
}
|
|
13
16
|
catch {
|
|
14
|
-
//
|
|
17
|
+
// Directory synchronization and close remain best-effort.
|
|
15
18
|
}
|
|
16
19
|
finally {
|
|
17
20
|
try {
|
|
18
|
-
|
|
21
|
+
const closing = file?.close();
|
|
22
|
+
if (file && io.asynchronous)
|
|
23
|
+
yield closing;
|
|
19
24
|
}
|
|
20
25
|
catch {
|
|
21
|
-
//
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
export function syncDirectoryBestEffortSync(fsModule, dirPath) {
|
|
26
|
-
let fd;
|
|
27
|
-
try {
|
|
28
|
-
fd = fsModule.openSync(dirPath, "r");
|
|
29
|
-
fsModule.fsyncSync(fd);
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
// Best-effort on platforms/filesystems that do not support directory fsync.
|
|
33
|
-
}
|
|
34
|
-
finally {
|
|
35
|
-
if (fd !== undefined) {
|
|
36
|
-
try {
|
|
37
|
-
fsModule.closeSync(fd);
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// Best-effort close after directory fsync.
|
|
41
|
-
}
|
|
26
|
+
// Preserve the operation's best-effort contract.
|
|
42
27
|
}
|
|
43
28
|
}
|
|
44
29
|
}
|
|
@@ -54,7 +39,7 @@ function assertDirectory(identity, dirPath) {
|
|
|
54
39
|
}
|
|
55
40
|
return identity;
|
|
56
41
|
}
|
|
57
|
-
|
|
42
|
+
async function pinDirectoryForMode(params) {
|
|
58
43
|
// Node does not enforce POSIX directory modes on Windows, and its directory
|
|
59
44
|
// descriptors are not consistently openable. mkdir(mode) remains the only
|
|
60
45
|
// bounded behavior there; never fall back to a pathname chmod.
|
|
@@ -84,81 +69,91 @@ export async function pinDirectoryForMode(params) {
|
|
|
84
69
|
return owner;
|
|
85
70
|
}
|
|
86
71
|
catch (error) {
|
|
87
|
-
|
|
72
|
+
try {
|
|
73
|
+
await handle.close();
|
|
74
|
+
}
|
|
75
|
+
catch (closeError) {
|
|
76
|
+
throw parentCloseFailure({ error }, closeError);
|
|
77
|
+
}
|
|
88
78
|
throw error;
|
|
89
79
|
}
|
|
90
80
|
}
|
|
91
|
-
export
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
81
|
+
export function* applyDirectoryMode(io, params) {
|
|
82
|
+
if (io.asynchronous) {
|
|
83
|
+
const owner = yield* wait(pinDirectoryForMode({
|
|
84
|
+
...params,
|
|
85
|
+
fsModule: io.asyncFs,
|
|
86
|
+
}));
|
|
87
|
+
let primary;
|
|
88
|
+
try {
|
|
89
|
+
if (owner)
|
|
90
|
+
yield* wait(owner.apply(params.mode));
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
primary = { error };
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
try {
|
|
98
|
+
if (owner)
|
|
99
|
+
yield* wait(owner.close());
|
|
100
|
+
}
|
|
101
|
+
catch (closeError) {
|
|
102
|
+
throw parentCloseFailure(primary, closeError);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
102
105
|
return;
|
|
103
106
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
params.fchmodSync?.(fd, params.mode & 0o7777);
|
|
111
|
-
}
|
|
112
|
-
finally {
|
|
113
|
-
params.fsModule.closeSync(fd);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
export async function writeTempFile(params) {
|
|
117
|
-
params.mutation?.assert();
|
|
118
|
-
const handle = await params.fsModule.open(params.tempPath, "wx", params.mode);
|
|
107
|
+
if (process.platform === "win32")
|
|
108
|
+
return;
|
|
109
|
+
const admit = (stat) => assertDirectory(stat, params.dirPath);
|
|
110
|
+
const expected = inspectAtomicIdentity(io, () => io.lstatExact(params.dirPath), undefined, false, admit);
|
|
111
|
+
const file = yield* io.open(params.dirPath, directoryOpenFlags());
|
|
112
|
+
let primary;
|
|
119
113
|
try {
|
|
120
|
-
|
|
121
|
-
const inspect = () => params.fsModule === fs
|
|
122
|
-
? syncFs.fstatSync(handle.fd, { bigint: true }) : handle.stat({ bigint: true });
|
|
123
|
-
const identity = await inspectFileIdentity(inspect);
|
|
124
|
-
params.onIdentity?.(identity);
|
|
114
|
+
inspectAtomicIdentity(io, () => file.statExact(), expected, false, admit);
|
|
125
115
|
params.mutation?.assert();
|
|
126
|
-
|
|
127
|
-
await handle.chmod(params.mode);
|
|
128
|
-
if (params.sync) {
|
|
129
|
-
await syncFileBestEffort(handle);
|
|
130
|
-
}
|
|
131
|
-
await inspectFileIdentity(inspect, identity);
|
|
132
|
-
return { handle, identity };
|
|
116
|
+
file.chmod(params.mode & 0o7777);
|
|
133
117
|
}
|
|
134
118
|
catch (error) {
|
|
119
|
+
primary = { error };
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
135
123
|
try {
|
|
136
|
-
|
|
124
|
+
file.close();
|
|
137
125
|
}
|
|
138
126
|
catch (closeError) {
|
|
139
|
-
throw
|
|
127
|
+
throw parentCloseFailure(primary, closeError);
|
|
140
128
|
}
|
|
141
|
-
throw error;
|
|
142
129
|
}
|
|
143
130
|
}
|
|
144
|
-
export function
|
|
131
|
+
export function* writeTempFile(io, params) {
|
|
145
132
|
params.mutation?.assert();
|
|
146
|
-
const
|
|
133
|
+
const file = yield* io.open(params.tempPath, "wx", params.mode);
|
|
147
134
|
try {
|
|
148
|
-
const
|
|
135
|
+
const openedInspection = inspectAtomicIdentity(io, () => file.statExact());
|
|
136
|
+
const identity = (io.asynchronous ? (yield openedInspection) : openedInspection);
|
|
149
137
|
params.onIdentity?.(identity);
|
|
150
138
|
params.mutation?.assert();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
139
|
+
const writing = file.writeFile(params.content, true);
|
|
140
|
+
if (io.asynchronous)
|
|
141
|
+
yield writing;
|
|
142
|
+
const chmod = file.chmod(params.mode);
|
|
143
|
+
if (io.asynchronous)
|
|
144
|
+
yield chmod;
|
|
145
|
+
if (params.sync)
|
|
146
|
+
yield* file.syncBestEffort();
|
|
147
|
+
const currentInspection = inspectAtomicIdentity(io, () => file.statExact(), identity);
|
|
148
|
+
if (io.asynchronous)
|
|
149
|
+
yield currentInspection;
|
|
150
|
+
return { file, identity };
|
|
158
151
|
}
|
|
159
152
|
catch (error) {
|
|
160
153
|
try {
|
|
161
|
-
|
|
154
|
+
const closing = file.close();
|
|
155
|
+
if (io.asynchronous)
|
|
156
|
+
yield closing;
|
|
162
157
|
}
|
|
163
158
|
catch (closeError) {
|
|
164
159
|
throw new AggregateError([error, closeError], "Atomic temp write and close failed");
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import type { BigIntStats } from "node:fs";
|
|
2
|
+
import { type AtomicFile, type AtomicIo, type Procedure } from "./atomic-io.js";
|
|
3
3
|
import type { AtomicMutation } from "./replace-file-mutation.js";
|
|
4
|
-
/**
|
|
5
|
-
export declare
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
writing
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
published: () => void;
|
|
21
|
-
};
|
|
4
|
+
/** Borrows the writer's descriptor; no later pathname open can replace it. */
|
|
5
|
+
export declare class AtomicDestination {
|
|
6
|
+
#private;
|
|
7
|
+
readonly io: AtomicIo;
|
|
8
|
+
readonly file: AtomicFile;
|
|
9
|
+
readonly pathname: string;
|
|
10
|
+
readonly mutation: AtomicMutation;
|
|
11
|
+
readonly rejectHardlinks: boolean;
|
|
12
|
+
readonly identity: BigIntStats;
|
|
13
|
+
constructor(io: AtomicIo, file: AtomicFile, pathname: string, mutation: AtomicMutation, rejectHardlinks: boolean, identity: BigIntStats);
|
|
14
|
+
verify(restore?: boolean): Procedure<void>;
|
|
15
|
+
beforeWrite(restore: boolean): Procedure<void>;
|
|
16
|
+
writing(): void;
|
|
17
|
+
published(): void;
|
|
18
|
+
}
|
|
19
|
+
export declare function captureAtomicDestination(io: AtomicIo, file: AtomicFile, pathname: string, mutation: AtomicMutation, rejectHardlinks: boolean): Procedure<AtomicDestination>;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs, {} from "node:fs/promises";
|
|
1
|
+
import { inspectAtomicIdentity } from "./atomic-io.js";
|
|
3
2
|
import { hasErrorCode } from "./file-cleanup.js";
|
|
4
3
|
import { FsSafeError } from "./errors.js";
|
|
5
|
-
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
4
|
function regular(stat, pathname, rejectHardlinks) {
|
|
7
5
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
8
6
|
throw new FsSafeError("path-mismatch", `Atomic replace destination changed: ${pathname}`);
|
|
@@ -12,112 +10,80 @@ function regular(stat, pathname, rejectHardlinks) {
|
|
|
12
10
|
}
|
|
13
11
|
return stat;
|
|
14
12
|
}
|
|
15
|
-
/**
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
/** Borrows the writer's descriptor; no later pathname open can replace it. */
|
|
14
|
+
export class AtomicDestination {
|
|
15
|
+
io;
|
|
16
|
+
file;
|
|
17
|
+
pathname;
|
|
18
|
+
mutation;
|
|
19
|
+
rejectHardlinks;
|
|
20
|
+
identity;
|
|
21
|
+
#writing = false;
|
|
22
|
+
#restoreDescriptorOnly = false;
|
|
23
|
+
constructor(io, file, pathname, mutation, rejectHardlinks, identity) {
|
|
24
|
+
this.io = io;
|
|
25
|
+
this.file = file;
|
|
26
|
+
this.pathname = pathname;
|
|
27
|
+
this.mutation = mutation;
|
|
28
|
+
this.rejectHardlinks = rejectHardlinks;
|
|
29
|
+
this.identity = identity;
|
|
30
|
+
}
|
|
31
|
+
*verify(restore = false) {
|
|
27
32
|
let metadataFailure = false;
|
|
28
|
-
const
|
|
33
|
+
const descriptorOnly = restore && this.#restoreDescriptorOnly;
|
|
34
|
+
const owner = this;
|
|
35
|
+
function readFailure(error) {
|
|
36
|
+
metadataFailure = hasErrorCode(error, "EIO");
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
function read(pathname) {
|
|
29
40
|
try {
|
|
30
|
-
|
|
41
|
+
const stat = pathname
|
|
42
|
+
? owner.io.lstatExact(owner.pathname, false) : owner.file.statExact();
|
|
43
|
+
return owner.io.asynchronous ? Promise.resolve(stat).catch(readFailure) : stat;
|
|
31
44
|
}
|
|
32
45
|
catch (error) {
|
|
33
|
-
|
|
34
|
-
throw error;
|
|
46
|
+
return readFailure(error);
|
|
35
47
|
}
|
|
36
|
-
};
|
|
37
|
-
try {
|
|
38
|
-
await inspectFileIdentity(async () => regular(await observe(inspect), pathname, rejectHardlinks), identity);
|
|
39
|
-
await inspectFileIdentity(async () => regular(await observe(() => fsModule.lstat(pathname, { bigint: true })), pathname, rejectHardlinks), identity);
|
|
40
48
|
}
|
|
41
|
-
|
|
42
|
-
if (writing && metadataFailure) {
|
|
43
|
-
// A metadata observation failed, but the already-owned descriptor can
|
|
44
|
-
// still restore its bytes after fresh exact descriptor verification.
|
|
45
|
-
restoreDescriptorOnly = true;
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
mutation.refuse(error);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
const verifyRestore = async () => {
|
|
52
|
-
if (!restoreDescriptorOnly)
|
|
53
|
-
return await verify();
|
|
49
|
+
const admit = (stat) => regular(stat, owner.pathname, owner.rejectHardlinks);
|
|
54
50
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
verify,
|
|
63
|
-
writing: () => { writing = true; mutation.destination("writing", pathname, identity); },
|
|
64
|
-
beforeWrite: async () => {
|
|
65
|
-
mutation.assert();
|
|
66
|
-
await verify();
|
|
67
|
-
},
|
|
68
|
-
beforeRestore: async () => { mutation.assert(); await verifyRestore(); },
|
|
69
|
-
verifyRestore,
|
|
70
|
-
assertBeforeMutation: () => mutation.assert(),
|
|
71
|
-
published: () => mutation.destination("published", pathname, identity),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
export function captureAtomicDestinationSync(fsModule, fd, pathname, mutation, rejectHardlinks) {
|
|
75
|
-
const identity = inspectFileIdentitySync(() => fsModule.fstatSync(fd, { bigint: true }));
|
|
76
|
-
regular(identity, pathname, rejectHardlinks);
|
|
77
|
-
let writing = false;
|
|
78
|
-
let restoreDescriptorOnly = false;
|
|
79
|
-
const verify = () => {
|
|
80
|
-
let metadataFailure = false;
|
|
81
|
-
const observe = (read) => {
|
|
82
|
-
try {
|
|
83
|
-
return read();
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
metadataFailure = hasErrorCode(error, "EIO");
|
|
87
|
-
throw error;
|
|
51
|
+
const descriptorInspection = inspectAtomicIdentity(this.io, () => read(false), this.identity, false, admit);
|
|
52
|
+
if (this.io.asynchronous)
|
|
53
|
+
yield descriptorInspection;
|
|
54
|
+
if (!descriptorOnly) {
|
|
55
|
+
const pathnameInspection = inspectAtomicIdentity(this.io, () => read(true), this.identity, false, admit);
|
|
56
|
+
if (this.io.asynchronous)
|
|
57
|
+
yield pathnameInspection;
|
|
88
58
|
}
|
|
89
|
-
};
|
|
90
|
-
try {
|
|
91
|
-
inspectFileIdentitySync(() => regular(observe(() => fsModule.fstatSync(fd, { bigint: true })), pathname, rejectHardlinks), identity);
|
|
92
|
-
inspectFileIdentitySync(() => regular(observe(() => fsModule.lstatSync(pathname, { bigint: true })), pathname, rejectHardlinks), identity);
|
|
93
59
|
}
|
|
94
60
|
catch (error) {
|
|
95
|
-
if (writing && metadataFailure) {
|
|
96
|
-
|
|
61
|
+
if (!descriptorOnly && this.#writing && metadataFailure) {
|
|
62
|
+
// Restore through the retained descriptor after a fresh exact check.
|
|
63
|
+
this.#restoreDescriptorOnly = true;
|
|
97
64
|
throw error;
|
|
98
65
|
}
|
|
99
|
-
mutation.refuse(error);
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
const verifyRestore = () => {
|
|
103
|
-
if (!restoreDescriptorOnly)
|
|
104
|
-
return verify();
|
|
105
|
-
try {
|
|
106
|
-
inspectFileIdentitySync(() => regular(fsModule.fstatSync(fd, { bigint: true }), pathname, rejectHardlinks), identity);
|
|
66
|
+
this.mutation.refuse(error);
|
|
107
67
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
68
|
+
}
|
|
69
|
+
*beforeWrite(restore) {
|
|
70
|
+
this.mutation.assert();
|
|
71
|
+
yield* this.verify(restore);
|
|
72
|
+
// Async identity observations may revoke authority before dispatch resumes.
|
|
73
|
+
if (this.io.asynchronous)
|
|
74
|
+
this.mutation.assert();
|
|
75
|
+
}
|
|
76
|
+
writing() {
|
|
77
|
+
this.#writing = true;
|
|
78
|
+
this.mutation.destination("writing", this.pathname, this.identity);
|
|
79
|
+
}
|
|
80
|
+
published() {
|
|
81
|
+
this.mutation.destination("published", this.pathname, this.identity);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function* captureAtomicDestination(io, file, pathname, mutation, rejectHardlinks) {
|
|
85
|
+
const inspection = inspectAtomicIdentity(io, () => file.statExact());
|
|
86
|
+
const identity = (io.asynchronous ? (yield inspection) : inspection);
|
|
87
|
+
regular(identity, pathname, rejectHardlinks);
|
|
88
|
+
return new AtomicDestination(io, file, pathname, mutation, rejectHardlinks, identity);
|
|
123
89
|
}
|
|
@@ -1,49 +1,32 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { type TempPathRegistration } from "./temp-cleanup.js";
|
|
4
|
-
type AsyncOwnerFileSystem = Pick<typeof fs, "lstat" | "open" | "unlink">;
|
|
5
|
-
type SyncOwnerFileSystem = Pick<typeof syncFs, "closeSync" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "unlinkSync">;
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
import { type AtomicFile, type AtomicIo, type Procedure } from "./atomic-io.js";
|
|
6
3
|
export type AtomicTempFailure = Readonly<{
|
|
7
4
|
error: unknown;
|
|
8
5
|
}>;
|
|
9
6
|
export declare function removePathIfIdentityUnchanged(targetPath: string, identity: Pick<BigIntStats, "dev" | "ino">): Promise<void>;
|
|
10
|
-
declare class AtomicTempOwner
|
|
7
|
+
export declare class AtomicTempOwner {
|
|
11
8
|
readonly pathname: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
private readonly io;
|
|
10
|
+
private resource;
|
|
11
|
+
private recordedIdentity;
|
|
12
|
+
private exists;
|
|
13
|
+
private readonly unregister;
|
|
14
|
+
constructor(pathname: string, io: AtomicIo);
|
|
17
15
|
start(): void;
|
|
18
16
|
readonly onIdentity: (identity: BigIntStats) => void;
|
|
19
17
|
get identity(): BigIntStats;
|
|
20
18
|
markRenamed(): void;
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
export declare class AsyncAtomicTempOwner extends AtomicTempOwner<FileHandle> {
|
|
19
|
+
private takeResource;
|
|
24
20
|
adopt(temp: {
|
|
25
|
-
|
|
21
|
+
file: AtomicFile;
|
|
26
22
|
identity: BigIntStats;
|
|
27
23
|
}): void;
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
private inspectOwned;
|
|
25
|
+
assertCurrent(pathname?: string): Procedure<void>;
|
|
26
|
+
assertPublished(pathname: string, expectedHash?: string, onVerified?: (identity: BigIntStats) => void): Procedure<void>;
|
|
27
|
+
private cleanupOwnedPath;
|
|
30
28
|
finish(params: {
|
|
31
|
-
fsModule: AsyncOwnerFileSystem;
|
|
32
29
|
originalFailure?: AtomicTempFailure;
|
|
33
30
|
throwOnCleanupError: boolean;
|
|
34
|
-
}):
|
|
35
|
-
}
|
|
36
|
-
export declare class SyncAtomicTempOwner extends AtomicTempOwner<number> {
|
|
37
|
-
adopt(temp: {
|
|
38
|
-
fd: number;
|
|
39
|
-
identity: BigIntStats;
|
|
40
|
-
}): void;
|
|
41
|
-
assertCurrent(fsModule: SyncOwnerFileSystem, pathname?: string): void;
|
|
42
|
-
assertPublished(fsModule: SyncOwnerFileSystem, pathname: string, expectedHash?: string, onVerified?: (identity: BigIntStats) => void): void;
|
|
43
|
-
finish(params: {
|
|
44
|
-
fsModule: SyncOwnerFileSystem;
|
|
45
|
-
originalFailure?: AtomicTempFailure;
|
|
46
|
-
throwOnCleanupError: boolean;
|
|
47
|
-
}): void;
|
|
31
|
+
}): Procedure<void>;
|
|
48
32
|
}
|
|
49
|
-
export {};
|