• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

okcontract / cells / 29101755823

10 Jul 2026 02:56PM UTC coverage: 78.431% (-9.8%) from 88.241%
29101755823

push

github

hbbio
README: mention local bun and biome to minimize npm dependencies

1829 of 2332 relevant lines covered (78.43%)

29.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.62
/src/array.ts
1
import type { AnyCell, MapCell, ValueCell } from "./cell";
2
import { collector, reuseOrCreate } from "./gc";
48✔
3
import type { SheetProxy } from "./proxy";
4

5
export type CellArray<T> = AnyCell<AnyCell<T>[]>;
6
export type ValueCellArray<T> = ValueCell<ValueCell<T>[]>;
7

8
/**
9
 * mapArray implements .map() for a cellified array.
10
 *
11
 * @param proxy
12
 * @param arr canonical form cell array
13
 * @param fn to map each element cell
14
 * @returns mapped array cell
15
 *
16
 * @description This function reuses existing mapped cells.
17
 * @todo Delete unused mapped cells
18
 */
19
export const mapArray = <T, U>(
23✔
20
  proxy: SheetProxy,
7✔
21
  arr: CellArray<T>,
5✔
22
  fn: (
4✔
23
    v: T,
24
    index?: number,
25
    cell?: AnyCell<T>
26
  ) => U | Promise<U | AnyCell<U>> | AnyCell<U>,
27
  name = "map"
17✔
28
): MapCell<MapCell<U, false>[], false> =>
29
  proxy.map(
10✔
30
    [arr],
6✔
31
    (cells, prev) => {
20✔
32
      if (!Array.isArray(cells))
29✔
33
        throw new Error(`not an array: ${typeof cells}`);
2✔
34
      if (!cells) return [];
16✔
35
      const set = new Set((prev || []).map((cell) => cell.id));
57✔
36
      const res = cells.map((cell, index) => {
43✔
37
        // reuse previously mapped cell
38
        const reuse = prev?.find((_c) => _c.dependencies?.[0] === cell.id);
69✔
39
        if (reuse !== undefined) set.delete(reuse.id);
53✔
40
        return (
7✔
41
          reuse ||
9✔
42
          // create new map
43
          proxy.map(
10✔
44
            [cell],
7✔
45
            (_cell) => fn(_cell, index, cell),
34✔
46
            `${cell.id}:[${index}]`
23✔
47
          )
1✔
48
        );
49
      });
4✔
50
      // collect unused previously mapped cells
51
      proxy._sheet.collect(...[...set]);
36✔
52
      return res;
12✔
53
    },
54
    name
4✔
55
  );
2✔
56

57
/**
58
 * Compares two values and returns a number indicating their order.
59
 *
60
 * This function is designed to be used as a comparator in sorting functions.
61
 * It returns:
62
 * - `1` if `a` is greater than `b`
63
 * - `-1` if `a` is less than `b`
64
 * - `0` if `a` is equal to `b`
65
 *
66
 * @param {*} a - The first value to compare.
67
 * @param {*} b - The second value to compare.
68
 * @returns {number} - Order.
69
 *
70
 * @example
71
 * const array = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
72
 * array.sort(defaultComparator);
73
 */
74
export const defaultComparator = <T>(a: T, b: T): number =>
41✔
75
  a > b ? 1 : a < b ? -1 : 0;
24✔
76

77
/**
78
 * implementation of sort for a cellified array.
79
 * @description this implementation relies on pointers but reuses the original cells.
80
 * @param proxy
81
 * @param arr
82
 * @param compare comparison function
83
 * @param noFail should be true when the following conditions are met:
84
 * 1. compare must be defined
85
 * 2. cells must be an array (possibly empty)
86
 * 3. compare should never fail
87
 */
88
export const sort = <T, NF extends boolean = false>(
19✔
89
  proxy: SheetProxy,
7✔
90
  arr: CellArray<T>,
5✔
91
  compare: (a: T, b: T) => number = defaultComparator,
29✔
92
  noFail?: NF
11✔
93
): MapCell<typeof arr extends AnyCell<infer U> ? U : never, NF> => {
3✔
94
  const coll =
13✔
95
    collector<MapCell<typeof arr extends AnyCell<infer U> ? U : never, NF>>(
10✔
96
      proxy
5✔
97
    );
4✔
98
  return proxy.mapNoPrevious(
27✔
99
    [arr],
6✔
100
    (cells) =>
11✔
101
      coll(
5✔
102
        proxy.mapNoPrevious(
20✔
103
          cells,
6✔
104
          (..._cells) =>
15✔
105
            _cells
7✔
106
              .map((_, index) => index)
23✔
107
              .sort((indexA, indexB) => compare(_cells[indexA], _cells[indexB]))
64✔
108
              .map((index) => cells[index]),
27✔
109
          "_sort",
9✔
110
          noFail || false
15✔
111
        )
1✔
112
      ),
2✔
113
    "sort",
8✔
114
    noFail || false
15✔
115
  );
3✔
116
};
117

118
/**
119
 * mapArrayCell is a variant of `mapArray` with a function taking
120
 * element cells as arguments.
121
 * @param proxy
122
 * @param arr
123
 * @param fn
124
 * @returns mapped array cell
125
 */
126
export const mapArrayCell = <T, U, NF extends boolean = false>(
27✔
127
  proxy: SheetProxy,
7✔
128
  arr: CellArray<T>,
5✔
129
  fn: AnyCell<(v: AnyCell<T>, idx?: number) => AnyCell<U>>,
4✔
130
  name = "map",
14✔
131
  nf?: NF
7✔
132
): MapCell<MapCell<U, NF>[], NF> => {
3✔
133
  let prevFn: (v: AnyCell<T>) => AnyCell<U>;
13✔
134
  return proxy.map(
17✔
135
    [arr, fn],
10✔
136
    (cells, _fn, prev) => {
27✔
137
      const arr = cells.map((cell, i) =>
34✔
138
        // @todo collect previously mapped cells
139
        reuseOrCreate(
14✔
140
          _fn === prevFn, // function unchanged
15✔
141
          () => prev?.find((_c) => _c.dependencies?.[0] === cell.id), // reuse
1✔
142
          () => _fn(cell, i) as MapCell<U, NF> // create new map
17✔
143
        )
144
      );
6✔
145
      prevFn = _fn;
17✔
146
      return arr;
12✔
147
    },
148
    name,
6✔
149
    nf
2✔
150
  );
3✔
151
};
152

153
/**
154
 * first element of recursive arrays.
155
 */
156
export const first = <T>(
20✔
157
  proxy: SheetProxy,
7✔
158
  arr: AnyCell<T | AnyCell<T>[]>,
5✔
159
  name = "last"
18✔
160
): MapCell<T, boolean> => {
3✔
161
  // @todo collectors (on path?)
162
  const aux = (arr: AnyCell<T | AnyCell<T>[]>) =>
20✔
163
    proxy.mapNoPrevious(
20✔
164
      [arr],
6✔
165
      (_arr) => {
15✔
166
        if (!Array.isArray(_arr)) return arr as AnyCell<T>;
44✔
167
        if (_arr.length === 0) return null; // @todo pointer to nullCell?
31✔
168
        return aux(_arr[0]);
21✔
169
      },
170
      name
4✔
171
    );
3✔
172
  return aux(arr);
17✔
173
};
174

175
/**
176
 * last element of recursive arrays.
177
 */
178
export const last = <T>(
19✔
179
  proxy: SheetProxy,
7✔
180
  arr: AnyCell<T | AnyCell<T>[]>,
5✔
181
  name = "last"
18✔
182
): MapCell<T, boolean> => {
3✔
183
  // @todo collectors (on path?)
184
  const aux = (arr: AnyCell<T | AnyCell<T>[]>) =>
20✔
185
    proxy.mapNoPrevious(
20✔
186
      [arr],
6✔
187
      (_arr) => {
15✔
188
        if (Array.isArray(_arr)) return aux(_arr[_arr.length - 1]);
66✔
189
        return arr as AnyCell<T>;
12✔
190
      },
191
      name
4✔
192
    );
3✔
193
  return aux(arr);
17✔
194
};
195

196
/**
197
 * reduce a cellified array.
198
 * @param proxy
199
 * @param arr
200
 * @param fn
201
 * @param init
202
 * @returns reduced cell
203
 */
204
export const reduce = <
21✔
205
  T,
206
  R extends unknown | Promise<unknown>,
207
  NF extends boolean = false
208
>(
209
  proxy: SheetProxy,
7✔
210
  arr: CellArray<T>,
5✔
211
  fn: (acc: R, elt: T, index?: number, cell?: AnyCell<T>) => R,
4✔
212
  init: R,
6✔
213
  name = "reduce",
17✔
214
  nf?: NF
7✔
215
): MapCell<R, NF> => {
3✔
216
  const coll = collector<MapCell<R, NF>>(proxy);
32✔
217
  return proxy.map(
17✔
218
    [arr],
6✔
219
    (cells) =>
11✔
220
      coll(
5✔
221
        proxy.mapNoPrevious(
20✔
222
          cells,
6✔
223
          (..._cells) =>
15✔
224
            _cells.reduce((acc, elt, i) => fn(acc, elt, i, cells[i]), init),
62✔
225
          "_reduce"
9✔
226
        )
1✔
227
      ),
2✔
228
    name,
6✔
229
    nf
2✔
230
  );
3✔
231
};
232

233
export const find = <T, NF extends boolean = false>(
19✔
234
  proxy: SheetProxy,
7✔
235
  arr: CellArray<T>,
5✔
236
  predicate: (v: T) => boolean,
11✔
237
  name = "find",
15✔
238
  nf?: NF
7✔
239
) => {
3✔
240
  const coll = collector<MapCell<T, NF>>(proxy);
32✔
241
  return proxy.map(
17✔
242
    [arr],
6✔
243
    (cells) =>
11✔
244
      coll(proxy.mapNoPrevious(cells, (..._cells) => _cells.find(predicate))),
70✔
245
    name,
6✔
246
    nf
2✔
247
  );
3✔
248
};
249

250
export const findIndex = <T, NF extends boolean = false>(
24✔
251
  proxy: SheetProxy,
7✔
252
  arr: CellArray<T>,
5✔
253
  fn: (v: T) => boolean,
4✔
254
  name = "find",
15✔
255
  nf?: NF
7✔
256
) => {
3✔
257
  const coll = collector<MapCell<number, NF>>(proxy);
32✔
258
  return proxy.map(
17✔
259
    [arr],
6✔
260
    (cells) =>
11✔
261
      coll(proxy.mapNoPrevious(cells, (..._cells) => _cells.findIndex(fn))),
68✔
262
    name,
6✔
263
    nf
2✔
264
  );
3✔
265
};
266

267
/**
268
 * filter updates a cellified array in a `ValueCell` using a predicate function as filter.
269
 * @param arr
270
 * @param predicate function that returns `true` for kept values.
271
 * @returns nothing
272
 * @description filtered out cells are _not_ deleted
273
 */
274
export const filter = <T, NF extends boolean = false>(
21✔
275
  proxy: SheetProxy,
7✔
276
  predicate: AnyCell<(elt: T) => boolean>,
11✔
277
  arr: CellArray<T>,
5✔
278
  name = "filter",
17✔
279
  nf?: NF
7✔
280
) => {
3✔
281
  const coll = collector<MapCell<AnyCell<T>[], NF>>(proxy);
32✔
282
  return proxy.map(
17✔
283
    [predicate, arr],
17✔
284
    (fn, cells) =>
15✔
285
      coll(
5✔
286
        proxy.mapNoPrevious(
20✔
287
          cells,
6✔
288
          (..._cells) => cells.filter((_, i) => fn(_cells[i])),
51✔
289
          "_filter"
9✔
290
        )
1✔
291
      ),
2✔
292
    name,
6✔
293
    nf
2✔
294
  );
3✔
295
};
296

297
/**
298
 * `flattenCellArray` is a utility function that maps and flattens an array
299
 * of cells into a single cell that contains a flattened array of values.
300
 * It creates a new cell that reacts to changes in the input array and updates
301
 * its value accordingly.
302
 *
303
 * @template T - The type of the elements in the input cell array.
304
 * @template NF - Type indicating if the cells are failsafe.
305
 *
306
 * @param {SheetProxy} proxy - An instance of `SheetProxy` used to create new
307
 * cells and manage dependencies.
308
 * @param {CellArray<T>} arr - An array of cells, where each cell contains a
309
 * value of type `T`.
310
 * @param {string} [name="flatten"] - An optional name for the resulting cell.
311
 * Defaults to "flatten".
312
 * @param {NF} [nf] - An optional flag indicating whether the cells can't fail.
313
 * Defaults to `false`.
314
 *
315
 * @returns {MapCell<T[], NF>} - A cell containing a flattened array of values.
316
 */
317
export const flattenCellArray = <T, NF extends boolean = false>(
×
318
  proxy: SheetProxy,
×
319
  arr: CellArray<T>,
×
320
  name = "flatten",
×
321
  nf?: NF
×
322
) => {
×
323
  const coll = collector<MapCell<T[], NF>>(proxy);
×
324
  return proxy.mapNoPrevious(
×
325
    [arr],
×
326
    (cells) =>
×
327
      cells === null
×
328
        ? null
×
329
        : coll(proxy.mapNoPrevious(cells, (..._cells) => _cells)),
×
330
    name,
×
331
    nf
×
332
  );
2✔
333
};
334

335
/**
336
 * filterPredicateCell updates a cellified array in a `ValueCell` using a predicate function as filter.
337
 * @param arr
338
 * @param predicate as Cell function that returns `true` for kept values.
339
 * @returns nothing
340
 * @description filtered out cells are _not_ deleted
341
 */
342
export const filterPredicateCell = <T, NF extends boolean = false>(
34✔
343
  proxy: SheetProxy,
7✔
344
  predicate: AnyCell<(elt: AnyCell<T>) => AnyCell<boolean>>,
11✔
345
  arr: CellArray<T>,
5✔
346
  name = "filter",
17✔
347
  nf?: NF
7✔
348
) => {
3✔
349
  const coll = collector<MapCell<AnyCell<T>[], NF>>(proxy);
32✔
350
  // Since predicate is a reactive function, we have to instantiate
351
  // the computation for each cell.
352
  // const keep = mapArrayCell(proxy, arr, predicate, "keep", nf);
353
  let prevFn: (elt: AnyCell<T>) => AnyCell<boolean>;
13✔
354
  const keep = proxy.map(
23✔
355
    [predicate, arr],
17✔
356
    (fn, cells, _prev: AnyCell<boolean>[]) => {
27✔
357
      // console.log({ keep: cells.length });
358
      // @todo if the predicate function has changed, collect previous mapped cells
359
      const keep = cells.map((cell) => {
39✔
360
        // We can reuse a cell only if the predicate hasn't changed.
361
        // @todo collect previously mapped cells for deleted cells in arr
362
        const reuse =
14✔
363
          prevFn === fn &&
17✔
364
          _prev?.find((_c) => _c.dependencies?.[0] === cell.id);
19✔
365
        return reuse || fn(cell);
24✔
366
      });
6✔
367
      prevFn = fn;
16✔
368
      return keep;
13✔
369
    },
370
    "keep",
8✔
371
    nf
2✔
372
  );
4✔
373
  return proxy.map(
17✔
374
    [arr, keep],
12✔
375
    (cells, _keep) =>
18✔
376
      coll(
5✔
377
        proxy.mapNoPrevious(
20✔
378
          _keep,
6✔
379
          (..._flat) => cells.filter((_, i) => _flat[i]),
45✔
380
          "filter.map"
12✔
381
        )
1✔
382
      ),
2✔
383
    name,
6✔
384
    nf
2✔
385
  );
2✔
386
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc