nano-git 0.3.1 → 0.3.3

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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/dist/core/types.d.mts +1 -1
  3. package/dist/objects/tree.d.mts +4 -0
  4. package/dist/objects/tree.mjs +53 -2
  5. package/dist/repository/tree/tree-patch.mjs +4 -4
  6. package/dist/repository/tree/tree-walk.mjs +1 -1
  7. package/dist/repository/tree/tree-writer.mjs +1 -1
  8. package/dist/workdir/change-index-plan.mjs +2 -2
  9. package/dist/workdir/change-index.mjs +6 -6
  10. package/dist/workdir/core.d.mts +13 -44
  11. package/dist/workdir/directory-view.mjs +2 -2
  12. package/dist/workdir/dirty-dir-plan.mjs +3 -3
  13. package/dist/workdir/file-backend.d.mts +34 -12
  14. package/dist/workdir/file-backend.mjs +67 -86
  15. package/dist/workdir/file.d.mts +2 -2
  16. package/dist/workdir/file.mjs +2 -2
  17. package/dist/workdir/ids.d.mts +1 -1
  18. package/dist/workdir/ids.mjs +3 -3
  19. package/dist/workdir/memory-backend.mjs +4 -39
  20. package/dist/workdir/memory.d.mts +2 -3
  21. package/dist/workdir/memory.mjs +2 -3
  22. package/dist/workdir/nodes.d.mts +7 -7
  23. package/dist/workdir/nodes.mjs +3 -3
  24. package/dist/workdir/origin.mjs +2 -2
  25. package/dist/workdir/overlay.d.mts +3 -3
  26. package/dist/workdir/sqlite-backend.d.mts +31 -14
  27. package/dist/workdir/sqlite-backend.mjs +113 -118
  28. package/dist/workdir/sqlite.d.mts +2 -2
  29. package/dist/workdir/sqlite.mjs +2 -2
  30. package/dist/workdir/state-store.d.mts +4 -4
  31. package/dist/workdir/{session-internal.mjs → workdir-path.mjs} +10 -10
  32. package/dist/workdir/{session-transaction.mjs → workdir-transaction.mjs} +10 -10
  33. package/dist/workdir/workdir.d.mts +30 -0
  34. package/dist/workdir/{session.mjs → workdir.mjs} +17 -17
  35. package/dist/workdir/write-tree.mjs +5 -5
  36. package/package.json +1 -1
  37. package/dist/workdir/memory-backend.d.mts +0 -17
  38. package/dist/workdir/session-id.mjs +0 -18
  39. package/dist/workdir/session.d.mts +0 -30
@@ -1,83 +1,71 @@
1
1
  import { sha1 } from "../core/types.mjs";
2
2
  import { createRootDirectoryNode } from "./nodes.mjs";
3
- import { createVirtualWorkdirSessionId } from "./session-id.mjs";
4
- import { openVirtualWorkdirSession } from "./session.mjs";
3
+ import { openVirtualWorkdir } from "./workdir.mjs";
5
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
6
5
  import { dirname, join } from "node:path";
7
6
  //#region src/workdir/file-backend.ts
8
7
  /**
9
- * Virtual Workdir 文件系统 backend
8
+ * Virtual Workdir 文件系统持久化实现
10
9
  */
11
10
  const FILE_WORKDIR_MANIFEST_VERSION = 5;
12
11
  const FILE_WORKDIR_TRANSACTION_SNAPSHOT_SUFFIX = ".txn-snapshot";
13
12
  /**
14
- * 创建基于文件系统目录的 Virtual Workdir backend
13
+ * 打开基于目录持久化的 VirtualWorkdir
15
14
  *
16
15
  * @example
17
16
  * ```ts
18
- * const backend = createFileVirtualWorkdirBackend("/tmp/workdirs");
19
- * const sessionId = backend.createSession({ baseTree: tree });
20
- * const session = backend.openSession(repo.objects, sessionId);
21
- * expect(session.baseTree).toBe(tree);
17
+ * const workdir = openFileVirtualWorkdir(repo.objects, "/tmp/workdir", {
18
+ * baseTree: tree,
19
+ * create: true,
20
+ * });
21
+ * expect(workdir.baseTree).toBe(tree);
22
22
  * ```
23
23
  */
24
- function createFileVirtualWorkdirBackend(rootDir, options = {}) {
25
- const sessionsRoot = join(rootDir, options.sessionsDirName ?? "sessions");
26
- mkdirSync(sessionsRoot, { recursive: true });
27
- return {
28
- kind: "file",
29
- createSession(options) {
30
- const sessionId = createVirtualWorkdirSessionId();
31
- createFileVirtualWorkdirStateStore(sessionsRoot, sessionId).reset(options.baseTree);
32
- return sessionId;
33
- },
34
- openSession(source, sessionId) {
35
- if (!hasSession(sessionsRoot, sessionId)) throw new Error(`Virtual workdir session not found: ${sessionId}`);
36
- validateSessionIntegrity(sessionsRoot, sessionId);
37
- return openVirtualWorkdirSession(source, createFileVirtualWorkdirStateStore(sessionsRoot, sessionId));
38
- },
39
- deleteSession(sessionId) {
40
- if (!hasSession(sessionsRoot, sessionId)) throw new Error(`Virtual workdir session not found: ${sessionId}`);
41
- rmSync(getSessionDir(sessionsRoot, sessionId), {
42
- recursive: true,
43
- force: true
44
- });
45
- },
46
- listSessions() {
47
- if (!existsSync(sessionsRoot)) return [];
48
- return readdirSync(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).filter((entry) => !entry.name.endsWith(FILE_WORKDIR_TRANSACTION_SNAPSHOT_SUFFIX)).filter((entry) => existsSync(getManifestPath(join(sessionsRoot, entry.name)))).map((entry) => decodePathToken(entry.name)).filter((sessionId) => {
49
- try {
50
- validateSessionIntegrity(sessionsRoot, sessionId);
51
- return true;
52
- } catch {
53
- return false;
54
- }
55
- }).sort();
56
- }
57
- };
24
+ function openFileVirtualWorkdir(source, workdirDir, options) {
25
+ const store = createFileVirtualWorkdirStateStore(workdirDir);
26
+ if (!hasFileVirtualWorkdir(workdirDir)) {
27
+ if (options.create !== true) throw new Error(`Virtual workdir not found: ${workdirDir}`);
28
+ store.reset(options.baseTree);
29
+ }
30
+ validateFileVirtualWorkdirIntegrity(workdirDir);
31
+ return openVirtualWorkdir(source, store);
32
+ }
33
+ /**
34
+ * 删除指定目录上的持久化 VirtualWorkdir
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * deleteFileVirtualWorkdir("/tmp/workdir");
39
+ * ```
40
+ */
41
+ function deleteFileVirtualWorkdir(workdirDir) {
42
+ if (!hasFileVirtualWorkdir(workdirDir)) throw new Error(`Virtual workdir not found: ${workdirDir}`);
43
+ rmSync(workdirDir, {
44
+ recursive: true,
45
+ force: true
46
+ });
58
47
  }
59
48
  /**
60
- * 创建单个 session 的文件系统状态存储
49
+ * 创建单个文件系统 VirtualWorkdir 的状态存储
61
50
  *
62
51
  * @example
63
52
  * ```ts
64
- * const store = createFileVirtualWorkdirStateStore("/tmp/workdirs/sessions", sessionId);
53
+ * const store = createFileVirtualWorkdirStateStore("/tmp/workdir");
65
54
  * expect(store.kind).toBe("file");
66
55
  * ```
67
56
  */
68
- function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
69
- const sessionDir = getSessionDir(sessionsRoot, sessionId);
70
- const manifestPath = getManifestPath(sessionDir);
71
- const contentDir = getContentDir(sessionDir);
57
+ function createFileVirtualWorkdirStateStore(workdirDir) {
58
+ const manifestPath = getManifestPath(workdirDir);
59
+ const contentDir = getContentDir(workdirDir);
72
60
  return {
73
61
  kind: "file",
74
62
  transact(fn) {
75
- const snapshotDir = `${sessionDir}${FILE_WORKDIR_TRANSACTION_SNAPSHOT_SUFFIX}`;
63
+ const snapshotDir = `${workdirDir}${FILE_WORKDIR_TRANSACTION_SNAPSHOT_SUFFIX}`;
76
64
  rmSync(snapshotDir, {
77
65
  recursive: true,
78
66
  force: true
79
67
  });
80
- if (existsSync(sessionDir)) copyDirectoryRecursive(sessionDir, snapshotDir);
68
+ if (existsSync(workdirDir)) copyDirectoryRecursive(workdirDir, snapshotDir);
81
69
  try {
82
70
  const result = fn();
83
71
  rmSync(snapshotDir, {
@@ -86,11 +74,11 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
86
74
  });
87
75
  return result;
88
76
  } catch (error) {
89
- rmSync(sessionDir, {
77
+ rmSync(workdirDir, {
90
78
  recursive: true,
91
79
  force: true
92
80
  });
93
- if (existsSync(snapshotDir)) renameSync(snapshotDir, sessionDir);
81
+ if (existsSync(snapshotDir)) renameSync(snapshotDir, workdirDir);
94
82
  throw error;
95
83
  }
96
84
  },
@@ -98,7 +86,7 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
98
86
  return readManifest(manifestPath).baseTree;
99
87
  },
100
88
  writeBaseTree(baseTree) {
101
- updateManifest(sessionDir, (manifest) => ({
89
+ updateManifest(workdirDir, (manifest) => ({
102
90
  ...manifest,
103
91
  baseTree
104
92
  }));
@@ -109,7 +97,7 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
109
97
  return restoreNode(record, contentDir);
110
98
  },
111
99
  setNode(node) {
112
- updateManifest(sessionDir, (manifest) => {
100
+ updateManifest(workdirDir, (manifest) => {
113
101
  const record = persistNode(contentDir, node);
114
102
  return {
115
103
  ...manifest,
@@ -121,7 +109,7 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
121
109
  });
122
110
  },
123
111
  deleteNode(id) {
124
- updateManifest(sessionDir, (manifest) => {
112
+ updateManifest(workdirDir, (manifest) => {
125
113
  if (manifest.nodes[id] === void 0) return manifest;
126
114
  const { [id]: _deleted, ...rest } = manifest.nodes;
127
115
  return {
@@ -137,7 +125,7 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
137
125
  return readManifest(manifestPath).changeRecords.map(restoreChangeRecord).find((record) => record.path === path) ?? null;
138
126
  },
139
127
  setChangeRecord(record) {
140
- updateManifest(sessionDir, (manifest) => {
128
+ updateManifest(workdirDir, (manifest) => {
141
129
  const others = manifest.changeRecords.filter((item) => item.path !== record.path);
142
130
  return {
143
131
  ...manifest,
@@ -146,7 +134,7 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
146
134
  });
147
135
  },
148
136
  deleteChangeRecord(path) {
149
- updateManifest(sessionDir, (manifest) => ({
137
+ updateManifest(workdirDir, (manifest) => ({
150
138
  ...manifest,
151
139
  changeRecords: manifest.changeRecords.filter((item) => item.path !== path)
152
140
  }));
@@ -158,7 +146,7 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
158
146
  return readManifest(manifestPath).dirtyDirSummaries.map(restoreDirtyDirSummary).find((summary) => summary.path === path) ?? null;
159
147
  },
160
148
  setDirtyDirSummary(summary) {
161
- updateManifest(sessionDir, (manifest) => {
149
+ updateManifest(workdirDir, (manifest) => {
162
150
  const others = manifest.dirtyDirSummaries.filter((item) => item.path !== summary.path);
163
151
  return {
164
152
  ...manifest,
@@ -167,46 +155,42 @@ function createFileVirtualWorkdirStateStore(sessionsRoot, sessionId) {
167
155
  });
168
156
  },
169
157
  deleteDirtyDirSummary(path) {
170
- updateManifest(sessionDir, (manifest) => ({
158
+ updateManifest(workdirDir, (manifest) => ({
171
159
  ...manifest,
172
160
  dirtyDirSummaries: manifest.dirtyDirSummaries.filter((item) => item.path !== path)
173
161
  }));
174
162
  },
175
163
  reset(baseTree) {
176
- rmSync(sessionDir, {
164
+ rmSync(workdirDir, {
177
165
  recursive: true,
178
166
  force: true
179
167
  });
180
- ensureSessionDirs(sessionDir, contentDir);
168
+ ensureWorkdirDirs(workdirDir, contentDir);
181
169
  writeManifestAtomic(manifestPath, createManifest(baseTree, { [createRootDirectoryNode(baseTree).id]: serializeDirectoryNode(createRootDirectoryNode(baseTree)) }));
182
170
  }
183
171
  };
184
172
  }
185
- function hasSession(sessionsRoot, sessionId) {
186
- return existsSync(getManifestPath(getSessionDir(sessionsRoot, sessionId)));
173
+ function hasFileVirtualWorkdir(workdirDir) {
174
+ return existsSync(getManifestPath(workdirDir));
187
175
  }
188
- function validateSessionIntegrity(sessionsRoot, sessionId) {
189
- const sessionDir = getSessionDir(sessionsRoot, sessionId);
190
- const manifest = readManifest(getManifestPath(sessionDir));
176
+ function validateFileVirtualWorkdirIntegrity(workdirDir) {
177
+ const manifest = readManifest(getManifestPath(workdirDir));
191
178
  const root = manifest.nodes.root;
192
- if (root === void 0) throw new Error(`Virtual workdir session is corrupted: missing root node for ${sessionId}`);
193
- if (restoreNode(root, getContentDir(sessionDir)).state.kind !== "directory") throw new Error(`Virtual workdir session is corrupted: root node is not a directory for ${sessionId}`);
194
- for (const record of Object.values(manifest.nodes)) restoreNode(record, getContentDir(sessionDir));
179
+ if (root === void 0) throw new Error(`Virtual workdir is corrupted: missing root node for ${workdirDir}`);
180
+ if (restoreNode(root, getContentDir(workdirDir)).state.kind !== "directory") throw new Error(`Virtual workdir is corrupted: root node is not a directory for ${workdirDir}`);
181
+ for (const record of Object.values(manifest.nodes)) restoreNode(record, getContentDir(workdirDir));
195
182
  }
196
- function getSessionDir(sessionsRoot, sessionId) {
197
- return join(sessionsRoot, encodePathToken(sessionId));
183
+ function getManifestPath(workdirDir) {
184
+ return join(workdirDir, "manifest.json");
198
185
  }
199
- function getManifestPath(sessionDir) {
200
- return join(sessionDir, "manifest.json");
201
- }
202
- function getContentDir(sessionDir) {
203
- return join(sessionDir, "content");
186
+ function getContentDir(workdirDir) {
187
+ return join(workdirDir, "content");
204
188
  }
205
189
  function getContentPath(contentDir, payloadRef) {
206
190
  return join(contentDir, `${encodePathToken(payloadRef)}.bin`);
207
191
  }
208
- function ensureSessionDirs(sessionDir, contentDir) {
209
- mkdirSync(sessionDir, { recursive: true });
192
+ function ensureWorkdirDirs(workdirDir, contentDir) {
193
+ mkdirSync(workdirDir, { recursive: true });
210
194
  mkdirSync(contentDir, { recursive: true });
211
195
  }
212
196
  function copyDirectoryRecursive(sourceDir, targetDir) {
@@ -230,16 +214,16 @@ function createManifest(baseTree, nodes) {
230
214
  dirtyDirSummaries: []
231
215
  };
232
216
  }
233
- function updateManifest(sessionDir, updater) {
234
- const manifestPath = getManifestPath(sessionDir);
235
- ensureSessionDirs(sessionDir, getContentDir(sessionDir));
217
+ function updateManifest(workdirDir, updater) {
218
+ const manifestPath = getManifestPath(workdirDir);
219
+ ensureWorkdirDirs(workdirDir, getContentDir(workdirDir));
236
220
  writeManifestAtomic(manifestPath, updater(readManifest(manifestPath)));
237
221
  }
238
222
  function writeManifestAtomic(path, manifest) {
239
223
  writeJsonAtomic(path, manifest);
240
224
  }
241
225
  function readManifest(manifestPath) {
242
- if (!existsSync(manifestPath)) throw new Error(`Virtual workdir session manifest not found: ${manifestPath}`);
226
+ if (!existsSync(manifestPath)) throw new Error(`Virtual workdir manifest not found: ${manifestPath}`);
243
227
  const manifest = readJson(manifestPath);
244
228
  validateManifest(manifest);
245
229
  return manifest;
@@ -445,8 +429,5 @@ function writeBufferAtomic(path, value) {
445
429
  function encodePathToken(value) {
446
430
  return encodeURIComponent(value);
447
431
  }
448
- function decodePathToken(value) {
449
- return decodeURIComponent(value);
450
- }
451
432
  //#endregion
452
- export { createFileVirtualWorkdirBackend };
433
+ export { createFileVirtualWorkdirStateStore, deleteFileVirtualWorkdir, openFileVirtualWorkdir };
@@ -1,2 +1,2 @@
1
- import { CreateFileVirtualWorkdirBackendOptions, createFileVirtualWorkdirBackend } from "./file-backend.mjs";
2
- export { type CreateFileVirtualWorkdirBackendOptions, createFileVirtualWorkdirBackend };
1
+ import { OpenFileVirtualWorkdirOptions, createFileVirtualWorkdirStateStore, deleteFileVirtualWorkdir, openFileVirtualWorkdir } from "./file-backend.mjs";
2
+ export { type OpenFileVirtualWorkdirOptions, createFileVirtualWorkdirStateStore, deleteFileVirtualWorkdir, openFileVirtualWorkdir };
@@ -1,2 +1,2 @@
1
- import { createFileVirtualWorkdirBackend } from "./file-backend.mjs";
2
- export { createFileVirtualWorkdirBackend };
1
+ import { createFileVirtualWorkdirStateStore, deleteFileVirtualWorkdir, openFileVirtualWorkdir } from "./file-backend.mjs";
2
+ export { createFileVirtualWorkdirStateStore, deleteFileVirtualWorkdir, openFileVirtualWorkdir };
@@ -1,5 +1,5 @@
1
1
  //#region src/workdir/ids.d.ts
2
- /** 会话内节点的稳定身份 */
2
+ /** workdir 内节点的稳定身份 */
3
3
  type NodeId = string & {
4
4
  readonly __brand: "NodeId";
5
5
  };
@@ -1,9 +1,9 @@
1
1
  //#region src/workdir/ids.ts
2
- /** 根目录节点的固定 ID(每个 session 一致) */
2
+ /** 根目录节点的固定 ID(每个 workdir 一致) */
3
3
  const VIRTUAL_ROOT_NODE_ID = "root";
4
4
  let nextNodeCounter = 1;
5
5
  /**
6
- * 分配新的 session 节点 ID
6
+ * 分配新的 workdir 节点 ID
7
7
  */
8
8
  function createNodeId() {
9
9
  const id = `node:${nextNodeCounter}`;
@@ -11,7 +11,7 @@ function createNodeId() {
11
11
  return id;
12
12
  }
13
13
  /**
14
- * 为仓库对象哈希派生稳定的 session 节点 ID(懒加载、未 copy 前复用)
14
+ * 为仓库对象哈希派生稳定的 workdir 节点 ID(懒加载、未 copy 前复用)
15
15
  */
16
16
  function originBackedNodeId(hash) {
17
17
  return `origin:${hash}`;
@@ -1,7 +1,5 @@
1
1
  import { VIRTUAL_ROOT_NODE_ID } from "./ids.mjs";
2
2
  import { createRootDirectoryNode } from "./nodes.mjs";
3
- import { createVirtualWorkdirSessionId } from "./session-id.mjs";
4
- import { openVirtualWorkdirSession } from "./session.mjs";
5
3
  //#region src/workdir/memory-backend.ts
6
4
  /**
7
5
  * Virtual Workdir 内存状态存储
@@ -78,39 +76,6 @@ function createVirtualWorkdirMemoryStateStore(baseTree) {
78
76
  }
79
77
  };
80
78
  }
81
- /**
82
- * 创建内存版 Virtual Workdir backend
83
- *
84
- * @example
85
- * ```ts
86
- * const backend = createMemoryVirtualWorkdirBackend();
87
- * const sessionId = backend.createSession({ baseTree: tree });
88
- * const session = backend.openSession(repo.objects, sessionId);
89
- * expect(session.baseTree).toBe(tree);
90
- * ```
91
- */
92
- function createMemoryVirtualWorkdirBackend() {
93
- const sessions = /* @__PURE__ */ new Map();
94
- return {
95
- kind: "memory",
96
- createSession(options) {
97
- const sessionId = createVirtualWorkdirSessionId();
98
- sessions.set(sessionId, createVirtualWorkdirMemoryStateStore(options.baseTree));
99
- return sessionId;
100
- },
101
- openSession(source, sessionId) {
102
- const store = sessions.get(sessionId);
103
- if (store === void 0) throw new Error(`Virtual workdir session not found: ${sessionId}`);
104
- return openVirtualWorkdirSession(source, store);
105
- },
106
- deleteSession(sessionId) {
107
- if (!sessions.delete(sessionId)) throw new Error(`Virtual workdir session not found: ${sessionId}`);
108
- },
109
- listSessions() {
110
- return Array.from(sessions.keys());
111
- }
112
- };
113
- }
114
79
  function resetState(state, baseTree) {
115
80
  state.baseTree = baseTree;
116
81
  state.nodes.clear();
@@ -120,7 +85,7 @@ function resetState(state, baseTree) {
120
85
  }
121
86
  function snapshotState(state) {
122
87
  const nodes = /* @__PURE__ */ new Map();
123
- for (const [nodeId, node] of state.nodes) nodes.set(nodeId, cloneSessionNode(node));
88
+ for (const [nodeId, node] of state.nodes) nodes.set(nodeId, cloneWorkdirNode(node));
124
89
  return {
125
90
  baseTree: state.baseTree,
126
91
  nodes,
@@ -133,11 +98,11 @@ function restoreState(state, snapshot) {
133
98
  state.nodes.clear();
134
99
  state.changeRecords.clear();
135
100
  state.dirtyDirSummaries.clear();
136
- for (const [nodeId, node] of snapshot.nodes) state.nodes.set(nodeId, cloneSessionNode(node));
101
+ for (const [nodeId, node] of snapshot.nodes) state.nodes.set(nodeId, cloneWorkdirNode(node));
137
102
  for (const [path, record] of snapshot.changeRecords) state.changeRecords.set(path, record);
138
103
  for (const [path, summary] of snapshot.dirtyDirSummaries) state.dirtyDirSummaries.set(path, summary);
139
104
  }
140
- function cloneSessionNode(node) {
105
+ function cloneWorkdirNode(node) {
141
106
  if (node.state.kind === "directory") return {
142
107
  id: node.id,
143
108
  origin: node.origin,
@@ -175,4 +140,4 @@ function cloneSessionNode(node) {
175
140
  };
176
141
  }
177
142
  //#endregion
178
- export { createMemoryVirtualWorkdirBackend, createVirtualWorkdirMemoryStateStore };
143
+ export { createVirtualWorkdirMemoryStateStore };
@@ -1,3 +1,2 @@
1
- import { createMemoryVirtualWorkdirBackend } from "./memory-backend.mjs";
2
- import { createVirtualWorkdirSession, openVirtualWorkdirSession } from "./session.mjs";
3
- export { createMemoryVirtualWorkdirBackend, createVirtualWorkdirSession, openVirtualWorkdirSession };
1
+ import { createVirtualWorkdir, openVirtualWorkdir } from "./workdir.mjs";
2
+ export { createVirtualWorkdir, openVirtualWorkdir };
@@ -1,3 +1,2 @@
1
- import { createVirtualWorkdirSession, openVirtualWorkdirSession } from "./session.mjs";
2
- import { createMemoryVirtualWorkdirBackend } from "./memory-backend.mjs";
3
- export { createMemoryVirtualWorkdirBackend, createVirtualWorkdirSession, openVirtualWorkdirSession };
1
+ import { createVirtualWorkdir, openVirtualWorkdir } from "./workdir.mjs";
2
+ export { createVirtualWorkdir, openVirtualWorkdir };
@@ -6,7 +6,7 @@ import { DirectoryOverlay } from "./overlay.mjs";
6
6
  /** Blob / 符号链接在 origin 与 state 中使用的 mode */
7
7
  type BlobObjectMode = "100644" | "100755" | "120000";
8
8
  /**
9
- * 节点来源(repo 对象或纯 session 新建)
9
+ * 节点来源(repo 对象或纯 workdir 新建)
10
10
  */
11
11
  type NodeOrigin = {
12
12
  readonly kind: "none";
@@ -21,7 +21,7 @@ type NodeOrigin = {
21
21
  /**
22
22
  * 目录节点当前状态
23
23
  *
24
- * `overlay` 表达 session 层增删改;子项 nodeId 通过 overlay 合成与懒加载解析。
24
+ * `overlay` 表达 workdir 层增删改;子项 nodeId 通过 overlay 合成与懒加载解析。
25
25
  */
26
26
  interface DirectoryNodeState {
27
27
  readonly kind: "directory";
@@ -44,14 +44,14 @@ interface SymlinkNodeState {
44
44
  readonly mode: "120000";
45
45
  readonly target?: Buffer;
46
46
  }
47
- type SessionNodeState = DirectoryNodeState | FileNodeState | SymlinkNodeState;
47
+ type WorkdirNodeState = DirectoryNodeState | FileNodeState | SymlinkNodeState;
48
48
  /**
49
- * 完整的会话节点记录
49
+ * 完整的 workdir 节点记录
50
50
  */
51
- interface SessionNode {
51
+ interface WorkdirNode {
52
52
  readonly id: NodeId;
53
53
  readonly origin: NodeOrigin;
54
- readonly state: SessionNodeState;
54
+ readonly state: WorkdirNodeState;
55
55
  }
56
56
  //#endregion
57
- export { SessionNode };
57
+ export { WorkdirNode };
@@ -2,7 +2,7 @@ import { VIRTUAL_ROOT_NODE_ID } from "./ids.mjs";
2
2
  import { cloneDirectoryOverlay, createEmptyDirectoryOverlay } from "./overlay.mjs";
3
3
  //#region src/workdir/nodes.ts
4
4
  /**
5
- * Virtual Workdir 会话节点状态模型
5
+ * Virtual Workdir 节点状态模型
6
6
  *
7
7
  * 节点身份(nodeId)与目录路径绑定分离;origin 描述 repo-backed 来源。
8
8
  */
@@ -63,7 +63,7 @@ function revertNodeState(node) {
63
63
  /**
64
64
  * 为 `copy` 创建新节点:共享 origin,目录为浅复制(子项绑定保留,但 nodeId 为新)
65
65
  */
66
- function cloneSessionNodeForCopy(source, newId) {
66
+ function cloneWorkdirNodeForCopy(source, newId) {
67
67
  const origin = source.origin;
68
68
  if (source.state.kind === "directory") return {
69
69
  id: newId,
@@ -103,4 +103,4 @@ function cloneSessionNodeForCopy(source, newId) {
103
103
  };
104
104
  }
105
105
  //#endregion
106
- export { cloneSessionNodeForCopy, createRootDirectoryNode, revertNodeState };
106
+ export { cloneWorkdirNodeForCopy, createRootDirectoryNode, revertNodeState };
@@ -28,7 +28,7 @@ function readRepoBlobContent(source, hash, path) {
28
28
  * 根据 tree 条目构造节点 origin
29
29
  */
30
30
  function treeEntryToNodeOrigin(entry) {
31
- if (entry.mode === "40000") return {
31
+ if (entry.mode === "040000") return {
32
32
  kind: "repo-tree",
33
33
  hash: entry.hash
34
34
  };
@@ -47,7 +47,7 @@ function treeEntryToNodeOrigin(entry) {
47
47
  * Git mode 转为 VirtualEntryKind
48
48
  */
49
49
  function modeToVirtualEntryKind(mode) {
50
- if (mode === "40000") return "tree";
50
+ if (mode === "040000") return "tree";
51
51
  if (mode === "120000") return "symlink";
52
52
  return "blob";
53
53
  }
@@ -2,12 +2,12 @@ import { NodeId } from "./ids.mjs";
2
2
 
3
3
  //#region src/workdir/overlay.d.ts
4
4
  /**
5
- * 目录 overlay 状态(挂在目录 SessionNode 上)
5
+ * 目录 overlay 状态(挂在目录 WorkdirNode 上)
6
6
  */
7
7
  interface DirectoryOverlay {
8
- /** session 新增或覆盖:条目名 -> 绑定的 nodeId */
8
+ /** workdir 新增或覆盖:条目名 -> 绑定的 nodeId */
9
9
  readonly addedEntries: Map<string, NodeId>;
10
- /** session 删除的 origin/先前条目名(tombstone) */
10
+ /** workdir 删除的 origin/先前条目名(tombstone) */
11
11
  readonly deletedNames: Set<string>;
12
12
  }
13
13
  //#endregion
@@ -1,30 +1,47 @@
1
- import { VirtualWorkdirBackend } from "./core.mjs";
1
+ import { ObjectDatabase } from "../core/types/odb.mjs";
2
+ import { CreateVirtualWorkdirOptions, VirtualWorkdir } from "./core.mjs";
2
3
  import { Database } from "bun:sqlite";
3
4
 
4
5
  //#region src/workdir/sqlite-backend.d.ts
5
- /** 创建 SQLite Virtual Workdir backend 的可选参数 */
6
- interface CreateSqliteVirtualWorkdirBackendOptions {
6
+ /** SQLite 连接层的可选参数 */
7
+ interface SqliteVirtualWorkdirConnectionOptions {
7
8
  /** 开启 WAL 模式,默认 true */
8
9
  readonly walMode?: boolean;
9
10
  }
11
+ /** 打开 SQLite VirtualWorkdir 的可选参数 */
12
+ interface OpenSqliteVirtualWorkdirOptions extends CreateVirtualWorkdirOptions, SqliteVirtualWorkdirConnectionOptions {
13
+ /** 不存在时按 baseTree 初始化 */
14
+ readonly create?: boolean;
15
+ }
10
16
  /**
11
- * SQLite Virtual Workdir backend(含资源释放能力)
17
+ * 基于 SQLite VirtualWorkdir
18
+ *
19
+ * 返回值附带 `[Symbol.dispose]()`,用于释放内部数据库连接。
12
20
  */
13
- interface SqliteVirtualWorkdirBackend extends VirtualWorkdirBackend {
14
- /** 释放 SQLite 数据库连接 */
21
+ type SqliteVirtualWorkdir = VirtualWorkdir & {
15
22
  [Symbol.dispose](): void;
16
- }
23
+ };
24
+ /**
25
+ * 打开基于 SQLite 的持久化 VirtualWorkdir
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * using workdir = openSqliteVirtualWorkdir(repo.objects, ":memory:", "demo", {
30
+ * baseTree: tree,
31
+ * create: true,
32
+ * });
33
+ * expect(workdir.baseTree).toBe(tree);
34
+ * ```
35
+ */
36
+ declare function openSqliteVirtualWorkdir(source: ObjectDatabase, dbPath: string, workdirKey: string, options: OpenSqliteVirtualWorkdirOptions): SqliteVirtualWorkdir;
17
37
  /**
18
- * 创建基于 SQLite Virtual Workdir backend
38
+ * 删除指定 key 上的 SQLite VirtualWorkdir
19
39
  *
20
40
  * @example
21
41
  * ```ts
22
- * using backend = createSqliteVirtualWorkdirBackend(":memory:");
23
- * const sessionId = backend.createSession({ baseTree: tree });
24
- * const session = backend.openSession(repo.objects, sessionId);
25
- * expect(session.baseTree).toBe(tree);
42
+ * deleteSqliteVirtualWorkdir("/tmp/workdir.sqlite", "demo");
26
43
  * ```
27
44
  */
28
- declare function createSqliteVirtualWorkdirBackend(dbPath: string, options?: CreateSqliteVirtualWorkdirBackendOptions): SqliteVirtualWorkdirBackend;
45
+ declare function deleteSqliteVirtualWorkdir(dbPath: string, workdirKey: string, options?: SqliteVirtualWorkdirConnectionOptions): void;
29
46
  //#endregion
30
- export { CreateSqliteVirtualWorkdirBackendOptions, SqliteVirtualWorkdirBackend, createSqliteVirtualWorkdirBackend };
47
+ export { OpenSqliteVirtualWorkdirOptions, SqliteVirtualWorkdir, deleteSqliteVirtualWorkdir, openSqliteVirtualWorkdir };