@pretable/stream-adapter 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +393 -359
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +54 -42
- package/dist/index.d.cts.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +54 -42
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +392 -357
- package/dist/index.mjs.map +1 -0
- package/package.json +5 -4
package/dist/index.mjs
CHANGED
|
@@ -1,374 +1,409 @@
|
|
|
1
|
-
import { create,
|
|
1
|
+
import { create, finish, isArrayNode, isComplete, isObjectNode, push } from "@cacheplane/json-stream";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
//#region src/create-batcher.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create a `requestAnimationFrame`-batched mutator that coalesces
|
|
6
|
+
* `add` / `update` / `remove` calls into a single `applyTransaction` per
|
|
7
|
+
* frame. Use this when driving a row model from a stream that emits faster
|
|
8
|
+
* than the browser can render.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const batcher = createBatcher(rowModel);
|
|
13
|
+
* batcher.add([{ id: "1", name: "Ada" }]);
|
|
14
|
+
* batcher.update([{ id: "1", changes: { age: 36 } }]);
|
|
15
|
+
* batcher.flush(); // optional — RAF will flush automatically
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
4
20
|
function createBatcher(rowModel) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (disposed) return;
|
|
116
|
-
disposed = true;
|
|
117
|
-
if (rafId !== null) {
|
|
118
|
-
cancelAnimationFrame(rafId);
|
|
119
|
-
rafId = null;
|
|
120
|
-
}
|
|
121
|
-
addBuffer = [];
|
|
122
|
-
updateBuffer = [];
|
|
123
|
-
removeBuffer = [];
|
|
124
|
-
errorListeners.clear();
|
|
125
|
-
}
|
|
126
|
-
};
|
|
21
|
+
let addBuffer = [];
|
|
22
|
+
let updateBuffer = [];
|
|
23
|
+
let removeBuffer = [];
|
|
24
|
+
let rafId = null;
|
|
25
|
+
let disposed = false;
|
|
26
|
+
let rejectError;
|
|
27
|
+
const errorListeners = /* @__PURE__ */ new Set();
|
|
28
|
+
let failed = false;
|
|
29
|
+
let failure;
|
|
30
|
+
const error = new Promise((_resolve, reject) => {
|
|
31
|
+
rejectError = reject;
|
|
32
|
+
});
|
|
33
|
+
error.catch(() => void 0);
|
|
34
|
+
function fail(error) {
|
|
35
|
+
if (disposed) return;
|
|
36
|
+
disposed = true;
|
|
37
|
+
failed = true;
|
|
38
|
+
failure = error;
|
|
39
|
+
if (rafId !== null) {
|
|
40
|
+
cancelAnimationFrame(rafId);
|
|
41
|
+
rafId = null;
|
|
42
|
+
}
|
|
43
|
+
addBuffer = [];
|
|
44
|
+
updateBuffer = [];
|
|
45
|
+
removeBuffer = [];
|
|
46
|
+
const listeners = [...errorListeners];
|
|
47
|
+
errorListeners.clear();
|
|
48
|
+
for (const listener of listeners) try {
|
|
49
|
+
listener(error);
|
|
50
|
+
} catch (_unused) {}
|
|
51
|
+
rejectError(error);
|
|
52
|
+
}
|
|
53
|
+
function scheduleFlush() {
|
|
54
|
+
if (rafId !== null || disposed) return;
|
|
55
|
+
rafId = requestAnimationFrame(() => {
|
|
56
|
+
rafId = null;
|
|
57
|
+
if (disposed) return;
|
|
58
|
+
try {
|
|
59
|
+
applyBuffered();
|
|
60
|
+
} catch (error) {
|
|
61
|
+
fail(error);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function applyBuffered() {
|
|
66
|
+
if (addBuffer.length === 0 && updateBuffer.length === 0 && removeBuffer.length === 0) return;
|
|
67
|
+
const bufferedAdds = addBuffer;
|
|
68
|
+
const bufferedUpdates = updateBuffer;
|
|
69
|
+
const bufferedRemovals = removeBuffer;
|
|
70
|
+
addBuffer = [];
|
|
71
|
+
updateBuffer = [];
|
|
72
|
+
removeBuffer = [];
|
|
73
|
+
const transaction = {};
|
|
74
|
+
if (bufferedAdds.length > 0) transaction.add = bufferedAdds.map((row) => ({ ...row }));
|
|
75
|
+
if (bufferedUpdates.length > 0) transaction.update = bufferedUpdates.map(({ id, changes }) => ({
|
|
76
|
+
id,
|
|
77
|
+
changes: { ...changes }
|
|
78
|
+
}));
|
|
79
|
+
if (bufferedRemovals.length > 0) transaction.remove = [...bufferedRemovals];
|
|
80
|
+
rowModel.applyTransaction(transaction);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
error,
|
|
84
|
+
subscribeError(listener) {
|
|
85
|
+
if (failed) {
|
|
86
|
+
try {
|
|
87
|
+
listener(failure);
|
|
88
|
+
} catch (_unused2) {}
|
|
89
|
+
return () => void 0;
|
|
90
|
+
}
|
|
91
|
+
if (disposed) return () => void 0;
|
|
92
|
+
errorListeners.add(listener);
|
|
93
|
+
return () => errorListeners.delete(listener);
|
|
94
|
+
},
|
|
95
|
+
add(rows) {
|
|
96
|
+
if (disposed) return;
|
|
97
|
+
addBuffer.push(...rows);
|
|
98
|
+
scheduleFlush();
|
|
99
|
+
},
|
|
100
|
+
update(patches) {
|
|
101
|
+
if (disposed) return;
|
|
102
|
+
updateBuffer.push(...patches);
|
|
103
|
+
scheduleFlush();
|
|
104
|
+
},
|
|
105
|
+
remove(ids) {
|
|
106
|
+
if (disposed) return;
|
|
107
|
+
removeBuffer.push(...ids);
|
|
108
|
+
scheduleFlush();
|
|
109
|
+
},
|
|
110
|
+
flush() {
|
|
111
|
+
if (disposed) return;
|
|
112
|
+
if (rafId !== null) {
|
|
113
|
+
cancelAnimationFrame(rafId);
|
|
114
|
+
rafId = null;
|
|
115
|
+
}
|
|
116
|
+
applyBuffered();
|
|
117
|
+
},
|
|
118
|
+
dispose() {
|
|
119
|
+
if (disposed) return;
|
|
120
|
+
disposed = true;
|
|
121
|
+
if (rafId !== null) {
|
|
122
|
+
cancelAnimationFrame(rafId);
|
|
123
|
+
rafId = null;
|
|
124
|
+
}
|
|
125
|
+
addBuffer = [];
|
|
126
|
+
updateBuffer = [];
|
|
127
|
+
removeBuffer = [];
|
|
128
|
+
errorListeners.clear();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/connect-element-stream.ts
|
|
135
|
+
/**
|
|
136
|
+
* Drive a row model from an `AsyncIterable<TRow>`. Each yielded row is added
|
|
137
|
+
* via a {@link createBatcher | RAF batcher}; the returned
|
|
138
|
+
* {@link StreamConnection} resolves `done` when the stream ends and
|
|
139
|
+
* supports `dispose()` for early cancellation.
|
|
140
|
+
*
|
|
141
|
+
* Pair with {@link parseElementStream} to turn a raw UTF-8 string stream
|
|
142
|
+
* (e.g., from `fetch().body`) into a row stream end-to-end.
|
|
143
|
+
*
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
130
146
|
function connectElementStream(rowModel, stream) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
};
|
|
147
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
148
|
+
const batcher = createBatcher(rowModel);
|
|
149
|
+
let disposed = false;
|
|
150
|
+
let sourceClosed = false;
|
|
151
|
+
let resolveDone;
|
|
152
|
+
let rejectDone;
|
|
153
|
+
const done = new Promise((resolve, reject) => {
|
|
154
|
+
resolveDone = resolve;
|
|
155
|
+
rejectDone = reject;
|
|
156
|
+
});
|
|
157
|
+
done.catch(() => void 0);
|
|
158
|
+
const closeSource = () => {
|
|
159
|
+
if (sourceClosed) return;
|
|
160
|
+
sourceClosed = true;
|
|
161
|
+
try {
|
|
162
|
+
var _iterator$return;
|
|
163
|
+
const closing = (_iterator$return = iterator.return) === null || _iterator$return === void 0 ? void 0 : _iterator$return.call(iterator);
|
|
164
|
+
if (closing !== void 0) Promise.resolve(closing).catch(() => void 0);
|
|
165
|
+
} catch (_unused) {}
|
|
166
|
+
};
|
|
167
|
+
let settled = false;
|
|
168
|
+
const settle = (failure, flush = true, close = failure !== void 0) => {
|
|
169
|
+
if (settled) return;
|
|
170
|
+
settled = true;
|
|
171
|
+
disposed = true;
|
|
172
|
+
if (close) closeSource();
|
|
173
|
+
let finalFailure = failure;
|
|
174
|
+
if (flush) try {
|
|
175
|
+
batcher.flush();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
var _finalFailure;
|
|
178
|
+
(_finalFailure = finalFailure) !== null && _finalFailure !== void 0 || (finalFailure = { error });
|
|
179
|
+
}
|
|
180
|
+
batcher.dispose();
|
|
181
|
+
if (finalFailure === void 0) resolveDone();
|
|
182
|
+
else rejectDone(finalFailure.error);
|
|
183
|
+
};
|
|
184
|
+
batcher.subscribeError((error) => {
|
|
185
|
+
settle({ error }, false, true);
|
|
186
|
+
});
|
|
187
|
+
(async () => {
|
|
188
|
+
try {
|
|
189
|
+
while (!disposed) {
|
|
190
|
+
const result = await iterator.next();
|
|
191
|
+
if (disposed) return;
|
|
192
|
+
if (result.done) {
|
|
193
|
+
settle();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
batcher.add([result.value]);
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
settle({ error: err }, true, true);
|
|
200
|
+
}
|
|
201
|
+
})().catch((error) => settle({ error }));
|
|
202
|
+
return {
|
|
203
|
+
done,
|
|
204
|
+
dispose() {
|
|
205
|
+
if (disposed) return;
|
|
206
|
+
settle(void 0, true, true);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
195
209
|
}
|
|
196
210
|
|
|
197
|
-
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/connect-partial-stream.ts
|
|
198
213
|
function sameValueZero(left, right) {
|
|
199
|
-
|
|
214
|
+
return left === right || typeof left === "number" && typeof right === "number" && Number.isNaN(left) && Number.isNaN(right);
|
|
200
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Drive a row model from an `AsyncIterable<Partial<TRow>>`. Every yielded
|
|
218
|
+
* partial updates the fixed `options.rowId` via a RAF-batched
|
|
219
|
+
* `{ id, changes }` transaction. A missing target is reported instead of
|
|
220
|
+
* fabricating a row; provide `createRow` when the stream is allowed to add one.
|
|
221
|
+
*
|
|
222
|
+
* Pair with {@link parsePartialStream} for end-to-end partial-update
|
|
223
|
+
* streaming over UTF-8 strings.
|
|
224
|
+
*
|
|
225
|
+
* @public
|
|
226
|
+
*/
|
|
201
227
|
function connectPartialStream(rowModel, stream, options) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
};
|
|
228
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
229
|
+
const batcher = createBatcher({ applyTransaction(transaction) {
|
|
230
|
+
const result = rowModel.applyTransaction(transaction);
|
|
231
|
+
if (result === void 0 || result.issues === void 0) return result;
|
|
232
|
+
let targetMissing = false;
|
|
233
|
+
for (const issue of result.issues) {
|
|
234
|
+
var _options$onIssue;
|
|
235
|
+
if (issue.code !== "unknown-update-id" || issue.rowId === void 0 || !sameValueZero(issue.rowId, options.rowId)) continue;
|
|
236
|
+
targetMissing = true;
|
|
237
|
+
(_options$onIssue = options.onIssue) === null || _options$onIssue === void 0 || _options$onIssue.call(options, {
|
|
238
|
+
code: "unknown-update-id",
|
|
239
|
+
rowId: options.rowId
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (targetMissing && options.createRow !== void 0) {
|
|
243
|
+
var _transaction$update;
|
|
244
|
+
const updates = (_transaction$update = transaction.update) !== null && _transaction$update !== void 0 ? _transaction$update : [];
|
|
245
|
+
const combinedChanges = {};
|
|
246
|
+
let hasTargetUpdate = false;
|
|
247
|
+
for (const update of updates) {
|
|
248
|
+
if (!sameValueZero(update.id, options.rowId)) continue;
|
|
249
|
+
Object.assign(combinedChanges, update.changes);
|
|
250
|
+
hasTargetUpdate = true;
|
|
251
|
+
}
|
|
252
|
+
if (hasTargetUpdate) {
|
|
253
|
+
const row = options.createRow(combinedChanges, options.rowId);
|
|
254
|
+
rowModel.applyTransaction({ add: [row] });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
} });
|
|
259
|
+
let disposed = false;
|
|
260
|
+
let sourceClosed = false;
|
|
261
|
+
let resolveDone;
|
|
262
|
+
let rejectDone;
|
|
263
|
+
const done = new Promise((resolve, reject) => {
|
|
264
|
+
resolveDone = resolve;
|
|
265
|
+
rejectDone = reject;
|
|
266
|
+
});
|
|
267
|
+
done.catch(() => void 0);
|
|
268
|
+
const closeSource = () => {
|
|
269
|
+
if (sourceClosed) return;
|
|
270
|
+
sourceClosed = true;
|
|
271
|
+
try {
|
|
272
|
+
var _iterator$return;
|
|
273
|
+
const closing = (_iterator$return = iterator.return) === null || _iterator$return === void 0 ? void 0 : _iterator$return.call(iterator);
|
|
274
|
+
if (closing !== void 0) Promise.resolve(closing).catch(() => void 0);
|
|
275
|
+
} catch (_unused) {}
|
|
276
|
+
};
|
|
277
|
+
let settled = false;
|
|
278
|
+
const settle = (failure, flush = true, close = failure !== void 0) => {
|
|
279
|
+
if (settled) return;
|
|
280
|
+
settled = true;
|
|
281
|
+
disposed = true;
|
|
282
|
+
if (close) closeSource();
|
|
283
|
+
let finalFailure = failure;
|
|
284
|
+
if (flush) try {
|
|
285
|
+
batcher.flush();
|
|
286
|
+
} catch (error) {
|
|
287
|
+
var _finalFailure;
|
|
288
|
+
(_finalFailure = finalFailure) !== null && _finalFailure !== void 0 || (finalFailure = { error });
|
|
289
|
+
}
|
|
290
|
+
batcher.dispose();
|
|
291
|
+
if (finalFailure === void 0) resolveDone();
|
|
292
|
+
else rejectDone(finalFailure.error);
|
|
293
|
+
};
|
|
294
|
+
batcher.subscribeError((error) => {
|
|
295
|
+
settle({ error }, false, true);
|
|
296
|
+
});
|
|
297
|
+
(async () => {
|
|
298
|
+
try {
|
|
299
|
+
while (!disposed) {
|
|
300
|
+
const result = await iterator.next();
|
|
301
|
+
if (disposed) return;
|
|
302
|
+
if (result.done) {
|
|
303
|
+
settle();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
batcher.update([{
|
|
307
|
+
id: options.rowId,
|
|
308
|
+
changes: result.value
|
|
309
|
+
}]);
|
|
310
|
+
}
|
|
311
|
+
} catch (err) {
|
|
312
|
+
settle({ error: err }, true, true);
|
|
313
|
+
}
|
|
314
|
+
})().catch((error) => settle({ error }));
|
|
315
|
+
return {
|
|
316
|
+
done,
|
|
317
|
+
dispose() {
|
|
318
|
+
if (disposed) return;
|
|
319
|
+
settle(void 0, true, true);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
298
322
|
}
|
|
323
|
+
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/parse-element-stream.ts
|
|
326
|
+
/**
|
|
327
|
+
* Parse a UTF-8 string stream into an `AsyncIterable<TRow>`. Built on
|
|
328
|
+
* `@cacheplane/json-stream`'s incremental JSON parser; emits each
|
|
329
|
+
* complete top-level array element as a typed row.
|
|
330
|
+
*
|
|
331
|
+
* Pair with {@link connectElementStream} for end-to-end element-stream
|
|
332
|
+
* → grid wiring.
|
|
333
|
+
*
|
|
334
|
+
* @public
|
|
335
|
+
*/
|
|
299
336
|
async function* parseElementStream(stream) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
throw new Error(state.error.message);
|
|
327
|
-
}
|
|
328
|
-
if (state.rootId !== null) {
|
|
329
|
-
const root = state.nodes[state.rootId];
|
|
330
|
-
if (isArrayNode(root)) {
|
|
331
|
-
while (yieldedCount < root.children.length) {
|
|
332
|
-
const childNode = state.nodes[root.children[yieldedCount]];
|
|
333
|
-
if (isComplete(childNode) && childNode.value !== void 0) {
|
|
334
|
-
yield childNode.value;
|
|
335
|
-
}
|
|
336
|
-
yieldedCount++;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
337
|
+
let state = create();
|
|
338
|
+
let yieldedCount = 0;
|
|
339
|
+
for await (const chunk of stream) {
|
|
340
|
+
state = push(state, chunk);
|
|
341
|
+
if (state.error) throw new Error(state.error.message);
|
|
342
|
+
if (state.rootId !== null) {
|
|
343
|
+
const root = state.nodes[state.rootId];
|
|
344
|
+
if (!isArrayNode(root)) throw new Error(`parseElementStream expects root to be an array, got "${root.kind}"`);
|
|
345
|
+
while (yieldedCount < root.children.length) {
|
|
346
|
+
const childNode = state.nodes[root.children[yieldedCount]];
|
|
347
|
+
if (!isComplete(childNode)) break;
|
|
348
|
+
if (childNode.value !== void 0) yield childNode.value;
|
|
349
|
+
yieldedCount++;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
state = finish(state);
|
|
354
|
+
if (state.error) throw new Error(state.error.message);
|
|
355
|
+
if (state.rootId !== null) {
|
|
356
|
+
const root = state.nodes[state.rootId];
|
|
357
|
+
if (isArrayNode(root)) while (yieldedCount < root.children.length) {
|
|
358
|
+
const childNode = state.nodes[root.children[yieldedCount]];
|
|
359
|
+
if (isComplete(childNode) && childNode.value !== void 0) yield childNode.value;
|
|
360
|
+
yieldedCount++;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
340
363
|
}
|
|
364
|
+
|
|
365
|
+
//#endregion
|
|
366
|
+
//#region src/parse-partial-stream.ts
|
|
367
|
+
/**
|
|
368
|
+
* Parse a UTF-8 string stream into an `AsyncIterable<Partial<TRow>>`.
|
|
369
|
+
*
|
|
370
|
+
* The root must be a single JSON **object**, not an array — a non-object root
|
|
371
|
+
* throws. Each yielded value is the cumulative snapshot of that object as more
|
|
372
|
+
* keys resolve, not a delta, so the last value yielded is the complete row.
|
|
373
|
+
* Useful when an LLM is streaming partial JSON for one row and you want
|
|
374
|
+
* field-by-field updates instead of waiting for the object to close.
|
|
375
|
+
*
|
|
376
|
+
* For a stream of many complete rows, use {@link parseElementStream}, which
|
|
377
|
+
* does take a top-level array.
|
|
378
|
+
*
|
|
379
|
+
* Pair with {@link connectPartialStream} for end-to-end partial-stream
|
|
380
|
+
* → grid wiring.
|
|
381
|
+
*
|
|
382
|
+
* @public
|
|
383
|
+
*/
|
|
341
384
|
async function* parsePartialStream(stream) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
state = finish(state);
|
|
363
|
-
if (state.error) {
|
|
364
|
-
throw new Error(state.error.message);
|
|
365
|
-
}
|
|
366
|
-
if (state.rootId !== null) {
|
|
367
|
-
const root = state.nodes[state.rootId];
|
|
368
|
-
if (isObjectNode(root) && root.value !== void 0 && root.value !== lastValue && Object.keys(root.value).length > 0) {
|
|
369
|
-
yield root.value;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
385
|
+
let state = create();
|
|
386
|
+
let lastValue;
|
|
387
|
+
for await (const chunk of stream) {
|
|
388
|
+
state = push(state, chunk);
|
|
389
|
+
if (state.error) throw new Error(state.error.message);
|
|
390
|
+
if (state.rootId !== null) {
|
|
391
|
+
const root = state.nodes[state.rootId];
|
|
392
|
+
if (!isObjectNode(root)) throw new Error(`parsePartialStream expects root to be an object, got "${root.kind}"`);
|
|
393
|
+
if (root.value !== void 0 && root.value !== lastValue && Object.keys(root.value).length > 0) {
|
|
394
|
+
lastValue = root.value;
|
|
395
|
+
yield root.value;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
state = finish(state);
|
|
400
|
+
if (state.error) throw new Error(state.error.message);
|
|
401
|
+
if (state.rootId !== null) {
|
|
402
|
+
const root = state.nodes[state.rootId];
|
|
403
|
+
if (isObjectNode(root) && root.value !== void 0 && root.value !== lastValue && Object.keys(root.value).length > 0) yield root.value;
|
|
404
|
+
}
|
|
372
405
|
}
|
|
373
406
|
|
|
407
|
+
//#endregion
|
|
374
408
|
export { connectElementStream, connectPartialStream, createBatcher, parseElementStream, parsePartialStream };
|
|
409
|
+
//# sourceMappingURL=index.mjs.map
|