@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,351 @@
1
+ /**
2
+ * Snapshot vocabulary: the workbook snapshot shape, the matrix tools, and the
3
+ * one range reader (docs/issue/0022 P0).
4
+ *
5
+ * The readers here are pure functions of their arguments. The two **accessors**
6
+ * are not, and the distinction is worth stating because the module used to claim
7
+ * otherwise:
8
+ *
9
+ * - `asWorkbookSnapshot` fills in a missing `sheetOrder` by writing it into the
10
+ * object it was handed. That is how a caller's `payload.snapshot` could be
11
+ * modified as a side effect of reading it, which is what made a stored
12
+ * operation's payload accumulate later revisions. `applyWorkbookOperation`
13
+ * now clones before returning, so the write lands on a copy — but this
14
+ * function still writes to its argument, and a caller must not assume
15
+ * otherwise.
16
+ * - `resolveSheet` materializes `cellData` on the sheet it returns. That is
17
+ * load-bearing, not incidental: writers mutate the returned sheet and expect
18
+ * the change to land in the snapshot, so handing back a copy would silently
19
+ * drop every edit.
20
+ *
21
+ * `cloneSnapshot` is the point of the move: it existed twice — once in the model
22
+ * layer and once again inside `client/SheetPanel.tsx` — which is exactly the
23
+ * drift a contract module exists to prevent.
24
+ */
25
+ import { SHEET_MAX_COLUMNS, SHEET_MAX_PHYSICAL_ROWS, SHEET_MAX_ROWS, } from "../types.js";
26
+ /**
27
+ * The snapshot shape. It is declared here rather than in `types.ts` because it
28
+ * is the *input* of these functions and of nothing else on the wire; the
29
+ * tool-facing types stay in `types.ts`.
30
+ */
31
+ /**
32
+ * The font family a blank sheet is created with.
33
+ *
34
+ * It lives in the contract because it is part of the stored snapshot's default
35
+ * style: the model, the xlsx reader and the engine all have to agree on it.
36
+ * Before the 0022 refactor it lived in the engine's font module, which made the
37
+ * model layer import *upward* into the engine (docs/issue/0022 §2).
38
+ */
39
+ export const DEFAULT_SHEET_FONT_FAMILY = 'PingFang SC';
40
+ /**
41
+ * The grid a new sheet starts with.
42
+ *
43
+ * One constant because *two* layers create sheets — the snapshot mutator and the
44
+ * Univer canvas — and a sheet's declared extent has to match the grid the user
45
+ * is looking at. It was written out as `1000` and `26` in seven places across
46
+ * five files, which is how the two drift apart without anyone editing either
47
+ * number. It is also a *starting* size, not a read limit: writes grow the sheet
48
+ * (`SHEET_MAX_ROWS` is the ceiling), and reads take the range they are given.
49
+ */
50
+ export const DEFAULT_SHEET_ROWS = 1000;
51
+ export const DEFAULT_SHEET_COLUMNS = 26;
52
+ /**
53
+ * Deterministic JSON, so two equal values serialize identically.
54
+ *
55
+ * Object keys are sorted, which `JSON.stringify` does not do: it follows
56
+ * insertion order, so two snapshots holding the same content in a different
57
+ * order would compare unequal. Used for content hashes and for deciding whether
58
+ * a merge actually changed anything.
59
+ *
60
+ * It lived in the model layer *and* in the browser's merge code, byte for byte.
61
+ */
62
+ export function stableJson(value) {
63
+ if (value === undefined)
64
+ return 'undefined';
65
+ if (value === null || typeof value !== 'object')
66
+ return JSON.stringify(value);
67
+ if (Array.isArray(value))
68
+ return `[${value.map(stableJson).join(',')}]`;
69
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(',')}}`;
70
+ }
71
+ /** Deep copy through JSON; snapshots are plain JSON by construction. */
72
+ export function cloneSnapshot(value) {
73
+ return JSON.parse(JSON.stringify(value));
74
+ }
75
+ /**
76
+ * Narrow `value` to a snapshot, defaulting `sheetOrder` from `sheets`.
77
+ *
78
+ * **Writes to its argument** when `sheetOrder` is absent (see the module note).
79
+ * Every caller here uses the return value, and `applyWorkbookOperation` clones,
80
+ * but a new caller should not read a snapshot *and* hand the same object
81
+ * somewhere else without knowing that.
82
+ */
83
+ export function asWorkbookSnapshot(value) {
84
+ if (typeof value !== 'object' || value === null)
85
+ throw new Error('工作簿快照格式无效');
86
+ const root = value;
87
+ if (typeof root.sheets !== 'object' || root.sheets === null)
88
+ throw new Error('工作簿缺少 sheets');
89
+ if (!Array.isArray(root.sheetOrder))
90
+ root.sheetOrder = Object.keys(root.sheets);
91
+ return root;
92
+ }
93
+ export function resolveSheet(root, ref) {
94
+ const requested = ref?.trim();
95
+ const sheet = requested === undefined || requested.length === 0
96
+ ? root.sheets[root.sheetOrder[0] ?? '']
97
+ : root.sheets[requested] ?? Object.values(root.sheets).find(candidate => candidate.name === requested);
98
+ if (sheet === undefined)
99
+ throw new Error(`sheet 不存在:${requested ?? '(第一个 sheet)'}`);
100
+ sheet.cellData ??= {};
101
+ return sheet;
102
+ }
103
+ export function validateIndex(value, label) {
104
+ if (!Number.isInteger(value) || value < 0)
105
+ throw new Error(`${label} 必须是大于等于 0 的整数`);
106
+ }
107
+ export function validateRange(range) {
108
+ if (range === undefined)
109
+ throw new Error('必须提供 range');
110
+ validateIndex(range.startRow, 'range.startRow');
111
+ validateIndex(range.startColumn, 'range.startColumn');
112
+ validateIndex(range.endRow, 'range.endRow');
113
+ validateIndex(range.endColumn, 'range.endColumn');
114
+ if (range.endRow < range.startRow || range.endColumn < range.startColumn)
115
+ throw new Error('range 结束位置不能小于开始位置');
116
+ if (range.endRow >= SHEET_MAX_PHYSICAL_ROWS)
117
+ throw new Error(`range 行号超过上限(最多 ${SHEET_MAX_ROWS} 条数据行 + 表头)`);
118
+ if (range.endColumn >= SHEET_MAX_COLUMNS)
119
+ throw new Error(`range 列号超过上限(最多 ${SHEET_MAX_COLUMNS} 列)`);
120
+ // No span check: `startRow`/`startColumn` are non-negative (above) and the end
121
+ // bounds are already inside the sheet, so the span cannot exceed it. Two
122
+ // checks that could never fire used to sit here, which is worse than noise —
123
+ // they read as a rule that something enforces.
124
+ }
125
+ /**
126
+ * A cell's formula in the one form this repository stores.
127
+ *
128
+ * `=SUM(A1:A2)` and `SUM(A1:A2)` are the same formula — the leading `=` is how a
129
+ * spreadsheet *displays* it, not part of it — and the whitespace around it is not
130
+ * part of it either. An input that reduces to nothing is not a formula at all.
131
+ *
132
+ * Four places wrote cells and each decided this for itself, so they disagreed:
133
+ * `update_cells` with `formula: '=SUM(A1:A2)'` stored `SUM(A1:A2)` in the
134
+ * snapshot and drew `=SUM(A1:A2)` on the canvas; `formula: ''` deleted the cell
135
+ * in the snapshot and left `{v:null,f:''}` on the canvas; `'='` was dropped by
136
+ * the snapshot and kept by the browser's merge. The snapshot and the canvas are
137
+ * two views of one workbook, so they cannot disagree about what is in it.
138
+ */
139
+ export function normalizeCellFormula(formula) {
140
+ if (typeof formula !== 'string')
141
+ return undefined;
142
+ const normalized = formula.trim().replace(/^=/, '');
143
+ return normalized.length === 0 ? undefined : normalized;
144
+ }
145
+ export function toCellValue(cell) {
146
+ if (cell === undefined)
147
+ return null;
148
+ if (typeof cell.e === 'string')
149
+ return cell.e;
150
+ if (cell.v === undefined || cell.v === null)
151
+ return null;
152
+ if (typeof cell.v === 'string' || typeof cell.v === 'number' || typeof cell.v === 'boolean')
153
+ return cell.v;
154
+ return String(cell.v);
155
+ }
156
+ export function cellType(value) {
157
+ if (value === null)
158
+ return 'blank';
159
+ if (typeof value === 'number')
160
+ return 'number';
161
+ if (typeof value === 'boolean')
162
+ return 'boolean';
163
+ if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}(?:T|$)/.test(value))
164
+ return 'date';
165
+ return 'string';
166
+ }
167
+ /**
168
+ * The last row and column that actually carry something, or `-1` when the sheet
169
+ * is empty.
170
+ *
171
+ * A whole-sheet read needs this: the caller asking for "the sheet" must get the
172
+ * sheet, and the only way to know how big that is, is to look. A hard-coded
173
+ * window cannot know, and a window that is too small loses data silently.
174
+ *
175
+ * A cell that holds only a style is skipped — formatting an empty range writes
176
+ * style ids into every cell it covers, and those must not stretch a read.
177
+ */
178
+ export function usedExtent(snapshot, sheetRef) {
179
+ const sheet = resolveSheet(asWorkbookSnapshot(snapshot), sheetRef);
180
+ let endRow = -1;
181
+ let endColumn = -1;
182
+ for (const [rowKey, line] of Object.entries(sheet.cellData ?? {})) {
183
+ const row = Number(rowKey);
184
+ if (!Number.isInteger(row) || row < 0)
185
+ continue;
186
+ let rowHasContent = false;
187
+ for (const [columnKey, cell] of Object.entries(line ?? {})) {
188
+ const column = Number(columnKey);
189
+ if (!Number.isInteger(column) || column < 0)
190
+ continue;
191
+ const hasValue = toCellValue(cell) !== null;
192
+ const hasFormula = typeof cell?.f === 'string' && cell.f !== '';
193
+ if (!hasValue && !hasFormula)
194
+ continue;
195
+ endColumn = Math.max(endColumn, column);
196
+ rowHasContent = true;
197
+ }
198
+ if (rowHasContent)
199
+ endRow = Math.max(endRow, row);
200
+ }
201
+ return { endRow, endColumn };
202
+ }
203
+ /**
204
+ * A style object out of a snapshot, narrowed to a plain record.
205
+ *
206
+ * Both storage shapes are real and both are read here: the browser's Univer
207
+ * residency keeps the object in the workbook's `styles` map and writes only its
208
+ * id onto the cell, while `format_cells` on the host writes the object inline.
209
+ * A resolver that knew one shape would silently drop the other's formatting.
210
+ */
211
+ function styleRecord(value) {
212
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
213
+ return {};
214
+ return { ...value };
215
+ }
216
+ /**
217
+ * The style a cell actually paints with: the sheet's default under the cell's
218
+ * own, field by field.
219
+ *
220
+ * The sheet default is load-bearing, not decoration. `DEFAULT_SHEET_FONT_FAMILY`
221
+ * lives in every sheet's `defaultStyle` and is the **only** place a font family
222
+ * is recorded for a cell the user never formatted — a reader that skipped it
223
+ * would report "no font" for most of a sheet, which is how the Sheet Block
224
+ * preview could not show the font the workbook is actually set in.
225
+ *
226
+ * `null` means there is nothing to paint, so the wire carries a compact matrix
227
+ * instead of an object per empty cell.
228
+ */
229
+ export function effectiveCellStyle(root, sheet, cell) {
230
+ const inline = cell === undefined ? {} : styleRecord(cell.s);
231
+ const named = typeof cell?.s === 'string' ? styleRecord(root.styles?.[cell.s]) : {};
232
+ const merged = { ...styleRecord(sheet.defaultStyle), ...named, ...inline };
233
+ return Object.keys(merged).length === 0 ? null : merged;
234
+ }
235
+ export function readWorkbookRange(snapshot, sheetRef, range, options = {}) {
236
+ const root = asWorkbookSnapshot(snapshot);
237
+ const sheet = resolveSheet(root, sheetRef);
238
+ validateRange(range);
239
+ const withStyles = options.styles === true;
240
+ const values = [];
241
+ const formulas = [];
242
+ const cells = [];
243
+ const styles = [];
244
+ for (let row = range.startRow; row <= range.endRow; row += 1) {
245
+ const line = [];
246
+ const formulaLine = [];
247
+ const cellLine = [];
248
+ const styleLine = [];
249
+ for (let column = range.startColumn; column <= range.endColumn; column += 1) {
250
+ const cell = sheet.cellData[String(row)]?.[String(column)];
251
+ const value = toCellValue(cell);
252
+ const formula = typeof cell?.f === 'string' ? cell.f : null;
253
+ line.push(value);
254
+ formulaLine.push(formula);
255
+ cellLine.push({
256
+ value,
257
+ formula,
258
+ type: formula !== null ? 'formula' : cellType(value),
259
+ ...(formula === null ? {} : { calculation: value === null ? 'unavailable' : 'cached' }),
260
+ ...(typeof value === 'string' && value.startsWith('#') ? { error: value } : {}),
261
+ });
262
+ if (withStyles)
263
+ styleLine.push(effectiveCellStyle(root, sheet, cell));
264
+ }
265
+ values.push(line);
266
+ formulas.push(formulaLine);
267
+ cells.push(cellLine);
268
+ if (withStyles)
269
+ styles.push(styleLine);
270
+ }
271
+ return {
272
+ sheetId: sheet.id,
273
+ sheetName: sheet.name,
274
+ range,
275
+ values,
276
+ formulas,
277
+ cells,
278
+ ...(withStyles ? { styles } : {}),
279
+ };
280
+ }
281
+ /**
282
+ * Return original sheet row indexes that are duplicates within a rectangular
283
+ * range. This follows Excel's default behavior: compare the selected columns,
284
+ * keep the first occurrence, and leave the header untouched when requested.
285
+ */
286
+ export function findDuplicateRows(values, range, columns, hasHeader = false, keep = 'first') {
287
+ const selectedColumns = columns === undefined || columns.length === 0
288
+ ? Array.from({ length: range.endColumn - range.startColumn + 1 }, (_value, index) => range.startColumn + index)
289
+ : [...new Set(columns)];
290
+ for (const column of selectedColumns) {
291
+ if (!Number.isInteger(column) || column < range.startColumn || column > range.endColumn) {
292
+ throw new Error('remove_duplicates 的 columns 必须位于 range 内');
293
+ }
294
+ }
295
+ if (keep !== 'first' && keep !== 'last')
296
+ throw new Error('remove_duplicates.keep 必须是 first 或 last');
297
+ const duplicateRows = [];
298
+ const seen = new Set();
299
+ const rowIndexes = Array.from({ length: values.length }, (_value, index) => index);
300
+ if (keep === 'last')
301
+ rowIndexes.reverse();
302
+ for (const offset of rowIndexes) {
303
+ if (hasHeader && offset === 0)
304
+ continue;
305
+ const row = values[offset] ?? [];
306
+ const key = JSON.stringify(selectedColumns.map(column => canonicalValue(row[column - range.startColumn])));
307
+ if (seen.has(key))
308
+ duplicateRows.push(range.startRow + offset);
309
+ else
310
+ seen.add(key);
311
+ }
312
+ return duplicateRows.sort((left, right) => left - right);
313
+ }
314
+ function canonicalValue(value) {
315
+ if (value === undefined || value === null)
316
+ return null;
317
+ if (typeof value === 'number' && Number.isNaN(value))
318
+ return 'NaN';
319
+ if (typeof value === 'object')
320
+ return JSON.stringify(value);
321
+ return value;
322
+ }
323
+ export function isSheetSchema(value) {
324
+ if (typeof value !== 'object' || value === null)
325
+ return false;
326
+ const schema = value;
327
+ if (!Number.isInteger(schema.version) || !Array.isArray(schema.columns) || schema.columns.length === 0 || schema.columns.length > SHEET_MAX_COLUMNS
328
+ || !Number.isInteger(schema.rowCount) || schema.rowCount < 0 || schema.rowCount > SHEET_MAX_ROWS
329
+ || typeof schema.updatedAt !== 'string')
330
+ return false;
331
+ const keys = new Set();
332
+ return schema.columns.every(column => {
333
+ if (typeof column !== 'object' || column === null)
334
+ return false;
335
+ const candidate = column;
336
+ if (typeof candidate.key !== 'string' || !/^[A-Za-z0-9_]+$/.test(candidate.key) || keys.has(candidate.key)
337
+ || typeof candidate.label !== 'string' || candidate.label.length === 0
338
+ || !['string', 'number', 'boolean', 'date', 'currency'].includes(candidate.type ?? ''))
339
+ return false;
340
+ keys.add(candidate.key);
341
+ return true;
342
+ });
343
+ }
344
+ /** Force a snapshot's `name` field to the workbook's user-facing name.
345
+ * Both the read and the write path need it, which is why it is vocabulary
346
+ * rather than a store helper. */
347
+ export function normalizeSnapshotName(snapshot, name) {
348
+ if (typeof snapshot !== 'object' || snapshot === null)
349
+ return snapshot;
350
+ return { ...snapshot, name };
351
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * JSON-safe metadata for the DSH adapter over Univer's Sheets Facade.
3
+ *
4
+ * The catalog is deliberately an allow-list. It gives an Agent discoverable
5
+ * method names and argument contracts without exposing arbitrary JavaScript or
6
+ * an Injector instance to the tool protocol. Methods that change workbook
7
+ * state map to the same durable operations used by the typed tools.
8
+ */
9
+ import type { WorkbookOperationType } from '../types.ts';
10
+ export type UniverApiMode = 'read' | 'write';
11
+ export type UniverApiAvailability = 'implemented' | 'metadata-only';
12
+ export interface UniverApiArgument {
13
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'json';
14
+ required?: boolean;
15
+ description: string;
16
+ }
17
+ export interface UniverApiDefinition {
18
+ api: string;
19
+ mode: UniverApiMode;
20
+ availability: UniverApiAvailability;
21
+ description: string;
22
+ args: Record<string, UniverApiArgument>;
23
+ operation?: WorkbookOperationType;
24
+ notes?: string;
25
+ }
26
+ /** Common Facade methods exposed by the DSH Host adapter. */
27
+ export declare const UNIVER_API_CATALOG: readonly UniverApiDefinition[];
28
+ export declare const UNIVER_API_VERSION = "0.25.1-dsh-1";
29
+ export declare function findUniverApi(api: string): UniverApiDefinition | undefined;
@@ -0,0 +1,264 @@
1
+ /**
2
+ * JSON-safe metadata for the DSH adapter over Univer's Sheets Facade.
3
+ *
4
+ * The catalog is deliberately an allow-list. It gives an Agent discoverable
5
+ * method names and argument contracts without exposing arbitrary JavaScript or
6
+ * an Injector instance to the tool protocol. Methods that change workbook
7
+ * state map to the same durable operations used by the typed tools.
8
+ */
9
+ const rangeArgs = {
10
+ sheet: { type: 'string', description: 'sheet id 或名称;也可传 sheetId/sheetName;单 sheet workbook 可省略' },
11
+ range: { type: 'object', required: true, description: '0-based 矩形范围:{ startRow, startColumn, endRow, endColumn };也接受 A1:D4' },
12
+ };
13
+ /** Common Facade methods exposed by the DSH Host adapter. */
14
+ export const UNIVER_API_CATALOG = [
15
+ {
16
+ api: 'workbook.getSnapshot',
17
+ mode: 'read',
18
+ availability: 'implemented',
19
+ description: '读取当前 workbook 的 Univer snapshot。大表格请优先使用 worksheet.getRangeValues。',
20
+ args: {},
21
+ },
22
+ {
23
+ api: 'workbook.getWorksheets',
24
+ mode: 'read',
25
+ availability: 'implemented',
26
+ description: '返回 workbook 内所有 worksheet 的 id、名称和顺序。',
27
+ args: {},
28
+ },
29
+ {
30
+ api: 'worksheet.getRangeValues',
31
+ mode: 'read',
32
+ availability: 'implemented',
33
+ description: '读取 worksheet 的矩形区域值。',
34
+ args: rangeArgs,
35
+ },
36
+ {
37
+ api: 'worksheet.setValues',
38
+ mode: 'write',
39
+ availability: 'implemented',
40
+ description: '通过 Univer Range.setValues 写入值矩阵;range 可只传起点或省略,Host 会按矩阵尺寸推导。',
41
+ args: { sheet: rangeArgs.sheet, range: { type: 'object', description: '可选 0-based 范围;也可只传 startRow/startColumn,或使用 A1:D4' }, values: { type: 'array', required: true, description: '二维数组,也接受单行一维数组' } },
42
+ operation: 'update_cells',
43
+ },
44
+ {
45
+ api: 'worksheet.clearContent',
46
+ mode: 'write',
47
+ availability: 'implemented',
48
+ description: '清除 worksheet range 的内容。',
49
+ args: rangeArgs,
50
+ operation: 'clear_range',
51
+ },
52
+ {
53
+ api: 'worksheet.setBackgroundColor',
54
+ mode: 'write',
55
+ availability: 'implemented',
56
+ description: '设置 range 背景色。',
57
+ args: { ...rangeArgs, color: { type: 'string', required: true, description: 'CSS 颜色' } },
58
+ operation: 'format_cells',
59
+ },
60
+ {
61
+ api: 'worksheet.setFontColor',
62
+ mode: 'write',
63
+ availability: 'implemented',
64
+ description: '设置 range 字体颜色。',
65
+ args: { ...rangeArgs, color: { type: 'string', required: true, description: 'CSS 颜色' } },
66
+ operation: 'format_cells',
67
+ },
68
+ {
69
+ api: 'worksheet.setFontFamily',
70
+ mode: 'write',
71
+ availability: 'implemented',
72
+ description: '设置 range 字体。',
73
+ args: { ...rangeArgs, family: { type: 'string', required: true, description: '字体 family' } },
74
+ operation: 'format_cells',
75
+ },
76
+ {
77
+ api: 'worksheet.setFontSize',
78
+ mode: 'write',
79
+ availability: 'implemented',
80
+ description: '设置 range 字号。',
81
+ args: { ...rangeArgs, size: { type: 'number', required: true, description: '字号,单位 pt' } },
82
+ operation: 'format_cells',
83
+ },
84
+ {
85
+ api: 'worksheet.setFontWeight',
86
+ mode: 'write',
87
+ availability: 'implemented',
88
+ description: '设置 range 粗体状态。',
89
+ args: { ...rangeArgs, bold: { type: 'boolean', required: true, description: '是否粗体' } },
90
+ operation: 'format_cells',
91
+ },
92
+ {
93
+ api: 'worksheet.setFontStyle',
94
+ mode: 'write',
95
+ availability: 'implemented',
96
+ description: '设置 range 斜体状态。',
97
+ args: { ...rangeArgs, italic: { type: 'boolean', required: true, description: '是否斜体' } },
98
+ operation: 'format_cells',
99
+ },
100
+ {
101
+ api: 'worksheet.setHorizontalAlignment',
102
+ mode: 'write',
103
+ availability: 'implemented',
104
+ description: '设置 range 水平对齐。',
105
+ args: { ...rangeArgs, alignment: { type: 'string', required: true, description: 'left、center 或 normal' } },
106
+ operation: 'format_cells',
107
+ },
108
+ {
109
+ api: 'worksheet.setVerticalAlignment',
110
+ mode: 'write',
111
+ availability: 'implemented',
112
+ description: '设置 range 垂直对齐。',
113
+ args: { ...rangeArgs, alignment: { type: 'string', required: true, description: 'top、middle 或 bottom' } },
114
+ operation: 'format_cells',
115
+ },
116
+ {
117
+ api: 'worksheet.merge',
118
+ mode: 'write',
119
+ availability: 'implemented',
120
+ description: '合并 worksheet range。',
121
+ args: rangeArgs,
122
+ operation: 'merge_cells',
123
+ },
124
+ {
125
+ api: 'worksheet.breakApart',
126
+ mode: 'write',
127
+ availability: 'implemented',
128
+ description: '取消 worksheet range 的合并。',
129
+ args: rangeArgs,
130
+ operation: 'unmerge_cells',
131
+ },
132
+ {
133
+ api: 'worksheet.sort',
134
+ mode: 'write',
135
+ availability: 'implemented',
136
+ description: '对 worksheet range 按列排序。',
137
+ args: { ...rangeArgs, column: { type: 'number', required: true, description: '0-based 排序列索引' }, direction: { type: 'string', description: 'asc 或 desc,默认 asc' } },
138
+ operation: 'sort_range',
139
+ },
140
+ {
141
+ api: 'worksheet.setColumnWidth',
142
+ mode: 'write',
143
+ availability: 'implemented',
144
+ description: '设置列宽。',
145
+ args: { sheet: rangeArgs.sheet, index: { type: 'number', required: true, description: '0-based 列索引' }, size: { type: 'number', required: true, description: '像素宽度' } },
146
+ operation: 'set_column_width',
147
+ },
148
+ {
149
+ api: 'worksheet.setRowHeight',
150
+ mode: 'write',
151
+ availability: 'implemented',
152
+ description: '设置行高。',
153
+ args: { sheet: rangeArgs.sheet, index: { type: 'number', required: true, description: '0-based 行索引' }, size: { type: 'number', required: true, description: '像素高度' } },
154
+ operation: 'set_row_height',
155
+ },
156
+ {
157
+ api: 'worksheet.setHyperLink',
158
+ mode: 'write',
159
+ availability: 'implemented',
160
+ description: '给 range 左上角单元格设置超链接;等价于 workbook_edit 的 set_hyperlink command。',
161
+ args: { ...rangeArgs, url: { type: 'string', required: true, description: '目标 URL' }, label: { type: 'string', description: '显示文本' } },
162
+ operation: 'set_hyperlink',
163
+ },
164
+ {
165
+ api: 'worksheet.cancelHyperLink',
166
+ mode: 'write',
167
+ availability: 'implemented',
168
+ description: '移除 range 左上角单元格的超链接。',
169
+ args: rangeArgs,
170
+ operation: 'remove_hyperlink',
171
+ },
172
+ {
173
+ api: 'worksheet.setFreeze',
174
+ mode: 'write',
175
+ availability: 'implemented',
176
+ description: '冻结指定行列。',
177
+ args: { sheet: rangeArgs.sheet, rows: { type: 'number', description: '冻结行数,默认 0' }, columns: { type: 'number', description: '冻结列数,默认 0' } },
178
+ operation: 'freeze_panes',
179
+ },
180
+ {
181
+ api: 'worksheet.deleteCells',
182
+ mode: 'write',
183
+ availability: 'implemented',
184
+ description: '删除 range 内容,或按 left/up 移动邻近单元格。',
185
+ args: { ...rangeArgs, shift: { type: 'string', description: 'clear、left 或 up,默认 clear' } },
186
+ operation: 'delete_cells',
187
+ },
188
+ {
189
+ api: 'worksheet.insertRows',
190
+ mode: 'write',
191
+ availability: 'implemented',
192
+ description: '在指定行位置插入行。',
193
+ args: { sheet: rangeArgs.sheet, index: { type: 'number', required: true, description: '0-based 行位置' }, count: { type: 'number', description: '行数,默认 1' } },
194
+ operation: 'insert_rows',
195
+ },
196
+ {
197
+ api: 'worksheet.deleteRows',
198
+ mode: 'write',
199
+ availability: 'implemented',
200
+ description: '删除指定行。',
201
+ args: { sheet: rangeArgs.sheet, index: { type: 'number', required: true, description: '0-based 行位置' }, count: { type: 'number', description: '行数,默认 1' } },
202
+ operation: 'delete_rows',
203
+ },
204
+ {
205
+ api: 'worksheet.insertColumns',
206
+ mode: 'write',
207
+ availability: 'implemented',
208
+ description: '在指定列位置插入列。',
209
+ args: { sheet: rangeArgs.sheet, index: { type: 'number', required: true, description: '0-based 列位置' }, count: { type: 'number', description: '列数,默认 1' } },
210
+ operation: 'insert_columns',
211
+ },
212
+ {
213
+ api: 'worksheet.deleteColumns',
214
+ mode: 'write',
215
+ availability: 'implemented',
216
+ description: '删除指定列。',
217
+ args: { sheet: rangeArgs.sheet, index: { type: 'number', required: true, description: '0-based 列位置' }, count: { type: 'number', description: '列数,默认 1' } },
218
+ operation: 'delete_columns',
219
+ },
220
+ {
221
+ api: 'worksheet.setTextRotation',
222
+ mode: 'write',
223
+ availability: 'implemented',
224
+ description: '设置 range 文字旋转角度。',
225
+ args: { ...rangeArgs, rotation: { type: 'number', required: true, description: '旋转角度' } },
226
+ operation: 'format_cells',
227
+ },
228
+ {
229
+ api: 'worksheet.setNumberFormat',
230
+ mode: 'write',
231
+ availability: 'implemented',
232
+ description: '设置 range 数字格式。',
233
+ args: { ...rangeArgs, format: { type: 'string', required: true, description: 'Univer number format' } },
234
+ operation: 'format_cells',
235
+ },
236
+ {
237
+ api: 'worksheet.setName',
238
+ mode: 'write',
239
+ availability: 'implemented',
240
+ description: '修改 worksheet 名称。',
241
+ args: { sheet: rangeArgs.sheet, name: { type: 'string', required: true, description: '新的 sheet 名称' } },
242
+ operation: 'rename_sheet',
243
+ },
244
+ {
245
+ api: 'workbook.createSheet',
246
+ mode: 'write',
247
+ availability: 'implemented',
248
+ description: '持久化创建 worksheet;可用 sheetId 指定稳定 id。',
249
+ args: { name: { type: 'string', required: true, description: 'sheet 名称' }, sheetId: { type: 'string', description: '可选稳定 sheet id' } },
250
+ operation: 'create_sheet',
251
+ },
252
+ {
253
+ api: 'workbook.deleteSheet',
254
+ mode: 'write',
255
+ availability: 'implemented',
256
+ description: '持久化删除 worksheet;工作簿至少保留一个 sheet。',
257
+ args: { sheet: { type: 'string', description: 'sheet id 或名称;也接受 sheetId/sheetName' } },
258
+ operation: 'delete_sheet',
259
+ },
260
+ ];
261
+ export const UNIVER_API_VERSION = '0.25.1-dsh-1';
262
+ export function findUniverApi(api) {
263
+ return UNIVER_API_CATALOG.find(item => item.api === api);
264
+ }
@@ -0,0 +1,8 @@
1
+ /** Shared wire-level errors for the Host API and browser client. */
2
+ export type DataSheetErrorCode = 'VALIDATION_ERROR' | 'WORKBOOK_NOT_FOUND' | 'WORKTREE_NOT_FOUND' | 'SESSION_SCOPE_DENIED' | 'REVISION_CONFLICT' | 'REVISION_UNAVAILABLE' | 'WORKTREE_STATE_INVALID' | 'PAYLOAD_TOO_LARGE' | 'PERSISTENCE_FAILED' | 'COLLABORATION_LIMIT' | 'INTERNAL_ERROR';
3
+ export declare class DataSheetError extends Error {
4
+ readonly code: DataSheetErrorCode;
5
+ readonly status: number;
6
+ readonly details?: Record<string, unknown> | undefined;
7
+ constructor(code: DataSheetErrorCode, message: string, status?: number, details?: Record<string, unknown> | undefined);
8
+ }
@@ -0,0 +1,12 @@
1
+ export class DataSheetError extends Error {
2
+ code;
3
+ status;
4
+ details;
5
+ constructor(code, message, status = 400, details) {
6
+ super(message);
7
+ this.code = code;
8
+ this.status = status;
9
+ this.details = details;
10
+ this.name = 'DataSheetError';
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ import type { LegacyRoute } from './routes.ts';
2
+ /** Every static route this plugin serves. */
3
+ export declare function assetRoutes(): LegacyRoute[];