@jtml/core 0.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/dist/cli.js ADDED
@@ -0,0 +1,684 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli.ts
5
+ var import_fs = require("fs");
6
+
7
+ // src/core/types.ts
8
+ var JTMLError = class extends Error {
9
+ constructor(message, code) {
10
+ super(message);
11
+ this.code = code;
12
+ this.name = "JTMLError";
13
+ }
14
+ };
15
+
16
+ // src/core/schema.ts
17
+ var SchemaManager = class {
18
+ constructor() {
19
+ this.schemas = /* @__PURE__ */ new Map();
20
+ }
21
+ /**
22
+ * Register a schema for reuse
23
+ */
24
+ register(schema) {
25
+ this.schemas.set(schema.id, schema);
26
+ }
27
+ /**
28
+ * Get a registered schema
29
+ */
30
+ get(id) {
31
+ return this.schemas.get(id);
32
+ }
33
+ /**
34
+ * Check if schema exists
35
+ */
36
+ has(id) {
37
+ return this.schemas.has(id);
38
+ }
39
+ /**
40
+ * Clear all schemas
41
+ */
42
+ clear() {
43
+ this.schemas.clear();
44
+ }
45
+ /**
46
+ * Export all schemas
47
+ */
48
+ export() {
49
+ return Array.from(this.schemas.values());
50
+ }
51
+ /**
52
+ * Import schemas
53
+ */
54
+ import(schemas) {
55
+ schemas.forEach((schema) => this.register(schema));
56
+ }
57
+ };
58
+ function inferType(value) {
59
+ if (value === null || value === void 0) {
60
+ return { type: "n" };
61
+ }
62
+ if (typeof value === "boolean") {
63
+ return { type: "b" };
64
+ }
65
+ if (typeof value === "number") {
66
+ return Number.isInteger(value) ? { type: "i" } : { type: "f" };
67
+ }
68
+ if (typeof value === "string") {
69
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
70
+ return { type: "t" };
71
+ }
72
+ return { type: "s" };
73
+ }
74
+ if (Array.isArray(value)) {
75
+ if (value.length === 0) {
76
+ return { type: "a" };
77
+ }
78
+ const firstItemType = inferType(value[0]);
79
+ return { type: "a", arrayOf: firstItemType.type };
80
+ }
81
+ if (typeof value === "object") {
82
+ return { type: "o" };
83
+ }
84
+ throw new JTMLError(`Cannot infer type for value: ${value}`, "TYPE_INFERENCE_ERROR");
85
+ }
86
+ function inferSchema(data, schemaId) {
87
+ if (!Array.isArray(data) && typeof data !== "object") {
88
+ throw new JTMLError("Schema inference requires array or object data", "INVALID_DATA");
89
+ }
90
+ const fields = [];
91
+ const sample = Array.isArray(data) ? data[0] : data;
92
+ if (!sample || typeof sample !== "object") {
93
+ throw new JTMLError("Cannot infer schema from empty or non-object data", "INVALID_DATA");
94
+ }
95
+ for (const [key, value] of Object.entries(sample)) {
96
+ const typeInfo = inferType(value);
97
+ let optional = false;
98
+ if (Array.isArray(data)) {
99
+ optional = data.some(
100
+ (item) => item[key] === null || item[key] === void 0
101
+ );
102
+ }
103
+ fields.push({
104
+ name: key,
105
+ typeInfo: { ...typeInfo, optional }
106
+ });
107
+ }
108
+ return {
109
+ id: schemaId,
110
+ fields,
111
+ version: "1.0"
112
+ };
113
+ }
114
+ function serializeSchema(schema) {
115
+ const fieldDefs = schema.fields.map((field) => {
116
+ let def = `${field.name}:${field.typeInfo.type}`;
117
+ if (field.typeInfo.arrayOf) {
118
+ def += `[]`;
119
+ }
120
+ if (field.typeInfo.enumValues) {
121
+ def += `[${field.typeInfo.enumValues.join(",")}]`;
122
+ }
123
+ if (field.typeInfo.refSchema) {
124
+ def += `[${field.typeInfo.refSchema}]`;
125
+ }
126
+ if (field.typeInfo.optional) {
127
+ def += "?";
128
+ }
129
+ return def;
130
+ }).join(" ");
131
+ return `@schema ${schema.id}
132
+ ${fieldDefs}`;
133
+ }
134
+ function parseSchema(schemaStr) {
135
+ const lines = schemaStr.trim().split("\n");
136
+ const headerMatch = lines[0].match(/^@schema\s+(\S+)/);
137
+ if (!headerMatch) {
138
+ throw new JTMLError("Invalid schema format", "SCHEMA_PARSE_ERROR");
139
+ }
140
+ const schemaId = headerMatch[1];
141
+ if (lines.length < 2 || !lines[1]) {
142
+ throw new JTMLError("Schema is missing field definitions", "SCHEMA_PARSE_ERROR");
143
+ }
144
+ const fieldLine = lines[1];
145
+ const fields = [];
146
+ const fieldDefs = fieldLine.split(/\s+/);
147
+ for (const fieldDef of fieldDefs) {
148
+ const match = fieldDef.match(/^(\w+):([ifsbtnoae]+)(\[\])?(\[([^\]]+)\])?(\?)?$/);
149
+ if (!match) {
150
+ throw new JTMLError(`Invalid field definition: ${fieldDef}`, "SCHEMA_PARSE_ERROR");
151
+ }
152
+ const [, name, type, isArray, , enumOrRef, isOptional] = match;
153
+ const typeInfo = {
154
+ type,
155
+ optional: !!isOptional
156
+ };
157
+ if (isArray) {
158
+ typeInfo.arrayOf = type;
159
+ }
160
+ if (enumOrRef) {
161
+ if (type === "e") {
162
+ typeInfo.enumValues = enumOrRef.split(",");
163
+ } else if (type === "ref") {
164
+ typeInfo.refSchema = enumOrRef;
165
+ }
166
+ }
167
+ fields.push({ name, typeInfo });
168
+ }
169
+ return {
170
+ id: schemaId,
171
+ fields,
172
+ version: "1.0"
173
+ };
174
+ }
175
+ var schemaManager = new SchemaManager();
176
+
177
+ // src/core/encoder.ts
178
+ var JTMLEncoder = class {
179
+ /**
180
+ * Encode JSON data to JTML format
181
+ */
182
+ encode(data, options = {}) {
183
+ const {
184
+ schemaId = "default",
185
+ schemaRef,
186
+ autoInferTypes = true,
187
+ includeSchema = true
188
+ } = options;
189
+ let schema;
190
+ if (schemaRef) {
191
+ schema = schemaManager.get(schemaRef);
192
+ if (!schema) {
193
+ throw new JTMLError(`Schema not found: ${schemaRef}`, "SCHEMA_NOT_FOUND");
194
+ }
195
+ return this.encodeWithSchema(data, schema, false);
196
+ }
197
+ if (autoInferTypes) {
198
+ schema = inferSchema(data, schemaId);
199
+ schemaManager.register(schema);
200
+ }
201
+ if (schema) {
202
+ return this.encodeWithSchema(data, schema, includeSchema);
203
+ }
204
+ return this.encodeSimple(data);
205
+ }
206
+ /**
207
+ * Encode with explicit schema
208
+ */
209
+ encodeWithSchema(data, schema, includeSchema) {
210
+ const parts = [];
211
+ if (includeSchema) {
212
+ parts.push(serializeSchema(schema));
213
+ parts.push("");
214
+ parts.push("@data");
215
+ } else {
216
+ parts.push(`@ref ${schema.id}`);
217
+ parts.push("@data");
218
+ }
219
+ if (Array.isArray(data)) {
220
+ parts.push("@array");
221
+ for (const item of data) {
222
+ parts.push(this.encodeRow(item, schema));
223
+ }
224
+ } else if (typeof data === "object" && data !== null) {
225
+ parts.push(this.encodeRow(data, schema));
226
+ }
227
+ return parts.join("\n");
228
+ }
229
+ /**
230
+ * Encode a single row according to schema
231
+ */
232
+ encodeRow(item, schema) {
233
+ if (typeof item !== "object" || item === null) {
234
+ throw new JTMLError("Cannot encode non-object item", "INVALID_DATA");
235
+ }
236
+ const values = [];
237
+ const obj = item;
238
+ for (const field of schema.fields) {
239
+ const value = obj[field.name];
240
+ values.push(this.encodeValue(value));
241
+ }
242
+ return values.join("|");
243
+ }
244
+ /**
245
+ * Encode a single value
246
+ */
247
+ encodeValue(value) {
248
+ if (value === null || value === void 0) {
249
+ return "";
250
+ }
251
+ if (typeof value === "boolean") {
252
+ return value ? "1" : "0";
253
+ }
254
+ if (typeof value === "number") {
255
+ return String(value);
256
+ }
257
+ if (typeof value === "string") {
258
+ return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\n/g, "\\n");
259
+ }
260
+ if (Array.isArray(value)) {
261
+ return `[${value.map((v) => this.encodeValue(v)).join(",")}]`;
262
+ }
263
+ if (typeof value === "object") {
264
+ return `{${Object.entries(value).map(([k, v]) => `${k}:${this.encodeValue(v)}`).join(",")}}`;
265
+ }
266
+ return String(value);
267
+ }
268
+ /**
269
+ * Simple encoding without schema (fallback)
270
+ */
271
+ encodeSimple(data) {
272
+ if (Array.isArray(data)) {
273
+ return data.map((item) => JSON.stringify(item)).join("\n");
274
+ }
275
+ return JSON.stringify(data);
276
+ }
277
+ /**
278
+ * Encode with metadata
279
+ */
280
+ encodeWithMetadata(data, metadata, options = {}) {
281
+ const dataEncoded = this.encode(data, options);
282
+ const metaParts = ["", "@meta"];
283
+ for (const [key, value] of Object.entries(metadata)) {
284
+ metaParts.push(`${key}:${this.encodeValue(value)}`);
285
+ }
286
+ return dataEncoded + "\n" + metaParts.join("\n");
287
+ }
288
+ };
289
+ var encoder = new JTMLEncoder();
290
+ function encode(data, options) {
291
+ return encoder.encode(data, options);
292
+ }
293
+
294
+ // src/core/decoder.ts
295
+ var JTMLDecoder = class {
296
+ /**
297
+ * Decode JTML format to JSON
298
+ */
299
+ decode(jtml, options = {}) {
300
+ const { schemaCache, strict = true } = options;
301
+ if (schemaCache) {
302
+ schemaCache.forEach((schema2, _id) => {
303
+ schemaManager.register(schema2);
304
+ });
305
+ }
306
+ const lines = jtml.trim().split("\n");
307
+ let schema;
308
+ let dataStartIndex = 0;
309
+ let metadata = {};
310
+ for (let i = 0; i < lines.length; i++) {
311
+ const line = lines[i].trim();
312
+ if (line.startsWith("@schema")) {
313
+ if (i + 1 >= lines.length) {
314
+ throw new JTMLError("Incomplete schema definition: missing field definitions", "SCHEMA_PARSE_ERROR");
315
+ }
316
+ const schemaLines = [line, lines[i + 1]];
317
+ schema = parseSchema(schemaLines.join("\n"));
318
+ schemaManager.register(schema);
319
+ i++;
320
+ } else if (line.startsWith("@ref")) {
321
+ const schemaId = line.split(/\s+/)[1];
322
+ if (!schemaId) {
323
+ throw new JTMLError("Missing schema ID in @ref directive", "SCHEMA_PARSE_ERROR");
324
+ }
325
+ schema = schemaManager.get(schemaId);
326
+ if (!schema) {
327
+ throw new JTMLError(`Schema not found: ${schemaId}`, "SCHEMA_NOT_FOUND");
328
+ }
329
+ } else if (line === "@data") {
330
+ dataStartIndex = i + 1;
331
+ break;
332
+ }
333
+ }
334
+ if (!schema && strict) {
335
+ throw new JTMLError("No schema found in JTML data", "SCHEMA_REQUIRED");
336
+ }
337
+ let metaStartIndex = -1;
338
+ for (let i = dataStartIndex; i < lines.length; i++) {
339
+ if (lines[i].trim() === "@meta") {
340
+ metaStartIndex = i + 1;
341
+ break;
342
+ }
343
+ }
344
+ const dataEndIndex = metaStartIndex > 0 ? metaStartIndex - 1 : lines.length;
345
+ const rawDataLines = lines.slice(dataStartIndex, dataEndIndex).filter((l) => l.trim());
346
+ const isArrayEncoding = rawDataLines[0]?.trim() === "@array";
347
+ const dataLines = isArrayEncoding ? rawDataLines.slice(1) : rawDataLines;
348
+ const results = [];
349
+ if (schema) {
350
+ for (const line of dataLines) {
351
+ if (line.trim()) {
352
+ results.push(this.decodeRow(line, schema));
353
+ }
354
+ }
355
+ } else {
356
+ for (const line of dataLines) {
357
+ if (line.trim()) {
358
+ results.push(JSON.parse(line));
359
+ }
360
+ }
361
+ }
362
+ if (metaStartIndex > 0) {
363
+ for (let i = metaStartIndex; i < lines.length; i++) {
364
+ const line = lines[i].trim();
365
+ if (line && !line.startsWith("@")) {
366
+ const colonIdx = line.indexOf(":");
367
+ if (colonIdx === -1) continue;
368
+ const key = line.slice(0, colonIdx);
369
+ const value = line.slice(colonIdx + 1);
370
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
371
+ metadata[key] = this.decodeValue(value);
372
+ }
373
+ }
374
+ }
375
+ if (!isArrayEncoding && results.length === 1 && Object.keys(metadata).length === 0) {
376
+ return results[0];
377
+ }
378
+ if (Object.keys(metadata).length > 0) {
379
+ return {
380
+ data: results.length === 1 ? results[0] : results,
381
+ metadata
382
+ };
383
+ }
384
+ return results;
385
+ }
386
+ /**
387
+ * Decode a single row according to schema
388
+ */
389
+ decodeRow(line, schema) {
390
+ const values = this.splitRow(line);
391
+ const result = {};
392
+ if (values.length !== schema.fields.length) {
393
+ throw new JTMLError(
394
+ `Row has ${values.length} values but schema expects ${schema.fields.length}`,
395
+ "SCHEMA_MISMATCH"
396
+ );
397
+ }
398
+ for (let i = 0; i < schema.fields.length; i++) {
399
+ const field = schema.fields[i];
400
+ const rawValue = values[i];
401
+ if (rawValue === "" || rawValue === null) {
402
+ result[field.name] = null;
403
+ } else {
404
+ result[field.name] = this.decodeValue(rawValue, field.typeInfo.type);
405
+ }
406
+ }
407
+ return result;
408
+ }
409
+ /**
410
+ * Split row by pipe delimiter, handling escaped pipes
411
+ */
412
+ splitRow(line) {
413
+ const parts = [];
414
+ let current = "";
415
+ let escaped = false;
416
+ for (let i = 0; i < line.length; i++) {
417
+ const char = line[i];
418
+ if (escaped) {
419
+ if (char === "n") {
420
+ current += "\n";
421
+ } else if (char === "\\") {
422
+ current += "\\";
423
+ } else if (char === "|") {
424
+ current += "|";
425
+ } else {
426
+ current += char;
427
+ }
428
+ escaped = false;
429
+ } else if (char === "\\") {
430
+ escaped = true;
431
+ } else if (char === "|") {
432
+ parts.push(current);
433
+ current = "";
434
+ } else {
435
+ current += char;
436
+ }
437
+ }
438
+ parts.push(current);
439
+ return parts;
440
+ }
441
+ /**
442
+ * Decode a single value
443
+ */
444
+ decodeValue(value, type) {
445
+ if (value === void 0 || value === "" || value === null) {
446
+ return null;
447
+ }
448
+ if (type === "b") {
449
+ return value === "1" || value === "true";
450
+ }
451
+ if (type === "i" || type === "f") {
452
+ const num = type === "i" ? parseInt(value, 10) : parseFloat(value);
453
+ if (!isFinite(num)) {
454
+ throw new JTMLError(`Invalid numeric value: ${value}`, "INVALID_VALUE");
455
+ }
456
+ return num;
457
+ }
458
+ if (!type && /^-?\d+(\.\d+)?$/.test(value)) {
459
+ return value.includes(".") ? parseFloat(value) : parseInt(value, 10);
460
+ }
461
+ if (value.startsWith("[") && value.endsWith("]")) {
462
+ const inner = value.slice(1, -1);
463
+ if (!inner) return [];
464
+ return inner.split(",").map((v) => this.decodeValue(v.trim(), type));
465
+ }
466
+ if (value.startsWith("{") && value.endsWith("}")) {
467
+ const inner = value.slice(1, -1);
468
+ const obj = /* @__PURE__ */ Object.create(null);
469
+ if (!inner) return obj;
470
+ const pairs = inner.split(",");
471
+ for (const pair of pairs) {
472
+ const colonIdx = pair.indexOf(":");
473
+ if (colonIdx === -1) continue;
474
+ const k = pair.slice(0, colonIdx).trim();
475
+ const v = pair.slice(colonIdx + 1).trim();
476
+ if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
477
+ obj[k] = this.decodeValue(v);
478
+ }
479
+ return obj;
480
+ }
481
+ return value;
482
+ }
483
+ };
484
+ var decoder = new JTMLDecoder();
485
+ function decode(jtml, options) {
486
+ return decoder.decode(jtml, options);
487
+ }
488
+
489
+ // src/utils/tokenizer.ts
490
+ function estimateTokens(text, tokenizer = "claude") {
491
+ const normalized = text.trim().replace(/\s+/g, " ");
492
+ const charsPerToken = {
493
+ gpt: 4,
494
+ claude: 3.8,
495
+ llama: 4.2
496
+ };
497
+ const ratio = charsPerToken[tokenizer];
498
+ let estimate = normalized.length / ratio;
499
+ const structuralChars = (normalized.match(/[{}[\]":,]/g) || []).length;
500
+ estimate += structuralChars * 0.3;
501
+ const numbers = (normalized.match(/\d+/g) || []).length;
502
+ estimate += numbers * 0.2;
503
+ return Math.ceil(estimate);
504
+ }
505
+ function compareTokens(jsonText, jtmlText, tokenizer = "claude") {
506
+ const jsonTokens = estimateTokens(jsonText, tokenizer);
507
+ const jtmlTokens = estimateTokens(jtmlText, tokenizer);
508
+ const savings = jsonTokens - jtmlTokens;
509
+ const savingsPercent = jsonTokens > 0 ? savings / jsonTokens * 100 : 0;
510
+ return {
511
+ jsonTokens,
512
+ jtmlTokens,
513
+ savings,
514
+ savingsPercent
515
+ };
516
+ }
517
+ function formatTokenStats(stats) {
518
+ return `
519
+ Token Comparison:
520
+ JSON: ${stats.jsonTokens} tokens
521
+ JTML: ${stats.jtmlTokens} tokens
522
+ Savings: ${stats.savings} tokens (${stats.savingsPercent.toFixed(2)}%)
523
+ `.trim();
524
+ }
525
+
526
+ // src/cli.ts
527
+ var args = process.argv.slice(2);
528
+ function printUsage() {
529
+ console.log(`
530
+ JTML - JSON Token-Minimized Language
531
+
532
+ Usage:
533
+ jtml encode <input.json> [output.jtml] Convert JSON to JTML
534
+ jtml decode <input.jtml> [output.json] Convert JTML to JSON
535
+ jtml compare <input.json> Compare token efficiency
536
+ jtml validate <input.jtml> Validate JTML format
537
+ jtml schema <input.json> Generate schema only
538
+
539
+ Options:
540
+ -h, --help Show this help message
541
+ -v, --version Show version
542
+ --schema-id <id> Set schema ID
543
+ --no-schema Encode without schema
544
+ --tokenizer <gpt|claude|llama> Set tokenizer for comparison
545
+
546
+ Examples:
547
+ jtml encode api-response.json # Output to stdout
548
+ jtml encode data.json output.jtml # Save to file
549
+ jtml decode data.jtml # Output to stdout
550
+ jtml compare large-api.json # Show token savings
551
+ `);
552
+ }
553
+ function getVersion() {
554
+ try {
555
+ const pkg = JSON.parse((0, import_fs.readFileSync)("./package.json", "utf-8"));
556
+ return pkg.version;
557
+ } catch {
558
+ return "unknown";
559
+ }
560
+ }
561
+ function main() {
562
+ if (args.length === 0 || args.includes("-h") || args.includes("--help")) {
563
+ printUsage();
564
+ process.exit(0);
565
+ }
566
+ if (args.includes("-v") || args.includes("--version")) {
567
+ console.log(`jtml v${getVersion()}`);
568
+ process.exit(0);
569
+ }
570
+ const command = args[0];
571
+ const inputFile = args[1];
572
+ const outputFile = args[2];
573
+ const schemaIdIndex = args.indexOf("--schema-id");
574
+ const schemaId = schemaIdIndex >= 0 ? args[schemaIdIndex + 1] : void 0;
575
+ const noSchema = args.includes("--no-schema");
576
+ const tokenizerIndex = args.indexOf("--tokenizer");
577
+ const tokenizer = tokenizerIndex >= 0 ? args[tokenizerIndex + 1] : "claude";
578
+ try {
579
+ switch (command) {
580
+ case "encode": {
581
+ if (!inputFile) {
582
+ console.error("Error: Input file required");
583
+ printUsage();
584
+ process.exit(1);
585
+ }
586
+ const jsonText = (0, import_fs.readFileSync)(inputFile, "utf-8");
587
+ const data = JSON.parse(jsonText);
588
+ const jtml = encode(data, {
589
+ schemaId,
590
+ includeSchema: !noSchema,
591
+ autoInferTypes: true
592
+ });
593
+ if (outputFile) {
594
+ (0, import_fs.writeFileSync)(outputFile, jtml, "utf-8");
595
+ console.log(`\u2713 Encoded to ${outputFile}`);
596
+ const stats = compareTokens(jsonText, jtml, tokenizer);
597
+ console.log(formatTokenStats(stats));
598
+ } else {
599
+ console.log(jtml);
600
+ }
601
+ break;
602
+ }
603
+ case "decode": {
604
+ if (!inputFile) {
605
+ console.error("Error: Input file required");
606
+ printUsage();
607
+ process.exit(1);
608
+ }
609
+ const jtml = (0, import_fs.readFileSync)(inputFile, "utf-8");
610
+ const data = decode(jtml);
611
+ const jsonText = JSON.stringify(data, null, 2);
612
+ if (outputFile) {
613
+ (0, import_fs.writeFileSync)(outputFile, jsonText, "utf-8");
614
+ console.log(`\u2713 Decoded to ${outputFile}`);
615
+ } else {
616
+ console.log(jsonText);
617
+ }
618
+ break;
619
+ }
620
+ case "compare": {
621
+ if (!inputFile) {
622
+ console.error("Error: Input file required");
623
+ printUsage();
624
+ process.exit(1);
625
+ }
626
+ const jsonText = (0, import_fs.readFileSync)(inputFile, "utf-8");
627
+ const data = JSON.parse(jsonText);
628
+ const jtml = encode(data);
629
+ const stats = compareTokens(jsonText, jtml, tokenizer);
630
+ console.log("\n=== Token Efficiency Comparison ===\n");
631
+ console.log(formatTokenStats(stats));
632
+ console.log(`
633
+ Tokenizer: ${tokenizer}`);
634
+ console.log(`JSON size: ${jsonText.length} chars`);
635
+ console.log(`JTML size: ${jtml.length} chars`);
636
+ console.log(`Compression: ${((jsonText.length - jtml.length) / jsonText.length * 100).toFixed(2)}%`);
637
+ break;
638
+ }
639
+ case "validate": {
640
+ if (!inputFile) {
641
+ console.error("Error: Input file required");
642
+ printUsage();
643
+ process.exit(1);
644
+ }
645
+ const jtml = (0, import_fs.readFileSync)(inputFile, "utf-8");
646
+ try {
647
+ decode(jtml);
648
+ console.log("\u2713 Valid JTML format");
649
+ } catch (error) {
650
+ console.error("\u2717 Invalid JTML format");
651
+ if (error instanceof Error) {
652
+ console.error(` ${error.message}`);
653
+ }
654
+ process.exit(1);
655
+ }
656
+ break;
657
+ }
658
+ case "schema": {
659
+ if (!inputFile) {
660
+ console.error("Error: Input file required");
661
+ printUsage();
662
+ process.exit(1);
663
+ }
664
+ const jsonText = (0, import_fs.readFileSync)(inputFile, "utf-8");
665
+ const data = JSON.parse(jsonText);
666
+ const jtml = encode(data, {
667
+ schemaId: schemaId || "generated",
668
+ includeSchema: true
669
+ });
670
+ const schemaOnly = jtml.split("@data")[0].trim();
671
+ console.log(schemaOnly);
672
+ break;
673
+ }
674
+ default:
675
+ console.error(`Error: Unknown command '${command}'`);
676
+ printUsage();
677
+ process.exit(1);
678
+ }
679
+ } catch (error) {
680
+ console.error("Error:", error instanceof Error ? error.message : String(error));
681
+ process.exit(1);
682
+ }
683
+ }
684
+ main();