@oimdb/core 1.5.0 → 2.0.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 +572 -68
- package/dist/index.cjs +1849 -1535
- package/dist/index.d.cts +587 -507
- package/dist/index.d.ts +587 -507
- package/dist/index.js +1833 -1530
- package/llms.txt +429 -0
- package/package.json +4 -2
package/llms.txt
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# OIMDB
|
|
2
|
+
|
|
3
|
+
> **For AI assistants:** You are reading the OIMDB API reference. Read this file fully before writing any OIMDB code — the API has specific patterns that are easy to get wrong without context.
|
|
4
|
+
> After reading, look for `debug.ts` or `*.debug.ts` files in the project — they describe the specific data model (collections, indexes, relations, computeds) of the app you are working on.
|
|
5
|
+
> If `@oimdb/mcp` is in the project's MCP config, call `oimdb_inspect` for live runtime state from the browser.
|
|
6
|
+
|
|
7
|
+
OIMDB is a TypeScript in-memory reactive database. It stores entities in typed collections, maintains indexes, and propagates changes through a reactive graph (computed values, effects, selectors) controlled by an explicit event queue. All change delivery is batched: subscriptions fire on `queue.flush()`, never mid-mutation.
|
|
8
|
+
|
|
9
|
+
## Packages
|
|
10
|
+
|
|
11
|
+
| Package | Purpose |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `@oimdb/core` | Collections, indexes, event queue, reactive primitives |
|
|
14
|
+
| `@oimdb/persist` | Persist/hydrate collections to localStorage, IndexedDB, memory |
|
|
15
|
+
| `@oimdb/react` | React hooks for collections and selectors |
|
|
16
|
+
| `@oimdb/async` | Async-aware reactive collection and index variants |
|
|
17
|
+
| `@oimdb/devtools` | Debug registry: name collections/indexes/computeds, inspect runtime state |
|
|
18
|
+
| `@oimdb/snapshot-manager` | Snapshot/restore collection state |
|
|
19
|
+
| `@oimdb/redux-adapter` | Bridge OIMDB collections into a Redux store |
|
|
20
|
+
|
|
21
|
+
## Core Concepts
|
|
22
|
+
|
|
23
|
+
### Event Queue
|
|
24
|
+
|
|
25
|
+
All reactive updates are batched. Mutations accumulate, then `queue.flush()` delivers all changes at once.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { OIMEventQueue, OIMEventQueueSchedulerImmediate } from '@oimdb/core';
|
|
29
|
+
|
|
30
|
+
const queue = new OIMEventQueue({
|
|
31
|
+
scheduler: new OIMEventQueueSchedulerImmediate(), // flush on next microtask
|
|
32
|
+
});
|
|
33
|
+
// or: OIMEventQueueSchedulerAnimationFrame, OIMEventQueueSchedulerTimeout, OIMEventQueueSchedulerMicrotask
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Collections
|
|
37
|
+
|
|
38
|
+
`OIMCollection` — base CRUD store. `OIMReactiveCollection` — same + emits events on flush.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { OIMReactiveCollection } from '@oimdb/core';
|
|
42
|
+
|
|
43
|
+
interface User { id: string; name: string; role: 'admin' | 'member'; }
|
|
44
|
+
|
|
45
|
+
const users = new OIMReactiveCollection<User, string>(queue, {
|
|
46
|
+
selectPk: (u) => u.id,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
users.upsertMany([
|
|
50
|
+
{ id: 'u1', name: 'Alice', role: 'admin' },
|
|
51
|
+
{ id: 'u2', name: 'Bob', role: 'member' },
|
|
52
|
+
]);
|
|
53
|
+
users.upsertOne({ id: 'u1', name: 'Alice Smith', role: 'admin' }); // update
|
|
54
|
+
users.removeOne({ id: 'u2', name: 'Bob', role: 'member' });
|
|
55
|
+
|
|
56
|
+
users.getOneByPk('u1'); // → entity or undefined
|
|
57
|
+
users.getAll(); // → TEntity[]
|
|
58
|
+
users.getAllPks(); // → TPk[]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Indexes
|
|
62
|
+
|
|
63
|
+
Indexes map keys to sets or arrays of PKs. Two storage shapes:
|
|
64
|
+
- **Set-based** — unordered, deduped (`OIMIndexManualSetBased`, `OIMReactiveIndexManualSetBased`)
|
|
65
|
+
- **Array-based** — ordered, with duplicates (`OIMIndexManualArrayBased`, `OIMReactiveIndexManualArrayBased`)
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { OIMReactiveIndexManualSetBased } from '@oimdb/core';
|
|
69
|
+
|
|
70
|
+
const byRole = new OIMReactiveIndexManualSetBased<'admin' | 'member', string>(queue);
|
|
71
|
+
|
|
72
|
+
byRole.setSlots('admin', [users.getSlotByPk('u1')!]);
|
|
73
|
+
byRole.getPksByKey('admin'); // → Set { 'u1' }
|
|
74
|
+
|
|
75
|
+
byRole.subscribeOnKey('admin', () => {
|
|
76
|
+
console.log('admin set changed');
|
|
77
|
+
});
|
|
78
|
+
queue.flush(); // delivers subscription
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### DX Layer — Collection Model
|
|
82
|
+
|
|
83
|
+
`createOIMCollectionContext` wires queue + collection + index factory + selectors together.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { createOIMCollectionContext } from '@oimdb/core';
|
|
87
|
+
|
|
88
|
+
const { collection, indexFactory, select } =
|
|
89
|
+
createOIMCollectionContext<User, string>(queue, { selectPk: (u) => u.id });
|
|
90
|
+
|
|
91
|
+
// Derived index: built automatically from entity data
|
|
92
|
+
const byRole = indexFactory.derivedSetIndex((u) => [u.role]);
|
|
93
|
+
|
|
94
|
+
// Reactive selectors
|
|
95
|
+
const adminUsers = select.entitiesBySetIndexKey(byRole, 'admin');
|
|
96
|
+
adminUsers.getValue(); // → (User | undefined)[]
|
|
97
|
+
adminUsers.watch((users) => { /* called on queue.flush() when value changes */ });
|
|
98
|
+
|
|
99
|
+
const u1 = select.byPk('u1');
|
|
100
|
+
u1.getValue(); // → User | undefined
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Computed Values
|
|
104
|
+
|
|
105
|
+
Derived values that recompute lazily when dependencies change. Recomputation is scheduled on `queue.flush()`.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { OIMComputeRuntime, OIMComputed, OIMEffectDependencyKeyedCollection } from '@oimdb/core';
|
|
109
|
+
|
|
110
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
111
|
+
|
|
112
|
+
const adminCount = new OIMComputed<number>(runtime, {
|
|
113
|
+
compute: () => byRole.getPksByKey('admin').size,
|
|
114
|
+
deps: [new OIMEffectDependencyKeyedCollection(users, /* pk */ 'u1')],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
adminCount.get(); // → number (triggers recompute if dirty)
|
|
118
|
+
adminCount.getIfReady(); // → number | undefined (no recompute)
|
|
119
|
+
adminCount.isReady; // → boolean
|
|
120
|
+
adminCount.needsRecompute; // → boolean
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Effects
|
|
124
|
+
|
|
125
|
+
Side effects that run when dependencies change, on `queue.flush()`.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { OIMEffect, OIMEffectDependencyComputed } from '@oimdb/core';
|
|
129
|
+
|
|
130
|
+
const logEffect = new OIMEffect(runtime, {
|
|
131
|
+
deps: [new OIMEffectDependencyComputed(adminCount)],
|
|
132
|
+
run: () => {
|
|
133
|
+
console.log('admin count:', adminCount.get());
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Dependency types:**
|
|
139
|
+
- `OIMEffectDependencyKeyedCollection(collection, pk)` — watch a specific PK
|
|
140
|
+
- `OIMEffectDependencyKeyedObject(object, key)` — watch a key in a reactive object
|
|
141
|
+
- `OIMEffectDependencyKeyedIndex(index, key)` — watch a key in an index
|
|
142
|
+
- `OIMEffectDependencyComputed(computed)` — depend on another computed
|
|
143
|
+
|
|
144
|
+
### Objects
|
|
145
|
+
|
|
146
|
+
Key-value stores for singleton data (settings, flags, etc.).
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { OIMReactiveObject } from '@oimdb/core';
|
|
150
|
+
|
|
151
|
+
const settings = new OIMReactiveObject<'theme' | 'lang', string>(queue);
|
|
152
|
+
settings.setProperty('theme', 'dark');
|
|
153
|
+
settings.get('theme'); // → 'dark' | undefined
|
|
154
|
+
settings.getAll(); // → { theme: 'dark' }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## React (`@oimdb/react`)
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm install @oimdb/react
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
All hooks use `useSyncExternalStore` and re-render only when watched data changes.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import {
|
|
167
|
+
OIMCollectionsProvider,
|
|
168
|
+
useOIMCollectionsContext,
|
|
169
|
+
useSelectEntityByPk,
|
|
170
|
+
useSelectEntitiesByPks,
|
|
171
|
+
useSelectEntitiesByIndexKeySetBased,
|
|
172
|
+
useSelectEntitiesByIndexKeyArrayBased,
|
|
173
|
+
useSelectPksByIndexKeySetBased,
|
|
174
|
+
useSelectValueByObjectKey,
|
|
175
|
+
} from '@oimdb/react';
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Setup
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
// store.ts — outside React
|
|
182
|
+
const queue = new OIMEventQueue();
|
|
183
|
+
const { collection: users, indexFactory } =
|
|
184
|
+
createOIMCollectionContext<User, string>(queue, { selectPk: (u) => u.id });
|
|
185
|
+
const byTeam = indexFactory.derivedSetIndex((u) => [u.teamId]);
|
|
186
|
+
export const collections = { users };
|
|
187
|
+
|
|
188
|
+
// App.tsx
|
|
189
|
+
<OIMCollectionsProvider collections={collections}>
|
|
190
|
+
<App />
|
|
191
|
+
</OIMCollectionsProvider>
|
|
192
|
+
|
|
193
|
+
// Component.tsx
|
|
194
|
+
type AppCollections = typeof collections;
|
|
195
|
+
const { users } = useOIMCollectionsContext<AppCollections>();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Hook reference
|
|
199
|
+
|
|
200
|
+
| Hook | What it watches |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `useSelectEntityByPk(collection, pk)` | One entity by PK |
|
|
203
|
+
| `useSelectEntitiesByPks(collection, pks)` | Multiple entities by PKs |
|
|
204
|
+
| `useSelectEntitiesByIndexKeySetBased(collection, index, key)` | Entities via set index key |
|
|
205
|
+
| `useSelectEntitiesByIndexKeysSetBased(collection, index, keys)` | Entities via multiple set index keys |
|
|
206
|
+
| `useSelectEntitiesByIndexKeyArrayBased(collection, index, key)` | Entities via array index key (ordered) |
|
|
207
|
+
| `useSelectEntitiesByIndexKeysArrayBased(collection, index, keys)` | Entities via multiple array index keys |
|
|
208
|
+
| `useSelectPksByIndexKeySetBased(index, key)` | Just PKs from set index (no collection needed) |
|
|
209
|
+
| `useSelectPksByIndexKeyArrayBased(index, key)` | Just PKs from array index |
|
|
210
|
+
| `useSelectValueByObjectKey(object, key)` | One key from `OIMReactiveObject` |
|
|
211
|
+
| `useSelectValuesByObjectKeys(object, keys)` | Multiple keys from `OIMReactiveObject` |
|
|
212
|
+
|
|
213
|
+
## Persist (`@oimdb/persist`)
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { createLocalStoragePersistor } from '@oimdb/persist';
|
|
217
|
+
|
|
218
|
+
const persistor = createLocalStoragePersistor({});
|
|
219
|
+
|
|
220
|
+
// Persist entire collection as one key
|
|
221
|
+
persistor.collection(users).entry({ storageKey: 'app:users' });
|
|
222
|
+
|
|
223
|
+
// Persist at a nested path within one key
|
|
224
|
+
persistor.collection(users).path({ storageKey: 'app', path: ['collections', 'users'] });
|
|
225
|
+
persistor.object(settings).path({ storageKey: 'app', path: ['settings'] });
|
|
226
|
+
|
|
227
|
+
// Persist each record separately (good for large collections)
|
|
228
|
+
persistor.collection(users).records({ bucketName: 'users' }); // memory/indexedDb only
|
|
229
|
+
|
|
230
|
+
await persistor.hydrate(); // load from storage into collections
|
|
231
|
+
persistor.start(); // autosave on every queue.flush()
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Error handling
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const persistor = createLocalStoragePersistor({
|
|
238
|
+
onError: (error, { resource, operation }) => {
|
|
239
|
+
console.error(`Persist ${operation} failed`, error);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Schema versioning / migration
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { createVersionedCodec } from '@oimdb/persist';
|
|
248
|
+
|
|
249
|
+
const codec = createVersionedCodec({
|
|
250
|
+
version: 2,
|
|
251
|
+
migrations: {
|
|
252
|
+
1: (data) => ({ ...data, newField: 'default' }), // v0 → v1
|
|
253
|
+
2: (data) => renameField(data), // v1 → v2
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
persistor.collection(users).entry({ storageKey: 'users', codec });
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Devtools (`@oimdb/devtools`)
|
|
261
|
+
|
|
262
|
+
Register collections and computeds by name. Keep registration in separate `*.debug.ts` files — excluded from production.
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// src/store/users.debug.ts
|
|
266
|
+
import { registry } from '@oimdb/devtools';
|
|
267
|
+
import { usersCollection, byRole } from './users';
|
|
268
|
+
|
|
269
|
+
registry.collection('users', usersCollection, {
|
|
270
|
+
indexes: { byRole },
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
// src/store/tasks.debug.ts
|
|
276
|
+
import { registry } from '@oimdb/devtools';
|
|
277
|
+
import { tasksCollection, byAssignee, openTaskCount } from './tasks';
|
|
278
|
+
|
|
279
|
+
registry
|
|
280
|
+
.collection('tasks', tasksCollection, {
|
|
281
|
+
indexes: { byAssignee },
|
|
282
|
+
relations: { assigneeId: 'users' }, // FK: tasks.assigneeId → users
|
|
283
|
+
})
|
|
284
|
+
.computed('openTaskCount', openTaskCount);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
// src/debug.ts — single debug entry point
|
|
289
|
+
import './store/users.debug';
|
|
290
|
+
import './store/tasks.debug';
|
|
291
|
+
|
|
292
|
+
import { registry } from '@oimdb/devtools';
|
|
293
|
+
if (typeof window !== 'undefined') {
|
|
294
|
+
(window as Record<string, unknown>).__OIMDB_DEV__ = registry;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
registry.dump(); // prints full state summary to console
|
|
300
|
+
registry.inspect(); // returns structured JSON
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
When a computed dep's source is not registered, it shows as `(unregistered)` in `dump()` — a signal to go add it to the debug file.
|
|
304
|
+
|
|
305
|
+
Install the browser extension from `packages/browser-extension/` (load unpacked in Chrome) for a live DevTools panel.
|
|
306
|
+
|
|
307
|
+
## Naming Conventions
|
|
308
|
+
|
|
309
|
+
| Kind | Prefix | Example |
|
|
310
|
+
|---|---|---|
|
|
311
|
+
| Class | `OIM` | `OIMCollection` |
|
|
312
|
+
| Interface | `IOIM` | `IOIMCollectionStore` |
|
|
313
|
+
| Type | `TOIM` | `TOIMCollectionOptions` |
|
|
314
|
+
| Enum | `EOIM` | `EOIMCollectionEventType` |
|
|
315
|
+
|
|
316
|
+
Variable names: drop the prefix, start lowercase. Context goes first: `prevCollectionIndexFactory`, not `collectionIndexFactoryPrev`.
|
|
317
|
+
|
|
318
|
+
## File Conventions
|
|
319
|
+
|
|
320
|
+
- One file → one named export
|
|
321
|
+
- Filename matches export name exactly
|
|
322
|
+
- Types → `src/types/`, Interfaces → `src/interfaces/`, Abstract classes → `src/abstract/`
|
|
323
|
+
|
|
324
|
+
## Common Patterns
|
|
325
|
+
|
|
326
|
+
### Wire up a complete feature
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
const queue = new OIMEventQueue({ scheduler: new OIMEventQueueSchedulerImmediate() });
|
|
330
|
+
|
|
331
|
+
const { collection: tasks, indexFactory, select } =
|
|
332
|
+
createOIMCollectionContext<Task, string>(queue, { selectPk: (t) => t.id });
|
|
333
|
+
|
|
334
|
+
const byStatus = indexFactory.derivedSetIndex((t) => [t.status]);
|
|
335
|
+
const byAssignee = indexFactory.derivedSetIndex((t) => [t.assigneeId]);
|
|
336
|
+
|
|
337
|
+
const runtime = new OIMComputeRuntime(queue);
|
|
338
|
+
|
|
339
|
+
const openCount = new OIMComputed<number>(runtime, {
|
|
340
|
+
compute: () => byStatus.getPksByKey('open').size,
|
|
341
|
+
deps: [new OIMEffectDependencyKeyedIndex(byStatus, 'open')],
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Subscribe to a collection index
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
const unsubscribe = byStatus.subscribeOnKey('open', () => {
|
|
349
|
+
console.log('open tasks:', byStatus.getPksByKey('open'));
|
|
350
|
+
});
|
|
351
|
+
// ...later
|
|
352
|
+
unsubscribe();
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Destroy lifecycle (always call destroy in reverse order)
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
logEffect.destroy();
|
|
359
|
+
openCount.destroy();
|
|
360
|
+
byStatus.destroy();
|
|
361
|
+
tasks.destroy();
|
|
362
|
+
queue.destroy();
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## DevTools UI (`@oimdb/mcp devtools`)
|
|
366
|
+
|
|
367
|
+
For human developers — runs a local web UI at `http://localhost:7433`:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
npx @oimdb/mcp devtools
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Auto-opens in the browser. Shows live collections, computeds, and dep status from the running app. Requires `registry.connect()` in `debug.ts`.
|
|
374
|
+
|
|
375
|
+
## MCP Setup (`@oimdb/mcp`)
|
|
376
|
+
|
|
377
|
+
`@oimdb/mcp` lets AI assistants query live OIMDB state from the running browser tab.
|
|
378
|
+
|
|
379
|
+
### Check if MCP is already configured
|
|
380
|
+
|
|
381
|
+
Look for `mcpServers.oimdb` in `.claude/settings.json` (project) or `~/.claude/settings.json` (global). If present, call `oimdb_inspect` directly — no setup needed.
|
|
382
|
+
|
|
383
|
+
### Configure MCP (if not already done)
|
|
384
|
+
|
|
385
|
+
Add to the project MCP config and ask the user to reload their editor:
|
|
386
|
+
|
|
387
|
+
- Claude Code: `.claude/settings.json`
|
|
388
|
+
- Cursor: `.cursor/mcp.json`
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"mcpServers": {
|
|
393
|
+
"oimdb": {
|
|
394
|
+
"command": "npx",
|
|
395
|
+
"args": ["-y", "@oimdb/mcp"]
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Connect the browser
|
|
402
|
+
|
|
403
|
+
The project's `debug.ts` (or `src/debug.ts`) must call `registry.connect()`:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
import { registry } from '@oimdb/devtools';
|
|
407
|
+
registry.connect(); // ws://localhost:7432
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
If this line is missing, add it and ask the user to reload the page.
|
|
411
|
+
|
|
412
|
+
### Available tools
|
|
413
|
+
|
|
414
|
+
| Tool | Use it when |
|
|
415
|
+
|---|---|
|
|
416
|
+
| `oimdb_inspect` | Need full state — collections, computeds, deps |
|
|
417
|
+
| `oimdb_dump` | Need a quick readable summary |
|
|
418
|
+
| `oimdb_collection` | Debugging one specific collection |
|
|
419
|
+
| `oimdb_computed` | Debugging why a computed is stale or wrong |
|
|
420
|
+
|
|
421
|
+
### One-shot CDP (no MCP config needed)
|
|
422
|
+
|
|
423
|
+
If Chrome is running with `--remote-debugging-port=9222`:
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
npx @oimdb/mcp cdp
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Prints full `inspect()` JSON to stdout. Use when MCP is not configured.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oimdb/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Core in-memory data library with type-safe indices, reactive subscriptions, and event processing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -30,10 +30,12 @@
|
|
|
30
30
|
"test": "jest",
|
|
31
31
|
"type-check": "tsc --noEmit",
|
|
32
32
|
"clean": "rimraf dist",
|
|
33
|
+
"prepack": "node -e \"require('fs').copyFileSync('../../llms.txt', 'llms.txt')\"",
|
|
33
34
|
"prepublishOnly": "npm run type-check && npm test && npm run build"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
36
|
-
"dist"
|
|
37
|
+
"dist",
|
|
38
|
+
"llms.txt"
|
|
37
39
|
],
|
|
38
40
|
"keywords": [
|
|
39
41
|
"oimdb",
|