@blackwell-systems/gcf 2.1.0 → 2.1.1

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.
Files changed (49) hide show
  1. package/README.md +20 -20
  2. package/dist/cjs/browser.d.ts +9 -0
  3. package/dist/cjs/browser.js +25 -0
  4. package/dist/cjs/browser.js.map +1 -0
  5. package/dist/cjs/cli.d.ts +5 -0
  6. package/dist/cjs/cli.js +136 -0
  7. package/dist/cjs/cli.js.map +1 -0
  8. package/dist/cjs/constants.d.ts +8 -0
  9. package/dist/cjs/constants.js +46 -0
  10. package/dist/cjs/constants.js.map +1 -0
  11. package/dist/cjs/decode.d.ts +5 -0
  12. package/dist/cjs/decode.js +197 -0
  13. package/dist/cjs/decode.js.map +1 -0
  14. package/dist/cjs/decode_generic.d.ts +4 -0
  15. package/dist/cjs/decode_generic.js +678 -0
  16. package/dist/cjs/decode_generic.js.map +1 -0
  17. package/dist/cjs/delta.d.ts +15 -0
  18. package/dist/cjs/delta.js +73 -0
  19. package/dist/cjs/delta.js.map +1 -0
  20. package/dist/cjs/encode.d.ts +5 -0
  21. package/dist/cjs/encode.js +89 -0
  22. package/dist/cjs/encode.js.map +1 -0
  23. package/dist/cjs/generic.d.ts +1 -0
  24. package/dist/cjs/generic.js +332 -0
  25. package/dist/cjs/generic.js.map +1 -0
  26. package/dist/cjs/index.cjs +1841 -0
  27. package/dist/cjs/index.d.ts +11 -0
  28. package/dist/cjs/index.js +32 -0
  29. package/dist/cjs/index.js.map +1 -0
  30. package/dist/cjs/packroot.d.ts +13 -0
  31. package/dist/cjs/packroot.js +50 -0
  32. package/dist/cjs/packroot.js.map +1 -0
  33. package/dist/cjs/scalar.d.ts +26 -0
  34. package/dist/cjs/scalar.js +339 -0
  35. package/dist/cjs/scalar.js.map +1 -0
  36. package/dist/cjs/session.d.ts +30 -0
  37. package/dist/cjs/session.js +140 -0
  38. package/dist/cjs/session.js.map +1 -0
  39. package/dist/cjs/stream.d.ts +66 -0
  40. package/dist/cjs/stream.js +127 -0
  41. package/dist/cjs/stream.js.map +1 -0
  42. package/dist/cjs/stream_generic.d.ts +37 -0
  43. package/dist/cjs/stream_generic.js +87 -0
  44. package/dist/cjs/stream_generic.js.map +1 -0
  45. package/dist/cjs/types.d.ts +75 -0
  46. package/dist/cjs/types.js +3 -0
  47. package/dist/cjs/types.js.map +1 -0
  48. package/dist/cli.js +0 -0
  49. package/package.json +5 -4
@@ -0,0 +1,1841 @@
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 src_exports = {};
22
+ __export(src_exports, {
23
+ GenericStreamEncoder: () => GenericStreamEncoder,
24
+ KIND_ABBREV: () => KIND_ABBREV,
25
+ KIND_EXPAND: () => KIND_EXPAND,
26
+ Session: () => Session,
27
+ StreamEncoder: () => StreamEncoder,
28
+ decode: () => decode,
29
+ decodeGeneric: () => decodeGeneric,
30
+ encode: () => encode,
31
+ encodeDelta: () => encodeDelta,
32
+ encodeGeneric: () => encodeGeneric,
33
+ encodeWithSession: () => encodeWithSession,
34
+ formatKey: () => formatKey,
35
+ formatScalar: () => formatScalar,
36
+ needsQuote: () => needsQuote,
37
+ parseScalar: () => parseScalar,
38
+ quoteString: () => quoteString,
39
+ verifyDelta: () => verifyDelta
40
+ });
41
+ module.exports = __toCommonJS(src_exports);
42
+
43
+ // src/constants.ts
44
+ var KIND_ABBREV = {
45
+ function: "fn",
46
+ type: "type",
47
+ method: "method",
48
+ interface: "iface",
49
+ var: "var",
50
+ const: "const",
51
+ resource: "resource",
52
+ table: "table",
53
+ class: "class",
54
+ selector: "selector",
55
+ field: "field",
56
+ route_handler: "route",
57
+ external: "ext",
58
+ file: "file",
59
+ package: "pkg",
60
+ service: "svc"
61
+ };
62
+ var KIND_EXPAND = {
63
+ fn: "function",
64
+ type: "type",
65
+ method: "method",
66
+ iface: "interface",
67
+ var: "var",
68
+ const: "const",
69
+ resource: "resource",
70
+ table: "table",
71
+ class: "class",
72
+ selector: "selector",
73
+ field: "field",
74
+ route: "route_handler",
75
+ ext: "external",
76
+ file: "file",
77
+ pkg: "package",
78
+ svc: "service"
79
+ };
80
+
81
+ // src/encode.ts
82
+ function groupByDistance(symbols) {
83
+ if (symbols.length === 0) return [];
84
+ const sorted = [...symbols].sort((a, b) => {
85
+ if (a.distance !== b.distance) return a.distance - b.distance;
86
+ return b.score - a.score;
87
+ });
88
+ const groups = [];
89
+ let current = null;
90
+ for (const s of sorted) {
91
+ if (current === null || current.distance !== s.distance) {
92
+ current = { distance: s.distance, symbols: [] };
93
+ groups.push(current);
94
+ }
95
+ current.symbols.push(s);
96
+ }
97
+ return groups;
98
+ }
99
+ function encode(p) {
100
+ const lines = [];
101
+ const groups = groupByDistance(p.symbols);
102
+ const symIndex = /* @__PURE__ */ new Map();
103
+ let nextID = 0;
104
+ for (const g of groups) {
105
+ for (const s of g.symbols) {
106
+ symIndex.set(s.qualifiedName, nextID++);
107
+ }
108
+ }
109
+ const validEdges = p.edges.filter(
110
+ (e) => symIndex.has(e.source) && symIndex.has(e.target)
111
+ ).length;
112
+ let header = `GCF profile=graph tool=${p.tool}`;
113
+ if (p.tokenBudget) header += ` budget=${p.tokenBudget}`;
114
+ if (p.tokensUsed) header += ` tokens=${p.tokensUsed}`;
115
+ header += ` symbols=${p.symbols.length}`;
116
+ if (validEdges > 0) header += ` edges=${validEdges}`;
117
+ if (p.packRoot) {
118
+ header += ` pack_root=${p.packRoot}`;
119
+ }
120
+ lines.push(header);
121
+ const groupNames = ["targets", "related", "extended"];
122
+ for (const g of groups) {
123
+ if (g.symbols.length === 0) continue;
124
+ let name;
125
+ if (g.distance < groupNames.length) {
126
+ name = groupNames[g.distance];
127
+ } else {
128
+ name = `distance_${g.distance}`;
129
+ }
130
+ lines.push(`## ${name}`);
131
+ for (const s of g.symbols) {
132
+ const idx = symIndex.get(s.qualifiedName);
133
+ const kind = KIND_ABBREV[s.kind] || s.kind;
134
+ lines.push(`@${idx} ${kind} ${s.qualifiedName} ${s.score.toFixed(2)} ${s.provenance}`);
135
+ }
136
+ }
137
+ if (p.edges.length > 0) {
138
+ lines.push(`## edges [${validEdges}]`);
139
+ for (const e of p.edges) {
140
+ const srcIdx = symIndex.get(e.source);
141
+ const tgtIdx = symIndex.get(e.target);
142
+ if (srcIdx === void 0 || tgtIdx === void 0) continue;
143
+ let line = `@${tgtIdx}<@${srcIdx} ${e.edgeType}`;
144
+ if (e.status && e.status !== "unchanged") {
145
+ line += ` ${e.status}`;
146
+ }
147
+ lines.push(line);
148
+ }
149
+ }
150
+ return lines.join("\n") + "\n";
151
+ }
152
+
153
+ // src/decode.ts
154
+ function decode(input) {
155
+ const lines = input.split("\n");
156
+ if (lines.length === 0) {
157
+ throw new Error("gcf: empty input");
158
+ }
159
+ const header = lines[0];
160
+ if (!header.startsWith("GCF ")) {
161
+ throw new Error(`gcf: invalid header, expected 'GCF ...' got "${header}"`);
162
+ }
163
+ const p = {
164
+ tool: "",
165
+ tokenBudget: 0,
166
+ tokensUsed: 0,
167
+ symbols: [],
168
+ edges: []
169
+ };
170
+ parseHeader(header.slice(4), p);
171
+ const isDelta = header.includes(" delta=true");
172
+ const validDeltaSections = /* @__PURE__ */ new Set(["removed", "added", "edges_removed", "edges_added"]);
173
+ const symbols = [];
174
+ const symByID = /* @__PURE__ */ new Map();
175
+ let currentDistance = 0;
176
+ let inEdges = false;
177
+ for (let i = 1; i < lines.length; i++) {
178
+ let line = lines[i].replace(/\r$/, "");
179
+ if (line === "") continue;
180
+ if (line.startsWith("##! ")) continue;
181
+ if (line.startsWith("## ")) {
182
+ let group = line.slice(3);
183
+ const bracketIdx = group.indexOf(" [");
184
+ if (bracketIdx >= 0) {
185
+ group = group.slice(0, bracketIdx);
186
+ }
187
+ if (isDelta && !validDeltaSections.has(group)) {
188
+ throw new Error(`malformed_delta: invalid delta section "${group}"`);
189
+ }
190
+ inEdges = group === "edges";
191
+ if (!inEdges) {
192
+ switch (group) {
193
+ case "targets":
194
+ currentDistance = 0;
195
+ break;
196
+ case "related":
197
+ currentDistance = 1;
198
+ break;
199
+ case "extended":
200
+ currentDistance = 2;
201
+ break;
202
+ default:
203
+ if (group.startsWith("distance_")) {
204
+ const d = parseInt(group.slice(9), 10);
205
+ if (!isNaN(d)) {
206
+ currentDistance = d;
207
+ }
208
+ }
209
+ break;
210
+ }
211
+ }
212
+ continue;
213
+ }
214
+ if (line.startsWith("# ")) {
215
+ continue;
216
+ }
217
+ if (inEdges) {
218
+ const edge = parseEdgeLine(line, symByID);
219
+ p.edges.push(edge);
220
+ } else {
221
+ const { symbol, id } = parseSymbolLine(line, currentDistance);
222
+ symbols.push(symbol);
223
+ symByID.set(id, symbol);
224
+ }
225
+ }
226
+ p.symbols = symbols;
227
+ return p;
228
+ }
229
+ function parseHeader(fields, p) {
230
+ const parts = fields.split(/\s+/);
231
+ for (const part of parts) {
232
+ const eqIdx = part.indexOf("=");
233
+ if (eqIdx < 0) continue;
234
+ const key = part.slice(0, eqIdx);
235
+ const value = part.slice(eqIdx + 1);
236
+ switch (key) {
237
+ case "tool":
238
+ p.tool = value;
239
+ break;
240
+ case "budget": {
241
+ const v = parseInt(value, 10);
242
+ if (isNaN(v)) throw new Error(`gcf: invalid budget "${value}"`);
243
+ p.tokenBudget = v;
244
+ break;
245
+ }
246
+ case "tokens": {
247
+ const v = parseInt(value, 10);
248
+ if (isNaN(v)) throw new Error(`gcf: invalid tokens "${value}"`);
249
+ p.tokensUsed = v;
250
+ break;
251
+ }
252
+ case "pack_root":
253
+ p.packRoot = value;
254
+ break;
255
+ case "symbols":
256
+ break;
257
+ }
258
+ }
259
+ }
260
+ function parseSymbolLine(line, distance) {
261
+ if (!line.startsWith("@")) {
262
+ throw new Error(`gcf: expected symbol line starting with @, got "${line}"`);
263
+ }
264
+ const parts = line.split(/\s+/);
265
+ if (parts.length < 5) {
266
+ throw new Error(
267
+ `invalid_node_line: symbol line needs at least 5 fields, got ${parts.length} in "${line}"`
268
+ );
269
+ }
270
+ const idStr = parts[0].slice(1);
271
+ const id = parseInt(idStr, 10);
272
+ if (isNaN(id)) {
273
+ throw new Error(`invalid_symbol_id: invalid symbol id "${idStr}"`);
274
+ }
275
+ let kind = parts[1];
276
+ if (KIND_EXPAND[kind]) {
277
+ kind = KIND_EXPAND[kind];
278
+ }
279
+ const qname = parts[2];
280
+ const score = parseFloat(parts[3]);
281
+ if (isNaN(score)) {
282
+ throw new Error(`invalid_score: invalid score "${parts[3]}"`);
283
+ }
284
+ const provenance = parts[4];
285
+ return {
286
+ symbol: {
287
+ qualifiedName: qname,
288
+ kind,
289
+ score,
290
+ provenance,
291
+ distance
292
+ },
293
+ id
294
+ };
295
+ }
296
+ function parseEdgeLine(line, symByID) {
297
+ const parts = line.split(/\s+/);
298
+ if (parts.length < 2) {
299
+ throw new Error(`gcf: edge line needs at least 2 fields, got "${line}"`);
300
+ }
301
+ const ref = parts[0];
302
+ const ltIdx = ref.indexOf("<");
303
+ if (ltIdx < 0) {
304
+ throw new Error(`invalid_edge_syntax: edge line missing '<' separator in "${ref}"`);
305
+ }
306
+ const targetIDStr = ref.slice(1, ltIdx);
307
+ const sourceIDStr = ref.slice(ltIdx + 2);
308
+ const targetID = parseInt(targetIDStr, 10);
309
+ if (isNaN(targetID)) {
310
+ throw new Error(`gcf: invalid target id "${targetIDStr}"`);
311
+ }
312
+ const sourceID = parseInt(sourceIDStr, 10);
313
+ if (isNaN(sourceID)) {
314
+ throw new Error(`gcf: invalid source id "${sourceIDStr}"`);
315
+ }
316
+ const targetSym = symByID.get(targetID);
317
+ const sourceSym = symByID.get(sourceID);
318
+ if (!targetSym || !sourceSym) {
319
+ throw new Error(
320
+ `unknown_edge_reference: edge references unknown symbol id(s): target=${targetID} source=${sourceID}`
321
+ );
322
+ }
323
+ const edgeType = parts[1];
324
+ const status = parts.length >= 3 ? parts[2] : void 0;
325
+ return {
326
+ source: sourceSym.qualifiedName,
327
+ target: targetSym.qualifiedName,
328
+ edgeType,
329
+ status
330
+ };
331
+ }
332
+
333
+ // src/session.ts
334
+ var Session = class {
335
+ symbols = /* @__PURE__ */ new Map();
336
+ nextID = 0;
337
+ /** Returns true if the symbol has been sent in a previous response. */
338
+ transmitted(qname) {
339
+ return this.symbols.has(qname);
340
+ }
341
+ /** Returns the session-global ID for a previously transmitted symbol, or -1 if not found. */
342
+ getID(qname) {
343
+ const id = this.symbols.get(qname);
344
+ return id !== void 0 ? id : -1;
345
+ }
346
+ /**
347
+ * Record marks symbols as transmitted and assigns session-global IDs.
348
+ * Call this after a successful encode to register newly-sent symbols.
349
+ */
350
+ record(symbols) {
351
+ for (const sym of symbols) {
352
+ if (!this.symbols.has(sym.qualifiedName)) {
353
+ this.symbols.set(sym.qualifiedName, this.nextID);
354
+ this.nextID++;
355
+ }
356
+ }
357
+ }
358
+ /** Returns the number of symbols tracked in this session. */
359
+ size() {
360
+ return this.symbols.size;
361
+ }
362
+ /** Clears the session state. */
363
+ reset() {
364
+ this.symbols.clear();
365
+ this.nextID = 0;
366
+ }
367
+ };
368
+ function groupByDistance2(symbols) {
369
+ if (symbols.length === 0) return [];
370
+ const groups = [];
371
+ let current = null;
372
+ for (const s of symbols) {
373
+ if (current === null || current.distance !== s.distance) {
374
+ current = { distance: s.distance, symbols: [] };
375
+ groups.push(current);
376
+ }
377
+ current.symbols.push(s);
378
+ }
379
+ return groups;
380
+ }
381
+ function encodeWithSession(p, sess) {
382
+ if (!sess) {
383
+ return encode(p);
384
+ }
385
+ const lines = [];
386
+ let header = `GCF profile=graph tool=${p.tool}`;
387
+ if (p.tokenBudget) header += ` budget=${p.tokenBudget}`;
388
+ if (p.tokensUsed) header += ` tokens=${p.tokensUsed}`;
389
+ header += ` symbols=${p.symbols.length}`;
390
+ if (p.edges.length > 0) header += ` edges=${p.edges.length}`;
391
+ header += " session=true";
392
+ if (p.packRoot) {
393
+ header += ` pack_root=${p.packRoot}`;
394
+ }
395
+ lines.push(header);
396
+ const localIndex = /* @__PURE__ */ new Map();
397
+ for (let i = 0; i < p.symbols.length; i++) {
398
+ localIndex.set(p.symbols[i].qualifiedName, i);
399
+ }
400
+ const newSymbols = [];
401
+ const groups = groupByDistance2(p.symbols);
402
+ const groupNames = ["targets", "related", "extended"];
403
+ for (const g of groups) {
404
+ if (g.symbols.length === 0) continue;
405
+ let name;
406
+ if (g.distance < groupNames.length) {
407
+ name = groupNames[g.distance];
408
+ } else {
409
+ name = `distance_${g.distance}`;
410
+ }
411
+ lines.push(`## ${name}`);
412
+ for (const s of g.symbols) {
413
+ const idx = localIndex.get(s.qualifiedName);
414
+ if (sess.transmitted(s.qualifiedName)) {
415
+ lines.push(`@${idx} # previously transmitted`);
416
+ } else {
417
+ const kind = KIND_ABBREV[s.kind] || s.kind;
418
+ lines.push(`@${idx} ${kind} ${s.qualifiedName} ${s.score.toFixed(2)} ${s.provenance}`);
419
+ newSymbols.push(s);
420
+ }
421
+ }
422
+ }
423
+ if (p.edges.length > 0) {
424
+ lines.push(`## edges [${p.edges.length}]`);
425
+ for (const e of p.edges) {
426
+ const srcIdx = localIndex.get(e.source);
427
+ const tgtIdx = localIndex.get(e.target);
428
+ if (srcIdx === void 0 || tgtIdx === void 0) continue;
429
+ let line = `@${tgtIdx}<@${srcIdx} ${e.edgeType}`;
430
+ if (e.status && e.status !== "unchanged") {
431
+ line += ` ${e.status}`;
432
+ }
433
+ lines.push(line);
434
+ }
435
+ }
436
+ sess.record(newSymbols);
437
+ return lines.join("\n") + "\n";
438
+ }
439
+
440
+ // src/packroot.ts
441
+ var import_node_crypto = require("node:crypto");
442
+
443
+ // src/scalar.ts
444
+ var JSON_NUMBER_RE = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
445
+ var NUMERIC_LIKE_RE = /^[+-]\.?\d|^\.\d|^0\d/;
446
+ function needsQuote(s) {
447
+ if (s === "") return true;
448
+ if (s === "-" || s === "~" || s === "^" || s === "true" || s === "false") return true;
449
+ if (JSON_NUMBER_RE.test(s)) return true;
450
+ if (NUMERIC_LIKE_RE.test(s)) return true;
451
+ if (s[0] === " " || s[s.length - 1] === " ") return true;
452
+ if (s[0] === "#" || s[0] === "@" || s[0] === ".") return true;
453
+ for (let i = 0; i < s.length; i++) {
454
+ const c = s.charCodeAt(i);
455
+ if (c === 34 || c === 92 || c < 32 || c === 10 || c === 13 || c === 124 || c === 44) return true;
456
+ if (c >= 128 && c <= 159) return true;
457
+ if (c > 127 && (c === 160 || c === 8232 || c === 8233 || c === 65279 || c === 5760 || c >= 8192 && c <= 8202 || c === 8239 || c === 8287 || c === 12288)) return true;
458
+ }
459
+ return false;
460
+ }
461
+ function quoteString(s) {
462
+ let out = '"';
463
+ for (let i = 0; i < s.length; i++) {
464
+ const c = s.charCodeAt(i);
465
+ switch (c) {
466
+ case 34:
467
+ out += '\\"';
468
+ break;
469
+ case 92:
470
+ out += "\\\\";
471
+ break;
472
+ case 8:
473
+ out += "\\b";
474
+ break;
475
+ case 12:
476
+ out += "\\f";
477
+ break;
478
+ case 10:
479
+ out += "\\n";
480
+ break;
481
+ case 13:
482
+ out += "\\r";
483
+ break;
484
+ case 9:
485
+ out += "\\t";
486
+ break;
487
+ default:
488
+ if (c < 32) {
489
+ out += "\\u" + c.toString(16).padStart(4, "0");
490
+ } else {
491
+ out += s[i];
492
+ }
493
+ }
494
+ }
495
+ return out + '"';
496
+ }
497
+ function formatScalar(v, delimiter = 0) {
498
+ if (v === null || v === void 0) return "-";
499
+ if (typeof v === "boolean") return v ? "true" : "false";
500
+ if (typeof v === "number") return formatNumber(v);
501
+ const s = String(v);
502
+ if (needsQuote(s) || delimiter && s.includes(String.fromCharCode(delimiter))) {
503
+ return quoteString(s);
504
+ }
505
+ return s;
506
+ }
507
+ function formatNumber(f) {
508
+ if (Object.is(f, -0)) return "-0";
509
+ if (f === 0) return "0";
510
+ const abs = Math.abs(f);
511
+ if (abs >= 1e-6 && abs < 1e21) {
512
+ return toPreciseDecimal(f);
513
+ }
514
+ let s = f.toExponential();
515
+ s = s.replace(/[eE]\+?0*(\d)/, "e+$1").replace(/[eE]-0*(\d)/, "e-$1");
516
+ return s;
517
+ }
518
+ function toPreciseDecimal(f) {
519
+ return String(f);
520
+ }
521
+ var BARE_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
522
+ function isBareKey(s) {
523
+ return BARE_KEY_RE.test(s);
524
+ }
525
+ function formatKey(s) {
526
+ return isBareKey(s) ? s : quoteString(s);
527
+ }
528
+ function parseScalar(s, tabularContext) {
529
+ if (s === "") return "";
530
+ if (s[0] === '"') return parseQuotedString(s);
531
+ if (s === "-") return null;
532
+ if (s === "~") {
533
+ if (!tabularContext) throw new Error("invalid_missing: ~ outside tabular row cell");
534
+ return MISSING;
535
+ }
536
+ if (s === "^" || s.startsWith("^{") && s.endsWith("}")) {
537
+ if (!tabularContext) throw new Error("invalid_attachment_marker: ^ outside tabular row cell");
538
+ if (s === "^") return ATTACHMENT;
539
+ return { __inlineSchema: s.slice(1) };
540
+ }
541
+ if (s === "true") return true;
542
+ if (s === "false") return false;
543
+ if (JSON_NUMBER_RE.test(s)) {
544
+ const f = Number(s);
545
+ if (!isNaN(f)) return f;
546
+ }
547
+ return s;
548
+ }
549
+ var MISSING = Symbol("missing");
550
+ var ATTACHMENT = Symbol("attachment");
551
+ function parseQuotedString(s) {
552
+ if (s.length < 2 || s[0] !== '"') throw new Error("unterminated_quote");
553
+ let out = "";
554
+ let i = 1;
555
+ while (i < s.length) {
556
+ if (s[i] === '"') {
557
+ if (i + 1 !== s.length) throw new Error("trailing_characters: after closing quote");
558
+ return out;
559
+ }
560
+ if (s[i] === "\\") {
561
+ if (i + 1 >= s.length) throw new Error("unterminated_quote");
562
+ i++;
563
+ switch (s[i]) {
564
+ case '"':
565
+ out += '"';
566
+ break;
567
+ case "\\":
568
+ out += "\\";
569
+ break;
570
+ case "/":
571
+ out += "/";
572
+ break;
573
+ case "b":
574
+ out += "\b";
575
+ break;
576
+ case "f":
577
+ out += "\f";
578
+ break;
579
+ case "n":
580
+ out += "\n";
581
+ break;
582
+ case "r":
583
+ out += "\r";
584
+ break;
585
+ case "t":
586
+ out += " ";
587
+ break;
588
+ case "u": {
589
+ if (i + 4 >= s.length) throw new Error("invalid_escape: incomplete unicode");
590
+ const hex = s.slice(i + 1, i + 5);
591
+ const code = parseInt(hex, 16);
592
+ if (isNaN(code)) throw new Error(`invalid_escape: invalid unicode \\u${hex}`);
593
+ if (code >= 55296 && code <= 56319) {
594
+ if (i + 10 >= s.length || s[i + 5] !== "\\" || s[i + 6] !== "u") {
595
+ throw new Error("invalid_surrogate: isolated high surrogate");
596
+ }
597
+ const hex2 = s.slice(i + 7, i + 11);
598
+ const low = parseInt(hex2, 16);
599
+ if (isNaN(low) || low < 56320 || low > 57343) {
600
+ throw new Error("invalid_surrogate: invalid low surrogate");
601
+ }
602
+ out += String.fromCodePoint(65536 + (code - 55296) * 1024 + (low - 56320));
603
+ i += 11;
604
+ continue;
605
+ }
606
+ if (code >= 56320 && code <= 57343) {
607
+ throw new Error("invalid_surrogate: isolated low surrogate");
608
+ }
609
+ out += String.fromCharCode(code);
610
+ i += 5;
611
+ continue;
612
+ }
613
+ default:
614
+ throw new Error(`invalid_escape: unknown \\${s[i]}`);
615
+ }
616
+ i++;
617
+ continue;
618
+ }
619
+ if (s.charCodeAt(i) < 32) {
620
+ throw new Error(`invalid_escape: unescaped control U+${s.charCodeAt(i).toString(16).padStart(4, "0")}`);
621
+ }
622
+ out += s[i];
623
+ i++;
624
+ }
625
+ throw new Error("unterminated_quote");
626
+ }
627
+ function splitRespectingQuotes(s, delim) {
628
+ const parts = [];
629
+ let current = "";
630
+ let inQuote = false;
631
+ let escaped = false;
632
+ for (let i = 0; i < s.length; i++) {
633
+ if (escaped) {
634
+ current += s[i];
635
+ escaped = false;
636
+ continue;
637
+ }
638
+ if (s[i] === "\\" && inQuote) {
639
+ current += s[i];
640
+ escaped = true;
641
+ continue;
642
+ }
643
+ if (s[i] === '"') {
644
+ inQuote = !inQuote;
645
+ current += s[i];
646
+ continue;
647
+ }
648
+ if (s[i] === delim && !inQuote) {
649
+ parts.push(current);
650
+ current = "";
651
+ continue;
652
+ }
653
+ current += s[i];
654
+ }
655
+ parts.push(current);
656
+ return parts;
657
+ }
658
+ function splitFieldDecl(s) {
659
+ if (s.length < 2 || s[0] !== "{") throw new Error("invalid field declaration");
660
+ const closeIdx = findClosingBrace(s);
661
+ if (closeIdx < 0) throw new Error("invalid field declaration");
662
+ const inner = s.slice(1, closeIdx);
663
+ if (!inner) return [];
664
+ const raw = splitRespectingQuotes(inner, ",");
665
+ const fields = [];
666
+ const seen = /* @__PURE__ */ new Set();
667
+ for (const f of raw) {
668
+ const trimmed = f.trim();
669
+ let name;
670
+ if (trimmed.length >= 2 && trimmed[0] === '"' && trimmed[trimmed.length - 1] === '"') {
671
+ name = parseQuotedString(trimmed);
672
+ } else {
673
+ if (!isBareKey(trimmed)) throw new Error(`invalid field name: ${trimmed}`);
674
+ name = trimmed;
675
+ }
676
+ if (seen.has(name)) throw new Error(`duplicate_field_name: ${name}`);
677
+ seen.add(name);
678
+ fields.push(name);
679
+ }
680
+ return fields;
681
+ }
682
+ function findClosingBrace(s) {
683
+ let inQuote = false;
684
+ let escaped = false;
685
+ for (let i = 0; i < s.length; i++) {
686
+ if (escaped) {
687
+ escaped = false;
688
+ continue;
689
+ }
690
+ if (s[i] === "\\" && inQuote) {
691
+ escaped = true;
692
+ continue;
693
+ }
694
+ if (s[i] === '"') {
695
+ inQuote = !inQuote;
696
+ continue;
697
+ }
698
+ if (s[i] === "}" && !inQuote) return i;
699
+ }
700
+ return -1;
701
+ }
702
+
703
+ // src/packroot.ts
704
+ function packRoot(symbols, edges, symbolKinds) {
705
+ const symRecords = [];
706
+ const edgeRecords = [];
707
+ for (const s of symbols) {
708
+ const kind = KIND_ABBREV[s.kind] || s.kind;
709
+ symRecords.push(`S ${kind} ${s.qualifiedName} ${formatNumber(s.score)} ${s.provenance} ${s.distance}
710
+ `);
711
+ }
712
+ const kindMap = symbolKinds ?? /* @__PURE__ */ new Map();
713
+ if (!symbolKinds) {
714
+ for (const s of symbols) {
715
+ const kind = KIND_ABBREV[s.kind] || s.kind;
716
+ kindMap.set(s.qualifiedName, kind);
717
+ }
718
+ }
719
+ for (const e of edges) {
720
+ const srcKind = kindMap.get(e.source) || "fn";
721
+ const tgtKind = kindMap.get(e.target) || "fn";
722
+ edgeRecords.push(`E ${srcKind} ${e.source} ${tgtKind} ${e.target} ${e.edgeType}
723
+ `);
724
+ }
725
+ symRecords.sort();
726
+ edgeRecords.sort();
727
+ const data = symRecords.join("") + edgeRecords.join("");
728
+ const hash = (0, import_node_crypto.createHash)("sha256").update(data, "utf8").digest("hex");
729
+ return `sha256:${hash}`;
730
+ }
731
+
732
+ // src/delta.ts
733
+ function encodeDelta(d) {
734
+ const lines = [];
735
+ let savings = 0;
736
+ if (d.fullTokens > 0) {
737
+ savings = Math.round(100 * (1 - d.deltaTokens / d.fullTokens));
738
+ }
739
+ lines.push(
740
+ `GCF profile=graph tool=${d.tool} delta=true base_root=${d.baseRoot} new_root=${d.newRoot} tokens=${d.deltaTokens} savings=${savings}%`
741
+ );
742
+ if (d.removed.length > 0) {
743
+ lines.push("## removed");
744
+ for (const s of d.removed) {
745
+ const kind = KIND_ABBREV[s.kind] || s.kind;
746
+ lines.push(`${kind} ${s.qualifiedName}`);
747
+ }
748
+ }
749
+ if (d.added.length > 0) {
750
+ lines.push("## added");
751
+ for (let i = 0; i < d.added.length; i++) {
752
+ const s = d.added[i];
753
+ const kind = KIND_ABBREV[s.kind] || s.kind;
754
+ lines.push(`@${i} ${kind} ${s.qualifiedName} ${s.score.toFixed(2)} ${s.provenance}`);
755
+ }
756
+ }
757
+ if (d.removedEdges.length > 0) {
758
+ lines.push("## edges_removed");
759
+ for (const e of d.removedEdges) {
760
+ lines.push(`${e.source} -> ${e.target} ${e.edgeType}`);
761
+ }
762
+ }
763
+ if (d.addedEdges.length > 0) {
764
+ lines.push("## edges_added");
765
+ for (const e of d.addedEdges) {
766
+ lines.push(`${e.source} -> ${e.target} ${e.edgeType}`);
767
+ }
768
+ }
769
+ return lines.join("\n") + "\n";
770
+ }
771
+ function verifyDelta(baseSymbols, baseEdges, removedSymbols, addedSymbols, removedEdges, addedEdges, expectedNewRoot) {
772
+ const removedNames = new Set(removedSymbols.map((s) => s.qualifiedName));
773
+ const newSymbols = baseSymbols.filter((s) => !removedNames.has(s.qualifiedName)).concat(addedSymbols);
774
+ const removedEdgeKeys = new Set(
775
+ removedEdges.map((e) => `${e.source} ${e.target} ${e.edgeType}`)
776
+ );
777
+ const newEdges = baseEdges.filter((e) => !removedEdgeKeys.has(`${e.source} ${e.target} ${e.edgeType}`)).concat(addedEdges);
778
+ const computed = packRoot(newSymbols, newEdges);
779
+ if (computed !== expectedNewRoot) {
780
+ throw new Error(
781
+ `pack root mismatch: expected ${expectedNewRoot}, computed ${computed}`
782
+ );
783
+ }
784
+ return { symbols: newSymbols, edges: newEdges };
785
+ }
786
+
787
+ // src/generic.ts
788
+ function indent(depth) {
789
+ return " ".repeat(depth);
790
+ }
791
+ function encodeGeneric(data) {
792
+ let out = "GCF profile=generic\n";
793
+ out += encodeRootValue(data);
794
+ return out;
795
+ }
796
+ function encodeRootValue(v) {
797
+ if (v === null || v === void 0) return "=-\n";
798
+ if (Array.isArray(v)) return encodeRootArray(v);
799
+ if (typeof v === "object") return encodeObject(v, 0);
800
+ return `=${formatScalar(v, 0)}
801
+ `;
802
+ }
803
+ function encodeObject(obj, depth) {
804
+ const prefix = indent(depth);
805
+ let out = "";
806
+ for (const key of Object.keys(obj)) {
807
+ const value = obj[key];
808
+ const fk = formatKey(key);
809
+ if (Array.isArray(value)) {
810
+ out += encodeNamedArray(fk, value, depth);
811
+ } else if (typeof value === "object" && value !== null) {
812
+ out += `${prefix}## ${fk}
813
+ `;
814
+ out += encodeObject(value, depth + 1);
815
+ } else {
816
+ out += `${prefix}${fk}=${formatScalar(value, 0)}
817
+ `;
818
+ }
819
+ }
820
+ return out;
821
+ }
822
+ function encodeRootArray(arr) {
823
+ if (arr.length === 0) return "## [0]\n";
824
+ if (allPrimitives(arr)) {
825
+ const vals = arr.map((v) => formatScalar(v, 44));
826
+ return `## [${arr.length}]: ${vals.join(",")}
827
+ `;
828
+ }
829
+ const fields = tabularFields(arr);
830
+ if (fields) return encodeTabular("## ", arr, fields, 0);
831
+ return encodeExpanded("## ", arr, 0);
832
+ }
833
+ function encodeNamedArray(name, arr, depth) {
834
+ const prefix = indent(depth);
835
+ if (arr.length === 0) return `${prefix}## ${name} [0]
836
+ `;
837
+ if (allPrimitives(arr)) {
838
+ const vals = arr.map((v) => formatScalar(v, 44));
839
+ return `${prefix}${name}[${arr.length}]: ${vals.join(",")}
840
+ `;
841
+ }
842
+ const fields = tabularFields(arr);
843
+ if (fields) return encodeTabular(`${prefix}## ${name} `, arr, fields, depth);
844
+ return encodeExpanded(`${prefix}## ${name} `, arr, depth);
845
+ }
846
+ function tabularFields(arr) {
847
+ if (arr.length === 0) return null;
848
+ const fieldOrder = [];
849
+ const seen = /* @__PURE__ */ new Set();
850
+ for (const item of arr) {
851
+ if (typeof item !== "object" || item === null || Array.isArray(item)) return null;
852
+ for (const k of Object.keys(item)) {
853
+ if (!seen.has(k)) {
854
+ fieldOrder.push(k);
855
+ seen.add(k);
856
+ }
857
+ }
858
+ }
859
+ return fieldOrder.length > 0 ? fieldOrder : null;
860
+ }
861
+ function inlineSchemaFields(arr, fieldName) {
862
+ const first = arr[0];
863
+ if (!first || !(fieldName in first)) return null;
864
+ const firstVal = first[fieldName];
865
+ if (firstVal === null || firstVal === void 0 || typeof firstVal !== "object" || Array.isArray(firstVal)) return null;
866
+ let canonicalKeys = null;
867
+ for (const item of arr) {
868
+ const obj = item;
869
+ if (!(fieldName in obj) || obj[fieldName] === null || obj[fieldName] === void 0) continue;
870
+ const v = obj[fieldName];
871
+ if (typeof v !== "object" || Array.isArray(v)) return null;
872
+ const keys = Object.keys(v);
873
+ for (const k of keys) {
874
+ const val = v[k];
875
+ if (val !== null && val !== void 0 && typeof val === "object") return null;
876
+ }
877
+ if (!canonicalKeys) {
878
+ canonicalKeys = keys;
879
+ } else {
880
+ if (keys.length !== canonicalKeys.length) return null;
881
+ for (let i = 0; i < keys.length; i++) {
882
+ if (keys[i] !== canonicalKeys[i]) return null;
883
+ }
884
+ }
885
+ }
886
+ if (!canonicalKeys || canonicalKeys.length < 3) return null;
887
+ return canonicalKeys;
888
+ }
889
+ function sharedArraySchema(arr, fieldName) {
890
+ const first = arr[0];
891
+ if (!first || !(fieldName in first)) return null;
892
+ const firstVal = first[fieldName];
893
+ if (!Array.isArray(firstVal)) return null;
894
+ let canonicalFields = null;
895
+ for (const item of arr) {
896
+ const obj = item;
897
+ if (!(fieldName in obj) || obj[fieldName] === null || obj[fieldName] === void 0) continue;
898
+ const v = obj[fieldName];
899
+ if (!Array.isArray(v)) return null;
900
+ const fields = tabularFields(v);
901
+ if (!fields) return null;
902
+ for (const arrItem of v) {
903
+ if (typeof arrItem !== "object" || arrItem === null) return null;
904
+ for (const val of Object.values(arrItem)) {
905
+ if (val !== null && val !== void 0 && typeof val === "object") return null;
906
+ }
907
+ }
908
+ if (!canonicalFields) {
909
+ canonicalFields = fields;
910
+ } else {
911
+ if (fields.length !== canonicalFields.length) return null;
912
+ for (let i = 0; i < fields.length; i++) {
913
+ if (fields[i] !== canonicalFields[i]) return null;
914
+ }
915
+ }
916
+ }
917
+ return canonicalFields;
918
+ }
919
+ function encodeTabular(headerPrefix, arr, fields, depth) {
920
+ const prefix = indent(depth);
921
+ const inlineSchemas = /* @__PURE__ */ new Map();
922
+ const sharedArrSchemas = /* @__PURE__ */ new Map();
923
+ for (const f of fields) {
924
+ const ifs = inlineSchemaFields(arr, f);
925
+ if (ifs) inlineSchemas.set(f, ifs);
926
+ const sas = sharedArraySchema(arr, f);
927
+ if (sas) sharedArrSchemas.set(f, sas);
928
+ }
929
+ const fmtFields = fields.map((f) => formatKey(f));
930
+ let out = `${headerPrefix}[${arr.length}]{${fmtFields.join(",")}}
931
+ `;
932
+ for (let i = 0; i < arr.length; i++) {
933
+ const obj = arr[i];
934
+ const cells = [];
935
+ const attachments = [];
936
+ let rowHasAttachment = false;
937
+ for (const f of fields) {
938
+ if (!(f in obj)) {
939
+ cells.push("~");
940
+ continue;
941
+ }
942
+ const v = obj[f];
943
+ if (v === null || v === void 0) {
944
+ cells.push("-");
945
+ continue;
946
+ }
947
+ if (typeof v === "object") {
948
+ const ifs = inlineSchemas.get(f);
949
+ if (ifs && !Array.isArray(v)) {
950
+ if (i === 0) {
951
+ const fmtIF = ifs.map((k) => formatKey(k));
952
+ cells.push(`^{${fmtIF.join(",")}}`);
953
+ } else {
954
+ cells.push("^");
955
+ }
956
+ attachments.push({ name: f, value: v, inline: true, inlineFields: ifs });
957
+ } else {
958
+ cells.push("^");
959
+ attachments.push({ name: f, value: v, inline: false });
960
+ }
961
+ rowHasAttachment = true;
962
+ } else {
963
+ cells.push(formatScalar(v, 124));
964
+ }
965
+ }
966
+ const row = cells.join("|");
967
+ if (rowHasAttachment) {
968
+ out += `${prefix}@${i} ${row}
969
+ `;
970
+ } else {
971
+ out += `${prefix}${row}
972
+ `;
973
+ }
974
+ for (const att of attachments) {
975
+ const fk = formatKey(att.name);
976
+ if (att.inline && att.inlineFields) {
977
+ const vals = att.inlineFields.map((inf) => {
978
+ const val = att.value[inf];
979
+ if (val === void 0) return "~";
980
+ return formatScalar(val, 124);
981
+ });
982
+ out += `${prefix}${vals.join("|")}
983
+ `;
984
+ } else if (Array.isArray(att.value)) {
985
+ const sas = sharedArrSchemas.get(att.name);
986
+ if (sas && i > 0) {
987
+ out += encodeAttachmentArrayShared(prefix, fk, att.value, depth + 2, sas);
988
+ } else {
989
+ out += encodeAttachmentArray(prefix, fk, att.value, depth + 2);
990
+ }
991
+ } else {
992
+ out += `${prefix}.${fk} {}
993
+ `;
994
+ out += encodeObject(att.value, depth + 2);
995
+ }
996
+ }
997
+ }
998
+ return out;
999
+ }
1000
+ function encodeAttachmentArray(attPrefix, fk, arr, depth) {
1001
+ if (arr.length === 0) return `${attPrefix}.${fk} [0]
1002
+ `;
1003
+ if (allPrimitives(arr)) {
1004
+ const vals = arr.map((v) => formatScalar(v, 44));
1005
+ return `${attPrefix}.${fk} [${arr.length}]: ${vals.join(",")}
1006
+ `;
1007
+ }
1008
+ const fields = tabularFields(arr);
1009
+ if (fields) return encodeTabular(`${attPrefix}.${fk} `, arr, fields, depth);
1010
+ return encodeExpanded(`${attPrefix}.${fk} `, arr, depth);
1011
+ }
1012
+ function encodeAttachmentArrayShared(attPrefix, fk, arr, depth, sharedFields) {
1013
+ if (arr.length === 0) return `${attPrefix}.${fk} [0]
1014
+ `;
1015
+ if (allPrimitives(arr)) {
1016
+ const vals = arr.map((v) => formatScalar(v, 44));
1017
+ return `${attPrefix}.${fk} [${arr.length}]: ${vals.join(",")}
1018
+ `;
1019
+ }
1020
+ const fields = tabularFields(arr);
1021
+ if (fields && fields.length === sharedFields.length && fields.every((f, i) => f === sharedFields[i])) {
1022
+ const prefix = indent(depth);
1023
+ let out = `${attPrefix}.${fk} [${arr.length}]
1024
+ `;
1025
+ for (const item of arr) {
1026
+ const obj = item;
1027
+ const cells = sharedFields.map((f) => {
1028
+ if (!(f in obj)) return "~";
1029
+ if (obj[f] === null || obj[f] === void 0) return "-";
1030
+ return formatScalar(obj[f], 124);
1031
+ });
1032
+ out += `${prefix}${cells.join("|")}
1033
+ `;
1034
+ }
1035
+ return out;
1036
+ }
1037
+ return encodeAttachmentArray(attPrefix, fk, arr, depth);
1038
+ }
1039
+ function encodeExpanded(headerPrefix, arr, depth) {
1040
+ const prefix = indent(depth);
1041
+ let out = `${headerPrefix}[${arr.length}]
1042
+ `;
1043
+ for (let i = 0; i < arr.length; i++) {
1044
+ const item = arr[i];
1045
+ if (Array.isArray(item)) {
1046
+ out += encodeExpandedArrayItem(prefix, i, item, depth);
1047
+ } else if (typeof item === "object" && item !== null) {
1048
+ out += `${prefix}@${i} {}
1049
+ `;
1050
+ out += encodeObject(item, depth + 1);
1051
+ } else {
1052
+ out += `${prefix}@${i} =${formatScalar(item, 0)}
1053
+ `;
1054
+ }
1055
+ }
1056
+ return out;
1057
+ }
1058
+ function encodeExpandedArrayItem(prefix, idx, arr, depth) {
1059
+ if (arr.length === 0) return `${prefix}@${idx} [0]
1060
+ `;
1061
+ if (allPrimitives(arr)) {
1062
+ const vals = arr.map((v) => formatScalar(v, 44));
1063
+ return `${prefix}@${idx} [${arr.length}]: ${vals.join(",")}
1064
+ `;
1065
+ }
1066
+ const fields = tabularFields(arr);
1067
+ if (fields) return encodeTabular(`${prefix}@${idx} `, arr, fields, depth + 1);
1068
+ return encodeExpanded(`${prefix}@${idx} `, arr, depth + 1);
1069
+ }
1070
+ function allPrimitives(arr) {
1071
+ return arr.every((v) => typeof v !== "object" || v === null);
1072
+ }
1073
+
1074
+ // src/decode_generic.ts
1075
+ function decodeGeneric(input) {
1076
+ input = input.trimEnd();
1077
+ if (!input) throw new Error("missing_header: empty input");
1078
+ const lines = input.split("\n");
1079
+ const header = lines[0].replace(/\r$/, "");
1080
+ if (!header.startsWith("GCF ")) throw new Error("missing_header: first line does not begin with GCF");
1081
+ const profile = parseHeaderProfile(header);
1082
+ if (profile === "graph") {
1083
+ const p = decode(input);
1084
+ return {
1085
+ tool: p.tool,
1086
+ tokenBudget: p.tokenBudget,
1087
+ tokensUsed: p.tokensUsed,
1088
+ packRoot: p.packRoot ?? "",
1089
+ symbols: p.symbols.map((s) => ({
1090
+ qualifiedName: s.qualifiedName,
1091
+ kind: s.kind,
1092
+ score: s.score,
1093
+ provenance: s.provenance,
1094
+ distance: s.distance
1095
+ })),
1096
+ edges: p.edges.map((e) => ({
1097
+ source: e.source,
1098
+ target: e.target,
1099
+ edgeType: e.edgeType,
1100
+ status: e.status ?? ""
1101
+ }))
1102
+ };
1103
+ }
1104
+ if (profile !== "generic") throw new Error(`unknown_profile: ${profile}`);
1105
+ const contentLines = [];
1106
+ let summaryLine = "";
1107
+ let deferredSectionCount = 0;
1108
+ for (let i = 1; i < lines.length; i++) {
1109
+ const l = lines[i].replace(/\r$/, "");
1110
+ if (l === "") continue;
1111
+ for (let j = 0; j < l.length; j++) {
1112
+ if (l[j] === " ") throw new Error("tab_indentation: tabs in leading whitespace");
1113
+ if (l[j] !== " ") break;
1114
+ }
1115
+ const trimmed = l.trimStart();
1116
+ if (trimmed.startsWith("# ")) continue;
1117
+ if (trimmed.startsWith("##! ")) {
1118
+ summaryLine = trimmed;
1119
+ continue;
1120
+ }
1121
+ if (trimmed.startsWith("## ") && trimmed.includes("[?]")) deferredSectionCount++;
1122
+ contentLines.push(l);
1123
+ }
1124
+ if (summaryLine && deferredSectionCount > 0) {
1125
+ validateSummaryCounts(summaryLine, deferredSectionCount, contentLines);
1126
+ }
1127
+ if (contentLines.length === 0) return {};
1128
+ const first = contentLines[0].trimStart();
1129
+ if (first.startsWith("=")) {
1130
+ if (contentLines.length > 1) throw new Error("trailing_characters: extra lines after root scalar");
1131
+ return parseScalar(first.slice(1), false);
1132
+ }
1133
+ if (first.startsWith("## [")) {
1134
+ const [arr] = parseArrayFromHeader(contentLines, 0, 0, first.slice(3));
1135
+ return arr;
1136
+ }
1137
+ const result = {};
1138
+ parseObjectBody(contentLines, 0, 0, result);
1139
+ return result;
1140
+ }
1141
+ function parseHeaderProfile(header) {
1142
+ const parts = header.split(/\s+/);
1143
+ if (parts.length < 2) throw new Error("missing_profile");
1144
+ const seen = /* @__PURE__ */ new Set();
1145
+ let profile = "";
1146
+ for (let i = 1; i < parts.length; i++) {
1147
+ const eq = parts[i].indexOf("=");
1148
+ if (eq < 0) throw new Error(`malformed_header_field: ${parts[i]}`);
1149
+ const key = parts[i].slice(0, eq);
1150
+ if (seen.has(key)) throw new Error(`duplicate_header_field: ${key}`);
1151
+ seen.add(key);
1152
+ if (key === "profile") profile = parts[i].slice(eq + 1);
1153
+ }
1154
+ if (!profile) throw new Error("missing_profile");
1155
+ return profile;
1156
+ }
1157
+ function parseObjectBody(lines, start, depth, out) {
1158
+ const ind = " ".repeat(depth);
1159
+ let i = start;
1160
+ while (i < lines.length) {
1161
+ const line = lines[i];
1162
+ if (depth > 0 && !line.startsWith(ind)) break;
1163
+ const content = depth > 0 ? line.slice(ind.length) : line;
1164
+ if (content.length > 0 && content[0] === " ") {
1165
+ throw new Error("invalid_indent: indentation increases by more than one level");
1166
+ }
1167
+ if (content.startsWith("## ")) {
1168
+ const hdr = content.slice(3);
1169
+ const bi = hdr.indexOf(" [");
1170
+ if (bi >= 0) {
1171
+ const name2 = parseKeyFromHeader(hdr.slice(0, bi));
1172
+ checkDup(out, name2);
1173
+ const [arr, consumed2] = parseArrayFromHeader(lines, i, depth, hdr.slice(bi));
1174
+ out[name2] = arr;
1175
+ i += consumed2;
1176
+ continue;
1177
+ }
1178
+ const name = parseKeyFromHeader(hdr);
1179
+ checkDup(out, name);
1180
+ i++;
1181
+ const nested = {};
1182
+ const consumed = parseObjectBody(lines, i, depth + 1, nested);
1183
+ out[name] = nested;
1184
+ i += consumed;
1185
+ continue;
1186
+ }
1187
+ if (!content.startsWith("@") && !content.startsWith("##")) {
1188
+ const bracketIdx = content.indexOf("[");
1189
+ if (bracketIdx > 0) {
1190
+ const rest = content.slice(bracketIdx);
1191
+ const closeIdx = rest.indexOf("]");
1192
+ if (closeIdx >= 0) {
1193
+ const after = rest.slice(closeIdx + 1);
1194
+ if (after.startsWith(": ") || after === ":") {
1195
+ const name = parseKeyFromHeader(content.slice(0, bracketIdx));
1196
+ checkDup(out, name);
1197
+ const [arr] = parseArrayFromHeader(lines, i, depth, rest);
1198
+ out[name] = arr;
1199
+ i++;
1200
+ continue;
1201
+ }
1202
+ }
1203
+ }
1204
+ }
1205
+ const eqIdx = findKeyValueSplit(content);
1206
+ if (eqIdx > 0) {
1207
+ const name = parseKeyFromHeader(content.slice(0, eqIdx));
1208
+ checkDup(out, name);
1209
+ out[name] = parseScalar(content.slice(eqIdx + 1), false);
1210
+ i++;
1211
+ continue;
1212
+ }
1213
+ i++;
1214
+ }
1215
+ return i - start;
1216
+ }
1217
+ function findKeyValueSplit(s) {
1218
+ if (!s.length) return -1;
1219
+ if (s[0] === '"') {
1220
+ for (let i = 1; i < s.length; i++) {
1221
+ if (s[i] === "\\") {
1222
+ i++;
1223
+ continue;
1224
+ }
1225
+ if (s[i] === '"') return i + 1 < s.length && s[i + 1] === "=" ? i + 1 : -1;
1226
+ }
1227
+ return -1;
1228
+ }
1229
+ return s.indexOf("=");
1230
+ }
1231
+ function parseKeyFromHeader(s) {
1232
+ s = s.trim();
1233
+ if (s.length >= 2 && s[0] === '"') return parseQuotedString(s);
1234
+ return s;
1235
+ }
1236
+ function checkDup(obj, key) {
1237
+ if (key in obj) throw new Error(`duplicate_key: ${key}`);
1238
+ }
1239
+ function parseArrayFromHeader(lines, headerLine, depth, bracketPart) {
1240
+ const bp = bracketPart.trimStart();
1241
+ if (!bp.startsWith("[")) throw new Error("invalid_count");
1242
+ const closeIdx = bp.indexOf("]");
1243
+ if (closeIdx < 0) throw new Error("invalid_count");
1244
+ const countStr = bp.slice(1, closeIdx);
1245
+ const afterBracket = bp.slice(closeIdx + 1);
1246
+ let count = -1;
1247
+ if (countStr !== "?") count = parseCount(countStr);
1248
+ if (count === 0 && !afterBracket.startsWith("{") && !afterBracket.startsWith(":")) {
1249
+ return [[], 1];
1250
+ }
1251
+ if (afterBracket.startsWith(": ") || afterBracket === ":") {
1252
+ const valsStr = afterBracket.startsWith(": ") ? afterBracket.slice(2) : "";
1253
+ if (!valsStr) {
1254
+ if (count >= 0 && count !== 0) throw new Error(`count_mismatch: declared ${count}, got 0`);
1255
+ return [[], 1];
1256
+ }
1257
+ const vals = splitRespectingQuotes(valsStr, ",");
1258
+ if (count >= 0 && vals.length !== count) throw new Error(`count_mismatch: declared ${count}, got ${vals.length}`);
1259
+ return [vals.map((v) => parseScalar(v.trim(), false)), 1];
1260
+ }
1261
+ if (afterBracket.startsWith("{")) {
1262
+ const braceEnd = findClosingBrace2(afterBracket);
1263
+ if (braceEnd < 0) throw new Error("invalid field declaration");
1264
+ const fields = splitFieldDecl(afterBracket.slice(0, braceEnd + 1));
1265
+ const [rows, consumed2] = parseTabularBody(lines, headerLine + 1, depth, fields, count);
1266
+ if (count >= 0 && rows.length !== count) throw new Error(`count_mismatch: declared ${count}, got ${rows.length}`);
1267
+ return [rows, consumed2 + 1];
1268
+ }
1269
+ const [items, consumed] = parseExpandedBody(lines, headerLine + 1, depth);
1270
+ if (count >= 0 && items.length !== count) throw new Error(`count_mismatch: declared ${count}, got ${items.length}`);
1271
+ return [items, consumed + 1];
1272
+ }
1273
+ function findClosingBrace2(s) {
1274
+ let inQuote = false, escaped = false;
1275
+ for (let i = 0; i < s.length; i++) {
1276
+ if (escaped) {
1277
+ escaped = false;
1278
+ continue;
1279
+ }
1280
+ if (s[i] === "\\" && inQuote) {
1281
+ escaped = true;
1282
+ continue;
1283
+ }
1284
+ if (s[i] === '"') {
1285
+ inQuote = !inQuote;
1286
+ continue;
1287
+ }
1288
+ if (s[i] === "}" && !inQuote) return i;
1289
+ }
1290
+ return -1;
1291
+ }
1292
+ function parseTabularBody(lines, start, depth, fields, expectedCount) {
1293
+ const ind = " ".repeat(depth);
1294
+ const rows = [];
1295
+ let i = start;
1296
+ const inlineSchemas = /* @__PURE__ */ new Map();
1297
+ const sharedArraySchemas = /* @__PURE__ */ new Map();
1298
+ while (i < lines.length) {
1299
+ const line = lines[i];
1300
+ const content = depth > 0 ? line.startsWith(ind) ? line.slice(ind.length) : null : line;
1301
+ if (content === null) break;
1302
+ if (content.startsWith("## ") || content.startsWith("##!")) break;
1303
+ if (content.length > 0 && content[0] === " ") {
1304
+ const trimmed = content.trimStart();
1305
+ if (trimmed.startsWith(".")) break;
1306
+ break;
1307
+ }
1308
+ let rowData = content;
1309
+ let rowHasID = false;
1310
+ if (rowData.startsWith("@")) {
1311
+ const sp = rowData.indexOf(" ");
1312
+ if (sp > 0) {
1313
+ const idStr = rowData.slice(1, sp);
1314
+ if (/^\d+$/.test(idStr)) {
1315
+ rowData = rowData.slice(sp + 1);
1316
+ rowHasID = true;
1317
+ }
1318
+ }
1319
+ }
1320
+ const vals = splitRespectingQuotes(rowData, "|");
1321
+ if (vals.length !== fields.length) throw new Error(`row_width_mismatch: expected ${fields.length}, got ${vals.length}`);
1322
+ const cellValues = /* @__PURE__ */ new Map();
1323
+ const traditionalAttFields = [];
1324
+ const inlineAttFields = [];
1325
+ const inlineAttOrder = [];
1326
+ const missingFields = /* @__PURE__ */ new Set();
1327
+ for (let j = 0; j < fields.length; j++) {
1328
+ const cellVal = vals[j];
1329
+ if (cellVal.startsWith("^{") && cellVal.endsWith("}")) {
1330
+ const schemaStr = cellVal.slice(1);
1331
+ const ifs = splitFieldDecl(schemaStr);
1332
+ inlineSchemas.set(fields[j], ifs);
1333
+ inlineAttFields.push(fields[j]);
1334
+ inlineAttOrder.push(fields[j]);
1335
+ continue;
1336
+ }
1337
+ const parsed = parseScalar(cellVal, true);
1338
+ if (parsed === MISSING) {
1339
+ missingFields.add(fields[j]);
1340
+ continue;
1341
+ }
1342
+ if (parsed === ATTACHMENT) {
1343
+ if (inlineSchemas.has(fields[j])) {
1344
+ inlineAttFields.push(fields[j]);
1345
+ inlineAttOrder.push(fields[j]);
1346
+ } else {
1347
+ traditionalAttFields.push(fields[j]);
1348
+ }
1349
+ continue;
1350
+ }
1351
+ if (parsed && typeof parsed === "object" && parsed.__inlineSchema) {
1352
+ const ifs = splitFieldDecl(parsed.__inlineSchema);
1353
+ inlineSchemas.set(fields[j], ifs);
1354
+ inlineAttFields.push(fields[j]);
1355
+ inlineAttOrder.push(fields[j]);
1356
+ continue;
1357
+ }
1358
+ cellValues.set(fields[j], parsed);
1359
+ }
1360
+ i++;
1361
+ const allAttFields = [...traditionalAttFields, ...inlineAttFields];
1362
+ const attachmentValues = /* @__PURE__ */ new Map();
1363
+ if (rowHasID && allAttFields.length > 0) {
1364
+ let inlineIdx = 0;
1365
+ while (i < lines.length && attachmentValues.size < allAttFields.length) {
1366
+ const aLine = lines[i];
1367
+ let aContent = null;
1368
+ if (depth === 0 || aLine.startsWith(ind)) {
1369
+ aContent = depth > 0 ? aLine.slice(ind.length) : aLine;
1370
+ } else {
1371
+ break;
1372
+ }
1373
+ if (aContent === null) break;
1374
+ let attContent = aContent;
1375
+ if (!attContent.startsWith(".") && attContent.startsWith(" .")) {
1376
+ attContent = attContent.slice(2);
1377
+ }
1378
+ if (attContent.startsWith(".")) {
1379
+ const rest = attContent.slice(1);
1380
+ const [attName, afterName] = parseAttachmentName(rest);
1381
+ const ifs2 = inlineSchemas.get(attName);
1382
+ if (ifs2 && !afterName.trimStart().startsWith("{}") && !afterName.trimStart().startsWith("[")) {
1383
+ const data = afterName.trimStart();
1384
+ const inlineVals2 = splitRespectingQuotes(data, "|");
1385
+ if (inlineVals2.length !== ifs2.length) throw new Error(`inline_width_mismatch: ${attName} expected ${ifs2.length}, got ${inlineVals2.length}`);
1386
+ const obj2 = {};
1387
+ for (let k = 0; k < ifs2.length; k++) {
1388
+ const p = parseScalar(inlineVals2[k], true);
1389
+ if (p !== MISSING) obj2[ifs2[k]] = p;
1390
+ }
1391
+ if (attachmentValues.has(attName)) throw new Error(`duplicate_attachment: ${attName}`);
1392
+ attachmentValues.set(attName, obj2);
1393
+ i++;
1394
+ continue;
1395
+ }
1396
+ const [name, val, consumed, parsedFields] = parseAttachment(lines, i, rest, depth + 2, sharedArraySchemas);
1397
+ if (attachmentValues.has(name)) throw new Error(`duplicate_attachment: ${name}`);
1398
+ if (rows.length === 0 && parsedFields) {
1399
+ sharedArraySchemas.set(name, parsedFields);
1400
+ }
1401
+ attachmentValues.set(name, val);
1402
+ i += consumed;
1403
+ continue;
1404
+ }
1405
+ let foundInline = false;
1406
+ let nextInlineField = "";
1407
+ while (inlineIdx < inlineAttOrder.length) {
1408
+ const candidate = inlineAttOrder[inlineIdx];
1409
+ if (!attachmentValues.has(candidate)) {
1410
+ nextInlineField = candidate;
1411
+ foundInline = true;
1412
+ break;
1413
+ }
1414
+ inlineIdx++;
1415
+ }
1416
+ if (!foundInline) break;
1417
+ const ifs = inlineSchemas.get(nextInlineField);
1418
+ const inlineVals = splitRespectingQuotes(aContent, "|");
1419
+ if (inlineVals.length !== ifs.length) throw new Error(`inline_width_mismatch: ${nextInlineField} expected ${ifs.length}, got ${inlineVals.length}`);
1420
+ const obj = {};
1421
+ for (let k = 0; k < ifs.length; k++) {
1422
+ const p = parseScalar(inlineVals[k], true);
1423
+ if (p !== MISSING) obj[ifs[k]] = p;
1424
+ }
1425
+ attachmentValues.set(nextInlineField, obj);
1426
+ inlineIdx++;
1427
+ i++;
1428
+ }
1429
+ for (const f of allAttFields) {
1430
+ if (!attachmentValues.has(f)) throw new Error(`missing_attachment: ${f}`);
1431
+ }
1432
+ if (i < lines.length) {
1433
+ let peekContent = null;
1434
+ if (depth === 0 || lines[i].startsWith(ind)) {
1435
+ peekContent = depth > 0 ? lines[i].slice(ind.length) : lines[i];
1436
+ }
1437
+ if (peekContent !== null) {
1438
+ let peekAtt = peekContent;
1439
+ if (!peekAtt.startsWith(".") && peekAtt.startsWith(" .")) {
1440
+ peekAtt = peekAtt.slice(2);
1441
+ }
1442
+ if (peekAtt.startsWith(".")) {
1443
+ const peekRest = peekAtt.slice(1);
1444
+ const [peekName] = parseAttachmentName(peekRest);
1445
+ if (attachmentValues.has(peekName)) {
1446
+ throw new Error(`duplicate_attachment: ${peekName}`);
1447
+ }
1448
+ }
1449
+ }
1450
+ }
1451
+ }
1452
+ const row = {};
1453
+ for (const f of fields) {
1454
+ if (missingFields.has(f)) continue;
1455
+ if (cellValues.has(f)) {
1456
+ row[f] = cellValues.get(f);
1457
+ continue;
1458
+ }
1459
+ if (attachmentValues.has(f)) {
1460
+ row[f] = attachmentValues.get(f);
1461
+ continue;
1462
+ }
1463
+ }
1464
+ if (!rowHasID || allAttFields.length === 0) {
1465
+ const attIndent = ind + " ";
1466
+ if (i < lines.length && lines[i].startsWith(attIndent)) {
1467
+ const peek = lines[i].slice(attIndent.length);
1468
+ if (peek.startsWith(".")) throw new Error(`orphan_attachment: ${peek}`);
1469
+ }
1470
+ }
1471
+ rows.push(row);
1472
+ if (expectedCount >= 0 && rows.length >= expectedCount) break;
1473
+ }
1474
+ return [rows, i - start];
1475
+ }
1476
+ function parseAttachmentName(rest) {
1477
+ if (rest[0] === '"') {
1478
+ for (let j = 1; j < rest.length; j++) {
1479
+ if (rest[j] === "\\") {
1480
+ j++;
1481
+ continue;
1482
+ }
1483
+ if (rest[j] === '"') {
1484
+ const name = parseQuotedString(rest.slice(0, j + 1));
1485
+ return [name, rest.slice(j + 1)];
1486
+ }
1487
+ }
1488
+ return ["", rest];
1489
+ }
1490
+ const sp = rest.indexOf(" ");
1491
+ if (sp >= 0) return [rest.slice(0, sp), rest.slice(sp)];
1492
+ return [rest, ""];
1493
+ }
1494
+ function parseAttachment(lines, lineIdx, rest, depth, sharedSchemas) {
1495
+ const [name, afterNameRaw] = parseAttachmentName(rest);
1496
+ const afterName = afterNameRaw.trimStart();
1497
+ if (afterName.startsWith("{}")) {
1498
+ const nested = {};
1499
+ const consumed = parseObjectBody(lines, lineIdx + 1, depth, nested);
1500
+ return [name, nested, consumed + 1, null];
1501
+ }
1502
+ if (afterName.startsWith("[")) {
1503
+ const closeBracket = afterName.indexOf("]");
1504
+ if (closeBracket < 0) throw new Error("invalid_count: missing ]");
1505
+ const afterClose = afterName.slice(closeBracket + 1);
1506
+ if (afterClose.startsWith("{")) {
1507
+ const endBrace = findClosingBrace2(afterClose);
1508
+ let parsedFields = null;
1509
+ if (endBrace >= 0) {
1510
+ try {
1511
+ parsedFields = splitFieldDecl(afterClose.slice(0, endBrace + 1));
1512
+ } catch {
1513
+ }
1514
+ }
1515
+ const [arr2, consumed2] = parseArrayFromHeader(lines, lineIdx, depth, afterName);
1516
+ return [name, arr2, consumed2, parsedFields];
1517
+ }
1518
+ const afterCloseForInline = afterName.slice(closeBracket + 1);
1519
+ if (afterCloseForInline.startsWith(": ") || afterCloseForInline === ":") {
1520
+ const [arr2, consumed2] = parseArrayFromHeader(lines, lineIdx, depth, afterName);
1521
+ return [name, arr2, consumed2, null];
1522
+ }
1523
+ if (sharedSchemas.has(name)) {
1524
+ const sf = sharedSchemas.get(name);
1525
+ const countStr = afterName.slice(1, closeBracket);
1526
+ let count = -1;
1527
+ if (countStr !== "?") count = parseInt(countStr, 10);
1528
+ if (count === 0) return [name, [], 1, null];
1529
+ const nextIdx = lineIdx + 1;
1530
+ const ind = " ".repeat(depth);
1531
+ let useShared = true;
1532
+ if (nextIdx < lines.length) {
1533
+ let nextContent = lines[nextIdx];
1534
+ if (depth > 0 && nextContent.startsWith(ind)) nextContent = nextContent.slice(ind.length);
1535
+ if (nextContent.trimStart().startsWith("@")) useShared = false;
1536
+ }
1537
+ if (useShared) {
1538
+ const [rows, consumed2] = parseTabularBody(lines, lineIdx + 1, depth, sf, count);
1539
+ if (count >= 0 && rows.length !== count) throw new Error(`count_mismatch: declared ${count}, got ${rows.length}`);
1540
+ return [name, rows, consumed2 + 1, null];
1541
+ }
1542
+ }
1543
+ const [arr, consumed] = parseArrayFromHeader(lines, lineIdx, depth, afterName);
1544
+ return [name, arr, consumed, null];
1545
+ }
1546
+ throw new Error(`invalid attachment form: ${afterName}`);
1547
+ }
1548
+ function parseExpandedBody(lines, start, depth) {
1549
+ const ind = " ".repeat(depth);
1550
+ const items = [];
1551
+ let i = start;
1552
+ while (i < lines.length) {
1553
+ const line = lines[i];
1554
+ const content = depth > 0 ? line.startsWith(ind) ? line.slice(ind.length) : null : line;
1555
+ if (content === null) break;
1556
+ if (content.startsWith("## ") || content.startsWith("##!")) break;
1557
+ if (!content.startsWith("@")) break;
1558
+ const sp = content.indexOf(" ");
1559
+ if (sp < 0) break;
1560
+ const idStr = content.slice(1, sp);
1561
+ const id = parseInt(idStr, 10);
1562
+ if (!isNaN(id) && id !== items.length) {
1563
+ throw new Error(`invalid_item_id: expected @${items.length}, got @${idStr}`);
1564
+ }
1565
+ const marker = content.slice(sp + 1);
1566
+ if (marker.startsWith("=")) {
1567
+ items.push(parseScalar(marker.slice(1), false));
1568
+ i++;
1569
+ continue;
1570
+ }
1571
+ if (marker.startsWith("{}")) {
1572
+ const nested = {};
1573
+ i++;
1574
+ const consumed = parseObjectBody(lines, i, depth + 1, nested);
1575
+ items.push(nested);
1576
+ i += consumed;
1577
+ continue;
1578
+ }
1579
+ if (marker.startsWith("[")) {
1580
+ const [arr, consumed] = parseArrayFromHeader(lines, i, depth + 1, marker);
1581
+ items.push(arr);
1582
+ i += consumed;
1583
+ continue;
1584
+ }
1585
+ break;
1586
+ }
1587
+ return [items, i - start];
1588
+ }
1589
+ function parseCount(s) {
1590
+ if (s === "0") return 0;
1591
+ if (!s.length || s[0] === "0") throw new Error(`invalid_count: ${s}`);
1592
+ const n = parseInt(s, 10);
1593
+ if (isNaN(n) || String(n) !== s) throw new Error(`invalid_count: ${s}`);
1594
+ return n;
1595
+ }
1596
+ function validateSummaryCounts(summaryLine, deferredCount, contentLines) {
1597
+ const parts = summaryLine.split(/\s+/);
1598
+ let countsStr = "";
1599
+ for (const p of parts) {
1600
+ if (p.startsWith("counts=")) {
1601
+ countsStr = p.slice(7);
1602
+ break;
1603
+ }
1604
+ }
1605
+ if (!countsStr) return;
1606
+ const countVals = countsStr.split(",");
1607
+ if (countVals.length !== deferredCount) {
1608
+ throw new Error(`count_mismatch: summary has ${countVals.length} count entries but ${deferredCount} deferred sections`);
1609
+ }
1610
+ const actualCounts = [];
1611
+ let inDeferred = false;
1612
+ let currentCount = 0;
1613
+ for (const l of contentLines) {
1614
+ const trimmed = l.trimStart();
1615
+ if (trimmed.startsWith("## ") && trimmed.includes("[?]")) {
1616
+ if (inDeferred) actualCounts.push(currentCount);
1617
+ inDeferred = true;
1618
+ currentCount = 0;
1619
+ continue;
1620
+ }
1621
+ if (trimmed.startsWith("## ")) {
1622
+ if (inDeferred) {
1623
+ actualCounts.push(currentCount);
1624
+ inDeferred = false;
1625
+ }
1626
+ continue;
1627
+ }
1628
+ if (inDeferred && !trimmed.startsWith(" ") && !trimmed.startsWith(".")) {
1629
+ currentCount++;
1630
+ }
1631
+ }
1632
+ if (inDeferred) actualCounts.push(currentCount);
1633
+ for (let i = 0; i < countVals.length; i++) {
1634
+ const declared = parseInt(countVals[i], 10);
1635
+ if (isNaN(declared)) throw new Error(`count_mismatch: invalid count value "${countVals[i]}"`);
1636
+ if (i < actualCounts.length && declared !== actualCounts[i]) {
1637
+ throw new Error(`count_mismatch: section ${i} declared ${declared} in summary, actual ${actualCounts[i]}`);
1638
+ }
1639
+ }
1640
+ }
1641
+
1642
+ // src/stream.ts
1643
+ var StreamEncoder = class {
1644
+ w;
1645
+ symIndex = /* @__PURE__ */ new Map();
1646
+ nextID = 0;
1647
+ currentGroup = "";
1648
+ groupCounts = /* @__PURE__ */ new Map();
1649
+ edgeCount = 0;
1650
+ edgesStarted = false;
1651
+ constructor(w, tool, opts = {}) {
1652
+ this.w = w;
1653
+ this.writeHeader(tool, opts);
1654
+ }
1655
+ writeHeader(tool, opts) {
1656
+ const parts = [`GCF profile=graph tool=${tool}`];
1657
+ if (opts.tokenBudget) parts.push(`budget=${opts.tokenBudget}`);
1658
+ if (opts.tokensUsed) parts.push(`tokens=${opts.tokensUsed}`);
1659
+ if (opts.packRoot) parts.push(`pack_root=${opts.packRoot}`);
1660
+ if (opts.session) parts.push("session=true");
1661
+ this.w.write(parts.join(" ") + "\n");
1662
+ }
1663
+ /**
1664
+ * Emit a symbol line immediately. Group headers are emitted automatically
1665
+ * when the distance changes.
1666
+ */
1667
+ writeSymbol(s) {
1668
+ const groupNames = ["targets", "related", "extended"];
1669
+ const groupName = s.distance < groupNames.length ? groupNames[s.distance] : `distance_${s.distance}`;
1670
+ if (groupName !== this.currentGroup) {
1671
+ this.w.write(`## ${groupName}
1672
+ `);
1673
+ this.currentGroup = groupName;
1674
+ }
1675
+ const id = this.nextID++;
1676
+ this.symIndex.set(s.qualifiedName, id);
1677
+ const kind = KIND_ABBREV[s.kind] || s.kind;
1678
+ this.w.write(`@${id} ${kind} ${s.qualifiedName} ${s.score.toFixed(2)} ${s.provenance}
1679
+ `);
1680
+ this.groupCounts.set(groupName, (this.groupCounts.get(groupName) || 0) + 1);
1681
+ }
1682
+ /**
1683
+ * Emit an edge line immediately. The edges section header is emitted
1684
+ * automatically on the first edge (with [?] deferred count).
1685
+ * Source and target must reference previously-written symbols.
1686
+ */
1687
+ writeEdge(e) {
1688
+ const srcIdx = this.symIndex.get(e.source);
1689
+ const tgtIdx = this.symIndex.get(e.target);
1690
+ if (srcIdx === void 0 || tgtIdx === void 0) return;
1691
+ if (!this.edgesStarted) {
1692
+ this.w.write("## edges [?]\n");
1693
+ this.edgesStarted = true;
1694
+ }
1695
+ let line = `@${tgtIdx}<@${srcIdx} ${e.edgeType}`;
1696
+ if (e.status && e.status !== "unchanged") {
1697
+ line += ` ${e.status}`;
1698
+ }
1699
+ this.w.write(line + "\n");
1700
+ this.edgeCount++;
1701
+ }
1702
+ /**
1703
+ * Emit a bare reference for a previously-transmitted symbol (session mode).
1704
+ */
1705
+ writeBareRef(qname, distance) {
1706
+ const groupNames = ["targets", "related", "extended"];
1707
+ const groupName = distance < groupNames.length ? groupNames[distance] : `distance_${distance}`;
1708
+ if (groupName !== this.currentGroup) {
1709
+ this.w.write(`## ${groupName}
1710
+ `);
1711
+ this.currentGroup = groupName;
1712
+ }
1713
+ const id = this.nextID++;
1714
+ this.symIndex.set(qname, id);
1715
+ this.w.write(`@${id} # previously transmitted
1716
+ `);
1717
+ this.groupCounts.set(groupName, (this.groupCounts.get(groupName) || 0) + 1);
1718
+ }
1719
+ /**
1720
+ * Emit the ##! summary trailer with final counts. Must be called after all
1721
+ * symbols and edges have been written.
1722
+ */
1723
+ close() {
1724
+ const deferredCounts = [];
1725
+ const groupOrder = ["targets", "related", "extended"];
1726
+ for (const g of groupOrder) {
1727
+ const c = this.groupCounts.get(g);
1728
+ if (c && c > 0) deferredCounts.push(c);
1729
+ }
1730
+ for (const [g, c] of this.groupCounts) {
1731
+ if (!groupOrder.includes(g) && c > 0) deferredCounts.push(c);
1732
+ }
1733
+ if (this.edgeCount > 0) {
1734
+ deferredCounts.push(this.edgeCount);
1735
+ }
1736
+ this.w.write(`##! summary symbols=${this.nextID} edges=${this.edgeCount} counts=${deferredCounts.join(",")}
1737
+ `);
1738
+ }
1739
+ /** Number of symbols written so far. */
1740
+ get symbolCount() {
1741
+ return this.nextID;
1742
+ }
1743
+ /** Number of edges written so far. */
1744
+ get edgeCount_() {
1745
+ return this.edgeCount;
1746
+ }
1747
+ };
1748
+
1749
+ // src/stream_generic.ts
1750
+ var GenericStreamEncoder = class {
1751
+ writer;
1752
+ sections = [];
1753
+ current = null;
1754
+ constructor(writer) {
1755
+ this.writer = writer;
1756
+ }
1757
+ /** Start a tabular array section with deferred count [?]. */
1758
+ beginArray(name, fields) {
1759
+ if (this.current !== null) {
1760
+ this.endArrayInternal();
1761
+ }
1762
+ this.writer.write(`## ${name} [?]{${fields.join(",")}}
1763
+ `);
1764
+ this.current = { name, fields, count: 0 };
1765
+ }
1766
+ /** Emit a single pipe-separated row immediately. */
1767
+ writeRow(values) {
1768
+ if (this.current === null) {
1769
+ return;
1770
+ }
1771
+ const parts = values.map(formatValue);
1772
+ this.writer.write(`${parts.join("|")}
1773
+ `);
1774
+ this.current.count++;
1775
+ }
1776
+ /** Close the current array section and record its count. */
1777
+ endArray() {
1778
+ this.endArrayInternal();
1779
+ }
1780
+ /** Emit a key=value line immediately. */
1781
+ writeKV(key, value) {
1782
+ this.writer.write(`${key}=${formatValue(value)}
1783
+ `);
1784
+ }
1785
+ /** Start a nested object section (## key). */
1786
+ writeSection(name) {
1787
+ if (this.current !== null) {
1788
+ this.endArrayInternal();
1789
+ }
1790
+ this.writer.write(`## ${name}
1791
+ `);
1792
+ }
1793
+ /** Emit a primitive array inline: name[N]: val1,val2,val3 */
1794
+ writeInlineArray(name, values) {
1795
+ const parts = values.map(formatValue);
1796
+ this.writer.write(`${name}[${values.length}]: ${parts.join(",")}
1797
+ `);
1798
+ }
1799
+ /** Emit the ##! summary trailer with final counts. Must be called after all data. */
1800
+ close() {
1801
+ if (this.current !== null) {
1802
+ this.endArrayInternal();
1803
+ }
1804
+ if (this.sections.length === 0) {
1805
+ return;
1806
+ }
1807
+ const counts = this.sections.map((s) => String(s.count));
1808
+ this.writer.write(`##! summary counts=${counts.join(",")}
1809
+ `);
1810
+ }
1811
+ endArrayInternal() {
1812
+ if (this.current === null) {
1813
+ return;
1814
+ }
1815
+ this.sections.push({ name: this.current.name, count: this.current.count });
1816
+ this.current = null;
1817
+ }
1818
+ };
1819
+ function formatValue(v) {
1820
+ return formatScalar(v, 124);
1821
+ }
1822
+ // Annotate the CommonJS export names for ESM import in node:
1823
+ 0 && (module.exports = {
1824
+ GenericStreamEncoder,
1825
+ KIND_ABBREV,
1826
+ KIND_EXPAND,
1827
+ Session,
1828
+ StreamEncoder,
1829
+ decode,
1830
+ decodeGeneric,
1831
+ encode,
1832
+ encodeDelta,
1833
+ encodeGeneric,
1834
+ encodeWithSession,
1835
+ formatKey,
1836
+ formatScalar,
1837
+ needsQuote,
1838
+ parseScalar,
1839
+ quoteString,
1840
+ verifyDelta
1841
+ });