@serfab/quereus-plugin-sereus 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +367 -360
- package/dist/cached-storage.d.ts +66 -0
- package/dist/cached-storage.js +103 -0
- package/dist/cached-storage.js.map +1 -0
- package/dist/cluster-size.d.ts +231 -0
- package/dist/cluster-size.js +240 -0
- package/dist/cluster-size.js.map +1 -0
- package/dist/compose-strand.d.ts +8 -2
- package/dist/compose-strand.js +23 -23
- package/dist/compose-strand.js.map +1 -1
- package/dist/connect-browser.js +2 -1
- package/dist/connect-browser.js.map +1 -1
- package/dist/connect.js +2 -1
- package/dist/connect.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parse-config.js +23 -11
- package/dist/parse-config.js.map +1 -1
- package/dist/plugin-browser.js +76684 -75082
- package/dist/plugin-browser.js.map +4 -4
- package/dist/strand-schema.d.ts +1 -1
- package/dist/strand-schema.js +398 -35
- package/dist/strand-schema.js.map +1 -1
- package/dist/types.d.ts +46 -10
- package/package.json +8 -8
- package/src/cached-storage.ts +107 -0
- package/src/cluster-size.ts +248 -0
- package/src/compose-strand.ts +33 -24
- package/src/connect-browser.ts +57 -56
- package/src/connect.ts +38 -37
- package/src/index.ts +10 -1
- package/src/parse-config.ts +70 -55
- package/src/plugin-browser.ts +29 -29
- package/src/plugin.ts +39 -39
- package/src/strand-schema.ts +398 -35
- package/src/types.ts +106 -69
package/README.md
CHANGED
|
@@ -1,360 +1,367 @@
|
|
|
1
|
-
# @serfab/quereus-plugin-sereus
|
|
2
|
-
|
|
3
|
-
A [Quereus](https://github.com/nicktobey/quereus) plugin that connects a Quereus
|
|
4
|
-
database to a [Sereus](../../docs/architecture.md) strand for SQL access. It
|
|
5
|
-
composes the [@optimystic/quereus-plugin-crypto](https://github.com/nicktobey/optimystic/tree/main/packages/quereus-plugin-crypto)
|
|
6
|
-
and [@optimystic/quereus-plugin-optimystic](https://github.com/nicktobey/optimystic/tree/main/packages/quereus-plugin-optimystic)
|
|
7
|
-
plugins, manages libp2p networking, and optionally applies a sApp schema.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- **Strand connection**: Connect a Quereus database to a Sereus strand with a single call.
|
|
12
|
-
- **Plugin composition**: Automatically registers the crypto and optimystic plugins.
|
|
13
|
-
- **Node management**: Creates a libp2p node or accepts an injected one (e.g. from a CadreNode).
|
|
14
|
-
- **Schema application**: Wraps DDL in `declare schema App { ... } apply schema App;`.
|
|
15
|
-
- **Two runtimes**: A Node entry (`./plugin`) and a self-contained browser bundle
|
|
16
|
-
(`./plugin-browser`) for Quoomb-web and other browser/worker hosts.
|
|
17
|
-
- **Two entry points**: A plugin-loader-compatible default export and a
|
|
18
|
-
programmatic `connectToStrand()` API.
|
|
19
|
-
|
|
20
|
-
## Artifacts
|
|
21
|
-
|
|
22
|
-
The package ships two plugin artifacts, selected automatically by the consumer's
|
|
23
|
-
environment via the `exports` map:
|
|
24
|
-
|
|
25
|
-
| Artifact | Built by | Transports | Default storage | Use from |
|
|
26
|
-
|---|---|---|---|---|
|
|
27
|
-
| `dist/plugin.js` | `tsc` | TCP + WebSockets | in-memory / FS | Node (`./plugin`) |
|
|
28
|
-
| `dist/plugin-browser.js` | `esbuild` | WebSockets + circuit-relay | IndexedDB | Browser/worker (`./plugin-browser`) |
|
|
29
|
-
|
|
30
|
-
The browser bundle is a single self-contained ESM file (~2.5 MiB raw, ~550 KiB
|
|
31
|
-
gzipped). It bundles only the TCP-free libp2p surface (`@optimystic/db-p2p/rn`)
|
|
32
|
-
and does **not** import `@quereus/quereus` at runtime — registrations are applied
|
|
33
|
-
against the host-supplied `db`, so it reuses the host's Quereus instance rather
|
|
34
|
-
than instantiating a second one.
|
|
35
|
-
|
|
36
|
-
## Installation
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npm install @serfab/quereus-plugin-sereus
|
|
40
|
-
# or
|
|
41
|
-
yarn add @serfab/quereus-plugin-sereus
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Using it from Quoomb Web (step by step)
|
|
47
|
-
|
|
48
|
-
[Quoomb Web](../../../quereus/packages/quoomb-web) loads plugins by URL: its
|
|
49
|
-
worker calls `import(url)` and registers the module's default export against the
|
|
50
|
-
live database. To connect Quoomb Web to a strand you need to (1) make the
|
|
51
|
-
**browser bundle** reachable at an importable URL, and (2) give it a strand id
|
|
52
|
-
and at least one **browser-dialable** bootstrap address.
|
|
53
|
-
|
|
54
|
-
### Prerequisites
|
|
55
|
-
|
|
56
|
-
- A **strand id** (UUID) to connect to.
|
|
57
|
-
- For networked use: a libp2p **bootstrap/relay reachable over secure WebSockets**
|
|
58
|
-
(`/wss`) from an `https://` page. Browsers cannot dial raw TCP, and a plain
|
|
59
|
-
`ws://`/`tcp` address will not dial from a TLS origin. WebSockets +
|
|
60
|
-
circuit-relay are the only supported transports in v1.
|
|
61
|
-
- The strand's **sApp schema** (optional — only needed to bootstrap a brand-new
|
|
62
|
-
strand or to expose `App.*` tables before any peer has).
|
|
63
|
-
|
|
64
|
-
### Step 1 — Make the browser bundle importable
|
|
65
|
-
|
|
66
|
-
Pick one of the hosting options below ([Hosting the browser
|
|
67
|
-
bundle](#hosting-the-browser-bundle)). The quickest are:
|
|
68
|
-
|
|
69
|
-
- **Published CDN URL** (recommended once published):
|
|
70
|
-
`https://cdn.jsdelivr.net/npm/@serfab/quereus-plugin-sereus/dist/plugin-browser.js`
|
|
71
|
-
- **Local dev**: copy `dist/plugin-browser.js` into Quoomb Web's `public/` folder
|
|
72
|
-
so it is served same-origin at `http://localhost:5173/plugin-browser.js`.
|
|
73
|
-
|
|
74
|
-
The URL must serve the file with `Content-Type: text/javascript` (or
|
|
75
|
-
`application/javascript`) and CORS that permits the page origin.
|
|
76
|
-
|
|
77
|
-
### Step 2a — Load via the Plugins panel (manual)
|
|
78
|
-
|
|
79
|
-
1. Open Quoomb Web → **Plugins** panel.
|
|
80
|
-
2. Paste the bundle URL into the plugin URL field and install it.
|
|
81
|
-
- The manual installer validates the URL and only accepts **`https://`** (or
|
|
82
|
-
`file://`) ending in `.js`/`.mjs`. A same-origin `http://localhost` URL is
|
|
83
|
-
**rejected here** — use Step 2b (config) for local-dev http, or serve
|
|
84
|
-
Quoomb over https.
|
|
85
|
-
3. Open the plugin's config and set at least:
|
|
86
|
-
- `strand_id` — the strand UUID.
|
|
87
|
-
- `bootstrap_nodes` — comma-separated `/wss` multiaddrs.
|
|
88
|
-
- `schema` — optional sApp DDL.
|
|
89
|
-
4. Run a query, e.g. `select * from App.Message`.
|
|
90
|
-
|
|
91
|
-
### Step 2b — Load via `quoomb.config.json` (autoload)
|
|
92
|
-
|
|
93
|
-
The config/autoload path calls the loader directly and **bypasses the
|
|
94
|
-
https-only check**, so it is the right choice for local `http://localhost`
|
|
95
|
-
during development. In Quoomb Web, open the **Config** modal and import this
|
|
96
|
-
JSON (or set it as `quoomb.config.json`):
|
|
97
|
-
|
|
98
|
-
```jsonc
|
|
99
|
-
{
|
|
100
|
-
"plugins": [
|
|
101
|
-
{
|
|
102
|
-
"source": "https://cdn.jsdelivr.net/npm/@serfab/quereus-plugin-sereus/dist/plugin-browser.js",
|
|
103
|
-
"config": {
|
|
104
|
-
"strand_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
105
|
-
"bootstrap_nodes": "/dns4/relay.example/tcp/443/wss/p2p/Qm...",
|
|
106
|
-
"schema": "table Message (Id integer primary key, Content text not null)"
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
],
|
|
110
|
-
"autoload": true
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
With `autoload: true`, the plugin loads on session start and applies the schema.
|
|
115
|
-
|
|
116
|
-
### Step 3 — Query the strand
|
|
117
|
-
|
|
118
|
-
Once loaded, the `optimystic` vtab is the default and `App.*` tables resolve
|
|
119
|
-
against the strand:
|
|
120
|
-
|
|
121
|
-
```sql
|
|
122
|
-
select * from App.Message;
|
|
123
|
-
insert into App.Message (Id, Content) values (1, 'hello strand');
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Notes & limitations
|
|
127
|
-
|
|
128
|
-
- **Storage.** When `storage` is not injected, the browser bundle opens a default
|
|
129
|
-
`IndexedDBRawStorage` against an IndexedDB database named
|
|
130
|
-
`sereus-strand-<strandId>`. State survives reload. The plugin treats the
|
|
131
|
-
IndexedDB handle as borrowed and does **not** close it on `shutdown()` — its
|
|
132
|
-
lifecycle is the page/worker.
|
|
133
|
-
- **
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- jsDelivr
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
import {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
import
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
|
277
|
-
|
|
278
|
-
| `
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `
|
|
282
|
-
| `
|
|
283
|
-
| `
|
|
284
|
-
| `
|
|
285
|
-
| `
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
|
295
|
-
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
298
|
-
| `
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
`
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
`
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
The
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
publish
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
1
|
+
# @serfab/quereus-plugin-sereus
|
|
2
|
+
|
|
3
|
+
A [Quereus](https://github.com/nicktobey/quereus) plugin that connects a Quereus
|
|
4
|
+
database to a [Sereus](../../docs/architecture.md) strand for SQL access. It
|
|
5
|
+
composes the [@optimystic/quereus-plugin-crypto](https://github.com/nicktobey/optimystic/tree/main/packages/quereus-plugin-crypto)
|
|
6
|
+
and [@optimystic/quereus-plugin-optimystic](https://github.com/nicktobey/optimystic/tree/main/packages/quereus-plugin-optimystic)
|
|
7
|
+
plugins, manages libp2p networking, and optionally applies a sApp schema.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Strand connection**: Connect a Quereus database to a Sereus strand with a single call.
|
|
12
|
+
- **Plugin composition**: Automatically registers the crypto and optimystic plugins.
|
|
13
|
+
- **Node management**: Creates a libp2p node or accepts an injected one (e.g. from a CadreNode).
|
|
14
|
+
- **Schema application**: Wraps DDL in `declare schema App { ... } apply schema App;`.
|
|
15
|
+
- **Two runtimes**: A Node entry (`./plugin`) and a self-contained browser bundle
|
|
16
|
+
(`./plugin-browser`) for Quoomb-web and other browser/worker hosts.
|
|
17
|
+
- **Two entry points**: A plugin-loader-compatible default export and a
|
|
18
|
+
programmatic `connectToStrand()` API.
|
|
19
|
+
|
|
20
|
+
## Artifacts
|
|
21
|
+
|
|
22
|
+
The package ships two plugin artifacts, selected automatically by the consumer's
|
|
23
|
+
environment via the `exports` map:
|
|
24
|
+
|
|
25
|
+
| Artifact | Built by | Transports | Default storage | Use from |
|
|
26
|
+
|---|---|---|---|---|
|
|
27
|
+
| `dist/plugin.js` | `tsc` | TCP + WebSockets | in-memory / FS | Node (`./plugin`) |
|
|
28
|
+
| `dist/plugin-browser.js` | `esbuild` | WebSockets + circuit-relay | IndexedDB | Browser/worker (`./plugin-browser`) |
|
|
29
|
+
|
|
30
|
+
The browser bundle is a single self-contained ESM file (~2.5 MiB raw, ~550 KiB
|
|
31
|
+
gzipped). It bundles only the TCP-free libp2p surface (`@optimystic/db-p2p/rn`)
|
|
32
|
+
and does **not** import `@quereus/quereus` at runtime — registrations are applied
|
|
33
|
+
against the host-supplied `db`, so it reuses the host's Quereus instance rather
|
|
34
|
+
than instantiating a second one.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @serfab/quereus-plugin-sereus
|
|
40
|
+
# or
|
|
41
|
+
yarn add @serfab/quereus-plugin-sereus
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Using it from Quoomb Web (step by step)
|
|
47
|
+
|
|
48
|
+
[Quoomb Web](../../../quereus/packages/quoomb-web) loads plugins by URL: its
|
|
49
|
+
worker calls `import(url)` and registers the module's default export against the
|
|
50
|
+
live database. To connect Quoomb Web to a strand you need to (1) make the
|
|
51
|
+
**browser bundle** reachable at an importable URL, and (2) give it a strand id
|
|
52
|
+
and at least one **browser-dialable** bootstrap address.
|
|
53
|
+
|
|
54
|
+
### Prerequisites
|
|
55
|
+
|
|
56
|
+
- A **strand id** (UUID) to connect to.
|
|
57
|
+
- For networked use: a libp2p **bootstrap/relay reachable over secure WebSockets**
|
|
58
|
+
(`/wss`) from an `https://` page. Browsers cannot dial raw TCP, and a plain
|
|
59
|
+
`ws://`/`tcp` address will not dial from a TLS origin. WebSockets +
|
|
60
|
+
circuit-relay are the only supported transports in v1.
|
|
61
|
+
- The strand's **sApp schema** (optional — only needed to bootstrap a brand-new
|
|
62
|
+
strand or to expose `App.*` tables before any peer has).
|
|
63
|
+
|
|
64
|
+
### Step 1 — Make the browser bundle importable
|
|
65
|
+
|
|
66
|
+
Pick one of the hosting options below ([Hosting the browser
|
|
67
|
+
bundle](#hosting-the-browser-bundle)). The quickest are:
|
|
68
|
+
|
|
69
|
+
- **Published CDN URL** (recommended once published):
|
|
70
|
+
`https://cdn.jsdelivr.net/npm/@serfab/quereus-plugin-sereus/dist/plugin-browser.js`
|
|
71
|
+
- **Local dev**: copy `dist/plugin-browser.js` into Quoomb Web's `public/` folder
|
|
72
|
+
so it is served same-origin at `http://localhost:5173/plugin-browser.js`.
|
|
73
|
+
|
|
74
|
+
The URL must serve the file with `Content-Type: text/javascript` (or
|
|
75
|
+
`application/javascript`) and CORS that permits the page origin.
|
|
76
|
+
|
|
77
|
+
### Step 2a — Load via the Plugins panel (manual)
|
|
78
|
+
|
|
79
|
+
1. Open Quoomb Web → **Plugins** panel.
|
|
80
|
+
2. Paste the bundle URL into the plugin URL field and install it.
|
|
81
|
+
- The manual installer validates the URL and only accepts **`https://`** (or
|
|
82
|
+
`file://`) ending in `.js`/`.mjs`. A same-origin `http://localhost` URL is
|
|
83
|
+
**rejected here** — use Step 2b (config) for local-dev http, or serve
|
|
84
|
+
Quoomb over https.
|
|
85
|
+
3. Open the plugin's config and set at least:
|
|
86
|
+
- `strand_id` — the strand UUID.
|
|
87
|
+
- `bootstrap_nodes` — comma-separated `/wss` multiaddrs.
|
|
88
|
+
- `schema` — optional sApp DDL.
|
|
89
|
+
4. Run a query, e.g. `select * from App.Message`.
|
|
90
|
+
|
|
91
|
+
### Step 2b — Load via `quoomb.config.json` (autoload)
|
|
92
|
+
|
|
93
|
+
The config/autoload path calls the loader directly and **bypasses the
|
|
94
|
+
https-only check**, so it is the right choice for local `http://localhost`
|
|
95
|
+
during development. In Quoomb Web, open the **Config** modal and import this
|
|
96
|
+
JSON (or set it as `quoomb.config.json`):
|
|
97
|
+
|
|
98
|
+
```jsonc
|
|
99
|
+
{
|
|
100
|
+
"plugins": [
|
|
101
|
+
{
|
|
102
|
+
"source": "https://cdn.jsdelivr.net/npm/@serfab/quereus-plugin-sereus/dist/plugin-browser.js",
|
|
103
|
+
"config": {
|
|
104
|
+
"strand_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
105
|
+
"bootstrap_nodes": "/dns4/relay.example/tcp/443/wss/p2p/Qm...",
|
|
106
|
+
"schema": "table Message (Id integer primary key, Content text not null)"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"autoload": true
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
With `autoload: true`, the plugin loads on session start and applies the schema.
|
|
115
|
+
|
|
116
|
+
### Step 3 — Query the strand
|
|
117
|
+
|
|
118
|
+
Once loaded, the `optimystic` vtab is the default and `App.*` tables resolve
|
|
119
|
+
against the strand:
|
|
120
|
+
|
|
121
|
+
```sql
|
|
122
|
+
select * from App.Message;
|
|
123
|
+
insert into App.Message (Id, Content) values (1, 'hello strand');
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Notes & limitations
|
|
127
|
+
|
|
128
|
+
- **Storage.** When `storage` is not injected, the browser bundle opens a default
|
|
129
|
+
`IndexedDBRawStorage` against an IndexedDB database named
|
|
130
|
+
`sereus-strand-<strandId>`. State survives reload. The plugin treats the
|
|
131
|
+
IndexedDB handle as borrowed and does **not** close it on `shutdown()` — its
|
|
132
|
+
lifecycle is the page/worker.
|
|
133
|
+
- **Network transactor by default.** Every connection commits through the
|
|
134
|
+
strand's libp2p cohort unless it asks otherwise; the in-process `local`
|
|
135
|
+
transactor is a testing/tooling option (`connectToStrand({ transactor:
|
|
136
|
+
'local' })`), not a browser deployment mode.
|
|
137
|
+
- **WebRTC** requires cross-origin isolation (COOP/COEP) and is tracked as a
|
|
138
|
+
follow-up; v1 is WebSockets + circuit-relay only.
|
|
139
|
+
- **Lifecycle.** Quoomb Web's loader keeps only the manifest and discards the
|
|
140
|
+
plugin's returned `shutdown()`, so the libp2p node is torn down with the
|
|
141
|
+
worker rather than on plugin unload.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Hosting the browser bundle
|
|
146
|
+
|
|
147
|
+
The browser bundle is just an ESM file; it can be served from anywhere that
|
|
148
|
+
returns a JavaScript content type with appropriate CORS.
|
|
149
|
+
|
|
150
|
+
### Published package on a CDN (recommended)
|
|
151
|
+
|
|
152
|
+
After `yarn pub:quereus-plugin-sereus` (see [Publishing](#publishing)), the npm
|
|
153
|
+
package is mirrored by the public CDNs, which serve the correct content type and
|
|
154
|
+
CORS automatically:
|
|
155
|
+
|
|
156
|
+
- jsDelivr: `https://cdn.jsdelivr.net/npm/@serfab/quereus-plugin-sereus/dist/plugin-browser.js`
|
|
157
|
+
- jsDelivr (pinned): `https://cdn.jsdelivr.net/npm/@serfab/quereus-plugin-sereus@0.7.1/dist/plugin-browser.js`
|
|
158
|
+
- unpkg: `https://unpkg.com/@serfab/quereus-plugin-sereus/dist/plugin-browser.js`
|
|
159
|
+
|
|
160
|
+
### Self-hosted / same-origin
|
|
161
|
+
|
|
162
|
+
Copy `dist/plugin-browser.js` to any static host (or Quoomb Web's `public/`) and
|
|
163
|
+
reference it by URL. Same-origin hosting sidesteps CORS entirely.
|
|
164
|
+
|
|
165
|
+
### Loading from GitHub
|
|
166
|
+
|
|
167
|
+
Loading the bundle straight from a GitHub repo has a caveat: **`dist/` is
|
|
168
|
+
gitignored**, and `raw.githubusercontent.com` serves files as `text/plain` with
|
|
169
|
+
`X-Content-Type-Options: nosniff`, so the browser refuses to `import()` it as a
|
|
170
|
+
module. Two routes that *do* work:
|
|
171
|
+
|
|
172
|
+
1. **jsDelivr's GitHub endpoint**, which serves repo files with the correct MIME
|
|
173
|
+
type and CORS — but only files that exist at the ref. Since `dist/` is not
|
|
174
|
+
committed, this requires the built bundle to be present at the ref you point
|
|
175
|
+
at (e.g. a CI step that commits `plugin-browser.js` to a `dist`/`gh-pages`
|
|
176
|
+
branch or a tag):
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
https://cdn.jsdelivr.net/gh/gotchoices/sereus@<tag-or-branch>/packages/quereus-plugin-sereus/dist/plugin-browser.js
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
2. **GitHub Pages**: publish `plugin-browser.js` to a Pages site, which serves it
|
|
183
|
+
with a JavaScript content type and CORS.
|
|
184
|
+
|
|
185
|
+
For most cases the npm-backed CDN URL above is simpler and is the recommended
|
|
186
|
+
GitHub-free hosting path. (Note: GitHub *Release assets* are served as
|
|
187
|
+
`application/octet-stream` and cannot be imported as ESM.)
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Programmatic API
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { Database } from '@quereus/quereus';
|
|
195
|
+
import { connectToStrand } from '@serfab/quereus-plugin-sereus';
|
|
196
|
+
|
|
197
|
+
const db = new Database();
|
|
198
|
+
const strand = await connectToStrand(db, {
|
|
199
|
+
strandId: '550e8400-e29b-41d4-a716-446655440000',
|
|
200
|
+
schema: 'table Message (Id integer primary key, Content text not null)',
|
|
201
|
+
bootstrapNodes: ['/ip4/1.2.3.4/tcp/9100/p2p/QmPeerId'],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
for await (const row of db.eval('select * from App.Message')) {
|
|
205
|
+
console.log(row);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await strand.shutdown();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Plugin-loader (Node)
|
|
212
|
+
|
|
213
|
+
The `./plugin` export is compatible with Quereus plugin-loader:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { Database, registerPlugin } from '@quereus/quereus';
|
|
217
|
+
import sereusPlugin from '@serfab/quereus-plugin-sereus/plugin';
|
|
218
|
+
|
|
219
|
+
const db = new Database();
|
|
220
|
+
const result = await registerPlugin(db, sereusPlugin, {
|
|
221
|
+
strand_id: '550e8400-e29b-41d4-a716-446655440000',
|
|
222
|
+
schema: 'table Message (Id integer primary key, Content text not null)',
|
|
223
|
+
bootstrap_nodes: '/ip4/1.2.3.4/tcp/9100/p2p/QmPeerId',
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await result.shutdown();
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Local transactor (in-process, no cohort)
|
|
230
|
+
|
|
231
|
+
`transactor: 'local'` sends every transaction straight to this process's raw
|
|
232
|
+
storage — no peers are consulted at all. It is for in-process tests and tooling
|
|
233
|
+
(this package's own e2e suites run on it); applications should stay on the
|
|
234
|
+
default `network` transactor, which a lone node coordinates for itself.
|
|
235
|
+
|
|
236
|
+
Pair it with a persistent `IRawStorage` to survive restart: the same storage
|
|
237
|
+
instance is wired into both the libp2p data path and the optimystic plugin's
|
|
238
|
+
local transactor.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { FileRawStorage } from '@optimystic/db-p2p-storage-fs';
|
|
242
|
+
|
|
243
|
+
const storage = new FileRawStorage('./data/my-strand');
|
|
244
|
+
const strand = await connectToStrand(db, {
|
|
245
|
+
strandId: 'abc',
|
|
246
|
+
transactor: 'local',
|
|
247
|
+
storage,
|
|
248
|
+
schema: 'table Msg (Id integer primary key, Body text not null)',
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The plugin treats `storage` as borrowed: `shutdown()` releases the libp2p node
|
|
253
|
+
and collection factory but does **not** close the storage.
|
|
254
|
+
|
|
255
|
+
### With an injected node
|
|
256
|
+
|
|
257
|
+
When integrating with an existing CadreNode or other libp2p host:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const strand = await connectToStrand(db, {
|
|
261
|
+
strandId: 'abc',
|
|
262
|
+
libp2pNode: existingNode,
|
|
263
|
+
coordinatedRepo: existingRepo,
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The plugin uses the injected node instead of creating one, and will not stop it
|
|
268
|
+
on shutdown.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Configuration
|
|
273
|
+
|
|
274
|
+
### `StrandConnectionOptions` (programmatic API)
|
|
275
|
+
|
|
276
|
+
| Option | Type | Default | Description |
|
|
277
|
+
|---|---|---|---|
|
|
278
|
+
| `strandId` | string | *required* | UUID of the strand to connect to |
|
|
279
|
+
| `bootstrapNodes` | string[] | `[]` | Bootstrap multiaddrs for peer discovery |
|
|
280
|
+
| `schema` | string | — | sApp schema DDL to apply |
|
|
281
|
+
| `sAppId` | string | `'unknown'` | sApp author public key |
|
|
282
|
+
| `sAppVersion` | string | `'1.0.0'` | sApp version |
|
|
283
|
+
| `port` | number | `0` | libp2p listening port (0 = random) |
|
|
284
|
+
| `enableCache` | boolean | `true` | Enable optimystic caching |
|
|
285
|
+
| `fretProfile` | `'edge' \| 'core'` | `'edge'` | FRET profile |
|
|
286
|
+
| `clusterSize` | number | `4` | Nodes the **strand's** replication cluster should have (the control network uses its own fixed `CONTROL_REPLICATION_BREADTH` and never reads this). 4 is the smallest breadth whose 0.75 super-majority (`ceil(4 × 0.75) = 3`) still commits with one holder offline; at 2 or 3 every holder must be awake for every write, and at 2 read repair cannot converge at all. Every peer on the strand should use the same value: a peer configured much higher derives a larger expected cohort and can reject a coordinator's smaller declared peer set as a downsize. Frozen when the libp2p node is created, so a change takes effect on the next restart. Must be an integer ≥ 2 (`MIN_CLUSTER_SIZE`, Optimystic's own minimum). Leaving it unset is *not* the same as passing 4 elsewhere — Optimystic's own fallback is 10, so this default is applied here. Ignored when `libp2pNode` is injected |
|
|
287
|
+
| `libp2pNode` | Libp2p | — | Inject an existing libp2p node |
|
|
288
|
+
| `coordinatedRepo` | IRepo | — | Required when `libp2pNode` is provided |
|
|
289
|
+
| `transactor` | `'local' \| 'network' \| 'test'` | `'network'` | Storage engine transactions go through. `'network'`: the strand's libp2p cohort, which a lone node coordinates for itself — the only value an application should use. `'local'`: this process's raw storage directly, no peers consulted (in-process tests and tooling). `'test'`: Optimystic's in-memory fake; no libp2p node is created unless one is injected (unit tests) |
|
|
290
|
+
| `storage` | IRawStorage | — | Persistent raw storage. Borrowed — not closed on `shutdown()` |
|
|
291
|
+
|
|
292
|
+
### Plugin settings (plugin-loader / Quoomb)
|
|
293
|
+
|
|
294
|
+
| Setting | Type | Default | Description |
|
|
295
|
+
|---|---|---|---|
|
|
296
|
+
| `strand_id` | string | *required* | UUID of the strand |
|
|
297
|
+
| `bootstrap_nodes` | string | `''` | Comma-separated bootstrap multiaddrs |
|
|
298
|
+
| `schema` | string | — | sApp schema DDL |
|
|
299
|
+
| `sapp_id` | string | `'unknown'` | sApp author public key |
|
|
300
|
+
| `sapp_version` | string | `'1.0.0'` | sApp version |
|
|
301
|
+
| `port` | number | `0` | libp2p listening port |
|
|
302
|
+
| `enable_cache` | boolean | `true` | Enable caching |
|
|
303
|
+
| `fret_profile` | string | `'edge'` | FRET profile (`'edge'` or `'core'`) |
|
|
304
|
+
| `transactor` | string | `'network'` | Storage engine (`'local'`, `'network'` or `'test'`) — see the `transactor` row above. An unrecognised value is rejected at load |
|
|
305
|
+
|
|
306
|
+
**Bootstrap multiaddrs.** Browsers can only dial transports reachable from an
|
|
307
|
+
`https://` page. Use `/wss` (or `/dns/.../wss`, or a relay-fronted multiaddr).
|
|
308
|
+
Plain `/tcp/.../ws` over HTTP won't dial from a TLS origin, and raw TCP is
|
|
309
|
+
unavailable in the browser.
|
|
310
|
+
|
|
311
|
+
## Provided functions and modules
|
|
312
|
+
|
|
313
|
+
Registered automatically from the composed plugins:
|
|
314
|
+
|
|
315
|
+
- **Virtual table module**: `optimystic` (set as default vtab)
|
|
316
|
+
- **Functions**: `StampId()`, `digest()`, `sign()`, `verify()`, `randomBytes()`
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Development
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
yarn build # tsc + esbuild (emits dist/plugin.js and dist/plugin-browser.js)
|
|
324
|
+
yarn test # Run tests (unit + e2e projects, plus browser-bundle smoke tests)
|
|
325
|
+
yarn test:e2e # Run only the e2e project (real libp2p + FileRawStorage)
|
|
326
|
+
yarn dev:test # Watch mode
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`build` runs `tsc -p tsconfig.build.json` (emits `dist/plugin.js`,
|
|
330
|
+
`dist/plugin-browser.js` declarations, and other entries) and then
|
|
331
|
+
`node scripts/build-browser.mjs` (overwrites `dist/plugin-browser.js` with the
|
|
332
|
+
bundled artifact and its sourcemap, and prints raw + gzipped size).
|
|
333
|
+
|
|
334
|
+
The browser-bundle smoke tests (`test/browser-bundle.spec.ts` and
|
|
335
|
+
`test/browser-shape.spec.ts`) run under the `unit` project: they build the
|
|
336
|
+
bundle on demand if missing, then check that the artifact parses as ESM, has no
|
|
337
|
+
Node-only or TCP imports, stays under the size caps, and that its default export
|
|
338
|
+
reaches the IndexedDB layer in a jsdom + `fake-indexeddb` environment.
|
|
339
|
+
|
|
340
|
+
The `e2e` project covers two scenarios over real libp2p + `FileRawStorage`:
|
|
341
|
+
|
|
342
|
+
- `test/e2e/local-transactor.e2e.spec.ts` — a single in-process connection on the
|
|
343
|
+
`local` transactor, including cold-restart persistence across the shared
|
|
344
|
+
storage directory.
|
|
345
|
+
- `test/e2e/networked.e2e.spec.ts` — two in-process peers exchanging strand data
|
|
346
|
+
through a `createLibp2pNode` mesh: cross-peer replication, bidirectional
|
|
347
|
+
convergence, and late-joiner catch-up.
|
|
348
|
+
|
|
349
|
+
## Publishing
|
|
350
|
+
|
|
351
|
+
From the monorepo root:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
yarn pub:quereus-plugin-sereus # clean + build + npm publish --access public
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
This is also included in the aggregate `yarn pub` release step. Publishing emits
|
|
358
|
+
both `dist/plugin.js` and the browser bundle `dist/plugin-browser.js` (both are
|
|
359
|
+
in the package `files`), so the CDN URLs under [Hosting the browser
|
|
360
|
+
bundle](#hosting-the-browser-bundle) resolve immediately after a successful
|
|
361
|
+
publish.
|
|
362
|
+
|
|
363
|
+
## License
|
|
364
|
+
|
|
365
|
+
MIT
|
|
366
|
+
</content>
|
|
367
|
+
</invoke>
|