lemon-model 1.2.1 → 1.2.3
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 +20 -2
- package/dist/buffer/index.d.ts +9 -0
- package/dist/buffer/index.js +26 -0
- package/dist/buffer/index.js.map +1 -0
- package/dist/buffer/network.d.ts +89 -0
- package/dist/buffer/network.js +172 -0
- package/dist/buffer/network.js.map +1 -0
- package/dist/buffer/stream.d.ts +220 -0
- package/dist/buffer/stream.js +563 -0
- package/dist/buffer/stream.js.map +1 -0
- package/dist/buffer/testing.d.ts +56 -0
- package/dist/buffer/testing.js +92 -0
- package/dist/buffer/testing.js.map +1 -0
- package/dist/buffer/token.d.ts +49 -0
- package/dist/buffer/token.js +112 -0
- package/dist/buffer/token.js.map +1 -0
- package/dist/esm/buffer/index.js +10 -0
- package/dist/esm/buffer/index.js.map +1 -0
- package/dist/esm/buffer/network.js +163 -0
- package/dist/esm/buffer/network.js.map +1 -0
- package/dist/esm/buffer/stream.js +554 -0
- package/dist/esm/buffer/stream.js.map +1 -0
- package/dist/esm/buffer/testing.js +88 -0
- package/dist/esm/buffer/testing.js.map +1 -0
- package/dist/esm/buffer/token.js +108 -0
- package/dist/esm/buffer/token.js.map +1 -0
- package/dist/esm/genai/mocks.js +1 -1
- package/dist/esm/genai/transport.js +3 -34
- package/dist/esm/genai/transport.js.map +1 -1
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logtrace/index.js +11 -0
- package/dist/esm/logtrace/index.js.map +1 -0
- package/dist/esm/logtrace/logtrace.js +251 -0
- package/dist/esm/logtrace/logtrace.js.map +1 -0
- package/dist/esm/logtrace/testing.js +77 -0
- package/dist/esm/logtrace/testing.js.map +1 -0
- package/dist/esm/progress/index.js +9 -0
- package/dist/esm/progress/index.js.map +1 -0
- package/dist/esm/progress/progress.js +256 -0
- package/dist/esm/progress/progress.js.map +1 -0
- package/dist/esm/progress/testing.js +98 -0
- package/dist/esm/progress/testing.js.map +1 -0
- package/dist/esm/socket/websocket.js +228 -2
- package/dist/esm/socket/websocket.js.map +1 -1
- package/dist/genai/mocks.d.ts +1 -1
- package/dist/genai/mocks.js +1 -1
- package/dist/genai/transport.js +3 -34
- package/dist/genai/transport.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/logtrace/index.d.ts +7 -0
- package/dist/logtrace/index.js +27 -0
- package/dist/logtrace/index.js.map +1 -0
- package/dist/logtrace/logtrace.d.ts +99 -0
- package/dist/logtrace/logtrace.js +256 -0
- package/dist/logtrace/logtrace.js.map +1 -0
- package/dist/logtrace/testing.d.ts +40 -0
- package/dist/logtrace/testing.js +81 -0
- package/dist/logtrace/testing.js.map +1 -0
- package/dist/progress/index.d.ts +8 -0
- package/dist/progress/index.js +25 -0
- package/dist/progress/index.js.map +1 -0
- package/dist/progress/progress.d.ts +112 -0
- package/dist/progress/progress.js +263 -0
- package/dist/progress/progress.js.map +1 -0
- package/dist/progress/testing.d.ts +33 -0
- package/dist/progress/testing.js +102 -0
- package/dist/progress/testing.js.map +1 -0
- package/dist/socket/types.d.ts +5 -3
- package/dist/socket/websocket.d.ts +77 -0
- package/dist/socket/websocket.js +232 -3
- package/dist/socket/websocket.js.map +1 -1
- package/package.json +19 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { createFilteredNetwork } from '../socket/websocket.js';
|
|
13
|
+
/** default serialized packet size limit (same family as `src/socket`) */
|
|
14
|
+
export const DEFAULT_MAX_PACKET_BYTES = 64 * 1024;
|
|
15
|
+
/** time-flow gauge - elapsed ratio against expectedMs, capped at 99% (only done() declares completion) */
|
|
16
|
+
export const createTimeProgressGauge = (expectedMs, now = Date.now) => {
|
|
17
|
+
const startedAt = now();
|
|
18
|
+
return () => {
|
|
19
|
+
if (!(expectedMs > 0))
|
|
20
|
+
return { percent: 99 };
|
|
21
|
+
const percent = Math.floor(((now() - startedAt) / expectedMs) * 100);
|
|
22
|
+
return { percent: Math.min(99, Math.max(0, percent)) };
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/** buffer-fill gauge - reads percent/bufferPercent from a GenAIStreamProgress-compatible source (structural type, no src/buffer dependency) */
|
|
26
|
+
export const createBufferProgressGauge = (source) => {
|
|
27
|
+
return () => {
|
|
28
|
+
var _a;
|
|
29
|
+
const snapshot = source();
|
|
30
|
+
const percent = (_a = snapshot === null || snapshot === void 0 ? void 0 : snapshot.percent) !== null && _a !== void 0 ? _a : snapshot === null || snapshot === void 0 ? void 0 : snapshot.bufferPercent;
|
|
31
|
+
return percent === undefined ? {} : { percent };
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const GAUGE_KEYS = ['percent', 'step', 'totalSteps', 'label'];
|
|
35
|
+
const textEncoder = new TextEncoder();
|
|
36
|
+
const byteLength = (value) => textEncoder.encode(value).length;
|
|
37
|
+
/** assign only defined values so a partial patch never erases fields */
|
|
38
|
+
const assignDefined = (target, patch) => {
|
|
39
|
+
if (!patch)
|
|
40
|
+
return;
|
|
41
|
+
for (const key of Object.keys(patch)) {
|
|
42
|
+
if (patch[key] !== undefined)
|
|
43
|
+
target[key] = patch[key];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/** create a server-side progress reporter over an injected sink */
|
|
47
|
+
export const createProgressReporter = (sink, options) => {
|
|
48
|
+
var _a, _b, _c, _d;
|
|
49
|
+
const type = (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'progress:update';
|
|
50
|
+
const throttleMs = (_b = options === null || options === void 0 ? void 0 : options.throttleMs) !== null && _b !== void 0 ? _b : 0;
|
|
51
|
+
const heartbeatMs = (_c = options === null || options === void 0 ? void 0 : options.heartbeatMs) !== null && _c !== void 0 ? _c : 0;
|
|
52
|
+
const maxPacketBytes = (_d = options === null || options === void 0 ? void 0 : options.maxPacketBytes) !== null && _d !== void 0 ? _d : DEFAULT_MAX_PACKET_BYTES;
|
|
53
|
+
const tasks = new Map();
|
|
54
|
+
let seq = 0;
|
|
55
|
+
let mid = 0;
|
|
56
|
+
const notifyError = (error, state) => { var _a; return (_a = options === null || options === void 0 ? void 0 : options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error, state); };
|
|
57
|
+
/** the reporter never interrupts task code: sink throw/reject only reaches onError */
|
|
58
|
+
const dispatch = (message) => {
|
|
59
|
+
try {
|
|
60
|
+
const result = sink(message);
|
|
61
|
+
if (result && typeof result.then === 'function') {
|
|
62
|
+
result.then(undefined, error => notifyError(error, message.data));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
notifyError(error, message.data);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const emit = (task) => {
|
|
70
|
+
task.dirty = false;
|
|
71
|
+
const snapshot = Object.assign(Object.assign({}, task.local), { ts: Date.now(), seq: ++seq });
|
|
72
|
+
if (task.gauge) {
|
|
73
|
+
try {
|
|
74
|
+
const measure = task.gauge();
|
|
75
|
+
for (const key of GAUGE_KEYS) {
|
|
76
|
+
if (snapshot[key] === undefined && measure[key] !== undefined)
|
|
77
|
+
snapshot[key] = measure[key];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
notifyError(error, snapshot);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let message = { type, data: snapshot, mid: `p${++mid}` };
|
|
85
|
+
if (snapshot.meta !== undefined && byteLength(JSON.stringify(message)) > maxPacketBytes) {
|
|
86
|
+
const { meta: _meta } = snapshot, stripped = __rest(snapshot, ["meta"]);
|
|
87
|
+
message = Object.assign(Object.assign({}, message), { data: stripped });
|
|
88
|
+
notifyError(new Error(`progress packet exceeds maxPacketBytes:${maxPacketBytes}`), snapshot);
|
|
89
|
+
}
|
|
90
|
+
task.last = message.data;
|
|
91
|
+
dispatch(message);
|
|
92
|
+
};
|
|
93
|
+
/** trailing edge: emit the latest snapshot once per window while updates keep arriving */
|
|
94
|
+
const openWindow = (task) => {
|
|
95
|
+
task.timer = setTimeout(() => {
|
|
96
|
+
task.timer = undefined;
|
|
97
|
+
if (!task.dirty || task.closed)
|
|
98
|
+
return;
|
|
99
|
+
emit(task);
|
|
100
|
+
openWindow(task);
|
|
101
|
+
}, throttleMs);
|
|
102
|
+
};
|
|
103
|
+
const cancelWindow = (task) => {
|
|
104
|
+
if (!task.timer)
|
|
105
|
+
return;
|
|
106
|
+
clearTimeout(task.timer);
|
|
107
|
+
task.timer = undefined;
|
|
108
|
+
};
|
|
109
|
+
const finish = (task, status, patch) => {
|
|
110
|
+
if (task.closed)
|
|
111
|
+
return;
|
|
112
|
+
task.closed = true;
|
|
113
|
+
cancelWindow(task);
|
|
114
|
+
assignDefined(task.local, patch);
|
|
115
|
+
task.local.status = status;
|
|
116
|
+
emit(task);
|
|
117
|
+
};
|
|
118
|
+
const heartbeatTimer = heartbeatMs > 0
|
|
119
|
+
? setInterval(() => {
|
|
120
|
+
tasks.forEach(task => {
|
|
121
|
+
if (!task.closed && task.local.status === 'running')
|
|
122
|
+
emit(task);
|
|
123
|
+
});
|
|
124
|
+
}, heartbeatMs)
|
|
125
|
+
: undefined;
|
|
126
|
+
return {
|
|
127
|
+
start: (id, initial, taskOptions) => {
|
|
128
|
+
const existing = tasks.get(id);
|
|
129
|
+
if (existing)
|
|
130
|
+
return existing.handle;
|
|
131
|
+
const local = { id, status: 'pending', ts: 0, seq: 0 };
|
|
132
|
+
assignDefined(local, initial);
|
|
133
|
+
const task = {
|
|
134
|
+
local,
|
|
135
|
+
last: local,
|
|
136
|
+
gauge: taskOptions === null || taskOptions === void 0 ? void 0 : taskOptions.gauge,
|
|
137
|
+
dirty: false,
|
|
138
|
+
closed: false,
|
|
139
|
+
handle: {
|
|
140
|
+
id,
|
|
141
|
+
get state() {
|
|
142
|
+
return task.last;
|
|
143
|
+
},
|
|
144
|
+
update: patch => {
|
|
145
|
+
if (task.closed)
|
|
146
|
+
return;
|
|
147
|
+
const _a = patch !== null && patch !== void 0 ? patch : {}, { status } = _a, rest = __rest(_a, ["status"]);
|
|
148
|
+
if (status && !(status === 'pending' && task.local.status === 'running')) {
|
|
149
|
+
task.local.status = status;
|
|
150
|
+
}
|
|
151
|
+
assignDefined(task.local, rest);
|
|
152
|
+
if (throttleMs <= 0)
|
|
153
|
+
return emit(task);
|
|
154
|
+
if (task.timer) {
|
|
155
|
+
task.dirty = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
emit(task);
|
|
159
|
+
openWindow(task);
|
|
160
|
+
},
|
|
161
|
+
done: patch => finish(task, 'done', patch),
|
|
162
|
+
error: (error, patch) => {
|
|
163
|
+
var _a;
|
|
164
|
+
task.local.error = typeof error === 'string' ? error : `${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`;
|
|
165
|
+
finish(task, 'error', patch);
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
tasks.set(id, task);
|
|
170
|
+
emit(task);
|
|
171
|
+
return task.handle;
|
|
172
|
+
},
|
|
173
|
+
close: () => {
|
|
174
|
+
if (heartbeatTimer)
|
|
175
|
+
clearInterval(heartbeatTimer);
|
|
176
|
+
tasks.forEach(task => {
|
|
177
|
+
cancelWindow(task);
|
|
178
|
+
if (task.dirty && !task.closed)
|
|
179
|
+
emit(task);
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
/** create a client-side progress consumer on a shared network */
|
|
185
|
+
export const createProgressConsumer = (network, options) => {
|
|
186
|
+
var _a, _b;
|
|
187
|
+
const typePrefix = (_a = options === null || options === void 0 ? void 0 : options.typePrefix) !== null && _a !== void 0 ? _a : 'progress:';
|
|
188
|
+
const maxTasks = Math.max(1, (_b = options === null || options === void 0 ? void 0 : options.maxTasks) !== null && _b !== void 0 ? _b : 100);
|
|
189
|
+
const rawKey = `"type":"${typePrefix}`;
|
|
190
|
+
/** two-stage filter: raw substring check (no parse) then parsed type re-check */
|
|
191
|
+
const filtered = createFilteredNetwork(network, raw => raw.includes(rawKey));
|
|
192
|
+
const store = new Map();
|
|
193
|
+
const handlers = new Set();
|
|
194
|
+
const evict = (keepId) => {
|
|
195
|
+
if (store.size <= maxTasks)
|
|
196
|
+
return;
|
|
197
|
+
let evictId;
|
|
198
|
+
for (const [id, state] of store) {
|
|
199
|
+
if (id !== keepId && (state.status === 'done' || state.status === 'error')) {
|
|
200
|
+
evictId = id;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (evictId === undefined) {
|
|
205
|
+
for (const id of store.keys()) {
|
|
206
|
+
if (id !== keepId) {
|
|
207
|
+
evictId = id;
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (evictId !== undefined)
|
|
213
|
+
store.delete(evictId);
|
|
214
|
+
};
|
|
215
|
+
const unsubscribe = filtered.onMessage(raw => {
|
|
216
|
+
let message;
|
|
217
|
+
try {
|
|
218
|
+
message = JSON.parse(raw);
|
|
219
|
+
}
|
|
220
|
+
catch (_a) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (typeof (message === null || message === void 0 ? void 0 : message.type) !== 'string' || !message.type.startsWith(typePrefix))
|
|
224
|
+
return;
|
|
225
|
+
const state = message.data;
|
|
226
|
+
if (!state || typeof state.id !== 'string' || typeof state.seq !== 'number')
|
|
227
|
+
return;
|
|
228
|
+
const existing = store.get(state.id);
|
|
229
|
+
if (existing && state.seq <= existing.seq)
|
|
230
|
+
return;
|
|
231
|
+
store.set(state.id, state);
|
|
232
|
+
if (!existing)
|
|
233
|
+
evict(state.id);
|
|
234
|
+
for (const handler of [...handlers]) {
|
|
235
|
+
try {
|
|
236
|
+
handler({ state, created: !existing });
|
|
237
|
+
}
|
|
238
|
+
catch (_b) {
|
|
239
|
+
// a subscriber error must not break other subscribers or the shared network
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
get: id => store.get(id),
|
|
245
|
+
list: () => [...store.values()],
|
|
246
|
+
onChange: handler => {
|
|
247
|
+
handlers.add(handler);
|
|
248
|
+
return () => handlers.delete(handler);
|
|
249
|
+
},
|
|
250
|
+
close: () => {
|
|
251
|
+
unsubscribe();
|
|
252
|
+
handlers.clear();
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../../src/progress/progress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
11
|
+
var t = {};
|
|
12
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
13
|
+
t[p] = s[p];
|
|
14
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
15
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
16
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
17
|
+
t[p[i]] = s[p[i]];
|
|
18
|
+
}
|
|
19
|
+
return t;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* `progress/testing.ts`
|
|
23
|
+
* - isolated entry for the progress end-to-end loop harness.
|
|
24
|
+
* - kept OUT of the package root barrel so production bundles do not include the in-memory socket simulator.
|
|
25
|
+
*
|
|
26
|
+
* Usage (tests / dev only):
|
|
27
|
+
* import { runProgressLoop } from 'lemon-model/progress/testing';
|
|
28
|
+
*
|
|
29
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
30
|
+
*/
|
|
31
|
+
import { createNetwork } from '../socket/socket.js';
|
|
32
|
+
import { createProgressConsumer, createProgressReporter, } from './progress.js';
|
|
33
|
+
/**
|
|
34
|
+
* Run an end-to-end loop: script -> ProgressReporter -> NetworkSupportable -> ProgressConsumer,
|
|
35
|
+
* and return numeric metrics for assertion (same pattern as `buffer/testing.runGenAIStreamNetworkLoop`).
|
|
36
|
+
*/
|
|
37
|
+
export const runProgressLoop = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const startedAt = Date.now();
|
|
40
|
+
const network = createNetwork(Object.assign({}, options.networkOptions));
|
|
41
|
+
const typePrefix = (_b = (_a = options.consumerOptions) === null || _a === void 0 ? void 0 : _a.typePrefix) !== null && _b !== void 0 ? _b : 'progress:';
|
|
42
|
+
// Keep wire packets so specs can compare emitted, applied, and stale counts.
|
|
43
|
+
const rawPackets = [];
|
|
44
|
+
const unsubscribeRaw = network.onMessage(raw => rawPackets.push(raw));
|
|
45
|
+
const consumer = createProgressConsumer(network, options.consumerOptions);
|
|
46
|
+
let applied = 0;
|
|
47
|
+
const unsubscribeChange = consumer.onChange(() => (applied += 1));
|
|
48
|
+
let emitted = 0;
|
|
49
|
+
const reporter = createProgressReporter(message => {
|
|
50
|
+
emitted += 1;
|
|
51
|
+
network.send(JSON.stringify(message));
|
|
52
|
+
}, options.reporterOptions);
|
|
53
|
+
try {
|
|
54
|
+
const tasks = new Map();
|
|
55
|
+
// Reuse task handles by id so each script patch models a real task update.
|
|
56
|
+
const taskOf = (id) => {
|
|
57
|
+
const existing = tasks.get(id);
|
|
58
|
+
if (existing)
|
|
59
|
+
return existing;
|
|
60
|
+
const started = reporter.start(id);
|
|
61
|
+
tasks.set(id, started);
|
|
62
|
+
return started;
|
|
63
|
+
};
|
|
64
|
+
for (const patch of options.script) {
|
|
65
|
+
const { id, ts: _ts, seq: _seq, status, error } = patch, rest = __rest(patch, ["id", "ts", "seq", "status", "error"]);
|
|
66
|
+
const task = taskOf(id !== null && id !== void 0 ? id : 'task');
|
|
67
|
+
if (status === 'done')
|
|
68
|
+
task.done(rest);
|
|
69
|
+
else if (status === 'error')
|
|
70
|
+
task.error(error !== null && error !== void 0 ? error : 'error', rest);
|
|
71
|
+
else
|
|
72
|
+
task.update(Object.assign(Object.assign({}, rest), { error, status }));
|
|
73
|
+
}
|
|
74
|
+
reporter.close();
|
|
75
|
+
yield new Promise(resolve => { var _a; return setTimeout(resolve, (_a = options.settleMs) !== null && _a !== void 0 ? _a : 50); });
|
|
76
|
+
// Only matching progress envelopes can become stale drops on a shared network.
|
|
77
|
+
const arrived = rawPackets.filter(raw => raw.includes(`"type":"${typePrefix}`)).length;
|
|
78
|
+
return {
|
|
79
|
+
elapsedMs: Date.now() - startedAt,
|
|
80
|
+
packets: rawPackets.length,
|
|
81
|
+
packetBytes: rawPackets.reduce((sum, raw) => sum + byteLength(raw), 0),
|
|
82
|
+
maxPacketBytes: rawPackets.reduce((max, raw) => Math.max(max, byteLength(raw)), 0),
|
|
83
|
+
emitted,
|
|
84
|
+
applied,
|
|
85
|
+
staleDropped: arrived - applied,
|
|
86
|
+
finalStates: consumer.list(),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
unsubscribeRaw();
|
|
91
|
+
unsubscribeChange();
|
|
92
|
+
consumer.close();
|
|
93
|
+
network.close();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
const textEncoder = new TextEncoder();
|
|
97
|
+
const byteLength = (value) => textEncoder.encode(value).length;
|
|
98
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../../src/progress/testing.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EACH,sBAAsB,EACtB,sBAAsB,GAKzB,MAAM,YAAY,CAAC;AA4BpB;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAO,OAA4B,EAAgC,EAAE;;IAChG,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,aAAa,mBAAM,OAAO,CAAC,cAAc,EAAG,CAAC;IAC7D,MAAM,UAAU,GAAG,MAAA,MAAA,OAAO,CAAC,eAAe,0CAAE,UAAU,mCAAI,WAAW,CAAC;IACtE,6EAA6E;IAC7E,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1E,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,iBAAiB,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,OAAO,CAAC,EAAE;QAC9C,OAAO,IAAI,CAAC,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1C,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAE5B,IAAI;QACA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;QACzD,2EAA2E;QAC3E,MAAM,MAAM,GAAG,CAAC,EAAU,EAA2B,EAAE;YACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;YAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YACvB,OAAO,OAAO,CAAC;QACnB,CAAC,CAAC;QACF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE;YAChC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,KAAc,KAAK,EAAd,IAAI,UAAK,KAAK,EAA1D,sCAAkD,CAAQ,CAAC;YACjE,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,aAAF,EAAE,cAAF,EAAE,GAAI,MAAM,CAAC,CAAC;YAClC,IAAI,MAAM,KAAK,MAAM;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;iBAClC,IAAI,MAAM,KAAK,OAAO;gBAAE,IAAI,CAAC,KAAK,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,OAAO,EAAE,IAAI,CAAC,CAAC;;gBAC3D,IAAI,CAAC,MAAM,iCAAM,IAAI,KAAE,KAAK,EAAE,MAAM,IAAG,CAAC;SAChD;QACD,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,WAAC,OAAA,UAAU,CAAC,OAAO,EAAE,MAAA,OAAO,CAAC,QAAQ,mCAAI,EAAE,CAAC,CAAA,EAAA,CAAC,CAAC;QAE1E,+EAA+E;QAC/E,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QACvF,OAAO;YACH,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YACjC,OAAO,EAAE,UAAU,CAAC,MAAM;YAC1B,WAAW,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtE,cAAc,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;YAClF,OAAO;YACP,OAAO;YACP,YAAY,EAAE,OAAO,GAAG,OAAO;YAC/B,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE;SAC/B,CAAC;KACL;YAAS;QACN,cAAc,EAAE,CAAC;QACjB,iBAAiB,EAAE,CAAC;QACpB,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,EAAE,CAAC;KACnB;AACL,CAAC,CAAA,CAAC;AAEF,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC"}
|
|
@@ -7,6 +7,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* `scope` values these WebSocket adapters put on `SocketErrorContext`.
|
|
12
|
+
*
|
|
13
|
+
* Consumers that branch on the scope (e.g. distinguishing a close from a real error) should import
|
|
14
|
+
* these instead of hardcoding the string, so the contract is compiler-checked across packages.
|
|
15
|
+
*/
|
|
16
|
+
export const WEBSOCKET_NETWORK_SCOPE = {
|
|
17
|
+
browser: 'browserWebSocket',
|
|
18
|
+
browserClose: 'browserWebSocket.close',
|
|
19
|
+
owned: 'ownedWebSocket',
|
|
20
|
+
ownedClose: 'ownedWebSocket.close',
|
|
21
|
+
};
|
|
10
22
|
/** extract a connection id from known API Gateway/WebSocket handshake response shapes */
|
|
11
23
|
export const extractWebSocketConnectionId = (message) => {
|
|
12
24
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
@@ -109,13 +121,13 @@ export class BrowserWebSocketNetwork {
|
|
|
109
121
|
if (this.detached)
|
|
110
122
|
return;
|
|
111
123
|
for (const handler of [...this.errorHandlers])
|
|
112
|
-
handler(event, { scope:
|
|
124
|
+
handler(event, { scope: WEBSOCKET_NETWORK_SCOPE.browser, network: this });
|
|
113
125
|
};
|
|
114
126
|
this.handleClose = (event) => {
|
|
115
127
|
if (this.detached)
|
|
116
128
|
return;
|
|
117
129
|
for (const handler of [...this.errorHandlers])
|
|
118
|
-
handler(event, { scope:
|
|
130
|
+
handler(event, { scope: WEBSOCKET_NETWORK_SCOPE.browserClose, network: this });
|
|
119
131
|
};
|
|
120
132
|
this.opened = this.isOpen()
|
|
121
133
|
? Promise.resolve()
|
|
@@ -204,6 +216,220 @@ export class BrowserWebSocketNetwork {
|
|
|
204
216
|
this.readyErrorHandler = undefined;
|
|
205
217
|
}
|
|
206
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Adapter that owns the WebSocket it creates.
|
|
221
|
+
*
|
|
222
|
+
* Unlike `BrowserWebSocketNetwork`, `close()` performs an actual socket close. The adapter only
|
|
223
|
+
* handles raw string send/receive, connect timeout, and event mapping; it knows nothing about
|
|
224
|
+
* message parsing, pending settlement, or routing.
|
|
225
|
+
*/
|
|
226
|
+
export class OwnedWebSocketNetwork {
|
|
227
|
+
constructor(options) {
|
|
228
|
+
var _a;
|
|
229
|
+
this.openHandlers = new Set();
|
|
230
|
+
this.messageHandlers = new Set();
|
|
231
|
+
this.errorHandlers = new Set();
|
|
232
|
+
this.closed = false;
|
|
233
|
+
this.hasOpened = false;
|
|
234
|
+
this.handleOpen = () => {
|
|
235
|
+
if (this.closed || this.hasOpened)
|
|
236
|
+
return;
|
|
237
|
+
this.hasOpened = true;
|
|
238
|
+
for (const handler of [...this.openHandlers])
|
|
239
|
+
handler();
|
|
240
|
+
this.openHandlers.clear();
|
|
241
|
+
};
|
|
242
|
+
this.handleMessage = (event) => {
|
|
243
|
+
if (this.closed || typeof event.data !== 'string')
|
|
244
|
+
return;
|
|
245
|
+
for (const handler of [...this.messageHandlers])
|
|
246
|
+
handler(event.data);
|
|
247
|
+
};
|
|
248
|
+
this.handleError = (event) => {
|
|
249
|
+
if (this.closed)
|
|
250
|
+
return;
|
|
251
|
+
for (const handler of [...this.errorHandlers])
|
|
252
|
+
handler(event, { scope: WEBSOCKET_NETWORK_SCOPE.owned, network: this });
|
|
253
|
+
};
|
|
254
|
+
this.handleClose = (event) => {
|
|
255
|
+
if (this.closed)
|
|
256
|
+
return;
|
|
257
|
+
for (const handler of [...this.errorHandlers])
|
|
258
|
+
handler(event, { scope: WEBSOCKET_NETWORK_SCOPE.ownedClose, network: this });
|
|
259
|
+
};
|
|
260
|
+
this.ws = options.socketFactory
|
|
261
|
+
? options.socketFactory({ url: options.url, protocols: options.protocols })
|
|
262
|
+
: createDefaultWebSocket(options);
|
|
263
|
+
this.opened = this.buildOpened((_a = options.connectTimeoutMs) !== null && _a !== void 0 ? _a : 15000);
|
|
264
|
+
/** swallow the orphan rejection for onOpen-only consumers; ready() callers still observe it (same promise) */
|
|
265
|
+
this.opened.catch(() => undefined);
|
|
266
|
+
this.ws.addEventListener('open', this.handleOpen);
|
|
267
|
+
this.ws.addEventListener('message', this.handleMessage);
|
|
268
|
+
this.ws.addEventListener('error', this.handleError);
|
|
269
|
+
this.ws.addEventListener('close', this.handleClose);
|
|
270
|
+
}
|
|
271
|
+
/** subscribe to the open transition; fires once, immediately if already open */
|
|
272
|
+
onOpen(handler) {
|
|
273
|
+
if (this.closed)
|
|
274
|
+
throw new Error(`@network connection error: closed - ownedWebSocket.onOpen`);
|
|
275
|
+
if (this.hasOpened || this.isOpen()) {
|
|
276
|
+
handler();
|
|
277
|
+
return () => undefined;
|
|
278
|
+
}
|
|
279
|
+
this.openHandlers.add(handler);
|
|
280
|
+
return () => this.openHandlers.delete(handler);
|
|
281
|
+
}
|
|
282
|
+
get readyState() {
|
|
283
|
+
if (this.closed)
|
|
284
|
+
return 'closed';
|
|
285
|
+
if (this.isOpen())
|
|
286
|
+
return 'open';
|
|
287
|
+
if (this.ws.readyState === stateValue(this.ws, 'CLOSING', 2))
|
|
288
|
+
return 'closing';
|
|
289
|
+
if (this.ws.readyState === stateValue(this.ws, 'CLOSED', 3))
|
|
290
|
+
return 'closed';
|
|
291
|
+
return 'connecting';
|
|
292
|
+
}
|
|
293
|
+
ready() {
|
|
294
|
+
if (this.closed)
|
|
295
|
+
return Promise.reject(new Error(`@network connection error: closed - ownedWebSocket.ready`));
|
|
296
|
+
return this.opened;
|
|
297
|
+
}
|
|
298
|
+
send(data) {
|
|
299
|
+
if (this.closed || !this.isOpen()) {
|
|
300
|
+
throw new Error(`@network connection error: ${this.readyState} - ownedWebSocket.send`);
|
|
301
|
+
}
|
|
302
|
+
this.ws.send(data);
|
|
303
|
+
}
|
|
304
|
+
onMessage(handler) {
|
|
305
|
+
if (this.closed)
|
|
306
|
+
throw new Error(`@network connection error: closed - ownedWebSocket.onMessage`);
|
|
307
|
+
this.messageHandlers.add(handler);
|
|
308
|
+
return () => this.messageHandlers.delete(handler);
|
|
309
|
+
}
|
|
310
|
+
configure(_options) {
|
|
311
|
+
// Owned WebSocket transport options are controlled by the runtime.
|
|
312
|
+
}
|
|
313
|
+
onError(handler) {
|
|
314
|
+
this.errorHandlers.add(handler);
|
|
315
|
+
return () => this.errorHandlers.delete(handler);
|
|
316
|
+
}
|
|
317
|
+
close(code, reason) {
|
|
318
|
+
if (this.closed)
|
|
319
|
+
return;
|
|
320
|
+
this.closed = true;
|
|
321
|
+
this.openHandlers.clear();
|
|
322
|
+
this.messageHandlers.clear();
|
|
323
|
+
this.errorHandlers.clear();
|
|
324
|
+
this.ws.removeEventListener('open', this.handleOpen);
|
|
325
|
+
this.ws.removeEventListener('message', this.handleMessage);
|
|
326
|
+
this.ws.removeEventListener('error', this.handleError);
|
|
327
|
+
this.ws.removeEventListener('close', this.handleClose);
|
|
328
|
+
try {
|
|
329
|
+
this.ws.close(code, reason);
|
|
330
|
+
}
|
|
331
|
+
catch (_a) {
|
|
332
|
+
// ignore close errors; the adapter is already detached
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
buildOpened(timeoutMs) {
|
|
336
|
+
if (this.isOpen())
|
|
337
|
+
return Promise.resolve();
|
|
338
|
+
return new Promise((resolve, reject) => {
|
|
339
|
+
let settled = false;
|
|
340
|
+
const timer = timeoutMs > 0
|
|
341
|
+
? setTimeout(() => {
|
|
342
|
+
rejectOnce(new Error(`timeout waiting for WebSocket open: ${timeoutMs}ms`));
|
|
343
|
+
this.close();
|
|
344
|
+
}, timeoutMs)
|
|
345
|
+
: undefined;
|
|
346
|
+
const cleanup = () => {
|
|
347
|
+
if (timer)
|
|
348
|
+
clearTimeout(timer);
|
|
349
|
+
this.ws.removeEventListener('open', onOpen);
|
|
350
|
+
this.ws.removeEventListener('error', onError);
|
|
351
|
+
this.ws.removeEventListener('close', onClose);
|
|
352
|
+
};
|
|
353
|
+
const resolveOnce = () => {
|
|
354
|
+
if (settled)
|
|
355
|
+
return;
|
|
356
|
+
settled = true;
|
|
357
|
+
cleanup();
|
|
358
|
+
resolve();
|
|
359
|
+
};
|
|
360
|
+
const rejectOnce = (error) => {
|
|
361
|
+
if (settled)
|
|
362
|
+
return;
|
|
363
|
+
settled = true;
|
|
364
|
+
cleanup();
|
|
365
|
+
reject(error);
|
|
366
|
+
};
|
|
367
|
+
const onOpen = () => resolveOnce();
|
|
368
|
+
const onError = (event) => rejectOnce(event);
|
|
369
|
+
const onClose = () => rejectOnce(new Error(`WebSocket closed before open`));
|
|
370
|
+
this.ws.addEventListener('open', onOpen, { once: true });
|
|
371
|
+
this.ws.addEventListener('error', onError, { once: true });
|
|
372
|
+
this.ws.addEventListener('close', onClose, { once: true });
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
isOpen() {
|
|
376
|
+
return this.ws.readyState === stateValue(this.ws, 'OPEN', 1);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/** create an owned WebSocket network adapter */
|
|
380
|
+
export const createOwnedWebSocketNetwork = (options) => new OwnedWebSocketNetwork(options);
|
|
381
|
+
/**
|
|
382
|
+
* Wrap a `NetworkSupportable` so subscribers only see inbound raw strings accepted by `shouldHandleRaw`.
|
|
383
|
+
*
|
|
384
|
+
* Both chatic and proxy share this single boundary mechanism. The predicate only filters inbound
|
|
385
|
+
* `onMessage`; outbound `send` and every other member delegate to the source unchanged.
|
|
386
|
+
*/
|
|
387
|
+
export const createFilteredNetwork = (source, shouldHandleRaw) => new FilteredNetwork(source, shouldHandleRaw);
|
|
388
|
+
/** `NetworkSupportable` decorator that filters inbound raw strings by ownership predicate */
|
|
389
|
+
class FilteredNetwork {
|
|
390
|
+
constructor(source, shouldHandleRaw) {
|
|
391
|
+
this.source = source;
|
|
392
|
+
this.shouldHandleRaw = shouldHandleRaw;
|
|
393
|
+
}
|
|
394
|
+
get readyState() {
|
|
395
|
+
return this.source.readyState;
|
|
396
|
+
}
|
|
397
|
+
ready() {
|
|
398
|
+
var _a, _b, _c;
|
|
399
|
+
return (_c = (_b = (_a = this.source).ready) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : Promise.resolve();
|
|
400
|
+
}
|
|
401
|
+
onOpen(handler) {
|
|
402
|
+
var _a, _b, _c;
|
|
403
|
+
return (_c = (_b = (_a = this.source).onOpen) === null || _b === void 0 ? void 0 : _b.call(_a, handler)) !== null && _c !== void 0 ? _c : (() => undefined);
|
|
404
|
+
}
|
|
405
|
+
send(data) {
|
|
406
|
+
this.source.send(data);
|
|
407
|
+
}
|
|
408
|
+
onMessage(handler) {
|
|
409
|
+
return this.source.onMessage(raw => {
|
|
410
|
+
if (!this.shouldHandleRaw(raw))
|
|
411
|
+
return;
|
|
412
|
+
handler(raw);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
configure(options) {
|
|
416
|
+
var _a, _b;
|
|
417
|
+
(_b = (_a = this.source).configure) === null || _b === void 0 ? void 0 : _b.call(_a, options);
|
|
418
|
+
}
|
|
419
|
+
onError(handler) {
|
|
420
|
+
return this.source.onError(handler);
|
|
421
|
+
}
|
|
422
|
+
close(code, reason) {
|
|
423
|
+
this.source.close(code, reason);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const createDefaultWebSocket = (options) => {
|
|
427
|
+
const WS = globalThis.WebSocket;
|
|
428
|
+
if (typeof WS !== 'function') {
|
|
429
|
+
throw new Error(`global WebSocket is not available; provide options.socketFactory`);
|
|
430
|
+
}
|
|
431
|
+
return new WS(options.url, options.protocols);
|
|
432
|
+
};
|
|
207
433
|
const waitWebSocketOpen = (ws, timeoutMs) => {
|
|
208
434
|
if (ws.readyState === stateValue(ws, 'OPEN', 1))
|
|
209
435
|
return Promise.resolve();
|