@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.
- package/LICENSE +21 -0
- package/README.md +172 -2
- package/cordis.patch.yml +7 -0
- package/lib/client.js +16766 -0
- package/lib/contract-envelope.d.ts +91 -0
- package/lib/contract-envelope.js +106 -0
- package/lib/contract-layout.d.ts +130 -0
- package/lib/contract-layout.js +138 -0
- package/lib/contract-refs.d.ts +39 -0
- package/lib/contract-refs.js +9 -0
- package/lib/contract-version.d.ts +311 -0
- package/lib/contract-version.js +270 -0
- package/lib/contract-wire.d.ts +147 -0
- package/lib/contract-wire.js +99 -0
- package/lib/contracts.d.ts +12 -0
- package/lib/contracts.js +12 -0
- package/lib/types/audit.d.ts +42 -0
- package/lib/types/audit.js +39 -0
- package/lib/types/client/ProjectImportMenu.d.ts +30 -0
- package/lib/types/client/ProjectImportMenu.js +70 -0
- package/lib/types/client/SheetPanel.d.ts +33 -0
- package/lib/types/client/SheetPanel.js +1766 -0
- package/lib/types/client/SheetToggle.d.ts +18 -0
- package/lib/types/client/SheetToggle.js +26 -0
- package/lib/types/client/index.d.ts +15 -0
- package/lib/types/client/index.js +110 -0
- package/lib/types/client/persistence/changes.d.ts +15 -0
- package/lib/types/client/persistence/changes.js +51 -0
- package/lib/types/client/persistence/ids.d.ts +34 -0
- package/lib/types/client/persistence/ids.js +48 -0
- package/lib/types/client/persistence/merge.d.ts +35 -0
- package/lib/types/client/persistence/merge.js +362 -0
- package/lib/types/client/store.d.ts +61 -0
- package/lib/types/client/store.js +152 -0
- package/lib/types/client/surface-context.d.ts +27 -0
- package/lib/types/client/surface-context.js +104 -0
- package/lib/types/client/surface-control.d.ts +2 -0
- package/lib/types/client/surface-control.js +12 -0
- package/lib/types/client/transport/workbookApi.d.ts +159 -0
- package/lib/types/client/transport/workbookApi.js +246 -0
- package/lib/types/client/univer/StandaloneWorkbookPage.d.ts +5 -0
- package/lib/types/client/univer/StandaloneWorkbookPage.js +7 -0
- package/lib/types/client/univer/WorkbookCanvas.d.ts +40 -0
- package/lib/types/client/univer/WorkbookCanvas.js +87 -0
- package/lib/types/client/univer/WorkbookView.d.ts +3 -0
- package/lib/types/client/univer/WorkbookView.js +2 -0
- package/lib/types/client/univer/loader.d.ts +18 -0
- package/lib/types/client/univer/loader.js +81 -0
- package/lib/types/client/univer/standalone-entry.d.ts +1 -0
- package/lib/types/client/univer/standalone-entry.js +19 -0
- package/lib/types/client/versions/VersionChrome.d.ts +33 -0
- package/lib/types/client/versions/VersionChrome.js +21 -0
- package/lib/types/client/versions/inspect.d.ts +70 -0
- package/lib/types/client/versions/inspect.js +88 -0
- package/lib/types/client/versions/preview-decoration.d.ts +11 -0
- package/lib/types/client/versions/preview-decoration.js +54 -0
- package/lib/types/client/versions/records.d.ts +19 -0
- package/lib/types/client/versions/records.js +70 -0
- package/lib/types/client/workspace-host.d.ts +61 -0
- package/lib/types/client/workspace-host.js +124 -0
- package/lib/types/collaboration-hub.d.ts +31 -0
- package/lib/types/collaboration-hub.js +69 -0
- package/lib/types/contract/ids.d.ts +24 -0
- package/lib/types/contract/ids.js +46 -0
- package/lib/types/contract/mutations.d.ts +25 -0
- package/lib/types/contract/mutations.js +782 -0
- package/lib/types/contract/operations.d.ts +54 -0
- package/lib/types/contract/operations.js +203 -0
- package/lib/types/contract/snapshots.d.ts +176 -0
- package/lib/types/contract/snapshots.js +351 -0
- package/lib/types/contract/univer-api-catalog.d.ts +29 -0
- package/lib/types/contract/univer-api-catalog.js +264 -0
- package/lib/types/errors.d.ts +8 -0
- package/lib/types/errors.js +12 -0
- package/lib/types/http/assets-routes.d.ts +3 -0
- package/lib/types/http/assets-routes.js +169 -0
- package/lib/types/http/collaboration.d.ts +10 -0
- package/lib/types/http/collaboration.js +154 -0
- package/lib/types/http/history.d.ts +15 -0
- package/lib/types/http/history.js +134 -0
- package/lib/types/http/project-files.d.ts +79 -0
- package/lib/types/http/project-files.js +206 -0
- package/lib/types/http/project-scope.d.ts +50 -0
- package/lib/types/http/project-scope.js +52 -0
- package/lib/types/http/routes.d.ts +98 -0
- package/lib/types/http/routes.js +323 -0
- package/lib/types/http/standalone.d.ts +9 -0
- package/lib/types/http/standalone.js +31 -0
- package/lib/types/http/trunk.d.ts +10 -0
- package/lib/types/http/trunk.js +322 -0
- package/lib/types/http/worktree.d.ts +9 -0
- package/lib/types/http/worktree.js +145 -0
- package/lib/types/index.d.ts +18 -0
- package/lib/types/index.js +156 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/invariant.js +15 -0
- package/lib/types/request-params.d.ts +21 -0
- package/lib/types/request-params.js +76 -0
- package/lib/types/store/paths.d.ts +68 -0
- package/lib/types/store/paths.js +148 -0
- package/lib/types/store/project.d.ts +88 -0
- package/lib/types/store/project.js +120 -0
- package/lib/types/store/read.d.ts +110 -0
- package/lib/types/store/read.js +404 -0
- package/lib/types/store/retention.d.ts +27 -0
- package/lib/types/store/retention.js +111 -0
- package/lib/types/store/write.d.ts +22 -0
- package/lib/types/store/write.js +210 -0
- package/lib/types/tools/arguments.d.ts +40 -0
- package/lib/types/tools/arguments.js +130 -0
- package/lib/types/tools/cells.d.ts +50 -0
- package/lib/types/tools/cells.js +298 -0
- package/lib/types/tools/index.d.ts +2 -0
- package/lib/types/tools/index.js +250 -0
- package/lib/types/tools/render.d.ts +47 -0
- package/lib/types/tools/render.js +290 -0
- package/lib/types/tools/univer-api.d.ts +18 -0
- package/lib/types/tools/univer-api.js +198 -0
- package/lib/types/tools/workbook-catalog.d.ts +3 -0
- package/lib/types/tools/workbook-catalog.js +83 -0
- package/lib/types/tools/workbook-edit.d.ts +192 -0
- package/lib/types/tools/workbook-edit.js +912 -0
- package/lib/types/tools/workbook-find.d.ts +11 -0
- package/lib/types/tools/workbook-find.js +89 -0
- package/lib/types/tools/workbook-history.d.ts +2 -0
- package/lib/types/tools/workbook-history.js +59 -0
- package/lib/types/tools/workbook-read.d.ts +21 -0
- package/lib/types/tools/workbook-read.js +204 -0
- package/lib/types/types.d.ts +377 -0
- package/lib/types/types.js +34 -0
- package/lib/types/univer/apply-operation.d.ts +14 -0
- package/lib/types/univer/apply-operation.js +398 -0
- package/lib/types/univer/chunk.d.ts +68 -0
- package/lib/types/univer/chunk.js +16 -0
- package/lib/types/univer/decoration.d.ts +34 -0
- package/lib/types/univer/decoration.js +88 -0
- package/lib/types/univer/diagnostics.d.ts +31 -0
- package/lib/types/univer/diagnostics.js +141 -0
- package/lib/types/univer/engine.d.ts +33 -0
- package/lib/types/univer/engine.js +338 -0
- package/lib/types/univer/entry.d.ts +8 -0
- package/lib/types/univer/entry.js +11 -0
- package/lib/types/univer/facade.d.ts +107 -0
- package/lib/types/univer/facade.js +1 -0
- package/lib/types/univer/fonts.d.ts +202 -0
- package/lib/types/univer/fonts.js +140 -0
- package/lib/types/univer/plugins/collaboration.d.ts +23 -0
- package/lib/types/univer/plugins/collaboration.js +47 -0
- package/lib/types/univer/plugins/export.d.ts +39 -0
- package/lib/types/univer/plugins/export.js +246 -0
- package/lib/types/univer/plugins/history.d.ts +24 -0
- package/lib/types/univer/plugins/history.js +50 -0
- package/lib/types/univer/plugins/version.d.ts +13 -0
- package/lib/types/univer/plugins/version.js +13 -0
- package/lib/types/version/diff.d.ts +36 -0
- package/lib/types/version/diff.js +156 -0
- package/lib/types/version/grouping.d.ts +7 -0
- package/lib/types/version/grouping.js +94 -0
- package/lib/types/version/records.d.ts +17 -0
- package/lib/types/version/records.js +102 -0
- package/lib/types/version/session.d.ts +36 -0
- package/lib/types/version/session.js +89 -0
- package/lib/types/version/summary.d.ts +24 -0
- package/lib/types/version/summary.js +111 -0
- package/lib/types/workbook-manager.d.ts +191 -0
- package/lib/types/workbook-manager.js +935 -0
- package/lib/types/xlsx-export-worker.d.ts +5 -0
- package/lib/types/xlsx-export-worker.js +24 -0
- package/lib/types/xlsx-export.d.ts +21 -0
- package/lib/types/xlsx-export.js +371 -0
- package/lib/types/xlsx-import-worker.d.ts +1 -0
- package/lib/types/xlsx-import-worker.js +19 -0
- package/lib/univer-client.js +690 -0
- package/lib/workbook-page.js +23826 -0
- package/lib/xlsx-export-worker.js +104 -0
- package/lib/xlsx-import-worker.js +46 -0
- package/package.json +106 -3
- package/src/audit.ts +58 -0
- package/src/client/ProjectImportMenu.tsx +100 -0
- package/src/client/SheetPanel.module.css +1578 -0
- package/src/client/SheetPanel.tsx +1934 -0
- package/src/client/SheetToggle.module.css +73 -0
- package/src/client/SheetToggle.tsx +65 -0
- package/src/client/contract.d.ts +19 -0
- package/src/client/index.ts +131 -0
- package/src/client/persistence/changes.ts +58 -0
- package/src/client/persistence/ids.ts +60 -0
- package/src/client/persistence/merge.ts +392 -0
- package/src/client/store.ts +188 -0
- package/src/client/surface-context.ts +123 -0
- package/src/client/surface-control.ts +13 -0
- package/src/client/transport/workbookApi.ts +355 -0
- package/src/client/univer/SheetView.module.css +110 -0
- package/src/client/univer/StandaloneWorkbookPage.module.css +8 -0
- package/src/client/univer/StandaloneWorkbookPage.tsx +15 -0
- package/src/client/univer/WorkbookCanvas.tsx +128 -0
- package/src/client/univer/WorkbookView.module.css +30 -0
- package/src/client/univer/WorkbookView.tsx +3 -0
- package/src/client/univer/loader.ts +90 -0
- package/src/client/univer/react-dom-client.d.ts +10 -0
- package/src/client/univer/standalone-entry.tsx +19 -0
- package/src/client/univer/standalone.css +12 -0
- package/src/client/univer/univer-locales.d.ts +89 -0
- package/src/client/univer/univer-modules.d.ts +26 -0
- package/src/client/versions/VersionChrome.tsx +114 -0
- package/src/client/versions/inspect.ts +138 -0
- package/src/client/versions/preview-decoration.ts +54 -0
- package/src/client/versions/records.ts +78 -0
- package/src/client/workspace-host.ts +147 -0
- package/src/collaboration-hub.ts +83 -0
- package/src/contract/ids.ts +50 -0
- package/src/contract/mutations.ts +791 -0
- package/src/contract/operations.ts +221 -0
- package/src/contract/snapshots.ts +383 -0
- package/src/contract/univer-api-catalog.ts +289 -0
- package/src/css-modules.d.ts +6 -0
- package/src/errors.ts +25 -0
- package/src/http/assets-routes.ts +177 -0
- package/src/http/collaboration.ts +144 -0
- package/src/http/history.ts +151 -0
- package/src/http/project-files.ts +227 -0
- package/src/http/project-scope.ts +77 -0
- package/src/http/routes.ts +364 -0
- package/src/http/standalone.ts +32 -0
- package/src/http/trunk.ts +322 -0
- package/src/http/worktree.ts +119 -0
- package/src/index.ts +165 -0
- package/src/invariant.ts +17 -0
- package/src/request-params.ts +92 -0
- package/src/store/paths.ts +165 -0
- package/src/store/project.ts +137 -0
- package/src/store/read.ts +454 -0
- package/src/store/retention.ts +106 -0
- package/src/store/write.ts +230 -0
- package/src/tools/arguments.ts +128 -0
- package/src/tools/cells.ts +298 -0
- package/src/tools/index.ts +274 -0
- package/src/tools/render.ts +301 -0
- package/src/tools/univer-api.ts +243 -0
- package/src/tools/workbook-catalog.ts +94 -0
- package/src/tools/workbook-edit.ts +987 -0
- package/src/tools/workbook-find.ts +80 -0
- package/src/tools/workbook-history.ts +63 -0
- package/src/tools/workbook-read.ts +210 -0
- package/src/types.ts +432 -0
- package/src/univer/apply-operation.ts +369 -0
- package/src/univer/chunk.ts +77 -0
- package/src/univer/decoration.ts +111 -0
- package/src/univer/diagnostics.ts +136 -0
- package/src/univer/engine.ts +370 -0
- package/src/univer/entry.ts +14 -0
- package/src/univer/facade.ts +91 -0
- package/src/univer/fonts.ts +148 -0
- package/src/univer/overrides.css +33 -0
- package/src/univer/plugins/collaboration.ts +47 -0
- package/src/univer/plugins/export.ts +236 -0
- package/src/univer/plugins/history.ts +49 -0
- package/src/univer/plugins/version.ts +13 -0
- package/src/version/diff.ts +169 -0
- package/src/version/grouping.ts +96 -0
- package/src/version/records.ts +113 -0
- package/src/version/session.ts +90 -0
- package/src/version/summary.ts +117 -0
- package/src/workbook-manager.ts +1111 -0
- package/src/xlsx-export-worker.ts +34 -0
- package/src/xlsx-export.ts +373 -0
- package/src/xlsx-import-worker.ts +30 -0
|
@@ -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 const manifest = 'dsh-data-table';
|
|
12
|
+
export const invariant = {
|
|
13
|
+
manifest,
|
|
14
|
+
reason: 'renderer input is the host-validated tool-call arguments (isSheetData); no runtime event/data relation to check',
|
|
15
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request vocabulary shared by both transports (docs/issue/0022 P3).
|
|
3
|
+
*
|
|
4
|
+
* The browser posts a JSON body and the Agent passes tool arguments, but both
|
|
5
|
+
* have to become the same `WorkbookOperationPayload`. These parsers are that
|
|
6
|
+
* translation, and they are validation-first: each one rejects what it cannot
|
|
7
|
+
* represent instead of coercing it.
|
|
8
|
+
*
|
|
9
|
+
* They live here rather than in `http/` because the Agent tools need exactly the
|
|
10
|
+
* same conversions, and a tool importing from the HTTP layer would make the two
|
|
11
|
+
* transports depend on each other.
|
|
12
|
+
*/
|
|
13
|
+
import { type WorkbookOperationPayload, type WorkbookOperationType, type WorkbookRange } from './types.ts';
|
|
14
|
+
export declare function parseIndex(value: unknown, label: string): number;
|
|
15
|
+
export declare function optionalRevision(value: unknown): number | undefined;
|
|
16
|
+
export declare function isOperationType(value: unknown): value is WorkbookOperationType;
|
|
17
|
+
export declare function asOperationPayload(value: unknown): WorkbookOperationPayload;
|
|
18
|
+
export declare function parseRange(value: unknown): WorkbookRange;
|
|
19
|
+
export declare function a1ColumnIndex(value: string): number;
|
|
20
|
+
export declare function parseA1Range(value: string): WorkbookRange;
|
|
21
|
+
export declare function normalizeValueMatrix(value: unknown): unknown[][];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { validateRange } from "./contract/snapshots.js";
|
|
2
|
+
export function parseIndex(value, label) {
|
|
3
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0)
|
|
4
|
+
throw new Error(`${label} 必须是大于等于 0 的整数`);
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
export function optionalRevision(value) {
|
|
8
|
+
if (value === undefined || value === null)
|
|
9
|
+
return undefined;
|
|
10
|
+
return parseIndex(value, 'baseRevision');
|
|
11
|
+
}
|
|
12
|
+
export function isOperationType(value) {
|
|
13
|
+
return value === 'update_cells' || value === 'clear_range' || value === 'delete_cells'
|
|
14
|
+
|| value === 'insert_rows' || value === 'delete_rows' || value === 'insert_columns'
|
|
15
|
+
|| value === 'delete_columns' || value === 'create_sheet' || value === 'delete_sheet'
|
|
16
|
+
|| value === 'rename_sheet' || value === 'append_rows' || value === 'format_cells'
|
|
17
|
+
|| value === 'merge_cells' || value === 'unmerge_cells' || value === 'freeze_panes'
|
|
18
|
+
|| value === 'set_column_width' || value === 'set_row_height' || value === 'sort_range'
|
|
19
|
+
|| value === 'set_hyperlink' || value === 'remove_hyperlink'
|
|
20
|
+
|| value === 'remove_duplicates'
|
|
21
|
+
|| value === 'batch' || value === 'snapshot_replace';
|
|
22
|
+
}
|
|
23
|
+
export function asOperationPayload(value) {
|
|
24
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
25
|
+
throw new Error('operation payload 必须是对象');
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
export function parseRange(value) {
|
|
29
|
+
if (typeof value === 'string')
|
|
30
|
+
return parseA1Range(value);
|
|
31
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
32
|
+
throw new Error('range 必须是对象');
|
|
33
|
+
const raw = value;
|
|
34
|
+
const range = {
|
|
35
|
+
startRow: parseIndex(raw.startRow, 'range.startRow'),
|
|
36
|
+
startColumn: parseIndex(raw.startColumn, 'range.startColumn'),
|
|
37
|
+
endRow: parseIndex(raw.endRow, 'range.endRow'),
|
|
38
|
+
endColumn: parseIndex(raw.endColumn, 'range.endColumn'),
|
|
39
|
+
};
|
|
40
|
+
// One validator, not two opinions. This checked only the span, so a range far
|
|
41
|
+
// past the end of any sheet (`startRow: 9e8`) was accepted here and rejected
|
|
42
|
+
// later by whichever mutator happened to consult the absolute bound — with a
|
|
43
|
+
// different message. `parseA1Range` reaches this through the object branch, so
|
|
44
|
+
// both spellings of a range are now validated identically.
|
|
45
|
+
validateRange(range);
|
|
46
|
+
return range;
|
|
47
|
+
}
|
|
48
|
+
export function a1ColumnIndex(value) {
|
|
49
|
+
let result = 0;
|
|
50
|
+
for (const character of value.toUpperCase())
|
|
51
|
+
result = result * 26 + character.charCodeAt(0) - 64;
|
|
52
|
+
return result - 1;
|
|
53
|
+
}
|
|
54
|
+
export function parseA1Range(value) {
|
|
55
|
+
const match = /^\s*([A-Za-z]+)(\d+)(?::([A-Za-z]+)(\d+))?\s*$/.exec(value);
|
|
56
|
+
if (match === null)
|
|
57
|
+
throw new Error('range 必须是 0-based 对象,或类似 A1:D4 的范围字符串');
|
|
58
|
+
const startColumn = a1ColumnIndex(match[1]);
|
|
59
|
+
const startRow = Number(match[2]) - 1;
|
|
60
|
+
const endColumn = match[3] === undefined ? startColumn : a1ColumnIndex(match[3]);
|
|
61
|
+
const endRow = match[4] === undefined ? startRow : Number(match[4]) - 1;
|
|
62
|
+
return parseRange({ startRow, startColumn, endRow, endColumn });
|
|
63
|
+
}
|
|
64
|
+
export function normalizeValueMatrix(value) {
|
|
65
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
66
|
+
throw new Error('values 必须是非空二维数组');
|
|
67
|
+
const rows = value.every(item => !Array.isArray(item)) ? [value] : value;
|
|
68
|
+
if (!rows.every(item => Array.isArray(item)))
|
|
69
|
+
throw new Error('values 必须是二维数组');
|
|
70
|
+
const width = Math.max(...rows.map(item => item.length));
|
|
71
|
+
if (width === 0)
|
|
72
|
+
throw new Error('values 不能包含空行');
|
|
73
|
+
// Ragged model-generated matrices are padded instead of rejected. This
|
|
74
|
+
// makes a four-row write safe when one row has a missing trailing field.
|
|
75
|
+
return rows.map(item => Array.from({ length: width }, (_value, index) => item[index] ?? null));
|
|
76
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Resolve the DSH home, honoring $DSH_HOME (mirror of dsh-home-paths). */
|
|
2
|
+
export declare function dshHome(): string;
|
|
3
|
+
/**
|
|
4
|
+
* Repository root for this plugin.
|
|
5
|
+
*
|
|
6
|
+
* Two answers, and the difference is which project the call belongs to:
|
|
7
|
+
*
|
|
8
|
+
* - **inside a Session** (an HTTP route or a tool call, which establish the
|
|
9
|
+
* scope — see `./project.ts`): `<project>/storages/data-table`, so a
|
|
10
|
+
* project directory is self-contained.
|
|
11
|
+
* - **outside one** (a script, a test, an un-migrated caller): the pre-project
|
|
12
|
+
* per-user location, unchanged. Answering that case with `process.cwd()`
|
|
13
|
+
* would silently start a *new* store for a caller that was reading the old
|
|
14
|
+
* one, which is worse than keeping it.
|
|
15
|
+
*
|
|
16
|
+
* `data-sheet` → `data-table` renamed that per-user root; resolving the root is
|
|
17
|
+
* the single choke point every read and write goes through, so the one-time
|
|
18
|
+
* migration runs here and is memoized afterwards.
|
|
19
|
+
*/
|
|
20
|
+
export declare function workbookRoot(): string;
|
|
21
|
+
/**
|
|
22
|
+
* The pre-project, per-user storage root.
|
|
23
|
+
*
|
|
24
|
+
* Named rather than inlined because it is read by more than the fallback: the
|
|
25
|
+
* workbook list offers its contents as "from the old location" for a user who
|
|
26
|
+
* has workbooks there and none here yet.
|
|
27
|
+
*/
|
|
28
|
+
export declare function globalWorkbookRoot(): string;
|
|
29
|
+
/** Storage root used before the data-sheet → data-table rename. */
|
|
30
|
+
export declare function legacyWorkbookRoot(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Move `<dshHome>/storages/data-sheet` to `storages/data-table` exactly once.
|
|
33
|
+
*
|
|
34
|
+
* Idempotent and safe to call from every read/write path: the old root is only
|
|
35
|
+
* moved when the new root does not exist yet, so a second call is a no-op and
|
|
36
|
+
* two populated roots are never merged (the legacy files are left untouched
|
|
37
|
+
* rather than deleted).
|
|
38
|
+
* @returns the outcome of the first migration attempt.
|
|
39
|
+
*/
|
|
40
|
+
export declare function migrateLegacyWorkbookStorage(): WorkbookStorageMigration;
|
|
41
|
+
/** Current root without triggering the migration (avoids recursion). */
|
|
42
|
+
/**
|
|
43
|
+
* Where oversized tool results are spilled.
|
|
44
|
+
*
|
|
45
|
+
* Under the storage root, and here rather than in the module that writes them:
|
|
46
|
+
* what a spilled result *is* belongs to the tool presentation, but where plugin
|
|
47
|
+
* data lives is the store's business — and so is what to do when there is too
|
|
48
|
+
* much of it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function toolOutputRoot(): string;
|
|
51
|
+
export declare function workbookRootWithoutMigration(): string;
|
|
52
|
+
/** Absolute snapshot path for one workbook. */
|
|
53
|
+
export declare function workbookPath(id: string): string;
|
|
54
|
+
/** Lightweight metadata path used by polling, workbook lists, and history. */
|
|
55
|
+
export declare function workbookIndexPath(id: string): string;
|
|
56
|
+
export declare function workbookSnapshotPath(id: string): string;
|
|
57
|
+
export declare function workbookCheckpointsPath(id: string): string;
|
|
58
|
+
/** Full operation log is kept out of the workbook metadata file. */
|
|
59
|
+
export declare function workbookOperationsPath(id: string): string;
|
|
60
|
+
/** Pending proposals (which may contain a full preview snapshot) are sidecar data. */
|
|
61
|
+
export declare function workbookProposalsPath(id: string): string;
|
|
62
|
+
export declare function workbookTransactionPath(id: string): string;
|
|
63
|
+
/** Isolated review worktrees live below the workbook repository and never
|
|
64
|
+
* participate in the trunk workbook catalog. */
|
|
65
|
+
export declare function workbookWorktreeDirectory(workbookId: string): string;
|
|
66
|
+
export declare function workbookWorktreePath(workbookId: string, worktreeId: string): string;
|
|
67
|
+
/** Outcome of {@link migrateLegacyWorkbookStorage}. */
|
|
68
|
+
export type WorkbookStorageMigration = 'no-legacy' | 'migrated' | 'target-exists' | 'failed';
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a workbook's files live in the DSH home (docs/issue/0022 P1).
|
|
3
|
+
*
|
|
4
|
+
* The single choke point every read and write goes through, which is why the
|
|
5
|
+
* one-time `data-sheet` → `data-table` rename is performed here rather than at
|
|
6
|
+
* a call site. Nothing else in the plugin may build a storage path.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, renameSync } from 'node:fs';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { dirname, join, resolve } from 'node:path';
|
|
11
|
+
import { normalizeWorkbookId, normalizeWorktreeId } from "../contract/ids.js";
|
|
12
|
+
import { currentProjectRoot, projectStoragesRoot } from "./project.js";
|
|
13
|
+
/**
|
|
14
|
+
* The home the migration has already been attempted for, and what happened.
|
|
15
|
+
*
|
|
16
|
+
* Keyed by the resolved home rather than a bare boolean: `$DSH_HOME` is read on
|
|
17
|
+
* every call, and the previous process-wide flag meant a home that changed
|
|
18
|
+
* mid-process was never migrated at all — it returned the *other* home's
|
|
19
|
+
* outcome. The memo exists to avoid re-stat-ing on every operation, which keying
|
|
20
|
+
* by home still does.
|
|
21
|
+
*/
|
|
22
|
+
let storageMigrationHome = null;
|
|
23
|
+
let storageMigrationOutcome = 'no-legacy';
|
|
24
|
+
/** Resolve the DSH home, honoring $DSH_HOME (mirror of dsh-home-paths). */
|
|
25
|
+
export function dshHome() {
|
|
26
|
+
const env = process.env.DSH_HOME;
|
|
27
|
+
const home = env !== undefined && env.trim().length > 0 ? env : join(homedir(), '.dsh');
|
|
28
|
+
return resolve(home);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Repository root for this plugin.
|
|
32
|
+
*
|
|
33
|
+
* Two answers, and the difference is which project the call belongs to:
|
|
34
|
+
*
|
|
35
|
+
* - **inside a Session** (an HTTP route or a tool call, which establish the
|
|
36
|
+
* scope — see `./project.ts`): `<project>/storages/data-table`, so a
|
|
37
|
+
* project directory is self-contained.
|
|
38
|
+
* - **outside one** (a script, a test, an un-migrated caller): the pre-project
|
|
39
|
+
* per-user location, unchanged. Answering that case with `process.cwd()`
|
|
40
|
+
* would silently start a *new* store for a caller that was reading the old
|
|
41
|
+
* one, which is worse than keeping it.
|
|
42
|
+
*
|
|
43
|
+
* `data-sheet` → `data-table` renamed that per-user root; resolving the root is
|
|
44
|
+
* the single choke point every read and write goes through, so the one-time
|
|
45
|
+
* migration runs here and is memoized afterwards.
|
|
46
|
+
*/
|
|
47
|
+
export function workbookRoot() {
|
|
48
|
+
migrateLegacyWorkbookStorage();
|
|
49
|
+
const project = currentProjectRoot();
|
|
50
|
+
return project === undefined ? globalWorkbookRoot() : projectStoragesRoot(project);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The pre-project, per-user storage root.
|
|
54
|
+
*
|
|
55
|
+
* Named rather than inlined because it is read by more than the fallback: the
|
|
56
|
+
* workbook list offers its contents as "from the old location" for a user who
|
|
57
|
+
* has workbooks there and none here yet.
|
|
58
|
+
*/
|
|
59
|
+
export function globalWorkbookRoot() {
|
|
60
|
+
return join(dshHome(), 'storages', 'data-table');
|
|
61
|
+
}
|
|
62
|
+
/** Storage root used before the data-sheet → data-table rename. */
|
|
63
|
+
export function legacyWorkbookRoot() {
|
|
64
|
+
return join(dshHome(), 'storages', 'data-sheet');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Move `<dshHome>/storages/data-sheet` to `storages/data-table` exactly once.
|
|
68
|
+
*
|
|
69
|
+
* Idempotent and safe to call from every read/write path: the old root is only
|
|
70
|
+
* moved when the new root does not exist yet, so a second call is a no-op and
|
|
71
|
+
* two populated roots are never merged (the legacy files are left untouched
|
|
72
|
+
* rather than deleted).
|
|
73
|
+
* @returns the outcome of the first migration attempt.
|
|
74
|
+
*/
|
|
75
|
+
export function migrateLegacyWorkbookStorage() {
|
|
76
|
+
const home = dshHome();
|
|
77
|
+
if (storageMigrationHome === home)
|
|
78
|
+
return storageMigrationOutcome;
|
|
79
|
+
storageMigrationHome = home;
|
|
80
|
+
const legacy = legacyWorkbookRoot();
|
|
81
|
+
const target = workbookRootWithoutMigration();
|
|
82
|
+
try {
|
|
83
|
+
if (legacy === target || !existsSync(legacy))
|
|
84
|
+
return (storageMigrationOutcome = 'no-legacy');
|
|
85
|
+
if (existsSync(target))
|
|
86
|
+
return (storageMigrationOutcome = 'target-exists');
|
|
87
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
88
|
+
renameSync(legacy, target);
|
|
89
|
+
return (storageMigrationOutcome = 'migrated');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// A failed rename must never take the plugin down; the legacy root stays
|
|
93
|
+
// where it is and the next call retries.
|
|
94
|
+
storageMigrationHome = null;
|
|
95
|
+
return (storageMigrationOutcome = 'failed');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Current root without triggering the migration (avoids recursion). */
|
|
99
|
+
/**
|
|
100
|
+
* Where oversized tool results are spilled.
|
|
101
|
+
*
|
|
102
|
+
* Under the storage root, and here rather than in the module that writes them:
|
|
103
|
+
* what a spilled result *is* belongs to the tool presentation, but where plugin
|
|
104
|
+
* data lives is the store's business — and so is what to do when there is too
|
|
105
|
+
* much of it.
|
|
106
|
+
*/
|
|
107
|
+
export function toolOutputRoot() {
|
|
108
|
+
return join(workbookRoot(), 'tool-output');
|
|
109
|
+
}
|
|
110
|
+
export function workbookRootWithoutMigration() {
|
|
111
|
+
return join(dshHome(), 'storages', 'data-table');
|
|
112
|
+
}
|
|
113
|
+
/** Absolute snapshot path for one workbook. */
|
|
114
|
+
export function workbookPath(id) {
|
|
115
|
+
// Keep this boundary safe even for callers outside the tool/HTTP adapters.
|
|
116
|
+
// Those adapters already normalize ids, but this exported repository helper
|
|
117
|
+
// must not allow `../` segments to escape the Datasheet storage directory.
|
|
118
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.json`);
|
|
119
|
+
}
|
|
120
|
+
/** Lightweight metadata path used by polling, workbook lists, and history. */
|
|
121
|
+
export function workbookIndexPath(id) {
|
|
122
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.index.json`);
|
|
123
|
+
}
|
|
124
|
+
export function workbookSnapshotPath(id) {
|
|
125
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.snapshot.json`);
|
|
126
|
+
}
|
|
127
|
+
export function workbookCheckpointsPath(id) {
|
|
128
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.checkpoints.json`);
|
|
129
|
+
}
|
|
130
|
+
/** Full operation log is kept out of the workbook metadata file. */
|
|
131
|
+
export function workbookOperationsPath(id) {
|
|
132
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.operations.json`);
|
|
133
|
+
}
|
|
134
|
+
/** Pending proposals (which may contain a full preview snapshot) are sidecar data. */
|
|
135
|
+
export function workbookProposalsPath(id) {
|
|
136
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.proposals.json`);
|
|
137
|
+
}
|
|
138
|
+
export function workbookTransactionPath(id) {
|
|
139
|
+
return join(workbookRoot(), `${normalizeWorkbookId(id)}.transaction.json`);
|
|
140
|
+
}
|
|
141
|
+
/** Isolated review worktrees live below the workbook repository and never
|
|
142
|
+
* participate in the trunk workbook catalog. */
|
|
143
|
+
export function workbookWorktreeDirectory(workbookId) {
|
|
144
|
+
return join(workbookRoot(), '.worktrees', normalizeWorkbookId(workbookId));
|
|
145
|
+
}
|
|
146
|
+
export function workbookWorktreePath(workbookId, worktreeId) {
|
|
147
|
+
return join(workbookWorktreeDirectory(workbookId), `${normalizeWorktreeId(worktreeId)}.json`);
|
|
148
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** This plugin's directory inside a project's `storages/`. */
|
|
2
|
+
export declare const PLUGIN_DIRECTORY = "data-table";
|
|
3
|
+
/**
|
|
4
|
+
* Run one unit of work with a project root in scope.
|
|
5
|
+
*
|
|
6
|
+
* @param root - Absolute project directory.
|
|
7
|
+
* @param body - The work; may be async, and keeps the scope across its awaits.
|
|
8
|
+
* @returns whatever `body` returns.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runInProject<T>(root: string, body: () => T): T;
|
|
11
|
+
/**
|
|
12
|
+
* The project root in scope, when there is one.
|
|
13
|
+
*
|
|
14
|
+
* `undefined` means "no project": a script, a test, or a caller that has not been
|
|
15
|
+
* migrated. `workbookRoot()` answers that case with the pre-project location
|
|
16
|
+
* rather than inventing a project, so an un-migrated caller keeps reading and
|
|
17
|
+
* writing where it always did instead of quietly starting a new store.
|
|
18
|
+
*/
|
|
19
|
+
export declare function currentProjectRoot(): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* The bits of DSH's Session registry this module reads.
|
|
22
|
+
*
|
|
23
|
+
* Read structurally — it arrives from the Host, and a caller may run without it
|
|
24
|
+
* (a test harness, an embedding with no Host services).
|
|
25
|
+
*/
|
|
26
|
+
export interface SessionRegistry {
|
|
27
|
+
/** Look one Session up by id; `undefined` when no such Session is live. */
|
|
28
|
+
get?: (id: string) => {
|
|
29
|
+
readonly header?: {
|
|
30
|
+
readonly cwd?: unknown;
|
|
31
|
+
};
|
|
32
|
+
} | undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The project directory one Session's store calls belong to, or `undefined` for
|
|
36
|
+
* "no project".
|
|
37
|
+
*
|
|
38
|
+
* The Session **header** is the only source, and it is read through the Host's
|
|
39
|
+
* registry (`ctx.sessions.get(id)`) rather than off the calling Agent's own
|
|
40
|
+
* Session object. That is the fix for a real bug (2026-10-02): the tools read
|
|
41
|
+
* `agent.session.cwd`, a field a Session does not have — a Session carries its
|
|
42
|
+
* directory at `session.header.cwd` — so every tool call answered "no project"
|
|
43
|
+
* and wrote to the per-user store while the browser panel wrote to
|
|
44
|
+
* `<project>/storages/data-table`. The Agent and the open Table were editing two
|
|
45
|
+
* different sets of workbooks, and neither could see the other's.
|
|
46
|
+
*
|
|
47
|
+
* Reading the registry also makes the two transports agree by construction: an
|
|
48
|
+
* HTTP request names a Session id, a tool call names the same id through its
|
|
49
|
+
* Agent, and both are answered by the same header. `data-excalidraw` resolves its
|
|
50
|
+
* scene directory the same way, for the same reason.
|
|
51
|
+
*
|
|
52
|
+
* The Host's sandbox policy is deliberately *not* consulted: a plugin context
|
|
53
|
+
* cannot read it (see `src/index.ts`), and the one attempt to use it as a
|
|
54
|
+
* fallback turned every workbook list into a bodyless 400.
|
|
55
|
+
*
|
|
56
|
+
* `undefined` is a real answer and not a failure: it means the caller named no
|
|
57
|
+
* Session, so nobody has said which project this is. The two ways to get it
|
|
58
|
+
* wrong are both worse than a missing project — `process.cwd()` would silently
|
|
59
|
+
* start a *new* per-project store for a caller that was reading the old per-user
|
|
60
|
+
* one (a test, a script, an older client), and a shared per-process default
|
|
61
|
+
* would mix two Sessions' writes. Callers therefore leave the scope unset, and
|
|
62
|
+
* `workbookRoot()` answers with the pre-project location it always used.
|
|
63
|
+
*
|
|
64
|
+
* @param sessions - DSH's Session registry, from the injected `sessions` service.
|
|
65
|
+
* @param sessionId - The Session naming the project; `undefined` or empty for a
|
|
66
|
+
* caller that named none.
|
|
67
|
+
* @returns an absolute project directory, or `undefined` when nothing named one.
|
|
68
|
+
*/
|
|
69
|
+
export declare function projectScopeOf(sessions: SessionRegistry | undefined, sessionId: string | undefined): string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* This plugin's storage inside one project.
|
|
72
|
+
*
|
|
73
|
+
* @param root - Project directory.
|
|
74
|
+
* @returns `<root>/storages/data-table`, which may not exist yet.
|
|
75
|
+
*/
|
|
76
|
+
export declare function projectStoragesRoot(root: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* The project's storage container, shared by every plugin.
|
|
79
|
+
*
|
|
80
|
+
* The file chooser lists this directory rather than the project root: it is
|
|
81
|
+
* where a project's data lives, and it is what a copy of the project directory
|
|
82
|
+
* carries. A user who wants a file from elsewhere uses the "从项目外导入" path,
|
|
83
|
+
* which is deliberately unrestricted.
|
|
84
|
+
*
|
|
85
|
+
* @param root - Project directory.
|
|
86
|
+
* @returns `<root>/storages`, which may not exist yet.
|
|
87
|
+
*/
|
|
88
|
+
export declare function projectStoragesDirectory(root: string): string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which project a store call belongs to (project-local storage, 2026-09-25).
|
|
3
|
+
*
|
|
4
|
+
* A workbook used to live in one per-user directory (`~/.dsh/storages/data-table`)
|
|
5
|
+
* shared by every Session. It now lives in the project it belongs to —
|
|
6
|
+
* `<project>/storages/data-table` — so copying a project directory copies its
|
|
7
|
+
* workbooks, and deleting one leaves nothing behind in the user's home.
|
|
8
|
+
*
|
|
9
|
+
* The store modules are called from three places that do **not** share a call
|
|
10
|
+
* shape: HTTP route handlers (which name a Session id), Agent tool executions
|
|
11
|
+
* (which carry the calling Agent), and plain scripts and tests (which have
|
|
12
|
+
* neither). Rather than thread a root parameter through every read and write —
|
|
13
|
+
* `workbookRoot()` is the single choke point they all pass through — the project
|
|
14
|
+
* travels in an async context for the duration of one request or tool call.
|
|
15
|
+
* `AsyncLocalStorage` rather than a module variable because one Host process
|
|
16
|
+
* serves several Sessions at once: a global would let two projects' writes
|
|
17
|
+
* interleave into the wrong directory, which is the one failure this must not
|
|
18
|
+
* have.
|
|
19
|
+
*
|
|
20
|
+
* The three callers converge on one question, asked in one place:
|
|
21
|
+
* {@link projectScopeOf} turns "this Session" into "this project directory" by
|
|
22
|
+
* looking the Session up in the Host's registry and reading its **header**. A
|
|
23
|
+
* tool and a browser request naming the same Session therefore mean the same
|
|
24
|
+
* directory by construction rather than by both computing it the same way.
|
|
25
|
+
*
|
|
26
|
+
* @module dsh-data-table/store/project
|
|
27
|
+
*/
|
|
28
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import { STORAGES_DIRECTORY, isProjectDirectory } from '#contracts';
|
|
31
|
+
/** This plugin's directory inside a project's `storages/`. */
|
|
32
|
+
export const PLUGIN_DIRECTORY = 'data-table';
|
|
33
|
+
const scope = new AsyncLocalStorage();
|
|
34
|
+
/**
|
|
35
|
+
* Run one unit of work with a project root in scope.
|
|
36
|
+
*
|
|
37
|
+
* @param root - Absolute project directory.
|
|
38
|
+
* @param body - The work; may be async, and keeps the scope across its awaits.
|
|
39
|
+
* @returns whatever `body` returns.
|
|
40
|
+
*/
|
|
41
|
+
export function runInProject(root, body) {
|
|
42
|
+
return scope.run(root, body);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The project root in scope, when there is one.
|
|
46
|
+
*
|
|
47
|
+
* `undefined` means "no project": a script, a test, or a caller that has not been
|
|
48
|
+
* migrated. `workbookRoot()` answers that case with the pre-project location
|
|
49
|
+
* rather than inventing a project, so an un-migrated caller keeps reading and
|
|
50
|
+
* writing where it always did instead of quietly starting a new store.
|
|
51
|
+
*/
|
|
52
|
+
export function currentProjectRoot() {
|
|
53
|
+
return scope.getStore();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The project directory one Session's store calls belong to, or `undefined` for
|
|
57
|
+
* "no project".
|
|
58
|
+
*
|
|
59
|
+
* The Session **header** is the only source, and it is read through the Host's
|
|
60
|
+
* registry (`ctx.sessions.get(id)`) rather than off the calling Agent's own
|
|
61
|
+
* Session object. That is the fix for a real bug (2026-10-02): the tools read
|
|
62
|
+
* `agent.session.cwd`, a field a Session does not have — a Session carries its
|
|
63
|
+
* directory at `session.header.cwd` — so every tool call answered "no project"
|
|
64
|
+
* and wrote to the per-user store while the browser panel wrote to
|
|
65
|
+
* `<project>/storages/data-table`. The Agent and the open Table were editing two
|
|
66
|
+
* different sets of workbooks, and neither could see the other's.
|
|
67
|
+
*
|
|
68
|
+
* Reading the registry also makes the two transports agree by construction: an
|
|
69
|
+
* HTTP request names a Session id, a tool call names the same id through its
|
|
70
|
+
* Agent, and both are answered by the same header. `data-excalidraw` resolves its
|
|
71
|
+
* scene directory the same way, for the same reason.
|
|
72
|
+
*
|
|
73
|
+
* The Host's sandbox policy is deliberately *not* consulted: a plugin context
|
|
74
|
+
* cannot read it (see `src/index.ts`), and the one attempt to use it as a
|
|
75
|
+
* fallback turned every workbook list into a bodyless 400.
|
|
76
|
+
*
|
|
77
|
+
* `undefined` is a real answer and not a failure: it means the caller named no
|
|
78
|
+
* Session, so nobody has said which project this is. The two ways to get it
|
|
79
|
+
* wrong are both worse than a missing project — `process.cwd()` would silently
|
|
80
|
+
* start a *new* per-project store for a caller that was reading the old per-user
|
|
81
|
+
* one (a test, a script, an older client), and a shared per-process default
|
|
82
|
+
* would mix two Sessions' writes. Callers therefore leave the scope unset, and
|
|
83
|
+
* `workbookRoot()` answers with the pre-project location it always used.
|
|
84
|
+
*
|
|
85
|
+
* @param sessions - DSH's Session registry, from the injected `sessions` service.
|
|
86
|
+
* @param sessionId - The Session naming the project; `undefined` or empty for a
|
|
87
|
+
* caller that named none.
|
|
88
|
+
* @returns an absolute project directory, or `undefined` when nothing named one.
|
|
89
|
+
*/
|
|
90
|
+
export function projectScopeOf(sessions, sessionId) {
|
|
91
|
+
if (sessionId === undefined || sessionId.length === 0)
|
|
92
|
+
return undefined;
|
|
93
|
+
const cwd = sessions?.get?.(sessionId)?.header?.cwd;
|
|
94
|
+
// A filesystem root counts as "not named": treating `/` as a project root
|
|
95
|
+
// meant creating `/storages/data-table`.
|
|
96
|
+
return isProjectDirectory(cwd) ? cwd : undefined;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* This plugin's storage inside one project.
|
|
100
|
+
*
|
|
101
|
+
* @param root - Project directory.
|
|
102
|
+
* @returns `<root>/storages/data-table`, which may not exist yet.
|
|
103
|
+
*/
|
|
104
|
+
export function projectStoragesRoot(root) {
|
|
105
|
+
return join(root, STORAGES_DIRECTORY, PLUGIN_DIRECTORY);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The project's storage container, shared by every plugin.
|
|
109
|
+
*
|
|
110
|
+
* The file chooser lists this directory rather than the project root: it is
|
|
111
|
+
* where a project's data lives, and it is what a copy of the project directory
|
|
112
|
+
* carries. A user who wants a file from elsewhere uses the "从项目外导入" path,
|
|
113
|
+
* which is deliberately unrestricted.
|
|
114
|
+
*
|
|
115
|
+
* @param root - Project directory.
|
|
116
|
+
* @returns `<root>/storages`, which may not exist yet.
|
|
117
|
+
*/
|
|
118
|
+
export function projectStoragesDirectory(root) {
|
|
119
|
+
return join(root, STORAGES_DIRECTORY);
|
|
120
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { type SheetSchema, type WorkbookChange, type WorkbookMeta, type WorkbookOperation, type WorkbookOperationType, type WorkbookProposal, type WorkbookSummary, type WorkbookWorktree, type WorkbookWorktreeStatus, type WorkbookWorktreeSummary } from '../types.ts';
|
|
2
|
+
/** Whether a workbook file exists. */
|
|
3
|
+
export declare function workbookExists(id: string): Promise<boolean>;
|
|
4
|
+
/** Read only the sidecar index; a missing/invalid sidecar falls back safely. */
|
|
5
|
+
export declare function readWorkbookIndex(id: string): Promise<WorkbookIndex | null>;
|
|
6
|
+
/** Read one workbook meta. */
|
|
7
|
+
export declare function readWorkbook(id: string, options?: {
|
|
8
|
+
includeCheckpoints?: boolean;
|
|
9
|
+
}): Promise<WorkbookMeta | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Reconstruct a historical workbook revision from the nearest checkpoint and
|
|
12
|
+
* the retained operation tail. A null result means the requested revision has
|
|
13
|
+
* been compacted or contains an opaque snapshot replacement.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readWorkbookAtRevision(workbookId: string, targetRevision: number): Promise<{
|
|
16
|
+
snapshot: unknown;
|
|
17
|
+
revision: number;
|
|
18
|
+
} | null>;
|
|
19
|
+
/** List workbook ids (files in the repo root, sorted by name). */
|
|
20
|
+
export declare function listWorkbooks(): Promise<string[]>;
|
|
21
|
+
/** List workbook summaries for the browser picker and other read-only clients. */
|
|
22
|
+
export declare function listWorkbookSummaries(): Promise<WorkbookSummary[]>;
|
|
23
|
+
/** List worktrees belonging to a workbook, optionally scoped to one session. */
|
|
24
|
+
export declare function listWorkbookWorktrees(workbookId: string, ownerSessionId?: string): Promise<WorkbookWorktreeSummary[]>;
|
|
25
|
+
/** Read one isolated worktree, including its snapshot and operation tail. */
|
|
26
|
+
export declare function readWorkbookWorktree(workbookId: string, worktreeId: string): Promise<WorkbookWorktree | null>;
|
|
27
|
+
export declare function readJsonFile(path: string): Promise<unknown | null>;
|
|
28
|
+
export declare function fileExists(path: string): Promise<boolean>;
|
|
29
|
+
/** Derive a lightweight summary from a stored workbook. */
|
|
30
|
+
export declare function summarize(meta: WorkbookMeta): WorkbookSummary;
|
|
31
|
+
export declare function withWorkbookTransactionQueue<T>(id: string, task: () => Promise<T>): Promise<T>;
|
|
32
|
+
/** Finish a prepared multi-file commit after a process interruption. */
|
|
33
|
+
export declare function recoverWorkbookTransactionNow(id: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Mirrored by the browser projection's `applyAppendRows`, which runs the same
|
|
36
|
+
* check against live Univer state. The hint names `headerRowIndex` because that
|
|
37
|
+
* is what unblocks a report sheet whose real header is not row 0.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isStoredOperation(value: unknown): value is WorkbookOperation;
|
|
40
|
+
export declare function isStoredIndexOperation(value: unknown): value is WorkbookIndexOperation;
|
|
41
|
+
export declare function isKnownOperationType(value: unknown): value is WorkbookOperationType;
|
|
42
|
+
export declare function isStoredSheetSummary(value: unknown): value is {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
schema?: SheetSchema;
|
|
46
|
+
};
|
|
47
|
+
export declare function isStoredIndexProposal(value: unknown): value is WorkbookIndexProposal;
|
|
48
|
+
export declare function isStoredProposal(value: unknown): value is WorkbookProposal;
|
|
49
|
+
export declare function isStoredCheckpoint(value: unknown): value is {
|
|
50
|
+
revision: number;
|
|
51
|
+
actor: 'agent' | 'user';
|
|
52
|
+
timestamp: string;
|
|
53
|
+
snapshot: unknown;
|
|
54
|
+
};
|
|
55
|
+
export interface WorkbookIndex {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
sheetOrder: string[];
|
|
59
|
+
sheets: Array<{
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
schema?: SheetSchema;
|
|
63
|
+
}>;
|
|
64
|
+
updatedAt: string;
|
|
65
|
+
revision: number;
|
|
66
|
+
operations: WorkbookIndexOperation[];
|
|
67
|
+
proposals: WorkbookIndexProposal[];
|
|
68
|
+
}
|
|
69
|
+
/** Payload-free operation summary used by catalog/history bootstrap. */
|
|
70
|
+
export interface WorkbookIndexOperation {
|
|
71
|
+
opId: string;
|
|
72
|
+
workbookId: string;
|
|
73
|
+
revision: number;
|
|
74
|
+
actor: 'agent' | 'user';
|
|
75
|
+
type: WorkbookOperationType;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
summary: string;
|
|
78
|
+
}
|
|
79
|
+
export interface WorkbookIndexProposal {
|
|
80
|
+
id: string;
|
|
81
|
+
workbookId: string;
|
|
82
|
+
actor: 'agent' | 'user';
|
|
83
|
+
baseRevision: number;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
updatedAt: string;
|
|
86
|
+
status: 'pending' | 'committed' | 'rejected' | 'expired';
|
|
87
|
+
summary: string;
|
|
88
|
+
changes: WorkbookChange[];
|
|
89
|
+
committedRevision?: number;
|
|
90
|
+
}
|
|
91
|
+
export interface WorkbookTransactionFile {
|
|
92
|
+
target: string;
|
|
93
|
+
value: unknown;
|
|
94
|
+
}
|
|
95
|
+
export interface StoredWorkbookTransaction {
|
|
96
|
+
revision: number;
|
|
97
|
+
files: Array<{
|
|
98
|
+
target: string;
|
|
99
|
+
temp: string;
|
|
100
|
+
}>;
|
|
101
|
+
}
|
|
102
|
+
/** In-process cache: the DSH host is the single mutation authority. */
|
|
103
|
+
export declare const workbookCache: Map<string, {
|
|
104
|
+
meta: WorkbookMeta;
|
|
105
|
+
checkpointsLoaded: boolean;
|
|
106
|
+
}>;
|
|
107
|
+
/** Serialize transaction recovery and commits independently from mutations. */
|
|
108
|
+
export declare const workbookTransactionQueues: Map<string, Promise<unknown>>;
|
|
109
|
+
export declare function isWorktreeStatus(value: unknown): value is WorkbookWorktreeStatus;
|
|
110
|
+
export declare function toWorktreeSummary(worktree: WorkbookWorktree): WorkbookWorktreeSummary;
|