@components-kit/open-workbook 0.1.2 → 0.1.3

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.
@@ -1,13 +1,13 @@
1
1
  import { mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
2
2
  import { createHash } from "node:crypto";
3
3
  import path from "node:path";
4
- import { BackupManager, BatchCompiler, DefaultPermissionPolicy, buildFormulaDependencyGraph, attachConflictGuidance, extractFormulaReferences, hashStable, LockManager, parseA1Address, PlanManager, SnapshotManager, TaskRegistry, TemplateRegistry, TransactionManager, traceDependents, tracePrecedents } from "@components-kit/open-workbook-excel-core";
4
+ import { BackupManager, BatchCompiler, DefaultPermissionPolicy, buildFormulaDependencyGraph, attachConflictGuidance, extractFormulaReferences, hashStable, LockManager, formatA1Address, parseA1Address, PlanManager, SnapshotManager, TaskRegistry, TemplateRegistry, TransactionManager, traceDependents, tracePrecedents } from "@components-kit/open-workbook-excel-core";
5
5
  import { makeRollbackConflict } from "@components-kit/open-workbook-excel-core";
6
6
  import { getToolCatalogSummary, PromptCatalog, ResourceCatalog, makeId, runtimeError } from "@components-kit/open-workbook-protocol";
7
7
  import { SessionRegistry } from "./session-registry.js";
8
8
  import { NativeFileBridge } from "./native-file-bridge.js";
9
9
  import { RuntimeStateStore } from "./state-store.js";
10
- const runtimeVersion = process.env.OPEN_WORKBOOK_VERSION ?? "0.1.2";
10
+ const runtimeVersion = process.env.OPEN_WORKBOOK_VERSION ?? "0.1.3";
11
11
  export class RuntimeService {
12
12
  sessions = new SessionRegistry();
13
13
  backups = new BackupManager();
@@ -2710,11 +2710,9 @@ export class RuntimeService {
2710
2710
  return cleaningError(input.workbookId, "normalize_headers", target, read.error);
2711
2711
  }
2712
2712
  const headerRowIndex = input.headerRowIndex ?? detectHeaderCandidates(read.values, 10)[0]?.rowIndex ?? 0;
2713
- const values = cloneMatrix(read.values);
2714
- const before = values[headerRowIndex] ?? [];
2713
+ const before = read.values[headerRowIndex] ?? [];
2715
2714
  const normalized = dedupeHeaders(before.map((value) => normalizeHeader(String(value ?? ""))));
2716
- values[headerRowIndex] = normalized;
2717
- const result = await this.writeCleanValues(target, values, "Normalize headers");
2715
+ const result = await this.writeCleanValues(headerRowTarget(target, headerRowIndex), [normalized], "Normalize headers");
2718
2716
  return cleaningReport(input.workbookId, "normalize_headers", target, changedCellCount([before], [normalized]), { headerRowIndex, headers: normalized }, result);
2719
2717
  }
2720
2718
  async cleanTrimWhitespace(input) {
@@ -3139,6 +3137,22 @@ export class RuntimeService {
3139
3137
  async resizeTable(request) {
3140
3138
  return this.mutateTable("table.resize", request, `Before resizing table ${request.tableName}`, await this.getTableBackupRanges(request));
3141
3139
  }
3140
+ async reorderTableColumns(request) {
3141
+ const infoResult = await this.getTableInfo(request);
3142
+ const info = infoResult.info;
3143
+ if (!info) {
3144
+ return {
3145
+ ok: false,
3146
+ error: runtimeError("NOT_FOUND", `Table ${request.tableName} could not be read before column reorder.`, { retryable: false }),
3147
+ table: infoResult
3148
+ };
3149
+ }
3150
+ const validation = validateTableColumnOrder(info, request.columnOrder);
3151
+ if (!validation.ok) {
3152
+ return validation;
3153
+ }
3154
+ return this.mutateTable("table.reorder_columns", request, `Before reordering columns in table ${request.tableName}`, await this.getTableBackupRanges(request));
3155
+ }
3142
3156
  async appendTableRows(request) {
3143
3157
  return this.mutateTable("table.append_rows", request, `Before appending rows to table ${request.tableName}`, await this.getTableBackupRanges(request));
3144
3158
  }
@@ -3268,15 +3282,18 @@ export class RuntimeService {
3268
3282
  workbookId: request.workbookId,
3269
3283
  goal: reason,
3270
3284
  scopes: tableMutationScopes(request, ranges),
3271
- destructiveLevel: method.includes("resize") || method.includes("copy_structure") ? "structure" : "values"
3285
+ destructiveLevel: method.includes("resize") || method.includes("copy_structure") || method.includes("reorder_columns") ? "structure" : "values"
3272
3286
  }, async () => {
3273
3287
  const backup = await this.createWorkbookBackup({
3274
3288
  workbookId: request.workbookId,
3275
3289
  reason,
3276
3290
  ranges
3277
3291
  });
3292
+ if (!("backup" in backup)) {
3293
+ return backup;
3294
+ }
3278
3295
  const result = await client.request(method, request);
3279
- return { ok: true, backup, result };
3296
+ return { ok: true, backup: backup.backup, result };
3280
3297
  });
3281
3298
  }
3282
3299
  async mutateFormulas(method, request, reason, validate) {
@@ -5338,6 +5355,58 @@ function pivotTemplateRequiredSourceFields(info) {
5338
5355
  function uniqueDefined(values) {
5339
5356
  return [...new Set(values.filter((value) => typeof value === "string" && value.length > 0))];
5340
5357
  }
5358
+ function validateTableColumnOrder(info, columnOrder) {
5359
+ const issues = [];
5360
+ if (columnOrder.length !== info.columns.length) {
5361
+ issues.push({
5362
+ code: "TABLE_COLUMN_ORDER_LENGTH_MISMATCH",
5363
+ message: `Column order must include exactly ${info.columns.length} column(s).`,
5364
+ details: { expectedCount: info.columns.length, actualCount: columnOrder.length }
5365
+ });
5366
+ }
5367
+ const seen = new Set();
5368
+ for (const requested of columnOrder) {
5369
+ const column = typeof requested === "number"
5370
+ ? info.columns.find((candidate) => candidate.index === requested)
5371
+ : info.columns.find((candidate) => candidate.name === requested);
5372
+ if (!column) {
5373
+ issues.push({
5374
+ code: "TABLE_COLUMN_NOT_FOUND",
5375
+ message: `Column ${String(requested)} is not present in table ${info.tableName}.`,
5376
+ details: { requested, availableColumns: info.columns.map((candidate) => candidate.name) }
5377
+ });
5378
+ continue;
5379
+ }
5380
+ if (seen.has(column.index)) {
5381
+ issues.push({
5382
+ code: "TABLE_COLUMN_ORDER_DUPLICATE",
5383
+ message: `Column ${column.name} appears more than once in the requested order.`,
5384
+ details: { requested, column }
5385
+ });
5386
+ }
5387
+ seen.add(column.index);
5388
+ }
5389
+ for (const column of info.columns) {
5390
+ if (!seen.has(column.index)) {
5391
+ issues.push({
5392
+ code: "TABLE_COLUMN_ORDER_MISSING_COLUMN",
5393
+ message: `Column ${column.name} is missing from the requested order.`,
5394
+ details: { column }
5395
+ });
5396
+ }
5397
+ }
5398
+ if (issues.length > 0) {
5399
+ return {
5400
+ ok: false,
5401
+ warnings: issues,
5402
+ error: runtimeError("INVALID_ARGUMENT", "Table column reorder requires a complete, unique column order.", {
5403
+ retryable: false,
5404
+ details: { issues }
5405
+ })
5406
+ };
5407
+ }
5408
+ return { ok: true };
5409
+ }
5341
5410
  function addExpectedPivotAxisIssues(issues, axis, expectedFields, actualFields) {
5342
5411
  if (!expectedFields?.length) {
5343
5412
  return;
@@ -5549,6 +5618,14 @@ function targetFromCleanInput(input) {
5549
5618
  address: input.address
5550
5619
  };
5551
5620
  }
5621
+ function headerRowTarget(target, headerRowIndex) {
5622
+ const parsed = parseA1Address(stripSheetName(target.address));
5623
+ const row = parsed.startRow + headerRowIndex;
5624
+ return {
5625
+ ...target,
5626
+ address: formatA1Address({ startColumn: parsed.startColumn, endColumn: parsed.endColumn, startRow: row, endRow: row })
5627
+ };
5628
+ }
5552
5629
  function cleaningReport(workbookId, action, target, changedCells, data, result) {
5553
5630
  const report = {
5554
5631
  ok: result ? result.ok : true,