@q1k-oss/mint-format 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,480 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ decode: () => decode,
24
+ default: () => index_default,
25
+ encode: () => encode,
26
+ estimateTokens: () => estimateTokens,
27
+ validate: () => validate
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var STATUS_SYMBOLS = {
31
+ completed: "\u2713",
32
+ complete: "\u2713",
33
+ success: "\u2713",
34
+ done: "\u2713",
35
+ passed: "\u2713",
36
+ true: "\u2713",
37
+ yes: "\u2713",
38
+ failed: "\u2717",
39
+ failure: "\u2717",
40
+ error: "\u2717",
41
+ rejected: "\u2717",
42
+ false: "\u2717",
43
+ no: "\u2717",
44
+ pending: "\u23F3",
45
+ waiting: "\u23F3",
46
+ in_progress: "\u23F3",
47
+ running: "\u23F3",
48
+ warning: "\u26A0",
49
+ warn: "\u26A0",
50
+ review: "?",
51
+ unknown: "?"
52
+ };
53
+ var REVERSE_SYMBOLS = {
54
+ "\u2713": "true",
55
+ "\u2717": "false",
56
+ "\u23F3": "pending",
57
+ "\u26A0": "warning",
58
+ "?": "unknown"
59
+ };
60
+ function needsQuoting(value) {
61
+ if (value === "") return true;
62
+ if (value.startsWith(" ") || value.endsWith(" ")) return true;
63
+ if (value.includes("|") || value.includes("\n") || value.includes("\r")) return true;
64
+ if (value.includes(",")) return true;
65
+ if (/^-?\d+\.?\d*$/.test(value)) return true;
66
+ if (["true", "false", "null"].includes(value.toLowerCase())) return true;
67
+ if (value.includes(":") && !value.includes("://")) return true;
68
+ if (value.includes('"')) return true;
69
+ return false;
70
+ }
71
+ function escapeString(value) {
72
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
73
+ }
74
+ function unescapeString(value) {
75
+ return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
76
+ }
77
+ function formatPrimitive(value, options, inTable = false) {
78
+ if (value === null || value === void 0) {
79
+ return inTable ? "-" : "null";
80
+ }
81
+ if (typeof value === "boolean") {
82
+ return String(value);
83
+ }
84
+ if (typeof value === "number") {
85
+ if (!isFinite(value)) return "null";
86
+ return String(value);
87
+ }
88
+ if (typeof value === "string") {
89
+ if (options.compact && STATUS_SYMBOLS[value.toLowerCase()]) {
90
+ return STATUS_SYMBOLS[value.toLowerCase()];
91
+ }
92
+ if (value === "" && inTable) return "-";
93
+ if (needsQuoting(value)) {
94
+ return `"${escapeString(value)}"`;
95
+ }
96
+ return value;
97
+ }
98
+ return String(value);
99
+ }
100
+ function isPrimitive(value) {
101
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
102
+ }
103
+ function isTableArray(arr) {
104
+ if (arr.length === 0) return false;
105
+ if (!arr.every((item) => item !== null && typeof item === "object" && !Array.isArray(item))) {
106
+ return false;
107
+ }
108
+ const firstKeys = Object.keys(arr[0]).sort().join(",");
109
+ const sameKeys = arr.every((item) => {
110
+ const keys = Object.keys(item).sort().join(",");
111
+ return keys === firstKeys;
112
+ });
113
+ if (!sameKeys) return false;
114
+ return arr.every((item) => {
115
+ const obj = item;
116
+ return Object.values(obj).every((val) => isPrimitive(val));
117
+ });
118
+ }
119
+ function isPrimitiveArray(arr) {
120
+ return arr.every((item) => isPrimitive(item));
121
+ }
122
+ function getColumnWidths(headers, rows) {
123
+ const widths = headers.map((h) => h.length);
124
+ for (const row of rows) {
125
+ for (let i = 0; i < row.length; i++) {
126
+ widths[i] = Math.max(widths[i] || 0, row[i]?.length || 0);
127
+ }
128
+ }
129
+ return widths;
130
+ }
131
+ function padCell(value, width) {
132
+ return value + " ".repeat(Math.max(0, width - value.length));
133
+ }
134
+ function encodeTable(arr, options, indentLevel) {
135
+ if (arr.length === 0) return "| |";
136
+ const indent = " ".repeat(options.indent || 2);
137
+ const baseIndent = indent.repeat(indentLevel);
138
+ const headers = Object.keys(arr[0]);
139
+ const rows = arr.map((obj) => headers.map((h) => formatPrimitive(obj[h], options, true)));
140
+ const widths = getColumnWidths(headers, rows);
141
+ const lines = [];
142
+ const headerCells = headers.map((h, i) => padCell(h, widths[i]));
143
+ lines.push(`${baseIndent}| ${headerCells.join(" | ")} |`);
144
+ for (const row of rows) {
145
+ const cells = row.map((cell, i) => padCell(cell, widths[i]));
146
+ lines.push(`${baseIndent}| ${cells.join(" | ")} |`);
147
+ }
148
+ return lines.join("\n");
149
+ }
150
+ function encodeValue(value, options, indentLevel) {
151
+ const indent = " ".repeat(options.indent || 2);
152
+ const baseIndent = indent.repeat(indentLevel);
153
+ if (value === null || typeof value !== "object") {
154
+ return formatPrimitive(value, options);
155
+ }
156
+ if (Array.isArray(value)) {
157
+ if (value.length === 0) {
158
+ return "[]";
159
+ }
160
+ if (isPrimitiveArray(value)) {
161
+ return value.map((v) => formatPrimitive(v, options)).join(", ");
162
+ }
163
+ if (isTableArray(value)) {
164
+ return "\n" + encodeTable(value, options, indentLevel + 1);
165
+ }
166
+ const lines2 = [];
167
+ for (const item of value) {
168
+ if (item === null || typeof item !== "object") {
169
+ lines2.push(`${baseIndent}${indent}- ${formatPrimitive(item, options)}`);
170
+ } else {
171
+ const encoded = encodeValue(item, options, indentLevel + 2);
172
+ if (encoded.includes("\n")) {
173
+ lines2.push(`${baseIndent}${indent}-`);
174
+ lines2.push(encoded);
175
+ } else {
176
+ lines2.push(`${baseIndent}${indent}- ${encoded}`);
177
+ }
178
+ }
179
+ }
180
+ return "\n" + lines2.join("\n");
181
+ }
182
+ const obj = value;
183
+ const keys = options.sortKeys ? Object.keys(obj).sort() : Object.keys(obj);
184
+ if (keys.length === 0) {
185
+ return "";
186
+ }
187
+ const lines = [];
188
+ for (const key of keys) {
189
+ const val = obj[key];
190
+ if (val === null) {
191
+ lines.push(`${baseIndent}${key}: null`);
192
+ } else if (typeof val !== "object") {
193
+ lines.push(`${baseIndent}${key}: ${formatPrimitive(val, options)}`);
194
+ } else if (Array.isArray(val)) {
195
+ if (val.length === 0) {
196
+ lines.push(`${baseIndent}${key}: []`);
197
+ } else if (isPrimitiveArray(val)) {
198
+ lines.push(`${baseIndent}${key}: ${val.map((v) => formatPrimitive(v, options)).join(", ")}`);
199
+ } else {
200
+ lines.push(`${baseIndent}${key}:${encodeValue(val, options, indentLevel)}`);
201
+ }
202
+ } else {
203
+ const nested = encodeValue(val, options, indentLevel + 1);
204
+ if (nested === "") {
205
+ lines.push(`${baseIndent}${key}:`);
206
+ } else if (nested.includes("\n")) {
207
+ lines.push(`${baseIndent}${key}:`);
208
+ lines.push(nested);
209
+ } else {
210
+ lines.push(`${baseIndent}${key}: ${nested}`);
211
+ }
212
+ }
213
+ }
214
+ return lines.join("\n");
215
+ }
216
+ function encode(value, options = {}) {
217
+ const opts = {
218
+ indent: 2,
219
+ compact: false,
220
+ sortKeys: false,
221
+ ...options
222
+ };
223
+ if (value === null || value === void 0) {
224
+ return "null";
225
+ }
226
+ if (typeof value !== "object") {
227
+ return formatPrimitive(value, opts);
228
+ }
229
+ if (Array.isArray(value)) {
230
+ if (value.length === 0) return "_: []";
231
+ if (isPrimitiveArray(value)) {
232
+ return `_: ${value.map((v) => formatPrimitive(v, opts)).join(", ")}`;
233
+ }
234
+ if (isTableArray(value)) {
235
+ return `_:
236
+ ${encodeTable(value, opts, 1)}`;
237
+ }
238
+ return `_:${encodeValue(value, opts, 0)}`;
239
+ }
240
+ return encodeValue(value, opts, 0);
241
+ }
242
+ function parsePrimitive(value) {
243
+ const trimmed = value.trim();
244
+ if (trimmed === "null" || trimmed === "-" || trimmed === "") {
245
+ return null;
246
+ }
247
+ if (trimmed === "true") return true;
248
+ if (trimmed === "false") return false;
249
+ if (REVERSE_SYMBOLS[trimmed]) {
250
+ return REVERSE_SYMBOLS[trimmed];
251
+ }
252
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
253
+ return unescapeString(trimmed.slice(1, -1));
254
+ }
255
+ if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
256
+ const num = Number(trimmed);
257
+ if (isFinite(num)) return num;
258
+ }
259
+ return trimmed;
260
+ }
261
+ function parseTable(lines, startIndex, indent) {
262
+ const result = [];
263
+ let headers = [];
264
+ let i = startIndex;
265
+ const headerLine = lines[i].trim();
266
+ if (!headerLine.startsWith("|")) {
267
+ return { value: [], endIndex: startIndex };
268
+ }
269
+ headers = headerLine.split("|").slice(1, -1).map((h) => h.trim());
270
+ i++;
271
+ while (i < lines.length) {
272
+ const line = lines[i];
273
+ const lineIndent = line.length - line.trimStart().length;
274
+ const trimmed = line.trim();
275
+ if (!trimmed.startsWith("|")) {
276
+ break;
277
+ }
278
+ if (lineIndent < indent && trimmed !== "") {
279
+ break;
280
+ }
281
+ const cells = trimmed.split("|").slice(1, -1).map((c) => parsePrimitive(c.trim()));
282
+ if (cells.length === headers.length) {
283
+ const row = {};
284
+ headers.forEach((h, idx) => {
285
+ row[h] = cells[idx];
286
+ });
287
+ result.push(row);
288
+ }
289
+ i++;
290
+ }
291
+ return { value: result, endIndex: i - 1 };
292
+ }
293
+ function parseDocument(lines, startIndex, baseIndent, options) {
294
+ const result = {};
295
+ let i = startIndex;
296
+ while (i < lines.length) {
297
+ const line = lines[i];
298
+ if (line.trim() === "" || line.trim().startsWith("#")) {
299
+ i++;
300
+ continue;
301
+ }
302
+ const lineIndent = line.length - line.trimStart().length;
303
+ if (lineIndent < baseIndent) {
304
+ break;
305
+ }
306
+ if (lineIndent > baseIndent) {
307
+ i++;
308
+ continue;
309
+ }
310
+ const trimmed = line.trim();
311
+ if (trimmed.startsWith("|")) {
312
+ i++;
313
+ continue;
314
+ }
315
+ const colonIndex = trimmed.indexOf(":");
316
+ if (colonIndex === -1) {
317
+ i++;
318
+ continue;
319
+ }
320
+ const key = trimmed.slice(0, colonIndex).trim();
321
+ const valueStr = trimmed.slice(colonIndex + 1).trim();
322
+ if (valueStr === "" || valueStr === "[]") {
323
+ let foundNested = false;
324
+ let nextIdx = i + 1;
325
+ while (nextIdx < lines.length && lines[nextIdx].trim() === "") {
326
+ nextIdx++;
327
+ }
328
+ if (nextIdx < lines.length) {
329
+ const nextLine = lines[nextIdx];
330
+ const nextIndent = nextLine.length - nextLine.trimStart().length;
331
+ const nextTrimmed = nextLine.trim();
332
+ if (nextIndent > baseIndent && nextTrimmed.startsWith("|")) {
333
+ const tableResult = parseTable(lines, nextIdx, nextIndent);
334
+ result[key] = tableResult.value;
335
+ i = tableResult.endIndex + 1;
336
+ foundNested = true;
337
+ } else if (nextIndent > baseIndent && nextTrimmed !== "" && !nextTrimmed.startsWith("#")) {
338
+ const nestedResult = parseDocument(lines, nextIdx, nextIndent, options);
339
+ result[key] = nestedResult.value;
340
+ i = nestedResult.endIndex;
341
+ foundNested = true;
342
+ }
343
+ }
344
+ if (!foundNested) {
345
+ result[key] = valueStr === "[]" ? [] : {};
346
+ i++;
347
+ }
348
+ } else if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
349
+ result[key] = parsePrimitive(valueStr);
350
+ i++;
351
+ } else if (valueStr.includes(" | ")) {
352
+ result[key] = valueStr.split(" | ").map((v) => parsePrimitive(v.trim()));
353
+ i++;
354
+ } else if (valueStr.includes(", ")) {
355
+ result[key] = valueStr.split(", ").map((v) => parsePrimitive(v.trim()));
356
+ i++;
357
+ } else {
358
+ result[key] = parsePrimitive(valueStr);
359
+ i++;
360
+ }
361
+ }
362
+ return { value: result, endIndex: i };
363
+ }
364
+ function decode(input, options = {}) {
365
+ const opts = {
366
+ strict: true,
367
+ indent: 2,
368
+ ...options
369
+ };
370
+ const normalized = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
371
+ const lines = normalized.split("\n");
372
+ if (lines.every((l) => l.trim() === "" || l.trim().startsWith("#"))) {
373
+ return {};
374
+ }
375
+ let startIndex = 0;
376
+ while (startIndex < lines.length && (lines[startIndex].trim() === "" || lines[startIndex].trim().startsWith("#"))) {
377
+ startIndex++;
378
+ }
379
+ if (startIndex >= lines.length) {
380
+ return {};
381
+ }
382
+ const firstLine = lines[startIndex].trim();
383
+ if (firstLine.startsWith("|")) {
384
+ const tableResult = parseTable(lines, startIndex, 0);
385
+ return tableResult.value;
386
+ }
387
+ if (firstLine.startsWith("_:")) {
388
+ const valueStr = firstLine.slice(2).trim();
389
+ if (valueStr === "" || valueStr === "[]") {
390
+ const nextLine = lines[startIndex + 1];
391
+ if (nextLine && nextLine.trim().startsWith("|")) {
392
+ const tableResult = parseTable(lines, startIndex + 1, 2);
393
+ return tableResult.value;
394
+ }
395
+ return [];
396
+ }
397
+ if (valueStr.includes(", ")) {
398
+ return valueStr.split(", ").map((v) => parsePrimitive(v.trim()));
399
+ }
400
+ return [parsePrimitive(valueStr)];
401
+ }
402
+ const result = parseDocument(lines, startIndex, 0, opts);
403
+ return result.value;
404
+ }
405
+ function validate(input) {
406
+ const errors = [];
407
+ const lines = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
408
+ let inTable = false;
409
+ let tableColumns = 0;
410
+ for (let i = 0; i < lines.length; i++) {
411
+ const line = lines[i];
412
+ const lineNum = i + 1;
413
+ const trimmed = line.trim();
414
+ if (trimmed === "" || trimmed.startsWith("#")) {
415
+ continue;
416
+ }
417
+ const indent = line.length - line.trimStart().length;
418
+ if (indent % 2 !== 0) {
419
+ errors.push({
420
+ line: lineNum,
421
+ column: 1,
422
+ message: `Inconsistent indentation: ${indent} spaces (should be multiple of 2)`,
423
+ context: line
424
+ });
425
+ }
426
+ if (trimmed.startsWith("|")) {
427
+ const pipes = trimmed.split("|").length - 1;
428
+ if (!inTable) {
429
+ inTable = true;
430
+ tableColumns = pipes;
431
+ } else {
432
+ if (pipes !== tableColumns) {
433
+ errors.push({
434
+ line: lineNum,
435
+ column: 1,
436
+ message: `Table column mismatch: expected ${tableColumns - 1} columns, got ${pipes - 1}`,
437
+ context: line
438
+ });
439
+ }
440
+ }
441
+ if (!trimmed.endsWith("|")) {
442
+ errors.push({
443
+ line: lineNum,
444
+ column: trimmed.length,
445
+ message: "Table row must end with |",
446
+ context: line
447
+ });
448
+ }
449
+ } else {
450
+ inTable = false;
451
+ tableColumns = 0;
452
+ }
453
+ }
454
+ return {
455
+ valid: errors.length === 0,
456
+ errors
457
+ };
458
+ }
459
+ function estimateTokens(data) {
460
+ const jsonStr = JSON.stringify(data, null, 2);
461
+ const mintStr = encode(data);
462
+ const jsonTokens = Math.ceil(jsonStr.length / 3.5);
463
+ const mintTokens = Math.ceil(mintStr.length / 3.5);
464
+ const savings = jsonTokens - mintTokens;
465
+ const savingsPercent = Math.round(savings / jsonTokens * 100);
466
+ return {
467
+ json: jsonTokens,
468
+ mint: mintTokens,
469
+ savings,
470
+ savingsPercent
471
+ };
472
+ }
473
+ var index_default = { encode, decode, validate, estimateTokens };
474
+ // Annotate the CommonJS export names for ESM import in node:
475
+ 0 && (module.exports = {
476
+ decode,
477
+ encode,
478
+ estimateTokens,
479
+ validate
480
+ });
@@ -0,0 +1,104 @@
1
+ /**
2
+ * 🌿 MINT Format - Minimal Inference Notation for Tokens
3
+ *
4
+ * A fresh, human-readable, token-efficient data format for LLM prompts.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ interface EncodeOptions {
9
+ /** Spaces per indentation level (default: 2) */
10
+ indent?: number;
11
+ /** Enable compact mode with symbols (default: false) */
12
+ compact?: boolean;
13
+ /** Sort object keys alphabetically (default: false) */
14
+ sortKeys?: boolean;
15
+ }
16
+ interface DecodeOptions {
17
+ /** Enable strict validation (default: true) */
18
+ strict?: boolean;
19
+ /** Expected indentation spaces (default: 2) */
20
+ indent?: number;
21
+ }
22
+ interface ValidationResult {
23
+ valid: boolean;
24
+ errors: ValidationError[];
25
+ }
26
+ interface ValidationError {
27
+ line: number;
28
+ column: number;
29
+ message: string;
30
+ context?: string;
31
+ }
32
+ interface TokenEstimate {
33
+ json: number;
34
+ mint: number;
35
+ savings: number;
36
+ savingsPercent: number;
37
+ }
38
+ /**
39
+ * Encode JavaScript value to MINT format
40
+ *
41
+ * @param value - Value to encode
42
+ * @param options - Encoding options
43
+ * @returns MINT-formatted string
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const data = {
48
+ * users: [
49
+ * { id: 1, name: 'Alice' },
50
+ * { id: 2, name: 'Bob' }
51
+ * ]
52
+ * };
53
+ *
54
+ * console.log(encode(data));
55
+ * // users:
56
+ * // | id | name |
57
+ * // | 1 | Alice |
58
+ * // | 2 | Bob |
59
+ * ```
60
+ */
61
+ declare function encode(value: unknown, options?: EncodeOptions): string;
62
+ /**
63
+ * Decode MINT string to JavaScript value
64
+ *
65
+ * @param input - MINT-formatted string
66
+ * @param options - Decoding options
67
+ * @returns Parsed JavaScript value
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const mint = `
72
+ * users:
73
+ * | id | name |
74
+ * | 1 | Alice |
75
+ * | 2 | Bob |
76
+ * `;
77
+ *
78
+ * const data = decode(mint);
79
+ * console.log(data.users[0].name); // "Alice"
80
+ * ```
81
+ */
82
+ declare function decode(input: string, options?: DecodeOptions): unknown;
83
+ /**
84
+ * Validate MINT syntax
85
+ *
86
+ * @param input - MINT-formatted string
87
+ * @returns Validation result with errors
88
+ */
89
+ declare function validate(input: string): ValidationResult;
90
+ /**
91
+ * Estimate token counts
92
+ *
93
+ * @param data - Data to analyze
94
+ * @returns Token estimates for JSON and MINT
95
+ */
96
+ declare function estimateTokens(data: unknown): TokenEstimate;
97
+ declare const _default: {
98
+ encode: typeof encode;
99
+ decode: typeof decode;
100
+ validate: typeof validate;
101
+ estimateTokens: typeof estimateTokens;
102
+ };
103
+
104
+ export { type DecodeOptions, type EncodeOptions, type TokenEstimate, type ValidationError, type ValidationResult, decode, _default as default, encode, estimateTokens, validate };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * 🌿 MINT Format - Minimal Inference Notation for Tokens
3
+ *
4
+ * A fresh, human-readable, token-efficient data format for LLM prompts.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ interface EncodeOptions {
9
+ /** Spaces per indentation level (default: 2) */
10
+ indent?: number;
11
+ /** Enable compact mode with symbols (default: false) */
12
+ compact?: boolean;
13
+ /** Sort object keys alphabetically (default: false) */
14
+ sortKeys?: boolean;
15
+ }
16
+ interface DecodeOptions {
17
+ /** Enable strict validation (default: true) */
18
+ strict?: boolean;
19
+ /** Expected indentation spaces (default: 2) */
20
+ indent?: number;
21
+ }
22
+ interface ValidationResult {
23
+ valid: boolean;
24
+ errors: ValidationError[];
25
+ }
26
+ interface ValidationError {
27
+ line: number;
28
+ column: number;
29
+ message: string;
30
+ context?: string;
31
+ }
32
+ interface TokenEstimate {
33
+ json: number;
34
+ mint: number;
35
+ savings: number;
36
+ savingsPercent: number;
37
+ }
38
+ /**
39
+ * Encode JavaScript value to MINT format
40
+ *
41
+ * @param value - Value to encode
42
+ * @param options - Encoding options
43
+ * @returns MINT-formatted string
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const data = {
48
+ * users: [
49
+ * { id: 1, name: 'Alice' },
50
+ * { id: 2, name: 'Bob' }
51
+ * ]
52
+ * };
53
+ *
54
+ * console.log(encode(data));
55
+ * // users:
56
+ * // | id | name |
57
+ * // | 1 | Alice |
58
+ * // | 2 | Bob |
59
+ * ```
60
+ */
61
+ declare function encode(value: unknown, options?: EncodeOptions): string;
62
+ /**
63
+ * Decode MINT string to JavaScript value
64
+ *
65
+ * @param input - MINT-formatted string
66
+ * @param options - Decoding options
67
+ * @returns Parsed JavaScript value
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const mint = `
72
+ * users:
73
+ * | id | name |
74
+ * | 1 | Alice |
75
+ * | 2 | Bob |
76
+ * `;
77
+ *
78
+ * const data = decode(mint);
79
+ * console.log(data.users[0].name); // "Alice"
80
+ * ```
81
+ */
82
+ declare function decode(input: string, options?: DecodeOptions): unknown;
83
+ /**
84
+ * Validate MINT syntax
85
+ *
86
+ * @param input - MINT-formatted string
87
+ * @returns Validation result with errors
88
+ */
89
+ declare function validate(input: string): ValidationResult;
90
+ /**
91
+ * Estimate token counts
92
+ *
93
+ * @param data - Data to analyze
94
+ * @returns Token estimates for JSON and MINT
95
+ */
96
+ declare function estimateTokens(data: unknown): TokenEstimate;
97
+ declare const _default: {
98
+ encode: typeof encode;
99
+ decode: typeof decode;
100
+ validate: typeof validate;
101
+ estimateTokens: typeof estimateTokens;
102
+ };
103
+
104
+ export { type DecodeOptions, type EncodeOptions, type TokenEstimate, type ValidationError, type ValidationResult, decode, _default as default, encode, estimateTokens, validate };
package/dist/index.js ADDED
@@ -0,0 +1,452 @@
1
+ // src/index.ts
2
+ var STATUS_SYMBOLS = {
3
+ completed: "\u2713",
4
+ complete: "\u2713",
5
+ success: "\u2713",
6
+ done: "\u2713",
7
+ passed: "\u2713",
8
+ true: "\u2713",
9
+ yes: "\u2713",
10
+ failed: "\u2717",
11
+ failure: "\u2717",
12
+ error: "\u2717",
13
+ rejected: "\u2717",
14
+ false: "\u2717",
15
+ no: "\u2717",
16
+ pending: "\u23F3",
17
+ waiting: "\u23F3",
18
+ in_progress: "\u23F3",
19
+ running: "\u23F3",
20
+ warning: "\u26A0",
21
+ warn: "\u26A0",
22
+ review: "?",
23
+ unknown: "?"
24
+ };
25
+ var REVERSE_SYMBOLS = {
26
+ "\u2713": "true",
27
+ "\u2717": "false",
28
+ "\u23F3": "pending",
29
+ "\u26A0": "warning",
30
+ "?": "unknown"
31
+ };
32
+ function needsQuoting(value) {
33
+ if (value === "") return true;
34
+ if (value.startsWith(" ") || value.endsWith(" ")) return true;
35
+ if (value.includes("|") || value.includes("\n") || value.includes("\r")) return true;
36
+ if (value.includes(",")) return true;
37
+ if (/^-?\d+\.?\d*$/.test(value)) return true;
38
+ if (["true", "false", "null"].includes(value.toLowerCase())) return true;
39
+ if (value.includes(":") && !value.includes("://")) return true;
40
+ if (value.includes('"')) return true;
41
+ return false;
42
+ }
43
+ function escapeString(value) {
44
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
45
+ }
46
+ function unescapeString(value) {
47
+ return value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
48
+ }
49
+ function formatPrimitive(value, options, inTable = false) {
50
+ if (value === null || value === void 0) {
51
+ return inTable ? "-" : "null";
52
+ }
53
+ if (typeof value === "boolean") {
54
+ return String(value);
55
+ }
56
+ if (typeof value === "number") {
57
+ if (!isFinite(value)) return "null";
58
+ return String(value);
59
+ }
60
+ if (typeof value === "string") {
61
+ if (options.compact && STATUS_SYMBOLS[value.toLowerCase()]) {
62
+ return STATUS_SYMBOLS[value.toLowerCase()];
63
+ }
64
+ if (value === "" && inTable) return "-";
65
+ if (needsQuoting(value)) {
66
+ return `"${escapeString(value)}"`;
67
+ }
68
+ return value;
69
+ }
70
+ return String(value);
71
+ }
72
+ function isPrimitive(value) {
73
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
74
+ }
75
+ function isTableArray(arr) {
76
+ if (arr.length === 0) return false;
77
+ if (!arr.every((item) => item !== null && typeof item === "object" && !Array.isArray(item))) {
78
+ return false;
79
+ }
80
+ const firstKeys = Object.keys(arr[0]).sort().join(",");
81
+ const sameKeys = arr.every((item) => {
82
+ const keys = Object.keys(item).sort().join(",");
83
+ return keys === firstKeys;
84
+ });
85
+ if (!sameKeys) return false;
86
+ return arr.every((item) => {
87
+ const obj = item;
88
+ return Object.values(obj).every((val) => isPrimitive(val));
89
+ });
90
+ }
91
+ function isPrimitiveArray(arr) {
92
+ return arr.every((item) => isPrimitive(item));
93
+ }
94
+ function getColumnWidths(headers, rows) {
95
+ const widths = headers.map((h) => h.length);
96
+ for (const row of rows) {
97
+ for (let i = 0; i < row.length; i++) {
98
+ widths[i] = Math.max(widths[i] || 0, row[i]?.length || 0);
99
+ }
100
+ }
101
+ return widths;
102
+ }
103
+ function padCell(value, width) {
104
+ return value + " ".repeat(Math.max(0, width - value.length));
105
+ }
106
+ function encodeTable(arr, options, indentLevel) {
107
+ if (arr.length === 0) return "| |";
108
+ const indent = " ".repeat(options.indent || 2);
109
+ const baseIndent = indent.repeat(indentLevel);
110
+ const headers = Object.keys(arr[0]);
111
+ const rows = arr.map((obj) => headers.map((h) => formatPrimitive(obj[h], options, true)));
112
+ const widths = getColumnWidths(headers, rows);
113
+ const lines = [];
114
+ const headerCells = headers.map((h, i) => padCell(h, widths[i]));
115
+ lines.push(`${baseIndent}| ${headerCells.join(" | ")} |`);
116
+ for (const row of rows) {
117
+ const cells = row.map((cell, i) => padCell(cell, widths[i]));
118
+ lines.push(`${baseIndent}| ${cells.join(" | ")} |`);
119
+ }
120
+ return lines.join("\n");
121
+ }
122
+ function encodeValue(value, options, indentLevel) {
123
+ const indent = " ".repeat(options.indent || 2);
124
+ const baseIndent = indent.repeat(indentLevel);
125
+ if (value === null || typeof value !== "object") {
126
+ return formatPrimitive(value, options);
127
+ }
128
+ if (Array.isArray(value)) {
129
+ if (value.length === 0) {
130
+ return "[]";
131
+ }
132
+ if (isPrimitiveArray(value)) {
133
+ return value.map((v) => formatPrimitive(v, options)).join(", ");
134
+ }
135
+ if (isTableArray(value)) {
136
+ return "\n" + encodeTable(value, options, indentLevel + 1);
137
+ }
138
+ const lines2 = [];
139
+ for (const item of value) {
140
+ if (item === null || typeof item !== "object") {
141
+ lines2.push(`${baseIndent}${indent}- ${formatPrimitive(item, options)}`);
142
+ } else {
143
+ const encoded = encodeValue(item, options, indentLevel + 2);
144
+ if (encoded.includes("\n")) {
145
+ lines2.push(`${baseIndent}${indent}-`);
146
+ lines2.push(encoded);
147
+ } else {
148
+ lines2.push(`${baseIndent}${indent}- ${encoded}`);
149
+ }
150
+ }
151
+ }
152
+ return "\n" + lines2.join("\n");
153
+ }
154
+ const obj = value;
155
+ const keys = options.sortKeys ? Object.keys(obj).sort() : Object.keys(obj);
156
+ if (keys.length === 0) {
157
+ return "";
158
+ }
159
+ const lines = [];
160
+ for (const key of keys) {
161
+ const val = obj[key];
162
+ if (val === null) {
163
+ lines.push(`${baseIndent}${key}: null`);
164
+ } else if (typeof val !== "object") {
165
+ lines.push(`${baseIndent}${key}: ${formatPrimitive(val, options)}`);
166
+ } else if (Array.isArray(val)) {
167
+ if (val.length === 0) {
168
+ lines.push(`${baseIndent}${key}: []`);
169
+ } else if (isPrimitiveArray(val)) {
170
+ lines.push(`${baseIndent}${key}: ${val.map((v) => formatPrimitive(v, options)).join(", ")}`);
171
+ } else {
172
+ lines.push(`${baseIndent}${key}:${encodeValue(val, options, indentLevel)}`);
173
+ }
174
+ } else {
175
+ const nested = encodeValue(val, options, indentLevel + 1);
176
+ if (nested === "") {
177
+ lines.push(`${baseIndent}${key}:`);
178
+ } else if (nested.includes("\n")) {
179
+ lines.push(`${baseIndent}${key}:`);
180
+ lines.push(nested);
181
+ } else {
182
+ lines.push(`${baseIndent}${key}: ${nested}`);
183
+ }
184
+ }
185
+ }
186
+ return lines.join("\n");
187
+ }
188
+ function encode(value, options = {}) {
189
+ const opts = {
190
+ indent: 2,
191
+ compact: false,
192
+ sortKeys: false,
193
+ ...options
194
+ };
195
+ if (value === null || value === void 0) {
196
+ return "null";
197
+ }
198
+ if (typeof value !== "object") {
199
+ return formatPrimitive(value, opts);
200
+ }
201
+ if (Array.isArray(value)) {
202
+ if (value.length === 0) return "_: []";
203
+ if (isPrimitiveArray(value)) {
204
+ return `_: ${value.map((v) => formatPrimitive(v, opts)).join(", ")}`;
205
+ }
206
+ if (isTableArray(value)) {
207
+ return `_:
208
+ ${encodeTable(value, opts, 1)}`;
209
+ }
210
+ return `_:${encodeValue(value, opts, 0)}`;
211
+ }
212
+ return encodeValue(value, opts, 0);
213
+ }
214
+ function parsePrimitive(value) {
215
+ const trimmed = value.trim();
216
+ if (trimmed === "null" || trimmed === "-" || trimmed === "") {
217
+ return null;
218
+ }
219
+ if (trimmed === "true") return true;
220
+ if (trimmed === "false") return false;
221
+ if (REVERSE_SYMBOLS[trimmed]) {
222
+ return REVERSE_SYMBOLS[trimmed];
223
+ }
224
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
225
+ return unescapeString(trimmed.slice(1, -1));
226
+ }
227
+ if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
228
+ const num = Number(trimmed);
229
+ if (isFinite(num)) return num;
230
+ }
231
+ return trimmed;
232
+ }
233
+ function parseTable(lines, startIndex, indent) {
234
+ const result = [];
235
+ let headers = [];
236
+ let i = startIndex;
237
+ const headerLine = lines[i].trim();
238
+ if (!headerLine.startsWith("|")) {
239
+ return { value: [], endIndex: startIndex };
240
+ }
241
+ headers = headerLine.split("|").slice(1, -1).map((h) => h.trim());
242
+ i++;
243
+ while (i < lines.length) {
244
+ const line = lines[i];
245
+ const lineIndent = line.length - line.trimStart().length;
246
+ const trimmed = line.trim();
247
+ if (!trimmed.startsWith("|")) {
248
+ break;
249
+ }
250
+ if (lineIndent < indent && trimmed !== "") {
251
+ break;
252
+ }
253
+ const cells = trimmed.split("|").slice(1, -1).map((c) => parsePrimitive(c.trim()));
254
+ if (cells.length === headers.length) {
255
+ const row = {};
256
+ headers.forEach((h, idx) => {
257
+ row[h] = cells[idx];
258
+ });
259
+ result.push(row);
260
+ }
261
+ i++;
262
+ }
263
+ return { value: result, endIndex: i - 1 };
264
+ }
265
+ function parseDocument(lines, startIndex, baseIndent, options) {
266
+ const result = {};
267
+ let i = startIndex;
268
+ while (i < lines.length) {
269
+ const line = lines[i];
270
+ if (line.trim() === "" || line.trim().startsWith("#")) {
271
+ i++;
272
+ continue;
273
+ }
274
+ const lineIndent = line.length - line.trimStart().length;
275
+ if (lineIndent < baseIndent) {
276
+ break;
277
+ }
278
+ if (lineIndent > baseIndent) {
279
+ i++;
280
+ continue;
281
+ }
282
+ const trimmed = line.trim();
283
+ if (trimmed.startsWith("|")) {
284
+ i++;
285
+ continue;
286
+ }
287
+ const colonIndex = trimmed.indexOf(":");
288
+ if (colonIndex === -1) {
289
+ i++;
290
+ continue;
291
+ }
292
+ const key = trimmed.slice(0, colonIndex).trim();
293
+ const valueStr = trimmed.slice(colonIndex + 1).trim();
294
+ if (valueStr === "" || valueStr === "[]") {
295
+ let foundNested = false;
296
+ let nextIdx = i + 1;
297
+ while (nextIdx < lines.length && lines[nextIdx].trim() === "") {
298
+ nextIdx++;
299
+ }
300
+ if (nextIdx < lines.length) {
301
+ const nextLine = lines[nextIdx];
302
+ const nextIndent = nextLine.length - nextLine.trimStart().length;
303
+ const nextTrimmed = nextLine.trim();
304
+ if (nextIndent > baseIndent && nextTrimmed.startsWith("|")) {
305
+ const tableResult = parseTable(lines, nextIdx, nextIndent);
306
+ result[key] = tableResult.value;
307
+ i = tableResult.endIndex + 1;
308
+ foundNested = true;
309
+ } else if (nextIndent > baseIndent && nextTrimmed !== "" && !nextTrimmed.startsWith("#")) {
310
+ const nestedResult = parseDocument(lines, nextIdx, nextIndent, options);
311
+ result[key] = nestedResult.value;
312
+ i = nestedResult.endIndex;
313
+ foundNested = true;
314
+ }
315
+ }
316
+ if (!foundNested) {
317
+ result[key] = valueStr === "[]" ? [] : {};
318
+ i++;
319
+ }
320
+ } else if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
321
+ result[key] = parsePrimitive(valueStr);
322
+ i++;
323
+ } else if (valueStr.includes(" | ")) {
324
+ result[key] = valueStr.split(" | ").map((v) => parsePrimitive(v.trim()));
325
+ i++;
326
+ } else if (valueStr.includes(", ")) {
327
+ result[key] = valueStr.split(", ").map((v) => parsePrimitive(v.trim()));
328
+ i++;
329
+ } else {
330
+ result[key] = parsePrimitive(valueStr);
331
+ i++;
332
+ }
333
+ }
334
+ return { value: result, endIndex: i };
335
+ }
336
+ function decode(input, options = {}) {
337
+ const opts = {
338
+ strict: true,
339
+ indent: 2,
340
+ ...options
341
+ };
342
+ const normalized = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
343
+ const lines = normalized.split("\n");
344
+ if (lines.every((l) => l.trim() === "" || l.trim().startsWith("#"))) {
345
+ return {};
346
+ }
347
+ let startIndex = 0;
348
+ while (startIndex < lines.length && (lines[startIndex].trim() === "" || lines[startIndex].trim().startsWith("#"))) {
349
+ startIndex++;
350
+ }
351
+ if (startIndex >= lines.length) {
352
+ return {};
353
+ }
354
+ const firstLine = lines[startIndex].trim();
355
+ if (firstLine.startsWith("|")) {
356
+ const tableResult = parseTable(lines, startIndex, 0);
357
+ return tableResult.value;
358
+ }
359
+ if (firstLine.startsWith("_:")) {
360
+ const valueStr = firstLine.slice(2).trim();
361
+ if (valueStr === "" || valueStr === "[]") {
362
+ const nextLine = lines[startIndex + 1];
363
+ if (nextLine && nextLine.trim().startsWith("|")) {
364
+ const tableResult = parseTable(lines, startIndex + 1, 2);
365
+ return tableResult.value;
366
+ }
367
+ return [];
368
+ }
369
+ if (valueStr.includes(", ")) {
370
+ return valueStr.split(", ").map((v) => parsePrimitive(v.trim()));
371
+ }
372
+ return [parsePrimitive(valueStr)];
373
+ }
374
+ const result = parseDocument(lines, startIndex, 0, opts);
375
+ return result.value;
376
+ }
377
+ function validate(input) {
378
+ const errors = [];
379
+ const lines = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
380
+ let inTable = false;
381
+ let tableColumns = 0;
382
+ for (let i = 0; i < lines.length; i++) {
383
+ const line = lines[i];
384
+ const lineNum = i + 1;
385
+ const trimmed = line.trim();
386
+ if (trimmed === "" || trimmed.startsWith("#")) {
387
+ continue;
388
+ }
389
+ const indent = line.length - line.trimStart().length;
390
+ if (indent % 2 !== 0) {
391
+ errors.push({
392
+ line: lineNum,
393
+ column: 1,
394
+ message: `Inconsistent indentation: ${indent} spaces (should be multiple of 2)`,
395
+ context: line
396
+ });
397
+ }
398
+ if (trimmed.startsWith("|")) {
399
+ const pipes = trimmed.split("|").length - 1;
400
+ if (!inTable) {
401
+ inTable = true;
402
+ tableColumns = pipes;
403
+ } else {
404
+ if (pipes !== tableColumns) {
405
+ errors.push({
406
+ line: lineNum,
407
+ column: 1,
408
+ message: `Table column mismatch: expected ${tableColumns - 1} columns, got ${pipes - 1}`,
409
+ context: line
410
+ });
411
+ }
412
+ }
413
+ if (!trimmed.endsWith("|")) {
414
+ errors.push({
415
+ line: lineNum,
416
+ column: trimmed.length,
417
+ message: "Table row must end with |",
418
+ context: line
419
+ });
420
+ }
421
+ } else {
422
+ inTable = false;
423
+ tableColumns = 0;
424
+ }
425
+ }
426
+ return {
427
+ valid: errors.length === 0,
428
+ errors
429
+ };
430
+ }
431
+ function estimateTokens(data) {
432
+ const jsonStr = JSON.stringify(data, null, 2);
433
+ const mintStr = encode(data);
434
+ const jsonTokens = Math.ceil(jsonStr.length / 3.5);
435
+ const mintTokens = Math.ceil(mintStr.length / 3.5);
436
+ const savings = jsonTokens - mintTokens;
437
+ const savingsPercent = Math.round(savings / jsonTokens * 100);
438
+ return {
439
+ json: jsonTokens,
440
+ mint: mintTokens,
441
+ savings,
442
+ savingsPercent
443
+ };
444
+ }
445
+ var index_default = { encode, decode, validate, estimateTokens };
446
+ export {
447
+ decode,
448
+ index_default as default,
449
+ encode,
450
+ estimateTokens,
451
+ validate
452
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@q1k-oss/mint-format",
3
+ "version": "1.0.0",
4
+ "description": "🌿 MINT Format encoder/decoder - Minimal Inference Notation for Tokens",
5
+ "keywords": [
6
+ "mint",
7
+ "format",
8
+ "serialization",
9
+ "llm",
10
+ "token",
11
+ "json",
12
+ "encoder",
13
+ "decoder",
14
+ "minimal"
15
+ ],
16
+ "homepage": "https://github.com/q1k-oss/mint",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/q1k-oss/mint.git",
20
+ "directory": "packages/mint"
21
+ },
22
+ "license": "MIT",
23
+ "author": "MINT Format Contributors",
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.cjs"
30
+ }
31
+ },
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
42
+ "clean": "rm -rf dist",
43
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
44
+ "lint": "eslint src --ext .ts",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "test:coverage": "vitest run --coverage",
48
+ "typecheck": "tsc --noEmit"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^20.10.0",
52
+ "tsup": "^8.0.1",
53
+ "typescript": "^5.3.2",
54
+ "vitest": "^1.0.0"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "engines": {
60
+ "node": ">=16.0.0"
61
+ }
62
+ }