@interocitor/core 0.0.0-beta.3 → 0.0.0-beta.5

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.
Files changed (98) hide show
  1. package/README.md +446 -91
  2. package/dist/adapters/cloudflare.d.ts +8 -9
  3. package/dist/adapters/cloudflare.d.ts.map +1 -1
  4. package/dist/adapters/cloudflare.js +38 -12
  5. package/dist/adapters/google-drive.d.ts +1 -1
  6. package/dist/adapters/google-drive.js +1 -2
  7. package/dist/adapters/memory.d.ts +4 -1
  8. package/dist/adapters/memory.d.ts.map +1 -1
  9. package/dist/adapters/memory.js +13 -2
  10. package/dist/adapters/webdav.d.ts +5 -0
  11. package/dist/adapters/webdav.d.ts.map +1 -1
  12. package/dist/adapters/webdav.js +18 -1
  13. package/dist/core/codec.d.ts +1 -1
  14. package/dist/core/codec.d.ts.map +1 -1
  15. package/dist/core/codec.js +39 -3
  16. package/dist/core/compaction.d.ts +9 -1
  17. package/dist/core/compaction.d.ts.map +1 -1
  18. package/dist/core/compaction.js +63 -7
  19. package/dist/core/crdt.d.ts +6 -3
  20. package/dist/core/crdt.d.ts.map +1 -1
  21. package/dist/core/crdt.js +53 -67
  22. package/dist/core/errors.d.ts +47 -0
  23. package/dist/core/errors.d.ts.map +1 -0
  24. package/dist/core/errors.js +61 -0
  25. package/dist/core/flush.d.ts +3 -3
  26. package/dist/core/flush.d.ts.map +1 -1
  27. package/dist/core/flush.js +64 -7
  28. package/dist/core/hlc.js +0 -1
  29. package/dist/core/ids.d.ts +49 -0
  30. package/dist/core/ids.d.ts.map +1 -0
  31. package/dist/core/ids.js +132 -0
  32. package/dist/core/internals.d.ts +10 -2
  33. package/dist/core/internals.d.ts.map +1 -1
  34. package/dist/core/internals.js +27 -9
  35. package/dist/core/manifest.d.ts +24 -5
  36. package/dist/core/manifest.d.ts.map +1 -1
  37. package/dist/core/manifest.js +80 -13
  38. package/dist/core/pull.d.ts +1 -1
  39. package/dist/core/pull.d.ts.map +1 -1
  40. package/dist/core/pull.js +21 -6
  41. package/dist/core/row-id.js +0 -1
  42. package/dist/core/schema-types.d.ts +22 -11
  43. package/dist/core/schema-types.d.ts.map +1 -1
  44. package/dist/core/schema-types.js +18 -9
  45. package/dist/core/schema-types.type-test.js +59 -5
  46. package/dist/core/sync-engine.d.ts +166 -12
  47. package/dist/core/sync-engine.d.ts.map +1 -1
  48. package/dist/core/sync-engine.js +1615 -219
  49. package/dist/core/table.d.ts +217 -17
  50. package/dist/core/table.d.ts.map +1 -1
  51. package/dist/core/table.js +376 -24
  52. package/dist/core/types.d.ts +413 -17
  53. package/dist/core/types.d.ts.map +1 -1
  54. package/dist/core/types.js +0 -1
  55. package/dist/crypto/encryption.d.ts.map +1 -1
  56. package/dist/crypto/encryption.js +6 -1
  57. package/dist/crypto/keys.js +0 -1
  58. package/dist/handshake/channel.js +0 -1
  59. package/dist/handshake/index.d.ts +5 -2
  60. package/dist/handshake/index.d.ts.map +1 -1
  61. package/dist/handshake/index.js +19 -2
  62. package/dist/handshake/qr.js +0 -1
  63. package/dist/index.d.ts +9 -7
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +8 -6
  66. package/dist/storage/credential-store.d.ts +25 -2
  67. package/dist/storage/credential-store.d.ts.map +1 -1
  68. package/dist/storage/credential-store.js +55 -8
  69. package/dist/storage/local-store.d.ts +4 -1
  70. package/dist/storage/local-store.d.ts.map +1 -1
  71. package/dist/storage/local-store.js +37 -21
  72. package/package.json +3 -3
  73. package/dist/adapters/cloudflare.js.map +0 -1
  74. package/dist/adapters/google-drive.js.map +0 -1
  75. package/dist/adapters/memory.js.map +0 -1
  76. package/dist/adapters/webdav.js.map +0 -1
  77. package/dist/core/codec.js.map +0 -1
  78. package/dist/core/compaction.js.map +0 -1
  79. package/dist/core/crdt.js.map +0 -1
  80. package/dist/core/flush.js.map +0 -1
  81. package/dist/core/hlc.js.map +0 -1
  82. package/dist/core/internals.js.map +0 -1
  83. package/dist/core/manifest.js.map +0 -1
  84. package/dist/core/pull.js.map +0 -1
  85. package/dist/core/row-id.js.map +0 -1
  86. package/dist/core/schema-types.js.map +0 -1
  87. package/dist/core/schema-types.type-test.js.map +0 -1
  88. package/dist/core/sync-engine.js.map +0 -1
  89. package/dist/core/table.js.map +0 -1
  90. package/dist/core/types.js.map +0 -1
  91. package/dist/crypto/encryption.js.map +0 -1
  92. package/dist/crypto/keys.js.map +0 -1
  93. package/dist/handshake/channel.js.map +0 -1
  94. package/dist/handshake/index.js.map +0 -1
  95. package/dist/handshake/qr.js.map +0 -1
  96. package/dist/index.js.map +0 -1
  97. package/dist/storage/credential-store.js.map +0 -1
  98. package/dist/storage/local-store.js.map +0 -1
@@ -1,23 +1,320 @@
1
1
  /**
2
- * Table<T> — typed handle for a named collection within a SyncEngine.
2
+ * Table<T> — typed handle for a named collection within a Interocitor.
3
3
  *
4
4
  * Wraps the engine's raw Row/string API with a type-safe surface.
5
5
  * All reads return plain T objects (internal HLC metadata stripped).
6
6
  * All writes accept Partial<T> and return T.
7
7
  */
8
+ import { createRowId } from "./row-id.js";
9
+ /**
10
+ * Thenable result of an async query.
11
+ *
12
+ * Two layers live here, kept distinct on purpose:
13
+ * 1. Async query identity: `descriptor` + `cacheKey`. Owned by core. Stable
14
+ * across `.sort()` chains. Used by any cache layer (React etc.) to dedupe
15
+ * loads, share in-flight promises, and keep stale data across remounts.
16
+ * 2. Sync derivation: `.sort(compareFn)` runs after the async load. Does not
17
+ * change `cacheKey`. Not part of cache identity. Pure post-processing.
18
+ *
19
+ * `then(...)` triggers a load through the engine cache. Use `load({ bypassCache })`
20
+ * to force a fresh fetch.
21
+ */
22
+ export class QueryResult {
23
+ constructor(descriptor, _engine,
24
+ /** Optional sync transform stack — runs after rows are loaded. */
25
+ _sortChain = [],
26
+ /** Optional pre-resolved promise (used for synthetic / engine-less results). */
27
+ _explicitPromise = null) {
28
+ this._engine = _engine;
29
+ this._sortChain = _sortChain;
30
+ this._explicitPromise = _explicitPromise;
31
+ // ─── internals ───────────────────────────────────────────────────
32
+ /**
33
+ * Memoization for the typed+sorted projection. Keyed by the raw rows
34
+ * reference from the engine cache. Same input ref ⇒ same output ref.
35
+ * Cleared automatically when the engine swaps in a fresh raw rows array
36
+ * (e.g. after a write invalidation).
37
+ */
38
+ this._projectionInputRef = null;
39
+ this._projectionOutput = null;
40
+ this.descriptor = descriptor;
41
+ this.cacheKey = _engine
42
+ ? _engine.getQueryCacheKey(descriptor)
43
+ : computeCacheKey(descriptor);
44
+ }
45
+ /** Public metadata. Mirrors `QueryMetadata` shape. */
46
+ get metadata() {
47
+ return { descriptor: this.descriptor, cacheKey: this.cacheKey };
48
+ }
49
+ /**
50
+ * Async load through engine cache.
51
+ * Pass `{ bypassCache: true }` to ignore cached snapshot and refetch.
52
+ */
53
+ load(options) {
54
+ return this._loadRaw(options).then(rows => this._applySort(rows));
55
+ }
56
+ /**
57
+ * Read cached rows synchronously, if available. Returns `undefined` only if
58
+ * the cache has no row snapshot. Does not start a load.
59
+ *
60
+ * Stale-while-revalidate contract: pending/error snapshots may still carry
61
+ * rows from the previous ready load. Expose those rows so consumers can keep
62
+ * stale data visible while a refresh is in flight.
63
+ *
64
+ * Stable reference contract: callers (React, useSyncExternalStore, etc.)
65
+ * MUST be able to compare the returned array by reference. We memoize the
66
+ * typed+sorted projection keyed by the raw rows reference from the engine
67
+ * cache. As long as the engine cache hasn't moved, this returns the exact
68
+ * same array instance.
69
+ */
70
+ peekCache() {
71
+ if (!this._engine)
72
+ return undefined;
73
+ const snap = this._engine.readQueryCache(this.descriptor);
74
+ if (!snap.rows)
75
+ return undefined;
76
+ return this._project(snap.rows);
77
+ }
78
+ /**
79
+ * Sync status read for cache consumers (e.g. React bindings). Mirrors the
80
+ * cache snapshot status without exposing engine internals.
81
+ */
82
+ peekStatus() {
83
+ if (!this._engine)
84
+ return { status: 'empty' };
85
+ const snap = this._engine.readQueryCache(this.descriptor);
86
+ return { status: snap.status, error: snap.error };
87
+ }
88
+ /**
89
+ * Render-time read. If the engine is ready and a cache snapshot exists,
90
+ * returns rows synchronously (no flash). Otherwise returns a promise.
91
+ * If the engine is not ready and `mode === 'cache-first'`, falls back to
92
+ * sync `undefined`-like behavior by returning a promise — caller decides.
93
+ */
94
+ readForRender(policy) {
95
+ const cached = this.peekCache();
96
+ if (cached && policy?.mode !== 'bypass-cache')
97
+ return cached;
98
+ return this.load({ bypassCache: policy?.mode === 'bypass-cache' });
99
+ }
100
+ then(onfulfilled, onrejected) {
101
+ return this.load().then(onfulfilled, onrejected);
102
+ }
103
+ /**
104
+ * Sort the result. Same signature as `Array.prototype.sort`.
105
+ * Sync-only. Does NOT change `cacheKey` — this is post-load derivation.
106
+ */
107
+ sort(compareFn) {
108
+ const next = (rows) => [...rows].sort(compareFn);
109
+ return new QueryResult(this.descriptor, this._engine, [...this._sortChain, next], this._explicitPromise);
110
+ }
111
+ /**
112
+ * Sort by a single field. Default: ascending.
113
+ *
114
+ * Deterministic and serializable, so it lives on the descriptor and
115
+ * participates in `cacheKey`. Two queries with the same shape and the
116
+ * same `orderBy` share a cache entry; differing only by direction or
117
+ * field produces a distinct cache entry.
118
+ *
119
+ * Re-applying `orderBy` overwrites the previous one (last write wins).
120
+ * The sync `.sort(compareFn)` chain is preserved on top of orderBy for
121
+ * post-load derivations.
122
+ */
123
+ orderBy(field, dir = 'asc') {
124
+ const nextDescriptor = {
125
+ ...this.descriptor,
126
+ orderBy: { field: field, dir },
127
+ };
128
+ return new QueryResult(nextDescriptor, this._engine, this._sortChain, this._explicitPromise);
129
+ }
130
+ /**
131
+ * Subscribe to changes that affect this query's table.
132
+ * Returns an unsubscribe function. Currently table-wide; finer-grained
133
+ * invalidation can be added in core later without changing this contract.
134
+ */
135
+ subscribe(cb) {
136
+ if (!this._engine)
137
+ return () => { };
138
+ const engine = this._engine;
139
+ const tableName = this.descriptor.table;
140
+ return engine.on(event => {
141
+ if (event.type === 'change' && event.table === tableName) {
142
+ cb({ type: 'change', rowId: event.rowId, row: rowToTyped(event.row) });
143
+ }
144
+ else if (event.type === 'delete' && event.table === tableName) {
145
+ cb({ type: 'delete', rowId: event.rowId });
146
+ }
147
+ });
148
+ }
149
+ _project(rawRows) {
150
+ if (this._projectionInputRef === rawRows && this._projectionOutput !== null) {
151
+ return this._projectionOutput;
152
+ }
153
+ const typed = rawRows.map(r => rowToTyped(r));
154
+ const sorted = this._applySort(typed);
155
+ this._projectionInputRef = rawRows;
156
+ this._projectionOutput = sorted;
157
+ return sorted;
158
+ }
159
+ _applySort(rows) {
160
+ if (this._sortChain.length === 0)
161
+ return rows;
162
+ let out = rows;
163
+ for (const fn of this._sortChain)
164
+ out = fn(out);
165
+ return out;
166
+ }
167
+ async _loadRaw(options) {
168
+ if (this._explicitPromise)
169
+ return this._explicitPromise;
170
+ if (!this._engine)
171
+ return [];
172
+ const rawRows = await this._engine.loadQueryRows(this.descriptor, options);
173
+ // Use the same projection cache so post-load + peekCache yield the
174
+ // same reference for the same raw rows.
175
+ return this._project(rawRows);
176
+ }
177
+ }
178
+ /**
179
+ * Stable, deterministic cache key derived from a query descriptor.
180
+ *
181
+ * Only async-affecting fields are included. Sync derivations (custom `sort`)
182
+ * are intentionally excluded.
183
+ */
184
+ export function computeCacheKey(descriptor) {
185
+ const parts = [`t=${descriptor.table}`];
186
+ if (descriptor.clause) {
187
+ const c = descriptor.clause;
188
+ parts.push(`w=${c.field}:${c.op}`);
189
+ if (c.value !== undefined)
190
+ parts.push(`v=${serializePrimitive(c.value)}`);
191
+ if (c.values !== undefined)
192
+ parts.push(`vs=${c.values.map(serializePrimitive).join(',')}`);
193
+ if (c.lower !== undefined)
194
+ parts.push(`l=${serializePrimitive(c.lower)}`);
195
+ if (c.upper !== undefined)
196
+ parts.push(`u=${serializePrimitive(c.upper)}`);
197
+ if (c.lowerOpen)
198
+ parts.push('lo=1');
199
+ if (c.upperOpen)
200
+ parts.push('uo=1');
201
+ }
202
+ if (descriptor.orderBy) {
203
+ parts.push(`o=${descriptor.orderBy.field}:${descriptor.orderBy.dir}`);
204
+ }
205
+ return parts.join('|');
206
+ }
207
+ function serializePrimitive(v) {
208
+ if (v instanceof Date)
209
+ return `d:${v.getTime()}`;
210
+ if (typeof v === 'string')
211
+ return `s:${v}`;
212
+ if (typeof v === 'number')
213
+ return `n:${v}`;
214
+ if (typeof v === 'boolean')
215
+ return `b:${v ? 1 : 0}`;
216
+ return `x:${String(v)}`;
217
+ }
218
+ /**
219
+ * Thenable result of a single-row read.
220
+ *
221
+ * Same contract as `QueryResult`, scaled down to one row:
222
+ * - identity is `descriptor` + `cacheKey`
223
+ * - `then(...)` / `load(...)` go through the engine row cache
224
+ * - `peekCache()` / `peekStatus()` are sync probes
225
+ * - `subscribe(cb)` fires on table events that touch the same rowId
226
+ *
227
+ * Returns `undefined` when the row is absent or deleted.
228
+ */
229
+ export class RowResult {
230
+ constructor(descriptor, _engine) {
231
+ this._engine = _engine;
232
+ /**
233
+ * Memoization for the typed projection. Keyed by the raw row reference.
234
+ * Same input ref ⇒ same output ref — required by useSyncExternalStore
235
+ * consumers and by selector memoization in the React hooks.
236
+ */
237
+ this._projectionInputRef = null;
238
+ this._projectionOutput = null;
239
+ this.descriptor = descriptor;
240
+ this.cacheKey = _engine
241
+ ? _engine.getRowCacheKey(descriptor)
242
+ : `r=${descriptor.table}|id=${descriptor.rowId}`;
243
+ }
244
+ get metadata() {
245
+ return { descriptor: this.descriptor, cacheKey: this.cacheKey };
246
+ }
247
+ _project(rawRow) {
248
+ if (!rawRow)
249
+ return undefined;
250
+ if (this._projectionInputRef === rawRow && this._projectionOutput !== null) {
251
+ return this._projectionOutput;
252
+ }
253
+ const typed = rowToTyped(rawRow);
254
+ this._projectionInputRef = rawRow;
255
+ this._projectionOutput = typed;
256
+ return typed;
257
+ }
258
+ load(options) {
259
+ if (!this._engine)
260
+ return Promise.resolve(undefined);
261
+ return this._engine.loadRow(this.descriptor, options).then(r => this._project(r));
262
+ }
263
+ /**
264
+ * Sync cached row, or `undefined` if the cache has no row. Pending/error
265
+ * snapshots may still carry the previous row for stale-while-revalidate.
266
+ */
267
+ peekCache() {
268
+ if (!this._engine)
269
+ return undefined;
270
+ const snap = this._engine.readRowCache(this.descriptor);
271
+ return this._project(snap.row);
272
+ }
273
+ peekStatus() {
274
+ if (!this._engine)
275
+ return { status: 'empty' };
276
+ const snap = this._engine.readRowCache(this.descriptor);
277
+ return { status: snap.status, error: snap.error };
278
+ }
279
+ readForRender(policy) {
280
+ const cached = this.peekCache();
281
+ if (cached !== undefined && policy?.mode !== 'bypass-cache')
282
+ return cached;
283
+ return this.load({ bypassCache: policy?.mode === 'bypass-cache' });
284
+ }
285
+ then(onfulfilled, onrejected) {
286
+ return this.load().then(onfulfilled, onrejected);
287
+ }
288
+ /**
289
+ * Subscribe to changes that touch this row's `rowId`.
290
+ * Returns an unsubscribe function. Filtering done here so callers don't
291
+ * see unrelated table events.
292
+ */
293
+ subscribe(cb) {
294
+ if (!this._engine)
295
+ return () => { };
296
+ const engine = this._engine;
297
+ const { table, rowId } = this.descriptor;
298
+ return engine.on(event => {
299
+ if (event.type === 'change' && event.table === table && event.rowId === rowId) {
300
+ cb({ type: 'change', rowId: event.rowId, row: rowToTyped(event.row) });
301
+ }
302
+ else if (event.type === 'delete' && event.table === table && event.rowId === rowId) {
303
+ cb({ type: 'delete', rowId: event.rowId });
304
+ }
305
+ });
306
+ }
307
+ }
8
308
  function rowToTyped(row) {
309
+ // Project payload only. _meta is engine-private; user types never see it.
9
310
  const result = {};
10
- for (const [key, val] of Object.entries(row)) {
11
- if (key.startsWith('_'))
12
- continue;
13
- if (val !== null && val !== undefined && typeof val === 'object' && 'value' in val && 'hlc' in val) {
14
- result[key] = val.value;
15
- }
311
+ for (const [key, entry] of Object.entries(row.payload)) {
312
+ result[key] = entry.value;
16
313
  }
17
314
  return result;
18
315
  }
19
316
  /**
20
- * Typed handle for a named collection within a {@link SyncEngine}.
317
+ * Typed handle for a named collection within a {@link Interocitor}.
21
318
  *
22
319
  * All reads return plain objects with CRDT metadata stripped away.
23
320
  *
@@ -35,29 +332,88 @@ export class Table {
35
332
  this.engine = engine;
36
333
  this.name = name;
37
334
  }
38
- /** Retrieve a single record by ID, or undefined if not found / deleted. */
39
- async get(rowId) {
40
- const row = await this.engine.get(this.name, rowId);
41
- return row ? rowToTyped(row) : undefined;
335
+ /**
336
+ * Build a single-row handle. Same descriptor + cacheKey contract as
337
+ * `query()`, scaled down to one row. Lazy: nothing fetched until
338
+ * `then()` / `load()` / `readForRender()` is called.
339
+ *
340
+ * Use `await table.row(id)` for an async fetch, `table.row(id).peekCache()`
341
+ * for a sync cache read, or pass the handle directly to `useRow`.
342
+ */
343
+ row(rowId) {
344
+ return new RowResult({ table: this.name, rowId }, this.engine);
42
345
  }
43
346
  /** Retrieve all live (non-deleted) records in this collection. */
44
- async query() {
45
- const rows = await this.engine.query(this.name);
46
- return rows.map(r => rowToTyped(r));
347
+ query() {
348
+ return new QueryResult({ table: this.name }, this.engine);
47
349
  }
48
350
  /** Build a field-scoped where query (Dexie-style, without string schema syntax). */
49
351
  where(field) {
50
352
  return new TableWhere(this.engine, this.name, field);
51
353
  }
52
- /** Insert or update a record. Returns the merged result. */
53
- async put(rowId, data, userId) {
354
+ /**
355
+ * Patch a record — only the provided fields are updated.
356
+ * Omitted fields are untouched (CRDT per-field merge).
357
+ * Returns the full merged row.
358
+ */
359
+ async patch(rowId, data, userId) {
54
360
  const row = await this.engine.put(this.name, rowId, data, userId);
55
361
  return rowToTyped(row);
56
362
  }
363
+ /** Back-compat alias for older code paths. */
364
+ async put(rowId, data, userId) {
365
+ return this.patch(rowId, data, userId);
366
+ }
367
+ /**
368
+ * Replace a record — writes ALL fields in `data`, explicitly nulling any
369
+ * fields present in the existing row but absent from `data`.
370
+ * Returns the full row.
371
+ */
372
+ async replace(rowId, data, userId) {
373
+ // Iterate ONLY existing payload keys. Meta is in a separate namespace
374
+ // and must never appear as a "field" to be nulled.
375
+ const existing = await this.engine.loadRow({ table: this.name, rowId });
376
+ const existingPayloadKeys = existing ? Object.keys(existing.payload) : [];
377
+ const nulled = Object.fromEntries(existingPayloadKeys.filter(k => !(k in data)).map(k => [k, null]));
378
+ const row = await this.engine.put(this.name, rowId, { ...nulled, ...data }, userId);
379
+ return rowToTyped(row);
380
+ }
381
+ /**
382
+ * Insert a new record with an auto-generated row ID.
383
+ * Returns the generated ID.
384
+ *
385
+ * @example
386
+ * const id = await table.add({ title: 'Buy milk' });
387
+ * const id = await table.add({ title: 'Buy milk' }, { prefix: 'task' });
388
+ */
389
+ async add(data, opts, userId) {
390
+ const id = createRowId({ prefix: opts?.prefix });
391
+ await this.engine.put(this.name, id, data, userId);
392
+ return id;
393
+ }
57
394
  /** Soft-delete a record. */
58
395
  async delete(rowId, userId) {
59
396
  return this.engine.delete(this.name, rowId, userId);
60
397
  }
398
+ /**
399
+ * Subscribe to changes in this table. Fires on every change/delete.
400
+ * Returns an unsubscribe function.
401
+ *
402
+ * @example
403
+ * const unsub = db.table('tasks').subscribe(event => {
404
+ * if (event.type === 'change') console.log(event.row);
405
+ * });
406
+ */
407
+ subscribe(cb) {
408
+ return this.engine.on(event => {
409
+ if (event.type === 'change' && event.table === this.name) {
410
+ cb({ type: 'change', rowId: event.rowId, row: rowToTyped(event.row) });
411
+ }
412
+ else if (event.type === 'delete' && event.table === this.name) {
413
+ cb({ type: 'delete', rowId: event.rowId });
414
+ }
415
+ });
416
+ }
61
417
  }
62
418
  class TableWhere {
63
419
  constructor(engine, table, field) {
@@ -65,12 +421,9 @@ class TableWhere {
65
421
  this.table = table;
66
422
  this.field = field;
67
423
  }
68
- async run(clause) {
69
- const rows = await this.engine.queryWhere(this.table, {
70
- field: this.field,
71
- ...clause,
72
- });
73
- return rows.map(row => rowToTyped(row));
424
+ run(clause) {
425
+ const fullClause = { field: this.field, ...clause };
426
+ return new QueryResult({ table: this.table, clause: fullClause }, this.engine);
74
427
  }
75
428
  equals(value) {
76
429
  return this.run({ op: 'equals', value });
@@ -103,4 +456,3 @@ class TableWhere {
103
456
  return this.run({ op: 'anyOf', values });
104
457
  }
105
458
  }
106
- //# sourceMappingURL=table.js.map