@web-ts-toolkit/json-frame 0.40.0 → 0.41.0

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 (6) hide show
  1. package/README.md +75 -16
  2. package/index.d.mts +115 -29
  3. package/index.d.ts +115 -29
  4. package/index.js +661 -292
  5. package/index.mjs +660 -292
  6. package/package.json +1 -1
package/index.mjs CHANGED
@@ -8,7 +8,38 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
8
8
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
9
 
10
10
  // src/errors.ts
11
+ var DIAGNOSTIC_KEY_LIMIT = 5;
11
12
  var freezeCopy = (values) => Object.freeze([...values]);
13
+ var summarizeDiagnosticValue = (value) => {
14
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
15
+ return value;
16
+ }
17
+ if (Array.isArray(value)) {
18
+ return Object.freeze({ kind: "array", length: value.length });
19
+ }
20
+ if (typeof value === "object") {
21
+ const keys = Object.keys(value);
22
+ return Object.freeze({
23
+ kind: "object",
24
+ keyCount: keys.length,
25
+ keys: freezeCopy(keys.slice(0, DIAGNOSTIC_KEY_LIMIT)),
26
+ truncated: keys.length > DIAGNOSTIC_KEY_LIMIT
27
+ });
28
+ }
29
+ if (typeof value === "undefined") {
30
+ return Object.freeze({ kind: "undefined" });
31
+ }
32
+ if (typeof value === "symbol") {
33
+ return Object.freeze({ kind: "symbol" });
34
+ }
35
+ if (typeof value === "bigint") {
36
+ return Object.freeze({ kind: "bigint" });
37
+ }
38
+ if (typeof value === "function") {
39
+ return Object.freeze({ kind: "function" });
40
+ }
41
+ return Object.freeze({ kind: "object", keyCount: 0, keys: freezeCopy([]), truncated: false });
42
+ };
12
43
  var JsonFrameError = class extends Error {
13
44
  constructor(message, context = {}) {
14
45
  super(message);
@@ -18,7 +49,9 @@ var JsonFrameError = class extends Error {
18
49
  this.path = context.path;
19
50
  this.row = context.row;
20
51
  this.column = context.column;
21
- this.value = context.value;
52
+ if ("value" in context) {
53
+ this.value = summarizeDiagnosticValue(context.value);
54
+ }
22
55
  }
23
56
  };
24
57
  var JsonFrameParseError = class extends JsonFrameError {
@@ -67,25 +100,494 @@ var ExportKeyCollisionError = class extends JsonFrameError {
67
100
  }
68
101
  };
69
102
 
103
+ // src/frame/column.ts
104
+ var INT32_MIN = -2147483648;
105
+ var INT32_MAX = 2147483647;
106
+ var freezeArray = (values) => Object.freeze([...values]);
107
+ var freezeColumnInfo = (info) => Object.freeze({ ...info });
108
+ var DATETIME_PATTERN = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})(?:[T ](?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})(?:\.(?<fraction>\d{1,9}))?)?$/;
109
+ var columnOperationCounters = {
110
+ materializeColumnCalls: 0,
111
+ materializedCells: 0,
112
+ scalarReads: 0,
113
+ rebuiltCells: 0,
114
+ nullableChecks: 0
115
+ };
116
+ var hasCategoricalMetadata = (field) => field.constraints?.enum !== void 0 || field.ordered === true || field.extDtype === "category";
117
+ var mapTableSchemaFieldType = (field) => {
118
+ if (hasCategoricalMetadata(field)) {
119
+ return "categorical";
120
+ }
121
+ switch (field.type) {
122
+ case "boolean":
123
+ return "boolean";
124
+ case "integer":
125
+ case "year":
126
+ return "integer";
127
+ case "number":
128
+ return "float";
129
+ case "date":
130
+ case "datetime":
131
+ return "datetime";
132
+ case "string":
133
+ return "string";
134
+ case "any":
135
+ return "unknown";
136
+ default:
137
+ return "unknown";
138
+ }
139
+ };
140
+ var inferColumnType = (values) => {
141
+ let nullable = false;
142
+ let sawBoolean = false;
143
+ let sawString = false;
144
+ let sawInteger = false;
145
+ let sawFloat = false;
146
+ let sawStructured = false;
147
+ for (const value of values) {
148
+ if (value === null) {
149
+ nullable = true;
150
+ continue;
151
+ }
152
+ if (typeof value === "boolean") {
153
+ sawBoolean = true;
154
+ continue;
155
+ }
156
+ if (typeof value === "string") {
157
+ sawString = true;
158
+ continue;
159
+ }
160
+ if (typeof value === "number") {
161
+ if (Number.isInteger(value)) {
162
+ sawInteger = true;
163
+ } else {
164
+ sawFloat = true;
165
+ }
166
+ continue;
167
+ }
168
+ sawStructured = true;
169
+ }
170
+ const nonNullKinds = [sawBoolean, sawString, sawInteger || sawFloat, sawStructured].filter(Boolean).length;
171
+ if (nonNullKinds === 0) {
172
+ return freezeColumnInfo({ type: "unknown", nullable });
173
+ }
174
+ if (sawStructured || nonNullKinds > 1 || sawBoolean && (sawString || sawInteger || sawFloat)) {
175
+ return freezeColumnInfo({ type: "mixed", nullable });
176
+ }
177
+ if (sawString) {
178
+ return freezeColumnInfo({ type: "string", nullable });
179
+ }
180
+ if (sawBoolean) {
181
+ return freezeColumnInfo({ type: "boolean", nullable });
182
+ }
183
+ if (sawFloat) {
184
+ return freezeColumnInfo({ type: "float", nullable });
185
+ }
186
+ return freezeColumnInfo({ type: "integer", nullable });
187
+ };
188
+ var isPandasNaiveIsoDatetime = (value) => {
189
+ const match = DATETIME_PATTERN.exec(value);
190
+ if (match?.groups === void 0) {
191
+ return false;
192
+ }
193
+ const year = Number(match.groups.year);
194
+ const month = Number(match.groups.month);
195
+ const day = Number(match.groups.day);
196
+ const hour = match.groups.hour === void 0 ? 0 : Number(match.groups.hour);
197
+ const minute = match.groups.minute === void 0 ? 0 : Number(match.groups.minute);
198
+ const second = match.groups.second === void 0 ? 0 : Number(match.groups.second);
199
+ if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59) {
200
+ return false;
201
+ }
202
+ const normalized = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
203
+ return normalized.getUTCFullYear() === year && normalized.getUTCMonth() === month - 1 && normalized.getUTCDate() === day && normalized.getUTCHours() === hour && normalized.getUTCMinutes() === minute && normalized.getUTCSeconds() === second;
204
+ };
205
+ var isScalarCategory = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
206
+ var isJsonContainer = (value) => {
207
+ if (Array.isArray(value)) {
208
+ return true;
209
+ }
210
+ if (value === null || typeof value !== "object") {
211
+ return false;
212
+ }
213
+ const prototype = Object.getPrototypeOf(value);
214
+ return prototype === Object.prototype || prototype === null;
215
+ };
216
+ var isColumnValueCompatible = (type, value) => {
217
+ if (value === null) {
218
+ return true;
219
+ }
220
+ switch (type) {
221
+ case "integer":
222
+ return typeof value === "number" && Number.isInteger(value);
223
+ case "float":
224
+ return typeof value === "number";
225
+ case "string":
226
+ return typeof value === "string";
227
+ case "boolean":
228
+ return typeof value === "boolean";
229
+ case "datetime":
230
+ return typeof value === "string" && isPandasNaiveIsoDatetime(value);
231
+ case "categorical":
232
+ return isScalarCategory(value);
233
+ case "mixed":
234
+ case "unknown":
235
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || isJsonContainer(value);
236
+ default: {
237
+ const exhaustive = type;
238
+ return exhaustive;
239
+ }
240
+ }
241
+ };
242
+ var getColumnCellPath = (orient, column, columnIndex, rowIndex, rowLabel) => {
243
+ switch (orient) {
244
+ case "records":
245
+ return `$[${rowIndex}][${JSON.stringify(column)}]`;
246
+ case "values":
247
+ return `$[${rowIndex}][${columnIndex}]`;
248
+ case "split":
249
+ return `$.data[${rowIndex}][${columnIndex}]`;
250
+ case "table":
251
+ return `$.data[${rowIndex}][${JSON.stringify(column)}]`;
252
+ case "index":
253
+ return `$[${JSON.stringify(String(rowLabel))}][${JSON.stringify(column)}]`;
254
+ case "columns":
255
+ return `$[${JSON.stringify(column)}][${JSON.stringify(String(rowLabel))}]`;
256
+ default:
257
+ return `$.data[${rowIndex}][${JSON.stringify(column)}]`;
258
+ }
259
+ };
260
+ var validateColumnValuesForType = ({
261
+ column,
262
+ columnIndex,
263
+ values,
264
+ type,
265
+ orient,
266
+ index
267
+ }) => {
268
+ for (let rowIndex = 0; rowIndex < values.length; rowIndex += 1) {
269
+ const value = values[rowIndex];
270
+ if (isColumnValueCompatible(type, value)) {
271
+ continue;
272
+ }
273
+ throw new JsonFrameValidationError("Column cells are incompatible with the declared logical type.", {
274
+ ...orient === void 0 ? {} : { orient },
275
+ path: getColumnCellPath(orient, column, columnIndex, rowIndex, index?.[rowIndex]),
276
+ row: rowIndex,
277
+ column,
278
+ value
279
+ });
280
+ }
281
+ };
282
+ var packColumn = (values, info, packThreshold) => {
283
+ if (packThreshold === 0 || values.length < packThreshold || info.nullable) {
284
+ return freezeArray(values);
285
+ }
286
+ if (info.type === "integer") {
287
+ const packed = new Int32Array(values.length);
288
+ for (let index = 0; index < values.length; index += 1) {
289
+ const value = values[index];
290
+ if (typeof value !== "number" || !Number.isInteger(value) || value < INT32_MIN || value > INT32_MAX) {
291
+ return freezeArray(values);
292
+ }
293
+ packed[index] = value;
294
+ }
295
+ return packed;
296
+ }
297
+ if (info.type === "float") {
298
+ const packed = new Float64Array(values.length);
299
+ for (let index = 0; index < values.length; index += 1) {
300
+ const value = values[index];
301
+ if (typeof value !== "number") {
302
+ return freezeArray(values);
303
+ }
304
+ packed[index] = value;
305
+ }
306
+ return packed;
307
+ }
308
+ return freezeArray(values);
309
+ };
310
+ var hasNullValue = (values) => {
311
+ columnOperationCounters.nullableChecks += values.length;
312
+ for (const value of values) {
313
+ if (value === null) {
314
+ return true;
315
+ }
316
+ }
317
+ return false;
318
+ };
319
+ var validateExplicitColumnTypes = (columns, columnTypes) => {
320
+ if (columnTypes === void 0) {
321
+ return;
322
+ }
323
+ const knownColumns = new Set(columns);
324
+ for (const column of Object.keys(columnTypes)) {
325
+ if (!knownColumns.has(column)) {
326
+ throw new JsonFrameOptionError(
327
+ "`options.columnTypes` keys must match parsed column names.",
328
+ `columnTypes.${column}`,
329
+ columnTypes[column]
330
+ );
331
+ }
332
+ }
333
+ };
334
+ var resolveSchemaTypeByColumn = (frame) => {
335
+ if (frame.tableSchema === void 0) {
336
+ return /* @__PURE__ */ new Map();
337
+ }
338
+ const schemaTypes = /* @__PURE__ */ new Map();
339
+ for (const field of frame.tableSchema.fields) {
340
+ if (frame.columns.includes(field.name)) {
341
+ schemaTypes.set(field.name, mapTableSchemaFieldType(field));
342
+ }
343
+ }
344
+ return schemaTypes;
345
+ };
346
+ var materializeColumn = (column) => {
347
+ columnOperationCounters.materializeColumnCalls += 1;
348
+ columnOperationCounters.materializedCells += column.length;
349
+ return Array.isArray(column) ? freezeArray(column) : freezeArray(Array.from(column));
350
+ };
351
+ var getStoredColumnValue = (column, position) => {
352
+ columnOperationCounters.scalarReads += 1;
353
+ return column[position];
354
+ };
355
+ var rebuildStoredColumn = (column, positions) => {
356
+ const rebuilt = new Array(positions.length);
357
+ for (let index = 0; index < positions.length; index += 1) {
358
+ rebuilt[index] = getStoredColumnValue(column, positions[index]);
359
+ }
360
+ columnOperationCounters.rebuiltCells += positions.length;
361
+ return rebuilt;
362
+ };
363
+ var materializeFrameData = (columns, data) => {
364
+ const materialized = /* @__PURE__ */ new Map();
365
+ for (const column of columns) {
366
+ const stored = data.get(column);
367
+ if (stored === void 0) {
368
+ throw new JsonFrameValidationError("Stored frame data is missing a declared column.", {
369
+ path: "$",
370
+ column
371
+ });
372
+ }
373
+ materialized.set(column, materializeColumn(stored));
374
+ }
375
+ return materialized;
376
+ };
377
+ var buildFrameState = (frame, options) => {
378
+ validateExplicitColumnTypes(frame.columns, options.columnTypes);
379
+ const schemaTypes = resolveSchemaTypeByColumn(frame);
380
+ const data = /* @__PURE__ */ new Map();
381
+ const columnInfo = /* @__PURE__ */ new Map();
382
+ for (let columnIndex = 0; columnIndex < frame.columns.length; columnIndex += 1) {
383
+ const column = frame.columns[columnIndex];
384
+ const values = frame.data.get(column);
385
+ if (values === void 0) {
386
+ throw new JsonFrameValidationError("Parsed frame data is missing a declared column.", {
387
+ orient: frame.orient,
388
+ column
389
+ });
390
+ }
391
+ const inferred = inferColumnType(values);
392
+ const resolvedType = schemaTypes.get(column) ?? options.columnTypes?.[column] ?? inferred.type;
393
+ if (schemaTypes.has(column) === false && options.columnTypes?.[column] !== void 0) {
394
+ validateColumnValuesForType({
395
+ column,
396
+ columnIndex,
397
+ values,
398
+ type: resolvedType,
399
+ orient: frame.orient,
400
+ index: frame.index
401
+ });
402
+ }
403
+ const info = freezeColumnInfo({ type: resolvedType, nullable: inferred.nullable });
404
+ columnInfo.set(column, info);
405
+ data.set(column, packColumn(values, info, options.packThreshold));
406
+ }
407
+ return {
408
+ columns: freezeArray(frame.columns),
409
+ index: freezeArray(frame.index),
410
+ indexKind: frame.indexKind,
411
+ data,
412
+ columnInfo,
413
+ ...frame.tableSchema === void 0 ? {} : { tableSchema: frame.tableSchema },
414
+ ...frame.tableIndexField === void 0 ? {} : { tableIndexField: frame.tableIndexField }
415
+ };
416
+ };
417
+ var createFrameStateFromRebuiltData = (frame, options) => {
418
+ const data = /* @__PURE__ */ new Map();
419
+ const columnInfo = /* @__PURE__ */ new Map();
420
+ for (const column of frame.columns) {
421
+ const values = frame.data.get(column);
422
+ if (values === void 0) {
423
+ throw new JsonFrameValidationError("Rebuilt frame data is missing a declared column.", {
424
+ path: "$",
425
+ column
426
+ });
427
+ }
428
+ const sourceInfo = frame.columnInfo.get(column);
429
+ if (sourceInfo === void 0) {
430
+ throw new JsonFrameValidationError("Rebuilt frame metadata is missing a declared column.", {
431
+ path: "$",
432
+ column
433
+ });
434
+ }
435
+ const info = freezeColumnInfo({
436
+ type: sourceInfo.type,
437
+ nullable: sourceInfo.nullable && hasNullValue(values)
438
+ });
439
+ columnInfo.set(column, info);
440
+ data.set(column, packColumn(values, info, options.packThreshold));
441
+ }
442
+ return {
443
+ columns: freezeArray(frame.columns),
444
+ index: freezeArray(frame.index),
445
+ indexKind: frame.indexKind,
446
+ data,
447
+ columnInfo,
448
+ ...frame.tableSchema === void 0 ? {} : { tableSchema: frame.tableSchema },
449
+ ...frame.tableIndexField === void 0 ? {} : { tableIndexField: frame.tableIndexField }
450
+ };
451
+ };
452
+ var createFrameState = (frame, options) => buildFrameState(frame, options);
453
+
454
+ // src/json.ts
455
+ var JSON_FRAME_MAX_DEPTH = 1e3;
456
+ var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
457
+ var rootPath = (path) => ({ type: "root", path });
458
+ var isPlainObject = (value) => {
459
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
460
+ return false;
461
+ }
462
+ const prototype = Object.getPrototypeOf(value);
463
+ return prototype === Object.prototype || prototype === null;
464
+ };
465
+ var pathToString = (path) => {
466
+ const segments = [];
467
+ let current = path;
468
+ while (current !== void 0 && current.type !== "root") {
469
+ segments.push(current);
470
+ current = current.parent;
471
+ }
472
+ let formatted = current?.type === "root" ? current.path : "$";
473
+ for (let index = segments.length - 1; index >= 0; index -= 1) {
474
+ const segment = segments[index];
475
+ if (segment.type === "index") {
476
+ formatted += `[${segment.index}]`;
477
+ } else if (segment.type === "property") {
478
+ formatted += IDENTIFIER_PATTERN.test(segment.key) ? `.${segment.key}` : `[${JSON.stringify(segment.key)}]`;
479
+ }
480
+ }
481
+ return formatted;
482
+ };
483
+ var childIndexPath = (parent, index) => ({ parent, type: "index", index });
484
+ var childPropertyPath = (parent, key) => ({ parent, type: "property", key });
485
+ var throwValidation = (message, path, value, orient) => {
486
+ throw new JsonFrameValidationError(message, {
487
+ ...orient === void 0 ? {} : { orient },
488
+ path: pathToString(path),
489
+ value
490
+ });
491
+ };
492
+ var cloneScalar = (value, path, orient) => {
493
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
494
+ return value;
495
+ }
496
+ if (typeof value === "number") {
497
+ if (!Number.isFinite(value)) {
498
+ throwValidation("Numbers must be finite JSON values.", path, value, orient);
499
+ }
500
+ return value;
501
+ }
502
+ if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
503
+ throwValidation("Input must contain only JSON-compatible values.", path, value, orient);
504
+ }
505
+ return void 0;
506
+ };
507
+ var createFrame = (source, path, depth, ancestors, orient) => {
508
+ const scalar = cloneScalar(source, path, orient);
509
+ if (scalar !== void 0) {
510
+ return { value: scalar };
511
+ }
512
+ if (depth > JSON_FRAME_MAX_DEPTH) {
513
+ throwValidation(
514
+ `JSON input exceeds the maximum supported nesting depth of ${JSON_FRAME_MAX_DEPTH}.`,
515
+ path,
516
+ source,
517
+ orient
518
+ );
519
+ }
520
+ if (Array.isArray(source)) {
521
+ if (ancestors.has(source)) {
522
+ throwValidation("Input contains a cyclic array.", path, source, orient);
523
+ }
524
+ const target2 = [];
525
+ ancestors.add(source);
526
+ return { value: target2, frame: { kind: "array", source, target: target2, path, depth, index: 0 } };
527
+ }
528
+ if (!isPlainObject(source)) {
529
+ throwValidation("Input objects must be plain JSON objects or arrays.", path, source, orient);
530
+ }
531
+ const objectSource = source;
532
+ if (ancestors.has(objectSource)) {
533
+ throwValidation("Input contains a cyclic object.", path, objectSource, orient);
534
+ }
535
+ const target = /* @__PURE__ */ Object.create(null);
536
+ ancestors.add(objectSource);
537
+ return {
538
+ value: target,
539
+ frame: { kind: "object", source: objectSource, target, keys: Object.keys(objectSource), path, depth, index: 0 }
540
+ };
541
+ };
542
+ var cloneJsonCompatible = (value, orient, path = "$") => {
543
+ const ancestors = /* @__PURE__ */ new Set();
544
+ const root = createFrame(value, rootPath(path), 0, ancestors, orient);
545
+ const stack = root.frame === void 0 ? [] : [root.frame];
546
+ while (stack.length > 0) {
547
+ const frame = stack[stack.length - 1];
548
+ if (frame.kind === "array") {
549
+ if (frame.index >= frame.source.length) {
550
+ ancestors.delete(frame.source);
551
+ stack.pop();
552
+ continue;
553
+ }
554
+ const index = frame.index;
555
+ frame.index += 1;
556
+ const childPath2 = childIndexPath(frame.path, index);
557
+ if (!(index in frame.source)) {
558
+ throwValidation("Sparse arrays are not valid JSON input.", childPath2, frame.source, orient);
559
+ }
560
+ const child2 = createFrame(frame.source[index], childPath2, frame.depth + 1, ancestors, orient);
561
+ frame.target.push(child2.value);
562
+ if (child2.frame !== void 0) {
563
+ stack.push(child2.frame);
564
+ }
565
+ continue;
566
+ }
567
+ if (frame.index >= frame.keys.length) {
568
+ ancestors.delete(frame.source);
569
+ stack.pop();
570
+ continue;
571
+ }
572
+ const key = frame.keys[frame.index];
573
+ frame.index += 1;
574
+ const childPath = childPropertyPath(frame.path, key);
575
+ const child = createFrame(frame.source[key], childPath, frame.depth + 1, ancestors, orient);
576
+ frame.target[key] = child.value;
577
+ if (child.frame !== void 0) {
578
+ stack.push(child.frame);
579
+ }
580
+ }
581
+ return root.value;
582
+ };
583
+
70
584
  // src/export/payload.ts
71
585
  var createRecord = () => /* @__PURE__ */ Object.create(null);
72
- var cloneJsonValue = (value) => {
73
- if (Array.isArray(value)) {
74
- return value.map((entry) => cloneJsonValue(entry));
75
- }
76
- if (value !== null && typeof value === "object") {
77
- const cloned = createRecord();
78
- for (const [key, entryValue] of Object.entries(value)) {
79
- cloned[key] = cloneJsonValue(entryValue);
80
- }
81
- return cloned;
82
- }
83
- return value;
84
- };
85
- var cloneSchemaField = (field, name = field.name) => {
586
+ var appendPath = (path, key) => `${path}[${JSON.stringify(key)}]`;
587
+ var cloneSchemaField = (field, name = field.name, path = "$.schema.fields[]") => {
86
588
  const cloned = createRecord();
87
589
  for (const [key, value] of Object.entries(field)) {
88
- cloned[key] = key === "name" ? name : value === void 0 ? void 0 : cloneJsonValue(value);
590
+ cloned[key] = key === "name" ? name : value === void 0 ? void 0 : cloneJsonCompatible(value, "table", appendPath(path, key));
89
591
  }
90
592
  return cloned;
91
593
  };
@@ -96,10 +598,12 @@ var cloneSchema = (schema, fields, primaryKey) => {
96
598
  if (key === "fields" || key === "primaryKey") {
97
599
  continue;
98
600
  }
99
- cloned[key] = value === void 0 ? void 0 : cloneJsonValue(value);
601
+ cloned[key] = value === void 0 ? void 0 : cloneJsonCompatible(value, "table", appendPath("$.schema", key));
100
602
  }
101
603
  }
102
- cloned.fields = fields.map((field) => cloneSchemaField(field));
604
+ cloned.fields = fields.map(
605
+ (field, index) => cloneSchemaField(field, field.name, `$.schema.fields[${index}]`)
606
+ );
103
607
  if (primaryKey !== void 0) {
104
608
  cloned.primaryKey = [...primaryKey];
105
609
  }
@@ -113,8 +617,9 @@ var mapColumnTypeToSchemaField = (name, info) => {
113
617
  case "float":
114
618
  return "number";
115
619
  case "string":
116
- case "categorical":
117
620
  return "string";
621
+ case "categorical":
622
+ return "any";
118
623
  case "boolean":
119
624
  return "boolean";
120
625
  case "datetime":
@@ -200,6 +705,22 @@ var resolveIndexFieldName = (state, options) => {
200
705
  }
201
706
  return indexField;
202
707
  };
708
+ var assertUniqueTableIndexLabels = (index, indexField) => {
709
+ const seen = /* @__PURE__ */ new Map();
710
+ for (let rowIndex = 0; rowIndex < index.length; rowIndex += 1) {
711
+ const label = index[rowIndex];
712
+ if (seen.has(label)) {
713
+ throw new JsonFrameValidationError("Cannot export duplicate index labels as a table primary key.", {
714
+ orient: "table",
715
+ path: `$.data[${rowIndex}][${JSON.stringify(indexField)}]`,
716
+ row: rowIndex,
717
+ column: indexField,
718
+ value: label
719
+ });
720
+ }
721
+ seen.set(label, rowIndex);
722
+ }
723
+ };
203
724
  var resolveIndexKeys = (state, orient) => {
204
725
  const seenKeys = /* @__PURE__ */ new Map();
205
726
  const keys = [];
@@ -269,19 +790,32 @@ var exportSplit = (state, materialized) => ({
269
790
  });
270
791
  var exportTable = (state, materialized, columnInfo, options) => {
271
792
  const indexField = resolveIndexFieldName(state, options);
793
+ if (indexField !== void 0) {
794
+ assertUniqueTableIndexLabels(state.index, indexField);
795
+ }
272
796
  const indexTemplate = getIndexFieldTemplate(state);
273
797
  const dataTemplates = getDataFieldTemplates(state);
274
798
  const fields = [];
275
799
  if (indexField !== void 0) {
276
800
  fields.push(
277
- indexTemplate === void 0 ? { name: indexField, type: inferIndexFieldType(state.index) } : cloneSchemaField(indexTemplate, indexField)
801
+ indexTemplate === void 0 ? { name: indexField, type: inferIndexFieldType(state.index) } : cloneSchemaField(indexTemplate, indexField, "$.schema.fields[0]")
278
802
  );
279
803
  }
280
804
  for (let columnIndex = 0; columnIndex < state.columns.length; columnIndex += 1) {
281
805
  const column = state.columns[columnIndex];
282
806
  const template = dataTemplates[columnIndex];
807
+ if (template === void 0) {
808
+ validateColumnValuesForType({
809
+ column,
810
+ columnIndex,
811
+ values: materialized.get(column) ?? [],
812
+ type: columnInfo.get(column).type,
813
+ orient: "table",
814
+ index: state.index
815
+ });
816
+ }
283
817
  fields.push(
284
- template === void 0 ? mapColumnTypeToSchemaField(column, columnInfo.get(column)) : cloneSchemaField(template, column)
818
+ template === void 0 ? mapColumnTypeToSchemaField(column, columnInfo.get(column)) : cloneSchemaField(template, column, `$.schema.fields[${fields.length}]`)
285
819
  );
286
820
  }
287
821
  const data = [];
@@ -301,185 +835,6 @@ var exportTable = (state, materialized, columnInfo, options) => {
301
835
  };
302
836
  };
303
837
 
304
- // src/frame/column.ts
305
- var INT32_MIN = -2147483648;
306
- var INT32_MAX = 2147483647;
307
- var freezeArray = (values) => Object.freeze([...values]);
308
- var freezeColumnInfo = (info) => Object.freeze({ ...info });
309
- var hasCategoricalMetadata = (field) => field.constraints?.enum !== void 0 || field.ordered === true || field.extDtype === "category";
310
- var mapTableSchemaFieldType = (field) => {
311
- if (hasCategoricalMetadata(field)) {
312
- return "categorical";
313
- }
314
- switch (field.type) {
315
- case "boolean":
316
- return "boolean";
317
- case "integer":
318
- case "year":
319
- return "integer";
320
- case "number":
321
- return "float";
322
- case "date":
323
- case "datetime":
324
- return "datetime";
325
- case "string":
326
- return "string";
327
- case "any":
328
- return "unknown";
329
- default:
330
- return "unknown";
331
- }
332
- };
333
- var inferColumnType = (values) => {
334
- let nullable = false;
335
- let sawBoolean = false;
336
- let sawString = false;
337
- let sawInteger = false;
338
- let sawFloat = false;
339
- let sawStructured = false;
340
- for (const value of values) {
341
- if (value === null) {
342
- nullable = true;
343
- continue;
344
- }
345
- if (typeof value === "boolean") {
346
- sawBoolean = true;
347
- continue;
348
- }
349
- if (typeof value === "string") {
350
- sawString = true;
351
- continue;
352
- }
353
- if (typeof value === "number") {
354
- if (Number.isInteger(value)) {
355
- sawInteger = true;
356
- } else {
357
- sawFloat = true;
358
- }
359
- continue;
360
- }
361
- sawStructured = true;
362
- }
363
- const nonNullKinds = [sawBoolean, sawString, sawInteger || sawFloat, sawStructured].filter(Boolean).length;
364
- if (nonNullKinds === 0) {
365
- return freezeColumnInfo({ type: "unknown", nullable });
366
- }
367
- if (sawStructured || nonNullKinds > 1 || sawBoolean && (sawString || sawInteger || sawFloat)) {
368
- return freezeColumnInfo({ type: "mixed", nullable });
369
- }
370
- if (sawString) {
371
- return freezeColumnInfo({ type: "string", nullable });
372
- }
373
- if (sawBoolean) {
374
- return freezeColumnInfo({ type: "boolean", nullable });
375
- }
376
- if (sawFloat) {
377
- return freezeColumnInfo({ type: "float", nullable });
378
- }
379
- return freezeColumnInfo({ type: "integer", nullable });
380
- };
381
- var packColumn = (values, info, packThreshold) => {
382
- if (packThreshold === 0 || values.length < packThreshold || info.nullable) {
383
- return freezeArray(values);
384
- }
385
- if (info.type === "integer") {
386
- const packed = new Int32Array(values.length);
387
- for (let index = 0; index < values.length; index += 1) {
388
- const value = values[index];
389
- if (typeof value !== "number" || !Number.isInteger(value) || value < INT32_MIN || value > INT32_MAX) {
390
- return freezeArray(values);
391
- }
392
- packed[index] = value;
393
- }
394
- return packed;
395
- }
396
- if (info.type === "float") {
397
- const packed = new Float64Array(values.length);
398
- for (let index = 0; index < values.length; index += 1) {
399
- const value = values[index];
400
- if (typeof value !== "number") {
401
- return freezeArray(values);
402
- }
403
- packed[index] = value;
404
- }
405
- return packed;
406
- }
407
- return freezeArray(values);
408
- };
409
- var validateExplicitColumnTypes = (columns, columnTypes) => {
410
- if (columnTypes === void 0) {
411
- return;
412
- }
413
- const knownColumns = new Set(columns);
414
- for (const column of Object.keys(columnTypes)) {
415
- if (!knownColumns.has(column)) {
416
- throw new JsonFrameOptionError(
417
- "`options.columnTypes` keys must match parsed column names.",
418
- `columnTypes.${column}`,
419
- columnTypes[column]
420
- );
421
- }
422
- }
423
- };
424
- var resolveSchemaTypeByColumn = (frame) => {
425
- if (frame.tableSchema === void 0) {
426
- return /* @__PURE__ */ new Map();
427
- }
428
- const schemaTypes = /* @__PURE__ */ new Map();
429
- for (const field of frame.tableSchema.fields) {
430
- if (frame.columns.includes(field.name)) {
431
- schemaTypes.set(field.name, mapTableSchemaFieldType(field));
432
- }
433
- }
434
- return schemaTypes;
435
- };
436
- var materializeColumn = (column) => Array.isArray(column) ? freezeArray(column) : freezeArray(Array.from(column));
437
- var materializeFrameData = (columns, data) => {
438
- const materialized = /* @__PURE__ */ new Map();
439
- for (const column of columns) {
440
- const stored = data.get(column);
441
- if (stored === void 0) {
442
- throw new JsonFrameValidationError("Stored frame data is missing a declared column.", {
443
- path: "$",
444
- column
445
- });
446
- }
447
- materialized.set(column, materializeColumn(stored));
448
- }
449
- return materialized;
450
- };
451
- var buildFrameState = (frame, options) => {
452
- validateExplicitColumnTypes(frame.columns, options.columnTypes);
453
- const schemaTypes = resolveSchemaTypeByColumn(frame);
454
- const data = /* @__PURE__ */ new Map();
455
- const columnInfo = /* @__PURE__ */ new Map();
456
- for (const column of frame.columns) {
457
- const values = frame.data.get(column);
458
- if (values === void 0) {
459
- throw new JsonFrameValidationError("Parsed frame data is missing a declared column.", {
460
- orient: frame.orient,
461
- column
462
- });
463
- }
464
- const inferred = inferColumnType(values);
465
- const resolvedType = schemaTypes.get(column) ?? options.columnTypes?.[column] ?? inferred.type;
466
- const info = freezeColumnInfo({ type: resolvedType, nullable: inferred.nullable });
467
- columnInfo.set(column, info);
468
- data.set(column, packColumn(values, info, options.packThreshold));
469
- }
470
- return {
471
- columns: freezeArray(frame.columns),
472
- index: freezeArray(frame.index),
473
- indexKind: frame.indexKind,
474
- data,
475
- columnInfo,
476
- ...frame.tableSchema === void 0 ? {} : { tableSchema: frame.tableSchema },
477
- ...frame.tableIndexField === void 0 ? {} : { tableIndexField: frame.tableIndexField }
478
- };
479
- };
480
- var createFrameStateFromData = (frame, options) => buildFrameState(frame, options);
481
- var createFrameState = (frame, options) => buildFrameState(frame, options);
482
-
483
838
  // src/frame/DataFrame.ts
484
839
  var hasOwn = Object.prototype.hasOwnProperty;
485
840
  var frameStateByInstance = /* @__PURE__ */ new WeakMap();
@@ -499,7 +854,7 @@ var cloneSchema2 = (schema, fields, primaryKey) => {
499
854
  }
500
855
  return Object.freeze(cloned);
501
856
  };
502
- var _state, _packThreshold, _DataFrame_instances, assertRowPosition_fn, materializedData_fn, createRow_fn, columnTypeOverrides_fn, rebuild_fn, selectSchema_fn, renameSchema_fn, resetIndexSchema_fn;
857
+ var _state, _packThreshold, _DataFrame_instances, assertRowPosition_fn, materializedData_fn, createRow_fn, columnInfoOverrides_fn, rebuild_fn, selectSchema_fn, renameSchema_fn, resetIndexSchema_fn;
503
858
  var DataFrame = class {
504
859
  constructor(state, packThreshold) {
505
860
  __privateAdd(this, _DataFrame_instances);
@@ -561,18 +916,22 @@ var DataFrame = class {
561
916
  }
562
917
  row(position) {
563
918
  __privateMethod(this, _DataFrame_instances, assertRowPosition_fn).call(this, position);
564
- return __privateMethod(this, _DataFrame_instances, createRow_fn).call(this, __privateMethod(this, _DataFrame_instances, materializedData_fn).call(this), position);
919
+ return __privateMethod(this, _DataFrame_instances, createRow_fn).call(this, position);
565
920
  }
566
921
  rows() {
567
- const materialized = __privateMethod(this, _DataFrame_instances, materializedData_fn).call(this);
568
- const rows = __privateGet(this, _state).index.map((_, position) => __privateMethod(this, _DataFrame_instances, createRow_fn).call(this, materialized, position));
922
+ const rows = __privateGet(this, _state).index.map((_, position) => __privateMethod(this, _DataFrame_instances, createRow_fn).call(this, position));
569
923
  return freezeArray2(rows);
570
924
  }
571
925
  filter(predicate) {
572
- const materialized = __privateMethod(this, _DataFrame_instances, materializedData_fn).call(this);
926
+ if (typeof predicate !== "function") {
927
+ throw new JsonFrameValidationError("`filter` predicate must be a function.", {
928
+ path: "$.predicate",
929
+ value: predicate
930
+ });
931
+ }
573
932
  const keptPositions = [];
574
933
  for (let position = 0; position < this.length; position += 1) {
575
- if (predicate(__privateMethod(this, _DataFrame_instances, createRow_fn).call(this, materialized, position), __privateGet(this, _state).index[position], position)) {
934
+ if (predicate(__privateMethod(this, _DataFrame_instances, createRow_fn).call(this, position), __privateGet(this, _state).index[position], position)) {
576
935
  keptPositions.push(position);
577
936
  }
578
937
  }
@@ -589,11 +948,16 @@ var DataFrame = class {
589
948
  });
590
949
  }
591
950
  sort(compare) {
592
- const materialized = __privateMethod(this, _DataFrame_instances, materializedData_fn).call(this);
951
+ if (typeof compare !== "function") {
952
+ throw new JsonFrameValidationError("`sort` compare must be a function.", {
953
+ path: "$.compare",
954
+ value: compare
955
+ });
956
+ }
593
957
  const ordering = __privateGet(this, _state).index.map((indexLabel, position) => ({
594
958
  indexLabel,
595
959
  position,
596
- row: __privateMethod(this, _DataFrame_instances, createRow_fn).call(this, materialized, position)
960
+ row: __privateMethod(this, _DataFrame_instances, createRow_fn).call(this, position)
597
961
  }));
598
962
  ordering.sort((left, right) => {
599
963
  const result = compare(left.row, right.row, left.indexLabel, right.indexLabel, left.position, right.position);
@@ -641,7 +1005,26 @@ var DataFrame = class {
641
1005
  });
642
1006
  }
643
1007
  rename(mapping) {
644
- const nextColumns = __privateGet(this, _state).columns.map((column) => hasOwn.call(mapping, column) ? mapping[column] : column);
1008
+ if (mapping === null || typeof mapping !== "object" || Array.isArray(mapping)) {
1009
+ throw new JsonFrameValidationError("`rename` mapping must be a non-array object.", {
1010
+ path: "$.mapping",
1011
+ value: mapping
1012
+ });
1013
+ }
1014
+ const nextColumns = __privateGet(this, _state).columns.map((column) => {
1015
+ if (!hasOwn.call(mapping, column)) {
1016
+ return column;
1017
+ }
1018
+ const renamedColumn = mapping[column];
1019
+ if (typeof renamedColumn !== "string") {
1020
+ throw new JsonFrameValidationError("`rename` mapping values must be strings for existing columns.", {
1021
+ path: `$.mapping[${JSON.stringify(column)}]`,
1022
+ column,
1023
+ value: renamedColumn
1024
+ });
1025
+ }
1026
+ return renamedColumn;
1027
+ });
645
1028
  const seen = /* @__PURE__ */ new Set();
646
1029
  for (let position = 0; position < nextColumns.length; position += 1) {
647
1030
  const column = nextColumns[position];
@@ -687,19 +1070,35 @@ assertRowPosition_fn = function(position) {
687
1070
  materializedData_fn = function() {
688
1071
  return materializeFrameData(__privateGet(this, _state).columns, __privateGet(this, _state).data);
689
1072
  };
690
- createRow_fn = function(materialized, position) {
1073
+ createRow_fn = function(position) {
691
1074
  const row = /* @__PURE__ */ Object.create(null);
692
1075
  for (const column of __privateGet(this, _state).columns) {
693
- row[column] = materialized.get(column)[position];
1076
+ const stored = __privateGet(this, _state).data.get(column);
1077
+ if (stored === void 0) {
1078
+ throw new JsonFrameValidationError("Stored frame data is missing a declared column.", {
1079
+ path: "$",
1080
+ column
1081
+ });
1082
+ }
1083
+ row[column] = getStoredColumnValue(stored, position);
694
1084
  }
695
1085
  return Object.freeze(row);
696
1086
  };
697
- columnTypeOverrides_fn = function(columns, sourceColumns) {
698
- return Object.freeze(
699
- Object.fromEntries(
700
- columns.map((column, position) => [column, __privateGet(this, _state).columnInfo.get(sourceColumns[position]).type])
701
- )
702
- );
1087
+ columnInfoOverrides_fn = function(columns, sourceColumns) {
1088
+ const columnInfo = /* @__PURE__ */ new Map();
1089
+ for (let position = 0; position < columns.length; position += 1) {
1090
+ const column = columns[position];
1091
+ const sourceColumn = sourceColumns[position];
1092
+ const sourceInfo = __privateGet(this, _state).columnInfo.get(sourceColumn);
1093
+ if (sourceInfo === void 0) {
1094
+ throw new JsonFrameValidationError("Stored frame metadata is missing a declared column.", {
1095
+ path: "$",
1096
+ column: sourceColumn
1097
+ });
1098
+ }
1099
+ columnInfo.set(column, sourceInfo);
1100
+ }
1101
+ return columnInfo;
703
1102
  };
704
1103
  rebuild_fn = function({
705
1104
  columns,
@@ -709,31 +1108,31 @@ rebuild_fn = function({
709
1108
  indexKind,
710
1109
  schema
711
1110
  }) {
712
- const materialized = __privateMethod(this, _DataFrame_instances, materializedData_fn).call(this);
713
1111
  const nextData = /* @__PURE__ */ new Map();
1112
+ const nextColumnInfo = __privateMethod(this, _DataFrame_instances, columnInfoOverrides_fn).call(this, columns, sourceColumns);
714
1113
  for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
715
1114
  const column = columns[columnIndex];
716
- const sourceColumn = materialized.get(sourceColumns[columnIndex]);
717
- if (sourceColumn === void 0) {
1115
+ const sourceStoredColumn = __privateGet(this, _state).data.get(sourceColumns[columnIndex]);
1116
+ if (sourceStoredColumn === void 0) {
718
1117
  throw new JsonFrameValidationError("Stored frame data is missing a declared column.", {
719
1118
  path: "$",
720
1119
  column
721
1120
  });
722
1121
  }
723
- nextData.set(column, freezeArray2(positions.map((position) => sourceColumn[position])));
1122
+ nextData.set(column, rebuildStoredColumn(sourceStoredColumn, positions));
724
1123
  }
725
- const state = createFrameStateFromData(
1124
+ const state = createFrameStateFromRebuiltData(
726
1125
  {
727
1126
  columns,
728
1127
  index,
729
1128
  indexKind,
730
1129
  data: nextData,
1130
+ columnInfo: nextColumnInfo,
731
1131
  ...schema.tableSchema === void 0 ? {} : { tableSchema: schema.tableSchema },
732
1132
  ...schema.tableIndexField === void 0 ? {} : { tableIndexField: schema.tableIndexField }
733
1133
  },
734
1134
  {
735
- packThreshold: __privateGet(this, _packThreshold),
736
- columnTypes: __privateMethod(this, _DataFrame_instances, columnTypeOverrides_fn).call(this, columns, sourceColumns)
1135
+ packThreshold: __privateGet(this, _packThreshold)
737
1136
  }
738
1137
  );
739
1138
  return createDataFrame(state, __privateGet(this, _packThreshold));
@@ -901,11 +1300,11 @@ var normalizeFromOrientOptions = (options) => {
901
1300
 
902
1301
  // src/parse/parse.ts
903
1302
  var hasOwn2 = Object.prototype.hasOwnProperty;
904
- var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
1303
+ var IDENTIFIER_PATTERN2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
905
1304
  var freezeArray3 = (values) => Object.freeze([...values]);
906
- var appendPropertyPath = (path, key) => IDENTIFIER_PATTERN.test(key) ? `${path}.${key}` : `${path}[${JSON.stringify(key)}]`;
1305
+ var appendPropertyPath = (path, key) => IDENTIFIER_PATTERN2.test(key) ? `${path}.${key}` : `${path}[${JSON.stringify(key)}]`;
907
1306
  var appendIndexPath = (path, index) => `${path}[${index}]`;
908
- var isPlainObject = (value) => {
1307
+ var isPlainObject2 = (value) => {
909
1308
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
910
1309
  return false;
911
1310
  }
@@ -925,6 +1324,18 @@ var assertSupportedIndexLabel = (value, orient, path) => {
925
1324
  value
926
1325
  });
927
1326
  };
1327
+ var assertUniqueTablePrimaryKeyLabel = (seen, label, row, column, path) => {
1328
+ if (seen.has(label)) {
1329
+ throw new JsonFrameValidationError("Table primary-key values must be unique.", {
1330
+ orient: "table",
1331
+ path,
1332
+ row,
1333
+ column,
1334
+ value: label
1335
+ });
1336
+ }
1337
+ seen.set(label, row);
1338
+ };
928
1339
  var assertUniqueStringColumns = (values, orient, path) => {
929
1340
  const columns = [];
930
1341
  const seen = /* @__PURE__ */ new Set();
@@ -982,52 +1393,6 @@ var finalizeFrame = ({
982
1393
  ...tableIndexField === void 0 ? {} : { tableIndexField }
983
1394
  };
984
1395
  };
985
- var validateJsonCompatible = (value, path = "$", stack = /* @__PURE__ */ new Set()) => {
986
- if (value === null || typeof value === "string" || typeof value === "boolean") {
987
- return value;
988
- }
989
- if (typeof value === "number") {
990
- if (!Number.isFinite(value)) {
991
- throw new JsonFrameValidationError("Numbers must be finite JSON values.", { path, value });
992
- }
993
- return value;
994
- }
995
- if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
996
- throw new JsonFrameValidationError("Input must contain only JSON-compatible values.", { path, value });
997
- }
998
- if (Array.isArray(value)) {
999
- if (stack.has(value)) {
1000
- throw new JsonFrameValidationError("Input contains a cyclic array.", { path, value });
1001
- }
1002
- stack.add(value);
1003
- const normalized2 = [];
1004
- for (let index = 0; index < value.length; index += 1) {
1005
- if (!(index in value)) {
1006
- stack.delete(value);
1007
- throw new JsonFrameValidationError("Sparse arrays are not valid JSON input.", {
1008
- path: appendIndexPath(path, index),
1009
- value
1010
- });
1011
- }
1012
- normalized2.push(validateJsonCompatible(value[index], appendIndexPath(path, index), stack));
1013
- }
1014
- stack.delete(value);
1015
- return normalized2;
1016
- }
1017
- if (!isPlainObject(value)) {
1018
- throw new JsonFrameValidationError("Input objects must be plain JSON objects or arrays.", { path, value });
1019
- }
1020
- if (stack.has(value)) {
1021
- throw new JsonFrameValidationError("Input contains a cyclic object.", { path, value });
1022
- }
1023
- stack.add(value);
1024
- const normalized = /* @__PURE__ */ Object.create(null);
1025
- for (const [key, entryValue] of Object.entries(value)) {
1026
- normalized[key] = validateJsonCompatible(entryValue, appendPropertyPath(path, key), stack);
1027
- }
1028
- stack.delete(value);
1029
- return normalized;
1030
- };
1031
1396
  var detectOrient = (value) => {
1032
1397
  if (Array.isArray(value)) {
1033
1398
  if (value.length === 0) {
@@ -1039,7 +1404,7 @@ var detectOrient = (value) => {
1039
1404
  if (value.every((entry) => Array.isArray(entry))) {
1040
1405
  return "values";
1041
1406
  }
1042
- if (value.every((entry) => isPlainObject(entry))) {
1407
+ if (value.every((entry) => isPlainObject2(entry))) {
1043
1408
  return "records";
1044
1409
  }
1045
1410
  throw new JsonFrameValidationError("Auto detection requires a non-empty array of row objects or row arrays.", {
@@ -1047,20 +1412,20 @@ var detectOrient = (value) => {
1047
1412
  value
1048
1413
  });
1049
1414
  }
1050
- if (!isPlainObject(value)) {
1415
+ if (!isPlainObject2(value)) {
1051
1416
  throw new JsonFrameValidationError("Auto detection requires a JSON array or object payload.", {
1052
1417
  path: "$",
1053
1418
  value
1054
1419
  });
1055
1420
  }
1056
- if (isPlainObject(value.schema) && Array.isArray(value.data)) {
1421
+ if (isPlainObject2(value.schema) && Array.isArray(value.data)) {
1057
1422
  return "table";
1058
1423
  }
1059
1424
  if (Array.isArray(value.columns) && Array.isArray(value.data) && (value.index === void 0 || Array.isArray(value.index))) {
1060
1425
  return "split";
1061
1426
  }
1062
1427
  const keys = Object.keys(value);
1063
- if (keys.length === 0 || keys.every((key) => isPlainObject(value[key]))) {
1428
+ if (keys.length === 0 || keys.every((key) => isPlainObject2(value[key]))) {
1064
1429
  throw new AmbiguousOrientError("Auto detection cannot distinguish between index and columns payloads.", [
1065
1430
  "index",
1066
1431
  "columns"
@@ -1082,7 +1447,7 @@ var parseRecords = (value, orient) => {
1082
1447
  for (let rowIndex = 0; rowIndex < value.length; rowIndex += 1) {
1083
1448
  const row = value[rowIndex];
1084
1449
  const rowPath = appendIndexPath("$", rowIndex);
1085
- if (!isPlainObject(row)) {
1450
+ if (!isPlainObject2(row)) {
1086
1451
  throw new JsonFrameValidationError("`records` rows must be JSON objects.", {
1087
1452
  orient,
1088
1453
  path: rowPath,
@@ -1111,7 +1476,7 @@ var parseRecords = (value, orient) => {
1111
1476
  });
1112
1477
  };
1113
1478
  var parseIndex = (value, orient) => {
1114
- if (!isPlainObject(value)) {
1479
+ if (!isPlainObject2(value)) {
1115
1480
  throw new JsonFrameValidationError("`index` payloads must be objects keyed by index label.", {
1116
1481
  orient,
1117
1482
  path: "$",
@@ -1126,7 +1491,7 @@ var parseIndex = (value, orient) => {
1126
1491
  const indexLabel = index[rowIndex];
1127
1492
  const row = value[indexLabel];
1128
1493
  const rowPath = appendPropertyPath("$", indexLabel);
1129
- if (!isPlainObject(row)) {
1494
+ if (!isPlainObject2(row)) {
1130
1495
  throw new JsonFrameValidationError("`index` payload rows must be JSON objects.", {
1131
1496
  orient,
1132
1497
  path: rowPath,
@@ -1154,7 +1519,7 @@ var parseIndex = (value, orient) => {
1154
1519
  });
1155
1520
  };
1156
1521
  var parseColumns = (value, orient) => {
1157
- if (!isPlainObject(value)) {
1522
+ if (!isPlainObject2(value)) {
1158
1523
  throw new JsonFrameValidationError("`columns` payloads must be objects keyed by column label.", {
1159
1524
  orient,
1160
1525
  path: "$",
@@ -1176,7 +1541,7 @@ var parseColumns = (value, orient) => {
1176
1541
  for (const column of columns) {
1177
1542
  const columnValue = value[column];
1178
1543
  const columnPath = appendPropertyPath("$", column);
1179
- if (!isPlainObject(columnValue)) {
1544
+ if (!isPlainObject2(columnValue)) {
1180
1545
  throw new JsonFrameValidationError("`columns` payload values must be objects keyed by index label.", {
1181
1546
  orient,
1182
1547
  path: columnPath,
@@ -1255,7 +1620,7 @@ var parseValues = (value, options, orient) => {
1255
1620
  });
1256
1621
  };
1257
1622
  var parseSplit = (value, orient) => {
1258
- if (!isPlainObject(value)) {
1623
+ if (!isPlainObject2(value)) {
1259
1624
  throw new JsonFrameValidationError("`split` payloads must be objects.", { orient, path: "$", value });
1260
1625
  }
1261
1626
  if (!Array.isArray(value.columns)) {
@@ -1330,7 +1695,7 @@ var parseSplit = (value, orient) => {
1330
1695
  });
1331
1696
  };
1332
1697
  var cloneConstraints = (value, orient, path) => {
1333
- if (!isPlainObject(value)) {
1698
+ if (!isPlainObject2(value)) {
1334
1699
  throw new JsonFrameValidationError("Table Schema field constraints must be an object when present.", {
1335
1700
  orient,
1336
1701
  path,
@@ -1372,7 +1737,7 @@ var cloneConstraints = (value, orient, path) => {
1372
1737
  var cloneTableField = (value, index) => {
1373
1738
  const orient = "table";
1374
1739
  const path = appendIndexPath("$.schema.fields", index);
1375
- if (!isPlainObject(value)) {
1740
+ if (!isPlainObject2(value)) {
1376
1741
  throw new JsonFrameValidationError("Table Schema fields must be objects.", {
1377
1742
  orient,
1378
1743
  path,
@@ -1419,7 +1784,7 @@ var cloneTableField = (value, index) => {
1419
1784
  };
1420
1785
  var cloneTableSchema = (value) => {
1421
1786
  const orient = "table";
1422
- if (!isPlainObject(value)) {
1787
+ if (!isPlainObject2(value)) {
1423
1788
  throw new JsonFrameValidationError("`table.schema` must be an object.", {
1424
1789
  orient,
1425
1790
  path: "$.schema",
@@ -1495,7 +1860,7 @@ var cloneTableSchema = (value) => {
1495
1860
  return Object.freeze(cloned);
1496
1861
  };
1497
1862
  var parseTable = (value, orient) => {
1498
- if (!isPlainObject(value)) {
1863
+ if (!isPlainObject2(value)) {
1499
1864
  throw new JsonFrameValidationError("`table` payloads must be objects.", { orient, path: "$", value });
1500
1865
  }
1501
1866
  const schema = cloneTableSchema(value.schema);
@@ -1520,10 +1885,11 @@ var parseTable = (value, orient) => {
1520
1885
  const fieldNames = new Set(schema.fields.map((field) => field.name));
1521
1886
  const index = [];
1522
1887
  const indexKind = primaryKey === void 0 ? "synthetic" : "source";
1888
+ const seenPrimaryKeyLabels = /* @__PURE__ */ new Map();
1523
1889
  for (let rowIndex = 0; rowIndex < value.data.length; rowIndex += 1) {
1524
1890
  const row = value.data[rowIndex];
1525
1891
  const rowPath = appendIndexPath("$.data", rowIndex);
1526
- if (!isPlainObject(row)) {
1892
+ if (!isPlainObject2(row)) {
1527
1893
  throw new JsonFrameValidationError("`table.data` rows must be objects.", {
1528
1894
  orient,
1529
1895
  path: rowPath,
@@ -1553,9 +1919,10 @@ var parseTable = (value, orient) => {
1553
1919
  value: row
1554
1920
  });
1555
1921
  } else {
1556
- index.push(
1557
- assertSupportedIndexLabel(row[primaryKey], orient, appendPropertyPath(rowPath, primaryKey))
1558
- );
1922
+ const primaryKeyPath = appendPropertyPath(rowPath, primaryKey);
1923
+ const indexLabel = assertSupportedIndexLabel(row[primaryKey], orient, primaryKeyPath);
1924
+ assertUniqueTablePrimaryKeyLabel(seenPrimaryKeyLabels, indexLabel, rowIndex, primaryKey, primaryKeyPath);
1925
+ index.push(indexLabel);
1559
1926
  }
1560
1927
  for (const column of columns) {
1561
1928
  data.get(column).push(hasOwn2.call(row, column) ? row[column] : null);
@@ -1585,7 +1952,7 @@ var parseInput = (input, options) => {
1585
1952
  throw error;
1586
1953
  }
1587
1954
  })();
1588
- const jsonValue = validateJsonCompatible(parsedValue);
1955
+ const jsonValue = cloneJsonCompatible(parsedValue);
1589
1956
  const orient = options.orient === "auto" ? detectOrient(jsonValue) : options.orient;
1590
1957
  switch (orient) {
1591
1958
  case "records":
@@ -1616,6 +1983,7 @@ function fromOrient(input, options) {
1616
1983
  export {
1617
1984
  AmbiguousOrientError,
1618
1985
  ExportKeyCollisionError,
1986
+ JSON_FRAME_MAX_DEPTH,
1619
1987
  JsonFrameError,
1620
1988
  JsonFrameOptionError,
1621
1989
  JsonFrameParseError,