typed-csv 1.0.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.
- package/README.md +190 -0
- package/dist/csv-loader/esbuild.d.mts +45 -0
- package/dist/csv-loader/esbuild.d.ts +45 -0
- package/dist/csv-loader/esbuild.js +2020 -0
- package/dist/csv-loader/esbuild.mjs +1985 -0
- package/dist/csv-loader/loader.d.mts +154 -0
- package/dist/csv-loader/loader.d.ts +154 -0
- package/dist/csv-loader/loader.js +1736 -0
- package/dist/csv-loader/loader.mjs +1701 -0
- package/dist/csv-loader/rollup.d.mts +60 -0
- package/dist/csv-loader/rollup.d.ts +60 -0
- package/dist/csv-loader/rollup.js +2006 -0
- package/dist/csv-loader/rollup.mjs +1971 -0
- package/dist/csv-loader/webpack.d.mts +42 -0
- package/dist/csv-loader/webpack.d.ts +42 -0
- package/dist/csv-loader/webpack.js +1979 -0
- package/dist/csv-loader/webpack.mjs +1948 -0
- package/dist/index.d.mts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +877 -0
- package/dist/index.mjs +845 -0
- package/package.json +76 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
// src/parser.ts
|
|
2
|
+
var ParseError = class extends Error {
|
|
3
|
+
constructor(message, position, schema, value) {
|
|
4
|
+
let fullMessage = message;
|
|
5
|
+
if (position !== void 0) {
|
|
6
|
+
fullMessage += ` at position ${position}`;
|
|
7
|
+
}
|
|
8
|
+
if (schema !== void 0) {
|
|
9
|
+
fullMessage += `. Schema: ${schema}`;
|
|
10
|
+
}
|
|
11
|
+
if (value !== void 0) {
|
|
12
|
+
fullMessage += `. Value: ${value}`;
|
|
13
|
+
}
|
|
14
|
+
super(fullMessage);
|
|
15
|
+
this.position = position;
|
|
16
|
+
this.schema = schema;
|
|
17
|
+
this.value = value;
|
|
18
|
+
this.name = "ParseError";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var Parser = class {
|
|
22
|
+
constructor(input) {
|
|
23
|
+
this.pos = 0;
|
|
24
|
+
this.input = input;
|
|
25
|
+
}
|
|
26
|
+
peek() {
|
|
27
|
+
return this.input[this.pos] || "";
|
|
28
|
+
}
|
|
29
|
+
consume() {
|
|
30
|
+
return this.input[this.pos++] || "";
|
|
31
|
+
}
|
|
32
|
+
skipWhitespace() {
|
|
33
|
+
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
|
|
34
|
+
this.pos++;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
match(str) {
|
|
38
|
+
return this.input.slice(this.pos, this.pos + str.length) === str;
|
|
39
|
+
}
|
|
40
|
+
consumeStr(str) {
|
|
41
|
+
if (this.match(str)) {
|
|
42
|
+
this.pos += str.length;
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
getPosition() {
|
|
48
|
+
return this.pos;
|
|
49
|
+
}
|
|
50
|
+
getInputLength() {
|
|
51
|
+
return this.input.length;
|
|
52
|
+
}
|
|
53
|
+
parseSchema() {
|
|
54
|
+
this.skipWhitespace();
|
|
55
|
+
let schema = this.parseSchemaInternal();
|
|
56
|
+
this.skipWhitespace();
|
|
57
|
+
if (this.consumeStr("[")) {
|
|
58
|
+
this.skipWhitespace();
|
|
59
|
+
if (!this.consumeStr("]")) {
|
|
60
|
+
throw new ParseError("Expected ]", this.pos);
|
|
61
|
+
}
|
|
62
|
+
schema = { type: "array", element: schema };
|
|
63
|
+
this.skipWhitespace();
|
|
64
|
+
}
|
|
65
|
+
if (this.consumeStr("|")) {
|
|
66
|
+
const members = [schema];
|
|
67
|
+
while (true) {
|
|
68
|
+
this.skipWhitespace();
|
|
69
|
+
const member = this.parseSchemaInternal();
|
|
70
|
+
members.push(member);
|
|
71
|
+
this.skipWhitespace();
|
|
72
|
+
if (!this.consumeStr("|")) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
type: "union",
|
|
78
|
+
members
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return schema;
|
|
82
|
+
}
|
|
83
|
+
parseSchemaInternal() {
|
|
84
|
+
this.skipWhitespace();
|
|
85
|
+
if (this.consumeStr("(")) {
|
|
86
|
+
this.skipWhitespace();
|
|
87
|
+
const schema = this.parseSchema();
|
|
88
|
+
this.skipWhitespace();
|
|
89
|
+
if (!this.consumeStr(")")) {
|
|
90
|
+
throw new ParseError("Expected )", this.pos);
|
|
91
|
+
}
|
|
92
|
+
this.skipWhitespace();
|
|
93
|
+
if (this.consumeStr("[")) {
|
|
94
|
+
this.skipWhitespace();
|
|
95
|
+
if (!this.consumeStr("]")) {
|
|
96
|
+
throw new ParseError("Expected ]", this.pos);
|
|
97
|
+
}
|
|
98
|
+
return { type: "array", element: schema };
|
|
99
|
+
}
|
|
100
|
+
return schema;
|
|
101
|
+
}
|
|
102
|
+
if (this.peek() === '"' || this.peek() === "'") {
|
|
103
|
+
return this.parseStringLiteralSchema();
|
|
104
|
+
}
|
|
105
|
+
if (this.consumeStr("~")) {
|
|
106
|
+
return this.parseReverseReferenceSchema();
|
|
107
|
+
}
|
|
108
|
+
if (this.consumeStr("@")) {
|
|
109
|
+
return this.parseReferenceSchema();
|
|
110
|
+
}
|
|
111
|
+
if (this.consumeStr("string")) {
|
|
112
|
+
if (this.consumeStr("[")) {
|
|
113
|
+
this.skipWhitespace();
|
|
114
|
+
if (!this.consumeStr("]")) {
|
|
115
|
+
throw new ParseError("Expected ]", this.pos);
|
|
116
|
+
}
|
|
117
|
+
return { type: "array", element: { type: "string" } };
|
|
118
|
+
}
|
|
119
|
+
return { type: "string" };
|
|
120
|
+
}
|
|
121
|
+
if (this.consumeStr("number")) {
|
|
122
|
+
if (this.consumeStr("[")) {
|
|
123
|
+
this.skipWhitespace();
|
|
124
|
+
if (!this.consumeStr("]")) {
|
|
125
|
+
throw new ParseError("Expected ]", this.pos);
|
|
126
|
+
}
|
|
127
|
+
return { type: "array", element: { type: "number" } };
|
|
128
|
+
}
|
|
129
|
+
return { type: "number" };
|
|
130
|
+
}
|
|
131
|
+
if (this.consumeStr("int")) {
|
|
132
|
+
if (this.consumeStr("[")) {
|
|
133
|
+
this.skipWhitespace();
|
|
134
|
+
if (!this.consumeStr("]")) {
|
|
135
|
+
throw new ParseError("Expected ]", this.pos);
|
|
136
|
+
}
|
|
137
|
+
return { type: "array", element: { type: "int" } };
|
|
138
|
+
}
|
|
139
|
+
return { type: "int" };
|
|
140
|
+
}
|
|
141
|
+
if (this.consumeStr("float")) {
|
|
142
|
+
if (this.consumeStr("[")) {
|
|
143
|
+
this.skipWhitespace();
|
|
144
|
+
if (!this.consumeStr("]")) {
|
|
145
|
+
throw new ParseError("Expected ]", this.pos);
|
|
146
|
+
}
|
|
147
|
+
return { type: "array", element: { type: "float" } };
|
|
148
|
+
}
|
|
149
|
+
return { type: "float" };
|
|
150
|
+
}
|
|
151
|
+
if (this.consumeStr("boolean")) {
|
|
152
|
+
if (this.consumeStr("[")) {
|
|
153
|
+
this.skipWhitespace();
|
|
154
|
+
if (!this.consumeStr("]")) {
|
|
155
|
+
throw new ParseError("Expected ]", this.pos);
|
|
156
|
+
}
|
|
157
|
+
return { type: "array", element: { type: "boolean" } };
|
|
158
|
+
}
|
|
159
|
+
return { type: "boolean" };
|
|
160
|
+
}
|
|
161
|
+
if (this.consumeStr("[")) {
|
|
162
|
+
const elements = [];
|
|
163
|
+
this.skipWhitespace();
|
|
164
|
+
if (this.peek() === "]") {
|
|
165
|
+
this.consume();
|
|
166
|
+
throw new ParseError("Empty array/tuple not allowed", this.pos);
|
|
167
|
+
}
|
|
168
|
+
elements.push(this.parseNamedSchema());
|
|
169
|
+
this.skipWhitespace();
|
|
170
|
+
if (this.consumeStr(";")) {
|
|
171
|
+
const remainingElements = [];
|
|
172
|
+
while (true) {
|
|
173
|
+
this.skipWhitespace();
|
|
174
|
+
remainingElements.push(this.parseNamedSchema());
|
|
175
|
+
this.skipWhitespace();
|
|
176
|
+
if (!this.consumeStr(";")) {
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
elements.push(...remainingElements);
|
|
181
|
+
}
|
|
182
|
+
this.skipWhitespace();
|
|
183
|
+
if (!this.consumeStr("]")) {
|
|
184
|
+
throw new ParseError("Expected ]", this.pos);
|
|
185
|
+
}
|
|
186
|
+
if (this.consumeStr("[")) {
|
|
187
|
+
this.skipWhitespace();
|
|
188
|
+
if (!this.consumeStr("]")) {
|
|
189
|
+
throw new ParseError("Expected ]", this.pos);
|
|
190
|
+
}
|
|
191
|
+
if (elements.length === 1 && !elements[0].name) {
|
|
192
|
+
return { type: "array", element: elements[0].schema };
|
|
193
|
+
}
|
|
194
|
+
return { type: "array", element: { type: "tuple", elements } };
|
|
195
|
+
}
|
|
196
|
+
if (elements.length === 1 && !elements[0].name) {
|
|
197
|
+
return { type: "array", element: elements[0].schema };
|
|
198
|
+
}
|
|
199
|
+
return { type: "tuple", elements };
|
|
200
|
+
}
|
|
201
|
+
throw new ParseError(
|
|
202
|
+
`Unknown type: ${this.peek() || "end of input"}`,
|
|
203
|
+
this.pos
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
parseStringLiteralSchema() {
|
|
207
|
+
const value = this.parseStringLiteral();
|
|
208
|
+
return {
|
|
209
|
+
type: "stringLiteral",
|
|
210
|
+
value
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
parseStringLiteral() {
|
|
214
|
+
const quote = this.peek();
|
|
215
|
+
if (quote !== '"' && quote !== "'") {
|
|
216
|
+
throw new ParseError("Expected string literal with quotes", this.pos);
|
|
217
|
+
}
|
|
218
|
+
this.consume();
|
|
219
|
+
let value = "";
|
|
220
|
+
while (this.pos < this.input.length) {
|
|
221
|
+
const char = this.peek();
|
|
222
|
+
if (char === "\\") {
|
|
223
|
+
this.consume();
|
|
224
|
+
const nextChar = this.consume();
|
|
225
|
+
if (nextChar === '"' || nextChar === "'" || nextChar === "\\" || nextChar === "|" || nextChar === ";" || nextChar === "(" || nextChar === ")") {
|
|
226
|
+
value += nextChar;
|
|
227
|
+
} else {
|
|
228
|
+
value += "\\" + nextChar;
|
|
229
|
+
}
|
|
230
|
+
} else if (char === quote) {
|
|
231
|
+
this.consume();
|
|
232
|
+
return value;
|
|
233
|
+
} else {
|
|
234
|
+
value += this.consume();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
throw new ParseError("Unterminated string literal", this.pos);
|
|
238
|
+
}
|
|
239
|
+
parseNamedSchema() {
|
|
240
|
+
this.skipWhitespace();
|
|
241
|
+
const startpos = this.pos;
|
|
242
|
+
let identifier = "";
|
|
243
|
+
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
|
|
244
|
+
identifier += this.consume();
|
|
245
|
+
}
|
|
246
|
+
if (identifier.length === 0) {
|
|
247
|
+
const schema = this.parseSchema();
|
|
248
|
+
return { schema };
|
|
249
|
+
}
|
|
250
|
+
this.skipWhitespace();
|
|
251
|
+
if (this.consumeStr(":")) {
|
|
252
|
+
this.skipWhitespace();
|
|
253
|
+
const name = identifier;
|
|
254
|
+
const schema = this.parseSchema();
|
|
255
|
+
return { name, schema };
|
|
256
|
+
} else {
|
|
257
|
+
this.pos = startpos;
|
|
258
|
+
const schema = this.parseSchema();
|
|
259
|
+
return { schema };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
parseReferenceSchema() {
|
|
263
|
+
let tableName = "";
|
|
264
|
+
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
|
|
265
|
+
tableName += this.consume();
|
|
266
|
+
}
|
|
267
|
+
if (tableName.length === 0) {
|
|
268
|
+
throw new ParseError("Expected table name after @", this.pos);
|
|
269
|
+
}
|
|
270
|
+
this.skipWhitespace();
|
|
271
|
+
if (this.consumeStr("[]")) {
|
|
272
|
+
this.skipWhitespace();
|
|
273
|
+
const isOptional2 = this.consumeStr("?");
|
|
274
|
+
return {
|
|
275
|
+
type: "reference",
|
|
276
|
+
tableName,
|
|
277
|
+
isArray: true,
|
|
278
|
+
isOptional: isOptional2
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const isOptional = this.consumeStr("?");
|
|
282
|
+
return {
|
|
283
|
+
type: "reference",
|
|
284
|
+
tableName,
|
|
285
|
+
isArray: false,
|
|
286
|
+
isOptional
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
parseReverseReferenceSchema() {
|
|
290
|
+
let tableName = "";
|
|
291
|
+
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
|
|
292
|
+
tableName += this.consume();
|
|
293
|
+
}
|
|
294
|
+
if (tableName.length === 0) {
|
|
295
|
+
throw new ParseError("Expected table name after ~", this.pos);
|
|
296
|
+
}
|
|
297
|
+
this.skipWhitespace();
|
|
298
|
+
if (!this.consumeStr("(")) {
|
|
299
|
+
throw new ParseError(
|
|
300
|
+
"Expected ( after reverse reference table name",
|
|
301
|
+
this.pos
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
this.skipWhitespace();
|
|
305
|
+
let foreignKey = "";
|
|
306
|
+
while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
|
|
307
|
+
foreignKey += this.consume();
|
|
308
|
+
}
|
|
309
|
+
if (foreignKey.length === 0) {
|
|
310
|
+
throw new ParseError("Expected foreign key name inside ()", this.pos);
|
|
311
|
+
}
|
|
312
|
+
this.skipWhitespace();
|
|
313
|
+
if (!this.consumeStr(")")) {
|
|
314
|
+
throw new ParseError("Expected ) after foreign key name", this.pos);
|
|
315
|
+
}
|
|
316
|
+
this.skipWhitespace();
|
|
317
|
+
const isOptional = this.consumeStr("?");
|
|
318
|
+
return {
|
|
319
|
+
type: "reverseReference",
|
|
320
|
+
tableName,
|
|
321
|
+
foreignKey,
|
|
322
|
+
isOptional
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
function parseSchema(schemaString) {
|
|
327
|
+
const parser = new Parser(schemaString.trim());
|
|
328
|
+
const schema = parser.parseSchema();
|
|
329
|
+
if (parser.getPosition() < parser.getInputLength()) {
|
|
330
|
+
throw new ParseError("Unexpected input after schema", parser.getPosition());
|
|
331
|
+
}
|
|
332
|
+
return schema;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// src/type-utils.ts
|
|
336
|
+
function schemaToTypeString(schema, resourceNames) {
|
|
337
|
+
switch (schema.type) {
|
|
338
|
+
case "string":
|
|
339
|
+
return "string";
|
|
340
|
+
case "number":
|
|
341
|
+
case "int":
|
|
342
|
+
case "float":
|
|
343
|
+
return "number";
|
|
344
|
+
case "boolean":
|
|
345
|
+
return "boolean";
|
|
346
|
+
case "stringLiteral":
|
|
347
|
+
return `"${schema.value}"`;
|
|
348
|
+
case "union":
|
|
349
|
+
return schema.members.map((m) => schemaToTypeString(m, resourceNames)).join(" | ");
|
|
350
|
+
case "reference": {
|
|
351
|
+
const typeName = resourceNames?.get(schema.tableName) || schema.tableName.charAt(0).toUpperCase() + schema.tableName.slice(1);
|
|
352
|
+
const baseType = schema.isArray ? `${typeName}[]` : typeName;
|
|
353
|
+
return schema.isOptional ? `${baseType} | null` : baseType;
|
|
354
|
+
}
|
|
355
|
+
case "reverseReference": {
|
|
356
|
+
const typeName = resourceNames?.get(schema.tableName) || schema.tableName.charAt(0).toUpperCase() + schema.tableName.slice(1);
|
|
357
|
+
const baseType = `${typeName}[]`;
|
|
358
|
+
return schema.isOptional ? `${baseType} | null` : baseType;
|
|
359
|
+
}
|
|
360
|
+
case "array":
|
|
361
|
+
if (schema.element.type === "tuple") {
|
|
362
|
+
const tupleElements2 = schema.element.elements.map((el) => {
|
|
363
|
+
const typeStr = schemaToTypeString(el.schema, resourceNames);
|
|
364
|
+
return el.name ? `${el.name}: ${typeStr}` : typeStr;
|
|
365
|
+
});
|
|
366
|
+
return `[${tupleElements2.join(", ")}][]`;
|
|
367
|
+
}
|
|
368
|
+
const elementType = schemaToTypeString(schema.element, resourceNames);
|
|
369
|
+
if (schema.element.type === "union") {
|
|
370
|
+
return `(${elementType})[]`;
|
|
371
|
+
}
|
|
372
|
+
return `${elementType}[]`;
|
|
373
|
+
case "tuple":
|
|
374
|
+
const tupleElements = schema.elements.map((el) => {
|
|
375
|
+
const typeStr = schemaToTypeString(el.schema, resourceNames);
|
|
376
|
+
return el.name ? `${el.name}: ${typeStr}` : typeStr;
|
|
377
|
+
});
|
|
378
|
+
return `[${tupleElements.join(", ")}]`;
|
|
379
|
+
default:
|
|
380
|
+
return "unknown";
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function createValidator(schema) {
|
|
384
|
+
return function validate(value) {
|
|
385
|
+
switch (schema.type) {
|
|
386
|
+
case "string":
|
|
387
|
+
return typeof value === "string";
|
|
388
|
+
case "number":
|
|
389
|
+
return typeof value === "number" && !isNaN(value);
|
|
390
|
+
case "int":
|
|
391
|
+
return typeof value === "number" && !isNaN(value) && Number.isInteger(value);
|
|
392
|
+
case "float":
|
|
393
|
+
return typeof value === "number" && !isNaN(value);
|
|
394
|
+
case "boolean":
|
|
395
|
+
return typeof value === "boolean";
|
|
396
|
+
case "stringLiteral":
|
|
397
|
+
return typeof value === "string" && value === schema.value;
|
|
398
|
+
case "union":
|
|
399
|
+
return schema.members.some((member) => createValidator(member)(value));
|
|
400
|
+
case "tuple":
|
|
401
|
+
if (!Array.isArray(value)) return false;
|
|
402
|
+
if (value.length !== schema.elements.length) return false;
|
|
403
|
+
return schema.elements.every(
|
|
404
|
+
(elementSchema, index) => createValidator(elementSchema.schema)(value[index])
|
|
405
|
+
);
|
|
406
|
+
case "array":
|
|
407
|
+
if (!Array.isArray(value)) return false;
|
|
408
|
+
return value.every((item) => createValidator(schema.element)(item));
|
|
409
|
+
case "reference":
|
|
410
|
+
if (schema.isOptional && value === null) return true;
|
|
411
|
+
if (schema.isArray) {
|
|
412
|
+
return Array.isArray(value) && value.every((id) => typeof id === "string");
|
|
413
|
+
}
|
|
414
|
+
return typeof value === "string" || Array.isArray(value) && value.every((id) => typeof id === "string");
|
|
415
|
+
case "reverseReference":
|
|
416
|
+
if (schema.isOptional && value === null) return true;
|
|
417
|
+
return Array.isArray(value);
|
|
418
|
+
default:
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/value-parser.ts
|
|
425
|
+
var ValueParser = class {
|
|
426
|
+
constructor(input, schemaString) {
|
|
427
|
+
this.pos = 0;
|
|
428
|
+
this.input = input;
|
|
429
|
+
this.schemaString = schemaString;
|
|
430
|
+
}
|
|
431
|
+
peek() {
|
|
432
|
+
return this.input[this.pos] || "";
|
|
433
|
+
}
|
|
434
|
+
consume() {
|
|
435
|
+
return this.input[this.pos++] || "";
|
|
436
|
+
}
|
|
437
|
+
skipWhitespace() {
|
|
438
|
+
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
|
|
439
|
+
this.pos++;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
consumeStr(str) {
|
|
443
|
+
if (this.input.slice(this.pos, this.pos + str.length) === str) {
|
|
444
|
+
this.pos += str.length;
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
parseValue(schema, allowOmitBrackets = false) {
|
|
450
|
+
this.skipWhitespace();
|
|
451
|
+
switch (schema.type) {
|
|
452
|
+
case "string":
|
|
453
|
+
return this.parseStringValue();
|
|
454
|
+
case "number":
|
|
455
|
+
return this.parseNumberValue();
|
|
456
|
+
case "int":
|
|
457
|
+
return this.parseIntValue();
|
|
458
|
+
case "float":
|
|
459
|
+
return this.parseFloatValue();
|
|
460
|
+
case "boolean":
|
|
461
|
+
return this.parseBooleanValue();
|
|
462
|
+
case "stringLiteral":
|
|
463
|
+
return this.parseStringLiteralValue(schema);
|
|
464
|
+
case "union":
|
|
465
|
+
return this.parseUnionValue(schema);
|
|
466
|
+
case "tuple":
|
|
467
|
+
return this.parseTupleValue(schema, allowOmitBrackets);
|
|
468
|
+
case "array":
|
|
469
|
+
return this.parseArrayValue(schema, allowOmitBrackets);
|
|
470
|
+
case "reference":
|
|
471
|
+
return this.parseReferenceValue(schema);
|
|
472
|
+
case "reverseReference":
|
|
473
|
+
return null;
|
|
474
|
+
default:
|
|
475
|
+
throw new ParseError(
|
|
476
|
+
`Unknown schema type: ${schema.type}`,
|
|
477
|
+
this.pos,
|
|
478
|
+
this.schemaString,
|
|
479
|
+
this.input
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
parseStringValue() {
|
|
484
|
+
let result = "";
|
|
485
|
+
while (this.pos < this.input.length) {
|
|
486
|
+
const char = this.peek();
|
|
487
|
+
if (char === "\\") {
|
|
488
|
+
this.consume();
|
|
489
|
+
const nextChar = this.consume();
|
|
490
|
+
if (nextChar === ";" || nextChar === "[" || nextChar === "]" || nextChar === "\\") {
|
|
491
|
+
result += nextChar;
|
|
492
|
+
} else {
|
|
493
|
+
result += "\\" + nextChar;
|
|
494
|
+
}
|
|
495
|
+
} else if (char === ";" || char === "]") {
|
|
496
|
+
break;
|
|
497
|
+
} else {
|
|
498
|
+
result += this.consume();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return result.trim();
|
|
502
|
+
}
|
|
503
|
+
parseNumberValue() {
|
|
504
|
+
let numStr = "";
|
|
505
|
+
while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
|
|
506
|
+
numStr += this.consume();
|
|
507
|
+
}
|
|
508
|
+
const num = parseFloat(numStr);
|
|
509
|
+
if (isNaN(num)) {
|
|
510
|
+
throw new ParseError(
|
|
511
|
+
"Invalid number",
|
|
512
|
+
this.pos - numStr.length,
|
|
513
|
+
this.schemaString,
|
|
514
|
+
this.input
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return num;
|
|
518
|
+
}
|
|
519
|
+
parseIntValue() {
|
|
520
|
+
let numStr = "";
|
|
521
|
+
while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
|
|
522
|
+
numStr += this.consume();
|
|
523
|
+
}
|
|
524
|
+
const num = parseFloat(numStr);
|
|
525
|
+
if (isNaN(num)) {
|
|
526
|
+
throw new ParseError(
|
|
527
|
+
"Invalid number",
|
|
528
|
+
this.pos - numStr.length,
|
|
529
|
+
this.schemaString,
|
|
530
|
+
this.input
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
if (!Number.isInteger(num)) {
|
|
534
|
+
throw new ParseError(
|
|
535
|
+
"Expected integer value",
|
|
536
|
+
this.pos - numStr.length,
|
|
537
|
+
this.schemaString,
|
|
538
|
+
this.input
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
return num;
|
|
542
|
+
}
|
|
543
|
+
parseFloatValue() {
|
|
544
|
+
return this.parseNumberValue();
|
|
545
|
+
}
|
|
546
|
+
parseBooleanValue() {
|
|
547
|
+
if (this.consumeStr("true")) {
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
if (this.consumeStr("false")) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
throw new ParseError(
|
|
554
|
+
"Expected true or false",
|
|
555
|
+
this.pos,
|
|
556
|
+
this.schemaString,
|
|
557
|
+
this.input
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
parseStringLiteralValue(schema) {
|
|
561
|
+
const quote = this.peek();
|
|
562
|
+
if (quote === '"' || quote === "'") {
|
|
563
|
+
this.consume();
|
|
564
|
+
let value = "";
|
|
565
|
+
while (this.pos < this.input.length) {
|
|
566
|
+
const char = this.peek();
|
|
567
|
+
if (char === "\\") {
|
|
568
|
+
this.consume();
|
|
569
|
+
const nextChar = this.consume();
|
|
570
|
+
if (nextChar === '"' || nextChar === "'" || nextChar === "\\" || nextChar === ";") {
|
|
571
|
+
value += nextChar;
|
|
572
|
+
} else {
|
|
573
|
+
value += "\\" + nextChar;
|
|
574
|
+
}
|
|
575
|
+
} else if (char === quote) {
|
|
576
|
+
this.consume();
|
|
577
|
+
if (value !== schema.value) {
|
|
578
|
+
throw new ParseError(
|
|
579
|
+
`Invalid value '"${value}"'. Expected '"${schema.value}"'`,
|
|
580
|
+
this.pos,
|
|
581
|
+
this.schemaString,
|
|
582
|
+
this.input
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
return value;
|
|
586
|
+
} else {
|
|
587
|
+
value += this.consume();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
throw new ParseError(
|
|
591
|
+
"Unterminated string literal",
|
|
592
|
+
this.pos,
|
|
593
|
+
this.schemaString,
|
|
594
|
+
this.input
|
|
595
|
+
);
|
|
596
|
+
} else {
|
|
597
|
+
let value = "";
|
|
598
|
+
while (this.pos < this.input.length) {
|
|
599
|
+
const char = this.peek();
|
|
600
|
+
if (char === ";" || char === "]" || char === ")") {
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
value += this.consume();
|
|
604
|
+
}
|
|
605
|
+
value = value.trim();
|
|
606
|
+
if (value !== schema.value) {
|
|
607
|
+
throw new ParseError(
|
|
608
|
+
`Invalid value '${value}'. Expected '${schema.value}'`,
|
|
609
|
+
this.pos - value.length,
|
|
610
|
+
this.schemaString,
|
|
611
|
+
this.input
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
return value;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
parseUnionValue(schema) {
|
|
618
|
+
const savedPos = this.pos;
|
|
619
|
+
const errors = [];
|
|
620
|
+
for (let i = 0; i < schema.members.length; i++) {
|
|
621
|
+
this.pos = savedPos;
|
|
622
|
+
try {
|
|
623
|
+
return this.parseValue(schema.members[i], false);
|
|
624
|
+
} catch (e) {
|
|
625
|
+
errors.push(e);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
throw new ParseError(
|
|
629
|
+
`Value does not match any union member. Tried ${schema.members.length} alternatives.`,
|
|
630
|
+
this.pos,
|
|
631
|
+
this.schemaString,
|
|
632
|
+
this.input
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
parseTupleValue(schema, allowOmitBrackets) {
|
|
636
|
+
let hasOpenBracket = false;
|
|
637
|
+
if (this.peek() === "[") {
|
|
638
|
+
this.consume();
|
|
639
|
+
hasOpenBracket = true;
|
|
640
|
+
} else if (!allowOmitBrackets) {
|
|
641
|
+
throw new ParseError(
|
|
642
|
+
"Expected [",
|
|
643
|
+
this.pos,
|
|
644
|
+
this.schemaString,
|
|
645
|
+
this.input
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
this.skipWhitespace();
|
|
649
|
+
if (this.peek() === "]" && hasOpenBracket) {
|
|
650
|
+
this.consume();
|
|
651
|
+
return [];
|
|
652
|
+
}
|
|
653
|
+
const result = [];
|
|
654
|
+
for (let i = 0; i < schema.elements.length; i++) {
|
|
655
|
+
this.skipWhitespace();
|
|
656
|
+
const elementSchema = schema.elements[i];
|
|
657
|
+
if (elementSchema.name) {
|
|
658
|
+
this.skipWhitespace();
|
|
659
|
+
const savedPos = this.pos;
|
|
660
|
+
if (this.consumeStr(`${elementSchema.name}:`)) {
|
|
661
|
+
this.skipWhitespace();
|
|
662
|
+
} else {
|
|
663
|
+
this.pos = savedPos;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
result.push(this.parseValue(elementSchema.schema, false));
|
|
667
|
+
this.skipWhitespace();
|
|
668
|
+
if (i < schema.elements.length - 1) {
|
|
669
|
+
if (!this.consumeStr(";")) {
|
|
670
|
+
throw new ParseError(
|
|
671
|
+
"Expected ;",
|
|
672
|
+
this.pos,
|
|
673
|
+
this.schemaString,
|
|
674
|
+
this.input
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
this.skipWhitespace();
|
|
680
|
+
if (hasOpenBracket) {
|
|
681
|
+
if (!this.consumeStr("]")) {
|
|
682
|
+
throw new ParseError(
|
|
683
|
+
"Expected ]",
|
|
684
|
+
this.pos,
|
|
685
|
+
this.schemaString,
|
|
686
|
+
this.input
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
692
|
+
parseArrayValue(schema, allowOmitBrackets) {
|
|
693
|
+
let hasOpenBracket = false;
|
|
694
|
+
const elementIsTupleOrArray = schema.element.type === "tuple" || schema.element.type === "array";
|
|
695
|
+
if (this.pos >= this.input.length || !this.input.trim()) {
|
|
696
|
+
return [];
|
|
697
|
+
}
|
|
698
|
+
if (this.peek() === "[") {
|
|
699
|
+
if (!elementIsTupleOrArray) {
|
|
700
|
+
this.consume();
|
|
701
|
+
hasOpenBracket = true;
|
|
702
|
+
} else {
|
|
703
|
+
const savedPos = this.pos;
|
|
704
|
+
this.consume();
|
|
705
|
+
this.skipWhitespace();
|
|
706
|
+
if (this.peek() === "]") {
|
|
707
|
+
this.consume();
|
|
708
|
+
return [];
|
|
709
|
+
} else if (this.peek() === "[") {
|
|
710
|
+
hasOpenBracket = true;
|
|
711
|
+
} else {
|
|
712
|
+
this.pos = savedPos;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
|
|
717
|
+
throw new ParseError(
|
|
718
|
+
"Expected [",
|
|
719
|
+
this.pos,
|
|
720
|
+
this.schemaString,
|
|
721
|
+
this.input
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
this.skipWhitespace();
|
|
725
|
+
if (this.peek() === "]" && hasOpenBracket) {
|
|
726
|
+
this.consume();
|
|
727
|
+
return [];
|
|
728
|
+
}
|
|
729
|
+
const result = [];
|
|
730
|
+
while (true) {
|
|
731
|
+
this.skipWhitespace();
|
|
732
|
+
result.push(this.parseValue(schema.element, elementIsTupleOrArray));
|
|
733
|
+
this.skipWhitespace();
|
|
734
|
+
if (!this.consumeStr(";")) {
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
this.skipWhitespace();
|
|
739
|
+
if (hasOpenBracket) {
|
|
740
|
+
if (!this.consumeStr("]")) {
|
|
741
|
+
throw new ParseError(
|
|
742
|
+
"Expected ]",
|
|
743
|
+
this.pos,
|
|
744
|
+
this.schemaString,
|
|
745
|
+
this.input
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
parseReferenceValue(schema) {
|
|
752
|
+
if (schema.isOptional) {
|
|
753
|
+
this.skipWhitespace();
|
|
754
|
+
if (this.pos >= this.input.length) {
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (schema.isArray) {
|
|
759
|
+
let hasOpenBracket = false;
|
|
760
|
+
if (this.peek() === "[") {
|
|
761
|
+
this.consume();
|
|
762
|
+
hasOpenBracket = true;
|
|
763
|
+
}
|
|
764
|
+
this.skipWhitespace();
|
|
765
|
+
if (this.peek() === "]" && hasOpenBracket) {
|
|
766
|
+
this.consume();
|
|
767
|
+
return [];
|
|
768
|
+
}
|
|
769
|
+
const ids = [];
|
|
770
|
+
while (true) {
|
|
771
|
+
this.skipWhitespace();
|
|
772
|
+
let id = "";
|
|
773
|
+
while (this.pos < this.input.length && this.peek() !== ";" && this.peek() !== "]") {
|
|
774
|
+
id += this.consume();
|
|
775
|
+
}
|
|
776
|
+
ids.push(id.trim());
|
|
777
|
+
this.skipWhitespace();
|
|
778
|
+
if (!this.consumeStr(";")) {
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (hasOpenBracket) {
|
|
783
|
+
if (!this.consumeStr("]")) {
|
|
784
|
+
throw new ParseError(
|
|
785
|
+
"Expected ]",
|
|
786
|
+
this.pos,
|
|
787
|
+
this.schemaString,
|
|
788
|
+
this.input
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return ids;
|
|
793
|
+
} else {
|
|
794
|
+
let id = "";
|
|
795
|
+
while (this.pos < this.input.length) {
|
|
796
|
+
const char = this.peek();
|
|
797
|
+
if (char === ";" || char === "]" || char === ",") {
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
id += this.consume();
|
|
801
|
+
}
|
|
802
|
+
return id.trim();
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
getPosition() {
|
|
806
|
+
return this.pos;
|
|
807
|
+
}
|
|
808
|
+
getInputLength() {
|
|
809
|
+
return this.input.length;
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
function parseValue(schema, valueString, schemaString) {
|
|
813
|
+
const sStr = schemaString || schemaToTypeString(schema);
|
|
814
|
+
const parser = new ValueParser(valueString.trim(), sStr);
|
|
815
|
+
const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
|
|
816
|
+
const value = parser.parseValue(schema, allowOmitBrackets);
|
|
817
|
+
if (parser.getPosition() < parser.getInputLength()) {
|
|
818
|
+
throw new ParseError(
|
|
819
|
+
"Unexpected input after value",
|
|
820
|
+
parser.getPosition(),
|
|
821
|
+
sStr,
|
|
822
|
+
valueString.trim()
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
return value;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// src/index.ts
|
|
829
|
+
function defineSchema(schemaString) {
|
|
830
|
+
const schema = parseSchema(schemaString);
|
|
831
|
+
const validator = createValidator(schema);
|
|
832
|
+
return {
|
|
833
|
+
schema,
|
|
834
|
+
validator,
|
|
835
|
+
parse: (valueString) => parseValue(schema, valueString)
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
export {
|
|
839
|
+
ParseError,
|
|
840
|
+
createValidator,
|
|
841
|
+
defineSchema,
|
|
842
|
+
parseSchema,
|
|
843
|
+
parseValue,
|
|
844
|
+
schemaToTypeString
|
|
845
|
+
};
|