lemon-model 1.2.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/socket/decorators.js +266 -0
- package/dist/esm/socket/decorators.js.map +1 -0
- package/dist/esm/socket/index.js +2 -0
- package/dist/esm/socket/index.js.map +1 -1
- package/dist/esm/socket/multi.js +94 -0
- package/dist/esm/socket/multi.js.map +1 -0
- package/dist/esm/socket/socket.js +82 -22
- package/dist/esm/socket/socket.js.map +1 -1
- package/dist/esm/socket/transport.js +385 -3
- package/dist/esm/socket/transport.js.map +1 -1
- package/dist/esm/sync/client.js +166 -0
- package/dist/esm/sync/client.js.map +1 -0
- package/dist/esm/sync/index.js +14 -0
- package/dist/esm/sync/index.js.map +1 -0
- package/dist/esm/sync/machine.js +181 -0
- package/dist/esm/sync/machine.js.map +1 -0
- package/dist/esm/sync/network.js +273 -0
- package/dist/esm/sync/network.js.map +1 -0
- package/dist/esm/sync/testing.js +44 -0
- package/dist/esm/sync/testing.js.map +1 -0
- package/dist/esm/sync/ticker.js +53 -0
- package/dist/esm/sync/ticker.js.map +1 -0
- package/dist/esm/sync/types.js +2 -0
- package/dist/{proxy → esm/sync}/types.js.map +1 -1
- package/dist/esm/upload/engine/engine.js +166 -0
- package/dist/esm/upload/engine/engine.js.map +1 -0
- package/dist/esm/upload/engine/executors.js +57 -0
- package/dist/esm/upload/engine/executors.js.map +1 -0
- package/dist/esm/upload/engine/index.js +13 -0
- package/dist/esm/upload/engine/index.js.map +1 -0
- package/dist/esm/upload/engine/progress.js +52 -0
- package/dist/esm/upload/engine/progress.js.map +1 -0
- package/dist/esm/upload/index.js +11 -0
- package/dist/esm/upload/index.js.map +1 -0
- package/dist/esm/upload/testing.js +73 -0
- package/dist/esm/upload/testing.js.map +1 -0
- package/dist/esm/upload/types.js +69 -0
- package/dist/esm/upload/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/socket/client/index.d.ts +9 -0
- package/dist/socket/client/index.js +26 -0
- package/dist/socket/client/index.js.map +1 -0
- package/dist/socket/client/keep-alive-controller.d.ts +35 -0
- package/dist/socket/client/keep-alive-controller.js +101 -0
- package/dist/socket/client/keep-alive-controller.js.map +1 -0
- package/dist/socket/client/message-router.d.ts +15 -0
- package/dist/socket/client/message-router.js +52 -0
- package/dist/socket/client/message-router.js.map +1 -0
- package/dist/socket/client/pending-request-store.d.ts +24 -0
- package/dist/socket/client/pending-request-store.js +78 -0
- package/dist/socket/client/pending-request-store.js.map +1 -0
- package/dist/socket/client/reconnect-controller.d.ts +56 -0
- package/dist/socket/client/reconnect-controller.js +177 -0
- package/dist/socket/client/reconnect-controller.js.map +1 -0
- package/dist/socket/client/rotation-controller.d.ts +32 -0
- package/dist/socket/client/rotation-controller.js +76 -0
- package/dist/socket/client/rotation-controller.js.map +1 -0
- package/dist/socket/client/shared-timer-scheduler.d.ts +17 -0
- package/dist/socket/client/shared-timer-scheduler.js +77 -0
- package/dist/socket/client/shared-timer-scheduler.js.map +1 -0
- package/dist/socket/client/socket-client.d.ts +2 -0
- package/dist/socket/client/socket-client.js +219 -0
- package/dist/socket/client/socket-client.js.map +1 -0
- package/dist/socket/client/types.d.ts +101 -0
- package/dist/socket/client/types.js +3 -0
- package/dist/socket/client/types.js.map +1 -0
- package/dist/socket/client/websocket-client-transport.d.ts +23 -0
- package/dist/socket/client/websocket-client-transport.js +152 -0
- package/dist/socket/client/websocket-client-transport.js.map +1 -0
- package/dist/socket/decorators.d.ts +38 -0
- package/dist/socket/decorators.js +271 -0
- package/dist/socket/decorators.js.map +1 -0
- package/dist/socket/index.d.ts +2 -0
- package/dist/socket/index.js +2 -0
- package/dist/socket/index.js.map +1 -1
- package/dist/socket/multi.d.ts +33 -0
- package/dist/socket/multi.js +98 -0
- package/dist/socket/multi.js.map +1 -0
- package/dist/socket/socket.d.ts +22 -4
- package/dist/socket/socket.js +82 -22
- package/dist/socket/socket.js.map +1 -1
- package/dist/socket/transport.d.ts +124 -6
- package/dist/socket/transport.js +389 -4
- package/dist/socket/transport.js.map +1 -1
- package/dist/sync/client.d.ts +16 -0
- package/dist/sync/client.js +171 -0
- package/dist/sync/client.js.map +1 -0
- package/dist/sync/index.d.ts +10 -0
- package/dist/sync/index.js +30 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/machine.d.ts +3 -0
- package/dist/sync/machine.js +185 -0
- package/dist/sync/machine.js.map +1 -0
- package/dist/sync/network.d.ts +38 -0
- package/dist/sync/network.js +278 -0
- package/dist/sync/network.js.map +1 -0
- package/dist/sync/testing.d.ts +38 -0
- package/dist/sync/testing.js +48 -0
- package/dist/sync/testing.js.map +1 -0
- package/dist/sync/ticker.d.ts +21 -0
- package/dist/sync/ticker.js +57 -0
- package/dist/sync/ticker.js.map +1 -0
- package/dist/sync/types.d.ts +115 -0
- package/dist/sync/types.js +3 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/upload/engine/engine.d.ts +52 -0
- package/dist/upload/engine/engine.js +172 -0
- package/dist/upload/engine/engine.js.map +1 -0
- package/dist/upload/engine/executors.d.ts +44 -0
- package/dist/upload/engine/executors.js +62 -0
- package/dist/upload/engine/executors.js.map +1 -0
- package/dist/upload/engine/index.d.ts +12 -0
- package/dist/upload/engine/index.js +29 -0
- package/dist/upload/engine/index.js.map +1 -0
- package/dist/upload/engine/progress.d.ts +48 -0
- package/dist/upload/engine/progress.js +57 -0
- package/dist/upload/engine/progress.js.map +1 -0
- package/dist/upload/index.d.ts +10 -0
- package/dist/{proxy → upload}/index.js +6 -8
- package/dist/upload/index.js.map +1 -0
- package/dist/upload/testing.d.ts +21 -0
- package/dist/upload/testing.js +76 -0
- package/dist/upload/testing.js.map +1 -0
- package/dist/upload/types.d.ts +230 -0
- package/dist/upload/types.js +74 -0
- package/dist/upload/types.js.map +1 -0
- package/package.json +26 -1
- package/dist/proxy/index.d.ts +0 -12
- package/dist/proxy/index.js.map +0 -1
- package/dist/proxy/proxy.d.ts +0 -75
- package/dist/proxy/proxy.js +0 -198
- package/dist/proxy/proxy.js.map +0 -1
- package/dist/proxy/transport.d.ts +0 -123
- package/dist/proxy/transport.js +0 -401
- package/dist/proxy/transport.js.map +0 -1
- package/dist/proxy/types.d.ts +0 -89
- package/dist/proxy/types.js +0 -12
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTranslatedNetwork = exports.createReconnectingNetwork = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `sync/network.ts`
|
|
6
|
+
* - L1 network decorators: reconnect-on-death wrapper and raw wire translator.
|
|
7
|
+
* - additive to `src/socket`; free to compose with `createFilteredNetwork` (translated -> filtered -> L3).
|
|
8
|
+
*
|
|
9
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
10
|
+
*/
|
|
11
|
+
const socket_1 = require("../socket");
|
|
12
|
+
/**
|
|
13
|
+
* default watchdog cadence for readyState-based death detection.
|
|
14
|
+
* needed because an in-memory `Network.close()` notifies no one (see `socket/socket.ts`); only a
|
|
15
|
+
* real `OwnedWebSocketNetwork`/`BrowserWebSocketNetwork` close notifies via `onError` (see below),
|
|
16
|
+
* so the watchdog is a fallback and can stay slow — 1s keeps idle browser cost negligible.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_WATCHDOG_MS = 1000;
|
|
19
|
+
/** create the L1 reconnect decorator: swaps the internal network on death, keeping instance identity for L3 */
|
|
20
|
+
const createReconnectingNetwork = (factory, options) => new ReconnectingNetwork(factory, options);
|
|
21
|
+
exports.createReconnectingNetwork = createReconnectingNetwork;
|
|
22
|
+
/** `NetworkSupportable` decorator that transparently swaps its internal network on death */
|
|
23
|
+
class ReconnectingNetwork {
|
|
24
|
+
constructor(factory, options) {
|
|
25
|
+
var _a, _b, _c, _d;
|
|
26
|
+
this.factory = factory;
|
|
27
|
+
this._generation = 0;
|
|
28
|
+
/** connected: 정상 감시 | connecting: 후보 open 대기 | backoff: 재시도 대기 | closed: 영구 종료 */
|
|
29
|
+
this.phase = 'connected';
|
|
30
|
+
this.retryCount = 0;
|
|
31
|
+
this.lastMessageAt = Date.now();
|
|
32
|
+
this.messageHandlers = new Set();
|
|
33
|
+
this.errorHandlers = new Set();
|
|
34
|
+
this.reconnectHandlers = new Set();
|
|
35
|
+
this.baseMs = (_a = options === null || options === void 0 ? void 0 : options.baseMs) !== null && _a !== void 0 ? _a : 1000;
|
|
36
|
+
this.factor = (_b = options === null || options === void 0 ? void 0 : options.factor) !== null && _b !== void 0 ? _b : 2;
|
|
37
|
+
this.maxMs = (_c = options === null || options === void 0 ? void 0 : options.maxMs) !== null && _c !== void 0 ? _c : 30000;
|
|
38
|
+
this.maxRetries = options === null || options === void 0 ? void 0 : options.maxRetries;
|
|
39
|
+
this.idleTimeoutMs = options === null || options === void 0 ? void 0 : options.idleTimeoutMs;
|
|
40
|
+
this.delayMs = this.baseMs;
|
|
41
|
+
this.current = factory();
|
|
42
|
+
this.attach(this.current);
|
|
43
|
+
this.watchdogTimer = setInterval(() => this.watch(), (_d = options === null || options === void 0 ? void 0 : options.watchdogMs) !== null && _d !== void 0 ? _d : DEFAULT_WATCHDOG_MS);
|
|
44
|
+
}
|
|
45
|
+
/** internal network generation (0 initially, +1 per reconnect) */
|
|
46
|
+
get generation() {
|
|
47
|
+
return this._generation;
|
|
48
|
+
}
|
|
49
|
+
get readyState() {
|
|
50
|
+
return this.phase === 'closed' ? 'closed' : this.current.readyState;
|
|
51
|
+
}
|
|
52
|
+
ready() {
|
|
53
|
+
var _a, _b, _c;
|
|
54
|
+
return (_c = (_b = (_a = this.current).ready) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : Promise.resolve();
|
|
55
|
+
}
|
|
56
|
+
onOpen(handler) {
|
|
57
|
+
var _a, _b, _c;
|
|
58
|
+
return (_c = (_b = (_a = this.current).onOpen) === null || _b === void 0 ? void 0 : _b.call(_a, handler)) !== null && _c !== void 0 ? _c : (() => undefined);
|
|
59
|
+
}
|
|
60
|
+
/** while disconnected (mid-reconnect or permanently closed) this throws synchronously; no send queuing */
|
|
61
|
+
send(data) {
|
|
62
|
+
if (this.phase === 'closed')
|
|
63
|
+
throw new Error(`@network is closed - reconnectingNetwork.send`);
|
|
64
|
+
if (this.current.readyState !== 'open') {
|
|
65
|
+
throw new Error(`@network is not connected - reconnectingNetwork.send`);
|
|
66
|
+
}
|
|
67
|
+
this.current.send(data);
|
|
68
|
+
}
|
|
69
|
+
onMessage(handler) {
|
|
70
|
+
this.messageHandlers.add(handler);
|
|
71
|
+
return () => this.messageHandlers.delete(handler);
|
|
72
|
+
}
|
|
73
|
+
onError(handler) {
|
|
74
|
+
this.errorHandlers.add(handler);
|
|
75
|
+
return () => this.errorHandlers.delete(handler);
|
|
76
|
+
}
|
|
77
|
+
onReconnect(handler) {
|
|
78
|
+
this.reconnectHandlers.add(handler);
|
|
79
|
+
return () => this.reconnectHandlers.delete(handler);
|
|
80
|
+
}
|
|
81
|
+
/** options are remembered and re-applied to every reconnected generation */
|
|
82
|
+
configure(options) {
|
|
83
|
+
var _a, _b;
|
|
84
|
+
this.configured = Object.assign(Object.assign({}, this.configured), options);
|
|
85
|
+
(_b = (_a = this.current).configure) === null || _b === void 0 ? void 0 : _b.call(_a, this.configured);
|
|
86
|
+
}
|
|
87
|
+
/** stop reconnecting and close the internal network for good */
|
|
88
|
+
close(code, reason) {
|
|
89
|
+
if (this.phase === 'closed')
|
|
90
|
+
return;
|
|
91
|
+
this.phase = 'closed';
|
|
92
|
+
clearInterval(this.watchdogTimer);
|
|
93
|
+
if (this.reconnectTimer)
|
|
94
|
+
clearTimeout(this.reconnectTimer);
|
|
95
|
+
this.detach();
|
|
96
|
+
this.current.close(code, reason);
|
|
97
|
+
this.messageHandlers.clear();
|
|
98
|
+
this.errorHandlers.clear();
|
|
99
|
+
this.reconnectHandlers.clear();
|
|
100
|
+
}
|
|
101
|
+
attach(network) {
|
|
102
|
+
this.unsubscribeMessage = network.onMessage(raw => {
|
|
103
|
+
this.lastMessageAt = Date.now();
|
|
104
|
+
for (const handler of [...this.messageHandlers])
|
|
105
|
+
handler(raw);
|
|
106
|
+
});
|
|
107
|
+
this.unsubscribeError = network.onError((error, context) => {
|
|
108
|
+
for (const handler of [...this.errorHandlers])
|
|
109
|
+
handler(error, Object.assign(Object.assign({}, context), { network: this }));
|
|
110
|
+
//! `OwnedWebSocketNetwork`/`BrowserWebSocketNetwork` notify a close via onError with this scope
|
|
111
|
+
//! (see `WEBSOCKET_NETWORK_SCOPE` in `socket/websocket.ts`). other onError calls (e.g. delivery
|
|
112
|
+
//! errors) are forwarded above but do not by themselves mean the network died.
|
|
113
|
+
if (context.scope === socket_1.WEBSOCKET_NETWORK_SCOPE.ownedClose ||
|
|
114
|
+
context.scope === socket_1.WEBSOCKET_NETWORK_SCOPE.browserClose) {
|
|
115
|
+
this.onInnerDeath();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
detach() {
|
|
120
|
+
var _a, _b, _c;
|
|
121
|
+
(_a = this.unsubscribeMessage) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
122
|
+
(_b = this.unsubscribeError) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
123
|
+
(_c = this.unsubscribeCandidateOpen) === null || _c === void 0 ? void 0 : _c.call(this);
|
|
124
|
+
}
|
|
125
|
+
onInnerDeath() {
|
|
126
|
+
if (this.phase === 'connected')
|
|
127
|
+
this.beginBackoff();
|
|
128
|
+
else if (this.phase === 'connecting')
|
|
129
|
+
this.failAttempt(new Error(`@candidate network died before open - reconnectingNetwork`), 'reconnectingNetwork.connect');
|
|
130
|
+
}
|
|
131
|
+
/** watchdog fallback for networks that close without notifying (e.g. in-memory `Network.close()`) */
|
|
132
|
+
watch() {
|
|
133
|
+
const state = this.current.readyState;
|
|
134
|
+
if (this.phase === 'connected') {
|
|
135
|
+
if (state === 'closing' || state === 'closed')
|
|
136
|
+
return this.beginBackoff();
|
|
137
|
+
if (this.idleTimeoutMs && Date.now() - this.lastMessageAt > this.idleTimeoutMs)
|
|
138
|
+
this.beginBackoff();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (this.phase === 'connecting') {
|
|
142
|
+
if (state === 'open')
|
|
143
|
+
return this.settleReconnected(); // onOpen 없는 network용 fallback
|
|
144
|
+
if (state === 'closing' || state === 'closed')
|
|
145
|
+
this.onInnerDeath();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
beginBackoff() {
|
|
149
|
+
this.detach();
|
|
150
|
+
this.phase = 'backoff';
|
|
151
|
+
this.reconnectTimer = setTimeout(() => this.attemptReconnect(), this.delayMs);
|
|
152
|
+
}
|
|
153
|
+
attemptReconnect() {
|
|
154
|
+
var _a, _b;
|
|
155
|
+
if (this.phase === 'closed')
|
|
156
|
+
return;
|
|
157
|
+
if (this.maxRetries != null && this.retryCount >= this.maxRetries) {
|
|
158
|
+
this.phase = 'closed';
|
|
159
|
+
clearInterval(this.watchdogTimer);
|
|
160
|
+
const error = new Error(`@network exceeded maxRetries[${this.maxRetries}] - reconnectingNetwork`);
|
|
161
|
+
for (const handler of [...this.errorHandlers])
|
|
162
|
+
handler(error, { scope: 'reconnectingNetwork.maxRetries', network: this });
|
|
163
|
+
this.messageHandlers.clear();
|
|
164
|
+
this.errorHandlers.clear();
|
|
165
|
+
this.reconnectHandlers.clear();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
let next;
|
|
169
|
+
try {
|
|
170
|
+
next = this.factory();
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
this.failAttempt(e, 'reconnectingNetwork.factory');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.current = next;
|
|
177
|
+
this.attach(next);
|
|
178
|
+
if (this.configured)
|
|
179
|
+
(_a = next.configure) === null || _a === void 0 ? void 0 : _a.call(next, this.configured);
|
|
180
|
+
this.phase = 'connecting';
|
|
181
|
+
//! success is declared only once the candidate actually OPENS. a factory return alone is not a
|
|
182
|
+
//! connection (a real WebSocket constructs fine while the server is down), so resetting backoff
|
|
183
|
+
//! here would degrade to a constant-rate reconnect storm with an onReconnect/tick per attempt.
|
|
184
|
+
const state = next.readyState;
|
|
185
|
+
if (state === 'open')
|
|
186
|
+
return this.settleReconnected();
|
|
187
|
+
if (state === 'closing' || state === 'closed')
|
|
188
|
+
return this.onInnerDeath();
|
|
189
|
+
this.unsubscribeCandidateOpen = (_b = next.onOpen) === null || _b === void 0 ? void 0 : _b.call(next, () => {
|
|
190
|
+
if (this.phase === 'connecting' && this.current === next)
|
|
191
|
+
this.settleReconnected();
|
|
192
|
+
});
|
|
193
|
+
//! a candidate stuck in 'connecting' is bounded by the inner network's own connect timeout
|
|
194
|
+
//! (e.g. `OwnedWebSocketNetwork.connectTimeoutMs`), which actual-closes and lands in onInnerDeath.
|
|
195
|
+
}
|
|
196
|
+
/** a reconnect attempt failed (factory throw, or candidate died before open): keep growing the backoff */
|
|
197
|
+
failAttempt(error, scope) {
|
|
198
|
+
this.detach();
|
|
199
|
+
this.retryCount++;
|
|
200
|
+
this.delayMs = Math.min(this.delayMs * this.factor, this.maxMs);
|
|
201
|
+
for (const handler of [...this.errorHandlers])
|
|
202
|
+
handler(error, { scope, network: this });
|
|
203
|
+
this.phase = 'backoff';
|
|
204
|
+
this.reconnectTimer = setTimeout(() => this.attemptReconnect(), this.delayMs);
|
|
205
|
+
}
|
|
206
|
+
/** the candidate opened: only now reset the backoff/retry budget and notify onReconnect */
|
|
207
|
+
settleReconnected() {
|
|
208
|
+
var _a;
|
|
209
|
+
(_a = this.unsubscribeCandidateOpen) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
210
|
+
this.unsubscribeCandidateOpen = undefined;
|
|
211
|
+
this.phase = 'connected';
|
|
212
|
+
this._generation++;
|
|
213
|
+
this.retryCount = 0;
|
|
214
|
+
this.delayMs = this.baseMs;
|
|
215
|
+
this.lastMessageAt = Date.now();
|
|
216
|
+
for (const handler of [...this.reconnectHandlers])
|
|
217
|
+
handler();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/** create the L1 wire translator decorator over a raw string, same slot as `createFilteredNetwork` */
|
|
221
|
+
const createTranslatedNetwork = (source, translator) => new TranslatedNetwork(source, translator);
|
|
222
|
+
exports.createTranslatedNetwork = createTranslatedNetwork;
|
|
223
|
+
/** `NetworkSupportable` decorator that translates raw strings at the wire boundary */
|
|
224
|
+
class TranslatedNetwork {
|
|
225
|
+
constructor(source, translator) {
|
|
226
|
+
this.source = source;
|
|
227
|
+
this.translator = translator;
|
|
228
|
+
this.errorHandlers = new Set();
|
|
229
|
+
this.unsubscribeSourceError = source.onError((error, context) => this.emitError(error, context));
|
|
230
|
+
}
|
|
231
|
+
get readyState() {
|
|
232
|
+
return this.source.readyState;
|
|
233
|
+
}
|
|
234
|
+
ready() {
|
|
235
|
+
var _a, _b, _c;
|
|
236
|
+
return (_c = (_b = (_a = this.source).ready) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : Promise.resolve();
|
|
237
|
+
}
|
|
238
|
+
onOpen(handler) {
|
|
239
|
+
var _a, _b, _c;
|
|
240
|
+
return (_c = (_b = (_a = this.source).onOpen) === null || _b === void 0 ? void 0 : _b.call(_a, handler)) !== null && _c !== void 0 ? _c : (() => undefined);
|
|
241
|
+
}
|
|
242
|
+
send(data) {
|
|
243
|
+
this.source.send(this.translator.outbound ? this.translator.outbound(data) : data);
|
|
244
|
+
}
|
|
245
|
+
onMessage(handler) {
|
|
246
|
+
return this.source.onMessage(raw => {
|
|
247
|
+
let translated;
|
|
248
|
+
try {
|
|
249
|
+
translated = this.translator.inbound ? this.translator.inbound(raw) : raw;
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
this.emitError(e, { scope: 'translatedNetwork.inbound' });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (translated === undefined)
|
|
256
|
+
return; // dropped by translator
|
|
257
|
+
handler(translated);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
configure(options) {
|
|
261
|
+
var _a, _b;
|
|
262
|
+
(_b = (_a = this.source).configure) === null || _b === void 0 ? void 0 : _b.call(_a, options);
|
|
263
|
+
}
|
|
264
|
+
onError(handler) {
|
|
265
|
+
this.errorHandlers.add(handler);
|
|
266
|
+
return () => this.errorHandlers.delete(handler);
|
|
267
|
+
}
|
|
268
|
+
close(code, reason) {
|
|
269
|
+
this.unsubscribeSourceError();
|
|
270
|
+
this.errorHandlers.clear();
|
|
271
|
+
this.source.close(code, reason);
|
|
272
|
+
}
|
|
273
|
+
emitError(error, context) {
|
|
274
|
+
for (const handler of [...this.errorHandlers])
|
|
275
|
+
handler(error, Object.assign(Object.assign({}, context), { network: this }));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/sync/network.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sync/testing.ts`
|
|
3
|
+
* - bridges the in-memory `Peer` simulator into a single `NetworkSupportable` for L3/L4 specs.
|
|
4
|
+
* - test/dev only; kept OUT of `sync/index.ts` so production bundles never pull in the simulator runtime.
|
|
5
|
+
*
|
|
6
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
7
|
+
*/
|
|
8
|
+
import { PeerOptions } from '../socket/testing';
|
|
9
|
+
import { NetworkSupportable, PeerSupportable } from '../socket/types';
|
|
10
|
+
/** server-side pieces of a bridged peer link */
|
|
11
|
+
export interface PeerBridge {
|
|
12
|
+
/** bridged client-side network. wire this into `createSocketClient()` */
|
|
13
|
+
network: NetworkSupportable;
|
|
14
|
+
/** plain server `Peer`. register `onMessage()`/`post()` on it as the simulated server band */
|
|
15
|
+
server: PeerSupportable;
|
|
16
|
+
/** raw downlink (server -> client). for JSONTransport/progress producers simulating server pushes that are not sync envelopes */
|
|
17
|
+
serverNetwork: NetworkSupportable;
|
|
18
|
+
/** assigned client id, usable with `server.post(message, { clientId })` */
|
|
19
|
+
clientId: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PeerBridgeOptions {
|
|
22
|
+
server?: PeerOptions;
|
|
23
|
+
client?: PeerOptions;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* create a connected client/server `Peer` pair and expose the client side as one `NetworkSupportable`.
|
|
27
|
+
*
|
|
28
|
+
* `Peer.dispatch` settles an envelope against its own pending map, and otherwise fans every remaining
|
|
29
|
+
* envelope out to `Peer.onMessage()` listeners whose return value becomes an automatic `result` reply
|
|
30
|
+
* (regardless of whether the listener actually returns anything). A client-side bridge built on
|
|
31
|
+
* `clientPeer.post()` + `clientPeer.onMessage()` therefore echoes every pull result and every server
|
|
32
|
+
* event straight back to the server as an empty `result`, and — since the server has no pending for
|
|
33
|
+
* that mid either — the server echoes it right back, forever. `send()` and `onMessage()` below instead
|
|
34
|
+
* talk to the raw uplink/downlink `Network` instances directly, bypassing `Peer` dispatch on the client
|
|
35
|
+
* side entirely, so no such reply is ever produced. The `server` `Peer` itself is untouched and keeps
|
|
36
|
+
* its normal `onMessage()`/`post()` contract for the simulated server band.
|
|
37
|
+
*/
|
|
38
|
+
export declare const createPeerBridge: (options?: PeerBridgeOptions) => PeerBridge;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPeerBridge = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `sync/testing.ts`
|
|
6
|
+
* - bridges the in-memory `Peer` simulator into a single `NetworkSupportable` for L3/L4 specs.
|
|
7
|
+
* - test/dev only; kept OUT of `sync/index.ts` so production bundles never pull in the simulator runtime.
|
|
8
|
+
*
|
|
9
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
10
|
+
*/
|
|
11
|
+
//! simulator runtime lives in the isolated `../socket/testing` module (kept out of the root barrel).
|
|
12
|
+
const testing_1 = require("../socket/testing");
|
|
13
|
+
/**
|
|
14
|
+
* create a connected client/server `Peer` pair and expose the client side as one `NetworkSupportable`.
|
|
15
|
+
*
|
|
16
|
+
* `Peer.dispatch` settles an envelope against its own pending map, and otherwise fans every remaining
|
|
17
|
+
* envelope out to `Peer.onMessage()` listeners whose return value becomes an automatic `result` reply
|
|
18
|
+
* (regardless of whether the listener actually returns anything). A client-side bridge built on
|
|
19
|
+
* `clientPeer.post()` + `clientPeer.onMessage()` therefore echoes every pull result and every server
|
|
20
|
+
* event straight back to the server as an empty `result`, and — since the server has no pending for
|
|
21
|
+
* that mid either — the server echoes it right back, forever. `send()` and `onMessage()` below instead
|
|
22
|
+
* talk to the raw uplink/downlink `Network` instances directly, bypassing `Peer` dispatch on the client
|
|
23
|
+
* side entirely, so no such reply is ever produced. The `server` `Peer` itself is untouched and keeps
|
|
24
|
+
* its normal `onMessage()`/`post()` contract for the simulated server band.
|
|
25
|
+
*/
|
|
26
|
+
const createPeerBridge = (options) => {
|
|
27
|
+
let serverNetwork;
|
|
28
|
+
const server = (0, testing_1.createPeer)(Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.server), { networkFactory: context => (serverNetwork = (0, testing_1.createNetwork)(Object.assign({ id: context.id, fromPeerId: context.fromPeerId, toPeerId: context.toPeerId, logger: context.logger }, context.options))) }));
|
|
29
|
+
const client = (0, testing_1.createPeer)(options === null || options === void 0 ? void 0 : options.client);
|
|
30
|
+
const clientId = client.connect(server);
|
|
31
|
+
const clientNetwork = client.network;
|
|
32
|
+
if (!clientNetwork || !serverNetwork) {
|
|
33
|
+
throw new Error(`@peer bridge network capture failed - sync.testing.createPeerBridge`);
|
|
34
|
+
}
|
|
35
|
+
const network = {
|
|
36
|
+
get readyState() {
|
|
37
|
+
return client.readyState;
|
|
38
|
+
},
|
|
39
|
+
ready: () => client.ready(),
|
|
40
|
+
send: (data) => clientNetwork.send(data),
|
|
41
|
+
onMessage: handler => serverNetwork.onMessage(handler),
|
|
42
|
+
onError: handler => client.onError(handler),
|
|
43
|
+
close: () => client.close(),
|
|
44
|
+
};
|
|
45
|
+
return { network, server, serverNetwork, clientId };
|
|
46
|
+
};
|
|
47
|
+
exports.createPeerBridge = createPeerBridge;
|
|
48
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/sync/testing.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACH,qGAAqG;AACrG,+CAA2E;AAoB3E;;;;;;;;;;;;GAYG;AACI,MAAM,gBAAgB,GAAG,CAAC,OAA2B,EAAc,EAAE;IACxE,IAAI,aAA6C,CAAC;IAClD,MAAM,MAAM,GAAoB,IAAA,oBAAU,kCACnC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAClB,cAAc,EAAE,OAAO,CAAC,EAAE,CACtB,CAAC,aAAa,GAAG,IAAA,uBAAa,kBAC1B,EAAE,EAAE,OAAO,CAAC,EAAE,EACd,UAAU,EAAE,OAAO,CAAC,UAAU,EAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAC1B,MAAM,EAAE,OAAO,CAAC,MAAM,IACnB,OAAO,CAAC,OAAO,EACpB,CAAC,IACT,CAAC;IACH,MAAM,MAAM,GAAoB,IAAA,oBAAU,EAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;IACrC,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa,EAAE;QAClC,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;KAC1F;IAED,MAAM,OAAO,GAAuB;QAChC,IAAI,UAAU;YACV,OAAO,MAAM,CAAC,UAAU,CAAC;QAC7B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;QAC3B,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,aAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QACjD,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,aAAc,CAAC,SAAS,CAAC,OAAO,CAAC;QACvD,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QAC3C,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;KAC9B,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AACxD,CAAC,CAAC;AAhCW,QAAA,gBAAgB,oBAgC3B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sync/ticker.ts`
|
|
3
|
+
* - opt-in tick utility for the service-side tick-owning point. works over any `() => Promise` (machine-agnostic).
|
|
4
|
+
*
|
|
5
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
6
|
+
*/
|
|
7
|
+
export interface SyncTickerOptions {
|
|
8
|
+
/** steady-state period */
|
|
9
|
+
intervalMs: number;
|
|
10
|
+
/** exponential backoff multiplier on failure (default 2) */
|
|
11
|
+
factor?: number;
|
|
12
|
+
/** backoff delay cap (default 60_000ms) */
|
|
13
|
+
maxMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface SyncTickerSupportable {
|
|
16
|
+
start(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
readonly running: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** create a self-rescheduling ticker: no overlap, backoff on failure, restore on success */
|
|
21
|
+
export declare const createSyncTicker: (tick: () => Promise<unknown>, options: SyncTickerOptions) => SyncTickerSupportable;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSyncTicker = void 0;
|
|
4
|
+
/** create a self-rescheduling ticker: no overlap, backoff on failure, restore on success */
|
|
5
|
+
const createSyncTicker = (tick, options) => new SyncTicker(tick, options);
|
|
6
|
+
exports.createSyncTicker = createSyncTicker;
|
|
7
|
+
class SyncTicker {
|
|
8
|
+
constructor(tick, options) {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
this.tick = tick;
|
|
11
|
+
this.options = options;
|
|
12
|
+
this._running = false;
|
|
13
|
+
/** bumped on every start()/stop(): an in-flight tick from an old epoch must not reschedule a second chain */
|
|
14
|
+
this.epoch = 0;
|
|
15
|
+
this.factor = (_a = options.factor) !== null && _a !== void 0 ? _a : 2;
|
|
16
|
+
this.maxMs = (_b = options.maxMs) !== null && _b !== void 0 ? _b : 60000;
|
|
17
|
+
this.delayMs = options.intervalMs;
|
|
18
|
+
}
|
|
19
|
+
get running() {
|
|
20
|
+
return this._running;
|
|
21
|
+
}
|
|
22
|
+
start() {
|
|
23
|
+
if (this._running)
|
|
24
|
+
return; // re-entrant start() is a no-op
|
|
25
|
+
this._running = true;
|
|
26
|
+
this.epoch++;
|
|
27
|
+
this.delayMs = this.options.intervalMs;
|
|
28
|
+
this.schedule(this.epoch);
|
|
29
|
+
}
|
|
30
|
+
stop() {
|
|
31
|
+
this._running = false;
|
|
32
|
+
this.epoch++;
|
|
33
|
+
if (this.timer)
|
|
34
|
+
clearTimeout(this.timer);
|
|
35
|
+
this.timer = undefined;
|
|
36
|
+
}
|
|
37
|
+
schedule(epoch) {
|
|
38
|
+
if (!this._running || epoch !== this.epoch)
|
|
39
|
+
return;
|
|
40
|
+
this.timer = setTimeout(() => this.run(epoch), this.delayMs);
|
|
41
|
+
}
|
|
42
|
+
/** the next tick is scheduled only after this one settles (setTimeout chain) - no overlap */
|
|
43
|
+
run(epoch) {
|
|
44
|
+
this.tick().then(() => {
|
|
45
|
+
if (epoch !== this.epoch)
|
|
46
|
+
return; // stale chain (stopped/restarted while in flight)
|
|
47
|
+
this.delayMs = this.options.intervalMs;
|
|
48
|
+
this.schedule(epoch);
|
|
49
|
+
}, () => {
|
|
50
|
+
if (epoch !== this.epoch)
|
|
51
|
+
return; // stale chain (stopped/restarted while in flight)
|
|
52
|
+
this.delayMs = Math.min(this.delayMs * this.factor, this.maxMs);
|
|
53
|
+
this.schedule(epoch);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=ticker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticker.js","sourceRoot":"","sources":["../../src/sync/ticker.ts"],"names":[],"mappings":";;;AAqBA,4FAA4F;AACrF,MAAM,gBAAgB,GAAG,CAAC,IAA4B,EAAE,OAA0B,EAAyB,EAAE,CAChH,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AADrB,QAAA,gBAAgB,oBACK;AAElC,MAAM,UAAU;IASZ,YAAoC,IAA4B,EAAmB,OAA0B;;QAAzE,SAAI,GAAJ,IAAI,CAAwB;QAAmB,YAAO,GAAP,OAAO,CAAmB;QAJrG,aAAQ,GAAG,KAAK,CAAC;QACzB,6GAA6G;QACrG,UAAK,GAAG,CAAC,CAAC;QAGd,IAAI,CAAC,MAAM,GAAG,MAAA,OAAO,CAAC,MAAM,mCAAI,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,MAAA,OAAO,CAAC,KAAK,mCAAI,KAAM,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IACtC,CAAC;IAED,IAAW,OAAO;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC;IACzB,CAAC;IAEM,KAAK;QACR,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,CAAC,gCAAgC;QAC3D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAEM,IAAI;QACP,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,IAAI,CAAC,KAAK;YAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IAC3B,CAAC;IAEO,QAAQ,CAAC,KAAa;QAC1B,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;YAAE,OAAO;QACnD,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACjE,CAAC;IAED,6FAA6F;IACrF,GAAG,CAAC,KAAa;QACrB,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CACZ,GAAG,EAAE;YACD,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,kDAAkD;YACpF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;YACvC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,EACD,GAAG,EAAE;YACD,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,kDAAkD;YACpF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAChE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,CACJ,CAAC;IACN,CAAC;CACJ"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sync/types.ts`
|
|
3
|
+
* - public contracts for the model sync client: L3 socket client runtime + L4 sync machine.
|
|
4
|
+
*
|
|
5
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
6
|
+
*/
|
|
7
|
+
import { NetworkSupportable, RawOwnershipPredicate, SocketErrorHandler, SocketMessage, SocketUnsubscribe } from '../socket';
|
|
8
|
+
/**
|
|
9
|
+
* minimal contract a sync target must satisfy — the machine only ever reads these three fields.
|
|
10
|
+
* `CoreModel` satisfies this automatically; any external model with id/updatedAt qualifies too.
|
|
11
|
+
*/
|
|
12
|
+
export interface SyncTarget {
|
|
13
|
+
/** unique id */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** last-modified timestamp (ms) — the field the default versionOf reads */
|
|
16
|
+
updatedAt?: number;
|
|
17
|
+
/** deleted timestamp (ms) — presence removes the model from the store */
|
|
18
|
+
deletedAt?: number;
|
|
19
|
+
}
|
|
20
|
+
/** mid generator */
|
|
21
|
+
export interface SocketClientIdentityProvider {
|
|
22
|
+
nextMid(): string;
|
|
23
|
+
}
|
|
24
|
+
export interface SocketClientOptions {
|
|
25
|
+
/** request-response wait limit (default 15_000ms) */
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
/** concurrent pending cap. request() beyond this rejects immediately (default unlimited) */
|
|
28
|
+
maxPending?: number;
|
|
29
|
+
/** ownership decision over raw strings this runtime handles. wrapped internally with createFilteredNetwork */
|
|
30
|
+
filter?: RawOwnershipPredicate;
|
|
31
|
+
/** mid generator (default provided) */
|
|
32
|
+
identity?: SocketClientIdentityProvider;
|
|
33
|
+
}
|
|
34
|
+
export interface SocketRequestOptions {
|
|
35
|
+
/** timeout override for this request only */
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface SocketClientSupportable {
|
|
39
|
+
/** underlying network (shared socket) */
|
|
40
|
+
readonly network: NetworkSupportable;
|
|
41
|
+
/** number of requests awaiting a response */
|
|
42
|
+
readonly pendingCount: number;
|
|
43
|
+
/** delegate network ready */
|
|
44
|
+
ready(): Promise<void>;
|
|
45
|
+
/** send a request and wait for the result/error of the same mid */
|
|
46
|
+
request<T = any, R = any>(type: string, data: T, options?: SocketRequestOptions): Promise<R>;
|
|
47
|
+
/** one-way send without waiting for a response */
|
|
48
|
+
post<T = any>(type: string, data: T): void;
|
|
49
|
+
/** subscribe to events of a specific type */
|
|
50
|
+
onType<T = any>(type: string, handler: (data: T, message: SocketMessage<T>) => void): SocketUnsubscribe;
|
|
51
|
+
/** subscribe to inbound envelopes not matched to a pending request. unmatched result/error is treated as a late response and quietly dropped */
|
|
52
|
+
onMessage(handler: (message: SocketMessage) => void): SocketUnsubscribe;
|
|
53
|
+
/** observe asynchronous errors (timeout, send failure, network error) */
|
|
54
|
+
onError(handler: SocketErrorHandler): SocketUnsubscribe;
|
|
55
|
+
/** reject all pending + detach listeners. does not close network (socket is shared) */
|
|
56
|
+
close(): void;
|
|
57
|
+
}
|
|
58
|
+
/** change notification */
|
|
59
|
+
export interface SyncChangeEvent<M extends SyncTarget> {
|
|
60
|
+
/** cause of the change */
|
|
61
|
+
cause: 'pull' | 'event';
|
|
62
|
+
/** models applied or removed by this change */
|
|
63
|
+
models: M[];
|
|
64
|
+
}
|
|
65
|
+
/** server-confirmed page extracted from a pull reply */
|
|
66
|
+
export interface SyncReplyPage<M extends SyncTarget> {
|
|
67
|
+
/** server-confirmed models */
|
|
68
|
+
models: M[];
|
|
69
|
+
/** cursor for the next page. absent means pull is done */
|
|
70
|
+
next?: any;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* protocol adapter — injected by the service.
|
|
74
|
+
* the machine does not know the wire protocol, and the adapter does not know local state.
|
|
75
|
+
*/
|
|
76
|
+
export interface SyncProtocolAdapter<M extends SyncTarget> {
|
|
77
|
+
/** shared version axis for freshness judgement and the watermark. undefined skips the model (default m => m.updatedAt).
|
|
78
|
+
* pure/total like parseEvent — never throw; return a positive finite number or undefined */
|
|
79
|
+
versionOf?: (model: M) => number | undefined;
|
|
80
|
+
/** build a pull request from since(versionOf-axis watermark) and cursor. omitted since means full pull */
|
|
81
|
+
buildPull(since?: number, cursor?: any): {
|
|
82
|
+
type: string;
|
|
83
|
+
data: any;
|
|
84
|
+
};
|
|
85
|
+
/** extract server-confirmed models and the next cursor from pull reply data */
|
|
86
|
+
parseReply(data: any): SyncReplyPage<M>;
|
|
87
|
+
/** extract this type's models from a server-sent event. undefined if not owned. must be a pure predicate/extractor with no side effects */
|
|
88
|
+
parseEvent(message: SocketMessage): M[] | undefined;
|
|
89
|
+
}
|
|
90
|
+
export interface ModelSyncOptions<M extends SyncTarget> {
|
|
91
|
+
adapter: SyncProtocolAdapter<M>;
|
|
92
|
+
/** run an initial pull right after register (default true). failure still leaves register valid; the next pull/tick pulls from scratch. set false and call pull() directly to observe failures */
|
|
93
|
+
initialPull?: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** sync handle for a single model type */
|
|
96
|
+
export interface ModelSyncSupportable<M extends SyncTarget> {
|
|
97
|
+
readonly type: string;
|
|
98
|
+
/** read-only local state lookup */
|
|
99
|
+
get(id: string): M | undefined;
|
|
100
|
+
list(): M[];
|
|
101
|
+
/** pull changes since the watermark and apply them. loops the cursor until parseReply.next is absent */
|
|
102
|
+
pull(): Promise<M[]>;
|
|
103
|
+
/** subscribe to change notifications */
|
|
104
|
+
onChange(handler: (event: SyncChangeEvent<M>) => void): SocketUnsubscribe;
|
|
105
|
+
/** detach this type's subscriptions/listeners */
|
|
106
|
+
close(): void;
|
|
107
|
+
}
|
|
108
|
+
export interface SyncMachineSupportable {
|
|
109
|
+
/** register a domain model type. re-registering the same type returns the existing handle and ignores the new options */
|
|
110
|
+
register<M extends SyncTarget>(type: string, options: ModelSyncOptions<M>): ModelSyncSupportable<M>;
|
|
111
|
+
/** pull every registered type once. the service calls this at whatever cadence it wants. a type whose pull is already in flight is skipped, not nested */
|
|
112
|
+
tick(): Promise<void>;
|
|
113
|
+
/** detach everything */
|
|
114
|
+
close(): void;
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `upload/engine/engine.ts`
|
|
3
|
+
* - the one upload flow every shell shares: `start` -> run executors -> `complete`.
|
|
4
|
+
* - swapping the server's transfer method never touches this file; a new `UploadTransfer` kind
|
|
5
|
+
* arrives as one more `UploadTransferExecutor` passed to the constructor.
|
|
6
|
+
* - runtime-neutral like the contract itself: no Node, DOM, or AWS types. the shell injects the
|
|
7
|
+
* three things that are shell-specific — reading bytes (`UploadSource`), the HTTP primitives
|
|
8
|
+
* (see `./executors`), and hashing.
|
|
9
|
+
*
|
|
10
|
+
* @copyright (C) 2026 LemonCloud Co Ltd. - All Rights Reserved.
|
|
11
|
+
*/
|
|
12
|
+
import type { UploadCompleteItem, UploadFailure, UploadService, UploadTransfer, UploadView } from '../types';
|
|
13
|
+
import type { UploadBatchProgressSink, UploadProgressSink } from './progress';
|
|
14
|
+
/** bytes provider; hides DOM `File`, RN uri, Electron path behind one shape */
|
|
15
|
+
export interface UploadSource {
|
|
16
|
+
name: string;
|
|
17
|
+
contentType: string;
|
|
18
|
+
contentSize: number;
|
|
19
|
+
/** whole send (roadmap 1·2 sizes) */
|
|
20
|
+
bytes(): Promise<Uint8Array>;
|
|
21
|
+
/** sha256 hex(64) when this shell chooses to hash at send time; omit to skip */
|
|
22
|
+
hash?(): Promise<string | undefined>;
|
|
23
|
+
}
|
|
24
|
+
/** moves bytes for one transfer kind and reports the receipt the server expects in `complete` */
|
|
25
|
+
export interface UploadTransferExecutor<T extends UploadTransfer = UploadTransfer> {
|
|
26
|
+
readonly kind: T['kind'];
|
|
27
|
+
/** `onProgress` is optional to CALL, not to accept: an executor that cannot observe bytes never calls it */
|
|
28
|
+
run(id: string, transfer: T, source: UploadSource, onProgress: UploadProgressSink): Promise<UploadCompleteItem>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* transfers in flight at once.
|
|
32
|
+
* - a browser queues beyond ~6 per origin, and a queued presigned url burns its `expiresAt` while it waits.
|
|
33
|
+
* - override per engine when a shell measures something better.
|
|
34
|
+
*/
|
|
35
|
+
export declare const UPLOAD_DEFAULT_CONCURRENCY = 3;
|
|
36
|
+
export declare class UploadEngine {
|
|
37
|
+
private readonly service;
|
|
38
|
+
private readonly executors;
|
|
39
|
+
private readonly options?;
|
|
40
|
+
private readonly kinds;
|
|
41
|
+
constructor(service: UploadService, executors: ReadonlyArray<UploadTransferExecutor>, options?: {
|
|
42
|
+
concurrency?: number;
|
|
43
|
+
});
|
|
44
|
+
/** returns one view per source, in order: `stored`, or `failed` with the reason. `onProgress` gets a snapshot per change */
|
|
45
|
+
upload(sources: ReadonlyArray<UploadSource>, onProgress?: UploadBatchProgressSink): Promise<UploadView[]>;
|
|
46
|
+
private transferTracked;
|
|
47
|
+
private transfer;
|
|
48
|
+
}
|
|
49
|
+
/** normalize an error thrown by the API adapter */
|
|
50
|
+
export declare const asApiFailure: (error: unknown) => UploadFailure;
|
|
51
|
+
/** normalize a non-2xx answer from the storage endpoint (status 0 = no response) */
|
|
52
|
+
export declare const asStorageFailure: (status: number, code?: string) => UploadFailure;
|