mvc-kit 2.9.0 → 2.10.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.
Files changed (102) hide show
  1. package/README.md +2 -0
  2. package/agent-config/claude-code/skills/guide/anti-patterns.md +64 -0
  3. package/agent-config/claude-code/skills/guide/api-reference.md +33 -2
  4. package/agent-config/claude-code/skills/guide/patterns.md +38 -0
  5. package/agent-config/copilot/copilot-instructions.md +5 -1
  6. package/agent-config/cursor/cursorrules +5 -1
  7. package/dist/Channel.cjs +5 -0
  8. package/dist/Channel.cjs.map +1 -1
  9. package/dist/Channel.d.ts +1 -0
  10. package/dist/Channel.d.ts.map +1 -1
  11. package/dist/Channel.js +5 -0
  12. package/dist/Channel.js.map +1 -1
  13. package/dist/Collection.cjs +20 -17
  14. package/dist/Collection.cjs.map +1 -1
  15. package/dist/Collection.d.ts +2 -2
  16. package/dist/Collection.d.ts.map +1 -1
  17. package/dist/Collection.js +20 -17
  18. package/dist/Collection.js.map +1 -1
  19. package/dist/Controller.cjs +5 -0
  20. package/dist/Controller.cjs.map +1 -1
  21. package/dist/Controller.d.ts +1 -0
  22. package/dist/Controller.d.ts.map +1 -1
  23. package/dist/Controller.js +5 -0
  24. package/dist/Controller.js.map +1 -1
  25. package/dist/EventBus.cjs +5 -0
  26. package/dist/EventBus.cjs.map +1 -1
  27. package/dist/EventBus.d.ts +1 -0
  28. package/dist/EventBus.d.ts.map +1 -1
  29. package/dist/EventBus.js +5 -0
  30. package/dist/EventBus.js.map +1 -1
  31. package/dist/Feed.cjs +4 -0
  32. package/dist/Feed.cjs.map +1 -1
  33. package/dist/Feed.d.ts +1 -0
  34. package/dist/Feed.d.ts.map +1 -1
  35. package/dist/Feed.js +4 -0
  36. package/dist/Feed.js.map +1 -1
  37. package/dist/Model.cjs +6 -3
  38. package/dist/Model.cjs.map +1 -1
  39. package/dist/Model.d.ts +1 -1
  40. package/dist/Model.d.ts.map +1 -1
  41. package/dist/Model.js +6 -3
  42. package/dist/Model.js.map +1 -1
  43. package/dist/Pagination.cjs +2 -0
  44. package/dist/Pagination.cjs.map +1 -1
  45. package/dist/Pagination.d.ts.map +1 -1
  46. package/dist/Pagination.js +2 -0
  47. package/dist/Pagination.js.map +1 -1
  48. package/dist/Pending.cjs +301 -0
  49. package/dist/Pending.cjs.map +1 -0
  50. package/dist/Pending.d.ts +91 -0
  51. package/dist/Pending.d.ts.map +1 -0
  52. package/dist/Pending.js +301 -0
  53. package/dist/Pending.js.map +1 -0
  54. package/dist/PersistentCollection.cjs +1 -1
  55. package/dist/PersistentCollection.cjs.map +1 -1
  56. package/dist/PersistentCollection.d.ts.map +1 -1
  57. package/dist/PersistentCollection.js +1 -1
  58. package/dist/PersistentCollection.js.map +1 -1
  59. package/dist/Selection.cjs +4 -0
  60. package/dist/Selection.cjs.map +1 -1
  61. package/dist/Selection.d.ts +1 -0
  62. package/dist/Selection.d.ts.map +1 -1
  63. package/dist/Selection.js +4 -0
  64. package/dist/Selection.js.map +1 -1
  65. package/dist/Service.cjs +5 -0
  66. package/dist/Service.cjs.map +1 -1
  67. package/dist/Service.d.ts +1 -0
  68. package/dist/Service.d.ts.map +1 -1
  69. package/dist/Service.js +5 -0
  70. package/dist/Service.js.map +1 -1
  71. package/dist/Sorting.cjs +2 -0
  72. package/dist/Sorting.cjs.map +1 -1
  73. package/dist/Sorting.d.ts.map +1 -1
  74. package/dist/Sorting.js +2 -0
  75. package/dist/Sorting.js.map +1 -1
  76. package/dist/ViewModel.cjs +45 -17
  77. package/dist/ViewModel.cjs.map +1 -1
  78. package/dist/ViewModel.d.ts +13 -4
  79. package/dist/ViewModel.d.ts.map +1 -1
  80. package/dist/ViewModel.js +45 -17
  81. package/dist/ViewModel.js.map +1 -1
  82. package/dist/bindPublicMethods.cjs +27 -0
  83. package/dist/bindPublicMethods.cjs.map +1 -0
  84. package/dist/bindPublicMethods.d.ts +18 -0
  85. package/dist/bindPublicMethods.d.ts.map +1 -0
  86. package/dist/bindPublicMethods.js +27 -0
  87. package/dist/bindPublicMethods.js.map +1 -0
  88. package/dist/index.d.ts +2 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/mvc-kit.cjs +2 -0
  91. package/dist/mvc-kit.cjs.map +1 -1
  92. package/dist/mvc-kit.js +2 -0
  93. package/dist/mvc-kit.js.map +1 -1
  94. package/dist/walkPrototypeChain.cjs.map +1 -1
  95. package/dist/walkPrototypeChain.d.ts +1 -1
  96. package/dist/walkPrototypeChain.js.map +1 -1
  97. package/dist/wrapAsyncMethods.cjs +15 -3
  98. package/dist/wrapAsyncMethods.cjs.map +1 -1
  99. package/dist/wrapAsyncMethods.d.ts.map +1 -1
  100. package/dist/wrapAsyncMethods.js +15 -3
  101. package/dist/wrapAsyncMethods.js.map +1 -1
  102. package/package.json +2 -1
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const bindPublicMethods = require("./bindPublicMethods.cjs");
3
4
  const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
5
+ const PROTECTED_KEYS = /* @__PURE__ */ new Set(["addCleanup"]);
4
6
  function freeze(obj) {
5
7
  return __DEV__ ? Object.freeze(obj) : obj;
6
8
  }
@@ -35,8 +37,9 @@ class Collection {
35
37
  }
36
38
  }
37
39
  this._items = freeze(result);
38
- this.rebuildIndex();
40
+ this._rebuildIndex();
39
41
  this._scheduleEvictionTimer();
42
+ bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
40
43
  }
41
44
  /**
42
45
  * Alias for Subscribable compatibility.
@@ -92,7 +95,7 @@ class Collection {
92
95
  result2 = this._evictForCapacity(result2);
93
96
  }
94
97
  this._items = freeze(result2);
95
- this.notify(prev2);
98
+ this._notify(prev2);
96
99
  this._scheduleEvictionTimer();
97
100
  return;
98
101
  }
@@ -120,7 +123,7 @@ class Collection {
120
123
  result = this._evictForCapacity(result);
121
124
  }
122
125
  this._items = freeze(result);
123
- this.notify(prev);
126
+ this._notify(prev);
124
127
  this._scheduleEvictionTimer();
125
128
  }
126
129
  /**
@@ -146,7 +149,7 @@ class Collection {
146
149
  this._index.set(item.id, item);
147
150
  if (this._timestamps) this._timestamps.set(item.id, Date.now());
148
151
  this._items = freeze(newItems);
149
- this.notify(prev2);
152
+ this._notify(prev2);
150
153
  } else {
151
154
  const prev2 = this._items;
152
155
  let result2 = [...prev2, item];
@@ -156,7 +159,7 @@ class Collection {
156
159
  result2 = this._evictForCapacity(result2);
157
160
  }
158
161
  this._items = freeze(result2);
159
- this.notify(prev2);
162
+ this._notify(prev2);
160
163
  this._scheduleEvictionTimer();
161
164
  }
162
165
  return;
@@ -200,7 +203,7 @@ class Collection {
200
203
  result = this._evictForCapacity(result);
201
204
  }
202
205
  this._items = freeze(result);
203
- this.notify(prev);
206
+ this._notify(prev);
204
207
  this._scheduleEvictionTimer();
205
208
  }
206
209
  /**
@@ -220,7 +223,7 @@ class Collection {
220
223
  this._items = freeze(prev2.filter((item) => item.id !== id));
221
224
  this._index.delete(id);
222
225
  this._timestamps?.delete(id);
223
- this.notify(prev2);
226
+ this._notify(prev2);
224
227
  this._scheduleEvictionTimer();
225
228
  return;
226
229
  }
@@ -235,7 +238,7 @@ class Collection {
235
238
  this._index.delete(id);
236
239
  this._timestamps?.delete(id);
237
240
  }
238
- this.notify(prev);
241
+ this._notify(prev);
239
242
  this._scheduleEvictionTimer();
240
243
  }
241
244
  /**
@@ -257,7 +260,7 @@ class Collection {
257
260
  newItems[idx] = updated;
258
261
  this._items = freeze(newItems);
259
262
  this._index.set(id, updated);
260
- this.notify(prev);
263
+ this._notify(prev);
261
264
  }
262
265
  /**
263
266
  * Replace all items.
@@ -279,8 +282,8 @@ class Collection {
279
282
  result = this._evictForCapacity(result);
280
283
  }
281
284
  this._items = freeze(result);
282
- this.rebuildIndex();
283
- this.notify(prev);
285
+ this._rebuildIndex();
286
+ this._notify(prev);
284
287
  this._scheduleEvictionTimer();
285
288
  }
286
289
  /**
@@ -298,7 +301,7 @@ class Collection {
298
301
  this._index.clear();
299
302
  this._timestamps?.clear();
300
303
  this._clearEvictionTimer();
301
- this.notify(prev);
304
+ this._notify(prev);
302
305
  }
303
306
  /**
304
307
  * Snapshot current state, apply callback mutations, and return a rollback function.
@@ -320,8 +323,8 @@ class Collection {
320
323
  if (timestampSnapshot) {
321
324
  this._timestamps = timestampSnapshot;
322
325
  }
323
- this.rebuildIndex();
324
- this.notify(prev);
326
+ this._rebuildIndex();
327
+ this._notify(prev);
325
328
  this._scheduleEvictionTimer();
326
329
  };
327
330
  }
@@ -398,12 +401,12 @@ class Collection {
398
401
  }
399
402
  this._cleanups.push(fn);
400
403
  }
401
- notify(prev) {
404
+ _notify(prev) {
402
405
  for (const listener of this._listeners) {
403
406
  listener(this._items, prev);
404
407
  }
405
408
  }
406
- rebuildIndex() {
409
+ _rebuildIndex() {
407
410
  this._index.clear();
408
411
  for (const item of this._items) {
409
412
  this._index.set(item.id, item);
@@ -478,7 +481,7 @@ class Collection {
478
481
  this._index.delete(item.id);
479
482
  this._timestamps.delete(item.id);
480
483
  }
481
- this.notify(prev);
484
+ this._notify(prev);
482
485
  this._scheduleEvictionTimer();
483
486
  }
484
487
  _scheduleEvictionTimer() {
@@ -1 +1 @@
1
- {"version":3,"file":"Collection.cjs","sources":["../src/Collection.ts"],"sourcesContent":["import type { Listener, Subscribable } from './types';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\nfunction freeze<T>(obj: T): T {\n return __DEV__ ? Object.freeze(obj) as T : obj;\n}\n\ntype CollectionState<T> = T[];\ntype CollectionListener<T> = Listener<CollectionState<T>>;\n\n/**\n * Reactive typed array with CRUD and query methods.\n */\nexport class Collection<T extends { id: string | number }> implements Subscribable<CollectionState<T>> {\n /** Maximum number of items before FIFO eviction. 0 = unlimited. */\n static MAX_SIZE = 0;\n /** Time-to-live in milliseconds. 0 = no expiry. */\n static TTL = 0;\n\n private _items: readonly T[] = [];\n private _disposed = false;\n private _listeners = new Set<CollectionListener<T>>();\n private _index = new Map<T['id'], T>();\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n private _timestamps: Map<T['id'], number> | null = null;\n private _evictionTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(initialItems: T[] = []) {\n let result = [...initialItems];\n\n if (this._ttl > 0) {\n this._timestamps = new Map();\n const now = Date.now();\n for (const item of result) {\n this._timestamps.set(item.id, now);\n }\n }\n\n if (this._maxSize > 0 && result.length > this._maxSize) {\n // FIFO: trim from the front (oldest items)\n const excess = result.length - this._maxSize;\n const evicted = result.slice(0, excess);\n result = result.slice(excess);\n for (const item of evicted) {\n this._timestamps?.delete(item.id);\n }\n }\n\n this._items = freeze(result);\n this.rebuildIndex();\n this._scheduleEvictionTimer();\n }\n\n /**\n * Alias for Subscribable compatibility.\n */\n get state(): T[] {\n return this._items as T[];\n }\n\n /** The raw array of items. */\n get items(): T[] {\n return this._items as T[];\n }\n\n /** Number of items in the collection. */\n get length(): number {\n return this._items.length;\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n // ── Config Accessors ──\n\n private get _maxSize(): number {\n return (this.constructor as typeof Collection).MAX_SIZE;\n }\n\n private get _ttl(): number {\n return (this.constructor as typeof Collection).TTL;\n }\n\n // ── CRUD Methods (notify listeners) ──\n\n /**\n * Add one or more items. Items with existing IDs are silently skipped.\n */\n add(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot add to disposed Collection');\n }\n\n if (items.length === 0) {\n return;\n }\n\n // Fast path: single item (most common case)\n if (items.length === 1) {\n const item = items[0];\n if (this._index.has(item.id)) return;\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-item path\n const seen = new Set<T['id']>();\n const newItems: T[] = [];\n for (const item of items) {\n if (!this._index.has(item.id) && !seen.has(item.id)) {\n newItems.push(item);\n seen.add(item.id);\n }\n }\n if (newItems.length === 0) return;\n\n const prev = this._items;\n let result = [...prev, ...newItems];\n\n for (const item of newItems) {\n this._index.set(item.id, item);\n }\n\n // Record timestamps for TTL\n if (this._timestamps) {\n const now = Date.now();\n for (const item of newItems) {\n this._timestamps.set(item.id, now);\n }\n }\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Add or replace items by ID. Existing items are replaced in-place\n * (preserving array position); new items are appended. Deduplicates\n * input — last occurrence wins. No-op if nothing changed (reference\n * comparison).\n */\n upsert(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot upsert on disposed Collection');\n }\n if (items.length === 0) return;\n\n // Fast path: single item (most common case — channel messages, real-time updates)\n if (items.length === 1) {\n const item = items[0];\n const existing = this._index.get(item.id);\n\n if (existing) {\n // Replace in-place — skip if same reference\n if (existing === item) return;\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = item;\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n this._items = freeze(newItems);\n this.notify(prev);\n } else {\n // New item — append (with capacity enforcement)\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n return;\n }\n\n // Multi-item path: deduplicate input — last occurrence wins\n const incoming = new Map<T['id'], T>();\n for (const item of items) {\n incoming.set(item.id, item);\n }\n\n const prev = this._items;\n let changed = false;\n const replaced = new Set<T['id']>();\n const newArray: T[] = [];\n\n // Replace existing items in-place\n for (const existing of prev) {\n if (incoming.has(existing.id)) {\n const replacement = incoming.get(existing.id)!;\n if (replacement !== existing) changed = true;\n newArray.push(replacement);\n replaced.add(existing.id);\n } else {\n newArray.push(existing);\n }\n }\n\n // Append genuinely new items\n for (const [id, item] of incoming) {\n if (!replaced.has(id)) {\n newArray.push(item);\n changed = true;\n }\n }\n\n if (!changed) return;\n\n // Record/refresh timestamps for TTL (upsert refreshes existing)\n if (this._timestamps) {\n const now = Date.now();\n for (const [id] of incoming) {\n this._timestamps.set(id, now);\n }\n }\n\n for (const [id, item] of incoming) {\n this._index.set(id, item);\n }\n\n // Enforce capacity before freeze/notify\n let result = newArray;\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove items by id(s).\n */\n remove(...ids: T['id'][]): void {\n if (this._disposed) {\n throw new Error('Cannot remove from disposed Collection');\n }\n\n if (ids.length === 0) {\n return;\n }\n\n // Fast path: single id (most common case)\n if (ids.length === 1) {\n const id = ids[0];\n if (!this._index.has(id)) return;\n const prev = this._items;\n this._items = freeze(prev.filter(item => item.id !== id));\n this._index.delete(id);\n this._timestamps?.delete(id);\n this.notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-id path\n const idSet = new Set(ids);\n const filtered = this._items.filter(item => !idSet.has(item.id));\n\n if (filtered.length === this._items.length) {\n return; // No items removed\n }\n\n const prev = this._items;\n this._items = freeze(filtered);\n\n for (const id of ids) {\n this._index.delete(id);\n this._timestamps?.delete(id);\n }\n\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Update an item by id with partial changes.\n */\n update(id: T['id'], changes: Partial<T>): void {\n if (this._disposed) {\n throw new Error('Cannot update disposed Collection');\n }\n\n // O(1) existence check via index Map\n const existing = this._index.get(id);\n if (!existing) return;\n\n // Check if anything actually changed (before any array work)\n const keys = Object.keys(changes) as (keyof T)[];\n const hasChanges = keys.some(key => changes[key] !== existing[key]);\n if (!hasChanges) return;\n\n const updated = { ...existing, ...changes, id };\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = updated;\n this._items = freeze(newItems);\n this._index.set(id, updated);\n\n this.notify(prev);\n }\n\n /**\n * Replace all items.\n */\n reset(items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot reset disposed Collection');\n }\n\n const prev = this._items;\n\n // Record timestamps for TTL\n if (this._timestamps) {\n this._timestamps.clear();\n const now = Date.now();\n for (const item of items) {\n this._timestamps.set(item.id, now);\n }\n }\n\n let result = [...items];\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this.rebuildIndex();\n\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove all items.\n */\n clear(): void {\n if (this._disposed) {\n throw new Error('Cannot clear disposed Collection');\n }\n\n if (this._items.length === 0) {\n return;\n }\n\n const prev = this._items;\n this._items = freeze([] as T[]);\n this._index.clear();\n this._timestamps?.clear();\n this._clearEvictionTimer();\n\n this.notify(prev);\n }\n\n /**\n * Snapshot current state, apply callback mutations, and return a rollback function.\n * Rollback restores items to pre-callback state regardless of later mutations.\n */\n optimistic(callback: () => void): () => void {\n if (this._disposed) {\n throw new Error('Cannot perform optimistic update on disposed Collection');\n }\n\n const snapshot = this._items;\n const timestampSnapshot = this._timestamps ? new Map(this._timestamps) : null;\n callback();\n\n let rolledBack = false;\n return () => {\n if (rolledBack || this._disposed) return;\n rolledBack = true;\n\n const prev = this._items;\n this._items = snapshot;\n if (timestampSnapshot) {\n this._timestamps = timestampSnapshot;\n }\n this.rebuildIndex();\n this.notify(prev);\n this._scheduleEvictionTimer();\n };\n }\n\n // ── Query Methods (pure, no notification) ──\n\n /**\n * Get item by id.\n */\n get(id: T['id']): T | undefined {\n return this._index.get(id);\n }\n\n /**\n * Check if item exists by id.\n */\n has(id: T['id']): boolean {\n return this._index.has(id);\n }\n\n /**\n * Find first item matching predicate.\n */\n find(predicate: (item: T) => boolean): T | undefined {\n return this._items.find(predicate);\n }\n\n /**\n * Filter items matching predicate.\n */\n filter(predicate: (item: T) => boolean): T[] {\n return this._items.filter(predicate) as T[];\n }\n\n /**\n * Return sorted copy.\n */\n sorted(compareFn: (a: T, b: T) => number): T[] {\n return [...this._items].sort(compareFn);\n }\n\n /**\n * Map items to new array.\n */\n map<U>(fn: (item: T) => U): U[] {\n return this._items.map(fn);\n }\n\n // ── Subscribable interface ──\n\n /** Subscribes to state changes. Returns an unsubscribe function. */\n subscribe(listener: CollectionListener<T>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n this._listeners.add(listener);\n\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n this._clearEvictionTimer();\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this.onDispose?.();\n this._listeners.clear();\n this._index.clear();\n this._timestamps?.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n\n /**\n * Called before items are auto-evicted by capacity or TTL.\n * Override to filter which items get evicted, or veto entirely.\n *\n * @param items - Candidates for eviction\n * @param reason - Why eviction is happening\n * @returns void to proceed with all, false to veto, or T[] subset to evict only those\n */\n protected onEvict?(items: T[], reason: 'capacity' | 'ttl'): T[] | false | void;\n\n private notify(prev: readonly T[]): void {\n for (const listener of this._listeners) {\n listener(this._items as T[], prev as T[]);\n }\n }\n\n private rebuildIndex(): void {\n this._index.clear();\n for (const item of this._items) {\n this._index.set(item.id, item);\n }\n }\n\n // ── Eviction Internals ──\n\n private _evictForCapacity(items: T[]): T[] {\n const excess = items.length - this._maxSize;\n if (excess <= 0) return items;\n\n const candidates = items.slice(0, excess);\n const toEvict = this._applyOnEvict(candidates, 'capacity');\n\n if (toEvict === false) return items; // veto\n\n if (toEvict.length === 0) return items; // nothing to evict\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const result = items.filter(item => !evictIds.has(item.id));\n\n // Clean up index and timestamps for evicted items\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps?.delete(item.id);\n }\n\n return result;\n }\n\n private _applyOnEvict(candidates: T[], reason: 'capacity' | 'ttl'): T[] | false {\n if (!this.onEvict) return candidates;\n\n const result = this.onEvict(candidates, reason);\n if (result === false) {\n // DEV warning when veto causes collection to exceed 2x MAX_SIZE\n if (__DEV__ && reason === 'capacity' && this._maxSize > 0) {\n const currentSize = this._items.length + candidates.length;\n if (currentSize > this._maxSize * 2) {\n console.warn(\n `[mvc-kit] Collection exceeded 2x MAX_SIZE (${currentSize}/${this._maxSize}). ` +\n `onEvict is vetoing eviction — this may cause unbounded growth.`\n );\n }\n }\n return false;\n }\n if (Array.isArray(result)) {\n // Only include items that are actually in the current items\n const candidateIds = new Set(candidates.map(c => c.id));\n return result.filter(item => candidateIds.has(item.id));\n }\n return candidates; // void = proceed with all\n }\n\n private _sweepExpired(): void {\n if (this._disposed || !this._timestamps || this._ttl <= 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n const expired: T[] = [];\n\n for (const item of this._items) {\n const ts = this._timestamps.get(item.id);\n if (ts !== undefined && (now - ts) >= ttl) {\n expired.push(item);\n }\n }\n\n if (expired.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const toEvict = this._applyOnEvict(expired, 'ttl');\n\n if (toEvict === false) {\n this._scheduleEvictionTimer();\n return;\n }\n\n if (toEvict.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const prev = this._items;\n this._items = freeze(\n (prev as T[]).filter((item: T) => !evictIds.has(item.id))\n );\n\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps.delete(item.id);\n }\n\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n private _scheduleEvictionTimer(): void {\n this._clearEvictionTimer();\n\n if (this._disposed || !this._timestamps || this._ttl <= 0 || this._timestamps.size === 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n let earliest = Infinity;\n\n for (const ts of this._timestamps.values()) {\n if (ts < earliest) earliest = ts;\n }\n\n const delay = Math.max(0, (earliest + ttl) - now);\n this._evictionTimer = setTimeout(() => this._sweepExpired(), delay);\n }\n\n private _clearEvictionTimer(): void {\n if (this._evictionTimer !== null) {\n clearTimeout(this._evictionTimer);\n this._evictionTimer = null;\n }\n }\n}\n"],"names":["prev","result"],"mappings":"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"}
1
+ {"version":3,"file":"Collection.cjs","sources":["../src/Collection.ts"],"sourcesContent":["import type { Listener, Subscribable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\nconst PROTECTED_KEYS = new Set(['addCleanup']);\n\nfunction freeze<T>(obj: T): T {\n return __DEV__ ? Object.freeze(obj) as T : obj;\n}\n\ntype CollectionState<T> = T[];\ntype CollectionListener<T> = Listener<CollectionState<T>>;\n\n/**\n * Reactive typed array with CRUD and query methods.\n */\nexport class Collection<T extends { id: string | number }> implements Subscribable<CollectionState<T>> {\n /** Maximum number of items before FIFO eviction. 0 = unlimited. */\n static MAX_SIZE = 0;\n /** Time-to-live in milliseconds. 0 = no expiry. */\n static TTL = 0;\n\n private _items: readonly T[] = [];\n private _disposed = false;\n private _listeners = new Set<CollectionListener<T>>();\n private _index = new Map<T['id'], T>();\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n private _timestamps: Map<T['id'], number> | null = null;\n private _evictionTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(initialItems: T[] = []) {\n let result = [...initialItems];\n\n if (this._ttl > 0) {\n this._timestamps = new Map();\n const now = Date.now();\n for (const item of result) {\n this._timestamps.set(item.id, now);\n }\n }\n\n if (this._maxSize > 0 && result.length > this._maxSize) {\n // FIFO: trim from the front (oldest items)\n const excess = result.length - this._maxSize;\n const evicted = result.slice(0, excess);\n result = result.slice(excess);\n for (const item of evicted) {\n this._timestamps?.delete(item.id);\n }\n }\n\n this._items = freeze(result);\n this._rebuildIndex();\n this._scheduleEvictionTimer();\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n /**\n * Alias for Subscribable compatibility.\n */\n get state(): T[] {\n return this._items as T[];\n }\n\n /** The raw array of items. */\n get items(): T[] {\n return this._items as T[];\n }\n\n /** Number of items in the collection. */\n get length(): number {\n return this._items.length;\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n // ── Config Accessors ──\n\n private get _maxSize(): number {\n return (this.constructor as typeof Collection).MAX_SIZE;\n }\n\n private get _ttl(): number {\n return (this.constructor as typeof Collection).TTL;\n }\n\n // ── CRUD Methods (notify listeners) ──\n\n /**\n * Add one or more items. Items with existing IDs are silently skipped.\n */\n add(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot add to disposed Collection');\n }\n\n if (items.length === 0) {\n return;\n }\n\n // Fast path: single item (most common case)\n if (items.length === 1) {\n const item = items[0];\n if (this._index.has(item.id)) return;\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-item path\n const seen = new Set<T['id']>();\n const newItems: T[] = [];\n for (const item of items) {\n if (!this._index.has(item.id) && !seen.has(item.id)) {\n newItems.push(item);\n seen.add(item.id);\n }\n }\n if (newItems.length === 0) return;\n\n const prev = this._items;\n let result = [...prev, ...newItems];\n\n for (const item of newItems) {\n this._index.set(item.id, item);\n }\n\n // Record timestamps for TTL\n if (this._timestamps) {\n const now = Date.now();\n for (const item of newItems) {\n this._timestamps.set(item.id, now);\n }\n }\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Add or replace items by ID. Existing items are replaced in-place\n * (preserving array position); new items are appended. Deduplicates\n * input — last occurrence wins. No-op if nothing changed (reference\n * comparison).\n */\n upsert(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot upsert on disposed Collection');\n }\n if (items.length === 0) return;\n\n // Fast path: single item (most common case — channel messages, real-time updates)\n if (items.length === 1) {\n const item = items[0];\n const existing = this._index.get(item.id);\n\n if (existing) {\n // Replace in-place — skip if same reference\n if (existing === item) return;\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = item;\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n this._items = freeze(newItems);\n this._notify(prev);\n } else {\n // New item — append (with capacity enforcement)\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n return;\n }\n\n // Multi-item path: deduplicate input — last occurrence wins\n const incoming = new Map<T['id'], T>();\n for (const item of items) {\n incoming.set(item.id, item);\n }\n\n const prev = this._items;\n let changed = false;\n const replaced = new Set<T['id']>();\n const newArray: T[] = [];\n\n // Replace existing items in-place\n for (const existing of prev) {\n if (incoming.has(existing.id)) {\n const replacement = incoming.get(existing.id)!;\n if (replacement !== existing) changed = true;\n newArray.push(replacement);\n replaced.add(existing.id);\n } else {\n newArray.push(existing);\n }\n }\n\n // Append genuinely new items\n for (const [id, item] of incoming) {\n if (!replaced.has(id)) {\n newArray.push(item);\n changed = true;\n }\n }\n\n if (!changed) return;\n\n // Record/refresh timestamps for TTL (upsert refreshes existing)\n if (this._timestamps) {\n const now = Date.now();\n for (const [id] of incoming) {\n this._timestamps.set(id, now);\n }\n }\n\n for (const [id, item] of incoming) {\n this._index.set(id, item);\n }\n\n // Enforce capacity before freeze/notify\n let result = newArray;\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove items by id(s).\n */\n remove(...ids: T['id'][]): void {\n if (this._disposed) {\n throw new Error('Cannot remove from disposed Collection');\n }\n\n if (ids.length === 0) {\n return;\n }\n\n // Fast path: single id (most common case)\n if (ids.length === 1) {\n const id = ids[0];\n if (!this._index.has(id)) return;\n const prev = this._items;\n this._items = freeze(prev.filter(item => item.id !== id));\n this._index.delete(id);\n this._timestamps?.delete(id);\n this._notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-id path\n const idSet = new Set(ids);\n const filtered = this._items.filter(item => !idSet.has(item.id));\n\n if (filtered.length === this._items.length) {\n return; // No items removed\n }\n\n const prev = this._items;\n this._items = freeze(filtered);\n\n for (const id of ids) {\n this._index.delete(id);\n this._timestamps?.delete(id);\n }\n\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Update an item by id with partial changes.\n */\n update(id: T['id'], changes: Partial<T>): void {\n if (this._disposed) {\n throw new Error('Cannot update disposed Collection');\n }\n\n // O(1) existence check via index Map\n const existing = this._index.get(id);\n if (!existing) return;\n\n // Check if anything actually changed (before any array work)\n const keys = Object.keys(changes) as (keyof T)[];\n const hasChanges = keys.some(key => changes[key] !== existing[key]);\n if (!hasChanges) return;\n\n const updated = { ...existing, ...changes, id };\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = updated;\n this._items = freeze(newItems);\n this._index.set(id, updated);\n\n this._notify(prev);\n }\n\n /**\n * Replace all items.\n */\n reset(items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot reset disposed Collection');\n }\n\n const prev = this._items;\n\n // Record timestamps for TTL\n if (this._timestamps) {\n this._timestamps.clear();\n const now = Date.now();\n for (const item of items) {\n this._timestamps.set(item.id, now);\n }\n }\n\n let result = [...items];\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this._rebuildIndex();\n\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove all items.\n */\n clear(): void {\n if (this._disposed) {\n throw new Error('Cannot clear disposed Collection');\n }\n\n if (this._items.length === 0) {\n return;\n }\n\n const prev = this._items;\n this._items = freeze([] as T[]);\n this._index.clear();\n this._timestamps?.clear();\n this._clearEvictionTimer();\n\n this._notify(prev);\n }\n\n /**\n * Snapshot current state, apply callback mutations, and return a rollback function.\n * Rollback restores items to pre-callback state regardless of later mutations.\n */\n optimistic(callback: () => void): () => void {\n if (this._disposed) {\n throw new Error('Cannot perform optimistic update on disposed Collection');\n }\n\n const snapshot = this._items;\n const timestampSnapshot = this._timestamps ? new Map(this._timestamps) : null;\n callback();\n\n let rolledBack = false;\n return () => {\n if (rolledBack || this._disposed) return;\n rolledBack = true;\n\n const prev = this._items;\n this._items = snapshot;\n if (timestampSnapshot) {\n this._timestamps = timestampSnapshot;\n }\n this._rebuildIndex();\n this._notify(prev);\n this._scheduleEvictionTimer();\n };\n }\n\n // ── Query Methods (pure, no notification) ──\n\n /**\n * Get item by id.\n */\n get(id: T['id']): T | undefined {\n return this._index.get(id);\n }\n\n /**\n * Check if item exists by id.\n */\n has(id: T['id']): boolean {\n return this._index.has(id);\n }\n\n /**\n * Find first item matching predicate.\n */\n find(predicate: (item: T) => boolean): T | undefined {\n return this._items.find(predicate);\n }\n\n /**\n * Filter items matching predicate.\n */\n filter(predicate: (item: T) => boolean): T[] {\n return this._items.filter(predicate) as T[];\n }\n\n /**\n * Return sorted copy.\n */\n sorted(compareFn: (a: T, b: T) => number): T[] {\n return [...this._items].sort(compareFn);\n }\n\n /**\n * Map items to new array.\n */\n map<U>(fn: (item: T) => U): U[] {\n return this._items.map(fn);\n }\n\n // ── Subscribable interface ──\n\n /** Subscribes to state changes. Returns an unsubscribe function. */\n subscribe(listener: CollectionListener<T>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n this._listeners.add(listener);\n\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n this._clearEvictionTimer();\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this.onDispose?.();\n this._listeners.clear();\n this._index.clear();\n this._timestamps?.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n\n /**\n * Called before items are auto-evicted by capacity or TTL.\n * Override to filter which items get evicted, or veto entirely.\n *\n * @param items - Candidates for eviction\n * @param reason - Why eviction is happening\n * @returns void to proceed with all, false to veto, or T[] subset to evict only those\n */\n protected onEvict?(items: T[], reason: 'capacity' | 'ttl'): T[] | false | void;\n\n private _notify(prev: readonly T[]): void {\n for (const listener of this._listeners) {\n listener(this._items as T[], prev as T[]);\n }\n }\n\n private _rebuildIndex(): void {\n this._index.clear();\n for (const item of this._items) {\n this._index.set(item.id, item);\n }\n }\n\n // ── Eviction Internals ──\n\n private _evictForCapacity(items: T[]): T[] {\n const excess = items.length - this._maxSize;\n if (excess <= 0) return items;\n\n const candidates = items.slice(0, excess);\n const toEvict = this._applyOnEvict(candidates, 'capacity');\n\n if (toEvict === false) return items; // veto\n\n if (toEvict.length === 0) return items; // nothing to evict\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const result = items.filter(item => !evictIds.has(item.id));\n\n // Clean up index and timestamps for evicted items\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps?.delete(item.id);\n }\n\n return result;\n }\n\n private _applyOnEvict(candidates: T[], reason: 'capacity' | 'ttl'): T[] | false {\n if (!this.onEvict) return candidates;\n\n const result = this.onEvict(candidates, reason);\n if (result === false) {\n // DEV warning when veto causes collection to exceed 2x MAX_SIZE\n if (__DEV__ && reason === 'capacity' && this._maxSize > 0) {\n const currentSize = this._items.length + candidates.length;\n if (currentSize > this._maxSize * 2) {\n console.warn(\n `[mvc-kit] Collection exceeded 2x MAX_SIZE (${currentSize}/${this._maxSize}). ` +\n `onEvict is vetoing eviction — this may cause unbounded growth.`\n );\n }\n }\n return false;\n }\n if (Array.isArray(result)) {\n // Only include items that are actually in the current items\n const candidateIds = new Set(candidates.map(c => c.id));\n return result.filter(item => candidateIds.has(item.id));\n }\n return candidates; // void = proceed with all\n }\n\n private _sweepExpired(): void {\n if (this._disposed || !this._timestamps || this._ttl <= 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n const expired: T[] = [];\n\n for (const item of this._items) {\n const ts = this._timestamps.get(item.id);\n if (ts !== undefined && (now - ts) >= ttl) {\n expired.push(item);\n }\n }\n\n if (expired.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const toEvict = this._applyOnEvict(expired, 'ttl');\n\n if (toEvict === false) {\n this._scheduleEvictionTimer();\n return;\n }\n\n if (toEvict.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const prev = this._items;\n this._items = freeze(\n (prev as T[]).filter((item: T) => !evictIds.has(item.id))\n );\n\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps.delete(item.id);\n }\n\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n private _scheduleEvictionTimer(): void {\n this._clearEvictionTimer();\n\n if (this._disposed || !this._timestamps || this._ttl <= 0 || this._timestamps.size === 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n let earliest = Infinity;\n\n for (const ts of this._timestamps.values()) {\n if (ts < earliest) earliest = ts;\n }\n\n const delay = Math.max(0, (earliest + ttl) - now);\n this._evictionTimer = setTimeout(() => this._sweepExpired(), delay);\n }\n\n private _clearEvictionTimer(): void {\n if (this._evictionTimer !== null) {\n clearTimeout(this._evictionTimer);\n this._evictionTimer = null;\n }\n }\n}\n"],"names":["bindPublicMethods","prev","result"],"mappings":"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"}
@@ -107,8 +107,8 @@ export declare class Collection<T extends {
107
107
  * @returns void to proceed with all, false to veto, or T[] subset to evict only those
108
108
  */
109
109
  protected onEvict?(items: T[], reason: 'capacity' | 'ttl'): T[] | false | void;
110
- private notify;
111
- private rebuildIndex;
110
+ private _notify;
111
+ private _rebuildIndex;
112
112
  private _evictForCapacity;
113
113
  private _applyOnEvict;
114
114
  private _sweepExpired;
@@ -1 +1 @@
1
- {"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQtD,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AAC9B,KAAK,kBAAkB,CAAC,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D;;GAEG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAE,YAAW,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACpG,mEAAmE;IACnE,MAAM,CAAC,QAAQ,SAAK;IACpB,mDAAmD;IACnD,MAAM,CAAC,GAAG,SAAK;IAEf,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,cAAc,CAA8C;gBAExD,YAAY,GAAE,CAAC,EAAO;IA0BlC;;OAEG;IACH,IAAI,KAAK,IAAI,CAAC,EAAE,CAEf;IAED,8BAA8B;IAC9B,IAAI,KAAK,IAAI,CAAC,EAAE,CAEf;IAED,yCAAyC;IACzC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,+CAA+C;IAC/C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,6EAA6E;IAC7E,IAAI,aAAa,IAAI,WAAW,CAK/B;IAID,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,IAAI,GAEf;IAID;;OAEG;IACH,GAAG,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IA8DxB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IA8F3B;;OAEG;IACH,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI;IA0C/B;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAyB9C;;OAEG;IACH,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IA8BvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAkBb;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IA2B5C;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS;IAI/B;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO;IAIzB;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,CAAC,GAAG,SAAS;IAIpD;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,CAAC,EAAE;IAI5C;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,EAAE;IAI9C;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IAM/B,oEAAoE;IACpE,SAAS,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAYtD,0EAA0E;IAC1E,OAAO,IAAI,IAAI;IAkBf,uEAAuE;IACvE,SAAS,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAO1C,uFAAuF;IACvF,SAAS,CAAC,SAAS,CAAC,IAAI,IAAI;IAE5B;;;;;;;OAOG;IACH,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,KAAK,GAAG,IAAI;IAE9E,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,aAAa;IA8CrB,OAAO,CAAC,sBAAsB;IAiB9B,OAAO,CAAC,mBAAmB;CAM5B"}
1
+ {"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
+ import { bindPublicMethods } from "./bindPublicMethods.js";
1
2
  const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
3
+ const PROTECTED_KEYS = /* @__PURE__ */ new Set(["addCleanup"]);
2
4
  function freeze(obj) {
3
5
  return __DEV__ ? Object.freeze(obj) : obj;
4
6
  }
@@ -33,8 +35,9 @@ class Collection {
33
35
  }
34
36
  }
35
37
  this._items = freeze(result);
36
- this.rebuildIndex();
38
+ this._rebuildIndex();
37
39
  this._scheduleEvictionTimer();
40
+ bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
38
41
  }
39
42
  /**
40
43
  * Alias for Subscribable compatibility.
@@ -90,7 +93,7 @@ class Collection {
90
93
  result2 = this._evictForCapacity(result2);
91
94
  }
92
95
  this._items = freeze(result2);
93
- this.notify(prev2);
96
+ this._notify(prev2);
94
97
  this._scheduleEvictionTimer();
95
98
  return;
96
99
  }
@@ -118,7 +121,7 @@ class Collection {
118
121
  result = this._evictForCapacity(result);
119
122
  }
120
123
  this._items = freeze(result);
121
- this.notify(prev);
124
+ this._notify(prev);
122
125
  this._scheduleEvictionTimer();
123
126
  }
124
127
  /**
@@ -144,7 +147,7 @@ class Collection {
144
147
  this._index.set(item.id, item);
145
148
  if (this._timestamps) this._timestamps.set(item.id, Date.now());
146
149
  this._items = freeze(newItems);
147
- this.notify(prev2);
150
+ this._notify(prev2);
148
151
  } else {
149
152
  const prev2 = this._items;
150
153
  let result2 = [...prev2, item];
@@ -154,7 +157,7 @@ class Collection {
154
157
  result2 = this._evictForCapacity(result2);
155
158
  }
156
159
  this._items = freeze(result2);
157
- this.notify(prev2);
160
+ this._notify(prev2);
158
161
  this._scheduleEvictionTimer();
159
162
  }
160
163
  return;
@@ -198,7 +201,7 @@ class Collection {
198
201
  result = this._evictForCapacity(result);
199
202
  }
200
203
  this._items = freeze(result);
201
- this.notify(prev);
204
+ this._notify(prev);
202
205
  this._scheduleEvictionTimer();
203
206
  }
204
207
  /**
@@ -218,7 +221,7 @@ class Collection {
218
221
  this._items = freeze(prev2.filter((item) => item.id !== id));
219
222
  this._index.delete(id);
220
223
  this._timestamps?.delete(id);
221
- this.notify(prev2);
224
+ this._notify(prev2);
222
225
  this._scheduleEvictionTimer();
223
226
  return;
224
227
  }
@@ -233,7 +236,7 @@ class Collection {
233
236
  this._index.delete(id);
234
237
  this._timestamps?.delete(id);
235
238
  }
236
- this.notify(prev);
239
+ this._notify(prev);
237
240
  this._scheduleEvictionTimer();
238
241
  }
239
242
  /**
@@ -255,7 +258,7 @@ class Collection {
255
258
  newItems[idx] = updated;
256
259
  this._items = freeze(newItems);
257
260
  this._index.set(id, updated);
258
- this.notify(prev);
261
+ this._notify(prev);
259
262
  }
260
263
  /**
261
264
  * Replace all items.
@@ -277,8 +280,8 @@ class Collection {
277
280
  result = this._evictForCapacity(result);
278
281
  }
279
282
  this._items = freeze(result);
280
- this.rebuildIndex();
281
- this.notify(prev);
283
+ this._rebuildIndex();
284
+ this._notify(prev);
282
285
  this._scheduleEvictionTimer();
283
286
  }
284
287
  /**
@@ -296,7 +299,7 @@ class Collection {
296
299
  this._index.clear();
297
300
  this._timestamps?.clear();
298
301
  this._clearEvictionTimer();
299
- this.notify(prev);
302
+ this._notify(prev);
300
303
  }
301
304
  /**
302
305
  * Snapshot current state, apply callback mutations, and return a rollback function.
@@ -318,8 +321,8 @@ class Collection {
318
321
  if (timestampSnapshot) {
319
322
  this._timestamps = timestampSnapshot;
320
323
  }
321
- this.rebuildIndex();
322
- this.notify(prev);
324
+ this._rebuildIndex();
325
+ this._notify(prev);
323
326
  this._scheduleEvictionTimer();
324
327
  };
325
328
  }
@@ -396,12 +399,12 @@ class Collection {
396
399
  }
397
400
  this._cleanups.push(fn);
398
401
  }
399
- notify(prev) {
402
+ _notify(prev) {
400
403
  for (const listener of this._listeners) {
401
404
  listener(this._items, prev);
402
405
  }
403
406
  }
404
- rebuildIndex() {
407
+ _rebuildIndex() {
405
408
  this._index.clear();
406
409
  for (const item of this._items) {
407
410
  this._index.set(item.id, item);
@@ -476,7 +479,7 @@ class Collection {
476
479
  this._index.delete(item.id);
477
480
  this._timestamps.delete(item.id);
478
481
  }
479
- this.notify(prev);
482
+ this._notify(prev);
480
483
  this._scheduleEvictionTimer();
481
484
  }
482
485
  _scheduleEvictionTimer() {
@@ -1 +1 @@
1
- {"version":3,"file":"Collection.js","sources":["../src/Collection.ts"],"sourcesContent":["import type { Listener, Subscribable } from './types';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\nfunction freeze<T>(obj: T): T {\n return __DEV__ ? Object.freeze(obj) as T : obj;\n}\n\ntype CollectionState<T> = T[];\ntype CollectionListener<T> = Listener<CollectionState<T>>;\n\n/**\n * Reactive typed array with CRUD and query methods.\n */\nexport class Collection<T extends { id: string | number }> implements Subscribable<CollectionState<T>> {\n /** Maximum number of items before FIFO eviction. 0 = unlimited. */\n static MAX_SIZE = 0;\n /** Time-to-live in milliseconds. 0 = no expiry. */\n static TTL = 0;\n\n private _items: readonly T[] = [];\n private _disposed = false;\n private _listeners = new Set<CollectionListener<T>>();\n private _index = new Map<T['id'], T>();\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n private _timestamps: Map<T['id'], number> | null = null;\n private _evictionTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(initialItems: T[] = []) {\n let result = [...initialItems];\n\n if (this._ttl > 0) {\n this._timestamps = new Map();\n const now = Date.now();\n for (const item of result) {\n this._timestamps.set(item.id, now);\n }\n }\n\n if (this._maxSize > 0 && result.length > this._maxSize) {\n // FIFO: trim from the front (oldest items)\n const excess = result.length - this._maxSize;\n const evicted = result.slice(0, excess);\n result = result.slice(excess);\n for (const item of evicted) {\n this._timestamps?.delete(item.id);\n }\n }\n\n this._items = freeze(result);\n this.rebuildIndex();\n this._scheduleEvictionTimer();\n }\n\n /**\n * Alias for Subscribable compatibility.\n */\n get state(): T[] {\n return this._items as T[];\n }\n\n /** The raw array of items. */\n get items(): T[] {\n return this._items as T[];\n }\n\n /** Number of items in the collection. */\n get length(): number {\n return this._items.length;\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n // ── Config Accessors ──\n\n private get _maxSize(): number {\n return (this.constructor as typeof Collection).MAX_SIZE;\n }\n\n private get _ttl(): number {\n return (this.constructor as typeof Collection).TTL;\n }\n\n // ── CRUD Methods (notify listeners) ──\n\n /**\n * Add one or more items. Items with existing IDs are silently skipped.\n */\n add(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot add to disposed Collection');\n }\n\n if (items.length === 0) {\n return;\n }\n\n // Fast path: single item (most common case)\n if (items.length === 1) {\n const item = items[0];\n if (this._index.has(item.id)) return;\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-item path\n const seen = new Set<T['id']>();\n const newItems: T[] = [];\n for (const item of items) {\n if (!this._index.has(item.id) && !seen.has(item.id)) {\n newItems.push(item);\n seen.add(item.id);\n }\n }\n if (newItems.length === 0) return;\n\n const prev = this._items;\n let result = [...prev, ...newItems];\n\n for (const item of newItems) {\n this._index.set(item.id, item);\n }\n\n // Record timestamps for TTL\n if (this._timestamps) {\n const now = Date.now();\n for (const item of newItems) {\n this._timestamps.set(item.id, now);\n }\n }\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Add or replace items by ID. Existing items are replaced in-place\n * (preserving array position); new items are appended. Deduplicates\n * input — last occurrence wins. No-op if nothing changed (reference\n * comparison).\n */\n upsert(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot upsert on disposed Collection');\n }\n if (items.length === 0) return;\n\n // Fast path: single item (most common case — channel messages, real-time updates)\n if (items.length === 1) {\n const item = items[0];\n const existing = this._index.get(item.id);\n\n if (existing) {\n // Replace in-place — skip if same reference\n if (existing === item) return;\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = item;\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n this._items = freeze(newItems);\n this.notify(prev);\n } else {\n // New item — append (with capacity enforcement)\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n return;\n }\n\n // Multi-item path: deduplicate input — last occurrence wins\n const incoming = new Map<T['id'], T>();\n for (const item of items) {\n incoming.set(item.id, item);\n }\n\n const prev = this._items;\n let changed = false;\n const replaced = new Set<T['id']>();\n const newArray: T[] = [];\n\n // Replace existing items in-place\n for (const existing of prev) {\n if (incoming.has(existing.id)) {\n const replacement = incoming.get(existing.id)!;\n if (replacement !== existing) changed = true;\n newArray.push(replacement);\n replaced.add(existing.id);\n } else {\n newArray.push(existing);\n }\n }\n\n // Append genuinely new items\n for (const [id, item] of incoming) {\n if (!replaced.has(id)) {\n newArray.push(item);\n changed = true;\n }\n }\n\n if (!changed) return;\n\n // Record/refresh timestamps for TTL (upsert refreshes existing)\n if (this._timestamps) {\n const now = Date.now();\n for (const [id] of incoming) {\n this._timestamps.set(id, now);\n }\n }\n\n for (const [id, item] of incoming) {\n this._index.set(id, item);\n }\n\n // Enforce capacity before freeze/notify\n let result = newArray;\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove items by id(s).\n */\n remove(...ids: T['id'][]): void {\n if (this._disposed) {\n throw new Error('Cannot remove from disposed Collection');\n }\n\n if (ids.length === 0) {\n return;\n }\n\n // Fast path: single id (most common case)\n if (ids.length === 1) {\n const id = ids[0];\n if (!this._index.has(id)) return;\n const prev = this._items;\n this._items = freeze(prev.filter(item => item.id !== id));\n this._index.delete(id);\n this._timestamps?.delete(id);\n this.notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-id path\n const idSet = new Set(ids);\n const filtered = this._items.filter(item => !idSet.has(item.id));\n\n if (filtered.length === this._items.length) {\n return; // No items removed\n }\n\n const prev = this._items;\n this._items = freeze(filtered);\n\n for (const id of ids) {\n this._index.delete(id);\n this._timestamps?.delete(id);\n }\n\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Update an item by id with partial changes.\n */\n update(id: T['id'], changes: Partial<T>): void {\n if (this._disposed) {\n throw new Error('Cannot update disposed Collection');\n }\n\n // O(1) existence check via index Map\n const existing = this._index.get(id);\n if (!existing) return;\n\n // Check if anything actually changed (before any array work)\n const keys = Object.keys(changes) as (keyof T)[];\n const hasChanges = keys.some(key => changes[key] !== existing[key]);\n if (!hasChanges) return;\n\n const updated = { ...existing, ...changes, id };\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = updated;\n this._items = freeze(newItems);\n this._index.set(id, updated);\n\n this.notify(prev);\n }\n\n /**\n * Replace all items.\n */\n reset(items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot reset disposed Collection');\n }\n\n const prev = this._items;\n\n // Record timestamps for TTL\n if (this._timestamps) {\n this._timestamps.clear();\n const now = Date.now();\n for (const item of items) {\n this._timestamps.set(item.id, now);\n }\n }\n\n let result = [...items];\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this.rebuildIndex();\n\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove all items.\n */\n clear(): void {\n if (this._disposed) {\n throw new Error('Cannot clear disposed Collection');\n }\n\n if (this._items.length === 0) {\n return;\n }\n\n const prev = this._items;\n this._items = freeze([] as T[]);\n this._index.clear();\n this._timestamps?.clear();\n this._clearEvictionTimer();\n\n this.notify(prev);\n }\n\n /**\n * Snapshot current state, apply callback mutations, and return a rollback function.\n * Rollback restores items to pre-callback state regardless of later mutations.\n */\n optimistic(callback: () => void): () => void {\n if (this._disposed) {\n throw new Error('Cannot perform optimistic update on disposed Collection');\n }\n\n const snapshot = this._items;\n const timestampSnapshot = this._timestamps ? new Map(this._timestamps) : null;\n callback();\n\n let rolledBack = false;\n return () => {\n if (rolledBack || this._disposed) return;\n rolledBack = true;\n\n const prev = this._items;\n this._items = snapshot;\n if (timestampSnapshot) {\n this._timestamps = timestampSnapshot;\n }\n this.rebuildIndex();\n this.notify(prev);\n this._scheduleEvictionTimer();\n };\n }\n\n // ── Query Methods (pure, no notification) ──\n\n /**\n * Get item by id.\n */\n get(id: T['id']): T | undefined {\n return this._index.get(id);\n }\n\n /**\n * Check if item exists by id.\n */\n has(id: T['id']): boolean {\n return this._index.has(id);\n }\n\n /**\n * Find first item matching predicate.\n */\n find(predicate: (item: T) => boolean): T | undefined {\n return this._items.find(predicate);\n }\n\n /**\n * Filter items matching predicate.\n */\n filter(predicate: (item: T) => boolean): T[] {\n return this._items.filter(predicate) as T[];\n }\n\n /**\n * Return sorted copy.\n */\n sorted(compareFn: (a: T, b: T) => number): T[] {\n return [...this._items].sort(compareFn);\n }\n\n /**\n * Map items to new array.\n */\n map<U>(fn: (item: T) => U): U[] {\n return this._items.map(fn);\n }\n\n // ── Subscribable interface ──\n\n /** Subscribes to state changes. Returns an unsubscribe function. */\n subscribe(listener: CollectionListener<T>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n this._listeners.add(listener);\n\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n this._clearEvictionTimer();\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this.onDispose?.();\n this._listeners.clear();\n this._index.clear();\n this._timestamps?.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n\n /**\n * Called before items are auto-evicted by capacity or TTL.\n * Override to filter which items get evicted, or veto entirely.\n *\n * @param items - Candidates for eviction\n * @param reason - Why eviction is happening\n * @returns void to proceed with all, false to veto, or T[] subset to evict only those\n */\n protected onEvict?(items: T[], reason: 'capacity' | 'ttl'): T[] | false | void;\n\n private notify(prev: readonly T[]): void {\n for (const listener of this._listeners) {\n listener(this._items as T[], prev as T[]);\n }\n }\n\n private rebuildIndex(): void {\n this._index.clear();\n for (const item of this._items) {\n this._index.set(item.id, item);\n }\n }\n\n // ── Eviction Internals ──\n\n private _evictForCapacity(items: T[]): T[] {\n const excess = items.length - this._maxSize;\n if (excess <= 0) return items;\n\n const candidates = items.slice(0, excess);\n const toEvict = this._applyOnEvict(candidates, 'capacity');\n\n if (toEvict === false) return items; // veto\n\n if (toEvict.length === 0) return items; // nothing to evict\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const result = items.filter(item => !evictIds.has(item.id));\n\n // Clean up index and timestamps for evicted items\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps?.delete(item.id);\n }\n\n return result;\n }\n\n private _applyOnEvict(candidates: T[], reason: 'capacity' | 'ttl'): T[] | false {\n if (!this.onEvict) return candidates;\n\n const result = this.onEvict(candidates, reason);\n if (result === false) {\n // DEV warning when veto causes collection to exceed 2x MAX_SIZE\n if (__DEV__ && reason === 'capacity' && this._maxSize > 0) {\n const currentSize = this._items.length + candidates.length;\n if (currentSize > this._maxSize * 2) {\n console.warn(\n `[mvc-kit] Collection exceeded 2x MAX_SIZE (${currentSize}/${this._maxSize}). ` +\n `onEvict is vetoing eviction — this may cause unbounded growth.`\n );\n }\n }\n return false;\n }\n if (Array.isArray(result)) {\n // Only include items that are actually in the current items\n const candidateIds = new Set(candidates.map(c => c.id));\n return result.filter(item => candidateIds.has(item.id));\n }\n return candidates; // void = proceed with all\n }\n\n private _sweepExpired(): void {\n if (this._disposed || !this._timestamps || this._ttl <= 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n const expired: T[] = [];\n\n for (const item of this._items) {\n const ts = this._timestamps.get(item.id);\n if (ts !== undefined && (now - ts) >= ttl) {\n expired.push(item);\n }\n }\n\n if (expired.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const toEvict = this._applyOnEvict(expired, 'ttl');\n\n if (toEvict === false) {\n this._scheduleEvictionTimer();\n return;\n }\n\n if (toEvict.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const prev = this._items;\n this._items = freeze(\n (prev as T[]).filter((item: T) => !evictIds.has(item.id))\n );\n\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps.delete(item.id);\n }\n\n this.notify(prev);\n this._scheduleEvictionTimer();\n }\n\n private _scheduleEvictionTimer(): void {\n this._clearEvictionTimer();\n\n if (this._disposed || !this._timestamps || this._ttl <= 0 || this._timestamps.size === 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n let earliest = Infinity;\n\n for (const ts of this._timestamps.values()) {\n if (ts < earliest) earliest = ts;\n }\n\n const delay = Math.max(0, (earliest + ttl) - now);\n this._evictionTimer = setTimeout(() => this._sweepExpired(), delay);\n }\n\n private _clearEvictionTimer(): void {\n if (this._evictionTimer !== null) {\n clearTimeout(this._evictionTimer);\n this._evictionTimer = null;\n }\n }\n}\n"],"names":["prev","result"],"mappings":"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"}
1
+ {"version":3,"file":"Collection.js","sources":["../src/Collection.ts"],"sourcesContent":["import type { Listener, Subscribable } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\nconst PROTECTED_KEYS = new Set(['addCleanup']);\n\nfunction freeze<T>(obj: T): T {\n return __DEV__ ? Object.freeze(obj) as T : obj;\n}\n\ntype CollectionState<T> = T[];\ntype CollectionListener<T> = Listener<CollectionState<T>>;\n\n/**\n * Reactive typed array with CRUD and query methods.\n */\nexport class Collection<T extends { id: string | number }> implements Subscribable<CollectionState<T>> {\n /** Maximum number of items before FIFO eviction. 0 = unlimited. */\n static MAX_SIZE = 0;\n /** Time-to-live in milliseconds. 0 = no expiry. */\n static TTL = 0;\n\n private _items: readonly T[] = [];\n private _disposed = false;\n private _listeners = new Set<CollectionListener<T>>();\n private _index = new Map<T['id'], T>();\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n private _timestamps: Map<T['id'], number> | null = null;\n private _evictionTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(initialItems: T[] = []) {\n let result = [...initialItems];\n\n if (this._ttl > 0) {\n this._timestamps = new Map();\n const now = Date.now();\n for (const item of result) {\n this._timestamps.set(item.id, now);\n }\n }\n\n if (this._maxSize > 0 && result.length > this._maxSize) {\n // FIFO: trim from the front (oldest items)\n const excess = result.length - this._maxSize;\n const evicted = result.slice(0, excess);\n result = result.slice(excess);\n for (const item of evicted) {\n this._timestamps?.delete(item.id);\n }\n }\n\n this._items = freeze(result);\n this._rebuildIndex();\n this._scheduleEvictionTimer();\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n /**\n * Alias for Subscribable compatibility.\n */\n get state(): T[] {\n return this._items as T[];\n }\n\n /** The raw array of items. */\n get items(): T[] {\n return this._items as T[];\n }\n\n /** Number of items in the collection. */\n get length(): number {\n return this._items.length;\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n // ── Config Accessors ──\n\n private get _maxSize(): number {\n return (this.constructor as typeof Collection).MAX_SIZE;\n }\n\n private get _ttl(): number {\n return (this.constructor as typeof Collection).TTL;\n }\n\n // ── CRUD Methods (notify listeners) ──\n\n /**\n * Add one or more items. Items with existing IDs are silently skipped.\n */\n add(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot add to disposed Collection');\n }\n\n if (items.length === 0) {\n return;\n }\n\n // Fast path: single item (most common case)\n if (items.length === 1) {\n const item = items[0];\n if (this._index.has(item.id)) return;\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-item path\n const seen = new Set<T['id']>();\n const newItems: T[] = [];\n for (const item of items) {\n if (!this._index.has(item.id) && !seen.has(item.id)) {\n newItems.push(item);\n seen.add(item.id);\n }\n }\n if (newItems.length === 0) return;\n\n const prev = this._items;\n let result = [...prev, ...newItems];\n\n for (const item of newItems) {\n this._index.set(item.id, item);\n }\n\n // Record timestamps for TTL\n if (this._timestamps) {\n const now = Date.now();\n for (const item of newItems) {\n this._timestamps.set(item.id, now);\n }\n }\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Add or replace items by ID. Existing items are replaced in-place\n * (preserving array position); new items are appended. Deduplicates\n * input — last occurrence wins. No-op if nothing changed (reference\n * comparison).\n */\n upsert(...items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot upsert on disposed Collection');\n }\n if (items.length === 0) return;\n\n // Fast path: single item (most common case — channel messages, real-time updates)\n if (items.length === 1) {\n const item = items[0];\n const existing = this._index.get(item.id);\n\n if (existing) {\n // Replace in-place — skip if same reference\n if (existing === item) return;\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = item;\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n this._items = freeze(newItems);\n this._notify(prev);\n } else {\n // New item — append (with capacity enforcement)\n const prev = this._items;\n let result = [...prev, item];\n this._index.set(item.id, item);\n if (this._timestamps) this._timestamps.set(item.id, Date.now());\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n return;\n }\n\n // Multi-item path: deduplicate input — last occurrence wins\n const incoming = new Map<T['id'], T>();\n for (const item of items) {\n incoming.set(item.id, item);\n }\n\n const prev = this._items;\n let changed = false;\n const replaced = new Set<T['id']>();\n const newArray: T[] = [];\n\n // Replace existing items in-place\n for (const existing of prev) {\n if (incoming.has(existing.id)) {\n const replacement = incoming.get(existing.id)!;\n if (replacement !== existing) changed = true;\n newArray.push(replacement);\n replaced.add(existing.id);\n } else {\n newArray.push(existing);\n }\n }\n\n // Append genuinely new items\n for (const [id, item] of incoming) {\n if (!replaced.has(id)) {\n newArray.push(item);\n changed = true;\n }\n }\n\n if (!changed) return;\n\n // Record/refresh timestamps for TTL (upsert refreshes existing)\n if (this._timestamps) {\n const now = Date.now();\n for (const [id] of incoming) {\n this._timestamps.set(id, now);\n }\n }\n\n for (const [id, item] of incoming) {\n this._index.set(id, item);\n }\n\n // Enforce capacity before freeze/notify\n let result = newArray;\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove items by id(s).\n */\n remove(...ids: T['id'][]): void {\n if (this._disposed) {\n throw new Error('Cannot remove from disposed Collection');\n }\n\n if (ids.length === 0) {\n return;\n }\n\n // Fast path: single id (most common case)\n if (ids.length === 1) {\n const id = ids[0];\n if (!this._index.has(id)) return;\n const prev = this._items;\n this._items = freeze(prev.filter(item => item.id !== id));\n this._index.delete(id);\n this._timestamps?.delete(id);\n this._notify(prev);\n this._scheduleEvictionTimer();\n return;\n }\n\n // Multi-id path\n const idSet = new Set(ids);\n const filtered = this._items.filter(item => !idSet.has(item.id));\n\n if (filtered.length === this._items.length) {\n return; // No items removed\n }\n\n const prev = this._items;\n this._items = freeze(filtered);\n\n for (const id of ids) {\n this._index.delete(id);\n this._timestamps?.delete(id);\n }\n\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Update an item by id with partial changes.\n */\n update(id: T['id'], changes: Partial<T>): void {\n if (this._disposed) {\n throw new Error('Cannot update disposed Collection');\n }\n\n // O(1) existence check via index Map\n const existing = this._index.get(id);\n if (!existing) return;\n\n // Check if anything actually changed (before any array work)\n const keys = Object.keys(changes) as (keyof T)[];\n const hasChanges = keys.some(key => changes[key] !== existing[key]);\n if (!hasChanges) return;\n\n const updated = { ...existing, ...changes, id };\n const prev = this._items;\n const idx = this._items.indexOf(existing);\n const newItems = [...prev];\n newItems[idx] = updated;\n this._items = freeze(newItems);\n this._index.set(id, updated);\n\n this._notify(prev);\n }\n\n /**\n * Replace all items.\n */\n reset(items: T[]): void {\n if (this._disposed) {\n throw new Error('Cannot reset disposed Collection');\n }\n\n const prev = this._items;\n\n // Record timestamps for TTL\n if (this._timestamps) {\n this._timestamps.clear();\n const now = Date.now();\n for (const item of items) {\n this._timestamps.set(item.id, now);\n }\n }\n\n let result = [...items];\n\n // Enforce capacity before freeze/notify\n if (this._maxSize > 0 && result.length > this._maxSize) {\n result = this._evictForCapacity(result);\n }\n\n this._items = freeze(result);\n this._rebuildIndex();\n\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n /**\n * Remove all items.\n */\n clear(): void {\n if (this._disposed) {\n throw new Error('Cannot clear disposed Collection');\n }\n\n if (this._items.length === 0) {\n return;\n }\n\n const prev = this._items;\n this._items = freeze([] as T[]);\n this._index.clear();\n this._timestamps?.clear();\n this._clearEvictionTimer();\n\n this._notify(prev);\n }\n\n /**\n * Snapshot current state, apply callback mutations, and return a rollback function.\n * Rollback restores items to pre-callback state regardless of later mutations.\n */\n optimistic(callback: () => void): () => void {\n if (this._disposed) {\n throw new Error('Cannot perform optimistic update on disposed Collection');\n }\n\n const snapshot = this._items;\n const timestampSnapshot = this._timestamps ? new Map(this._timestamps) : null;\n callback();\n\n let rolledBack = false;\n return () => {\n if (rolledBack || this._disposed) return;\n rolledBack = true;\n\n const prev = this._items;\n this._items = snapshot;\n if (timestampSnapshot) {\n this._timestamps = timestampSnapshot;\n }\n this._rebuildIndex();\n this._notify(prev);\n this._scheduleEvictionTimer();\n };\n }\n\n // ── Query Methods (pure, no notification) ──\n\n /**\n * Get item by id.\n */\n get(id: T['id']): T | undefined {\n return this._index.get(id);\n }\n\n /**\n * Check if item exists by id.\n */\n has(id: T['id']): boolean {\n return this._index.has(id);\n }\n\n /**\n * Find first item matching predicate.\n */\n find(predicate: (item: T) => boolean): T | undefined {\n return this._items.find(predicate);\n }\n\n /**\n * Filter items matching predicate.\n */\n filter(predicate: (item: T) => boolean): T[] {\n return this._items.filter(predicate) as T[];\n }\n\n /**\n * Return sorted copy.\n */\n sorted(compareFn: (a: T, b: T) => number): T[] {\n return [...this._items].sort(compareFn);\n }\n\n /**\n * Map items to new array.\n */\n map<U>(fn: (item: T) => U): U[] {\n return this._items.map(fn);\n }\n\n // ── Subscribable interface ──\n\n /** Subscribes to state changes. Returns an unsubscribe function. */\n subscribe(listener: CollectionListener<T>): () => void {\n if (this._disposed) {\n return () => {};\n }\n\n this._listeners.add(listener);\n\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n this._clearEvictionTimer();\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this.onDispose?.();\n this._listeners.clear();\n this._index.clear();\n this._timestamps?.clear();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n\n /**\n * Called before items are auto-evicted by capacity or TTL.\n * Override to filter which items get evicted, or veto entirely.\n *\n * @param items - Candidates for eviction\n * @param reason - Why eviction is happening\n * @returns void to proceed with all, false to veto, or T[] subset to evict only those\n */\n protected onEvict?(items: T[], reason: 'capacity' | 'ttl'): T[] | false | void;\n\n private _notify(prev: readonly T[]): void {\n for (const listener of this._listeners) {\n listener(this._items as T[], prev as T[]);\n }\n }\n\n private _rebuildIndex(): void {\n this._index.clear();\n for (const item of this._items) {\n this._index.set(item.id, item);\n }\n }\n\n // ── Eviction Internals ──\n\n private _evictForCapacity(items: T[]): T[] {\n const excess = items.length - this._maxSize;\n if (excess <= 0) return items;\n\n const candidates = items.slice(0, excess);\n const toEvict = this._applyOnEvict(candidates, 'capacity');\n\n if (toEvict === false) return items; // veto\n\n if (toEvict.length === 0) return items; // nothing to evict\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const result = items.filter(item => !evictIds.has(item.id));\n\n // Clean up index and timestamps for evicted items\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps?.delete(item.id);\n }\n\n return result;\n }\n\n private _applyOnEvict(candidates: T[], reason: 'capacity' | 'ttl'): T[] | false {\n if (!this.onEvict) return candidates;\n\n const result = this.onEvict(candidates, reason);\n if (result === false) {\n // DEV warning when veto causes collection to exceed 2x MAX_SIZE\n if (__DEV__ && reason === 'capacity' && this._maxSize > 0) {\n const currentSize = this._items.length + candidates.length;\n if (currentSize > this._maxSize * 2) {\n console.warn(\n `[mvc-kit] Collection exceeded 2x MAX_SIZE (${currentSize}/${this._maxSize}). ` +\n `onEvict is vetoing eviction — this may cause unbounded growth.`\n );\n }\n }\n return false;\n }\n if (Array.isArray(result)) {\n // Only include items that are actually in the current items\n const candidateIds = new Set(candidates.map(c => c.id));\n return result.filter(item => candidateIds.has(item.id));\n }\n return candidates; // void = proceed with all\n }\n\n private _sweepExpired(): void {\n if (this._disposed || !this._timestamps || this._ttl <= 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n const expired: T[] = [];\n\n for (const item of this._items) {\n const ts = this._timestamps.get(item.id);\n if (ts !== undefined && (now - ts) >= ttl) {\n expired.push(item);\n }\n }\n\n if (expired.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const toEvict = this._applyOnEvict(expired, 'ttl');\n\n if (toEvict === false) {\n this._scheduleEvictionTimer();\n return;\n }\n\n if (toEvict.length === 0) {\n this._scheduleEvictionTimer();\n return;\n }\n\n const evictIds = new Set(toEvict.map(item => item.id));\n const prev = this._items;\n this._items = freeze(\n (prev as T[]).filter((item: T) => !evictIds.has(item.id))\n );\n\n for (const item of toEvict) {\n this._index.delete(item.id);\n this._timestamps.delete(item.id);\n }\n\n this._notify(prev);\n this._scheduleEvictionTimer();\n }\n\n private _scheduleEvictionTimer(): void {\n this._clearEvictionTimer();\n\n if (this._disposed || !this._timestamps || this._ttl <= 0 || this._timestamps.size === 0) return;\n\n const now = Date.now();\n const ttl = this._ttl;\n let earliest = Infinity;\n\n for (const ts of this._timestamps.values()) {\n if (ts < earliest) earliest = ts;\n }\n\n const delay = Math.max(0, (earliest + ttl) - now);\n this._evictionTimer = setTimeout(() => this._sweepExpired(), delay);\n }\n\n private _clearEvictionTimer(): void {\n if (this._evictionTimer !== null) {\n clearTimeout(this._evictionTimer);\n this._evictionTimer = null;\n }\n }\n}\n"],"names":["prev","result"],"mappings":"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"}
@@ -1,10 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const bindPublicMethods = require("./bindPublicMethods.cjs");
4
+ const PROTECTED_KEYS = /* @__PURE__ */ new Set(["addCleanup", "subscribeTo", "listenTo"]);
3
5
  class Controller {
4
6
  _disposed = false;
5
7
  _initialized = false;
6
8
  _abortController = null;
7
9
  _cleanups = null;
10
+ constructor() {
11
+ bindPublicMethods.bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);
12
+ }
8
13
  /** Whether this instance has been disposed. */
9
14
  get disposed() {
10
15
  return this._disposed;
@@ -1 +1 @@
1
- {"version":3,"file":"Controller.cjs","sources":["../src/Controller.ts"],"sourcesContent":["import type { Disposable, Subscribable, Listener, EventPayload } from './types';\n\n/**\n * Base class for stateless orchestrators.\n * Controllers coordinate between ViewModels, Models, and Services.\n */\nexport abstract class Controller implements Disposable {\n private _disposed = false;\n private _initialized = false;\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** Whether init() has been called. */\n get initialized(): boolean {\n return this._initialized;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /** Initializes the instance. Called automatically by React hooks after mount. */\n init(): void | Promise<void> {\n if (this._initialized || this._disposed) return;\n this._initialized = true;\n return this.onInit?.();\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this.onDispose?.();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */\n protected subscribeTo<T>(source: Subscribable<T>, listener: Listener<T>): () => void {\n const unsubscribe = source.subscribe(listener);\n this.addCleanup(unsubscribe);\n return unsubscribe;\n }\n\n /** Subscribes to a typed event on a Channel or EventBus with automatic cleanup on dispose. @protected */\n protected listenTo<K extends string, S extends { on(event: K, handler: (payload: any) => void): () => void }>(\n source: S,\n event: K,\n handler: (payload: EventPayload<S, K>) => void,\n ): () => void {\n const unsubscribe = source.on(event, handler);\n this.addCleanup(unsubscribe);\n return unsubscribe;\n }\n\n /** Lifecycle hook called at the end of init(). Override to load initial data. @protected */\n protected onInit?(): void | Promise<void>;\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"names":[],"mappings":";;AAMO,MAAe,WAAiC;AAAA,EAC7C,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,mBAA2C;AAAA,EAC3C,YAAmC;AAAA;AAAA,EAG3C,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,cAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,gBAA6B;AAC/B,QAAI,CAAC,KAAK,kBAAkB;AAC1B,WAAK,mBAAmB,IAAI,gBAAA;AAAA,IAC9B;AACA,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA,EAGA,OAA6B;AAC3B,QAAI,KAAK,gBAAgB,KAAK,UAAW;AACzC,SAAK,eAAe;AACpB,WAAO,KAAK,SAAA;AAAA,EACd;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AAEA,SAAK,YAAY;AACjB,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAA;AAAA,EACP;AAAA;AAAA,EAGU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,CAAA;AAAA,IACnB;AACA,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAAA;AAAA,EAGU,YAAe,QAAyB,UAAmC;AACnF,UAAM,cAAc,OAAO,UAAU,QAAQ;AAC7C,SAAK,WAAW,WAAW;AAC3B,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,SACR,QACA,OACA,SACY;AACZ,UAAM,cAAc,OAAO,GAAG,OAAO,OAAO;AAC5C,SAAK,WAAW,WAAW;AAC3B,WAAO;AAAA,EACT;AAMF;;"}
1
+ {"version":3,"file":"Controller.cjs","sources":["../src/Controller.ts"],"sourcesContent":["import type { Disposable, Subscribable, Listener, EventPayload } from './types';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst PROTECTED_KEYS = new Set(['addCleanup', 'subscribeTo', 'listenTo']);\n\n/**\n * Base class for stateless orchestrators.\n * Controllers coordinate between ViewModels, Models, and Services.\n */\nexport abstract class Controller implements Disposable {\n private _disposed = false;\n private _initialized = false;\n private _abortController: AbortController | null = null;\n private _cleanups: (() => void)[] | null = null;\n\n constructor() {\n bindPublicMethods(this, Object.prototype, PROTECTED_KEYS);\n }\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** Whether init() has been called. */\n get initialized(): boolean {\n return this._initialized;\n }\n\n /** AbortSignal that fires when this instance is disposed. Lazily created. */\n get disposeSignal(): AbortSignal {\n if (!this._abortController) {\n this._abortController = new AbortController();\n }\n return this._abortController.signal;\n }\n\n /** Initializes the instance. Called automatically by React hooks after mount. */\n init(): void | Promise<void> {\n if (this._initialized || this._disposed) return;\n this._initialized = true;\n return this.onInit?.();\n }\n\n /** Tears down the instance, releasing all subscriptions and resources. */\n dispose(): void {\n if (this._disposed) {\n return;\n }\n\n this._disposed = true;\n this._abortController?.abort();\n if (this._cleanups) {\n for (const fn of this._cleanups) fn();\n this._cleanups = null;\n }\n this.onDispose?.();\n }\n\n /** Registers a cleanup function to be called on dispose. @protected */\n protected addCleanup(fn: () => void): void {\n if (!this._cleanups) {\n this._cleanups = [];\n }\n this._cleanups.push(fn);\n }\n\n /** Subscribes to an external Subscribable with automatic cleanup on dispose. @protected */\n protected subscribeTo<T>(source: Subscribable<T>, listener: Listener<T>): () => void {\n const unsubscribe = source.subscribe(listener);\n this.addCleanup(unsubscribe);\n return unsubscribe;\n }\n\n /** Subscribes to a typed event on a Channel or EventBus with automatic cleanup on dispose. @protected */\n protected listenTo<K extends string, S extends { on(event: K, handler: (payload: any) => void): () => void }>(\n source: S,\n event: K,\n handler: (payload: EventPayload<S, K>) => void,\n ): () => void {\n const unsubscribe = source.on(event, handler);\n this.addCleanup(unsubscribe);\n return unsubscribe;\n }\n\n /** Lifecycle hook called at the end of init(). Override to load initial data. @protected */\n protected onInit?(): void | Promise<void>;\n /** Lifecycle hook called during dispose(). Override for custom teardown. @protected */\n protected onDispose?(): void;\n}\n"],"names":["bindPublicMethods"],"mappings":";;;AAGA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,cAAc,eAAe,UAAU,CAAC;AAMjE,MAAe,WAAiC;AAAA,EAC7C,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,mBAA2C;AAAA,EAC3C,YAAmC;AAAA,EAE3C,cAAc;AACZA,sBAAAA,kBAAkB,MAAM,OAAO,WAAW,cAAc;AAAA,EAC1D;AAAA;AAAA,EAGA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,cAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,gBAA6B;AAC/B,QAAI,CAAC,KAAK,kBAAkB;AAC1B,WAAK,mBAAmB,IAAI,gBAAA;AAAA,IAC9B;AACA,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA,EAGA,OAA6B;AAC3B,QAAI,KAAK,gBAAgB,KAAK,UAAW;AACzC,SAAK,eAAe;AACpB,WAAO,KAAK,SAAA;AAAA,EACd;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AAEA,SAAK,YAAY;AACjB,SAAK,kBAAkB,MAAA;AACvB,QAAI,KAAK,WAAW;AAClB,iBAAW,MAAM,KAAK,UAAW,IAAA;AACjC,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAA;AAAA,EACP;AAAA;AAAA,EAGU,WAAW,IAAsB;AACzC,QAAI,CAAC,KAAK,WAAW;AACnB,WAAK,YAAY,CAAA;AAAA,IACnB;AACA,SAAK,UAAU,KAAK,EAAE;AAAA,EACxB;AAAA;AAAA,EAGU,YAAe,QAAyB,UAAmC;AACnF,UAAM,cAAc,OAAO,UAAU,QAAQ;AAC7C,SAAK,WAAW,WAAW;AAC3B,WAAO;AAAA,EACT;AAAA;AAAA,EAGU,SACR,QACA,OACA,SACY;AACZ,UAAM,cAAc,OAAO,GAAG,OAAO,OAAO;AAC5C,SAAK,WAAW,WAAW;AAC3B,WAAO;AAAA,EACT;AAMF;;"}
@@ -8,6 +8,7 @@ export declare abstract class Controller implements Disposable {
8
8
  private _initialized;
9
9
  private _abortController;
10
10
  private _cleanups;
11
+ constructor();
11
12
  /** Whether this instance has been disposed. */
12
13
  get disposed(): boolean;
13
14
  /** Whether init() has been called. */