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.
@@ -0,0 +1,1701 @@
1
+ // src/csv-loader/loader.ts
2
+ import { parse } from "csv-parse/sync";
3
+
4
+ // src/parser.ts
5
+ var ParseError = class extends Error {
6
+ constructor(message, position, schema, value) {
7
+ let fullMessage = message;
8
+ if (position !== void 0) {
9
+ fullMessage += ` at position ${position}`;
10
+ }
11
+ if (schema !== void 0) {
12
+ fullMessage += `. Schema: ${schema}`;
13
+ }
14
+ if (value !== void 0) {
15
+ fullMessage += `. Value: ${value}`;
16
+ }
17
+ super(fullMessage);
18
+ this.position = position;
19
+ this.schema = schema;
20
+ this.value = value;
21
+ this.name = "ParseError";
22
+ }
23
+ };
24
+ var Parser = class {
25
+ constructor(input) {
26
+ this.pos = 0;
27
+ this.input = input;
28
+ }
29
+ peek() {
30
+ return this.input[this.pos] || "";
31
+ }
32
+ consume() {
33
+ return this.input[this.pos++] || "";
34
+ }
35
+ skipWhitespace() {
36
+ while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
37
+ this.pos++;
38
+ }
39
+ }
40
+ match(str) {
41
+ return this.input.slice(this.pos, this.pos + str.length) === str;
42
+ }
43
+ consumeStr(str) {
44
+ if (this.match(str)) {
45
+ this.pos += str.length;
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ getPosition() {
51
+ return this.pos;
52
+ }
53
+ getInputLength() {
54
+ return this.input.length;
55
+ }
56
+ parseSchema() {
57
+ this.skipWhitespace();
58
+ let schema = this.parseSchemaInternal();
59
+ this.skipWhitespace();
60
+ if (this.consumeStr("[")) {
61
+ this.skipWhitespace();
62
+ if (!this.consumeStr("]")) {
63
+ throw new ParseError("Expected ]", this.pos);
64
+ }
65
+ schema = { type: "array", element: schema };
66
+ this.skipWhitespace();
67
+ }
68
+ if (this.consumeStr("|")) {
69
+ const members = [schema];
70
+ while (true) {
71
+ this.skipWhitespace();
72
+ const member = this.parseSchemaInternal();
73
+ members.push(member);
74
+ this.skipWhitespace();
75
+ if (!this.consumeStr("|")) {
76
+ break;
77
+ }
78
+ }
79
+ return {
80
+ type: "union",
81
+ members
82
+ };
83
+ }
84
+ return schema;
85
+ }
86
+ parseSchemaInternal() {
87
+ this.skipWhitespace();
88
+ if (this.consumeStr("(")) {
89
+ this.skipWhitespace();
90
+ const schema = this.parseSchema();
91
+ this.skipWhitespace();
92
+ if (!this.consumeStr(")")) {
93
+ throw new ParseError("Expected )", this.pos);
94
+ }
95
+ this.skipWhitespace();
96
+ if (this.consumeStr("[")) {
97
+ this.skipWhitespace();
98
+ if (!this.consumeStr("]")) {
99
+ throw new ParseError("Expected ]", this.pos);
100
+ }
101
+ return { type: "array", element: schema };
102
+ }
103
+ return schema;
104
+ }
105
+ if (this.peek() === '"' || this.peek() === "'") {
106
+ return this.parseStringLiteralSchema();
107
+ }
108
+ if (this.consumeStr("~")) {
109
+ return this.parseReverseReferenceSchema();
110
+ }
111
+ if (this.consumeStr("@")) {
112
+ return this.parseReferenceSchema();
113
+ }
114
+ if (this.consumeStr("string")) {
115
+ if (this.consumeStr("[")) {
116
+ this.skipWhitespace();
117
+ if (!this.consumeStr("]")) {
118
+ throw new ParseError("Expected ]", this.pos);
119
+ }
120
+ return { type: "array", element: { type: "string" } };
121
+ }
122
+ return { type: "string" };
123
+ }
124
+ if (this.consumeStr("number")) {
125
+ if (this.consumeStr("[")) {
126
+ this.skipWhitespace();
127
+ if (!this.consumeStr("]")) {
128
+ throw new ParseError("Expected ]", this.pos);
129
+ }
130
+ return { type: "array", element: { type: "number" } };
131
+ }
132
+ return { type: "number" };
133
+ }
134
+ if (this.consumeStr("int")) {
135
+ if (this.consumeStr("[")) {
136
+ this.skipWhitespace();
137
+ if (!this.consumeStr("]")) {
138
+ throw new ParseError("Expected ]", this.pos);
139
+ }
140
+ return { type: "array", element: { type: "int" } };
141
+ }
142
+ return { type: "int" };
143
+ }
144
+ if (this.consumeStr("float")) {
145
+ if (this.consumeStr("[")) {
146
+ this.skipWhitespace();
147
+ if (!this.consumeStr("]")) {
148
+ throw new ParseError("Expected ]", this.pos);
149
+ }
150
+ return { type: "array", element: { type: "float" } };
151
+ }
152
+ return { type: "float" };
153
+ }
154
+ if (this.consumeStr("boolean")) {
155
+ if (this.consumeStr("[")) {
156
+ this.skipWhitespace();
157
+ if (!this.consumeStr("]")) {
158
+ throw new ParseError("Expected ]", this.pos);
159
+ }
160
+ return { type: "array", element: { type: "boolean" } };
161
+ }
162
+ return { type: "boolean" };
163
+ }
164
+ if (this.consumeStr("[")) {
165
+ const elements = [];
166
+ this.skipWhitespace();
167
+ if (this.peek() === "]") {
168
+ this.consume();
169
+ throw new ParseError("Empty array/tuple not allowed", this.pos);
170
+ }
171
+ elements.push(this.parseNamedSchema());
172
+ this.skipWhitespace();
173
+ if (this.consumeStr(";")) {
174
+ const remainingElements = [];
175
+ while (true) {
176
+ this.skipWhitespace();
177
+ remainingElements.push(this.parseNamedSchema());
178
+ this.skipWhitespace();
179
+ if (!this.consumeStr(";")) {
180
+ break;
181
+ }
182
+ }
183
+ elements.push(...remainingElements);
184
+ }
185
+ this.skipWhitespace();
186
+ if (!this.consumeStr("]")) {
187
+ throw new ParseError("Expected ]", this.pos);
188
+ }
189
+ if (this.consumeStr("[")) {
190
+ this.skipWhitespace();
191
+ if (!this.consumeStr("]")) {
192
+ throw new ParseError("Expected ]", this.pos);
193
+ }
194
+ if (elements.length === 1 && !elements[0].name) {
195
+ return { type: "array", element: elements[0].schema };
196
+ }
197
+ return { type: "array", element: { type: "tuple", elements } };
198
+ }
199
+ if (elements.length === 1 && !elements[0].name) {
200
+ return { type: "array", element: elements[0].schema };
201
+ }
202
+ return { type: "tuple", elements };
203
+ }
204
+ throw new ParseError(
205
+ `Unknown type: ${this.peek() || "end of input"}`,
206
+ this.pos
207
+ );
208
+ }
209
+ parseStringLiteralSchema() {
210
+ const value = this.parseStringLiteral();
211
+ return {
212
+ type: "stringLiteral",
213
+ value
214
+ };
215
+ }
216
+ parseStringLiteral() {
217
+ const quote = this.peek();
218
+ if (quote !== '"' && quote !== "'") {
219
+ throw new ParseError("Expected string literal with quotes", this.pos);
220
+ }
221
+ this.consume();
222
+ let value = "";
223
+ while (this.pos < this.input.length) {
224
+ const char = this.peek();
225
+ if (char === "\\") {
226
+ this.consume();
227
+ const nextChar = this.consume();
228
+ if (nextChar === '"' || nextChar === "'" || nextChar === "\\" || nextChar === "|" || nextChar === ";" || nextChar === "(" || nextChar === ")") {
229
+ value += nextChar;
230
+ } else {
231
+ value += "\\" + nextChar;
232
+ }
233
+ } else if (char === quote) {
234
+ this.consume();
235
+ return value;
236
+ } else {
237
+ value += this.consume();
238
+ }
239
+ }
240
+ throw new ParseError("Unterminated string literal", this.pos);
241
+ }
242
+ parseNamedSchema() {
243
+ this.skipWhitespace();
244
+ const startpos = this.pos;
245
+ let identifier = "";
246
+ while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
247
+ identifier += this.consume();
248
+ }
249
+ if (identifier.length === 0) {
250
+ const schema = this.parseSchema();
251
+ return { schema };
252
+ }
253
+ this.skipWhitespace();
254
+ if (this.consumeStr(":")) {
255
+ this.skipWhitespace();
256
+ const name = identifier;
257
+ const schema = this.parseSchema();
258
+ return { name, schema };
259
+ } else {
260
+ this.pos = startpos;
261
+ const schema = this.parseSchema();
262
+ return { schema };
263
+ }
264
+ }
265
+ parseReferenceSchema() {
266
+ let tableName = "";
267
+ while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
268
+ tableName += this.consume();
269
+ }
270
+ if (tableName.length === 0) {
271
+ throw new ParseError("Expected table name after @", this.pos);
272
+ }
273
+ this.skipWhitespace();
274
+ if (this.consumeStr("[]")) {
275
+ this.skipWhitespace();
276
+ const isOptional2 = this.consumeStr("?");
277
+ return {
278
+ type: "reference",
279
+ tableName,
280
+ isArray: true,
281
+ isOptional: isOptional2
282
+ };
283
+ }
284
+ const isOptional = this.consumeStr("?");
285
+ return {
286
+ type: "reference",
287
+ tableName,
288
+ isArray: false,
289
+ isOptional
290
+ };
291
+ }
292
+ parseReverseReferenceSchema() {
293
+ let tableName = "";
294
+ while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
295
+ tableName += this.consume();
296
+ }
297
+ if (tableName.length === 0) {
298
+ throw new ParseError("Expected table name after ~", this.pos);
299
+ }
300
+ this.skipWhitespace();
301
+ if (!this.consumeStr("(")) {
302
+ throw new ParseError(
303
+ "Expected ( after reverse reference table name",
304
+ this.pos
305
+ );
306
+ }
307
+ this.skipWhitespace();
308
+ let foreignKey = "";
309
+ while (this.pos < this.input.length && /[a-zA-Z0-9\-_]/.test(this.peek())) {
310
+ foreignKey += this.consume();
311
+ }
312
+ if (foreignKey.length === 0) {
313
+ throw new ParseError("Expected foreign key name inside ()", this.pos);
314
+ }
315
+ this.skipWhitespace();
316
+ if (!this.consumeStr(")")) {
317
+ throw new ParseError("Expected ) after foreign key name", this.pos);
318
+ }
319
+ this.skipWhitespace();
320
+ const isOptional = this.consumeStr("?");
321
+ return {
322
+ type: "reverseReference",
323
+ tableName,
324
+ foreignKey,
325
+ isOptional
326
+ };
327
+ }
328
+ };
329
+ function parseSchema(schemaString) {
330
+ const parser = new Parser(schemaString.trim());
331
+ const schema = parser.parseSchema();
332
+ if (parser.getPosition() < parser.getInputLength()) {
333
+ throw new ParseError("Unexpected input after schema", parser.getPosition());
334
+ }
335
+ return schema;
336
+ }
337
+
338
+ // src/type-utils.ts
339
+ function schemaToTypeString(schema, resourceNames) {
340
+ switch (schema.type) {
341
+ case "string":
342
+ return "string";
343
+ case "number":
344
+ case "int":
345
+ case "float":
346
+ return "number";
347
+ case "boolean":
348
+ return "boolean";
349
+ case "stringLiteral":
350
+ return `"${schema.value}"`;
351
+ case "union":
352
+ return schema.members.map((m) => schemaToTypeString(m, resourceNames)).join(" | ");
353
+ case "reference": {
354
+ const typeName = resourceNames?.get(schema.tableName) || schema.tableName.charAt(0).toUpperCase() + schema.tableName.slice(1);
355
+ const baseType = schema.isArray ? `${typeName}[]` : typeName;
356
+ return schema.isOptional ? `${baseType} | null` : baseType;
357
+ }
358
+ case "reverseReference": {
359
+ const typeName = resourceNames?.get(schema.tableName) || schema.tableName.charAt(0).toUpperCase() + schema.tableName.slice(1);
360
+ const baseType = `${typeName}[]`;
361
+ return schema.isOptional ? `${baseType} | null` : baseType;
362
+ }
363
+ case "array":
364
+ if (schema.element.type === "tuple") {
365
+ const tupleElements2 = schema.element.elements.map((el) => {
366
+ const typeStr = schemaToTypeString(el.schema, resourceNames);
367
+ return el.name ? `${el.name}: ${typeStr}` : typeStr;
368
+ });
369
+ return `[${tupleElements2.join(", ")}][]`;
370
+ }
371
+ const elementType = schemaToTypeString(schema.element, resourceNames);
372
+ if (schema.element.type === "union") {
373
+ return `(${elementType})[]`;
374
+ }
375
+ return `${elementType}[]`;
376
+ case "tuple":
377
+ const tupleElements = schema.elements.map((el) => {
378
+ const typeStr = schemaToTypeString(el.schema, resourceNames);
379
+ return el.name ? `${el.name}: ${typeStr}` : typeStr;
380
+ });
381
+ return `[${tupleElements.join(", ")}]`;
382
+ default:
383
+ return "unknown";
384
+ }
385
+ }
386
+ function createValidator(schema) {
387
+ return function validate(value) {
388
+ switch (schema.type) {
389
+ case "string":
390
+ return typeof value === "string";
391
+ case "number":
392
+ return typeof value === "number" && !isNaN(value);
393
+ case "int":
394
+ return typeof value === "number" && !isNaN(value) && Number.isInteger(value);
395
+ case "float":
396
+ return typeof value === "number" && !isNaN(value);
397
+ case "boolean":
398
+ return typeof value === "boolean";
399
+ case "stringLiteral":
400
+ return typeof value === "string" && value === schema.value;
401
+ case "union":
402
+ return schema.members.some((member) => createValidator(member)(value));
403
+ case "tuple":
404
+ if (!Array.isArray(value)) return false;
405
+ if (value.length !== schema.elements.length) return false;
406
+ return schema.elements.every(
407
+ (elementSchema, index) => createValidator(elementSchema.schema)(value[index])
408
+ );
409
+ case "array":
410
+ if (!Array.isArray(value)) return false;
411
+ return value.every((item) => createValidator(schema.element)(item));
412
+ case "reference":
413
+ if (schema.isOptional && value === null) return true;
414
+ if (schema.isArray) {
415
+ return Array.isArray(value) && value.every((id) => typeof id === "string");
416
+ }
417
+ return typeof value === "string" || Array.isArray(value) && value.every((id) => typeof id === "string");
418
+ case "reverseReference":
419
+ if (schema.isOptional && value === null) return true;
420
+ return Array.isArray(value);
421
+ default:
422
+ return false;
423
+ }
424
+ };
425
+ }
426
+
427
+ // src/value-parser.ts
428
+ var ValueParser = class {
429
+ constructor(input, schemaString) {
430
+ this.pos = 0;
431
+ this.input = input;
432
+ this.schemaString = schemaString;
433
+ }
434
+ peek() {
435
+ return this.input[this.pos] || "";
436
+ }
437
+ consume() {
438
+ return this.input[this.pos++] || "";
439
+ }
440
+ skipWhitespace() {
441
+ while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
442
+ this.pos++;
443
+ }
444
+ }
445
+ consumeStr(str) {
446
+ if (this.input.slice(this.pos, this.pos + str.length) === str) {
447
+ this.pos += str.length;
448
+ return true;
449
+ }
450
+ return false;
451
+ }
452
+ parseValue(schema, allowOmitBrackets = false) {
453
+ this.skipWhitespace();
454
+ switch (schema.type) {
455
+ case "string":
456
+ return this.parseStringValue();
457
+ case "number":
458
+ return this.parseNumberValue();
459
+ case "int":
460
+ return this.parseIntValue();
461
+ case "float":
462
+ return this.parseFloatValue();
463
+ case "boolean":
464
+ return this.parseBooleanValue();
465
+ case "stringLiteral":
466
+ return this.parseStringLiteralValue(schema);
467
+ case "union":
468
+ return this.parseUnionValue(schema);
469
+ case "tuple":
470
+ return this.parseTupleValue(schema, allowOmitBrackets);
471
+ case "array":
472
+ return this.parseArrayValue(schema, allowOmitBrackets);
473
+ case "reference":
474
+ return this.parseReferenceValue(schema);
475
+ case "reverseReference":
476
+ return null;
477
+ default:
478
+ throw new ParseError(
479
+ `Unknown schema type: ${schema.type}`,
480
+ this.pos,
481
+ this.schemaString,
482
+ this.input
483
+ );
484
+ }
485
+ }
486
+ parseStringValue() {
487
+ let result = "";
488
+ while (this.pos < this.input.length) {
489
+ const char = this.peek();
490
+ if (char === "\\") {
491
+ this.consume();
492
+ const nextChar = this.consume();
493
+ if (nextChar === ";" || nextChar === "[" || nextChar === "]" || nextChar === "\\") {
494
+ result += nextChar;
495
+ } else {
496
+ result += "\\" + nextChar;
497
+ }
498
+ } else if (char === ";" || char === "]") {
499
+ break;
500
+ } else {
501
+ result += this.consume();
502
+ }
503
+ }
504
+ return result.trim();
505
+ }
506
+ parseNumberValue() {
507
+ let numStr = "";
508
+ while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
509
+ numStr += this.consume();
510
+ }
511
+ const num = parseFloat(numStr);
512
+ if (isNaN(num)) {
513
+ throw new ParseError(
514
+ "Invalid number",
515
+ this.pos - numStr.length,
516
+ this.schemaString,
517
+ this.input
518
+ );
519
+ }
520
+ return num;
521
+ }
522
+ parseIntValue() {
523
+ let numStr = "";
524
+ while (this.pos < this.input.length && /[\d.\-+eE]/.test(this.peek())) {
525
+ numStr += this.consume();
526
+ }
527
+ const num = parseFloat(numStr);
528
+ if (isNaN(num)) {
529
+ throw new ParseError(
530
+ "Invalid number",
531
+ this.pos - numStr.length,
532
+ this.schemaString,
533
+ this.input
534
+ );
535
+ }
536
+ if (!Number.isInteger(num)) {
537
+ throw new ParseError(
538
+ "Expected integer value",
539
+ this.pos - numStr.length,
540
+ this.schemaString,
541
+ this.input
542
+ );
543
+ }
544
+ return num;
545
+ }
546
+ parseFloatValue() {
547
+ return this.parseNumberValue();
548
+ }
549
+ parseBooleanValue() {
550
+ if (this.consumeStr("true")) {
551
+ return true;
552
+ }
553
+ if (this.consumeStr("false")) {
554
+ return false;
555
+ }
556
+ throw new ParseError(
557
+ "Expected true or false",
558
+ this.pos,
559
+ this.schemaString,
560
+ this.input
561
+ );
562
+ }
563
+ parseStringLiteralValue(schema) {
564
+ const quote = this.peek();
565
+ if (quote === '"' || quote === "'") {
566
+ this.consume();
567
+ let value = "";
568
+ while (this.pos < this.input.length) {
569
+ const char = this.peek();
570
+ if (char === "\\") {
571
+ this.consume();
572
+ const nextChar = this.consume();
573
+ if (nextChar === '"' || nextChar === "'" || nextChar === "\\" || nextChar === ";") {
574
+ value += nextChar;
575
+ } else {
576
+ value += "\\" + nextChar;
577
+ }
578
+ } else if (char === quote) {
579
+ this.consume();
580
+ if (value !== schema.value) {
581
+ throw new ParseError(
582
+ `Invalid value '"${value}"'. Expected '"${schema.value}"'`,
583
+ this.pos,
584
+ this.schemaString,
585
+ this.input
586
+ );
587
+ }
588
+ return value;
589
+ } else {
590
+ value += this.consume();
591
+ }
592
+ }
593
+ throw new ParseError(
594
+ "Unterminated string literal",
595
+ this.pos,
596
+ this.schemaString,
597
+ this.input
598
+ );
599
+ } else {
600
+ let value = "";
601
+ while (this.pos < this.input.length) {
602
+ const char = this.peek();
603
+ if (char === ";" || char === "]" || char === ")") {
604
+ break;
605
+ }
606
+ value += this.consume();
607
+ }
608
+ value = value.trim();
609
+ if (value !== schema.value) {
610
+ throw new ParseError(
611
+ `Invalid value '${value}'. Expected '${schema.value}'`,
612
+ this.pos - value.length,
613
+ this.schemaString,
614
+ this.input
615
+ );
616
+ }
617
+ return value;
618
+ }
619
+ }
620
+ parseUnionValue(schema) {
621
+ const savedPos = this.pos;
622
+ const errors = [];
623
+ for (let i = 0; i < schema.members.length; i++) {
624
+ this.pos = savedPos;
625
+ try {
626
+ return this.parseValue(schema.members[i], false);
627
+ } catch (e) {
628
+ errors.push(e);
629
+ }
630
+ }
631
+ throw new ParseError(
632
+ `Value does not match any union member. Tried ${schema.members.length} alternatives.`,
633
+ this.pos,
634
+ this.schemaString,
635
+ this.input
636
+ );
637
+ }
638
+ parseTupleValue(schema, allowOmitBrackets) {
639
+ let hasOpenBracket = false;
640
+ if (this.peek() === "[") {
641
+ this.consume();
642
+ hasOpenBracket = true;
643
+ } else if (!allowOmitBrackets) {
644
+ throw new ParseError(
645
+ "Expected [",
646
+ this.pos,
647
+ this.schemaString,
648
+ this.input
649
+ );
650
+ }
651
+ this.skipWhitespace();
652
+ if (this.peek() === "]" && hasOpenBracket) {
653
+ this.consume();
654
+ return [];
655
+ }
656
+ const result = [];
657
+ for (let i = 0; i < schema.elements.length; i++) {
658
+ this.skipWhitespace();
659
+ const elementSchema = schema.elements[i];
660
+ if (elementSchema.name) {
661
+ this.skipWhitespace();
662
+ const savedPos = this.pos;
663
+ if (this.consumeStr(`${elementSchema.name}:`)) {
664
+ this.skipWhitespace();
665
+ } else {
666
+ this.pos = savedPos;
667
+ }
668
+ }
669
+ result.push(this.parseValue(elementSchema.schema, false));
670
+ this.skipWhitespace();
671
+ if (i < schema.elements.length - 1) {
672
+ if (!this.consumeStr(";")) {
673
+ throw new ParseError(
674
+ "Expected ;",
675
+ this.pos,
676
+ this.schemaString,
677
+ this.input
678
+ );
679
+ }
680
+ }
681
+ }
682
+ this.skipWhitespace();
683
+ if (hasOpenBracket) {
684
+ if (!this.consumeStr("]")) {
685
+ throw new ParseError(
686
+ "Expected ]",
687
+ this.pos,
688
+ this.schemaString,
689
+ this.input
690
+ );
691
+ }
692
+ }
693
+ return result;
694
+ }
695
+ parseArrayValue(schema, allowOmitBrackets) {
696
+ let hasOpenBracket = false;
697
+ const elementIsTupleOrArray = schema.element.type === "tuple" || schema.element.type === "array";
698
+ if (this.pos >= this.input.length || !this.input.trim()) {
699
+ return [];
700
+ }
701
+ if (this.peek() === "[") {
702
+ if (!elementIsTupleOrArray) {
703
+ this.consume();
704
+ hasOpenBracket = true;
705
+ } else {
706
+ const savedPos = this.pos;
707
+ this.consume();
708
+ this.skipWhitespace();
709
+ if (this.peek() === "]") {
710
+ this.consume();
711
+ return [];
712
+ } else if (this.peek() === "[") {
713
+ hasOpenBracket = true;
714
+ } else {
715
+ this.pos = savedPos;
716
+ }
717
+ }
718
+ }
719
+ if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
720
+ throw new ParseError(
721
+ "Expected [",
722
+ this.pos,
723
+ this.schemaString,
724
+ this.input
725
+ );
726
+ }
727
+ this.skipWhitespace();
728
+ if (this.peek() === "]" && hasOpenBracket) {
729
+ this.consume();
730
+ return [];
731
+ }
732
+ const result = [];
733
+ while (true) {
734
+ this.skipWhitespace();
735
+ result.push(this.parseValue(schema.element, elementIsTupleOrArray));
736
+ this.skipWhitespace();
737
+ if (!this.consumeStr(";")) {
738
+ break;
739
+ }
740
+ }
741
+ this.skipWhitespace();
742
+ if (hasOpenBracket) {
743
+ if (!this.consumeStr("]")) {
744
+ throw new ParseError(
745
+ "Expected ]",
746
+ this.pos,
747
+ this.schemaString,
748
+ this.input
749
+ );
750
+ }
751
+ }
752
+ return result;
753
+ }
754
+ parseReferenceValue(schema) {
755
+ if (schema.isOptional) {
756
+ this.skipWhitespace();
757
+ if (this.pos >= this.input.length) {
758
+ return null;
759
+ }
760
+ }
761
+ if (schema.isArray) {
762
+ let hasOpenBracket = false;
763
+ if (this.peek() === "[") {
764
+ this.consume();
765
+ hasOpenBracket = true;
766
+ }
767
+ this.skipWhitespace();
768
+ if (this.peek() === "]" && hasOpenBracket) {
769
+ this.consume();
770
+ return [];
771
+ }
772
+ const ids = [];
773
+ while (true) {
774
+ this.skipWhitespace();
775
+ let id = "";
776
+ while (this.pos < this.input.length && this.peek() !== ";" && this.peek() !== "]") {
777
+ id += this.consume();
778
+ }
779
+ ids.push(id.trim());
780
+ this.skipWhitespace();
781
+ if (!this.consumeStr(";")) {
782
+ break;
783
+ }
784
+ }
785
+ if (hasOpenBracket) {
786
+ if (!this.consumeStr("]")) {
787
+ throw new ParseError(
788
+ "Expected ]",
789
+ this.pos,
790
+ this.schemaString,
791
+ this.input
792
+ );
793
+ }
794
+ }
795
+ return ids;
796
+ } else {
797
+ let id = "";
798
+ while (this.pos < this.input.length) {
799
+ const char = this.peek();
800
+ if (char === ";" || char === "]" || char === ",") {
801
+ break;
802
+ }
803
+ id += this.consume();
804
+ }
805
+ return id.trim();
806
+ }
807
+ }
808
+ getPosition() {
809
+ return this.pos;
810
+ }
811
+ getInputLength() {
812
+ return this.input.length;
813
+ }
814
+ };
815
+ function parseValue(schema, valueString, schemaString) {
816
+ const sStr = schemaString || schemaToTypeString(schema);
817
+ const parser = new ValueParser(valueString.trim(), sStr);
818
+ const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
819
+ const value = parser.parseValue(schema, allowOmitBrackets);
820
+ if (parser.getPosition() < parser.getInputLength()) {
821
+ throw new ParseError(
822
+ "Unexpected input after value",
823
+ parser.getPosition(),
824
+ sStr,
825
+ valueString.trim()
826
+ );
827
+ }
828
+ return value;
829
+ }
830
+
831
+ // src/csv-loader/reference-resolver.ts
832
+ import * as fs from "fs";
833
+ import * as path from "path";
834
+ var referenceTableCache = /* @__PURE__ */ new Map();
835
+ var loadingFiles = /* @__PURE__ */ new Set();
836
+ function hasNestedReferences(schema) {
837
+ switch (schema.type) {
838
+ case "reference":
839
+ case "reverseReference":
840
+ return true;
841
+ case "tuple":
842
+ return schema.elements.some((el) => hasNestedReferences(el.schema));
843
+ case "array":
844
+ return hasNestedReferences(schema.element);
845
+ case "union":
846
+ return schema.members.some((m) => hasNestedReferences(m));
847
+ default:
848
+ return false;
849
+ }
850
+ }
851
+ function loadReferenceTable(schema, refBaseDir, defaultPrimaryKey, currentFilePath) {
852
+ const baseDir = refBaseDir || (currentFilePath ? path.dirname(currentFilePath) : process.cwd());
853
+ const fileName = `${schema.tableName}.csv`;
854
+ const refFilePath = path.isAbsolute(fileName) ? fileName : path.join(baseDir, fileName);
855
+ let refTable;
856
+ if (referenceTableCache.has(refFilePath)) {
857
+ refTable = referenceTableCache.get(refFilePath);
858
+ } else {
859
+ if (loadingFiles.has(refFilePath)) {
860
+ throw new Error(
861
+ `Circular reference detected: table "${schema.tableName}" (${refFilePath}) is already being loaded`
862
+ );
863
+ }
864
+ loadingFiles.add(refFilePath);
865
+ try {
866
+ const refContent = fs.readFileSync(refFilePath, "utf-8");
867
+ const refResult = parseCsv(refContent, {
868
+ currentFilePath: refFilePath,
869
+ emitTypes: false
870
+ });
871
+ refTable = refResult.data;
872
+ referenceTableCache.set(refFilePath, refTable);
873
+ } catch (error) {
874
+ throw new Error(
875
+ `Failed to load referenced table "${schema.tableName}" from ${refFilePath}: ${error instanceof Error ? error.message : String(error)}`
876
+ );
877
+ } finally {
878
+ loadingFiles.delete(refFilePath);
879
+ }
880
+ }
881
+ const lookup = /* @__PURE__ */ new Map();
882
+ refTable.forEach((row) => {
883
+ const pkValue = row[defaultPrimaryKey];
884
+ if (pkValue !== void 0) {
885
+ lookup.set(String(pkValue), row);
886
+ }
887
+ });
888
+ return { lookup, refTable };
889
+ }
890
+ function resolveReferenceId(id, lookup, tableName) {
891
+ const obj = lookup.get(id);
892
+ if (!obj) {
893
+ throw new Error(`Reference to "${tableName}" with id="${id}" not found`);
894
+ }
895
+ return obj;
896
+ }
897
+ function parseReferenceIds(schema, valueString) {
898
+ const trimmed = valueString.trim();
899
+ if (schema.isOptional && trimmed === "") {
900
+ return null;
901
+ }
902
+ return parseValue(schema, trimmed);
903
+ }
904
+ function parseValueWithReferenceIds(valueString, schema) {
905
+ if (!hasNestedReferences(schema)) {
906
+ return parseValue(schema, valueString);
907
+ }
908
+ switch (schema.type) {
909
+ case "reference":
910
+ return parseReferenceIds(schema, valueString);
911
+ case "reverseReference":
912
+ return null;
913
+ case "tuple": {
914
+ const parsed = parseValue(schema, valueString);
915
+ return schema.elements.map(
916
+ (el, i) => hasNestedReferences(el.schema) ? extractNestedReferenceIds(parsed[i], el.schema) : parsed[i]
917
+ );
918
+ }
919
+ case "array": {
920
+ const parsed = parseValue(schema, valueString);
921
+ return parsed.map(
922
+ (item) => hasNestedReferences(schema.element) ? extractNestedReferenceIds(item, schema.element) : item
923
+ );
924
+ }
925
+ case "union": {
926
+ for (const member of schema.members) {
927
+ if (hasNestedReferences(member)) {
928
+ try {
929
+ const parsed = parseValue(member, valueString);
930
+ return extractNestedReferenceIds(parsed, member);
931
+ } catch {
932
+ }
933
+ }
934
+ }
935
+ return parseValue(schema, valueString);
936
+ }
937
+ default:
938
+ return parseValue(schema, valueString);
939
+ }
940
+ }
941
+ function extractNestedReferenceIds(value, schema) {
942
+ switch (schema.type) {
943
+ case "reference":
944
+ if (value === null || value === void 0) return value;
945
+ if (schema.isArray) {
946
+ const ids = Array.isArray(value) ? value : [value];
947
+ return ids.map((id) => String(id));
948
+ }
949
+ return String(value);
950
+ case "reverseReference":
951
+ return null;
952
+ case "tuple": {
953
+ if (!Array.isArray(value)) return value;
954
+ return schema.elements.map(
955
+ (el, i) => hasNestedReferences(el.schema) ? extractNestedReferenceIds(value[i], el.schema) : value[i]
956
+ );
957
+ }
958
+ case "array": {
959
+ if (!Array.isArray(value)) return value;
960
+ return value.map(
961
+ (item) => hasNestedReferences(schema.element) ? extractNestedReferenceIds(item, schema.element) : item
962
+ );
963
+ }
964
+ case "union": {
965
+ for (const member of schema.members) {
966
+ if (hasNestedReferences(member)) {
967
+ try {
968
+ return extractNestedReferenceIds(value, member);
969
+ } catch {
970
+ }
971
+ }
972
+ }
973
+ return value;
974
+ }
975
+ default:
976
+ return value;
977
+ }
978
+ }
979
+ function collectReferenceFields(schema, name) {
980
+ const fields = [];
981
+ switch (schema.type) {
982
+ case "reference":
983
+ fields.push({
984
+ name,
985
+ tableName: schema.tableName,
986
+ isArray: schema.isArray,
987
+ schema
988
+ });
989
+ break;
990
+ case "reverseReference":
991
+ fields.push({
992
+ name,
993
+ tableName: schema.tableName,
994
+ isArray: true,
995
+ foreignKey: schema.foreignKey,
996
+ schema
997
+ });
998
+ break;
999
+ case "tuple":
1000
+ for (const el of schema.elements) {
1001
+ fields.push(...collectReferenceFields(el.schema, name));
1002
+ }
1003
+ break;
1004
+ case "array":
1005
+ fields.push(...collectReferenceFields(schema.element, name));
1006
+ break;
1007
+ case "union":
1008
+ for (const member of schema.members) {
1009
+ fields.push(...collectReferenceFields(member, name));
1010
+ }
1011
+ break;
1012
+ }
1013
+ return fields;
1014
+ }
1015
+ function parseValueWithReferences(valueString, schema, refBaseDir, defaultPrimaryKey, currentFilePath, currentRowPk) {
1016
+ if (!hasNestedReferences(schema)) {
1017
+ return parseValue(schema, valueString);
1018
+ }
1019
+ switch (schema.type) {
1020
+ case "reference":
1021
+ return parseReferenceValue(
1022
+ schema,
1023
+ valueString,
1024
+ refBaseDir,
1025
+ defaultPrimaryKey,
1026
+ currentFilePath
1027
+ );
1028
+ case "reverseReference": {
1029
+ if (currentRowPk === void 0) return [];
1030
+ return resolveReverseReference(
1031
+ schema,
1032
+ currentRowPk,
1033
+ refBaseDir,
1034
+ defaultPrimaryKey,
1035
+ currentFilePath
1036
+ );
1037
+ }
1038
+ case "tuple": {
1039
+ const parsed = parseValue(schema, valueString);
1040
+ return schema.elements.map(
1041
+ (el, i) => resolveNestedReferences(
1042
+ parsed[i],
1043
+ el.schema,
1044
+ refBaseDir,
1045
+ defaultPrimaryKey,
1046
+ currentFilePath,
1047
+ currentRowPk
1048
+ )
1049
+ );
1050
+ }
1051
+ case "array": {
1052
+ const parsed = parseValue(schema, valueString);
1053
+ return parsed.map(
1054
+ (item) => resolveNestedReferences(
1055
+ item,
1056
+ schema.element,
1057
+ refBaseDir,
1058
+ defaultPrimaryKey,
1059
+ currentFilePath,
1060
+ currentRowPk
1061
+ )
1062
+ );
1063
+ }
1064
+ case "union": {
1065
+ const errors = [];
1066
+ for (const member of schema.members) {
1067
+ if (hasNestedReferences(member)) {
1068
+ try {
1069
+ const parsed = parseValue(member, valueString);
1070
+ return resolveNestedReferences(
1071
+ parsed,
1072
+ member,
1073
+ refBaseDir,
1074
+ defaultPrimaryKey,
1075
+ currentFilePath,
1076
+ currentRowPk
1077
+ );
1078
+ } catch (e) {
1079
+ errors.push(e instanceof Error ? e : new Error(String(e)));
1080
+ }
1081
+ }
1082
+ }
1083
+ if (errors.length > 0 && errors.every(
1084
+ (e) => /not found|Circular reference|Failed to load/.test(e.message)
1085
+ )) {
1086
+ for (const member of schema.members) {
1087
+ if (!hasNestedReferences(member)) {
1088
+ try {
1089
+ return parseValue(member, valueString);
1090
+ } catch {
1091
+ }
1092
+ }
1093
+ }
1094
+ }
1095
+ return parseValue(schema, valueString);
1096
+ }
1097
+ default:
1098
+ return parseValue(schema, valueString);
1099
+ }
1100
+ }
1101
+ function resolveReverseReference(schema, pkValue, refBaseDir, defaultPrimaryKey, currentFilePath) {
1102
+ const { refTable } = loadReferenceTable(
1103
+ schema,
1104
+ refBaseDir,
1105
+ defaultPrimaryKey,
1106
+ currentFilePath
1107
+ );
1108
+ const pkStr = String(pkValue);
1109
+ return refTable.filter((row) => {
1110
+ const fkValue = row[schema.foreignKey];
1111
+ const fkStr = fkValue !== null && fkValue !== void 0 && typeof fkValue === "object" ? String(fkValue[defaultPrimaryKey]) : String(fkValue);
1112
+ return fkStr === pkStr;
1113
+ });
1114
+ }
1115
+ function resolveNestedReferences(value, schema, refBaseDir, defaultPrimaryKey, currentFilePath, currentRowPk) {
1116
+ switch (schema.type) {
1117
+ case "reference": {
1118
+ if (value === null || value === void 0) return value;
1119
+ const { lookup } = loadReferenceTable(
1120
+ schema,
1121
+ refBaseDir,
1122
+ defaultPrimaryKey,
1123
+ currentFilePath
1124
+ );
1125
+ if (schema.isArray) {
1126
+ const ids = Array.isArray(value) ? value : [value];
1127
+ return ids.map(
1128
+ (id) => resolveReferenceId(String(id), lookup, schema.tableName)
1129
+ );
1130
+ }
1131
+ return resolveReferenceId(String(value), lookup, schema.tableName);
1132
+ }
1133
+ case "reverseReference": {
1134
+ if (currentRowPk === void 0) return [];
1135
+ const results = resolveReverseReference(
1136
+ schema,
1137
+ currentRowPk,
1138
+ refBaseDir,
1139
+ defaultPrimaryKey,
1140
+ currentFilePath
1141
+ );
1142
+ return results;
1143
+ }
1144
+ case "tuple": {
1145
+ if (!Array.isArray(value)) return value;
1146
+ return schema.elements.map(
1147
+ (el, i) => resolveNestedReferences(
1148
+ value[i],
1149
+ el.schema,
1150
+ refBaseDir,
1151
+ defaultPrimaryKey,
1152
+ currentFilePath,
1153
+ currentRowPk
1154
+ )
1155
+ );
1156
+ }
1157
+ case "array": {
1158
+ if (!Array.isArray(value)) return value;
1159
+ return value.map(
1160
+ (item) => resolveNestedReferences(
1161
+ item,
1162
+ schema.element,
1163
+ refBaseDir,
1164
+ defaultPrimaryKey,
1165
+ currentFilePath,
1166
+ currentRowPk
1167
+ )
1168
+ );
1169
+ }
1170
+ case "union": {
1171
+ const errors = [];
1172
+ for (const member of schema.members) {
1173
+ if (hasNestedReferences(member)) {
1174
+ try {
1175
+ return resolveNestedReferences(
1176
+ value,
1177
+ member,
1178
+ refBaseDir,
1179
+ defaultPrimaryKey,
1180
+ currentFilePath,
1181
+ currentRowPk
1182
+ );
1183
+ } catch (e) {
1184
+ errors.push(e instanceof Error ? e : new Error(String(e)));
1185
+ }
1186
+ }
1187
+ }
1188
+ if (errors.length > 0) {
1189
+ throw errors[0];
1190
+ }
1191
+ return value;
1192
+ }
1193
+ default:
1194
+ return value;
1195
+ }
1196
+ }
1197
+ function parseReferenceValue(schema, valueString, refBaseDir, defaultPrimaryKey, currentFilePath) {
1198
+ const trimmed = valueString.trim();
1199
+ if (schema.isOptional && trimmed === "") {
1200
+ return null;
1201
+ }
1202
+ const { lookup } = loadReferenceTable(
1203
+ schema,
1204
+ refBaseDir,
1205
+ defaultPrimaryKey,
1206
+ currentFilePath
1207
+ );
1208
+ const ids = parseValue(schema, trimmed);
1209
+ if (ids === null) return null;
1210
+ if (schema.isArray && Array.isArray(ids)) {
1211
+ return ids.map((id) => resolveReferenceId(id, lookup, schema.tableName));
1212
+ }
1213
+ return resolveReferenceId(ids, lookup, schema.tableName);
1214
+ }
1215
+
1216
+ // src/csv-loader/type-gen.ts
1217
+ import * as path2 from "path";
1218
+ function resolveReferencesInSchemaString(schemaString, resourceNames) {
1219
+ return schemaString.replace(/@([a-zA-Z0-9\-_]+)(\[\])?/g, (_match, tableName, arraySuffix) => {
1220
+ const typeName = resourceNames.get(tableName) || tableName.charAt(0).toUpperCase() + tableName.slice(1);
1221
+ return arraySuffix ? `${typeName}[]` : typeName;
1222
+ }).replace(/; ?/g, ", ");
1223
+ }
1224
+ function generateTypeDefinition(resourceName, propertyConfigs, references, currentFilePath, typeDeclarations = []) {
1225
+ const typeName = resourceName ? `${resourceName}Table` : "Table";
1226
+ const currentTableName = currentFilePath ? path2.basename(currentFilePath, path2.extname(currentFilePath)) : void 0;
1227
+ const singularType = resourceName ? resourceName.charAt(0).toUpperCase() + resourceName.slice(1) : `${typeName}[number]`;
1228
+ const imports = [];
1229
+ const resourceNames = /* @__PURE__ */ new Map();
1230
+ references.forEach((tableName) => {
1231
+ if (tableName === currentTableName) {
1232
+ resourceNames.set(tableName, singularType);
1233
+ return;
1234
+ }
1235
+ const typeBase = tableName.charAt(0).toUpperCase() + tableName.slice(1);
1236
+ resourceNames.set(tableName, typeBase);
1237
+ let importPath;
1238
+ if (currentFilePath) {
1239
+ importPath = `./${tableName}.csv`;
1240
+ } else {
1241
+ importPath = `../${tableName}.csv`;
1242
+ }
1243
+ imports.push(`import type { ${typeBase} } from '${importPath}';`);
1244
+ });
1245
+ const importSection = imports.length > 0 ? imports.join("\n") + "\n\n" : "";
1246
+ const typeDeclarationSection = typeDeclarations.length > 0 ? typeDeclarations.map(
1247
+ (decl) => `export type ${decl.name} = ${decl.schemaString ? resolveReferencesInSchemaString(decl.schemaString, resourceNames) : schemaToTypeString(decl.schema, resourceNames)};`
1248
+ ).join("\n") + "\n\n" : "";
1249
+ const properties = propertyConfigs.map((config) => {
1250
+ const typeStr = config.declaredTypeName ? config.declaredTypeName : config.schemaString ? resolveReferencesInSchemaString(config.schemaString, resourceNames) : schemaToTypeString(config.schema, resourceNames);
1251
+ return ` readonly ${config.name}: ${typeStr};`;
1252
+ }).join("\n");
1253
+ let exportAlias = "";
1254
+ if (resourceName) {
1255
+ const singularType2 = resourceName.charAt(0).toUpperCase() + resourceName.slice(1);
1256
+ exportAlias = `
1257
+ export type ${singularType2} = ${typeName}[number];`;
1258
+ }
1259
+ return `${importSection}${typeDeclarationSection}type ${typeName} = readonly {
1260
+ ${properties}
1261
+ }[];
1262
+ ${exportAlias}
1263
+
1264
+ declare function getData(): ${typeName};
1265
+ export default getData;
1266
+ `;
1267
+ }
1268
+
1269
+ // src/csv-loader/type-declarations.ts
1270
+ function parseTypeDeclaration(line, commentChar = "#") {
1271
+ const trimmed = line.trim();
1272
+ if (!trimmed.startsWith(commentChar)) return null;
1273
+ const content = trimmed.slice(commentChar.length).trim();
1274
+ const match = content.match(/^type\s+([A-Z][a-zA-Z0-9]*)\s*=\s*(.+)$/);
1275
+ if (!match) return null;
1276
+ const [, typeName, schemaString] = match;
1277
+ return { typeName, schemaString };
1278
+ }
1279
+ function expandTypeName(schemaString, declaredTypes) {
1280
+ const trimmed = schemaString.trim();
1281
+ if (declaredTypes.has(trimmed)) {
1282
+ return declaredTypes.get(trimmed);
1283
+ }
1284
+ return null;
1285
+ }
1286
+ function expandSchemaString(schemaString, declaredTypes) {
1287
+ let result = schemaString;
1288
+ let prev = "";
1289
+ while (prev !== result) {
1290
+ prev = result;
1291
+ result = expandSchemaInString(result, declaredTypes);
1292
+ }
1293
+ return result;
1294
+ }
1295
+ function expandSchemaInString(schemaString, declaredTypes) {
1296
+ const expanded = expandTypeName(schemaString.trim(), declaredTypes);
1297
+ if (expanded !== null) {
1298
+ return expanded;
1299
+ }
1300
+ const trimmed = schemaString.trim();
1301
+ if (trimmed.endsWith("[]")) {
1302
+ const inner = trimmed.slice(0, -2);
1303
+ const expandedInner = expandSchemaInString(inner, declaredTypes);
1304
+ return `${expandedInner}[]`;
1305
+ }
1306
+ if (schemaString.includes("|")) {
1307
+ const parts = splitByToken(schemaString, "|");
1308
+ if (parts.length > 1) {
1309
+ const expandedParts = parts.map(
1310
+ (part) => expandSchemaInString(part.trim(), declaredTypes)
1311
+ );
1312
+ return expandedParts.join(" | ");
1313
+ }
1314
+ }
1315
+ if (schemaString.startsWith("[") && schemaString.endsWith("]")) {
1316
+ const inner = schemaString.slice(1, -1);
1317
+ if (inner.includes(";")) {
1318
+ const elements = splitByToken(inner, ";");
1319
+ const expandedElements = elements.map(
1320
+ (el) => expandSchemaInString(el.trim(), declaredTypes)
1321
+ );
1322
+ return `[${expandedElements.join("; ")}]`;
1323
+ }
1324
+ return `[${expandSchemaInString(inner, declaredTypes)}]`;
1325
+ }
1326
+ const typeNameMatch = schemaString.trim().match(/^[A-Z][a-zA-Z0-9]*$/);
1327
+ if (typeNameMatch) {
1328
+ const expanded2 = expandTypeName(schemaString.trim(), declaredTypes);
1329
+ if (expanded2 !== null) {
1330
+ return expanded2;
1331
+ }
1332
+ }
1333
+ return schemaString;
1334
+ }
1335
+ function splitByToken(str, token) {
1336
+ const result = [];
1337
+ let current = "";
1338
+ let inQuote = null;
1339
+ for (let i = 0; i < str.length; i++) {
1340
+ const char = str[i];
1341
+ if (inQuote) {
1342
+ if (char === inQuote && str[i - 1] !== "\\") {
1343
+ inQuote = null;
1344
+ }
1345
+ current += char;
1346
+ } else if (char === '"' || char === "'") {
1347
+ inQuote = char;
1348
+ current += char;
1349
+ } else if (char === token && inQuote === null) {
1350
+ result.push(current);
1351
+ current = "";
1352
+ } else {
1353
+ current += char;
1354
+ }
1355
+ }
1356
+ if (current.length > 0 || str.endsWith(token)) {
1357
+ result.push(current);
1358
+ }
1359
+ return result;
1360
+ }
1361
+ function resolveTypeReferences(schema, declaredTypes) {
1362
+ switch (schema.type) {
1363
+ case "union":
1364
+ return {
1365
+ type: "union",
1366
+ members: schema.members.map(
1367
+ (m) => resolveTypeReferences(m, declaredTypes)
1368
+ )
1369
+ };
1370
+ case "tuple":
1371
+ return {
1372
+ type: "tuple",
1373
+ elements: schema.elements.map((el) => ({
1374
+ name: el.name,
1375
+ schema: resolveTypeReferences(el.schema, declaredTypes)
1376
+ }))
1377
+ };
1378
+ case "array":
1379
+ return {
1380
+ type: "array",
1381
+ element: resolveTypeReferences(schema.element, declaredTypes)
1382
+ };
1383
+ case "reference":
1384
+ return schema;
1385
+ default:
1386
+ return schema;
1387
+ }
1388
+ }
1389
+ function parseReverseReferenceDeclaration(line, commentChar = "#") {
1390
+ const trimmed = line.trim();
1391
+ if (!trimmed.startsWith(commentChar)) return null;
1392
+ const content = trimmed.slice(commentChar.length).trim();
1393
+ const match = content.match(/^inject\s+(\w+)\s*=\s*~(\w+)\((\w+)\)(\?)?$/);
1394
+ if (!match) return null;
1395
+ const [, fieldName, tableName, foreignKey, optionalMark] = match;
1396
+ const isOptional = optionalMark === "?";
1397
+ const schema = {
1398
+ type: "reverseReference",
1399
+ tableName,
1400
+ foreignKey,
1401
+ isOptional
1402
+ };
1403
+ return {
1404
+ fieldName,
1405
+ tableName,
1406
+ foreignKey,
1407
+ isOptional,
1408
+ schema
1409
+ };
1410
+ }
1411
+
1412
+ // src/csv-loader/loader.ts
1413
+ function parseCsv(content, options = {}) {
1414
+ const delimiter = options.delimiter ?? ",";
1415
+ const quote = options.quote ?? '"';
1416
+ const escape = options.escape ?? "\\";
1417
+ const bom = options.bom ?? true;
1418
+ const comment = options.comment === false ? void 0 : options.comment ?? "#";
1419
+ const trim = options.trim ?? true;
1420
+ const emitTypes = options.emitTypes ?? true;
1421
+ const refBaseDir = options.refBaseDir;
1422
+ const defaultPrimaryKey = options.defaultPrimaryKey ?? "id";
1423
+ const reverseReferences = [];
1424
+ const typeDeclarationsRaw = [];
1425
+ let filteredContent = content;
1426
+ if (comment) {
1427
+ const lines = content.split(/\r?\n/);
1428
+ const nonCommentLines = [];
1429
+ for (const line of lines) {
1430
+ const trimmed = line.trim();
1431
+ if (trimmed.startsWith(comment)) {
1432
+ const typeDecl = parseTypeDeclaration(trimmed, comment);
1433
+ if (typeDecl) {
1434
+ typeDeclarationsRaw.push(typeDecl);
1435
+ continue;
1436
+ }
1437
+ const decl = parseReverseReferenceDeclaration(trimmed, comment);
1438
+ if (decl) {
1439
+ reverseReferences.push(decl);
1440
+ continue;
1441
+ }
1442
+ continue;
1443
+ }
1444
+ nonCommentLines.push(line);
1445
+ }
1446
+ filteredContent = nonCommentLines.join("\n");
1447
+ }
1448
+ const records = parse(filteredContent, {
1449
+ delimiter,
1450
+ quote,
1451
+ escape,
1452
+ bom,
1453
+ comment: void 0,
1454
+ trim,
1455
+ skip_empty_lines: true,
1456
+ relax_column_count: true
1457
+ });
1458
+ const filteredRecords = records;
1459
+ if (filteredRecords.length < 2) {
1460
+ throw new Error("CSV must have at least 2 rows: headers and schemas");
1461
+ }
1462
+ const headers = filteredRecords[0];
1463
+ const schemas = filteredRecords[1];
1464
+ if (headers.length !== schemas.length) {
1465
+ throw new Error(
1466
+ `Header count (${headers.length}) does not match schema count (${schemas.length})`
1467
+ );
1468
+ }
1469
+ const dataRows = filteredRecords.slice(2);
1470
+ for (let col = 0; col < schemas.length; col++) {
1471
+ const cell = (schemas[col] ?? "").trim();
1472
+ if (comment && cell.startsWith(comment)) {
1473
+ const typeDecl = parseTypeDeclaration(cell, comment);
1474
+ if (typeDecl) {
1475
+ typeDeclarationsRaw.push(typeDecl);
1476
+ continue;
1477
+ }
1478
+ const decl = parseReverseReferenceDeclaration(cell, comment);
1479
+ if (decl) {
1480
+ reverseReferences.push(decl);
1481
+ }
1482
+ }
1483
+ }
1484
+ const declaredTypeNames = /* @__PURE__ */ new Set();
1485
+ for (const decl of typeDeclarationsRaw) {
1486
+ declaredTypeNames.add(decl.typeName);
1487
+ }
1488
+ const declaredSchemaStrings = /* @__PURE__ */ new Map();
1489
+ for (const decl of typeDeclarationsRaw) {
1490
+ declaredSchemaStrings.set(decl.typeName, decl.schemaString);
1491
+ }
1492
+ const typeDeclarationsParsed = [];
1493
+ for (const decl of typeDeclarationsRaw) {
1494
+ const expandedSchema = expandSchemaString(
1495
+ decl.schemaString,
1496
+ declaredSchemaStrings
1497
+ );
1498
+ const schema = parseSchema(expandedSchema.trim());
1499
+ typeDeclarationsParsed.push({
1500
+ name: decl.typeName,
1501
+ schema,
1502
+ schemaString: decl.schemaString
1503
+ });
1504
+ }
1505
+ const declaredTypes = /* @__PURE__ */ new Map();
1506
+ for (const decl of typeDeclarationsParsed) {
1507
+ declaredTypes.set(decl.name, decl.schema);
1508
+ }
1509
+ const typeDeclarations = [];
1510
+ for (const decl of typeDeclarationsParsed) {
1511
+ const resolvedSchema = resolveTypeReferences(decl.schema, declaredTypes);
1512
+ typeDeclarations.push({
1513
+ name: decl.name,
1514
+ schema: resolvedSchema,
1515
+ schemaString: decl.schemaString
1516
+ });
1517
+ }
1518
+ for (const decl of typeDeclarations) {
1519
+ declaredTypes.set(decl.name, decl.schema);
1520
+ }
1521
+ const resolveReferences = options.resolveReferences ?? true;
1522
+ const propertyConfigs = headers.map(
1523
+ (header, index) => {
1524
+ const schemaString = schemas[index];
1525
+ let schema;
1526
+ let declaredTypeName;
1527
+ let columnSchemaString;
1528
+ if (declaredTypes.has(schemaString)) {
1529
+ schema = declaredTypes.get(schemaString);
1530
+ declaredTypeName = schemaString;
1531
+ } else {
1532
+ const expandedSchema = expandSchemaString(
1533
+ schemaString,
1534
+ declaredSchemaStrings
1535
+ );
1536
+ schema = parseSchema(expandedSchema.trim());
1537
+ if (expandedSchema !== schemaString) {
1538
+ columnSchemaString = schemaString;
1539
+ }
1540
+ }
1541
+ const config = {
1542
+ name: header,
1543
+ schema,
1544
+ validator: createValidator(schema),
1545
+ parser: (valueString) => parseValue(schema, valueString),
1546
+ declaredTypeName,
1547
+ schemaString: columnSchemaString
1548
+ };
1549
+ if (schema.type === "reference") {
1550
+ config.isReference = true;
1551
+ config.referenceTableName = schema.tableName;
1552
+ config.referenceIsArray = schema.isArray;
1553
+ if (resolveReferences) {
1554
+ config.parser = (valueString) => {
1555
+ return parseReferenceValue(
1556
+ schema,
1557
+ valueString,
1558
+ refBaseDir,
1559
+ defaultPrimaryKey,
1560
+ options.currentFilePath
1561
+ );
1562
+ };
1563
+ } else {
1564
+ config.parser = (valueString) => {
1565
+ return parseReferenceIds(schema, valueString);
1566
+ };
1567
+ }
1568
+ } else if (hasNestedReferences(schema)) {
1569
+ config.isReference = true;
1570
+ if (resolveReferences) {
1571
+ config.parser = (valueString) => {
1572
+ return parseValueWithReferences(
1573
+ valueString,
1574
+ schema,
1575
+ refBaseDir,
1576
+ defaultPrimaryKey,
1577
+ options.currentFilePath
1578
+ );
1579
+ };
1580
+ } else {
1581
+ config.parser = (valueString) => {
1582
+ return parseValueWithReferenceIds(valueString, schema);
1583
+ };
1584
+ }
1585
+ }
1586
+ return config;
1587
+ }
1588
+ );
1589
+ for (const decl of reverseReferences) {
1590
+ const config = {
1591
+ name: decl.fieldName,
1592
+ schema: decl.schema,
1593
+ validator: createValidator(decl.schema),
1594
+ parser: (_valueString) => {
1595
+ return null;
1596
+ },
1597
+ isReference: true,
1598
+ isReverseReference: true,
1599
+ referenceTableName: decl.tableName,
1600
+ referenceIsArray: true,
1601
+ reverseReferenceForeignKey: decl.foreignKey
1602
+ };
1603
+ propertyConfigs.push(config);
1604
+ }
1605
+ const references = /* @__PURE__ */ new Set();
1606
+ function collectReferences(schema) {
1607
+ if (schema.type === "reference") {
1608
+ references.add(schema.tableName);
1609
+ } else if (schema.type === "reverseReference") {
1610
+ references.add(schema.tableName);
1611
+ } else if (schema.type === "tuple") {
1612
+ schema.elements.forEach((el) => collectReferences(el.schema));
1613
+ } else if (schema.type === "array") {
1614
+ collectReferences(schema.element);
1615
+ } else if (schema.type === "union") {
1616
+ schema.members.forEach((m) => collectReferences(m));
1617
+ }
1618
+ }
1619
+ propertyConfigs.forEach((config) => {
1620
+ if (config.isReference && config.referenceTableName) {
1621
+ references.add(config.referenceTableName);
1622
+ }
1623
+ collectReferences(config.schema);
1624
+ });
1625
+ const objects = dataRows.map((row, rowIndex) => {
1626
+ const obj = {};
1627
+ propertyConfigs.forEach((config, colIndex) => {
1628
+ if (config.isReverseReference) {
1629
+ return;
1630
+ }
1631
+ const rawValue = row[colIndex] ?? "";
1632
+ try {
1633
+ const parsed = config.parser(rawValue);
1634
+ if (!config.isReference && !config.validator(parsed)) {
1635
+ throw new Error(
1636
+ `Validation failed for property "${config.name}" at row ${rowIndex + 3}: ${rawValue}`
1637
+ );
1638
+ }
1639
+ obj[config.name] = parsed;
1640
+ } catch (error) {
1641
+ if (error instanceof Error) {
1642
+ throw new Error(
1643
+ `Failed to parse property "${config.name}" at row ${rowIndex + 3}, column ${colIndex + 1}: ${error.message}`
1644
+ );
1645
+ }
1646
+ throw error;
1647
+ }
1648
+ });
1649
+ return obj;
1650
+ });
1651
+ if (resolveReferences) {
1652
+ for (const decl of reverseReferences) {
1653
+ for (const obj of objects) {
1654
+ const pkValue = obj[defaultPrimaryKey];
1655
+ if (pkValue !== void 0) {
1656
+ const resolved = resolveReverseReference(
1657
+ decl.schema,
1658
+ pkValue,
1659
+ refBaseDir,
1660
+ defaultPrimaryKey,
1661
+ options.currentFilePath
1662
+ );
1663
+ obj[decl.fieldName] = decl.isOptional && resolved.length === 0 ? null : resolved;
1664
+ } else {
1665
+ obj[decl.fieldName] = decl.isOptional ? null : [];
1666
+ }
1667
+ }
1668
+ }
1669
+ }
1670
+ const referenceFields = [];
1671
+ if (!resolveReferences) {
1672
+ for (const config of propertyConfigs) {
1673
+ if (hasNestedReferences(config.schema)) {
1674
+ referenceFields.push(
1675
+ ...collectReferenceFields(config.schema, config.name)
1676
+ );
1677
+ }
1678
+ }
1679
+ }
1680
+ const result = {
1681
+ data: objects,
1682
+ propertyConfigs,
1683
+ references,
1684
+ referenceFields,
1685
+ reverseReferences,
1686
+ typeDeclarations
1687
+ };
1688
+ if (emitTypes) {
1689
+ result.typeDefinition = generateTypeDefinition(
1690
+ options.resourceName || "",
1691
+ propertyConfigs,
1692
+ references,
1693
+ options.currentFilePath,
1694
+ typeDeclarations
1695
+ );
1696
+ }
1697
+ return result;
1698
+ }
1699
+ export {
1700
+ parseCsv
1701
+ };