@zenoaihq/tson 1.0.2 → 1.1.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 CHANGED
@@ -455,7 +455,7 @@ MIT License - see [LICENSE](../LICENSE) file for details.
455
455
  ---
456
456
 
457
457
  **Package:** `@zenoaihq/tson`
458
- **Version:** 1.0.2
458
+ **Version:** 1.1.0
459
459
  **Status:** Production Ready
460
460
  **Node.js:** 16.0.0+
461
461
  **TypeScript:** 5.3+
package/dist/index.cjs CHANGED
@@ -192,10 +192,10 @@ function splitByDelimiter(text, delimiter) {
192
192
  function parseKeySchema(keyString) {
193
193
  const trimmed = keyString.trim();
194
194
  if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
195
- return { keyName: unescapeString(trimmed.slice(1, -1)), schema: null };
195
+ return { keyName: unescapeString(trimmed.slice(1, -1)), schema: null, count: null };
196
196
  }
197
197
  if (!trimmed.includes("(")) {
198
- return { keyName: trimmed, schema: null };
198
+ return { keyName: trimmed, schema: null, count: null };
199
199
  }
200
200
  const parenIdx = trimmed.indexOf("(");
201
201
  let keyName = trimmed.slice(0, parenIdx).trim();
@@ -209,14 +209,54 @@ function parseKeySchema(keyString) {
209
209
  if (schemaStr.startsWith("@")) {
210
210
  schemaStr = schemaStr.slice(1);
211
211
  }
212
+ let nestedCount = null;
213
+ const hashIdx = findTrailingHash(schemaStr);
214
+ if (hashIdx !== -1) {
215
+ const countPart = schemaStr.slice(hashIdx + 1).trim();
216
+ const parsedCount = parseInt(countPart, 10);
217
+ if (!isNaN(parsedCount)) {
218
+ nestedCount = parsedCount;
219
+ schemaStr = schemaStr.slice(0, hashIdx).trim();
220
+ }
221
+ }
212
222
  const schemaKeys = splitByDelimiter(schemaStr, ",");
213
- return { keyName, schema: schemaKeys };
223
+ return { keyName, schema: schemaKeys, count: nestedCount };
224
+ }
225
+ function findTrailingHash(schemaStr) {
226
+ let inQuotes = false;
227
+ let depthParen = 0;
228
+ for (let i = schemaStr.length - 1; i >= 0; i--) {
229
+ const char = schemaStr[i];
230
+ if (char === '"') {
231
+ let backslashCount = 0;
232
+ for (let j = i - 1; j >= 0; j--) {
233
+ if (schemaStr[j] === "\\") {
234
+ backslashCount++;
235
+ } else {
236
+ break;
237
+ }
238
+ }
239
+ if (backslashCount % 2 === 0) {
240
+ inQuotes = !inQuotes;
241
+ }
242
+ }
243
+ if (!inQuotes) {
244
+ if (char === ")") {
245
+ depthParen++;
246
+ } else if (char === "(") {
247
+ depthParen--;
248
+ } else if (char === "#" && depthParen === 0) {
249
+ return i;
250
+ }
251
+ }
252
+ }
253
+ return -1;
214
254
  }
215
255
  function buildSchemaMap(keys) {
216
256
  const schemaMap = {};
217
257
  for (const key of keys) {
218
- const { keyName, schema } = parseKeySchema(key);
219
- schemaMap[keyName] = schema;
258
+ const { keyName, schema, count } = parseKeySchema(key);
259
+ schemaMap[keyName] = { schema, count };
220
260
  }
221
261
  return schemaMap;
222
262
  }
@@ -331,7 +371,9 @@ function serializeTabular(arr) {
331
371
  keyStr = `"${escapeString(keyStr)}"`;
332
372
  }
333
373
  if (key in nestedSchemas) {
334
- const schemaKeys = nestedSchemas[key];
374
+ const schemaInfo = nestedSchemas[key];
375
+ const schemaKeys = schemaInfo.schema;
376
+ const nestedCount = schemaInfo.count;
335
377
  const formattedSchemaKeys = schemaKeys.map((sk) => {
336
378
  if (needsQuoting(sk)) {
337
379
  return `"${escapeString(sk)}"`;
@@ -339,7 +381,11 @@ function serializeTabular(arr) {
339
381
  return sk;
340
382
  });
341
383
  const schemaStr = formattedSchemaKeys.join(",");
342
- keyStr = `${keyStr}(@${schemaStr})`;
384
+ if (nestedCount !== null) {
385
+ keyStr = `${keyStr}(@${schemaStr}#${nestedCount})`;
386
+ } else {
387
+ keyStr = `${keyStr}(@${schemaStr})`;
388
+ }
343
389
  }
344
390
  keyParts.push(keyStr);
345
391
  }
@@ -350,7 +396,12 @@ function serializeTabular(arr) {
350
396
  for (const key of keys) {
351
397
  const value = obj[key];
352
398
  if (key in nestedSchemas) {
353
- valueParts.push(serializeSchematizedObject(value, nestedSchemas[key]));
399
+ const schemaInfo = nestedSchemas[key];
400
+ if (schemaInfo.count !== null) {
401
+ valueParts.push(serializeSchematizedArray(value, schemaInfo.schema));
402
+ } else {
403
+ valueParts.push(serializeSchematizedObject(value, schemaInfo.schema));
404
+ }
354
405
  } else {
355
406
  valueParts.push(serializeValue(value));
356
407
  }
@@ -364,19 +415,49 @@ function detectNestedSchemas(arr, keys) {
364
415
  const nestedSchemas = {};
365
416
  for (const key of keys) {
366
417
  const values = arr.map((obj) => obj[key]);
367
- if (!values.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
368
- continue;
369
- }
370
- if (values.length === 0) {
418
+ if (values.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
419
+ if (values.length === 0) {
420
+ continue;
421
+ }
422
+ const firstKeys = Object.keys(values[0]);
423
+ const allSame = values.slice(1).every((v) => {
424
+ const objKeys = Object.keys(v);
425
+ return objKeys.length === firstKeys.length && objKeys.every((k, i) => k === firstKeys[i]);
426
+ });
427
+ if (allSame) {
428
+ nestedSchemas[key] = { schema: firstKeys, count: null };
429
+ }
371
430
  continue;
372
431
  }
373
- const firstKeys = Object.keys(values[0]);
374
- const allSame = values.slice(1).every((v) => {
375
- const objKeys = Object.keys(v);
376
- return objKeys.length === firstKeys.length && objKeys.every((k, i) => k === firstKeys[i]);
377
- });
378
- if (allSame) {
379
- nestedSchemas[key] = firstKeys;
432
+ if (values.every((v) => Array.isArray(v))) {
433
+ if (values.length === 0) {
434
+ continue;
435
+ }
436
+ let allUniform = true;
437
+ let firstSchema = null;
438
+ let arrayCount = null;
439
+ for (const v of values) {
440
+ const arr2 = v;
441
+ if (!isUniformObjectArray(arr2)) {
442
+ allUniform = false;
443
+ break;
444
+ }
445
+ if (arr2.length === 0) {
446
+ continue;
447
+ }
448
+ const vSchema = Object.keys(arr2[0]);
449
+ const vCount = arr2.length;
450
+ if (firstSchema === null) {
451
+ firstSchema = vSchema;
452
+ arrayCount = vCount;
453
+ } else if (vSchema.length !== firstSchema.length || !vSchema.every((k, i) => k === firstSchema[i]) || vCount !== arrayCount) {
454
+ allUniform = false;
455
+ break;
456
+ }
457
+ }
458
+ if (allUniform && firstSchema !== null) {
459
+ nestedSchemas[key] = { schema: firstSchema, count: arrayCount };
460
+ }
380
461
  }
381
462
  }
382
463
  return nestedSchemas;
@@ -392,6 +473,16 @@ function serializeSchematizedObject(obj, schema) {
392
473
  }
393
474
  return "{" + valueParts.join(",") + "}";
394
475
  }
476
+ function serializeSchematizedArray(arr, schema) {
477
+ if (arr.length === 0) {
478
+ return "[]";
479
+ }
480
+ const objParts = [];
481
+ for (const obj of arr) {
482
+ objParts.push(serializeSchematizedObject(obj, schema));
483
+ }
484
+ return "[" + objParts.join(",") + "]";
485
+ }
395
486
 
396
487
  // src/deserializer.ts
397
488
  function loads(s) {
@@ -444,8 +535,14 @@ function parseKeyedObject(content) {
444
535
  }
445
536
  const keysPart = parts[0];
446
537
  const { keys, count } = parseKeys(keysPart);
538
+ if (keys.length === 0 && count !== null) {
539
+ return Array.from({ length: count }, () => ({}));
540
+ }
447
541
  const schemaMap = buildSchemaMap(keys);
448
542
  const fieldNames = keys.map((k) => parseKeySchema(k).keyName);
543
+ if (keys.length === 0) {
544
+ return {};
545
+ }
449
546
  if (parts.length === 1) {
450
547
  throw new Error("Invalid object format: missing values");
451
548
  }
@@ -468,9 +565,11 @@ function parseSingleObject(fieldNames, valuesStr, schemaMap) {
468
565
  for (let i = 0; i < fieldNames.length; i++) {
469
566
  const fieldName = fieldNames[i];
470
567
  const valueStr = values[i];
471
- const schema = schemaMap[fieldName];
568
+ const schemaInfo = schemaMap[fieldName];
569
+ const schema = schemaInfo?.schema;
570
+ const count = schemaInfo?.count;
472
571
  if (schema) {
473
- obj[fieldName] = parseSchematizedValue(valueStr, schema);
572
+ obj[fieldName] = parseSchematizedValue(valueStr, schema, count);
474
573
  } else {
475
574
  obj[fieldName] = parseValue(valueStr);
476
575
  }
@@ -493,7 +592,36 @@ function parseTabularArray(fieldNames, rowParts, schemaMap, expectedCount) {
493
592
  }
494
593
  return result;
495
594
  }
496
- function parseSchematizedValue(valueStr, schema) {
595
+ function parseSchematizedValue(valueStr, schema, expectedCount = null) {
596
+ const trimmed = valueStr.trim();
597
+ if (expectedCount !== null) {
598
+ return parseSchematizedArray(trimmed, schema, expectedCount);
599
+ }
600
+ return parseSingleSchematizedObject(trimmed, schema);
601
+ }
602
+ function parseSchematizedArray(valueStr, schema, expectedCount) {
603
+ const trimmed = valueStr.trim();
604
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
605
+ throw new Error(`Schematized array must be wrapped in brackets: ${valueStr}`);
606
+ }
607
+ const content = trimmed.slice(1, -1).trim();
608
+ if (content.length === 0) {
609
+ return [];
610
+ }
611
+ const parts = splitByDelimiter(content, ",");
612
+ if (parts.length !== expectedCount) {
613
+ throw new Error(
614
+ `Array count mismatch: expected ${expectedCount} objects but got ${parts.length}`
615
+ );
616
+ }
617
+ const result = [];
618
+ for (const part of parts) {
619
+ const obj = parseSingleSchematizedObject(part.trim(), schema);
620
+ result.push(obj);
621
+ }
622
+ return result;
623
+ }
624
+ function parseSingleSchematizedObject(valueStr, schema) {
497
625
  const trimmed = valueStr.trim();
498
626
  if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
499
627
  throw new Error(`Schematized value must be wrapped in braces: ${valueStr}`);
@@ -513,12 +641,14 @@ function parseSchematizedValue(valueStr, schema) {
513
641
  const obj = {};
514
642
  for (let i = 0; i < fieldNames.length; i++) {
515
643
  const fieldName = fieldNames[i];
516
- const valueStr2 = values[i];
517
- const nestedSchema = nestedSchemaMap[fieldName];
644
+ const valStr = values[i];
645
+ const schemaInfo = nestedSchemaMap[fieldName];
646
+ const nestedSchema = schemaInfo?.schema;
647
+ const nestedCount = schemaInfo?.count;
518
648
  if (nestedSchema) {
519
- obj[fieldName] = parseSchematizedValue(valueStr2, nestedSchema);
649
+ obj[fieldName] = parseSchematizedValue(valStr, nestedSchema, nestedCount);
520
650
  } else {
521
- obj[fieldName] = parseValue(valueStr2);
651
+ obj[fieldName] = parseValue(valStr);
522
652
  }
523
653
  }
524
654
  return obj;
@@ -542,20 +672,228 @@ function parseArray(text) {
542
672
  return result;
543
673
  }
544
674
 
675
+ // src/prettify.ts
676
+ function prettify(tsonStr, indent = " ") {
677
+ if (!tsonStr || !tsonStr.trim()) {
678
+ return tsonStr;
679
+ }
680
+ return prettifyValue(tsonStr.trim(), indent, 0);
681
+ }
682
+ function minify(tsonStr) {
683
+ if (!tsonStr) {
684
+ return tsonStr;
685
+ }
686
+ const lines = tsonStr.split("\n");
687
+ const result = [];
688
+ for (const line of lines) {
689
+ result.push(line.trim());
690
+ }
691
+ return result.join("");
692
+ }
693
+ function prettifyValue(text, indent, depth) {
694
+ text = text.trim();
695
+ if (!text) {
696
+ return text;
697
+ }
698
+ if (text.startsWith("{@")) {
699
+ return prettifyObject(text, indent, depth);
700
+ }
701
+ if (text.startsWith("{")) {
702
+ return text;
703
+ }
704
+ if (text.startsWith("[")) {
705
+ return prettifyArray(text, indent, depth);
706
+ }
707
+ return text;
708
+ }
709
+ function prettifyObject(text, indent, depth) {
710
+ if (!text.startsWith("{@") || !text.endsWith("}")) {
711
+ return text;
712
+ }
713
+ const content = text.slice(2, -1);
714
+ if (!content) {
715
+ return "{@}";
716
+ }
717
+ const parts = splitTopLevel(content, "|");
718
+ if (parts.length === 0) {
719
+ return text;
720
+ }
721
+ const schema = parts[0];
722
+ const valueRows = parts.slice(1);
723
+ const nextIndent = indent.repeat(depth + 1);
724
+ if (valueRows.length === 0) {
725
+ return `{@${schema}}`;
726
+ }
727
+ const lines = [`{@${schema}`];
728
+ for (const row of valueRows) {
729
+ lines.push(`${nextIndent}|${row}`);
730
+ }
731
+ lines[lines.length - 1] = lines[lines.length - 1] + "}";
732
+ return lines.join("\n");
733
+ }
734
+ function prettifyArray(text, indent, depth) {
735
+ if (!text.startsWith("[") || !text.endsWith("]")) {
736
+ return text;
737
+ }
738
+ const content = text.slice(1, -1);
739
+ if (!content) {
740
+ return "[]";
741
+ }
742
+ const elements = splitTopLevel(content, ",");
743
+ if (elements.length <= 1) {
744
+ return text;
745
+ }
746
+ const nextIndent = indent.repeat(depth + 1);
747
+ const lines = [];
748
+ for (let i = 0; i < elements.length; i++) {
749
+ const prettified = prettifyValue(elements[i].trim(), indent, depth + 1);
750
+ if (i === 0) {
751
+ lines.push(`[${prettified}`);
752
+ } else {
753
+ lines.push(`${nextIndent},${prettified}`);
754
+ }
755
+ }
756
+ lines[lines.length - 1] = lines[lines.length - 1] + "]";
757
+ return lines.join("\n");
758
+ }
759
+ function splitTopLevel(text, delimiter) {
760
+ const parts = [];
761
+ const current = [];
762
+ let depth = 0;
763
+ let inString = false;
764
+ let escapeNext = false;
765
+ for (let i = 0; i < text.length; i++) {
766
+ const char = text[i];
767
+ if (escapeNext) {
768
+ current.push(char);
769
+ escapeNext = false;
770
+ continue;
771
+ }
772
+ if (char === "\\") {
773
+ current.push(char);
774
+ escapeNext = true;
775
+ continue;
776
+ }
777
+ if (char === '"') {
778
+ inString = !inString;
779
+ current.push(char);
780
+ continue;
781
+ }
782
+ if (inString) {
783
+ current.push(char);
784
+ continue;
785
+ }
786
+ if (char === "{" || char === "[") {
787
+ depth++;
788
+ current.push(char);
789
+ } else if (char === "}" || char === "]") {
790
+ depth--;
791
+ current.push(char);
792
+ } else if (char === delimiter && depth === 0) {
793
+ parts.push(current.join(""));
794
+ current.length = 0;
795
+ } else {
796
+ current.push(char);
797
+ }
798
+ }
799
+ if (current.length > 0) {
800
+ parts.push(current.join(""));
801
+ }
802
+ return parts;
803
+ }
804
+
805
+ // src/fileio.ts
806
+ async function loadJson(filepath) {
807
+ const fs = await import('fs/promises');
808
+ const content = await fs.readFile(filepath, "utf-8");
809
+ return JSON.parse(content);
810
+ }
811
+ async function loadTson(filepath) {
812
+ const fs = await import('fs/promises');
813
+ const content = await fs.readFile(filepath, "utf-8");
814
+ return loads(content);
815
+ }
816
+ async function saveTson(data, filepath, options = {}) {
817
+ const fs = await import('fs/promises');
818
+ let tsonStr = dumps(data);
819
+ if (options.format === "pretty") {
820
+ tsonStr = prettify(tsonStr, options.indent || " ");
821
+ }
822
+ await fs.writeFile(filepath, tsonStr, "utf-8");
823
+ }
824
+ async function saveTsonString(tsonStr, filepath, options = {}) {
825
+ const fs = await import('fs/promises');
826
+ if (options.format === "pretty") {
827
+ tsonStr = prettify(tsonStr, options.indent || " ");
828
+ } else if (options.format === "compact") {
829
+ tsonStr = minify(tsonStr);
830
+ }
831
+ await fs.writeFile(filepath, tsonStr, "utf-8");
832
+ }
833
+ async function jsonToTson(inputPath, outputPath, options = {}) {
834
+ const data = await loadJson(inputPath);
835
+ let tsonStr = dumps(data);
836
+ if (options.format === "pretty") {
837
+ tsonStr = prettify(tsonStr);
838
+ }
839
+ if (outputPath) {
840
+ await saveTsonString(tsonStr, outputPath);
841
+ }
842
+ return tsonStr;
843
+ }
844
+ async function tsonToJson(inputPath, outputPath, options = {}) {
845
+ const data = await loadTson(inputPath);
846
+ const jsonStr = JSON.stringify(data, null, options.indent ?? 2);
847
+ if (outputPath) {
848
+ const fs = await import('fs/promises');
849
+ await fs.writeFile(outputPath, jsonStr, "utf-8");
850
+ }
851
+ return jsonStr;
852
+ }
853
+ async function readTsonString(filepath) {
854
+ const fs = await import('fs/promises');
855
+ return fs.readFile(filepath, "utf-8");
856
+ }
857
+ async function prettifyFile(inputPath, outputPath, indent = " ") {
858
+ const tsonStr = await readTsonString(inputPath);
859
+ const prettyStr = prettify(tsonStr, indent);
860
+ const target = outputPath || inputPath;
861
+ await saveTsonString(prettyStr, target);
862
+ return prettyStr;
863
+ }
864
+ async function minifyFile(inputPath, outputPath) {
865
+ const tsonStr = await readTsonString(inputPath);
866
+ const compactStr = minify(tsonStr);
867
+ const target = outputPath || inputPath;
868
+ await saveTsonString(compactStr, target);
869
+ return compactStr;
870
+ }
871
+
545
872
  exports.buildSchemaMap = buildSchemaMap;
546
873
  exports.dump = dump;
547
874
  exports.dumps = dumps;
548
875
  exports.escapeString = escapeString;
549
876
  exports.formatPrimitive = formatPrimitive;
550
877
  exports.isUniformObjectArray = isUniformObjectArray;
878
+ exports.jsonToTson = jsonToTson;
551
879
  exports.load = load;
880
+ exports.loadJson = loadJson;
881
+ exports.loadTson = loadTson;
552
882
  exports.loads = loads;
553
883
  exports.looksLikeNumber = looksLikeNumber;
884
+ exports.minify = minify;
885
+ exports.minifyFile = minifyFile;
554
886
  exports.needsQuoting = needsQuoting;
555
887
  exports.parseKeySchema = parseKeySchema;
556
888
  exports.parseKeys = parseKeys;
557
889
  exports.parsePrimitive = parsePrimitive;
890
+ exports.prettify = prettify;
891
+ exports.prettifyFile = prettifyFile;
892
+ exports.readTsonString = readTsonString;
893
+ exports.saveTson = saveTson;
894
+ exports.saveTsonString = saveTsonString;
558
895
  exports.splitByDelimiter = splitByDelimiter;
896
+ exports.tsonToJson = tsonToJson;
559
897
  exports.unescapeString = unescapeString;
560
898
  //# sourceMappingURL=index.cjs.map
561
899
  //# sourceMappingURL=index.cjs.map