@deepseek-ai/dsh-atomic-write 0.1.6-alpha.1 → 0.1.6-alpha.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/README.i18n.yaml +2 -2
- package/README.md +2 -0
- package/README.zh.md +2 -0
- package/lib/index.js +8 -3
- package/lib/types/index.d.ts +3 -2
- package/package.json +1 -1
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/util/atomic-write/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 89770a6d318da778e9dcb7a462dc7754244a0e41
|
|
6
|
+
README.zh.md: a64771ae1266f132254c82aaa71f193dbf33cd06
|
package/README.md
CHANGED
|
@@ -58,6 +58,8 @@ Only writers contend — readers never take the lock — and a contender backs o
|
|
|
58
58
|
|
|
59
59
|
### Failures to plan for
|
|
60
60
|
|
|
61
|
+
Windows retries one `EPERM` when the lock cannot be observed, because its holder can release between exclusive creation and the existence check. A repeated unconfirmed `EPERM` is rethrown without running the operation.
|
|
62
|
+
|
|
61
63
|
The lock's parent directory must already exist, so `withFileLock` rejects an invalid parent hierarchy before running the operation. A process that exits while holding the lock leaves the lock sibling behind; later writers time out, and an operator removes it only after verifying that no writer still owns it.
|
|
62
64
|
|
|
63
65
|
-----
|
package/README.zh.md
CHANGED
|
@@ -58,6 +58,8 @@ await withFileLock('/home/u/.dsh/settings.yaml', async () => {
|
|
|
58
58
|
|
|
59
59
|
### 需要规划的失败
|
|
60
60
|
|
|
61
|
+
Windows 在无法观察到锁时会对 `EPERM` 重试一次,因为持锁方可能在独占创建与存在性检查之间释放锁。再次出现无法确认锁存在的 `EPERM` 时,会重新抛出错误且不运行操作。
|
|
62
|
+
|
|
61
63
|
锁的父目录必须已经存在,因此 `withFileLock` 会在运行操作之前拒绝无效的父目录层级。持锁进程退出时会把锁文件留在原地;后续写入方超时失败,操作者只有在确认没有写入方仍持有该锁后才会移除它。
|
|
62
64
|
|
|
63
65
|
-----
|
package/lib/index.js
CHANGED
|
@@ -109,8 +109,9 @@ const DEFAULT_LOCK_WAIT_MS = 2e3;
|
|
|
109
109
|
* rename-based commit of {@link writeFileAtomic}, readers stay lock-free and
|
|
110
110
|
* only writers contend. `EEXIST` is contention directly; an `EPERM` is
|
|
111
111
|
* contention only when a fresh `lstat` confirms the lock path exists, covering
|
|
112
|
-
* Windows exclusive-create behavior
|
|
113
|
-
*
|
|
112
|
+
* Windows exclusive-create behavior. Windows retries one unconfirmed EPERM
|
|
113
|
+
* because the holder can release before the probe; a repeated unconfirmed
|
|
114
|
+
* permission error is rethrown. Contention backs off exponentially and times out
|
|
114
115
|
* after the deadline. The contender never removes an existing lock because
|
|
115
116
|
* file age cannot prove that its owner stopped; orphan recovery is an operator
|
|
116
117
|
* action. The parent directory must exist.
|
|
@@ -123,6 +124,7 @@ async function withFileLock(filename, operation, options) {
|
|
|
123
124
|
const lockPath = `${filename}.lock`;
|
|
124
125
|
const deadline = Date.now() + (options?.waitMs ?? DEFAULT_LOCK_WAIT_MS);
|
|
125
126
|
let delay = LOCK_RETRY_INITIAL_MS;
|
|
127
|
+
let retriedUnconfirmedPermissionError = false;
|
|
126
128
|
for (;;) {
|
|
127
129
|
try {
|
|
128
130
|
await writeFile(lockPath, `${process.pid}\n`, {
|
|
@@ -131,7 +133,10 @@ async function withFileLock(filename, operation, options) {
|
|
|
131
133
|
});
|
|
132
134
|
break;
|
|
133
135
|
} catch (error) {
|
|
134
|
-
if (!await isLockContention(error, lockPath))
|
|
136
|
+
if (!await isLockContention(error, lockPath)) {
|
|
137
|
+
if (process.platform !== "win32" || error?.code !== "EPERM" || retriedUnconfirmedPermissionError) throw error;
|
|
138
|
+
retriedUnconfirmedPermissionError = true;
|
|
139
|
+
}
|
|
135
140
|
}
|
|
136
141
|
if (Date.now() >= deadline) throw new Error(`atomic-write: timed out waiting for the writer lock at ${lockPath}`);
|
|
137
142
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
package/lib/types/index.d.ts
CHANGED
|
@@ -61,8 +61,9 @@ export interface FileLockOptions {
|
|
|
61
61
|
* rename-based commit of {@link writeFileAtomic}, readers stay lock-free and
|
|
62
62
|
* only writers contend. `EEXIST` is contention directly; an `EPERM` is
|
|
63
63
|
* contention only when a fresh `lstat` confirms the lock path exists, covering
|
|
64
|
-
* Windows exclusive-create behavior
|
|
65
|
-
*
|
|
64
|
+
* Windows exclusive-create behavior. Windows retries one unconfirmed EPERM
|
|
65
|
+
* because the holder can release before the probe; a repeated unconfirmed
|
|
66
|
+
* permission error is rethrown. Contention backs off exponentially and times out
|
|
66
67
|
* after the deadline. The contender never removes an existing lock because
|
|
67
68
|
* file age cannot prove that its owner stopped; orphan recovery is an operator
|
|
68
69
|
* action. The parent directory must exist.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-atomic-write",
|
|
3
3
|
"description": "Zero-dependency atomic file replacement: exclusive-create random-suffix temp + rename carrying the caller-stated permissions (writeFileAtomic)",
|
|
4
|
-
"version": "0.1.6-alpha.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|