@particle-academy/holy-sheet 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +550 -0
- package/dist/index.cjs +103 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +103 -61
- package/dist/index.js.map +1 -1
- package/package.json +67 -66
package/dist/index.d.cts
CHANGED
|
@@ -223,7 +223,7 @@ interface SetMetaOp {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/** This package's own version, pinned to package.json by `version.test.ts`. */
|
|
226
|
-
declare const VERSION = "2.
|
|
226
|
+
declare const VERSION = "2.6.0";
|
|
227
227
|
type Any$6 = any;
|
|
228
228
|
/**
|
|
229
229
|
* Agent — the structured-tool surface for Holy Sheet. Mirrors PHP `Agent`.
|
|
@@ -295,7 +295,7 @@ type Any$5 = any;
|
|
|
295
295
|
* over `Agent`. Provided for parity / DI ergonomics; most callers use `Agent`.
|
|
296
296
|
*/
|
|
297
297
|
declare class HolySheet {
|
|
298
|
-
static readonly VERSION = "2.
|
|
298
|
+
static readonly VERSION = "2.6.0";
|
|
299
299
|
validate(schema: Any$5): ValidationError[];
|
|
300
300
|
toBytes(schema: Any$5): Uint8Array;
|
|
301
301
|
write(schema: Any$5, path: string): Promise<WriteResult>;
|
package/dist/index.d.ts
CHANGED
|
@@ -223,7 +223,7 @@ interface SetMetaOp {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/** This package's own version, pinned to package.json by `version.test.ts`. */
|
|
226
|
-
declare const VERSION = "2.
|
|
226
|
+
declare const VERSION = "2.6.0";
|
|
227
227
|
type Any$6 = any;
|
|
228
228
|
/**
|
|
229
229
|
* Agent — the structured-tool surface for Holy Sheet. Mirrors PHP `Agent`.
|
|
@@ -295,7 +295,7 @@ type Any$5 = any;
|
|
|
295
295
|
* over `Agent`. Provided for parity / DI ergonomics; most callers use `Agent`.
|
|
296
296
|
*/
|
|
297
297
|
declare class HolySheet {
|
|
298
|
-
static readonly VERSION = "2.
|
|
298
|
+
static readonly VERSION = "2.6.0";
|
|
299
299
|
validate(schema: Any$5): ValidationError[];
|
|
300
300
|
toBytes(schema: Any$5): Uint8Array;
|
|
301
301
|
write(schema: Any$5, path: string): Promise<WriteResult>;
|
package/dist/index.js
CHANGED
|
@@ -493,6 +493,54 @@ var DateConverter = {
|
|
|
493
493
|
}
|
|
494
494
|
};
|
|
495
495
|
|
|
496
|
+
// src/exceptions.ts
|
|
497
|
+
var SchemaException = class _SchemaException extends Error {
|
|
498
|
+
constructor(errors, message) {
|
|
499
|
+
super(message ?? summarize(errors));
|
|
500
|
+
this.name = "SchemaException";
|
|
501
|
+
this.errors = errors;
|
|
502
|
+
Object.setPrototypeOf(this, _SchemaException.prototype);
|
|
503
|
+
}
|
|
504
|
+
static fromErrors(errors) {
|
|
505
|
+
return new _SchemaException(errors);
|
|
506
|
+
}
|
|
507
|
+
getErrors() {
|
|
508
|
+
return this.errors;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
function summarize(errors) {
|
|
512
|
+
if (errors.length === 1) {
|
|
513
|
+
const e = errors[0];
|
|
514
|
+
return `[holy-sheet] schema invalid at ${e.path}: expected ${e.expected}, got ${e.got}`;
|
|
515
|
+
}
|
|
516
|
+
const first2 = errors[0];
|
|
517
|
+
const rest = errors.length - 1;
|
|
518
|
+
return `[holy-sheet] schema invalid at ${first2.path}: expected ${first2.expected}, got ${first2.got} (+${rest} more)`;
|
|
519
|
+
}
|
|
520
|
+
var UnsupportedFormatException = class _UnsupportedFormatException extends Error {
|
|
521
|
+
constructor(message, path = null, mimetype = null) {
|
|
522
|
+
super(message);
|
|
523
|
+
this.name = "UnsupportedFormatException";
|
|
524
|
+
this.path = path;
|
|
525
|
+
this.mimetype = mimetype;
|
|
526
|
+
Object.setPrototypeOf(this, _UnsupportedFormatException.prototype);
|
|
527
|
+
}
|
|
528
|
+
static notAZip(path = null) {
|
|
529
|
+
return new _UnsupportedFormatException(
|
|
530
|
+
`[holy-sheet] cannot read ${path ?? "this file"}: it is not a zip archive, and xlsx and ods both are`,
|
|
531
|
+
path
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
static unknownPackage(path, mimetype) {
|
|
535
|
+
const what = mimetype !== null && mimetype !== "" ? `its mimetype is ${mimetype}` : "it has neither xl/workbook.xml nor an OpenDocument mimetype";
|
|
536
|
+
return new _UnsupportedFormatException(
|
|
537
|
+
`[holy-sheet] cannot read ${path ?? "this file"}: ${what}. Supported: xlsx, ods`,
|
|
538
|
+
path,
|
|
539
|
+
mimetype !== "" ? mimetype : null
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
|
|
496
544
|
// src/schema/theme.ts
|
|
497
545
|
var Theme = class {
|
|
498
546
|
constructor(key) {
|
|
@@ -570,7 +618,7 @@ var Normalizer = class {
|
|
|
570
618
|
const columnFormat = columnFormats.get(c) ?? null;
|
|
571
619
|
const rowBand = theme.dataFormat(r);
|
|
572
620
|
const merged = rowBand !== null ? columnFormat ? columnFormat.mergeWith(rowBand) : rowBand : columnFormat;
|
|
573
|
-
cells.set(address, this.buildCell(address, value, merged));
|
|
621
|
+
cells.set(address, this.buildCell(address, value, merged, name));
|
|
574
622
|
});
|
|
575
623
|
});
|
|
576
624
|
if (sheet.totals && isPlainObject(sheet.totals) && Object.keys(sheet.totals).length > 0 && rows.length > 0 && columns.length > 0) {
|
|
@@ -594,7 +642,7 @@ var Normalizer = class {
|
|
|
594
642
|
}
|
|
595
643
|
}
|
|
596
644
|
if (sheet.cells != null) {
|
|
597
|
-
for (const [address, cell] of this.normalizeCellMap(sheet.cells)) {
|
|
645
|
+
for (const [address, cell] of this.normalizeCellMap(sheet.cells, name)) {
|
|
598
646
|
const beneath = cells.get(address);
|
|
599
647
|
cells.set(
|
|
600
648
|
address,
|
|
@@ -618,14 +666,18 @@ var Normalizer = class {
|
|
|
618
666
|
toInt(sheet.frozenCols ?? 0)
|
|
619
667
|
);
|
|
620
668
|
}
|
|
621
|
-
normalizeCellMap(map) {
|
|
669
|
+
normalizeCellMap(map, sheetName2) {
|
|
622
670
|
const cells = /* @__PURE__ */ new Map();
|
|
623
671
|
for (const [address, cellData] of Object.entries(map)) {
|
|
624
|
-
if (!
|
|
672
|
+
if (!isCellSpec(cellData)) {
|
|
625
673
|
const [bare, formula] = promoteFormula(cellData);
|
|
626
674
|
cells.set(
|
|
627
675
|
address,
|
|
628
|
-
new Cell(
|
|
676
|
+
new Cell(
|
|
677
|
+
address,
|
|
678
|
+
formula !== null ? null : this.coerceValue(bare, null, address, sheetName2),
|
|
679
|
+
formula
|
|
680
|
+
)
|
|
629
681
|
);
|
|
630
682
|
continue;
|
|
631
683
|
}
|
|
@@ -640,7 +692,7 @@ var Normalizer = class {
|
|
|
640
692
|
address,
|
|
641
693
|
new Cell(
|
|
642
694
|
address,
|
|
643
|
-
this.coerceValue(rawValue, format),
|
|
695
|
+
this.coerceValue(rawValue, format, address, sheetName2),
|
|
644
696
|
cellData?.formula ?? null,
|
|
645
697
|
format,
|
|
646
698
|
comment,
|
|
@@ -721,8 +773,8 @@ var Normalizer = class {
|
|
|
721
773
|
return null;
|
|
722
774
|
}
|
|
723
775
|
}
|
|
724
|
-
buildCell(address, value, columnFormat) {
|
|
725
|
-
if (
|
|
776
|
+
buildCell(address, value, columnFormat, sheetName2) {
|
|
777
|
+
if (isCellSpec(value)) {
|
|
726
778
|
const cellFormat = isPlainObject(value.format) ? CellFormat.fromInput(value.format) : null;
|
|
727
779
|
const merged = columnFormat ? columnFormat.mergeWith(cellFormat) : cellFormat;
|
|
728
780
|
const comment = isPlainObject(value.comment) ? new CellComment(
|
|
@@ -733,7 +785,7 @@ var Normalizer = class {
|
|
|
733
785
|
const rawValue = value.value ?? null;
|
|
734
786
|
return new Cell(
|
|
735
787
|
address,
|
|
736
|
-
this.coerceValue(rawValue, merged),
|
|
788
|
+
this.coerceValue(rawValue, merged, address, sheetName2),
|
|
737
789
|
value.formula ?? null,
|
|
738
790
|
merged,
|
|
739
791
|
comment,
|
|
@@ -743,12 +795,12 @@ var Normalizer = class {
|
|
|
743
795
|
const [bare, formula] = promoteFormula(value);
|
|
744
796
|
return new Cell(
|
|
745
797
|
address,
|
|
746
|
-
formula !== null ? null : this.coerceValue(bare, columnFormat),
|
|
798
|
+
formula !== null ? null : this.coerceValue(bare, columnFormat, address, sheetName2),
|
|
747
799
|
formula,
|
|
748
800
|
columnFormat
|
|
749
801
|
);
|
|
750
802
|
}
|
|
751
|
-
coerceValue(value, format) {
|
|
803
|
+
coerceValue(value, format, address = "?", sheetName2) {
|
|
752
804
|
if (value === null || value === void 0) return null;
|
|
753
805
|
const df = format?.displayFormat;
|
|
754
806
|
if (df === "date" || df === "datetime") {
|
|
@@ -762,12 +814,36 @@ var Normalizer = class {
|
|
|
762
814
|
if (value instanceof Date) {
|
|
763
815
|
return DateConverter.toSerial(value, true);
|
|
764
816
|
}
|
|
765
|
-
if (typeof value
|
|
817
|
+
if (typeof value === "bigint") {
|
|
818
|
+
return String(value);
|
|
819
|
+
}
|
|
820
|
+
if (typeof value === "object" && !Array.isArray(value) && hasOwnToString(value)) {
|
|
766
821
|
return String(value);
|
|
767
822
|
}
|
|
823
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
824
|
+
const where = sheetName2 === void 0 ? address : `${sheetName2}!${address}`;
|
|
825
|
+
const got = typeOf(value);
|
|
826
|
+
throw SchemaException.fromErrors([
|
|
827
|
+
{
|
|
828
|
+
path: where,
|
|
829
|
+
expected: "a string, number, bool, null, Date, or an object with its own toString",
|
|
830
|
+
got,
|
|
831
|
+
value,
|
|
832
|
+
hint: Array.isArray(value) ? 'This cell holds an array. The usual cause is a `rows` entry nested one level too deep: `rows: [[["x"]]]` makes cell A1 an array, where `rows: [["x"]]` makes it the string "x". For a cell with formatting use the object form: {value: "x", format: {...}}.' : "Give the cell a scalar or a Date, or define toString() on the object so the conversion is yours rather than implicit."
|
|
833
|
+
}
|
|
834
|
+
]);
|
|
835
|
+
}
|
|
768
836
|
return value;
|
|
769
837
|
}
|
|
770
838
|
};
|
|
839
|
+
function isCellSpec(value) {
|
|
840
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
841
|
+
return Object.getPrototypeOf(value) === Object.prototype;
|
|
842
|
+
}
|
|
843
|
+
function hasOwnToString(value) {
|
|
844
|
+
const fn = value.toString;
|
|
845
|
+
return typeof fn === "function" && fn !== Object.prototype.toString;
|
|
846
|
+
}
|
|
771
847
|
function promoteFormula(value) {
|
|
772
848
|
if (typeof value === "string" && value.length > 1 && value[0] === "=") {
|
|
773
849
|
return [null, value.slice(1)];
|
|
@@ -1299,54 +1375,6 @@ function phpRound(x, n) {
|
|
|
1299
1375
|
return Math.sign(x) * Math.round(Math.abs(x) * factor) / factor;
|
|
1300
1376
|
}
|
|
1301
1377
|
|
|
1302
|
-
// src/exceptions.ts
|
|
1303
|
-
var SchemaException = class _SchemaException extends Error {
|
|
1304
|
-
constructor(errors, message) {
|
|
1305
|
-
super(message ?? summarize(errors));
|
|
1306
|
-
this.name = "SchemaException";
|
|
1307
|
-
this.errors = errors;
|
|
1308
|
-
Object.setPrototypeOf(this, _SchemaException.prototype);
|
|
1309
|
-
}
|
|
1310
|
-
static fromErrors(errors) {
|
|
1311
|
-
return new _SchemaException(errors);
|
|
1312
|
-
}
|
|
1313
|
-
getErrors() {
|
|
1314
|
-
return this.errors;
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
function summarize(errors) {
|
|
1318
|
-
if (errors.length === 1) {
|
|
1319
|
-
const e = errors[0];
|
|
1320
|
-
return `[holy-sheet] schema invalid at ${e.path}: expected ${e.expected}, got ${e.got}`;
|
|
1321
|
-
}
|
|
1322
|
-
const first2 = errors[0];
|
|
1323
|
-
const rest = errors.length - 1;
|
|
1324
|
-
return `[holy-sheet] schema invalid at ${first2.path}: expected ${first2.expected}, got ${first2.got} (+${rest} more)`;
|
|
1325
|
-
}
|
|
1326
|
-
var UnsupportedFormatException = class _UnsupportedFormatException extends Error {
|
|
1327
|
-
constructor(message, path = null, mimetype = null) {
|
|
1328
|
-
super(message);
|
|
1329
|
-
this.name = "UnsupportedFormatException";
|
|
1330
|
-
this.path = path;
|
|
1331
|
-
this.mimetype = mimetype;
|
|
1332
|
-
Object.setPrototypeOf(this, _UnsupportedFormatException.prototype);
|
|
1333
|
-
}
|
|
1334
|
-
static notAZip(path = null) {
|
|
1335
|
-
return new _UnsupportedFormatException(
|
|
1336
|
-
`[holy-sheet] cannot read ${path ?? "this file"}: it is not a zip archive, and xlsx and ods both are`,
|
|
1337
|
-
path
|
|
1338
|
-
);
|
|
1339
|
-
}
|
|
1340
|
-
static unknownPackage(path, mimetype) {
|
|
1341
|
-
const what = mimetype !== null && mimetype !== "" ? `its mimetype is ${mimetype}` : "it has neither xl/workbook.xml nor an OpenDocument mimetype";
|
|
1342
|
-
return new _UnsupportedFormatException(
|
|
1343
|
-
`[holy-sheet] cannot read ${path ?? "this file"}: ${what}. Supported: xlsx, ods`,
|
|
1344
|
-
path,
|
|
1345
|
-
mimetype !== "" ? mimetype : null
|
|
1346
|
-
);
|
|
1347
|
-
}
|
|
1348
|
-
};
|
|
1349
|
-
|
|
1350
1378
|
// src/schema/repairer.ts
|
|
1351
1379
|
var VALID_THEMES = ["default", "minimal", "plain", "business"];
|
|
1352
1380
|
var ISO_DATE2 = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?(\.\d+)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
@@ -1593,7 +1621,21 @@ var Validator = class {
|
|
|
1593
1621
|
"Each row is an array of cell values, in column order."
|
|
1594
1622
|
)
|
|
1595
1623
|
);
|
|
1624
|
+
return;
|
|
1596
1625
|
}
|
|
1626
|
+
row.forEach((cell, k) => {
|
|
1627
|
+
if (Array.isArray(cell)) {
|
|
1628
|
+
errors.push(
|
|
1629
|
+
error(
|
|
1630
|
+
`${path}.rows[${j}][${k}]`,
|
|
1631
|
+
"a cell value or a cell object",
|
|
1632
|
+
"array",
|
|
1633
|
+
cell,
|
|
1634
|
+
'This cell is an array, which usually means the row is nested one level too deep: `rows: [[["x"]]]` makes cell A1 an array, where `rows: [["x"]]` makes it "x". For a cell with formatting use the object form: {value: "x", format: {...}}.'
|
|
1635
|
+
)
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1597
1639
|
});
|
|
1598
1640
|
}
|
|
1599
1641
|
}
|
|
@@ -5177,7 +5219,7 @@ var holy_sheet_schema_default = {
|
|
|
5177
5219
|
};
|
|
5178
5220
|
|
|
5179
5221
|
// src/agent.ts
|
|
5180
|
-
var VERSION = "2.
|
|
5222
|
+
var VERSION = "2.6.0";
|
|
5181
5223
|
function toU8(input) {
|
|
5182
5224
|
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
5183
5225
|
}
|