nedb-engine 4.3.2 → 5.0.1
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 +100 -27
- package/index.d.ts +64 -64
- package/index.js +84 -84
- package/nedb.darwin-arm64.node +0 -0
- package/nedb.darwin-x64.node +0 -0
- package/nedb.linux-arm64-gnu.node +0 -0
- package/nedb.linux-arm64-musl.node +0 -0
- package/nedb.linux-x64-gnu.node +0 -0
- package/nedb.linux-x64-musl.node +0 -0
- package/nedb.win32-x64-msvc.node +0 -0
- package/nedbd-v2-darwin-arm64 +0 -0
- package/nedbd-v2-darwin-x64 +0 -0
- package/nedbd-v2-linux-arm64 +0 -0
- package/nedbd-v2-linux-arm64-musl +0 -0
- package/nedbd-v2-linux-x64 +0 -0
- package/nedbd-v2-linux-x64-musl +0 -0
- package/nedbd-v2-win-x64.exe +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ One Rust core → ships to **PyPI** and **npm** from a single source.
|
|
|
18
18
|
**[Studio → studio.interchained.org](https://studio.interchained.org)** · **[nedb.aiassist.net](https://nedb.aiassist.net)**
|
|
19
19
|
|
|
20
20
|
> ## 🟢 Free in production under $1M revenue
|
|
21
|
-
> NEDB
|
|
21
|
+
> NEDB is licensed under the **Business Source License 1.1** (since 4.0.0). If your organisation's annual
|
|
22
22
|
> revenue is **under USD $1,000,000**, you may use it in production — commercially, embedded, in
|
|
23
23
|
> closed-source software — with **no permission needed and no royalty**. At **$1M or more**, you
|
|
24
24
|
> need an additional use grant from Interchained LLC: **licensing@interchained.org**.
|
|
@@ -38,14 +38,74 @@ endpoint already answers `psql`, SQLAlchemy Core **and** ORM, asyncpg and
|
|
|
38
38
|
node-postgres against a live store — but it gets there by *translating* SQL into
|
|
39
39
|
NQL, and a translation can only reach as far as the target language's shape.
|
|
40
40
|
|
|
41
|
-
**[neSQL](https://github.com/Eth-Interchained/neSQL)** removes the translation
|
|
42
|
-
PostgreSQL's real grammar
|
|
43
|
-
|
|
41
|
+
**[neSQL](https://github.com/Eth-Interchained/neSQL)** removes the translation.
|
|
42
|
+
PostgreSQL's real grammar (`gram.y`, 19,513 lines, 492 keywords, vendored from
|
|
43
|
+
17.4 at [`vendor/postgresql/`](vendor/postgresql/) with its licence intact),
|
|
44
|
+
extended with NEDB's temporal and causal clauses. **Two front-ends, one plan.
|
|
45
|
+
NQL folded in, not deleted.**
|
|
44
46
|
|
|
45
47
|
[](https://pypi.org/project/nesql/)
|
|
46
48
|
[](https://crates.io/crates/nesql)
|
|
47
49
|
[](https://www.npmjs.com/package/nesql-engine)
|
|
48
50
|
|
|
51
|
+
### Available now, opt-in: `NEDBD_SQL_ENGINE=1`
|
|
52
|
+
|
|
53
|
+
The SQL engine is in this release and it is **off by default**. Turn it on and a
|
|
54
|
+
user collection is answered by a real SQL evaluator instead of a translation —
|
|
55
|
+
every one of these works, and every one is refused *by name* without it:
|
|
56
|
+
|
|
57
|
+
```sql
|
|
58
|
+
SELECT o._id, d.name FROM orders o JOIN drivers d ON o.driver = d._id;
|
|
59
|
+
SELECT status, sum(total), avg(total) FROM orders GROUP BY status;
|
|
60
|
+
SELECT _id FROM orders WHERE driver IN (SELECT _id FROM drivers);
|
|
61
|
+
SELECT DISTINCT status FROM orders;
|
|
62
|
+
SELECT _id FROM orders UNION SELECT _id FROM drivers;
|
|
63
|
+
SELECT status, array_agg(_id ORDER BY total DESC) FROM orders GROUP BY status;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`sum(total), avg(total)` in one grouped row is the one worth pointing at. An NQL
|
|
67
|
+
grouped row carries the group key, `count`, and **one** named aggregate — so that
|
|
68
|
+
query was never slow, it was *unrepresentable*. No translator can fix a row
|
|
69
|
+
model, which is the whole reason neSQL exists.
|
|
70
|
+
|
|
71
|
+
**And NQL's own verbs are now SQL clauses**, so they compose with all of the
|
|
72
|
+
above rather than living on a separate path:
|
|
73
|
+
|
|
74
|
+
```sql
|
|
75
|
+
-- full-text search from NQL, a join from SQL, one statement
|
|
76
|
+
SELECT o._id, d.name
|
|
77
|
+
FROM orders SEARCH 'acme' o
|
|
78
|
+
JOIN drivers d ON o.driver = d._id;
|
|
79
|
+
|
|
80
|
+
-- one relation in the past, joined against another at the tip
|
|
81
|
+
SELECT h.total, n.total
|
|
82
|
+
FROM orders AS OF SYSTEM TIME 412 h
|
|
83
|
+
JOIN audit n ON h._id = n._id;
|
|
84
|
+
|
|
85
|
+
SELECT _id FROM orders VALID AS OF '2026-01-01';
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
There is **one implementation** of each verb — the SQL side parses them and the
|
|
89
|
+
NQL engine still executes them — so neither language is a reimplementation of
|
|
90
|
+
the other. `AS OF SYSTEM TIME`, `VALID AS OF` and `SEARCH` are **unreserved
|
|
91
|
+
keywords**: a collection aliased `search`, or a column named `valid`, keeps
|
|
92
|
+
working exactly as before.
|
|
93
|
+
|
|
94
|
+
**Why it is opt-in rather than the default**, stated plainly because the reason
|
|
95
|
+
is the interesting part. A parity harness runs the same corpus through both
|
|
96
|
+
engines and asserts identical answers — 44 checks, in CI, and it is what earns
|
|
97
|
+
the flag being flipped rather than a benchmark. It already found two real
|
|
98
|
+
divergences: `SELECT *` returned its columns in a different order on each
|
|
99
|
+
engine, and the SQL evaluator built its column list from the **first row alone**,
|
|
100
|
+
so a field only later documents carried silently did not appear at all.
|
|
101
|
+
|
|
102
|
+
Both are fixed. But 44 checks over six documents proves agreement on the shapes
|
|
103
|
+
we thought to test, and the evaluator still materialises each relation — the
|
|
104
|
+
`WHERE` is pushed into the scan, which narrows *what* is read but not *whether*.
|
|
105
|
+
Flipping the default changes the read path of every existing deployment, and
|
|
106
|
+
"correct on six rows" is not "safe on six million". So it ships as a flag, with
|
|
107
|
+
the bar for changing that written down.
|
|
108
|
+
|
|
49
109
|
---
|
|
50
110
|
|
|
51
111
|
## New in 3.3.0 — the query language grew up
|
|
@@ -200,46 +260,59 @@ all the work in this sentence. Every refusal below traces to the same cause:
|
|
|
200
260
|
NQL is the engine's native language, so SQL has to be rewritten into it, and a
|
|
201
261
|
rewrite can only ever reach as far as the target language's shape.
|
|
202
262
|
|
|
203
|
-
| Supported
|
|
263
|
+
| Supported on the default path | Refused there, with the reason | `NEDBD_SQL_ENGINE=1` |
|
|
204
264
|
| --- | --- | --- |
|
|
205
|
-
| `*`, a column list, `COUNT(*)`, `SUM`/`AVG`/`MIN`/`MAX(col)` | `JOIN` — NQL is single-collection | ✅
|
|
206
|
-
| `WHERE` — the whole NQL predicate surface | subqueries, `UNION`, window functions | ✅ subqueries
|
|
207
|
-
| `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT`, `OFFSET` | expressions in the select list | ✅ |
|
|
208
|
-
| one named aggregate per grouped row | `sum(x), avg(x)` in one query — NQL's grouped row holds *one* | ✅ the row-model cap
|
|
209
|
-
|
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
265
|
+
| `*`, a column list, `COUNT(*)`, `SUM`/`AVG`/`MIN`/`MAX(col)` | `JOIN` — NQL is single-collection | ✅ **works** (nested-loop + hash) |
|
|
266
|
+
| `WHERE` — the whole NQL predicate surface | subqueries, `UNION`, window functions | ✅ **subqueries, `EXISTS`, `UNION`/`INTERSECT`/`EXCEPT` work**; window functions arrive with the grammar |
|
|
267
|
+
| `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT`, `OFFSET` | expressions in the select list | ✅ **works** |
|
|
268
|
+
| one named aggregate per grouped row | `sum(x), avg(x)` in one query — NQL's grouped row holds *one* | ✅ **works** — the row-model cap is gone |
|
|
269
|
+
| — | `DISTINCT`, `array_agg(x ORDER BY y)`, derived tables | ✅ **works** |
|
|
270
|
+
| `AS OF SYSTEM TIME <seq>`, `VALID AS OF`, `SEARCH` | — | ✅ **also works**, and composes with joins and aggregates |
|
|
271
|
+
| `TRACE`, `TRAVERSE`, `LINK` | — | ↩︎ still answered by the NQL path; SQL has no spelling for them yet |
|
|
272
|
+
| `INSERT` / `UPDATE` / `DELETE`, all with `RETURNING` | an `INSERT` with no column list | writes always take the NQL path |
|
|
273
|
+
| `_caused_by` / `_valid_from` / `_valid_to` as INSERT columns | values that are expressions, not literals | as above |
|
|
274
|
+
| — | DDL, `TRUNCATE`, `GRANT`/`REVOKE` | ⛔️ **refused on both**, and always will be |
|
|
212
275
|
|
|
213
276
|
The `⛔️` row is the one that is not a limitation. `TRUNCATE` is refused because
|
|
214
277
|
NEDB is append-only *so that history cannot be discarded* — that is the product,
|
|
215
278
|
not a gap — and DDL is refused because collections are created by the first write
|
|
216
279
|
to them. Those answers do not change.
|
|
217
280
|
|
|
218
|
-
### Every other row on that table
|
|
281
|
+
### Every other row on that table was a translation artefact — and one flag removes them
|
|
219
282
|
|
|
220
283
|
> ### 🆕 [**neSQL**](https://github.com/Eth-Interchained/neSQL) — PostgreSQL's grammar, NEDB's memory
|
|
221
284
|
>
|
|
222
|
-
>
|
|
223
|
-
>
|
|
224
|
-
>
|
|
225
|
-
>
|
|
285
|
+
> Those refusals were never the engine's limits. `sqlselect.rs` has had
|
|
286
|
+
> nested-loop and hash joins, subqueries, `EXISTS`, quantified comparisons, set
|
|
287
|
+
> operations, `array_agg(x ORDER BY y)` and derived tables for some time — they
|
|
288
|
+
> were simply unreachable *through a translator*, because the translator's
|
|
289
|
+
> target was NQL. Set `NEDBD_SQL_ENGINE=1` and they are reachable.
|
|
290
|
+
>
|
|
291
|
+
> neSQL vendors PostgreSQL's **real grammar** — `gram.y`, 19,513 lines and 492
|
|
292
|
+
> keywords, from 17.4, licence intact — and extends it with the clauses NEDB
|
|
293
|
+
> needs, rather than rewriting SQL into a language that cannot express it.
|
|
226
294
|
>
|
|
227
|
-
> It is worth knowing *why*
|
|
228
|
-
> `PORTION` appear **zero** times in PostgreSQL's grammar. Postgres
|
|
229
|
-
> SQL at all
|
|
230
|
-
> temporal clauses are additions to the vendored grammar
|
|
231
|
-
> from it
|
|
295
|
+
> It is worth knowing *why* the temporal clauses were never free: `SYSTEM_TIME`,
|
|
296
|
+
> `PERIOD` and `PORTION` appear **zero** times in PostgreSQL's grammar. Postgres
|
|
297
|
+
> has no temporal SQL at all, and `AS OF SYSTEM TIME` is a CockroachDB
|
|
298
|
+
> extension — so NEDB's temporal clauses are additions *to* the vendored grammar
|
|
299
|
+
> rather than deviations *from* it. The same road CockroachDB, Materialize and
|
|
300
|
+
> RisingWave took. What the grammar does hand over free: `WITH RECURSIVE`,
|
|
301
|
+
> window functions, `GROUPING SETS` and `MERGE`.
|
|
232
302
|
>
|
|
233
303
|
> ```bash
|
|
234
304
|
> pip install nesql · cargo add nesql · npm install nesql-engine
|
|
235
305
|
> ```
|
|
236
306
|
>
|
|
237
|
-
>
|
|
238
|
-
>
|
|
239
|
-
>
|
|
307
|
+
> Those three are **reserved names**, not a product: each loads and answers
|
|
308
|
+
> `is_release() == false`, because a package that imports cleanly and then lies
|
|
309
|
+
> is worse than one that isn't published. The engine is what ships today, and
|
|
310
|
+
> neSQL will be this same engine under its own name.
|
|
240
311
|
>
|
|
241
|
-
> **NQL is not being deleted
|
|
242
|
-
>
|
|
312
|
+
> **NQL is not being deleted, and it is not being wrapped.** Its verbs are SQL
|
|
313
|
+
> clauses now — `AS OF SYSTEM TIME`, `VALID AS OF`, `SEARCH` — parsed by the SQL
|
|
314
|
+
> side and executed by the NQL engine. One implementation, two front-ends,
|
|
315
|
+
> neither one a second-class guest.
|
|
243
316
|
|
|
244
317
|
Every refusal names the boundary instead of saying "syntax error", and a
|
|
245
318
|
grouped query that projects a column SQL would reject gets Postgres's own
|
package/index.d.ts
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
// nedb-engine — public type surface.
|
|
2
|
-
//
|
|
3
|
-
// Runtime behavior (durable-mode auto-flush-on-exit) is added by the wrapper in
|
|
4
|
-
// index.js; the type surface is exactly the generated native binding's.
|
|
5
|
-
export * from './native';
|
|
6
|
-
|
|
7
|
-
// ── wrap adapter family (wrap/*.js) ─────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
/** Options accepted by every wrap_* constructor. */
|
|
10
|
-
export interface WrapOptions {
|
|
11
|
-
/** Logical database name (default "default"). */
|
|
12
|
-
dbName?: string;
|
|
13
|
-
/** HTTP nedbd server (v1 AOF, `--dag` v2, `--dag-v3` v3). Overrides embedded DAG. */
|
|
14
|
-
nedbdUrl?: string;
|
|
15
|
-
/** Bearer token for nedbd (NEDBD_TOKEN on the server). */
|
|
16
|
-
nedbdToken?: string;
|
|
17
|
-
/** Durable DAG store directory (embedded mode). */
|
|
18
|
-
dagPath?: string;
|
|
19
|
-
/** 64-hex TMK → AES-256-GCM at-rest encryption (embedded DAG mode). */
|
|
20
|
-
dagTmk?: string;
|
|
21
|
-
/** Explicit NedbCore class (testing / custom builds). */
|
|
22
|
-
native?: unknown;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** The `.nedb` attribute — full NEDB layer-2 API. */
|
|
26
|
-
export interface NedbSurface {
|
|
27
|
-
register(pattern: string, collection: string, opts?: {
|
|
28
|
-
idExtractor?: (key: string) => string;
|
|
29
|
-
valueParser?: (raw: unknown) => Record<string, unknown>;
|
|
30
|
-
valueType?: 'string' | 'hash' | 'json';
|
|
31
|
-
}): NedbSurface;
|
|
32
|
-
backfill(opts?: { pattern?: string; collection?: string; batchSize?: number }): number;
|
|
33
|
-
shadowWrites: boolean;
|
|
34
|
-
readonly engineKind: 'dag-embedded' | 'nedbd-http' | 'aof-embedded';
|
|
35
|
-
|
|
36
|
-
put(coll: string, id: string, doc: Record<string, unknown>): Record<string, unknown>;
|
|
37
|
-
get(coll: string, id: string, asOf?: number): Record<string, unknown> | null;
|
|
38
|
-
query(nql: string): Array<Record<string, unknown>>;
|
|
39
|
-
createIndex(coll: string, field: string, kind?: string): void;
|
|
40
|
-
delete(coll: string, id: string): void;
|
|
41
|
-
link(frm: string, rel: string, to: string): void;
|
|
42
|
-
unlink(frm: string, rel: string, to: string): void;
|
|
43
|
-
neighbors(frm: string, rel: string, asOf?: number): string[];
|
|
44
|
-
inbound(to: string, rel: string, asOf?: number): string[];
|
|
45
|
-
verify(): boolean;
|
|
46
|
-
readonly head: string;
|
|
47
|
-
readonly seq: number;
|
|
48
|
-
checkpoint(): string;
|
|
49
|
-
/** DAG-native: latest node (null on non-DAG backends). */
|
|
50
|
-
tip(): Record<string, unknown> | null;
|
|
51
|
-
/** DAG-native: changefeed page, after_seq exclusive. */
|
|
52
|
-
since(afterSeq: number | bigint, limit?: number): {
|
|
53
|
-
nodes: Array<Record<string, unknown>>; from_seq: number; to_seq: number;
|
|
54
|
-
head_seq: number; has_more: boolean;
|
|
55
|
-
};
|
|
56
|
-
/** DAG-native: replication readiness. */
|
|
57
|
-
scanStatus(): { scan_complete: boolean; tip_seq: number; indexed_count: number; [k: string]: unknown };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export declare function wrapRedis<T extends object = object>(client: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
61
|
-
export declare function wrapSqlite<T extends object = object>(conn: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
62
|
-
export declare function wrapMysql<T extends object = object>(conn: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
63
|
-
export declare function wrapPg<T extends object = object>(conn: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
64
|
-
export declare function wrapMongo<T extends object = object>(client: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
1
|
+
// nedb-engine — public type surface.
|
|
2
|
+
//
|
|
3
|
+
// Runtime behavior (durable-mode auto-flush-on-exit) is added by the wrapper in
|
|
4
|
+
// index.js; the type surface is exactly the generated native binding's.
|
|
5
|
+
export * from './native';
|
|
6
|
+
|
|
7
|
+
// ── wrap adapter family (wrap/*.js) ─────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
/** Options accepted by every wrap_* constructor. */
|
|
10
|
+
export interface WrapOptions {
|
|
11
|
+
/** Logical database name (default "default"). */
|
|
12
|
+
dbName?: string;
|
|
13
|
+
/** HTTP nedbd server (v1 AOF, `--dag` v2, `--dag-v3` v3). Overrides embedded DAG. */
|
|
14
|
+
nedbdUrl?: string;
|
|
15
|
+
/** Bearer token for nedbd (NEDBD_TOKEN on the server). */
|
|
16
|
+
nedbdToken?: string;
|
|
17
|
+
/** Durable DAG store directory (embedded mode). */
|
|
18
|
+
dagPath?: string;
|
|
19
|
+
/** 64-hex TMK → AES-256-GCM at-rest encryption (embedded DAG mode). */
|
|
20
|
+
dagTmk?: string;
|
|
21
|
+
/** Explicit NedbCore class (testing / custom builds). */
|
|
22
|
+
native?: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The `.nedb` attribute — full NEDB layer-2 API. */
|
|
26
|
+
export interface NedbSurface {
|
|
27
|
+
register(pattern: string, collection: string, opts?: {
|
|
28
|
+
idExtractor?: (key: string) => string;
|
|
29
|
+
valueParser?: (raw: unknown) => Record<string, unknown>;
|
|
30
|
+
valueType?: 'string' | 'hash' | 'json';
|
|
31
|
+
}): NedbSurface;
|
|
32
|
+
backfill(opts?: { pattern?: string; collection?: string; batchSize?: number }): number;
|
|
33
|
+
shadowWrites: boolean;
|
|
34
|
+
readonly engineKind: 'dag-embedded' | 'nedbd-http' | 'aof-embedded';
|
|
35
|
+
|
|
36
|
+
put(coll: string, id: string, doc: Record<string, unknown>): Record<string, unknown>;
|
|
37
|
+
get(coll: string, id: string, asOf?: number): Record<string, unknown> | null;
|
|
38
|
+
query(nql: string): Array<Record<string, unknown>>;
|
|
39
|
+
createIndex(coll: string, field: string, kind?: string): void;
|
|
40
|
+
delete(coll: string, id: string): void;
|
|
41
|
+
link(frm: string, rel: string, to: string): void;
|
|
42
|
+
unlink(frm: string, rel: string, to: string): void;
|
|
43
|
+
neighbors(frm: string, rel: string, asOf?: number): string[];
|
|
44
|
+
inbound(to: string, rel: string, asOf?: number): string[];
|
|
45
|
+
verify(): boolean;
|
|
46
|
+
readonly head: string;
|
|
47
|
+
readonly seq: number;
|
|
48
|
+
checkpoint(): string;
|
|
49
|
+
/** DAG-native: latest node (null on non-DAG backends). */
|
|
50
|
+
tip(): Record<string, unknown> | null;
|
|
51
|
+
/** DAG-native: changefeed page, after_seq exclusive. */
|
|
52
|
+
since(afterSeq: number | bigint, limit?: number): {
|
|
53
|
+
nodes: Array<Record<string, unknown>>; from_seq: number; to_seq: number;
|
|
54
|
+
head_seq: number; has_more: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** DAG-native: replication readiness. */
|
|
57
|
+
scanStatus(): { scan_complete: boolean; tip_seq: number; indexed_count: number; [k: string]: unknown };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export declare function wrapRedis<T extends object = object>(client: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
61
|
+
export declare function wrapSqlite<T extends object = object>(conn: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
62
|
+
export declare function wrapMysql<T extends object = object>(conn: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
63
|
+
export declare function wrapPg<T extends object = object>(conn: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
|
64
|
+
export declare function wrapMongo<T extends object = object>(client: T, opts?: WrapOptions): T & { nedb: NedbSurface };
|
package/index.js
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2026 INTERCHAINED LLC
|
|
2
|
-
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
-
// NEDB · © 2026 INTERCHAINED LLC × Eth-Interchained × Vex (Claude Opus 5)
|
|
4
|
-
|
|
5
|
-
'use strict';
|
|
6
|
-
// nedb-engine — durable-mode auto-flush-on-exit wrapper.
|
|
7
|
-
//
|
|
8
|
-
// The native addon (generated napi binding in ./native.js) exposes `NedbCore`.
|
|
9
|
-
// A durable `NedbCore.open(path)` buffers writes in the engine's id-index WAL and
|
|
10
|
-
// only makes them durable on `flush()`; a hard exit (Ctrl+C, `SIGTERM` from an
|
|
11
|
-
// orchestrator, `pm2 stop`) that never runs an explicit flush would lose writes
|
|
12
|
-
// staged since the last flush.
|
|
13
|
-
//
|
|
14
|
-
// We close that gap the libuv-cooperative way — `process.on('SIGINT'|'SIGTERM'
|
|
15
|
-
// |'exit', () => db.flush())` — NOT a C-level signal handler inside the addon,
|
|
16
|
-
// which would clobber libuv's own signal machinery. In-memory databases
|
|
17
|
-
// (`new NedbCore()`) are never armed; there is nothing to flush.
|
|
18
|
-
//
|
|
19
|
-
// Escape hatch: set NEDB_NO_EXIT_FLUSH=1 to leave signal handling entirely to
|
|
20
|
-
// the host app (it can still call `db.flush()` itself).
|
|
21
|
-
//
|
|
22
|
-
// © INTERCHAINED LLC × Vex (Interchained AI fleet: GLM · Claude · Opus · Fable · GPT-6)
|
|
23
|
-
const native = require('./native.js');
|
|
24
|
-
|
|
25
|
-
const Native = native.NedbCore;
|
|
26
|
-
|
|
27
|
-
// The napi class defines `open` as a NON-writable, NON-configurable static, so the
|
|
28
|
-
// 2.5.x wrapper's `NedbCore.open = …` threw ("Cannot assign to read only property")
|
|
29
|
-
// — and CI's `napi build` was overwriting this file with the generated loader
|
|
30
|
-
// anyway, so the published package never carried the wrapper at all. Both fixed
|
|
31
|
-
// in 2.8.5: wrap by SUBCLASS (an own static on the subclass shadows the parent's),
|
|
32
|
-
// build with `--js native.js` so this file survives, and gate the publish on
|
|
33
|
-
// `NedbCore.__exitFlushWrapped` (see test/durability.test.mjs).
|
|
34
|
-
let NedbCore = Native;
|
|
35
|
-
if (Native && typeof Native.open === 'function' && !Native.__exitFlushWrapped) {
|
|
36
|
-
// Durable handles opened in this process. Strong refs: a durable DB is meant to
|
|
37
|
-
// live for the process, and we must be able to flush it on the way out.
|
|
38
|
-
const live = new Set();
|
|
39
|
-
let armed = false;
|
|
40
|
-
|
|
41
|
-
const flushAll = () => {
|
|
42
|
-
for (const db of live) {
|
|
43
|
-
try {
|
|
44
|
-
db.flush();
|
|
45
|
-
} catch (_) {
|
|
46
|
-
// Best-effort on shutdown — never throw out of an exit handler.
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const arm = () => {
|
|
52
|
-
if (armed || process.env.NEDB_NO_EXIT_FLUSH) return;
|
|
53
|
-
armed = true;
|
|
54
|
-
// 'exit' fires on normal termination; handlers must be synchronous, and
|
|
55
|
-
// db.flush() is a synchronous native call — so this is safe and sufficient
|
|
56
|
-
// for clean exits and uncaught-exception exits.
|
|
57
|
-
process.on('exit', flushAll);
|
|
58
|
-
// Registering a SIGINT/SIGTERM listener SUPPRESSES Node's default
|
|
59
|
-
// termination, so once we listen we own the exit: flush, then terminate with
|
|
60
|
-
// the conventional 128+signum status.
|
|
61
|
-
const onSignal = (signum) => () => {
|
|
62
|
-
flushAll();
|
|
63
|
-
process.exit(128 + signum);
|
|
64
|
-
};
|
|
65
|
-
process.on('SIGINT', onSignal(2));
|
|
66
|
-
process.on('SIGTERM', onSignal(15));
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
NedbCore = class NedbCore extends Native {
|
|
70
|
-
static open(path) {
|
|
71
|
-
const db = Native.open(path);
|
|
72
|
-
live.add(db);
|
|
73
|
-
arm();
|
|
74
|
-
return db;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
// Mark so a re-require (or a wrapped re-export) never double-wraps.
|
|
78
|
-
Object.defineProperty(NedbCore, '__exitFlushWrapped', { value: true, enumerable: false });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
module.exports = { ...native, NedbCore };
|
|
82
|
-
// Explicit named re-export so ESM `import { NedbCore } from 'nedb-engine'` (used
|
|
83
|
-
// by the test suite) resolves the class through cjs-module-lexer.
|
|
84
|
-
module.exports.NedbCore = NedbCore;
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 INTERCHAINED LLC
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
// NEDB · © 2026 INTERCHAINED LLC × Eth-Interchained × Vex (Claude Opus 5)
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
// nedb-engine — durable-mode auto-flush-on-exit wrapper.
|
|
7
|
+
//
|
|
8
|
+
// The native addon (generated napi binding in ./native.js) exposes `NedbCore`.
|
|
9
|
+
// A durable `NedbCore.open(path)` buffers writes in the engine's id-index WAL and
|
|
10
|
+
// only makes them durable on `flush()`; a hard exit (Ctrl+C, `SIGTERM` from an
|
|
11
|
+
// orchestrator, `pm2 stop`) that never runs an explicit flush would lose writes
|
|
12
|
+
// staged since the last flush.
|
|
13
|
+
//
|
|
14
|
+
// We close that gap the libuv-cooperative way — `process.on('SIGINT'|'SIGTERM'
|
|
15
|
+
// |'exit', () => db.flush())` — NOT a C-level signal handler inside the addon,
|
|
16
|
+
// which would clobber libuv's own signal machinery. In-memory databases
|
|
17
|
+
// (`new NedbCore()`) are never armed; there is nothing to flush.
|
|
18
|
+
//
|
|
19
|
+
// Escape hatch: set NEDB_NO_EXIT_FLUSH=1 to leave signal handling entirely to
|
|
20
|
+
// the host app (it can still call `db.flush()` itself).
|
|
21
|
+
//
|
|
22
|
+
// © INTERCHAINED LLC × Vex (Interchained AI fleet: GLM · Claude · Opus · Fable · GPT-6)
|
|
23
|
+
const native = require('./native.js');
|
|
24
|
+
|
|
25
|
+
const Native = native.NedbCore;
|
|
26
|
+
|
|
27
|
+
// The napi class defines `open` as a NON-writable, NON-configurable static, so the
|
|
28
|
+
// 2.5.x wrapper's `NedbCore.open = …` threw ("Cannot assign to read only property")
|
|
29
|
+
// — and CI's `napi build` was overwriting this file with the generated loader
|
|
30
|
+
// anyway, so the published package never carried the wrapper at all. Both fixed
|
|
31
|
+
// in 2.8.5: wrap by SUBCLASS (an own static on the subclass shadows the parent's),
|
|
32
|
+
// build with `--js native.js` so this file survives, and gate the publish on
|
|
33
|
+
// `NedbCore.__exitFlushWrapped` (see test/durability.test.mjs).
|
|
34
|
+
let NedbCore = Native;
|
|
35
|
+
if (Native && typeof Native.open === 'function' && !Native.__exitFlushWrapped) {
|
|
36
|
+
// Durable handles opened in this process. Strong refs: a durable DB is meant to
|
|
37
|
+
// live for the process, and we must be able to flush it on the way out.
|
|
38
|
+
const live = new Set();
|
|
39
|
+
let armed = false;
|
|
40
|
+
|
|
41
|
+
const flushAll = () => {
|
|
42
|
+
for (const db of live) {
|
|
43
|
+
try {
|
|
44
|
+
db.flush();
|
|
45
|
+
} catch (_) {
|
|
46
|
+
// Best-effort on shutdown — never throw out of an exit handler.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const arm = () => {
|
|
52
|
+
if (armed || process.env.NEDB_NO_EXIT_FLUSH) return;
|
|
53
|
+
armed = true;
|
|
54
|
+
// 'exit' fires on normal termination; handlers must be synchronous, and
|
|
55
|
+
// db.flush() is a synchronous native call — so this is safe and sufficient
|
|
56
|
+
// for clean exits and uncaught-exception exits.
|
|
57
|
+
process.on('exit', flushAll);
|
|
58
|
+
// Registering a SIGINT/SIGTERM listener SUPPRESSES Node's default
|
|
59
|
+
// termination, so once we listen we own the exit: flush, then terminate with
|
|
60
|
+
// the conventional 128+signum status.
|
|
61
|
+
const onSignal = (signum) => () => {
|
|
62
|
+
flushAll();
|
|
63
|
+
process.exit(128 + signum);
|
|
64
|
+
};
|
|
65
|
+
process.on('SIGINT', onSignal(2));
|
|
66
|
+
process.on('SIGTERM', onSignal(15));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
NedbCore = class NedbCore extends Native {
|
|
70
|
+
static open(path) {
|
|
71
|
+
const db = Native.open(path);
|
|
72
|
+
live.add(db);
|
|
73
|
+
arm();
|
|
74
|
+
return db;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
// Mark so a re-require (or a wrapped re-export) never double-wraps.
|
|
78
|
+
Object.defineProperty(NedbCore, '__exitFlushWrapped', { value: true, enumerable: false });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { ...native, NedbCore };
|
|
82
|
+
// Explicit named re-export so ESM `import { NedbCore } from 'nedb-engine'` (used
|
|
83
|
+
// by the test suite) resolves the class through cjs-module-lexer.
|
|
84
|
+
module.exports.NedbCore = NedbCore;
|
package/nedb.darwin-arm64.node
CHANGED
|
Binary file
|
package/nedb.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/nedb.linux-x64-gnu.node
CHANGED
|
Binary file
|
package/nedb.linux-x64-musl.node
CHANGED
|
Binary file
|
package/nedb.win32-x64-msvc.node
CHANGED
|
Binary file
|
package/nedbd-v2-darwin-arm64
CHANGED
|
Binary file
|
package/nedbd-v2-darwin-x64
CHANGED
|
Binary file
|
package/nedbd-v2-linux-arm64
CHANGED
|
Binary file
|
|
Binary file
|
package/nedbd-v2-linux-x64
CHANGED
|
Binary file
|
package/nedbd-v2-linux-x64-musl
CHANGED
|
Binary file
|
package/nedbd-v2-win-x64.exe
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nedb-engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "NEDB \u2014 hash-chained, time-traveling, bi-temporal embedded database with Rust native core. SQL, Redis, MongoDB adapters. Causal Write Provenance. RESP2 wire protocol.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|