mvc-kit 2.2.3 → 2.3.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/README.md +7 -1
- package/agent-config/claude-code/skills/guide/api-reference.md +6 -0
- package/agent-config/copilot/copilot-instructions.md +9 -0
- package/agent-config/cursor/cursorrules +9 -0
- package/dist/Collection.d.ts +22 -0
- package/dist/Collection.d.ts.map +1 -1
- package/dist/mvc-kit.cjs +1 -1
- package/dist/mvc-kit.cjs.map +1 -1
- package/dist/mvc-kit.js +407 -295
- package/dist/mvc-kit.js.map +1 -1
- package/package.json +1 -1
package/dist/mvc-kit.js
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
1
|
import { E as w } from "./singleton-CaEXSbYg.js";
|
|
2
|
-
import { h as N, s as
|
|
3
|
-
class
|
|
4
|
-
constructor(t,
|
|
5
|
-
super(
|
|
2
|
+
import { h as N, s as $, t as L, a as B } from "./singleton-CaEXSbYg.js";
|
|
3
|
+
class C extends Error {
|
|
4
|
+
constructor(t, s) {
|
|
5
|
+
super(s ?? `HTTP ${t}`), this.status = t, this.name = "HttpError";
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
function
|
|
9
|
-
return
|
|
8
|
+
function y(r) {
|
|
9
|
+
return r instanceof Error && r.name === "AbortError";
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
return
|
|
11
|
+
function S(r) {
|
|
12
|
+
return r === 401 ? "unauthorized" : r === 403 ? "forbidden" : r === 404 ? "not_found" : r === 422 ? "validation" : r === 429 ? "rate_limited" : r >= 500 ? "server_error" : "unknown";
|
|
13
13
|
}
|
|
14
|
-
function T(
|
|
15
|
-
return typeof
|
|
14
|
+
function T(r) {
|
|
15
|
+
return typeof r == "object" && r !== null && typeof r.status == "number" && typeof r.statusText == "string" && !(r instanceof Error);
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
return
|
|
17
|
+
function E(r) {
|
|
18
|
+
return r instanceof Error && r.name === "AbortError" ? {
|
|
19
19
|
code: "abort",
|
|
20
20
|
message: "Request was aborted",
|
|
21
|
-
original:
|
|
22
|
-
} :
|
|
23
|
-
code:
|
|
24
|
-
message:
|
|
25
|
-
status:
|
|
26
|
-
original:
|
|
27
|
-
} : T(
|
|
28
|
-
code:
|
|
29
|
-
message:
|
|
30
|
-
status:
|
|
31
|
-
original:
|
|
32
|
-
} :
|
|
21
|
+
original: r
|
|
22
|
+
} : r instanceof C ? {
|
|
23
|
+
code: S(r.status),
|
|
24
|
+
message: r.message,
|
|
25
|
+
status: r.status,
|
|
26
|
+
original: r
|
|
27
|
+
} : T(r) ? {
|
|
28
|
+
code: S(r.status),
|
|
29
|
+
message: r.statusText || `HTTP ${r.status}`,
|
|
30
|
+
status: r.status,
|
|
31
|
+
original: r
|
|
32
|
+
} : r instanceof TypeError && r.message.toLowerCase().includes("fetch") ? {
|
|
33
33
|
code: "network",
|
|
34
|
-
message:
|
|
35
|
-
original:
|
|
36
|
-
} :
|
|
34
|
+
message: r.message,
|
|
35
|
+
original: r
|
|
36
|
+
} : r instanceof Error && r.name === "TimeoutError" ? {
|
|
37
37
|
code: "timeout",
|
|
38
|
-
message:
|
|
39
|
-
original:
|
|
40
|
-
} :
|
|
38
|
+
message: r.message,
|
|
39
|
+
original: r
|
|
40
|
+
} : r instanceof Error ? {
|
|
41
41
|
code: "unknown",
|
|
42
|
-
message:
|
|
43
|
-
original:
|
|
42
|
+
message: r.message,
|
|
43
|
+
original: r
|
|
44
44
|
} : {
|
|
45
45
|
code: "unknown",
|
|
46
|
-
message: String(
|
|
47
|
-
original:
|
|
46
|
+
message: String(r),
|
|
47
|
+
original: r
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
function
|
|
52
|
-
return
|
|
50
|
+
const d = typeof __MVC_KIT_DEV__ < "u" && __MVC_KIT_DEV__;
|
|
51
|
+
function O(r) {
|
|
52
|
+
return r !== null && typeof r == "object" && typeof r.subscribe == "function";
|
|
53
53
|
}
|
|
54
|
-
function
|
|
55
|
-
let
|
|
56
|
-
for (;
|
|
57
|
-
const i = Object.getOwnPropertyDescriptors(
|
|
58
|
-
for (const [
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
function b(r, t, s) {
|
|
55
|
+
let e = Object.getPrototypeOf(r);
|
|
56
|
+
for (; e && e !== t; ) {
|
|
57
|
+
const i = Object.getOwnPropertyDescriptors(e);
|
|
58
|
+
for (const [n, a] of Object.entries(i))
|
|
59
|
+
n !== "constructor" && s(n, a, e);
|
|
60
|
+
e = Object.getPrototypeOf(e);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
const
|
|
64
|
-
class
|
|
63
|
+
const z = Object.freeze({ loading: !1, error: null, errorCode: null }), v = ["async", "subscribeAsync"], A = /* @__PURE__ */ new Set(["onInit", "onSet", "onDispose"]);
|
|
64
|
+
class m {
|
|
65
65
|
_state;
|
|
66
66
|
_initialState;
|
|
67
67
|
_disposed = !1;
|
|
@@ -124,29 +124,29 @@ class b {
|
|
|
124
124
|
*/
|
|
125
125
|
set(t) {
|
|
126
126
|
if (this._disposed) return;
|
|
127
|
-
if (
|
|
127
|
+
if (d && this._stateTracking) {
|
|
128
128
|
console.error(
|
|
129
129
|
"[mvc-kit] set() called inside a getter. Getters must be pure — they read state and return a value. They must never call set(), which would cause an infinite render loop. Move this logic to an action method."
|
|
130
130
|
);
|
|
131
131
|
return;
|
|
132
132
|
}
|
|
133
|
-
const
|
|
134
|
-
if (!Object.keys(
|
|
135
|
-
(
|
|
133
|
+
const s = typeof t == "function" ? t(this._state) : t;
|
|
134
|
+
if (!Object.keys(s).some(
|
|
135
|
+
(o) => s[o] !== this._state[o]
|
|
136
136
|
))
|
|
137
137
|
return;
|
|
138
|
-
const
|
|
139
|
-
this._state =
|
|
140
|
-
for (const
|
|
141
|
-
a
|
|
138
|
+
const n = this._state, a = Object.freeze({ ...n, ...s });
|
|
139
|
+
this._state = a, this._revision++, this.onSet?.(n, a);
|
|
140
|
+
for (const o of this._listeners)
|
|
141
|
+
o(a, n);
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
144
|
* Emits a typed event via the internal EventBus.
|
|
145
145
|
* Safe to call during dispose cleanup callbacks.
|
|
146
146
|
* @protected
|
|
147
147
|
*/
|
|
148
|
-
emit(t,
|
|
149
|
-
(this._eventBus?.disposed ?? this._disposed) || this.events.emit(t,
|
|
148
|
+
emit(t, s) {
|
|
149
|
+
(this._eventBus?.disposed ?? this._disposed) || this.events.emit(t, s);
|
|
150
150
|
}
|
|
151
151
|
/** Subscribes to state changes. Returns an unsubscribe function. */
|
|
152
152
|
subscribe(t) {
|
|
@@ -172,8 +172,8 @@ class b {
|
|
|
172
172
|
reset(t) {
|
|
173
173
|
if (!this._disposed) {
|
|
174
174
|
this._abortController?.abort(), this._abortController = null, this._teardownSubscriptions(), this._state = t ? Object.freeze({ ...t }) : this._initialState, this._revision++, this._asyncStates.clear(), this._asyncSnapshots.clear(), this._notifyAsync(), this._trackSubscribables();
|
|
175
|
-
for (const
|
|
176
|
-
|
|
175
|
+
for (const s of this._listeners)
|
|
176
|
+
s(this._state, this._state);
|
|
177
177
|
return this.onInit?.();
|
|
178
178
|
}
|
|
179
179
|
}
|
|
@@ -187,9 +187,9 @@ class b {
|
|
|
187
187
|
this._cleanups || (this._cleanups = []), this._cleanups.push(t);
|
|
188
188
|
}
|
|
189
189
|
/** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */
|
|
190
|
-
subscribeTo(t,
|
|
191
|
-
const
|
|
192
|
-
return this._subscriptionCleanups || (this._subscriptionCleanups = []), this._subscriptionCleanups.push(
|
|
190
|
+
subscribeTo(t, s) {
|
|
191
|
+
const e = t.subscribe(s);
|
|
192
|
+
return this._subscriptionCleanups || (this._subscriptionCleanups = []), this._subscriptionCleanups.push(e), e;
|
|
193
193
|
}
|
|
194
194
|
// ── Async tracking API ──────────────────────────────────────────
|
|
195
195
|
/** Proxy providing `TaskState` (loading, error, errorCode) per async method. */
|
|
@@ -197,18 +197,18 @@ class b {
|
|
|
197
197
|
if (!this._asyncProxy) {
|
|
198
198
|
const t = this;
|
|
199
199
|
this._asyncProxy = new Proxy({}, {
|
|
200
|
-
get(
|
|
201
|
-
return t._asyncSnapshots.get(
|
|
200
|
+
get(s, e) {
|
|
201
|
+
return t._asyncSnapshots.get(e) ?? z;
|
|
202
202
|
},
|
|
203
|
-
has(
|
|
204
|
-
return t._asyncSnapshots.has(
|
|
203
|
+
has(s, e) {
|
|
204
|
+
return t._asyncSnapshots.has(e);
|
|
205
205
|
},
|
|
206
206
|
ownKeys() {
|
|
207
207
|
return Array.from(t._asyncSnapshots.keys());
|
|
208
208
|
},
|
|
209
|
-
getOwnPropertyDescriptor(
|
|
210
|
-
if (t._asyncSnapshots.has(
|
|
211
|
-
return { configurable: !0, enumerable: !0, value: t._asyncSnapshots.get(
|
|
209
|
+
getOwnPropertyDescriptor(s, e) {
|
|
210
|
+
if (t._asyncSnapshots.has(e))
|
|
211
|
+
return { configurable: !0, enumerable: !0, value: t._asyncSnapshots.get(e) };
|
|
212
212
|
}
|
|
213
213
|
});
|
|
214
214
|
}
|
|
@@ -234,95 +234,95 @@ class b {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
_guardReservedKeys() {
|
|
237
|
-
|
|
238
|
-
if (
|
|
237
|
+
b(this, m.prototype, (t) => {
|
|
238
|
+
if (v.includes(t))
|
|
239
239
|
throw new Error(
|
|
240
240
|
`[mvc-kit] "${t}" is a reserved property on ViewModel and cannot be overridden.`
|
|
241
241
|
);
|
|
242
242
|
});
|
|
243
243
|
}
|
|
244
244
|
_wrapMethods() {
|
|
245
|
-
for (const i of
|
|
245
|
+
for (const i of v)
|
|
246
246
|
if (Object.getOwnPropertyDescriptor(this, i)?.value !== void 0)
|
|
247
247
|
throw new Error(
|
|
248
248
|
`[mvc-kit] "${i}" is a reserved property on ViewModel and cannot be overridden.`
|
|
249
249
|
);
|
|
250
|
-
const t = this,
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
let
|
|
256
|
-
const l = function(...
|
|
250
|
+
const t = this, s = /* @__PURE__ */ new Set(), e = [];
|
|
251
|
+
d && (this._activeOps = /* @__PURE__ */ new Map()), b(this, m.prototype, (i, n) => {
|
|
252
|
+
if (n.get || n.set || typeof n.value != "function" || i.startsWith("_") || A.has(i) || s.has(i)) return;
|
|
253
|
+
s.add(i);
|
|
254
|
+
const a = n.value;
|
|
255
|
+
let o = !1;
|
|
256
|
+
const l = function(...h) {
|
|
257
257
|
if (t._disposed) {
|
|
258
|
-
|
|
258
|
+
d && console.warn(`[mvc-kit] "${i}" called after dispose — ignored.`);
|
|
259
259
|
return;
|
|
260
260
|
}
|
|
261
|
-
|
|
261
|
+
d && !t._initialized && console.warn(
|
|
262
262
|
`[mvc-kit] "${i}" called before init(). Async tracking is active only after init().`
|
|
263
263
|
);
|
|
264
|
-
let
|
|
264
|
+
let u;
|
|
265
265
|
try {
|
|
266
|
-
|
|
267
|
-
} catch (
|
|
268
|
-
throw
|
|
266
|
+
u = a.apply(t, h);
|
|
267
|
+
} catch (_) {
|
|
268
|
+
throw _;
|
|
269
269
|
}
|
|
270
|
-
if (!
|
|
271
|
-
return
|
|
272
|
-
let
|
|
273
|
-
return
|
|
274
|
-
(
|
|
275
|
-
if (t._disposed) return
|
|
276
|
-
if (
|
|
270
|
+
if (!u || typeof u.then != "function")
|
|
271
|
+
return o || (o = !0, t._asyncStates.delete(i), t._asyncSnapshots.delete(i), t[i] = a.bind(t)), u;
|
|
272
|
+
let c = t._asyncStates.get(i);
|
|
273
|
+
return c || (c = { loading: !1, error: null, errorCode: null, count: 0 }, t._asyncStates.set(i, c)), c.count++, c.loading = !0, c.error = null, c.errorCode = null, t._asyncSnapshots.set(i, Object.freeze({ loading: !0, error: null, errorCode: null })), t._notifyAsync(), d && t._activeOps && t._activeOps.set(i, (t._activeOps.get(i) ?? 0) + 1), u.then(
|
|
274
|
+
(_) => {
|
|
275
|
+
if (t._disposed) return _;
|
|
276
|
+
if (c.count--, c.loading = c.count > 0, t._asyncSnapshots.set(
|
|
277
277
|
i,
|
|
278
|
-
Object.freeze({ loading:
|
|
279
|
-
), t._notifyAsync(),
|
|
280
|
-
const
|
|
281
|
-
|
|
278
|
+
Object.freeze({ loading: c.loading, error: c.error, errorCode: c.errorCode })
|
|
279
|
+
), t._notifyAsync(), d && t._activeOps) {
|
|
280
|
+
const f = (t._activeOps.get(i) ?? 1) - 1;
|
|
281
|
+
f <= 0 ? t._activeOps.delete(i) : t._activeOps.set(i, f);
|
|
282
282
|
}
|
|
283
|
-
return
|
|
283
|
+
return _;
|
|
284
284
|
},
|
|
285
|
-
(
|
|
286
|
-
if (
|
|
287
|
-
if (t._disposed || (
|
|
285
|
+
(_) => {
|
|
286
|
+
if (y(_)) {
|
|
287
|
+
if (t._disposed || (c.count--, c.loading = c.count > 0, t._asyncSnapshots.set(
|
|
288
288
|
i,
|
|
289
|
-
Object.freeze({ loading:
|
|
290
|
-
), t._notifyAsync()),
|
|
289
|
+
Object.freeze({ loading: c.loading, error: c.error, errorCode: c.errorCode })
|
|
290
|
+
), t._notifyAsync()), d && t._activeOps) {
|
|
291
291
|
const p = (t._activeOps.get(i) ?? 1) - 1;
|
|
292
292
|
p <= 0 ? t._activeOps.delete(i) : t._activeOps.set(i, p);
|
|
293
293
|
}
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
296
296
|
if (t._disposed) return;
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
297
|
+
c.count--, c.loading = c.count > 0;
|
|
298
|
+
const f = E(_);
|
|
299
|
+
if (c.error = f.message, c.errorCode = f.code, t._asyncSnapshots.set(
|
|
300
300
|
i,
|
|
301
|
-
Object.freeze({ loading:
|
|
302
|
-
), t._notifyAsync(),
|
|
301
|
+
Object.freeze({ loading: c.loading, error: f.message, errorCode: f.code })
|
|
302
|
+
), t._notifyAsync(), d && t._activeOps) {
|
|
303
303
|
const p = (t._activeOps.get(i) ?? 1) - 1;
|
|
304
304
|
p <= 0 ? t._activeOps.delete(i) : t._activeOps.set(i, p);
|
|
305
305
|
}
|
|
306
|
-
throw
|
|
306
|
+
throw _;
|
|
307
307
|
}
|
|
308
308
|
);
|
|
309
309
|
};
|
|
310
|
-
|
|
311
|
-
}),
|
|
312
|
-
const i =
|
|
313
|
-
for (const
|
|
314
|
-
|
|
315
|
-
console.warn(`[mvc-kit] "${
|
|
316
|
-
} : t[
|
|
310
|
+
e.push(i), t[i] = l;
|
|
311
|
+
}), e.length > 0 && this.addCleanup(() => {
|
|
312
|
+
const i = d && t._activeOps ? new Map(t._activeOps) : null;
|
|
313
|
+
for (const n of e)
|
|
314
|
+
d ? t[n] = () => {
|
|
315
|
+
console.warn(`[mvc-kit] "${n}" called after dispose — ignored.`);
|
|
316
|
+
} : t[n] = () => {
|
|
317
317
|
};
|
|
318
|
-
t._asyncListeners.clear(), t._asyncStates.clear(), t._asyncSnapshots.clear(),
|
|
318
|
+
t._asyncListeners.clear(), t._asyncStates.clear(), t._asyncSnapshots.clear(), d && i && i.size > 0 && t._scheduleGhostCheck(i);
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
321
|
_scheduleGhostCheck(t) {
|
|
322
|
-
|
|
323
|
-
for (const [
|
|
322
|
+
d && setTimeout(() => {
|
|
323
|
+
for (const [s, e] of t)
|
|
324
324
|
console.warn(
|
|
325
|
-
`[mvc-kit] Ghost async operation detected: "${
|
|
325
|
+
`[mvc-kit] Ghost async operation detected: "${s}" had ${e} pending call(s) when the ViewModel was disposed. Consider using disposeSignal to cancel in-flight work.`
|
|
326
326
|
);
|
|
327
327
|
}, this.constructor.GHOST_TIMEOUT);
|
|
328
328
|
}
|
|
@@ -339,13 +339,13 @@ class b {
|
|
|
339
339
|
*/
|
|
340
340
|
_installStateProxy() {
|
|
341
341
|
const t = new Proxy({}, {
|
|
342
|
-
get: (
|
|
342
|
+
get: (s, e) => (this._stateTracking?.add(e), this._state[e]),
|
|
343
343
|
ownKeys: () => Reflect.ownKeys(this._state),
|
|
344
|
-
getOwnPropertyDescriptor: (
|
|
344
|
+
getOwnPropertyDescriptor: (s, e) => Reflect.getOwnPropertyDescriptor(this._state, e),
|
|
345
345
|
set: () => {
|
|
346
346
|
throw new Error("Cannot mutate state directly. Use set() instead.");
|
|
347
347
|
},
|
|
348
|
-
has: (
|
|
348
|
+
has: (s, e) => e in this._state
|
|
349
349
|
});
|
|
350
350
|
Object.defineProperty(this, "state", {
|
|
351
351
|
get: () => this._stateTracking ? t : this._state,
|
|
@@ -370,21 +370,21 @@ class b {
|
|
|
370
370
|
*/
|
|
371
371
|
_trackSubscribables() {
|
|
372
372
|
for (const t of Object.getOwnPropertyNames(this)) {
|
|
373
|
-
const
|
|
374
|
-
if (!
|
|
375
|
-
const
|
|
376
|
-
source:
|
|
373
|
+
const s = this[t];
|
|
374
|
+
if (!O(s)) continue;
|
|
375
|
+
const e = {
|
|
376
|
+
source: s,
|
|
377
377
|
revision: 0,
|
|
378
|
-
unsubscribe:
|
|
378
|
+
unsubscribe: s.subscribe(() => {
|
|
379
379
|
if (!this._disposed) {
|
|
380
|
-
|
|
380
|
+
e.revision++, this._revision++, this._state = Object.freeze({ ...this._state });
|
|
381
381
|
for (const i of this._listeners)
|
|
382
382
|
i(this._state, this._state);
|
|
383
383
|
}
|
|
384
384
|
})
|
|
385
385
|
};
|
|
386
|
-
this._trackedSources.set(t,
|
|
387
|
-
get: () => (this._sourceTracking?.set(t,
|
|
386
|
+
this._trackedSources.set(t, e), Object.defineProperty(this, t, {
|
|
387
|
+
get: () => (this._sourceTracking?.set(t, e), s),
|
|
388
388
|
configurable: !0,
|
|
389
389
|
enumerable: !1
|
|
390
390
|
});
|
|
@@ -400,8 +400,8 @@ class b {
|
|
|
400
400
|
*/
|
|
401
401
|
_memoizeGetters() {
|
|
402
402
|
const t = /* @__PURE__ */ new Set();
|
|
403
|
-
|
|
404
|
-
!
|
|
403
|
+
b(this, m.prototype, (s, e) => {
|
|
404
|
+
!e.get || t.has(s) || (t.add(s), this._wrapGetter(s, e.get));
|
|
405
405
|
});
|
|
406
406
|
}
|
|
407
407
|
/**
|
|
@@ -414,58 +414,58 @@ class b {
|
|
|
414
414
|
* Tier 2 (medium): revision changed but this getter's deps didn't → return cached
|
|
415
415
|
* Tier 3 (slow): at least one dep changed → full recompute with tracking
|
|
416
416
|
*/
|
|
417
|
-
_wrapGetter(t,
|
|
418
|
-
let
|
|
417
|
+
_wrapGetter(t, s) {
|
|
418
|
+
let e, i = -1, n, a, o;
|
|
419
419
|
Object.defineProperty(this, t, {
|
|
420
420
|
get: () => {
|
|
421
421
|
if (this._disposed || i === this._revision)
|
|
422
|
-
return
|
|
423
|
-
if (
|
|
424
|
-
let
|
|
425
|
-
for (const [
|
|
426
|
-
if (this._state[
|
|
427
|
-
|
|
422
|
+
return e;
|
|
423
|
+
if (n && a) {
|
|
424
|
+
let c = !0;
|
|
425
|
+
for (const [_, f] of a)
|
|
426
|
+
if (this._state[_] !== f) {
|
|
427
|
+
c = !1;
|
|
428
428
|
break;
|
|
429
429
|
}
|
|
430
|
-
if (
|
|
431
|
-
for (const [
|
|
432
|
-
const p = this._trackedSources.get(
|
|
433
|
-
if (p && p.revision !==
|
|
434
|
-
|
|
430
|
+
if (c && o)
|
|
431
|
+
for (const [_, f] of o) {
|
|
432
|
+
const p = this._trackedSources.get(_);
|
|
433
|
+
if (p && p.revision !== f) {
|
|
434
|
+
c = !1;
|
|
435
435
|
break;
|
|
436
436
|
}
|
|
437
437
|
}
|
|
438
|
-
if (
|
|
439
|
-
return i = this._revision,
|
|
438
|
+
if (c)
|
|
439
|
+
return i = this._revision, e;
|
|
440
440
|
}
|
|
441
|
-
const l = this._stateTracking,
|
|
441
|
+
const l = this._stateTracking, h = this._sourceTracking;
|
|
442
442
|
this._stateTracking = /* @__PURE__ */ new Set(), this._sourceTracking = /* @__PURE__ */ new Map();
|
|
443
443
|
try {
|
|
444
|
-
|
|
445
|
-
} catch (
|
|
446
|
-
throw this._stateTracking = l, this._sourceTracking =
|
|
444
|
+
e = s.call(this);
|
|
445
|
+
} catch (c) {
|
|
446
|
+
throw this._stateTracking = l, this._sourceTracking = h, c;
|
|
447
447
|
}
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
if (this._stateTracking = l, this._sourceTracking =
|
|
451
|
-
for (const
|
|
452
|
-
if (
|
|
453
|
-
for (const [
|
|
454
|
-
|
|
455
|
-
c = /* @__PURE__ */ new Map();
|
|
456
|
-
for (const o of r)
|
|
457
|
-
c.set(o, this._state[o]);
|
|
448
|
+
n = this._stateTracking;
|
|
449
|
+
const u = this._sourceTracking;
|
|
450
|
+
if (this._stateTracking = l, this._sourceTracking = h, l)
|
|
451
|
+
for (const c of n) l.add(c);
|
|
452
|
+
if (h)
|
|
453
|
+
for (const [c, _] of u)
|
|
454
|
+
h.set(c, _);
|
|
458
455
|
a = /* @__PURE__ */ new Map();
|
|
459
|
-
for (const
|
|
460
|
-
a.set(
|
|
461
|
-
|
|
456
|
+
for (const c of n)
|
|
457
|
+
a.set(c, this._state[c]);
|
|
458
|
+
o = /* @__PURE__ */ new Map();
|
|
459
|
+
for (const [c, _] of u)
|
|
460
|
+
o.set(c, _.revision);
|
|
461
|
+
return i = this._revision, e;
|
|
462
462
|
},
|
|
463
463
|
configurable: !0,
|
|
464
464
|
enumerable: !0
|
|
465
465
|
});
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
|
-
class
|
|
468
|
+
class j {
|
|
469
469
|
_state;
|
|
470
470
|
_committed;
|
|
471
471
|
_disposed = !1;
|
|
@@ -474,8 +474,8 @@ class M {
|
|
|
474
474
|
_abortController = null;
|
|
475
475
|
_cleanups = null;
|
|
476
476
|
constructor(t) {
|
|
477
|
-
const
|
|
478
|
-
this._state =
|
|
477
|
+
const s = Object.freeze({ ...t });
|
|
478
|
+
this._state = s, this._committed = s;
|
|
479
479
|
}
|
|
480
480
|
/** Current frozen state object. */
|
|
481
481
|
get state() {
|
|
@@ -529,15 +529,15 @@ class M {
|
|
|
529
529
|
set(t) {
|
|
530
530
|
if (this._disposed)
|
|
531
531
|
throw new Error("Cannot set state on disposed Model");
|
|
532
|
-
const
|
|
533
|
-
if (!Object.keys(
|
|
534
|
-
(
|
|
532
|
+
const s = typeof t == "function" ? t(this._state) : t;
|
|
533
|
+
if (!Object.keys(s).some(
|
|
534
|
+
(o) => s[o] !== this._state[o]
|
|
535
535
|
))
|
|
536
536
|
return;
|
|
537
|
-
const
|
|
538
|
-
this._state =
|
|
539
|
-
for (const
|
|
540
|
-
a
|
|
537
|
+
const n = this._state, a = Object.freeze({ ...n, ...s });
|
|
538
|
+
this._state = a, this.onSet?.(n, a);
|
|
539
|
+
for (const o of this._listeners)
|
|
540
|
+
o(a, n);
|
|
541
541
|
}
|
|
542
542
|
/**
|
|
543
543
|
* Mark current state as the new baseline (not dirty).
|
|
@@ -557,8 +557,8 @@ class M {
|
|
|
557
557
|
return;
|
|
558
558
|
const t = this._state;
|
|
559
559
|
this._state = this._committed, this.onSet?.(t, this._state);
|
|
560
|
-
for (const
|
|
561
|
-
|
|
560
|
+
for (const s of this._listeners)
|
|
561
|
+
s(this._state, t);
|
|
562
562
|
}
|
|
563
563
|
/** Subscribes to state changes. Returns an unsubscribe function. */
|
|
564
564
|
subscribe(t) {
|
|
@@ -589,29 +589,49 @@ class M {
|
|
|
589
589
|
this._cleanups || (this._cleanups = []), this._cleanups.push(t);
|
|
590
590
|
}
|
|
591
591
|
/** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */
|
|
592
|
-
subscribeTo(t,
|
|
593
|
-
const
|
|
594
|
-
return this.addCleanup(
|
|
592
|
+
subscribeTo(t, s) {
|
|
593
|
+
const e = t.subscribe(s);
|
|
594
|
+
return this.addCleanup(e), e;
|
|
595
595
|
}
|
|
596
|
-
shallowEqual(t,
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
596
|
+
shallowEqual(t, s) {
|
|
597
|
+
const e = Object.keys(t), i = Object.keys(s);
|
|
598
|
+
if (e.length !== i.length)
|
|
599
599
|
return !1;
|
|
600
|
-
for (const
|
|
601
|
-
if (t[
|
|
600
|
+
for (const n of e)
|
|
601
|
+
if (t[n] !== s[n])
|
|
602
602
|
return !1;
|
|
603
603
|
return !0;
|
|
604
604
|
}
|
|
605
605
|
}
|
|
606
|
-
|
|
606
|
+
const x = typeof __MVC_KIT_DEV__ < "u" && __MVC_KIT_DEV__;
|
|
607
|
+
class D {
|
|
608
|
+
/** Maximum number of items before FIFO eviction. 0 = unlimited. */
|
|
609
|
+
static MAX_SIZE = 0;
|
|
610
|
+
/** Time-to-live in milliseconds. 0 = no expiry. */
|
|
611
|
+
static TTL = 0;
|
|
607
612
|
_items = [];
|
|
608
613
|
_disposed = !1;
|
|
609
614
|
_listeners = /* @__PURE__ */ new Set();
|
|
610
615
|
_index = /* @__PURE__ */ new Map();
|
|
611
616
|
_abortController = null;
|
|
612
617
|
_cleanups = null;
|
|
618
|
+
_timestamps = null;
|
|
619
|
+
_evictionTimer = null;
|
|
613
620
|
constructor(t = []) {
|
|
614
|
-
|
|
621
|
+
let s = [...t];
|
|
622
|
+
if (this._ttl > 0) {
|
|
623
|
+
this._timestamps = /* @__PURE__ */ new Map();
|
|
624
|
+
const e = Date.now();
|
|
625
|
+
for (const i of s)
|
|
626
|
+
this._timestamps.set(i.id, e);
|
|
627
|
+
}
|
|
628
|
+
if (this._maxSize > 0 && s.length > this._maxSize) {
|
|
629
|
+
const e = s.length - this._maxSize, i = s.slice(0, e);
|
|
630
|
+
s = s.slice(e);
|
|
631
|
+
for (const n of i)
|
|
632
|
+
this._timestamps?.delete(n.id);
|
|
633
|
+
}
|
|
634
|
+
this._items = Object.freeze(s), this.rebuildIndex(), this._scheduleEvictionTimer();
|
|
615
635
|
}
|
|
616
636
|
/**
|
|
617
637
|
* Alias for Subscribable compatibility.
|
|
@@ -635,6 +655,13 @@ class x {
|
|
|
635
655
|
get disposeSignal() {
|
|
636
656
|
return this._abortController || (this._abortController = new AbortController()), this._abortController.signal;
|
|
637
657
|
}
|
|
658
|
+
// ── Config Accessors ──
|
|
659
|
+
get _maxSize() {
|
|
660
|
+
return this.constructor.MAX_SIZE;
|
|
661
|
+
}
|
|
662
|
+
get _ttl() {
|
|
663
|
+
return this.constructor.TTL;
|
|
664
|
+
}
|
|
638
665
|
// ── CRUD Methods (notify listeners) ──
|
|
639
666
|
/**
|
|
640
667
|
* Add one or more items. Items with existing IDs are silently skipped.
|
|
@@ -644,15 +671,20 @@ class x {
|
|
|
644
671
|
throw new Error("Cannot add to disposed Collection");
|
|
645
672
|
if (t.length === 0)
|
|
646
673
|
return;
|
|
647
|
-
const
|
|
648
|
-
for (const
|
|
649
|
-
!this._index.has(
|
|
650
|
-
if (
|
|
674
|
+
const s = /* @__PURE__ */ new Set(), e = [];
|
|
675
|
+
for (const a of t)
|
|
676
|
+
!this._index.has(a.id) && !s.has(a.id) && (e.push(a), s.add(a.id));
|
|
677
|
+
if (e.length === 0) return;
|
|
651
678
|
const i = this._items;
|
|
652
|
-
|
|
653
|
-
for (const
|
|
654
|
-
this._index.set(
|
|
655
|
-
this.
|
|
679
|
+
let n = [...i, ...e];
|
|
680
|
+
for (const a of e)
|
|
681
|
+
this._index.set(a.id, a);
|
|
682
|
+
if (this._timestamps) {
|
|
683
|
+
const a = Date.now();
|
|
684
|
+
for (const o of e)
|
|
685
|
+
this._timestamps.set(o.id, a);
|
|
686
|
+
}
|
|
687
|
+
this._maxSize > 0 && n.length > this._maxSize && (n = this._evictForCapacity(n)), this._items = Object.freeze(n), this.notify(i), this._scheduleEvictionTimer();
|
|
656
688
|
}
|
|
657
689
|
/**
|
|
658
690
|
* Add or replace items by ID. Existing items are replaced in-place
|
|
@@ -664,26 +696,30 @@ class x {
|
|
|
664
696
|
if (this._disposed)
|
|
665
697
|
throw new Error("Cannot upsert on disposed Collection");
|
|
666
698
|
if (t.length === 0) return;
|
|
667
|
-
const
|
|
668
|
-
for (const
|
|
669
|
-
|
|
670
|
-
const
|
|
699
|
+
const s = /* @__PURE__ */ new Map();
|
|
700
|
+
for (const l of t)
|
|
701
|
+
s.set(l.id, l);
|
|
702
|
+
const e = this._items;
|
|
671
703
|
let i = !1;
|
|
672
|
-
const
|
|
673
|
-
for (const
|
|
674
|
-
if (
|
|
675
|
-
const
|
|
676
|
-
|
|
704
|
+
const n = /* @__PURE__ */ new Set(), a = [];
|
|
705
|
+
for (const l of e)
|
|
706
|
+
if (s.has(l.id)) {
|
|
707
|
+
const h = s.get(l.id);
|
|
708
|
+
h !== l && (i = !0), a.push(h), n.add(l.id);
|
|
677
709
|
} else
|
|
678
|
-
|
|
679
|
-
for (const [
|
|
680
|
-
|
|
681
|
-
if (i)
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
710
|
+
a.push(l);
|
|
711
|
+
for (const [l, h] of s)
|
|
712
|
+
n.has(l) || (a.push(h), i = !0);
|
|
713
|
+
if (!i) return;
|
|
714
|
+
if (this._timestamps) {
|
|
715
|
+
const l = Date.now();
|
|
716
|
+
for (const [h] of s)
|
|
717
|
+
this._timestamps.set(h, l);
|
|
686
718
|
}
|
|
719
|
+
for (const [l, h] of s)
|
|
720
|
+
this._index.set(l, h);
|
|
721
|
+
let o = a;
|
|
722
|
+
this._maxSize > 0 && o.length > this._maxSize && (o = this._evictForCapacity(o)), this._items = Object.freeze(o), this.notify(e), this._scheduleEvictionTimer();
|
|
687
723
|
}
|
|
688
724
|
/**
|
|
689
725
|
* Remove items by id(s).
|
|
@@ -693,29 +729,29 @@ class x {
|
|
|
693
729
|
throw new Error("Cannot remove from disposed Collection");
|
|
694
730
|
if (t.length === 0)
|
|
695
731
|
return;
|
|
696
|
-
const
|
|
697
|
-
if (
|
|
732
|
+
const s = new Set(t), e = this._items.filter((n) => !s.has(n.id));
|
|
733
|
+
if (e.length === this._items.length)
|
|
698
734
|
return;
|
|
699
735
|
const i = this._items;
|
|
700
|
-
this._items = Object.freeze(
|
|
701
|
-
for (const
|
|
702
|
-
this._index.delete(
|
|
703
|
-
this.notify(i);
|
|
736
|
+
this._items = Object.freeze(e);
|
|
737
|
+
for (const n of t)
|
|
738
|
+
this._index.delete(n), this._timestamps?.delete(n);
|
|
739
|
+
this.notify(i), this._scheduleEvictionTimer();
|
|
704
740
|
}
|
|
705
741
|
/**
|
|
706
742
|
* Update an item by id with partial changes.
|
|
707
743
|
*/
|
|
708
|
-
update(t,
|
|
744
|
+
update(t, s) {
|
|
709
745
|
if (this._disposed)
|
|
710
746
|
throw new Error("Cannot update disposed Collection");
|
|
711
|
-
const
|
|
712
|
-
if (
|
|
747
|
+
const e = this._items.findIndex((u) => u.id === t);
|
|
748
|
+
if (e === -1)
|
|
713
749
|
return;
|
|
714
|
-
const i = this._items[
|
|
715
|
-
if (!Object.keys(
|
|
750
|
+
const i = this._items[e], n = { ...i, ...s, id: t };
|
|
751
|
+
if (!Object.keys(s).some((u) => s[u] !== i[u]))
|
|
716
752
|
return;
|
|
717
|
-
const l = this._items,
|
|
718
|
-
|
|
753
|
+
const l = this._items, h = [...l];
|
|
754
|
+
h[e] = n, this._items = Object.freeze(h), this._index.set(t, n), this.notify(l);
|
|
719
755
|
}
|
|
720
756
|
/**
|
|
721
757
|
* Replace all items.
|
|
@@ -723,8 +759,15 @@ class x {
|
|
|
723
759
|
reset(t) {
|
|
724
760
|
if (this._disposed)
|
|
725
761
|
throw new Error("Cannot reset disposed Collection");
|
|
726
|
-
const
|
|
727
|
-
|
|
762
|
+
const s = this._items;
|
|
763
|
+
if (this._timestamps) {
|
|
764
|
+
this._timestamps.clear();
|
|
765
|
+
const i = Date.now();
|
|
766
|
+
for (const n of t)
|
|
767
|
+
this._timestamps.set(n.id, i);
|
|
768
|
+
}
|
|
769
|
+
let e = [...t];
|
|
770
|
+
this._maxSize > 0 && e.length > this._maxSize && (e = this._evictForCapacity(e)), this._items = Object.freeze(e), this.rebuildIndex(), this.notify(s), this._scheduleEvictionTimer();
|
|
728
771
|
}
|
|
729
772
|
/**
|
|
730
773
|
* Remove all items.
|
|
@@ -735,7 +778,7 @@ class x {
|
|
|
735
778
|
if (this._items.length === 0)
|
|
736
779
|
return;
|
|
737
780
|
const t = this._items;
|
|
738
|
-
this._items = Object.freeze([]), this._index.clear(), this.notify(t);
|
|
781
|
+
this._items = Object.freeze([]), this._index.clear(), this._timestamps?.clear(), this._clearEvictionTimer(), this.notify(t);
|
|
739
782
|
}
|
|
740
783
|
/**
|
|
741
784
|
* Snapshot current state, apply callback mutations, and return a rollback function.
|
|
@@ -744,14 +787,14 @@ class x {
|
|
|
744
787
|
optimistic(t) {
|
|
745
788
|
if (this._disposed)
|
|
746
789
|
throw new Error("Cannot perform optimistic update on disposed Collection");
|
|
747
|
-
const e = this.
|
|
790
|
+
const s = this._items, e = this._timestamps ? new Map(this._timestamps) : null;
|
|
748
791
|
t();
|
|
749
|
-
let
|
|
792
|
+
let i = !1;
|
|
750
793
|
return () => {
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
const
|
|
754
|
-
this._items = e, this.rebuildIndex(), this.notify(
|
|
794
|
+
if (i || this._disposed) return;
|
|
795
|
+
i = !0;
|
|
796
|
+
const n = this._items;
|
|
797
|
+
this._items = s, e && (this._timestamps = e), this.rebuildIndex(), this.notify(n), this._scheduleEvictionTimer();
|
|
755
798
|
};
|
|
756
799
|
}
|
|
757
800
|
// ── Query Methods (pure, no notification) ──
|
|
@@ -802,11 +845,11 @@ class x {
|
|
|
802
845
|
/** Tears down the instance, releasing all subscriptions and resources. */
|
|
803
846
|
dispose() {
|
|
804
847
|
if (!this._disposed) {
|
|
805
|
-
if (this._disposed = !0, this._abortController?.abort(), this._cleanups) {
|
|
848
|
+
if (this._disposed = !0, this._clearEvictionTimer(), this._abortController?.abort(), this._cleanups) {
|
|
806
849
|
for (const t of this._cleanups) t();
|
|
807
850
|
this._cleanups = null;
|
|
808
851
|
}
|
|
809
|
-
this.onDispose?.(), this._listeners.clear(), this._index.clear();
|
|
852
|
+
this.onDispose?.(), this._listeners.clear(), this._index.clear(), this._timestamps?.clear();
|
|
810
853
|
}
|
|
811
854
|
}
|
|
812
855
|
/** Registers a cleanup function to be called on dispose. @protected */
|
|
@@ -814,16 +857,85 @@ class x {
|
|
|
814
857
|
this._cleanups || (this._cleanups = []), this._cleanups.push(t);
|
|
815
858
|
}
|
|
816
859
|
notify(t) {
|
|
817
|
-
for (const
|
|
818
|
-
|
|
860
|
+
for (const s of this._listeners)
|
|
861
|
+
s(this._items, t);
|
|
819
862
|
}
|
|
820
863
|
rebuildIndex() {
|
|
821
864
|
this._index.clear();
|
|
822
865
|
for (const t of this._items)
|
|
823
866
|
this._index.set(t.id, t);
|
|
824
867
|
}
|
|
868
|
+
// ── Eviction Internals ──
|
|
869
|
+
_evictForCapacity(t) {
|
|
870
|
+
const s = t.length - this._maxSize;
|
|
871
|
+
if (s <= 0) return t;
|
|
872
|
+
const e = t.slice(0, s), i = this._applyOnEvict(e, "capacity");
|
|
873
|
+
if (i === !1 || i.length === 0) return t;
|
|
874
|
+
const n = new Set(i.map((o) => o.id)), a = t.filter((o) => !n.has(o.id));
|
|
875
|
+
for (const o of i)
|
|
876
|
+
this._index.delete(o.id), this._timestamps?.delete(o.id);
|
|
877
|
+
return a;
|
|
878
|
+
}
|
|
879
|
+
_applyOnEvict(t, s) {
|
|
880
|
+
if (!this.onEvict) return t;
|
|
881
|
+
const e = this.onEvict(t, s);
|
|
882
|
+
if (e === !1) {
|
|
883
|
+
if (x && s === "capacity" && this._maxSize > 0) {
|
|
884
|
+
const i = this._items.length + t.length;
|
|
885
|
+
i > this._maxSize * 2 && console.warn(
|
|
886
|
+
`[mvc-kit] Collection exceeded 2x MAX_SIZE (${i}/${this._maxSize}). onEvict is vetoing eviction — this may cause unbounded growth.`
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
return !1;
|
|
890
|
+
}
|
|
891
|
+
if (Array.isArray(e)) {
|
|
892
|
+
const i = new Set(t.map((n) => n.id));
|
|
893
|
+
return e.filter((n) => i.has(n.id));
|
|
894
|
+
}
|
|
895
|
+
return t;
|
|
896
|
+
}
|
|
897
|
+
_sweepExpired() {
|
|
898
|
+
if (this._disposed || !this._timestamps || this._ttl <= 0) return;
|
|
899
|
+
const t = Date.now(), s = this._ttl, e = [];
|
|
900
|
+
for (const o of this._items) {
|
|
901
|
+
const l = this._timestamps.get(o.id);
|
|
902
|
+
l !== void 0 && t - l >= s && e.push(o);
|
|
903
|
+
}
|
|
904
|
+
if (e.length === 0) {
|
|
905
|
+
this._scheduleEvictionTimer();
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const i = this._applyOnEvict(e, "ttl");
|
|
909
|
+
if (i === !1) {
|
|
910
|
+
this._scheduleEvictionTimer();
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
if (i.length === 0) {
|
|
914
|
+
this._scheduleEvictionTimer();
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
const n = new Set(i.map((o) => o.id)), a = this._items;
|
|
918
|
+
this._items = Object.freeze(
|
|
919
|
+
a.filter((o) => !n.has(o.id))
|
|
920
|
+
);
|
|
921
|
+
for (const o of i)
|
|
922
|
+
this._index.delete(o.id), this._timestamps.delete(o.id);
|
|
923
|
+
this.notify(a), this._scheduleEvictionTimer();
|
|
924
|
+
}
|
|
925
|
+
_scheduleEvictionTimer() {
|
|
926
|
+
if (this._clearEvictionTimer(), this._disposed || !this._timestamps || this._ttl <= 0 || this._timestamps.size === 0) return;
|
|
927
|
+
const t = Date.now(), s = this._ttl;
|
|
928
|
+
let e = 1 / 0;
|
|
929
|
+
for (const n of this._timestamps.values())
|
|
930
|
+
n < e && (e = n);
|
|
931
|
+
const i = Math.max(0, e + s - t);
|
|
932
|
+
this._evictionTimer = setTimeout(() => this._sweepExpired(), i);
|
|
933
|
+
}
|
|
934
|
+
_clearEvictionTimer() {
|
|
935
|
+
this._evictionTimer !== null && (clearTimeout(this._evictionTimer), this._evictionTimer = null);
|
|
936
|
+
}
|
|
825
937
|
}
|
|
826
|
-
class
|
|
938
|
+
class I {
|
|
827
939
|
_disposed = !1;
|
|
828
940
|
_initialized = !1;
|
|
829
941
|
_abortController = null;
|
|
@@ -860,12 +972,12 @@ class D {
|
|
|
860
972
|
this._cleanups || (this._cleanups = []), this._cleanups.push(t);
|
|
861
973
|
}
|
|
862
974
|
/** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */
|
|
863
|
-
subscribeTo(t,
|
|
864
|
-
const
|
|
865
|
-
return this.addCleanup(
|
|
975
|
+
subscribeTo(t, s) {
|
|
976
|
+
const e = t.subscribe(s);
|
|
977
|
+
return this.addCleanup(e), e;
|
|
866
978
|
}
|
|
867
979
|
}
|
|
868
|
-
class
|
|
980
|
+
class P {
|
|
869
981
|
_disposed = !1;
|
|
870
982
|
_initialized = !1;
|
|
871
983
|
_abortController = null;
|
|
@@ -902,13 +1014,13 @@ class I {
|
|
|
902
1014
|
this._cleanups || (this._cleanups = []), this._cleanups.push(t);
|
|
903
1015
|
}
|
|
904
1016
|
}
|
|
905
|
-
const
|
|
1017
|
+
const g = typeof __MVC_KIT_DEV__ < "u" && __MVC_KIT_DEV__, k = Object.freeze({
|
|
906
1018
|
connected: !1,
|
|
907
1019
|
reconnecting: !1,
|
|
908
1020
|
attempt: 0,
|
|
909
1021
|
error: null
|
|
910
1022
|
});
|
|
911
|
-
class
|
|
1023
|
+
class R {
|
|
912
1024
|
// Static config (subclass overrides)
|
|
913
1025
|
/** Base delay (ms) for reconnection backoff. */
|
|
914
1026
|
static RECONNECT_BASE = 1e3;
|
|
@@ -978,10 +1090,10 @@ class P {
|
|
|
978
1090
|
/** Initiates a connection with automatic reconnection on failure. */
|
|
979
1091
|
connect() {
|
|
980
1092
|
if (this._disposed) {
|
|
981
|
-
|
|
1093
|
+
g && console.warn("[mvc-kit] connect() called after dispose — ignored.");
|
|
982
1094
|
return;
|
|
983
1095
|
}
|
|
984
|
-
|
|
1096
|
+
g && !this._initialized && console.warn("[mvc-kit] connect() called before init()."), !(this._connState === 1 || this._connState === 2) && (this._reconnectTimer !== null && (clearTimeout(this._reconnectTimer), this._reconnectTimer = null), this._attemptConnect(0));
|
|
985
1097
|
}
|
|
986
1098
|
/** Closes the connection and cancels any pending reconnection. */
|
|
987
1099
|
disconnect() {
|
|
@@ -999,15 +1111,15 @@ class P {
|
|
|
999
1111
|
}
|
|
1000
1112
|
// ── Subclass signals ────────────────────────────────────────────
|
|
1001
1113
|
/** Call from subclass when a message arrives from the transport. @protected */
|
|
1002
|
-
receive(t,
|
|
1114
|
+
receive(t, s) {
|
|
1003
1115
|
if (this._disposed) {
|
|
1004
|
-
|
|
1116
|
+
g && console.warn(`[mvc-kit] receive("${String(t)}") called after dispose — ignored.`);
|
|
1005
1117
|
return;
|
|
1006
1118
|
}
|
|
1007
|
-
const
|
|
1008
|
-
if (
|
|
1009
|
-
for (const i of
|
|
1010
|
-
i(
|
|
1119
|
+
const e = this._handlers.get(t);
|
|
1120
|
+
if (e)
|
|
1121
|
+
for (const i of e)
|
|
1122
|
+
i(s);
|
|
1011
1123
|
}
|
|
1012
1124
|
/** Call from subclass when the transport connection drops unexpectedly. Triggers reconnection. @protected */
|
|
1013
1125
|
disconnected() {
|
|
@@ -1015,20 +1127,20 @@ class P {
|
|
|
1015
1127
|
}
|
|
1016
1128
|
// ── Consumer API ────────────────────────────────────────────────
|
|
1017
1129
|
/** Subscribes to a specific message type. Returns an unsubscribe function. */
|
|
1018
|
-
on(t,
|
|
1130
|
+
on(t, s) {
|
|
1019
1131
|
if (this._disposed) return () => {
|
|
1020
1132
|
};
|
|
1021
|
-
let
|
|
1022
|
-
return
|
|
1023
|
-
|
|
1133
|
+
let e = this._handlers.get(t);
|
|
1134
|
+
return e || (e = /* @__PURE__ */ new Set(), this._handlers.set(t, e)), e.add(s), () => {
|
|
1135
|
+
e.delete(s);
|
|
1024
1136
|
};
|
|
1025
1137
|
}
|
|
1026
1138
|
/** Subscribes to a message type, auto-removing the handler after the first invocation. */
|
|
1027
|
-
once(t,
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1139
|
+
once(t, s) {
|
|
1140
|
+
const e = this.on(t, (i) => {
|
|
1141
|
+
e(), s(i);
|
|
1030
1142
|
});
|
|
1031
|
-
return
|
|
1143
|
+
return e;
|
|
1032
1144
|
}
|
|
1033
1145
|
// ── Infrastructure ──────────────────────────────────────────────
|
|
1034
1146
|
/** Registers a cleanup function to be called on dispose. @protected */
|
|
@@ -1036,46 +1148,46 @@ class P {
|
|
|
1036
1148
|
this._cleanups || (this._cleanups = []), this._cleanups.push(t);
|
|
1037
1149
|
}
|
|
1038
1150
|
/** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */
|
|
1039
|
-
subscribeTo(t,
|
|
1040
|
-
const
|
|
1041
|
-
return this.addCleanup(
|
|
1151
|
+
subscribeTo(t, s) {
|
|
1152
|
+
const e = t.subscribe(s);
|
|
1153
|
+
return this.addCleanup(e), e;
|
|
1042
1154
|
}
|
|
1043
1155
|
// ── Backoff ─────────────────────────────────────────────────────
|
|
1044
1156
|
/** Computes the reconnect backoff delay with jitter for the given attempt number. @protected */
|
|
1045
1157
|
_calculateDelay(t) {
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1158
|
+
const s = this.constructor, e = Math.min(
|
|
1159
|
+
s.RECONNECT_BASE * Math.pow(s.RECONNECT_FACTOR, t),
|
|
1160
|
+
s.RECONNECT_MAX
|
|
1049
1161
|
);
|
|
1050
|
-
return Math.random() *
|
|
1162
|
+
return Math.random() * e;
|
|
1051
1163
|
}
|
|
1052
1164
|
// ── Internals ───────────────────────────────────────────────────
|
|
1053
1165
|
_setStatus(t) {
|
|
1054
|
-
const
|
|
1055
|
-
if (!(
|
|
1166
|
+
const s = this._status;
|
|
1167
|
+
if (!(s.connected === t.connected && s.reconnecting === t.reconnecting && s.attempt === t.attempt && s.error === t.error)) {
|
|
1056
1168
|
this._status = Object.freeze(t);
|
|
1057
|
-
for (const
|
|
1058
|
-
|
|
1169
|
+
for (const e of this._listeners)
|
|
1170
|
+
e(this._status, s);
|
|
1059
1171
|
}
|
|
1060
1172
|
}
|
|
1061
1173
|
_attemptConnect(t) {
|
|
1062
1174
|
if (this._disposed) return;
|
|
1063
1175
|
this._connState = 1, this._connectAbort?.abort(), this._connectAbort = new AbortController();
|
|
1064
|
-
const
|
|
1176
|
+
const s = this._abortController ? AbortSignal.any([this._abortController.signal, this._connectAbort.signal]) : this._connectAbort.signal;
|
|
1065
1177
|
this._setStatus({
|
|
1066
1178
|
connected: !1,
|
|
1067
1179
|
reconnecting: t > 0,
|
|
1068
1180
|
attempt: t,
|
|
1069
1181
|
error: null
|
|
1070
1182
|
});
|
|
1071
|
-
let
|
|
1183
|
+
let e;
|
|
1072
1184
|
try {
|
|
1073
|
-
|
|
1185
|
+
e = this.open(s);
|
|
1074
1186
|
} catch (i) {
|
|
1075
1187
|
this._onOpenFailed(t, i);
|
|
1076
1188
|
return;
|
|
1077
1189
|
}
|
|
1078
|
-
|
|
1190
|
+
e && typeof e.then == "function" ? e.then(
|
|
1079
1191
|
() => this._onOpenSucceeded(),
|
|
1080
1192
|
(i) => this._onOpenFailed(t, i)
|
|
1081
1193
|
) : this._onOpenSucceeded();
|
|
@@ -1088,47 +1200,47 @@ class P {
|
|
|
1088
1200
|
error: null
|
|
1089
1201
|
}));
|
|
1090
1202
|
}
|
|
1091
|
-
_onOpenFailed(t,
|
|
1092
|
-
this._disposed || this._connState !== 0 && (this._connectAbort?.abort(), this._connectAbort = null, this._connState = 3, this._scheduleReconnect(t + 1,
|
|
1203
|
+
_onOpenFailed(t, s) {
|
|
1204
|
+
this._disposed || this._connState !== 0 && (this._connectAbort?.abort(), this._connectAbort = null, this._connState = 3, this._scheduleReconnect(t + 1, s));
|
|
1093
1205
|
}
|
|
1094
|
-
_scheduleReconnect(t,
|
|
1095
|
-
const
|
|
1096
|
-
if (t >
|
|
1206
|
+
_scheduleReconnect(t, s) {
|
|
1207
|
+
const e = this.constructor;
|
|
1208
|
+
if (t > e.MAX_ATTEMPTS) {
|
|
1097
1209
|
this._connState = 0, this._setStatus({
|
|
1098
1210
|
connected: !1,
|
|
1099
1211
|
reconnecting: !1,
|
|
1100
1212
|
attempt: t,
|
|
1101
|
-
error:
|
|
1213
|
+
error: s instanceof Error ? s.message : "Max reconnection attempts reached"
|
|
1102
1214
|
});
|
|
1103
1215
|
return;
|
|
1104
1216
|
}
|
|
1105
|
-
const i =
|
|
1217
|
+
const i = s instanceof Error ? s.message : s ? String(s) : null;
|
|
1106
1218
|
this._setStatus({
|
|
1107
1219
|
connected: !1,
|
|
1108
1220
|
reconnecting: !0,
|
|
1109
1221
|
attempt: t,
|
|
1110
1222
|
error: i
|
|
1111
1223
|
});
|
|
1112
|
-
const
|
|
1224
|
+
const n = this._calculateDelay(t - 1);
|
|
1113
1225
|
this._reconnectTimer = setTimeout(() => {
|
|
1114
1226
|
this._reconnectTimer = null, this._attemptConnect(t);
|
|
1115
|
-
},
|
|
1227
|
+
}, n);
|
|
1116
1228
|
}
|
|
1117
1229
|
}
|
|
1118
1230
|
export {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1231
|
+
R as Channel,
|
|
1232
|
+
D as Collection,
|
|
1233
|
+
I as Controller,
|
|
1122
1234
|
w as EventBus,
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1235
|
+
C as HttpError,
|
|
1236
|
+
j as Model,
|
|
1237
|
+
P as Service,
|
|
1238
|
+
m as ViewModel,
|
|
1239
|
+
E as classifyError,
|
|
1128
1240
|
N as hasSingleton,
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1241
|
+
y as isAbortError,
|
|
1242
|
+
$ as singleton,
|
|
1243
|
+
L as teardown,
|
|
1244
|
+
B as teardownAll
|
|
1133
1245
|
};
|
|
1134
1246
|
//# sourceMappingURL=mvc-kit.js.map
|