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.
Files changed (75) hide show
  1. package/README.md +20 -2
  2. package/dist/buffer/index.d.ts +9 -0
  3. package/dist/buffer/index.js +26 -0
  4. package/dist/buffer/index.js.map +1 -0
  5. package/dist/buffer/network.d.ts +89 -0
  6. package/dist/buffer/network.js +172 -0
  7. package/dist/buffer/network.js.map +1 -0
  8. package/dist/buffer/stream.d.ts +220 -0
  9. package/dist/buffer/stream.js +563 -0
  10. package/dist/buffer/stream.js.map +1 -0
  11. package/dist/buffer/testing.d.ts +56 -0
  12. package/dist/buffer/testing.js +92 -0
  13. package/dist/buffer/testing.js.map +1 -0
  14. package/dist/buffer/token.d.ts +49 -0
  15. package/dist/buffer/token.js +112 -0
  16. package/dist/buffer/token.js.map +1 -0
  17. package/dist/esm/buffer/index.js +10 -0
  18. package/dist/esm/buffer/index.js.map +1 -0
  19. package/dist/esm/buffer/network.js +163 -0
  20. package/dist/esm/buffer/network.js.map +1 -0
  21. package/dist/esm/buffer/stream.js +554 -0
  22. package/dist/esm/buffer/stream.js.map +1 -0
  23. package/dist/esm/buffer/testing.js +88 -0
  24. package/dist/esm/buffer/testing.js.map +1 -0
  25. package/dist/esm/buffer/token.js +108 -0
  26. package/dist/esm/buffer/token.js.map +1 -0
  27. package/dist/esm/genai/mocks.js +1 -1
  28. package/dist/esm/genai/transport.js +3 -34
  29. package/dist/esm/genai/transport.js.map +1 -1
  30. package/dist/esm/index.js +3 -0
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/logtrace/index.js +11 -0
  33. package/dist/esm/logtrace/index.js.map +1 -0
  34. package/dist/esm/logtrace/logtrace.js +251 -0
  35. package/dist/esm/logtrace/logtrace.js.map +1 -0
  36. package/dist/esm/logtrace/testing.js +77 -0
  37. package/dist/esm/logtrace/testing.js.map +1 -0
  38. package/dist/esm/progress/index.js +9 -0
  39. package/dist/esm/progress/index.js.map +1 -0
  40. package/dist/esm/progress/progress.js +256 -0
  41. package/dist/esm/progress/progress.js.map +1 -0
  42. package/dist/esm/progress/testing.js +98 -0
  43. package/dist/esm/progress/testing.js.map +1 -0
  44. package/dist/esm/socket/websocket.js +228 -2
  45. package/dist/esm/socket/websocket.js.map +1 -1
  46. package/dist/genai/mocks.d.ts +1 -1
  47. package/dist/genai/mocks.js +1 -1
  48. package/dist/genai/transport.js +3 -34
  49. package/dist/genai/transport.js.map +1 -1
  50. package/dist/index.d.ts +3 -0
  51. package/dist/index.js +3 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/logtrace/index.d.ts +7 -0
  54. package/dist/logtrace/index.js +27 -0
  55. package/dist/logtrace/index.js.map +1 -0
  56. package/dist/logtrace/logtrace.d.ts +99 -0
  57. package/dist/logtrace/logtrace.js +256 -0
  58. package/dist/logtrace/logtrace.js.map +1 -0
  59. package/dist/logtrace/testing.d.ts +40 -0
  60. package/dist/logtrace/testing.js +81 -0
  61. package/dist/logtrace/testing.js.map +1 -0
  62. package/dist/progress/index.d.ts +8 -0
  63. package/dist/progress/index.js +25 -0
  64. package/dist/progress/index.js.map +1 -0
  65. package/dist/progress/progress.d.ts +112 -0
  66. package/dist/progress/progress.js +263 -0
  67. package/dist/progress/progress.js.map +1 -0
  68. package/dist/progress/testing.d.ts +33 -0
  69. package/dist/progress/testing.js +102 -0
  70. package/dist/progress/testing.js.map +1 -0
  71. package/dist/socket/types.d.ts +5 -3
  72. package/dist/socket/websocket.d.ts +77 -0
  73. package/dist/socket/websocket.js +232 -3
  74. package/dist/socket/websocket.js.map +1 -1
  75. package/package.json +19 -1
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.runLogTraceLoop = void 0;
13
+ /**
14
+ * `logtrace/testing.ts`
15
+ * - isolated entry for the logtrace verification loop harness.
16
+ * - kept OUT of the package root barrel so production bundles do not include the in-memory socket simulator.
17
+ *
18
+ * Usage (tests / dev only):
19
+ * import { runLogTraceLoop } from 'lemon-model/logtrace/testing';
20
+ *
21
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
22
+ */
23
+ const socket_1 = require("../socket/socket");
24
+ const logtrace_1 = require("./logtrace");
25
+ /**
26
+ * Run an end-to-end verification loop:
27
+ * entries -> LogTraceReporter (batching) -> NetworkSupportable -> LogTraceConsumer (ring buffer).
28
+ *
29
+ * Metrics-based assertions belong in specs; this helper only records numbers.
30
+ */
31
+ const runLogTraceLoop = (options) => __awaiter(void 0, void 0, void 0, function* () {
32
+ const startedAt = Date.now();
33
+ const network = (0, socket_1.createNetwork)(options.networkOptions);
34
+ // Raw packets measure batching and packet budgets, separate from delivery.
35
+ const rawPackets = [];
36
+ const unsubscribeRaw = network.onMessage(packet => rawPackets.push(packet));
37
+ const consumer = (0, logtrace_1.createLogTraceConsumer)(network, options.consumerOptions);
38
+ let delivered = 0;
39
+ let truncated = 0;
40
+ // Consumer callbacks are the source of delivered/truncated metrics.
41
+ const unsubscribeEntry = consumer.onEntry(entry => {
42
+ delivered += 1;
43
+ if (entry.truncated)
44
+ truncated += 1;
45
+ });
46
+ let batches = 0;
47
+ const reporter = (0, logtrace_1.createLogTraceReporter)(message => {
48
+ batches += 1;
49
+ network.send(JSON.stringify(message));
50
+ }, options.reporterOptions);
51
+ try {
52
+ // Replay input synchronously; reporter options decide when batches flush.
53
+ for (const record of options.entries)
54
+ reporter.log(record.level, record.message, record.json);
55
+ reporter.close();
56
+ yield new Promise(resolve => { var _a; return setTimeout(resolve, (_a = options.settleMs) !== null && _a !== void 0 ? _a : 50); });
57
+ const packetBytes = rawPackets.reduce((sum, packet) => sum + byteLength(packet), 0);
58
+ const maxPacketBytes = rawPackets.reduce((max, packet) => Math.max(max, byteLength(packet)), 0);
59
+ return {
60
+ elapsedMs: Date.now() - startedAt,
61
+ packets: rawPackets.length,
62
+ packetBytes,
63
+ maxPacketBytes,
64
+ batches,
65
+ delivered,
66
+ truncated,
67
+ gapCount: consumer.gapCount,
68
+ finalEntries: consumer.list(),
69
+ };
70
+ }
71
+ finally {
72
+ unsubscribeRaw();
73
+ unsubscribeEntry();
74
+ consumer.close();
75
+ network.close();
76
+ }
77
+ });
78
+ exports.runLogTraceLoop = runLogTraceLoop;
79
+ const textEncoder = new TextEncoder();
80
+ const byteLength = (value) => textEncoder.encode(value).length;
81
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/logtrace/testing.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA;;;;;;;;;GASG;AACH,6CAAiD;AAEjD,yCAOoB;AA6BpB;;;;;GAKG;AACI,MAAM,eAAe,GAAG,CAAO,OAA4B,EAAgC,EAAE;IAChG,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAA,sBAAa,EAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACtD,2EAA2E;IAC3E,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,IAAA,iCAAsB,EAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1E,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,oEAAoE;IACpE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QAC9C,SAAS,IAAI,CAAC,CAAC;QACf,IAAI,KAAK,CAAC,SAAS;YAAE,SAAS,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,QAAQ,GAAG,IAAA,iCAAsB,EAAC,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,0EAA0E;QAC1E,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO;YAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC9F,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,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChG,OAAO;YACH,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;YACjC,OAAO,EAAE,UAAU,CAAC,MAAM;YAC1B,WAAW;YACX,cAAc;YACd,OAAO;YACP,SAAS;YACT,SAAS;YACT,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE;SAChC,CAAC;KACL;YAAS;QACN,cAAc,EAAE,CAAC;QACjB,gBAAgB,EAAE,CAAC;QACnB,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,EAAE,CAAC;KACnB;AACL,CAAC,CAAA,CAAC;AA7CW,QAAA,eAAe,mBA6C1B;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"}
@@ -0,0 +1,8 @@
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';
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * `progress/index.ts`
19
+ * - public re-export for the progress module.
20
+ * - `./testing` is intentionally excluded from this barrel (subpath-only, see SPEC.md).
21
+ *
22
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
23
+ */
24
+ __exportStar(require("./progress"), exports);
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/progress/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,6CAA2B"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * `progress/progress.ts`
3
+ * - one-way (server -> client) task progress reporting over a shared WebSocket.
4
+ * - contract + reporter + gauges + consumer in one file (see SPEC.md - SSOT).
5
+ *
6
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
7
+ */
8
+ import { NetworkSupportable, SocketMessage, SocketUnsubscribe } from '../socket/types';
9
+ /** default serialized packet size limit (same family as `src/socket`) */
10
+ export declare const DEFAULT_MAX_PACKET_BYTES: number;
11
+ /** task execution status */
12
+ export declare type ProgressStatus = 'pending' | 'running' | 'done' | 'error';
13
+ /** progress snapshot of one task - the unit that travels the wire */
14
+ export interface ProgressState {
15
+ /** task id (assigned by the service) */
16
+ id: string;
17
+ /** execution status */
18
+ status: ProgressStatus;
19
+ /** 0~100 progress ratio (omitted when not measurable) */
20
+ percent?: number;
21
+ /** current step / total steps */
22
+ step?: number;
23
+ totalSteps?: number;
24
+ /** human-readable current step description */
25
+ label?: string;
26
+ /** error summary when status is error */
27
+ error?: string;
28
+ /** emitted timestamp (epoch ms) - display only */
29
+ ts: number;
30
+ /** reporter-wide monotonic sequence - latest-wins judgement */
31
+ seq: number;
32
+ /** service-defined extra info. keep it small (see packet limit) */
33
+ meta?: Record<string, any>;
34
+ }
35
+ /** measurement produced by a gauge - subset merged into snapshots */
36
+ export declare type ProgressMeasure = Partial<Pick<ProgressState, 'percent' | 'step' | 'totalSteps' | 'label'>>;
37
+ /**
38
+ * progress measurement strategy - separates "how" percent is computed from the reporter.
39
+ * must be a pure read with no side effects. one function fits every strategy.
40
+ */
41
+ export declare type ProgressGauge = () => ProgressMeasure;
42
+ /** time-flow gauge - elapsed ratio against expectedMs, capped at 99% (only done() declares completion) */
43
+ export declare const createTimeProgressGauge: (expectedMs: number, now?: () => number) => ProgressGauge;
44
+ /** buffer-fill gauge - reads percent/bufferPercent from a GenAIStreamProgress-compatible source (structural type, no src/buffer dependency) */
45
+ export declare const createBufferProgressGauge: (source: () => {
46
+ percent?: number;
47
+ bufferPercent?: number;
48
+ } | undefined) => ProgressGauge;
49
+ /** server-side send path injection (Ports & Adapters). a rejected Promise is wired to onError */
50
+ export declare type ProgressSink = (message: SocketMessage<ProgressState>) => void | Promise<void>;
51
+ export interface ProgressReporterOptions {
52
+ /** envelope type (default 'progress:update') */
53
+ type?: string;
54
+ /** update() emit coalescing window. 0 emits every update immediately (default 0) */
55
+ throttleMs?: number;
56
+ /** re-emit interval for running snapshots. 0 disables (default 0) */
57
+ heartbeatMs?: number;
58
+ /** serialized size limit. an oversized snapshot is emitted without meta and reported via onError (default DEFAULT_MAX_PACKET_BYTES = 64kb) */
59
+ maxPacketBytes?: number;
60
+ /** observe emit failures / size overruns */
61
+ onError?: (error: any, state: ProgressState) => void;
62
+ }
63
+ /** reporting handle for one task */
64
+ export interface ProgressTaskSupportable {
65
+ readonly id: string;
66
+ /** current local snapshot */
67
+ readonly state: ProgressState;
68
+ /** patch then emit (throttled). ignored after done/error. status regression (running -> pending) drops status only, rest of the patch applies */
69
+ update(patch: Partial<Omit<ProgressState, 'id' | 'ts' | 'seq' | 'status'>> & {
70
+ status?: 'pending' | 'running';
71
+ }): void;
72
+ /** transition to a terminal status and emit immediately (throttle bypassed). later update is ignored */
73
+ done(patch?: Partial<Pick<ProgressState, 'label' | 'meta'>>): void;
74
+ error(error: string | Error, patch?: Partial<Pick<ProgressState, 'label' | 'meta'>>): void;
75
+ }
76
+ /** start options for one task */
77
+ export interface ProgressTaskOptions {
78
+ /** percent auto-measurement strategy. gauge() is merged into the snapshot right before update/heartbeat emits. explicit update values win over gauge */
79
+ gauge?: ProgressGauge;
80
+ }
81
+ export interface ProgressReporterSupportable {
82
+ /** declare a task start and get its handle. calling again with the same id returns the existing handle */
83
+ start(id: string, initial?: Partial<Pick<ProgressState, 'label' | 'percent' | 'step' | 'totalSteps' | 'meta'>>, options?: ProgressTaskOptions): ProgressTaskSupportable;
84
+ /** flush unemitted snapshots + release timers. must be called before the lambda invocation ends */
85
+ close(): void;
86
+ }
87
+ /** change notification */
88
+ export interface ProgressChangeEvent {
89
+ state: ProgressState;
90
+ /** true when this consumer sees the task for the first time */
91
+ created: boolean;
92
+ }
93
+ export interface ProgressConsumerOptions {
94
+ /** inbound type prefix (default 'progress:') */
95
+ typePrefix?: string;
96
+ /** retained task count limit. evicts oldest terminal (done/error) tasks first (default 100) */
97
+ maxTasks?: number;
98
+ }
99
+ export interface ProgressConsumerSupportable {
100
+ /** latest snapshot by task id */
101
+ get(id: string): ProgressState | undefined;
102
+ /** every retained snapshot - in first-applied order (seq is per-reporter, not a cross-task sort key) */
103
+ list(): ProgressState[];
104
+ /** subscribe to applied changes (stale drops are not notified) */
105
+ onChange(handler: (event: ProgressChangeEvent) => void): SocketUnsubscribe;
106
+ /** unsubscribe. the network stays open (shared socket) */
107
+ close(): void;
108
+ }
109
+ /** create a server-side progress reporter over an injected sink */
110
+ export declare const createProgressReporter: (sink: ProgressSink, options?: ProgressReporterOptions) => ProgressReporterSupportable;
111
+ /** create a client-side progress consumer on a shared network */
112
+ export declare const createProgressConsumer: (network: NetworkSupportable, options?: ProgressConsumerOptions) => ProgressConsumerSupportable;
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.createProgressConsumer = exports.createProgressReporter = exports.createBufferProgressGauge = exports.createTimeProgressGauge = exports.DEFAULT_MAX_PACKET_BYTES = void 0;
15
+ const websocket_1 = require("../socket/websocket");
16
+ /** default serialized packet size limit (same family as `src/socket`) */
17
+ exports.DEFAULT_MAX_PACKET_BYTES = 64 * 1024;
18
+ /** time-flow gauge - elapsed ratio against expectedMs, capped at 99% (only done() declares completion) */
19
+ const createTimeProgressGauge = (expectedMs, now = Date.now) => {
20
+ const startedAt = now();
21
+ return () => {
22
+ if (!(expectedMs > 0))
23
+ return { percent: 99 };
24
+ const percent = Math.floor(((now() - startedAt) / expectedMs) * 100);
25
+ return { percent: Math.min(99, Math.max(0, percent)) };
26
+ };
27
+ };
28
+ exports.createTimeProgressGauge = createTimeProgressGauge;
29
+ /** buffer-fill gauge - reads percent/bufferPercent from a GenAIStreamProgress-compatible source (structural type, no src/buffer dependency) */
30
+ const createBufferProgressGauge = (source) => {
31
+ return () => {
32
+ var _a;
33
+ const snapshot = source();
34
+ 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;
35
+ return percent === undefined ? {} : { percent };
36
+ };
37
+ };
38
+ exports.createBufferProgressGauge = createBufferProgressGauge;
39
+ const GAUGE_KEYS = ['percent', 'step', 'totalSteps', 'label'];
40
+ const textEncoder = new TextEncoder();
41
+ const byteLength = (value) => textEncoder.encode(value).length;
42
+ /** assign only defined values so a partial patch never erases fields */
43
+ const assignDefined = (target, patch) => {
44
+ if (!patch)
45
+ return;
46
+ for (const key of Object.keys(patch)) {
47
+ if (patch[key] !== undefined)
48
+ target[key] = patch[key];
49
+ }
50
+ };
51
+ /** create a server-side progress reporter over an injected sink */
52
+ const createProgressReporter = (sink, options) => {
53
+ var _a, _b, _c, _d;
54
+ const type = (_a = options === null || options === void 0 ? void 0 : options.type) !== null && _a !== void 0 ? _a : 'progress:update';
55
+ const throttleMs = (_b = options === null || options === void 0 ? void 0 : options.throttleMs) !== null && _b !== void 0 ? _b : 0;
56
+ const heartbeatMs = (_c = options === null || options === void 0 ? void 0 : options.heartbeatMs) !== null && _c !== void 0 ? _c : 0;
57
+ const maxPacketBytes = (_d = options === null || options === void 0 ? void 0 : options.maxPacketBytes) !== null && _d !== void 0 ? _d : exports.DEFAULT_MAX_PACKET_BYTES;
58
+ const tasks = new Map();
59
+ let seq = 0;
60
+ let mid = 0;
61
+ 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); };
62
+ /** the reporter never interrupts task code: sink throw/reject only reaches onError */
63
+ const dispatch = (message) => {
64
+ try {
65
+ const result = sink(message);
66
+ if (result && typeof result.then === 'function') {
67
+ result.then(undefined, error => notifyError(error, message.data));
68
+ }
69
+ }
70
+ catch (error) {
71
+ notifyError(error, message.data);
72
+ }
73
+ };
74
+ const emit = (task) => {
75
+ task.dirty = false;
76
+ const snapshot = Object.assign(Object.assign({}, task.local), { ts: Date.now(), seq: ++seq });
77
+ if (task.gauge) {
78
+ try {
79
+ const measure = task.gauge();
80
+ for (const key of GAUGE_KEYS) {
81
+ if (snapshot[key] === undefined && measure[key] !== undefined)
82
+ snapshot[key] = measure[key];
83
+ }
84
+ }
85
+ catch (error) {
86
+ notifyError(error, snapshot);
87
+ }
88
+ }
89
+ let message = { type, data: snapshot, mid: `p${++mid}` };
90
+ if (snapshot.meta !== undefined && byteLength(JSON.stringify(message)) > maxPacketBytes) {
91
+ const { meta: _meta } = snapshot, stripped = __rest(snapshot, ["meta"]);
92
+ message = Object.assign(Object.assign({}, message), { data: stripped });
93
+ notifyError(new Error(`progress packet exceeds maxPacketBytes:${maxPacketBytes}`), snapshot);
94
+ }
95
+ task.last = message.data;
96
+ dispatch(message);
97
+ };
98
+ /** trailing edge: emit the latest snapshot once per window while updates keep arriving */
99
+ const openWindow = (task) => {
100
+ task.timer = setTimeout(() => {
101
+ task.timer = undefined;
102
+ if (!task.dirty || task.closed)
103
+ return;
104
+ emit(task);
105
+ openWindow(task);
106
+ }, throttleMs);
107
+ };
108
+ const cancelWindow = (task) => {
109
+ if (!task.timer)
110
+ return;
111
+ clearTimeout(task.timer);
112
+ task.timer = undefined;
113
+ };
114
+ const finish = (task, status, patch) => {
115
+ if (task.closed)
116
+ return;
117
+ task.closed = true;
118
+ cancelWindow(task);
119
+ assignDefined(task.local, patch);
120
+ task.local.status = status;
121
+ emit(task);
122
+ };
123
+ const heartbeatTimer = heartbeatMs > 0
124
+ ? setInterval(() => {
125
+ tasks.forEach(task => {
126
+ if (!task.closed && task.local.status === 'running')
127
+ emit(task);
128
+ });
129
+ }, heartbeatMs)
130
+ : undefined;
131
+ return {
132
+ start: (id, initial, taskOptions) => {
133
+ const existing = tasks.get(id);
134
+ if (existing)
135
+ return existing.handle;
136
+ const local = { id, status: 'pending', ts: 0, seq: 0 };
137
+ assignDefined(local, initial);
138
+ const task = {
139
+ local,
140
+ last: local,
141
+ gauge: taskOptions === null || taskOptions === void 0 ? void 0 : taskOptions.gauge,
142
+ dirty: false,
143
+ closed: false,
144
+ handle: {
145
+ id,
146
+ get state() {
147
+ return task.last;
148
+ },
149
+ update: patch => {
150
+ if (task.closed)
151
+ return;
152
+ const _a = patch !== null && patch !== void 0 ? patch : {}, { status } = _a, rest = __rest(_a, ["status"]);
153
+ if (status && !(status === 'pending' && task.local.status === 'running')) {
154
+ task.local.status = status;
155
+ }
156
+ assignDefined(task.local, rest);
157
+ if (throttleMs <= 0)
158
+ return emit(task);
159
+ if (task.timer) {
160
+ task.dirty = true;
161
+ return;
162
+ }
163
+ emit(task);
164
+ openWindow(task);
165
+ },
166
+ done: patch => finish(task, 'done', patch),
167
+ error: (error, patch) => {
168
+ var _a;
169
+ task.local.error = typeof error === 'string' ? error : `${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`;
170
+ finish(task, 'error', patch);
171
+ },
172
+ },
173
+ };
174
+ tasks.set(id, task);
175
+ emit(task);
176
+ return task.handle;
177
+ },
178
+ close: () => {
179
+ if (heartbeatTimer)
180
+ clearInterval(heartbeatTimer);
181
+ tasks.forEach(task => {
182
+ cancelWindow(task);
183
+ if (task.dirty && !task.closed)
184
+ emit(task);
185
+ });
186
+ },
187
+ };
188
+ };
189
+ exports.createProgressReporter = createProgressReporter;
190
+ /** create a client-side progress consumer on a shared network */
191
+ const createProgressConsumer = (network, options) => {
192
+ var _a, _b;
193
+ const typePrefix = (_a = options === null || options === void 0 ? void 0 : options.typePrefix) !== null && _a !== void 0 ? _a : 'progress:';
194
+ const maxTasks = Math.max(1, (_b = options === null || options === void 0 ? void 0 : options.maxTasks) !== null && _b !== void 0 ? _b : 100);
195
+ const rawKey = `"type":"${typePrefix}`;
196
+ /** two-stage filter: raw substring check (no parse) then parsed type re-check */
197
+ const filtered = (0, websocket_1.createFilteredNetwork)(network, raw => raw.includes(rawKey));
198
+ const store = new Map();
199
+ const handlers = new Set();
200
+ const evict = (keepId) => {
201
+ if (store.size <= maxTasks)
202
+ return;
203
+ let evictId;
204
+ for (const [id, state] of store) {
205
+ if (id !== keepId && (state.status === 'done' || state.status === 'error')) {
206
+ evictId = id;
207
+ break;
208
+ }
209
+ }
210
+ if (evictId === undefined) {
211
+ for (const id of store.keys()) {
212
+ if (id !== keepId) {
213
+ evictId = id;
214
+ break;
215
+ }
216
+ }
217
+ }
218
+ if (evictId !== undefined)
219
+ store.delete(evictId);
220
+ };
221
+ const unsubscribe = filtered.onMessage(raw => {
222
+ let message;
223
+ try {
224
+ message = JSON.parse(raw);
225
+ }
226
+ catch (_a) {
227
+ return;
228
+ }
229
+ if (typeof (message === null || message === void 0 ? void 0 : message.type) !== 'string' || !message.type.startsWith(typePrefix))
230
+ return;
231
+ const state = message.data;
232
+ if (!state || typeof state.id !== 'string' || typeof state.seq !== 'number')
233
+ return;
234
+ const existing = store.get(state.id);
235
+ if (existing && state.seq <= existing.seq)
236
+ return;
237
+ store.set(state.id, state);
238
+ if (!existing)
239
+ evict(state.id);
240
+ for (const handler of [...handlers]) {
241
+ try {
242
+ handler({ state, created: !existing });
243
+ }
244
+ catch (_b) {
245
+ // a subscriber error must not break other subscribers or the shared network
246
+ }
247
+ }
248
+ });
249
+ return {
250
+ get: id => store.get(id),
251
+ list: () => [...store.values()],
252
+ onChange: handler => {
253
+ handlers.add(handler);
254
+ return () => handlers.delete(handler);
255
+ },
256
+ close: () => {
257
+ unsubscribe();
258
+ handlers.clear();
259
+ },
260
+ };
261
+ };
262
+ exports.createProgressConsumer = createProgressConsumer;
263
+ //# 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,33 @@
1
+ import { SocketNetworkOptions } from '../socket/types';
2
+ import { ProgressConsumerOptions, ProgressReporterOptions, ProgressState } from './progress';
3
+ export interface ProgressLoopOptions {
4
+ /** update patch sequence to replay (start -> patches -> done/error). patch.id selects the task (default 'task') */
5
+ script: Array<Partial<ProgressState>>;
6
+ reporterOptions?: ProgressReporterOptions;
7
+ consumerOptions?: ProgressConsumerOptions;
8
+ /** in-memory network conditions (unordered, jitterMs, maxPacketBytes, ...) */
9
+ networkOptions?: SocketNetworkOptions & {
10
+ id?: string;
11
+ };
12
+ /** wait time for async packet delivery */
13
+ settleMs?: number;
14
+ }
15
+ export interface ProgressLoopMetrics {
16
+ elapsedMs: number;
17
+ /** raw packet count / total bytes / largest packet bytes (throttle / packet-limit checks) */
18
+ packets: number;
19
+ packetBytes: number;
20
+ maxPacketBytes: number;
21
+ /** snapshots emitted by the reporter (throttle suppression check) */
22
+ emitted: number;
23
+ /** applied by the consumer / dropped by seq judgement */
24
+ applied: number;
25
+ staleDropped: number;
26
+ /** final consumer states at loop end */
27
+ finalStates: ProgressState[];
28
+ }
29
+ /**
30
+ * Run an end-to-end loop: script -> ProgressReporter -> NetworkSupportable -> ProgressConsumer,
31
+ * and return numeric metrics for assertion (same pattern as `buffer/testing.runGenAIStreamNetworkLoop`).
32
+ */
33
+ export declare const runProgressLoop: (options: ProgressLoopOptions) => Promise<ProgressLoopMetrics>;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.runProgressLoop = void 0;
24
+ /**
25
+ * `progress/testing.ts`
26
+ * - isolated entry for the progress end-to-end loop harness.
27
+ * - kept OUT of the package root barrel so production bundles do not include the in-memory socket simulator.
28
+ *
29
+ * Usage (tests / dev only):
30
+ * import { runProgressLoop } from 'lemon-model/progress/testing';
31
+ *
32
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
33
+ */
34
+ const socket_1 = require("../socket/socket");
35
+ const progress_1 = require("./progress");
36
+ /**
37
+ * Run an end-to-end loop: script -> ProgressReporter -> NetworkSupportable -> ProgressConsumer,
38
+ * and return numeric metrics for assertion (same pattern as `buffer/testing.runGenAIStreamNetworkLoop`).
39
+ */
40
+ const runProgressLoop = (options) => __awaiter(void 0, void 0, void 0, function* () {
41
+ var _a, _b;
42
+ const startedAt = Date.now();
43
+ const network = (0, socket_1.createNetwork)(Object.assign({}, options.networkOptions));
44
+ const typePrefix = (_b = (_a = options.consumerOptions) === null || _a === void 0 ? void 0 : _a.typePrefix) !== null && _b !== void 0 ? _b : 'progress:';
45
+ // Keep wire packets so specs can compare emitted, applied, and stale counts.
46
+ const rawPackets = [];
47
+ const unsubscribeRaw = network.onMessage(raw => rawPackets.push(raw));
48
+ const consumer = (0, progress_1.createProgressConsumer)(network, options.consumerOptions);
49
+ let applied = 0;
50
+ const unsubscribeChange = consumer.onChange(() => (applied += 1));
51
+ let emitted = 0;
52
+ const reporter = (0, progress_1.createProgressReporter)(message => {
53
+ emitted += 1;
54
+ network.send(JSON.stringify(message));
55
+ }, options.reporterOptions);
56
+ try {
57
+ const tasks = new Map();
58
+ // Reuse task handles by id so each script patch models a real task update.
59
+ const taskOf = (id) => {
60
+ const existing = tasks.get(id);
61
+ if (existing)
62
+ return existing;
63
+ const started = reporter.start(id);
64
+ tasks.set(id, started);
65
+ return started;
66
+ };
67
+ for (const patch of options.script) {
68
+ const { id, ts: _ts, seq: _seq, status, error } = patch, rest = __rest(patch, ["id", "ts", "seq", "status", "error"]);
69
+ const task = taskOf(id !== null && id !== void 0 ? id : 'task');
70
+ if (status === 'done')
71
+ task.done(rest);
72
+ else if (status === 'error')
73
+ task.error(error !== null && error !== void 0 ? error : 'error', rest);
74
+ else
75
+ task.update(Object.assign(Object.assign({}, rest), { error, status }));
76
+ }
77
+ reporter.close();
78
+ yield new Promise(resolve => { var _a; return setTimeout(resolve, (_a = options.settleMs) !== null && _a !== void 0 ? _a : 50); });
79
+ // Only matching progress envelopes can become stale drops on a shared network.
80
+ const arrived = rawPackets.filter(raw => raw.includes(`"type":"${typePrefix}`)).length;
81
+ return {
82
+ elapsedMs: Date.now() - startedAt,
83
+ packets: rawPackets.length,
84
+ packetBytes: rawPackets.reduce((sum, raw) => sum + byteLength(raw), 0),
85
+ maxPacketBytes: rawPackets.reduce((max, raw) => Math.max(max, byteLength(raw)), 0),
86
+ emitted,
87
+ applied,
88
+ staleDropped: arrived - applied,
89
+ finalStates: consumer.list(),
90
+ };
91
+ }
92
+ finally {
93
+ unsubscribeRaw();
94
+ unsubscribeChange();
95
+ consumer.close();
96
+ network.close();
97
+ }
98
+ });
99
+ exports.runProgressLoop = runProgressLoop;
100
+ const textEncoder = new TextEncoder();
101
+ const byteLength = (value) => textEncoder.encode(value).length;
102
+ //# sourceMappingURL=testing.js.map