@monlite/core 0.5.0 β 0.6.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 +92 -12
- package/dist/index.cjs +331 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -6
- package/dist/index.d.ts +66 -6
- package/dist/index.js +327 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
# π monlite
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
|
|
6
|
-
monlite is a local-first document database that lives inside your app as a
|
|
7
|
-
single `.db` file. No server to run, no schema to define, no migrations to
|
|
8
|
-
manage. You get the flexibility of MongoDB, the familiarity of a Prisma-style
|
|
9
|
-
API, and the reliability of SQLite β all in one `npm install`.
|
|
3
|
+
> Your app's local database. A MongoDB-like API and Prisma-style DX in a single
|
|
4
|
+
> file. Zero config, zero migrations, zero server.
|
|
10
5
|
|
|
11
6
|
```ts
|
|
12
7
|
import { createDb } from "@monlite/core";
|
|
@@ -18,7 +13,36 @@ await users.create({ data: { name: "Ali", age: 28 } });
|
|
|
18
13
|
await users.findMany({ where: { age: { gte: 18 } } });
|
|
19
14
|
```
|
|
20
15
|
|
|
21
|
-
That's
|
|
16
|
+
That's the whole setup. Your data is in `app.db`.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Mental model (read this first)
|
|
21
|
+
|
|
22
|
+
monlite is **one database with one query API.** You never have to choose "SQL or
|
|
23
|
+
NoSQL." You only choose, per collection, **where each field is stored:**
|
|
24
|
+
|
|
25
|
+
- **Document mode** (default) β the whole document is stored as JSON. Flexible
|
|
26
|
+
and schema-free, like MongoDB.
|
|
27
|
+
- **Structured mode** (`db.collection(name, { schema })`) β the fields you
|
|
28
|
+
declare become real SQL columns (typed, indexed, joinable). Anything else
|
|
29
|
+
overflows into JSON automatically.
|
|
30
|
+
|
|
31
|
+
> **A schema changes the _storage_, never the _syntax_.**
|
|
32
|
+
> `create`, `find`, `where`, `orderBy`, `groupBy` are identical in both modes and
|
|
33
|
+
> return identical results β structured mode is just faster and SQL-native underneath.
|
|
34
|
+
|
|
35
|
+
Raw SQL is the one optional place SQL becomes visible: the `$queryRaw` escape
|
|
36
|
+
hatch, for joins/CTEs/window functions the document API doesn't cover.
|
|
37
|
+
|
|
38
|
+
| You decide⦠| Document (default) | Structured (`{ schema }`) |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| **How you query** | `find` / `where` / `orderBy` / `groupBy` | **identical** |
|
|
41
|
+
| **Where a field lives** | JSON `data` blob | a native column (declared) β the rest overflow to JSON |
|
|
42
|
+
| **Pick it when** | the shape is unknown or varies per record | the shape is stable and you want joins, FKs, reporting, or fast native indexes |
|
|
43
|
+
|
|
44
|
+
You can mix both in the same `.db`, and move a collection from document to
|
|
45
|
+
structured later without changing a single query.
|
|
22
46
|
|
|
23
47
|
---
|
|
24
48
|
|
|
@@ -57,6 +81,20 @@ If your data is structured and you already know your schema, plain SQLite adds
|
|
|
57
81
|
nothing on top of monlite β use it directly. monlite earns its keep when your
|
|
58
82
|
documents are dynamic, schema-free, or mirror a cloud NoSQL store.
|
|
59
83
|
|
|
84
|
+
### How monlite compares
|
|
85
|
+
|
|
86
|
+
| | monlite | MongoDB | better-sqlite3 | Prisma + SQLite |
|
|
87
|
+
|---|---|---|---|---|
|
|
88
|
+
| Schema-free documents | β
| β
| β οΈ manual JSON | β |
|
|
89
|
+
| Native typed columns | β
(opt-in) | β | β
| β
|
|
|
90
|
+
| Same API for both | β
| β | β | β |
|
|
91
|
+
| Raw SQL escape hatch | β
| β | β
| β
(`$queryRaw`) |
|
|
92
|
+
| No server / single file | β
| β | β
| β
|
|
|
93
|
+
| No migrations / codegen | β
| β
| β
| β |
|
|
94
|
+
| Aggregation API | β
| β
| β οΈ manual | β οΈ limited |
|
|
95
|
+
| Local-first sync | β
(`@monlite/sync`) | β οΈ Atlas/Realm | β | β |
|
|
96
|
+
| Runtime dependencies | **0** (Node 22.5+) | server | 1 (native) | several |
|
|
97
|
+
|
|
60
98
|
---
|
|
61
99
|
|
|
62
100
|
## Setup
|
|
@@ -124,6 +162,9 @@ await users.createMany({ data: [{ name: "Sara" }, { name: "Omar" }] });
|
|
|
124
162
|
// read
|
|
125
163
|
await users.findById("β¦"); // doc | null
|
|
126
164
|
await users.findFirst({ where: { name: "Ali" } }); // doc | null
|
|
165
|
+
await users.findUnique({ where: { email: "a@x.com" } }); // alias of findFirst
|
|
166
|
+
await users.findFirstOrThrow({ where: { name: "Ali" } }); // throws if missing
|
|
167
|
+
await users.exists({ role: "admin" }); // boolean
|
|
127
168
|
await users.findMany({
|
|
128
169
|
where: { age: { gte: 18 } },
|
|
129
170
|
orderBy: { age: "desc" },
|
|
@@ -167,10 +208,11 @@ where: { age: { gt: 18 } } // gt, gte, lt, lte
|
|
|
167
208
|
where: { role: { in: ["admin", "editor"] } }
|
|
168
209
|
where: { role: { notIn: ["guest"] } }
|
|
169
210
|
|
|
170
|
-
// String (case-sensitive; wildcards are matched literally)
|
|
211
|
+
// String (case-sensitive by default; wildcards are matched literally)
|
|
171
212
|
where: { name: { contains: "li" } }
|
|
172
213
|
where: { name: { startsWith: "A" } }
|
|
173
214
|
where: { name: { endsWith: "i" } }
|
|
215
|
+
where: { name: { contains: "ALI", mode: "insensitive" } } // case-insensitive (ASCII)
|
|
174
216
|
|
|
175
217
|
// Arrays
|
|
176
218
|
where: { tags: { contains: "admin" } } // element membership
|
|
@@ -277,14 +319,15 @@ await users.distinct("tags"); // ["a", "b", "c"]
|
|
|
277
319
|
|
|
278
320
|
---
|
|
279
321
|
|
|
280
|
-
## Structured collections (
|
|
322
|
+
## Structured collections (native SQL columns)
|
|
281
323
|
|
|
282
324
|
By default a collection is **document mode** β schema-free, every field stored
|
|
283
325
|
as JSON. Pass a `schema` to make it a **structured collection**: the declared
|
|
284
326
|
fields become real, typed SQL columns (fast, indexable, joinable, constrainable)
|
|
285
327
|
and any *other* fields overflow into a JSON column. **The CRUD/query API is
|
|
286
|
-
identical** β `find`, `where`, `orderBy`, `groupBy`, `distinct`, updates
|
|
287
|
-
|
|
328
|
+
identical** β `find`, `where`, `orderBy`, `groupBy`, `distinct`, updates. As the
|
|
329
|
+
[mental model](#mental-model-read-this-first) says: a schema changes the storage,
|
|
330
|
+
not the syntax.
|
|
288
331
|
|
|
289
332
|
```ts
|
|
290
333
|
const orders = db.collection("orders", {
|
|
@@ -341,6 +384,43 @@ createDb("./app.db", { verbose: (sql) => console.log(sql) }); // see json_extrac
|
|
|
341
384
|
|
|
342
385
|
---
|
|
343
386
|
|
|
387
|
+
## Sync & local-first
|
|
388
|
+
|
|
389
|
+
The companion package [`@monlite/sync`](https://www.npmjs.com/package/@monlite/sync)
|
|
390
|
+
replicates a local monlite database with a remote source of truth β MongoDB
|
|
391
|
+
first β so apps can work offline and converge when reconnected.
|
|
392
|
+
|
|
393
|
+
Opt in with `{ sync: true }` (adds a change feed + tombstones + versioning; zero
|
|
394
|
+
overhead when off), then drive an engine:
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
import { createDb } from "@monlite/core";
|
|
398
|
+
import { sync, MongoAdapter } from "@monlite/sync";
|
|
399
|
+
import { MongoClient } from "mongodb";
|
|
400
|
+
|
|
401
|
+
const db = createDb("./app.db", { sync: true });
|
|
402
|
+
const mongo = new MongoClient(uri);
|
|
403
|
+
await mongo.connect();
|
|
404
|
+
|
|
405
|
+
const engine = sync(db, {
|
|
406
|
+
adapter: new MongoAdapter({ client: mongo, db: "app" }),
|
|
407
|
+
collections: "*",
|
|
408
|
+
mode: "two-way", // "pull" | "push" | "two-way"
|
|
409
|
+
conflict: "lww", // or a custom resolver
|
|
410
|
+
interval: 5000,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
await engine.start();
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Pull / push / two-way replication, last-write-wins (or custom) conflict
|
|
417
|
+
resolution, and pluggable adapters (`MongoAdapter`, `MonliteAdapter` for
|
|
418
|
+
monlite-to-monlite, `MemoryAdapter` for tests). monlite's ObjectId-compatible
|
|
419
|
+
`_id`s map 1:1 to Mongo `_id`s. See the
|
|
420
|
+
[`@monlite/sync` README](https://www.npmjs.com/package/@monlite/sync) for details.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
344
424
|
## SQL escape hatch
|
|
345
425
|
|
|
346
426
|
When you need full SQL power β complex joins, analytics, cross-collection
|