@zenoaihq/tson 1.0.1 → 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.1
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
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // src/utils.ts
4
- var SPECIAL_CHARS = /* @__PURE__ */ new Set([",", "|", "@", "#", "{", "}", "[", "]", "\n", "\r", " ", " "]);
4
+ var SPECIAL_CHARS = /* @__PURE__ */ new Set([",", "|", "@", "#", "{", "}", "[", "]", "\n", "\r", " ", " ", '"', "(", ")"]);
5
5
  function needsQuoting(value) {
6
6
  if (value.length === 0) {
7
7
  return true;
@@ -33,7 +33,36 @@ function escapeString(value) {
33
33
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
34
34
  }
35
35
  function unescapeString(value) {
36
- return value.replace(/\\t/g, " ").replace(/\\r/g, "\r").replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
36
+ const result = [];
37
+ let i = 0;
38
+ while (i < value.length) {
39
+ if (value[i] === "\\" && i + 1 < value.length) {
40
+ const nextChar = value[i + 1];
41
+ if (nextChar === "\\") {
42
+ result.push("\\");
43
+ i += 2;
44
+ } else if (nextChar === '"') {
45
+ result.push('"');
46
+ i += 2;
47
+ } else if (nextChar === "n") {
48
+ result.push("\n");
49
+ i += 2;
50
+ } else if (nextChar === "r") {
51
+ result.push("\r");
52
+ i += 2;
53
+ } else if (nextChar === "t") {
54
+ result.push(" ");
55
+ i += 2;
56
+ } else {
57
+ result.push(value[i]);
58
+ i += 1;
59
+ }
60
+ } else {
61
+ result.push(value[i]);
62
+ i += 1;
63
+ }
64
+ }
65
+ return result.join("");
37
66
  }
38
67
  function formatPrimitive(value) {
39
68
  if (value === null) {
@@ -162,11 +191,11 @@ function splitByDelimiter(text, delimiter) {
162
191
  }
163
192
  function parseKeySchema(keyString) {
164
193
  const trimmed = keyString.trim();
194
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
195
+ return { keyName: unescapeString(trimmed.slice(1, -1)), schema: null, count: null };
196
+ }
165
197
  if (!trimmed.includes("(")) {
166
- if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
167
- return { keyName: unescapeString(trimmed.slice(1, -1)), schema: null };
168
- }
169
- return { keyName: trimmed, schema: null };
198
+ return { keyName: trimmed, schema: null, count: null };
170
199
  }
171
200
  const parenIdx = trimmed.indexOf("(");
172
201
  let keyName = trimmed.slice(0, parenIdx).trim();
@@ -180,14 +209,54 @@ function parseKeySchema(keyString) {
180
209
  if (schemaStr.startsWith("@")) {
181
210
  schemaStr = schemaStr.slice(1);
182
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
+ }
183
222
  const schemaKeys = splitByDelimiter(schemaStr, ",");
184
- 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;
185
254
  }
186
255
  function buildSchemaMap(keys) {
187
256
  const schemaMap = {};
188
257
  for (const key of keys) {
189
- const { keyName, schema } = parseKeySchema(key);
190
- schemaMap[keyName] = schema;
258
+ const { keyName, schema, count } = parseKeySchema(key);
259
+ schemaMap[keyName] = { schema, count };
191
260
  }
192
261
  return schemaMap;
193
262
  }
@@ -302,9 +371,21 @@ function serializeTabular(arr) {
302
371
  keyStr = `"${escapeString(keyStr)}"`;
303
372
  }
304
373
  if (key in nestedSchemas) {
305
- const schemaKeys = nestedSchemas[key];
306
- const schemaStr = schemaKeys.join(",");
307
- keyStr = `${keyStr}(@${schemaStr})`;
374
+ const schemaInfo = nestedSchemas[key];
375
+ const schemaKeys = schemaInfo.schema;
376
+ const nestedCount = schemaInfo.count;
377
+ const formattedSchemaKeys = schemaKeys.map((sk) => {
378
+ if (needsQuoting(sk)) {
379
+ return `"${escapeString(sk)}"`;
380
+ }
381
+ return sk;
382
+ });
383
+ const schemaStr = formattedSchemaKeys.join(",");
384
+ if (nestedCount !== null) {
385
+ keyStr = `${keyStr}(@${schemaStr}#${nestedCount})`;
386
+ } else {
387
+ keyStr = `${keyStr}(@${schemaStr})`;
388
+ }
308
389
  }
309
390
  keyParts.push(keyStr);
310
391
  }
@@ -315,7 +396,12 @@ function serializeTabular(arr) {
315
396
  for (const key of keys) {
316
397
  const value = obj[key];
317
398
  if (key in nestedSchemas) {
318
- 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
+ }
319
405
  } else {
320
406
  valueParts.push(serializeValue(value));
321
407
  }
@@ -329,19 +415,49 @@ function detectNestedSchemas(arr, keys) {
329
415
  const nestedSchemas = {};
330
416
  for (const key of keys) {
331
417
  const values = arr.map((obj) => obj[key]);
332
- if (!values.every((v) => typeof v === "object" && v !== null && !Array.isArray(v))) {
333
- continue;
334
- }
335
- 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
+ }
336
430
  continue;
337
431
  }
338
- const firstKeys = Object.keys(values[0]);
339
- const allSame = values.slice(1).every((v) => {
340
- const objKeys = Object.keys(v);
341
- return objKeys.length === firstKeys.length && objKeys.every((k, i) => k === firstKeys[i]);
342
- });
343
- if (allSame) {
344
- 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
+ }
345
461
  }
346
462
  }
347
463
  return nestedSchemas;
@@ -357,6 +473,16 @@ function serializeSchematizedObject(obj, schema) {
357
473
  }
358
474
  return "{" + valueParts.join(",") + "}";
359
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
+ }
360
486
 
361
487
  // src/deserializer.ts
362
488
  function loads(s) {
@@ -409,8 +535,14 @@ function parseKeyedObject(content) {
409
535
  }
410
536
  const keysPart = parts[0];
411
537
  const { keys, count } = parseKeys(keysPart);
538
+ if (keys.length === 0 && count !== null) {
539
+ return Array.from({ length: count }, () => ({}));
540
+ }
412
541
  const schemaMap = buildSchemaMap(keys);
413
542
  const fieldNames = keys.map((k) => parseKeySchema(k).keyName);
543
+ if (keys.length === 0) {
544
+ return {};
545
+ }
414
546
  if (parts.length === 1) {
415
547
  throw new Error("Invalid object format: missing values");
416
548
  }
@@ -433,9 +565,11 @@ function parseSingleObject(fieldNames, valuesStr, schemaMap) {
433
565
  for (let i = 0; i < fieldNames.length; i++) {
434
566
  const fieldName = fieldNames[i];
435
567
  const valueStr = values[i];
436
- const schema = schemaMap[fieldName];
568
+ const schemaInfo = schemaMap[fieldName];
569
+ const schema = schemaInfo?.schema;
570
+ const count = schemaInfo?.count;
437
571
  if (schema) {
438
- obj[fieldName] = parseSchematizedValue(valueStr, schema);
572
+ obj[fieldName] = parseSchematizedValue(valueStr, schema, count);
439
573
  } else {
440
574
  obj[fieldName] = parseValue(valueStr);
441
575
  }
@@ -458,7 +592,36 @@ function parseTabularArray(fieldNames, rowParts, schemaMap, expectedCount) {
458
592
  }
459
593
  return result;
460
594
  }
461
- 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) {
462
625
  const trimmed = valueStr.trim();
463
626
  if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
464
627
  throw new Error(`Schematized value must be wrapped in braces: ${valueStr}`);
@@ -478,12 +641,14 @@ function parseSchematizedValue(valueStr, schema) {
478
641
  const obj = {};
479
642
  for (let i = 0; i < fieldNames.length; i++) {
480
643
  const fieldName = fieldNames[i];
481
- const valueStr2 = values[i];
482
- const nestedSchema = nestedSchemaMap[fieldName];
644
+ const valStr = values[i];
645
+ const schemaInfo = nestedSchemaMap[fieldName];
646
+ const nestedSchema = schemaInfo?.schema;
647
+ const nestedCount = schemaInfo?.count;
483
648
  if (nestedSchema) {
484
- obj[fieldName] = parseSchematizedValue(valueStr2, nestedSchema);
649
+ obj[fieldName] = parseSchematizedValue(valStr, nestedSchema, nestedCount);
485
650
  } else {
486
- obj[fieldName] = parseValue(valueStr2);
651
+ obj[fieldName] = parseValue(valStr);
487
652
  }
488
653
  }
489
654
  return obj;
@@ -507,20 +672,228 @@ function parseArray(text) {
507
672
  return result;
508
673
  }
509
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
+
510
872
  exports.buildSchemaMap = buildSchemaMap;
511
873
  exports.dump = dump;
512
874
  exports.dumps = dumps;
513
875
  exports.escapeString = escapeString;
514
876
  exports.formatPrimitive = formatPrimitive;
515
877
  exports.isUniformObjectArray = isUniformObjectArray;
878
+ exports.jsonToTson = jsonToTson;
516
879
  exports.load = load;
880
+ exports.loadJson = loadJson;
881
+ exports.loadTson = loadTson;
517
882
  exports.loads = loads;
518
883
  exports.looksLikeNumber = looksLikeNumber;
884
+ exports.minify = minify;
885
+ exports.minifyFile = minifyFile;
519
886
  exports.needsQuoting = needsQuoting;
520
887
  exports.parseKeySchema = parseKeySchema;
521
888
  exports.parseKeys = parseKeys;
522
889
  exports.parsePrimitive = parsePrimitive;
890
+ exports.prettify = prettify;
891
+ exports.prettifyFile = prettifyFile;
892
+ exports.readTsonString = readTsonString;
893
+ exports.saveTson = saveTson;
894
+ exports.saveTsonString = saveTsonString;
523
895
  exports.splitByDelimiter = splitByDelimiter;
896
+ exports.tsonToJson = tsonToJson;
524
897
  exports.unescapeString = unescapeString;
525
898
  //# sourceMappingURL=index.cjs.map
526
899
  //# sourceMappingURL=index.cjs.map