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