@blackwell-systems/gcf 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -21
- package/dist/decode_generic.d.ts +9 -0
- package/dist/decode_generic.d.ts.map +1 -0
- package/dist/decode_generic.js +236 -0
- package/dist/decode_generic.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/stream.d.ts +67 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +123 -0
- package/dist/stream.js.map +1 -0
- package/package.json +1 -1
- package/src/decode_generic.ts +237 -0
- package/src/index.ts +2 -0
- package/src/stream.ts +154 -0
package/README.md
CHANGED
|
@@ -44,33 +44,21 @@ Or install globally: `npm install -g @blackwell-systems/gcf` then use `gcf` dire
|
|
|
44
44
|
### Quick Start
|
|
45
45
|
|
|
46
46
|
```typescript
|
|
47
|
-
import {
|
|
47
|
+
import { encodeGeneric } from '@blackwell-systems/gcf';
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
symbols: [
|
|
54
|
-
{ qualifiedName: 'pkg.AuthMiddleware', kind: 'function', score: 0.78, provenance: 'lsp_resolved', distance: 0 },
|
|
55
|
-
{ qualifiedName: 'pkg.NewServer', kind: 'function', score: 0.54, provenance: 'lsp_resolved', distance: 1 },
|
|
56
|
-
],
|
|
57
|
-
edges: [
|
|
58
|
-
{ source: 'pkg.NewServer', target: 'pkg.AuthMiddleware', edgeType: 'calls' },
|
|
49
|
+
const output = encodeGeneric({
|
|
50
|
+
employees: [
|
|
51
|
+
{ id: 1, name: 'Alice', department: 'Engineering', salary: 95000 },
|
|
52
|
+
{ id: 2, name: 'Bob', department: 'Sales', salary: 72000 },
|
|
59
53
|
],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const output = encode(p);
|
|
54
|
+
});
|
|
63
55
|
```
|
|
64
56
|
|
|
65
57
|
Output:
|
|
66
58
|
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
## related
|
|
71
|
-
@1 fn pkg.NewServer 0.54 lsp_resolved
|
|
72
|
-
## edges [1]
|
|
73
|
-
@0<@1 calls
|
|
59
|
+
## employees [2]{id,name,department,salary}
|
|
60
|
+
1|Alice|Engineering|95000
|
|
61
|
+
2|Bob|Sales|72000
|
|
74
62
|
```
|
|
75
63
|
|
|
76
64
|
## Decode
|
|
@@ -97,6 +85,40 @@ const out2 = encodeWithSession(payload2, sess); // reused symbols as "@N # prev
|
|
|
97
85
|
|
|
98
86
|
By the 5th call in a session: 92.7% token savings vs JSON.
|
|
99
87
|
|
|
88
|
+
## Streaming Encode
|
|
89
|
+
|
|
90
|
+
Write GCF output incrementally as symbols and edges arrive. Zero buffering, O(1) memory per row. Ideal for MCP servers that walk large graphs or paginate results:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { StreamEncoder } from '@blackwell-systems/gcf';
|
|
94
|
+
|
|
95
|
+
const enc = new StreamEncoder(writer, 'context_for_task', { tokenBudget: 5000 });
|
|
96
|
+
|
|
97
|
+
// Symbols emit immediately as they're discovered.
|
|
98
|
+
enc.writeSymbol({ qualifiedName: 'pkg.Auth', kind: 'function', score: 0.95, provenance: 'lsp', distance: 0 });
|
|
99
|
+
enc.writeSymbol({ qualifiedName: 'pkg.Server', kind: 'function', score: 0.60, provenance: 'lsp', distance: 1 });
|
|
100
|
+
|
|
101
|
+
// Edges emit immediately too.
|
|
102
|
+
enc.writeEdge({ source: 'pkg.Server', target: 'pkg.Auth', edgeType: 'calls' });
|
|
103
|
+
|
|
104
|
+
// Close emits the ## _summary trailer with final counts.
|
|
105
|
+
enc.close();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Output:
|
|
109
|
+
```
|
|
110
|
+
GCF tool=context_for_task budget=5000
|
|
111
|
+
## targets
|
|
112
|
+
@0 fn pkg.Auth 0.95 lsp
|
|
113
|
+
## related
|
|
114
|
+
@1 fn pkg.Server 0.60 lsp
|
|
115
|
+
## edges [?]
|
|
116
|
+
@0<@1 calls
|
|
117
|
+
## _summary symbols=2 edges=1 sections=targets:1,related:1,edges:1
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `writer` is any object with a `write(s: string)` method (Node.js streams, web WritableStreams, or a simple callback). Standard `decode()` handles streaming output with no changes.
|
|
121
|
+
|
|
100
122
|
## Delta Encoding
|
|
101
123
|
|
|
102
124
|
When the consumer already has a prior context pack, send only what changed:
|
|
@@ -153,6 +175,7 @@ Works on objects, arrays, and primitives. Arrays of uniform objects get tabular
|
|
|
153
175
|
| `encodeGeneric(data: unknown): string` | Encode any value to GCF tabular format |
|
|
154
176
|
| `decode(input: string): Payload` | Parse GCF text back to a Payload |
|
|
155
177
|
| `encodeWithSession(p: Payload, s: Session): string` | Encode with session deduplication |
|
|
178
|
+
| `new StreamEncoder(w, tool, opts)` | Create a streaming encoder (zero-buffering) |
|
|
156
179
|
| `encodeDelta(d: DeltaPayload): string` | Encode a delta (added/removed only) |
|
|
157
180
|
| `new Session()` | Create a new session tracker |
|
|
158
181
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decode any GCF text (tabular or graph profile) back into a JS value.
|
|
3
|
+
* Returns objects, arrays, and primitives matching the original structure.
|
|
4
|
+
*
|
|
5
|
+
* If the input starts with "GCF " (graph profile), falls back to decode()
|
|
6
|
+
* and returns the Payload as a plain object.
|
|
7
|
+
*/
|
|
8
|
+
export declare function decodeGeneric(input: string): any;
|
|
9
|
+
//# sourceMappingURL=decode_generic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode_generic.d.ts","sourceRoot":"","sources":["../src/decode_generic.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAiChD"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { decode } from './decode.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decode any GCF text (tabular or graph profile) back into a JS value.
|
|
4
|
+
* Returns objects, arrays, and primitives matching the original structure.
|
|
5
|
+
*
|
|
6
|
+
* If the input starts with "GCF " (graph profile), falls back to decode()
|
|
7
|
+
* and returns the Payload as a plain object.
|
|
8
|
+
*/
|
|
9
|
+
export function decodeGeneric(input) {
|
|
10
|
+
input = input.trimEnd();
|
|
11
|
+
if (!input)
|
|
12
|
+
return null;
|
|
13
|
+
const lines = input.split('\n');
|
|
14
|
+
// Graph profile fallback.
|
|
15
|
+
if (lines[0].startsWith('GCF ')) {
|
|
16
|
+
const p = decode(input);
|
|
17
|
+
return {
|
|
18
|
+
tool: p.tool,
|
|
19
|
+
tokenBudget: p.tokenBudget,
|
|
20
|
+
tokensUsed: p.tokensUsed,
|
|
21
|
+
packRoot: p.packRoot ?? '',
|
|
22
|
+
symbols: p.symbols.map(s => ({
|
|
23
|
+
qualifiedName: s.qualifiedName,
|
|
24
|
+
kind: s.kind,
|
|
25
|
+
score: s.score,
|
|
26
|
+
provenance: s.provenance,
|
|
27
|
+
distance: s.distance,
|
|
28
|
+
})),
|
|
29
|
+
edges: p.edges.map(e => ({
|
|
30
|
+
source: e.source,
|
|
31
|
+
target: e.target,
|
|
32
|
+
edgeType: e.edgeType,
|
|
33
|
+
...(e.status ? { status: e.status } : {}),
|
|
34
|
+
})),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const result = {};
|
|
38
|
+
parseObject(lines, 0, 0, result);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
function parseObject(lines, start, depth, out) {
|
|
42
|
+
const indent = ' '.repeat(depth);
|
|
43
|
+
let i = start;
|
|
44
|
+
while (i < lines.length) {
|
|
45
|
+
const raw = lines[i].replace(/\r$/, '');
|
|
46
|
+
if (raw === '' || raw.startsWith('# ')) {
|
|
47
|
+
i++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (depth > 0 && !raw.startsWith(indent))
|
|
51
|
+
break;
|
|
52
|
+
const content = depth > 0 ? raw.slice(indent.length) : raw;
|
|
53
|
+
// Skip _summary.
|
|
54
|
+
if (content.startsWith('## _summary')) {
|
|
55
|
+
i++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Tabular array or section header.
|
|
59
|
+
if (content.startsWith('## ')) {
|
|
60
|
+
const header = content.slice(3);
|
|
61
|
+
const bracketIdx = header.indexOf(' [');
|
|
62
|
+
if (bracketIdx >= 0) {
|
|
63
|
+
const name = header.slice(0, bracketIdx);
|
|
64
|
+
const rest = header.slice(bracketIdx + 2);
|
|
65
|
+
const closeBracket = rest.indexOf(']');
|
|
66
|
+
if (closeBracket >= 0) {
|
|
67
|
+
const afterBracket = rest.slice(closeBracket + 1);
|
|
68
|
+
if (afterBracket.startsWith('{')) {
|
|
69
|
+
// Tabular with fields.
|
|
70
|
+
const fieldEnd = afterBracket.indexOf('}');
|
|
71
|
+
if (fieldEnd >= 0) {
|
|
72
|
+
const fields = afterBracket.slice(1, fieldEnd).split(',');
|
|
73
|
+
i++;
|
|
74
|
+
const [rows, consumed] = parseTabularRows(lines, i, depth, fields);
|
|
75
|
+
out[name] = rows;
|
|
76
|
+
i += consumed;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const countStr = rest.slice(0, closeBracket);
|
|
82
|
+
if (countStr === '0') {
|
|
83
|
+
out[name] = [];
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
// Non-uniform array.
|
|
88
|
+
i++;
|
|
89
|
+
const [items, consumed] = parseNonUniformArray(lines, i, depth);
|
|
90
|
+
out[name] = items;
|
|
91
|
+
i += consumed;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Plain section header.
|
|
97
|
+
let name = header;
|
|
98
|
+
const bi = name.indexOf(' [');
|
|
99
|
+
if (bi >= 0)
|
|
100
|
+
name = name.slice(0, bi);
|
|
101
|
+
i++;
|
|
102
|
+
const nested = {};
|
|
103
|
+
const consumed = parseObject(lines, i, depth + 1, nested);
|
|
104
|
+
out[name] = nested;
|
|
105
|
+
i += consumed;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
// Inline primitive array: name[N]: val1,val2,...
|
|
109
|
+
const bracketIdx = content.indexOf('[');
|
|
110
|
+
if (bracketIdx > 0) {
|
|
111
|
+
const colonIdx = content.indexOf(']: ');
|
|
112
|
+
if (colonIdx > bracketIdx) {
|
|
113
|
+
const name = content.slice(0, bracketIdx);
|
|
114
|
+
const valsStr = content.slice(colonIdx + 3);
|
|
115
|
+
out[name] = valsStr.split(',').map(v => parseValue(v.trim()));
|
|
116
|
+
i++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Key=value.
|
|
121
|
+
const eqIdx = content.indexOf('=');
|
|
122
|
+
if (eqIdx > 0) {
|
|
123
|
+
const key = content.slice(0, eqIdx);
|
|
124
|
+
const val = content.slice(eqIdx + 1);
|
|
125
|
+
out[key] = parseValue(val);
|
|
126
|
+
i++;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
i++;
|
|
130
|
+
}
|
|
131
|
+
return i - start;
|
|
132
|
+
}
|
|
133
|
+
function parseTabularRows(lines, start, depth, fields) {
|
|
134
|
+
const indent = ' '.repeat(depth);
|
|
135
|
+
const rows = [];
|
|
136
|
+
let i = start;
|
|
137
|
+
while (i < lines.length) {
|
|
138
|
+
const raw = lines[i].replace(/\r$/, '');
|
|
139
|
+
if (raw === '') {
|
|
140
|
+
i++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
|
|
144
|
+
if (content === null)
|
|
145
|
+
break;
|
|
146
|
+
if (content.startsWith('## '))
|
|
147
|
+
break;
|
|
148
|
+
if (content.startsWith('# ')) {
|
|
149
|
+
i++;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
let rowData = content;
|
|
153
|
+
let hasNested = false;
|
|
154
|
+
if (rowData.startsWith('@')) {
|
|
155
|
+
const sp = rowData.indexOf(' ');
|
|
156
|
+
if (sp > 0) {
|
|
157
|
+
rowData = rowData.slice(sp + 1);
|
|
158
|
+
hasNested = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const vals = rowData.split('|');
|
|
162
|
+
const row = {};
|
|
163
|
+
for (let j = 0; j < fields.length; j++) {
|
|
164
|
+
row[fields[j]] = j < vals.length ? parseValue(vals[j]) : null;
|
|
165
|
+
}
|
|
166
|
+
i++;
|
|
167
|
+
if (hasNested) {
|
|
168
|
+
const nestedIndent = indent + ' ';
|
|
169
|
+
while (i < lines.length) {
|
|
170
|
+
const nl = lines[i].replace(/\r$/, '');
|
|
171
|
+
if (!nl.startsWith(nestedIndent))
|
|
172
|
+
break;
|
|
173
|
+
const nc = nl.slice(nestedIndent.length);
|
|
174
|
+
if (nc.startsWith('.')) {
|
|
175
|
+
const fieldName = nc.slice(1);
|
|
176
|
+
i++;
|
|
177
|
+
const nested = {};
|
|
178
|
+
const consumed = parseObject(lines, i, depth + 2, nested);
|
|
179
|
+
row[fieldName] = nested;
|
|
180
|
+
i += consumed;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
rows.push(row);
|
|
188
|
+
}
|
|
189
|
+
return [rows, i - start];
|
|
190
|
+
}
|
|
191
|
+
function parseNonUniformArray(lines, start, depth) {
|
|
192
|
+
const indent = ' '.repeat(depth);
|
|
193
|
+
const items = [];
|
|
194
|
+
let i = start;
|
|
195
|
+
while (i < lines.length) {
|
|
196
|
+
const raw = lines[i].replace(/\r$/, '');
|
|
197
|
+
if (raw === '') {
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
|
|
202
|
+
if (content === null)
|
|
203
|
+
break;
|
|
204
|
+
if (content.startsWith('## '))
|
|
205
|
+
break;
|
|
206
|
+
if (content.startsWith('@')) {
|
|
207
|
+
const sp = content.indexOf(' ');
|
|
208
|
+
if (sp > 0) {
|
|
209
|
+
items.push(parseValue(content.slice(sp + 1)));
|
|
210
|
+
}
|
|
211
|
+
i++;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return [items, i - start];
|
|
218
|
+
}
|
|
219
|
+
function parseValue(s) {
|
|
220
|
+
if (s === '-')
|
|
221
|
+
return null;
|
|
222
|
+
if (s === 'true')
|
|
223
|
+
return true;
|
|
224
|
+
if (s === 'false')
|
|
225
|
+
return false;
|
|
226
|
+
if (s === '""')
|
|
227
|
+
return '';
|
|
228
|
+
if (s.length >= 2 && s[0] === '"' && s[s.length - 1] === '"') {
|
|
229
|
+
return s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
230
|
+
}
|
|
231
|
+
const n = Number(s);
|
|
232
|
+
if (!isNaN(n) && s !== '')
|
|
233
|
+
return n;
|
|
234
|
+
return s;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=decode_generic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode_generic.js","sourceRoot":"","sources":["../src/decode_generic.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,6 @@ export { decode } from './decode.js';
|
|
|
5
5
|
export { Session, encodeWithSession } from './session.js';
|
|
6
6
|
export { encodeDelta } from './delta.js';
|
|
7
7
|
export { encodeGeneric } from './generic.js';
|
|
8
|
+
export { decodeGeneric } from './decode_generic.js';
|
|
9
|
+
export { StreamEncoder, type StreamWriter, type StreamOptions } from './stream.js';
|
|
8
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,6 @@ export { decode } from './decode.js';
|
|
|
4
4
|
export { Session, encodeWithSession } from './session.js';
|
|
5
5
|
export { encodeDelta } from './delta.js';
|
|
6
6
|
export { encodeGeneric } from './generic.js';
|
|
7
|
+
export { decodeGeneric } from './decode_generic.js';
|
|
8
|
+
export { StreamEncoder } from './stream.js';
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAyC,MAAM,aAAa,CAAC"}
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Symbol, Edge } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for the streaming encoder.
|
|
4
|
+
*/
|
|
5
|
+
export interface StreamOptions {
|
|
6
|
+
tokenBudget?: number;
|
|
7
|
+
tokensUsed?: number;
|
|
8
|
+
packRoot?: string;
|
|
9
|
+
session?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A writable sink for streaming output. Accepts string chunks.
|
|
13
|
+
* Compatible with Node.js streams, web WritableStreams, or simple callbacks.
|
|
14
|
+
*/
|
|
15
|
+
export interface StreamWriter {
|
|
16
|
+
write(chunk: string): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* StreamEncoder writes GCF output incrementally as symbols and edges arrive.
|
|
20
|
+
* Zero buffering: each symbol/edge is written immediately. A trailer summary
|
|
21
|
+
* is emitted on close() with the final counts.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const chunks: string[] = [];
|
|
26
|
+
* const enc = new StreamEncoder({ write: (s) => chunks.push(s) }, 'context_for_task', { tokenBudget: 5000 });
|
|
27
|
+
* enc.writeSymbol({ qualifiedName: 'pkg.Auth', kind: 'function', score: 0.95, provenance: 'lsp', distance: 0 });
|
|
28
|
+
* enc.writeEdge({ source: 'pkg.Server', target: 'pkg.Auth', edgeType: 'calls' });
|
|
29
|
+
* enc.close();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class StreamEncoder {
|
|
33
|
+
private w;
|
|
34
|
+
private symIndex;
|
|
35
|
+
private nextID;
|
|
36
|
+
private currentGroup;
|
|
37
|
+
private groupCounts;
|
|
38
|
+
private edgeCount;
|
|
39
|
+
private edgesStarted;
|
|
40
|
+
constructor(w: StreamWriter, tool: string, opts?: StreamOptions);
|
|
41
|
+
private writeHeader;
|
|
42
|
+
/**
|
|
43
|
+
* Emit a symbol line immediately. Group headers are emitted automatically
|
|
44
|
+
* when the distance changes.
|
|
45
|
+
*/
|
|
46
|
+
writeSymbol(s: Symbol): void;
|
|
47
|
+
/**
|
|
48
|
+
* Emit an edge line immediately. The edges section header is emitted
|
|
49
|
+
* automatically on the first edge (with [?] deferred count).
|
|
50
|
+
* Source and target must reference previously-written symbols.
|
|
51
|
+
*/
|
|
52
|
+
writeEdge(e: Edge): void;
|
|
53
|
+
/**
|
|
54
|
+
* Emit a bare reference for a previously-transmitted symbol (session mode).
|
|
55
|
+
*/
|
|
56
|
+
writeBareRef(qname: string, distance: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* Emit the ## _summary trailer with final counts. Must be called after all
|
|
59
|
+
* symbols and edges have been written.
|
|
60
|
+
*/
|
|
61
|
+
close(): void;
|
|
62
|
+
/** Number of symbols written so far. */
|
|
63
|
+
get symbolCount(): number;
|
|
64
|
+
/** Number of edges written so far. */
|
|
65
|
+
get edgeCount_(): number;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,CAAC,CAAe;IACxB,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,YAAY,CAAS;gBAEjB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB;IAKnE,OAAO,CAAC,WAAW;IASnB;;;OAGG;IACH,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAoB5B;;;;OAIG;IACH,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI;IAkBxB;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAkBnD;;;OAGG;IACH,KAAK,IAAI,IAAI;IAkBb,wCAAwC;IACxC,IAAI,WAAW,IAAI,MAAM,CAAwB;IAEjD,sCAAsC;IACtC,IAAI,UAAU,IAAI,MAAM,CAA2B;CACpD"}
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { KIND_ABBREV } from './constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* StreamEncoder writes GCF output incrementally as symbols and edges arrive.
|
|
4
|
+
* Zero buffering: each symbol/edge is written immediately. A trailer summary
|
|
5
|
+
* is emitted on close() with the final counts.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const chunks: string[] = [];
|
|
10
|
+
* const enc = new StreamEncoder({ write: (s) => chunks.push(s) }, 'context_for_task', { tokenBudget: 5000 });
|
|
11
|
+
* enc.writeSymbol({ qualifiedName: 'pkg.Auth', kind: 'function', score: 0.95, provenance: 'lsp', distance: 0 });
|
|
12
|
+
* enc.writeEdge({ source: 'pkg.Server', target: 'pkg.Auth', edgeType: 'calls' });
|
|
13
|
+
* enc.close();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class StreamEncoder {
|
|
17
|
+
w;
|
|
18
|
+
symIndex = new Map();
|
|
19
|
+
nextID = 0;
|
|
20
|
+
currentGroup = '';
|
|
21
|
+
groupCounts = new Map();
|
|
22
|
+
edgeCount = 0;
|
|
23
|
+
edgesStarted = false;
|
|
24
|
+
constructor(w, tool, opts = {}) {
|
|
25
|
+
this.w = w;
|
|
26
|
+
this.writeHeader(tool, opts);
|
|
27
|
+
}
|
|
28
|
+
writeHeader(tool, opts) {
|
|
29
|
+
const parts = [`GCF tool=${tool}`];
|
|
30
|
+
if (opts.tokenBudget)
|
|
31
|
+
parts.push(`budget=${opts.tokenBudget}`);
|
|
32
|
+
if (opts.tokensUsed)
|
|
33
|
+
parts.push(`tokens=${opts.tokensUsed}`);
|
|
34
|
+
if (opts.packRoot)
|
|
35
|
+
parts.push(`pack_root=${opts.packRoot}`);
|
|
36
|
+
if (opts.session)
|
|
37
|
+
parts.push('session=true');
|
|
38
|
+
this.w.write(parts.join(' ') + '\n');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Emit a symbol line immediately. Group headers are emitted automatically
|
|
42
|
+
* when the distance changes.
|
|
43
|
+
*/
|
|
44
|
+
writeSymbol(s) {
|
|
45
|
+
const groupNames = ['targets', 'related', 'extended'];
|
|
46
|
+
const groupName = s.distance < groupNames.length
|
|
47
|
+
? groupNames[s.distance]
|
|
48
|
+
: `distance_${s.distance}`;
|
|
49
|
+
if (groupName !== this.currentGroup) {
|
|
50
|
+
this.w.write(`## ${groupName}\n`);
|
|
51
|
+
this.currentGroup = groupName;
|
|
52
|
+
}
|
|
53
|
+
const id = this.nextID++;
|
|
54
|
+
this.symIndex.set(s.qualifiedName, id);
|
|
55
|
+
const kind = KIND_ABBREV[s.kind] || s.kind;
|
|
56
|
+
this.w.write(`@${id} ${kind} ${s.qualifiedName} ${s.score.toFixed(2)} ${s.provenance}\n`);
|
|
57
|
+
this.groupCounts.set(groupName, (this.groupCounts.get(groupName) || 0) + 1);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Emit an edge line immediately. The edges section header is emitted
|
|
61
|
+
* automatically on the first edge (with [?] deferred count).
|
|
62
|
+
* Source and target must reference previously-written symbols.
|
|
63
|
+
*/
|
|
64
|
+
writeEdge(e) {
|
|
65
|
+
const srcIdx = this.symIndex.get(e.source);
|
|
66
|
+
const tgtIdx = this.symIndex.get(e.target);
|
|
67
|
+
if (srcIdx === undefined || tgtIdx === undefined)
|
|
68
|
+
return;
|
|
69
|
+
if (!this.edgesStarted) {
|
|
70
|
+
this.w.write('## edges [?]\n');
|
|
71
|
+
this.edgesStarted = true;
|
|
72
|
+
}
|
|
73
|
+
let line = `@${tgtIdx}<@${srcIdx} ${e.edgeType}`;
|
|
74
|
+
if (e.status && e.status !== 'unchanged') {
|
|
75
|
+
line += ` ${e.status}`;
|
|
76
|
+
}
|
|
77
|
+
this.w.write(line + '\n');
|
|
78
|
+
this.edgeCount++;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Emit a bare reference for a previously-transmitted symbol (session mode).
|
|
82
|
+
*/
|
|
83
|
+
writeBareRef(qname, distance) {
|
|
84
|
+
const groupNames = ['targets', 'related', 'extended'];
|
|
85
|
+
const groupName = distance < groupNames.length
|
|
86
|
+
? groupNames[distance]
|
|
87
|
+
: `distance_${distance}`;
|
|
88
|
+
if (groupName !== this.currentGroup) {
|
|
89
|
+
this.w.write(`## ${groupName}\n`);
|
|
90
|
+
this.currentGroup = groupName;
|
|
91
|
+
}
|
|
92
|
+
const id = this.nextID++;
|
|
93
|
+
this.symIndex.set(qname, id);
|
|
94
|
+
this.w.write(`@${id} # previously transmitted\n`);
|
|
95
|
+
this.groupCounts.set(groupName, (this.groupCounts.get(groupName) || 0) + 1);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Emit the ## _summary trailer with final counts. Must be called after all
|
|
99
|
+
* symbols and edges have been written.
|
|
100
|
+
*/
|
|
101
|
+
close() {
|
|
102
|
+
const sections = [];
|
|
103
|
+
const groupOrder = ['targets', 'related', 'extended'];
|
|
104
|
+
for (const g of groupOrder) {
|
|
105
|
+
const c = this.groupCounts.get(g);
|
|
106
|
+
if (c && c > 0)
|
|
107
|
+
sections.push(`${g}:${c}`);
|
|
108
|
+
}
|
|
109
|
+
for (const [g, c] of this.groupCounts) {
|
|
110
|
+
if (!groupOrder.includes(g) && c > 0)
|
|
111
|
+
sections.push(`${g}:${c}`);
|
|
112
|
+
}
|
|
113
|
+
if (this.edgeCount > 0) {
|
|
114
|
+
sections.push(`edges:${this.edgeCount}`);
|
|
115
|
+
}
|
|
116
|
+
this.w.write(`## _summary symbols=${this.nextID} edges=${this.edgeCount} sections=${sections.join(',')}\n`);
|
|
117
|
+
}
|
|
118
|
+
/** Number of symbols written so far. */
|
|
119
|
+
get symbolCount() { return this.nextID; }
|
|
120
|
+
/** Number of edges written so far. */
|
|
121
|
+
get edgeCount_() { return this.edgeCount; }
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { decode } from './decode.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Decode any GCF text (tabular or graph profile) back into a JS value.
|
|
5
|
+
* Returns objects, arrays, and primitives matching the original structure.
|
|
6
|
+
*
|
|
7
|
+
* If the input starts with "GCF " (graph profile), falls back to decode()
|
|
8
|
+
* and returns the Payload as a plain object.
|
|
9
|
+
*/
|
|
10
|
+
export function decodeGeneric(input: string): any {
|
|
11
|
+
input = input.trimEnd();
|
|
12
|
+
if (!input) return null;
|
|
13
|
+
|
|
14
|
+
const lines = input.split('\n');
|
|
15
|
+
|
|
16
|
+
// Graph profile fallback.
|
|
17
|
+
if (lines[0].startsWith('GCF ')) {
|
|
18
|
+
const p = decode(input);
|
|
19
|
+
return {
|
|
20
|
+
tool: p.tool,
|
|
21
|
+
tokenBudget: p.tokenBudget,
|
|
22
|
+
tokensUsed: p.tokensUsed,
|
|
23
|
+
packRoot: p.packRoot ?? '',
|
|
24
|
+
symbols: p.symbols.map(s => ({
|
|
25
|
+
qualifiedName: s.qualifiedName,
|
|
26
|
+
kind: s.kind,
|
|
27
|
+
score: s.score,
|
|
28
|
+
provenance: s.provenance,
|
|
29
|
+
distance: s.distance,
|
|
30
|
+
})),
|
|
31
|
+
edges: p.edges.map(e => ({
|
|
32
|
+
source: e.source,
|
|
33
|
+
target: e.target,
|
|
34
|
+
edgeType: e.edgeType,
|
|
35
|
+
...(e.status ? { status: e.status } : {}),
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result: Record<string, any> = {};
|
|
41
|
+
parseObject(lines, 0, 0, result);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseObject(lines: string[], start: number, depth: number, out: Record<string, any>): number {
|
|
46
|
+
const indent = ' '.repeat(depth);
|
|
47
|
+
let i = start;
|
|
48
|
+
|
|
49
|
+
while (i < lines.length) {
|
|
50
|
+
const raw = lines[i].replace(/\r$/, '');
|
|
51
|
+
if (raw === '' || raw.startsWith('# ')) { i++; continue; }
|
|
52
|
+
|
|
53
|
+
if (depth > 0 && !raw.startsWith(indent)) break;
|
|
54
|
+
|
|
55
|
+
const content = depth > 0 ? raw.slice(indent.length) : raw;
|
|
56
|
+
|
|
57
|
+
// Skip _summary.
|
|
58
|
+
if (content.startsWith('## _summary')) { i++; continue; }
|
|
59
|
+
|
|
60
|
+
// Tabular array or section header.
|
|
61
|
+
if (content.startsWith('## ')) {
|
|
62
|
+
const header = content.slice(3);
|
|
63
|
+
const bracketIdx = header.indexOf(' [');
|
|
64
|
+
|
|
65
|
+
if (bracketIdx >= 0) {
|
|
66
|
+
const name = header.slice(0, bracketIdx);
|
|
67
|
+
const rest = header.slice(bracketIdx + 2);
|
|
68
|
+
const closeBracket = rest.indexOf(']');
|
|
69
|
+
|
|
70
|
+
if (closeBracket >= 0) {
|
|
71
|
+
const afterBracket = rest.slice(closeBracket + 1);
|
|
72
|
+
|
|
73
|
+
if (afterBracket.startsWith('{')) {
|
|
74
|
+
// Tabular with fields.
|
|
75
|
+
const fieldEnd = afterBracket.indexOf('}');
|
|
76
|
+
if (fieldEnd >= 0) {
|
|
77
|
+
const fields = afterBracket.slice(1, fieldEnd).split(',');
|
|
78
|
+
i++;
|
|
79
|
+
const [rows, consumed] = parseTabularRows(lines, i, depth, fields);
|
|
80
|
+
out[name] = rows;
|
|
81
|
+
i += consumed;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
const countStr = rest.slice(0, closeBracket);
|
|
86
|
+
if (countStr === '0') {
|
|
87
|
+
out[name] = [];
|
|
88
|
+
i++;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// Non-uniform array.
|
|
92
|
+
i++;
|
|
93
|
+
const [items, consumed] = parseNonUniformArray(lines, i, depth);
|
|
94
|
+
out[name] = items;
|
|
95
|
+
i += consumed;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Plain section header.
|
|
102
|
+
let name = header;
|
|
103
|
+
const bi = name.indexOf(' [');
|
|
104
|
+
if (bi >= 0) name = name.slice(0, bi);
|
|
105
|
+
i++;
|
|
106
|
+
const nested: Record<string, any> = {};
|
|
107
|
+
const consumed = parseObject(lines, i, depth + 1, nested);
|
|
108
|
+
out[name] = nested;
|
|
109
|
+
i += consumed;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Inline primitive array: name[N]: val1,val2,...
|
|
114
|
+
const bracketIdx = content.indexOf('[');
|
|
115
|
+
if (bracketIdx > 0) {
|
|
116
|
+
const colonIdx = content.indexOf(']: ');
|
|
117
|
+
if (colonIdx > bracketIdx) {
|
|
118
|
+
const name = content.slice(0, bracketIdx);
|
|
119
|
+
const valsStr = content.slice(colonIdx + 3);
|
|
120
|
+
out[name] = valsStr.split(',').map(v => parseValue(v.trim()));
|
|
121
|
+
i++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Key=value.
|
|
127
|
+
const eqIdx = content.indexOf('=');
|
|
128
|
+
if (eqIdx > 0) {
|
|
129
|
+
const key = content.slice(0, eqIdx);
|
|
130
|
+
const val = content.slice(eqIdx + 1);
|
|
131
|
+
out[key] = parseValue(val);
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return i - start;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parseTabularRows(lines: string[], start: number, depth: number, fields: string[]): [any[], number] {
|
|
143
|
+
const indent = ' '.repeat(depth);
|
|
144
|
+
const rows: any[] = [];
|
|
145
|
+
let i = start;
|
|
146
|
+
|
|
147
|
+
while (i < lines.length) {
|
|
148
|
+
const raw = lines[i].replace(/\r$/, '');
|
|
149
|
+
if (raw === '') { i++; continue; }
|
|
150
|
+
|
|
151
|
+
const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
|
|
152
|
+
if (content === null) break;
|
|
153
|
+
if (content.startsWith('## ')) break;
|
|
154
|
+
if (content.startsWith('# ')) { i++; continue; }
|
|
155
|
+
|
|
156
|
+
let rowData = content;
|
|
157
|
+
let hasNested = false;
|
|
158
|
+
if (rowData.startsWith('@')) {
|
|
159
|
+
const sp = rowData.indexOf(' ');
|
|
160
|
+
if (sp > 0) {
|
|
161
|
+
rowData = rowData.slice(sp + 1);
|
|
162
|
+
hasNested = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const vals = rowData.split('|');
|
|
167
|
+
const row: Record<string, any> = {};
|
|
168
|
+
for (let j = 0; j < fields.length; j++) {
|
|
169
|
+
row[fields[j]] = j < vals.length ? parseValue(vals[j]) : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
i++;
|
|
173
|
+
|
|
174
|
+
if (hasNested) {
|
|
175
|
+
const nestedIndent = indent + ' ';
|
|
176
|
+
while (i < lines.length) {
|
|
177
|
+
const nl = lines[i].replace(/\r$/, '');
|
|
178
|
+
if (!nl.startsWith(nestedIndent)) break;
|
|
179
|
+
const nc = nl.slice(nestedIndent.length);
|
|
180
|
+
|
|
181
|
+
if (nc.startsWith('.')) {
|
|
182
|
+
const fieldName = nc.slice(1);
|
|
183
|
+
i++;
|
|
184
|
+
const nested: Record<string, any> = {};
|
|
185
|
+
const consumed = parseObject(lines, i, depth + 2, nested);
|
|
186
|
+
row[fieldName] = nested;
|
|
187
|
+
i += consumed;
|
|
188
|
+
} else {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
rows.push(row);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return [rows, i - start];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function parseNonUniformArray(lines: string[], start: number, depth: number): [any[], number] {
|
|
201
|
+
const indent = ' '.repeat(depth);
|
|
202
|
+
const items: any[] = [];
|
|
203
|
+
let i = start;
|
|
204
|
+
|
|
205
|
+
while (i < lines.length) {
|
|
206
|
+
const raw = lines[i].replace(/\r$/, '');
|
|
207
|
+
if (raw === '') { i++; continue; }
|
|
208
|
+
const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
|
|
209
|
+
if (content === null) break;
|
|
210
|
+
if (content.startsWith('## ')) break;
|
|
211
|
+
|
|
212
|
+
if (content.startsWith('@')) {
|
|
213
|
+
const sp = content.indexOf(' ');
|
|
214
|
+
if (sp > 0) {
|
|
215
|
+
items.push(parseValue(content.slice(sp + 1)));
|
|
216
|
+
}
|
|
217
|
+
i++;
|
|
218
|
+
} else {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return [items, i - start];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function parseValue(s: string): any {
|
|
227
|
+
if (s === '-') return null;
|
|
228
|
+
if (s === 'true') return true;
|
|
229
|
+
if (s === 'false') return false;
|
|
230
|
+
if (s === '""') return '';
|
|
231
|
+
if (s.length >= 2 && s[0] === '"' && s[s.length - 1] === '"') {
|
|
232
|
+
return s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
233
|
+
}
|
|
234
|
+
const n = Number(s);
|
|
235
|
+
if (!isNaN(n) && s !== '') return n;
|
|
236
|
+
return s;
|
|
237
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,3 +5,5 @@ export { decode } from './decode.js';
|
|
|
5
5
|
export { Session, encodeWithSession } from './session.js';
|
|
6
6
|
export { encodeDelta } from './delta.js';
|
|
7
7
|
export { encodeGeneric } from './generic.js';
|
|
8
|
+
export { decodeGeneric } from './decode_generic.js';
|
|
9
|
+
export { StreamEncoder, type StreamWriter, type StreamOptions } from './stream.js';
|
package/src/stream.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { KIND_ABBREV } from './constants.js';
|
|
2
|
+
import type { Symbol, Edge } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options for the streaming encoder.
|
|
6
|
+
*/
|
|
7
|
+
export interface StreamOptions {
|
|
8
|
+
tokenBudget?: number;
|
|
9
|
+
tokensUsed?: number;
|
|
10
|
+
packRoot?: string;
|
|
11
|
+
session?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A writable sink for streaming output. Accepts string chunks.
|
|
16
|
+
* Compatible with Node.js streams, web WritableStreams, or simple callbacks.
|
|
17
|
+
*/
|
|
18
|
+
export interface StreamWriter {
|
|
19
|
+
write(chunk: string): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* StreamEncoder writes GCF output incrementally as symbols and edges arrive.
|
|
24
|
+
* Zero buffering: each symbol/edge is written immediately. A trailer summary
|
|
25
|
+
* is emitted on close() with the final counts.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const chunks: string[] = [];
|
|
30
|
+
* const enc = new StreamEncoder({ write: (s) => chunks.push(s) }, 'context_for_task', { tokenBudget: 5000 });
|
|
31
|
+
* enc.writeSymbol({ qualifiedName: 'pkg.Auth', kind: 'function', score: 0.95, provenance: 'lsp', distance: 0 });
|
|
32
|
+
* enc.writeEdge({ source: 'pkg.Server', target: 'pkg.Auth', edgeType: 'calls' });
|
|
33
|
+
* enc.close();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class StreamEncoder {
|
|
37
|
+
private w: StreamWriter;
|
|
38
|
+
private symIndex: Map<string, number> = new Map();
|
|
39
|
+
private nextID = 0;
|
|
40
|
+
private currentGroup = '';
|
|
41
|
+
private groupCounts: Map<string, number> = new Map();
|
|
42
|
+
private edgeCount = 0;
|
|
43
|
+
private edgesStarted = false;
|
|
44
|
+
|
|
45
|
+
constructor(w: StreamWriter, tool: string, opts: StreamOptions = {}) {
|
|
46
|
+
this.w = w;
|
|
47
|
+
this.writeHeader(tool, opts);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private writeHeader(tool: string, opts: StreamOptions): void {
|
|
51
|
+
const parts = [`GCF tool=${tool}`];
|
|
52
|
+
if (opts.tokenBudget) parts.push(`budget=${opts.tokenBudget}`);
|
|
53
|
+
if (opts.tokensUsed) parts.push(`tokens=${opts.tokensUsed}`);
|
|
54
|
+
if (opts.packRoot) parts.push(`pack_root=${opts.packRoot}`);
|
|
55
|
+
if (opts.session) parts.push('session=true');
|
|
56
|
+
this.w.write(parts.join(' ') + '\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Emit a symbol line immediately. Group headers are emitted automatically
|
|
61
|
+
* when the distance changes.
|
|
62
|
+
*/
|
|
63
|
+
writeSymbol(s: Symbol): void {
|
|
64
|
+
const groupNames = ['targets', 'related', 'extended'];
|
|
65
|
+
const groupName = s.distance < groupNames.length
|
|
66
|
+
? groupNames[s.distance]
|
|
67
|
+
: `distance_${s.distance}`;
|
|
68
|
+
|
|
69
|
+
if (groupName !== this.currentGroup) {
|
|
70
|
+
this.w.write(`## ${groupName}\n`);
|
|
71
|
+
this.currentGroup = groupName;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const id = this.nextID++;
|
|
75
|
+
this.symIndex.set(s.qualifiedName, id);
|
|
76
|
+
|
|
77
|
+
const kind = KIND_ABBREV[s.kind] || s.kind;
|
|
78
|
+
this.w.write(`@${id} ${kind} ${s.qualifiedName} ${s.score.toFixed(2)} ${s.provenance}\n`);
|
|
79
|
+
|
|
80
|
+
this.groupCounts.set(groupName, (this.groupCounts.get(groupName) || 0) + 1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Emit an edge line immediately. The edges section header is emitted
|
|
85
|
+
* automatically on the first edge (with [?] deferred count).
|
|
86
|
+
* Source and target must reference previously-written symbols.
|
|
87
|
+
*/
|
|
88
|
+
writeEdge(e: Edge): void {
|
|
89
|
+
const srcIdx = this.symIndex.get(e.source);
|
|
90
|
+
const tgtIdx = this.symIndex.get(e.target);
|
|
91
|
+
if (srcIdx === undefined || tgtIdx === undefined) return;
|
|
92
|
+
|
|
93
|
+
if (!this.edgesStarted) {
|
|
94
|
+
this.w.write('## edges [?]\n');
|
|
95
|
+
this.edgesStarted = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let line = `@${tgtIdx}<@${srcIdx} ${e.edgeType}`;
|
|
99
|
+
if (e.status && e.status !== 'unchanged') {
|
|
100
|
+
line += ` ${e.status}`;
|
|
101
|
+
}
|
|
102
|
+
this.w.write(line + '\n');
|
|
103
|
+
this.edgeCount++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Emit a bare reference for a previously-transmitted symbol (session mode).
|
|
108
|
+
*/
|
|
109
|
+
writeBareRef(qname: string, distance: number): void {
|
|
110
|
+
const groupNames = ['targets', 'related', 'extended'];
|
|
111
|
+
const groupName = distance < groupNames.length
|
|
112
|
+
? groupNames[distance]
|
|
113
|
+
: `distance_${distance}`;
|
|
114
|
+
|
|
115
|
+
if (groupName !== this.currentGroup) {
|
|
116
|
+
this.w.write(`## ${groupName}\n`);
|
|
117
|
+
this.currentGroup = groupName;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const id = this.nextID++;
|
|
121
|
+
this.symIndex.set(qname, id);
|
|
122
|
+
this.w.write(`@${id} # previously transmitted\n`);
|
|
123
|
+
|
|
124
|
+
this.groupCounts.set(groupName, (this.groupCounts.get(groupName) || 0) + 1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Emit the ## _summary trailer with final counts. Must be called after all
|
|
129
|
+
* symbols and edges have been written.
|
|
130
|
+
*/
|
|
131
|
+
close(): void {
|
|
132
|
+
const sections: string[] = [];
|
|
133
|
+
const groupOrder = ['targets', 'related', 'extended'];
|
|
134
|
+
|
|
135
|
+
for (const g of groupOrder) {
|
|
136
|
+
const c = this.groupCounts.get(g);
|
|
137
|
+
if (c && c > 0) sections.push(`${g}:${c}`);
|
|
138
|
+
}
|
|
139
|
+
for (const [g, c] of this.groupCounts) {
|
|
140
|
+
if (!groupOrder.includes(g) && c > 0) sections.push(`${g}:${c}`);
|
|
141
|
+
}
|
|
142
|
+
if (this.edgeCount > 0) {
|
|
143
|
+
sections.push(`edges:${this.edgeCount}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.w.write(`## _summary symbols=${this.nextID} edges=${this.edgeCount} sections=${sections.join(',')}\n`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Number of symbols written so far. */
|
|
150
|
+
get symbolCount(): number { return this.nextID; }
|
|
151
|
+
|
|
152
|
+
/** Number of edges written so far. */
|
|
153
|
+
get edgeCount_(): number { return this.edgeCount; }
|
|
154
|
+
}
|