@reddb-io/browser 0.0.0-stage → 0.1.0-alpha.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/LICENSE +111 -0
- package/README.md +334 -2
- package/package.json +44 -4
- package/scripts/verify-dist.mjs +12 -0
- package/src/index.d.ts +63 -0
- package/src/index.js +150 -0
- package/src/protocol.js +10 -0
- package/src/wasm/reddb_wasm.d.ts +89 -0
- package/src/wasm/reddb_wasm.js +497 -0
- package/src/wasm/reddb_wasm_bg.wasm +0 -0
- package/src/wasm/reddb_wasm_bg.wasm.d.ts +30 -0
- package/src/worker.js +127 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: RedDB.io <hello@reddb.io>
|
|
6
|
+
|
|
7
|
+
Licensed Work: RedDB
|
|
8
|
+
The Licensed Work is (c) 2026 RedDB.io
|
|
9
|
+
|
|
10
|
+
Additional Use Grant: You may make use of the Licensed Work, provided that
|
|
11
|
+
you do not use the Licensed Work for a Database
|
|
12
|
+
Service.
|
|
13
|
+
|
|
14
|
+
A "Database Service" is a commercial offering that
|
|
15
|
+
allows third parties (other than your employees and
|
|
16
|
+
individual contractors) to access the functionality
|
|
17
|
+
of the Licensed Work by creating tables, documents,
|
|
18
|
+
graphs, vectors, key-value entries, queues, or
|
|
19
|
+
time-series whose schemas and/or data are determined
|
|
20
|
+
by those third parties, or by querying, storing, or
|
|
21
|
+
retrieving data determined by those third parties,
|
|
22
|
+
whether as a hosted, managed, "as-a-service",
|
|
23
|
+
database-as-a-service (DBaaS), platform-as-a-service
|
|
24
|
+
(PaaS), software-as-a-service (SaaS), or cloud
|
|
25
|
+
database product.
|
|
26
|
+
|
|
27
|
+
For clarity, the Additional Use Grant permits, among
|
|
28
|
+
other things, the following uses, none of which
|
|
29
|
+
constitute a Database Service: personal use; internal
|
|
30
|
+
use within your own organization; research and
|
|
31
|
+
educational use; development and testing; use in
|
|
32
|
+
open source projects; and self-hosting the Licensed
|
|
33
|
+
Work for your own organization's own data.
|
|
34
|
+
|
|
35
|
+
Change Date: 2030-06-22
|
|
36
|
+
|
|
37
|
+
Change License: GNU Affero General Public License version 3.0
|
|
38
|
+
|
|
39
|
+
For information about alternative licensing arrangements for the Licensed
|
|
40
|
+
Work, please contact hello@reddb.io.
|
|
41
|
+
|
|
42
|
+
Notice
|
|
43
|
+
|
|
44
|
+
Business Source License 1.1
|
|
45
|
+
|
|
46
|
+
Terms
|
|
47
|
+
|
|
48
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
49
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
50
|
+
Licensor may make an Additional Use Grant, above, permitting limited
|
|
51
|
+
production use.
|
|
52
|
+
|
|
53
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
54
|
+
available distribution of a specific version of the Licensed Work under this
|
|
55
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
56
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
57
|
+
above terminate.
|
|
58
|
+
|
|
59
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
60
|
+
currently in effect as described in this License, you must purchase a
|
|
61
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
62
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
63
|
+
|
|
64
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
65
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
66
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
67
|
+
for each version of the Licensed Work released by Licensor.
|
|
68
|
+
|
|
69
|
+
You must conspicuously display this License on each original or modified copy
|
|
70
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
71
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
72
|
+
License apply to your use of that work.
|
|
73
|
+
|
|
74
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
75
|
+
terminate your rights under this License for the current and all other
|
|
76
|
+
versions of the Licensed Work.
|
|
77
|
+
|
|
78
|
+
This License does not grant you any right in any trademark or logo of
|
|
79
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
80
|
+
Licensor as expressly required by this License).
|
|
81
|
+
|
|
82
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
83
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
84
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
85
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
86
|
+
TITLE.
|
|
87
|
+
|
|
88
|
+
MariaDB hereby grants you permission to use this License's text to license
|
|
89
|
+
your works, and to refer to it using the trademark "Business Source License",
|
|
90
|
+
as long as you comply with the Covenants of Licensor below.
|
|
91
|
+
|
|
92
|
+
Covenants of Licensor
|
|
93
|
+
|
|
94
|
+
In consideration of the right to use this License's text and the "Business
|
|
95
|
+
Source License" name and trademark, Licensor covenants to MariaDB, and to all
|
|
96
|
+
other recipients of the licensed work to be provided by Licensor:
|
|
97
|
+
|
|
98
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
|
99
|
+
or a license that is compatible with GPL Version 2.0 or a later version,
|
|
100
|
+
where "compatible" means that software provided under the Change License can
|
|
101
|
+
be included in a program with software provided under GPL Version 2.0 or a
|
|
102
|
+
later version. Licensor may specify additional Change Licenses without
|
|
103
|
+
limitation.
|
|
104
|
+
|
|
105
|
+
2. To either: (a) specify an additional grant of rights to use that does not
|
|
106
|
+
impose any additional restriction on the right granted in this License, as
|
|
107
|
+
the Additional Use Grant; or (b) insert the text "None".
|
|
108
|
+
|
|
109
|
+
3. To specify a Change Date.
|
|
110
|
+
|
|
111
|
+
4. Not to modify this License in any other way.
|
package/README.md
CHANGED
|
@@ -1,3 +1,335 @@
|
|
|
1
|
-
#
|
|
1
|
+
# RedDB Browser — experimental persistence and query engine
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This experimental package is the first distribution of RedDB Browser. It runs the
|
|
4
|
+
canonical Rust `.rdb` artifact publication/recovery code, RQL parser and shared
|
|
5
|
+
scalar evaluator in a dedicated WebAssembly worker, persisting through a real
|
|
6
|
+
OPFS sync access handle.
|
|
7
|
+
Artifact storage requires a secure context, module workers, OPFS and Web Locks.
|
|
8
|
+
The storage-independent runtime only needs module workers and WebAssembly.
|
|
9
|
+
|
|
10
|
+
The target is a fully offline local database with a JavaScript/TypeScript API.
|
|
11
|
+
The Rust/WASM engine ships with the package; local operation must not require a
|
|
12
|
+
RedDB server, account, CDN or external API. Applications bundle the JS, worker
|
|
13
|
+
and WASM assets and make them available locally or through their own offline
|
|
14
|
+
application cache. Sync is optional and must never gate local reads or writes.
|
|
15
|
+
|
|
16
|
+
**It is not a database SDK yet.** It executes storage-free scalar SELECTs;
|
|
17
|
+
queries over stored data, five-model local transactions, synchronization,
|
|
18
|
+
conflict resolution and forwarding between tabs remain pending. It is published on the `experimental` npm channel and does not replace the remote `@reddb-io/client`.
|
|
19
|
+
|
|
20
|
+
## Use from JS/TS
|
|
21
|
+
|
|
22
|
+
See the [application guide](https://github.com/reddb-io/reddb/blob/main/docs/clients/browser.md) for installation,
|
|
23
|
+
Vite/React/Vue/Svelte, plain JavaScript, Worker ownership and offline assets.
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm add @reddb-io/browser@experimental
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { openArtifact } from '@reddb-io/browser'
|
|
31
|
+
import RedDBWorker from '@reddb-io/browser/worker?worker'
|
|
32
|
+
|
|
33
|
+
const artifact = await openArtifact({ name: 'my-app', worker: new RedDBWorker() })
|
|
34
|
+
try {
|
|
35
|
+
console.log(await artifact.executeScalar('SELECT 42 AS answer'))
|
|
36
|
+
} finally {
|
|
37
|
+
await artifact.close()
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Consumers receive built JS/Worker/WASM assets and TypeScript declarations; they
|
|
42
|
+
need no Rust toolchain. The commands below are for engine/package contributors.
|
|
43
|
+
|
|
44
|
+
## Shared runtime and language interop
|
|
45
|
+
|
|
46
|
+
`createRuntime({ worker? })` opens no file and requires neither OPFS nor Web
|
|
47
|
+
Locks. It exposes `info`, `validateQuery`, `executeScalar` and `close`:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { createRuntime } from '@reddb-io/browser'
|
|
51
|
+
import RedDBWorker from '@reddb-io/browser/worker?worker'
|
|
52
|
+
const runtime = await createRuntime({ worker: new RedDBWorker() })
|
|
53
|
+
try {
|
|
54
|
+
console.log(await runtime.info())
|
|
55
|
+
console.log(await runtime.executeScalar('SELECT 6 * 7 AS answer'))
|
|
56
|
+
} finally { await runtime.close() }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The versioned Worker/WASM contract reports `abiVersion: 1`,
|
|
60
|
+
`valueEncoding: 'reddb-json-v1'` and capabilities. Currently `queryValidation`
|
|
61
|
+
and `scalarQueries` are true; `storedQueries`, `transactions` and `sync` are
|
|
62
|
+
false. Mismatched SDK/Worker/WASM versions fail with `VERSION_MISMATCH` before
|
|
63
|
+
opening storage. Cache the JS, Worker and WASM as one versioned unit.
|
|
64
|
+
|
|
65
|
+
`createInteropRuntime({ worker? })` returns a synchronous bridge object with
|
|
66
|
+
`invoke(method, params?)` and `close()`. `invoke` accepts `info`, `validateQuery`
|
|
67
|
+
and `executeScalar` (queries take `{ sql }`) and resolves JSON-safe envelopes:
|
|
68
|
+
`{ ok: true, result }` or `{ ok: false, error: { code, message } }`.
|
|
69
|
+
A void result is `null`; exact integer/decimal tags are unchanged. `close()` is
|
|
70
|
+
idempotent and cancels pending initialization/operations. Calls after close
|
|
71
|
+
return `CLOSED`. Each bridge owns a dedicated Worker.
|
|
72
|
+
|
|
73
|
+
Source adapters use this same bridge for
|
|
74
|
+
[Dart/Flutter Web](https://github.com/reddb-io/reddb/blob/main/drivers/dart/BROWSER.md) and
|
|
75
|
+
[C#/Blazor](https://github.com/reddb-io/reddb/blob/main/drivers/dotnet/src/Reddb.Browser/README.md). They are not yet
|
|
76
|
+
published on pub.dev/NuGet. Their real compiled browser probes exercise scalar
|
|
77
|
+
execution, exact values, errors and disposal online and offline. Full offline
|
|
78
|
+
browser restarts are verified on Chromium/Firefox; Playwright WPE persistent
|
|
79
|
+
profiles lack working CacheStorage, so Safari/iOS qualification remains pending. Run `pnpm --filter @reddb-io/browser build:interop` followed by
|
|
80
|
+
`pnpm --filter @reddb-io/browser test:interop` with Dart 3.13.7 and .NET 8.0.426.
|
|
81
|
+
These adapters do not expose artifact storage or qualify complete offline apps.
|
|
82
|
+
|
|
83
|
+
## Build and verify
|
|
84
|
+
|
|
85
|
+
Install the repository-pinned Rust toolchain, its `wasm32-unknown-unknown` target,
|
|
86
|
+
and `wasm-bindgen-cli` **0.2.120**. Install workspace dependencies with pnpm.
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
pnpm --filter @reddb-io/browser build
|
|
90
|
+
pnpm --filter @reddb-io/browser exec playwright install --with-deps chromium firefox webkit
|
|
91
|
+
pnpm --filter @reddb-io/browser test
|
|
92
|
+
cargo test --locked -p reddb-io-file
|
|
93
|
+
cargo test --locked -p reddb-io-engine
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The `WASM_BINDGEN` environment variable can point at the pinned CLI executable.
|
|
97
|
+
`REDDB_BROWSER_BROWSERS=chromium` restricts the local browser test matrix; CI runs all
|
|
98
|
+
three engines. Generated JS/WASM bindings are ignored and rebuilt from Rust.
|
|
99
|
+
|
|
100
|
+
The tests probe the browser's actual native APIs. If a port lacks synchronous
|
|
101
|
+
OPFS (currently Playwright's WPE WebKit runner), its persistence tests explicitly
|
|
102
|
+
report skips; the unsupported-browser error path still runs. I/O or format
|
|
103
|
+
failures never cause a skip. Chromium and Firefox exercise persistence and
|
|
104
|
+
cross-tab ownership. WebKit persistence and real Safari/iOS qualification remain
|
|
105
|
+
pending; a green CI run does not imply that coverage.
|
|
106
|
+
|
|
107
|
+
The conformance tests compile a separate test-only WASM artifact and execute the same
|
|
108
|
+
assertions as the native engine tests: scalar goldens/errors, compiled row
|
|
109
|
+
filtering/sorting/projection, joins, aggregation, windows, set operations and
|
|
110
|
+
cancellation. These do not require OPFS, so they run even on browser ports
|
|
111
|
+
without synchronous storage. The same worker also checks the shared 16 KiB page
|
|
112
|
+
layout, checksum corruption detection, freelist trunk recovery, pinned-page
|
|
113
|
+
eviction and dirty writeback. Native unit tests for these primitives run from
|
|
114
|
+
`reddb-engine`; native module paths retain the same types, with pager restart
|
|
115
|
+
coverage. The native pager now shares byte I/O, sealed superblock writes and
|
|
116
|
+
in-file double-write staging/recovery over `VfsFile` with WASM. Shared fixtures
|
|
117
|
+
interrupt each write/flush in page publication, root publication and recovery,
|
|
118
|
+
and check short reads, checksums and bounded frames. A test-only OPFS worker
|
|
119
|
+
recovers a torn page after a full browser restart with networking disabled
|
|
120
|
+
and the HTTP host disconnected.
|
|
121
|
+
Those fixtures are excluded from the production SDK. Pager orchestration now
|
|
122
|
+
also lives in `reddb-engine`: cached I/O, deferred reclamation, freelist and
|
|
123
|
+
allocation fences, physical-tail reservation and checkpoint markers. The native
|
|
124
|
+
adapter supplies file locks, encryption and its WAL writer through a synchronous
|
|
125
|
+
barrier; portable pagers without a WAL reject nonzero page LSNs. Shared native/WASM
|
|
126
|
+
fixtures cover failed allocation fences and WAL barriers. The OPFS restart fixture
|
|
127
|
+
uses this complete pager and verifies checkpoint completion through another fresh
|
|
128
|
+
pager. The production JS/TS API is unchanged: storage-backed queries, local
|
|
129
|
+
transactions, runtime WAL/MVCC integration and encryption qualification remain pending.
|
|
130
|
+
Native differential tests additionally compare
|
|
131
|
+
the scalar corpus to actual `RedDBRuntime` query results.
|
|
132
|
+
|
|
133
|
+
The shared core also owns the native B-tree/cursor, compressed value layout and
|
|
134
|
+
overflow chains. Native APIs alias these implementations through a page-access
|
|
135
|
+
contract; the browser executes them in test-only WASM fixtures. Conformance covers
|
|
136
|
+
splits, scans, seeks, updates/deletes, large values and reclamation. An additional
|
|
137
|
+
OPFS test mutates a native-created tree, restarts the browser with the HTTP host
|
|
138
|
+
stopped, and reopens the exported file natively. The fixture includes native
|
|
139
|
+
encoded entities as well as opaque B-tree values. These lower-level records do
|
|
140
|
+
not supply runtime collection schemas or atomic database transactions. No raw
|
|
141
|
+
tree API is added to the production SDK.
|
|
142
|
+
|
|
143
|
+
## Current query contract
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
const artifact = await openArtifact({ name: 'example' })
|
|
147
|
+
await artifact.executeScalar('SELECT 2 + 3 * 4 AS answer')
|
|
148
|
+
// { columns: ['answer'], rows: [[14]] }
|
|
149
|
+
await artifact.close()
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`executeScalar` uses the shared Rust evaluator, built-in type/operator/function
|
|
153
|
+
catalogs and output labels. It accepts storage-free scalar SELECT expressions
|
|
154
|
+
and literal LIMIT/OFFSET; unsupported clauses and stored-data queries fail
|
|
155
|
+
explicitly. It never reads or changes the artifact. Integer values outside JS's
|
|
156
|
+
safe range retain canonical `$int`/`$uint` tags instead of rounding. This small
|
|
157
|
+
entrypoint is an execution milestone, not the full RQL/storage runtime.
|
|
158
|
+
|
|
159
|
+
## Current storage contract
|
|
160
|
+
|
|
161
|
+
`openArtifact({ name })` opens or creates `reddb-browser/<name>.rdb` inside the
|
|
162
|
+
origin's private filesystem. It exposes `append`, `readWal`, `checkpoint`,
|
|
163
|
+
`readSnapshot`, `generation`, `validateQuery` and `close`. These are artifact
|
|
164
|
+
primitives: WAL payloads are native binary records, and checkpoint images are
|
|
165
|
+
complete native store snapshots. Passing arbitrary SQL as a payload does not
|
|
166
|
+
execute it. The caller must incorporate committed WAL into a checkpoint image.
|
|
167
|
+
|
|
168
|
+
The browser and native file authority share the same format codecs and write
|
|
169
|
+
ordering. Acknowledgement follows both the WAL flush and root publication flush.
|
|
170
|
+
After a storage operation fails, that Rust handle rejects further I/O until it
|
|
171
|
+
is reopened and recovered. Existing corrupt files are preserved and reported;
|
|
172
|
+
they are never silently replaced. A second open returns `FILE_BUSY`; closing or
|
|
173
|
+
terminating the owning worker releases the lock. Invalid input and parse errors
|
|
174
|
+
do not invalidate the storage handle.
|
|
175
|
+
|
|
176
|
+
The host must provide exclusive ownership when using Rust's `EmbeddedRdbFile`
|
|
177
|
+
directly. The browser wrapper enforces this with Web Locks and OPFS's default
|
|
178
|
+
exclusive access handle. The prototype does not use relaxed/read-write-unsafe
|
|
179
|
+
access, emulate file locks, or require `SharedArrayBuffer` or isolation headers.
|
|
180
|
+
|
|
181
|
+
OPFS is subject to origin quota, storage eviction and explicit site-data clearing.
|
|
182
|
+
Applications can request persistent storage through `navigator.storage.persist()`;
|
|
183
|
+
these primitives do not promise that a browser will grant it or sync after closing.
|
|
184
|
+
|
|
185
|
+
## Offline asset loading
|
|
186
|
+
|
|
187
|
+
The SDK does not register a service worker or control application updates. A
|
|
188
|
+
PWA should include `index.js`, `protocol.js`, `worker.js`, `wasm/reddb_wasm.js` and
|
|
189
|
+
`wasm/reddb_wasm_bg.wasm` from `src/` in its own versioned application cache,
|
|
190
|
+
along with its application shell. Those assets must remain available together
|
|
191
|
+
after installation. A locally packaged host can serve the same assets locally.
|
|
192
|
+
HTTPS or a trusted localhost context is required for OPFS and Web Locks.
|
|
193
|
+
|
|
194
|
+
The browser suite includes a test host's service worker and restarts the entire
|
|
195
|
+
browser with networking disabled. It reloads the JS/worker/WASM assets, recovers
|
|
196
|
+
OPFS data, writes again and executes a scalar query without reaching the HTTP
|
|
197
|
+
host. This verifies the current artifact/scalar surface offline; storage-backed
|
|
198
|
+
queries and application transactions still require the engine work below.
|
|
199
|
+
|
|
200
|
+
## Shared transaction state
|
|
201
|
+
|
|
202
|
+
The canonical native MVCC snapshot manager, visibility predicate, serializable
|
|
203
|
+
read/write conflict tracking and per-connection transaction state now live in
|
|
204
|
+
`reddb-engine::transaction`. The server uses the same types and algorithms.
|
|
205
|
+
Native/WASM fixtures cover snapshot versus read-committed reads, aborted writers,
|
|
206
|
+
savepoint sub-XIDs, failed commit preparation, publication-guard lifetime,
|
|
207
|
+
retained vacuum horizons, checkpoint exclusions, unique-key reservations and
|
|
208
|
+
serializable row/vault predicates with tenant separation. On Chromium/Firefox,
|
|
209
|
+
the test host also caches this separate conformance artifact and reruns the suite
|
|
210
|
+
after a full offline browser restart with the HTTP listener stopped.
|
|
211
|
+
|
|
212
|
+
These are volatile engine assertions in a test-only WASM artifact. They do not
|
|
213
|
+
expose transactions through the JS/TS SDK or qualify durable browser transaction
|
|
214
|
+
recovery. Storage-backed dispatch, write conflict validation, WAL publication and
|
|
215
|
+
recovery still require the runtime integration below.
|
|
216
|
+
|
|
217
|
+
## Shared entities and read visibility
|
|
218
|
+
|
|
219
|
+
The native `UnifiedEntity` model (rows/documents, graph, vectors, time series,
|
|
220
|
+
queues and auxiliary references/embeddings) now lives in
|
|
221
|
+
`reddb-engine::storage::entity`. Native paths re-export those same types.
|
|
222
|
+
Entity timestamps retain Unix **seconds**; WASM obtains the host wall clock from
|
|
223
|
+
`Date.now`, while query timing continues to use the monotonic performance clock.
|
|
224
|
+
The browser test worker independently checks entity/embedding/reference timestamps
|
|
225
|
+
against JS time, including after the offline restart.
|
|
226
|
+
|
|
227
|
+
`SnapshotContext`, own/aborted subtransaction visibility, logical-ID SSI read
|
|
228
|
+
tracking and table-row version selection are also shared. The native store
|
|
229
|
+
supplies candidates through `TableRowVersionStore`; its existing dispatch,
|
|
230
|
+
auth/tenant/moderation gates and statement-frame lifetime remain in the host.
|
|
231
|
+
Native persisted-row tests cover both dump and paged reopen through the shared
|
|
232
|
+
entity/visibility selector. Six new native/WASM cases exercise the representation
|
|
233
|
+
and read rules, repeated offline on Chromium/Firefox. They use in-memory fixtures
|
|
234
|
+
in WASM, not browser-persisted collections.
|
|
235
|
+
|
|
236
|
+
Native value tags, checked decoding and Zstd compression now have one owner in
|
|
237
|
+
`reddb-file::native_value`; the logical entity adapter lives in
|
|
238
|
+
`reddb-engine::storage::entity_codec`. Native dump, paged-record and WAL paths
|
|
239
|
+
use that same adapter. A frozen corpus produced by the original native writer
|
|
240
|
+
checks all store versions V1–V12 and all value variants, including exact integers,
|
|
241
|
+
compressed text/blob values, document bodies, MVCC fields and auxiliary data.
|
|
242
|
+
Four native/WASM conformance cases compare the original bytes and reject
|
|
243
|
+
truncated values, invalid lengths/tags, inconsistent compressed lengths and
|
|
244
|
+
excessive array nesting. Arrays use a checked cursor and an explicit decoding
|
|
245
|
+
stack bounded by the existing JSON nesting limit. This is not a general
|
|
246
|
+
browser memory-budget qualification.
|
|
247
|
+
|
|
248
|
+
The OPFS tree fixture now imports eight encoded native entities, decodes and
|
|
249
|
+
updates their sequence/timestamps in WASM, restarts the browser offline with the
|
|
250
|
+
HTTP host stopped, and exports for native verification. It retains opaque
|
|
251
|
+
metadata bytes without interpreting native metadata policy. A separate native
|
|
252
|
+
dump test checks the complete entity corpus and actual native metadata. These
|
|
253
|
+
remain test-only records without application collection schemas or transactional
|
|
254
|
+
publication. Storage-backed execution and durable runtime transaction/WAL
|
|
255
|
+
integration are still needed before JS/TS can query collections. No file bytes,
|
|
256
|
+
format versions or public application APIs change.
|
|
257
|
+
|
|
258
|
+
## Shared query records and materialization
|
|
259
|
+
|
|
260
|
+
`reddb-engine::query::unified` now owns the native `UnifiedRecord`/`UnifiedResult`
|
|
261
|
+
representation, interned system keys, matched graph records/paths and query
|
|
262
|
+
statistics. Native module paths re-export the exact same types. Native stored
|
|
263
|
+
graph nodes retain their conversion through `From<&StoredNode>`; their storage
|
|
264
|
+
and graph executor remain native.
|
|
265
|
+
|
|
266
|
+
`reddb-engine::query::records` owns the existing full/lean/projected/universal
|
|
267
|
+
entity materializers, by value and by reference, including catalog-schema
|
|
268
|
+
fallback, binary-document field promotion, managed timestamps, logical `rid`,
|
|
269
|
+
KV/document capabilities, graph endpoints, typed time-series tags and identity
|
|
270
|
+
hints. The native runtime calls these same functions. Representation does not
|
|
271
|
+
supply authorization, tenancy, visibility, secret handling, catalog discovery or
|
|
272
|
+
transaction publication; those policies remain with the caller/runtime.
|
|
273
|
+
|
|
274
|
+
A frozen pre-extraction native oracle checks 112 results (eight paths over
|
|
275
|
+
14 entity shapes), preserving existing differences between materialization
|
|
276
|
+
paths. Six relocated native record-layout property tests and four shared
|
|
277
|
+
native/WASM cases cover schema overwrite/overflow, projections, document bodies,
|
|
278
|
+
typed tags, graph representation and identities. The offline OPFS fixture now
|
|
279
|
+
materializes full/projected records from its persisted entities after restart
|
|
280
|
+
and verifies their fields before native export. Native integration compares
|
|
281
|
+
shared records with actual stored-data query results after dump and paged reopen.
|
|
282
|
+
|
|
283
|
+
These are internal/test-only engine paths. The public JS/TS API still exposes
|
|
284
|
+
artifact operations and storage-free scalar SELECTs; stored-data dispatch,
|
|
285
|
+
durable runtime transactions/WAL and application collection catalogs remain
|
|
286
|
+
unfinished. No file format, wire envelope or public browser API changes here.
|
|
287
|
+
|
|
288
|
+
## Shared resident collection storage
|
|
289
|
+
|
|
290
|
+
The growing/sealed segment, typed metadata, key/unique-key and physical graph
|
|
291
|
+
candidate indexes, and resident payload accounting now live in
|
|
292
|
+
`reddb-engine::storage`. Native paths re-export the same implementations. The
|
|
293
|
+
native segment manager retains lifecycle, consolidation, explicit snapshot and
|
|
294
|
+
moderation gates. Storage clocks use Unix seconds on both native and WASM hosts.
|
|
295
|
+
Four shared conformance cases exercise all original record-oracle entity shapes,
|
|
296
|
+
key-version lookup and raw mutation, bounded/cancelled scans, metadata and sealing;
|
|
297
|
+
the browser worker repeats them after the existing offline restart.
|
|
298
|
+
|
|
299
|
+
These are canonical resident storage operations, not durable application
|
|
300
|
+
transactions. WAL publication, catalog dispatch and the application SDK still
|
|
301
|
+
need integration before JS/TS can query and mutate persisted collections. The
|
|
302
|
+
production artifact/scalar API and all file formats remain unchanged.
|
|
303
|
+
|
|
304
|
+
## Shared snapshot recovery
|
|
305
|
+
|
|
306
|
+
Native and WASM now use the same typed metadata/entity-record codecs and streaming
|
|
307
|
+
V1–V12 dump reader. Recovery preserves empty collection registration, record
|
|
308
|
+
metadata, cross-references and the optional V10+ catalog blob. The shared file
|
|
309
|
+
layer verifies the checksum before any restore callback and lends the payload
|
|
310
|
+
without copying the entire snapshot. Hosts must restore into private state and
|
|
311
|
+
publish it only after successful recovery.
|
|
312
|
+
|
|
313
|
+
Frozen pre-extraction native metadata bytes and entity fixtures check compatible
|
|
314
|
+
encoding and decoding. Shared native/WASM cases also cover truncated metadata,
|
|
315
|
+
invalid counts/tags/UTF-8, nesting at the RQL depth limit, checksum failures,
|
|
316
|
+
legacy optional tails and host callback errors. Metadata nesting above the existing
|
|
317
|
+
RQL JSON limit is now rejected; previously the native metadata reader was unbounded.
|
|
318
|
+
The native host retains collection/index restoration, ID allocation and WAL policy.
|
|
319
|
+
These internal recovery primitives do not yet expose stored-data queries or
|
|
320
|
+
application transactions through the browser SDK.
|
|
321
|
+
|
|
322
|
+
## Remaining work toward the approved Browser design
|
|
323
|
+
|
|
324
|
+
1. Extract the remaining storage-coupled executor/runtime into the shared portable engine and
|
|
325
|
+
preserve native query/transaction semantics for tables, documents, KV, graph
|
|
326
|
+
and vectors. Port clocks, crypto randomness and task scheduling.
|
|
327
|
+
2. Build the application-facing SDK, local transaction/outbox atomicity, change
|
|
328
|
+
observation, and one-owner request forwarding between tabs.
|
|
329
|
+
3. Implement versioned, policy-filtered snapshot and resumable change protocols
|
|
330
|
+
in `reddb-wire` and the server. Revalidate normal authorization/RLS on upload;
|
|
331
|
+
preserve transaction grouping, idempotency, stable graph references and deletes.
|
|
332
|
+
4. Add explicit conflict resolution, expired-cursor rebootstrap without losing
|
|
333
|
+
pending intent, identity separation, offline assets and the storage-status UI.
|
|
334
|
+
5. Validate two-device sync and engine conformance, Safari/iOS on real devices,
|
|
335
|
+
quota/failure cases, bundle size, startup, memory and query performance.
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddb-io/browser",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Experimental RedDB browser persistence and shared scalar execution; stored-data queries and sync remain pending.",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.d.ts",
|
|
10
|
+
"import": "./src/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./worker": "./src/worker.js",
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "node scripts/build.mjs",
|
|
17
|
+
"test": "node scripts/build-test.mjs && node --test test/runtime.test.mjs test/browser.test.mjs",
|
|
18
|
+
"prepack": "node scripts/verify-dist.mjs",
|
|
19
|
+
"test:distribution": "node --test test/distribution.test.mjs",
|
|
20
|
+
"build:interop": "node scripts/build-interop.mjs",
|
|
21
|
+
"test:interop": "node --test test/interop.test.mjs"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"playwright": "1.61.1",
|
|
25
|
+
"vite": "8.3.5",
|
|
26
|
+
"typescript": "7.0.2"
|
|
27
|
+
},
|
|
28
|
+
"license": "BUSL-1.1",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/reddb-io/reddb.git",
|
|
32
|
+
"directory": "packages/browser"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/reddb-io/reddb/tree/main/packages/browser",
|
|
35
|
+
"types": "./src/index.d.ts",
|
|
36
|
+
"files": [
|
|
37
|
+
"src/",
|
|
38
|
+
"scripts/verify-dist.mjs",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"tag": "experimental"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const directory = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
6
|
+
for (const path of ['src/index.js', 'src/index.d.ts', 'src/protocol.js', 'src/worker.js', 'src/wasm/reddb_wasm.js', 'LICENSE']) {
|
|
7
|
+
if (!readFileSync(resolve(directory, path)).length) throw new Error(`Missing browser distribution file: ${path}`)
|
|
8
|
+
}
|
|
9
|
+
const wasm = readFileSync(resolve(directory, 'src/wasm/reddb_wasm_bg.wasm'))
|
|
10
|
+
if (wasm.length < 8 || !wasm.subarray(0, 8).equals(Buffer.from([0, 97, 115, 109, 1, 0, 0, 0]))) {
|
|
11
|
+
throw new Error('Build the browser WASM before packing: pnpm --filter @reddb-io/browser build')
|
|
12
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Experimental persistence and scalar execution; stored-data queries and sync remain pending. */
|
|
2
|
+
export class BrowserError extends Error {
|
|
3
|
+
readonly code: string
|
|
4
|
+
constructor(code: string, message: string)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RuntimeInfo {
|
|
8
|
+
readonly abiVersion: 1
|
|
9
|
+
readonly valueEncoding: 'reddb-json-v1'
|
|
10
|
+
readonly capabilities: {
|
|
11
|
+
readonly queryValidation: boolean
|
|
12
|
+
readonly scalarQueries: boolean
|
|
13
|
+
readonly storedQueries: boolean
|
|
14
|
+
readonly transactions: boolean
|
|
15
|
+
readonly sync: boolean
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Runtime {
|
|
20
|
+
/** Compiled engine capabilities; does not imply OPFS availability on this host. */
|
|
21
|
+
info(): Promise<RuntimeInfo>
|
|
22
|
+
/** Parse with the canonical RQL front-end; does not execute the statement. */
|
|
23
|
+
validateQuery(sql: string): Promise<void>
|
|
24
|
+
/** Storage-free scalar SELECT only; does not read or mutate the artifact. */
|
|
25
|
+
executeScalar(sql: string): Promise<{ columns: string[]; rows: unknown[][] }>
|
|
26
|
+
/** Terminate the storage-independent worker, rejecting pending operations. */
|
|
27
|
+
close(): Promise<void>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type InteropResult =
|
|
31
|
+
| { ok: true; result: unknown }
|
|
32
|
+
| { ok: false; error: { code: string; message: string } }
|
|
33
|
+
|
|
34
|
+
export interface RuntimeBridge {
|
|
35
|
+
/** info, validateQuery or executeScalar. Failures resolve as structured envelopes. */
|
|
36
|
+
invoke(method: string, params?: Record<string, unknown>): Promise<InteropResult>
|
|
37
|
+
/** Synchronous, idempotent cancellation, including during initialization. */
|
|
38
|
+
close(): void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** No OPFS or Web Locks required; scalar execution only. */
|
|
42
|
+
export function createRuntime(options?: { worker?: Worker }): Promise<Runtime>
|
|
43
|
+
/** JSON-safe adapter boundary; does not expose artifact mutation or stored-data queries. */
|
|
44
|
+
export function createInteropRuntime(options?: { worker?: Worker }): RuntimeBridge
|
|
45
|
+
|
|
46
|
+
export interface Artifact extends Runtime {
|
|
47
|
+
generation(): Promise<string>
|
|
48
|
+
readWal(): Promise<Uint8Array[]>
|
|
49
|
+
readSnapshot(): Promise<Uint8Array | undefined>
|
|
50
|
+
/** Low-level native WAL payloads, not SQL commands. */
|
|
51
|
+
append(payloads: Uint8Array[]): Promise<void>
|
|
52
|
+
/** Complete native store snapshot; the caller must fold in the committed WAL. */
|
|
53
|
+
checkpoint(snapshot: Uint8Array, minWalBytes?: number): Promise<void>
|
|
54
|
+
/** Flush/close through the owner before terminating the worker. */
|
|
55
|
+
close(): Promise<void>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Open/create an exclusively owned OPFS .rdb file. Another owner returns FILE_BUSY. */
|
|
59
|
+
export function openArtifact(options: {
|
|
60
|
+
name: string
|
|
61
|
+
/** Fresh module Worker supplied by a bundler (e.g. Vite ?worker). The artifact owns its lifetime. */
|
|
62
|
+
worker?: Worker
|
|
63
|
+
}): Promise<Artifact>
|