@particle-academy/holy-sheet 2.3.0 → 2.3.1
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 +48 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +48 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -103,7 +103,7 @@ interface BuilderOptions {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/** This package's own version, pinned to package.json by `version.test.ts`. */
|
|
106
|
-
declare const VERSION = "2.3.
|
|
106
|
+
declare const VERSION = "2.3.1";
|
|
107
107
|
type Any$5 = any;
|
|
108
108
|
/**
|
|
109
109
|
* Agent — the structured-tool surface for Holy Sheet. Mirrors PHP `Agent`.
|
|
@@ -144,7 +144,7 @@ type Any$4 = any;
|
|
|
144
144
|
* over `Agent`. Provided for parity / DI ergonomics; most callers use `Agent`.
|
|
145
145
|
*/
|
|
146
146
|
declare class HolySheet {
|
|
147
|
-
static readonly VERSION = "2.3.
|
|
147
|
+
static readonly VERSION = "2.3.1";
|
|
148
148
|
validate(schema: Any$4): ValidationError[];
|
|
149
149
|
toBytes(schema: Any$4): Uint8Array;
|
|
150
150
|
write(schema: Any$4, path: string): Promise<WriteResult>;
|
|
@@ -322,6 +322,12 @@ declare class Normalizer {
|
|
|
322
322
|
declare class FormulaLinter {
|
|
323
323
|
private index;
|
|
324
324
|
private cache;
|
|
325
|
+
/**
|
|
326
|
+
* Every sheet name, keyed by its lower-cased form. Excel matches sheet names
|
|
327
|
+
* without regard to case, and a reference to a sheet that is not here is
|
|
328
|
+
* `#REF!` rather than a range of blanks.
|
|
329
|
+
*/
|
|
330
|
+
private sheetNames;
|
|
325
331
|
lint(schema: unknown): FormulaProblem[];
|
|
326
332
|
private buildIndex;
|
|
327
333
|
private isError;
|
|
@@ -337,6 +343,10 @@ declare class FormulaLinter {
|
|
|
337
343
|
private parsePrimary;
|
|
338
344
|
private resolveRefOrRange;
|
|
339
345
|
private cleanRef;
|
|
346
|
+
/**
|
|
347
|
+
* Unwrap the single quotes Excel puts around a sheet name that needs them,
|
|
348
|
+
* and turn its doubled quote back into one: 'Q3 ''Final''' is Q3 'Final'.
|
|
349
|
+
*/
|
|
340
350
|
private cleanSheetName;
|
|
341
351
|
private resolveCell;
|
|
342
352
|
private resolveRange;
|
|
@@ -353,6 +363,7 @@ declare class FormulaLinter {
|
|
|
353
363
|
private minMax;
|
|
354
364
|
private ifFn;
|
|
355
365
|
private hint;
|
|
366
|
+
private hintRef;
|
|
356
367
|
private hintValue;
|
|
357
368
|
}
|
|
358
369
|
|
package/dist/index.d.ts
CHANGED
|
@@ -103,7 +103,7 @@ interface BuilderOptions {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/** This package's own version, pinned to package.json by `version.test.ts`. */
|
|
106
|
-
declare const VERSION = "2.3.
|
|
106
|
+
declare const VERSION = "2.3.1";
|
|
107
107
|
type Any$5 = any;
|
|
108
108
|
/**
|
|
109
109
|
* Agent — the structured-tool surface for Holy Sheet. Mirrors PHP `Agent`.
|
|
@@ -144,7 +144,7 @@ type Any$4 = any;
|
|
|
144
144
|
* over `Agent`. Provided for parity / DI ergonomics; most callers use `Agent`.
|
|
145
145
|
*/
|
|
146
146
|
declare class HolySheet {
|
|
147
|
-
static readonly VERSION = "2.3.
|
|
147
|
+
static readonly VERSION = "2.3.1";
|
|
148
148
|
validate(schema: Any$4): ValidationError[];
|
|
149
149
|
toBytes(schema: Any$4): Uint8Array;
|
|
150
150
|
write(schema: Any$4, path: string): Promise<WriteResult>;
|
|
@@ -322,6 +322,12 @@ declare class Normalizer {
|
|
|
322
322
|
declare class FormulaLinter {
|
|
323
323
|
private index;
|
|
324
324
|
private cache;
|
|
325
|
+
/**
|
|
326
|
+
* Every sheet name, keyed by its lower-cased form. Excel matches sheet names
|
|
327
|
+
* without regard to case, and a reference to a sheet that is not here is
|
|
328
|
+
* `#REF!` rather than a range of blanks.
|
|
329
|
+
*/
|
|
330
|
+
private sheetNames;
|
|
325
331
|
lint(schema: unknown): FormulaProblem[];
|
|
326
332
|
private buildIndex;
|
|
327
333
|
private isError;
|
|
@@ -337,6 +343,10 @@ declare class FormulaLinter {
|
|
|
337
343
|
private parsePrimary;
|
|
338
344
|
private resolveRefOrRange;
|
|
339
345
|
private cleanRef;
|
|
346
|
+
/**
|
|
347
|
+
* Unwrap the single quotes Excel puts around a sheet name that needs them,
|
|
348
|
+
* and turn its doubled quote back into one: 'Q3 ''Final''' is Q3 'Final'.
|
|
349
|
+
*/
|
|
340
350
|
private cleanSheetName;
|
|
341
351
|
private resolveCell;
|
|
342
352
|
private resolveRange;
|
|
@@ -353,6 +363,7 @@ declare class FormulaLinter {
|
|
|
353
363
|
private minMax;
|
|
354
364
|
private ifFn;
|
|
355
365
|
private hint;
|
|
366
|
+
private hintRef;
|
|
356
367
|
private hintValue;
|
|
357
368
|
}
|
|
358
369
|
|
package/dist/index.js
CHANGED
|
@@ -719,6 +719,7 @@ var ERR_REF = "#REF!";
|
|
|
719
719
|
var ERR_NAME = "#NAME?";
|
|
720
720
|
var ERR_DIV0 = "#DIV/0!";
|
|
721
721
|
var ERR_CIRC = "#CIRC!";
|
|
722
|
+
var SHEET_QUALIFIER = "('(?:[^']|'')+'|[A-Za-z_][A-Za-z0-9_.]*)!";
|
|
722
723
|
var LinterError = class extends Error {
|
|
723
724
|
constructor(errorCode) {
|
|
724
725
|
super(errorCode);
|
|
@@ -729,9 +730,16 @@ var FormulaLinter = class {
|
|
|
729
730
|
constructor() {
|
|
730
731
|
this.index = /* @__PURE__ */ new Map();
|
|
731
732
|
this.cache = /* @__PURE__ */ new Map();
|
|
733
|
+
/**
|
|
734
|
+
* Every sheet name, keyed by its lower-cased form. Excel matches sheet names
|
|
735
|
+
* without regard to case, and a reference to a sheet that is not here is
|
|
736
|
+
* `#REF!` rather than a range of blanks.
|
|
737
|
+
*/
|
|
738
|
+
this.sheetNames = /* @__PURE__ */ new Map();
|
|
732
739
|
}
|
|
733
740
|
lint(schema) {
|
|
734
741
|
const workbook = new Normalizer().normalize(schema);
|
|
742
|
+
this.sheetNames = new Map(workbook.sheets.map((sheet) => [sheet.name.toLowerCase(), sheet.name]));
|
|
735
743
|
this.index = this.buildIndex(workbook);
|
|
736
744
|
this.cache = /* @__PURE__ */ new Map();
|
|
737
745
|
const issues = [];
|
|
@@ -807,6 +815,24 @@ var FormulaLinter = class {
|
|
|
807
815
|
tokens.push({ type: "STRING", value });
|
|
808
816
|
continue;
|
|
809
817
|
}
|
|
818
|
+
if (ch === "'") {
|
|
819
|
+
const start = i;
|
|
820
|
+
i++;
|
|
821
|
+
for (; ; ) {
|
|
822
|
+
if (i >= len) throw new LinterError(ERR_NAME);
|
|
823
|
+
if (src[i] === "'") {
|
|
824
|
+
if (i + 1 < len && src[i + 1] === "'") {
|
|
825
|
+
i += 2;
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
i++;
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
831
|
+
i++;
|
|
832
|
+
}
|
|
833
|
+
tokens.push({ type: "SHEET", value: src.slice(start, i) });
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
810
836
|
if (isAlpha(ch) || ch === "_" || ch === "$") {
|
|
811
837
|
const start = i;
|
|
812
838
|
while (i < len && (isAlnum(src[i]) || src[i] === "_" || src[i] === "$" || src[i] === ".")) i++;
|
|
@@ -924,16 +950,18 @@ var FormulaLinter = class {
|
|
|
924
950
|
c.pos++;
|
|
925
951
|
return tok.value;
|
|
926
952
|
}
|
|
927
|
-
if (tok.type === "IDENT") {
|
|
953
|
+
if (tok.type === "SHEET" || tok.type === "IDENT") {
|
|
928
954
|
const next = c.tokens[c.pos + 1];
|
|
929
955
|
const next2 = c.tokens[c.pos + 2];
|
|
930
956
|
if (next && next.type === "OP" && next.value === "!" && next2 && next2.type === "IDENT") {
|
|
931
|
-
const sheetName = this.cleanSheetName(tok.value);
|
|
957
|
+
const sheetName = this.sheetNames.get(this.cleanSheetName(tok.value).toLowerCase());
|
|
958
|
+
if (sheetName === void 0) throw new LinterError(ERR_REF);
|
|
932
959
|
c.pos += 2;
|
|
933
960
|
const startTok = c.tokens[c.pos].value;
|
|
934
961
|
c.pos++;
|
|
935
962
|
return this.resolveRefOrRange(startTok, sheetName, c, stack);
|
|
936
963
|
}
|
|
964
|
+
if (tok.type === "SHEET") throw new LinterError(ERR_NAME);
|
|
937
965
|
if (next && next.type === "OP" && next.value === "(") {
|
|
938
966
|
const name = tok.value.toUpperCase();
|
|
939
967
|
c.pos += 2;
|
|
@@ -978,9 +1006,13 @@ var FormulaLinter = class {
|
|
|
978
1006
|
const r = ref.replace(/\$/g, "").toUpperCase();
|
|
979
1007
|
return /^[A-Z]+\d+$/.test(r) ? r : null;
|
|
980
1008
|
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Unwrap the single quotes Excel puts around a sheet name that needs them,
|
|
1011
|
+
* and turn its doubled quote back into one: 'Q3 ''Final''' is Q3 'Final'.
|
|
1012
|
+
*/
|
|
981
1013
|
cleanSheetName(name) {
|
|
982
1014
|
if (name.length >= 2 && name[0] === "'" && name[name.length - 1] === "'") {
|
|
983
|
-
return name.slice(1, -1);
|
|
1015
|
+
return name.slice(1, -1).split("''").join("'");
|
|
984
1016
|
}
|
|
985
1017
|
return name;
|
|
986
1018
|
}
|
|
@@ -1148,7 +1180,7 @@ var FormulaLinter = class {
|
|
|
1148
1180
|
case ERR_VALUE:
|
|
1149
1181
|
return this.hintValue(formula, sheet);
|
|
1150
1182
|
case ERR_REF:
|
|
1151
|
-
return
|
|
1183
|
+
return this.hintRef(formula);
|
|
1152
1184
|
case ERR_NAME:
|
|
1153
1185
|
return "The formula references an unknown function or has a syntax error. Holy Sheet supports: SUM, AVERAGE, COUNT, COUNTA, MIN, MAX, IF, ROUND, ABS, LEN, UPPER, LOWER, CONCAT.";
|
|
1154
1186
|
case ERR_DIV0:
|
|
@@ -1159,11 +1191,20 @@ var FormulaLinter = class {
|
|
|
1159
1191
|
return "Formula evaluation failed.";
|
|
1160
1192
|
}
|
|
1161
1193
|
}
|
|
1194
|
+
hintRef(formula) {
|
|
1195
|
+
for (const m of formula.matchAll(new RegExp(SHEET_QUALIFIER, "g"))) {
|
|
1196
|
+
const name = this.cleanSheetName(m[1]);
|
|
1197
|
+
if (!this.sheetNames.has(name.toLowerCase())) {
|
|
1198
|
+
return `The formula refers to a sheet named '${name}', and this workbook has no such sheet. Its sheets are: ${[...this.sheetNames.values()].join(", ")}. Quote a name that contains spaces or punctuation: 'My Sheet'!A1.`;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return "A cell reference points to a cell that doesn't exist in the workbook. Check column letters and row numbers.";
|
|
1202
|
+
}
|
|
1162
1203
|
hintValue(formula, sheet) {
|
|
1163
|
-
const re =
|
|
1204
|
+
const re = new RegExp(`(?:${SHEET_QUALIFIER})?\\$?([A-Z]+)\\$?(\\d+)`, "gi");
|
|
1164
1205
|
const offenders = [];
|
|
1165
1206
|
for (const m of formula.matchAll(re)) {
|
|
1166
|
-
const sheetName = m[1] ? m[1] : sheet;
|
|
1207
|
+
const sheetName = m[1] ? this.sheetNames.get(this.cleanSheetName(m[1]).toLowerCase()) ?? this.cleanSheetName(m[1]) : sheet;
|
|
1167
1208
|
const a1 = m[2].toUpperCase() + m[3];
|
|
1168
1209
|
const key = sheetName + "!" + a1;
|
|
1169
1210
|
const cell = this.index.get(key);
|
|
@@ -4113,7 +4154,7 @@ var holy_sheet_schema_default = {
|
|
|
4113
4154
|
};
|
|
4114
4155
|
|
|
4115
4156
|
// src/agent.ts
|
|
4116
|
-
var VERSION = "2.3.
|
|
4157
|
+
var VERSION = "2.3.1";
|
|
4117
4158
|
function toU8(input) {
|
|
4118
4159
|
return input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
4119
4160
|
}
|