@orkestrel/table 0.0.4 → 0.0.6

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.
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # @orkestrel/table
2
2
 
3
- The environment-agnostic tabular document for the `@orkestrel` line a schema of typed column cells,
4
- the rows held against it, and one lens of sort, filter, and page that decides which of them the view
5
- shows. A grid, a report, a terminal listing, and a CSV export hold the same thing in different
6
- places, so this package ships what they share and draws none of it. Every row carries its own
7
- identity in a column the schema names, so a pick survives a re-sort; `view` and every tally are
8
- worked out on read, so no second copy of an answer can go stale; and budgets bound what one
9
- schema may retain, so a document that arrives from a wire costs a known maximum before anything
10
- decides to trust it.
11
- Built on `@orkestrel/contract` and `@orkestrel/emitter`.
3
+ > The environment-agnostic tabular document: a `TableSchema` declaring the columns, a `Table`
4
+ > holding the rows given against it, and one lens of sort, filter, and page deciding which of them
5
+ > the view shows.
6
+
7
+ Open a table with the `createTable` function, seed or add its rows through `table.rows`, and read
8
+ `table.view` for the rows to draw. Every row carries its own identity in a column the schema names,
9
+ so a pick survives a re-sort, and budgets bound what one schema may retain, so a document that
10
+ arrives from a wire costs a known maximum before anything decides to trust it. Built on
11
+ `@orkestrel/contract` and `@orkestrel/emitter`, and part of the `@orkestrel` line.
12
12
 
13
13
  ## Install
14
14
 
@@ -23,8 +23,6 @@ npm install @orkestrel/table
23
23
 
24
24
  ## Usage
25
25
 
26
- Declare the columns, hold the rows, and read the ones to draw:
27
-
28
26
  ```ts
29
27
  import { createTable } from '@orkestrel/table'
30
28
 
@@ -9,21 +9,26 @@ var COLUMN_CELLS = Object.freeze([
9
9
  "flag",
10
10
  "choice"
11
11
  ]);
12
- /** Names the maximum number of columns one schema may declare. */
12
+ /** Names the maximum number of columns one schema may declare: 256. */
13
13
  var COLUMN_LIMIT = 256;
14
- /** Names the maximum number of choices one `choice` column may offer. */
14
+ /** Names the maximum number of choices one `choice` column may offer: 1024. */
15
15
  var CHOICE_LIMIT = 1024;
16
- /** Names the maximum length, in UTF-16 code units, of a schema name or column key. */
16
+ /** Names the maximum length of a schema name or column key: 128 UTF-16 code units. */
17
17
  var NAME_LIMIT = 128;
18
- /** Names the maximum length, in UTF-16 code units, of any single retained string. */
18
+ /** Names the maximum length of any single retained string: 65536 UTF-16 code units. */
19
19
  var STRING_LIMIT = 65536;
20
- /** Names the maximum total length, in UTF-16 code units, of every string one schema retains. */
20
+ /**
21
+ * Names the maximum total length of every string one schema retains: 1048576 UTF-16 code units.
22
+ */
21
23
  var TEXT_LIMIT = 1048576;
22
- /** Names the maximum total number of records, arrays, and leaves one schema retains. */
24
+ /** Names the maximum total number of records, arrays, and leaves one schema retains: 16384. */
23
25
  var NODE_LIMIT = 16384;
24
26
  //#endregion
25
27
  //#region src/core/errors.ts
26
- /** Represents an error raised by the table domain. */
28
+ /**
29
+ * Represents an error raised by the table domain — a machine-readable `code` and optional
30
+ * structured `context`.
31
+ */
27
32
  var TableError = class extends Error {
28
33
  /** Holds the machine-readable reason for this failure. */
29
34
  code;
@@ -44,18 +49,19 @@ var TableError = class extends Error {
44
49
  }
45
50
  };
46
51
  /**
47
- * Determines whether an unknown value is a table error.
52
+ * Determines whether a caught value is a table error, so a `catch` branches on `code` without an
53
+ * assertion.
48
54
  *
49
55
  * @param input - The value to inspect.
50
56
  * @returns True if the value is a {@link TableError} instance; false otherwise.
51
57
  */
52
58
  function isTableError(input) {
53
- return input instanceof TableError;
59
+ return (0, _orkestrel_contract.isInstance)(input, TableError);
54
60
  }
55
61
  //#endregion
56
62
  //#region src/core/helpers.ts
57
63
  /**
58
- * Finds one column by key.
64
+ * Finds one column by key; `undefined` when the schema declares no such column.
59
65
  *
60
66
  * @param schema - The schema whose columns to search.
61
67
  * @param key - The column key to find.
@@ -65,7 +71,8 @@ function extractColumn(schema, key) {
65
71
  return schema.columns.find((column) => column.key === key);
66
72
  }
67
73
  /**
68
- * Reads one row's declared identity.
74
+ * Reads one row's declared identity; `undefined` when its key cell is missing, empty, or not a
75
+ * string.
69
76
  *
70
77
  * @param schema - The schema that names the identity column.
71
78
  * @param row - The row whose identity to read.
@@ -77,7 +84,8 @@ function extractKey(schema, row) {
77
84
  return (0, _orkestrel_contract.isString)(key) && key.length > 0 ? key : void 0;
78
85
  }
79
86
  /**
80
- * Computes one atomic 0/1/N membership change over known keys.
87
+ * Computes one atomic 0/1/N membership change over the keys a caller may address — the engine
88
+ * selection and expansion share.
81
89
  *
82
90
  * @param known - Every key the caller may change.
83
91
  * @param current - The current key set.
@@ -87,7 +95,7 @@ function extractKey(schema, row) {
87
95
  * set when membership changes.
88
96
  */
89
97
  function computeKeys(known, current, input, include) {
90
- const requested = input === void 0 ? known : Array.isArray(input) ? input : [input];
98
+ const requested = input === void 0 ? known : (0, _orkestrel_contract.isArray)(input) ? input : [input];
91
99
  const population = new Set(known);
92
100
  if (requested.some((key) => !population.has(key))) return void 0;
93
101
  const next = new Set(current);
@@ -96,7 +104,8 @@ function computeKeys(known, current, input, include) {
96
104
  return next.size !== current.size || [...next].some((key) => !current.has(key)) ? next : current;
97
105
  }
98
106
  /**
99
- * Merges lens terms into a column-keyed list, replacing the entry that names the same column.
107
+ * Merges lens terms into a column-keyed list, replacing the entry that names the same column
108
+ * the `set` write.
100
109
  *
101
110
  * @param current - The list as it stands.
102
111
  * @param requested - The terms to write, in the order they are written.
@@ -114,7 +123,8 @@ function mergeTerms(current, requested) {
114
123
  return Object.freeze(next);
115
124
  }
116
125
  /**
117
- * Removes every lens term naming one of the given columns.
126
+ * Removes every lens term naming one of the given columns — the drop `sort.remove` and
127
+ * `filter.remove` share.
118
128
  *
119
129
  * @param current - The list as it stands.
120
130
  * @param columns - The column keys to drop.
@@ -125,7 +135,8 @@ function removeTerms(current, columns) {
125
135
  return Object.freeze(current.filter((term) => !removed.has(term.column)));
126
136
  }
127
137
  /**
128
- * Checks whether two lens lists hold the same terms in the same order.
138
+ * Checks whether two lens lists hold the same terms in the same order, with the supplied test
139
+ * deciding the operands.
129
140
  *
130
141
  * @param left - The first list.
131
142
  * @param right - The second list.
@@ -140,7 +151,8 @@ function matchesTerms(left, right, equal) {
140
151
  });
141
152
  }
142
153
  /**
143
- * Checks whether a value has the shape required by one column cell.
154
+ * Checks whether one column can hold a value the shape gate every write and every seed passes
155
+ * through.
144
156
  *
145
157
  * @param column - The column that owns the cell.
146
158
  * @param value - The unknown value to inspect.
@@ -156,7 +168,7 @@ function matchesCell(column, value) {
156
168
  }
157
169
  }
158
170
  /**
159
- * Compares two cells in ascending order according to one column.
171
+ * Compares two of one column's cells the way its `cell` fixes, describing ascending order.
160
172
  *
161
173
  * @param column - The column that fixes the comparison.
162
174
  * @param left - The first cell, or absence.
@@ -182,7 +194,8 @@ function compareCells(column, left, right) {
182
194
  }
183
195
  }
184
196
  /**
185
- * Checks whether one column admits a filter and all its operands.
197
+ * Checks whether one column admits a filter and every operand it carries — the gate `filter.set`
198
+ * and {@link matchesFilter} share.
186
199
  *
187
200
  * @param column - The column that fixes the accepted operators and cell shapes.
188
201
  * @param filter - The filter to inspect.
@@ -197,7 +210,7 @@ function admitsFilter(column, filter) {
197
210
  }
198
211
  }
199
212
  /**
200
- * Tests one cell against a filter according to its column.
213
+ * Tests one of a column's cells against one filter the way its `cell` fixes.
201
214
  *
202
215
  * @param column - The column that fixes the accepted operators.
203
216
  * @param cell - The cell to test, or absence.
@@ -213,7 +226,8 @@ function matchesFilter(column, cell, filter) {
213
226
  }
214
227
  }
215
228
  /**
216
- * Keeps the rows accepted by every filter.
229
+ * Keeps the rows every filter accepts, in the order given; a supplied {@link CellMatcher}
230
+ * replaces the default per column.
217
231
  *
218
232
  * @param schema - The schema that declares the filtered columns.
219
233
  * @param rows - The rows to filter.
@@ -231,7 +245,8 @@ function filterRows(schema, rows, filters, matchers) {
231
245
  })));
232
246
  }
233
247
  /**
234
- * Orders rows stably by a sequence of terms.
248
+ * Orders rows by the terms given, stably; a supplied {@link CellComparator} replaces the default
249
+ * per column.
235
250
  *
236
251
  * @param schema - The schema that declares the sorted columns.
237
252
  * @param rows - The rows to order.
@@ -259,7 +274,8 @@ function sortRows(schema, rows, orders, comparators) {
259
274
  return Object.freeze(indexed.map((entry) => entry.row));
260
275
  }
261
276
  /**
262
- * Audits a structurally valid schema for domain and budget faults.
277
+ * Audits a structurally valid schema for domain faults and budget breaches, returning human
278
+ * diagnostics.
263
279
  *
264
280
  * @param schema - The table schema to audit.
265
281
  * @returns Frozen human-readable diagnostics, or an empty list when the schema is sound.
@@ -358,7 +374,8 @@ function auditTable(schema) {
358
374
  return Object.freeze(faults);
359
375
  }
360
376
  /**
361
- * Projects a schema into declaration-ordered JSON.
377
+ * Projects a schema into JSON in declaration order, dropping every absent member; raises `SCHEMA`
378
+ * for a `meta` it cannot own.
362
379
  *
363
380
  * @param schema - The schema to project.
364
381
  * @returns A deeply owned JSON record with absent members omitted.
@@ -397,7 +414,8 @@ function serializeTable(schema) {
397
414
  }
398
415
  }
399
416
  /**
400
- * Projects rows into schema-column-ordered JSON.
417
+ * Projects rows into JSON with each row's cells in the schema's column order, dropping every
418
+ * absent cell.
401
419
  *
402
420
  * @param schema - The schema that fixes cell order.
403
421
  * @param rows - The rows to project.
@@ -425,7 +443,8 @@ function serializeRows(schema, rows) {
425
443
  //#endregion
426
444
  //#region src/core/validators.ts
427
445
  /**
428
- * Determines whether an unknown value has a table cell shape.
446
+ * Determines whether an unknown value has a table cell shape — a string, a finite number, or a
447
+ * boolean.
429
448
  *
430
449
  * @param input - The value to inspect.
431
450
  * @returns True if the value is a string, finite number, or boolean; false otherwise.
@@ -434,7 +453,8 @@ function isTableCell(input) {
434
453
  return (0, _orkestrel_contract.unionOf)(_orkestrel_contract.isString, _orkestrel_contract.isFiniteNumber, _orkestrel_contract.isBoolean)(input);
435
454
  }
436
455
  /**
437
- * Determines whether an unknown value is a record of table cells.
456
+ * Determines whether an unknown value is a record whose every own key is a string and every value
457
+ * a {@link TableCell}.
438
458
  *
439
459
  * @param input - The value to inspect.
440
460
  * @returns True if every own key is a string and every value is a table cell; false otherwise.
@@ -456,7 +476,8 @@ function isColumnCell(input) {
456
476
  return COLUMN_CELLS.some((cell) => cell === input);
457
477
  }
458
478
  /**
459
- * Determines whether an unknown value is one exact column choice record.
479
+ * Determines whether an unknown value is one exact {@link ColumnChoice} record; an unknown member
480
+ * refuses it.
460
481
  *
461
482
  * @param input - The value to inspect.
462
483
  * @returns True if the value is a column choice; false otherwise.
@@ -473,7 +494,8 @@ function isColumnChoice(input) {
473
494
  return outcome.success && outcome.value;
474
495
  }
475
496
  /**
476
- * Determines whether an unknown value is one exact discriminated table column.
497
+ * Determines whether an unknown value is one exact discriminated {@link TableColumn}, checked
498
+ * against its cell's own options.
477
499
  *
478
500
  * @param input - The value to inspect.
479
501
  * @returns True if the value is a structurally valid table column; false otherwise.
@@ -515,7 +537,8 @@ function isTableColumn(input) {
515
537
  return outcome.success && outcome.value;
516
538
  }
517
539
  /**
518
- * Determines whether an unknown value has one exact structural table-schema shape.
540
+ * Determines whether an unknown value has the exact shape of a {@link TableSchema} — the shape
541
+ * alone, with no domain check.
519
542
  *
520
543
  * @param input - The value to inspect.
521
544
  * @returns True if the value has the exact structure of a table schema; false otherwise.
@@ -538,7 +561,8 @@ function isStructuralTableSchema(input) {
538
561
  return outcome.success && outcome.value;
539
562
  }
540
563
  /**
541
- * Determines whether an unknown value is one semantically sound table schema.
564
+ * Determines whether an unknown value is a {@link TableSchema} a table can be opened against —
565
+ * the exact shape, and an audit that finds nothing.
542
566
  *
543
567
  * @param input - The value to inspect.
544
568
  * @returns True if the value has valid structure, domain relationships, and
@@ -560,7 +584,8 @@ function cloneRow(row) {
560
584
  return Object.freeze({ ...row });
561
585
  }
562
586
  /**
563
- * Clones a table schema into an owned frozen snapshot.
587
+ * Clones a whole schema into an owned frozen snapshot, freezing every nested column, choice list,
588
+ * choice, and `meta`; raises `SCHEMA` for a `meta` it cannot own.
564
589
  *
565
590
  * @param schema - The schema to own.
566
591
  * @returns A frozen schema with every nested column, choice, list, and metadata record owned.
@@ -591,7 +616,7 @@ function cloneSchema(schema) {
591
616
  //#endregion
592
617
  //#region src/core/parsers.ts
593
618
  /**
594
- * Parses unknown wire data into an owned, semantically sound table schema.
619
+ * Parses unknown wire data into an owned, structurally valid, semantically sound table schema.
595
620
  *
596
621
  * @param input - The unknown schema value to parse.
597
622
  * @returns An owned table schema, or `undefined` on refusal.
@@ -605,7 +630,8 @@ function parseTable(input) {
605
630
  return outcome.success ? outcome.value : void 0;
606
631
  }
607
632
  /**
608
- * Parses unknown wire rows against one table schema.
633
+ * Parses unknown wire data into owned rows against one table schema, coercing a numeric string
634
+ * and `'true'` / `'false'`.
609
635
  *
610
636
  * @param schema - The schema that declares the accepted keys and cell shapes.
611
637
  * @param input - The unknown row-list value to parse.
@@ -776,7 +802,7 @@ var FilterManager = class {
776
802
  /** Filters one column or several. */
777
803
  set(input) {
778
804
  this.#gate();
779
- const requested = Array.isArray(input) ? input : [input];
805
+ const requested = (0, _orkestrel_contract.isArray)(input) ? input : [input];
780
806
  for (const filter of requested) this.#validate(filter);
781
807
  const next = mergeTerms(this.#read(), requested);
782
808
  if (matchesTerms(next, this.#read(), (filter, other) => this.#operands(filter, other))) return;
@@ -788,7 +814,7 @@ var FilterManager = class {
788
814
  /** Stops filtering by one or more columns. */
789
815
  remove(input) {
790
816
  this.#gate();
791
- const columns = input === void 0 ? this.#schema.columns.map((column) => column.key) : Array.isArray(input) ? input : [input];
817
+ const columns = input === void 0 ? this.#schema.columns.map((column) => column.key) : (0, _orkestrel_contract.isArray)(input) ? input : [input];
792
818
  for (const column of columns) if (extractColumn(this.#schema, column) === void 0) return false;
793
819
  const next = removeTerms(this.#read(), columns);
794
820
  if (next.length !== this.#read().length) {
@@ -883,7 +909,7 @@ var PaginationManager = class {
883
909
  this.#emitter.emit("paginate", nextPage);
884
910
  }
885
911
  #normalize(value) {
886
- return Number.isFinite(value) ? Math.max(1, Math.trunc(value)) : 1;
912
+ return (0, _orkestrel_contract.isFiniteNumber)(value) ? Math.max(1, Math.trunc(value)) : 1;
887
913
  }
888
914
  };
889
915
  //#endregion
@@ -929,7 +955,7 @@ var RowManager = class {
929
955
  /** Appends one row or several. */
930
956
  add(input) {
931
957
  this.#gate();
932
- const rows = Array.isArray(input) ? input : [input];
958
+ const rows = (0, _orkestrel_contract.isArray)(input) ? input : [input];
933
959
  const keys = /* @__PURE__ */ new Set();
934
960
  for (const row of this.#read()) {
935
961
  const key = extractKey(this.#schema, row);
@@ -948,7 +974,7 @@ var RowManager = class {
948
974
  /** Merges one row or several into the rows their keys name. */
949
975
  update(input) {
950
976
  this.#gate();
951
- const updates = (Array.isArray(input) ? input : [input]).map((row) => cloneRow(row));
977
+ const updates = ((0, _orkestrel_contract.isArray)(input) ? input : [input]).map((row) => cloneRow(row));
952
978
  const current = this.#read();
953
979
  const locations = [];
954
980
  for (const update of updates) {
@@ -1008,7 +1034,7 @@ var RowManager = class {
1008
1034
  const requested = input === void 0 ? current.flatMap((row) => {
1009
1035
  const key = extractKey(this.#schema, row);
1010
1036
  return key === void 0 ? [] : [key];
1011
- }) : Array.isArray(input) ? input : [input];
1037
+ }) : (0, _orkestrel_contract.isArray)(input) ? input : [input];
1012
1038
  const keys = new Set(requested);
1013
1039
  const known = new Set(current.flatMap((row) => {
1014
1040
  const key = extractKey(this.#schema, row);
@@ -1131,7 +1157,7 @@ var SortManager = class {
1131
1157
  /** Sorts by one column or several. */
1132
1158
  set(input) {
1133
1159
  this.#gate();
1134
- const requested = Array.isArray(input) ? input : [input];
1160
+ const requested = (0, _orkestrel_contract.isArray)(input) ? input : [input];
1135
1161
  for (const order of requested) this.#require(order.column);
1136
1162
  const next = mergeTerms(this.#read(), requested);
1137
1163
  if (matchesTerms(next, this.#read(), (order, other) => order.direction === other.direction)) return;
@@ -1141,7 +1167,7 @@ var SortManager = class {
1141
1167
  /** Stops sorting by one or more columns. */
1142
1168
  remove(input) {
1143
1169
  this.#gate();
1144
- const columns = input === void 0 ? this.#schema.columns.map((column) => column.key) : Array.isArray(input) ? input : [input];
1170
+ const columns = input === void 0 ? this.#schema.columns.map((column) => column.key) : (0, _orkestrel_contract.isArray)(input) ? input : [input];
1145
1171
  for (const column of columns) if (extractColumn(this.#schema, column) === void 0) return false;
1146
1172
  const next = removeTerms(this.#read(), columns);
1147
1173
  if (next.length !== this.#read().length) {
@@ -1156,7 +1182,10 @@ var SortManager = class {
1156
1182
  };
1157
1183
  //#endregion
1158
1184
  //#region src/core/Table.ts
1159
- /** Holds a schema, its rows, and the lens through which they are read. */
1185
+ /**
1186
+ * Holds a schema, its rows, and the lens through which they are read, implementing
1187
+ * {@link TableInterface} exactly.
1188
+ */
1160
1189
  var Table = class {
1161
1190
  #emitter;
1162
1191
  #schema;
@@ -1324,17 +1353,43 @@ var Table = class {
1324
1353
  //#endregion
1325
1354
  //#region src/core/factories.ts
1326
1355
  /**
1327
- * Opens a table against a schema.
1356
+ * Opens a table against a schema. The schema is copied, and the copy is what the table declares.
1328
1357
  *
1329
1358
  * @param schema - The table declaration to own.
1330
1359
  * @param options - Initial rows, lens overrides, pagination, and emitter wiring.
1331
1360
  * @returns A live table interface.
1332
1361
  * @throws A {@link TableError} coded `SCHEMA` when the schema is unusable, `KEY` when a seeded
1333
1362
  * identity is unusable or repeated, and `CELL` when a seeded cell is invalid.
1334
- * @example
1363
+ * @example Open a table
1335
1364
  * ```ts
1336
- * const table = createTable({ key: 'id', columns: [{ cell: 'text', key: 'id' }] })
1337
- * table.rows.add({ id: '1' })
1365
+ * import { createTable } from '@orkestrel/table'
1366
+ *
1367
+ * const table = createTable(
1368
+ * {
1369
+ * label: 'People',
1370
+ * key: 'id',
1371
+ * columns: [
1372
+ * { cell: 'text', key: 'id', label: 'Reference' },
1373
+ * { cell: 'text', key: 'name', label: 'Name' },
1374
+ * { cell: 'number', key: 'age', label: 'Age' },
1375
+ * ],
1376
+ * },
1377
+ * {
1378
+ * rows: [
1379
+ * { id: '1', name: 'Ada', age: 36 },
1380
+ * { id: '2', name: 'Grace', age: 45 },
1381
+ * { id: '3', name: 'Alan', age: 41 },
1382
+ * ],
1383
+ * limit: 2,
1384
+ * },
1385
+ * )
1386
+ *
1387
+ * table.filter.set({ column: 'name', operator: 'contains', text: 'a' })
1388
+ * table.sort.set({ column: 'age', direction: 'descending' })
1389
+ *
1390
+ * table.count // 3 — every name holds a lowercase 'a'
1391
+ * table.pagination.count // 2 — two pages of two
1392
+ * table.view.map((row) => row.name) // ['Grace', 'Alan'] — page one, oldest first
1338
1393
  * ```
1339
1394
  */
1340
1395
  function createTable(schema, options) {