@orkestrel/table 0.0.11 → 0.0.13

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.
@@ -266,7 +266,7 @@ export declare interface EqualsFilter {
266
266
  * ```
267
267
  */
268
268
  export declare interface ExpansionManagerInterface {
269
- /** Holds the keys of the rows opened right now. */
269
+ /** Holds the keys of the rows opened. */
270
270
  readonly keys: ReadonlySet<TableKey>;
271
271
  /** Opens every row the table holds, one row, or several. */
272
272
  expand(): void;
@@ -488,6 +488,10 @@ export declare function isTableColumn(input: unknown): input is TableColumn;
488
488
  * Determines whether a caught value is a table error, so a `catch` branches on `code` without an
489
489
  * assertion.
490
490
  *
491
+ * @remarks
492
+ * Recognizes self-branded errors across copies and subclasses and refuses transparent proxies.
493
+ * Validates the declared code and optional JSON context.
494
+ *
491
495
  * @param input - The value to inspect.
492
496
  * @returns True if the value is a {@link TableError} instance; false otherwise.
493
497
  */
@@ -611,7 +615,7 @@ export declare interface PaginationManagerInterface {
611
615
  * argument out to stop paging.
612
616
  *
613
617
  * @remarks
614
- * The page moves to wherever that first row now falls.
618
+ * The page moves to wherever that first row falls.
615
619
  *
616
620
  * @param limit - How many rows a page holds. Leave it out to stop paging, and the view shows
617
621
  * every row the filter admits.
@@ -773,7 +777,7 @@ export declare interface RowManagerInterface {
773
777
  * ```
774
778
  */
775
779
  export declare interface SelectionManagerInterface {
776
- /** Holds the keys of the rows picked right now. */
780
+ /** Holds the keys of the rows picked. */
777
781
  readonly keys: ReadonlySet<TableKey>;
778
782
  /**
779
783
  * Picks every row the table holds, one row, or several. Every row, not every visible one.
@@ -1054,10 +1058,10 @@ export declare type TableErrorCode = 'SCHEMA' | 'COLUMN' | 'KEY' | 'CELL' | 'DES
1054
1058
  * `write` and `remove` carry one key and fire once per row, in the order the table wrote or
1055
1059
  * removed them. A listener that wants the row reads it back.
1056
1060
  *
1057
- * `sort` and `filter` carry the whole axis as it now stands, and `select` and `expand` carry the
1061
+ * `sort` and `filter` carry the whole axis as it stands, and `select` and `expand` carry the
1058
1062
  * whole key set. Each payload is owned by the listener, so nothing it holds moves underneath it.
1059
1063
  *
1060
- * `paginate` carries the page the table now shows, and fires whenever the paged window moves,
1064
+ * `paginate` carries the page the table shows, and fires whenever the paged window moves,
1061
1065
  * including a clamp and a change of page size.
1062
1066
  *
1063
1067
  * `clear` is a signal, and it is the whole announcement of that reset: a clear emits no `remove`,
@@ -1102,7 +1106,7 @@ export declare type TableFilter = ContainsFilter | BetweenFilter | EqualsFilter;
1102
1106
  * `pagination`. Nothing else is stored. `view` and `count` are worked out when they are read, so no
1103
1107
  * second copy of the answer can go stale.
1104
1108
  *
1105
- * A write validates all of itself before any of it lands, and announces itself once it has.
1109
+ * A write validates all of itself before any of it lands, and announces itself after it has.
1106
1110
  *
1107
1111
  * @example
1108
1112
  * ```ts
@@ -1129,7 +1133,7 @@ export declare interface TableInterface {
1129
1133
  /** Manages which stretch of them the view shows. */
1130
1134
  readonly pagination: PaginationManagerInterface;
1131
1135
  /**
1132
- * Returns the rows to draw right now: filtered, then sorted, then paged.
1136
+ * Returns the rows to draw: filtered, then sorted, then paged.
1133
1137
  *
1134
1138
  * @remarks
1135
1139
  * It is worked out on every read and never stored, so it is right the instant anything moves.
@@ -1,6 +1,6 @@
1
- import { arrayOf, attempt, cloneJSONRecord, isArray, isBoolean, isBoundedJSONRecord, isContractError, isFiniteNumber, isInstance, isRecord, isString, parseNumber, readArrayEntries, recordOf, unionOf } from "@orkestrel/contract";
1
+ import { GUARD_DEPTH_LIMIT, INTRINSICS, arrayOf, attempt, cloneJSONRecord, holds, isArray, isBoolean, isBoundedJSONRecord, isContractError, isFiniteNumber, isRecord, isString, parseNumber, readArrayEntries, recordOf, unionOf } from "@orkestrel/contract";
2
2
  import { Emitter } from "@orkestrel/emitter";
3
- //#region src/core/constants.ts
3
+ // src/core/constants.ts
4
4
  /** Lists every column cell, in the order declared by the public contract. */
5
5
  var COLUMN_CELLS = Object.freeze([
6
6
  "text",
@@ -22,8 +22,7 @@ var STRING_LIMIT = 65536;
22
22
  var TEXT_LIMIT = 1048576;
23
23
  /** Names the maximum total number of records, arrays, and leaves one schema retains: 16384. */
24
24
  var NODE_LIMIT = 16384;
25
- //#endregion
26
- //#region src/core/errors.ts
25
+ // src/core/errors.ts
27
26
  /**
28
27
  * Represents an error raised by the table domain — a machine-readable `code` and optional
29
28
  * structured `context`.
@@ -42,6 +41,7 @@ var TableError = class extends Error {
42
41
  */
43
42
  constructor(code, message, context) {
44
43
  super(message);
44
+ INTRINSICS.define(this, Symbol.for("@orkestrel/table.TableError"), { value: this });
45
45
  this.name = "TableError";
46
46
  this.code = code;
47
47
  if (context !== void 0) this.context = context;
@@ -51,14 +51,45 @@ var TableError = class extends Error {
51
51
  * Determines whether a caught value is a table error, so a `catch` branches on `code` without an
52
52
  * assertion.
53
53
  *
54
+ * @remarks
55
+ * Recognizes self-branded errors across copies and subclasses and refuses transparent proxies.
56
+ * Validates the declared code and optional JSON context.
57
+ *
54
58
  * @param input - The value to inspect.
55
59
  * @returns True if the value is a {@link TableError} instance; false otherwise.
56
60
  */
57
61
  function isTableError(input) {
58
- return isInstance(input, TableError);
62
+ return holds(() => {
63
+ if (!(input instanceof Error) || INTRINSICS.prototype(input) === Error.prototype) return false;
64
+ if (INTRINSICS.describe(input, Symbol.for("@orkestrel/table.TableError"))?.value !== input) return false;
65
+ const code = INTRINSICS.reflect.read(input, "code");
66
+ const context = INTRINSICS.reflect.read(input, "context");
67
+ if (code !== "SCHEMA" && code !== "COLUMN" && code !== "KEY" && code !== "CELL" && code !== "DESTROYED") return false;
68
+ if (context === void 0) return true;
69
+ if (!isRecord(context)) return false;
70
+ const pending = [{
71
+ value: context,
72
+ ancestors: []
73
+ }];
74
+ for (const { value, ancestors } of pending) {
75
+ if (value === null || [
76
+ "string",
77
+ "number",
78
+ "boolean"
79
+ ].includes(typeof value)) continue;
80
+ if (!Array.isArray(value) && !isRecord(value)) return false;
81
+ if (ancestors.length >= GUARD_DEPTH_LIMIT || ancestors.includes(value)) return false;
82
+ const path = [...ancestors, value];
83
+ const children = Array.isArray(value) ? Array.from({ length: value.length }, (_, index) => value[index]) : Object.values(value);
84
+ for (const child of children) pending.push({
85
+ value: child,
86
+ ancestors: path
87
+ });
88
+ }
89
+ return true;
90
+ });
59
91
  }
60
- //#endregion
61
- //#region src/core/helpers.ts
92
+ // src/core/helpers.ts
62
93
  /**
63
94
  * Finds one column by key; `undefined` when the schema declares no such column.
64
95
  *
@@ -281,88 +312,102 @@ function sortRows(schema, rows, orders, comparators) {
281
312
  */
282
313
  function auditTable(schema) {
283
314
  const faults = [];
284
- const columns = /* @__PURE__ */ new Set();
285
- let choiceExceeded;
286
- let nameExceeded = schema.name !== void 0 && schema.name.length > 128;
315
+ const columns = [];
316
+ const longName = schema.name !== void 0 && schema.name.length > 128;
287
317
  if (schema.columns.length > 256) faults.push(`schema declares more than 256 columns`);
288
318
  const columnCount = Math.min(schema.columns.length, 257);
289
- for (let index = 0; index < columnCount; index += 1) {
319
+ const indices = Array.from({ length: columnCount }, (_, index) => index);
320
+ const [choiceExceeded, nameExceeded] = indices.reduce(([choice, name], index) => {
290
321
  const column = schema.columns[index];
291
- if (column === void 0) continue;
292
- if (column.key.length > 128) nameExceeded = true;
293
- if (choiceExceeded === void 0 && column.cell === "choice" && column.choices.length > 1024) choiceExceeded = column.key;
294
- }
322
+ if (column === void 0) return [choice, name];
323
+ const exceeded = column.key.length > 128;
324
+ return [choice === void 0 && column.cell === "choice" && column.choices.length > 1024 ? column.key : choice, name || exceeded];
325
+ }, [void 0, longName]);
295
326
  if (choiceExceeded !== void 0) faults.push(`column "${choiceExceeded}" offers more than ${CHOICE_LIMIT} choices`);
296
327
  if (nameExceeded) faults.push(`schema contains a name longer than 128`);
297
328
  const pending = [schema];
298
329
  const metadata = [false];
299
- let position = 0;
300
- let stringExceeded = false;
301
- let textExceeded = false;
302
- let nodeExceeded = false;
303
- let text = 0;
304
- while (position < pending.length) {
305
- const node = pending[position];
330
+ const measurements = [];
331
+ for (const [position, node] of pending.entries()) {
306
332
  const inMeta = metadata[position] === true;
307
- position += 1;
308
333
  if (isString(node)) {
309
- if (node.length > 65536) stringExceeded = true;
310
- text = Math.min(TEXT_LIMIT + 1, text + node.length);
311
- if (text > 1048576) textExceeded = true;
334
+ measurements.push([
335
+ node.length,
336
+ node.length > STRING_LIMIT,
337
+ false
338
+ ]);
312
339
  continue;
313
340
  }
314
341
  if (isArray(node)) {
315
342
  const read = readArrayEntries(node);
316
343
  if (!read.success || !read.value.dense) continue;
317
- for (const entry of read.value.entries) {
318
- if (pending.length >= 16384) {
319
- nodeExceeded = true;
320
- continue;
321
- }
344
+ const exceeded = read.value.entries.reduce((refused, entry) => {
345
+ if (pending.length >= 16384) return true;
322
346
  pending.push(entry);
323
347
  metadata.push(inMeta);
324
- }
348
+ return refused;
349
+ }, false);
350
+ measurements.push([
351
+ 0,
352
+ false,
353
+ exceeded
354
+ ]);
325
355
  continue;
326
356
  }
327
357
  if (!isRecord(node)) continue;
328
358
  const keys = attempt(() => Object.keys(node));
329
359
  if (!keys.success) continue;
330
- for (const key of keys.value) {
331
- if (inMeta) {
332
- if (key.length > 65536) stringExceeded = true;
333
- text = Math.min(TEXT_LIMIT + 1, text + key.length);
334
- if (text > 1048576) textExceeded = true;
335
- }
360
+ const measured = keys.value.reduce(([text, string, refused], key) => {
361
+ const length = text + (inMeta ? key.length : 0);
362
+ const oversized = string || inMeta && key.length > 65536;
336
363
  const value = attempt(() => node[key]);
337
- if (!value.success || value.value === void 0) continue;
338
- if (pending.length >= 16384) {
339
- nodeExceeded = true;
340
- continue;
341
- }
364
+ if (!value.success || value.value === void 0) return [
365
+ length,
366
+ oversized,
367
+ refused
368
+ ];
369
+ if (pending.length >= 16384) return [
370
+ length,
371
+ oversized,
372
+ true
373
+ ];
342
374
  pending.push(value.value);
343
375
  metadata.push(inMeta || key === "meta");
344
- }
345
- }
376
+ return [
377
+ length,
378
+ oversized,
379
+ refused
380
+ ];
381
+ }, [
382
+ 0,
383
+ false,
384
+ false
385
+ ]);
386
+ measurements.push(measured);
387
+ }
388
+ const stringExceeded = measurements.some((entry) => entry[1]);
389
+ const textExceeded = measurements.reduce((total, entry) => total + entry[0], 0) > TEXT_LIMIT;
390
+ const nodeExceeded = measurements.some((entry) => entry[2]);
346
391
  if (stringExceeded) faults.push(`schema contains a string longer than ${STRING_LIMIT}`);
347
392
  if (textExceeded) faults.push(`schema retains more than ${TEXT_LIMIT} string code units`);
348
393
  if (nodeExceeded) faults.push(`schema retains more than ${NODE_LIMIT} nodes`);
349
- for (let index = 0; index < columnCount; index += 1) {
394
+ for (const index of indices) {
350
395
  const column = schema.columns[index];
351
396
  if (column === void 0) continue;
352
397
  if (!nodeExceeded && column.meta !== void 0) {
353
398
  if (!attempt(() => cloneJSONRecord(column.meta)).success) faults.push(`column "${column.key}" has metadata that cannot be owned`);
354
399
  }
355
400
  if (column.key.length === 0) faults.push("column \"\" has an empty key");
356
- if (columns.has(column.key)) faults.push(`column "${column.key}" is declared more than once`);
357
- columns.add(column.key);
401
+ if (columns.includes(column.key)) faults.push(`column "${column.key}" is declared more than once`);
402
+ columns.push(column.key);
358
403
  if (column.cell === "choice") {
359
- const choices = /* @__PURE__ */ new Set();
404
+ const choices = [];
360
405
  const choiceCount = Math.min(column.choices.length, CHOICE_LIMIT + 1);
361
- for (let choiceIndex = 0; choiceIndex < choiceCount; choiceIndex += 1) {
406
+ for (const choiceIndex of Array.from({ length: choiceCount }, (_, position) => position)) {
362
407
  const choice = column.choices[choiceIndex];
363
408
  if (choice === void 0) continue;
364
- if (choices.has(choice.value)) faults.push(`column "${column.key}" offers choice "${choice.value}" more than once`);
365
- choices.add(choice.value);
409
+ if (choices.includes(choice.value)) faults.push(`column "${column.key}" offers choice "${choice.value}" more than once`);
410
+ choices.push(choice.value);
366
411
  }
367
412
  if (column.choices.length === 0) faults.push(`column "${column.key}" offers no choices`);
368
413
  }
@@ -447,8 +492,7 @@ function serializeRows(schema, rows) {
447
492
  function matchesTableRow(left, right) {
448
493
  return left !== void 0 && Object.keys(left).length === Object.keys(right).length && Object.keys(right).every((key) => Object.hasOwn(left, key) && left[key] === right[key]);
449
494
  }
450
- //#endregion
451
- //#region src/core/validators.ts
495
+ // src/core/validators.ts
452
496
  /**
453
497
  * Determines whether an unknown value has a table cell shape — a string, a finite number, or a
454
498
  * boolean.
@@ -579,8 +623,7 @@ function isTableSchema(input) {
579
623
  const outcome = attempt(() => isStructuralTableSchema(input) && auditTable(input).length === 0);
580
624
  return outcome.success && outcome.value;
581
625
  }
582
- //#endregion
583
- //#region src/core/cloners.ts
626
+ // src/core/cloners.ts
584
627
  /**
585
628
  * Clones one row into an owned frozen snapshot.
586
629
  *
@@ -601,13 +644,12 @@ function cloneSchema(schema) {
601
644
  return Object.freeze({
602
645
  ...schema,
603
646
  columns: Object.freeze(schema.columns.map((column) => {
604
- let meta = {};
605
- if (column.meta !== void 0) try {
606
- meta = { meta: cloneJSONRecord(column.meta) };
607
- } catch (error) {
608
- if (!isContractError(error)) throw error;
647
+ const owned = column.meta === void 0 ? void 0 : attempt(() => cloneJSONRecord(column.meta));
648
+ if (owned !== void 0 && !owned.success) {
649
+ if (!isContractError(owned.error)) throw owned.error;
609
650
  throw new TableError("SCHEMA", `column "${column.key}" has metadata that cannot be owned`, { column: column.key });
610
651
  }
652
+ const meta = owned === void 0 ? {} : { meta: owned.value };
611
653
  if (column.cell === "choice") return Object.freeze({
612
654
  ...column,
613
655
  ...meta,
@@ -620,8 +662,7 @@ function cloneSchema(schema) {
620
662
  }))
621
663
  });
622
664
  }
623
- //#endregion
624
- //#region src/core/parsers.ts
665
+ // src/core/parsers.ts
625
666
  /**
626
667
  * Parses unknown wire data into an owned, structurally valid, semantically sound table schema.
627
668
  *
@@ -659,12 +700,8 @@ function parseRows(schema, input) {
659
700
  const column = extractColumn(schema, key);
660
701
  if (column === void 0) return void 0;
661
702
  const inputCell = candidate[key];
662
- let cell = inputCell;
663
- if (column.cell === "number" && isString(inputCell)) {
664
- if (inputCell.length > 65536) return void 0;
665
- cell = parseNumber(inputCell);
666
- } else if (column.cell === "flag" && inputCell === "true") cell = true;
667
- else if (column.cell === "flag" && inputCell === "false") cell = false;
703
+ if (column.cell === "number" && isString(inputCell) && inputCell.length > 65536) return void 0;
704
+ const cell = column.cell === "number" && isString(inputCell) ? parseNumber(inputCell) : column.cell === "flag" && inputCell === "true" ? true : column.cell === "flag" && inputCell === "false" ? false : inputCell;
668
705
  if (!matchesCell(column, cell)) return void 0;
669
706
  Object.defineProperty(row, key, {
670
707
  value: cell,
@@ -683,8 +720,7 @@ function parseRows(schema, input) {
683
720
  });
684
721
  return outcome.success ? outcome.value : void 0;
685
722
  }
686
- //#endregion
687
- //#region src/core/tables/KeyManager.ts
723
+ // src/core/tables/KeyManager.ts
688
724
  /** Manages the key set one table axis holds, and the event it announces when that set moves. */
689
725
  var KeyManager = class {
690
726
  #emitter;
@@ -711,7 +747,7 @@ var KeyManager = class {
711
747
  this.#read = read;
712
748
  this.#write = write;
713
749
  }
714
- /** Returns the keys held right now. */
750
+ /** Returns the keys held. */
715
751
  get keys() {
716
752
  return new Set(this.#read());
717
753
  }
@@ -735,8 +771,7 @@ var KeyManager = class {
735
771
  return input === void 0 ? void 0 : true;
736
772
  }
737
773
  };
738
- //#endregion
739
- //#region src/core/tables/ExpansionManager.ts
774
+ // src/core/tables/ExpansionManager.ts
740
775
  /** Manages the keys of the rows somebody has opened. */
741
776
  var ExpansionManager = class {
742
777
  #keys;
@@ -752,7 +787,7 @@ var ExpansionManager = class {
752
787
  constructor(emitter, gate, rows, read, write) {
753
788
  this.#keys = new KeyManager(emitter, "expand", gate, rows, read, write);
754
789
  }
755
- /** Returns the keys of the rows opened right now. */
790
+ /** Returns the keys of the rows opened. */
756
791
  get keys() {
757
792
  return this.#keys.keys;
758
793
  }
@@ -769,8 +804,7 @@ var ExpansionManager = class {
769
804
  return this.#keys.change(input, (included) => !included) === true;
770
805
  }
771
806
  };
772
- //#endregion
773
- //#region src/core/tables/FilterManager.ts
807
+ // src/core/tables/FilterManager.ts
774
808
  /** Manages the filters one table applies with and-only composition. */
775
809
  var FilterManager = class {
776
810
  #schema;
@@ -844,8 +878,7 @@ var FilterManager = class {
844
878
  return left.operator === "equals" && right.operator === "equals" && left.value === right.value;
845
879
  }
846
880
  };
847
- //#endregion
848
- //#region src/core/tables/PaginationManager.ts
881
+ // src/core/tables/PaginationManager.ts
849
882
  /** Manages the page arithmetic over one table's filtered rows. */
850
883
  var PaginationManager = class {
851
884
  #emitter;
@@ -919,8 +952,7 @@ var PaginationManager = class {
919
952
  return isFiniteNumber(value) ? Math.max(1, Math.trunc(value)) : 1;
920
953
  }
921
954
  };
922
- //#endregion
923
- //#region src/core/tables/RowManager.ts
955
+ // src/core/tables/RowManager.ts
924
956
  /** Manages the rows one table holds in its own order. */
925
957
  var RowManager = class {
926
958
  #schema;
@@ -994,8 +1026,7 @@ var RowManager = class {
994
1026
  }
995
1027
  const next = [...current];
996
1028
  const moved = [];
997
- for (let index = 0; index < updates.length; index += 1) {
998
- const update = updates[index];
1029
+ for (const [index, update] of updates.entries()) {
999
1030
  const location = locations[index];
1000
1031
  if (update === void 0 || location === void 0) continue;
1001
1032
  const previous = next[location];
@@ -1121,8 +1152,7 @@ var RowManager = class {
1121
1152
  throw new TableError("KEY", message, { key });
1122
1153
  }
1123
1154
  };
1124
- //#endregion
1125
- //#region src/core/tables/SelectionManager.ts
1155
+ // src/core/tables/SelectionManager.ts
1126
1156
  /** Manages the keys of the rows somebody has picked. */
1127
1157
  var SelectionManager = class {
1128
1158
  #keys;
@@ -1138,7 +1168,7 @@ var SelectionManager = class {
1138
1168
  constructor(emitter, gate, rows, read, write) {
1139
1169
  this.#keys = new KeyManager(emitter, "select", gate, rows, read, write);
1140
1170
  }
1141
- /** Returns the keys of the rows picked right now. */
1171
+ /** Returns the keys of the rows picked. */
1142
1172
  get keys() {
1143
1173
  return this.#keys.keys;
1144
1174
  }
@@ -1155,8 +1185,7 @@ var SelectionManager = class {
1155
1185
  return this.#keys.change(input, (included) => !included) === true;
1156
1186
  }
1157
1187
  };
1158
- //#endregion
1159
- //#region src/core/tables/SortManager.ts
1188
+ // src/core/tables/SortManager.ts
1160
1189
  /** Manages the ordered sort terms of one table. */
1161
1190
  var SortManager = class {
1162
1191
  #schema;
@@ -1215,8 +1244,7 @@ var SortManager = class {
1215
1244
  if (extractColumn(this.#schema, column) === void 0) throw new TableError("COLUMN", `The schema declares no column named "${column}"`, { column });
1216
1245
  }
1217
1246
  };
1218
- //#endregion
1219
- //#region src/core/Table.ts
1247
+ // src/core/Table.ts
1220
1248
  /**
1221
1249
  * Holds a schema, its rows, and the lens through which they are read, implementing
1222
1250
  * {@link TableInterface} exactly.
@@ -1385,8 +1413,7 @@ var Table = class {
1385
1413
  if (this.#destroyed) throw new TableError("DESTROYED", "The table was destroyed and cannot change");
1386
1414
  }
1387
1415
  };
1388
- //#endregion
1389
- //#region src/core/factories.ts
1416
+ // src/core/factories.ts
1390
1417
  /**
1391
1418
  * Opens a table against a schema. The schema is copied, and the copy is what the table declares.
1392
1419
  *
@@ -1430,7 +1457,6 @@ var Table = class {
1430
1457
  function createTable(schema, options) {
1431
1458
  return new Table(schema, options);
1432
1459
  }
1433
- //#endregion
1434
1460
  export { CHOICE_LIMIT, COLUMN_CELLS, COLUMN_LIMIT, NAME_LIMIT, NODE_LIMIT, STRING_LIMIT, TEXT_LIMIT, Table, TableError, admitsFilter, auditTable, cloneRow, cloneSchema, compareCells, computeKeys, createTable, extractColumn, extractKey, filterRows, isColumnCell, isColumnChoice, isStructuralTableSchema, isTableCell, isTableColumn, isTableError, isTableRow, isTableSchema, matchesCell, matchesFilter, matchesTableRow, matchesTerms, mergeTerms, parseRows, parseTable, removeTerms, serializeRows, serializeTable, sortRows };
1435
1461
 
1436
1462
  //# sourceMappingURL=index.js.map