Skip to main content

Devtools

@oimdb/devtools is a zero-overhead debug registry. It gives your collections and indexes human-readable names, describes how they relate, and exposes runtime state for inspection — by you, by AI assistants, or by browser tooling.

Nothing in @oimdb/devtools ever runs in production: the debug registration files are only imported from your debug entry point, which is excluded from your production bundle.

Installation

npm install @oimdb/devtools --save-dev

The pattern

Keep business logic and debug registration in separate files, colocated:

src/store/
users.ts ← creates collection + indexes, exports them
users.debug.ts ← registers with devtools, never imported in prod
tasks.ts
tasks.debug.ts
src/debug.ts ← imports all *.debug.ts — your debug entry point

Registration

// users.debug.ts
import { registry } from '@oimdb/devtools';
import { usersCollection, byRole, byEmail } from './users';

registry.collection('users', usersCollection, {
indexes: { byRole, byEmail },
description: 'Registered app users',
});
// tasks.debug.ts
import { registry } from '@oimdb/devtools';
import { tasksCollection, byAssignee, byStatus } from './tasks';

registry.collection('tasks', tasksCollection, {
indexes: { byAssignee, byStatus },
relations: {
assigneeId: 'users', // tasks.assigneeId is a FK to the users collection
},
});
// debug.ts — single entry point, import all debug files here
import './store/users.debug';
import './store/tasks.debug';
import './store/posts.debug';

Inspecting at runtime

import { registry } from '@oimdb/devtools';

// Structured output — good for programmatic use
const state = registry.inspect();
console.log(state.collections.users.count);
console.log(state.collections.users.sampleEntity);

// Human-readable dump — good for console debugging
registry.dump();

dump() output:

[OIMDB DevRegistry]

users (3 entities)
fields: id, name, role
indexes: byRole (2 keys), byEmail (3 keys)

tasks (5 entities)
fields: id, title, assigneeId, status
indexes: byAssignee (2 keys), byStatus (3 keys)
relations: assigneeId → users

Exposing to browser devtools

// debug.ts
import './store/users.debug';
import './store/tasks.debug';
import { registry } from '@oimdb/devtools';

if (typeof window !== 'undefined') {
(window as Record<string, unknown>).__OIMDB_DEV__ = registry;
}

Then in the browser console:

__OIMDB_DEV__.dump()
__OIMDB_DEV__.inspect()

Flush history

Call trackFlushes to record entity counts on every queue flush. The last 50 flushes appear in inspect() and in the DevTools UI.

// debug.ts
import { registry } from '@oimdb/devtools';
import { EOIMEventQueueEventType } from '@oimdb/core';
import { queue } from './store';

registry.trackFlushes(handler =>
queue.emitter.on(EOIMEventQueueEventType.AFTER_FLUSH, handler)
);

Computed values

Register OIMComputed instances to see their status in inspect() and dump():

// tasks.debug.ts
import { registry } from '@oimdb/devtools';
import { openTaskCount, totalPrice } from './tasks';

registry
.collection('tasks', tasksCollection, { indexes: { byAssignee } })
.computed('openTaskCount', openTaskCount)
.computed('totalPrice', totalPrice);

dump() output includes a computeds section:

computeds:
openTaskCount fresh — value: 3
totalPrice stale — last: 149.99
filteredTasks not computed yet
  • fresh — value is up to date
  • stale — deps changed since last compute; value shown is the previous one
  • not computed yetqueue.flush() hasn't run since creation

This is the fastest way to find a broken selector: stale or never-computed entries point directly at the problem.

API

registry.collection(name, collection, options?)

Registers a collection. collection is duck-typed — any object with getAll() and getAllPks() works, including OIMCollection and OIMReactiveCollection.

OptionTypeDescription
indexesRecord<string, { getKeys(): unknown[] }>Named indexes to expose
relationsRecord<string, string>FK field → target collection name
descriptionstringOptional human/AI description

Returns this for chaining.

registry.computed(name, computed)

Registers an OIMComputed instance by name. computed is duck-typed — any object with needsRecompute: boolean, isReady: boolean, and getIfReady(): unknown works.

Returns this for chaining.

registry.inspect()

Returns a TOIMDevInspectResult with the current state of all registered collections and computed values.

registry.dump()

Prints a formatted summary to console.log.

new OIMDevRegistry()

Creates a fresh isolated registry. Useful for testing or multiple app instances. The named export registry is a singleton created with this constructor.