@orkestrel/table 0.0.12 → 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.
@@ -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;
@@ -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;
@@ -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;
@@ -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