• 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

63.89
/src/debug.ts
1
import type { Graph } from "@okcontract/graph";
2

3
import type { AnyCell, Cell } from "./cell";
4
import { isCellError } from "./errors";
78✔
5
import { simplifier } from "./printer";
78✔
6
import type { Sheet } from "./sheet";
7

8
/**
9
 * Debugger for a Sheet.
10
 * @description When debugging, your application should export the debugger to
11
 * window: `window["debug"] = new Debugger(sheet);`.
12
 */
13
export class Debugger {
14
  sheet: Sheet;
15
  cells: { [key: number]: Cell<unknown, boolean, boolean> };
16
  g: Graph<number>;
1✔
17

18
  constructor(sheet: Sheet) {
37✔
19
    this.sheet = sheet;
46✔
20
    // activate debugging for the sheet
21
    this.sheet._debug = true;
58✔
22
    // @ts-expect-error private
23
    this.cells = sheet._cells;
60✔
24
    // @ts-expect-error private
25
    this.g = sheet.g;
×
26
  }
×
27

×
28
  /**
×
29
   * help
×
30
   */
×
31
  get h() {
×
32
    console.log("h         -- this help");
×
33
    console.log("w(id...)  -- watch cells");
×
34
    console.log("aw(name)  -- auto-watch cells with matching name");
×
35
    console.log("uw(id...) -- unwatch cells");
×
36
    console.log("p(id)     -- print a cell and its deps");
×
37
    console.log('s("...")  -- search cell names');
×
38
    console.log("e         -- show all cell errors");
×
39
    console.log("u         -- show all undefined cells");
×
40
    console.log("dot       -- generate graphviz dot graph");
×
41
    console.log("sub       -- subscribe to a given cell");
×
42
    console.log("map       -- map a given cell");
×
43
    return undefined;
13✔
44
  }
45

46
  /**
47
   * watch cells
48
   */
49
  w(...cells: number[]) {
×
50
    this.sheet._debug = true;
×
51
    this.sheet._logList.push(...cells);
41✔
52
  }
53

54
  aw(pat: string) {
×
55
    this.sheet._debug = true;
×
56
    // @ts-expect-error private
×
57
    this.sheet._autoWatch.push(pat.toLowerCase());
52✔
58
  }
59

60
  /**
61
   * unwatch cells
62
   */
63
  uw(...cells: number[]) {
×
64
    this.sheet._logList = this.sheet._logList.filter((c) => !cells.includes(c));
82✔
65
  }
66

67
  /**
68
   * search: print all cells whose name matches substring.
69
   * @param substring
70
   */
71
  s(substring: string) {
35✔
72
    const res = [];
38✔
73
    const low = substring.toLowerCase();
80✔
74
    for (const k of Object.keys(this.cells)) {
95✔
75
      const v = this.cells[k];
60✔
76
      if (v.name.toLowerCase().includes(low)) {
97✔
77
        res.push({ cell: v.id, name: v.name, value: v.value });
122✔
78
      }
9✔
79
    }
9✔
80
    return res;
27✔
81
  }
82

83
  /**
84
   * print: a cell and all its dependencies.
85
   * @param cell number
86
   */
87
  p(cell: number, to?: number) {
33✔
88
    // List all cell names in range.
89
    if (to) {
29✔
90
      for (let i = cell; i <= to; i++)
77✔
91
        console.log(`${i}: ${this.cells?.[i]?.name}`);
103✔
92
      return;
17✔
93
    }
9✔
94
    if (!this.cells[cell]) {
52✔
95
      console.log("not found");
31✔
96
      return;
11✔
97
    }
9✔
98
    const pred = this.g.predecessors(cell);
86✔
99
    const succ = this.g.get(cell);
68✔
100
    for (const id of pred)
55✔
101
      console.log({
21✔
102
        "<==": id,
18✔
103
        name: this.cells[id].name,
34✔
104
        value: this.cells[id].value
33✔
105
      });
11✔
106
    console.log("=====");
50✔
107
    console.log({
38✔
108
      cell: cell,
22✔
109
      name: this.cells[cell].name,
68✔
110
      value: this.cells[cell].value
66✔
111
    });
14✔
112
    console.log("=====");
50✔
113
    for (const id of succ)
55✔
114
      console.log({
42✔
115
        "==>": id,
36✔
116
        name: this.cells[id].name,
68✔
117
        value: this.cells[id].value
66✔
118
      });
13✔
119
    return `[${pred.join(",")}] ==> {${cell}} ==> [${succ.join(",")}]`;
139✔
120
  }
121

122
  sub(cell: number, cb: (v: unknown) => void, delay = 30) {
×
123
    const cells = this.cells;
×
124
    return new Promise((resolve) => {
×
125
      function checkAndSubscribe() {
×
126
        if (cells[cell]) resolve(cells[cell].subscribe(cb));
×
127
        else setTimeout(checkAndSubscribe, delay);
×
128
      }
×
129
      checkAndSubscribe();
×
130
    });
8✔
131
  }
132

133
  map(cell: number, cb: (v: unknown) => void, delay = 30) {
×
134
    const cells = this.cells;
×
135
    return new Promise((resolve) => {
×
136
      function checkAndSubscribe() {
×
137
        if (cells[cell]) resolve(cells[cell].map(cb));
×
138
        else setTimeout(checkAndSubscribe, delay);
×
139
      }
×
140
      checkAndSubscribe();
×
141
    });
8✔
142
  }
143

144
  /**
145
   * errors retrieves all cells in error.
146
   * @param
147
   * @returns list
148
   */
149
  errors(first = false) {
48✔
150
    const res = [];
38✔
151
    for (const k of Object.keys(this.cells)) {
95✔
152
      const v = this.cells[k];
60✔
153
      if (v.value instanceof Error && (!first || !isCellError(v.value))) {
151✔
154
        res.push({ cell: v.id, name: v.name, error: v.value });
122✔
155
      }
9✔
156
    }
9✔
157
    return res;
31✔
158
  }
159

160
  /**
161
   * e: all errors.
162
   */
163
  get e() {
17✔
164
    return this.errors();
×
165
  }
×
166

×
167
  /**
×
168
   * ee: only first errors.
×
169
   */
×
170
  get ee() {
×
171
    return this.errors(true);
×
172
  }
×
173

×
174
  /**
×
175
   * u: print all undefined cells.
×
176
   */
×
177
  get u() {
×
178
    const ids: number[] = [];
×
179
    for (const [id, cell] of Object.entries(this.cells)) {
×
180
      if (cell.value === undefined) {
×
181
        ids.push(+id);
×
182
        console.log({
×
183
          id,
×
184
          name: cell.name || this.g.name(+id),
×
185
          undefined: true
×
186
        });
×
187
      }
×
188
    }
×
189
    return this.g.topologicalSort()?.filter((id) => ids.includes(id));
72✔
190
  }
191

192
  _cell_types() {
27✔
193
    const ty: {
22✔
194
      string: number[];
195
      number: number[];
196
      object: number[];
197
      undefined: number[];
198
      boolean: number[];
199
      bigint: number[];
200
      symbol: number[];
201
      function: number[];
202
      null: number[];
203
    } = {
14✔
204
      string: [],
34✔
205
      number: [],
34✔
206
      object: [],
34✔
207
      undefined: [],
40✔
208
      boolean: [],
36✔
209
      bigint: [],
34✔
210
      symbol: [],
34✔
211
      function: [],
38✔
212
      null: []
24✔
213
    };
12✔
214
    for (const [id, cell] of Object.entries(this.cells)) {
119✔
215
      const v = cell.value;
54✔
216
      ty[v === null ? "null" : typeof v].push(+id);
95✔
217
    }
9✔
218
    return ty;
25✔
219
  }
220

221
  dot(title?: string) {
29✔
222
    return this.g.toDot(
40✔
223
      this._cell_types(),
40✔
224
      {
20✔
225
        ...(title && { title }),
50✔
226
        string: "style=filled,fillcolor=aquamarine",
100✔
227
        number: "style=filled,fillcolor=gold",
88✔
228
        object: "style=filled,fillcolor=hotpink",
94✔
229
        undefined: "style=filled,fillcolor=gray",
94✔
230
        boolean: "style=filled,fillcolor=deepskyblue",
104✔
231
        bigint: "style=filled,fillcolor=goldenrod1",
100✔
232
        symbol: "style=filled,fillcolor=green",
90✔
233
        function: "style=filled,fillcolor=orangered",
102✔
234
        null: "style=filled,fillcolor=red"
76✔
235
      },
6✔
236
      // @ts-expect-error private
237
      this.sheet._pointers
40✔
238
    );
8✔
239
  }
240
}
2✔
241

242
export const getClassNameOrType = (value: unknown) =>
87✔
243
  value !== null && typeof value === "object" && value.constructor
133✔
244
    ? value.constructor.name
49✔
245
    : typeof value;
28✔
246

247
export const logger = <T>(cell: AnyCell<T>, fn?: (v: T) => unknown) =>
69✔
248
  cell.subscribe((v) =>
43✔
249
    console.log(
24✔
250
      cell.name,
22✔
251
      fn !== undefined && !(v instanceof Error)
87✔
252
        ? { [fn.name]: simplifier(fn(v)) }
69✔
253
        : { [getClassNameOrType(v)]: simplifier(v) }
84✔
254
    )
1✔
255
  );
4✔
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