mvc-kit 2.12.5 → 2.13.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/agent-config/bin/postinstall.mjs +4 -3
- package/agent-config/bin/setup.mjs +5 -1
- package/agent-config/claude-code/agents/mvc-kit-architect.md +11 -8
- package/agent-config/claude-code/skills/guide/SKILL.md +20 -7
- package/agent-config/claude-code/skills/guide/patterns.md +12 -0
- package/agent-config/claude-code/skills/guide/recipes.md +510 -0
- package/agent-config/claude-code/skills/guide/testing.md +297 -0
- package/agent-config/claude-code/skills/review/SKILL.md +3 -13
- package/agent-config/claude-code/skills/review/checklist.md +30 -5
- package/agent-config/claude-code/skills/scaffold/SKILL.md +4 -13
- package/agent-config/lib/install-claude.mjs +84 -25
- package/dist/Channel.cjs +276 -300
- package/dist/Channel.cjs.map +1 -1
- package/dist/Channel.js +275 -299
- package/dist/Channel.js.map +1 -1
- package/dist/Collection.cjs +424 -504
- package/dist/Collection.cjs.map +1 -1
- package/dist/Collection.js +423 -503
- package/dist/Collection.js.map +1 -1
- package/dist/Controller.cjs +70 -67
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.js +69 -66
- package/dist/Controller.js.map +1 -1
- package/dist/EventBus.cjs +77 -88
- package/dist/EventBus.cjs.map +1 -1
- package/dist/EventBus.js +76 -87
- package/dist/EventBus.js.map +1 -1
- package/dist/Feed.cjs +81 -77
- package/dist/Feed.cjs.map +1 -1
- package/dist/Feed.js +80 -76
- package/dist/Feed.js.map +1 -1
- package/dist/Model.cjs +181 -207
- package/dist/Model.cjs.map +1 -1
- package/dist/Model.js +179 -205
- package/dist/Model.js.map +1 -1
- package/dist/Pagination.cjs +75 -73
- package/dist/Pagination.cjs.map +1 -1
- package/dist/Pagination.js +74 -72
- package/dist/Pagination.js.map +1 -1
- package/dist/Pending.cjs +255 -287
- package/dist/Pending.cjs.map +1 -1
- package/dist/Pending.js +253 -285
- package/dist/Pending.js.map +1 -1
- package/dist/PersistentCollection.cjs +242 -285
- package/dist/PersistentCollection.cjs.map +1 -1
- package/dist/PersistentCollection.js +241 -284
- package/dist/PersistentCollection.js.map +1 -1
- package/dist/Resource.cjs +166 -174
- package/dist/Resource.cjs.map +1 -1
- package/dist/Resource.js +164 -172
- package/dist/Resource.js.map +1 -1
- package/dist/Selection.cjs +84 -94
- package/dist/Selection.cjs.map +1 -1
- package/dist/Selection.js +83 -93
- package/dist/Selection.js.map +1 -1
- package/dist/Service.cjs +54 -55
- package/dist/Service.cjs.map +1 -1
- package/dist/Service.js +53 -54
- package/dist/Service.js.map +1 -1
- package/dist/Sorting.cjs +102 -101
- package/dist/Sorting.cjs.map +1 -1
- package/dist/Sorting.js +102 -101
- package/dist/Sorting.js.map +1 -1
- package/dist/Trackable.cjs +112 -80
- package/dist/Trackable.cjs.map +1 -1
- package/dist/Trackable.js +111 -79
- package/dist/Trackable.js.map +1 -1
- package/dist/ViewModel.cjs +528 -576
- package/dist/ViewModel.cjs.map +1 -1
- package/dist/ViewModel.js +525 -573
- package/dist/ViewModel.js.map +1 -1
- package/dist/bindPublicMethods.cjs +43 -24
- package/dist/bindPublicMethods.cjs.map +1 -1
- package/dist/bindPublicMethods.js +43 -24
- package/dist/bindPublicMethods.js.map +1 -1
- package/dist/errors.cjs +67 -68
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +68 -71
- package/dist/errors.js.map +1 -1
- package/dist/mvc-kit.cjs +44 -46
- package/dist/mvc-kit.js +5 -32
- package/dist/produceDraft.cjs +105 -95
- package/dist/produceDraft.cjs.map +1 -1
- package/dist/produceDraft.js +106 -97
- package/dist/produceDraft.js.map +1 -1
- package/dist/react/components/CardList.cjs +30 -40
- package/dist/react/components/CardList.cjs.map +1 -1
- package/dist/react/components/CardList.js +31 -41
- package/dist/react/components/CardList.js.map +1 -1
- package/dist/react/components/DataTable.cjs +146 -169
- package/dist/react/components/DataTable.cjs.map +1 -1
- package/dist/react/components/DataTable.js +147 -170
- package/dist/react/components/DataTable.js.map +1 -1
- package/dist/react/components/InfiniteScroll.cjs +51 -42
- package/dist/react/components/InfiniteScroll.cjs.map +1 -1
- package/dist/react/components/InfiniteScroll.js +52 -43
- package/dist/react/components/InfiniteScroll.js.map +1 -1
- package/dist/react/components/types.cjs +10 -6
- package/dist/react/components/types.cjs.map +1 -1
- package/dist/react/components/types.js +11 -9
- package/dist/react/components/types.js.map +1 -1
- package/dist/react/guards.cjs +10 -6
- package/dist/react/guards.cjs.map +1 -1
- package/dist/react/guards.js +11 -9
- package/dist/react/guards.js.map +1 -1
- package/dist/react/provider.cjs +23 -20
- package/dist/react/provider.cjs.map +1 -1
- package/dist/react/provider.js +23 -21
- package/dist/react/provider.js.map +1 -1
- package/dist/react/use-event-bus.cjs +24 -20
- package/dist/react/use-event-bus.cjs.map +1 -1
- package/dist/react/use-event-bus.js +24 -21
- package/dist/react/use-event-bus.js.map +1 -1
- package/dist/react/use-instance.cjs +43 -36
- package/dist/react/use-instance.cjs.map +1 -1
- package/dist/react/use-instance.js +43 -36
- package/dist/react/use-instance.js.map +1 -1
- package/dist/react/use-local.cjs +48 -64
- package/dist/react/use-local.cjs.map +1 -1
- package/dist/react/use-local.js +47 -63
- package/dist/react/use-local.js.map +1 -1
- package/dist/react/use-model.cjs +84 -98
- package/dist/react/use-model.cjs.map +1 -1
- package/dist/react/use-model.js +84 -100
- package/dist/react/use-model.js.map +1 -1
- package/dist/react/use-singleton.cjs +19 -23
- package/dist/react/use-singleton.cjs.map +1 -1
- package/dist/react/use-singleton.js +16 -20
- package/dist/react/use-singleton.js.map +1 -1
- package/dist/react/use-subscribe-only.cjs +28 -22
- package/dist/react/use-subscribe-only.cjs.map +1 -1
- package/dist/react/use-subscribe-only.js +28 -22
- package/dist/react/use-subscribe-only.js.map +1 -1
- package/dist/react/use-teardown.cjs +20 -19
- package/dist/react/use-teardown.cjs.map +1 -1
- package/dist/react/use-teardown.js +20 -19
- package/dist/react/use-teardown.js.map +1 -1
- package/dist/react-native/NativeCollection.cjs +98 -78
- package/dist/react-native/NativeCollection.cjs.map +1 -1
- package/dist/react-native/NativeCollection.js +97 -77
- package/dist/react-native/NativeCollection.js.map +1 -1
- package/dist/react-native.cjs +2 -4
- package/dist/react-native.js +1 -4
- package/dist/react.cjs +24 -26
- package/dist/react.js +1 -17
- package/dist/singleton.cjs +28 -22
- package/dist/singleton.cjs.map +1 -1
- package/dist/singleton.js +29 -26
- package/dist/singleton.js.map +1 -1
- package/dist/walkPrototypeChain.cjs +20 -12
- package/dist/walkPrototypeChain.cjs.map +1 -1
- package/dist/walkPrototypeChain.js +21 -13
- package/dist/walkPrototypeChain.js.map +1 -1
- package/dist/web/IndexedDBCollection.cjs +53 -36
- package/dist/web/IndexedDBCollection.cjs.map +1 -1
- package/dist/web/IndexedDBCollection.js +52 -35
- package/dist/web/IndexedDBCollection.js.map +1 -1
- package/dist/web/WebStorageCollection.cjs +82 -84
- package/dist/web/WebStorageCollection.cjs.map +1 -1
- package/dist/web/WebStorageCollection.js +81 -83
- package/dist/web/WebStorageCollection.js.map +1 -1
- package/dist/web/idb.cjs +107 -99
- package/dist/web/idb.cjs.map +1 -1
- package/dist/web/idb.js +108 -105
- package/dist/web/idb.js.map +1 -1
- package/dist/web.cjs +4 -6
- package/dist/web.js +1 -5
- package/dist/wrapAsyncMethods.cjs +141 -168
- package/dist/wrapAsyncMethods.cjs.map +1 -1
- package/dist/wrapAsyncMethods.js +141 -168
- package/dist/wrapAsyncMethods.js.map +1 -1
- package/package.json +8 -8
- package/src/Pending.test.ts +1 -2
- package/src/Sorting.test.ts +1 -1
- package/src/produceDraft.test.ts +3 -3
- package/src/react/components/CardList.test.tsx +1 -1
- package/src/react/components/DataTable.test.tsx +1 -1
- package/src/react/components/InfiniteScroll.test.tsx +5 -5
- package/dist/mvc-kit.cjs.map +0 -1
- package/dist/mvc-kit.js.map +0 -1
- package/dist/react-native.cjs.map +0 -1
- package/dist/react-native.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/web.cjs.map +0 -1
- package/dist/web.js.map +0 -1
package/dist/Channel.cjs
CHANGED
|
@@ -1,302 +1,278 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
const require_bindPublicMethods = require("./bindPublicMethods.cjs");
|
|
2
|
+
//#region src/Channel.ts
|
|
3
|
+
var __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
|
|
4
|
+
var PROTECTED_KEYS = new Set([
|
|
5
|
+
"receive",
|
|
6
|
+
"disconnected",
|
|
7
|
+
"addCleanup",
|
|
8
|
+
"subscribeTo",
|
|
9
|
+
"listenTo"
|
|
10
|
+
]);
|
|
11
|
+
var ConnectionState = /* @__PURE__ */ function(ConnectionState) {
|
|
12
|
+
ConnectionState[ConnectionState["Idle"] = 0] = "Idle";
|
|
13
|
+
ConnectionState[ConnectionState["Connecting"] = 1] = "Connecting";
|
|
14
|
+
ConnectionState[ConnectionState["Connected"] = 2] = "Connected";
|
|
15
|
+
ConnectionState[ConnectionState["Reconnecting"] = 3] = "Reconnecting";
|
|
16
|
+
ConnectionState[ConnectionState["Disposed"] = 4] = "Disposed";
|
|
17
|
+
return ConnectionState;
|
|
18
|
+
}(ConnectionState || {});
|
|
19
|
+
var INITIAL_STATUS = Object.freeze({
|
|
20
|
+
connected: false,
|
|
21
|
+
reconnecting: false,
|
|
22
|
+
attempt: 0,
|
|
23
|
+
error: null
|
|
11
24
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
attempt: 0,
|
|
264
|
-
error: null
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
_onOpenFailed(attempt, error) {
|
|
268
|
-
if (this._disposed) return;
|
|
269
|
-
if (this._connState === 0) return;
|
|
270
|
-
this._connectAbort?.abort();
|
|
271
|
-
this._connectAbort = null;
|
|
272
|
-
this._connState = 3;
|
|
273
|
-
this._scheduleReconnect(attempt + 1, error);
|
|
274
|
-
}
|
|
275
|
-
_scheduleReconnect(attempt, error) {
|
|
276
|
-
const ctor = this.constructor;
|
|
277
|
-
if (attempt > ctor.MAX_ATTEMPTS) {
|
|
278
|
-
this._connState = 0;
|
|
279
|
-
this._setStatus({
|
|
280
|
-
connected: false,
|
|
281
|
-
reconnecting: false,
|
|
282
|
-
attempt,
|
|
283
|
-
error: error instanceof Error ? error.message : "Max reconnection attempts reached"
|
|
284
|
-
});
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
const errorMsg = error instanceof Error ? error.message : error ? String(error) : null;
|
|
288
|
-
this._setStatus({
|
|
289
|
-
connected: false,
|
|
290
|
-
reconnecting: true,
|
|
291
|
-
attempt,
|
|
292
|
-
error: errorMsg
|
|
293
|
-
});
|
|
294
|
-
const delay = this._calculateDelay(attempt - 1);
|
|
295
|
-
this._reconnectTimer = setTimeout(() => {
|
|
296
|
-
this._reconnectTimer = null;
|
|
297
|
-
this._attemptConnect(attempt);
|
|
298
|
-
}, delay);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
25
|
+
/**
|
|
26
|
+
* Abstract persistent connection with automatic reconnection and exponential backoff.
|
|
27
|
+
* Subclass to implement WebSocket, SSE, or other transport protocols.
|
|
28
|
+
*/
|
|
29
|
+
var Channel = class {
|
|
30
|
+
/** Base delay (ms) for reconnection backoff. */
|
|
31
|
+
static RECONNECT_BASE = 1e3;
|
|
32
|
+
/** Maximum delay cap (ms) for reconnection backoff. */
|
|
33
|
+
static RECONNECT_MAX = 3e4;
|
|
34
|
+
/** Exponential backoff multiplier for reconnection delay. */
|
|
35
|
+
static RECONNECT_FACTOR = 2;
|
|
36
|
+
/** Maximum number of reconnection attempts before giving up. */
|
|
37
|
+
static MAX_ATTEMPTS = Infinity;
|
|
38
|
+
_status = INITIAL_STATUS;
|
|
39
|
+
_connState = ConnectionState.Idle;
|
|
40
|
+
_disposed = false;
|
|
41
|
+
_initialized = false;
|
|
42
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
43
|
+
_handlers = /* @__PURE__ */ new Map();
|
|
44
|
+
_abortController = null;
|
|
45
|
+
_connectAbort = null;
|
|
46
|
+
_reconnectTimer = null;
|
|
47
|
+
_cleanups = null;
|
|
48
|
+
constructor() {
|
|
49
|
+
require_bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
|
|
50
|
+
}
|
|
51
|
+
/** Current connection status. */
|
|
52
|
+
get state() {
|
|
53
|
+
return this._status;
|
|
54
|
+
}
|
|
55
|
+
/** Subscribes to connection status changes. Returns an unsubscribe function. */
|
|
56
|
+
subscribe(listener) {
|
|
57
|
+
if (this._disposed) return () => {};
|
|
58
|
+
this._listeners.add(listener);
|
|
59
|
+
return () => {
|
|
60
|
+
this._listeners.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Whether this instance has been disposed. */
|
|
64
|
+
get disposed() {
|
|
65
|
+
return this._disposed;
|
|
66
|
+
}
|
|
67
|
+
/** Whether init() has been called. */
|
|
68
|
+
get initialized() {
|
|
69
|
+
return this._initialized;
|
|
70
|
+
}
|
|
71
|
+
/** AbortSignal that fires when this instance is disposed. Lazily created. */
|
|
72
|
+
get disposeSignal() {
|
|
73
|
+
if (!this._abortController) this._abortController = new AbortController();
|
|
74
|
+
return this._abortController.signal;
|
|
75
|
+
}
|
|
76
|
+
/** Initializes the instance. Called automatically by React hooks after mount. */
|
|
77
|
+
init() {
|
|
78
|
+
if (this._initialized || this._disposed) return;
|
|
79
|
+
this._initialized = true;
|
|
80
|
+
return this.onInit?.();
|
|
81
|
+
}
|
|
82
|
+
/** Tears down the instance, releasing all subscriptions and resources. */
|
|
83
|
+
dispose() {
|
|
84
|
+
if (this._disposed) return;
|
|
85
|
+
this._disposed = true;
|
|
86
|
+
this._connState = ConnectionState.Disposed;
|
|
87
|
+
if (this._reconnectTimer !== null) {
|
|
88
|
+
clearTimeout(this._reconnectTimer);
|
|
89
|
+
this._reconnectTimer = null;
|
|
90
|
+
}
|
|
91
|
+
this._connectAbort?.abort();
|
|
92
|
+
this._connectAbort = null;
|
|
93
|
+
this._abortController?.abort();
|
|
94
|
+
try {
|
|
95
|
+
this.close();
|
|
96
|
+
} catch {}
|
|
97
|
+
if (this._cleanups) {
|
|
98
|
+
for (const fn of this._cleanups) fn();
|
|
99
|
+
this._cleanups = null;
|
|
100
|
+
}
|
|
101
|
+
this.onDispose?.();
|
|
102
|
+
this._listeners.clear();
|
|
103
|
+
this._handlers.clear();
|
|
104
|
+
}
|
|
105
|
+
/** Initiates a connection with automatic reconnection on failure. */
|
|
106
|
+
connect() {
|
|
107
|
+
if (this._disposed) {
|
|
108
|
+
if (__DEV__) console.warn("[mvc-kit] connect() called after dispose — ignored.");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (__DEV__ && !this._initialized) console.warn("[mvc-kit] connect() called before init().");
|
|
112
|
+
if (this._connState === ConnectionState.Connecting || this._connState === ConnectionState.Connected) return;
|
|
113
|
+
if (this._reconnectTimer !== null) {
|
|
114
|
+
clearTimeout(this._reconnectTimer);
|
|
115
|
+
this._reconnectTimer = null;
|
|
116
|
+
}
|
|
117
|
+
this._attemptConnect(0);
|
|
118
|
+
}
|
|
119
|
+
/** Closes the connection and cancels any pending reconnection. */
|
|
120
|
+
disconnect() {
|
|
121
|
+
if (this._disposed) return;
|
|
122
|
+
if (this._reconnectTimer !== null) {
|
|
123
|
+
clearTimeout(this._reconnectTimer);
|
|
124
|
+
this._reconnectTimer = null;
|
|
125
|
+
}
|
|
126
|
+
this._connectAbort?.abort();
|
|
127
|
+
this._connectAbort = null;
|
|
128
|
+
if (this._connState === ConnectionState.Connected || this._connState === ConnectionState.Connecting) {
|
|
129
|
+
this._connState = ConnectionState.Idle;
|
|
130
|
+
try {
|
|
131
|
+
this.close();
|
|
132
|
+
} catch {}
|
|
133
|
+
} else this._connState = ConnectionState.Idle;
|
|
134
|
+
this._setStatus({
|
|
135
|
+
connected: false,
|
|
136
|
+
reconnecting: false,
|
|
137
|
+
attempt: 0,
|
|
138
|
+
error: null
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** Call from subclass when a message arrives from the transport. @protected */
|
|
142
|
+
receive(type, payload) {
|
|
143
|
+
if (this._disposed) {
|
|
144
|
+
if (__DEV__) console.warn(`[mvc-kit] receive("${String(type)}") called after dispose — ignored.`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const handlers = this._handlers.get(type);
|
|
148
|
+
if (handlers) for (const handler of handlers) handler(payload);
|
|
149
|
+
}
|
|
150
|
+
/** Call from subclass when the transport connection drops unexpectedly. Triggers reconnection. @protected */
|
|
151
|
+
disconnected() {
|
|
152
|
+
if (this._disposed) return;
|
|
153
|
+
if (this._connState !== ConnectionState.Connected && this._connState !== ConnectionState.Connecting) return;
|
|
154
|
+
this._connectAbort?.abort();
|
|
155
|
+
this._connectAbort = null;
|
|
156
|
+
this._connState = ConnectionState.Reconnecting;
|
|
157
|
+
this._scheduleReconnect(1);
|
|
158
|
+
}
|
|
159
|
+
/** Subscribes to a specific message type. Returns an unsubscribe function. */
|
|
160
|
+
on(type, handler) {
|
|
161
|
+
if (this._disposed) return () => {};
|
|
162
|
+
let handlers = this._handlers.get(type);
|
|
163
|
+
if (!handlers) {
|
|
164
|
+
handlers = /* @__PURE__ */ new Set();
|
|
165
|
+
this._handlers.set(type, handlers);
|
|
166
|
+
}
|
|
167
|
+
handlers.add(handler);
|
|
168
|
+
return () => {
|
|
169
|
+
handlers.delete(handler);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** Subscribes to a message type, auto-removing the handler after the first invocation. */
|
|
173
|
+
once(type, handler) {
|
|
174
|
+
const unsubscribe = this.on(type, (payload) => {
|
|
175
|
+
unsubscribe();
|
|
176
|
+
handler(payload);
|
|
177
|
+
});
|
|
178
|
+
return unsubscribe;
|
|
179
|
+
}
|
|
180
|
+
/** Registers a cleanup function to be called on dispose. @protected */
|
|
181
|
+
addCleanup(fn) {
|
|
182
|
+
if (!this._cleanups) this._cleanups = [];
|
|
183
|
+
this._cleanups.push(fn);
|
|
184
|
+
}
|
|
185
|
+
/** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */
|
|
186
|
+
subscribeTo(source, listener) {
|
|
187
|
+
const unsubscribe = source.subscribe(listener);
|
|
188
|
+
this.addCleanup(unsubscribe);
|
|
189
|
+
return unsubscribe;
|
|
190
|
+
}
|
|
191
|
+
/** Subscribes to a typed event on a Channel or EventBus with automatic cleanup on dispose. @protected */
|
|
192
|
+
listenTo(source, event, handler) {
|
|
193
|
+
const unsubscribe = source.on(event, handler);
|
|
194
|
+
this.addCleanup(unsubscribe);
|
|
195
|
+
return unsubscribe;
|
|
196
|
+
}
|
|
197
|
+
/** Computes the reconnect backoff delay with jitter for the given attempt number. @protected */
|
|
198
|
+
_calculateDelay(attempt) {
|
|
199
|
+
const ctor = this.constructor;
|
|
200
|
+
const capped = Math.min(ctor.RECONNECT_BASE * Math.pow(ctor.RECONNECT_FACTOR, attempt), ctor.RECONNECT_MAX);
|
|
201
|
+
return Math.random() * capped;
|
|
202
|
+
}
|
|
203
|
+
_setStatus(next) {
|
|
204
|
+
const prev = this._status;
|
|
205
|
+
if (prev.connected === next.connected && prev.reconnecting === next.reconnecting && prev.attempt === next.attempt && prev.error === next.error) return;
|
|
206
|
+
this._status = Object.freeze(next);
|
|
207
|
+
for (const listener of this._listeners) listener(this._status, prev);
|
|
208
|
+
}
|
|
209
|
+
_attemptConnect(attempt) {
|
|
210
|
+
if (this._disposed) return;
|
|
211
|
+
this._connState = ConnectionState.Connecting;
|
|
212
|
+
this._connectAbort?.abort();
|
|
213
|
+
this._connectAbort = new AbortController();
|
|
214
|
+
const signal = this._abortController ? AbortSignal.any([this._abortController.signal, this._connectAbort.signal]) : this._connectAbort.signal;
|
|
215
|
+
this._setStatus({
|
|
216
|
+
connected: false,
|
|
217
|
+
reconnecting: attempt > 0,
|
|
218
|
+
attempt,
|
|
219
|
+
error: null
|
|
220
|
+
});
|
|
221
|
+
let result;
|
|
222
|
+
try {
|
|
223
|
+
result = this.open(signal);
|
|
224
|
+
} catch (e) {
|
|
225
|
+
this._onOpenFailed(attempt, e);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (result && typeof result.then === "function") result.then(() => this._onOpenSucceeded(), (e) => this._onOpenFailed(attempt, e));
|
|
229
|
+
else this._onOpenSucceeded();
|
|
230
|
+
}
|
|
231
|
+
_onOpenSucceeded() {
|
|
232
|
+
if (this._disposed) return;
|
|
233
|
+
if (this._connState !== ConnectionState.Connecting) return;
|
|
234
|
+
this._connState = ConnectionState.Connected;
|
|
235
|
+
this._setStatus({
|
|
236
|
+
connected: true,
|
|
237
|
+
reconnecting: false,
|
|
238
|
+
attempt: 0,
|
|
239
|
+
error: null
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
_onOpenFailed(attempt, error) {
|
|
243
|
+
if (this._disposed) return;
|
|
244
|
+
if (this._connState === ConnectionState.Idle) return;
|
|
245
|
+
this._connectAbort?.abort();
|
|
246
|
+
this._connectAbort = null;
|
|
247
|
+
this._connState = ConnectionState.Reconnecting;
|
|
248
|
+
this._scheduleReconnect(attempt + 1, error);
|
|
249
|
+
}
|
|
250
|
+
_scheduleReconnect(attempt, error) {
|
|
251
|
+
if (attempt > this.constructor.MAX_ATTEMPTS) {
|
|
252
|
+
this._connState = ConnectionState.Idle;
|
|
253
|
+
this._setStatus({
|
|
254
|
+
connected: false,
|
|
255
|
+
reconnecting: false,
|
|
256
|
+
attempt,
|
|
257
|
+
error: error instanceof Error ? error.message : "Max reconnection attempts reached"
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const errorMsg = error instanceof Error ? error.message : error ? String(error) : null;
|
|
262
|
+
this._setStatus({
|
|
263
|
+
connected: false,
|
|
264
|
+
reconnecting: true,
|
|
265
|
+
attempt,
|
|
266
|
+
error: errorMsg
|
|
267
|
+
});
|
|
268
|
+
const delay = this._calculateDelay(attempt - 1);
|
|
269
|
+
this._reconnectTimer = setTimeout(() => {
|
|
270
|
+
this._reconnectTimer = null;
|
|
271
|
+
this._attemptConnect(attempt);
|
|
272
|
+
}, delay);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
//#endregion
|
|
301
276
|
exports.Channel = Channel;
|
|
302
|
-
|
|
277
|
+
|
|
278
|
+
//# sourceMappingURL=Channel.cjs.map
|