@fraczak/k 7.1.0 → 7.2.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.
@@ -6,7 +6,11 @@ function pLabel(label) {
6
6
  return nameRE.test(label) ? ` ${label}` : ` ${JSON.stringify(label)}`;
7
7
  }
8
8
 
9
- function propertyListToFilter(propertyList, varPrefix = "X") {
9
+ function propertyListToFilter(
10
+ propertyList,
11
+ varPrefix = "X",
12
+ limit = Infinity
13
+ ) {
10
14
  if (!propertyList || propertyList.length === 0) return "(...)";
11
15
 
12
16
  const refCount = new Array(propertyList.length).fill(0);
@@ -22,9 +26,15 @@ function propertyListToFilter(propertyList, varPrefix = "X") {
22
26
  }
23
27
 
24
28
  const defined = new Set();
29
+ let curLen = 0;
25
30
 
26
31
  function fmt(nodeId) {
27
- if (varNames.has(nodeId) && defined.has(nodeId)) return varNames.get(nodeId);
32
+ if (curLen >= limit) return "...";
33
+ if (varNames.has(nodeId) && defined.has(nodeId)) {
34
+ const name = varNames.get(nodeId);
35
+ curLen += name.length;
36
+ return name;
37
+ }
28
38
 
29
39
  const [kind, edges] = propertyList[nodeId];
30
40
  const hasVar = varNames.has(nodeId);
@@ -32,16 +42,35 @@ function propertyListToFilter(propertyList, varPrefix = "X") {
32
42
 
33
43
  const suffix = hasVar ? `=${varNames.get(nodeId)}` : "";
34
44
 
35
- if (kind === "any") return hasVar ? varNames.get(nodeId) : "(...)";
45
+ if (kind === "any") {
46
+ const s = (hasVar ? varNames.get(nodeId) : "(...)") + suffix;
47
+ curLen += s.length;
48
+ return s;
49
+ }
36
50
 
37
51
  const isOpen = kind.startsWith("open-");
38
52
  const isProduct = kind.endsWith("product");
39
53
  const open = isProduct ? "{" : "<";
40
54
  const close = isProduct ? "}" : ">";
41
55
 
42
- if (edges.length === 0 && !isOpen) return `${open}${close}${suffix}`;
56
+ if (edges.length === 0 && !isOpen) {
57
+ const s = `${open}${close}${suffix}`;
58
+ curLen += s.length;
59
+ return s;
60
+ }
43
61
 
44
- const fields = edges.map(([label, target]) => `${fmt(target)}${pLabel(label)}`);
62
+ const fields = [];
63
+ for (let i = 0; i < edges.length; i++) {
64
+ if (curLen >= limit) {
65
+ fields.push("...");
66
+ break;
67
+ }
68
+ const [label, target] = edges[i];
69
+ const targetStr = fmt(target);
70
+ const labelStr = pLabel(label);
71
+ fields.push(`${targetStr}${labelStr}`);
72
+ curLen += targetStr.length + labelStr.length + 2;
73
+ }
45
74
  if (isOpen) fields.push("...");
46
75
  return `${open}${fields.join(", ")}${close}${suffix}`;
47
76
  }
@@ -49,81 +78,94 @@ function propertyListToFilter(propertyList, varPrefix = "X") {
49
78
  return fmt(0);
50
79
  }
51
80
 
52
- function valueToK(root) {
53
- let result = "";
54
- const stack = [{
55
- val: root,
56
- assign(s) { result = s; }
57
- }];
81
+ function valueToK(root, limit = Infinity) {
82
+ const out = [];
83
+ let curLen = 0;
84
+ const stack = [root];
58
85
 
59
86
  while (stack.length > 0) {
60
- const frame = stack.pop();
61
- if (frame.finishProduct) {
62
- frame.assign(`{${frame.parts.join(", ")}}`);
87
+ if (curLen >= limit) {
88
+ out.push("...");
89
+ break;
90
+ }
91
+ const item = stack.pop();
92
+ if (typeof item === "string") {
93
+ if (curLen + item.length > limit) {
94
+ out.push(item.slice(0, Math.max(0, limit - curLen)));
95
+ out.push("...");
96
+ break;
97
+ }
98
+ out.push(item);
99
+ curLen += item.length;
63
100
  continue;
64
101
  }
65
- if (frame.finishVariant) {
66
- const base = frame.base;
67
- const tags = frame.tags;
68
- frame.assign(tags.length === 0 ? base : `${base}|${tags.reverse().join("|")}`);
102
+
103
+ if (item == null) {
104
+ const s = String(item);
105
+ if (curLen + s.length > limit) {
106
+ out.push(s.slice(0, Math.max(0, limit - curLen)));
107
+ out.push("...");
108
+ break;
109
+ }
110
+ out.push(s);
111
+ curLen += s.length;
69
112
  continue;
70
113
  }
71
114
 
72
- const v = frame.val;
73
- if (isVariant(v)) {
115
+ if (isVariant(item)) {
74
116
  const tags = [];
75
- let curr = v;
117
+ let curr = item;
76
118
  while (isVariant(curr)) {
77
119
  tags.push(pLabel(curr.tag).trimStart());
78
120
  curr = curr.value;
121
+ if (tags.length > limit) break;
79
122
  }
80
- const finishFrame = {
81
- finishVariant: true,
82
- tags,
83
- base: "",
84
- assign: frame.assign
85
- };
86
- stack.push(finishFrame);
87
- stack.push({
88
- val: curr,
89
- assign(s) { finishFrame.base = s; }
90
- });
123
+ if (tags.length > 0) {
124
+ stack.push("|" + tags.reverse().join("|"));
125
+ }
126
+ stack.push(curr);
91
127
  continue;
92
128
  }
93
129
 
94
- if (isProduct(v)) {
95
- const keys = Object.keys(v.product);
130
+ if (isProduct(item)) {
131
+ const keys = Object.keys(item.product);
96
132
  if (keys.length === 0) {
97
- frame.assign("{}");
133
+ out.push("{}");
134
+ curLen += 2;
98
135
  continue;
99
136
  }
100
- const parts = new Array(keys.length);
101
- const finishFrame = {
102
- finishProduct: true,
103
- parts,
104
- assign: frame.assign
105
- };
106
- stack.push(finishFrame);
137
+ out.push("{");
138
+ curLen += 1;
139
+ stack.push("}");
107
140
  for (let i = keys.length - 1; i >= 0; i--) {
108
141
  const k = keys[i];
109
- const label = pLabel(k);
110
- stack.push({
111
- val: v.product[k],
112
- assign(s) { parts[i] = `${s}${label}`; }
113
- });
142
+ if (i < keys.length - 1) {
143
+ stack.push(", ");
144
+ }
145
+ stack.push(pLabel(k));
146
+ stack.push(item.product[k]);
114
147
  }
115
148
  continue;
116
149
  }
117
150
 
118
- frame.assign(String(v));
151
+ const s = String(item);
152
+ if (curLen + s.length > limit) {
153
+ out.push(s.slice(0, Math.max(0, limit - curLen)));
154
+ out.push("...");
155
+ break;
156
+ }
157
+ out.push(s);
158
+ curLen += s.length;
119
159
  }
120
160
 
121
- return result;
161
+ return out.join("");
122
162
  }
123
163
 
124
- function valueWithEnvelopeToK(value) {
164
+ function valueWithEnvelopeToK(value, limit = Infinity) {
125
165
  if (value === undefined) return "... undefined";
126
- return `${valueToK(value)} ?${propertyListToFilter(value.pattern)}`;
166
+ const valStr = valueToK(value, limit);
167
+ const patStr = propertyListToFilter(value.pattern, "X", limit);
168
+ return `${valStr} ?${patStr}`;
127
169
  }
128
170
 
129
171
  export { propertyListToFilter, valueToK, valueWithEnvelopeToK };
@@ -3,6 +3,15 @@ import { Value, isProduct, isVariant } from "../../Value.mjs";
3
3
  const UNIT = Value.product({});
4
4
  const BIT0 = Value.variant("0", UNIT);
5
5
  const BIT1 = Value.variant("1", UNIT);
6
+ const BIT_KEYS = [
7
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
8
+ ];
9
+
10
+ let lazyStringDecoder = null;
11
+
12
+ function setLazyStringDecoder(fn) {
13
+ lazyStringDecoder = fn;
14
+ }
6
15
 
7
16
  function bitValue(bit) {
8
17
  return bit === 0 ? BIT0 : BIT1;
@@ -25,7 +34,8 @@ function requireVariant(value, where) {
25
34
  function bitsProduct(width, n) {
26
35
  const product = {};
27
36
  for (let i = width - 1; i >= 0; i--) {
28
- product[String(i)] = bitValue((n >> i) & 1);
37
+ const key = i < BIT_KEYS.length ? BIT_KEYS[i] : String(i);
38
+ product[key] = bitValue((n >> i) & 1);
29
39
  }
30
40
  return Value.product(product);
31
41
  }
@@ -34,7 +44,8 @@ function parseBitsProduct(value, maxBit, where) {
34
44
  const product = requireProduct(value, where);
35
45
  let n = 0;
36
46
  for (let i = maxBit; i >= 0; i--) {
37
- const entry = requireVariant(product[String(i)], `${where}.bit${i}`);
47
+ const key = i < BIT_KEYS.length ? BIT_KEYS[i] : String(i);
48
+ const entry = requireVariant(product[key], `${where}.bit${i}`);
38
49
  if (entry.tag !== "0" && entry.tag !== "1") {
39
50
  throw new Error(`${where}.bit${i}: expected tag 0 or 1`);
40
51
  }
@@ -194,20 +205,31 @@ function textToStringValue(text) {
194
205
  }
195
206
 
196
207
  function stringValueToText(value) {
197
- let out = "";
208
+ if (lazyStringDecoder) {
209
+ const fast = lazyStringDecoder(value);
210
+ if (fast !== null) return fast;
211
+ }
212
+
213
+ const chunks = [];
214
+ let chunk = "";
198
215
  let cursor = value;
199
216
 
200
217
  while (true) {
201
218
  const list = requireVariant(cursor, "string");
202
219
  if (list.tag === "nil") {
203
220
  requireProduct(list.value, "string.nil");
204
- return out;
221
+ if (chunk.length > 0) chunks.push(chunk);
222
+ return chunks.join("");
205
223
  }
206
224
  if (list.tag !== "cons") {
207
225
  throw new Error(`Unsupported string list tag: ${list.tag}`);
208
226
  }
209
227
  const pair = requireProduct(list.value, "string.cons");
210
- out += String.fromCodePoint(unicodeValueToCodePoint(pair.car));
228
+ chunk += String.fromCodePoint(unicodeValueToCodePoint(pair.car));
229
+ if (chunk.length >= 8192) {
230
+ chunks.push(chunk);
231
+ chunk = "";
232
+ }
211
233
  cursor = pair.cdr;
212
234
  }
213
235
  }
@@ -216,5 +238,6 @@ export {
216
238
  codePointToUnicodeValue,
217
239
  unicodeValueToCodePoint,
218
240
  textToStringValue,
219
- stringValueToText
241
+ stringValueToText,
242
+ setLazyStringDecoder
220
243
  };
package/codecs/show.mjs CHANGED
@@ -51,6 +51,10 @@ async function main() {
51
51
  else { usage(); process.exit(1); }
52
52
  }
53
53
 
54
+ stdout.on("error", (err) => {
55
+ if (err.code === "EPIPE") process.exit(0);
56
+ });
57
+
54
58
  const buffer = fileArg ? fs.readFileSync(fileArg) : await readAll(stdin);
55
59
 
56
60
  // forward unchanged to stdout
package/codecs/utf16.mjs CHANGED
@@ -10,7 +10,8 @@ import {
10
10
  export const doc = `
11
11
  UTF-16 text codec (BOM-aware).
12
12
 
13
- Reads UTF-16 text (LE/BE with BOM or LE without BOM), outputs UTF-16LE with BOM.
13
+ Reads UTF-16 text (LE/BE with BOM or LE without BOM),
14
+ outputs UTF-16LE with BOM.
14
15
  `;
15
16
 
16
17
  function decodeUtf16Input(buf) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "7.1.0",
3
+ "version": "7.2.0",
4
4
  "description": "k — a language for content-addressable schemas and data transformations over algebraic data types (products and tagged unions); includes runtime, contract derivation, REPL, and codec toolchain",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -58,6 +58,7 @@
58
58
  "k-wasm": "backends/wasm/bin/k-wasm.mjs",
59
59
  "k-wasm-compile": "backends/wasm/bin/k-wasm-compile.mjs",
60
60
  "k-wasm-run": "backends/wasm/bin/k-wasm-run.mjs",
61
+ "k-djson": "codecs/djson.mjs",
61
62
  "k-ieee": "codecs/ieee.mjs",
62
63
  "k-int": "codecs/int.mjs",
63
64
  "k-json": "codecs/json.mjs",
package/repl.mjs CHANGED
@@ -50,7 +50,7 @@ const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
50
50
  const COMMAND_NAMES = [
51
51
  "help", "rel",
52
52
  "codecs", "rels", "codec", "input", "reset", "klib", "ko", "load",
53
- "engine", "wasm", "pkir", "js",
53
+ "engine", "wasm", "pkir", "js", "limit",
54
54
  "quit", "exit"
55
55
  ];
56
56
  const CODEC_COMMAND_NAMES = ["load", "unload", "list"];
@@ -95,7 +95,8 @@ function createState(options = {}) {
95
95
  lastResult: null,
96
96
  lastMain: null,
97
97
  lastTiming: null,
98
- showTiming: true
98
+ showTiming: true,
99
+ printLimit: options.printLimit ?? Infinity
99
100
  };
100
101
  }
101
102
 
@@ -354,7 +355,8 @@ function formatCodecOutput(output) {
354
355
  }
355
356
 
356
357
  function printValue(value, state = null) {
357
- const lines = [valueWithEnvelopeToK(value)];
358
+ const limit = state?.printLimit ?? Infinity;
359
+ const lines = [valueWithEnvelopeToK(value, limit)];
358
360
  if (!state || value === undefined) return lines[0];
359
361
 
360
362
  const store = state.codecs || {};
@@ -363,7 +365,11 @@ function printValue(value, state = null) {
363
365
  try {
364
366
  const output = codec.print(value, { state });
365
367
  if (output != null) {
366
- lines.push(`${codec.name}: ${formatCodecOutput(output)}`);
368
+ let text = formatCodecOutput(output);
369
+ if (Number.isFinite(limit) && text.length > limit) {
370
+ text = text.slice(0, limit) + "...";
371
+ }
372
+ lines.push(`${codec.name}: ${text}`);
367
373
  }
368
374
  } catch {
369
375
  // Codec cannot format this value; silently skip
@@ -1265,9 +1271,34 @@ async function evaluateCommand(line, state) {
1265
1271
  state.engine = "js";
1266
1272
  return ["engine set to js"];
1267
1273
  }
1274
+ case "limit": {
1275
+ const mode = (arg || "").trim().toLowerCase();
1276
+ if (!mode) {
1277
+ const current = Number.isFinite(state.printLimit)
1278
+ ? state.printLimit
1279
+ : "none";
1280
+ return [`Current output limit: ${current}`];
1281
+ }
1282
+ if (mode === "none" || mode === "infinity" || mode === "off") {
1283
+ state.printLimit = Infinity;
1284
+ return ["output limit disabled"];
1285
+ }
1286
+ const n = parseInt(mode, 10);
1287
+ if (Number.isNaN(n) || n < 0) {
1288
+ throw new Error(
1289
+ `Invalid limit '${arg.trim()}'. Usage: :limit [num|none]`
1290
+ );
1291
+ }
1292
+ state.printLimit = n;
1293
+ return [`output limit set to ${n}`];
1294
+ }
1268
1295
  case "reset": {
1269
1296
  const currentEngine = state.engine;
1270
- const fresh = createState({ engine: currentEngine });
1297
+ const currentLimit = state.printLimit;
1298
+ const fresh = createState({
1299
+ engine: currentEngine,
1300
+ printLimit: currentLimit
1301
+ });
1271
1302
  Object.assign(state, fresh);
1272
1303
  return ["reset"];
1273
1304
  }
@@ -1302,6 +1333,7 @@ function helpText() {
1302
1333
  ":engine [wasm|pkir|js] display or switch evaluation engine",
1303
1334
  ":rel name show relation definition",
1304
1335
  ":rels list relation aliases",
1336
+ ":limit [num|none] set maximum characters for value output",
1305
1337
  ":codec load file load a codec module from file (e.g. :codec load codecs/int.mjs)",
1306
1338
  ":codec unload name unload a registered codec",
1307
1339
  ":codec list list loaded codecs (or simply :codecs)",
@@ -1336,6 +1368,7 @@ function cliUsage() {
1336
1368
  console.log(" --pkir Shortcut for --engine=pkir.");
1337
1369
  console.log(" --wasm Shortcut for --engine=wasm.");
1338
1370
  console.log(" --js Shortcut for --engine=js.");
1371
+ console.log(" --limit=<num> Set maximum characters for output.");
1339
1372
  console.log(" -h, --help Show this help.");
1340
1373
  }
1341
1374
 
@@ -1431,6 +1464,7 @@ function startRepl(options = {}) {
1431
1464
  if (isMainEntrypoint()) {
1432
1465
  const args = argv.slice(2);
1433
1466
  let engine = "wasm";
1467
+ let printLimit = Infinity;
1434
1468
  for (let i = 0; i < args.length; i++) {
1435
1469
  const arg = args[i];
1436
1470
  if (arg === "-h" || arg === "--help") {
@@ -1461,13 +1495,30 @@ if (isMainEntrypoint()) {
1461
1495
  exit(1);
1462
1496
  }
1463
1497
  engine = val;
1498
+ } else if (arg.startsWith("--limit=")) {
1499
+ const val = parseInt(arg.slice("--limit=".length), 10);
1500
+ if (Number.isNaN(val) || val < 0) {
1501
+ console.error(
1502
+ `Invalid limit '${arg.slice("--limit=".length)}'.`
1503
+ );
1504
+ exit(1);
1505
+ }
1506
+ printLimit = val;
1507
+ } else if (arg === "--limit") {
1508
+ i++;
1509
+ const val = parseInt(args[i] || "", 10);
1510
+ if (Number.isNaN(val) || val < 0) {
1511
+ console.error(`Invalid limit '${args[i]}'.`);
1512
+ exit(1);
1513
+ }
1514
+ printLimit = val;
1464
1515
  } else {
1465
1516
  console.error(`Unknown option '${arg}'.`);
1466
1517
  cliUsage();
1467
1518
  exit(1);
1468
1519
  }
1469
1520
  }
1470
- startRepl({ engine });
1521
+ startRepl({ engine, printLimit });
1471
1522
  }
1472
1523
 
1473
1524
  export {
@@ -91,7 +91,14 @@ for (const filename of verifiedExamples) {
91
91
  }
92
92
 
93
93
  // Bundle verified codecs for browser VFS as 100% self-contained ES modules
94
- const verifiedCodecs = ["json.mjs", "utf8.mjs", "int.mjs", "unit.mjs", "ieee.mjs"];
94
+ const verifiedCodecs = [
95
+ "json.mjs",
96
+ "djson.mjs",
97
+ "utf8.mjs",
98
+ "int.mjs",
99
+ "unit.mjs",
100
+ "ieee.mjs"
101
+ ];
95
102
  const codecsDir = path.join(projectRoot, "codecs");
96
103
 
97
104
  const browserCodecSdkShim = `
@@ -8,10 +8,15 @@ import { fileURLToPath } from "node:url";
8
8
  import * as SDK from "../codecs/runtime/codec-sdk.mjs";
9
9
  import * as IntCodec from "../codecs/int.mjs";
10
10
  import * as JsonCodec from "../codecs/json.mjs";
11
+ import * as DjsonCodec from "../codecs/djson.mjs";
11
12
  import * as Utf8Codec from "../codecs/utf8.mjs";
12
13
  import * as Utf16Codec from "../codecs/utf16.mjs";
13
14
  import * as IeeeCodec from "../codecs/ieee.mjs";
14
15
  import * as UnitCodec from "../codecs/unit.mjs";
16
+ import {
17
+ valueToK,
18
+ valueWithEnvelopeToK
19
+ } from "../codecs/runtime/show-value.mjs";
15
20
 
16
21
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
17
22
 
@@ -49,6 +54,7 @@ console.log("2. Verifying in-memory parse -> enveloped value (P, v)...");
49
54
  for (const [name, codec, sampleInput] of [
50
55
  ["int.mjs", IntCodec, "42"],
51
56
  ["json.mjs", JsonCodec, '{"x": 1}'],
57
+ ["djson.mjs", DjsonCodec, '{"x": 1, "arr": [true, null, "hi"]}'],
52
58
  ["utf8.mjs", Utf8Codec, "hello"],
53
59
  ["utf16.mjs", Utf16Codec, "hello"],
54
60
  ["ieee.mjs", IeeeCodec, "3.14"],
@@ -68,6 +74,7 @@ console.log("3. Verifying CLI --help flag...");
68
74
  for (const relPath of [
69
75
  "codecs/int.mjs",
70
76
  "codecs/json.mjs",
77
+ "codecs/djson.mjs",
71
78
  "codecs/utf8.mjs",
72
79
  "codecs/utf16.mjs",
73
80
  "codecs/ieee.mjs",
@@ -111,6 +118,24 @@ console.log("4. Verifying CLI --parse and --print pipelines...");
111
118
  assert.deepEqual(JSON.parse(printRes.stdout.toString("utf8")), JSON.parse(jsonInput));
112
119
  }
113
120
 
121
+ // djson.mjs: dynamic JSON AST with singleton pattern
122
+ {
123
+ const djsonInput = JSON.stringify({
124
+ name: "k",
125
+ count: 42.5,
126
+ tags: ["fast", "typed"],
127
+ meta: { active: true, extra: null }
128
+ });
129
+ const parseRes = runCLI("codecs/djson.mjs", ["--parse"], djsonInput);
130
+ assert.equal(parseRes.status, 0);
131
+ const printRes = runCLI("codecs/djson.mjs", ["--print"], parseRes.stdout);
132
+ assert.equal(printRes.status, 0);
133
+ assert.deepEqual(
134
+ JSON.parse(printRes.stdout.toString("utf8")),
135
+ JSON.parse(djsonInput)
136
+ );
137
+ }
138
+
114
139
  // utf8.mjs: unicode text
115
140
  {
116
141
  const utf8Input = "Hello 🌍, ąęćłó! \nSecond line.";
@@ -183,4 +208,147 @@ console.log("6. Verifying pattern derivation via k type system (patternFromFilte
183
208
  assert.throws(() => SDK.patternFromFilter(".field"), /Main expression must be a filter or a type name/);
184
209
  }
185
210
 
211
+ // 7. Lazy Prefix-Free Wire Decoding
212
+ console.log("7. Verifying lazy prefix-free wire decoding...");
213
+ {
214
+ assert.equal(typeof SDK.decodeWireLazy, "function");
215
+
216
+ // Lazy decoding should instantiate root in sub-millisecond time
217
+ const sampleStr = "a".repeat(20000);
218
+ const parsedStr = Utf8Codec.parse(sampleStr);
219
+ const wireStr = SDK.encodeToWire(parsedStr, parsedStr.pattern);
220
+
221
+ const t0 = performance.now();
222
+ const decodedLazy = SDK.decodeWire(wireStr);
223
+ const constructMs = performance.now() - t0;
224
+ assert(
225
+ constructMs < 50,
226
+ `Lazy decode took ${constructMs}ms (expected <50ms)`
227
+ );
228
+ assert.equal(decodedLazy.value.tag, "cons");
229
+
230
+ // On-demand traversal through print()
231
+ const printedStr = Utf8Codec.print(decodedLazy.value);
232
+ assert.equal(printedStr, sampleStr);
233
+
234
+ // Lazy list of integers
235
+ const ints = Array.from({ length: 5000 }, (_, i) => i).join(",");
236
+ const sampleList = `[${ints}]`;
237
+ const parsedList = IntCodec.parse(sampleList);
238
+ const wireList = SDK.encodeToWire(parsedList, parsedList.pattern);
239
+ const decodedList = SDK.decodeWireLazy(wireList);
240
+ assert.equal(IntCodec.print(decodedList.value), sampleList);
241
+
242
+ // Verify stack-safe toJSON on deep variant chains
243
+ let deepVal = SDK.Value.product({});
244
+ for (let i = 0; i < 15000; i++) {
245
+ deepVal = SDK.Value.variant("step", deepVal);
246
+ }
247
+ const json = deepVal.toJSON();
248
+ assert(json && typeof json === "object");
249
+ }
250
+
251
+ // 8. High-Speed Direct Bitstream String Decoding & Bounded Rendering
252
+ console.log("8. Verifying fast string decoding & bounded rendering...");
253
+ {
254
+ const testStrings = [
255
+ "hello world",
256
+ "Zażółć gęślą jaźń",
257
+ "Unicode: 🚀🎉 𝒦-lang π ≈ 3.14159",
258
+ "a".repeat(50000),
259
+ ""
260
+ ];
261
+
262
+ for (const s of testStrings) {
263
+ const parsed = Utf8Codec.parse(s);
264
+ const wire = SDK.encodeToWire(parsed, parsed.pattern);
265
+ const decoded = SDK.decodeWire(wire);
266
+ const restored = Utf8Codec.print(decoded.value);
267
+ assert.equal(
268
+ restored,
269
+ s,
270
+ `String round-trip failed for "${s.slice(0, 20)}..."`
271
+ );
272
+ }
273
+
274
+ // Fast decoding performance: 50,000 ASCII chars in < 500ms
275
+ const bigAscii = "x".repeat(50000);
276
+ const bigParsed = Utf8Codec.parse(bigAscii);
277
+ const bigWire = SDK.encodeToWire(bigParsed, bigParsed.pattern);
278
+ const tStart = performance.now();
279
+ const bigDecoded = SDK.decodeWire(bigWire);
280
+ const bigRestored = Utf8Codec.print(bigDecoded.value);
281
+ const elapsed = performance.now() - tStart;
282
+ assert.equal(bigRestored.length, 50000);
283
+ assert(
284
+ elapsed < 500,
285
+ `Fast string decoding took ${elapsed}ms (expected < 500ms)`
286
+ );
287
+
288
+ // Bounded rendering: valueToK with limit
289
+ let deepVal = SDK.Value.product({});
290
+ for (let i = 0; i < 1000; i++) {
291
+ deepVal = SDK.Value.variant("item", deepVal);
292
+ }
293
+ const preview = valueToK(deepVal, 30);
294
+ assert(preview.endsWith("..."), "Output should end with '...'");
295
+ assert(preview.length <= 35, "Preview length should stay bounded");
296
+
297
+ // valueWithEnvelopeToK with limit
298
+ const enveloped = SDK.withPattern(deepVal, [["any", []]]);
299
+ const envPreview = valueWithEnvelopeToK(enveloped, 30);
300
+ assert(envPreview.includes("..."), "Preview should contain '...'");
301
+
302
+ // Ensure early termination: evaluating deep chain with limit 20
303
+ let hugeVal = SDK.Value.product({});
304
+ for (let i = 0; i < 50000; i++) {
305
+ hugeVal = SDK.Value.variant("v", hugeVal);
306
+ }
307
+ const tEarly0 = performance.now();
308
+ const earlyPreview = valueToK(hugeVal, 20);
309
+ const tEarlyElapsed = performance.now() - tEarly0;
310
+ assert(earlyPreview.endsWith("..."));
311
+ assert(
312
+ tEarlyElapsed < 50,
313
+ `Early termination took ${tEarlyElapsed}ms (expected < 50ms)`
314
+ );
315
+ }
316
+
317
+ // 9. DJSON Singleton Pattern Invariant & Round-Trip
318
+ console.log("9. Verifying DJSON singleton pattern invariant...");
319
+ {
320
+ assert(Array.isArray(DjsonCodec.DJSON_PATTERN));
321
+ assert.equal(DjsonCodec.DJSON_PATTERN.length, 29);
322
+
323
+ const samples = [
324
+ null,
325
+ true,
326
+ false,
327
+ 123.456,
328
+ "Hello world!",
329
+ [],
330
+ [1, "text", false, null],
331
+ {},
332
+ { key: "value", nested: [1, 2, { deep: true }] }
333
+ ];
334
+
335
+ for (const sample of samples) {
336
+ const jsonStr = JSON.stringify(sample);
337
+ const enveloped = DjsonCodec.parse(jsonStr);
338
+ assert.strictEqual(
339
+ enveloped.pattern,
340
+ DjsonCodec.DJSON_PATTERN,
341
+ "Every DJSON value must share the exact singleton pattern"
342
+ );
343
+ const wire = SDK.encodeToWire(enveloped, enveloped.pattern);
344
+ const decoded = SDK.decodeWire(wire);
345
+ const restored = DjsonCodec.print(decoded.value);
346
+ assert.deepEqual(
347
+ JSON.parse(restored),
348
+ sample,
349
+ `DJSON round-trip mismatch for: ${jsonStr}`
350
+ );
351
+ }
352
+ }
353
+
186
354
  console.log("OK - All codec tests passed.");
@@ -140,6 +140,7 @@ if (chromiumBin) {
140
140
  const expectedFiles = [
141
141
  "arithmetics.k",
142
142
  "core.k",
143
+ "djson.mjs",
143
144
  "ieee.k",
144
145
  "ieee.mjs",
145
146
  "int.mjs",