@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,322 @@
1
+ import { DataSheetError } from "../errors.js";
2
+ import { lineageOfValue } from "../audit.js";
3
+ import { deleteWorkbook, readWorkbook, readWorkbookIndex, commitWorkbookProposal, rejectWorkbookProposal, rollbackWorkbook, mutateWorkbook, } from "../workbook-manager.js";
4
+ import { parseIndex, parseRange, isOperationType, asOperationPayload, optionalRevision } from "../request-params.js";
5
+ import { readWorkbookRange, workbookExists } from "../workbook-manager.js";
6
+ import { usedExtent } from "../contract/snapshots.js";
7
+ import { parseRevision } from "./routes.js";
8
+ import { standaloneWorkbookHtml } from "./standalone.js";
9
+ import { readJsonBody, requireDataSheetSession, writeOperationError } from "./routes.js";
10
+ export async function handleTrunkRoute(route, req, res, url) {
11
+ const id = route.id;
12
+ if (route.kind === 'view' && req.method === 'GET') {
13
+ const id = route.id;
14
+ const meta = await readWorkbook(id);
15
+ const title = meta?.name ?? id;
16
+ res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-cache' });
17
+ res.end(standaloneWorkbookHtml(title));
18
+ return true;
19
+ }
20
+ if (route.kind === 'snapshot' && (req.method === 'DELETE' || req.method === 'POST' && url.searchParams.get('action') === 'delete')) {
21
+ let body = {};
22
+ try {
23
+ body = await readJsonBody(req);
24
+ }
25
+ catch { /* confirmation is checked below */ }
26
+ if (body.confirm !== true) {
27
+ res.writeHead(400, { 'content-type': 'application/json; charset=utf-8' });
28
+ res.end(JSON.stringify({ ok: false, error: '删除工作簿必须显式传入 confirm: true' }));
29
+ return true;
30
+ }
31
+ try {
32
+ if (!await workbookExists(id)) {
33
+ res.writeHead(404, { 'content-type': 'application/json; charset=utf-8' });
34
+ res.end(JSON.stringify({ ok: false, error: `工作簿不存在:${id}` }));
35
+ return true;
36
+ }
37
+ await deleteWorkbook(id);
38
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
39
+ res.end(JSON.stringify({ ok: true, workbookId: id }));
40
+ }
41
+ catch (error) {
42
+ writeOperationError(res, error);
43
+ }
44
+ return true;
45
+ }
46
+ if (route.kind === 'range') {
47
+ const sessionId = requireDataSheetSession(req, res, url);
48
+ if (sessionId === null)
49
+ return true;
50
+ if (req.method !== 'GET') {
51
+ res.writeHead(405);
52
+ res.end();
53
+ return true;
54
+ }
55
+ const id = route.id;
56
+ try {
57
+ const meta = await readWorkbook(id);
58
+ if (meta === null) {
59
+ writeOperationError(res, new DataSheetError('WORKBOOK_NOT_FOUND', '工作簿不存在', 404));
60
+ return true;
61
+ }
62
+ const sheetRef = url.searchParams.get('sheet') ?? '';
63
+ const rangeParam = url.searchParams.get('range') ?? '';
64
+ // A bounded, read-only snapshot of one range: this is how another
65
+ // plugin materializes a Sheet as its own data source (§8.6).
66
+ // No range asked for means *the sheet*, so the extent is measured rather
67
+ // than assumed. This used to default to `A1:Z1000` — rows 0–999 and columns
68
+ // A–Z — which silently handed back a prefix of any larger sheet, and the
69
+ // `truncated` flag below could not say so because it compared a value with
70
+ // itself (`values.length - 1 < endRow - startRow` is `n < n`). Data
71
+ // were being dropped before the loader ever saw them.
72
+ const requested = rangeParam.trim() === '' ? undefined : parseRange(rangeParam);
73
+ const extent = usedExtent(meta.snapshot, sheetRef);
74
+ const range = requested ?? { startRow: 0, startColumn: 0, endRow: extent.endRow, endColumn: extent.endColumn };
75
+ // this range twice — as a SQL relation (values only) and as the Sheet
76
+ // Block's read-only preview, which has to draw the range the way the sheet
77
+ // draws it. Making it opt-in keeps the relation path from paying for a
78
+ // matrix it never reads.
79
+ const withStyles = url.searchParams.get('styles') === '1';
80
+ const result = readWorkbookRange(meta.snapshot, sheetRef, range, withStyles ? { styles: true } : {});
81
+ const header = result.values[0] ?? [];
82
+ const body = result.values.slice(1);
83
+ // An empty sheet reads as no columns rather than one unnamed one.
84
+ const columns = header.every(value => value === null)
85
+ ? []
86
+ : header.map((value, index) => typeof value === 'string' && value.trim() !== '' ? value.trim() : `column_${index + 1}`);
87
+ // 响应按 wire 契约标注,字段变化会在编译期被发现。
88
+ const payload = {
89
+ ok: true,
90
+ workbookId: id,
91
+ workbookName: meta.name,
92
+ revision: meta.revision ?? 0,
93
+ sheetId: result.sheetId,
94
+ sheetName: result.sheetName,
95
+ range: result.range,
96
+ columns,
97
+ rows: body,
98
+ // The style matrix spans the **whole** requested range, header row
99
+ // included, so it indexes exactly like `result.values` — the preview
100
+ // pairs `styles[row][column]` with the value it just drew rather than
101
+ // re-deriving which row is the header.
102
+ ...(result.styles === undefined ? {} : { styles: result.styles }),
103
+ // Only a narrow request can be short of the sheet: that is the single
104
+ // case where more data exists than was returned, and saying so is what
105
+ // lets the reader ask for the rest.
106
+ truncated: requested !== undefined && (extent.endRow > requested.endRow || extent.endColumn > requested.endColumn),
107
+ };
108
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
109
+ res.end(JSON.stringify(payload));
110
+ }
111
+ catch (error) {
112
+ writeOperationError(res, error instanceof DataSheetError ? error : new DataSheetError('VALIDATION_ERROR', error instanceof Error ? error.message : String(error), 400));
113
+ }
114
+ return true;
115
+ }
116
+ if (route.kind === 'proposals' && req.method === 'GET') {
117
+ const index = await readWorkbookIndex(id);
118
+ const meta = index === null ? await readWorkbook(id) : null;
119
+ if (index === null && meta === null) {
120
+ res.writeHead(404);
121
+ res.end();
122
+ return true;
123
+ }
124
+ const proposals = (index?.proposals ?? meta?.proposals ?? []).filter(proposal => proposal.status === 'pending').map(proposal => ({
125
+ id: proposal.id,
126
+ summary: proposal.summary,
127
+ baseRevision: proposal.baseRevision,
128
+ createdAt: proposal.createdAt,
129
+ changes: proposal.changes,
130
+ }));
131
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
132
+ res.end(JSON.stringify({ ok: true, workbookId: id, revision: index?.revision ?? meta?.revision ?? 0, proposals }));
133
+ return true;
134
+ }
135
+ if (route.kind === 'proposal-action' && req.method === 'POST') {
136
+ let body = {};
137
+ try {
138
+ body = await readJsonBody(req);
139
+ }
140
+ catch { /* reject does not need a body */ }
141
+ try {
142
+ const proposalId = route.proposalId;
143
+ if (route.action === 'commit') {
144
+ const result = await commitWorkbookProposal({ workbookId: id, proposalId, actor: 'user', expectedRevision: optionalRevision(body.baseRevision) });
145
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
146
+ res.end(JSON.stringify({ ok: true, revision: result.meta.revision ?? 0, operation: result.operation }));
147
+ }
148
+ else {
149
+ const meta = await rejectWorkbookProposal(id, proposalId);
150
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
151
+ res.end(JSON.stringify({ ok: true, revision: meta.revision ?? 0, proposalId }));
152
+ }
153
+ }
154
+ catch (error) {
155
+ writeOperationError(res, error);
156
+ }
157
+ return true;
158
+ }
159
+ if (route.kind === 'rollback' && req.method === 'POST') {
160
+ let body;
161
+ try {
162
+ body = await readJsonBody(req);
163
+ }
164
+ catch {
165
+ writeOperationError(res, new DataSheetError('VALIDATION_ERROR', 'bad json'));
166
+ return true;
167
+ }
168
+ try {
169
+ const result = await rollbackWorkbook({
170
+ workbookId: id,
171
+ targetRevision: parseIndex(body.targetRevision, 'targetRevision'),
172
+ expectedRevision: optionalRevision(body.baseRevision),
173
+ actor: 'user',
174
+ // The browser names the Version (`恢复到 V3`) because only it knows
175
+ // the display number; the manager falls back to the revision.
176
+ ...(typeof body.summary === 'string' && body.summary.trim() !== '' ? { summary: body.summary.trim() } : {}),
177
+ lineage: lineageOfValue(body.lineage),
178
+ });
179
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
180
+ res.end(JSON.stringify({
181
+ ok: true,
182
+ revision: result.meta.revision ?? 0,
183
+ operation: result.operation,
184
+ }));
185
+ }
186
+ catch (error) {
187
+ writeOperationError(res, error);
188
+ }
189
+ return true;
190
+ }
191
+ if (route.kind === 'changes' && req.method === 'GET') {
192
+ const meta = await readWorkbook(id);
193
+ if (meta === null) {
194
+ res.writeHead(404);
195
+ res.end();
196
+ return true;
197
+ }
198
+ const after = parseRevision(url.searchParams.get('after'));
199
+ const operations = (meta.operations ?? []).filter(operation => operation.revision > after);
200
+ const currentRevision = meta.revision ?? 0;
201
+ const oldestRevision = operations[0]?.revision ?? currentRevision;
202
+ const requiresSnapshot = after < oldestRevision - 1;
203
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
204
+ res.end(JSON.stringify({
205
+ ok: true,
206
+ workbookId: id,
207
+ revision: currentRevision,
208
+ operations,
209
+ requiresSnapshot,
210
+ snapshot: requiresSnapshot ? meta?.snapshot : undefined,
211
+ }));
212
+ return true;
213
+ }
214
+ if (route.kind === 'snapshot' && req.method === 'GET') {
215
+ const meta = await readWorkbook(id);
216
+ if (meta === null) {
217
+ res.writeHead(404);
218
+ res.end();
219
+ return true;
220
+ }
221
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
222
+ res.end(JSON.stringify({
223
+ ok: true,
224
+ workbookId: meta.id,
225
+ revision: meta.revision ?? 0,
226
+ updatedAt: meta.updatedAt,
227
+ snapshot: meta.snapshot,
228
+ }));
229
+ return true;
230
+ }
231
+ if (route.kind === 'operation' && req.method === 'POST') {
232
+ let body;
233
+ try {
234
+ body = await readJsonBody(req);
235
+ }
236
+ catch {
237
+ writeOperationError(res, new DataSheetError('VALIDATION_ERROR', 'bad json'));
238
+ return true;
239
+ }
240
+ const type = body.type;
241
+ if (!isOperationType(type)) {
242
+ writeOperationError(res, new DataSheetError('VALIDATION_ERROR', 'invalid operation type'));
243
+ return true;
244
+ }
245
+ try {
246
+ const result = await mutateWorkbook({
247
+ workbookId: id,
248
+ // Browser HTTP routes are always user-originated. Agent writes use
249
+ // the tool host directly and cannot spoof this audit field.
250
+ actor: 'user',
251
+ type,
252
+ payload: asOperationPayload(body.payload),
253
+ expectedRevision: optionalRevision(body.baseRevision),
254
+ opId: typeof body.opId === 'string' ? body.opId : undefined,
255
+ versionId: typeof body.versionId === 'string' ? body.versionId : undefined,
256
+ // `temporary` remains accepted for browser bundles that have not
257
+ // been upgraded yet; the mutation authority normalizes it to
258
+ // the clearer durable `open` status.
259
+ versionStatus: body.versionStatus === 'open' || body.versionStatus === 'temporary' || body.versionStatus === 'final' ? body.versionStatus : undefined,
260
+ // Browser routes are always user-originated, and a user edit
261
+ // session is an auto checkpoint (PRD v3 §4).
262
+ versionKind: 'auto',
263
+ lineage: lineageOfValue(body.lineage),
264
+ });
265
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
266
+ res.end(JSON.stringify({
267
+ ok: true,
268
+ revision: result.meta.revision ?? 0,
269
+ operation: result.operation,
270
+ ...(result.rebasedOperations === undefined ? {} : { rebasedOperations: result.rebasedOperations }),
271
+ ...(result.transformed === undefined ? {} : { transformed: result.transformed }),
272
+ }));
273
+ }
274
+ catch (error) {
275
+ writeOperationError(res, error);
276
+ }
277
+ return true;
278
+ }
279
+ if (route.kind === 'snapshot' && req.method === 'POST') {
280
+ let body;
281
+ try {
282
+ body = await readJsonBody(req);
283
+ }
284
+ catch {
285
+ writeOperationError(res, new DataSheetError('VALIDATION_ERROR', 'bad json'));
286
+ return true;
287
+ }
288
+ if (!Object.prototype.hasOwnProperty.call(body, 'snapshot')) {
289
+ writeOperationError(res, new DataSheetError('VALIDATION_ERROR', 'snapshot is required'));
290
+ return true;
291
+ }
292
+ const snapshot = body.snapshot;
293
+ try {
294
+ const versionSummary = typeof body.versionSummary === 'string' && body.versionSummary.trim() !== '' ? body.versionSummary.trim() : undefined;
295
+ const versionId = typeof body.versionId === 'string' && body.versionId.trim() !== '' ? body.versionId.trim() : undefined;
296
+ const requestedVersionStatus = body.versionStatus === 'open' || body.versionStatus === 'final' ? body.versionStatus : undefined;
297
+ const result = await mutateWorkbook({
298
+ workbookId: id,
299
+ actor: 'user',
300
+ type: 'snapshot_replace',
301
+ payload: { snapshot },
302
+ expectedRevision: optionalRevision(body.baseRevision),
303
+ opId: typeof body.opId === 'string' ? body.opId : undefined,
304
+ lineage: lineageOfValue(body.lineage),
305
+ ...(versionId === undefined ? {} : { versionId }),
306
+ // A named checkpoint is an explicit, self-contained Version even
307
+ // when the content did not change (PRD §8); a plain snapshot flush
308
+ // is one Step of the editing session instead (PRD v3.1 §4).
309
+ versionKind: versionSummary === undefined ? 'auto' : 'named',
310
+ ...(requestedVersionStatus === undefined ? {} : { versionStatus: requestedVersionStatus }),
311
+ ...(versionSummary === undefined ? {} : { versionSummary, versionStatus: requestedVersionStatus ?? 'final' }),
312
+ });
313
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
314
+ res.end(JSON.stringify({ ok: true, revision: result.meta.revision ?? 0, operation: result.operation }));
315
+ }
316
+ catch (error) {
317
+ writeOperationError(res, error);
318
+ }
319
+ return true;
320
+ }
321
+ return false;
322
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Isolated review worktrees over HTTP (docs/issue/0022 P3).
3
+ *
4
+ * Draft copies are session-scoped, which is why every handler here resolves the
5
+ * session first and checks the owner before touching a worktree.
6
+ */
7
+ import type { IncomingMessage, ServerResponse } from 'node:http';
8
+ import { type WorkbookRoute } from './routes.ts';
9
+ export declare function handleWorktreeRoute(route: WorkbookRoute, req: IncomingMessage, res: ServerResponse, url: URL): Promise<boolean>;
@@ -0,0 +1,145 @@
1
+ import { DataSheetError } from "../errors.js";
2
+ import { diffReachedLimit, diffWorkbookSnapshots } from "../version/diff.js";
3
+ import { createWorkbookWorktree, listWorkbookWorktrees, mergeWorkbookWorktree, mutateWorkbookWorktree, readWorkbookAtRevision, readWorkbookWorktree, setWorkbookWorktreeStatus, } from "../workbook-manager.js";
4
+ import { isOperationType, asOperationPayload, optionalRevision } from "../request-params.js";
5
+ import { readJsonBody, requireDataSheetSession, writeOperationError } from "./routes.js";
6
+ export async function handleWorktreeRoute(route, req, res, url) {
7
+ const id = route.id;
8
+ if (route.kind === 'worktrees') {
9
+ const sessionId = requireDataSheetSession(req, res, url);
10
+ if (sessionId === null)
11
+ return true;
12
+ if (req.method === 'GET') {
13
+ const worktrees = await listWorkbookWorktrees(id, sessionId);
14
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
15
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktrees }));
16
+ return true;
17
+ }
18
+ if (req.method === 'POST') {
19
+ let body;
20
+ try {
21
+ body = await readJsonBody(req);
22
+ }
23
+ catch (error) {
24
+ writeOperationError(res, error);
25
+ return true;
26
+ }
27
+ try {
28
+ const worktree = await createWorkbookWorktree({ workbookId: id, ownerSessionId: sessionId, name: typeof body.name === 'string' ? body.name : undefined, worktreeId: typeof body.worktreeId === 'string' ? body.worktreeId : undefined });
29
+ res.writeHead(201, { 'content-type': 'application/json; charset=utf-8' });
30
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktree }));
31
+ }
32
+ catch (error) {
33
+ writeOperationError(res, error);
34
+ }
35
+ return true;
36
+ }
37
+ res.writeHead(405);
38
+ res.end();
39
+ return true;
40
+ }
41
+ if (route.kind === 'worktree' && req.method === 'GET') {
42
+ const sessionId = requireDataSheetSession(req, res, url);
43
+ if (sessionId === null)
44
+ return true;
45
+ const worktree = await readWorkbookWorktree(id, route.worktreeId);
46
+ if (worktree === null) {
47
+ writeOperationError(res, new DataSheetError('WORKTREE_NOT_FOUND', 'worktree 不存在', 404));
48
+ return true;
49
+ }
50
+ if (worktree.ownerSessionId !== sessionId) {
51
+ writeOperationError(res, new DataSheetError('SESSION_SCOPE_DENIED', '当前 session 无权访问该 worktree', 403));
52
+ return true;
53
+ }
54
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
55
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktree }));
56
+ return true;
57
+ }
58
+ if (route.kind === 'worktree-operations' && req.method === 'POST') {
59
+ const sessionId = requireDataSheetSession(req, res, url);
60
+ if (sessionId === null)
61
+ return true;
62
+ let body;
63
+ try {
64
+ body = await readJsonBody(req);
65
+ }
66
+ catch (error) {
67
+ writeOperationError(res, error);
68
+ return true;
69
+ }
70
+ if (!isOperationType(body.type)) {
71
+ writeOperationError(res, new DataSheetError('VALIDATION_ERROR', 'invalid operation type'));
72
+ return true;
73
+ }
74
+ try {
75
+ const result = await mutateWorkbookWorktree({
76
+ workbookId: id,
77
+ worktreeId: route.worktreeId,
78
+ ownerSessionId: sessionId,
79
+ actor: 'user',
80
+ type: body.type,
81
+ payload: asOperationPayload(body.payload),
82
+ expectedRevision: optionalRevision(body.baseRevision),
83
+ opId: typeof body.opId === 'string' ? body.opId : undefined,
84
+ });
85
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
86
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktree: result.worktree, revision: result.worktree.revision, operation: result.operation }));
87
+ }
88
+ catch (error) {
89
+ writeOperationError(res, error);
90
+ }
91
+ return true;
92
+ }
93
+ if (route.kind === 'worktree-action' && req.method === 'POST') {
94
+ const sessionId = requireDataSheetSession(req, res, url);
95
+ if (sessionId === null)
96
+ return true;
97
+ let body = {};
98
+ try {
99
+ body = await readJsonBody(req);
100
+ }
101
+ catch { /* empty action body is valid */ }
102
+ const worktreeId = route.worktreeId;
103
+ const action = route.action;
104
+ try {
105
+ if (action === 'merge') {
106
+ const result = await mergeWorkbookWorktree({ workbookId: id, worktreeId, ownerSessionId: sessionId, actor: 'user', expectedRevision: optionalRevision(body.baseRevision) });
107
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
108
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktree: result.worktree, revision: result.mutation.meta.revision ?? 0, operation: result.mutation.operation }));
109
+ }
110
+ else {
111
+ const worktree = await setWorkbookWorktreeStatus({ workbookId: id, worktreeId, ownerSessionId: sessionId, status: action === 'discard' ? 'discarded' : action });
112
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
113
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktree }));
114
+ }
115
+ }
116
+ catch (error) {
117
+ writeOperationError(res, error);
118
+ }
119
+ return true;
120
+ }
121
+ if (route.kind === 'worktree-diff' && req.method === 'GET') {
122
+ const sessionId = requireDataSheetSession(req, res, url);
123
+ if (sessionId === null)
124
+ return true;
125
+ const worktree = await readWorkbookWorktree(id, route.worktreeId);
126
+ if (worktree === null) {
127
+ writeOperationError(res, new DataSheetError('WORKTREE_NOT_FOUND', 'worktree 不存在', 404));
128
+ return true;
129
+ }
130
+ if (worktree.ownerSessionId !== sessionId) {
131
+ writeOperationError(res, new DataSheetError('SESSION_SCOPE_DENIED', '当前 session 无权访问该 worktree', 403));
132
+ return true;
133
+ }
134
+ const base = await readWorkbookAtRevision(id, worktree.baseRevision);
135
+ if (base === null) {
136
+ writeOperationError(res, new DataSheetError('REVISION_UNAVAILABLE', 'worktree 基线 revision 已被压缩或无法重放', 409));
137
+ return true;
138
+ }
139
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
140
+ const changes = diffWorkbookSnapshots(base.snapshot, worktree.snapshot);
141
+ res.end(JSON.stringify({ ok: true, workbookId: id, worktreeId: worktree.id, from: worktree.baseRevision, to: worktree.revision, changes, truncated: diffReachedLimit(changes) }));
142
+ return true;
143
+ }
144
+ return false;
145
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Node half of dsh-data-table: the plugin body.
3
+ *
4
+ * It does two things: assemble the HTTP routes (the asset routes plus one
5
+ * prefix route whose sub-paths are matched in `http/routes.ts`) and register the
6
+ * Agent tool surface. Everything it hands to either transport lives in a module
7
+ * beside it, so this file is the list of what the plugin offers — not how any of
8
+ * it works (docs/issue/0022 P3b).
9
+ * @module dsh-data-table
10
+ */
11
+ import type { Context } from '@deepseek-ai/cordis';
12
+ export declare const name = "dsh-data-table";
13
+ export declare const inject: readonly ["tools", "connection", "sessions"];
14
+ /**
15
+ * Plugin body: chunk route + workbook/sheet tools.
16
+ * @param ctx - registrant context.
17
+ */
18
+ export declare function apply(ctx: Context): void;
@@ -0,0 +1,156 @@
1
+ import { setLineageScope } from "./audit.js";
2
+ import { UNIVER_API_CATALOG, UNIVER_API_VERSION } from "./contract/univer-api-catalog.js";
3
+ import { listWorkbookSummaries } from "./workbook-manager.js";
4
+ import { assetRoutes } from "./http/assets-routes.js";
5
+ import { handleCollaborationRoute } from "./http/collaboration.js";
6
+ import { handleHistoryRoute } from "./http/history.js";
7
+ import { handleTrunkRoute } from "./http/trunk.js";
8
+ import { handleWorktreeRoute } from "./http/worktree.js";
9
+ import { guardDataSheetRequest, matchWorkbookRoute, readJsonBody, registerConnectionRoutes, writeOperationError, } from "./http/routes.js";
10
+ import { projectFor, runForProject, sessionIdOf } from "./http/project-scope.js";
11
+ import { projectFileRoutes } from "./http/project-files.js";
12
+ import { registerWorkbookTools } from "./tools/index.js";
13
+ import { executeWorkbookCatalog } from "./tools/workbook-catalog.js";
14
+ export const name = 'dsh-data-table';
15
+ // `sessions` is read to turn a Session id into that Session's project directory,
16
+ // in both transports: a browser request names its id in a header, and a tool call
17
+ // names the same id through its Agent. Cordis throws on undeclared service access,
18
+ // and the throw lands inside a request handler whose exception the web server
19
+ // reports as a bodyless 400 — so a missing declaration looked exactly like a
20
+ // malformed request (2026-09-25).
21
+ //
22
+ // `sandboxPolicy` is deliberately *not* declared: it is not reachable from a plugin context in
23
+ // the web deployment (`cannot get property "sandboxPolicy" without inject` in
24
+ // `~/.dsh/logs/dsh-web-3080.log`, 77 times), so reading it as a fallback turned every workbook
25
+ // list into a 400. A Session's own header is the answer, and a caller that names no Session
26
+ // keeps the pre-project store instead of a project guessed from a policy.
27
+ export const inject = ['tools', 'connection', 'sessions'];
28
+ /**
29
+ * Plugin body: chunk route + workbook/sheet tools.
30
+ * @param ctx - registrant context.
31
+ */
32
+ export function apply(ctx) {
33
+ setLineageScope(ctx);
34
+ const routes = [...assetRoutes(), {
35
+ // A prefix route: everything under `/data-sheet/workbook/` is one entry
36
+ // point, and `matchWorkbookRoute` decides which domain handles it.
37
+ kind: 'prefix',
38
+ // Workbook projection and operation endpoints.
39
+ // Paths:
40
+ // GET /data-sheet/workbook/<id>.json
41
+ // POST /data-sheet/workbook/<id>.json (legacy snapshot checkpoint)
42
+ // GET /data-sheet/workbook/<id>/changes?after=N
43
+ // GET /data-sheet/workbook/<id>/collaboration (live SSE session)
44
+ // GET /data-sheet/workbook/<id>/history[?revision=N]
45
+ // POST /data-sheet/workbook/<id>/operations (fine-grained mutation)
46
+ // NOTE: prefix route paths must NOT end with a trailing slash — the host
47
+ // matcher appends its own "/" (startsWith(`${prefix}/`)), so a trailing
48
+ // slash makes subpaths unmatchable.
49
+ path: '/data-sheet/workbook',
50
+ handler: async (req, res) => {
51
+ if (!guardDataSheetRequest(req, res))
52
+ return;
53
+ // Writes must be JSON: a form-encoded body would be parsed as an empty
54
+ // object and silently drop the payload.
55
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
56
+ const contentType = String(req.headers['content-type'] ?? '').toLowerCase();
57
+ if (!contentType.startsWith('application/json')) {
58
+ res.writeHead(415, { 'content-type': 'application/json; charset=utf-8' });
59
+ res.end(JSON.stringify({ ok: false, code: 'VALIDATION_ERROR', error: '写请求必须使用 application/json' }));
60
+ return;
61
+ }
62
+ }
63
+ const url = new URL(req.url ?? '/', 'http://x');
64
+ // `decodeURIComponent` throws on a malformed escape (`/x/%E0%A4%A`), and
65
+ // that would escape the handler as an unhandled URIError. A path we cannot
66
+ // decode is simply not one of our routes.
67
+ let pathname;
68
+ try {
69
+ pathname = decodeURIComponent(url.pathname);
70
+ }
71
+ catch {
72
+ res.writeHead(400);
73
+ res.end();
74
+ return;
75
+ }
76
+ const route = matchWorkbookRoute(pathname);
77
+ if (route === null) {
78
+ res.writeHead(404);
79
+ res.end();
80
+ return;
81
+ }
82
+ // Worktrees are session-scoped draft copies and are handled first, so a
83
+ // draft can never be mistaken for a normal workbook mutation. The live
84
+ // stream is next because it holds the response open.
85
+ // Every workbook path resolves inside the requesting Session's project, so
86
+ // the store root it writes to is that project's `storages/data-table`.
87
+ const project = projectFor(ctx, sessionIdOf(req.headers, url));
88
+ const dispatched = await runForProject(project, async () => {
89
+ if (await handleWorktreeRoute(route, req, res, url))
90
+ return true;
91
+ if (await handleCollaborationRoute(route, req, res, url))
92
+ return true;
93
+ if (await handleHistoryRoute(route, req, res, url))
94
+ return true;
95
+ if (await handleTrunkRoute(route, req, res, url))
96
+ return true;
97
+ return false;
98
+ });
99
+ if (dispatched)
100
+ return;
101
+ res.writeHead(405);
102
+ res.end();
103
+ },
104
+ }, {
105
+ kind: 'exact',
106
+ path: '/data-sheet/workbooks.json',
107
+ handler: async (_req, res) => {
108
+ if (!guardDataSheetRequest(_req, res))
109
+ return;
110
+ const project = projectFor(ctx, sessionIdOf(_req.headers, new URL(_req.url ?? '/', 'http://x')));
111
+ // POST routes to the Agent's catalog implementation so the browser and the
112
+ // model share one create/rename/delete path.
113
+ if (_req.method === 'POST') {
114
+ let body;
115
+ try {
116
+ body = await readJsonBody(_req);
117
+ }
118
+ catch (error) {
119
+ writeOperationError(res, error);
120
+ return;
121
+ }
122
+ try {
123
+ const result = await runForProject(project, async () => executeWorkbookCatalog({ ...body, action: body.action }));
124
+ res.writeHead(body.action === 'create' ? 201 : 200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
125
+ res.end(JSON.stringify(result));
126
+ }
127
+ catch (error) {
128
+ writeOperationError(res, error);
129
+ }
130
+ return;
131
+ }
132
+ if (_req.method !== 'GET' && _req.method !== 'HEAD') {
133
+ res.writeHead(405, { 'content-type': 'application/json; charset=utf-8' });
134
+ res.end(JSON.stringify({ ok: false, error: 'method not allowed' }));
135
+ return;
136
+ }
137
+ const workbooks = await runForProject(project, async () => listWorkbookSummaries());
138
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
139
+ res.end(JSON.stringify({ ok: true, workbooks }));
140
+ },
141
+ }, {
142
+ // Agent-facing metadata endpoint. It is deliberately separate from the
143
+ // mutation routes so a client can discover the safe Univer surface without
144
+ // receiving a browser-only Injector or executing arbitrary JavaScript.
145
+ kind: 'exact',
146
+ path: '/data-sheet/univer-api.json',
147
+ handler: async (_req, res) => {
148
+ if (!guardDataSheetRequest(_req, res))
149
+ return;
150
+ res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
151
+ res.end(JSON.stringify({ ok: true, version: UNIVER_API_VERSION, catalog: UNIVER_API_CATALOG }));
152
+ },
153
+ }, ...projectFileRoutes(ctx)];
154
+ registerConnectionRoutes(ctx, routes, '/data-sheet');
155
+ registerWorkbookTools(ctx);
156
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Package-owned invariant companion for dsh-data-table.
3
+ *
4
+ * Runtime invariant: the browser half can only render a sheet that the host
5
+ * tool admitted, because the renderer parses the durable tool-call arguments
6
+ * through {@link isSheetData} — the same shape the tool validated. A renderer
7
+ * reaching an unvalidated sheet is a build-level type violation, not a runtime
8
+ * state, so the companion registers the manifest name only.
9
+ * @module dsh-data-table/invariant
10
+ */
11
+ export declare const manifest = "dsh-data-table";
12
+ export declare const invariant: {
13
+ manifest: string;
14
+ reason: string;
15
+ };