lemon-model 1.2.2 → 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.
Files changed (41) hide show
  1. package/dist/esm/index.js +2 -0
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/logtrace/index.js +11 -0
  4. package/dist/esm/logtrace/index.js.map +1 -0
  5. package/dist/esm/logtrace/logtrace.js +251 -0
  6. package/dist/esm/logtrace/logtrace.js.map +1 -0
  7. package/dist/esm/logtrace/testing.js +77 -0
  8. package/dist/esm/logtrace/testing.js.map +1 -0
  9. package/dist/esm/progress/index.js +9 -0
  10. package/dist/esm/progress/index.js.map +1 -0
  11. package/dist/esm/progress/progress.js +256 -0
  12. package/dist/esm/progress/progress.js.map +1 -0
  13. package/dist/esm/progress/testing.js +98 -0
  14. package/dist/esm/progress/testing.js.map +1 -0
  15. package/dist/esm/socket/websocket.js +18 -4
  16. package/dist/esm/socket/websocket.js.map +1 -1
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +2 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/logtrace/index.d.ts +7 -0
  21. package/dist/logtrace/index.js +27 -0
  22. package/dist/logtrace/index.js.map +1 -0
  23. package/dist/logtrace/logtrace.d.ts +99 -0
  24. package/dist/logtrace/logtrace.js +256 -0
  25. package/dist/logtrace/logtrace.js.map +1 -0
  26. package/dist/logtrace/testing.d.ts +40 -0
  27. package/dist/logtrace/testing.js +81 -0
  28. package/dist/logtrace/testing.js.map +1 -0
  29. package/dist/progress/index.d.ts +8 -0
  30. package/dist/progress/index.js +25 -0
  31. package/dist/progress/index.js.map +1 -0
  32. package/dist/progress/progress.d.ts +112 -0
  33. package/dist/progress/progress.js +263 -0
  34. package/dist/progress/progress.js.map +1 -0
  35. package/dist/progress/testing.d.ts +33 -0
  36. package/dist/progress/testing.js +102 -0
  37. package/dist/progress/testing.js.map +1 -0
  38. package/dist/socket/websocket.d.ts +14 -0
  39. package/dist/socket/websocket.js +19 -5
  40. package/dist/socket/websocket.js.map +1 -1
  41. package/package.json +13 -1
package/dist/esm/index.js CHANGED
@@ -13,4 +13,6 @@ export * from './cores/index.js';
13
13
  export * from './socket/index.js';
14
14
  export * from './genai/index.js';
15
15
  export * from './buffer/index.js';
16
+ export * from './progress/index.js';
17
+ export * from './logtrace/index.js';
16
18
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `logtrace/index.ts`
3
+ * - one-way realtime log stream (reporter/consumer) over a shared socket.
4
+ *
5
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
6
+ */
7
+ export * from './logtrace.js';
8
+ //! NOTE: the verification loop harness (`./testing`) is intentionally NOT re-exported here.
9
+ //! It pulls in the in-memory socket simulator — import it via the isolated
10
+ //! `lemon-model/logtrace/testing` entry so production bundles stay clean.
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logtrace/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,cAAc,YAAY,CAAC;AAE3B,4FAA4F;AAC5F,2EAA2E;AAC3E,0EAA0E"}
@@ -0,0 +1,251 @@
1
+ import { createFilteredNetwork } from '../socket/websocket.js';
2
+ const DEFAULT_TYPE = 'log:trace';
3
+ const DEFAULT_TYPE_PREFIX = 'log:';
4
+ const DEFAULT_MIN_LEVEL = 'debug';
5
+ const DEFAULT_FLUSH_COUNT = 20;
6
+ const DEFAULT_FLUSH_INTERVAL_MS = 250;
7
+ const DEFAULT_MAX_PACKET_BYTES = 64 * 1024;
8
+ const DEFAULT_MAX_ENTRIES = 1000;
9
+ const LEVEL_WEIGHT = { debug: 0, info: 1, warn: 2, error: 3 };
10
+ /** create a batching log reporter over an injected send path */
11
+ export const createLogTraceReporter = (sink, options) => new LogTraceReporter(sink, options);
12
+ /** create a log consumer over a shared raw string network */
13
+ export const createLogTraceConsumer = (network, options) => new LogTraceConsumer(network, options);
14
+ /** server-side log reporter - batches entries and emits SocketMessage<LogTraceBatch> via the sink */
15
+ class LogTraceReporter {
16
+ constructor(sink, options) {
17
+ var _a, _b, _c, _d, _e, _f, _g;
18
+ this.buffer = [];
19
+ this.bufferBytes = 0;
20
+ this.seqNo = 0;
21
+ this.midNo = 0;
22
+ this.sentOnce = false;
23
+ this.closed = false;
24
+ const maxPacketBytes = (_a = options === null || options === void 0 ? void 0 : options.maxPacketBytes) !== null && _a !== void 0 ? _a : DEFAULT_MAX_PACKET_BYTES;
25
+ this.sink = sink;
26
+ this.type = (_b = options === null || options === void 0 ? void 0 : options.type) !== null && _b !== void 0 ? _b : DEFAULT_TYPE;
27
+ this.source = (_c = options === null || options === void 0 ? void 0 : options.source) !== null && _c !== void 0 ? _c : nextRandomSource();
28
+ this.minLevel = LEVEL_WEIGHT[(_d = options === null || options === void 0 ? void 0 : options.minLevel) !== null && _d !== void 0 ? _d : DEFAULT_MIN_LEVEL];
29
+ this.flushCount = (_e = options === null || options === void 0 ? void 0 : options.flushCount) !== null && _e !== void 0 ? _e : DEFAULT_FLUSH_COUNT;
30
+ this.flushIntervalMs = (_f = options === null || options === void 0 ? void 0 : options.flushIntervalMs) !== null && _f !== void 0 ? _f : DEFAULT_FLUSH_INTERVAL_MS;
31
+ this.maxBatchBytes = (_g = options === null || options === void 0 ? void 0 : options.maxBatchBytes) !== null && _g !== void 0 ? _g : Math.floor((maxPacketBytes * 3) / 4);
32
+ this.onError = options === null || options === void 0 ? void 0 : options.onError;
33
+ }
34
+ /** record one log entry. auto-flushes when a flush condition is reached */
35
+ log(level, message, json) {
36
+ if (this.closed)
37
+ return;
38
+ //! seq is issued only past this gate so a raised minLevel never creates seq gaps.
39
+ if (LEVEL_WEIGHT[level] < this.minLevel)
40
+ return;
41
+ const entry = { level, ts: Date.now(), message, seq: ++this.seqNo };
42
+ if (json !== undefined)
43
+ entry.json = json;
44
+ let bytes = byteLength(JSON.stringify(entry));
45
+ if (bytes > this.maxBatchBytes)
46
+ bytes = this.truncate(entry, bytes);
47
+ //! adding this entry would exceed the batch budget: flush the current buffer, start a new batch.
48
+ if (this.buffer.length > 0 && this.bufferBytes + bytes + 1 > this.maxBatchBytes)
49
+ this.flush();
50
+ this.buffer.push(entry);
51
+ this.bufferBytes += bytes + 1;
52
+ if (level === 'error' || this.buffer.length >= this.flushCount)
53
+ return this.flush();
54
+ if (this.flushIntervalMs <= 0)
55
+ return;
56
+ //! leading edge: the first batch of this reporter flushes immediately so the viewer never waits a window.
57
+ if (!this.sentOnce)
58
+ return this.flush();
59
+ if (!this.timer)
60
+ this.timer = setTimeout(() => this.flush(), this.flushIntervalMs);
61
+ }
62
+ debug(message, json) {
63
+ this.log('debug', message, json);
64
+ }
65
+ info(message, json) {
66
+ this.log('info', message, json);
67
+ }
68
+ warn(message, json) {
69
+ this.log('warn', message, json);
70
+ }
71
+ error(message, json) {
72
+ this.log('error', message, json);
73
+ }
74
+ /** send the buffered entries now */
75
+ flush() {
76
+ this.clearTimer();
77
+ if (this.buffer.length <= 0)
78
+ return;
79
+ const entries = this.buffer;
80
+ this.buffer = [];
81
+ this.bufferBytes = 0;
82
+ this.sentOnce = true;
83
+ const message = {
84
+ type: this.type,
85
+ data: { entries, source: this.source },
86
+ mid: `l${++this.midNo}`,
87
+ };
88
+ //! at-most-once: a failed batch is reported to onError and dropped. never breaks the caller.
89
+ try {
90
+ const result = this.sink(message);
91
+ if (result && typeof result.then === 'function')
92
+ result.catch(e => this.emitError(e, entries));
93
+ }
94
+ catch (e) {
95
+ this.emitError(e, entries);
96
+ }
97
+ }
98
+ /** flush + release the timer. later log calls are ignored */
99
+ close() {
100
+ this.flush();
101
+ this.closed = true;
102
+ }
103
+ /** fit an oversized entry into the batch budget: drop json first, then cut the message */
104
+ truncate(entry, bytes) {
105
+ const error = new Error(`@entry[${entry.seq}] exceeds batch budget(${this.maxBatchBytes}) - logtrace.truncate`);
106
+ entry.truncated = true;
107
+ if (entry.json !== undefined) {
108
+ delete entry.json;
109
+ bytes = byteLength(JSON.stringify(entry));
110
+ }
111
+ //! every removed character frees at least 1 byte, so this loop converges quickly.
112
+ while (bytes > this.maxBatchBytes && entry.message.length > 0) {
113
+ entry.message = entry.message.slice(0, Math.max(0, entry.message.length - (bytes - this.maxBatchBytes)));
114
+ bytes = byteLength(JSON.stringify(entry));
115
+ }
116
+ this.emitError(error, [entry]);
117
+ return bytes;
118
+ }
119
+ emitError(error, entries) {
120
+ var _a;
121
+ try {
122
+ (_a = this.onError) === null || _a === void 0 ? void 0 : _a.call(this, error, entries);
123
+ }
124
+ catch (_b) {
125
+ // The error observer must never break the reporter.
126
+ }
127
+ }
128
+ clearTimer() {
129
+ if (!this.timer)
130
+ return;
131
+ clearTimeout(this.timer);
132
+ this.timer = undefined;
133
+ }
134
+ }
135
+ /** client-side log consumer - unpacks batches into a (ts, seq)-sorted ring buffer */
136
+ class LogTraceConsumer {
137
+ constructor(network, options) {
138
+ var _a, _b, _c;
139
+ this.handlers = new Set();
140
+ this.sources = new Map();
141
+ this.entries = [];
142
+ this.typePrefix = (_a = options === null || options === void 0 ? void 0 : options.typePrefix) !== null && _a !== void 0 ? _a : DEFAULT_TYPE_PREFIX;
143
+ this.minLevel = LEVEL_WEIGHT[(_b = options === null || options === void 0 ? void 0 : options.minLevel) !== null && _b !== void 0 ? _b : DEFAULT_MIN_LEVEL];
144
+ this.maxEntries = (_c = options === null || options === void 0 ? void 0 : options.maxEntries) !== null && _c !== void 0 ? _c : DEFAULT_MAX_ENTRIES;
145
+ //! raw stage of the 2-stage filter: substring check without parsing.
146
+ const marker = `"type":"${this.typePrefix}`;
147
+ const filtered = createFilteredNetwork(network, raw => raw.includes(marker));
148
+ this.unsubscribe = filtered.onMessage(raw => this.receive(raw));
149
+ }
150
+ /** subscribe to received entries */
151
+ onEntry(handler) {
152
+ this.handlers.add(handler);
153
+ return () => {
154
+ this.handlers.delete(handler);
155
+ };
156
+ }
157
+ /** list retained entries in (ts, seq) ascending order */
158
+ list(query) {
159
+ var _a;
160
+ const min = LEVEL_WEIGHT[(_a = query === null || query === void 0 ? void 0 : query.minLevel) !== null && _a !== void 0 ? _a : DEFAULT_MIN_LEVEL];
161
+ let listed = min > 0 ? this.entries.filter(entry => LEVEL_WEIGHT[entry.level] >= min) : [...this.entries];
162
+ const limit = query === null || query === void 0 ? void 0 : query.limit;
163
+ if (limit !== undefined && limit >= 0 && listed.length > limit)
164
+ listed = listed.slice(listed.length - limit);
165
+ return listed;
166
+ }
167
+ /** observed wire loss - sum of (max seq - received count) per source */
168
+ get gapCount() {
169
+ let gaps = 0;
170
+ for (const state of this.sources.values())
171
+ gaps += Math.max(0, state.maxSeq - state.seen.size);
172
+ return gaps;
173
+ }
174
+ /** drop retained entries (subscriptions and loss accounting stay) */
175
+ clear() {
176
+ this.entries = [];
177
+ }
178
+ /** unsubscribe from the network. does NOT close the network (shared socket) */
179
+ close() {
180
+ this.unsubscribe();
181
+ this.handlers.clear();
182
+ }
183
+ receive(raw) {
184
+ let message;
185
+ try {
186
+ message = JSON.parse(raw);
187
+ }
188
+ catch (_a) {
189
+ return; // the raw substring filter is best-effort; a non-JSON hit is simply not ours.
190
+ }
191
+ if (!message || typeof message.type !== 'string' || !message.type.startsWith(this.typePrefix))
192
+ return;
193
+ const batch = message.data;
194
+ if (!batch || typeof batch.source !== 'string' || !Array.isArray(batch.entries))
195
+ return;
196
+ const state = this.stateOf(batch.source);
197
+ for (const entry of batch.entries) {
198
+ if (!isLogTraceEntry(entry))
199
+ continue;
200
+ //! dedup + loss accounting happen BEFORE the minLevel gate so gapCount stays a pure wire-loss metric.
201
+ if (state.seen.has(entry.seq))
202
+ continue;
203
+ state.seen.add(entry.seq);
204
+ if (entry.seq > state.maxSeq)
205
+ state.maxSeq = entry.seq;
206
+ if (LEVEL_WEIGHT[entry.level] < this.minLevel)
207
+ continue;
208
+ const accepted = Object.assign(Object.assign({}, entry), { source: batch.source });
209
+ this.store(accepted);
210
+ for (const handler of [...this.handlers])
211
+ handler(accepted);
212
+ }
213
+ }
214
+ /** binary-insert by (ts, seq) so list() never re-sorts; evict the oldest on overflow */
215
+ store(entry) {
216
+ let lo = 0;
217
+ let hi = this.entries.length;
218
+ while (lo < hi) {
219
+ const mid = (lo + hi) >> 1;
220
+ if (compareEntry(this.entries[mid], entry) <= 0)
221
+ lo = mid + 1;
222
+ else
223
+ hi = mid;
224
+ }
225
+ this.entries.splice(lo, 0, entry);
226
+ if (this.entries.length > this.maxEntries)
227
+ this.entries.shift();
228
+ }
229
+ stateOf(source) {
230
+ let state = this.sources.get(source);
231
+ if (!state) {
232
+ // ponytail: the per-source seen Set grows with log volume; swap to seq-range tracking if sessions get huge.
233
+ state = { maxSeq: 0, seen: new Set() };
234
+ this.sources.set(source, state);
235
+ }
236
+ return state;
237
+ }
238
+ }
239
+ let sourceNo = 0;
240
+ const nextRandomSource = () => `src-${Math.random().toString(36).slice(2, 8)}${++sourceNo}`;
241
+ const compareEntry = (a, b) => a.ts - b.ts || a.seq - b.seq;
242
+ const isLogTraceEntry = (entry) => !!entry &&
243
+ typeof entry === 'object' &&
244
+ LEVEL_WEIGHT[entry.level] !== undefined &&
245
+ typeof entry.ts === 'number' &&
246
+ typeof entry.message === 'string' &&
247
+ Number.isInteger(entry.seq) &&
248
+ entry.seq > 0;
249
+ const textEncoder = new TextEncoder();
250
+ const byteLength = (value) => textEncoder.encode(value).length;
251
+ //# sourceMappingURL=logtrace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logtrace.js","sourceRoot":"","sources":["../../../src/logtrace/logtrace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
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
+ /**
11
+ * `logtrace/testing.ts`
12
+ * - isolated entry for the logtrace verification loop harness.
13
+ * - kept OUT of the package root barrel so production bundles do not include the in-memory socket simulator.
14
+ *
15
+ * Usage (tests / dev only):
16
+ * import { runLogTraceLoop } from 'lemon-model/logtrace/testing';
17
+ *
18
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
19
+ */
20
+ import { createNetwork } from '../socket/socket.js';
21
+ import { createLogTraceConsumer, createLogTraceReporter, } from './logtrace.js';
22
+ /**
23
+ * Run an end-to-end verification loop:
24
+ * entries -> LogTraceReporter (batching) -> NetworkSupportable -> LogTraceConsumer (ring buffer).
25
+ *
26
+ * Metrics-based assertions belong in specs; this helper only records numbers.
27
+ */
28
+ export const runLogTraceLoop = (options) => __awaiter(void 0, void 0, void 0, function* () {
29
+ const startedAt = Date.now();
30
+ const network = createNetwork(options.networkOptions);
31
+ // Raw packets measure batching and packet budgets, separate from delivery.
32
+ const rawPackets = [];
33
+ const unsubscribeRaw = network.onMessage(packet => rawPackets.push(packet));
34
+ const consumer = createLogTraceConsumer(network, options.consumerOptions);
35
+ let delivered = 0;
36
+ let truncated = 0;
37
+ // Consumer callbacks are the source of delivered/truncated metrics.
38
+ const unsubscribeEntry = consumer.onEntry(entry => {
39
+ delivered += 1;
40
+ if (entry.truncated)
41
+ truncated += 1;
42
+ });
43
+ let batches = 0;
44
+ const reporter = createLogTraceReporter(message => {
45
+ batches += 1;
46
+ network.send(JSON.stringify(message));
47
+ }, options.reporterOptions);
48
+ try {
49
+ // Replay input synchronously; reporter options decide when batches flush.
50
+ for (const record of options.entries)
51
+ reporter.log(record.level, record.message, record.json);
52
+ reporter.close();
53
+ yield new Promise(resolve => { var _a; return setTimeout(resolve, (_a = options.settleMs) !== null && _a !== void 0 ? _a : 50); });
54
+ const packetBytes = rawPackets.reduce((sum, packet) => sum + byteLength(packet), 0);
55
+ const maxPacketBytes = rawPackets.reduce((max, packet) => Math.max(max, byteLength(packet)), 0);
56
+ return {
57
+ elapsedMs: Date.now() - startedAt,
58
+ packets: rawPackets.length,
59
+ packetBytes,
60
+ maxPacketBytes,
61
+ batches,
62
+ delivered,
63
+ truncated,
64
+ gapCount: consumer.gapCount,
65
+ finalEntries: consumer.list(),
66
+ };
67
+ }
68
+ finally {
69
+ unsubscribeRaw();
70
+ unsubscribeEntry();
71
+ consumer.close();
72
+ network.close();
73
+ }
74
+ });
75
+ const textEncoder = new TextEncoder();
76
+ const byteLength = (value) => textEncoder.encode(value).length;
77
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../../src/logtrace/testing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `progress/index.ts`
3
+ * - public re-export for the progress module.
4
+ * - `./testing` is intentionally excluded from this barrel (subpath-only, see SPEC.md).
5
+ *
6
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
7
+ */
8
+ export * from './progress.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/progress/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,YAAY,CAAC"}
@@ -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