lemon-model 1.2.3 → 1.2.4

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 (106) hide show
  1. package/dist/esm/index.js +1 -0
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/socket/decorators.js +266 -0
  4. package/dist/esm/socket/decorators.js.map +1 -0
  5. package/dist/esm/socket/index.js +2 -0
  6. package/dist/esm/socket/index.js.map +1 -1
  7. package/dist/esm/socket/multi.js +94 -0
  8. package/dist/esm/socket/multi.js.map +1 -0
  9. package/dist/esm/socket/socket.js +82 -22
  10. package/dist/esm/socket/socket.js.map +1 -1
  11. package/dist/esm/socket/transport.js +385 -3
  12. package/dist/esm/socket/transport.js.map +1 -1
  13. package/dist/esm/sync/client.js +166 -0
  14. package/dist/esm/sync/client.js.map +1 -0
  15. package/dist/esm/sync/index.js +14 -0
  16. package/dist/esm/sync/index.js.map +1 -0
  17. package/dist/esm/sync/machine.js +181 -0
  18. package/dist/esm/sync/machine.js.map +1 -0
  19. package/dist/esm/sync/network.js +273 -0
  20. package/dist/esm/sync/network.js.map +1 -0
  21. package/dist/esm/sync/testing.js +44 -0
  22. package/dist/esm/sync/testing.js.map +1 -0
  23. package/dist/esm/sync/ticker.js +53 -0
  24. package/dist/esm/sync/ticker.js.map +1 -0
  25. package/dist/esm/sync/types.js +2 -0
  26. package/dist/{proxy → esm/sync}/types.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/socket/client/index.d.ts +9 -0
  31. package/dist/{proxy → socket/client}/index.js +8 -11
  32. package/dist/socket/client/index.js.map +1 -0
  33. package/dist/socket/client/keep-alive-controller.d.ts +35 -0
  34. package/dist/socket/client/keep-alive-controller.js +101 -0
  35. package/dist/socket/client/keep-alive-controller.js.map +1 -0
  36. package/dist/socket/client/message-router.d.ts +15 -0
  37. package/dist/socket/client/message-router.js +52 -0
  38. package/dist/socket/client/message-router.js.map +1 -0
  39. package/dist/socket/client/pending-request-store.d.ts +24 -0
  40. package/dist/socket/client/pending-request-store.js +78 -0
  41. package/dist/socket/client/pending-request-store.js.map +1 -0
  42. package/dist/socket/client/reconnect-controller.d.ts +56 -0
  43. package/dist/socket/client/reconnect-controller.js +177 -0
  44. package/dist/socket/client/reconnect-controller.js.map +1 -0
  45. package/dist/socket/client/rotation-controller.d.ts +32 -0
  46. package/dist/socket/client/rotation-controller.js +76 -0
  47. package/dist/socket/client/rotation-controller.js.map +1 -0
  48. package/dist/socket/client/shared-timer-scheduler.d.ts +17 -0
  49. package/dist/socket/client/shared-timer-scheduler.js +77 -0
  50. package/dist/socket/client/shared-timer-scheduler.js.map +1 -0
  51. package/dist/socket/client/socket-client.d.ts +2 -0
  52. package/dist/socket/client/socket-client.js +219 -0
  53. package/dist/socket/client/socket-client.js.map +1 -0
  54. package/dist/socket/client/types.d.ts +101 -0
  55. package/dist/socket/client/types.js +3 -0
  56. package/dist/socket/client/types.js.map +1 -0
  57. package/dist/socket/client/websocket-client-transport.d.ts +23 -0
  58. package/dist/socket/client/websocket-client-transport.js +152 -0
  59. package/dist/socket/client/websocket-client-transport.js.map +1 -0
  60. package/dist/socket/decorators.d.ts +38 -0
  61. package/dist/socket/decorators.js +271 -0
  62. package/dist/socket/decorators.js.map +1 -0
  63. package/dist/socket/index.d.ts +2 -0
  64. package/dist/socket/index.js +2 -0
  65. package/dist/socket/index.js.map +1 -1
  66. package/dist/socket/multi.d.ts +33 -0
  67. package/dist/socket/multi.js +98 -0
  68. package/dist/socket/multi.js.map +1 -0
  69. package/dist/socket/socket.d.ts +22 -4
  70. package/dist/socket/socket.js +82 -22
  71. package/dist/socket/socket.js.map +1 -1
  72. package/dist/socket/transport.d.ts +124 -6
  73. package/dist/socket/transport.js +389 -4
  74. package/dist/socket/transport.js.map +1 -1
  75. package/dist/sync/client.d.ts +16 -0
  76. package/dist/sync/client.js +171 -0
  77. package/dist/sync/client.js.map +1 -0
  78. package/dist/sync/index.d.ts +10 -0
  79. package/dist/sync/index.js +30 -0
  80. package/dist/sync/index.js.map +1 -0
  81. package/dist/sync/machine.d.ts +3 -0
  82. package/dist/sync/machine.js +185 -0
  83. package/dist/sync/machine.js.map +1 -0
  84. package/dist/sync/network.d.ts +38 -0
  85. package/dist/sync/network.js +278 -0
  86. package/dist/sync/network.js.map +1 -0
  87. package/dist/sync/testing.d.ts +38 -0
  88. package/dist/sync/testing.js +48 -0
  89. package/dist/sync/testing.js.map +1 -0
  90. package/dist/sync/ticker.d.ts +21 -0
  91. package/dist/sync/ticker.js +57 -0
  92. package/dist/sync/ticker.js.map +1 -0
  93. package/dist/sync/types.d.ts +115 -0
  94. package/dist/sync/types.js +3 -0
  95. package/dist/sync/types.js.map +1 -0
  96. package/package.json +1 -1
  97. package/dist/proxy/index.d.ts +0 -12
  98. package/dist/proxy/index.js.map +0 -1
  99. package/dist/proxy/proxy.d.ts +0 -75
  100. package/dist/proxy/proxy.js +0 -198
  101. package/dist/proxy/proxy.js.map +0 -1
  102. package/dist/proxy/transport.d.ts +0 -123
  103. package/dist/proxy/transport.js +0 -401
  104. package/dist/proxy/transport.js.map +0 -1
  105. package/dist/proxy/types.d.ts +0 -89
  106. package/dist/proxy/types.js +0 -12
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `sync/client.ts`
3
+ * - L3 socket client runtime: envelope encode/decode, mid-based request/response tracking, type-based routing.
4
+ *
5
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
6
+ */
7
+ import { NetworkSupportable } from '../socket';
8
+ import { SocketClientIdentityProvider, SocketClientOptions, SocketClientSupportable } from './types';
9
+ /**
10
+ * create a mid generator with its own counter and an instance-unique prefix.
11
+ * without the unique prefix, two bundled copies of lemon-model sharing one socket would both
12
+ * count from `sync-1` and settle each other's pendings on mid collision.
13
+ */
14
+ export declare const createSocketClientIdentityProvider: (prefix?: string) => SocketClientIdentityProvider;
15
+ /** create the L3 socket client runtime over a shared network */
16
+ export declare const createSocketClient: (network: NetworkSupportable, options?: SocketClientOptions) => SocketClientSupportable;
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSocketClient = exports.createSocketClientIdentityProvider = void 0;
4
+ /**
5
+ * `sync/client.ts`
6
+ * - L3 socket client runtime: envelope encode/decode, mid-based request/response tracking, type-based routing.
7
+ *
8
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
9
+ */
10
+ const socket_1 = require("../socket");
11
+ const DEFAULT_TIMEOUT_MS = 15000;
12
+ /**
13
+ * create a mid generator with its own counter and an instance-unique prefix.
14
+ * without the unique prefix, two bundled copies of lemon-model sharing one socket would both
15
+ * count from `sync-1` and settle each other's pendings on mid collision.
16
+ */
17
+ const createSocketClientIdentityProvider = (prefix) => {
18
+ let midNo = 0;
19
+ const midPrefix = prefix !== null && prefix !== void 0 ? prefix : `sync-${Math.random().toString(36).slice(2, 8)}`;
20
+ return { nextMid: () => `${midPrefix}-${++midNo}` };
21
+ };
22
+ exports.createSocketClientIdentityProvider = createSocketClientIdentityProvider;
23
+ /** create the L3 socket client runtime over a shared network */
24
+ const createSocketClient = (network, options) => new SocketClient(network, options);
25
+ exports.createSocketClient = createSocketClient;
26
+ /** L3 runtime: request/response tracking (with timeout) + type routing over one `NetworkSupportable` */
27
+ class SocketClient {
28
+ constructor(source, options) {
29
+ var _a, _b;
30
+ this.pending = new Map();
31
+ this.typeListeners = new Map();
32
+ this.messageListeners = new Set();
33
+ this.errorListeners = new Set();
34
+ this.closed = false;
35
+ this.network = (options === null || options === void 0 ? void 0 : options.filter) ? (0, socket_1.createFilteredNetwork)(source, options.filter) : source;
36
+ this.timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT_MS;
37
+ this.maxPending = options === null || options === void 0 ? void 0 : options.maxPending;
38
+ this.identity = (_b = options === null || options === void 0 ? void 0 : options.identity) !== null && _b !== void 0 ? _b : (0, exports.createSocketClientIdentityProvider)();
39
+ this.unsubscribeNetworkMessage = this.network.onMessage(raw => this.receive(raw));
40
+ this.unsubscribeNetworkError = this.network.onError((error, context) => this.emitError(error, context));
41
+ }
42
+ /** number of requests awaiting a response */
43
+ get pendingCount() {
44
+ return this.pending.size;
45
+ }
46
+ /** delegate network ready */
47
+ ready() {
48
+ var _a, _b, _c;
49
+ return (_c = (_b = (_a = this.network).ready) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : Promise.resolve();
50
+ }
51
+ /** send a request and wait for the result/error of the same mid */
52
+ request(type, data, options) {
53
+ return new Promise((resolve, reject) => {
54
+ var _a;
55
+ if (this.closed) {
56
+ reject(new Error(`@socketClient is closed - socketClient.request(${type})`));
57
+ return;
58
+ }
59
+ if (this.maxPending != null && this.pending.size >= this.maxPending) {
60
+ reject(new Error(`@pending[${this.pending.size}] exceeds maxPending[${this.maxPending}] - socketClient.request(${type})`));
61
+ return;
62
+ }
63
+ const mid = this.identity.nextMid();
64
+ const timeoutMs = (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : this.timeoutMs;
65
+ const timer = timeoutMs > 0
66
+ ? setTimeout(() => {
67
+ this.pending.delete(mid);
68
+ const error = new Error(`@request[${type}:${mid}] timeout(${timeoutMs}ms) - socketClient.request`);
69
+ this.emitError(error, { scope: 'socketClient.request.timeout', mid });
70
+ reject(error);
71
+ }, timeoutMs)
72
+ : undefined;
73
+ this.pending.set(mid, { type, resolve, reject, timer });
74
+ try {
75
+ this.network.send(JSON.stringify({ type, data, mid }));
76
+ }
77
+ catch (e) {
78
+ this.pending.delete(mid);
79
+ if (timer)
80
+ clearTimeout(timer);
81
+ reject(e);
82
+ }
83
+ });
84
+ }
85
+ /** one-way send without waiting for a response */
86
+ post(type, data) {
87
+ if (this.closed)
88
+ throw new Error(`@socketClient is closed - socketClient.post(${type})`);
89
+ const mid = this.identity.nextMid();
90
+ try {
91
+ this.network.send(JSON.stringify({ type, data, mid }));
92
+ }
93
+ catch (e) {
94
+ this.emitError(e, { scope: 'socketClient.post', mid });
95
+ }
96
+ }
97
+ /** subscribe to events of a specific type */
98
+ onType(type, handler) {
99
+ let listeners = this.typeListeners.get(type);
100
+ if (!listeners) {
101
+ listeners = new Set();
102
+ this.typeListeners.set(type, listeners);
103
+ }
104
+ const set = listeners;
105
+ set.add(handler);
106
+ return () => set.delete(handler);
107
+ }
108
+ /** subscribe to inbound envelopes not matched to a pending request */
109
+ onMessage(handler) {
110
+ this.messageListeners.add(handler);
111
+ return () => this.messageListeners.delete(handler);
112
+ }
113
+ /** observe asynchronous errors (timeout, send failure, network error) */
114
+ onError(handler) {
115
+ this.errorListeners.add(handler);
116
+ return () => this.errorListeners.delete(handler);
117
+ }
118
+ /** reject all pending + detach listeners. does not close network (socket is shared) */
119
+ close() {
120
+ if (this.closed)
121
+ return;
122
+ this.closed = true;
123
+ this.unsubscribeNetworkMessage();
124
+ this.unsubscribeNetworkError();
125
+ for (const [mid, request] of this.pending) {
126
+ if (request.timer)
127
+ clearTimeout(request.timer);
128
+ request.reject(new Error(`@socketClient is closed - socketClient.close(${mid})`));
129
+ }
130
+ this.pending.clear();
131
+ this.typeListeners.clear();
132
+ this.messageListeners.clear();
133
+ this.errorListeners.clear();
134
+ }
135
+ receive(raw) {
136
+ let message;
137
+ try {
138
+ const parsed = JSON.parse(raw);
139
+ if (!parsed || typeof parsed !== 'object' || typeof parsed.type !== 'string')
140
+ return;
141
+ message = parsed;
142
+ }
143
+ catch (_a) {
144
+ return; // not our envelope shape (e.g. json:* packet, progress string); ignore quietly
145
+ }
146
+ if (message.type === 'result' || message.type === 'error') {
147
+ const pending = this.pending.get(message.mid);
148
+ if (!pending)
149
+ return; // unmatched result/error: late response, drop silently
150
+ this.pending.delete(message.mid);
151
+ if (pending.timer)
152
+ clearTimeout(pending.timer);
153
+ if (message.type === 'result')
154
+ pending.resolve(message.data);
155
+ else
156
+ pending.reject(message.data);
157
+ return;
158
+ }
159
+ for (const listener of [...this.messageListeners])
160
+ listener(message);
161
+ const typeListeners = this.typeListeners.get(message.type);
162
+ if (typeListeners)
163
+ for (const listener of [...typeListeners])
164
+ listener(message.data, message);
165
+ }
166
+ emitError(error, context) {
167
+ for (const listener of [...this.errorListeners])
168
+ listener(error, Object.assign(Object.assign({}, context), { network: this.network }));
169
+ }
170
+ }
171
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/sync/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `sync/index.ts`
3
+ * - model sync client: L3 socket client runtime + L4 sync machine.
4
+ *
5
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
6
+ */
7
+ export * from './types';
8
+ export * from './client';
9
+ export * from './machine';
10
+ export * from './ticker';
@@ -0,0 +1,30 @@
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
+ * `sync/index.ts`
19
+ * - model sync client: L3 socket client runtime + L4 sync machine.
20
+ *
21
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
22
+ */
23
+ __exportStar(require("./types"), exports);
24
+ __exportStar(require("./client"), exports);
25
+ __exportStar(require("./machine"), exports);
26
+ __exportStar(require("./ticker"), exports);
27
+ //! NOTE: the `Peer` test bridge (`./testing`) is intentionally NOT re-exported here.
28
+ //! It is a test-only utility — import it via `lemon-model/sync/testing`-style relative paths
29
+ //! within specs so production bundles do not pull in the simulator runtime.
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;GAKG;AACH,0CAAwB;AACxB,2CAAyB;AACzB,4CAA0B;AAC1B,2CAAyB;AAEzB,qFAAqF;AACrF,6FAA6F;AAC7F,4EAA4E"}
@@ -0,0 +1,3 @@
1
+ import { SocketClientSupportable, SyncMachineSupportable } from './types';
2
+ /** create the L4 sync machine over an L3 socket client */
3
+ export declare const createSyncMachine: (client: SocketClientSupportable) => SyncMachineSupportable;
@@ -0,0 +1,185 @@
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.createSyncMachine = void 0;
13
+ /** envelope types the machine never treats as a domain event, even if `Peer`/L3 forward them */
14
+ const NON_EVENT_TYPES = new Set(['result', 'error', 'ping', 'pong']);
15
+ /** create the L4 sync machine over an L3 socket client */
16
+ const createSyncMachine = (client) => new SyncMachine(client);
17
+ exports.createSyncMachine = createSyncMachine;
18
+ /** L4 machine: registry of per-type handles + one shared event fan-out subscription */
19
+ class SyncMachine {
20
+ constructor(client) {
21
+ this.client = client;
22
+ this.handles = new Map();
23
+ this.closed = false;
24
+ this.unsubscribe = client.onMessage(message => this.dispatchEvent(message));
25
+ }
26
+ /** register a domain model type. re-registering the same type returns the existing handle and ignores the new options */
27
+ register(type, options) {
28
+ var _a;
29
+ if (!type)
30
+ throw new Error(`@type (string) is required - syncMachine.register`);
31
+ if (this.closed)
32
+ throw new Error(`@syncMachine is closed - syncMachine.register(${type})`);
33
+ const existing = this.handles.get(type);
34
+ if (existing)
35
+ return existing;
36
+ const handle = new ModelSyncHandle(type, this.client, options.adapter, () => this.handles.delete(type));
37
+ this.handles.set(type, handle);
38
+ if ((_a = options.initialPull) !== null && _a !== void 0 ? _a : true)
39
+ handle.pull().catch(() => undefined);
40
+ return handle;
41
+ }
42
+ /** pull every registered type once. a type whose pull is already in flight is skipped, not nested */
43
+ tick() {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ yield Promise.all([...this.handles.values()].map(handle => handle.pull().catch(() => undefined)));
46
+ });
47
+ }
48
+ /** detach everything */
49
+ close() {
50
+ if (this.closed)
51
+ return;
52
+ this.closed = true;
53
+ this.unsubscribe();
54
+ for (const handle of [...this.handles.values()])
55
+ handle.close();
56
+ this.handles.clear();
57
+ }
58
+ dispatchEvent(message) {
59
+ if (NON_EVENT_TYPES.has(message.type))
60
+ return;
61
+ for (const handle of [...this.handles.values()])
62
+ handle.applyEvent(message);
63
+ }
64
+ }
65
+ /** sync handle for a single model type: local store, watermark, freshness judgement */
66
+ class ModelSyncHandle {
67
+ constructor(type, client, adapter, onClose) {
68
+ var _a;
69
+ this.client = client;
70
+ this.adapter = adapter;
71
+ this.onClose = onClose;
72
+ this.store = new Map();
73
+ this.changeListeners = new Set();
74
+ this.watermark = 0;
75
+ this.closed = false;
76
+ this.type = type;
77
+ this.versionOf = (_a = adapter.versionOf) !== null && _a !== void 0 ? _a : (model => model.updatedAt);
78
+ }
79
+ /** read-only local state lookup */
80
+ get(id) {
81
+ return this.store.get(id);
82
+ }
83
+ list() {
84
+ return [...this.store.values()];
85
+ }
86
+ /** pull changes since the watermark and apply them. loops the cursor until parseReply.next is absent */
87
+ pull() {
88
+ if (this.closed)
89
+ return Promise.reject(new Error(`@handle[${this.type}] is closed - modelSync.pull`));
90
+ if (this.pulling)
91
+ return this.pulling; // already in flight: join it instead of nesting a new pull
92
+ const promise = this.doPull().finally(() => {
93
+ if (this.pulling === promise)
94
+ this.pulling = undefined;
95
+ });
96
+ this.pulling = promise;
97
+ return promise;
98
+ }
99
+ /** subscribe to change notifications */
100
+ onChange(handler) {
101
+ this.changeListeners.add(handler);
102
+ return () => this.changeListeners.delete(handler);
103
+ }
104
+ /** detach this type's subscriptions/listeners */
105
+ close() {
106
+ if (this.closed)
107
+ return;
108
+ this.closed = true;
109
+ this.changeListeners.clear();
110
+ this.onClose();
111
+ }
112
+ /** apply a server-sent event owned by this type (called by the machine's fan-out) */
113
+ applyEvent(message) {
114
+ if (this.closed)
115
+ return;
116
+ const models = this.adapter.parseEvent(message);
117
+ if (!models)
118
+ return; // not owned by this type
119
+ const changed = this.applyModels(models);
120
+ if (changed.length)
121
+ this.emitChange('event', changed);
122
+ }
123
+ doPull() {
124
+ var _a, _b;
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const since = this.watermark || undefined;
127
+ let cursor;
128
+ const applied = [];
129
+ for (;;) {
130
+ const { type, data } = this.adapter.buildPull(since, cursor);
131
+ const reply = yield this.client.request(type, data);
132
+ const page = this.adapter.parseReply(reply);
133
+ const appliedPage = this.applyModels((_a = page.models) !== null && _a !== void 0 ? _a : []);
134
+ //! watermark advances from pull-applied models ONLY — an event-advanced watermark would
135
+ //! let a lost event skip the pulled backfill of models updated in between (safety-net hole).
136
+ for (const model of appliedPage)
137
+ this.watermark = Math.max(this.watermark, (_b = this.versionOf(model)) !== null && _b !== void 0 ? _b : 0);
138
+ //! emit per page so applied pages are notified even if a later page request fails.
139
+ if (appliedPage.length)
140
+ this.emitChange('pull', appliedPage);
141
+ applied.push(...appliedPage);
142
+ cursor = page.next;
143
+ if (!cursor)
144
+ break;
145
+ }
146
+ return applied;
147
+ });
148
+ }
149
+ /** apply the versionOf freshness rule to each incoming model; returns the ones actually applied/removed */
150
+ applyModels(models) {
151
+ const changed = [];
152
+ for (const model of models) {
153
+ if (this.applyOne(model))
154
+ changed.push(model);
155
+ }
156
+ return changed;
157
+ }
158
+ applyOne(incoming) {
159
+ if ((incoming === null || incoming === void 0 ? void 0 : incoming.id) == null)
160
+ return false; // no id: ignore
161
+ const version = this.versionOf(incoming);
162
+ if (version == null)
163
+ return false; // no version: ignore
164
+ const local = this.store.get(incoming.id);
165
+ if (!local && incoming.deletedAt)
166
+ return false; // delete of something already absent: ignore
167
+ if (local) {
168
+ const localVersion = this.versionOf(local);
169
+ if (localVersion != null && version <= localVersion)
170
+ return false; // stale or same value: ignore
171
+ // local version == null: local is stale by definition, fall through and overwrite
172
+ }
173
+ if (incoming.deletedAt)
174
+ this.store.delete(incoming.id);
175
+ else
176
+ this.store.set(incoming.id, incoming);
177
+ return true;
178
+ }
179
+ emitChange(cause, models) {
180
+ const event = { cause, models };
181
+ for (const listener of [...this.changeListeners])
182
+ listener(event);
183
+ }
184
+ }
185
+ //# sourceMappingURL=machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/sync/machine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `sync/network.ts`
3
+ * - L1 network decorators: reconnect-on-death wrapper and raw wire translator.
4
+ * - additive to `src/socket`; free to compose with `createFilteredNetwork` (translated -> filtered -> L3).
5
+ *
6
+ * @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
7
+ */
8
+ import { NetworkSupportable, SocketUnsubscribe } from '../socket';
9
+ export interface ReconnectOptions {
10
+ /** first retry delay (default 1_000ms) */
11
+ baseMs?: number;
12
+ /** exponential backoff multiplier (default 2) */
13
+ factor?: number;
14
+ /** retry delay cap (default 30_000ms) */
15
+ maxMs?: number;
16
+ /** retry count cap (default unlimited). onError notified then permanently closed once reached */
17
+ maxRetries?: number;
18
+ /** force a reconnect when nothing has been received for this long (default off) */
19
+ idleTimeoutMs?: number;
20
+ /** readyState watchdog cadence (default 1_000ms). fallback for networks that close without notifying */
21
+ watchdogMs?: number;
22
+ }
23
+ export interface ReconnectingNetworkSupportable extends NetworkSupportable {
24
+ /** notified on every successful reconnect (initial connect excluded). the service catches up here with machine.tick() */
25
+ onReconnect(handler: () => void): SocketUnsubscribe;
26
+ /** internal network generation (0 initially, +1 per reconnect) */
27
+ readonly generation: number;
28
+ }
29
+ /** create the L1 reconnect decorator: swaps the internal network on death, keeping instance identity for L3 */
30
+ export declare const createReconnectingNetwork: (factory: () => NetworkSupportable, options?: ReconnectOptions) => ReconnectingNetworkSupportable;
31
+ export interface WireTranslator {
32
+ /** outbound raw transform (default passthrough) */
33
+ outbound?: (raw: string) => string;
34
+ /** inbound raw transform. returning undefined drops the raw (default passthrough) */
35
+ inbound?: (raw: string) => string | undefined;
36
+ }
37
+ /** create the L1 wire translator decorator over a raw string, same slot as `createFilteredNetwork` */
38
+ export declare const createTranslatedNetwork: (source: NetworkSupportable, translator: WireTranslator) => NetworkSupportable;