@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.d.ts CHANGED
@@ -6,8 +6,29 @@ type JsonArray = readonly JsonValue[];
6
6
  interface JsonObject {
7
7
  readonly [key: string]: JsonValue;
8
8
  }
9
- /** Any JSON-compatible value accepted by `fromOrient` payloads and exporters. */
9
+ /**
10
+ * Any JSON-compatible value accepted by `fromOrient` payloads and exporters.
11
+ * Arrays/objects may nest up to `JSON_FRAME_MAX_DEPTH` levels from the parsed
12
+ * root value; deeper containers fail with `JsonFrameValidationError`.
13
+ */
10
14
  type JsonValue = JsonPrimitive | JsonArray | JsonObject;
15
+ /**
16
+ * Recursive compile-time JSON compatibility check for domain row interfaces.
17
+ *
18
+ * This lets ordinary interfaces with known JSON-compatible properties be used
19
+ * as `DataFrame` row models without requiring a string index signature. It is
20
+ * only a TypeScript constraint; runtime validation still follows the selected
21
+ * orient and does not validate application schemas from `TRow`.
22
+ */
23
+ type JsonCompatible<T> = T extends JsonPrimitive ? T : T extends (...args: never[]) => unknown ? never : T extends readonly (infer TItem)[] ? readonly JsonCompatible<TItem>[] : T extends object ? {
24
+ readonly [K in keyof T]: JsonCompatible<T[K]>;
25
+ } : never;
26
+ /** Object-shaped row model whose known properties are JSON-compatible. */
27
+ type JsonCompatibleRow<TRow extends object> = {
28
+ readonly [K in keyof TRow]: JsonCompatible<TRow[K]>;
29
+ };
30
+ /** Default row shape used when callers do not provide a domain row model. */
31
+ type JsonRow = Record<string, JsonValue>;
11
32
  /** pandas orient names supported by `fromOrient`. */
12
33
  type ResolvedOrient = 'records' | 'index' | 'columns' | 'values' | 'split' | 'table';
13
34
  /** Orient selection for `fromOrient`. `auto` performs structural detection when possible. */
@@ -20,27 +41,39 @@ type IndexLabel = string | number;
20
41
  type IndexKind = 'source' | 'synthetic';
21
42
  /** Parsed payload for `orient='records'`. */
22
43
  type RecordsPayload = readonly JsonObject[];
23
- /** Parsed payload for `orient='index'`. */
44
+ /**
45
+ * Parsed payload for `orient='index'`.
46
+ *
47
+ * Row order follows JavaScript property enumeration for object keys, including
48
+ * numeric ordering of integer-like keys. Use `SplitPayload` or `TablePayload`
49
+ * when exact row order matters for integer-like index labels.
50
+ */
24
51
  type IndexPayload = Readonly<Record<string, JsonObject>>;
25
- /** Parsed payload for `orient='columns'`. */
52
+ /**
53
+ * Parsed payload for `orient='columns'`.
54
+ *
55
+ * Row order follows JavaScript property enumeration for each column object's
56
+ * index keys, including numeric ordering of integer-like keys. Use
57
+ * `SplitPayload` or `TablePayload` when exact row order matters for
58
+ * integer-like index labels.
59
+ */
26
60
  type ColumnsPayload = Readonly<Record<string, JsonObject>>;
27
61
  /** Parsed payload for `orient='values'`. */
28
62
  type ValuesPayload = readonly JsonArray[];
29
63
  /** Parsed payload for `orient='split'`. */
30
- interface SplitPayload {
64
+ interface SplitPayload extends JsonObject {
31
65
  readonly columns: readonly ColumnLabel[];
32
66
  readonly index: readonly IndexLabel[];
33
67
  readonly data: readonly JsonArray[];
34
68
  }
35
69
  /** Known Frictionless/Table Schema field types emitted by pandas. */
36
70
  type TableSchemaFieldType = 'any' | 'array' | 'boolean' | 'date' | 'datetime' | 'duration' | 'geojson' | 'integer' | 'number' | 'object' | 'string' | 'year' | 'yearmonth' | (string & {});
71
+ type JsonMetadata = Readonly<Record<string, JsonValue>>;
37
72
  /** Table Schema constraint metadata preserved when present. */
38
- interface TableSchemaConstraints {
73
+ type TableSchemaConstraints = JsonMetadata & {
39
74
  /** Enumerated allowed scalar values, commonly used for pandas categoricals. */
40
75
  readonly enum?: readonly JsonPrimitive[];
41
- readonly [key: string]: JsonValue | undefined;
42
- }
43
- type JsonMetadata = Readonly<Record<string, JsonValue | undefined>>;
76
+ };
44
77
  /** Table Schema field metadata preserved during table ingestion and export. */
45
78
  type TableSchemaField = JsonMetadata & {
46
79
  readonly name: string;
@@ -56,14 +89,25 @@ type TableSchemaField = JsonMetadata & {
56
89
  type TableSchema = JsonMetadata & {
57
90
  readonly fields: readonly TableSchemaField[];
58
91
  readonly primaryKey?: readonly string[];
92
+ /** Table Schema format version emitted by pandas, not the installed pandas package version. */
59
93
  readonly pandas_version?: string;
60
94
  };
61
95
  /** Parsed payload for `orient='table'`. */
62
- interface TablePayload {
96
+ interface TablePayload extends JsonObject {
63
97
  readonly schema: TableSchema;
64
98
  readonly data: readonly JsonObject[];
65
99
  }
66
- /** Logical column types inferred or preserved by the frame. */
100
+ /**
101
+ * Logical column types inferred, preserved from Table Schema, or supplied with
102
+ * `options.columnTypes`.
103
+ *
104
+ * Explicit non-table `columnTypes` are validated against non-null cells without
105
+ * coercion. `datetime` accepts pandas-style timezone-naive ISO date/datetime
106
+ * strings for generated Table Schema output, not numeric epoch values.
107
+ * `categorical` accepts non-null scalar JSON values and exports as Table Schema
108
+ * `type: 'any'` with `extDtype: 'category'` when no source field metadata is
109
+ * available. `mixed` and `unknown` accept any JSON-compatible cell value.
110
+ */
67
111
  type ColumnType = 'integer' | 'float' | 'string' | 'boolean' | 'datetime' | 'categorical' | 'mixed' | 'unknown';
68
112
  /** Public logical type metadata for a stored column. */
69
113
  interface ColumnInfo {
@@ -75,16 +119,21 @@ interface ColumnInfo {
75
119
  /**
76
120
  * Options for `fromOrient`.
77
121
  *
78
- * `columns` is required only for explicit `values` input because that orient
79
- * carries no column labels. `columnTypes` supplies explicit logical metadata
80
- * for label-preserving orients that do not carry Table Schema field types.
122
+ * `columns` is required whenever the payload is `values`, including non-empty
123
+ * `values` arrays detected by `auto`, because that orient carries no column
124
+ * labels. `columnTypes` supplies explicit logical metadata for label-preserving
125
+ * orients that do not carry Table Schema field types.
81
126
  */
82
127
  interface FromOrientOptions {
83
128
  /** Explicit orient, or `auto` to detect only structurally unambiguous shapes. */
84
129
  readonly orient?: Orient;
85
- /** Explicit column names used when parsing `orient='values'`. */
130
+ /** Explicit column names used whenever parsing a `values` payload. */
86
131
  readonly columns?: readonly ColumnLabel[];
87
- /** Explicit logical column types applied after parsing when no Table Schema is present. */
132
+ /**
133
+ * Explicit logical column types applied after parsing when no Table Schema is
134
+ * present. Non-null cells must already be compatible; values are never
135
+ * coerced to satisfy the declared logical type.
136
+ */
88
137
  readonly columnTypes?: Readonly<Partial<Record<ColumnLabel, ColumnType>>>;
89
138
  /** Packing threshold for typed-array storage. `0` disables packing. */
90
139
  readonly packThreshold?: number;
@@ -101,9 +150,12 @@ type ToJSONStringOptions = ToTableOptions;
101
150
  *
102
151
  * Instances are created by `fromOrient()`. The generic row type improves row
103
152
  * and callback ergonomics only; runtime validation still follows the JSON
104
- * Frame contracts for the selected orient.
153
+ * Frame contracts for the selected orient. Immutability is structural and
154
+ * shallow: frame-owned arrays, records, and maps are protected, but nested JSON
155
+ * cell objects/arrays returned from rows or exporters may retain identity and
156
+ * remain caller-mutable.
105
157
  */
106
- interface DataFrame<TRow extends Record<string, JsonValue> = Record<string, JsonValue>> {
158
+ interface DataFrame<TRow extends JsonCompatibleRow<TRow> = JsonRow> {
107
159
  readonly columns: readonly string[];
108
160
  readonly index: readonly IndexLabel[];
109
161
  readonly columnInfo: ReadonlyMap<string, ColumnInfo>;
@@ -113,31 +165,53 @@ interface DataFrame<TRow extends Record<string, JsonValue> = Record<string, Json
113
165
  toColumns(): ColumnsPayload;
114
166
  toValues(): ValuesPayload;
115
167
  toSplit(): SplitPayload;
168
+ /** Exports Table Schema JSON; source index labels must be unique primary-key values. */
116
169
  toTable(options?: ToTableOptions): TablePayload;
170
+ /** Serializes an exported orient, including table metadata, within `JSON_FRAME_MAX_DEPTH`. */
117
171
  toJSONString(orient: ResolvedOrient, options?: ToJSONStringOptions): string;
118
172
  row(position: number): Readonly<TRow>;
119
173
  rows(): readonly Readonly<TRow>[];
120
174
  filter(predicate: (row: Readonly<TRow>, index: IndexLabel, position: number) => boolean): DataFrame<TRow>;
121
175
  sort(compare: (left: Readonly<TRow>, right: Readonly<TRow>, leftIndex: IndexLabel, rightIndex: IndexLabel, leftPosition: number, rightPosition: number) => number): DataFrame<TRow>;
122
- select(...columns: readonly string[]): DataFrame<Record<string, JsonValue>>;
123
- rename(mapping: Readonly<Record<string, string>>): DataFrame<Record<string, JsonValue>>;
176
+ select(...columns: readonly string[]): DataFrame<JsonRow>;
177
+ rename(mapping: Readonly<Record<string, string>>): DataFrame<JsonRow>;
124
178
  resetIndex(): DataFrame<TRow>;
125
179
  }
126
180
 
127
181
  /**
128
182
  * Parses pandas-compatible JSON into an immutable `DataFrame`.
129
183
  *
130
- * `auto` detection recognizes only structurally unambiguous payloads. Pass an
131
- * explicit `options.orient` for `index`, `columns`, empty arrays, empty
132
- * objects, and all `values` payloads. The generic row type is for TypeScript
133
- * ergonomics and is not runtime-validated beyond the documented JSON-orient
134
- * contracts.
184
+ * `auto` detection recognizes only structurally unambiguous payloads, including
185
+ * non-empty `values` arrays. Every `values` payload still requires
186
+ * `options.columns` because that orient carries no column labels; empty
187
+ * `values` input also requires explicit `options.orient: 'values'`. Pass an
188
+ * explicit `options.orient` for `index`, `columns`, empty arrays, and empty
189
+ * objects. The generic row type is for TypeScript ergonomics and is not
190
+ * runtime-validated beyond the documented JSON-orient contracts. `index` and
191
+ * `columns` object-key payloads follow JavaScript property enumeration order
192
+ * for integer-like keys; use `split` or `table` when exact row order must be
193
+ * preserved for those labels. JSON arrays/objects may nest up to
194
+ * `JSON_FRAME_MAX_DEPTH` levels from the parsed root value. Explicit non-table
195
+ * `columnTypes` are validated against non-null cells and never coerce values.
135
196
  */
136
- declare function fromOrient<TRow extends Record<string, JsonValue> = Record<string, JsonValue>>(input: string, options?: FromOrientOptions): DataFrame<TRow>;
137
- declare function fromOrient<TRow extends Record<string, JsonValue>>(input: readonly TRow[], options?: FromOrientOptions & {
197
+ declare function fromOrient<TRow extends JsonCompatibleRow<TRow> = JsonRow>(input: string, options?: FromOrientOptions): DataFrame<TRow>;
198
+ declare function fromOrient<TRow extends JsonCompatibleRow<TRow>>(input: readonly TRow[], options?: FromOrientOptions & {
138
199
  readonly orient?: 'auto' | 'records';
139
200
  }): DataFrame<TRow>;
140
- declare function fromOrient<TRow extends Record<string, JsonValue> = Record<string, JsonValue>>(input: JsonValue, options?: FromOrientOptions): DataFrame<TRow>;
201
+ declare function fromOrient<TRow extends JsonCompatibleRow<TRow> = JsonRow>(input: SplitPayload, options: FromOrientOptions & {
202
+ readonly orient: 'split';
203
+ }): DataFrame<TRow>;
204
+ declare function fromOrient<TRow extends JsonCompatibleRow<TRow> = JsonRow>(input: TablePayload, options: FromOrientOptions & {
205
+ readonly orient: 'table';
206
+ }): DataFrame<TRow>;
207
+ declare function fromOrient<TRow extends JsonCompatibleRow<TRow> = JsonRow>(input: JsonValue, options?: FromOrientOptions): DataFrame<TRow>;
208
+
209
+ /**
210
+ * Maximum supported nesting for JSON arrays/objects traversed by the package.
211
+ * The parsed root is depth 0; an array/object at depth 1000 is accepted, while
212
+ * an array/object at depth 1001 fails with `JsonFrameValidationError`.
213
+ */
214
+ declare const JSON_FRAME_MAX_DEPTH = 1000;
141
215
 
142
216
  type JsonFrameErrorContext = {
143
217
  readonly orient?: Orient;
@@ -146,13 +220,25 @@ type JsonFrameErrorContext = {
146
220
  readonly column?: string;
147
221
  readonly value?: unknown;
148
222
  };
223
+ /** Bounded value stored on structured errors instead of caller-owned containers. */
224
+ type JsonFrameDiagnosticValue = string | number | boolean | null | Readonly<{
225
+ readonly kind: 'array';
226
+ readonly length: number;
227
+ }> | Readonly<{
228
+ readonly kind: 'object';
229
+ readonly keyCount: number;
230
+ readonly keys: readonly string[];
231
+ readonly truncated: boolean;
232
+ }> | Readonly<{
233
+ readonly kind: 'undefined' | 'symbol' | 'bigint' | 'function';
234
+ }>;
149
235
  /** Base class for all structured `@web-ts-toolkit/json-frame` runtime failures. */
150
236
  declare class JsonFrameError extends Error {
151
237
  readonly orient?: Orient;
152
238
  readonly path?: string;
153
239
  readonly row?: number;
154
240
  readonly column?: string;
155
- readonly value?: unknown;
241
+ readonly value?: JsonFrameDiagnosticValue;
156
242
  constructor(message: string, context?: JsonFrameErrorContext);
157
243
  }
158
244
  /** Raised when a JSON string cannot be parsed before orient validation begins. */
@@ -193,4 +279,4 @@ declare class ExportKeyCollisionError extends JsonFrameError {
193
279
  constructor(message: string, orient: ResolvedOrient, key: string, labels: readonly (string | number)[]);
194
280
  }
195
281
 
196
- export { AmbiguousOrientError, type ColumnInfo, type ColumnLabel, type ColumnType, type ColumnsPayload, type DataFrame, ExportKeyCollisionError, type FromOrientOptions, type IndexKind, type IndexLabel, type IndexPayload, type JsonArray, JsonFrameError, JsonFrameOptionError, JsonFrameParseError, JsonFrameValidationError, type JsonObject, type JsonPrimitive, type JsonValue, type Orient, type RecordsPayload, type ResolvedOrient, type SplitPayload, type TablePayload, type TableSchema, type TableSchemaConstraints, type TableSchemaField, type TableSchemaFieldType, type ToJSONStringOptions, type ToTableOptions, UnsupportedFeatureError, type ValuesPayload, fromOrient };
282
+ export { AmbiguousOrientError, type ColumnInfo, type ColumnLabel, type ColumnType, type ColumnsPayload, type DataFrame, ExportKeyCollisionError, type FromOrientOptions, type IndexKind, type IndexLabel, type IndexPayload, JSON_FRAME_MAX_DEPTH, type JsonArray, type JsonCompatible, type JsonCompatibleRow, type JsonFrameDiagnosticValue, JsonFrameError, JsonFrameOptionError, JsonFrameParseError, JsonFrameValidationError, type JsonObject, type JsonPrimitive, type JsonRow, type JsonValue, type Orient, type RecordsPayload, type ResolvedOrient, type SplitPayload, type TablePayload, type TableSchema, type TableSchemaConstraints, type TableSchemaField, type TableSchemaFieldType, type ToJSONStringOptions, type ToTableOptions, UnsupportedFeatureError, type ValuesPayload, fromOrient };