@particle-academy/holy-sheet 2.4.0 → 2.4.2
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/dist/index.cjs +234 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +234 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -177,7 +177,7 @@ var CsvBuilder = {
|
|
|
177
177
|
return ArrayBuilder.build(dataRows, headers, sheetName2, options);
|
|
178
178
|
}
|
|
179
179
|
};
|
|
180
|
-
function parseRows(csv, delimiter,
|
|
180
|
+
function parseRows(csv, delimiter, quote2) {
|
|
181
181
|
const rows = [];
|
|
182
182
|
let field = "";
|
|
183
183
|
let row = [];
|
|
@@ -197,9 +197,9 @@ function parseRows(csv, delimiter, quote) {
|
|
|
197
197
|
for (let i = 0; i < csv.length; i++) {
|
|
198
198
|
const ch = csv[i];
|
|
199
199
|
if (inQuotes) {
|
|
200
|
-
if (ch ===
|
|
201
|
-
if (csv[i + 1] ===
|
|
202
|
-
field +=
|
|
200
|
+
if (ch === quote2) {
|
|
201
|
+
if (csv[i + 1] === quote2) {
|
|
202
|
+
field += quote2;
|
|
203
203
|
i++;
|
|
204
204
|
} else {
|
|
205
205
|
inQuotes = false;
|
|
@@ -209,7 +209,7 @@ function parseRows(csv, delimiter, quote) {
|
|
|
209
209
|
}
|
|
210
210
|
continue;
|
|
211
211
|
}
|
|
212
|
-
if (ch ===
|
|
212
|
+
if (ch === quote2) {
|
|
213
213
|
inQuotes = true;
|
|
214
214
|
started = true;
|
|
215
215
|
continue;
|
|
@@ -2682,13 +2682,13 @@ function unescapeXml(s) {
|
|
|
2682
2682
|
});
|
|
2683
2683
|
}
|
|
2684
2684
|
function findTagEnd(src, from) {
|
|
2685
|
-
let
|
|
2685
|
+
let quote2 = "";
|
|
2686
2686
|
for (let i = from; i < src.length; i++) {
|
|
2687
2687
|
const ch = src[i];
|
|
2688
|
-
if (
|
|
2689
|
-
if (ch ===
|
|
2688
|
+
if (quote2) {
|
|
2689
|
+
if (ch === quote2) quote2 = "";
|
|
2690
2690
|
} else if (ch === '"' || ch === "'") {
|
|
2691
|
-
|
|
2691
|
+
quote2 = ch;
|
|
2692
2692
|
} else if (ch === ">") {
|
|
2693
2693
|
return i;
|
|
2694
2694
|
}
|
|
@@ -3836,16 +3836,132 @@ function phpString(value) {
|
|
|
3836
3836
|
if (isArr(value)) return "Array";
|
|
3837
3837
|
return String(value);
|
|
3838
3838
|
}
|
|
3839
|
+
function phpTrim2(value) {
|
|
3840
|
+
return value.replace(/^[ \t\n\r\x00\x0B]+|[ \t\n\r\x00\x0B]+$/g, "");
|
|
3841
|
+
}
|
|
3842
|
+
function phpInteger(value) {
|
|
3843
|
+
if (typeof value === "number") {
|
|
3844
|
+
return Number.isInteger(value) && Math.abs(value) <= 2 ** 63 ? value || 0 : null;
|
|
3845
|
+
}
|
|
3846
|
+
return typeof value === "string" && /^[0-9]+$/.test(value) ? phpInt2(value) : null;
|
|
3847
|
+
}
|
|
3848
|
+
function isIndexKey(key) {
|
|
3849
|
+
return /^[0-9]+$/.test(key) || /^-[1-9][0-9]*$/.test(key) && BigInt(key) >= -(2n ** 63n);
|
|
3850
|
+
}
|
|
3839
3851
|
function asciiUpper(value) {
|
|
3840
3852
|
return value.replace(/[a-z]+/g, (s) => s.toUpperCase());
|
|
3841
3853
|
}
|
|
3842
3854
|
function parseAddress(address) {
|
|
3843
|
-
const m = /^([A-Z]+)(\d+)$/.exec(asciiUpper(address
|
|
3855
|
+
const m = /^([A-Z]+)(\d+)$/.exec(asciiUpper(phpTrim2(address)));
|
|
3844
3856
|
if (!m) return null;
|
|
3845
3857
|
return [CellAddress.index(m[1]), parseInt(m[2], 10)];
|
|
3846
3858
|
}
|
|
3847
3859
|
var letter = CellAddress.letter;
|
|
3848
3860
|
|
|
3861
|
+
// src/ops/sheet-op-schema.ts
|
|
3862
|
+
var SheetOpSchema = {
|
|
3863
|
+
/** Every op type, in the order the variants are listed. */
|
|
3864
|
+
TYPES: [
|
|
3865
|
+
"set_cell",
|
|
3866
|
+
"set_range",
|
|
3867
|
+
"set_workbook",
|
|
3868
|
+
"clear_cell",
|
|
3869
|
+
"insert_rows",
|
|
3870
|
+
"delete_rows",
|
|
3871
|
+
"insert_columns",
|
|
3872
|
+
"delete_columns",
|
|
3873
|
+
"add_sheet",
|
|
3874
|
+
"remove_sheet",
|
|
3875
|
+
"rename_sheet",
|
|
3876
|
+
"move_sheet",
|
|
3877
|
+
"replace_sheet",
|
|
3878
|
+
"set_merged_regions",
|
|
3879
|
+
"set_column_widths",
|
|
3880
|
+
"set_frozen",
|
|
3881
|
+
"set_meta"
|
|
3882
|
+
],
|
|
3883
|
+
/** A fresh object on every call, so a caller editing it changes nothing here. */
|
|
3884
|
+
jsonSchema() {
|
|
3885
|
+
const address = () => ({ type: "string", pattern: "^[A-Za-z]+[0-9]+$" });
|
|
3886
|
+
const sheet = () => ({ type: "string", minLength: 1 });
|
|
3887
|
+
const count = () => ({ type: "integer", minimum: 1 });
|
|
3888
|
+
const position2 = () => ({ type: "integer", minimum: 1 });
|
|
3889
|
+
const object = () => ({ type: "object" });
|
|
3890
|
+
const nullableObject = () => ({ type: ["object", "null"] });
|
|
3891
|
+
const value = () => ({ type: ["string", "number", "boolean", "null"] });
|
|
3892
|
+
const variants = [
|
|
3893
|
+
variant(
|
|
3894
|
+
"set_cell",
|
|
3895
|
+
{ sheet: sheet(), address: address(), value: value(), formula: { type: "string" }, computedValue: value(), format: nullableObject(), comment: nullableObject() },
|
|
3896
|
+
["sheet", "address"],
|
|
3897
|
+
"Write one cell. Omitting formula or computedValue clears it; omitting format or comment keeps it; null clears it."
|
|
3898
|
+
),
|
|
3899
|
+
variant(
|
|
3900
|
+
"set_range",
|
|
3901
|
+
{ sheet: sheet(), start: address(), end: address(), values: { type: "array", items: { type: "array", items: value() } } },
|
|
3902
|
+
["sheet", "start", "values"],
|
|
3903
|
+
"Write a block of values row-major from start, each as a set_cell with no formula."
|
|
3904
|
+
),
|
|
3905
|
+
variant("set_workbook", { data: object() }, ["data"], "Replace the whole workbook schema."),
|
|
3906
|
+
variant("clear_cell", { sheet: sheet(), address: address() }, ["sheet", "address"], "Remove one cell."),
|
|
3907
|
+
variant("insert_rows", { sheet: sheet(), at: position2(), count: count() }, ["sheet", "at", "count"], "Insert rows before 1-based row `at`; cells, merges below move down."),
|
|
3908
|
+
variant("delete_rows", { sheet: sheet(), at: position2(), count: count() }, ["sheet", "at", "count"], "Delete rows starting at 1-based row `at`; cells, merges below move up."),
|
|
3909
|
+
variant("insert_columns", { sheet: sheet(), at: position2(), count: count() }, ["sheet", "at", "count"], "Insert columns before 1-based column `at` (A = 1); cells, merges and widths move right."),
|
|
3910
|
+
variant("delete_columns", { sheet: sheet(), at: position2(), count: count() }, ["sheet", "at", "count"], "Delete columns starting at 1-based column `at`; cells, merges and widths move left."),
|
|
3911
|
+
variant("add_sheet", { index: { type: "integer", minimum: 0 }, sheet: object() }, ["index", "sheet"], "Insert a sheet at a 0-based position."),
|
|
3912
|
+
variant("remove_sheet", { sheet: sheet() }, ["sheet"], "Remove a sheet by name."),
|
|
3913
|
+
variant("rename_sheet", { sheet: sheet(), name: sheet() }, ["sheet", "name"], "Rename a sheet."),
|
|
3914
|
+
variant("move_sheet", { sheet: sheet(), toIndex: { type: "integer", minimum: 0 } }, ["sheet", "toIndex"], "Move a sheet to a 0-based position."),
|
|
3915
|
+
variant("replace_sheet", { sheet: sheet(), data: object() }, ["sheet", "data"], "Replace one sheet whole."),
|
|
3916
|
+
variant(
|
|
3917
|
+
"set_merged_regions",
|
|
3918
|
+
{ sheet: sheet(), mergedRegions: { type: "array", items: { type: "object", required: ["start", "end"], properties: { start: address(), end: address() } } } },
|
|
3919
|
+
["sheet", "mergedRegions"],
|
|
3920
|
+
"Set every merged region of a sheet."
|
|
3921
|
+
),
|
|
3922
|
+
// A LIST too, as PHP 2.3.2 declares. PHP encodes a map whose keys run
|
|
3923
|
+
// 0..n-1 as a JSON list, so widths for columns A, B and C arrive as
|
|
3924
|
+
// `[120, 80, 140]` and no widths as `[]`; this port passes such a list
|
|
3925
|
+
// through a diff unchanged. A list's position is the column index.
|
|
3926
|
+
variant(
|
|
3927
|
+
"set_column_widths",
|
|
3928
|
+
{
|
|
3929
|
+
sheet: sheet(),
|
|
3930
|
+
columnWidths: {
|
|
3931
|
+
type: ["object", "array"],
|
|
3932
|
+
items: { type: "number", minimum: 0 },
|
|
3933
|
+
additionalProperties: { type: "number", minimum: 0 }
|
|
3934
|
+
}
|
|
3935
|
+
},
|
|
3936
|
+
["sheet", "columnWidths"],
|
|
3937
|
+
"Set every column width of a sheet (0-based column index to pixels; a list is indexed by position); empty removes them."
|
|
3938
|
+
),
|
|
3939
|
+
variant(
|
|
3940
|
+
"set_frozen",
|
|
3941
|
+
{ sheet: sheet(), rows: { type: "integer", minimum: 0 }, cols: { type: "integer", minimum: 0 } },
|
|
3942
|
+
["sheet", "rows", "cols"],
|
|
3943
|
+
"Set frozen rows and columns."
|
|
3944
|
+
),
|
|
3945
|
+
variant("set_meta", { meta: nullableObject() }, ["meta"], "Replace the workbook meta, or remove it with null.")
|
|
3946
|
+
];
|
|
3947
|
+
return {
|
|
3948
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
3949
|
+
title: "Holy Sheet op",
|
|
3950
|
+
description: "One op from Agent::diff, applied by Agent::reduce.",
|
|
3951
|
+
oneOf: variants
|
|
3952
|
+
};
|
|
3953
|
+
}
|
|
3954
|
+
};
|
|
3955
|
+
function variant(type, properties, required, description) {
|
|
3956
|
+
return {
|
|
3957
|
+
type: "object",
|
|
3958
|
+
description,
|
|
3959
|
+
required: ["type", ...required],
|
|
3960
|
+
additionalProperties: false,
|
|
3961
|
+
properties: { type: { const: type }, ...properties }
|
|
3962
|
+
};
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3849
3965
|
// src/ops/sheet-reducer.ts
|
|
3850
3966
|
var SheetReducer = {
|
|
3851
3967
|
/** Sheet keys the granular ops address. A sheet with any other key is authored form. */
|
|
@@ -3858,6 +3974,9 @@ var SheetReducer = {
|
|
|
3858
3974
|
},
|
|
3859
3975
|
apply(schema, op) {
|
|
3860
3976
|
const type = get(op, "type");
|
|
3977
|
+
if (typeof type !== "string" || !SheetOpSchema.TYPES.includes(type)) {
|
|
3978
|
+
return schema;
|
|
3979
|
+
}
|
|
3861
3980
|
if (type === "set_workbook") {
|
|
3862
3981
|
return isArr(get(op, "data")) ? op.data : schema;
|
|
3863
3982
|
}
|
|
@@ -3875,14 +3994,22 @@ var SheetReducer = {
|
|
|
3875
3994
|
if (!isArr(get(op, "sheet"))) {
|
|
3876
3995
|
return schema;
|
|
3877
3996
|
}
|
|
3878
|
-
const index =
|
|
3879
|
-
|
|
3997
|
+
const index = has(op, "index") ? phpInteger(op.index) : sheets.length;
|
|
3998
|
+
if (index === null) {
|
|
3999
|
+
return schema;
|
|
4000
|
+
}
|
|
4001
|
+
sheets.splice(Math.max(0, Math.min(sheets.length, index)), 0, op.sheet);
|
|
3880
4002
|
return { ...schema, sheets };
|
|
3881
4003
|
}
|
|
3882
4004
|
const at2 = find(sheets, phpString(get(op, "sheet") ?? ""));
|
|
3883
4005
|
if (at2 === null) {
|
|
3884
4006
|
return schema;
|
|
3885
4007
|
}
|
|
4008
|
+
for (const field of POSITION_FIELDS) {
|
|
4009
|
+
if (has(op, field) && phpInteger(op[field]) === null) {
|
|
4010
|
+
return schema;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
3886
4013
|
switch (type) {
|
|
3887
4014
|
case "remove_sheet":
|
|
3888
4015
|
sheets.splice(at2, 1);
|
|
@@ -3892,7 +4019,7 @@ var SheetReducer = {
|
|
|
3892
4019
|
break;
|
|
3893
4020
|
case "move_sheet": {
|
|
3894
4021
|
const [moved] = sheets.splice(at2, 1);
|
|
3895
|
-
const to = Math.max(0, Math.min(sheets.length,
|
|
4022
|
+
const to = Math.max(0, Math.min(sheets.length, phpInteger(get(op, "toIndex") ?? at2) ?? at2));
|
|
3896
4023
|
sheets.splice(to, 0, moved);
|
|
3897
4024
|
break;
|
|
3898
4025
|
}
|
|
@@ -3908,8 +4035,8 @@ var SheetReducer = {
|
|
|
3908
4035
|
sheets[at2] = setOrUnset(sheets[at2], "columnWidths", get(op, "columnWidths") ?? [], EMPTY);
|
|
3909
4036
|
break;
|
|
3910
4037
|
case "set_frozen":
|
|
3911
|
-
sheets[at2] = setOrUnset(sheets[at2], "frozenRows",
|
|
3912
|
-
sheets[at2] = setOrUnset(sheets[at2], "frozenCols",
|
|
4038
|
+
sheets[at2] = setOrUnset(sheets[at2], "frozenRows", phpInteger(get(op, "rows") ?? 0) ?? 0, 0);
|
|
4039
|
+
sheets[at2] = setOrUnset(sheets[at2], "frozenCols", phpInteger(get(op, "cols") ?? 0) ?? 0, 0);
|
|
3913
4040
|
break;
|
|
3914
4041
|
case "set_cell":
|
|
3915
4042
|
sheets[at2] = setCell(sheets[at2], op);
|
|
@@ -3918,7 +4045,7 @@ var SheetReducer = {
|
|
|
3918
4045
|
sheets[at2] = setRange(sheets[at2], op);
|
|
3919
4046
|
break;
|
|
3920
4047
|
case "clear_cell": {
|
|
3921
|
-
const address = asciiUpper(phpString(get(op, "address") ?? ""));
|
|
4048
|
+
const address = asciiUpper(phpTrim2(phpString(get(op, "address") ?? "")));
|
|
3922
4049
|
const cells = sheets[at2].cells;
|
|
3923
4050
|
if (isArr(cells) && !Array.isArray(cells) && has(cells, address)) {
|
|
3924
4051
|
const copy = { ...cells };
|
|
@@ -3928,16 +4055,16 @@ var SheetReducer = {
|
|
|
3928
4055
|
break;
|
|
3929
4056
|
}
|
|
3930
4057
|
case "insert_rows":
|
|
3931
|
-
sheets[at2] = shiftRows(sheets[at2],
|
|
4058
|
+
sheets[at2] = shiftRows(sheets[at2], position(op, "at"), Math.max(0, position(op, "count")));
|
|
3932
4059
|
break;
|
|
3933
4060
|
case "delete_rows":
|
|
3934
|
-
sheets[at2] = shiftRows(sheets[at2],
|
|
4061
|
+
sheets[at2] = shiftRows(sheets[at2], position(op, "at"), -Math.max(0, position(op, "count")));
|
|
3935
4062
|
break;
|
|
3936
4063
|
case "insert_columns":
|
|
3937
|
-
sheets[at2] = shiftColumns(sheets[at2],
|
|
4064
|
+
sheets[at2] = shiftColumns(sheets[at2], position(op, "at"), Math.max(0, position(op, "count")));
|
|
3938
4065
|
break;
|
|
3939
4066
|
case "delete_columns":
|
|
3940
|
-
sheets[at2] = shiftColumns(sheets[at2],
|
|
4067
|
+
sheets[at2] = shiftColumns(sheets[at2], position(op, "at"), -Math.max(0, position(op, "count")));
|
|
3941
4068
|
break;
|
|
3942
4069
|
default:
|
|
3943
4070
|
return schema;
|
|
@@ -3945,6 +4072,10 @@ var SheetReducer = {
|
|
|
3945
4072
|
return { ...schema, sheets };
|
|
3946
4073
|
}
|
|
3947
4074
|
};
|
|
4075
|
+
var POSITION_FIELDS = ["index", "toIndex", "rows", "cols", "at", "count"];
|
|
4076
|
+
function position(op, key) {
|
|
4077
|
+
return phpInteger(get(op, key) ?? 0) ?? 0;
|
|
4078
|
+
}
|
|
3948
4079
|
var EMPTY = /* @__PURE__ */ Symbol("empty");
|
|
3949
4080
|
function find(sheets, name) {
|
|
3950
4081
|
for (let i = 0; i < sheets.length; i++) {
|
|
@@ -3964,7 +4095,7 @@ function setOrUnset(sheet, key, value, empty) {
|
|
|
3964
4095
|
return out;
|
|
3965
4096
|
}
|
|
3966
4097
|
function setCell(sheet, op) {
|
|
3967
|
-
const address = asciiUpper(phpString(get(op, "address") ?? ""));
|
|
4098
|
+
const address = asciiUpper(phpTrim2(phpString(get(op, "address") ?? "")));
|
|
3968
4099
|
if (parseAddress(address) === null) {
|
|
3969
4100
|
return sheet;
|
|
3970
4101
|
}
|
|
@@ -4044,6 +4175,9 @@ function shiftColumns(sheet, at2, delta) {
|
|
|
4044
4175
|
if (isArr(get(sheet, "columnWidths"))) {
|
|
4045
4176
|
const widths = /* @__PURE__ */ new Map();
|
|
4046
4177
|
for (const [index, width] of entriesOf(sheet.columnWidths)) {
|
|
4178
|
+
if (!isIndexKey(index)) {
|
|
4179
|
+
continue;
|
|
4180
|
+
}
|
|
4047
4181
|
const i = phpInt2(index);
|
|
4048
4182
|
const number = i + 1;
|
|
4049
4183
|
if (number < at2) {
|
|
@@ -4170,6 +4304,21 @@ var SheetDiff = {
|
|
|
4170
4304
|
* example `columnWidths` decoded from `{"1":140,"0":120}`) as a map, not a
|
|
4171
4305
|
* list, and so as different from the same widths in order. A JS object has
|
|
4172
4306
|
* no such order to compare.
|
|
4307
|
+
*
|
|
4308
|
+
* Throws a `TypeError` on a value JSON cannot hold, as PHP 2.3.2 throws
|
|
4309
|
+
* `JsonException`, rather than calling two such values the same:
|
|
4310
|
+
*
|
|
4311
|
+
* - a non-finite number (PHP: NAN, INF);
|
|
4312
|
+
* - a lone UTF-16 surrogate in a string or a key, which is this runtime's
|
|
4313
|
+
* invalid UTF-8 (a JS string cannot hold invalid UTF-8, and PHP's own
|
|
4314
|
+
* `json_decode` refuses a lone surrogate);
|
|
4315
|
+
* - a function or a symbol, which `JSON.stringify` would silently drop
|
|
4316
|
+
* (a bigint already throws there);
|
|
4317
|
+
* - nesting deeper than PHP's `json_encode` depth of 4096, where every object
|
|
4318
|
+
* and array is a level, an empty one included. The walk is iterative, so
|
|
4319
|
+
* this is PHP's limit and not the call stack's.
|
|
4320
|
+
*
|
|
4321
|
+
* `undefined` stays what JSON makes it: an absent key, and `null` in a list.
|
|
4173
4322
|
*/
|
|
4174
4323
|
same(a, b) {
|
|
4175
4324
|
return canon(a) === canon(b);
|
|
@@ -4421,19 +4570,19 @@ function lines(sheet, axis) {
|
|
|
4421
4570
|
}
|
|
4422
4571
|
const [col, row] = parsed;
|
|
4423
4572
|
const line = axis === "row" ? row : col + 1;
|
|
4424
|
-
const
|
|
4573
|
+
const position2 = axis === "row" ? col : row;
|
|
4425
4574
|
let group = grouped.get(line);
|
|
4426
4575
|
if (group === void 0) {
|
|
4427
4576
|
group = /* @__PURE__ */ new Map();
|
|
4428
4577
|
grouped.set(line, group);
|
|
4429
4578
|
}
|
|
4430
|
-
group.set(
|
|
4579
|
+
group.set(position2, cell);
|
|
4431
4580
|
max = Math.max(max, line);
|
|
4432
4581
|
}
|
|
4433
4582
|
const out = [];
|
|
4434
4583
|
for (let i = 1; i <= max; i++) {
|
|
4435
4584
|
const positions = [...(grouped.get(i) ?? /* @__PURE__ */ new Map()).entries()].sort((x, y) => x[0] - y[0]);
|
|
4436
|
-
out.push(canonEntries(positions.map(([
|
|
4585
|
+
out.push(canonEntries(positions.map(([position2, cell]) => [String(position2), cell])));
|
|
4437
4586
|
}
|
|
4438
4587
|
return out;
|
|
4439
4588
|
}
|
|
@@ -4460,108 +4609,72 @@ function rowMajor(addresses) {
|
|
|
4460
4609
|
return parsed.map(([, , address]) => address);
|
|
4461
4610
|
}
|
|
4462
4611
|
function canon(value) {
|
|
4463
|
-
|
|
4464
|
-
|
|
4612
|
+
return isArr(value) ? canonEntries(entriesFor(value)) : scalar(value);
|
|
4613
|
+
}
|
|
4614
|
+
var JSON_DEPTH = 4096;
|
|
4615
|
+
function canonEntries(root) {
|
|
4616
|
+
const stack = [];
|
|
4617
|
+
const open = (key, entries) => {
|
|
4618
|
+
if (stack.length === JSON_DEPTH) {
|
|
4619
|
+
throw unencodable("Maximum stack depth exceeded");
|
|
4620
|
+
}
|
|
4621
|
+
const list = entries.every(([k], i) => k === String(i));
|
|
4622
|
+
if (!list) {
|
|
4623
|
+
entries = entries.slice().sort((x, y) => x[0] < y[0] ? -1 : x[0] > y[0] ? 1 : 0);
|
|
4624
|
+
}
|
|
4625
|
+
stack.push({ key, entries, list, next: 0, parts: [] });
|
|
4626
|
+
};
|
|
4627
|
+
open("", root);
|
|
4628
|
+
for (; ; ) {
|
|
4629
|
+
const top = stack[stack.length - 1];
|
|
4630
|
+
if (top.next < top.entries.length) {
|
|
4631
|
+
const [key, item] = top.entries[top.next++];
|
|
4632
|
+
if (isArr(item)) {
|
|
4633
|
+
open(key, entriesFor(item));
|
|
4634
|
+
} else {
|
|
4635
|
+
top.parts.push(member(top, key, scalar(item)));
|
|
4636
|
+
}
|
|
4637
|
+
continue;
|
|
4638
|
+
}
|
|
4639
|
+
stack.pop();
|
|
4640
|
+
const text = top.list ? "[" + top.parts.join(",") + "]" : "{" + top.parts.join(",") + "}";
|
|
4641
|
+
const parent = stack[stack.length - 1];
|
|
4642
|
+
if (parent === void 0) {
|
|
4643
|
+
return text;
|
|
4644
|
+
}
|
|
4645
|
+
parent.parts.push(member(parent, top.key, text));
|
|
4465
4646
|
}
|
|
4466
|
-
|
|
4467
|
-
|
|
4647
|
+
}
|
|
4648
|
+
function entriesFor(value) {
|
|
4649
|
+
return Array.isArray(value) ? Array.from(value, (item, i) => [String(i), item]) : entriesOf(value);
|
|
4650
|
+
}
|
|
4651
|
+
function member(frame, key, text) {
|
|
4652
|
+
return frame.list ? text : quote(key) + ":" + text;
|
|
4653
|
+
}
|
|
4654
|
+
function scalar(value) {
|
|
4655
|
+
switch (typeof value) {
|
|
4656
|
+
case "number":
|
|
4657
|
+
if (!Number.isFinite(value)) {
|
|
4658
|
+
throw unencodable("Inf and NaN cannot be JSON encoded");
|
|
4659
|
+
}
|
|
4660
|
+
break;
|
|
4661
|
+
case "string":
|
|
4662
|
+
return quote(value);
|
|
4663
|
+
case "function":
|
|
4664
|
+
case "symbol":
|
|
4665
|
+
throw unencodable(`a ${typeof value} cannot be JSON encoded`);
|
|
4468
4666
|
}
|
|
4469
4667
|
return JSON.stringify(value) ?? "null";
|
|
4470
4668
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4669
|
+
var LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
4670
|
+
function quote(text) {
|
|
4671
|
+
if (LONE_SURROGATE.test(text)) {
|
|
4672
|
+
throw unencodable("Single unpaired UTF-16 surrogate");
|
|
4474
4673
|
}
|
|
4475
|
-
|
|
4476
|
-
return "{" + sorted.map(([key, item]) => JSON.stringify(key) + ":" + canon(item)).join(",") + "}";
|
|
4674
|
+
return JSON.stringify(text);
|
|
4477
4675
|
}
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
var SheetOpSchema = {
|
|
4481
|
-
/** Every op type, in the order the variants are listed. */
|
|
4482
|
-
TYPES: [
|
|
4483
|
-
"set_cell",
|
|
4484
|
-
"set_range",
|
|
4485
|
-
"set_workbook",
|
|
4486
|
-
"clear_cell",
|
|
4487
|
-
"insert_rows",
|
|
4488
|
-
"delete_rows",
|
|
4489
|
-
"insert_columns",
|
|
4490
|
-
"delete_columns",
|
|
4491
|
-
"add_sheet",
|
|
4492
|
-
"remove_sheet",
|
|
4493
|
-
"rename_sheet",
|
|
4494
|
-
"move_sheet",
|
|
4495
|
-
"replace_sheet",
|
|
4496
|
-
"set_merged_regions",
|
|
4497
|
-
"set_column_widths",
|
|
4498
|
-
"set_frozen",
|
|
4499
|
-
"set_meta"
|
|
4500
|
-
],
|
|
4501
|
-
/** A fresh object on every call, so a caller editing it changes nothing here. */
|
|
4502
|
-
jsonSchema() {
|
|
4503
|
-
const address = () => ({ type: "string", pattern: "^[A-Za-z]+[0-9]+$" });
|
|
4504
|
-
const sheet = () => ({ type: "string", minLength: 1 });
|
|
4505
|
-
const count = () => ({ type: "integer", minimum: 1 });
|
|
4506
|
-
const position = () => ({ type: "integer", minimum: 1 });
|
|
4507
|
-
const object = () => ({ type: "object" });
|
|
4508
|
-
const nullableObject = () => ({ type: ["object", "null"] });
|
|
4509
|
-
const value = () => ({ type: ["string", "number", "boolean", "null"] });
|
|
4510
|
-
const variants = [
|
|
4511
|
-
variant(
|
|
4512
|
-
"set_cell",
|
|
4513
|
-
{ sheet: sheet(), address: address(), value: value(), formula: { type: "string" }, computedValue: value(), format: nullableObject(), comment: nullableObject() },
|
|
4514
|
-
["sheet", "address"],
|
|
4515
|
-
"Write one cell. Omitting formula or computedValue clears it; omitting format or comment keeps it; null clears it."
|
|
4516
|
-
),
|
|
4517
|
-
variant(
|
|
4518
|
-
"set_range",
|
|
4519
|
-
{ sheet: sheet(), start: address(), end: address(), values: { type: "array", items: { type: "array", items: value() } } },
|
|
4520
|
-
["sheet", "start", "values"],
|
|
4521
|
-
"Write a block of values row-major from start, each as a set_cell with no formula."
|
|
4522
|
-
),
|
|
4523
|
-
variant("set_workbook", { data: object() }, ["data"], "Replace the whole workbook schema."),
|
|
4524
|
-
variant("clear_cell", { sheet: sheet(), address: address() }, ["sheet", "address"], "Remove one cell."),
|
|
4525
|
-
variant("insert_rows", { sheet: sheet(), at: position(), count: count() }, ["sheet", "at", "count"], "Insert rows before 1-based row `at`; cells, merges below move down."),
|
|
4526
|
-
variant("delete_rows", { sheet: sheet(), at: position(), count: count() }, ["sheet", "at", "count"], "Delete rows starting at 1-based row `at`; cells, merges below move up."),
|
|
4527
|
-
variant("insert_columns", { sheet: sheet(), at: position(), count: count() }, ["sheet", "at", "count"], "Insert columns before 1-based column `at` (A = 1); cells, merges and widths move right."),
|
|
4528
|
-
variant("delete_columns", { sheet: sheet(), at: position(), count: count() }, ["sheet", "at", "count"], "Delete columns starting at 1-based column `at`; cells, merges and widths move left."),
|
|
4529
|
-
variant("add_sheet", { index: { type: "integer", minimum: 0 }, sheet: object() }, ["index", "sheet"], "Insert a sheet at a 0-based position."),
|
|
4530
|
-
variant("remove_sheet", { sheet: sheet() }, ["sheet"], "Remove a sheet by name."),
|
|
4531
|
-
variant("rename_sheet", { sheet: sheet(), name: sheet() }, ["sheet", "name"], "Rename a sheet."),
|
|
4532
|
-
variant("move_sheet", { sheet: sheet(), toIndex: { type: "integer", minimum: 0 } }, ["sheet", "toIndex"], "Move a sheet to a 0-based position."),
|
|
4533
|
-
variant("replace_sheet", { sheet: sheet(), data: object() }, ["sheet", "data"], "Replace one sheet whole."),
|
|
4534
|
-
variant(
|
|
4535
|
-
"set_merged_regions",
|
|
4536
|
-
{ sheet: sheet(), mergedRegions: { type: "array", items: { type: "object", required: ["start", "end"], properties: { start: address(), end: address() } } } },
|
|
4537
|
-
["sheet", "mergedRegions"],
|
|
4538
|
-
"Set every merged region of a sheet."
|
|
4539
|
-
),
|
|
4540
|
-
variant("set_column_widths", { sheet: sheet(), columnWidths: object() }, ["sheet", "columnWidths"], "Set every column width of a sheet (0-based column index to pixels)."),
|
|
4541
|
-
variant(
|
|
4542
|
-
"set_frozen",
|
|
4543
|
-
{ sheet: sheet(), rows: { type: "integer", minimum: 0 }, cols: { type: "integer", minimum: 0 } },
|
|
4544
|
-
["sheet", "rows", "cols"],
|
|
4545
|
-
"Set frozen rows and columns."
|
|
4546
|
-
),
|
|
4547
|
-
variant("set_meta", { meta: nullableObject() }, ["meta"], "Replace the workbook meta, or remove it with null.")
|
|
4548
|
-
];
|
|
4549
|
-
return {
|
|
4550
|
-
$schema: "http://json-schema.org/draft-07/schema#",
|
|
4551
|
-
title: "Holy Sheet op",
|
|
4552
|
-
description: "One op from Agent::diff, applied by Agent::reduce.",
|
|
4553
|
-
oneOf: variants
|
|
4554
|
-
};
|
|
4555
|
-
}
|
|
4556
|
-
};
|
|
4557
|
-
function variant(type, properties, required, description) {
|
|
4558
|
-
return {
|
|
4559
|
-
type: "object",
|
|
4560
|
-
description,
|
|
4561
|
-
required: ["type", ...required],
|
|
4562
|
-
additionalProperties: false,
|
|
4563
|
-
properties: { type: { const: type }, ...properties }
|
|
4564
|
-
};
|
|
4676
|
+
function unencodable(reason) {
|
|
4677
|
+
return new TypeError(`[holy-sheet] cannot compare a value JSON cannot hold: ${reason}`);
|
|
4565
4678
|
}
|
|
4566
4679
|
|
|
4567
4680
|
// src/holy-sheet.schema.json
|
|
@@ -4928,7 +5041,7 @@ var holy_sheet_schema_default = {
|
|
|
4928
5041
|
};
|
|
4929
5042
|
|
|
4930
5043
|
// src/agent.ts
|
|
4931
|
-
var VERSION = "2.4.
|
|
5044
|
+
var VERSION = "2.4.2";
|
|
4932
5045
|
function toU8(input) {
|
|
4933
5046
|
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
4934
5047
|
}
|