@blackwell-systems/gcf 0.4.0 → 0.6.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 +10 -22
- 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_generic.d.ts +38 -0
- package/dist/stream_generic.d.ts.map +1 -0
- package/dist/stream_generic.js +105 -0
- package/dist/stream_generic.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_generic.ts +127 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
# gcf-typescript
|
|
8
8
|
|
|
9
|
-
TypeScript implementation of [GCF
|
|
9
|
+
TypeScript implementation of [GCF](https://gcformat.com/) — the most token-efficient wire format for LLMs. A drop-in alternative to JSON and TOON for any structured data.
|
|
10
10
|
|
|
11
11
|
**79% fewer input tokens than JSON. 75% fewer output tokens. 52% smaller than TOON. 100% LLM comprehension at 500 symbols, where JSON scores 76.9% and TOON scores 92.3%.**
|
|
12
12
|
|
|
@@ -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
|
|
@@ -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":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEhC,0BAA0B;IAC1B,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE;YAC1B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC3B,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ;aACrB,CAAC,CAAC;YACH,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACvB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1C,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,KAAe,EAAE,KAAa,EAAE,KAAa,EAAE,GAAwB;IAC1F,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,KAAK,CAAC;IAEd,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAE1D,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,MAAM;QAEhD,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAE3D,iBAAiB;QACjB,IAAI,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAEzD,mCAAmC;QACnC,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAExC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;gBACpB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBACzC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAEvC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;oBACtB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;oBAElD,IAAI,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;wBACjC,uBAAuB;wBACvB,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;wBAC3C,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;4BAClB,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;4BAC1D,CAAC,EAAE,CAAC;4BACJ,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,gBAAgB,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;4BACnE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;4BACjB,CAAC,IAAI,QAAQ,CAAC;4BACd,SAAS;wBACX,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;wBAC7C,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;4BACrB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;4BACf,CAAC,EAAE,CAAC;4BACJ,SAAS;wBACX,CAAC;wBACD,qBAAqB;wBACrB,CAAC,EAAE,CAAC;wBACJ,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,oBAAoB,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;wBAChE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;wBAClB,CAAC,IAAI,QAAQ,CAAC;wBACd,SAAS;oBACX,CAAC;gBACH,CAAC;YACH,CAAC;YAED,wBAAwB;YACxB,IAAI,IAAI,GAAG,MAAM,CAAC;YAClB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,EAAE,IAAI,CAAC;gBAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtC,CAAC,EAAE,CAAC;YACJ,MAAM,MAAM,GAAwB,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;YAC1D,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;YACnB,CAAC,IAAI,QAAQ,CAAC;YACd,SAAS;QACX,CAAC;QAED,iDAAiD;QACjD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,QAAQ,GAAG,UAAU,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;gBAC5C,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC9D,CAAC,EAAE,CAAC;gBACJ,SAAS;YACX,CAAC;QACH,CAAC;QAED,aAAa;QACb,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACpC,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YACrC,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAC3B,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QAED,CAAC,EAAE,CAAC;IACN,CAAC;IAED,OAAO,CAAC,GAAG,KAAK,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAe,EAAE,KAAa,EAAE,KAAa,EAAE,MAAgB;IACvF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,IAAI,GAAU,EAAE,CAAC;IACvB,IAAI,CAAC,GAAG,KAAK,CAAC;IAEd,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;YAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAElC,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC7F,IAAI,OAAO,KAAK,IAAI;YAAE,MAAM;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,MAAM;QACrC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAEhD,IAAI,OAAO,GAAG,OAAO,CAAC;QACtB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;gBAChC,SAAS,GAAG,IAAI,CAAC;YACnB,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,GAAG,GAAwB,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChE,CAAC;QAED,CAAC,EAAE,CAAC;QAEJ,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,YAAY,GAAG,MAAM,GAAG,IAAI,CAAC;YACnC,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBACxB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;oBAAE,MAAM;gBACxC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;gBAEzC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvB,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC9B,CAAC,EAAE,CAAC;oBACJ,MAAM,MAAM,GAAwB,EAAE,CAAC;oBACvC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;oBAC1D,GAAG,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;oBACxB,CAAC,IAAI,QAAQ,CAAC;gBAChB,CAAC;qBAAM,CAAC;oBACN,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAe,EAAE,KAAa,EAAE,KAAa;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,KAAK,GAAU,EAAE,CAAC;IACxB,IAAI,CAAC,GAAG,KAAK,CAAC;IAEd,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;YAAC,CAAC,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QAClC,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC7F,IAAI,OAAO,KAAK,IAAI;YAAE,MAAM;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,MAAM;QAErC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;gBACX,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,CAAC;YACD,CAAC,EAAE,CAAC;QACN,CAAC;aAAM,CAAC;YACN,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,CAAC,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAChC,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QAC7D,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IACpC,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,7 @@ 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';
|
|
8
9
|
export { StreamEncoder, type StreamWriter, type StreamOptions } from './stream.js';
|
|
10
|
+
export { GenericStreamEncoder } from './stream_generic.js';
|
|
9
11
|
//# 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;AAC7C,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,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;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,7 @@ 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';
|
|
7
8
|
export { StreamEncoder } from './stream.js';
|
|
9
|
+
export { GenericStreamEncoder } from './stream_generic.js';
|
|
8
10
|
//# 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;AAC7C,OAAO,EAAE,aAAa,EAAyC,MAAM,aAAa,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;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { StreamWriter } from './stream.js';
|
|
2
|
+
/**
|
|
3
|
+
* GenericStreamEncoder writes GCF tabular output incrementally as rows arrive.
|
|
4
|
+
* Zero buffering: each row is written immediately. A trailer summary is
|
|
5
|
+
* emitted on close() with the final counts.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const enc = new GenericStreamEncoder({ write: (s) => process.stdout.write(s) });
|
|
10
|
+
* enc.beginArray('employees', ['id', 'name', 'department', 'salary']);
|
|
11
|
+
* enc.writeRow([1, 'Alice', 'Engineering', 95000]);
|
|
12
|
+
* enc.writeRow([2, 'Bob', 'Sales', 72000]);
|
|
13
|
+
* enc.endArray();
|
|
14
|
+
* enc.close();
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare class GenericStreamEncoder {
|
|
18
|
+
private readonly writer;
|
|
19
|
+
private sections;
|
|
20
|
+
private current;
|
|
21
|
+
constructor(writer: StreamWriter);
|
|
22
|
+
/** Start a tabular array section with deferred count [?]. */
|
|
23
|
+
beginArray(name: string, fields: string[]): void;
|
|
24
|
+
/** Emit a single pipe-separated row immediately. */
|
|
25
|
+
writeRow(values: unknown[]): void;
|
|
26
|
+
/** Close the current array section and record its count. */
|
|
27
|
+
endArray(): void;
|
|
28
|
+
/** Emit a key=value line immediately. */
|
|
29
|
+
writeKV(key: string, value: unknown): void;
|
|
30
|
+
/** Start a nested object section (## key). */
|
|
31
|
+
writeSection(name: string): void;
|
|
32
|
+
/** Emit a primitive array inline: name[N]: val1,val2,val3 */
|
|
33
|
+
writeInlineArray(name: string, values: unknown[]): void;
|
|
34
|
+
/** Emit the ## _summary trailer with final counts. Must be called after all data. */
|
|
35
|
+
close(): void;
|
|
36
|
+
private endArrayInternal;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=stream_generic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream_generic.d.ts","sourceRoot":"","sources":["../src/stream_generic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,OAAO,CAA4B;gBAE/B,MAAM,EAAE,YAAY;IAIhC,6DAA6D;IAC7D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAQhD,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IASjC,4DAA4D;IAC5D,QAAQ,IAAI,IAAI;IAIhB,yCAAyC;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI1C,8CAA8C;IAC9C,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOhC,6DAA6D;IAC7D,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IAKvD,qFAAqF;IACrF,KAAK,IAAI,IAAI;IAgBb,OAAO,CAAC,gBAAgB;CAOzB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenericStreamEncoder writes GCF tabular output incrementally as rows arrive.
|
|
3
|
+
* Zero buffering: each row is written immediately. A trailer summary is
|
|
4
|
+
* emitted on close() with the final counts.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const enc = new GenericStreamEncoder({ write: (s) => process.stdout.write(s) });
|
|
9
|
+
* enc.beginArray('employees', ['id', 'name', 'department', 'salary']);
|
|
10
|
+
* enc.writeRow([1, 'Alice', 'Engineering', 95000]);
|
|
11
|
+
* enc.writeRow([2, 'Bob', 'Sales', 72000]);
|
|
12
|
+
* enc.endArray();
|
|
13
|
+
* enc.close();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class GenericStreamEncoder {
|
|
17
|
+
writer;
|
|
18
|
+
sections = [];
|
|
19
|
+
current = null;
|
|
20
|
+
constructor(writer) {
|
|
21
|
+
this.writer = writer;
|
|
22
|
+
}
|
|
23
|
+
/** Start a tabular array section with deferred count [?]. */
|
|
24
|
+
beginArray(name, fields) {
|
|
25
|
+
if (this.current !== null) {
|
|
26
|
+
this.endArrayInternal();
|
|
27
|
+
}
|
|
28
|
+
this.writer.write(`## ${name} [?]{${fields.join(',')}}\n`);
|
|
29
|
+
this.current = { name, fields, count: 0 };
|
|
30
|
+
}
|
|
31
|
+
/** Emit a single pipe-separated row immediately. */
|
|
32
|
+
writeRow(values) {
|
|
33
|
+
if (this.current === null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const parts = values.map(formatValue);
|
|
37
|
+
this.writer.write(`${parts.join('|')}\n`);
|
|
38
|
+
this.current.count++;
|
|
39
|
+
}
|
|
40
|
+
/** Close the current array section and record its count. */
|
|
41
|
+
endArray() {
|
|
42
|
+
this.endArrayInternal();
|
|
43
|
+
}
|
|
44
|
+
/** Emit a key=value line immediately. */
|
|
45
|
+
writeKV(key, value) {
|
|
46
|
+
this.writer.write(`${key}=${formatValue(value)}\n`);
|
|
47
|
+
}
|
|
48
|
+
/** Start a nested object section (## key). */
|
|
49
|
+
writeSection(name) {
|
|
50
|
+
if (this.current !== null) {
|
|
51
|
+
this.endArrayInternal();
|
|
52
|
+
}
|
|
53
|
+
this.writer.write(`## ${name}\n`);
|
|
54
|
+
}
|
|
55
|
+
/** Emit a primitive array inline: name[N]: val1,val2,val3 */
|
|
56
|
+
writeInlineArray(name, values) {
|
|
57
|
+
const parts = values.map(formatValue);
|
|
58
|
+
this.writer.write(`${name}[${values.length}]: ${parts.join(',')}\n`);
|
|
59
|
+
}
|
|
60
|
+
/** Emit the ## _summary trailer with final counts. Must be called after all data. */
|
|
61
|
+
close() {
|
|
62
|
+
if (this.current !== null) {
|
|
63
|
+
this.endArrayInternal();
|
|
64
|
+
}
|
|
65
|
+
if (this.sections.length === 0) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let totalRows = 0;
|
|
69
|
+
const sectionParts = [];
|
|
70
|
+
for (const s of this.sections) {
|
|
71
|
+
sectionParts.push(`${s.name}:${s.count}`);
|
|
72
|
+
totalRows += s.count;
|
|
73
|
+
}
|
|
74
|
+
this.writer.write(`## _summary rows=${totalRows} sections=${sectionParts.join(',')}\n`);
|
|
75
|
+
}
|
|
76
|
+
endArrayInternal() {
|
|
77
|
+
if (this.current === null) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.sections.push({ name: this.current.name, count: this.current.count });
|
|
81
|
+
this.current = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function formatValue(v) {
|
|
85
|
+
if (v === null || v === undefined) {
|
|
86
|
+
return '-';
|
|
87
|
+
}
|
|
88
|
+
if (typeof v === 'boolean') {
|
|
89
|
+
return v ? 'true' : 'false';
|
|
90
|
+
}
|
|
91
|
+
if (typeof v === 'number') {
|
|
92
|
+
return String(v);
|
|
93
|
+
}
|
|
94
|
+
if (typeof v === 'string') {
|
|
95
|
+
if (v === '') {
|
|
96
|
+
return '""';
|
|
97
|
+
}
|
|
98
|
+
if (v.includes('|') || v.includes('\n')) {
|
|
99
|
+
return `"${v.replace(/"/g, '\\"')}"`;
|
|
100
|
+
}
|
|
101
|
+
return v;
|
|
102
|
+
}
|
|
103
|
+
return String(v);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=stream_generic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream_generic.js","sourceRoot":"","sources":["../src/stream_generic.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,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';
|
|
8
9
|
export { StreamEncoder, type StreamWriter, type StreamOptions } from './stream.js';
|
|
10
|
+
export { GenericStreamEncoder } from './stream_generic.js';
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { StreamWriter } from './stream.js';
|
|
2
|
+
|
|
3
|
+
interface SectionCount {
|
|
4
|
+
name: string;
|
|
5
|
+
count: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ActiveArray {
|
|
9
|
+
name: string;
|
|
10
|
+
fields: string[];
|
|
11
|
+
count: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* GenericStreamEncoder writes GCF tabular output incrementally as rows arrive.
|
|
16
|
+
* Zero buffering: each row is written immediately. A trailer summary is
|
|
17
|
+
* emitted on close() with the final counts.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const enc = new GenericStreamEncoder({ write: (s) => process.stdout.write(s) });
|
|
22
|
+
* enc.beginArray('employees', ['id', 'name', 'department', 'salary']);
|
|
23
|
+
* enc.writeRow([1, 'Alice', 'Engineering', 95000]);
|
|
24
|
+
* enc.writeRow([2, 'Bob', 'Sales', 72000]);
|
|
25
|
+
* enc.endArray();
|
|
26
|
+
* enc.close();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class GenericStreamEncoder {
|
|
30
|
+
private readonly writer: StreamWriter;
|
|
31
|
+
private sections: SectionCount[] = [];
|
|
32
|
+
private current: ActiveArray | null = null;
|
|
33
|
+
|
|
34
|
+
constructor(writer: StreamWriter) {
|
|
35
|
+
this.writer = writer;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Start a tabular array section with deferred count [?]. */
|
|
39
|
+
beginArray(name: string, fields: string[]): void {
|
|
40
|
+
if (this.current !== null) {
|
|
41
|
+
this.endArrayInternal();
|
|
42
|
+
}
|
|
43
|
+
this.writer.write(`## ${name} [?]{${fields.join(',')}}\n`);
|
|
44
|
+
this.current = { name, fields, count: 0 };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Emit a single pipe-separated row immediately. */
|
|
48
|
+
writeRow(values: unknown[]): void {
|
|
49
|
+
if (this.current === null) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const parts = values.map(formatValue);
|
|
53
|
+
this.writer.write(`${parts.join('|')}\n`);
|
|
54
|
+
this.current.count++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Close the current array section and record its count. */
|
|
58
|
+
endArray(): void {
|
|
59
|
+
this.endArrayInternal();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Emit a key=value line immediately. */
|
|
63
|
+
writeKV(key: string, value: unknown): void {
|
|
64
|
+
this.writer.write(`${key}=${formatValue(value)}\n`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Start a nested object section (## key). */
|
|
68
|
+
writeSection(name: string): void {
|
|
69
|
+
if (this.current !== null) {
|
|
70
|
+
this.endArrayInternal();
|
|
71
|
+
}
|
|
72
|
+
this.writer.write(`## ${name}\n`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Emit a primitive array inline: name[N]: val1,val2,val3 */
|
|
76
|
+
writeInlineArray(name: string, values: unknown[]): void {
|
|
77
|
+
const parts = values.map(formatValue);
|
|
78
|
+
this.writer.write(`${name}[${values.length}]: ${parts.join(',')}\n`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Emit the ## _summary trailer with final counts. Must be called after all data. */
|
|
82
|
+
close(): void {
|
|
83
|
+
if (this.current !== null) {
|
|
84
|
+
this.endArrayInternal();
|
|
85
|
+
}
|
|
86
|
+
if (this.sections.length === 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
let totalRows = 0;
|
|
90
|
+
const sectionParts: string[] = [];
|
|
91
|
+
for (const s of this.sections) {
|
|
92
|
+
sectionParts.push(`${s.name}:${s.count}`);
|
|
93
|
+
totalRows += s.count;
|
|
94
|
+
}
|
|
95
|
+
this.writer.write(`## _summary rows=${totalRows} sections=${sectionParts.join(',')}\n`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private endArrayInternal(): void {
|
|
99
|
+
if (this.current === null) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.sections.push({ name: this.current.name, count: this.current.count });
|
|
103
|
+
this.current = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatValue(v: unknown): string {
|
|
108
|
+
if (v === null || v === undefined) {
|
|
109
|
+
return '-';
|
|
110
|
+
}
|
|
111
|
+
if (typeof v === 'boolean') {
|
|
112
|
+
return v ? 'true' : 'false';
|
|
113
|
+
}
|
|
114
|
+
if (typeof v === 'number') {
|
|
115
|
+
return String(v);
|
|
116
|
+
}
|
|
117
|
+
if (typeof v === 'string') {
|
|
118
|
+
if (v === '') {
|
|
119
|
+
return '""';
|
|
120
|
+
}
|
|
121
|
+
if (v.includes('|') || v.includes('\n')) {
|
|
122
|
+
return `"${v.replace(/"/g, '\\"')}"`;
|
|
123
|
+
}
|
|
124
|
+
return v;
|
|
125
|
+
}
|
|
126
|
+
return String(v);
|
|
127
|
+
}
|