@monlite/core 0.7.0 → 0.9.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/dist/index.d.cts CHANGED
@@ -1,9 +1,134 @@
1
+ /**
2
+ * A collection. In **document** mode (default) every document is stored as JSON
3
+ * in a `data` column — schema-free. In **structured** mode (when a `schema` is
4
+ * given) the listed fields become real, typed SQL columns (fast, indexable,
5
+ * joinable) while any other fields overflow into a JSON `data` column. The CRUD
6
+ * and query API is identical in both modes.
7
+ */
8
+ declare class Collection<T = Doc> {
9
+ private readonly mon;
10
+ readonly name: string;
11
+ readonly mode: CollectionMode;
12
+ private initialized;
13
+ private readonly columnDefs;
14
+ private readonly columnOrder;
15
+ /** Declared native columns (empty in document mode). */
16
+ private readonly columns;
17
+ private readonly jsonColumns;
18
+ private insertSqlCache?;
19
+ private readonly trackPath;
20
+ constructor(mon: Monlite, name: string, options?: CollectionOptions);
21
+ private get db();
22
+ /** Run a DB operation, normalizing driver errors into typed MonliteErrors. */
23
+ private guard;
24
+ /** Native column names declared for this collection (structured mode). */
25
+ get columnNames(): string[];
26
+ private ensureTable;
27
+ private columnDdl;
28
+ /** Auto-additive migration: add declared columns missing from an existing table. */
29
+ private migrateColumns;
30
+ private rowToDoc;
31
+ private encodeColumn;
32
+ private insertColumns;
33
+ private insertSql;
34
+ /** Split an input document into a row aligned with `insertColumns()`. */
35
+ private buildInsert;
36
+ /** Build the `SET` clause + values to persist an updated document. */
37
+ private buildUpdateSet;
38
+ /** Sync store for recording local changes (both document and structured). */
39
+ private get recorder();
40
+ /** @internal Read a full document by id (mode-aware), synchronously. */
41
+ getRaw(id: string): WithId<T> | null;
42
+ /**
43
+ * @internal Apply a remote change to storage WITHOUT recording it to the
44
+ * change feed (the sync store records the `remote` feed row itself). Used by
45
+ * `@monlite/sync` so structured collections sync correctly through the same
46
+ * column/overflow split as local writes.
47
+ */
48
+ applyRemoteWrite(op: "upsert" | "delete", id: string, doc: Record<string, any> | undefined, ts: number): void;
49
+ /** @internal Notify reactivity watchers and plugins that documents changed. */
50
+ private afterWrite;
51
+ create(args: CreateArgs<T>): Promise<WithId<T>>;
52
+ createMany(args: CreateManyArgs<T>): Promise<{
53
+ count: number;
54
+ }>;
55
+ private buildFindSql;
56
+ /** @internal Synchronous core of findMany (used by reactivity). */
57
+ findManyCore(args?: FindManyArgs<T>): WithId<T>[];
58
+ /** @internal Synchronous core of exists (used by reactivity). */
59
+ existsCore(where: WhereInput<T> | undefined): boolean;
60
+ findMany(args?: FindManyArgs<T>): Promise<WithId<T>[]>;
61
+ findFirst(args?: FindFirstArgs<T>): Promise<WithId<T> | null>;
62
+ /** Alias of {@link findFirst} for Prisma familiarity. */
63
+ findUnique(args?: FindFirstArgs<T>): Promise<WithId<T> | null>;
64
+ /** Like {@link findFirst} but throws if no document matches. */
65
+ findFirstOrThrow(args?: FindFirstArgs<T>): Promise<WithId<T>>;
66
+ /** True if at least one document matches. */
67
+ exists(where?: WhereInput<T>): Promise<boolean>;
68
+ /**
69
+ * Subscribe to a live query. The callback fires immediately with the current
70
+ * results (`type: "init"`) and again whenever a change affects the result set
71
+ * (row-level: only relevant changes trigger a recompute). Includes changes
72
+ * applied by `@monlite/sync`.
73
+ */
74
+ watch(args: FindManyArgs<T> | undefined, cb: (event: LiveEvent<T>) => void): WatchHandle<T>;
75
+ /** Show SQLite's query plan for a `findMany`, and whether it uses an index. */
76
+ explain(args?: FindManyArgs<T>): Promise<ExplainResult>;
77
+ findById(id: string): Promise<WithId<T> | null>;
78
+ count(args?: CountArgs<T>): Promise<number>;
79
+ /**
80
+ * Return the distinct values of a field. Array fields stored in JSON are
81
+ * unwound (each element counts as a value), matching MongoDB's `distinct`.
82
+ */
83
+ distinct(field: string, where?: WhereInput<T>): Promise<any[]>;
84
+ private runUpdate;
85
+ update(args: UpdateArgs<T>): Promise<WithId<T> | null>;
86
+ updateMany(args: UpdateArgs<T>): Promise<{
87
+ count: number;
88
+ }>;
89
+ upsert(args: UpsertArgs<T>): Promise<WithId<T>>;
90
+ private runDelete;
91
+ delete(args: DeleteArgs<T>): Promise<WithId<T> | null>;
92
+ deleteMany(args?: DeleteArgs<T>): Promise<{
93
+ count: number;
94
+ }>;
95
+ aggregate(args?: AggregateArgs<T>): Promise<AggregateResult>;
96
+ groupBy(args: GroupByArgs<T>): Promise<GroupByResult[]>;
97
+ }
98
+
99
+ /** Documents that changed in a single write, delivered to `afterWrite`. */
100
+ interface PluginChange {
101
+ collection: string;
102
+ ids: string[];
103
+ }
104
+ /**
105
+ * A monlite plugin. Plugins keep `@monlite/core` lean — heavier or optional
106
+ * capabilities (full-text search, vector, encryption) live in their own
107
+ * packages and hook in here.
108
+ */
109
+ interface MonlitePlugin {
110
+ name: string;
111
+ /** Called once when the database opens (e.g. to create auxiliary tables). */
112
+ init?(db: Monlite): void;
113
+ /**
114
+ * Called synchronously after every committed write (including changes applied
115
+ * by `@monlite/sync`), so derived state like a search index stays in sync.
116
+ */
117
+ afterWrite?(db: Monlite, change: PluginChange): void;
118
+ /**
119
+ * Methods to attach to every collection handle (e.g. `search`). The impl
120
+ * receives the collection as its first argument, then the caller's arguments.
121
+ */
122
+ collectionMethods?: Record<string, (collection: Collection<any>, ...args: any[]) => any>;
123
+ }
124
+
1
125
  /**
2
126
  * Public type surface for monlite.
3
127
  *
4
128
  * Documents are plain objects. monlite adds three system fields to every
5
129
  * stored document: `_id`, `created_at`, and `updated_at`.
6
130
  */
131
+
7
132
  /** A free-form document. */
8
133
  type Doc = Record<string, any>;
9
134
  /** System fields monlite manages on every document. */
@@ -49,6 +174,38 @@ interface ColumnInfo {
49
174
  notNull: boolean;
50
175
  primaryKey: boolean;
51
176
  }
177
+ /** An update delivered to a `watch()` subscriber. */
178
+ interface LiveEvent<T = Doc> {
179
+ /** `"init"` is the first delivery; `"change"` for every update after. */
180
+ type: "init" | "change";
181
+ /** The full current result set. */
182
+ results: WithId<T>[];
183
+ /** Documents that entered the result set since the last event. */
184
+ added: WithId<T>[];
185
+ /** Documents that left the result set. */
186
+ removed: WithId<T>[];
187
+ /** Documents still in the set whose contents changed. */
188
+ changed: WithId<T>[];
189
+ }
190
+ /** Handle returned by `collection.watch()`. */
191
+ interface WatchHandle<T = Doc> {
192
+ /** The current result set (kept up to date). */
193
+ readonly results: WithId<T>[];
194
+ /** Stop receiving updates. */
195
+ stop(): void;
196
+ }
197
+ /** Result of `collection.explain()`. */
198
+ interface ExplainResult {
199
+ sql: string;
200
+ /** Whether SQLite's planner uses an index (vs a full scan). */
201
+ usesIndex: boolean;
202
+ /** Raw EXPLAIN QUERY PLAN rows. */
203
+ plan: Array<{
204
+ id: number;
205
+ parent: number;
206
+ detail: string;
207
+ }>;
208
+ }
52
209
  /** Per-field operators, Prisma-style (no `$` prefix). */
53
210
  interface FieldFilter<V = any> {
54
211
  equals?: V | null;
@@ -206,6 +363,8 @@ interface MonliteOptions {
206
363
  * when installed, otherwise the built-in `node:sqlite` (Node >= 22.5).
207
364
  */
208
365
  driver?: DriverName;
366
+ /** Opt-in plugins (e.g. `@monlite/fts`). */
367
+ plugins?: MonlitePlugin[];
209
368
  /**
210
369
  * Enable sync metadata (change feed, tombstones, version tracking) so the
211
370
  * database can replicate via `@monlite/sync`. Off by default — adds zero
@@ -231,85 +390,6 @@ interface MonliteOptions {
231
390
  verbose?: (sql: string) => void;
232
391
  }
233
392
 
234
- /**
235
- * A collection. In **document** mode (default) every document is stored as JSON
236
- * in a `data` column — schema-free. In **structured** mode (when a `schema` is
237
- * given) the listed fields become real, typed SQL columns (fast, indexable,
238
- * joinable) while any other fields overflow into a JSON `data` column. The CRUD
239
- * and query API is identical in both modes.
240
- */
241
- declare class Collection<T = Doc> {
242
- private readonly mon;
243
- readonly name: string;
244
- readonly mode: CollectionMode;
245
- private initialized;
246
- private readonly columnDefs;
247
- private readonly columnOrder;
248
- /** Declared native columns (empty in document mode). */
249
- private readonly columns;
250
- private readonly jsonColumns;
251
- private insertSqlCache?;
252
- private readonly trackPath;
253
- constructor(mon: Monlite, name: string, options?: CollectionOptions);
254
- private get db();
255
- /** Run a DB operation, normalizing driver errors into typed MonliteErrors. */
256
- private guard;
257
- /** Native column names declared for this collection (structured mode). */
258
- get columnNames(): string[];
259
- private ensureTable;
260
- private rowToDoc;
261
- private encodeColumn;
262
- private insertColumns;
263
- private insertSql;
264
- /** Split an input document into a row aligned with `insertColumns()`. */
265
- private buildInsert;
266
- /** Build the `SET` clause + values to persist an updated document. */
267
- private buildUpdateSet;
268
- /** Sync store for recording local changes (both document and structured). */
269
- private get recorder();
270
- /** @internal Read a full document by id (mode-aware), synchronously. */
271
- getRaw(id: string): WithId<T> | null;
272
- /**
273
- * @internal Apply a remote change to storage WITHOUT recording it to the
274
- * change feed (the sync store records the `remote` feed row itself). Used by
275
- * `@monlite/sync` so structured collections sync correctly through the same
276
- * column/overflow split as local writes.
277
- */
278
- applyRemoteWrite(op: "upsert" | "delete", id: string, doc: Record<string, any> | undefined, ts: number): void;
279
- create(args: CreateArgs<T>): Promise<WithId<T>>;
280
- createMany(args: CreateManyArgs<T>): Promise<{
281
- count: number;
282
- }>;
283
- findMany(args?: FindManyArgs<T>): Promise<WithId<T>[]>;
284
- findFirst(args?: FindFirstArgs<T>): Promise<WithId<T> | null>;
285
- /** Alias of {@link findFirst} for Prisma familiarity. */
286
- findUnique(args?: FindFirstArgs<T>): Promise<WithId<T> | null>;
287
- /** Like {@link findFirst} but throws if no document matches. */
288
- findFirstOrThrow(args?: FindFirstArgs<T>): Promise<WithId<T>>;
289
- /** True if at least one document matches. */
290
- exists(where?: WhereInput<T>): Promise<boolean>;
291
- findById(id: string): Promise<WithId<T> | null>;
292
- count(args?: CountArgs<T>): Promise<number>;
293
- /**
294
- * Return the distinct values of a field. Array fields stored in JSON are
295
- * unwound (each element counts as a value), matching MongoDB's `distinct`.
296
- */
297
- distinct(field: string, where?: WhereInput<T>): Promise<any[]>;
298
- private runUpdate;
299
- update(args: UpdateArgs<T>): Promise<WithId<T> | null>;
300
- updateMany(args: UpdateArgs<T>): Promise<{
301
- count: number;
302
- }>;
303
- upsert(args: UpsertArgs<T>): Promise<WithId<T>>;
304
- private runDelete;
305
- delete(args: DeleteArgs<T>): Promise<WithId<T> | null>;
306
- deleteMany(args?: DeleteArgs<T>): Promise<{
307
- count: number;
308
- }>;
309
- aggregate(args?: AggregateArgs<T>): Promise<AggregateResult>;
310
- groupBy(args: GroupByArgs<T>): Promise<GroupByResult[]>;
311
- }
312
-
313
393
  /**
314
394
  * The minimal SQLite driver surface monlite needs. Implemented by both the
315
395
  * better-sqlite3 and the built-in node:sqlite backends so the rest of the
@@ -476,6 +556,46 @@ declare class SyncStore {
476
556
  private ensureCollTable;
477
557
  }
478
558
 
559
+ /** Minimal query surface a {@link LiveQuery} needs (satisfied by Collection). */
560
+ interface Queryable<T> {
561
+ readonly name: string;
562
+ findManyCore(args: FindManyArgs<T>): WithId<T>[];
563
+ existsCore(where: WhereInput<T> | undefined): boolean;
564
+ }
565
+ /**
566
+ * A live query. Holds the current result set and recomputes only when a changed
567
+ * row is actually relevant to it (row-level matching): either the row is already
568
+ * in the result set, or it now matches the filter.
569
+ */
570
+ declare class LiveQuery<T = Doc> {
571
+ private readonly source;
572
+ private readonly args;
573
+ private readonly cb;
574
+ results: WithId<T>[];
575
+ stopped: boolean;
576
+ private ids;
577
+ constructor(source: Queryable<T>, args: FindManyArgs<T>, cb: (event: LiveEvent<T>) => void);
578
+ /** Called by the Reactor with the ids that changed this tick. */
579
+ notify(changedIds: Set<string>): void;
580
+ private isRelevant;
581
+ private recompute;
582
+ }
583
+ /**
584
+ * In-process reactivity hub. Collections emit `(collection, ids)` after each
585
+ * write; the reactor coalesces them per microtask and notifies live queries.
586
+ */
587
+ declare class Reactor {
588
+ private readonly byCollection;
589
+ private readonly pending;
590
+ private flushScheduled;
591
+ hasWatchers(collection: string): boolean;
592
+ register(collection: string, lq: LiveQuery<any>): void;
593
+ unregister(collection: string, lq: LiveQuery<any>): void;
594
+ /** Record that documents changed; schedule a notification flush. */
595
+ emit(collection: string, ids: string[]): void;
596
+ private flush;
597
+ }
598
+
479
599
  /**
480
600
  * A monlite database — a thin document layer over a single SQLite file.
481
601
  * Create one with {@link createDb}.
@@ -485,11 +605,16 @@ declare class Monlite {
485
605
  readonly driver: Driver;
486
606
  /** @internal */
487
607
  readonly autoIndexer: AutoIndexer;
608
+ /** @internal Reactivity hub for `collection.watch()`. */
609
+ readonly reactor: Reactor;
488
610
  /** @internal Sync metadata store; present only when `{ sync: true }`. */
489
611
  readonly $sync?: SyncStore;
490
612
  private readonly collections;
613
+ private readonly plugins;
491
614
  private closed;
492
615
  constructor(filename: string, options?: MonliteOptions);
616
+ /** @internal Notify plugins that documents changed (post-commit). */
617
+ firePluginAfterWrite(collection: string, ids: string[]): void;
493
618
  /** Stable node id for LWW tie-breaking (only when sync is enabled). */
494
619
  get nodeId(): string | undefined;
495
620
  /** The underlying native database handle (escape hatch). */
@@ -523,6 +648,11 @@ declare class Monlite {
523
648
  $drop(name: string): Promise<void>;
524
649
  /** Drop every collection in the database. */
525
650
  $dropAll(): Promise<void>;
651
+ /**
652
+ * Write a consistent on-disk snapshot of the database to `path` (via
653
+ * `VACUUM INTO`). The destination file must not already exist.
654
+ */
655
+ backup(path: string): Promise<void>;
526
656
  /** Close the underlying SQLite connection. */
527
657
  $disconnect(): Promise<void>;
528
658
  private assertOpen;
@@ -585,4 +715,4 @@ declare function objectId(): string;
585
715
  /** True when a value looks like a monlite/ObjectId id (24 hex chars). */
586
716
  declare function isObjectId(value: unknown): value is string;
587
717
 
588
- export { type AggregateArgs, type AggregateResult, type ApplyResult, Collection, type CollectionMode, type CollectionOptions, type CollectionSchema, type ColumnDef, type ColumnInfo, type ColumnType, type ConflictResolver, type ConflictRow, type CountArgs, type CreateArgs, type CreateManyArgs, Monlite as Db, type DeleteArgs, type Doc, type Driver, type DriverName, type FieldFilter, type FieldSelection, type FilterInput, type FindFirstArgs, type FindManyArgs, type GroupByArgs, type GroupByResult, type HavingComparison, type HavingInput, type LocalChange, Monlite, MonliteConstraintError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, type MonliteOptions, MonliteQueryError, MonliteUniqueConstraintError, type OrderBy, type PreparedStatement, type RemoteChange, type Select, type SortOrder, type SyncOp, type SyncStateRow, SyncStore, type SystemFields, type UpdateArgs, type UpdateData, type UpdateOperators, type UpsertArgs, type Version, type WhereInput as WhereClause, type WhereInput, type WithId, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
718
+ export { type AggregateArgs, type AggregateResult, type ApplyResult, Collection, type CollectionMode, type CollectionOptions, type CollectionSchema, type ColumnDef, type ColumnInfo, type ColumnType, type ConflictResolver, type ConflictRow, type CountArgs, type CreateArgs, type CreateManyArgs, Monlite as Db, type DeleteArgs, type Doc, type Driver, type DriverName, type ExplainResult, type FieldFilter, type FieldSelection, type FilterInput, type FindFirstArgs, type FindManyArgs, type GroupByArgs, type GroupByResult, type HavingComparison, type HavingInput, type LiveEvent, type LocalChange, Monlite, MonliteConstraintError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, type MonliteOptions, type MonlitePlugin, MonliteQueryError, MonliteUniqueConstraintError, type OrderBy, type PluginChange, type PreparedStatement, type RemoteChange, type Select, type SortOrder, type SyncOp, type SyncStateRow, SyncStore, type SystemFields, type UpdateArgs, type UpdateData, type UpdateOperators, type UpsertArgs, type Version, type WatchHandle, type WhereInput as WhereClause, type WhereInput, type WithId, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };