@sheetxl/primitives 0.8.0-beta.2 → 0.8.0-beta.20

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.
@@ -5,6 +5,7 @@ import { IReferenceRange } from './IReferenceRange';
5
5
  import { IFunction } from './IFunction';
6
6
  import { IFunctionArguments } from './IFunctionArguments';
7
7
  import { IRuntime } from './IRuntime';
8
+ import { IRichData } from './IRichData';
8
9
  /**
9
10
  * Everything a formula function is allowed to know about the world outside its arguments.
10
11
  *
@@ -28,7 +29,8 @@ import { IRuntime } from './IRuntime';
28
29
  *
29
30
  * | Category | Key methods |
30
31
  * |---|---|
31
- * | Evaluation lifecycle | `markVolatile`, `getRow`, `getColumn`, `getEntireCoords`, `getRandom` |
32
+ * | Evaluation lifecycle | `getRow`, `getColumn`, `getEntireCoords`, `getRandom` |
33
+ * | Answering later | `defer` |
32
34
  * | Argument reflection | `getArgs` |
33
35
  * | Output hints | `formatResults` |
34
36
  * | Range construction | `createRange`, `flattenAreas`, `filterHidden` |
@@ -37,6 +39,7 @@ import { IRuntime } from './IRuntime';
37
39
  * | Dates | `getNow`, `toSerialDate`, `fromSerialDate`, `getDateParts`, `getSerialDate`, `parseAsDate`, `parseAsSerialDate`, `isValidDate` |
38
40
  * | Coercion | `parseAsNumber`, `parseAsLiteral`, `toText`, `compare` |
39
41
  * | Criteria | `createCriteria` |
42
+ * | Grouping | `createPivot`, `getPivotAt` |
40
43
  * | Sheet introspection | `getSheetCount`, `getSheetIndex`, `getAddress`, `getNumberFormat` |
41
44
  *
42
45
  * ### Implementors
@@ -64,8 +67,8 @@ export interface IFormulaContext {
64
67
  * A union `(A1:A5,B1:B2)` reaches a {@link IFunction.ParameterTypeJSON.reportType} slot as one
65
68
  * `IRange` per area. Functions that answer over it do so because they walk MEMBERS and a union
66
69
  * simply has more of them — `LARGE((DA1:DA5,DK1:DK2),2)` is the second largest of all seven
67
- * cells. This is that walk, and it is a helper rather than a marshalling step on purpose: Excel
68
- * flattens in exactly the same place, a shared reducer its bodies opt into, which is why the
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
69
72
  * same union is flattened by `LARGE`, refused by `SUMPRODUCT` and answered `#REF!` by `ROWS`.
70
73
  *
71
74
  * The result is a single column in area order, row-major within each area. SHAPE IS NOT
@@ -114,7 +117,7 @@ export interface IFormulaContext {
114
117
  */
115
118
  getColumn(): number;
116
119
  /**
117
- * Whether the call being evaluated is in Excel's VALUE mode, where a multi-cell reference at a
120
+ * Whether the call being evaluated is in VALUE mode, where a multi-cell reference at a
118
121
  * slot that reads ONE value is implicitly intersected with the formula's cell (the `@` rule);
119
122
  * `false` is ARRAY mode, where such a slot reads the reference's first cell.
120
123
  *
@@ -153,7 +156,7 @@ export interface IFormulaContext {
153
156
  * A few arguments honor only their top-left corner and are re-sized to the shape of the range
154
157
  * that drives them — `SUMIF`'s `sum_range`, `LOOKUP`'s `result_vector`. The window can therefore
155
158
  * run off the bottom or right of the grid: `SUMIF(A1:A5,">0",A1048574)` asks for five rows
156
- * starting three from the end. Excel reads the part that fits and treats the rest as blank, which
159
+ * starting three from the end. The part that fits is read and the rest is blank, which
157
160
  * is what this returns; positions past the clamp read as `undefined` from `getValueAt`.
158
161
  *
159
162
  * @param anchor The coords of the argument as written; only its top-left corner is honored.
@@ -219,16 +222,30 @@ export interface IFormulaContext {
219
222
  */
220
223
  getSheetIndex(sheet?: string | IReferenceRange): number;
221
224
  /**
222
- * Returns a copy of a range with hidden rows or columns removed.
225
+ * Returns a range as it reads without its hidden rows or columns.
223
226
  *
224
227
  * @param range The range to filter.
225
- * @param axes Which axis to filter on. Default is `IRange.AxisSelection.Row`.
226
- * @returns A new range without the hidden rows or columns.
228
+ * @param options See {@link IFormulaContext.FilterHiddenOptions}.
229
+ * @returns `range` itself when nothing in it is hidden; otherwise the same range reading with
230
+ * the hidden rows or columns skipped ({@link IRange.IteratorOptions.processRowSkip}).
227
231
  *
228
232
  * @remarks
229
233
  * Use this for functions that ignore hidden data, such as `SUBTOTAL`.
234
+ *
235
+ * Nothing is copied. The shape of `range` is kept, and a reference stays a reference: a hidden
236
+ * cell is not visited and reads as empty, so every position still means the cell it meant, and
237
+ * an aggregate that ignores blanks ignores it. A computed array has no rows or columns to hide
238
+ * and is returned as it is.
239
+ *
240
+ * A hidden row is a hidden row, whoever hid it: a filter and the user leave the same mark, and
241
+ * nothing here tells them apart. What a filter adds is a state of the range's SHEET, which
242
+ * {@link IFormulaContext.FilterHiddenOptions.onlyWhileFiltered} reads.
243
+ *
244
+ * A formula that calls `SUBTOTAL` or `AGGREGATE` is recalculated when a row or column is hidden
245
+ * or shown, and when a filter is set or cleared. One that reaches this only through a custom
246
+ * function is not: it reads the state when it is next calculated.
230
247
  */
231
- filterHidden(range: IRange, axes?: IRange.AxisSelection): IRange;
248
+ filterHidden(range: IRange, options?: IFormulaContext.FilterHiddenOptions): IRange;
232
249
  /**
233
250
  * Returns a copy of a range with any cell whose formula is a nested total removed.
234
251
  *
@@ -260,14 +277,6 @@ export interface IFormulaContext {
260
277
  * chosen one. When a range is returned every cell takes the same format.
261
278
  */
262
279
  formatResults(numFmt: string): void;
263
- /**
264
- * Marks this function as volatile, so it re-evaluates on every calculation.
265
- *
266
- * @remarks
267
- * Call this when the result depends on something outside the arguments, such as the clock.
268
- * A function can also be declared volatile in its registration instead.
269
- */
270
- markVolatile(): void;
271
280
  /**
272
281
  * Coerces a value to a serial date.
273
282
  *
@@ -333,6 +342,51 @@ export interface IFormulaContext {
333
342
  * ```
334
343
  */
335
344
  createCriteria(criteria: Scalar, options?: IFormulaContext.CriteriaOptions): IFormulaContext.CriteriaEvaluator;
345
+ /**
346
+ * Groups the records of a range two ways at once — by row keys and by column keys — at every
347
+ * key level of each, and crosses every row group with every column group.
348
+ *
349
+ * @param rows The row key columns, or `null` for none: every record is then in the one row group.
350
+ * @param columns The column key columns, or `null` for none.
351
+ * @param options Which records take part. See {@link IFormulaContext.PivotOptions}.
352
+ * @returns The two axes of groups and what their crossings hold.
353
+ * @throws `#VALUE!` for more than 32 key columns on one axis, or more groups and crossings than
354
+ * the pivot can number.
355
+ *
356
+ * @remarks
357
+ * Every row is a record, blank rows included. Two keys are equal by the rule `UNIQUE` uses: text
358
+ * ignores case, and types never mix, so `1`, `"1"` and `TRUE` are three groups. A blank key is a
359
+ * key of its own, distinct from `""`, and an error is a key like any other value.
360
+ *
361
+ * Each axis is a tree: its root is every kept record, the root's children are the groups of the
362
+ * first key column, theirs add the second, and so on, each in the order its first record appears.
363
+ * A subtotal is then one group, never a union of its children.
364
+ *
365
+ * One pivot answers what a grouping per key level, and another per pair of levels, would — in
366
+ * one pass, which is what `PIVOTBY` and a pivot table need.
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * const pivot = FormulaContext.createPivot(regions, years);
371
+ * for (const region of pivot.getRows().getRoot().getChildren()) {
372
+ * const total = pivot.getMembers(sales, region); // every year
373
+ * const [name] = region.getKeys();
374
+ * }
375
+ * ```
376
+ */
377
+ createPivot(rows: IRange | null, columns: IRange | null, options?: IFormulaContext.PivotOptions): IFormulaContext.Pivot;
378
+ /**
379
+ * The pivot table a reference touches, as the values it shows.
380
+ *
381
+ * @param reference Any cell or range of the table, its page fields included.
382
+ * @returns The view, or `null` when the reference touches no pivot table, or the table's values
383
+ * are not computed (it is not modeled, or its view has not loaded yet).
384
+ *
385
+ * @remarks
386
+ * What `GETPIVOTDATA` reads. The view answers for the values the table shows; matching a request
387
+ * to its fields and items by name is the caller's.
388
+ */
389
+ getPivotAt(reference: IReferenceRange): IFormulaContext.PivotView | null;
336
390
  /**
337
391
  * Reads a typed literal in the workbook's language — the parse side of {@link toText}.
338
392
  *
@@ -406,15 +460,14 @@ export interface IFormulaContext {
406
460
  * @remarks
407
461
  * Always read the clock through this, never `Date.now()`. The time is fixed for the whole
408
462
  * calculation pass, so every cell in one pass agrees, collaborators evaluating the same
409
- * workbook agree, and tests can pin it. Pair it with {@link markVolatile} so the function
463
+ * workbook agree, and tests can pin it. Pair it with `Volatile<T>` so the function
410
464
  * picks up a fresh time on the next pass.
411
465
  *
412
466
  * This is a calendar clock, not a stopwatch — use `performance.now()` to measure elapsed time.
413
467
  *
414
468
  * @example
415
469
  * ```typescript
416
- * export function NOW(): number {
417
- * FormulaContext.markVolatile();
470
+ * export function NOW(): Volatile<number> {
418
471
  * return FormulaContext.getNow();
419
472
  * }
420
473
  * ```
@@ -516,12 +569,11 @@ export interface IFormulaContext {
516
569
  * want the raw carry above — `DATEDIF("2020-01-31","2020-03-01","md")` is `-1`
517
570
  * because Jan 31 rolls into March. `EDATE`/`EOMONTH` want the opposite and CLAMP to
518
571
  * the target month's length, so they pre-clamp before calling here. One conversion,
519
- * two opposite rules, both matching Excel. Baking either one in breaks the other.
572
+ * two opposite rules. Baking either one in breaks the other.
520
573
  * * **Results may legitimately leave the calendar.** `YEARFRAC` basis 1 anchors a year
521
574
  * length on January 1 of `laterYear + 1`, which at the top of the calendar is
522
- * `10000-01-01`; Excel answers `1.9972602739726026` for
523
- * `YEARFRAC(DATE(9998,1,1),DATE(9999,12,31),1)` rather than `#NUM!`. A range check
524
- * here would break it.
575
+ * `10000-01-01`; `YEARFRAC(DATE(9998,1,1),DATE(9999,12,31),1)` is
576
+ * `1.9972602739726026`, not `#NUM!`. A range check here would break it.
525
577
  * * **The 16-bit argument limits are per-function.** `DATE`'s month errors outside
526
578
  * `[-32767, 32766]` while its day saturates to `32767`, and `TIME` errors outside
527
579
  * `[0, 32767]` on every argument — same magic number, three different rules.
@@ -529,8 +581,8 @@ export interface IFormulaContext {
529
581
  * Note the asymmetry with {@link getDateParts}, which DOES throw `#NUM!` outside the
530
582
  * calendar because it answers a user-facing question. An internal intermediate that
531
583
  * steps one day past the end must therefore not be round-tripped through it; compute
532
- * on the fields instead. That mistake made `YEARFRAC(DATE(9999,1,1),DATE(9999,12,31),0)`
533
- * `#NUM!` where Excel says `1`.
584
+ * on the fields instead, or `YEARFRAC(DATE(9999,1,1),DATE(9999,12,31),0)` becomes `#NUM!`
585
+ * rather than `1`.
534
586
  */
535
587
  getSerialDate(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millis?: number): number;
536
588
  /**
@@ -546,8 +598,8 @@ export interface IFormulaContext {
546
598
  *
547
599
  * @remarks
548
600
  * Argument REFLECTION, for the one thing a declaration cannot say: what happened at *this* call.
549
- * It exists to serve Excel's declared `any`, which means "no contract, the body decides" — every
550
- * other Excel type maps onto a type we already have and the marshaller handles it.
601
+ * It exists to serve a declared `any`, which means "no contract, the body decides" — every
602
+ * other declared type maps onto a type we already have and the marshaller handles it.
551
603
  *
552
604
  * The returned view is valid ONLY while the body that asked for it is running. Views are pooled
553
605
  * by dispatch depth, so retaining one across an `await`, a callback, or a nested evaluation hands
@@ -556,6 +608,65 @@ export interface IFormulaContext {
556
608
  * @throws If called outside a function dispatch — there are no arguments to reflect on.
557
609
  */
558
610
  getArgs(): IFunctionArguments;
611
+ /**
612
+ * Answers this call later: hands `executor` a call to settle, and leaves the cell showing what
613
+ * the function returns now.
614
+ *
615
+ * @param executor Runs once at the end of the recalculation with every call made against it.
616
+ * Pass a module-level function by name: calls join one batch when they name the same function.
617
+ * @param payload Everything the executor needs to answer this call, and how a later evaluation
618
+ * recognizes it: while the call is still waiting, a recalculation with
619
+ * an equal payload leaves it be rather than asking again. Once it has stopped, a recalculation
620
+ * calls the function again; what it fetched is answered from the workbook's cache
621
+ * (`IDeferContext.fetch`).
622
+ * @returns The last answer this call gave for the same question, when the function is being
623
+ * called again (a recalculation); `undefined` on a first call or a changed question. Return it
624
+ * to keep showing it until the new answer arrives, or return something else to show that the
625
+ * cell is waiting.
626
+ *
627
+ * @remarks
628
+ * The function still returns a value: what this call answers until its result arrives (nothing
629
+ * returned reads as blank), and it is what every formula over the cell calculates
630
+ * from meanwhile: return `#BUSY!` when they should wait as an error, or text such as
631
+ * `'Fetching…'` when that is what they should show.
632
+ *
633
+ * The executor runs after the recalculation, outside any cell, so the ambient `FormulaContext`
634
+ * is not its own: it reads the workbook through `ctx.getFormulaContext()`. Whatever the body
635
+ * knows about its cell goes in the payload.
636
+ *
637
+ * @example
638
+ * ```typescript
639
+ * function fetchAll(
640
+ * calls: readonly IFormulaContext.DeferredCall<string>[],
641
+ * ctx: IFormulaContext.IDeferContext
642
+ * ): void {
643
+ * for (const call of calls) {
644
+ * ctx.fetch(call.payload as string)
645
+ * .then((response) => response.text())
646
+ * .then((text) => call.resolve(text), (error) => call.reject(error));
647
+ * }
648
+ * }
649
+ *
650
+ * export function FETCHTEXT(url: string): string {
651
+ * FormulaContext.defer(fetchAll, url);
652
+ * return 'Fetching…';
653
+ * }
654
+ * ```
655
+ */
656
+ defer(executor: IFormulaContext.DeferExecutor, payload?: unknown): unknown;
657
+ /**
658
+ * A rich value of a registered type, made from its data as the type's JSON holds it, for the
659
+ * workbook being calculated.
660
+ *
661
+ * @param type The name of the value's kind (`'hyperlink'`): what its `getKind().getName()`
662
+ * will answer.
663
+ * @param data The value's data, as its JSON's `data`.
664
+ *
665
+ * @remarks
666
+ * A type nobody has registered still makes a value: one that carries the data and reads as
667
+ * nothing.
668
+ */
669
+ createRichData(type: string, data?: any): IRichData;
559
670
  }
560
671
  /**
561
672
  * {@inheritDoc IFormulaContext}
@@ -566,6 +677,28 @@ export interface IFormulaContext {
566
677
  * {@link IFormulaContext}
567
678
  */
568
679
  export declare namespace IFormulaContext {
680
+ /**
681
+ * Options for {@link IFormulaContext.filterHidden}.
682
+ */
683
+ interface FilterHiddenOptions {
684
+ /**
685
+ * Which axis to filter on.
686
+ * @defaultValue `IRange.AxisSelection.Row`
687
+ */
688
+ axes?: IRange.AxisSelection;
689
+ /**
690
+ * Skip the hidden rows or columns only while the range's sheet is filtered: while some
691
+ * AutoFilter or table on that sheet holds a condition, whether or not it hides a row. Until
692
+ * then `range` is returned as it is, hidden rows included.
693
+ *
694
+ * @remarks
695
+ * This is `SUBTOTAL` 1-11 (they include manually hidden rows, which is what an
696
+ * unfiltered sheet reads as). `SUBTOTAL` 101-111 and `AGGREGATE` leave it off and skip
697
+ * always.
698
+ * @defaultValue false
699
+ */
700
+ onlyWhileFiltered?: boolean;
701
+ }
569
702
  /**
570
703
  * Options for {@link IFormulaContext.parseAsNumber}.
571
704
  */
@@ -599,6 +732,160 @@ export declare namespace IFormulaContext {
599
732
  */
600
733
  anchor?: 'whole' | 'start';
601
734
  }
735
+ /**
736
+ * Options for {@link IFormulaContext.createPivot}.
737
+ */
738
+ interface PivotOptions {
739
+ /**
740
+ * One column, as tall as the keys, selecting the records that take part. A record is kept
741
+ * when its member is `TRUE` or a nonzero number, and left out otherwise.
742
+ */
743
+ filter?: IRange;
744
+ }
745
+ /**
746
+ * The records of a range grouped by rows and by columns — see {@link IFormulaContext.createPivot}.
747
+ */
748
+ interface Pivot {
749
+ /**
750
+ * The number of records kept by the filter.
751
+ */
752
+ getRecordCount(): number;
753
+ /**
754
+ * The row groups.
755
+ */
756
+ getRows(): PivotAxis;
757
+ /**
758
+ * The column groups.
759
+ */
760
+ getColumns(): PivotAxis;
761
+ /**
762
+ * The members of `values` in both a row group and a column group, as one column in record order.
763
+ *
764
+ * @param values One column, as tall as the keys.
765
+ * @param row A group of this pivot's rows; omit it, or pass the root, for every row.
766
+ * @param column A group of this pivot's columns; omit it, or pass the root, for every column.
767
+ * @returns An array, never a reference, or `null` when no kept record is in both groups.
768
+ */
769
+ getMembers(values: IRange, row?: PivotGroup, column?: PivotGroup): IRange | null;
770
+ /**
771
+ * What `fn` returns over the members {@link getMembers} would give, computed without them.
772
+ *
773
+ * @returns `null` when no kept record is in both groups, `undefined` when the pivot does not
774
+ * compute `fn` this way: call `fn` over {@link getMembers} instead.
775
+ *
776
+ * @remarks
777
+ * Optional. Where it is present it answers for the built-in aggregates it knows (`SUM`,
778
+ * `COUNT`, `AVERAGE`, ...) with exactly the value the call would return, the first error
779
+ * member included.
780
+ */
781
+ getAggregate?(values: IRange, fn: IFunction, row?: PivotGroup, column?: PivotGroup): Scalar | null | undefined;
782
+ }
783
+ /**
784
+ * A pivot table as the values it shows — see {@link IFormulaContext.getPivotAt}.
785
+ */
786
+ interface PivotView {
787
+ /**
788
+ * The table's values, in its order.
789
+ */
790
+ getDataFields(): readonly PivotViewDataField[];
791
+ /**
792
+ * The fields on the table's rows, columns and pages, in that order.
793
+ */
794
+ getFields(): readonly PivotViewField[];
795
+ /**
796
+ * A value the table shows.
797
+ *
798
+ * @param dataField The value, by index into {@link getDataFields}.
799
+ * @param items Per field, by index into {@link getFields}, the item by index into its
800
+ * {@link PivotViewField.items}. A field left out is totaled over.
801
+ * @returns The value, or `undefined` when the table does not show it: a line it does not show
802
+ * (a hidden or collapsed item, a subtotal or grand total turned off), an item not on its page,
803
+ * or a crossing with no records.
804
+ */
805
+ getValue(dataField: number, items: ReadonlyMap<number, number>): Scalar | undefined;
806
+ /**
807
+ * Whether the table's values are known here.
808
+ *
809
+ * @returns `false` for a table that is there but whose values the host does not compute: its
810
+ * fields and values are empty, and a caller that answered from it before keeps that answer, as
811
+ * the table only changes on a refresh. Absent means `true`.
812
+ */
813
+ isComputed?(): boolean;
814
+ }
815
+ /**
816
+ * A value of a {@link PivotView}.
817
+ */
818
+ interface PivotViewDataField {
819
+ /** As the table shows it (`Sum of Sales`). */
820
+ readonly name: string;
821
+ /** The name of the source field it summarizes (`Sales`). */
822
+ readonly sourceName: string;
823
+ }
824
+ /**
825
+ * A field of a {@link PivotView}.
826
+ */
827
+ interface PivotViewField {
828
+ /** As the table shows it: its caption, or the source field's name. */
829
+ readonly name: string;
830
+ readonly items: readonly PivotViewItem[];
831
+ }
832
+ /**
833
+ * An item of a {@link PivotViewField}.
834
+ */
835
+ interface PivotViewItem {
836
+ /** The item's value; `null` for the blank item. */
837
+ readonly value: Scalar;
838
+ /** The value as the table shows it, in the field's number format; `(blank)` for the blank item. */
839
+ readonly text: string;
840
+ /** The caption the item was renamed to, if it was. */
841
+ readonly name?: string;
842
+ }
843
+ /**
844
+ * One axis of a {@link Pivot}: its groups at every key level, as a tree under one root.
845
+ */
846
+ interface PivotAxis {
847
+ /**
848
+ * The group of every kept record, at level `0`.
849
+ */
850
+ getRoot(): PivotGroup;
851
+ /**
852
+ * The number of groups, the root included.
853
+ */
854
+ getGroupCount(): number;
855
+ }
856
+ /**
857
+ * One group of a {@link PivotAxis}. The axis answers the same object for a group every time, so a
858
+ * group can key a map.
859
+ */
860
+ interface PivotGroup {
861
+ /**
862
+ * The number of key columns the group is keyed by: `0` for the root, `1` for the groups of the
863
+ * first key column, and so on.
864
+ */
865
+ getLevel(): number;
866
+ /**
867
+ * The group one level up, or `null` for the root.
868
+ */
869
+ getParent(): PivotGroup | null;
870
+ /**
871
+ * The groups one level down, in the order their first records appear.
872
+ */
873
+ getChildren(): readonly PivotGroup[];
874
+ /**
875
+ * The keys, one per level down to this group's own. Each level's key is spelled by the first
876
+ * record of the group at that level, so a group's keys begin with its parent's. A blank key is
877
+ * `null`.
878
+ */
879
+ getKeys(): readonly Scalar[];
880
+ /**
881
+ * The number of kept records in the group.
882
+ */
883
+ getSize(): number;
884
+ /**
885
+ * The row of the group's first record within the keys.
886
+ */
887
+ getFirstRecord(): number;
888
+ }
602
889
  /**
603
890
  * A serial date split into fields — see {@link IFormulaContext.getDateParts}.
604
891
  *
@@ -630,5 +917,150 @@ export declare namespace IFormulaContext {
630
917
  */
631
918
  readonly dow: number;
632
919
  }
920
+ /**
921
+ * Answers the calls made by {@link IFormulaContext.defer} against it. Invoked once at the end of
922
+ * a recalculation with every such call; how many requests that becomes is the executor's choice.
923
+ */
924
+ type DeferExecutor<T = any> = (calls: readonly DeferredCall<T>[], ctx: IDeferContext) => void | Promise<void>;
925
+ /**
926
+ * One call made by {@link IFormulaContext.defer}, as its executor receives it.
927
+ *
928
+ * @remarks
929
+ * Settle each call itself, never by its position in a response. An executor that returns a
930
+ * promise which rejects fails every call it has not settled.
931
+ */
932
+ interface DeferredCall<T = any> {
933
+ /** What the function passed to `defer`. */
934
+ readonly payload: unknown;
935
+ /** Settles the call with its result. */
936
+ resolve(value: T, options?: DeferredCall.ResolveOptions): void;
937
+ /** Settles the call with an error: a `FormulaError` as itself, anything else as `#VALUE!`. */
938
+ reject(error: unknown): void;
939
+ /** Runs `fn` when the result is no longer wanted. */
940
+ onCancel(fn: () => void): void;
941
+ /**
942
+ * Whether the result is no longer wanted. Only an executor that holds calls to settle later
943
+ * needs it; a result for a cancelled call is dropped.
944
+ */
945
+ readonly cancelled: boolean;
946
+ }
947
+ namespace DeferredCall {
948
+ /**
949
+ * Options for {@link DeferredCall.resolve}.
950
+ */
951
+ interface ResolveOptions {
952
+ /**
953
+ * Whether the blanks of an array result stay blank when it spills. Omitted, they read as
954
+ * `0`, as any spilled array's blanks do.
955
+ *
956
+ * @remarks
957
+ * For data whose empty fields mean "nothing here", such as an imported file. The result
958
+ * keeps them while a formula passes it on as it is (`IF(c, result, …)`). A function that
959
+ * answers element by element builds a new array, whose blanks read as `0`
960
+ * (`IFERROR(result, …)`, `result & ""`).
961
+ * @defaultValue false
962
+ */
963
+ keepBlanks?: boolean;
964
+ }
965
+ }
966
+ /**
967
+ * What an executor is given beside its calls.
968
+ */
969
+ interface IDeferContext {
970
+ /** A random number in `[0, 1)` from the calculation's source, which a test can seed. */
971
+ getRandom(): number;
972
+ /**
973
+ * State kept for this executor across recalculations, made by `init` on first ask. Keep data
974
+ * here (maps, numbers, strings, buffers), not connections or sockets.
975
+ */
976
+ getState<S>(init: () => S): S;
977
+ /**
978
+ * Groups `calls` by payload and runs `answer` once per distinct payload with every call that
979
+ * asked it: one request per question, however many cells asked.
980
+ *
981
+ * @param calls The calls to group, usually every call the executor was handed.
982
+ * @param answer Settles one group. When it throws, or the promise it returns rejects, the
983
+ * group's calls it has not settled fail with that error; the other groups are not affected.
984
+ * @returns Resolves once every group has been answered. It never rejects.
985
+ *
986
+ * @remarks
987
+ * Payloads are equal as `defer` compares them: the same primitive, or plain objects or arrays
988
+ * whose own values are the same one level deep. Groups are answered concurrently, in the
989
+ * order their first call appears.
990
+ *
991
+ * @example
992
+ * ```typescript
993
+ * async function fetchQuotes(
994
+ * calls: readonly IFormulaContext.DeferredCall<number>[],
995
+ * ctx: IFormulaContext.IDeferContext
996
+ * ): Promise<void> {
997
+ * await ctx.byPayload(calls, async (ticker: string, group) => {
998
+ * const response = await ctx.fetch(`https://api.example.com/quote/${ticker}`);
999
+ * const price: number = (await response.json()).price;
1000
+ * for (const call of group) call.resolve(price);
1001
+ * });
1002
+ * }
1003
+ * ```
1004
+ */
1005
+ byPayload<T = any, P = unknown>(calls: readonly DeferredCall<T>[], answer: (payload: P, group: readonly DeferredCall<T>[]) => void | Promise<void>): Promise<void>;
1006
+ /**
1007
+ * A rich value of a registered type, for the workbook being calculated: what
1008
+ * {@link IFormulaContext.createRichData} makes inside a cell, made here for a call's result.
1009
+ */
1010
+ createRichData(type: string, data?: any): IRichData;
1011
+ /**
1012
+ * Adds bytes to the workbook being calculated as a resource (an `IResource`), for a rich value
1013
+ * to hold: pass it in {@link createRichData}'s data. The workbook saves it while a value holds
1014
+ * it. With `fetchedFrom` it is the workbook's copy of that URL: {@link fetch} answers the URL
1015
+ * from it, and adding the same URL again answers the resource already added.
1016
+ */
1017
+ addResource(buffer: ArrayBuffer, options?: {
1018
+ fetchedFrom?: string;
1019
+ }): object;
1020
+ /**
1021
+ * Fetches through the workbook being calculated: the standard `fetch`, answered by the
1022
+ * fetchers the host registered for the URL's scheme, with the host's authentication. An
1023
+ * answer the workbook already has for the URL (a picture it keeps, or anything fetched while
1024
+ * it is open) answers from the workbook, and two calls for one URL share one request. In an
1025
+ * external calculation (*Refresh All*) it goes to the source.
1026
+ *
1027
+ * Rejects when the URL cannot be reached, with a `FormulaError.Blocked` when the site responded
1028
+ * but the browser would not let the page read it.
1029
+ */
1030
+ fetch(input: string, init?: RequestInit): Promise<Response>;
1031
+ /**
1032
+ * The formula context of the workbook being calculated, with no cell: its parsing, text,
1033
+ * comparison and dates, read with the workbook's locale and date system.
1034
+ *
1035
+ * @remarks
1036
+ * Valid for as long as the executor runs, across every `await`. The ambient `FormulaContext`
1037
+ * is not: it belongs to the cell being evaluated, and an executor runs after that evaluation
1038
+ * ends. What needs a cell (`getRow`, `getReference`, `getValueAt`, `defer`,
1039
+ * `formatResults`, `getRandom`) throws here; {@link getRandom} is the executor's own.
1040
+ */
1041
+ getFormulaContext(): IFormulaContext;
1042
+ /**
1043
+ * Reads delimited text (CSV, TSV) into rows of values, each field read the way the workbook
1044
+ * reads typed entry: numbers, dates, booleans and error labels become values, anything else
1045
+ * stays text, and an empty field is blank.
1046
+ *
1047
+ * @param text The delimited text.
1048
+ * @param options How the text is split.
1049
+ * @returns The rows, padded with blanks to the widest row.
1050
+ */
1051
+ parseText(text: string, options?: IDeferContext.ParseTextOptions): Promise<Scalar[][]>;
1052
+ }
1053
+ namespace IDeferContext {
1054
+ /**
1055
+ * Options for {@link IDeferContext.parseText}.
1056
+ */
1057
+ interface ParseTextOptions {
1058
+ /**
1059
+ * The character between fields.
1060
+ * @defaultValue `','`
1061
+ */
1062
+ delimiter?: string;
1063
+ }
1064
+ }
633
1065
  }
634
1066
  //# sourceMappingURL=IFormulaContext.d.ts.map