@orkestrel/table 0.0.11 → 0.0.12
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 +1 -1
- package/dist/src/browser/index.d.ts +6 -773
- package/dist/src/browser/index.js +5 -419
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +3 -3
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +7 -7
- package/dist/src/core/index.d.ts +7 -7
- package/dist/src/core/index.js +3 -3
- package/dist/src/core/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { attempt, isInstance, parseJSON, parseNumber } from "@orkestrel/contract";
|
|
2
|
+
import { extractKey, matchesTableRow, parseRows, parseTable } from "../core/index.js";
|
|
2
3
|
import { Emitter } from "@orkestrel/emitter";
|
|
3
4
|
//#region src/browser/errors.ts
|
|
4
5
|
/** Represents a native table binding failure with a machine-readable code. */
|
|
@@ -22,422 +23,6 @@ function isTableBindingError(value) {
|
|
|
22
23
|
return isInstance(value, TableBindingError);
|
|
23
24
|
}
|
|
24
25
|
//#endregion
|
|
25
|
-
//#region src/core/errors.ts
|
|
26
|
-
/**
|
|
27
|
-
* Represents an error raised by the table domain — a machine-readable `code` and optional
|
|
28
|
-
* structured `context`.
|
|
29
|
-
*/
|
|
30
|
-
var TableError = class extends Error {
|
|
31
|
-
/** Holds the machine-readable reason for this failure. */
|
|
32
|
-
code;
|
|
33
|
-
/** Holds structured values that locate or explain this failure. */
|
|
34
|
-
context;
|
|
35
|
-
/**
|
|
36
|
-
* Creates a table error.
|
|
37
|
-
*
|
|
38
|
-
* @param code - The machine-readable reason.
|
|
39
|
-
* @param message - The human-readable failure text.
|
|
40
|
-
* @param context - Optional structured failure details.
|
|
41
|
-
*/
|
|
42
|
-
constructor(code, message, context) {
|
|
43
|
-
super(message);
|
|
44
|
-
this.name = "TableError";
|
|
45
|
-
this.code = code;
|
|
46
|
-
if (context !== void 0) this.context = context;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
//#endregion
|
|
50
|
-
//#region src/core/cloners.ts
|
|
51
|
-
/**
|
|
52
|
-
* Clones one row into an owned frozen snapshot.
|
|
53
|
-
*
|
|
54
|
-
* @param row - The row to own.
|
|
55
|
-
* @returns A frozen copy of the row's cells.
|
|
56
|
-
*/
|
|
57
|
-
function cloneRow(row) {
|
|
58
|
-
return Object.freeze({ ...row });
|
|
59
|
-
}
|
|
60
|
-
//#endregion
|
|
61
|
-
//#region src/core/constants.ts
|
|
62
|
-
/** Lists every column cell, in the order declared by the public contract. */
|
|
63
|
-
var COLUMN_CELLS = Object.freeze([
|
|
64
|
-
"text",
|
|
65
|
-
"number",
|
|
66
|
-
"flag",
|
|
67
|
-
"choice"
|
|
68
|
-
]);
|
|
69
|
-
/** Names the maximum number of choices one `choice` column may offer: 1024. */
|
|
70
|
-
var CHOICE_LIMIT = 1024;
|
|
71
|
-
/** Names the maximum length of any single retained string: 65536 UTF-16 code units. */
|
|
72
|
-
var STRING_LIMIT = 65536;
|
|
73
|
-
/**
|
|
74
|
-
* Names the maximum total length of every string one schema retains: 1048576 UTF-16 code units.
|
|
75
|
-
*/
|
|
76
|
-
var TEXT_LIMIT = 1048576;
|
|
77
|
-
/** Names the maximum total number of records, arrays, and leaves one schema retains: 16384. */
|
|
78
|
-
var NODE_LIMIT = 16384;
|
|
79
|
-
//#endregion
|
|
80
|
-
//#region src/core/helpers.ts
|
|
81
|
-
/**
|
|
82
|
-
* Finds one column by key; `undefined` when the schema declares no such column.
|
|
83
|
-
*
|
|
84
|
-
* @param schema - The schema whose columns to search.
|
|
85
|
-
* @param key - The column key to find.
|
|
86
|
-
* @returns The declared column, or `undefined` when no column has that key.
|
|
87
|
-
*/
|
|
88
|
-
function extractColumn(schema, key) {
|
|
89
|
-
return schema.columns.find((column) => column.key === key);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Reads one row's declared identity; `undefined` when its key cell is missing, empty, or not a
|
|
93
|
-
* string.
|
|
94
|
-
*
|
|
95
|
-
* @param schema - The schema that names the identity column.
|
|
96
|
-
* @param row - The row whose identity to read.
|
|
97
|
-
* @returns The non-empty string identity, or `undefined` when it is unusable.
|
|
98
|
-
*/
|
|
99
|
-
function extractKey(schema, row) {
|
|
100
|
-
if (!Object.hasOwn(row, schema.key)) return void 0;
|
|
101
|
-
const key = row[schema.key];
|
|
102
|
-
return isString(key) && key.length > 0 ? key : void 0;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Checks whether one column can hold a value — the shape gate every write and every seed passes
|
|
106
|
-
* through.
|
|
107
|
-
*
|
|
108
|
-
* @param column - The column that owns the cell.
|
|
109
|
-
* @param value - The unknown value to inspect.
|
|
110
|
-
* @returns True if the column can hold the value; false otherwise.
|
|
111
|
-
*/
|
|
112
|
-
function matchesCell(column, value) {
|
|
113
|
-
if (isString(value) && value.length > 65536) return false;
|
|
114
|
-
switch (column.cell) {
|
|
115
|
-
case "text": return isString(value);
|
|
116
|
-
case "number": return isFiniteNumber(value);
|
|
117
|
-
case "flag": return isBoolean(value);
|
|
118
|
-
case "choice": return isString(value) && column.choices.some((choice) => choice.value === value);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Audits a structurally valid schema for domain faults and budget breaches, returning human
|
|
123
|
-
* diagnostics.
|
|
124
|
-
*
|
|
125
|
-
* @param schema - The table schema to audit.
|
|
126
|
-
* @returns Frozen human-readable diagnostics, or an empty list when the schema is sound.
|
|
127
|
-
*/
|
|
128
|
-
function auditTable(schema) {
|
|
129
|
-
const faults = [];
|
|
130
|
-
const columns = /* @__PURE__ */ new Set();
|
|
131
|
-
let choiceExceeded;
|
|
132
|
-
let nameExceeded = schema.name !== void 0 && schema.name.length > 128;
|
|
133
|
-
if (schema.columns.length > 256) faults.push(`schema declares more than 256 columns`);
|
|
134
|
-
const columnCount = Math.min(schema.columns.length, 257);
|
|
135
|
-
for (let index = 0; index < columnCount; index += 1) {
|
|
136
|
-
const column = schema.columns[index];
|
|
137
|
-
if (column === void 0) continue;
|
|
138
|
-
if (column.key.length > 128) nameExceeded = true;
|
|
139
|
-
if (choiceExceeded === void 0 && column.cell === "choice" && column.choices.length > 1024) choiceExceeded = column.key;
|
|
140
|
-
}
|
|
141
|
-
if (choiceExceeded !== void 0) faults.push(`column "${choiceExceeded}" offers more than ${CHOICE_LIMIT} choices`);
|
|
142
|
-
if (nameExceeded) faults.push(`schema contains a name longer than 128`);
|
|
143
|
-
const pending = [schema];
|
|
144
|
-
const metadata = [false];
|
|
145
|
-
let position = 0;
|
|
146
|
-
let stringExceeded = false;
|
|
147
|
-
let textExceeded = false;
|
|
148
|
-
let nodeExceeded = false;
|
|
149
|
-
let text = 0;
|
|
150
|
-
while (position < pending.length) {
|
|
151
|
-
const node = pending[position];
|
|
152
|
-
const inMeta = metadata[position] === true;
|
|
153
|
-
position += 1;
|
|
154
|
-
if (isString(node)) {
|
|
155
|
-
if (node.length > 65536) stringExceeded = true;
|
|
156
|
-
text = Math.min(TEXT_LIMIT + 1, text + node.length);
|
|
157
|
-
if (text > 1048576) textExceeded = true;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if (isArray(node)) {
|
|
161
|
-
const read = readArrayEntries(node);
|
|
162
|
-
if (!read.success || !read.value.dense) continue;
|
|
163
|
-
for (const entry of read.value.entries) {
|
|
164
|
-
if (pending.length >= 16384) {
|
|
165
|
-
nodeExceeded = true;
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
pending.push(entry);
|
|
169
|
-
metadata.push(inMeta);
|
|
170
|
-
}
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (!isRecord(node)) continue;
|
|
174
|
-
const keys = attempt(() => Object.keys(node));
|
|
175
|
-
if (!keys.success) continue;
|
|
176
|
-
for (const key of keys.value) {
|
|
177
|
-
if (inMeta) {
|
|
178
|
-
if (key.length > 65536) stringExceeded = true;
|
|
179
|
-
text = Math.min(TEXT_LIMIT + 1, text + key.length);
|
|
180
|
-
if (text > 1048576) textExceeded = true;
|
|
181
|
-
}
|
|
182
|
-
const value = attempt(() => node[key]);
|
|
183
|
-
if (!value.success || value.value === void 0) continue;
|
|
184
|
-
if (pending.length >= 16384) {
|
|
185
|
-
nodeExceeded = true;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
pending.push(value.value);
|
|
189
|
-
metadata.push(inMeta || key === "meta");
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (stringExceeded) faults.push(`schema contains a string longer than ${STRING_LIMIT}`);
|
|
193
|
-
if (textExceeded) faults.push(`schema retains more than ${TEXT_LIMIT} string code units`);
|
|
194
|
-
if (nodeExceeded) faults.push(`schema retains more than ${NODE_LIMIT} nodes`);
|
|
195
|
-
for (let index = 0; index < columnCount; index += 1) {
|
|
196
|
-
const column = schema.columns[index];
|
|
197
|
-
if (column === void 0) continue;
|
|
198
|
-
if (!nodeExceeded && column.meta !== void 0) {
|
|
199
|
-
if (!attempt(() => cloneJSONRecord(column.meta)).success) faults.push(`column "${column.key}" has metadata that cannot be owned`);
|
|
200
|
-
}
|
|
201
|
-
if (column.key.length === 0) faults.push("column \"\" has an empty key");
|
|
202
|
-
if (columns.has(column.key)) faults.push(`column "${column.key}" is declared more than once`);
|
|
203
|
-
columns.add(column.key);
|
|
204
|
-
if (column.cell === "choice") {
|
|
205
|
-
const choices = /* @__PURE__ */ new Set();
|
|
206
|
-
const choiceCount = Math.min(column.choices.length, CHOICE_LIMIT + 1);
|
|
207
|
-
for (let choiceIndex = 0; choiceIndex < choiceCount; choiceIndex += 1) {
|
|
208
|
-
const choice = column.choices[choiceIndex];
|
|
209
|
-
if (choice === void 0) continue;
|
|
210
|
-
if (choices.has(choice.value)) faults.push(`column "${column.key}" offers choice "${choice.value}" more than once`);
|
|
211
|
-
choices.add(choice.value);
|
|
212
|
-
}
|
|
213
|
-
if (column.choices.length === 0) faults.push(`column "${column.key}" offers no choices`);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
const key = extractColumn(schema, schema.key);
|
|
217
|
-
if (key === void 0) faults.push(`schema key "${schema.key}" names no declared column`);
|
|
218
|
-
else if (key.cell === "number" || key.cell === "flag") faults.push(`schema key "${schema.key}" names a ${key.cell} column, which holds no identity`);
|
|
219
|
-
return Object.freeze(faults);
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Projects a schema into JSON in declaration order, dropping every absent member; raises `SCHEMA`
|
|
223
|
-
* for a `meta` it cannot own.
|
|
224
|
-
*
|
|
225
|
-
* @param schema - The schema to project.
|
|
226
|
-
* @returns A deeply owned JSON record with absent members omitted.
|
|
227
|
-
* @throws A {@link TableError} coded `SCHEMA` when metadata cannot be owned.
|
|
228
|
-
*/
|
|
229
|
-
function serializeTable(schema) {
|
|
230
|
-
const output = {};
|
|
231
|
-
if (schema.name !== void 0) output.name = schema.name;
|
|
232
|
-
if (schema.label !== void 0) output.label = schema.label;
|
|
233
|
-
if (schema.help !== void 0) output.help = schema.help;
|
|
234
|
-
output.key = schema.key;
|
|
235
|
-
output.columns = schema.columns.map((column) => {
|
|
236
|
-
const entry = {
|
|
237
|
-
cell: column.cell,
|
|
238
|
-
key: column.key
|
|
239
|
-
};
|
|
240
|
-
if (column.label !== void 0) entry.label = column.label;
|
|
241
|
-
if (column.help !== void 0) entry.help = column.help;
|
|
242
|
-
if (column.hidden !== void 0) entry.hidden = column.hidden;
|
|
243
|
-
if (column.meta !== void 0) entry.meta = column.meta;
|
|
244
|
-
if (column.cell === "choice") entry.choices = column.choices.map((choice) => {
|
|
245
|
-
const option = {
|
|
246
|
-
value: choice.value,
|
|
247
|
-
label: choice.label
|
|
248
|
-
};
|
|
249
|
-
if (choice.help !== void 0) option.help = choice.help;
|
|
250
|
-
return option;
|
|
251
|
-
});
|
|
252
|
-
return entry;
|
|
253
|
-
});
|
|
254
|
-
try {
|
|
255
|
-
return cloneJSONRecord(output);
|
|
256
|
-
} catch (error) {
|
|
257
|
-
if (!isContractError(error)) throw error;
|
|
258
|
-
throw new TableError("SCHEMA", "schema contains metadata that cannot be owned");
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
/** Compares complete row values independently of their snapshot identities.
|
|
262
|
-
* @param left - The previous row, if present.
|
|
263
|
-
* @param right - The next row.
|
|
264
|
-
* @returns Whether both rows carry exactly the same cells.
|
|
265
|
-
*/
|
|
266
|
-
function matchesTableRow(left, right) {
|
|
267
|
-
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]);
|
|
268
|
-
}
|
|
269
|
-
//#endregion
|
|
270
|
-
//#region src/core/validators.ts
|
|
271
|
-
/**
|
|
272
|
-
* Determines whether an unknown value is a declared column cell.
|
|
273
|
-
*
|
|
274
|
-
* @param input - The value to inspect.
|
|
275
|
-
* @returns True if the value is a declared column cell; false otherwise.
|
|
276
|
-
*/
|
|
277
|
-
function isColumnCell(input) {
|
|
278
|
-
return COLUMN_CELLS.some((cell) => cell === input);
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Determines whether an unknown value is one exact {@link ColumnChoice} record; an unknown member
|
|
282
|
-
* refuses it.
|
|
283
|
-
*
|
|
284
|
-
* @param input - The value to inspect.
|
|
285
|
-
* @returns True if the value is a column choice; false otherwise.
|
|
286
|
-
*/
|
|
287
|
-
function isColumnChoice(input) {
|
|
288
|
-
const outcome = attempt(() => {
|
|
289
|
-
if (!isRecord(input) || !Reflect.ownKeys(input).every((key) => isString(key))) return false;
|
|
290
|
-
return recordOf({
|
|
291
|
-
value: isString,
|
|
292
|
-
label: isString,
|
|
293
|
-
help: isString
|
|
294
|
-
}, ["help"])(input);
|
|
295
|
-
});
|
|
296
|
-
return outcome.success && outcome.value;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Determines whether an unknown value is one exact discriminated {@link TableColumn}, checked
|
|
300
|
-
* against its cell's own options.
|
|
301
|
-
*
|
|
302
|
-
* @param input - The value to inspect.
|
|
303
|
-
* @returns True if the value is a structurally valid table column; false otherwise.
|
|
304
|
-
*/
|
|
305
|
-
function isTableColumn(input) {
|
|
306
|
-
const outcome = attempt(() => {
|
|
307
|
-
if (!isRecord(input) || !Object.hasOwn(input, "cell") || !Object.hasOwn(input, "key")) return false;
|
|
308
|
-
const cell = input.cell;
|
|
309
|
-
if (!isColumnCell(cell)) return false;
|
|
310
|
-
if (!Reflect.ownKeys(input).every((key) => {
|
|
311
|
-
if (!isString(key)) return false;
|
|
312
|
-
if ([
|
|
313
|
-
"cell",
|
|
314
|
-
"key",
|
|
315
|
-
"label",
|
|
316
|
-
"help",
|
|
317
|
-
"hidden",
|
|
318
|
-
"meta"
|
|
319
|
-
].includes(key)) return true;
|
|
320
|
-
return cell === "choice" && key === "choices";
|
|
321
|
-
})) return false;
|
|
322
|
-
const key = input.key;
|
|
323
|
-
const hasLabel = Object.hasOwn(input, "label");
|
|
324
|
-
const label = hasLabel ? input.label : void 0;
|
|
325
|
-
const hasHelp = Object.hasOwn(input, "help");
|
|
326
|
-
const help = hasHelp ? input.help : void 0;
|
|
327
|
-
const hasHidden = Object.hasOwn(input, "hidden");
|
|
328
|
-
const hidden = hasHidden ? input.hidden : void 0;
|
|
329
|
-
const hasMeta = Object.hasOwn(input, "meta");
|
|
330
|
-
const meta = hasMeta ? input.meta : void 0;
|
|
331
|
-
if (hasMeta) {
|
|
332
|
-
if (!isBoundedJSONRecord(meta)) return false;
|
|
333
|
-
if (!attempt(() => cloneJSONRecord(meta)).success) return false;
|
|
334
|
-
}
|
|
335
|
-
if (!isString(key) || hasLabel && !isString(label) || hasHelp && !isString(help) || hasHidden && !isBoolean(hidden)) return false;
|
|
336
|
-
if (cell !== "choice") return !Object.hasOwn(input, "choices");
|
|
337
|
-
return Object.hasOwn(input, "choices") && arrayOf(isColumnChoice)(input.choices);
|
|
338
|
-
});
|
|
339
|
-
return outcome.success && outcome.value;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Determines whether an unknown value has the exact shape of a {@link TableSchema} — the shape
|
|
343
|
-
* alone, with no domain check.
|
|
344
|
-
*
|
|
345
|
-
* @param input - The value to inspect.
|
|
346
|
-
* @returns True if the value has the exact structure of a table schema; false otherwise.
|
|
347
|
-
*/
|
|
348
|
-
function isStructuralTableSchema(input) {
|
|
349
|
-
const outcome = attempt(() => {
|
|
350
|
-
if (!isRecord(input) || !Reflect.ownKeys(input).every((key) => isString(key))) return false;
|
|
351
|
-
return recordOf({
|
|
352
|
-
name: isString,
|
|
353
|
-
label: isString,
|
|
354
|
-
help: isString,
|
|
355
|
-
key: isString,
|
|
356
|
-
columns: arrayOf(isTableColumn)
|
|
357
|
-
}, [
|
|
358
|
-
"name",
|
|
359
|
-
"label",
|
|
360
|
-
"help"
|
|
361
|
-
])(input);
|
|
362
|
-
});
|
|
363
|
-
return outcome.success && outcome.value;
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* Determines whether an unknown value is a {@link TableSchema} a table can be opened against —
|
|
367
|
-
* the exact shape, and an audit that finds nothing.
|
|
368
|
-
*
|
|
369
|
-
* @param input - The value to inspect.
|
|
370
|
-
* @returns True if the value has valid structure, domain relationships, and
|
|
371
|
-
* budgets; false otherwise.
|
|
372
|
-
*/
|
|
373
|
-
function isTableSchema(input) {
|
|
374
|
-
const outcome = attempt(() => isStructuralTableSchema(input) && auditTable(input).length === 0);
|
|
375
|
-
return outcome.success && outcome.value;
|
|
376
|
-
}
|
|
377
|
-
//#endregion
|
|
378
|
-
//#region src/core/parsers.ts
|
|
379
|
-
/**
|
|
380
|
-
* Parses unknown wire data into an owned, structurally valid, semantically sound table schema.
|
|
381
|
-
*
|
|
382
|
-
* @param input - The unknown schema value to parse.
|
|
383
|
-
* @returns An owned table schema, or `undefined` on refusal.
|
|
384
|
-
*/
|
|
385
|
-
function parseTable(input) {
|
|
386
|
-
const outcome = attempt(() => {
|
|
387
|
-
if (!isTableSchema(input)) return void 0;
|
|
388
|
-
const projected = serializeTable(input);
|
|
389
|
-
return isTableSchema(projected) ? projected : void 0;
|
|
390
|
-
});
|
|
391
|
-
return outcome.success ? outcome.value : void 0;
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Parses unknown wire data into owned rows against one table schema, coercing a numeric string
|
|
395
|
-
* and `'true'` / `'false'`.
|
|
396
|
-
*
|
|
397
|
-
* @param schema - The schema that declares the accepted keys and cell shapes.
|
|
398
|
-
* @param input - The unknown row-list value to parse.
|
|
399
|
-
* @returns Frozen owned rows, or `undefined` when any row is refused.
|
|
400
|
-
*/
|
|
401
|
-
function parseRows(schema, input) {
|
|
402
|
-
const outcome = attempt(() => {
|
|
403
|
-
if (!isTableSchema(schema) || !isArray(input)) return;
|
|
404
|
-
const read = readArrayEntries(input);
|
|
405
|
-
if (!read.success || !read.value.dense) return void 0;
|
|
406
|
-
const keys = /* @__PURE__ */ new Set();
|
|
407
|
-
const rows = [];
|
|
408
|
-
for (const candidate of read.value.entries) {
|
|
409
|
-
if (!isRecord(candidate)) return void 0;
|
|
410
|
-
const row = {};
|
|
411
|
-
for (const key of Reflect.ownKeys(candidate)) {
|
|
412
|
-
if (!isString(key) || !Object.hasOwn(candidate, key)) return void 0;
|
|
413
|
-
const column = extractColumn(schema, key);
|
|
414
|
-
if (column === void 0) return void 0;
|
|
415
|
-
const inputCell = candidate[key];
|
|
416
|
-
let cell = inputCell;
|
|
417
|
-
if (column.cell === "number" && isString(inputCell)) {
|
|
418
|
-
if (inputCell.length > 65536) return void 0;
|
|
419
|
-
cell = parseNumber(inputCell);
|
|
420
|
-
} else if (column.cell === "flag" && inputCell === "true") cell = true;
|
|
421
|
-
else if (column.cell === "flag" && inputCell === "false") cell = false;
|
|
422
|
-
if (!matchesCell(column, cell)) return void 0;
|
|
423
|
-
Object.defineProperty(row, key, {
|
|
424
|
-
value: cell,
|
|
425
|
-
enumerable: true,
|
|
426
|
-
configurable: true,
|
|
427
|
-
writable: true
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
const owned = cloneRow(row);
|
|
431
|
-
const key = extractKey(schema, owned);
|
|
432
|
-
if (key === void 0 || keys.has(key)) return void 0;
|
|
433
|
-
keys.add(key);
|
|
434
|
-
rows.push(owned);
|
|
435
|
-
}
|
|
436
|
-
return Object.freeze(rows);
|
|
437
|
-
});
|
|
438
|
-
return outcome.success ? outcome.value : void 0;
|
|
439
|
-
}
|
|
440
|
-
//#endregion
|
|
441
26
|
//#region src/browser/helpers.ts
|
|
442
27
|
/** Reads declared header metadata and explicit row keys into a validated core projection.
|
|
443
28
|
* @param element - A native table with data-table-key naming its identity column.
|
|
@@ -486,7 +71,7 @@ function readTableSchema(element) {
|
|
|
486
71
|
* @throws A binding error for duplicate keys, undeclared cells, or malformed values.
|
|
487
72
|
*/
|
|
488
73
|
function readTableRows(schema, elements) {
|
|
489
|
-
const
|
|
74
|
+
const candidates = elements.map((element) => {
|
|
490
75
|
const cells = { [schema.key]: element.getAttribute("data-table-key") };
|
|
491
76
|
const seen = /* @__PURE__ */ new Set();
|
|
492
77
|
for (const cell of element.cells) {
|
|
@@ -504,7 +89,8 @@ function readTableRows(schema, elements) {
|
|
|
504
89
|
});
|
|
505
90
|
}
|
|
506
91
|
return cells;
|
|
507
|
-
})
|
|
92
|
+
});
|
|
93
|
+
const rows = parseRows(schema, candidates);
|
|
508
94
|
if (rows === void 0) throw new TableBindingError("STRUCTURE", "Native row keys or cells are invalid");
|
|
509
95
|
return rows;
|
|
510
96
|
}
|