@bilig/headless 0.1.28 → 0.1.30
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/README.md +1 -2
- package/dist/change-order.d.ts +7 -0
- package/dist/change-order.js +64 -0
- package/dist/change-order.js.map +1 -0
- package/dist/deferred-literal-history.d.ts +13 -0
- package/dist/deferred-literal-history.js +67 -0
- package/dist/deferred-literal-history.js.map +1 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/initial-sheet-load.d.ts +3 -0
- package/dist/initial-sheet-load.js +12 -0
- package/dist/initial-sheet-load.js.map +1 -0
- package/dist/matrix-mutation-plan.d.ts +11 -22
- package/dist/matrix-mutation-plan.js +59 -14
- package/dist/matrix-mutation-plan.js.map +1 -1
- package/dist/tracked-engine-event-refs.d.ts +11 -0
- package/dist/tracked-engine-event-refs.js +18 -0
- package/dist/tracked-engine-event-refs.js.map +1 -0
- package/dist/work-paper-errors.d.ts +106 -0
- package/dist/work-paper-errors.js +194 -0
- package/dist/work-paper-errors.js.map +1 -0
- package/dist/work-paper-runtime.d.ts +253 -0
- package/dist/{headless-workbook.js → work-paper-runtime.js} +581 -273
- package/dist/work-paper-runtime.js.map +1 -0
- package/dist/work-paper-scratch-evaluator.d.ts +29 -0
- package/dist/work-paper-scratch-evaluator.js +30 -0
- package/dist/work-paper-scratch-evaluator.js.map +1 -0
- package/dist/work-paper-sheet-replacement.d.ts +27 -0
- package/dist/work-paper-sheet-replacement.js +38 -0
- package/dist/work-paper-sheet-replacement.js.map +1 -0
- package/dist/work-paper-types.d.ts +267 -0
- package/dist/work-paper-types.js +2 -0
- package/dist/work-paper-types.js.map +1 -0
- package/dist/work-paper.d.ts +3 -48
- package/dist/work-paper.js +3 -2
- package/dist/work-paper.js.map +1 -1
- package/package.json +6 -6
- package/dist/errors.d.ts +0 -112
- package/dist/errors.js +0 -206
- package/dist/errors.js.map +0 -1
- package/dist/headless-workbook.d.ts +0 -239
- package/dist/headless-workbook.js.map +0 -1
- package/dist/types.d.ts +0 -252
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { SpreadsheetEngine } from "@bilig/core";
|
|
1
|
+
import { SpreadsheetEngine, makeCellKey } from "@bilig/core";
|
|
2
2
|
import { ErrorCode, MAX_COLS, MAX_ROWS, ValueTag, } from "@bilig/protocol";
|
|
3
3
|
import { excelSerialToDateParts, formatAddress, formatRangeAddress, installExternalFunctionAdapter, isArrayValue, isCellReferenceText, parseCellAddress, parseFormula, parseRangeAddress, serializeFormula, translateFormulaReferences, } from "@bilig/formula";
|
|
4
|
-
import {
|
|
4
|
+
import { tryLoadInitialLiteralSheet } from "./initial-sheet-load.js";
|
|
5
|
+
import { orderWorkPaperCellChanges } from "./change-order.js";
|
|
6
|
+
import { WorkPaperConfigValueTooBigError, WorkPaperConfigValueTooSmallError, WorkPaperEvaluationSuspendedError, WorkPaperExpectedValueOfTypeError, WorkPaperOperationError, WorkPaperParseError, WorkPaperSheetError, WorkPaperExpectedOneOfValuesError, WorkPaperFunctionPluginValidationError, WorkPaperInvalidArgumentsError, WorkPaperLanguageAlreadyRegisteredError, WorkPaperLanguageNotRegisteredError, WorkPaperNamedExpressionDoesNotExistError, WorkPaperNamedExpressionNameIsAlreadyTakenError, WorkPaperNamedExpressionNameIsInvalidError, WorkPaperNoOperationToRedoError, WorkPaperNoOperationToUndoError, WorkPaperNoRelativeAddressesAllowedError, WorkPaperNoSheetWithIdError, WorkPaperNoSheetWithNameError, WorkPaperNotAFormulaError, WorkPaperNothingToPasteError, WorkPaperSheetNameAlreadyTakenError, WorkPaperSheetSizeLimitExceededError, WorkPaperUnableToParseError, } from "./work-paper-errors.js";
|
|
5
7
|
import { buildMatrixMutationPlan } from "./matrix-mutation-plan.js";
|
|
8
|
+
import { captureTrackedEngineEvent } from "./tracked-engine-event-refs.js";
|
|
9
|
+
import { calculateWorkPaperFormulaInScratchWorkbook } from "./work-paper-scratch-evaluator.js";
|
|
10
|
+
import { replaceWorkPaperSheetContent } from "./work-paper-sheet-replacement.js";
|
|
11
|
+
const EMPTY_NAMED_EXPRESSION_VALUES = new Map();
|
|
12
|
+
const VISIBILITY_SHEET_STRIDE = MAX_ROWS * MAX_COLS;
|
|
6
13
|
const DEFAULT_CONFIG = Object.freeze({
|
|
7
14
|
accentSensitive: false,
|
|
8
15
|
caseSensitive: false,
|
|
@@ -43,7 +50,7 @@ const DEFAULT_CONFIG = Object.freeze({
|
|
|
43
50
|
useRegularExpressions: true,
|
|
44
51
|
useWildcards: true,
|
|
45
52
|
});
|
|
46
|
-
const
|
|
53
|
+
const WORKPAPER_CONFIG_KEYS = [
|
|
47
54
|
"accentSensitive",
|
|
48
55
|
"caseSensitive",
|
|
49
56
|
"caseFirst",
|
|
@@ -83,46 +90,44 @@ const HEADLESS_CONFIG_KEYS = [
|
|
|
83
90
|
"useRegularExpressions",
|
|
84
91
|
"useWildcards",
|
|
85
92
|
];
|
|
86
|
-
const
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"HeadlessParseError",
|
|
121
|
-
"HeadlessOperationError",
|
|
93
|
+
const WORKPAPER_PUBLIC_ERROR_NAMES = new Set([
|
|
94
|
+
"WorkPaperConfigValueTooBigError",
|
|
95
|
+
"WorkPaperConfigValueTooSmallError",
|
|
96
|
+
"WorkPaperEvaluationSuspendedError",
|
|
97
|
+
"WorkPaperExpectedOneOfValuesError",
|
|
98
|
+
"WorkPaperExpectedValueOfTypeError",
|
|
99
|
+
"WorkPaperFunctionPluginValidationError",
|
|
100
|
+
"WorkPaperInvalidAddressError",
|
|
101
|
+
"WorkPaperInvalidArgumentsError",
|
|
102
|
+
"WorkPaperLanguageAlreadyRegisteredError",
|
|
103
|
+
"WorkPaperLanguageNotRegisteredError",
|
|
104
|
+
"WorkPaperMissingTranslationError",
|
|
105
|
+
"WorkPaperNamedExpressionDoesNotExistError",
|
|
106
|
+
"WorkPaperNamedExpressionNameIsAlreadyTakenError",
|
|
107
|
+
"WorkPaperNamedExpressionNameIsInvalidError",
|
|
108
|
+
"WorkPaperNoOperationToRedoError",
|
|
109
|
+
"WorkPaperNoOperationToUndoError",
|
|
110
|
+
"WorkPaperNoRelativeAddressesAllowedError",
|
|
111
|
+
"WorkPaperNoSheetWithIdError",
|
|
112
|
+
"WorkPaperNoSheetWithNameError",
|
|
113
|
+
"WorkPaperNotAFormulaError",
|
|
114
|
+
"WorkPaperNothingToPasteError",
|
|
115
|
+
"WorkPaperProtectedFunctionTranslationError",
|
|
116
|
+
"WorkPaperSheetNameAlreadyTakenError",
|
|
117
|
+
"WorkPaperSheetSizeLimitExceededError",
|
|
118
|
+
"WorkPaperSourceLocationHasArrayError",
|
|
119
|
+
"WorkPaperTargetLocationHasArrayError",
|
|
120
|
+
"WorkPaperUnableToParseError",
|
|
121
|
+
"WorkPaperConfigError",
|
|
122
|
+
"WorkPaperSheetError",
|
|
123
|
+
"WorkPaperNamedExpressionError",
|
|
124
|
+
"WorkPaperClipboardError",
|
|
125
|
+
"WorkPaperParseError",
|
|
126
|
+
"WorkPaperOperationError",
|
|
122
127
|
]);
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
const
|
|
128
|
+
const WORKPAPER_VERSION = "0.1.2";
|
|
129
|
+
const WORKPAPER_BUILD_DATE = "2026-04-10";
|
|
130
|
+
const WORKPAPER_RELEASE_DATE = "2026-04-10";
|
|
126
131
|
const globalCustomFunctions = new Map();
|
|
127
132
|
let customAdapterInstalled = false;
|
|
128
133
|
let nextWorkbookId = 1;
|
|
@@ -165,6 +170,10 @@ function clonePluginDefinition(plugin) {
|
|
|
165
170
|
function cloneConfig(config) {
|
|
166
171
|
return {
|
|
167
172
|
...config,
|
|
173
|
+
chooseAddressMappingPolicy: config.chooseAddressMappingPolicy
|
|
174
|
+
? { ...config.chooseAddressMappingPolicy }
|
|
175
|
+
: undefined,
|
|
176
|
+
context: config.context !== undefined ? structuredClone(config.context) : undefined,
|
|
168
177
|
currencySymbol: config.currencySymbol ? [...config.currencySymbol] : undefined,
|
|
169
178
|
dateFormats: config.dateFormats ? [...config.dateFormats] : undefined,
|
|
170
179
|
functionPlugins: config.functionPlugins
|
|
@@ -259,7 +268,7 @@ function makeNamedExpressionKey(name, scope) {
|
|
|
259
268
|
return `${scope ?? "workbook"}:${normalizeName(name)}`;
|
|
260
269
|
}
|
|
261
270
|
function makeInternalScopedName(scope, name) {
|
|
262
|
-
return `
|
|
271
|
+
return `__BILIG_WORKPAPER_SCOPE_${scope}_${normalizeName(name)}`;
|
|
263
272
|
}
|
|
264
273
|
function isFormulaContent(content) {
|
|
265
274
|
return typeof content === "string" && content.trim().startsWith("=");
|
|
@@ -267,7 +276,7 @@ function isFormulaContent(content) {
|
|
|
267
276
|
function isCellValueMatrix(value) {
|
|
268
277
|
return Array.isArray(value);
|
|
269
278
|
}
|
|
270
|
-
function
|
|
279
|
+
function isWorkPaperSheetMatrix(value) {
|
|
271
280
|
return Array.isArray(value);
|
|
272
281
|
}
|
|
273
282
|
function matrixContainsFormulaContent(content) {
|
|
@@ -284,7 +293,7 @@ function stripLeadingEquals(formula) {
|
|
|
284
293
|
}
|
|
285
294
|
function assertRowAndColumn(value, label) {
|
|
286
295
|
if (!Number.isInteger(value) || value < 0) {
|
|
287
|
-
throw new
|
|
296
|
+
throw new WorkPaperInvalidArgumentsError(`${label} to be a non-negative integer`);
|
|
288
297
|
}
|
|
289
298
|
}
|
|
290
299
|
function assertRange(range) {
|
|
@@ -295,7 +304,7 @@ function assertRange(range) {
|
|
|
295
304
|
assertRowAndColumn(range.end.row, "end.row");
|
|
296
305
|
assertRowAndColumn(range.end.col, "end.col");
|
|
297
306
|
if (range.start.sheet !== range.end.sheet) {
|
|
298
|
-
throw new
|
|
307
|
+
throw new WorkPaperInvalidArgumentsError("Ranges must stay on a single sheet");
|
|
299
308
|
}
|
|
300
309
|
}
|
|
301
310
|
function isCellRange(value) {
|
|
@@ -443,7 +452,7 @@ function formulaHasRelativeReferences(node) {
|
|
|
443
452
|
function compareSheetNames(left, right) {
|
|
444
453
|
return left.localeCompare(right);
|
|
445
454
|
}
|
|
446
|
-
function
|
|
455
|
+
function checkWorkPaperLicenseKeyValidity(licenseKey) {
|
|
447
456
|
if (!licenseKey || licenseKey.trim().length === 0) {
|
|
448
457
|
return "missing";
|
|
449
458
|
}
|
|
@@ -454,56 +463,80 @@ function checkHeadlessLicenseKeyValidity(licenseKey) {
|
|
|
454
463
|
}
|
|
455
464
|
return "invalid";
|
|
456
465
|
}
|
|
457
|
-
function
|
|
466
|
+
function validateWorkPaperConfig(config) {
|
|
458
467
|
if (config.maxRows !== undefined && (!Number.isInteger(config.maxRows) || config.maxRows < 1)) {
|
|
459
|
-
throw new
|
|
468
|
+
throw new WorkPaperConfigValueTooSmallError("maxRows", 1);
|
|
460
469
|
}
|
|
461
470
|
if (config.maxColumns !== undefined &&
|
|
462
471
|
(!Number.isInteger(config.maxColumns) || config.maxColumns < 1)) {
|
|
463
|
-
throw new
|
|
472
|
+
throw new WorkPaperConfigValueTooSmallError("maxColumns", 1);
|
|
464
473
|
}
|
|
465
474
|
if ((config.maxRows ?? MAX_ROWS) > MAX_ROWS) {
|
|
466
|
-
throw new
|
|
475
|
+
throw new WorkPaperConfigValueTooBigError("maxRows", MAX_ROWS);
|
|
467
476
|
}
|
|
468
477
|
if ((config.maxColumns ?? MAX_COLS) > MAX_COLS) {
|
|
469
|
-
throw new
|
|
478
|
+
throw new WorkPaperConfigValueTooBigError("maxColumns", MAX_COLS);
|
|
470
479
|
}
|
|
471
480
|
if (config.decimalSeparator !== undefined &&
|
|
472
481
|
config.decimalSeparator !== "." &&
|
|
473
482
|
config.decimalSeparator !== ",") {
|
|
474
|
-
throw new
|
|
483
|
+
throw new WorkPaperExpectedOneOfValuesError('".", ","', "decimalSeparator");
|
|
475
484
|
}
|
|
476
485
|
if (config.arrayColumnSeparator !== undefined &&
|
|
477
486
|
config.arrayColumnSeparator !== "," &&
|
|
478
487
|
config.arrayColumnSeparator !== ";") {
|
|
479
|
-
throw new
|
|
488
|
+
throw new WorkPaperExpectedOneOfValuesError('",", ";"', "arrayColumnSeparator");
|
|
480
489
|
}
|
|
481
490
|
if (config.arrayRowSeparator !== undefined &&
|
|
482
491
|
config.arrayRowSeparator !== ";" &&
|
|
483
492
|
config.arrayRowSeparator !== "|") {
|
|
484
|
-
throw new
|
|
493
|
+
throw new WorkPaperExpectedOneOfValuesError('";", "|"', "arrayRowSeparator");
|
|
485
494
|
}
|
|
486
495
|
if (config.ignoreWhiteSpace !== undefined &&
|
|
487
496
|
config.ignoreWhiteSpace !== "standard" &&
|
|
488
497
|
config.ignoreWhiteSpace !== "any") {
|
|
489
|
-
throw new
|
|
498
|
+
throw new WorkPaperExpectedOneOfValuesError('"standard", "any"', "ignoreWhiteSpace");
|
|
490
499
|
}
|
|
491
500
|
if (config.caseFirst !== undefined &&
|
|
492
501
|
config.caseFirst !== "upper" &&
|
|
493
502
|
config.caseFirst !== "lower" &&
|
|
494
503
|
config.caseFirst !== "false") {
|
|
495
|
-
throw new
|
|
504
|
+
throw new WorkPaperExpectedOneOfValuesError('"upper", "lower", "false"', "caseFirst");
|
|
505
|
+
}
|
|
506
|
+
if (config.chooseAddressMappingPolicy !== undefined &&
|
|
507
|
+
(typeof config.chooseAddressMappingPolicy !== "object" ||
|
|
508
|
+
config.chooseAddressMappingPolicy === null ||
|
|
509
|
+
(config.chooseAddressMappingPolicy.mode !== "dense" &&
|
|
510
|
+
config.chooseAddressMappingPolicy.mode !== "sparse"))) {
|
|
511
|
+
throw new WorkPaperExpectedOneOfValuesError('"dense", "sparse"', "chooseAddressMappingPolicy.mode");
|
|
512
|
+
}
|
|
513
|
+
if (config.parseDateTime !== undefined && typeof config.parseDateTime !== "function") {
|
|
514
|
+
throw new WorkPaperExpectedValueOfTypeError("function", "parseDateTime");
|
|
515
|
+
}
|
|
516
|
+
if (config.stringifyDateTime !== undefined && typeof config.stringifyDateTime !== "function") {
|
|
517
|
+
throw new WorkPaperExpectedValueOfTypeError("function", "stringifyDateTime");
|
|
518
|
+
}
|
|
519
|
+
if (config.stringifyDuration !== undefined && typeof config.stringifyDuration !== "function") {
|
|
520
|
+
throw new WorkPaperExpectedValueOfTypeError("function", "stringifyDuration");
|
|
521
|
+
}
|
|
522
|
+
if (config.context !== undefined) {
|
|
523
|
+
try {
|
|
524
|
+
structuredClone(config.context);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
throw new WorkPaperExpectedValueOfTypeError("structured-cloneable value", "context");
|
|
528
|
+
}
|
|
496
529
|
}
|
|
497
530
|
}
|
|
498
531
|
function validateSheetWithinLimits(sheetName, sheet, config) {
|
|
499
532
|
const height = sheet.length;
|
|
500
533
|
const width = Math.max(0, ...sheet.map((row) => row.length));
|
|
501
534
|
if (height > (config.maxRows ?? MAX_ROWS) || width > (config.maxColumns ?? MAX_COLS)) {
|
|
502
|
-
throw new
|
|
535
|
+
throw new WorkPaperSheetSizeLimitExceededError();
|
|
503
536
|
}
|
|
504
537
|
sheet.forEach((row) => {
|
|
505
538
|
if (!Array.isArray(row)) {
|
|
506
|
-
throw new
|
|
539
|
+
throw new WorkPaperUnableToParseError({ sheetName, reason: "Rows must be arrays" });
|
|
507
540
|
}
|
|
508
541
|
});
|
|
509
542
|
}
|
|
@@ -549,7 +582,7 @@ function formatQualifiedCellAddress(sheetName, row, col) {
|
|
|
549
582
|
const base = formatAddress(row, col);
|
|
550
583
|
return sheetName ? `${quoteSheetNameIfNeeded(sheetName)}!${base}` : base;
|
|
551
584
|
}
|
|
552
|
-
class
|
|
585
|
+
class WorkPaperEmitter {
|
|
553
586
|
listeners = {
|
|
554
587
|
sheetAdded: new Set(),
|
|
555
588
|
sheetRemoved: new Set(),
|
|
@@ -671,17 +704,17 @@ class HeadlessEmitter {
|
|
|
671
704
|
Object.values(this.detailedListeners).forEach((listeners) => listeners.clear());
|
|
672
705
|
}
|
|
673
706
|
}
|
|
674
|
-
export class
|
|
675
|
-
static version =
|
|
676
|
-
static buildDate =
|
|
677
|
-
static releaseDate =
|
|
707
|
+
export class WorkPaper {
|
|
708
|
+
static version = WORKPAPER_VERSION;
|
|
709
|
+
static buildDate = WORKPAPER_BUILD_DATE;
|
|
710
|
+
static releaseDate = WORKPAPER_RELEASE_DATE;
|
|
678
711
|
static languages = {};
|
|
679
712
|
static defaultConfig = cloneConfig(DEFAULT_CONFIG);
|
|
680
713
|
static languageRegistry = new Map();
|
|
681
714
|
static functionPluginRegistry = new Map();
|
|
682
715
|
workbookId = nextWorkbookId++;
|
|
683
|
-
engine
|
|
684
|
-
emitter = new
|
|
716
|
+
engine;
|
|
717
|
+
emitter = new WorkPaperEmitter();
|
|
685
718
|
namedExpressions = new Map();
|
|
686
719
|
functionSnapshot = new Map();
|
|
687
720
|
functionAliasLookup = new Map();
|
|
@@ -689,6 +722,8 @@ export class HeadlessWorkbook {
|
|
|
689
722
|
internals;
|
|
690
723
|
config;
|
|
691
724
|
clipboard = null;
|
|
725
|
+
visibilityCache = null;
|
|
726
|
+
namedExpressionValueCache = null;
|
|
692
727
|
batchDepth = 0;
|
|
693
728
|
batchStartVisibility = null;
|
|
694
729
|
batchStartNamedValues = null;
|
|
@@ -699,14 +734,23 @@ export class HeadlessWorkbook {
|
|
|
699
734
|
suspendedVisibility = null;
|
|
700
735
|
suspendedNamedValues = null;
|
|
701
736
|
queuedEvents = [];
|
|
737
|
+
trackedEngineEvents = [];
|
|
738
|
+
engineEventCaptureEnabled = true;
|
|
739
|
+
unsubscribeEngineEvents = null;
|
|
702
740
|
disposed = false;
|
|
703
741
|
constructor(configInput = {}) {
|
|
704
742
|
ensureCustomAdapterInstalled();
|
|
705
|
-
|
|
743
|
+
validateWorkPaperConfig(configInput);
|
|
706
744
|
this.config = {
|
|
707
745
|
...cloneConfig(DEFAULT_CONFIG),
|
|
708
746
|
...cloneConfig(configInput),
|
|
709
747
|
};
|
|
748
|
+
this.engine = new SpreadsheetEngine({
|
|
749
|
+
workbookName: "Workbook",
|
|
750
|
+
useColumnIndex: this.config.useColumnIndex,
|
|
751
|
+
trackReplicaVersions: false,
|
|
752
|
+
});
|
|
753
|
+
this.attachEngineEventTracking();
|
|
710
754
|
this.captureFunctionRegistry();
|
|
711
755
|
this.internals = Object.freeze({
|
|
712
756
|
graph: Object.freeze({
|
|
@@ -765,51 +809,59 @@ export class HeadlessWorkbook {
|
|
|
765
809
|
});
|
|
766
810
|
}
|
|
767
811
|
static buildEmpty(configInput = {}, namedExpressions = []) {
|
|
768
|
-
const workbook = new
|
|
769
|
-
|
|
770
|
-
|
|
812
|
+
const workbook = new WorkPaper(configInput);
|
|
813
|
+
workbook.withEngineEventCaptureDisabled(() => {
|
|
814
|
+
namedExpressions.forEach((expression) => {
|
|
815
|
+
workbook.upsertNamedExpressionInternal(expression, { duringInitialization: true });
|
|
816
|
+
});
|
|
771
817
|
});
|
|
772
818
|
workbook.clearHistoryStacks();
|
|
819
|
+
workbook.primeChangeTrackingCaches();
|
|
773
820
|
return workbook;
|
|
774
821
|
}
|
|
775
822
|
static buildFromArray(sheet, configInput = {}, namedExpressions = []) {
|
|
776
823
|
return this.buildFromSheets({ Sheet1: sheet }, configInput, namedExpressions);
|
|
777
824
|
}
|
|
778
825
|
static buildFromSheets(sheets, configInput = {}, namedExpressions = []) {
|
|
779
|
-
const workbook = new
|
|
826
|
+
const workbook = new WorkPaper(configInput);
|
|
780
827
|
Object.entries(sheets).forEach(([sheetName, sheet]) => {
|
|
781
828
|
validateSheetWithinLimits(sheetName, sheet, workbook.config);
|
|
782
829
|
});
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
830
|
+
workbook.withEngineEventCaptureDisabled(() => {
|
|
831
|
+
Object.keys(sheets).forEach((sheetName) => {
|
|
832
|
+
workbook.engine.createSheet(sheetName);
|
|
833
|
+
});
|
|
834
|
+
namedExpressions.forEach((expression) => {
|
|
835
|
+
workbook.upsertNamedExpressionInternal(expression, { duringInitialization: true });
|
|
836
|
+
});
|
|
837
|
+
Object.entries(sheets).forEach(([sheetName, sheet]) => {
|
|
838
|
+
const sheetId = workbook.requireSheetId(sheetName);
|
|
839
|
+
if (!tryLoadInitialLiteralSheet(workbook.engine, sheetId, sheet)) {
|
|
840
|
+
workbook.replaceSheetContentInternal(sheetId, sheet, { duringInitialization: true });
|
|
841
|
+
}
|
|
842
|
+
});
|
|
792
843
|
});
|
|
793
844
|
workbook.clearHistoryStacks();
|
|
845
|
+
workbook.primeChangeTrackingCaches();
|
|
794
846
|
return workbook;
|
|
795
847
|
}
|
|
796
848
|
static getLanguage(languageCode) {
|
|
797
849
|
const language = this.languageRegistry.get(languageCode);
|
|
798
850
|
if (!language) {
|
|
799
|
-
throw new
|
|
851
|
+
throw new WorkPaperLanguageNotRegisteredError(languageCode);
|
|
800
852
|
}
|
|
801
853
|
return structuredClone(language);
|
|
802
854
|
}
|
|
803
855
|
static registerLanguage(languageCode, languagePackage) {
|
|
804
856
|
if (this.languageRegistry.has(languageCode)) {
|
|
805
|
-
throw new
|
|
857
|
+
throw new WorkPaperLanguageAlreadyRegisteredError(languageCode);
|
|
806
858
|
}
|
|
807
859
|
this.languageRegistry.set(languageCode, structuredClone(languagePackage));
|
|
808
860
|
this.languages[languageCode] = structuredClone(languagePackage);
|
|
809
861
|
}
|
|
810
862
|
static unregisterLanguage(languageCode) {
|
|
811
863
|
if (!this.languageRegistry.delete(languageCode)) {
|
|
812
|
-
throw new
|
|
864
|
+
throw new WorkPaperLanguageNotRegisteredError(languageCode);
|
|
813
865
|
}
|
|
814
866
|
delete this.languages[languageCode];
|
|
815
867
|
}
|
|
@@ -830,7 +882,7 @@ export class HeadlessWorkbook {
|
|
|
830
882
|
const existing = this.functionPluginRegistry.get(plugin.id);
|
|
831
883
|
const nextPlugin = clonePluginDefinition(existing ?? plugin);
|
|
832
884
|
if (!nextPlugin.implementedFunctions[functionId]) {
|
|
833
|
-
throw
|
|
885
|
+
throw WorkPaperFunctionPluginValidationError.functionNotDeclaredInPlugin(functionId, plugin.id);
|
|
834
886
|
}
|
|
835
887
|
this.functionPluginRegistry.set(nextPlugin.id, nextPlugin);
|
|
836
888
|
if (translations) {
|
|
@@ -884,7 +936,7 @@ export class HeadlessWorkbook {
|
|
|
884
936
|
Object.entries(translations).forEach(([languageCode, functionTranslations]) => {
|
|
885
937
|
const existing = this.languageRegistry.get(languageCode);
|
|
886
938
|
if (!existing) {
|
|
887
|
-
throw new
|
|
939
|
+
throw new WorkPaperLanguageNotRegisteredError(languageCode);
|
|
888
940
|
}
|
|
889
941
|
const nextLanguage = {
|
|
890
942
|
...structuredClone(existing),
|
|
@@ -950,7 +1002,7 @@ export class HeadlessWorkbook {
|
|
|
950
1002
|
return this.internals.lazilyTransformingAstService;
|
|
951
1003
|
}
|
|
952
1004
|
get licenseKeyValidityState() {
|
|
953
|
-
return
|
|
1005
|
+
return checkWorkPaperLicenseKeyValidity(this.config.licenseKey);
|
|
954
1006
|
}
|
|
955
1007
|
updateConfig(next) {
|
|
956
1008
|
this.assertNotDisposed();
|
|
@@ -958,7 +1010,7 @@ export class HeadlessWorkbook {
|
|
|
958
1010
|
...this.config,
|
|
959
1011
|
...cloneConfig(next),
|
|
960
1012
|
};
|
|
961
|
-
const hasChanges =
|
|
1013
|
+
const hasChanges = WORKPAPER_CONFIG_KEYS.some((key) => Object.hasOwn(next, key) && this.config[key] !== next[key]);
|
|
962
1014
|
if (!hasChanges) {
|
|
963
1015
|
return;
|
|
964
1016
|
}
|
|
@@ -981,9 +1033,10 @@ export class HeadlessWorkbook {
|
|
|
981
1033
|
this.assertNotDisposed();
|
|
982
1034
|
const isOutermost = this.batchDepth === 0;
|
|
983
1035
|
if (isOutermost) {
|
|
984
|
-
this.batchStartVisibility = this.
|
|
985
|
-
this.batchStartNamedValues = this.
|
|
1036
|
+
this.batchStartVisibility = this.ensureVisibilityCache();
|
|
1037
|
+
this.batchStartNamedValues = this.ensureNamedExpressionValueCache();
|
|
986
1038
|
this.batchUndoStackLength = this.getUndoStack().length;
|
|
1039
|
+
this.drainTrackedEngineEvents();
|
|
987
1040
|
}
|
|
988
1041
|
this.batchDepth += 1;
|
|
989
1042
|
try {
|
|
@@ -999,7 +1052,7 @@ export class HeadlessWorkbook {
|
|
|
999
1052
|
if (!isOutermost) {
|
|
1000
1053
|
return [];
|
|
1001
1054
|
}
|
|
1002
|
-
const changes = this.
|
|
1055
|
+
const changes = this.computeChangesAfterMutation(this.batchStartVisibility ?? new Map(), this.batchStartNamedValues ?? new Map());
|
|
1003
1056
|
this.batchStartVisibility = null;
|
|
1004
1057
|
this.batchStartNamedValues = null;
|
|
1005
1058
|
if (!this.evaluationSuspended) {
|
|
@@ -1016,8 +1069,9 @@ export class HeadlessWorkbook {
|
|
|
1016
1069
|
return;
|
|
1017
1070
|
}
|
|
1018
1071
|
this.evaluationSuspended = true;
|
|
1019
|
-
this.suspendedVisibility = this.
|
|
1020
|
-
this.suspendedNamedValues = this.
|
|
1072
|
+
this.suspendedVisibility = this.ensureVisibilityCache();
|
|
1073
|
+
this.suspendedNamedValues = this.ensureNamedExpressionValueCache();
|
|
1074
|
+
this.drainTrackedEngineEvents();
|
|
1021
1075
|
this.emitter.emitDetailed({ eventName: "evaluationSuspended", payload: {} });
|
|
1022
1076
|
}
|
|
1023
1077
|
resumeEvaluation() {
|
|
@@ -1025,7 +1079,7 @@ export class HeadlessWorkbook {
|
|
|
1025
1079
|
if (!this.evaluationSuspended) {
|
|
1026
1080
|
return [];
|
|
1027
1081
|
}
|
|
1028
|
-
const changes = this.
|
|
1082
|
+
const changes = this.computeChangesAfterMutation(this.suspendedVisibility ?? new Map(), this.suspendedNamedValues ?? new Map());
|
|
1029
1083
|
this.evaluationSuspended = false;
|
|
1030
1084
|
this.suspendedVisibility = null;
|
|
1031
1085
|
this.suspendedNamedValues = null;
|
|
@@ -1043,7 +1097,7 @@ export class HeadlessWorkbook {
|
|
|
1043
1097
|
this.assertNotDisposed();
|
|
1044
1098
|
return this.captureChanges(undefined, () => {
|
|
1045
1099
|
if (!this.engine.undo()) {
|
|
1046
|
-
throw new
|
|
1100
|
+
throw new WorkPaperNoOperationToUndoError();
|
|
1047
1101
|
}
|
|
1048
1102
|
});
|
|
1049
1103
|
}
|
|
@@ -1051,7 +1105,7 @@ export class HeadlessWorkbook {
|
|
|
1051
1105
|
this.assertNotDisposed();
|
|
1052
1106
|
return this.captureChanges(undefined, () => {
|
|
1053
1107
|
if (!this.engine.redo()) {
|
|
1054
|
-
throw new
|
|
1108
|
+
throw new WorkPaperNoOperationToRedoError();
|
|
1055
1109
|
}
|
|
1056
1110
|
});
|
|
1057
1111
|
}
|
|
@@ -1090,7 +1144,7 @@ export class HeadlessWorkbook {
|
|
|
1090
1144
|
paste(targetLeftCorner) {
|
|
1091
1145
|
this.assertNotDisposed();
|
|
1092
1146
|
if (!this.clipboard) {
|
|
1093
|
-
throw new
|
|
1147
|
+
throw new WorkPaperNothingToPasteError();
|
|
1094
1148
|
}
|
|
1095
1149
|
return this.captureChanges(undefined, () => {
|
|
1096
1150
|
this.applySerializedMatrix(targetLeftCorner, this.clipboard.serialized, this.clipboard.sourceAnchor);
|
|
@@ -1441,7 +1495,7 @@ export class HeadlessWorkbook {
|
|
|
1441
1495
|
}
|
|
1442
1496
|
addNamedExpression(expressionName, expression, scope, options) {
|
|
1443
1497
|
if (!this.isItPossibleToAddNamedExpression(expressionName, expression, scope)) {
|
|
1444
|
-
throw new
|
|
1498
|
+
throw new WorkPaperNamedExpressionNameIsAlreadyTakenError(expressionName);
|
|
1445
1499
|
}
|
|
1446
1500
|
return this.captureChanges({
|
|
1447
1501
|
eventName: "namedExpressionAdded",
|
|
@@ -1456,7 +1510,7 @@ export class HeadlessWorkbook {
|
|
|
1456
1510
|
}
|
|
1457
1511
|
changeNamedExpression(expressionName, expression, scope, options) {
|
|
1458
1512
|
if (!this.isItPossibleToChangeNamedExpression(expressionName, expression, scope)) {
|
|
1459
|
-
throw new
|
|
1513
|
+
throw new WorkPaperNamedExpressionDoesNotExistError(expressionName);
|
|
1460
1514
|
}
|
|
1461
1515
|
return this.captureChanges(undefined, () => {
|
|
1462
1516
|
this.upsertNamedExpressionInternal({ name: expressionName, expression, scope, options }, { duringInitialization: false });
|
|
@@ -1464,7 +1518,7 @@ export class HeadlessWorkbook {
|
|
|
1464
1518
|
}
|
|
1465
1519
|
removeNamedExpression(expressionName, scope) {
|
|
1466
1520
|
if (!this.isItPossibleToRemoveNamedExpression(expressionName, scope)) {
|
|
1467
|
-
throw new
|
|
1521
|
+
throw new WorkPaperNamedExpressionDoesNotExistError(expressionName);
|
|
1468
1522
|
}
|
|
1469
1523
|
const existing = this.namedExpressionRecord(expressionName, scope);
|
|
1470
1524
|
return this.captureChanges({
|
|
@@ -1497,44 +1551,57 @@ export class HeadlessWorkbook {
|
|
|
1497
1551
|
}
|
|
1498
1552
|
normalizeFormula(formula) {
|
|
1499
1553
|
if (!formula.trim().startsWith("=")) {
|
|
1500
|
-
throw new
|
|
1554
|
+
throw new WorkPaperNotAFormulaError();
|
|
1501
1555
|
}
|
|
1502
1556
|
try {
|
|
1503
1557
|
return `=${serializeFormula(parseFormula(stripLeadingEquals(formula)))}`;
|
|
1504
1558
|
}
|
|
1505
1559
|
catch (error) {
|
|
1506
|
-
throw new
|
|
1560
|
+
throw new WorkPaperParseError(this.messageOf(error, `Unable to normalize formula`));
|
|
1507
1561
|
}
|
|
1508
1562
|
}
|
|
1509
1563
|
calculateFormula(formula, scope) {
|
|
1510
1564
|
if (!formula.trim().startsWith("=")) {
|
|
1511
|
-
throw new
|
|
1565
|
+
throw new WorkPaperNotAFormulaError();
|
|
1512
1566
|
}
|
|
1513
1567
|
try {
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1568
|
+
return calculateWorkPaperFormulaInScratchWorkbook({
|
|
1569
|
+
createWorkbook: (config) => {
|
|
1570
|
+
const temporaryWorkbook = new WorkPaper(config);
|
|
1571
|
+
return {
|
|
1572
|
+
engine: temporaryWorkbook.engine,
|
|
1573
|
+
registerNamedExpression: (expression) => {
|
|
1574
|
+
temporaryWorkbook.upsertNamedExpressionInternal(expression, {
|
|
1575
|
+
duringInitialization: true,
|
|
1576
|
+
});
|
|
1577
|
+
},
|
|
1578
|
+
requireSheetId: (sheetName) => temporaryWorkbook.requireSheetId(sheetName),
|
|
1579
|
+
replaceSheetContent: (sheetId, sheet) => {
|
|
1580
|
+
temporaryWorkbook.replaceSheetContentInternal(sheetId, sheet, {
|
|
1581
|
+
duringInitialization: true,
|
|
1582
|
+
});
|
|
1583
|
+
},
|
|
1584
|
+
clearHistoryStacks: () => temporaryWorkbook.clearHistoryStacks(),
|
|
1585
|
+
applyRawContent: (address, content) => temporaryWorkbook.applyRawContent(address, content),
|
|
1586
|
+
getRangeValues: (range) => temporaryWorkbook.getRangeValues(range),
|
|
1587
|
+
getCellValue: (address) => temporaryWorkbook.getCellValue(address),
|
|
1588
|
+
dispose: () => temporaryWorkbook.dispose(),
|
|
1589
|
+
};
|
|
1590
|
+
},
|
|
1591
|
+
config: this.getConfig(),
|
|
1592
|
+
serializedSheets: this.getAllSheetsSerialized(),
|
|
1593
|
+
namedExpressions: this.getAllNamedExpressionsSerialized(),
|
|
1594
|
+
formula,
|
|
1595
|
+
scope,
|
|
1596
|
+
});
|
|
1530
1597
|
}
|
|
1531
1598
|
catch (error) {
|
|
1532
|
-
throw new
|
|
1599
|
+
throw new WorkPaperParseError(this.messageOf(error, "Unable to calculate formula"));
|
|
1533
1600
|
}
|
|
1534
1601
|
}
|
|
1535
1602
|
getNamedExpressionsFromFormula(formula) {
|
|
1536
1603
|
if (!formula.trim().startsWith("=")) {
|
|
1537
|
-
throw new
|
|
1604
|
+
throw new WorkPaperNotAFormulaError();
|
|
1538
1605
|
}
|
|
1539
1606
|
try {
|
|
1540
1607
|
const parsed = parseFormula(stripLeadingEquals(formula));
|
|
@@ -1543,7 +1610,7 @@ export class HeadlessWorkbook {
|
|
|
1543
1610
|
return [...names].toSorted(compareSheetNames);
|
|
1544
1611
|
}
|
|
1545
1612
|
catch (error) {
|
|
1546
|
-
throw new
|
|
1613
|
+
throw new WorkPaperParseError(this.messageOf(error, "Unable to inspect formula"));
|
|
1547
1614
|
}
|
|
1548
1615
|
}
|
|
1549
1616
|
validateFormula(formula) {
|
|
@@ -1560,7 +1627,7 @@ export class HeadlessWorkbook {
|
|
|
1560
1627
|
}
|
|
1561
1628
|
getRegisteredFunctionNames(languageCode) {
|
|
1562
1629
|
const code = languageCode ?? this.config.language ?? "enGB";
|
|
1563
|
-
const language =
|
|
1630
|
+
const language = WorkPaper.languageRegistry.get(code);
|
|
1564
1631
|
const functions = [...this.functionSnapshot.values()]
|
|
1565
1632
|
.filter((binding) => binding.publicName === binding.publicName.toUpperCase())
|
|
1566
1633
|
.map((binding) => binding.publicName)
|
|
@@ -1575,13 +1642,13 @@ export class HeadlessWorkbook {
|
|
|
1575
1642
|
if (!binding) {
|
|
1576
1643
|
return undefined;
|
|
1577
1644
|
}
|
|
1578
|
-
const plugin =
|
|
1645
|
+
const plugin = WorkPaper.functionPluginRegistry.get(binding.pluginId);
|
|
1579
1646
|
return plugin ? clonePluginDefinition(plugin) : undefined;
|
|
1580
1647
|
}
|
|
1581
1648
|
getAllFunctionPlugins() {
|
|
1582
1649
|
const pluginIds = new Set([...this.functionSnapshot.values()].map((binding) => binding.pluginId));
|
|
1583
1650
|
return [...pluginIds]
|
|
1584
|
-
.map((pluginId) =>
|
|
1651
|
+
.map((pluginId) => WorkPaper.functionPluginRegistry.get(pluginId))
|
|
1585
1652
|
.filter((plugin) => plugin !== undefined)
|
|
1586
1653
|
.map((plugin) => clonePluginDefinition(plugin));
|
|
1587
1654
|
}
|
|
@@ -1622,28 +1689,28 @@ export class HeadlessWorkbook {
|
|
|
1622
1689
|
return { hours, minutes, seconds };
|
|
1623
1690
|
}
|
|
1624
1691
|
setCellContents(address, content) {
|
|
1692
|
+
this.assertNotDisposed();
|
|
1625
1693
|
if (!this.isItPossibleToSetCellContents(address, content)) {
|
|
1626
|
-
throw new
|
|
1694
|
+
throw new WorkPaperOperationError("Cell contents cannot be set");
|
|
1695
|
+
}
|
|
1696
|
+
if (!isWorkPaperSheetMatrix(content) &&
|
|
1697
|
+
this.enqueueDeferredBatchLiteral(address.sheet, address.row, address.col, content)) {
|
|
1698
|
+
return [];
|
|
1627
1699
|
}
|
|
1628
1700
|
return this.captureChanges(undefined, () => {
|
|
1629
|
-
if (
|
|
1701
|
+
if (isWorkPaperSheetMatrix(content)) {
|
|
1630
1702
|
this.flushPendingBatchOps();
|
|
1631
1703
|
this.applyMatrixContents(address, content);
|
|
1632
1704
|
return;
|
|
1633
1705
|
}
|
|
1634
|
-
const sheetName = this.sheetName(address.sheet);
|
|
1635
|
-
const a1 = this.a1(address);
|
|
1636
|
-
if (this.enqueueDeferredBatchLiteral(sheetName, a1, content)) {
|
|
1637
|
-
return;
|
|
1638
|
-
}
|
|
1639
1706
|
this.flushPendingBatchOps();
|
|
1640
|
-
this.applyRawContent(
|
|
1707
|
+
this.applyRawContent(address, content);
|
|
1641
1708
|
});
|
|
1642
1709
|
}
|
|
1643
1710
|
swapRowIndexes(sheetId, rowAOrMappings, rowB) {
|
|
1644
1711
|
const mappings = this.normalizeAxisSwapMappings("row", rowAOrMappings, rowB);
|
|
1645
1712
|
if (!this.isItPossibleToSwapRowIndexes(sheetId, mappings)) {
|
|
1646
|
-
throw new
|
|
1713
|
+
throw new WorkPaperOperationError("Rows cannot be swapped");
|
|
1647
1714
|
}
|
|
1648
1715
|
return this.batch(() => {
|
|
1649
1716
|
mappings.forEach(([rowA, mappedRowB]) => {
|
|
@@ -1663,7 +1730,7 @@ export class HeadlessWorkbook {
|
|
|
1663
1730
|
}
|
|
1664
1731
|
setRowOrder(sheetId, rowOrder) {
|
|
1665
1732
|
if (!this.isItPossibleToSetRowOrder(sheetId, rowOrder)) {
|
|
1666
|
-
throw new
|
|
1733
|
+
throw new WorkPaperOperationError("Row order is invalid");
|
|
1667
1734
|
}
|
|
1668
1735
|
const current = rowOrder.toSorted((left, right) => left - right);
|
|
1669
1736
|
return this.batch(() => {
|
|
@@ -1681,7 +1748,7 @@ export class HeadlessWorkbook {
|
|
|
1681
1748
|
swapColumnIndexes(sheetId, columnAOrMappings, columnB) {
|
|
1682
1749
|
const mappings = this.normalizeAxisSwapMappings("column", columnAOrMappings, columnB);
|
|
1683
1750
|
if (!this.isItPossibleToSwapColumnIndexes(sheetId, mappings)) {
|
|
1684
|
-
throw new
|
|
1751
|
+
throw new WorkPaperOperationError("Columns cannot be swapped");
|
|
1685
1752
|
}
|
|
1686
1753
|
return this.batch(() => {
|
|
1687
1754
|
mappings.forEach(([columnA, mappedColumnB]) => {
|
|
@@ -1701,7 +1768,7 @@ export class HeadlessWorkbook {
|
|
|
1701
1768
|
}
|
|
1702
1769
|
setColumnOrder(sheetId, columnOrder) {
|
|
1703
1770
|
if (!this.isItPossibleToSetColumnOrder(sheetId, columnOrder)) {
|
|
1704
|
-
throw new
|
|
1771
|
+
throw new WorkPaperOperationError("Column order is invalid");
|
|
1705
1772
|
}
|
|
1706
1773
|
const current = columnOrder.toSorted((left, right) => left - right);
|
|
1707
1774
|
return this.batch(() => {
|
|
@@ -1719,7 +1786,7 @@ export class HeadlessWorkbook {
|
|
|
1719
1786
|
addRows(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1720
1787
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1721
1788
|
if (!this.isItPossibleToAddRows(sheetId, ...indexes)) {
|
|
1722
|
-
throw new
|
|
1789
|
+
throw new WorkPaperOperationError("Rows cannot be added");
|
|
1723
1790
|
}
|
|
1724
1791
|
return this.batch(() => {
|
|
1725
1792
|
indexes.forEach(([start, amount]) => {
|
|
@@ -1730,7 +1797,7 @@ export class HeadlessWorkbook {
|
|
|
1730
1797
|
removeRows(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1731
1798
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1732
1799
|
if (!this.isItPossibleToRemoveRows(sheetId, ...indexes)) {
|
|
1733
|
-
throw new
|
|
1800
|
+
throw new WorkPaperOperationError("Rows cannot be removed");
|
|
1734
1801
|
}
|
|
1735
1802
|
return this.batch(() => {
|
|
1736
1803
|
indexes
|
|
@@ -1743,7 +1810,7 @@ export class HeadlessWorkbook {
|
|
|
1743
1810
|
addColumns(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1744
1811
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1745
1812
|
if (!this.isItPossibleToAddColumns(sheetId, ...indexes)) {
|
|
1746
|
-
throw new
|
|
1813
|
+
throw new WorkPaperOperationError("Columns cannot be added");
|
|
1747
1814
|
}
|
|
1748
1815
|
return this.batch(() => {
|
|
1749
1816
|
indexes.forEach(([start, amount]) => {
|
|
@@ -1754,7 +1821,7 @@ export class HeadlessWorkbook {
|
|
|
1754
1821
|
removeColumns(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1755
1822
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1756
1823
|
if (!this.isItPossibleToRemoveColumns(sheetId, ...indexes)) {
|
|
1757
|
-
throw new
|
|
1824
|
+
throw new WorkPaperOperationError("Columns cannot be removed");
|
|
1758
1825
|
}
|
|
1759
1826
|
return this.batch(() => {
|
|
1760
1827
|
indexes
|
|
@@ -1766,7 +1833,7 @@ export class HeadlessWorkbook {
|
|
|
1766
1833
|
}
|
|
1767
1834
|
moveCells(source, target) {
|
|
1768
1835
|
if (!this.isItPossibleToMoveCells(source, target)) {
|
|
1769
|
-
throw new
|
|
1836
|
+
throw new WorkPaperOperationError("Cells cannot be moved");
|
|
1770
1837
|
}
|
|
1771
1838
|
const sourceHeight = source.end.row - source.start.row;
|
|
1772
1839
|
const sourceWidth = source.end.col - source.start.col;
|
|
@@ -1780,7 +1847,7 @@ export class HeadlessWorkbook {
|
|
|
1780
1847
|
}
|
|
1781
1848
|
moveRows(sheetId, start, count, target) {
|
|
1782
1849
|
if (!this.isItPossibleToMoveRows(sheetId, start, count, target)) {
|
|
1783
|
-
throw new
|
|
1850
|
+
throw new WorkPaperOperationError("Rows cannot be moved");
|
|
1784
1851
|
}
|
|
1785
1852
|
return this.captureChanges(undefined, () => {
|
|
1786
1853
|
this.engine.moveRows(this.sheetName(sheetId), start, count, target);
|
|
@@ -1788,7 +1855,7 @@ export class HeadlessWorkbook {
|
|
|
1788
1855
|
}
|
|
1789
1856
|
moveColumns(sheetId, start, count, target) {
|
|
1790
1857
|
if (!this.isItPossibleToMoveColumns(sheetId, start, count, target)) {
|
|
1791
|
-
throw new
|
|
1858
|
+
throw new WorkPaperOperationError("Columns cannot be moved");
|
|
1792
1859
|
}
|
|
1793
1860
|
return this.captureChanges(undefined, () => {
|
|
1794
1861
|
this.engine.moveColumns(this.sheetName(sheetId), start, count, target);
|
|
@@ -1798,10 +1865,11 @@ export class HeadlessWorkbook {
|
|
|
1798
1865
|
this.assertNotDisposed();
|
|
1799
1866
|
const name = sheetName?.trim() || this.nextSheetName();
|
|
1800
1867
|
if (!this.isItPossibleToAddSheet(name)) {
|
|
1801
|
-
throw new
|
|
1868
|
+
throw new WorkPaperSheetNameAlreadyTakenError(name);
|
|
1802
1869
|
}
|
|
1803
|
-
const beforeVisibility = this.
|
|
1804
|
-
const beforeNames = this.
|
|
1870
|
+
const beforeVisibility = this.ensureVisibilityCache();
|
|
1871
|
+
const beforeNames = this.ensureNamedExpressionValueCache();
|
|
1872
|
+
this.drainTrackedEngineEvents();
|
|
1805
1873
|
this.engine.createSheet(name);
|
|
1806
1874
|
const sheetId = this.requireSheetId(name);
|
|
1807
1875
|
const payload = { sheetId, sheetName: name };
|
|
@@ -1811,7 +1879,7 @@ export class HeadlessWorkbook {
|
|
|
1811
1879
|
else {
|
|
1812
1880
|
this.emitter.emitDetailed({ eventName: "sheetAdded", payload });
|
|
1813
1881
|
}
|
|
1814
|
-
const changes = this.
|
|
1882
|
+
const changes = this.computeChangesAfterMutation(beforeVisibility, beforeNames);
|
|
1815
1883
|
if (!this.shouldSuppressEvents() && changes.length > 0) {
|
|
1816
1884
|
this.emitter.emitDetailed({ eventName: "valuesUpdated", payload: { changes } });
|
|
1817
1885
|
}
|
|
@@ -1819,7 +1887,7 @@ export class HeadlessWorkbook {
|
|
|
1819
1887
|
}
|
|
1820
1888
|
removeSheet(sheetId) {
|
|
1821
1889
|
if (!this.isItPossibleToRemoveSheet(sheetId)) {
|
|
1822
|
-
throw new
|
|
1890
|
+
throw new WorkPaperSheetError(`Sheet '${sheetId}' cannot be removed`);
|
|
1823
1891
|
}
|
|
1824
1892
|
const sheetName = this.sheetName(sheetId);
|
|
1825
1893
|
return this.captureChanges({
|
|
@@ -1835,7 +1903,7 @@ export class HeadlessWorkbook {
|
|
|
1835
1903
|
}
|
|
1836
1904
|
clearSheet(sheetId) {
|
|
1837
1905
|
if (!this.isItPossibleToClearSheet(sheetId)) {
|
|
1838
|
-
throw new
|
|
1906
|
+
throw new WorkPaperSheetError(`Sheet '${sheetId}' cannot be cleared`);
|
|
1839
1907
|
}
|
|
1840
1908
|
return this.captureChanges(undefined, () => {
|
|
1841
1909
|
const dimensions = this.getSheetDimensions(sheetId);
|
|
@@ -1851,7 +1919,7 @@ export class HeadlessWorkbook {
|
|
|
1851
1919
|
}
|
|
1852
1920
|
setSheetContent(sheetId, content) {
|
|
1853
1921
|
if (!this.isItPossibleToReplaceSheetContent(sheetId, content)) {
|
|
1854
|
-
throw new
|
|
1922
|
+
throw new WorkPaperSheetError(`Sheet '${sheetId}' cannot be replaced`);
|
|
1855
1923
|
}
|
|
1856
1924
|
return this.captureChanges(undefined, () => {
|
|
1857
1925
|
this.replaceSheetContentInternal(sheetId, content, { duringInitialization: false });
|
|
@@ -1859,7 +1927,7 @@ export class HeadlessWorkbook {
|
|
|
1859
1927
|
}
|
|
1860
1928
|
renameSheet(sheetId, nextName) {
|
|
1861
1929
|
if (!this.isItPossibleToRenameSheet(sheetId, nextName)) {
|
|
1862
|
-
throw new
|
|
1930
|
+
throw new WorkPaperSheetError(`Sheet '${sheetId}' cannot be renamed to '${nextName}'`);
|
|
1863
1931
|
}
|
|
1864
1932
|
const oldName = this.sheetName(sheetId);
|
|
1865
1933
|
const newName = nextName.trim();
|
|
@@ -1891,7 +1959,7 @@ export class HeadlessWorkbook {
|
|
|
1891
1959
|
}
|
|
1892
1960
|
if (Array.isArray(content)) {
|
|
1893
1961
|
if (!content.every((row) => Array.isArray(row))) {
|
|
1894
|
-
throw new
|
|
1962
|
+
throw new WorkPaperInvalidArgumentsError("Content matrix must be a two-dimensional array");
|
|
1895
1963
|
}
|
|
1896
1964
|
const height = content.length;
|
|
1897
1965
|
const width = Math.max(0, ...content.map((row) => row.length));
|
|
@@ -1991,7 +2059,7 @@ export class HeadlessWorkbook {
|
|
|
1991
2059
|
isItPossibleToAddSheet(sheetName) {
|
|
1992
2060
|
const trimmed = sheetName.trim();
|
|
1993
2061
|
if (trimmed.length === 0) {
|
|
1994
|
-
throw new
|
|
2062
|
+
throw new WorkPaperInvalidArgumentsError("Sheet name must be non-empty");
|
|
1995
2063
|
}
|
|
1996
2064
|
return !this.doesSheetExist(trimmed);
|
|
1997
2065
|
}
|
|
@@ -2004,7 +2072,7 @@ export class HeadlessWorkbook {
|
|
|
2004
2072
|
isItPossibleToReplaceSheetContent(sheetId, content) {
|
|
2005
2073
|
this.sheetRecord(sheetId);
|
|
2006
2074
|
if (!content.every((row) => Array.isArray(row))) {
|
|
2007
|
-
throw new
|
|
2075
|
+
throw new WorkPaperInvalidArgumentsError("Sheet content must be a two-dimensional array");
|
|
2008
2076
|
}
|
|
2009
2077
|
const height = content.length;
|
|
2010
2078
|
const width = Math.max(0, ...content.map((row) => row.length));
|
|
@@ -2014,7 +2082,7 @@ export class HeadlessWorkbook {
|
|
|
2014
2082
|
this.sheetRecord(sheetId);
|
|
2015
2083
|
const trimmed = nextName.trim();
|
|
2016
2084
|
if (trimmed.length === 0) {
|
|
2017
|
-
throw new
|
|
2085
|
+
throw new WorkPaperInvalidArgumentsError("Sheet name must be non-empty");
|
|
2018
2086
|
}
|
|
2019
2087
|
const existing = this.engine.workbook.getSheet(trimmed);
|
|
2020
2088
|
return !existing || existing.id === sheetId;
|
|
@@ -2038,12 +2106,61 @@ export class HeadlessWorkbook {
|
|
|
2038
2106
|
return;
|
|
2039
2107
|
}
|
|
2040
2108
|
this.disposed = true;
|
|
2109
|
+
this.unsubscribeEngineEvents?.();
|
|
2110
|
+
this.unsubscribeEngineEvents = null;
|
|
2041
2111
|
this.emitter.clear();
|
|
2042
2112
|
this.clearFunctionBindings();
|
|
2043
2113
|
this.clipboard = null;
|
|
2114
|
+
this.visibilityCache = null;
|
|
2115
|
+
this.namedExpressionValueCache = null;
|
|
2044
2116
|
this.queuedEvents = [];
|
|
2117
|
+
this.trackedEngineEvents = [];
|
|
2045
2118
|
this.namedExpressions.clear();
|
|
2046
2119
|
}
|
|
2120
|
+
attachEngineEventTracking() {
|
|
2121
|
+
this.unsubscribeEngineEvents?.();
|
|
2122
|
+
this.trackedEngineEvents = [];
|
|
2123
|
+
this.unsubscribeEngineEvents = this.engine.subscribe((event) => {
|
|
2124
|
+
if (!this.engineEventCaptureEnabled) {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
this.trackedEngineEvents.push(captureTrackedEngineEvent(event));
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
withEngineEventCaptureDisabled(callback) {
|
|
2131
|
+
const previous = this.engineEventCaptureEnabled;
|
|
2132
|
+
this.engineEventCaptureEnabled = false;
|
|
2133
|
+
this.trackedEngineEvents = [];
|
|
2134
|
+
try {
|
|
2135
|
+
return callback();
|
|
2136
|
+
}
|
|
2137
|
+
finally {
|
|
2138
|
+
this.engineEventCaptureEnabled = previous;
|
|
2139
|
+
this.trackedEngineEvents = [];
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
drainTrackedEngineEvents() {
|
|
2143
|
+
const events = this.trackedEngineEvents;
|
|
2144
|
+
this.trackedEngineEvents = [];
|
|
2145
|
+
return events;
|
|
2146
|
+
}
|
|
2147
|
+
primeChangeTrackingCaches() {
|
|
2148
|
+
this.visibilityCache = this.captureVisibilitySnapshot();
|
|
2149
|
+
this.namedExpressionValueCache = this.captureNamedExpressionValueSnapshot();
|
|
2150
|
+
this.drainTrackedEngineEvents();
|
|
2151
|
+
}
|
|
2152
|
+
ensureVisibilityCache() {
|
|
2153
|
+
if (!this.visibilityCache) {
|
|
2154
|
+
this.visibilityCache = this.captureVisibilitySnapshot();
|
|
2155
|
+
}
|
|
2156
|
+
return this.visibilityCache;
|
|
2157
|
+
}
|
|
2158
|
+
ensureNamedExpressionValueCache() {
|
|
2159
|
+
if (!this.namedExpressionValueCache) {
|
|
2160
|
+
this.namedExpressionValueCache = this.captureNamedExpressionValueSnapshot();
|
|
2161
|
+
}
|
|
2162
|
+
return this.namedExpressionValueCache;
|
|
2163
|
+
}
|
|
2047
2164
|
flushPendingBatchOps() {
|
|
2048
2165
|
if (this.pendingBatchOps.length === 0) {
|
|
2049
2166
|
return;
|
|
@@ -2052,22 +2169,22 @@ export class HeadlessWorkbook {
|
|
|
2052
2169
|
const potentialNewCells = this.pendingBatchPotentialNewCells;
|
|
2053
2170
|
this.pendingBatchOps = [];
|
|
2054
2171
|
this.pendingBatchPotentialNewCells = 0;
|
|
2055
|
-
this.engine.
|
|
2056
|
-
captureUndo: true,
|
|
2057
|
-
potentialNewCells: potentialNewCells > 0 ? potentialNewCells : undefined,
|
|
2058
|
-
});
|
|
2172
|
+
this.engine.applyCellMutationsAt(ops, potentialNewCells > 0 ? potentialNewCells : undefined);
|
|
2059
2173
|
}
|
|
2060
|
-
enqueueDeferredBatchLiteral(
|
|
2174
|
+
enqueueDeferredBatchLiteral(sheetId, row, col, content) {
|
|
2061
2175
|
if (this.batchDepth === 0 ||
|
|
2062
2176
|
!isDeferredBatchLiteralContent(content) ||
|
|
2063
2177
|
isFormulaContent(content)) {
|
|
2064
2178
|
return false;
|
|
2065
2179
|
}
|
|
2066
2180
|
if (content === null) {
|
|
2067
|
-
this.pendingBatchOps.push({ kind: "clearCell",
|
|
2181
|
+
this.pendingBatchOps.push({ sheetId, mutation: { kind: "clearCell", row, col } });
|
|
2068
2182
|
return true;
|
|
2069
2183
|
}
|
|
2070
|
-
this.pendingBatchOps.push({
|
|
2184
|
+
this.pendingBatchOps.push({
|
|
2185
|
+
sheetId,
|
|
2186
|
+
mutation: { kind: "setCellValue", row, col, value: content },
|
|
2187
|
+
});
|
|
2071
2188
|
this.pendingBatchPotentialNewCells += 1;
|
|
2072
2189
|
return true;
|
|
2073
2190
|
}
|
|
@@ -2077,19 +2194,19 @@ export class HeadlessWorkbook {
|
|
|
2077
2194
|
}
|
|
2078
2195
|
assertNotDisposed() {
|
|
2079
2196
|
if (this.disposed) {
|
|
2080
|
-
throw new
|
|
2197
|
+
throw new WorkPaperOperationError("Workbook has been disposed");
|
|
2081
2198
|
}
|
|
2082
2199
|
}
|
|
2083
2200
|
assertReadable() {
|
|
2084
2201
|
this.prepareReadableState();
|
|
2085
2202
|
if (this.evaluationSuspended) {
|
|
2086
|
-
throw new
|
|
2203
|
+
throw new WorkPaperEvaluationSuspendedError();
|
|
2087
2204
|
}
|
|
2088
2205
|
}
|
|
2089
2206
|
sheetRecord(sheetId) {
|
|
2090
2207
|
const sheet = this.engine.workbook.getSheetById(sheetId);
|
|
2091
2208
|
if (!sheet) {
|
|
2092
|
-
throw new
|
|
2209
|
+
throw new WorkPaperNoSheetWithIdError(sheetId);
|
|
2093
2210
|
}
|
|
2094
2211
|
return sheet;
|
|
2095
2212
|
}
|
|
@@ -2099,7 +2216,7 @@ export class HeadlessWorkbook {
|
|
|
2099
2216
|
requireSheetId(name) {
|
|
2100
2217
|
const sheetId = this.getSheetId(name);
|
|
2101
2218
|
if (sheetId === undefined) {
|
|
2102
|
-
throw new
|
|
2219
|
+
throw new WorkPaperNoSheetWithNameError(name);
|
|
2103
2220
|
}
|
|
2104
2221
|
return sheetId;
|
|
2105
2222
|
}
|
|
@@ -2138,15 +2255,16 @@ export class HeadlessWorkbook {
|
|
|
2138
2255
|
}
|
|
2139
2256
|
captureVisibilitySnapshot() {
|
|
2140
2257
|
const snapshot = new Map();
|
|
2258
|
+
const strings = this.engine.strings;
|
|
2259
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
2141
2260
|
this.listSheetRecords().forEach((sheet) => {
|
|
2142
2261
|
const cells = new Map();
|
|
2143
|
-
sheet.grid.forEachCellEntry((
|
|
2144
|
-
const
|
|
2145
|
-
const value = this.engine.getCellValue(sheet.name, address);
|
|
2262
|
+
sheet.grid.forEachCellEntry((cellIndex, row, col) => {
|
|
2263
|
+
const value = cellStore.getValue(cellIndex, (id) => strings.get(id));
|
|
2146
2264
|
if (value.tag === ValueTag.Empty) {
|
|
2147
2265
|
return;
|
|
2148
2266
|
}
|
|
2149
|
-
cells.set(
|
|
2267
|
+
cells.set(makeCellKey(sheet.id, row, col), value);
|
|
2150
2268
|
});
|
|
2151
2269
|
snapshot.set(sheet.id, {
|
|
2152
2270
|
sheetId: sheet.id,
|
|
@@ -2158,36 +2276,178 @@ export class HeadlessWorkbook {
|
|
|
2158
2276
|
return snapshot;
|
|
2159
2277
|
}
|
|
2160
2278
|
captureNamedExpressionValueSnapshot() {
|
|
2279
|
+
if (this.namedExpressions.size === 0) {
|
|
2280
|
+
return EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2281
|
+
}
|
|
2161
2282
|
const snapshot = new Map();
|
|
2162
2283
|
[...this.namedExpressions.values()].forEach((expression) => {
|
|
2163
2284
|
snapshot.set(makeNamedExpressionKey(expression.publicName, expression.scope), cloneNamedExpressionValue(this.evaluateNamedExpression(expression)));
|
|
2164
2285
|
});
|
|
2165
2286
|
return snapshot;
|
|
2166
2287
|
}
|
|
2167
|
-
|
|
2288
|
+
computeCellChanges(beforeVisibility, afterVisibility) {
|
|
2168
2289
|
const cellChanges = [];
|
|
2169
2290
|
afterVisibility.forEach((afterSheet, sheetId) => {
|
|
2170
2291
|
const beforeSheet = beforeVisibility.get(sheetId);
|
|
2171
|
-
const
|
|
2292
|
+
const cellKeys = new Set([
|
|
2172
2293
|
...(beforeSheet?.cells.keys() ?? []),
|
|
2173
2294
|
...afterSheet.cells.keys(),
|
|
2174
2295
|
]);
|
|
2175
|
-
[...
|
|
2176
|
-
|
|
2177
|
-
|
|
2296
|
+
[...cellKeys]
|
|
2297
|
+
.toSorted((left, right) => left - right)
|
|
2298
|
+
.forEach((cellKey) => {
|
|
2299
|
+
const beforeValue = beforeSheet?.cells.get(cellKey) ?? emptyValue();
|
|
2300
|
+
const afterValue = afterSheet.cells.get(cellKey) ?? emptyValue();
|
|
2178
2301
|
if (valuesEqual(beforeValue, afterValue)) {
|
|
2179
2302
|
return;
|
|
2180
2303
|
}
|
|
2181
|
-
const
|
|
2304
|
+
const localKey = cellKey - afterSheet.sheetId * VISIBILITY_SHEET_STRIDE;
|
|
2305
|
+
const row = Math.floor(localKey / MAX_COLS);
|
|
2306
|
+
const col = localKey % MAX_COLS;
|
|
2307
|
+
const address = formatAddress(row, col);
|
|
2182
2308
|
cellChanges.push({
|
|
2183
2309
|
kind: "cell",
|
|
2184
|
-
address: { sheet: sheetId, row
|
|
2310
|
+
address: { sheet: sheetId, row, col },
|
|
2185
2311
|
sheetName: afterSheet.sheetName,
|
|
2186
2312
|
a1: address,
|
|
2187
|
-
newValue:
|
|
2313
|
+
newValue: afterValue,
|
|
2188
2314
|
});
|
|
2189
2315
|
});
|
|
2190
2316
|
});
|
|
2317
|
+
return orderWorkPaperCellChanges(cellChanges, this.listSheetRecords());
|
|
2318
|
+
}
|
|
2319
|
+
computeCellChangesFromTrackedEvents(beforeVisibility, events) {
|
|
2320
|
+
if (events.some((event) => event.invalidation === "full" ||
|
|
2321
|
+
event.hasInvalidatedRanges ||
|
|
2322
|
+
event.hasInvalidatedRows ||
|
|
2323
|
+
event.hasInvalidatedColumns)) {
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
2327
|
+
const strings = this.engine.strings;
|
|
2328
|
+
const nextVisibility = beforeVisibility;
|
|
2329
|
+
const sheetNames = new Map();
|
|
2330
|
+
const ensureSheetName = (sheetId) => {
|
|
2331
|
+
const cached = sheetNames.get(sheetId);
|
|
2332
|
+
if (cached !== undefined) {
|
|
2333
|
+
return cached;
|
|
2334
|
+
}
|
|
2335
|
+
const sheet = this.engine.workbook.getSheetById(sheetId);
|
|
2336
|
+
if (!sheet) {
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
sheetNames.set(sheetId, sheet.name);
|
|
2340
|
+
return sheet.name;
|
|
2341
|
+
};
|
|
2342
|
+
const ensureMutableSheet = (sheetId, sheetName) => {
|
|
2343
|
+
const existing = nextVisibility.get(sheetId);
|
|
2344
|
+
if (existing) {
|
|
2345
|
+
return existing;
|
|
2346
|
+
}
|
|
2347
|
+
const created = {
|
|
2348
|
+
sheetId,
|
|
2349
|
+
sheetName,
|
|
2350
|
+
order: this.sheetRecord(sheetId).order,
|
|
2351
|
+
cells: new Map(),
|
|
2352
|
+
};
|
|
2353
|
+
nextVisibility.set(sheetId, created);
|
|
2354
|
+
return created;
|
|
2355
|
+
};
|
|
2356
|
+
const collectDirectChanges = (changedCellIndices, seen) => {
|
|
2357
|
+
const changes = [];
|
|
2358
|
+
let previousSheetId = -1;
|
|
2359
|
+
let previousRow = -1;
|
|
2360
|
+
let previousCol = -1;
|
|
2361
|
+
let isSorted = true;
|
|
2362
|
+
for (let index = 0; index < changedCellIndices.length; index += 1) {
|
|
2363
|
+
const cellIndex = changedCellIndices[index];
|
|
2364
|
+
const sheetId = cellStore.sheetIds[cellIndex];
|
|
2365
|
+
const row = cellStore.rows[cellIndex];
|
|
2366
|
+
const col = cellStore.cols[cellIndex];
|
|
2367
|
+
if (sheetId === undefined || row === undefined || col === undefined) {
|
|
2368
|
+
return null;
|
|
2369
|
+
}
|
|
2370
|
+
if (seen) {
|
|
2371
|
+
const logicalCellKey = makeCellKey(sheetId, row, col);
|
|
2372
|
+
if (seen.has(logicalCellKey)) {
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2375
|
+
seen.add(logicalCellKey);
|
|
2376
|
+
}
|
|
2377
|
+
const sheetName = ensureSheetName(sheetId);
|
|
2378
|
+
if (!sheetName) {
|
|
2379
|
+
return null;
|
|
2380
|
+
}
|
|
2381
|
+
const cellKey = makeCellKey(sheetId, row, col);
|
|
2382
|
+
const address = formatAddress(row, col);
|
|
2383
|
+
const beforeValue = beforeVisibility.get(sheetId)?.cells.get(cellKey) ?? emptyValue();
|
|
2384
|
+
const afterValue = cellStore.getValue(cellIndex, (id) => strings.get(id));
|
|
2385
|
+
if (valuesEqual(beforeValue, afterValue)) {
|
|
2386
|
+
continue;
|
|
2387
|
+
}
|
|
2388
|
+
if (previousSheetId > sheetId ||
|
|
2389
|
+
(previousSheetId === sheetId &&
|
|
2390
|
+
(previousRow > row || (previousRow === row && previousCol > col)))) {
|
|
2391
|
+
isSorted = false;
|
|
2392
|
+
}
|
|
2393
|
+
previousSheetId = sheetId;
|
|
2394
|
+
previousRow = row;
|
|
2395
|
+
previousCol = col;
|
|
2396
|
+
const sheet = ensureMutableSheet(sheetId, sheetName);
|
|
2397
|
+
if (afterValue.tag === ValueTag.Empty) {
|
|
2398
|
+
sheet.cells.delete(cellKey);
|
|
2399
|
+
}
|
|
2400
|
+
else {
|
|
2401
|
+
sheet.cells.set(cellKey, afterValue);
|
|
2402
|
+
}
|
|
2403
|
+
changes.push({
|
|
2404
|
+
kind: "cell",
|
|
2405
|
+
address: { sheet: sheetId, row, col },
|
|
2406
|
+
sheetName,
|
|
2407
|
+
a1: address,
|
|
2408
|
+
newValue: afterValue,
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
return { changes, isSorted };
|
|
2412
|
+
};
|
|
2413
|
+
if (events.length === 1) {
|
|
2414
|
+
const event = events[0];
|
|
2415
|
+
const direct = collectDirectChanges(event.changedCellIndices, new Set());
|
|
2416
|
+
if (direct === null) {
|
|
2417
|
+
return null;
|
|
2418
|
+
}
|
|
2419
|
+
return {
|
|
2420
|
+
changes: direct.isSorted
|
|
2421
|
+
? direct.changes
|
|
2422
|
+
: orderWorkPaperCellChanges(direct.changes, this.listSheetRecords(), event.explicitChangedCount),
|
|
2423
|
+
nextVisibility,
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
const latestCellIndicesByKey = new Map();
|
|
2427
|
+
for (const event of events) {
|
|
2428
|
+
for (let index = 0; index < event.changedCellIndices.length; index += 1) {
|
|
2429
|
+
const cellIndex = event.changedCellIndices[index];
|
|
2430
|
+
const sheetId = cellStore.sheetIds[cellIndex];
|
|
2431
|
+
const row = cellStore.rows[cellIndex];
|
|
2432
|
+
const col = cellStore.cols[cellIndex];
|
|
2433
|
+
if (sheetId === undefined || row === undefined || col === undefined) {
|
|
2434
|
+
return null;
|
|
2435
|
+
}
|
|
2436
|
+
latestCellIndicesByKey.set(makeCellKey(sheetId, row, col), cellIndex);
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
const direct = collectDirectChanges(Uint32Array.from(latestCellIndicesByKey.values()), null);
|
|
2440
|
+
if (direct === null) {
|
|
2441
|
+
return null;
|
|
2442
|
+
}
|
|
2443
|
+
return {
|
|
2444
|
+
changes: direct.isSorted
|
|
2445
|
+
? direct.changes
|
|
2446
|
+
: orderWorkPaperCellChanges(direct.changes, this.listSheetRecords()),
|
|
2447
|
+
nextVisibility,
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
2450
|
+
computeNamedExpressionChanges(beforeNames, afterNames) {
|
|
2191
2451
|
const namedExpressionChanges = [];
|
|
2192
2452
|
afterNames.forEach((afterValue, key) => {
|
|
2193
2453
|
const beforeValue = beforeNames.get(key);
|
|
@@ -2205,40 +2465,73 @@ export class HeadlessWorkbook {
|
|
|
2205
2465
|
newValue: cloneNamedExpressionValue(afterValue),
|
|
2206
2466
|
});
|
|
2207
2467
|
});
|
|
2208
|
-
return
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2468
|
+
return namedExpressionChanges.toSorted(compareWorkPaperNamedExpressionChanges);
|
|
2469
|
+
}
|
|
2470
|
+
computeChangesAfterMutation(beforeVisibility, beforeNames) {
|
|
2471
|
+
const hasNamedExpressions = this.namedExpressions.size > 0;
|
|
2472
|
+
const afterNames = hasNamedExpressions
|
|
2473
|
+
? this.captureNamedExpressionValueSnapshot()
|
|
2474
|
+
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2475
|
+
const fastPath = this.computeCellChangesFromTrackedEvents(beforeVisibility, this.drainTrackedEngineEvents());
|
|
2476
|
+
let cellChanges;
|
|
2477
|
+
if (fastPath) {
|
|
2478
|
+
cellChanges = fastPath.changes;
|
|
2479
|
+
this.visibilityCache = fastPath.nextVisibility;
|
|
2480
|
+
}
|
|
2481
|
+
else {
|
|
2482
|
+
const afterVisibility = this.captureVisibilitySnapshot();
|
|
2483
|
+
cellChanges = this.computeCellChanges(beforeVisibility, afterVisibility);
|
|
2484
|
+
this.visibilityCache = afterVisibility;
|
|
2485
|
+
}
|
|
2486
|
+
this.namedExpressionValueCache = afterNames;
|
|
2487
|
+
return hasNamedExpressions
|
|
2488
|
+
? [...cellChanges, ...this.computeNamedExpressionChanges(beforeNames, afterNames)]
|
|
2489
|
+
: cellChanges;
|
|
2212
2490
|
}
|
|
2213
2491
|
captureChanges(semanticEvent, mutate) {
|
|
2214
2492
|
this.assertNotDisposed();
|
|
2215
2493
|
if (semanticEvent !== undefined) {
|
|
2216
2494
|
this.flushPendingBatchOps();
|
|
2217
2495
|
}
|
|
2218
|
-
if (
|
|
2496
|
+
if (this.shouldSuppressEvents()) {
|
|
2219
2497
|
try {
|
|
2220
2498
|
mutate();
|
|
2221
2499
|
}
|
|
2222
2500
|
catch (error) {
|
|
2223
|
-
if (error instanceof Error &&
|
|
2501
|
+
if (error instanceof Error && WORKPAPER_PUBLIC_ERROR_NAMES.has(error.name)) {
|
|
2224
2502
|
throw error;
|
|
2225
2503
|
}
|
|
2226
|
-
throw new
|
|
2504
|
+
throw new WorkPaperOperationError(this.messageOf(error, "Mutation failed"));
|
|
2505
|
+
}
|
|
2506
|
+
if (semanticEvent) {
|
|
2507
|
+
this.queuedEvents.push(semanticEvent);
|
|
2227
2508
|
}
|
|
2228
2509
|
return [];
|
|
2229
2510
|
}
|
|
2230
|
-
const beforeVisibility = this.
|
|
2231
|
-
const beforeNames = this.
|
|
2511
|
+
const beforeVisibility = this.ensureVisibilityCache();
|
|
2512
|
+
const beforeNames = this.ensureNamedExpressionValueCache();
|
|
2513
|
+
this.drainTrackedEngineEvents();
|
|
2232
2514
|
try {
|
|
2233
2515
|
mutate();
|
|
2234
2516
|
}
|
|
2235
2517
|
catch (error) {
|
|
2236
|
-
if (error instanceof Error &&
|
|
2518
|
+
if (error instanceof Error && WORKPAPER_PUBLIC_ERROR_NAMES.has(error.name)) {
|
|
2237
2519
|
throw error;
|
|
2238
2520
|
}
|
|
2239
|
-
throw new
|
|
2240
|
-
}
|
|
2241
|
-
const changes =
|
|
2521
|
+
throw new WorkPaperOperationError(this.messageOf(error, "Mutation failed"));
|
|
2522
|
+
}
|
|
2523
|
+
const changes = semanticEvent === undefined
|
|
2524
|
+
? this.computeChangesAfterMutation(beforeVisibility, beforeNames)
|
|
2525
|
+
: (() => {
|
|
2526
|
+
const afterVisibility = this.captureVisibilitySnapshot();
|
|
2527
|
+
const afterNames = this.captureNamedExpressionValueSnapshot();
|
|
2528
|
+
this.visibilityCache = afterVisibility;
|
|
2529
|
+
this.namedExpressionValueCache = afterNames;
|
|
2530
|
+
return [
|
|
2531
|
+
...this.computeCellChanges(beforeVisibility, afterVisibility),
|
|
2532
|
+
...this.computeNamedExpressionChanges(beforeNames, afterNames),
|
|
2533
|
+
];
|
|
2534
|
+
})();
|
|
2242
2535
|
if (semanticEvent) {
|
|
2243
2536
|
const event = withEventChanges(semanticEvent, changes);
|
|
2244
2537
|
if (this.shouldSuppressEvents()) {
|
|
@@ -2325,99 +2618,109 @@ export class HeadlessWorkbook {
|
|
|
2325
2618
|
if (typeof raw === "string" && raw.startsWith("=")) {
|
|
2326
2619
|
nextValue = `=${translateFormulaReferences(raw.slice(1), destination.row - (sourceAnchor.row + rowOffset), destination.col - (sourceAnchor.col + columnOffset))}`;
|
|
2327
2620
|
}
|
|
2328
|
-
this.applyRawContent(
|
|
2621
|
+
this.applyRawContent(destination, nextValue);
|
|
2329
2622
|
});
|
|
2330
2623
|
});
|
|
2331
2624
|
return;
|
|
2332
2625
|
}
|
|
2333
|
-
const
|
|
2334
|
-
const { ops, potentialNewCells } = buildMatrixMutationPlan({
|
|
2626
|
+
const { refs, potentialNewCells } = buildMatrixMutationPlan({
|
|
2335
2627
|
target: targetLeftCorner,
|
|
2336
|
-
targetSheetName: sheetName,
|
|
2337
2628
|
content: serialized,
|
|
2338
2629
|
rewriteFormula: (formula, destination, rowOffset, columnOffset) => this.rewriteFormulaForStorage(translateFormulaReferences(stripLeadingEquals(formula), destination.row - (sourceAnchor.row + rowOffset), destination.col - (sourceAnchor.col + columnOffset)), destination.sheet),
|
|
2339
2630
|
});
|
|
2340
|
-
if (
|
|
2631
|
+
if (refs.length === 0) {
|
|
2341
2632
|
return;
|
|
2342
2633
|
}
|
|
2343
|
-
this.engine.
|
|
2634
|
+
this.engine.applyCellMutationsAtWithOptions(refs, {
|
|
2635
|
+
captureUndo: true,
|
|
2636
|
+
potentialNewCells,
|
|
2637
|
+
source: "local",
|
|
2638
|
+
});
|
|
2344
2639
|
}
|
|
2345
2640
|
applyMatrixContents(address, content, options = {}) {
|
|
2346
2641
|
this.flushPendingBatchOps();
|
|
2347
|
-
|
|
2348
|
-
content.forEach((row, rowOffset) => {
|
|
2349
|
-
row.forEach((raw, columnOffset) => {
|
|
2350
|
-
if (raw === null && options.skipNulls) {
|
|
2351
|
-
return;
|
|
2352
|
-
}
|
|
2353
|
-
this.applyRawContent(this.sheetName(address.sheet), formatAddress(address.row + rowOffset, address.col + columnOffset), raw, address.sheet);
|
|
2354
|
-
});
|
|
2355
|
-
});
|
|
2356
|
-
return;
|
|
2357
|
-
}
|
|
2358
|
-
const sheetName = this.sheetName(address.sheet);
|
|
2359
|
-
const { ops, potentialNewCells } = buildMatrixMutationPlan({
|
|
2642
|
+
const { leadingRefs, formulaRefs, refs, potentialNewCells, trailingLiteralRefs } = buildMatrixMutationPlan({
|
|
2360
2643
|
target: address,
|
|
2361
|
-
targetSheetName: sheetName,
|
|
2362
2644
|
content,
|
|
2645
|
+
deferLiteralAddresses: options.deferLiteralAddresses,
|
|
2363
2646
|
skipNulls: options.skipNulls,
|
|
2364
2647
|
rewriteFormula: (formula, destination) => this.rewriteFormulaForStorage(stripLeadingEquals(formula), destination.sheet),
|
|
2365
2648
|
});
|
|
2366
|
-
if (
|
|
2649
|
+
if (refs.length === 0) {
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
const applyPlannedRefs = (phaseRefs, applyOptions) => {
|
|
2653
|
+
if (phaseRefs.length === 0) {
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
this.engine.applyCellMutationsAtWithOptions(phaseRefs, applyOptions);
|
|
2657
|
+
};
|
|
2658
|
+
const phaseSource = options.captureUndo === false ? "restore" : "local";
|
|
2659
|
+
if (formulaRefs.length === 0) {
|
|
2660
|
+
applyPlannedRefs(refs, {
|
|
2661
|
+
captureUndo: options.captureUndo,
|
|
2662
|
+
potentialNewCells,
|
|
2663
|
+
source: phaseSource,
|
|
2664
|
+
});
|
|
2367
2665
|
return;
|
|
2368
2666
|
}
|
|
2369
|
-
|
|
2667
|
+
applyPlannedRefs(leadingRefs, {
|
|
2668
|
+
captureUndo: options.captureUndo,
|
|
2669
|
+
potentialNewCells,
|
|
2670
|
+
source: phaseSource,
|
|
2671
|
+
});
|
|
2672
|
+
applyPlannedRefs(formulaRefs, {
|
|
2673
|
+
captureUndo: options.captureUndo,
|
|
2674
|
+
potentialNewCells,
|
|
2675
|
+
source: phaseSource,
|
|
2676
|
+
});
|
|
2677
|
+
applyPlannedRefs(trailingLiteralRefs, {
|
|
2370
2678
|
captureUndo: options.captureUndo,
|
|
2371
2679
|
potentialNewCells,
|
|
2680
|
+
source: phaseSource,
|
|
2372
2681
|
});
|
|
2373
2682
|
}
|
|
2374
2683
|
replaceSheetContentInternal(sheetId, content, options) {
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
skipNulls: true,
|
|
2684
|
+
replaceWorkPaperSheetContent({
|
|
2685
|
+
sheetId,
|
|
2686
|
+
sheetName: this.sheetName(sheetId),
|
|
2687
|
+
content,
|
|
2688
|
+
duringInitialization: options.duringInitialization,
|
|
2689
|
+
listSpills: () => this.engine.workbook.listSpills(),
|
|
2690
|
+
getSheetDimensions: (nextSheetId) => this.getSheetDimensions(nextSheetId),
|
|
2691
|
+
clearRange: (input) => this.engine.clearRange(input),
|
|
2692
|
+
applyMatrixContents: (address, nextContent, applyOptions) => this.applyMatrixContents(address, nextContent, applyOptions),
|
|
2693
|
+
clearHistoryStacks: () => this.clearHistoryStacks(),
|
|
2694
|
+
getUndoStackLength: () => this.getUndoStack().length,
|
|
2695
|
+
mergeUndoHistory: (undoStackStart) => this.mergeUndoHistory(undoStackStart),
|
|
2388
2696
|
});
|
|
2389
|
-
if (options.duringInitialization) {
|
|
2390
|
-
this.clearHistoryStacks();
|
|
2391
|
-
return;
|
|
2392
|
-
}
|
|
2393
|
-
this.mergeUndoHistory(undoStackStart);
|
|
2394
2697
|
}
|
|
2395
|
-
applyRawContent(
|
|
2698
|
+
applyRawContent(address, content) {
|
|
2396
2699
|
if (content === null) {
|
|
2397
|
-
this.engine.
|
|
2700
|
+
this.engine.clearCellAt(address.sheet, address.row, address.col);
|
|
2398
2701
|
return;
|
|
2399
2702
|
}
|
|
2400
2703
|
if (typeof content === "boolean" || typeof content === "number") {
|
|
2401
|
-
this.engine.
|
|
2704
|
+
this.engine.setCellValueAt(address.sheet, address.row, address.col, content);
|
|
2402
2705
|
return;
|
|
2403
2706
|
}
|
|
2404
2707
|
if (typeof content === "string" && content.trim().startsWith("=")) {
|
|
2405
|
-
this.engine.
|
|
2708
|
+
this.engine.setCellFormulaAt(address.sheet, address.row, address.col, this.rewriteFormulaForStorage(stripLeadingEquals(content), address.sheet));
|
|
2406
2709
|
return;
|
|
2407
2710
|
}
|
|
2408
|
-
this.engine.
|
|
2711
|
+
this.engine.setCellValueAt(address.sheet, address.row, address.col, content);
|
|
2409
2712
|
}
|
|
2410
2713
|
captureFunctionRegistry() {
|
|
2411
2714
|
const allowedPluginIds = this.config.functionPlugins && this.config.functionPlugins.length > 0
|
|
2412
2715
|
? new Set(this.config.functionPlugins.map((plugin) => plugin.id))
|
|
2413
2716
|
: undefined;
|
|
2414
|
-
|
|
2717
|
+
WorkPaper.functionPluginRegistry.forEach((plugin) => {
|
|
2415
2718
|
if (allowedPluginIds && !allowedPluginIds.has(plugin.id)) {
|
|
2416
2719
|
return;
|
|
2417
2720
|
}
|
|
2418
2721
|
Object.keys(plugin.implementedFunctions).forEach((functionId) => {
|
|
2419
2722
|
const normalized = functionId.trim().toUpperCase();
|
|
2420
|
-
const internalName = `
|
|
2723
|
+
const internalName = `__BILIG_WORKPAPER_FN_${this.workbookId}_${normalized}`;
|
|
2421
2724
|
const implementation = plugin.functions?.[normalized];
|
|
2422
2725
|
const binding = {
|
|
2423
2726
|
pluginId: plugin.id,
|
|
@@ -2450,7 +2753,7 @@ export class HeadlessWorkbook {
|
|
|
2450
2753
|
this.internalFunctionLookup.clear();
|
|
2451
2754
|
}
|
|
2452
2755
|
rebuildWithConfig(nextConfig) {
|
|
2453
|
-
|
|
2756
|
+
validateWorkPaperConfig(nextConfig);
|
|
2454
2757
|
const serializedSheets = this.getAllSheetsSerialized();
|
|
2455
2758
|
Object.entries(serializedSheets).forEach(([sheetName, sheet]) => {
|
|
2456
2759
|
validateSheetWithinLimits(sheetName, sheet, nextConfig);
|
|
@@ -2466,43 +2769,53 @@ export class HeadlessWorkbook {
|
|
|
2466
2769
|
: null;
|
|
2467
2770
|
this.clearFunctionBindings();
|
|
2468
2771
|
this.namedExpressions.clear();
|
|
2469
|
-
this.engine = new SpreadsheetEngine({
|
|
2772
|
+
this.engine = new SpreadsheetEngine({
|
|
2773
|
+
workbookName: "Workbook",
|
|
2774
|
+
useColumnIndex: this.config.useColumnIndex,
|
|
2775
|
+
trackReplicaVersions: false,
|
|
2776
|
+
});
|
|
2777
|
+
this.attachEngineEventTracking();
|
|
2470
2778
|
this.config = cloneConfig(nextConfig);
|
|
2471
2779
|
this.captureFunctionRegistry();
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2780
|
+
this.withEngineEventCaptureDisabled(() => {
|
|
2781
|
+
Object.keys(serializedSheets).forEach((sheetName) => {
|
|
2782
|
+
this.engine.createSheet(sheetName);
|
|
2783
|
+
});
|
|
2784
|
+
serializedNamedExpressions.forEach((expression) => {
|
|
2785
|
+
this.upsertNamedExpressionInternal(expression, { duringInitialization: true });
|
|
2786
|
+
});
|
|
2787
|
+
Object.entries(serializedSheets).forEach(([sheetName, sheet]) => {
|
|
2788
|
+
const sheetId = this.requireSheetId(sheetName);
|
|
2789
|
+
if (!tryLoadInitialLiteralSheet(this.engine, sheetId, sheet)) {
|
|
2790
|
+
this.replaceSheetContentInternal(sheetId, sheet, { duringInitialization: true });
|
|
2791
|
+
}
|
|
2792
|
+
});
|
|
2481
2793
|
});
|
|
2482
2794
|
this.clearHistoryStacks();
|
|
2795
|
+
this.primeChangeTrackingCaches();
|
|
2483
2796
|
this.clipboard = clipboard;
|
|
2484
2797
|
if (suspended) {
|
|
2485
|
-
this.suspendedVisibility = this.
|
|
2486
|
-
this.suspendedNamedValues = this.
|
|
2798
|
+
this.suspendedVisibility = this.ensureVisibilityCache();
|
|
2799
|
+
this.suspendedNamedValues = this.ensureNamedExpressionValueCache();
|
|
2487
2800
|
}
|
|
2488
2801
|
}
|
|
2489
2802
|
normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals = []) {
|
|
2490
2803
|
if (typeof startOrInterval === "number") {
|
|
2491
2804
|
if (Array.isArray(countOrInterval)) {
|
|
2492
|
-
throw new
|
|
2805
|
+
throw new WorkPaperInvalidArgumentsError("Axis interval count must be a number");
|
|
2493
2806
|
}
|
|
2494
2807
|
const resolvedCount = typeof countOrInterval === "number" ? countOrInterval : 1;
|
|
2495
2808
|
return [[startOrInterval, resolvedCount]];
|
|
2496
2809
|
}
|
|
2497
2810
|
if (typeof countOrInterval === "number") {
|
|
2498
|
-
throw new
|
|
2811
|
+
throw new WorkPaperInvalidArgumentsError("Axis interval count is only valid with a numeric start");
|
|
2499
2812
|
}
|
|
2500
2813
|
return [startOrInterval, ...(countOrInterval ? [countOrInterval] : []), ...restIntervals].map(([start, count]) => [start, count ?? 1]);
|
|
2501
2814
|
}
|
|
2502
2815
|
normalizeAxisSwapMappings(label, startOrMappings, end) {
|
|
2503
2816
|
if (typeof startOrMappings === "number") {
|
|
2504
2817
|
if (end === undefined) {
|
|
2505
|
-
throw new
|
|
2818
|
+
throw new WorkPaperInvalidArgumentsError(`${label} swap requires two indexes`);
|
|
2506
2819
|
}
|
|
2507
2820
|
return [[startOrMappings, end]];
|
|
2508
2821
|
}
|
|
@@ -2531,6 +2844,9 @@ export class HeadlessWorkbook {
|
|
|
2531
2844
|
return collected;
|
|
2532
2845
|
}
|
|
2533
2846
|
rewriteFormulaForStorage(formula, ownerSheetId) {
|
|
2847
|
+
if (this.namedExpressions.size === 0 && this.functionAliasLookup.size === 0) {
|
|
2848
|
+
return formula;
|
|
2849
|
+
}
|
|
2534
2850
|
try {
|
|
2535
2851
|
const transformed = transformFormulaNode(parseFormula(stripLeadingEquals(formula)), (node) => {
|
|
2536
2852
|
if (node.kind === "NameRef") {
|
|
@@ -2544,10 +2860,13 @@ export class HeadlessWorkbook {
|
|
|
2544
2860
|
return serializeFormula(transformed);
|
|
2545
2861
|
}
|
|
2546
2862
|
catch (error) {
|
|
2547
|
-
throw new
|
|
2863
|
+
throw new WorkPaperParseError(this.messageOf(error, "Unable to store formula"));
|
|
2548
2864
|
}
|
|
2549
2865
|
}
|
|
2550
2866
|
restorePublicFormula(formula, ownerSheetId) {
|
|
2867
|
+
if (this.namedExpressions.size === 0 && this.functionAliasLookup.size === 0) {
|
|
2868
|
+
return formula;
|
|
2869
|
+
}
|
|
2551
2870
|
const transformed = transformFormulaNode(parseFormula(formula), (node) => {
|
|
2552
2871
|
if (node.kind === "NameRef") {
|
|
2553
2872
|
return this.rewriteNameRefForPublic(node, ownerSheetId);
|
|
@@ -2594,7 +2913,7 @@ export class HeadlessWorkbook {
|
|
|
2594
2913
|
validateNamedExpression(expressionName, expression, scope) {
|
|
2595
2914
|
const trimmed = expressionName.trim();
|
|
2596
2915
|
if (!/^[A-Za-z_][A-Za-z0-9_.]*$/.test(trimmed) || isCellReferenceText(trimmed)) {
|
|
2597
|
-
throw new
|
|
2916
|
+
throw new WorkPaperNamedExpressionNameIsInvalidError(expressionName);
|
|
2598
2917
|
}
|
|
2599
2918
|
if (scope !== undefined) {
|
|
2600
2919
|
this.sheetRecord(scope);
|
|
@@ -2603,14 +2922,14 @@ export class HeadlessWorkbook {
|
|
|
2603
2922
|
try {
|
|
2604
2923
|
const parsed = parseFormula(stripLeadingEquals(expression));
|
|
2605
2924
|
if (formulaHasRelativeReferences(parsed)) {
|
|
2606
|
-
throw new
|
|
2925
|
+
throw new WorkPaperNoRelativeAddressesAllowedError();
|
|
2607
2926
|
}
|
|
2608
2927
|
}
|
|
2609
2928
|
catch (error) {
|
|
2610
|
-
if (error instanceof
|
|
2929
|
+
if (error instanceof WorkPaperNoRelativeAddressesAllowedError) {
|
|
2611
2930
|
throw error;
|
|
2612
2931
|
}
|
|
2613
|
-
throw new
|
|
2932
|
+
throw new WorkPaperUnableToParseError({
|
|
2614
2933
|
expressionName,
|
|
2615
2934
|
reason: this.messageOf(error, `Invalid named expression formula for '${expressionName}'`),
|
|
2616
2935
|
});
|
|
@@ -2652,7 +2971,7 @@ export class HeadlessWorkbook {
|
|
|
2652
2971
|
if (direct) {
|
|
2653
2972
|
return direct;
|
|
2654
2973
|
}
|
|
2655
|
-
throw new
|
|
2974
|
+
throw new WorkPaperNamedExpressionDoesNotExistError(name);
|
|
2656
2975
|
}
|
|
2657
2976
|
evaluateNamedExpression(expression) {
|
|
2658
2977
|
const raw = expression.expression;
|
|
@@ -2734,23 +3053,12 @@ function cloneNamedExpressionValue(value) {
|
|
|
2734
3053
|
}
|
|
2735
3054
|
return value.map((row) => row.map((cell) => cloneCellValue(cell)));
|
|
2736
3055
|
}
|
|
2737
|
-
function
|
|
3056
|
+
function compareWorkPaperNamedExpressionChanges(left, right) {
|
|
2738
3057
|
if (left.kind !== "named-expression" || right.kind !== "named-expression") {
|
|
2739
3058
|
return 0;
|
|
2740
3059
|
}
|
|
2741
3060
|
return (left.scope ?? -1) - (right.scope ?? -1) || left.name.localeCompare(right.name);
|
|
2742
3061
|
}
|
|
2743
|
-
function compareHeadlessCellChanges(sheets) {
|
|
2744
|
-
const orderBySheet = new Map(sheets.map((sheet) => [sheet.id, sheet.order]));
|
|
2745
|
-
return (left, right) => {
|
|
2746
|
-
if (left.kind !== "cell" || right.kind !== "cell") {
|
|
2747
|
-
return 0;
|
|
2748
|
-
}
|
|
2749
|
-
return ((orderBySheet.get(left.address.sheet) ?? 0) - (orderBySheet.get(right.address.sheet) ?? 0) ||
|
|
2750
|
-
left.address.row - right.address.row ||
|
|
2751
|
-
left.address.col - right.address.col);
|
|
2752
|
-
};
|
|
2753
|
-
}
|
|
2754
3062
|
function sourceRangeRef(sheetName, range) {
|
|
2755
3063
|
return {
|
|
2756
3064
|
sheetName,
|
|
@@ -2765,4 +3073,4 @@ function sumNumbers(values) {
|
|
|
2765
3073
|
}
|
|
2766
3074
|
return filtered.reduce((sum, value) => sum + value, 0);
|
|
2767
3075
|
}
|
|
2768
|
-
//# sourceMappingURL=
|
|
3076
|
+
//# sourceMappingURL=work-paper-runtime.js.map
|