@deepseek-ai/dsh-atomic-write 0.0.1-rc.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/LICENSE +28 -0
- package/README.i18n.yaml +6 -0
- package/README.md +45 -0
- package/README.zh.md +45 -0
- package/lib/index.js +97 -0
- package/lib/invariant.js +23 -0
- package/lib/types/index.d.ts +57 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, DeepSeek
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/util/atomic-write/README.md
|
|
5
|
+
README.md: 2ff4abb6ac10d8b592ccd2056b4f1f92cc8518b0
|
|
6
|
+
README.zh.md: 570a5243aefbe6870c1991786f711adc77faacae
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# dsh-atomic-write
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
Zero-dependency atomic file replacement shared by file-backed stores that must never leave partial, symlink-hijacked, or wider-than-intended content on disk — the user-settings document (`dsh-settings-local`) and the credentials store (`dsh-credentials-local`).
|
|
6
|
+
|
|
7
|
+
## Surface
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { withFileLock, writeFileAtomic } from '@deepseek-ai/dsh-atomic-write'
|
|
11
|
+
|
|
12
|
+
declare const text: string
|
|
13
|
+
declare const render: (previous: string) => string
|
|
14
|
+
|
|
15
|
+
await writeFileAtomic('/home/u/.dsh/settings.yaml', text, { mode: 0o600 })
|
|
16
|
+
|
|
17
|
+
// Read-modify-write against the same file from several processes.
|
|
18
|
+
await withFileLock('/home/u/.dsh/settings.yaml', async () => {
|
|
19
|
+
await writeFileAtomic('/home/u/.dsh/settings.yaml', render(text), { mode: 0o600 })
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`writeFileAtomic` commits one already-rendered string. The contract, in the order failures would exploit it:
|
|
24
|
+
|
|
25
|
+
- **Exclusive-create temp** (`wx`, random suffix): the open refuses to follow a symlink planted at a guessable temp path.
|
|
26
|
+
- **The fresh inode carries `mode` through the rename**: replacing a wider-permission file narrows it without a chmod race. `mode` is required so the permission decision stays visible at every call site (subject to the process umask, like every fresh inode).
|
|
27
|
+
- **`rename` replaces a symlinked target itself**, never writing through to its referent.
|
|
28
|
+
- **Same-directory sibling** keeps the rename on one filesystem, so the swap stays atomic.
|
|
29
|
+
- Parent directories are created; on any failure the temp is removed and the failure rethrown; readers observe either the old or the new complete content.
|
|
30
|
+
|
|
31
|
+
`withFileLock` serializes the writers of one file across processes, for the read-render-commit cycles a bare atomic commit cannot make safe on its own. The lock is a `wx`-created `<filename>.lock` sibling, so readers never contend; waiters back off exponentially and fail with a timeout rather than block forever. A contender never removes the existing lock: age cannot distinguish a crashed owner from a paused live writer.
|
|
32
|
+
|
|
33
|
+
## Model Experience
|
|
34
|
+
|
|
35
|
+
None, as this is a pure filesystem primitive; nothing here reaches a model request.
|
|
36
|
+
|
|
37
|
+
#### KV Cache effect
|
|
38
|
+
|
|
39
|
+
None; nothing here enters a request prefix.
|
|
40
|
+
|
|
41
|
+
## Known Limitations and Deferred Work
|
|
42
|
+
|
|
43
|
+
- **Atomic, not durable** — no `fsync` of the file or its directory, so after a crash the rename may be observed unwound. The file-backed stores here re-read and republish on boot, keeping durability the caller's policy.
|
|
44
|
+
- **String content only** — no `Buffer` or stream form until a consumer needs one.
|
|
45
|
+
- **Orphaned locks require operator recovery** — a process that exits while holding the lock can leave the sibling behind. Later writers time out without deleting it; an operator removes it only after verifying that no writer still owns it. File age alone is not safe evidence of abandonment.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# dsh-atomic-write
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
零依赖的原子文件替换,供绝不允许在磁盘上留下不完整、被符号链接劫持或权限过宽内容的文件型存储共用:用户设置文档(`dsh-settings-local`)与凭据存储(`dsh-credentials-local`)。
|
|
6
|
+
|
|
7
|
+
## 接口面
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { withFileLock, writeFileAtomic } from '@deepseek-ai/dsh-atomic-write'
|
|
11
|
+
|
|
12
|
+
declare const text: string
|
|
13
|
+
declare const render: (previous: string) => string
|
|
14
|
+
|
|
15
|
+
await writeFileAtomic('/home/u/.dsh/settings.yaml', text, { mode: 0o600 })
|
|
16
|
+
|
|
17
|
+
// Read-modify-write against the same file from several processes.
|
|
18
|
+
await withFileLock('/home/u/.dsh/settings.yaml', async () => {
|
|
19
|
+
await writeFileAtomic('/home/u/.dsh/settings.yaml', render(text), { mode: 0o600 })
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`writeFileAtomic` 提交一份已经渲染好的字符串。约定按故障利用它的先后顺序列出:
|
|
24
|
+
|
|
25
|
+
- **独占创建临时文件**(`wx` + 随机后缀):open 拒绝跟随预先埋在可猜测临时路径上的符号链接。
|
|
26
|
+
- **全新 inode 携带 `mode` 走完 rename**:替换权限过宽的旧文件时直接收窄,不存在 chmod 竞态。`mode` 为必填,让权限决策始终可见于每个调用点(与所有新建 inode 一样受进程 umask 影响)。
|
|
27
|
+
- **`rename` 替换的是符号链接目标本身**,绝不写穿到其指向的文件。
|
|
28
|
+
- **同目录兄弟文件**保证 rename 落在同一文件系统上,交换保持原子。
|
|
29
|
+
- 自动创建父目录;任何失败都会移除临时文件并重新抛出该失败;读取方只会观察到旧内容或完整的新内容。
|
|
30
|
+
|
|
31
|
+
`withFileLock` 跨进程串行化同一文件的写入方,服务于单靠原子提交无法保证安全的读-渲染-提交循环。锁是以 `wx` 创建的同目录 `<filename>.lock`,因此读取方从不参与竞争;等待方按指数退避,超时即失败而非无限阻塞。竞争者绝不移除现有锁:锁龄无法区分已经崩溃的所有者与被暂停但仍存活的写入方。
|
|
32
|
+
|
|
33
|
+
## 模型体验
|
|
34
|
+
|
|
35
|
+
无:本包是纯文件系统原语,此处没有任何内容会到达模型请求。
|
|
36
|
+
|
|
37
|
+
#### KV Cache 影响
|
|
38
|
+
|
|
39
|
+
无;此处没有任何内容会进入请求前缀。
|
|
40
|
+
|
|
41
|
+
## 已知限制与暂缓事项
|
|
42
|
+
|
|
43
|
+
- **原子但不保证持久**——不对文件或其所在目录做 `fsync`,因此崩溃后可能观察到 rename 被回退。此处的文件型存储在启动时重新读取并重新发布,把持久性留作调用方的策略。
|
|
44
|
+
- **仅支持字符串内容**——在有消费方需要之前,不提供 `Buffer` 或流式形态。
|
|
45
|
+
- **遗留锁需要操作者恢复**——进程持锁退出时可能留下同级锁文件。后续写入方超时也不会删除它;操作者只有在确认没有写入方仍拥有该锁后才会移除。文件存续时间本身不能安全证明它已无人持有。
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
//#region lib/types/index.js
|
|
5
|
+
/**
|
|
6
|
+
* Zero-dependency atomic file replacement and writer coordination.
|
|
7
|
+
* `writeFileAtomic` writes a random-suffix sibling with exclusive create and
|
|
8
|
+
* the caller's permission bits, then renames it over the target, so readers
|
|
9
|
+
* observe either the old or the new complete content and a replaced file ends
|
|
10
|
+
* up with exactly the stated mode. `withFileLock` serializes cross-process
|
|
11
|
+
* writers of one file through a `wx`-created `<file>.lock` sibling, so a
|
|
12
|
+
* read-modify-write cycle can never resurrect a state another writer just
|
|
13
|
+
* replaced; readers stay lock-free because the rename commit is atomic.
|
|
14
|
+
* @module @deepseek-ai/dsh-atomic-write
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Replace `filename` with `content` in one atomic step, creating parent
|
|
18
|
+
* directories. The content is first written to a random-suffix sibling opened
|
|
19
|
+
* with exclusive create (`wx`): the open refuses to follow a symlink planted
|
|
20
|
+
* at the temp path, and the fresh inode carries `options.mode` through the
|
|
21
|
+
* rename, so replacing a wider-permission file narrows it without a chmod
|
|
22
|
+
* race. The rename also replaces a symlinked target itself instead of writing
|
|
23
|
+
* through to its referent, and the same-directory sibling keeps the rename on
|
|
24
|
+
* one filesystem. On any failure the temp file is removed and the failure
|
|
25
|
+
* rethrown. Crash durability (fsync) is out of scope.
|
|
26
|
+
* @param filename - final path receiving the content.
|
|
27
|
+
* @param content - complete next file content.
|
|
28
|
+
* @param options - permission bits for the replacement inode.
|
|
29
|
+
*/
|
|
30
|
+
async function writeFileAtomic(filename, content, options) {
|
|
31
|
+
await mkdir(dirname(filename), {
|
|
32
|
+
recursive: true,
|
|
33
|
+
...options.dirMode === void 0 ? {} : { mode: options.dirMode }
|
|
34
|
+
});
|
|
35
|
+
const temp = `${filename}.${randomBytes(6).toString("hex")}.tmp`;
|
|
36
|
+
try {
|
|
37
|
+
await writeFile(temp, content, {
|
|
38
|
+
mode: options.mode,
|
|
39
|
+
flag: "wx"
|
|
40
|
+
});
|
|
41
|
+
await rename(temp, filename);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
await rm(temp, { force: true });
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Whether an exclusive create failed because the path already exists. */
|
|
48
|
+
function isEEXIST(error) {
|
|
49
|
+
return error?.code === "EEXIST";
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Writer-lock protocol constants. These are robustness invariants of the
|
|
53
|
+
* cross-process write protocol, not deployment tunables: contention normally
|
|
54
|
+
* resolves within the retry deadline, while expiry fails the contender without
|
|
55
|
+
* guessing whether the existing lock still has an owner.
|
|
56
|
+
*/
|
|
57
|
+
const LOCK_RETRY_INITIAL_MS = 20;
|
|
58
|
+
const LOCK_RETRY_MAX_MS = 200;
|
|
59
|
+
const LOCK_TIMEOUT_MS = 2e3;
|
|
60
|
+
/**
|
|
61
|
+
* Hold the cross-process writer lock for `filename` around one operation. The
|
|
62
|
+
* lock is a `wx`-created sibling (`<filename>.lock`); paired with the
|
|
63
|
+
* rename-based commit of {@link writeFileAtomic}, readers stay lock-free and
|
|
64
|
+
* only writers contend. Contention backs off exponentially and fails with a
|
|
65
|
+
* timed-out error after the deadline. The contender never removes an existing
|
|
66
|
+
* lock because file age cannot prove that its owner stopped; orphan recovery
|
|
67
|
+
* is an operator action. The parent directory must exist.
|
|
68
|
+
* @param filename - the file whose writers this lock serializes.
|
|
69
|
+
* @param operation - the read-render-commit cycle to run while holding the lock.
|
|
70
|
+
* @returns the operation's result; the lock releases on both outcomes.
|
|
71
|
+
*/
|
|
72
|
+
async function withFileLock(filename, operation) {
|
|
73
|
+
const lockPath = `${filename}.lock`;
|
|
74
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
75
|
+
let delay = LOCK_RETRY_INITIAL_MS;
|
|
76
|
+
for (;;) {
|
|
77
|
+
try {
|
|
78
|
+
await writeFile(lockPath, `${process.pid}\n`, {
|
|
79
|
+
mode: 384,
|
|
80
|
+
flag: "wx"
|
|
81
|
+
});
|
|
82
|
+
break;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (!isEEXIST(error)) throw error;
|
|
85
|
+
}
|
|
86
|
+
if (Date.now() >= deadline) throw new Error(`atomic-write: timed out waiting for the writer lock at ${lockPath}`);
|
|
87
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
88
|
+
delay = Math.min(delay * 2, LOCK_RETRY_MAX_MS);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return await operation();
|
|
92
|
+
} finally {
|
|
93
|
+
await rm(lockPath, { force: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { withFileLock, writeFileAtomic };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-atomic-write`.
|
|
4
|
+
* @module @deepseek-ai/dsh-atomic-write/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@deepseek-ai/dsh-atomic-write";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "atomic-write-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: this pure filesystem primitive owns no event stream or mutable runtime
|
|
13
|
+
* data; its replacement contract is enforced by unit tests.
|
|
14
|
+
*/
|
|
15
|
+
const install = () => {};
|
|
16
|
+
/**
|
|
17
|
+
* Register this package's invariant companion.
|
|
18
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
+
*/
|
|
21
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
+
//#endregion
|
|
23
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency atomic file replacement and writer coordination.
|
|
3
|
+
* `writeFileAtomic` writes a random-suffix sibling with exclusive create and
|
|
4
|
+
* the caller's permission bits, then renames it over the target, so readers
|
|
5
|
+
* observe either the old or the new complete content and a replaced file ends
|
|
6
|
+
* up with exactly the stated mode. `withFileLock` serializes cross-process
|
|
7
|
+
* writers of one file through a `wx`-created `<file>.lock` sibling, so a
|
|
8
|
+
* read-modify-write cycle can never resurrect a state another writer just
|
|
9
|
+
* replaced; readers stay lock-free because the rename commit is atomic.
|
|
10
|
+
* @module @deepseek-ai/dsh-atomic-write
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Filesystem options for {@link writeFileAtomic}; `mode` is required so the
|
|
14
|
+
* permission decision stays visible at every call site.
|
|
15
|
+
*/
|
|
16
|
+
export interface WriteFileAtomicOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Permission bits stamped on the fresh temp inode and carried through the
|
|
19
|
+
* rename (subject to the process umask, like every fresh inode).
|
|
20
|
+
*/
|
|
21
|
+
mode: number;
|
|
22
|
+
/**
|
|
23
|
+
* Permission bits for parent directories this call creates (subject to the
|
|
24
|
+
* umask; existing directories keep their mode). Omission uses the mkdir
|
|
25
|
+
* default — pass `0o700` when the tree holds user-private data.
|
|
26
|
+
*/
|
|
27
|
+
dirMode?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Replace `filename` with `content` in one atomic step, creating parent
|
|
31
|
+
* directories. The content is first written to a random-suffix sibling opened
|
|
32
|
+
* with exclusive create (`wx`): the open refuses to follow a symlink planted
|
|
33
|
+
* at the temp path, and the fresh inode carries `options.mode` through the
|
|
34
|
+
* rename, so replacing a wider-permission file narrows it without a chmod
|
|
35
|
+
* race. The rename also replaces a symlinked target itself instead of writing
|
|
36
|
+
* through to its referent, and the same-directory sibling keeps the rename on
|
|
37
|
+
* one filesystem. On any failure the temp file is removed and the failure
|
|
38
|
+
* rethrown. Crash durability (fsync) is out of scope.
|
|
39
|
+
* @param filename - final path receiving the content.
|
|
40
|
+
* @param content - complete next file content.
|
|
41
|
+
* @param options - permission bits for the replacement inode.
|
|
42
|
+
*/
|
|
43
|
+
export declare function writeFileAtomic(filename: string, content: string, options: WriteFileAtomicOptions): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Hold the cross-process writer lock for `filename` around one operation. The
|
|
46
|
+
* lock is a `wx`-created sibling (`<filename>.lock`); paired with the
|
|
47
|
+
* rename-based commit of {@link writeFileAtomic}, readers stay lock-free and
|
|
48
|
+
* only writers contend. Contention backs off exponentially and fails with a
|
|
49
|
+
* timed-out error after the deadline. The contender never removes an existing
|
|
50
|
+
* lock because file age cannot prove that its owner stopped; orphan recovery
|
|
51
|
+
* is an operator action. The parent directory must exist.
|
|
52
|
+
* @param filename - the file whose writers this lock serializes.
|
|
53
|
+
* @param operation - the read-render-commit cycle to run while holding the lock.
|
|
54
|
+
* @returns the operation's result; the lock releases on both outcomes.
|
|
55
|
+
*/
|
|
56
|
+
export declare function withFileLock<T>(filename: string, operation: () => Promise<T>): Promise<T>;
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-atomic-write`.
|
|
3
|
+
* @module @deepseek-ai/dsh-atomic-write/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "atomic-write-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deepseek-ai/dsh-atomic-write",
|
|
3
|
+
"description": "Zero-dependency atomic file replacement: exclusive-create random-suffix temp + rename carrying the caller-stated permissions (writeFileAtomic)",
|
|
4
|
+
"version": "0.0.1-rc.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "restricted"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/util/atomic-write"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./src/*": "./src/*",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"lib/index.js",
|
|
30
|
+
"lib/invariant.js",
|
|
31
|
+
"lib/types/**/*.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"license": "BSD-3-Clause",
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
36
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1"
|
|
41
|
+
}
|
|
42
|
+
}
|