@tita-agent/dsh-data-table 0.0.0-stage → 0.2.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.
Files changed (267) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -2
  3. package/cordis.patch.yml +7 -0
  4. package/lib/client.js +16766 -0
  5. package/lib/contract-envelope.d.ts +91 -0
  6. package/lib/contract-envelope.js +106 -0
  7. package/lib/contract-layout.d.ts +130 -0
  8. package/lib/contract-layout.js +138 -0
  9. package/lib/contract-refs.d.ts +39 -0
  10. package/lib/contract-refs.js +9 -0
  11. package/lib/contract-version.d.ts +311 -0
  12. package/lib/contract-version.js +270 -0
  13. package/lib/contract-wire.d.ts +147 -0
  14. package/lib/contract-wire.js +99 -0
  15. package/lib/contracts.d.ts +12 -0
  16. package/lib/contracts.js +12 -0
  17. package/lib/types/audit.d.ts +42 -0
  18. package/lib/types/audit.js +39 -0
  19. package/lib/types/client/ProjectImportMenu.d.ts +30 -0
  20. package/lib/types/client/ProjectImportMenu.js +70 -0
  21. package/lib/types/client/SheetPanel.d.ts +33 -0
  22. package/lib/types/client/SheetPanel.js +1766 -0
  23. package/lib/types/client/SheetToggle.d.ts +18 -0
  24. package/lib/types/client/SheetToggle.js +26 -0
  25. package/lib/types/client/index.d.ts +15 -0
  26. package/lib/types/client/index.js +110 -0
  27. package/lib/types/client/persistence/changes.d.ts +15 -0
  28. package/lib/types/client/persistence/changes.js +51 -0
  29. package/lib/types/client/persistence/ids.d.ts +34 -0
  30. package/lib/types/client/persistence/ids.js +48 -0
  31. package/lib/types/client/persistence/merge.d.ts +35 -0
  32. package/lib/types/client/persistence/merge.js +362 -0
  33. package/lib/types/client/store.d.ts +61 -0
  34. package/lib/types/client/store.js +152 -0
  35. package/lib/types/client/surface-context.d.ts +27 -0
  36. package/lib/types/client/surface-context.js +104 -0
  37. package/lib/types/client/surface-control.d.ts +2 -0
  38. package/lib/types/client/surface-control.js +12 -0
  39. package/lib/types/client/transport/workbookApi.d.ts +159 -0
  40. package/lib/types/client/transport/workbookApi.js +246 -0
  41. package/lib/types/client/univer/StandaloneWorkbookPage.d.ts +5 -0
  42. package/lib/types/client/univer/StandaloneWorkbookPage.js +7 -0
  43. package/lib/types/client/univer/WorkbookCanvas.d.ts +40 -0
  44. package/lib/types/client/univer/WorkbookCanvas.js +87 -0
  45. package/lib/types/client/univer/WorkbookView.d.ts +3 -0
  46. package/lib/types/client/univer/WorkbookView.js +2 -0
  47. package/lib/types/client/univer/loader.d.ts +18 -0
  48. package/lib/types/client/univer/loader.js +81 -0
  49. package/lib/types/client/univer/standalone-entry.d.ts +1 -0
  50. package/lib/types/client/univer/standalone-entry.js +19 -0
  51. package/lib/types/client/versions/VersionChrome.d.ts +33 -0
  52. package/lib/types/client/versions/VersionChrome.js +21 -0
  53. package/lib/types/client/versions/inspect.d.ts +70 -0
  54. package/lib/types/client/versions/inspect.js +88 -0
  55. package/lib/types/client/versions/preview-decoration.d.ts +11 -0
  56. package/lib/types/client/versions/preview-decoration.js +54 -0
  57. package/lib/types/client/versions/records.d.ts +19 -0
  58. package/lib/types/client/versions/records.js +70 -0
  59. package/lib/types/client/workspace-host.d.ts +61 -0
  60. package/lib/types/client/workspace-host.js +124 -0
  61. package/lib/types/collaboration-hub.d.ts +31 -0
  62. package/lib/types/collaboration-hub.js +69 -0
  63. package/lib/types/contract/ids.d.ts +24 -0
  64. package/lib/types/contract/ids.js +46 -0
  65. package/lib/types/contract/mutations.d.ts +25 -0
  66. package/lib/types/contract/mutations.js +782 -0
  67. package/lib/types/contract/operations.d.ts +54 -0
  68. package/lib/types/contract/operations.js +203 -0
  69. package/lib/types/contract/snapshots.d.ts +176 -0
  70. package/lib/types/contract/snapshots.js +351 -0
  71. package/lib/types/contract/univer-api-catalog.d.ts +29 -0
  72. package/lib/types/contract/univer-api-catalog.js +264 -0
  73. package/lib/types/errors.d.ts +8 -0
  74. package/lib/types/errors.js +12 -0
  75. package/lib/types/http/assets-routes.d.ts +3 -0
  76. package/lib/types/http/assets-routes.js +169 -0
  77. package/lib/types/http/collaboration.d.ts +10 -0
  78. package/lib/types/http/collaboration.js +154 -0
  79. package/lib/types/http/history.d.ts +15 -0
  80. package/lib/types/http/history.js +134 -0
  81. package/lib/types/http/project-files.d.ts +79 -0
  82. package/lib/types/http/project-files.js +206 -0
  83. package/lib/types/http/project-scope.d.ts +50 -0
  84. package/lib/types/http/project-scope.js +52 -0
  85. package/lib/types/http/routes.d.ts +98 -0
  86. package/lib/types/http/routes.js +323 -0
  87. package/lib/types/http/standalone.d.ts +9 -0
  88. package/lib/types/http/standalone.js +31 -0
  89. package/lib/types/http/trunk.d.ts +10 -0
  90. package/lib/types/http/trunk.js +322 -0
  91. package/lib/types/http/worktree.d.ts +9 -0
  92. package/lib/types/http/worktree.js +145 -0
  93. package/lib/types/index.d.ts +18 -0
  94. package/lib/types/index.js +156 -0
  95. package/lib/types/invariant.d.ts +15 -0
  96. package/lib/types/invariant.js +15 -0
  97. package/lib/types/request-params.d.ts +21 -0
  98. package/lib/types/request-params.js +76 -0
  99. package/lib/types/store/paths.d.ts +68 -0
  100. package/lib/types/store/paths.js +148 -0
  101. package/lib/types/store/project.d.ts +88 -0
  102. package/lib/types/store/project.js +120 -0
  103. package/lib/types/store/read.d.ts +110 -0
  104. package/lib/types/store/read.js +404 -0
  105. package/lib/types/store/retention.d.ts +27 -0
  106. package/lib/types/store/retention.js +111 -0
  107. package/lib/types/store/write.d.ts +22 -0
  108. package/lib/types/store/write.js +210 -0
  109. package/lib/types/tools/arguments.d.ts +40 -0
  110. package/lib/types/tools/arguments.js +130 -0
  111. package/lib/types/tools/cells.d.ts +50 -0
  112. package/lib/types/tools/cells.js +298 -0
  113. package/lib/types/tools/index.d.ts +2 -0
  114. package/lib/types/tools/index.js +250 -0
  115. package/lib/types/tools/render.d.ts +47 -0
  116. package/lib/types/tools/render.js +290 -0
  117. package/lib/types/tools/univer-api.d.ts +18 -0
  118. package/lib/types/tools/univer-api.js +198 -0
  119. package/lib/types/tools/workbook-catalog.d.ts +3 -0
  120. package/lib/types/tools/workbook-catalog.js +83 -0
  121. package/lib/types/tools/workbook-edit.d.ts +192 -0
  122. package/lib/types/tools/workbook-edit.js +912 -0
  123. package/lib/types/tools/workbook-find.d.ts +11 -0
  124. package/lib/types/tools/workbook-find.js +89 -0
  125. package/lib/types/tools/workbook-history.d.ts +2 -0
  126. package/lib/types/tools/workbook-history.js +59 -0
  127. package/lib/types/tools/workbook-read.d.ts +21 -0
  128. package/lib/types/tools/workbook-read.js +204 -0
  129. package/lib/types/types.d.ts +377 -0
  130. package/lib/types/types.js +34 -0
  131. package/lib/types/univer/apply-operation.d.ts +14 -0
  132. package/lib/types/univer/apply-operation.js +398 -0
  133. package/lib/types/univer/chunk.d.ts +68 -0
  134. package/lib/types/univer/chunk.js +16 -0
  135. package/lib/types/univer/decoration.d.ts +34 -0
  136. package/lib/types/univer/decoration.js +88 -0
  137. package/lib/types/univer/diagnostics.d.ts +31 -0
  138. package/lib/types/univer/diagnostics.js +141 -0
  139. package/lib/types/univer/engine.d.ts +33 -0
  140. package/lib/types/univer/engine.js +338 -0
  141. package/lib/types/univer/entry.d.ts +8 -0
  142. package/lib/types/univer/entry.js +11 -0
  143. package/lib/types/univer/facade.d.ts +107 -0
  144. package/lib/types/univer/facade.js +1 -0
  145. package/lib/types/univer/fonts.d.ts +202 -0
  146. package/lib/types/univer/fonts.js +140 -0
  147. package/lib/types/univer/plugins/collaboration.d.ts +23 -0
  148. package/lib/types/univer/plugins/collaboration.js +47 -0
  149. package/lib/types/univer/plugins/export.d.ts +39 -0
  150. package/lib/types/univer/plugins/export.js +246 -0
  151. package/lib/types/univer/plugins/history.d.ts +24 -0
  152. package/lib/types/univer/plugins/history.js +50 -0
  153. package/lib/types/univer/plugins/version.d.ts +13 -0
  154. package/lib/types/univer/plugins/version.js +13 -0
  155. package/lib/types/version/diff.d.ts +36 -0
  156. package/lib/types/version/diff.js +156 -0
  157. package/lib/types/version/grouping.d.ts +7 -0
  158. package/lib/types/version/grouping.js +94 -0
  159. package/lib/types/version/records.d.ts +17 -0
  160. package/lib/types/version/records.js +102 -0
  161. package/lib/types/version/session.d.ts +36 -0
  162. package/lib/types/version/session.js +89 -0
  163. package/lib/types/version/summary.d.ts +24 -0
  164. package/lib/types/version/summary.js +111 -0
  165. package/lib/types/workbook-manager.d.ts +191 -0
  166. package/lib/types/workbook-manager.js +935 -0
  167. package/lib/types/xlsx-export-worker.d.ts +5 -0
  168. package/lib/types/xlsx-export-worker.js +24 -0
  169. package/lib/types/xlsx-export.d.ts +21 -0
  170. package/lib/types/xlsx-export.js +371 -0
  171. package/lib/types/xlsx-import-worker.d.ts +1 -0
  172. package/lib/types/xlsx-import-worker.js +19 -0
  173. package/lib/univer-client.js +690 -0
  174. package/lib/workbook-page.js +23826 -0
  175. package/lib/xlsx-export-worker.js +104 -0
  176. package/lib/xlsx-import-worker.js +46 -0
  177. package/package.json +106 -3
  178. package/src/audit.ts +58 -0
  179. package/src/client/ProjectImportMenu.tsx +100 -0
  180. package/src/client/SheetPanel.module.css +1578 -0
  181. package/src/client/SheetPanel.tsx +1934 -0
  182. package/src/client/SheetToggle.module.css +73 -0
  183. package/src/client/SheetToggle.tsx +65 -0
  184. package/src/client/contract.d.ts +19 -0
  185. package/src/client/index.ts +131 -0
  186. package/src/client/persistence/changes.ts +58 -0
  187. package/src/client/persistence/ids.ts +60 -0
  188. package/src/client/persistence/merge.ts +392 -0
  189. package/src/client/store.ts +188 -0
  190. package/src/client/surface-context.ts +123 -0
  191. package/src/client/surface-control.ts +13 -0
  192. package/src/client/transport/workbookApi.ts +355 -0
  193. package/src/client/univer/SheetView.module.css +110 -0
  194. package/src/client/univer/StandaloneWorkbookPage.module.css +8 -0
  195. package/src/client/univer/StandaloneWorkbookPage.tsx +15 -0
  196. package/src/client/univer/WorkbookCanvas.tsx +128 -0
  197. package/src/client/univer/WorkbookView.module.css +30 -0
  198. package/src/client/univer/WorkbookView.tsx +3 -0
  199. package/src/client/univer/loader.ts +90 -0
  200. package/src/client/univer/react-dom-client.d.ts +10 -0
  201. package/src/client/univer/standalone-entry.tsx +19 -0
  202. package/src/client/univer/standalone.css +12 -0
  203. package/src/client/univer/univer-locales.d.ts +89 -0
  204. package/src/client/univer/univer-modules.d.ts +26 -0
  205. package/src/client/versions/VersionChrome.tsx +114 -0
  206. package/src/client/versions/inspect.ts +138 -0
  207. package/src/client/versions/preview-decoration.ts +54 -0
  208. package/src/client/versions/records.ts +78 -0
  209. package/src/client/workspace-host.ts +147 -0
  210. package/src/collaboration-hub.ts +83 -0
  211. package/src/contract/ids.ts +50 -0
  212. package/src/contract/mutations.ts +791 -0
  213. package/src/contract/operations.ts +221 -0
  214. package/src/contract/snapshots.ts +383 -0
  215. package/src/contract/univer-api-catalog.ts +289 -0
  216. package/src/css-modules.d.ts +6 -0
  217. package/src/errors.ts +25 -0
  218. package/src/http/assets-routes.ts +177 -0
  219. package/src/http/collaboration.ts +144 -0
  220. package/src/http/history.ts +151 -0
  221. package/src/http/project-files.ts +227 -0
  222. package/src/http/project-scope.ts +77 -0
  223. package/src/http/routes.ts +364 -0
  224. package/src/http/standalone.ts +32 -0
  225. package/src/http/trunk.ts +322 -0
  226. package/src/http/worktree.ts +119 -0
  227. package/src/index.ts +165 -0
  228. package/src/invariant.ts +17 -0
  229. package/src/request-params.ts +92 -0
  230. package/src/store/paths.ts +165 -0
  231. package/src/store/project.ts +137 -0
  232. package/src/store/read.ts +454 -0
  233. package/src/store/retention.ts +106 -0
  234. package/src/store/write.ts +230 -0
  235. package/src/tools/arguments.ts +128 -0
  236. package/src/tools/cells.ts +298 -0
  237. package/src/tools/index.ts +274 -0
  238. package/src/tools/render.ts +301 -0
  239. package/src/tools/univer-api.ts +243 -0
  240. package/src/tools/workbook-catalog.ts +94 -0
  241. package/src/tools/workbook-edit.ts +987 -0
  242. package/src/tools/workbook-find.ts +80 -0
  243. package/src/tools/workbook-history.ts +63 -0
  244. package/src/tools/workbook-read.ts +210 -0
  245. package/src/types.ts +432 -0
  246. package/src/univer/apply-operation.ts +369 -0
  247. package/src/univer/chunk.ts +77 -0
  248. package/src/univer/decoration.ts +111 -0
  249. package/src/univer/diagnostics.ts +136 -0
  250. package/src/univer/engine.ts +370 -0
  251. package/src/univer/entry.ts +14 -0
  252. package/src/univer/facade.ts +91 -0
  253. package/src/univer/fonts.ts +148 -0
  254. package/src/univer/overrides.css +33 -0
  255. package/src/univer/plugins/collaboration.ts +47 -0
  256. package/src/univer/plugins/export.ts +236 -0
  257. package/src/univer/plugins/history.ts +49 -0
  258. package/src/univer/plugins/version.ts +13 -0
  259. package/src/version/diff.ts +169 -0
  260. package/src/version/grouping.ts +96 -0
  261. package/src/version/records.ts +113 -0
  262. package/src/version/session.ts +90 -0
  263. package/src/version/summary.ts +117 -0
  264. package/src/workbook-manager.ts +1111 -0
  265. package/src/xlsx-export-worker.ts +34 -0
  266. package/src/xlsx-export.ts +373 -0
  267. package/src/xlsx-import-worker.ts +30 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * The only module that replaces a workbook's bytes (docs/issue/0022 P1).
3
+ *
4
+ * Every commit is a write-ahead journal plus temp-file renames, so an
5
+ * interrupted save is finished by the next read rather than leaving half a
6
+ * workbook on disk. It takes the journal lock from `store/read.ts` so a commit
7
+ * can never interleave with the recovery of another.
8
+ */
9
+ import { mkdir, rename, rm, unlink, writeFile } from 'node:fs/promises';
10
+ import { randomUUID } from 'node:crypto';
11
+ import { DEFAULT_WORKBOOK } from "../types.js";
12
+ import { normalizeWorkbookId, normalizeWorktreeId } from "../contract/ids.js";
13
+ import { PROPOSAL_LOG_LIMIT, operationSummary } from "../contract/operations.js";
14
+ import { validateSnapshotBounds } from "../contract/mutations.js";
15
+ import { cloneSnapshot, normalizeSnapshotName } from "../contract/snapshots.js";
16
+ import { workbookCheckpointsPath, workbookIndexPath, workbookOperationsPath, workbookPath, workbookProposalsPath, workbookRoot, workbookSnapshotPath, workbookTransactionPath, workbookWorktreeDirectory, workbookWorktreePath, } from "./paths.js";
17
+ import { fileExists, readWorkbook, recoverWorkbookTransactionNow, summarize, withWorkbookTransactionQueue, workbookCache, } from "./read.js";
18
+ /** Write one workbook meta atomically (creates the repo root on demand). */
19
+ export async function writeWorkbook(meta, options = {}) {
20
+ const normalizedId = normalizeWorkbookId(meta.id);
21
+ await withWorkbookTransactionQueue(normalizedId, async () => {
22
+ await mkdir(workbookRoot(), { recursive: true });
23
+ await recoverWorkbookTransactionNow(normalizedId);
24
+ // The DSH metadata name is the user-facing workbook name. Keep the Univer
25
+ // snapshot name aligned so exports and other consumers use the same name.
26
+ const normalizedMeta = { ...meta, id: normalizedId, snapshot: normalizeSnapshotName(meta.snapshot, meta.name) };
27
+ validateSnapshotBounds(normalizedMeta.snapshot);
28
+ const operationLog = normalizedMeta.operations ?? [];
29
+ const proposalLog = normalizedMeta.proposals ?? [];
30
+ // Versions are the durable audit trail. Keep the append-only operation log
31
+ // instead of truncating it to a recent tail so old versions can be replayed.
32
+ normalizedMeta.operations = operationLog;
33
+ // The single place this bound is applied, since every write comes through here.
34
+ normalizedMeta.proposals = proposalLog.slice(-PROPOSAL_LOG_LIMIT);
35
+ const snapshotExists = await fileExists(workbookSnapshotPath(normalizedId));
36
+ const persistSnapshot = options.persistSnapshot === true || !snapshotExists;
37
+ const checkpointsExist = await fileExists(workbookCheckpointsPath(normalizedId));
38
+ const snapshotRevision = persistSnapshot
39
+ ? normalizedMeta.revision ?? 0
40
+ : (typeof normalizedMeta.snapshotRevision === 'number' ? normalizedMeta.snapshotRevision : 0);
41
+ const { snapshot: _snapshot, checkpoints: _checkpoints, operations: _operations, proposals: _proposals, ...metadata } = normalizedMeta;
42
+ const metadataWithSnapshotRevision = { ...metadata, snapshotRevision };
43
+ const files = [
44
+ { target: workbookOperationsPath(normalizedId), value: normalizedMeta.operations ?? [] },
45
+ { target: workbookProposalsPath(normalizedId), value: normalizedMeta.proposals ?? [] },
46
+ { target: workbookPath(normalizedId), value: metadataWithSnapshotRevision },
47
+ { target: workbookIndexPath(normalizedId), value: buildWorkbookIndex(normalizedMeta) },
48
+ ];
49
+ if (persistSnapshot)
50
+ files.unshift({ target: workbookSnapshotPath(normalizedId), value: { revision: normalizedMeta.revision ?? 0, snapshot: normalizedMeta.snapshot } });
51
+ if (options.persistCheckpoints === true || !checkpointsExist)
52
+ files.splice(persistSnapshot ? 1 : 0, 0, { target: workbookCheckpointsPath(normalizedId), value: { checkpoints: normalizedMeta.checkpoints ?? [] } });
53
+ await commitWorkbookTransaction(normalizedId, files, normalizedMeta.revision ?? 0);
54
+ workbookCache.set(normalizedId, {
55
+ meta: { ...normalizedMeta, snapshotRevision, checkpoints: normalizedMeta.checkpoints ?? [] },
56
+ checkpointsLoaded: options.persistCheckpoints === true || !checkpointsExist,
57
+ });
58
+ });
59
+ }
60
+ export async function writeJsonAtomically(path, value) {
61
+ const tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
62
+ try {
63
+ await writeFile(tempPath, JSON.stringify(value, null, 2), 'utf8');
64
+ await rename(tempPath, path);
65
+ }
66
+ finally {
67
+ try {
68
+ await unlink(tempPath);
69
+ }
70
+ catch { /* rename already removed it */ }
71
+ }
72
+ }
73
+ export async function commitWorkbookTransaction(id, files, revision) {
74
+ const transactionId = randomUUID();
75
+ const journalPath = workbookTransactionPath(id);
76
+ const prepared = files.map(file => ({ target: file.target, temp: `${file.target}.${transactionId}.tmp` }));
77
+ let journalWritten = false;
78
+ try {
79
+ for (const [index, file] of files.entries()) {
80
+ await writeFile(prepared[index].temp, JSON.stringify(file.value, null, 2), 'utf8');
81
+ }
82
+ await writeJsonAtomically(journalPath, { revision, files: prepared });
83
+ journalWritten = true;
84
+ for (const file of prepared)
85
+ await rename(file.temp, file.target);
86
+ await unlink(journalPath);
87
+ }
88
+ catch (error) {
89
+ // Once the journal exists, its prepared files are intentionally retained
90
+ // so the next read can complete the commit. Before that point cleanup is
91
+ // safe because no target has been replaced.
92
+ if (!journalWritten) {
93
+ await Promise.all(prepared.map(file => unlink(file.temp).catch(() => undefined)));
94
+ }
95
+ throw error;
96
+ }
97
+ }
98
+ export async function writeWorktree(worktree) {
99
+ await mkdir(workbookWorktreeDirectory(worktree.workbookId), { recursive: true });
100
+ await writeJsonAtomically(workbookWorktreePath(worktree.workbookId, worktree.id), worktree);
101
+ }
102
+ export function buildWorkbookIndex(meta) {
103
+ const summary = summarize(meta);
104
+ return {
105
+ id: summary.id,
106
+ name: summary.name,
107
+ sheetOrder: summary.sheets.map(sheet => sheet.id),
108
+ sheets: summary.sheets,
109
+ updatedAt: summary.updatedAt,
110
+ revision: meta.revision ?? 0,
111
+ operations: (meta.operations ?? []).map(operation => ({
112
+ opId: operation.opId,
113
+ workbookId: operation.workbookId,
114
+ revision: operation.revision,
115
+ actor: operation.actor,
116
+ type: operation.type,
117
+ timestamp: operation.timestamp,
118
+ summary: operationSummary(operation),
119
+ })),
120
+ proposals: (meta.proposals ?? []).map(({ snapshot: _snapshot, ...proposal }) => proposal),
121
+ };
122
+ }
123
+ /**
124
+ * Delete a workbook and all of its Datasheet sidecars. `main` is the stable
125
+ * default workbook and is intentionally protected from accidental deletion.
126
+ * The queue makes deletion serialize with an in-flight save for the same id.
127
+ */
128
+ export async function deleteWorkbook(id) {
129
+ const normalizedId = normalizeWorkbookId(id);
130
+ if (normalizedId === DEFAULT_WORKBOOK)
131
+ throw new Error('默认工作簿 main 不能删除');
132
+ await withWorkbookQueue(normalizedId, async () => {
133
+ workbookCache.delete(normalizedId);
134
+ const paths = [
135
+ workbookPath(normalizedId),
136
+ workbookIndexPath(normalizedId),
137
+ workbookSnapshotPath(normalizedId),
138
+ workbookCheckpointsPath(normalizedId),
139
+ workbookOperationsPath(normalizedId),
140
+ workbookProposalsPath(normalizedId),
141
+ workbookTransactionPath(normalizedId),
142
+ ];
143
+ for (const path of paths) {
144
+ try {
145
+ await unlink(path);
146
+ }
147
+ catch (error) {
148
+ if (error.code !== 'ENOENT')
149
+ throw error;
150
+ }
151
+ }
152
+ await rm(workbookWorktreeDirectory(normalizedId), { recursive: true, force: true });
153
+ });
154
+ }
155
+ /** Rename the user-facing workbook label without changing its stable id or
156
+ * history. The snapshot name is kept in sync by `writeWorkbook`. */
157
+ export async function renameWorkbook(id, name) {
158
+ const normalizedId = normalizeWorkbookId(id);
159
+ const normalizedName = name.trim();
160
+ if (normalizedName.length === 0)
161
+ throw new Error('工作簿名称不能为空');
162
+ return withWorkbookQueue(normalizedId, async () => {
163
+ const existing = await readWorkbook(normalizedId);
164
+ if (existing === null)
165
+ throw new Error(`工作簿不存在:${id}`);
166
+ const meta = cloneSnapshot(existing);
167
+ meta.name = normalizedName;
168
+ meta.updatedAt = new Date().toISOString();
169
+ await writeWorkbook(meta);
170
+ return meta;
171
+ });
172
+ }
173
+ /** Serialize mutations (and worktree writes) per id.
174
+ * `deleteWorkbook` and `renameWorkbook` take the same lock as `mutateWorkbook`,
175
+ * so a delete can never interleave with an in-flight save. The manager imports
176
+ * them from here rather than owning them, because the store must not depend on
177
+ * its callers. */
178
+ const mutationQueues = new Map();
179
+ const worktreeQueues = new Map();
180
+ export async function withWorkbookQueue(id, task) {
181
+ const previous = mutationQueues.get(id) ?? Promise.resolve();
182
+ let release;
183
+ const current = new Promise(resolveRelease => { release = resolveRelease; });
184
+ mutationQueues.set(id, current);
185
+ await previous;
186
+ try {
187
+ return await task();
188
+ }
189
+ finally {
190
+ release();
191
+ if (mutationQueues.get(id) === current)
192
+ mutationQueues.delete(id);
193
+ }
194
+ }
195
+ export async function withWorktreeQueue(workbookId, worktreeId, task) {
196
+ const key = `${normalizeWorkbookId(workbookId)}:${normalizeWorktreeId(worktreeId)}`;
197
+ const previous = worktreeQueues.get(key) ?? Promise.resolve();
198
+ let release;
199
+ const current = new Promise(resolveRelease => { release = resolveRelease; });
200
+ worktreeQueues.set(key, current);
201
+ await previous;
202
+ try {
203
+ return await task();
204
+ }
205
+ finally {
206
+ release();
207
+ if (worktreeQueues.get(key) === current)
208
+ worktreeQueues.delete(key);
209
+ }
210
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Reading tool arguments (docs/issue/0022 P3b).
3
+ *
4
+ * The Agent hands a tool an untyped record; every field has to be validated
5
+ * before it can become part of an operation. These are the conversions several
6
+ * tools share — the ones only one tool needs live in that tool.
7
+ */
8
+ import { type WorkbookCellDeleteShift } from '../types.ts';
9
+ export type ToolArguments = Record<string, unknown>;
10
+ export declare function asToolArguments(value: unknown, label?: string): ToolArguments;
11
+ /** Validate and normalize a workbook id argument. There is no implicit
12
+ * `main`: every Agent call must identify the workbook it intends to change. */
13
+ export declare function workbookIdOf(raw: unknown): string;
14
+ /** ISO timestamp helper. */
15
+ export declare function now(): string;
16
+ export declare function optionalIntent(value: unknown, name?: string): string | undefined;
17
+ export declare function optionalAuditText(value: unknown, name: string, maxLength: number): string | undefined;
18
+ export declare function optionalString(value: unknown): string | undefined;
19
+ export declare function parseCount(value: unknown): number;
20
+ /**
21
+ * `verifyHeader` defaults to true because comparing header labels is the only
22
+ * guard that catches a column order or rename mistake before rows are written.
23
+ * Only an explicit `false` relaxes it.
24
+ */
25
+ export declare function parseHeaderVerification(value: unknown, label: string): boolean;
26
+ export declare function parseCellDeleteShift(value: unknown): WorkbookCellDeleteShift;
27
+ export declare function commandPayload(raw: ToolArguments): ToolArguments;
28
+ /**
29
+ * Accept the vocabulary used by both the public Agent tool and the Univer
30
+ * facade. Older callers use `sheet`, while generated calls often use the
31
+ * persisted field name `sheetId` or the human-facing `sheetName`.
32
+ */
33
+ export declare function sheetRefFromArguments(raw: ToolArguments, label?: string): string | undefined;
34
+ /** Resolve an optional Agent sheet reference. A missing reference is safe only
35
+ * when the workbook has one sheet; otherwise returning the first sheet would
36
+ * make a write silently target the wrong tab. */
37
+ export declare function sheetRefForSnapshot(snapshot: unknown, raw: ToolArguments, label: string): string;
38
+ export declare function commandSheet(raw: ToolArguments, index: number, _optional?: boolean): string | undefined;
39
+ export declare function commandIndex(raw: ToolArguments, index: number, field: 'index' | 'startRow' | 'startColumn'): number;
40
+ export declare function parseNonEmptyString(value: unknown, label: string): string;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Reading tool arguments (docs/issue/0022 P3b).
3
+ *
4
+ * The Agent hands a tool an untyped record; every field has to be validated
5
+ * before it can become part of an operation. These are the conversions several
6
+ * tools share — the ones only one tool needs live in that tool.
7
+ */
8
+ import { SHEET_MAX_ROWS } from "../types.js";
9
+ import { normalizeWorkbookId } from "../contract/ids.js";
10
+ import { parseIndex } from "../request-params.js";
11
+ export function asToolArguments(value, label = '参数') {
12
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
13
+ throw new Error(label + '必须是对象');
14
+ return value;
15
+ }
16
+ /** Validate and normalize a workbook id argument. There is no implicit
17
+ * `main`: every Agent call must identify the workbook it intends to change. */
18
+ export function workbookIdOf(raw) {
19
+ if (typeof raw !== 'string' || raw.trim().length === 0) {
20
+ throw new Error('必须明确指定 workbookId;请先调用 workbook_catalog(action=list) 获取工作簿 id');
21
+ }
22
+ return normalizeWorkbookId(raw);
23
+ }
24
+ /** ISO timestamp helper. */
25
+ export function now() {
26
+ return new Date().toISOString();
27
+ }
28
+ export function optionalIntent(value, name = 'intent') {
29
+ if (value === undefined || value === null)
30
+ return undefined;
31
+ if (typeof value !== 'string' || value.trim().length === 0 || value.trim().length > 280)
32
+ throw new Error(`${name} 必须是 1 到 280 字符的字符串`);
33
+ return value.trim();
34
+ }
35
+ export function optionalAuditText(value, name, maxLength) {
36
+ if (value === undefined || value === null)
37
+ return undefined;
38
+ if (typeof value !== 'string' || value.trim().length === 0 || value.trim().length > maxLength)
39
+ throw new Error(`${name} 必须是 1 到 ${maxLength} 字符的字符串`);
40
+ return value.trim();
41
+ }
42
+ export function optionalString(value) {
43
+ if (value === undefined || value === null)
44
+ return undefined;
45
+ if (typeof value !== 'string' || value.trim().length === 0)
46
+ throw new Error('opId 必须是非空字符串');
47
+ return value.trim();
48
+ }
49
+ export function parseCount(value) {
50
+ const count = parseIndex(value, 'count');
51
+ if (count < 1 || count > SHEET_MAX_ROWS)
52
+ throw new Error(`count 必须是 1 到 ${SHEET_MAX_ROWS} 之间的整数`);
53
+ return count;
54
+ }
55
+ /**
56
+ * `verifyHeader` defaults to true because comparing header labels is the only
57
+ * guard that catches a column order or rename mistake before rows are written.
58
+ * Only an explicit `false` relaxes it.
59
+ */
60
+ export function parseHeaderVerification(value, label) {
61
+ if (value === undefined || value === null)
62
+ return true;
63
+ if (typeof value !== 'boolean')
64
+ throw new Error(`${label} 必须是布尔值`);
65
+ return value;
66
+ }
67
+ export function parseCellDeleteShift(value) {
68
+ if (value === undefined || value === null || value === '')
69
+ return 'clear';
70
+ if (value === 'clear' || value === 'left' || value === 'up')
71
+ return value;
72
+ throw new Error('shift 必须是 clear、left 或 up');
73
+ }
74
+ export function commandPayload(raw) {
75
+ const nested = raw.payload;
76
+ if (nested === undefined || nested === null)
77
+ return raw;
78
+ if (typeof nested !== 'object' || Array.isArray(nested))
79
+ throw new Error('command.payload 必须是对象');
80
+ // Permit callers to send the durable { type, payload } shape while keeping
81
+ // the documented flat command shape. Explicit top-level fields win.
82
+ return { ...nested, ...raw };
83
+ }
84
+ /**
85
+ * Accept the vocabulary used by both the public Agent tool and the Univer
86
+ * facade. Older callers use `sheet`, while generated calls often use the
87
+ * persisted field name `sheetId` or the human-facing `sheetName`.
88
+ */
89
+ export function sheetRefFromArguments(raw, label = 'sheet') {
90
+ const candidates = ['sheet', 'sheetId', 'sheetName', 'sheet_id', 'sheet_name']
91
+ .filter(key => raw[key] !== undefined && raw[key] !== null && raw[key] !== '');
92
+ if (candidates.length === 0)
93
+ return undefined;
94
+ const refs = candidates.map(key => parseNonEmptyString(raw[key], `${label}.${key}`));
95
+ const first = refs[0];
96
+ if (refs.some(ref => ref !== first))
97
+ throw new Error(`${label}、sheetId、sheetName 只能指定同一个 sheet`);
98
+ return first;
99
+ }
100
+ /** Resolve an optional Agent sheet reference. A missing reference is safe only
101
+ * when the workbook has one sheet; otherwise returning the first sheet would
102
+ * make a write silently target the wrong tab. */
103
+ export function sheetRefForSnapshot(snapshot, raw, label) {
104
+ const explicit = sheetRefFromArguments(raw, label);
105
+ if (explicit !== undefined)
106
+ return explicit;
107
+ const root = asToolArguments(snapshot, 'workbook snapshot');
108
+ const order = Array.isArray(root.sheetOrder) ? root.sheetOrder.filter(item => typeof item === 'string') : [];
109
+ if (order.length === 1 && order[0] !== undefined)
110
+ return order[0];
111
+ const sheets = typeof root.sheets === 'object' && root.sheets !== null && !Array.isArray(root.sheets)
112
+ ? Object.entries(root.sheets).map(([id, value]) => {
113
+ const sheet = value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
114
+ return `${id}${typeof sheet.name === 'string' ? `(${sheet.name})` : ''}`;
115
+ })
116
+ : [];
117
+ throw new Error(`${label} 缺少 sheet;当前工作簿有多个 sheet,请使用 sheetId 或 sheetName:${sheets.join('、')}`);
118
+ }
119
+ export function commandSheet(raw, index, _optional = false) {
120
+ return sheetRefFromArguments(raw, 'commands[' + index + '].sheet');
121
+ }
122
+ export function commandIndex(raw, index, field) {
123
+ const value = raw[field] ?? (field === 'startRow' ? raw.index : field === 'startColumn' ? raw.index : undefined);
124
+ return parseIndex(value, 'commands[' + index + '].' + field);
125
+ }
126
+ export function parseNonEmptyString(value, label) {
127
+ if (typeof value !== 'string' || value.trim().length === 0)
128
+ throw new Error(`${label} 必须是非空字符串`);
129
+ return value.trim();
130
+ }
@@ -0,0 +1,50 @@
1
+ import { type SheetColumn, type SheetColumnType, type WorkbookCellUpdate, type WorkbookOperationPayload, type WorkbookRange } from '../types.ts';
2
+ import { type ToolArguments } from './arguments.ts';
3
+ export declare function parseCells(value: unknown): WorkbookCellUpdate[];
4
+ /**
5
+ * Resolve `cells[].valueFromFile` into a literal value (docs/issue/0011 I4 / T7).
6
+ *
7
+ * Long text had to be inlined into the tool call, and the same SQL was retyped
8
+ * into every workbook that needed it. A cell may now name a local text file
9
+ * instead. The content is read here and handed to the ordinary `parseCells`
10
+ * validation, so nothing bypasses the existing cell limits.
11
+ */
12
+ export declare function resolveCellFileValues(value: unknown[], label: string): Promise<unknown[]>;
13
+ /**
14
+ * Resolve the target range of a value matrix.
15
+ *
16
+ * The matrix size is unambiguous, so it is the authority: `range` only needs
17
+ * to anchor the write, and a `range` whose declared end disagrees with
18
+ * `values` is corrected and reported as a warning instead of failing a large
19
+ * batch. End coordinates are inclusive, which the tool description now states.
20
+ */
21
+ export declare function parseValueRange(value: unknown, matrix: unknown[][], label: string, warnings?: string[]): WorkbookRange;
22
+ export declare function parseApiValues(matrix: unknown[][], range: WorkbookRange): WorkbookCellUpdate[];
23
+ /** A single cell of long text (typically a SQL body) read from a local file. */
24
+ export declare const TEXT_CELL_MAX_BYTES: number;
25
+ export declare function parseFreeze(value: unknown): NonNullable<WorkbookOperationPayload['freeze']>;
26
+ export declare function parseSort(value: unknown): NonNullable<WorkbookOperationPayload['sort']>;
27
+ export interface ValidatedSheet {
28
+ name?: string;
29
+ columns: SheetColumn[];
30
+ rows: Array<Record<string, unknown>>;
31
+ }
32
+ export declare function cellValue(type: SheetColumnType, value: unknown): number | string | boolean;
33
+ /** Validate one sheet argument; throws a model-facing message on bad input. */
34
+ export declare function validateSheet(args: {
35
+ name: unknown;
36
+ columns?: unknown;
37
+ rows: unknown;
38
+ }): ValidatedSheet;
39
+ export declare function chunkRows(rows: Array<Record<string, unknown>>, size: number): Array<Array<Record<string, unknown>>>;
40
+ export declare function dimensionRange(index: number, dimension: 'row' | 'column'): WorkbookRange;
41
+ /**
42
+ * Row/column structural commands accept either an explicit `range` (matching
43
+ * the documented command vocabulary) or the older `index`/`startRow` +
44
+ * `count` pair. Missing position arguments report the full accepted contract
45
+ * instead of the single `startRow` field the old implementation demanded.
46
+ */
47
+ export declare function dimensionCommandRange(raw: ToolArguments, index: number, dimension: 'row' | 'column'): WorkbookRange;
48
+ export declare function parseFiniteNumber(value: unknown, label: string): number;
49
+ export declare function parseHyperlink(value: unknown): string;
50
+ export declare function parseHyperlinkLabel(value: unknown): string;