@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 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
- # Temporary Holding Version
1
+ # RedDB Browser — experimental persistence and query engine
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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>