@sheetxl/primitives 0.8.0-beta.12 → 0.8.0-beta.14
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/browser/esm/index.mjs +71 -38
- package/node/esm/index.mjs +85 -50
- package/package.json +1 -1
- package/types/src/ErrorPolicy.d.ts +2 -2
- package/types/src/Flatten.d.ts +2 -2
- package/types/src/FormulaContext.d.ts +6 -6
- package/types/src/FormulaError.d.ts +1 -1
- package/types/src/IFormulaContext.d.ts +104 -20
- package/types/src/IFunction.d.ts +10 -11
- package/types/src/IFunctionArguments.d.ts +3 -4
- package/types/src/IRange.d.ts +4 -4
- package/types/src/IRangeStats.d.ts +6 -6
- package/types/src/Repeating.d.ts +3 -3
- package/types/src/ReportType.d.ts +2 -2
- package/types/src/Scalar.d.ts +26 -2
- package/types/src/ValueKind.d.ts +2 -2
- package/types/src/WildCard.d.ts +1 -1
package/browser/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license @sheetxl/primitives - Primitives - Base types for SheetXL functions. - v0.8.0-beta.
|
|
2
|
+
* @license @sheetxl/primitives - Primitives - Base types for SheetXL functions. - v0.8.0-beta.14
|
|
3
3
|
*
|
|
4
4
|
* (C) 2026-present SheetXL Inc. & Michael T. Ford
|
|
5
5
|
* License: The license can be found at https://www.sheetxl.com/license.
|
|
@@ -21,6 +21,39 @@ var e;
|
|
|
21
21
|
}
|
|
22
22
|
return i === null ? e : i.join("");
|
|
23
23
|
};
|
|
24
|
+
let n = e.MIN_NUMBER = 22250738585072014e-324;
|
|
25
|
+
e.flushSubnormal = function(e) {
|
|
26
|
+
return e > -n && e < n ? 0 : e;
|
|
27
|
+
}, e.isNumberTextInRange = function(e, t = ".") {
|
|
28
|
+
let n = e.length;
|
|
29
|
+
if (n < 309 && e.indexOf("E") < 0 && e.indexOf("e") < 0) return !0;
|
|
30
|
+
let r = t.charCodeAt(0), i = !1, a = !1, o = !1, s = 0, c = 0, l = 0, u = 0;
|
|
31
|
+
for (; u < n; u++) {
|
|
32
|
+
let t = e.charCodeAt(u);
|
|
33
|
+
if (t >= 48 && t <= 57) i = !0, a ? (l++, o || (t === 48 ? c++ : o = !0)) : (o || t !== 48) && (o = !0, s++);
|
|
34
|
+
else if (t === r) a = !0;
|
|
35
|
+
else if ((t === 69 || t === 101) && i) break;
|
|
36
|
+
}
|
|
37
|
+
let d = 0;
|
|
38
|
+
if (u < n) {
|
|
39
|
+
let t = u + 1, r = !1, i = e.charCodeAt(t);
|
|
40
|
+
i === 45 ? (r = !0, t++) : i === 43 && t++;
|
|
41
|
+
let a = 0;
|
|
42
|
+
for (; t < n; t++) {
|
|
43
|
+
let n = e.charCodeAt(t);
|
|
44
|
+
if (n < 48 || n > 57) break;
|
|
45
|
+
d = 10 * d + (n - 48), a++;
|
|
46
|
+
}
|
|
47
|
+
if (a === 0) return !0;
|
|
48
|
+
r && (d = -d);
|
|
49
|
+
}
|
|
50
|
+
if (o) {
|
|
51
|
+
let e = (s > 0 ? s - 1 : -(c + 1)) + d;
|
|
52
|
+
return e >= -309 && e <= 307;
|
|
53
|
+
}
|
|
54
|
+
let f = d - l;
|
|
55
|
+
return f >= -308 && f <= 308;
|
|
56
|
+
};
|
|
24
57
|
})(e ||= {});
|
|
25
58
|
var t, n, r, i, a = {
|
|
26
59
|
Number: "number",
|
|
@@ -370,34 +403,34 @@ var v = class {
|
|
|
370
403
|
}
|
|
371
404
|
}, y = class {
|
|
372
405
|
createRange(e, t, n) {
|
|
373
|
-
throw
|
|
406
|
+
throw x("createRange");
|
|
374
407
|
}
|
|
375
408
|
flattenAreas(e) {
|
|
376
|
-
throw
|
|
409
|
+
throw x("flattenAreas");
|
|
377
410
|
}
|
|
378
411
|
getReference(e, t) {
|
|
379
|
-
throw
|
|
412
|
+
throw x("getReference");
|
|
380
413
|
}
|
|
381
414
|
getResizedReference(e, t, n) {
|
|
382
|
-
throw
|
|
415
|
+
throw x("getResizedReference");
|
|
383
416
|
}
|
|
384
417
|
getNumberFormat(e, t) {
|
|
385
|
-
throw
|
|
418
|
+
throw x("getNumberFormat");
|
|
386
419
|
}
|
|
387
420
|
fromSerialDate(e, t) {
|
|
388
|
-
throw
|
|
421
|
+
throw x("fromSerialDate");
|
|
389
422
|
}
|
|
390
423
|
toSerialDate(e, t) {
|
|
391
|
-
throw
|
|
424
|
+
throw x("toSerialDate");
|
|
392
425
|
}
|
|
393
426
|
getDateParts(e, t) {
|
|
394
|
-
throw
|
|
427
|
+
throw x("getDateParts");
|
|
395
428
|
}
|
|
396
429
|
getSerialDate(e, t, n, r, i, a, o) {
|
|
397
|
-
throw
|
|
430
|
+
throw x("getSerialDate");
|
|
398
431
|
}
|
|
399
432
|
getNow() {
|
|
400
|
-
throw
|
|
433
|
+
throw x("getNow");
|
|
401
434
|
}
|
|
402
435
|
getRandom() {
|
|
403
436
|
return Math.random();
|
|
@@ -406,10 +439,10 @@ var v = class {
|
|
|
406
439
|
return null;
|
|
407
440
|
}
|
|
408
441
|
getEntireCoords() {
|
|
409
|
-
return
|
|
442
|
+
return T;
|
|
410
443
|
}
|
|
411
444
|
isValidDate(e, t) {
|
|
412
|
-
return e instanceof Date || Object.prototype.toString.call(e) === "[object Date]" ? !isNaN(e.getTime()) : !t && typeof e == "number" && !(e < 0 || e >
|
|
445
|
+
return e instanceof Date || Object.prototype.toString.call(e) === "[object Date]" ? !isNaN(e.getTime()) : !t && typeof e == "number" && !(e < 0 || e > D);
|
|
413
446
|
}
|
|
414
447
|
getRow() {
|
|
415
448
|
return 0;
|
|
@@ -445,22 +478,22 @@ var v = class {
|
|
|
445
478
|
return null;
|
|
446
479
|
}
|
|
447
480
|
getAddress(e, t) {
|
|
448
|
-
throw
|
|
481
|
+
throw x("getAddress");
|
|
449
482
|
}
|
|
450
483
|
parseAsSerialDate(e, t) {
|
|
451
|
-
throw
|
|
484
|
+
throw x("parseAsSerialDate");
|
|
452
485
|
}
|
|
453
486
|
parseAsDate(e, t) {
|
|
454
|
-
throw
|
|
487
|
+
throw x("parseAsDate");
|
|
455
488
|
}
|
|
456
489
|
parseAsNumber(e, t) {
|
|
457
|
-
throw
|
|
490
|
+
throw x("parseAsNumber");
|
|
458
491
|
}
|
|
459
492
|
createCriteria(e, t) {
|
|
460
|
-
throw
|
|
493
|
+
throw x("createCriteria");
|
|
461
494
|
}
|
|
462
495
|
createGrouping(e, t) {
|
|
463
|
-
throw
|
|
496
|
+
throw x("createGrouping");
|
|
464
497
|
}
|
|
465
498
|
parseAsLiteral(e) {
|
|
466
499
|
let t = e.toUpperCase();
|
|
@@ -475,13 +508,13 @@ var v = class {
|
|
|
475
508
|
return r === i ? e < t ? -1 : +(e > t) : r < i ? -1 : 1;
|
|
476
509
|
}
|
|
477
510
|
getRuntime() {
|
|
478
|
-
return
|
|
511
|
+
return E;
|
|
479
512
|
}
|
|
480
513
|
getArgs() {
|
|
481
|
-
throw new
|
|
514
|
+
throw new w(S);
|
|
482
515
|
}
|
|
483
516
|
createRichData(e, t) {
|
|
484
|
-
throw new C
|
|
517
|
+
throw new w(C);
|
|
485
518
|
}
|
|
486
519
|
}, b = (e) => {
|
|
487
520
|
if (e == null) return 0;
|
|
@@ -491,19 +524,19 @@ var v = class {
|
|
|
491
524
|
case "boolean": return 3;
|
|
492
525
|
default: return 4;
|
|
493
526
|
}
|
|
494
|
-
}, x = "FormulaContext.getArgs() is only valid while a function body is running",
|
|
527
|
+
}, x = (e) => new w(`FormulaContext.${e}() is only valid while a function body is running. A deferred executor reads its workbook through ctx.getFormulaContext().`), S = "FormulaContext.getArgs() is only valid while a function body is running", C = "FormulaContext.createRichData() needs a workbook being calculated", w = class extends Error {
|
|
495
528
|
constructor(e = "Not implemented in StubbedFormulaContext") {
|
|
496
529
|
super(e), this.name = "NotImplementedError";
|
|
497
530
|
}
|
|
498
|
-
},
|
|
531
|
+
}, T = Object.freeze({
|
|
499
532
|
colStart: 0,
|
|
500
533
|
rowStart: 0,
|
|
501
534
|
colEnd: 2 ** 14 - 1,
|
|
502
535
|
rowEnd: 2 ** 20 - 1
|
|
503
|
-
}),
|
|
536
|
+
}), E = {
|
|
504
537
|
isDate1904: () => !1,
|
|
505
|
-
getLocale: () =>
|
|
506
|
-
getLocaleProfile: () =>
|
|
538
|
+
getLocale: () => A.tag,
|
|
539
|
+
getLocaleProfile: () => A,
|
|
507
540
|
isR1C1: () => !1,
|
|
508
541
|
getNow: () => 0,
|
|
509
542
|
getRandom: () => Math.random(),
|
|
@@ -512,12 +545,12 @@ var v = class {
|
|
|
512
545
|
getOS: () => "unknown",
|
|
513
546
|
getUser: () => "unidentified",
|
|
514
547
|
getDescription: () => "Stubbed Formula Context"
|
|
515
|
-
},
|
|
548
|
+
}, D = 2958465, O = new y(), k = class {
|
|
516
549
|
constructor() {
|
|
517
|
-
this._scope =
|
|
550
|
+
this._scope = O, this._argsFn = null;
|
|
518
551
|
}
|
|
519
552
|
_setScope(e) {
|
|
520
|
-
this._scope = e ??
|
|
553
|
+
this._scope = e ?? O;
|
|
521
554
|
}
|
|
522
555
|
_getScope() {
|
|
523
556
|
return this._scope;
|
|
@@ -529,7 +562,7 @@ var v = class {
|
|
|
529
562
|
return this._getScope().getNumberFormat(e, t);
|
|
530
563
|
}
|
|
531
564
|
getEntireCoords() {
|
|
532
|
-
return this._getScope().getEntireCoords() ??
|
|
565
|
+
return this._getScope().getEntireCoords() ?? T;
|
|
533
566
|
}
|
|
534
567
|
parseAsSerialDate(e, t) {
|
|
535
568
|
return this._getScope().parseAsSerialDate(e, t);
|
|
@@ -548,7 +581,7 @@ var v = class {
|
|
|
548
581
|
}
|
|
549
582
|
parseAsLiteral(e) {
|
|
550
583
|
let t = this._getScope();
|
|
551
|
-
return t.parseAsLiteral ? t.parseAsLiteral(e) :
|
|
584
|
+
return t.parseAsLiteral ? t.parseAsLiteral(e) : O.parseAsLiteral(e);
|
|
552
585
|
}
|
|
553
586
|
toText(e) {
|
|
554
587
|
return this._getScope().toText(e);
|
|
@@ -638,18 +671,18 @@ var v = class {
|
|
|
638
671
|
return this._getScope().getAddress(e, t);
|
|
639
672
|
}
|
|
640
673
|
compare(e, t, n) {
|
|
641
|
-
return this._getScope().compare?.(e, t, n) ??
|
|
674
|
+
return this._getScope().compare?.(e, t, n) ?? O.compare(e, t, n);
|
|
642
675
|
}
|
|
643
676
|
getRuntime() {
|
|
644
677
|
return this._getScope().getRuntime();
|
|
645
678
|
}
|
|
646
679
|
createRichData(e, t) {
|
|
647
680
|
let n = this._getScope();
|
|
648
|
-
return n.createRichData ? n.createRichData(e, t) :
|
|
681
|
+
return n.createRichData ? n.createRichData(e, t) : O.createRichData(e, t);
|
|
649
682
|
}
|
|
650
683
|
getArgs() {
|
|
651
684
|
let e = this._argsFn, t = e ? e() : null;
|
|
652
|
-
if (!t) throw new
|
|
685
|
+
if (!t) throw new w(S);
|
|
653
686
|
return t;
|
|
654
687
|
}
|
|
655
688
|
get [Symbol.toStringTag]() {
|
|
@@ -658,7 +691,7 @@ var v = class {
|
|
|
658
691
|
toString() {
|
|
659
692
|
return this._getScope()?.toString?.() ?? "{STUB SCOPE}";
|
|
660
693
|
}
|
|
661
|
-
},
|
|
694
|
+
}, A = Object.freeze({
|
|
662
695
|
tag: "en-US",
|
|
663
696
|
numberingSystem: "latn",
|
|
664
697
|
decimal: ".",
|
|
@@ -738,5 +771,5 @@ var v = class {
|
|
|
738
771
|
sensitivity: "base",
|
|
739
772
|
numeric: !0
|
|
740
773
|
})
|
|
741
|
-
}),
|
|
742
|
-
export {
|
|
774
|
+
}), j = new k();
|
|
775
|
+
export { A as DEFAULT_LOCALE_PROFILE, j as FormulaContext, i as FormulaError, t as IRange, r as IRangeStats, o as InteropScalarType, v as Observable, e as Scalar, a as ScalarType, k as ScopedFormulaContext, y as StubbedFormulaContext, s as ValueKind, u as WildCard, d as isLazyValue };
|
package/node/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license @sheetxl/primitives - Primitives - Base types for SheetXL functions. - v0.8.0-beta.
|
|
2
|
+
* @license @sheetxl/primitives - Primitives - Base types for SheetXL functions. - v0.8.0-beta.14
|
|
3
3
|
*
|
|
4
4
|
* (C) 2026-present SheetXL Inc. & Michael T. Ford
|
|
5
5
|
* License: The license can be found at https://www.sheetxl.com/license.
|
|
@@ -21,6 +21,41 @@ var e;
|
|
|
21
21
|
}
|
|
22
22
|
return null === n ? e : n.join("");
|
|
23
23
|
};
|
|
24
|
+
const r = e.MIN_NUMBER = 22250738585072014e-324;
|
|
25
|
+
e.flushSubnormal = function(e) {
|
|
26
|
+
return e > -r && e < r ? 0 : e;
|
|
27
|
+
}, e.isNumberTextInRange = function(e, t = ".") {
|
|
28
|
+
const r = e.length;
|
|
29
|
+
if (r < 309 && e.indexOf("E") < 0 && e.indexOf("e") < 0) return !0;
|
|
30
|
+
const o = t.charCodeAt(0);
|
|
31
|
+
let n = !1, s = !1, a = !1, i = 0, c = 0, u = 0, l = 0;
|
|
32
|
+
for (; l < r; l++) {
|
|
33
|
+
const t = e.charCodeAt(l);
|
|
34
|
+
if (t >= 48 && t <= 57) n = !0, s ? (u++, a || (48 === t ? c++ : a = !0)) : (a || 48 !== t) && (a = !0, i++);
|
|
35
|
+
else if (t === o) s = !0;
|
|
36
|
+
else if ((69 === t || 101 === t) && n) break;
|
|
37
|
+
}
|
|
38
|
+
let d = 0;
|
|
39
|
+
if (l < r) {
|
|
40
|
+
let t = l + 1, o = !1;
|
|
41
|
+
const n = e.charCodeAt(t);
|
|
42
|
+
45 === n ? (o = !0, t++) : 43 === n && t++;
|
|
43
|
+
let s = 0;
|
|
44
|
+
for (; t < r; t++) {
|
|
45
|
+
const r = e.charCodeAt(t);
|
|
46
|
+
if (r < 48 || r > 57) break;
|
|
47
|
+
d = 10 * d + (r - 48), s++;
|
|
48
|
+
}
|
|
49
|
+
if (0 === s) return !0;
|
|
50
|
+
o && (d = -d);
|
|
51
|
+
}
|
|
52
|
+
if (a) {
|
|
53
|
+
const e = (i > 0 ? i - 1 : -(c + 1)) + d;
|
|
54
|
+
return e >= -309 && e <= 307;
|
|
55
|
+
}
|
|
56
|
+
const g = d - u;
|
|
57
|
+
return g >= -308 && g <= 308;
|
|
58
|
+
};
|
|
24
59
|
})(e || (e = {}));
|
|
25
60
|
var t, r, o, n, s = {
|
|
26
61
|
Number: "number",
|
|
@@ -270,7 +305,7 @@ var t, r, o, n, s = {
|
|
|
270
305
|
}
|
|
271
306
|
}
|
|
272
307
|
e.Unknown = C;
|
|
273
|
-
class
|
|
308
|
+
class f extends o {
|
|
274
309
|
constructor(e, o) {
|
|
275
310
|
super(t.Field, r.Field, e, o);
|
|
276
311
|
}
|
|
@@ -278,8 +313,8 @@ var t, r, o, n, s = {
|
|
|
278
313
|
this.code = n.Code.Field;
|
|
279
314
|
}
|
|
280
315
|
}
|
|
281
|
-
e.Field =
|
|
282
|
-
class
|
|
316
|
+
e.Field = f;
|
|
317
|
+
class b extends o {
|
|
283
318
|
constructor(e, o) {
|
|
284
319
|
super(t.Calc, r.Calc, e, o);
|
|
285
320
|
}
|
|
@@ -287,8 +322,8 @@ var t, r, o, n, s = {
|
|
|
287
322
|
this.code = n.Code.Calc;
|
|
288
323
|
}
|
|
289
324
|
}
|
|
290
|
-
e.Calc =
|
|
291
|
-
class
|
|
325
|
+
e.Calc = b;
|
|
326
|
+
class A extends o {
|
|
292
327
|
constructor(e, o) {
|
|
293
328
|
super(t.Busy, r.Busy, e, o);
|
|
294
329
|
}
|
|
@@ -296,7 +331,7 @@ var t, r, o, n, s = {
|
|
|
296
331
|
this.code = n.Code.Busy;
|
|
297
332
|
}
|
|
298
333
|
}
|
|
299
|
-
e.Busy =
|
|
334
|
+
e.Busy = A;
|
|
300
335
|
class N extends o {
|
|
301
336
|
constructor(e, o) {
|
|
302
337
|
super(t.Python, r.Python, e, o);
|
|
@@ -306,7 +341,7 @@ var t, r, o, n, s = {
|
|
|
306
341
|
}
|
|
307
342
|
}
|
|
308
343
|
e.Python = N;
|
|
309
|
-
class
|
|
344
|
+
class D extends o {
|
|
310
345
|
constructor(e, o) {
|
|
311
346
|
super(t.Timeout, r.Timeout, e, o);
|
|
312
347
|
}
|
|
@@ -314,7 +349,7 @@ var t, r, o, n, s = {
|
|
|
314
349
|
this.code = n.Code.Timeout;
|
|
315
350
|
}
|
|
316
351
|
}
|
|
317
|
-
e.Timeout =
|
|
352
|
+
e.Timeout = D;
|
|
318
353
|
}(n || (n = {}));
|
|
319
354
|
var g = {};
|
|
320
355
|
g[n.Code.Parse] = n.Parse, g[n.Code.Null] = n.Null, g[n.Code.Div0] = n.Div0, g[n.Code.Value] = n.Value, g[n.Code.Ref] = n.Ref, g[n.Code.Name] = n.Name, g[n.Code.Num] = n.Num, g[n.Code.NA] = n.NA, g[n.Code.GettingData] = n.GettingData, g[n.Code.Spill] = n.Spill, g[n.Code.Connect] = n.Connect, g[n.Code.Blocked] = n.Blocked, g[n.Code.Unknown] = n.Unknown, g[n.Code.Field] = n.Field, g[n.Code.Calc] = n.Calc, g[n.Code.Busy] = n.Busy, g[n.Code.Python] = n.Python, g[n.Code.Timeout] = n.Timeout;
|
|
@@ -351,7 +386,7 @@ for (let e = 0; e < C.length; e++) {
|
|
|
351
386
|
return !!e && !0 === e.isFormulaError && (void 0 === t || e.getCode() === t);
|
|
352
387
|
};
|
|
353
388
|
})(n || (n = {}));
|
|
354
|
-
var
|
|
389
|
+
var f = class {
|
|
355
390
|
constructor(e) {
|
|
356
391
|
this._subscribeFn = e;
|
|
357
392
|
}
|
|
@@ -371,36 +406,36 @@ var w = class {
|
|
|
371
406
|
"function" == typeof n && n();
|
|
372
407
|
}, o;
|
|
373
408
|
}
|
|
374
|
-
},
|
|
409
|
+
}, b = class {
|
|
375
410
|
createRange(e, t, r) {
|
|
376
|
-
throw
|
|
411
|
+
throw N("createRange");
|
|
377
412
|
}
|
|
378
413
|
flattenAreas(e) {
|
|
379
|
-
throw
|
|
414
|
+
throw N("flattenAreas");
|
|
380
415
|
}
|
|
381
416
|
getReference(e, t) {
|
|
382
|
-
throw
|
|
417
|
+
throw N("getReference");
|
|
383
418
|
}
|
|
384
419
|
getResizedReference(e, t, r) {
|
|
385
|
-
throw
|
|
420
|
+
throw N("getResizedReference");
|
|
386
421
|
}
|
|
387
422
|
getNumberFormat(e, t) {
|
|
388
|
-
throw
|
|
423
|
+
throw N("getNumberFormat");
|
|
389
424
|
}
|
|
390
425
|
fromSerialDate(e, t) {
|
|
391
|
-
throw
|
|
426
|
+
throw N("fromSerialDate");
|
|
392
427
|
}
|
|
393
428
|
toSerialDate(e, t) {
|
|
394
|
-
throw
|
|
429
|
+
throw N("toSerialDate");
|
|
395
430
|
}
|
|
396
431
|
getDateParts(e, t) {
|
|
397
|
-
throw
|
|
432
|
+
throw N("getDateParts");
|
|
398
433
|
}
|
|
399
434
|
getSerialDate(e, t, r, o, n, s, a) {
|
|
400
|
-
throw
|
|
435
|
+
throw N("getSerialDate");
|
|
401
436
|
}
|
|
402
437
|
getNow() {
|
|
403
|
-
throw
|
|
438
|
+
throw N("getNow");
|
|
404
439
|
}
|
|
405
440
|
getRandom() {
|
|
406
441
|
return Math.random();
|
|
@@ -409,10 +444,10 @@ var w = class {
|
|
|
409
444
|
return null;
|
|
410
445
|
}
|
|
411
446
|
getEntireCoords() {
|
|
412
|
-
return
|
|
447
|
+
return _;
|
|
413
448
|
}
|
|
414
449
|
isValidDate(e, t) {
|
|
415
|
-
return e instanceof Date || "[object Date]" === Object.prototype.toString.call(e) ? !isNaN(e.getTime()) : !t && "number" == typeof e && !(e < 0 || e >
|
|
450
|
+
return e instanceof Date || "[object Date]" === Object.prototype.toString.call(e) ? !isNaN(e.getTime()) : !t && "number" == typeof e && !(e < 0 || e > x);
|
|
416
451
|
}
|
|
417
452
|
getRow() {
|
|
418
453
|
return 0;
|
|
@@ -448,22 +483,22 @@ var w = class {
|
|
|
448
483
|
return null;
|
|
449
484
|
}
|
|
450
485
|
getAddress(e, t) {
|
|
451
|
-
throw
|
|
486
|
+
throw N("getAddress");
|
|
452
487
|
}
|
|
453
488
|
parseAsSerialDate(e, t) {
|
|
454
|
-
throw
|
|
489
|
+
throw N("parseAsSerialDate");
|
|
455
490
|
}
|
|
456
491
|
parseAsDate(e, t) {
|
|
457
|
-
throw
|
|
492
|
+
throw N("parseAsDate");
|
|
458
493
|
}
|
|
459
494
|
parseAsNumber(e, t) {
|
|
460
|
-
throw
|
|
495
|
+
throw N("parseAsNumber");
|
|
461
496
|
}
|
|
462
497
|
createCriteria(e, t) {
|
|
463
|
-
throw
|
|
498
|
+
throw N("createCriteria");
|
|
464
499
|
}
|
|
465
500
|
createGrouping(e, t) {
|
|
466
|
-
throw
|
|
501
|
+
throw N("createGrouping");
|
|
467
502
|
}
|
|
468
503
|
parseAsLiteral(e) {
|
|
469
504
|
const t = e.toUpperCase();
|
|
@@ -474,19 +509,19 @@ var w = class {
|
|
|
474
509
|
}
|
|
475
510
|
compare(e, t, r) {
|
|
476
511
|
if (e === t) return 0;
|
|
477
|
-
const o =
|
|
512
|
+
const o = A(e), n = A(t);
|
|
478
513
|
return o !== n ? o < n ? -1 : 1 : e < t ? -1 : e > t ? 1 : 0;
|
|
479
514
|
}
|
|
480
515
|
getRuntime() {
|
|
481
|
-
return
|
|
516
|
+
return R;
|
|
482
517
|
}
|
|
483
518
|
getArgs() {
|
|
484
|
-
throw new D
|
|
519
|
+
throw new w(D);
|
|
485
520
|
}
|
|
486
521
|
createRichData(e, t) {
|
|
487
|
-
throw new
|
|
522
|
+
throw new w(y);
|
|
488
523
|
}
|
|
489
|
-
},
|
|
524
|
+
}, A = (e) => {
|
|
490
525
|
if (null == e) return 0;
|
|
491
526
|
switch (typeof e) {
|
|
492
527
|
case "number": return 1;
|
|
@@ -494,19 +529,19 @@ var w = class {
|
|
|
494
529
|
case "boolean": return 3;
|
|
495
530
|
default: return 4;
|
|
496
531
|
}
|
|
497
|
-
}, N = "FormulaContext.getArgs() is only valid while a function body is running",
|
|
532
|
+
}, N = (e) => new w(`FormulaContext.${e}() is only valid while a function body is running. A deferred executor reads its workbook through ctx.getFormulaContext().`), D = "FormulaContext.getArgs() is only valid while a function body is running", y = "FormulaContext.createRichData() needs a workbook being calculated", w = class extends Error {
|
|
498
533
|
constructor(e = "Not implemented in StubbedFormulaContext") {
|
|
499
534
|
super(e), this.name = "NotImplementedError";
|
|
500
535
|
}
|
|
501
|
-
},
|
|
536
|
+
}, _ = Object.freeze({
|
|
502
537
|
colStart: 0,
|
|
503
538
|
rowStart: 0,
|
|
504
539
|
colEnd: Math.pow(2, 14) - 1,
|
|
505
540
|
rowEnd: Math.pow(2, 20) - 1
|
|
506
|
-
}),
|
|
541
|
+
}), R = {
|
|
507
542
|
isDate1904: () => !1,
|
|
508
|
-
getLocale: () =>
|
|
509
|
-
getLocaleProfile: () =>
|
|
543
|
+
getLocale: () => k.tag,
|
|
544
|
+
getLocaleProfile: () => k,
|
|
510
545
|
isR1C1: () => !1,
|
|
511
546
|
getNow: () => 0,
|
|
512
547
|
getRandom: () => Math.random(),
|
|
@@ -515,12 +550,12 @@ var w = class {
|
|
|
515
550
|
getOS: () => "unknown",
|
|
516
551
|
getUser: () => "unidentified",
|
|
517
552
|
getDescription: () => "Stubbed Formula Context"
|
|
518
|
-
},
|
|
553
|
+
}, x = 2958465, F = new b(), T = class {
|
|
519
554
|
constructor() {
|
|
520
|
-
this._scope =
|
|
555
|
+
this._scope = F, this._argsFn = null;
|
|
521
556
|
}
|
|
522
557
|
_setScope(e) {
|
|
523
|
-
this._scope = e ??
|
|
558
|
+
this._scope = e ?? F;
|
|
524
559
|
}
|
|
525
560
|
_getScope() {
|
|
526
561
|
return this._scope;
|
|
@@ -532,7 +567,7 @@ var w = class {
|
|
|
532
567
|
return this._getScope().getNumberFormat(e, t);
|
|
533
568
|
}
|
|
534
569
|
getEntireCoords() {
|
|
535
|
-
return this._getScope().getEntireCoords() ??
|
|
570
|
+
return this._getScope().getEntireCoords() ?? _;
|
|
536
571
|
}
|
|
537
572
|
parseAsSerialDate(e, t) {
|
|
538
573
|
return this._getScope().parseAsSerialDate(e, t);
|
|
@@ -551,7 +586,7 @@ var w = class {
|
|
|
551
586
|
}
|
|
552
587
|
parseAsLiteral(e) {
|
|
553
588
|
const t = this._getScope();
|
|
554
|
-
return t.parseAsLiteral ? t.parseAsLiteral(e) :
|
|
589
|
+
return t.parseAsLiteral ? t.parseAsLiteral(e) : F.parseAsLiteral(e);
|
|
555
590
|
}
|
|
556
591
|
toText(e) {
|
|
557
592
|
return this._getScope().toText(e);
|
|
@@ -641,18 +676,18 @@ var w = class {
|
|
|
641
676
|
return this._getScope().getAddress(e, t);
|
|
642
677
|
}
|
|
643
678
|
compare(e, t, r) {
|
|
644
|
-
return this._getScope().compare?.(e, t, r) ??
|
|
679
|
+
return this._getScope().compare?.(e, t, r) ?? F.compare(e, t, r);
|
|
645
680
|
}
|
|
646
681
|
getRuntime() {
|
|
647
682
|
return this._getScope().getRuntime();
|
|
648
683
|
}
|
|
649
684
|
createRichData(e, t) {
|
|
650
685
|
const r = this._getScope();
|
|
651
|
-
return r.createRichData ? r.createRichData(e, t) :
|
|
686
|
+
return r.createRichData ? r.createRichData(e, t) : F.createRichData(e, t);
|
|
652
687
|
}
|
|
653
688
|
getArgs() {
|
|
654
689
|
const e = this._argsFn, t = e ? e() : null;
|
|
655
|
-
if (!t) throw new D
|
|
690
|
+
if (!t) throw new w(D);
|
|
656
691
|
return t;
|
|
657
692
|
}
|
|
658
693
|
get [Symbol.toStringTag]() {
|
|
@@ -661,7 +696,7 @@ var w = class {
|
|
|
661
696
|
toString() {
|
|
662
697
|
return this._getScope()?.toString?.() ?? "{STUB SCOPE}";
|
|
663
698
|
}
|
|
664
|
-
},
|
|
699
|
+
}, k = Object.freeze({
|
|
665
700
|
tag: "en-US",
|
|
666
701
|
numberingSystem: "latn",
|
|
667
702
|
decimal: ".",
|
|
@@ -741,5 +776,5 @@ var w = class {
|
|
|
741
776
|
sensitivity: "base",
|
|
742
777
|
numeric: !0
|
|
743
778
|
})
|
|
744
|
-
}), P = new
|
|
745
|
-
export {
|
|
779
|
+
}), P = new T();
|
|
780
|
+
export { k as DEFAULT_LOCALE_PROFILE, P as FormulaContext, n as FormulaError, t as IRange, o as IRangeStats, a as InteropScalarType, f as Observable, e as Scalar, s as ScalarType, T as ScopedFormulaContext, b as StubbedFormulaContext, i as ValueKind, l as WildCard, d as isLazyValue };
|
package/package.json
CHANGED
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
* if (range.hasError()) return range.firstError();
|
|
25
25
|
* ```
|
|
26
26
|
*
|
|
27
|
-
* placed before its own validation where
|
|
27
|
+
* placed before its own validation where the member's error outranks it (`LARGE(range-with-#N/A, 0)`
|
|
28
28
|
* is `#N/A`, not `#NUM!`). Aggregators and consumers say it; reshapers and lookups, whose members
|
|
29
29
|
* pass through or are never read, need no line at all. Where a function takes several ranges the
|
|
30
30
|
* lines run in ARGUMENT order — the whole of argument N before N+1, row-major within — which is
|
|
31
|
-
*
|
|
31
|
+
* the which-error-wins order, and a function that pairs its ranges (the paired
|
|
32
32
|
* statistics) orders its own scan the way it pairs.
|
|
33
33
|
*
|
|
34
34
|
* > *A bare error propagates unless your function opts in to seeing it; the members of a range
|
package/types/src/Flatten.d.ts
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* Both are the same mechanism at different granularity — the marshaller picks the unit from the
|
|
20
20
|
* element type — and a blank drops out of either. The scalar case is the common one; the range case
|
|
21
21
|
* exists because a reference UNION is a single argument holding several areas: a parameter that does
|
|
22
|
-
* not declare `Flatten` refuses one outright (`#VALUE!` — `TRANSPOSE((A1:A3,C1:C3))`
|
|
23
|
-
* body that declares it answers for the union itself, in one line, the way
|
|
22
|
+
* not declare `Flatten` refuses one outright (`#VALUE!` — `TRANSPOSE((A1:A3,C1:C3))`), and a
|
|
23
|
+
* body that declares it answers for the union itself, in one line, the way the built-ins do:
|
|
24
24
|
* `AREAS` counts the areas, `SUM` adds them, `ROWS` answers `#REF!` to more than one, the lookups
|
|
25
25
|
* `#N/A`.
|
|
26
26
|
*
|
|
@@ -97,7 +97,7 @@ export declare class StubbedFormulaContext implements IFormulaContext {
|
|
|
97
97
|
* Unimplemented. See {@link IFormulaContext.createGrouping}.
|
|
98
98
|
*
|
|
99
99
|
* @remarks
|
|
100
|
-
* Grouping needs
|
|
100
|
+
* Grouping needs the engine's key equality, which this stub does not have.
|
|
101
101
|
*/
|
|
102
102
|
createGrouping(keys: IRange, options?: IFormulaContext.GroupingOptions): IFormulaContext.Grouping;
|
|
103
103
|
/**
|
|
@@ -115,15 +115,15 @@ export declare class StubbedFormulaContext implements IFormulaContext {
|
|
|
115
115
|
* Renders a scalar as text. See {@link IFormulaContext.toText}.
|
|
116
116
|
*
|
|
117
117
|
* @remarks
|
|
118
|
-
* A BASIC rendering, deliberately NOT
|
|
118
|
+
* A BASIC rendering, deliberately NOT the engine's. The engine's rule -- coercion General (15
|
|
119
119
|
* significant digits, plain decimal while the rendering fits 20 characters, else
|
|
120
120
|
* scientific) written with the workbook LOCALE's decimal separator -- is
|
|
121
121
|
* `formatNumberGeneral` in `@sheetxl/sdk`, and every evaluation path reaches it through
|
|
122
122
|
* the calculation context rather than through this stub. Numbers here get the plain JS
|
|
123
|
-
* rendering, which diverges from
|
|
123
|
+
* rendering, which diverges from the engine on both precision and separator.
|
|
124
124
|
*
|
|
125
125
|
* This exists so authoring, type defs and other non-runtime access get usable text
|
|
126
|
-
* instead of throwing. Anything that needs
|
|
126
|
+
* instead of throwing. Anything that needs the full rule is running in the engine, and
|
|
127
127
|
* the engine never lands here.
|
|
128
128
|
*/
|
|
129
129
|
toText(value: Scalar | undefined): string;
|
|
@@ -131,14 +131,14 @@ export declare class StubbedFormulaContext implements IFormulaContext {
|
|
|
131
131
|
* Orders two scalars. See {@link IFormulaContext.compare}.
|
|
132
132
|
*
|
|
133
133
|
* @remarks
|
|
134
|
-
* A BASIC total order, deliberately NOT
|
|
134
|
+
* A BASIC total order, deliberately NOT the engine's. The engine's rule -- locale collation, the word
|
|
135
135
|
* sort, a blank taking the other operand's type, 11-decimal numeric normalization -- is
|
|
136
136
|
* `ScalarCollation` in `@sheetxl/sdk`, and every evaluation path reaches it through the
|
|
137
137
|
* calculation context rather than through this stub. `options` is ignored: strength is a
|
|
138
138
|
* collation concept and nothing here collates.
|
|
139
139
|
*
|
|
140
140
|
* This exists so authoring, type defs and other non-runtime access get a usable ordering
|
|
141
|
-
* instead of throwing. Anything that needs
|
|
141
|
+
* instead of throwing. Anything that needs the full rule is running in the engine, and the
|
|
142
142
|
* engine never lands here.
|
|
143
143
|
*/
|
|
144
144
|
compare(a: Scalar, b: Scalar, options?: Intl.CollatorOptions): number;
|
|
@@ -20,7 +20,7 @@ import { IReferenceRange } from './IReferenceRange';
|
|
|
20
20
|
*
|
|
21
21
|
* SpillErrorScalarSubType | "Unknown" | "Collision" | "IndeterminateSize" | "WorksheetEdge" | "OutOfMemoryWhileCalc" | "Table" | "MergedCell"
|
|
22
22
|
* # Office 2023
|
|
23
|
-
* 10. #CONNECT! This occurs when a data type is used that is not currently supported
|
|
23
|
+
* 10. #CONNECT! This occurs when a data type is used that is not currently supported.
|
|
24
24
|
* 11. #BLOCKED! Indicates that an action or feature is blocked, possibly due to security settings, protected view, or other restrictions. Often related to online features, external links, or macros.
|
|
25
25
|
* 12. #UNKNOWN! May be returned in the case of a protected workbook.
|
|
26
26
|
* 13. #FIELD! Invalid field name for a data type.
|
|
@@ -67,8 +67,8 @@ export interface IFormulaContext {
|
|
|
67
67
|
* A union `(A1:A5,B1:B2)` reaches a {@link IFunction.ParameterTypeJSON.reportType} slot as one
|
|
68
68
|
* `IRange` per area. Functions that answer over it do so because they walk MEMBERS and a union
|
|
69
69
|
* simply has more of them — `LARGE((DA1:DA5,DK1:DK2),2)` is the second largest of all seven
|
|
70
|
-
* cells. This is that walk, and it is a helper rather than a marshalling step on purpose:
|
|
71
|
-
*
|
|
70
|
+
* cells. This is that walk, and it is a helper rather than a marshalling step on purpose:
|
|
71
|
+
* flattening is a shared reducer the bodies opt into, which is why the
|
|
72
72
|
* same union is flattened by `LARGE`, refused by `SUMPRODUCT` and answered `#REF!` by `ROWS`.
|
|
73
73
|
*
|
|
74
74
|
* The result is a single column in area order, row-major within each area. SHAPE IS NOT
|
|
@@ -117,7 +117,7 @@ export interface IFormulaContext {
|
|
|
117
117
|
*/
|
|
118
118
|
getColumn(): number;
|
|
119
119
|
/**
|
|
120
|
-
* Whether the call being evaluated is in
|
|
120
|
+
* Whether the call being evaluated is in VALUE mode, where a multi-cell reference at a
|
|
121
121
|
* slot that reads ONE value is implicitly intersected with the formula's cell (the `@` rule);
|
|
122
122
|
* `false` is ARRAY mode, where such a slot reads the reference's first cell.
|
|
123
123
|
*
|
|
@@ -156,7 +156,7 @@ export interface IFormulaContext {
|
|
|
156
156
|
* A few arguments honor only their top-left corner and are re-sized to the shape of the range
|
|
157
157
|
* that drives them — `SUMIF`'s `sum_range`, `LOOKUP`'s `result_vector`. The window can therefore
|
|
158
158
|
* run off the bottom or right of the grid: `SUMIF(A1:A5,">0",A1048574)` asks for five rows
|
|
159
|
-
* starting three from the end.
|
|
159
|
+
* starting three from the end. The part that fits is read and the rest is blank, which
|
|
160
160
|
* is what this returns; positions past the clamp read as `undefined` from `getValueAt`.
|
|
161
161
|
*
|
|
162
162
|
* @param anchor The coords of the argument as written; only its top-left corner is honored.
|
|
@@ -560,12 +560,11 @@ export interface IFormulaContext {
|
|
|
560
560
|
* want the raw carry above — `DATEDIF("2020-01-31","2020-03-01","md")` is `-1`
|
|
561
561
|
* because Jan 31 rolls into March. `EDATE`/`EOMONTH` want the opposite and CLAMP to
|
|
562
562
|
* the target month's length, so they pre-clamp before calling here. One conversion,
|
|
563
|
-
* two opposite rules
|
|
563
|
+
* two opposite rules. Baking either one in breaks the other.
|
|
564
564
|
* * **Results may legitimately leave the calendar.** `YEARFRAC` basis 1 anchors a year
|
|
565
565
|
* length on January 1 of `laterYear + 1`, which at the top of the calendar is
|
|
566
|
-
* `10000-01-01`;
|
|
567
|
-
* `
|
|
568
|
-
* here would break it.
|
|
566
|
+
* `10000-01-01`; `YEARFRAC(DATE(9998,1,1),DATE(9999,12,31),1)` is
|
|
567
|
+
* `1.9972602739726026`, not `#NUM!`. A range check here would break it.
|
|
569
568
|
* * **The 16-bit argument limits are per-function.** `DATE`'s month errors outside
|
|
570
569
|
* `[-32767, 32766]` while its day saturates to `32767`, and `TIME` errors outside
|
|
571
570
|
* `[0, 32767]` on every argument — same magic number, three different rules.
|
|
@@ -573,8 +572,8 @@ export interface IFormulaContext {
|
|
|
573
572
|
* Note the asymmetry with {@link getDateParts}, which DOES throw `#NUM!` outside the
|
|
574
573
|
* calendar because it answers a user-facing question. An internal intermediate that
|
|
575
574
|
* steps one day past the end must therefore not be round-tripped through it; compute
|
|
576
|
-
* on the fields instead
|
|
577
|
-
*
|
|
575
|
+
* on the fields instead, or `YEARFRAC(DATE(9999,1,1),DATE(9999,12,31),0)` becomes `#NUM!`
|
|
576
|
+
* rather than `1`.
|
|
578
577
|
*/
|
|
579
578
|
getSerialDate(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millis?: number): number;
|
|
580
579
|
/**
|
|
@@ -590,8 +589,8 @@ export interface IFormulaContext {
|
|
|
590
589
|
*
|
|
591
590
|
* @remarks
|
|
592
591
|
* Argument REFLECTION, for the one thing a declaration cannot say: what happened at *this* call.
|
|
593
|
-
* It exists to serve
|
|
594
|
-
* other
|
|
592
|
+
* It exists to serve a declared `any`, which means "no contract, the body decides" — every
|
|
593
|
+
* other declared type maps onto a type we already have and the marshaller handles it.
|
|
595
594
|
*
|
|
596
595
|
* The returned view is valid ONLY while the body that asked for it is running. Views are pooled
|
|
597
596
|
* by dispatch depth, so retaining one across an `await`, a callback, or a nested evaluation hands
|
|
@@ -610,7 +609,7 @@ export interface IFormulaContext {
|
|
|
610
609
|
* recognizes it: while the call is still waiting, a recalculation with
|
|
611
610
|
* an equal payload leaves it be rather than asking again. Once it has stopped, a recalculation
|
|
612
611
|
* calls the function again; what it fetched is answered from the workbook's cache
|
|
613
|
-
* (`
|
|
612
|
+
* (`IDeferContext.fetch`).
|
|
614
613
|
* @returns The last answer this call gave for the same question, when the function is being
|
|
615
614
|
* called again (a recalculation); `undefined` on a first call or a changed question. Return it
|
|
616
615
|
* to keep showing it until the new answer arrives, or return something else to show that the
|
|
@@ -622,14 +621,15 @@ export interface IFormulaContext {
|
|
|
622
621
|
* from meanwhile: return `#BUSY!` when they should wait as an error, or text such as
|
|
623
622
|
* `'Fetching…'` when that is what they should show.
|
|
624
623
|
*
|
|
625
|
-
* The executor runs after the recalculation, outside any cell
|
|
626
|
-
*
|
|
624
|
+
* The executor runs after the recalculation, outside any cell, so the ambient `FormulaContext`
|
|
625
|
+
* is not its own: it reads the workbook through `ctx.getFormulaContext()`. Whatever the body
|
|
626
|
+
* knows about its cell goes in the payload.
|
|
627
627
|
*
|
|
628
628
|
* @example
|
|
629
629
|
* ```typescript
|
|
630
630
|
* function fetchAll(
|
|
631
631
|
* calls: readonly IFormulaContext.DeferredCall<string>[],
|
|
632
|
-
* ctx: IFormulaContext.
|
|
632
|
+
* ctx: IFormulaContext.IDeferContext
|
|
633
633
|
* ): void {
|
|
634
634
|
* for (const call of calls) {
|
|
635
635
|
* ctx.fetch(call.payload as string)
|
|
@@ -683,7 +683,7 @@ export declare namespace IFormulaContext {
|
|
|
683
683
|
* then `range` is returned as it is, hidden rows included.
|
|
684
684
|
*
|
|
685
685
|
* @remarks
|
|
686
|
-
* This is `SUBTOTAL` 1-11 (
|
|
686
|
+
* This is `SUBTOTAL` 1-11 (they include manually hidden rows, which is what an
|
|
687
687
|
* unfiltered sheet reads as). `SUBTOTAL` 101-111 and `AGGREGATE` leave it off and skip
|
|
688
688
|
* always.
|
|
689
689
|
* @defaultValue false
|
|
@@ -812,7 +812,7 @@ export declare namespace IFormulaContext {
|
|
|
812
812
|
* Answers the calls made by {@link IFormulaContext.defer} against it. Invoked once at the end of
|
|
813
813
|
* a recalculation with every such call; how many requests that becomes is the executor's choice.
|
|
814
814
|
*/
|
|
815
|
-
type DeferExecutor<T = any> = (calls: readonly DeferredCall<T>[], ctx:
|
|
815
|
+
type DeferExecutor<T = any> = (calls: readonly DeferredCall<T>[], ctx: IDeferContext) => void | Promise<void>;
|
|
816
816
|
/**
|
|
817
817
|
* One call made by {@link IFormulaContext.defer}, as its executor receives it.
|
|
818
818
|
*
|
|
@@ -824,7 +824,7 @@ export declare namespace IFormulaContext {
|
|
|
824
824
|
/** What the function passed to `defer`. */
|
|
825
825
|
readonly payload: unknown;
|
|
826
826
|
/** Settles the call with its result. */
|
|
827
|
-
resolve(value: T): void;
|
|
827
|
+
resolve(value: T, options?: DeferredCall.ResolveOptions): void;
|
|
828
828
|
/** Settles the call with an error: a `FormulaError` as itself, anything else as `#VALUE!`. */
|
|
829
829
|
reject(error: unknown): void;
|
|
830
830
|
/** Runs `fn` when the result is no longer wanted. */
|
|
@@ -835,10 +835,29 @@ export declare namespace IFormulaContext {
|
|
|
835
835
|
*/
|
|
836
836
|
readonly cancelled: boolean;
|
|
837
837
|
}
|
|
838
|
+
namespace DeferredCall {
|
|
839
|
+
/**
|
|
840
|
+
* Options for {@link DeferredCall.resolve}.
|
|
841
|
+
*/
|
|
842
|
+
interface ResolveOptions {
|
|
843
|
+
/**
|
|
844
|
+
* Whether the blanks of an array result stay blank when it spills. Omitted, they read as
|
|
845
|
+
* `0`, as any spilled array's blanks do.
|
|
846
|
+
*
|
|
847
|
+
* @remarks
|
|
848
|
+
* For data whose empty fields mean "nothing here", such as an imported file. The result
|
|
849
|
+
* keeps them while a formula passes it on as it is (`IF(c, result, …)`). A function that
|
|
850
|
+
* answers element by element builds a new array, whose blanks read as `0`
|
|
851
|
+
* (`IFERROR(result, …)`, `result & ""`).
|
|
852
|
+
* @defaultValue false
|
|
853
|
+
*/
|
|
854
|
+
keepBlanks?: boolean;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
838
857
|
/**
|
|
839
858
|
* What an executor is given beside its calls.
|
|
840
859
|
*/
|
|
841
|
-
interface
|
|
860
|
+
interface IDeferContext {
|
|
842
861
|
/** A random number in `[0, 1)` from the calculation's source, which a test can seed. */
|
|
843
862
|
getRandom(): number;
|
|
844
863
|
/**
|
|
@@ -846,6 +865,35 @@ export declare namespace IFormulaContext {
|
|
|
846
865
|
* here (maps, numbers, strings, buffers), not connections or sockets.
|
|
847
866
|
*/
|
|
848
867
|
getState<S>(init: () => S): S;
|
|
868
|
+
/**
|
|
869
|
+
* Groups `calls` by payload and runs `answer` once per distinct payload with every call that
|
|
870
|
+
* asked it: one request per question, however many cells asked.
|
|
871
|
+
*
|
|
872
|
+
* @param calls The calls to group, usually every call the executor was handed.
|
|
873
|
+
* @param answer Settles one group. When it throws, or the promise it returns rejects, the
|
|
874
|
+
* group's calls it has not settled fail with that error; the other groups are not affected.
|
|
875
|
+
* @returns Resolves once every group has been answered. It never rejects.
|
|
876
|
+
*
|
|
877
|
+
* @remarks
|
|
878
|
+
* Payloads are equal as `defer` compares them: the same primitive, or plain objects or arrays
|
|
879
|
+
* whose own values are the same one level deep. Groups are answered concurrently, in the
|
|
880
|
+
* order their first call appears.
|
|
881
|
+
*
|
|
882
|
+
* @example
|
|
883
|
+
* ```typescript
|
|
884
|
+
* async function fetchQuotes(
|
|
885
|
+
* calls: readonly IFormulaContext.DeferredCall<number>[],
|
|
886
|
+
* ctx: IFormulaContext.IDeferContext
|
|
887
|
+
* ): Promise<void> {
|
|
888
|
+
* await ctx.byPayload(calls, async (ticker: string, group) => {
|
|
889
|
+
* const response = await ctx.fetch(`https://api.example.com/quote/${ticker}`);
|
|
890
|
+
* const price: number = (await response.json()).price;
|
|
891
|
+
* for (const call of group) call.resolve(price);
|
|
892
|
+
* });
|
|
893
|
+
* }
|
|
894
|
+
* ```
|
|
895
|
+
*/
|
|
896
|
+
byPayload<T = any, P = unknown>(calls: readonly DeferredCall<T>[], answer: (payload: P, group: readonly DeferredCall<T>[]) => void | Promise<void>): Promise<void>;
|
|
849
897
|
/**
|
|
850
898
|
* A rich value of a registered type, for the workbook being calculated: what
|
|
851
899
|
* {@link IFormulaContext.createRichData} makes inside a cell, made here for a call's result.
|
|
@@ -866,8 +914,44 @@ export declare namespace IFormulaContext {
|
|
|
866
914
|
* answer the workbook already has for the URL (a picture it keeps, or anything fetched while
|
|
867
915
|
* it is open) answers from the workbook, and two calls for one URL share one request. In an
|
|
868
916
|
* external calculation (*Refresh All*) it goes to the source.
|
|
917
|
+
*
|
|
918
|
+
* Rejects when the URL cannot be reached, with a `FormulaError.Blocked` when the site responded
|
|
919
|
+
* but the browser would not let the page read it.
|
|
869
920
|
*/
|
|
870
921
|
fetch(input: string, init?: RequestInit): Promise<Response>;
|
|
922
|
+
/**
|
|
923
|
+
* The formula context of the workbook being calculated, with no cell: its parsing, text,
|
|
924
|
+
* comparison and dates, read with the workbook's locale and date system.
|
|
925
|
+
*
|
|
926
|
+
* @remarks
|
|
927
|
+
* Valid for as long as the executor runs, across every `await`. The ambient `FormulaContext`
|
|
928
|
+
* is not: it belongs to the cell being evaluated, and an executor runs after that evaluation
|
|
929
|
+
* ends. What needs a cell (`getRow`, `getReference`, `getValueAt`, `defer`, `markVolatile`,
|
|
930
|
+
* `formatResults`, `getRandom`) throws here; {@link getRandom} is the executor's own.
|
|
931
|
+
*/
|
|
932
|
+
getFormulaContext(): IFormulaContext;
|
|
933
|
+
/**
|
|
934
|
+
* Reads delimited text (CSV, TSV) into rows of values, each field read the way the workbook
|
|
935
|
+
* reads typed entry: numbers, dates, booleans and error labels become values, anything else
|
|
936
|
+
* stays text, and an empty field is blank.
|
|
937
|
+
*
|
|
938
|
+
* @param text The delimited text.
|
|
939
|
+
* @param options How the text is split.
|
|
940
|
+
* @returns The rows, padded with blanks to the widest row.
|
|
941
|
+
*/
|
|
942
|
+
parseText(text: string, options?: IDeferContext.ParseTextOptions): Promise<Scalar[][]>;
|
|
943
|
+
}
|
|
944
|
+
namespace IDeferContext {
|
|
945
|
+
/**
|
|
946
|
+
* Options for {@link IDeferContext.parseText}.
|
|
947
|
+
*/
|
|
948
|
+
interface ParseTextOptions {
|
|
949
|
+
/**
|
|
950
|
+
* The character between fields.
|
|
951
|
+
* @defaultValue `','`
|
|
952
|
+
*/
|
|
953
|
+
delimiter?: string;
|
|
954
|
+
}
|
|
871
955
|
}
|
|
872
956
|
}
|
|
873
957
|
//# sourceMappingURL=IFormulaContext.d.ts.map
|
package/types/src/IFunction.d.ts
CHANGED
|
@@ -304,7 +304,7 @@ export declare namespace IFunction {
|
|
|
304
304
|
*
|
|
305
305
|
* @remarks
|
|
306
306
|
* STATIC, and read from the formula text at compile time: it is true of the call whether or
|
|
307
|
-
* not the call runs, which is what a lazy argument
|
|
307
|
+
* not the call runs, which is what a lazy argument requires.
|
|
308
308
|
*
|
|
309
309
|
* @see {@link ReturnTypeJSON.volatile}
|
|
310
310
|
*/
|
|
@@ -343,7 +343,7 @@ export declare namespace IFunction {
|
|
|
343
343
|
* @remarks
|
|
344
344
|
* Only meaningful for a VARIADIC function; a fixed parameter list is already its own maximum.
|
|
345
345
|
* The number is the whole CALL's argument count, prefix and optional suffix included, and it
|
|
346
|
-
* is refused
|
|
346
|
+
* is refused at entry, before evaluation.
|
|
347
347
|
*
|
|
348
348
|
* @see {@link Repeating}
|
|
349
349
|
*/
|
|
@@ -645,8 +645,8 @@ export declare namespace IFunction {
|
|
|
645
645
|
* Generated from the `NoLift<T>` marker.
|
|
646
646
|
*
|
|
647
647
|
* @remarks
|
|
648
|
-
* Per parameter, because Excel
|
|
649
|
-
* and lifts one at `weekend`. An ARRAY at the slot still lifts, a one-cell reference is an
|
|
648
|
+
* Per parameter, because Excel's built-ins differ by slot: `NETWORKDAYS.INTL` refuses a
|
|
649
|
+
* reference at its date slots and lifts one at `weekend`. An ARRAY at the slot still lifts, a one-cell reference is an
|
|
650
650
|
* ordinary argument, and a repeating slot absorbs a container as members, so the marker only
|
|
651
651
|
* ever applies to a non-repeating scalar parameter.
|
|
652
652
|
*/
|
|
@@ -686,7 +686,7 @@ export declare namespace IFunction {
|
|
|
686
686
|
* union is the range arm, handed over whole. The slot never lifts.
|
|
687
687
|
*
|
|
688
688
|
* @remarks
|
|
689
|
-
*
|
|
689
|
+
* Some array slots read a one-cell argument as THE ARGUMENT rather than as a
|
|
690
690
|
* one-cell container: `SORT(<#N/A cell>,1,1,<#DIV/0! cell>)` is `#N/A` where
|
|
691
691
|
* `TAKE(<#N/A cell>,<#DIV/0! cell>)` is `#DIV/0!`. The engine's default
|
|
692
692
|
* for a range slot is TAKE's; this flag is SORT's. {@link scalar} and {@link range} each carry
|
|
@@ -753,7 +753,7 @@ export declare namespace IFunction {
|
|
|
753
753
|
*
|
|
754
754
|
* @remarks
|
|
755
755
|
* Collected at COMPILE time from the formula text (`CompiledFormula.hasVolatile`), because
|
|
756
|
-
* that is where
|
|
756
|
+
* that is where volatility is decided: `=IF(TRUE,1,RAND())` recalculates on every idle pass though
|
|
757
757
|
* no branch selects the arm, and with lazy arguments the call never happens for the engine to
|
|
758
758
|
* observe.
|
|
759
759
|
*
|
|
@@ -776,11 +776,10 @@ export declare namespace IFunction {
|
|
|
776
776
|
* parameter. Omitted at the default of 255.
|
|
777
777
|
*
|
|
778
778
|
* @remarks
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
* takes 254.
|
|
779
|
+
* The cap is one number per NAME, compared against the whole call's argument count, so a call
|
|
780
|
+
* past it is refused AT ENTRY rather than answered — which is why the cap lives on the
|
|
781
|
+
* declaration and not in a body. It is not derivable from the shape: in Excel `STDEV` takes 255
|
|
782
|
+
* arguments and `STDEV.S`, the same function under its dotted name, takes 254.
|
|
784
783
|
*
|
|
785
784
|
* @defaultValue 255
|
|
786
785
|
*/
|
|
@@ -15,9 +15,8 @@ import { IFunction } from './IFunction';
|
|
|
15
15
|
* - What was the value BEFORE the parameter's type coerced it?
|
|
16
16
|
* - Was that empty slot an omission, or a blank cell the caller pointed at?
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
* parameters
|
|
20
|
-
* declined to express one. `CUMIPMT` refuses a boolean at every slot while `FV` coerces one at
|
|
18
|
+
* Some functions have behaviors that turn on exactly those questions, and they cluster at
|
|
19
|
+
* parameters declared `any` — where there is nothing to express as a type. `CUMIPMT` refuses a boolean at every slot while `FV` coerces one at
|
|
21
20
|
* every slot; the difference is not the values, it is that `CUMIPMT`'s slots are `any` and its
|
|
22
21
|
* body decides.
|
|
23
22
|
*
|
|
@@ -142,7 +141,7 @@ export interface IFunctionArguments {
|
|
|
142
141
|
*/
|
|
143
142
|
getKindAt(index: number): ValueKind;
|
|
144
143
|
/**
|
|
145
|
-
* True when the argument at `index` is DIRECT —
|
|
144
|
+
* True when the argument at `index` is DIRECT — typed into the list of arguments:
|
|
146
145
|
* the evaluated value IS a value, not a container or a callable.
|
|
147
146
|
*
|
|
148
147
|
* @param index The PARAMETER index — the position in the body's own signature.
|
package/types/src/IRange.d.ts
CHANGED
|
@@ -255,7 +255,7 @@ export interface IRange<T extends Scalar = Scalar> {
|
|
|
255
255
|
* @remarks
|
|
256
256
|
* Errors INSIDE a range are the body's to judge: the engine hands a valid range over whole and
|
|
257
257
|
* never looks inside, so a function whose answer depends on every member says so with this
|
|
258
|
-
* line, placed before its own validation where
|
|
258
|
+
* line, placed before its own validation where the member's error outranks it. A reshaper or a
|
|
259
259
|
* lookup, whose members pass through or are never read, needs no line at all.
|
|
260
260
|
*/
|
|
261
261
|
firstError(): FormulaError.Known | null;
|
|
@@ -268,7 +268,7 @@ export interface IRange<T extends Scalar = Scalar> {
|
|
|
268
268
|
* @remarks
|
|
269
269
|
* `options.axis` is the TRAVERSAL order feeding the ordinal, not the orientation of the result:
|
|
270
270
|
* the result is always Nx1, and a row is `flatten().transpose()`. The default is row-major,
|
|
271
|
-
* which is the order
|
|
271
|
+
* which is the order two range operands pair in.
|
|
272
272
|
*
|
|
273
273
|
* `options.type` carries a read type onto the result, so a member the type filters out becomes
|
|
274
274
|
* a HOLE at its ordinal rather than compacting the column. That is what lets two operands be
|
|
@@ -916,7 +916,7 @@ export declare namespace IRange {
|
|
|
916
916
|
*
|
|
917
917
|
* - By default, `broadcast()` follows strict alignment rules like NumPy and will
|
|
918
918
|
* throw an error if the input ranges cannot be broadcasted together.
|
|
919
|
-
* - When `misalignValue` is provided, `broadcast()` will instead
|
|
919
|
+
* - When `misalignValue` is provided, `broadcast()` will instead follow Excel's behavior:
|
|
920
920
|
* - It finds the **largest valid intersection** of the ranges.
|
|
921
921
|
* - It expands the result to the **union of both range sizes**.
|
|
922
922
|
* - Any cells that do not have valid corresponding values from the inputs
|
|
@@ -974,7 +974,7 @@ export declare namespace IRange {
|
|
|
974
974
|
*
|
|
975
975
|
* @remarks
|
|
976
976
|
* Off, a dimension of 1 is a short dimension like any other and pads `misalignValue` past
|
|
977
|
-
* its one position:
|
|
977
|
+
* its one position: the `MAP` rule, where a 1-row array is not stretched.
|
|
978
978
|
*/
|
|
979
979
|
tile?: boolean;
|
|
980
980
|
/**
|
|
@@ -71,7 +71,7 @@ export interface IRangeStats {
|
|
|
71
71
|
*/
|
|
72
72
|
getAggregates(options?: IRange.IteratorOptions): Readonly<IRangeStats.Aggregates>;
|
|
73
73
|
/**
|
|
74
|
-
* Returns the sum as
|
|
74
|
+
* Returns the sum as `SUM` reports it — {@link getAggregates}'s `sum` with Excel's
|
|
75
75
|
* cancellation-to-zero rule applied.
|
|
76
76
|
*
|
|
77
77
|
* @param total A total the caller accumulated across SEVERAL stats instances. Omit for a
|
|
@@ -82,7 +82,7 @@ export interface IRangeStats {
|
|
|
82
82
|
* @remarks
|
|
83
83
|
* Collapses a total to exactly zero when it lands at least 50 binary exponents below the
|
|
84
84
|
* operands that produced it, so `SUM(0.1,0.2,-0.3)` is `0` rather than `5.55111512312578E-17`.
|
|
85
|
-
* Use this wherever you mean "the number
|
|
85
|
+
* Use this wherever you mean "the number SUM shows" — a status bar, a report
|
|
86
86
|
* total, a formula. Use `getAggregates().sum` where you mean the true total of the data.
|
|
87
87
|
*
|
|
88
88
|
* The two genuinely differ, and neither is a rounding of the other:
|
|
@@ -92,12 +92,12 @@ export interface IRangeStats {
|
|
|
92
92
|
* and `PERCENTOF`, and to NOTHING else — `SUMIF`, `SUMPRODUCT`, `SERIESSUM`, `DSUM`, the
|
|
93
93
|
* `SUMX2MY2` family and every binary operator all keep the residue over the same values.
|
|
94
94
|
* That is why this is a method you call and not a change to `getAggregates`.
|
|
95
|
-
* - `AVERAGE` divides THIS, not the raw sum: snapping the mean instead is
|
|
95
|
+
* - `AVERAGE` divides THIS, not the raw sum: snapping the mean instead is wrong.
|
|
96
96
|
*
|
|
97
97
|
* It is also cheap when it does not fire: a bound derived from `min`/`max` rejects any
|
|
98
98
|
* ordinary total without re-reading the range.
|
|
99
99
|
*
|
|
100
|
-
* **Multiple ranges.**
|
|
100
|
+
* **Multiple ranges.** The rule applies on the *final* accumulation step, so a total
|
|
101
101
|
* spanning several ranges must be judged against the range holding the LAST value — pass the
|
|
102
102
|
* running total as `total` and call this on that range. (Snapping each range separately gets
|
|
103
103
|
* `SUM(A1:A3,0)` and `SUM(A1:A1,A2:A2,A3:A3)` wrong in opposite directions.) A composite
|
|
@@ -245,7 +245,7 @@ export declare namespace IRangeStats {
|
|
|
245
245
|
* Sorting requires materializing the full numeric population (O(n log n), one allocation).
|
|
246
246
|
* Unlike `Aggregates`, sorted views are **not composable across sub-ranges**.
|
|
247
247
|
*
|
|
248
|
-
* Covers the following
|
|
248
|
+
* Covers the following functions (all share one sort per coords per calc cycle):
|
|
249
249
|
* - `LARGE`, `SMALL`
|
|
250
250
|
* - `MEDIAN`, `PERCENTILE`, `PERCENTILE.INC`, `PERCENTILE.EXC`
|
|
251
251
|
* - `QUARTILE`, `QUARTILE.INC`, `QUARTILE.EXC` (q1 = k=0.25, q3 = k=0.75)
|
|
@@ -310,7 +310,7 @@ export declare namespace IRangeStats {
|
|
|
310
310
|
* one outside the population is not found.
|
|
311
311
|
* @param exclusive If true, uses exclusive formula (0 and 1 are excluded).
|
|
312
312
|
* @param significance Number of DECIMAL PLACES to TRUNCATE the result to — despite what
|
|
313
|
-
* Excel's
|
|
313
|
+
* Excel's help calls the argument, it is not significant digits and it does not round.
|
|
314
314
|
* Truncated toward zero first (1.9 places means 1), and must be at least 1. Omit it for the
|
|
315
315
|
* exact untruncated rank, which is also the only form that cannot fail.
|
|
316
316
|
* @returns The percent rank of x; `null` if x is not found or there are no numbers in range.
|
package/types/src/Repeating.d.ts
CHANGED
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
* omitted when there is a single repetition. `SORTBY`'s legal arities are 2 and 1+2n and nothing
|
|
47
47
|
* else — `SORTBY(array, by1)` is legal, `SORTBY(array, by1, o1, by2)` is not. It is not a general
|
|
48
48
|
* "the final element of the final group may be omitted" rule; that rule would accept a formula
|
|
49
|
-
*
|
|
49
|
+
* that must be refused. An optional slot anywhere else in the group is a compiler error.
|
|
50
50
|
*
|
|
51
51
|
* ### The maximum, `Max`
|
|
52
52
|
*
|
|
53
|
-
* The second type argument is the largest number of arguments the WHOLE CALL accepts
|
|
54
|
-
*
|
|
53
|
+
* The second type argument is the largest number of arguments the WHOLE CALL accepts: one
|
|
54
|
+
* number per NAME, compared against the call's total argument count and
|
|
55
55
|
* refused at entry (`Excess_function_arguments`), never a count of this parameter's own share.
|
|
56
56
|
* `SWITCH`'s 254 therefore covers its `expression` and its trailing `default` as well as the
|
|
57
57
|
* pairs, and `CHOOSEROWS`' covers its `array`.
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* what can arrive and {@link IFunctionArguments.getAreaCountAt} answers the union question the
|
|
28
28
|
* kind cannot — a plain reference is one area, a union is more.
|
|
29
29
|
*
|
|
30
|
-
* Its use is meant to stay countable: a slot earns it with a
|
|
31
|
-
*
|
|
30
|
+
* Its use is meant to stay countable: a slot earns it with a concrete behavior that needs it,
|
|
31
|
+
* never speculatively.
|
|
32
32
|
*
|
|
33
33
|
* @example
|
|
34
34
|
* ```ts
|
package/types/src/Scalar.d.ts
CHANGED
|
@@ -31,8 +31,8 @@ export declare namespace Scalar {
|
|
|
31
31
|
*/
|
|
32
32
|
const MAX_SIGNIFICANT_DIGITS = 15;
|
|
33
33
|
/**
|
|
34
|
-
* Applies
|
|
35
|
-
* 15th becomes `0`, so the text parses to the double
|
|
34
|
+
* Applies the 15-digit entry rule to numeric text: every significand digit past the
|
|
35
|
+
* 15th becomes `0`, so the text parses to the double a cell stores.
|
|
36
36
|
*
|
|
37
37
|
* @param text Numeric text in any accepted form — signed, grouped, percent,
|
|
38
38
|
* currency-affixed, or scientific. The scan is locale-blind: only digit
|
|
@@ -46,6 +46,30 @@ export declare namespace Scalar {
|
|
|
46
46
|
* truncation, not rounding: `1234567890.123456789` stores `…345`, never `…346`.
|
|
47
47
|
*/
|
|
48
48
|
function truncateNumberText(text: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* The smallest magnitude a stored number can have: the smallest normal double. A smaller one is
|
|
51
|
+
* stored as `0`.
|
|
52
|
+
*/
|
|
53
|
+
const MIN_NUMBER = 2.2250738585072014e-308;
|
|
54
|
+
/**
|
|
55
|
+
* Applies the stored-number floor to a number entering a workbook — typed, parsed from text, written
|
|
56
|
+
* through the API or loaded from a file: a magnitude below {@link MIN_NUMBER} becomes `0`.
|
|
57
|
+
*/
|
|
58
|
+
function flushSubnormal(value: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* Whether numeric text is inside the range accepted as a number, judged from the text as
|
|
61
|
+
* written rather than the double it parses to.
|
|
62
|
+
*
|
|
63
|
+
* @param text Numeric text in any accepted form, as {@link truncateNumberText} takes it. Only
|
|
64
|
+
* digits, `decimal` and an exponent marker are read.
|
|
65
|
+
* @param decimal The decimal separator `text` is written with.
|
|
66
|
+
* @returns `false` when the leading significant digit sits outside `1E-309` … `1E+307`
|
|
67
|
+
* (`1E-310`, `0.00001E-305`, `1E+308`); for a zero mantissa, when its scale — exponent less
|
|
68
|
+
* fraction digits — is outside ±308 (`0E-309`, `0.0E-308` and `0E+309`, but not `0E-308`,
|
|
69
|
+
* `000E-308` or `0.0E+309`). Text out of range is not a number: a formula literal will not
|
|
70
|
+
* commit, `VALUE` is `#VALUE!` and typed entry stays text.
|
|
71
|
+
*/
|
|
72
|
+
function isNumberTextInRange(text: string, decimal?: string): boolean;
|
|
49
73
|
/**
|
|
50
74
|
* Encodings of `ScalarType` for persistence.
|
|
51
75
|
*/
|
package/types/src/ValueKind.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @remarks
|
|
6
6
|
* A value's kind and its extent are different axes: `{10}`, `A1` and a bare `10` all reach a
|
|
7
|
-
* range-typed parameter as a 1x1 container, yet
|
|
7
|
+
* range-typed parameter as a 1x1 container, yet they are distinct:
|
|
8
8
|
* `VLOOKUP(10,10,1,TRUE)` is `#N/A` while `VLOOKUP(10,{10},1,TRUE)` is 10, and `TYPE` answers
|
|
9
9
|
* 1 vs 64. The kind is what the CALLER supplied; the extent is merely its size.
|
|
10
10
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* argument can be: a reference, a non-reference array, a callable, and no argument at all. As an
|
|
14
14
|
* argument kind, {@link ScalarType.Null} means a BLANK value.
|
|
15
15
|
*
|
|
16
|
-
* The names and values are ours. The numeric codes
|
|
16
|
+
* The names and values are ours. The numeric codes the `TYPE` function answers are a
|
|
17
17
|
* PROJECTION of this kind (see `TYPE`'s implementation), not the kind itself.
|
|
18
18
|
*
|
|
19
19
|
* @see {@link IFunctionArguments.getKindAt}
|
package/types/src/WildCard.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const WildCard: {
|
|
|
11
11
|
* Compiles Excel wildcard text into a regex.
|
|
12
12
|
*
|
|
13
13
|
* @param anchor Where the pattern has to line up: `true` for the WHOLE string, `'start'` for a
|
|
14
|
-
* PREFIX, `false` for anywhere in it.
|
|
14
|
+
* PREFIX, `false` for anywhere in it. A criterion matches a whole cell, so every lookup and criteria
|
|
15
15
|
* caller anchors; `SEARCH`, which looks for a substring, does not; and a `D` function's criteria
|
|
16
16
|
* table is the prefix form — `DSUM` with the criterion `appl` takes the row whose value is
|
|
17
17
|
* `Apple`, where `COUNTIF` with the same criterion takes none.
|