mvc-kit 2.7.1 → 2.8.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 +18 -1
- package/agent-config/claude-code/skills/guide/SKILL.md +1 -0
- package/agent-config/claude-code/skills/guide/api-reference.md +8 -1
- package/agent-config/claude-code/skills/scaffold/templates/model.md +38 -1
- package/agent-config/copilot/copilot-instructions.md +2 -1
- package/agent-config/cursor/cursorrules +2 -1
- package/dist/Collection.cjs +31 -17
- package/dist/Collection.cjs.map +1 -1
- package/dist/Collection.d.ts.map +1 -1
- package/dist/Collection.js +31 -17
- package/dist/Collection.js.map +1 -1
- package/dist/Model.cjs +22 -4
- package/dist/Model.cjs.map +1 -1
- package/dist/Model.d.ts +2 -0
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +22 -4
- package/dist/Model.js.map +1 -1
- package/dist/PersistentCollection.cjs +8 -10
- package/dist/PersistentCollection.cjs.map +1 -1
- package/dist/PersistentCollection.d.ts +1 -0
- package/dist/PersistentCollection.d.ts.map +1 -1
- package/dist/PersistentCollection.js +8 -10
- package/dist/PersistentCollection.js.map +1 -1
- package/dist/Resource.cjs +20 -156
- package/dist/Resource.cjs.map +1 -1
- package/dist/Resource.d.ts +0 -2
- package/dist/Resource.d.ts.map +1 -1
- package/dist/Resource.js +20 -156
- package/dist/Resource.js.map +1 -1
- package/dist/ViewModel.cjs +177 -227
- package/dist/ViewModel.cjs.map +1 -1
- package/dist/ViewModel.d.ts +9 -12
- package/dist/ViewModel.d.ts.map +1 -1
- package/dist/ViewModel.js +177 -227
- package/dist/ViewModel.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/use-instance.cjs +31 -21
- package/dist/react/use-instance.cjs.map +1 -1
- package/dist/react/use-instance.d.ts +1 -1
- package/dist/react/use-instance.d.ts.map +1 -1
- package/dist/react/use-instance.js +32 -22
- package/dist/react/use-instance.js.map +1 -1
- package/dist/react/use-model.cjs +29 -2
- package/dist/react/use-model.cjs.map +1 -1
- package/dist/react/use-model.d.ts +9 -0
- package/dist/react/use-model.d.ts.map +1 -1
- package/dist/react/use-model.js +30 -3
- package/dist/react/use-model.js.map +1 -1
- package/dist/react.cjs +1 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +2 -1
- package/dist/walkPrototypeChain.cjs.map +1 -1
- package/dist/walkPrototypeChain.d.ts +2 -2
- package/dist/walkPrototypeChain.js.map +1 -1
- package/dist/wrapAsyncMethods.cjs +179 -0
- package/dist/wrapAsyncMethods.cjs.map +1 -0
- package/dist/wrapAsyncMethods.d.ts +35 -0
- package/dist/wrapAsyncMethods.d.ts.map +1 -0
- package/dist/wrapAsyncMethods.js +179 -0
- package/dist/wrapAsyncMethods.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -666,6 +666,22 @@ function UserForm() {
|
|
|
666
666
|
}
|
|
667
667
|
```
|
|
668
668
|
|
|
669
|
+
#### `useModelRef(factory)`
|
|
670
|
+
|
|
671
|
+
Create component-scoped Model with lifecycle management (init + dispose) but **no subscription**. The parent never re-renders from model state changes. Use with `useField` for per-field isolation in large forms.
|
|
672
|
+
|
|
673
|
+
```tsx
|
|
674
|
+
function UserForm() {
|
|
675
|
+
const model = useModelRef(() => new UserModel({ name: '', email: '' }));
|
|
676
|
+
return (
|
|
677
|
+
<form>
|
|
678
|
+
<NameField model={model} />
|
|
679
|
+
<FormActions model={model} />
|
|
680
|
+
</form>
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
669
685
|
#### `useField(model, key)`
|
|
670
686
|
|
|
671
687
|
Subscribe to a single field with surgical re-renders. The returned `set()` calls the Model's `set()` directly — use custom setter methods on the Model for any logic beyond simple assignment.
|
|
@@ -806,6 +822,7 @@ function App() {
|
|
|
806
822
|
| `useLocal(Class \| factory, ...args)` | Component-scoped, auto-disposed, auto-init |
|
|
807
823
|
| `useSingleton(Class, ...args)` | Singleton, registry-managed, auto-init |
|
|
808
824
|
| `useModel(factory)` | Model with validation/dirty state, auto-init |
|
|
825
|
+
| `useModelRef(factory)` | Model lifecycle only (no subscription). For per-field forms. |
|
|
809
826
|
| `useField(model, key)` | Single field subscription |
|
|
810
827
|
| `useEvent(source, event, handler)` | Subscribe to EventBus or ViewModel event |
|
|
811
828
|
| `useEmit(bus)` | Get stable emit function |
|
|
@@ -864,7 +881,7 @@ Each core class and React hook has a dedicated reference doc with full API detai
|
|
|
864
881
|
| [useLocal](src/react/use-local.md) | Component-scoped instance, auto-init/dispose, deps array for recreate |
|
|
865
882
|
| [useInstance](src/react/use-instance.md) | Subscribe to an existing Subscribable (no lifecycle management) |
|
|
866
883
|
| [useSingleton](src/react/use-singleton.md) | Singleton resolution with auto-init and shared state |
|
|
867
|
-
| [useModel & useField](src/react/use-model.md) | Model binding with validation/dirty state; surgical per-field subscriptions |
|
|
884
|
+
| [useModel, useModelRef & useField](src/react/use-model.md) | Model binding with validation/dirty state; lifecycle-only ref; surgical per-field subscriptions |
|
|
868
885
|
| [useEvent & useEmit](src/react/use-event-bus.md) | Subscribe to and emit typed events from EventBus or ViewModel |
|
|
869
886
|
| [useTeardown](src/react/use-teardown.md) | Dispose singleton instances on component unmount |
|
|
870
887
|
|
|
@@ -53,6 +53,7 @@ Private fields → Computed getters → Lifecycle → Actions → Setters
|
|
|
53
53
|
| `useSingleton(Class, ...args)` | Singleton instance, shared state |
|
|
54
54
|
| `useInstance(subscribable)` | Subscribe to existing instance |
|
|
55
55
|
| `useModel(factory)` | Model with validation/dirty state |
|
|
56
|
+
| `useModelRef(factory)` | Model lifecycle only, no subscription. For per-field forms. |
|
|
56
57
|
| `useField(model, key)` | Single field subscription |
|
|
57
58
|
| `useEvent(source, event, handler)` | Subscribe to EventBus or ViewModel event |
|
|
58
59
|
| `useEmit(bus)` | Stable emit function |
|
|
@@ -10,7 +10,7 @@ import { HttpError, isAbortError, classifyError } from 'mvc-kit';
|
|
|
10
10
|
import type { Subscribable, Disposable, Initializable, Listener, Updater, ValidationErrors, TaskState, AppError, AsyncMethodKeys, ResourceAsyncMethodKeys, ChannelStatus } from 'mvc-kit';
|
|
11
11
|
|
|
12
12
|
// React hooks
|
|
13
|
-
import { useLocal, useSingleton, useInstance, useModel, useField, useEvent, useEmit, useResolve, useTeardown, Provider } from 'mvc-kit/react';
|
|
13
|
+
import { useLocal, useSingleton, useInstance, useModel, useModelRef, useField, useEvent, useEmit, useResolve, useTeardown, Provider } from 'mvc-kit/react';
|
|
14
14
|
import type { StateOf, ItemOf, SingletonClass, ProviderRegistry, ModelHandle, FieldHandle, ProviderProps } from 'mvc-kit/react';
|
|
15
15
|
|
|
16
16
|
// Web storage adapters
|
|
@@ -343,6 +343,13 @@ Returns `ModelHandle: { state, errors, valid, dirty, model }`.
|
|
|
343
343
|
const { state, errors, valid, dirty, model } = useModel(() => new UserModel({ name: '' }));
|
|
344
344
|
```
|
|
345
345
|
|
|
346
|
+
### useModelRef(factory)
|
|
347
|
+
Lifecycle-only (init + dispose), no subscription. Returns `M` directly. Use with `useField` for per-field isolation in large forms.
|
|
348
|
+
```tsx
|
|
349
|
+
const model = useModelRef(() => new FormModel({ name: '', email: '' }));
|
|
350
|
+
// Pass model to children that use useField — parent never re-renders from model changes.
|
|
351
|
+
```
|
|
352
|
+
|
|
346
353
|
### useField(model, key)
|
|
347
354
|
Returns `FieldHandle: { value, error, set }`. Surgical re-renders.
|
|
348
355
|
```tsx
|
|
@@ -79,8 +79,10 @@ describe('{{Name}}Model', () => {
|
|
|
79
79
|
|
|
80
80
|
## React Usage
|
|
81
81
|
|
|
82
|
+
### Simple form (useModel)
|
|
83
|
+
|
|
82
84
|
```tsx
|
|
83
|
-
import { useModel
|
|
85
|
+
import { useModel } from 'mvc-kit/react';
|
|
84
86
|
import { {{Name}}Model } from '../models/{{Name}}Model';
|
|
85
87
|
|
|
86
88
|
function {{Name}}Form() {
|
|
@@ -100,3 +102,38 @@ function {{Name}}Form() {
|
|
|
100
102
|
);
|
|
101
103
|
}
|
|
102
104
|
```
|
|
105
|
+
|
|
106
|
+
### Large form with per-field isolation (useModelRef + useField)
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { useModelRef, useField, useInstance } from 'mvc-kit/react';
|
|
110
|
+
import { {{Name}}Model } from '../models/{{Name}}Model';
|
|
111
|
+
|
|
112
|
+
// Parent — creates model, never re-renders from field changes
|
|
113
|
+
function {{Name}}Form() {
|
|
114
|
+
const model = useModelRef(() => new {{Name}}Model({ name: '' }));
|
|
115
|
+
return (
|
|
116
|
+
<form>
|
|
117
|
+
<NameField model={model} />
|
|
118
|
+
<FormActions model={model} />
|
|
119
|
+
</form>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Per-field child — only re-renders when this field changes
|
|
124
|
+
function NameField({ model }: { model: {{Name}}Model }) {
|
|
125
|
+
const { value, error, set } = useField(model, 'name');
|
|
126
|
+
return (
|
|
127
|
+
<div>
|
|
128
|
+
<input value={value} onChange={e => set(e.target.value)} />
|
|
129
|
+
{error && <span className="error">{error}</span>}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Submit button — subscribes to full model for valid/dirty
|
|
135
|
+
function FormActions({ model }: { model: {{Name}}Model }) {
|
|
136
|
+
useInstance(model);
|
|
137
|
+
return <button disabled={!model.valid || !model.dirty}>Save</button>;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
@@ -21,7 +21,7 @@ This project uses **mvc-kit**, a zero-dependency TypeScript-first reactive state
|
|
|
21
21
|
```typescript
|
|
22
22
|
import { ViewModel, Model, Collection, PersistentCollection, Resource, Controller, Service, EventBus, Channel } from 'mvc-kit';
|
|
23
23
|
import { singleton, teardownAll, HttpError, isAbortError, classifyError } from 'mvc-kit';
|
|
24
|
-
import { useLocal, useSingleton, useInstance, useModel, useField, useEvent, useEmit, useResolve, useTeardown, Provider } from 'mvc-kit/react';
|
|
24
|
+
import { useLocal, useSingleton, useInstance, useModel, useModelRef, useField, useEvent, useEmit, useResolve, useTeardown, Provider } from 'mvc-kit/react';
|
|
25
25
|
import { WebStorageCollection, IndexedDBCollection } from 'mvc-kit/web';
|
|
26
26
|
import { NativeCollection } from 'mvc-kit/react-native';
|
|
27
27
|
```
|
|
@@ -110,6 +110,7 @@ function ItemsPage() {
|
|
|
110
110
|
| `useSingleton(Class, ...args)` | Singleton, shared state. Returns `[state, instance]`. |
|
|
111
111
|
| `useInstance(subscribable)` | Subscribe to existing instance. Returns `state`. |
|
|
112
112
|
| `useModel(factory)` | Model with `{ state, errors, valid, dirty, model }`. |
|
|
113
|
+
| `useModelRef(factory)` | Model lifecycle only (no subscription). Returns `M`. For per-field forms. |
|
|
113
114
|
| `useField(model, key)` | Single field: `{ value, error, set }`. |
|
|
114
115
|
| `useEvent(source, event, handler)` | EventBus/ViewModel event subscription. |
|
|
115
116
|
| `useEmit(bus)` | Stable emit function. |
|
|
@@ -21,7 +21,7 @@ This project uses **mvc-kit**, a zero-dependency TypeScript-first reactive state
|
|
|
21
21
|
```typescript
|
|
22
22
|
import { ViewModel, Model, Collection, PersistentCollection, Resource, Controller, Service, EventBus, Channel } from 'mvc-kit';
|
|
23
23
|
import { singleton, teardownAll, HttpError, isAbortError, classifyError } from 'mvc-kit';
|
|
24
|
-
import { useLocal, useSingleton, useInstance, useModel, useField, useEvent, useEmit, useResolve, useTeardown, Provider } from 'mvc-kit/react';
|
|
24
|
+
import { useLocal, useSingleton, useInstance, useModel, useModelRef, useField, useEvent, useEmit, useResolve, useTeardown, Provider } from 'mvc-kit/react';
|
|
25
25
|
import { WebStorageCollection, IndexedDBCollection } from 'mvc-kit/web';
|
|
26
26
|
import { NativeCollection } from 'mvc-kit/react-native';
|
|
27
27
|
```
|
|
@@ -110,6 +110,7 @@ function ItemsPage() {
|
|
|
110
110
|
| `useSingleton(Class, ...args)` | Singleton, shared state. Returns `[state, instance]`. |
|
|
111
111
|
| `useInstance(subscribable)` | Subscribe to existing instance. Returns `state`. |
|
|
112
112
|
| `useModel(factory)` | Model with `{ state, errors, valid, dirty, model }`. |
|
|
113
|
+
| `useModelRef(factory)` | Model lifecycle only (no subscription). Returns `M`. For per-field forms. |
|
|
113
114
|
| `useField(model, key)` | Single field: `{ value, error, set }`. |
|
|
114
115
|
| `useEvent(source, event, handler)` | EventBus/ViewModel event subscription. |
|
|
115
116
|
| `useEmit(bus)` | Stable emit function. |
|
package/dist/Collection.cjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
|
|
4
|
+
function freeze(obj) {
|
|
5
|
+
return __DEV__ ? Object.freeze(obj) : obj;
|
|
6
|
+
}
|
|
4
7
|
class Collection {
|
|
5
8
|
/** Maximum number of items before FIFO eviction. 0 = unlimited. */
|
|
6
9
|
static MAX_SIZE = 0;
|
|
@@ -31,7 +34,7 @@ class Collection {
|
|
|
31
34
|
this._timestamps?.delete(item.id);
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
|
-
this._items =
|
|
37
|
+
this._items = freeze(result);
|
|
35
38
|
this.rebuildIndex();
|
|
36
39
|
this._scheduleEvictionTimer();
|
|
37
40
|
}
|
|
@@ -78,6 +81,21 @@ class Collection {
|
|
|
78
81
|
if (items.length === 0) {
|
|
79
82
|
return;
|
|
80
83
|
}
|
|
84
|
+
if (items.length === 1) {
|
|
85
|
+
const item = items[0];
|
|
86
|
+
if (this._index.has(item.id)) return;
|
|
87
|
+
const prev2 = this._items;
|
|
88
|
+
let result2 = [...prev2, item];
|
|
89
|
+
this._index.set(item.id, item);
|
|
90
|
+
if (this._timestamps) this._timestamps.set(item.id, Date.now());
|
|
91
|
+
if (this._maxSize > 0 && result2.length > this._maxSize) {
|
|
92
|
+
result2 = this._evictForCapacity(result2);
|
|
93
|
+
}
|
|
94
|
+
this._items = freeze(result2);
|
|
95
|
+
this.notify(prev2);
|
|
96
|
+
this._scheduleEvictionTimer();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
81
99
|
const seen = /* @__PURE__ */ new Set();
|
|
82
100
|
const newItems = [];
|
|
83
101
|
for (const item of items) {
|
|
@@ -101,7 +119,7 @@ class Collection {
|
|
|
101
119
|
if (this._maxSize > 0 && result.length > this._maxSize) {
|
|
102
120
|
result = this._evictForCapacity(result);
|
|
103
121
|
}
|
|
104
|
-
this._items =
|
|
122
|
+
this._items = freeze(result);
|
|
105
123
|
this.notify(prev);
|
|
106
124
|
this._scheduleEvictionTimer();
|
|
107
125
|
}
|
|
@@ -154,7 +172,7 @@ class Collection {
|
|
|
154
172
|
if (this._maxSize > 0 && result.length > this._maxSize) {
|
|
155
173
|
result = this._evictForCapacity(result);
|
|
156
174
|
}
|
|
157
|
-
this._items =
|
|
175
|
+
this._items = freeze(result);
|
|
158
176
|
this.notify(prev);
|
|
159
177
|
this._scheduleEvictionTimer();
|
|
160
178
|
}
|
|
@@ -174,7 +192,7 @@ class Collection {
|
|
|
174
192
|
return;
|
|
175
193
|
}
|
|
176
194
|
const prev = this._items;
|
|
177
|
-
this._items =
|
|
195
|
+
this._items = freeze(filtered);
|
|
178
196
|
for (const id of ids) {
|
|
179
197
|
this._index.delete(id);
|
|
180
198
|
this._timestamps?.delete(id);
|
|
@@ -189,21 +207,17 @@ class Collection {
|
|
|
189
207
|
if (this._disposed) {
|
|
190
208
|
throw new Error("Cannot update disposed Collection");
|
|
191
209
|
}
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
const existing = this._items[idx];
|
|
197
|
-
const updated = { ...existing, ...changes, id };
|
|
210
|
+
const existing = this._index.get(id);
|
|
211
|
+
if (!existing) return;
|
|
198
212
|
const keys = Object.keys(changes);
|
|
199
213
|
const hasChanges = keys.some((key) => changes[key] !== existing[key]);
|
|
200
|
-
if (!hasChanges)
|
|
201
|
-
|
|
202
|
-
}
|
|
214
|
+
if (!hasChanges) return;
|
|
215
|
+
const updated = { ...existing, ...changes, id };
|
|
203
216
|
const prev = this._items;
|
|
217
|
+
const idx = this._items.indexOf(existing);
|
|
204
218
|
const newItems = [...prev];
|
|
205
219
|
newItems[idx] = updated;
|
|
206
|
-
this._items =
|
|
220
|
+
this._items = freeze(newItems);
|
|
207
221
|
this._index.set(id, updated);
|
|
208
222
|
this.notify(prev);
|
|
209
223
|
}
|
|
@@ -226,7 +240,7 @@ class Collection {
|
|
|
226
240
|
if (this._maxSize > 0 && result.length > this._maxSize) {
|
|
227
241
|
result = this._evictForCapacity(result);
|
|
228
242
|
}
|
|
229
|
-
this._items =
|
|
243
|
+
this._items = freeze(result);
|
|
230
244
|
this.rebuildIndex();
|
|
231
245
|
this.notify(prev);
|
|
232
246
|
this._scheduleEvictionTimer();
|
|
@@ -242,7 +256,7 @@ class Collection {
|
|
|
242
256
|
return;
|
|
243
257
|
}
|
|
244
258
|
const prev = this._items;
|
|
245
|
-
this._items =
|
|
259
|
+
this._items = freeze([]);
|
|
246
260
|
this._index.clear();
|
|
247
261
|
this._timestamps?.clear();
|
|
248
262
|
this._clearEvictionTimer();
|
|
@@ -419,7 +433,7 @@ class Collection {
|
|
|
419
433
|
}
|
|
420
434
|
const evictIds = new Set(toEvict.map((item) => item.id));
|
|
421
435
|
const prev = this._items;
|
|
422
|
-
this._items =
|
|
436
|
+
this._items = freeze(
|
|
423
437
|
prev.filter((item) => !evictIds.has(item.id))
|
|
424
438
|
);
|
|
425
439
|
for (const item of toEvict) {
|
package/dist/Collection.cjs.map
CHANGED
|
@@ -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\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 = Object.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 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 = Object.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 // 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 = Object.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 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 = Object.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 const idx = this._items.findIndex(item => item.id === id);\n if (idx === -1) {\n return;\n }\n\n const existing = this._items[idx];\n const updated = { ...existing, ...changes, id }; // Ensure id is preserved\n\n // Check if anything actually changed\n const keys = Object.keys(changes) as (keyof T)[];\n const hasChanges = keys.some(key => changes[key] !== existing[key]);\n if (!hasChanges) {\n return;\n }\n\n const prev = this._items;\n const newItems = [...prev];\n newItems[idx] = updated;\n this._items = Object.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 = Object.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 = Object.freeze([]);\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 = Object.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":[],"mappings":"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"}
|
|
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 // 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 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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"}
|
package/dist/Collection.d.ts.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"Collection.d.ts","sourceRoot":"","sources":["../src/Collection.ts"],"names":[],"mappings":"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"}
|
package/dist/Collection.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
|
|
2
|
+
function freeze(obj) {
|
|
3
|
+
return __DEV__ ? Object.freeze(obj) : obj;
|
|
4
|
+
}
|
|
2
5
|
class Collection {
|
|
3
6
|
/** Maximum number of items before FIFO eviction. 0 = unlimited. */
|
|
4
7
|
static MAX_SIZE = 0;
|
|
@@ -29,7 +32,7 @@ class Collection {
|
|
|
29
32
|
this._timestamps?.delete(item.id);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
|
-
this._items =
|
|
35
|
+
this._items = freeze(result);
|
|
33
36
|
this.rebuildIndex();
|
|
34
37
|
this._scheduleEvictionTimer();
|
|
35
38
|
}
|
|
@@ -76,6 +79,21 @@ class Collection {
|
|
|
76
79
|
if (items.length === 0) {
|
|
77
80
|
return;
|
|
78
81
|
}
|
|
82
|
+
if (items.length === 1) {
|
|
83
|
+
const item = items[0];
|
|
84
|
+
if (this._index.has(item.id)) return;
|
|
85
|
+
const prev2 = this._items;
|
|
86
|
+
let result2 = [...prev2, item];
|
|
87
|
+
this._index.set(item.id, item);
|
|
88
|
+
if (this._timestamps) this._timestamps.set(item.id, Date.now());
|
|
89
|
+
if (this._maxSize > 0 && result2.length > this._maxSize) {
|
|
90
|
+
result2 = this._evictForCapacity(result2);
|
|
91
|
+
}
|
|
92
|
+
this._items = freeze(result2);
|
|
93
|
+
this.notify(prev2);
|
|
94
|
+
this._scheduleEvictionTimer();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
79
97
|
const seen = /* @__PURE__ */ new Set();
|
|
80
98
|
const newItems = [];
|
|
81
99
|
for (const item of items) {
|
|
@@ -99,7 +117,7 @@ class Collection {
|
|
|
99
117
|
if (this._maxSize > 0 && result.length > this._maxSize) {
|
|
100
118
|
result = this._evictForCapacity(result);
|
|
101
119
|
}
|
|
102
|
-
this._items =
|
|
120
|
+
this._items = freeze(result);
|
|
103
121
|
this.notify(prev);
|
|
104
122
|
this._scheduleEvictionTimer();
|
|
105
123
|
}
|
|
@@ -152,7 +170,7 @@ class Collection {
|
|
|
152
170
|
if (this._maxSize > 0 && result.length > this._maxSize) {
|
|
153
171
|
result = this._evictForCapacity(result);
|
|
154
172
|
}
|
|
155
|
-
this._items =
|
|
173
|
+
this._items = freeze(result);
|
|
156
174
|
this.notify(prev);
|
|
157
175
|
this._scheduleEvictionTimer();
|
|
158
176
|
}
|
|
@@ -172,7 +190,7 @@ class Collection {
|
|
|
172
190
|
return;
|
|
173
191
|
}
|
|
174
192
|
const prev = this._items;
|
|
175
|
-
this._items =
|
|
193
|
+
this._items = freeze(filtered);
|
|
176
194
|
for (const id of ids) {
|
|
177
195
|
this._index.delete(id);
|
|
178
196
|
this._timestamps?.delete(id);
|
|
@@ -187,21 +205,17 @@ class Collection {
|
|
|
187
205
|
if (this._disposed) {
|
|
188
206
|
throw new Error("Cannot update disposed Collection");
|
|
189
207
|
}
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
const existing = this._items[idx];
|
|
195
|
-
const updated = { ...existing, ...changes, id };
|
|
208
|
+
const existing = this._index.get(id);
|
|
209
|
+
if (!existing) return;
|
|
196
210
|
const keys = Object.keys(changes);
|
|
197
211
|
const hasChanges = keys.some((key) => changes[key] !== existing[key]);
|
|
198
|
-
if (!hasChanges)
|
|
199
|
-
|
|
200
|
-
}
|
|
212
|
+
if (!hasChanges) return;
|
|
213
|
+
const updated = { ...existing, ...changes, id };
|
|
201
214
|
const prev = this._items;
|
|
215
|
+
const idx = this._items.indexOf(existing);
|
|
202
216
|
const newItems = [...prev];
|
|
203
217
|
newItems[idx] = updated;
|
|
204
|
-
this._items =
|
|
218
|
+
this._items = freeze(newItems);
|
|
205
219
|
this._index.set(id, updated);
|
|
206
220
|
this.notify(prev);
|
|
207
221
|
}
|
|
@@ -224,7 +238,7 @@ class Collection {
|
|
|
224
238
|
if (this._maxSize > 0 && result.length > this._maxSize) {
|
|
225
239
|
result = this._evictForCapacity(result);
|
|
226
240
|
}
|
|
227
|
-
this._items =
|
|
241
|
+
this._items = freeze(result);
|
|
228
242
|
this.rebuildIndex();
|
|
229
243
|
this.notify(prev);
|
|
230
244
|
this._scheduleEvictionTimer();
|
|
@@ -240,7 +254,7 @@ class Collection {
|
|
|
240
254
|
return;
|
|
241
255
|
}
|
|
242
256
|
const prev = this._items;
|
|
243
|
-
this._items =
|
|
257
|
+
this._items = freeze([]);
|
|
244
258
|
this._index.clear();
|
|
245
259
|
this._timestamps?.clear();
|
|
246
260
|
this._clearEvictionTimer();
|
|
@@ -417,7 +431,7 @@ class Collection {
|
|
|
417
431
|
}
|
|
418
432
|
const evictIds = new Set(toEvict.map((item) => item.id));
|
|
419
433
|
const prev = this._items;
|
|
420
|
-
this._items =
|
|
434
|
+
this._items = freeze(
|
|
421
435
|
prev.filter((item) => !evictIds.has(item.id))
|
|
422
436
|
);
|
|
423
437
|
for (const item of toEvict) {
|