@monlite/core 0.6.0 → 0.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 CHANGED
@@ -319,6 +319,31 @@ await users.distinct("tags"); // ["a", "b", "c"]
319
319
 
320
320
  ---
321
321
 
322
+ ## Live queries (reactivity)
323
+
324
+ `collection.watch()` keeps a query result live. The callback fires once
325
+ immediately (`type: "init"`) and again whenever a change **affects this query** —
326
+ matching is **row-level**, so unrelated writes don't trigger a recompute. It also
327
+ fires for changes applied by `@monlite/sync`, so the UI updates when cloud data
328
+ arrives.
329
+
330
+ ```ts
331
+ const handle = users.watch({ where: { role: "admin" } }, (event) => {
332
+ event.results; // full current result set
333
+ event.added; // docs that just entered the set
334
+ event.removed; // docs that just left
335
+ event.changed; // docs still in the set whose contents changed
336
+ });
337
+
338
+ handle.results; // current results, kept up to date
339
+ handle.stop(); // unsubscribe
340
+ ```
341
+
342
+ Perfect for Electron/Tauri UIs: bind `handle.results` to your view and it stays
343
+ in sync with every write (local or synced).
344
+
345
+ ---
346
+
322
347
  ## Structured collections (native SQL columns)
323
348
 
324
349
  By default a collection is **document mode** — schema-free, every field stored
@@ -362,6 +387,11 @@ await db.$queryRaw`
362
387
  Column types: `"TEXT" | "INTEGER" | "REAL" | "BLOB" | "JSON"`. A full column
363
388
  definition supports `index`, `unique`, `notNull`, `default`, and `references`.
364
389
 
390
+ **Migrations are automatic for additive changes.** Re-opening a collection with a
391
+ new declared column adds it (`ALTER TABLE ADD COLUMN`) on declaration — give
392
+ `NOT NULL` columns a `default` so existing rows can be backfilled. Destructive
393
+ changes (rename/drop/type-change) still need a manual migration.
394
+
365
395
  ### Do I have to care: JSON vs native columns?
366
396
 
367
397
  - **For correctness — no.** Both modes return identical results through the same API.
@@ -379,8 +409,10 @@ createDb("./app.db", { verbose: (sql) => console.log(sql) }); // see json_extrac
379
409
  > **Rule of thumb:** unknown/flexible shape → document (JSON); known/stable shape
380
410
  > with heavy joins, reporting, or external SQL tooling → structured (native columns).
381
411
 
382
- > Note: structured collections are not yet covered by `@monlite/sync` (document
383
- > collections are) that's planned follow-up work.
412
+ > Both document and structured collections are syncable via
413
+ > [`@monlite/sync`](#sync--local-first). To sync a structured collection, open it
414
+ > with its `schema` on every node before syncing (so each side knows the native
415
+ > columns).
384
416
 
385
417
  ---
386
418
 
@@ -470,6 +502,13 @@ ON users(json_extract(data, '$.address.city'));
470
502
 
471
503
  You never think about indexes. Disable with `createDb("./app.db", { autoIndex: false })`.
472
504
 
505
+ Want to see whether a query uses an index? Ask:
506
+
507
+ ```ts
508
+ await users.explain({ where: { "address.city": "Riyadh" } });
509
+ // { sql, usesIndex: true, plan: [{ id, parent, detail }, …] }
510
+ ```
511
+
473
512
  ---
474
513
 
475
514
  ## Database management
@@ -478,6 +517,7 @@ You never think about indexes. Disable with `createDb("./app.db", { autoIndex: f
478
517
  await db.$collections(); // string[] of collection names
479
518
  await db.$drop("users"); // drop a collection and its data
480
519
  await db.$dropAll(); // drop everything
520
+ await db.backup("./snapshot.db"); // consistent on-disk snapshot
481
521
  await db.$disconnect(); // close the connection
482
522
  db.sqlite; // the underlying native driver handle
483
523
  db.driverName; // "better-sqlite3" | "node:sqlite"