@buley/dash 4.1.0 → 4.2.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 +46 -5
- package/dist/src/auth/manager.d.ts +8 -0
- package/dist/src/auth/manager.js +39 -0
- package/dist/src/engine/sqlite.d.ts +3 -0
- package/dist/src/engine/sqlite.js +24 -7
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/schema/lens.d.ts +29 -0
- package/dist/src/schema/lens.js +122 -0
- package/dist/src/sync/hybrid-provider.d.ts +1 -0
- package/dist/src/sync/hybrid-provider.js +64 -26
- package/dist/src/sync/verify.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/dist/src/sync/webtransport-provider.d.ts +0 -25
- package/dist/src/sync/webtransport-provider.js +0 -128
package/README.md
CHANGED
|
@@ -4,12 +4,53 @@
|
|
|
4
4
|
|
|
5
5
|
Dash 2.0 isn't just a database; it's a complete data engine for building high-performance, intelligent web applications. It brings server-grade power to the client, enabling apps that feel instant, work offline, and understand your users.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## The "Real" Filesystem: OPFS
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
If you built your local-first app a year or two ago, you probably had to persist data by serializing the whole DB to IndexedDB or localStorage. It was slow and blocked the main thread.
|
|
10
|
+
|
|
11
|
+
### The Shift
|
|
12
|
+
|
|
13
|
+
The new standard is **SQLite over OPFS** (Origin Private File System).
|
|
14
|
+
|
|
15
|
+
### Why it’s sexy
|
|
16
|
+
|
|
17
|
+
It gives the browser direct, performant handle access to a virtual file system optimized for random access.
|
|
18
|
+
|
|
19
|
+
### The Result
|
|
20
|
+
|
|
21
|
+
You can run full ACID transactions on a **multi-gigabyte SQLite file** in the browser with **near-native desktop performance**, without ever blocking the UI thread (thanks to SharedArrayBuffer).
|
|
22
|
+
|
|
23
|
+
Combined with **Zero-Copy (BYOB)** readers and **UCAN**-based Zero-Trust Auth, Dash isn't just a database—it's a local-first operating system.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
### 🚀 Production-Grade Storage
|
|
28
|
+
|
|
29
|
+
- **Stateful Serverless**: Powered by Cloudflare Durable Objects and Native SQLite.
|
|
30
|
+
- **Zero-Copy IO (BYOB)**: "Bring Your Own Buffer" readers for max throughput.
|
|
31
|
+
- **ACID Compliant**: Full transactional integrity in the browser.
|
|
32
|
+
|
|
33
|
+
### 🧠 Hardware-Accelerated AI
|
|
34
|
+
|
|
35
|
+
- **Vector Search**: Built-in vector embeddings and search.
|
|
36
|
+
- **3-Tier Acceleration**:
|
|
37
|
+
1. **WebNN**: NPU acceleration (Apple Neural Engine, etc.)
|
|
38
|
+
2. **WebGPU**: High-performance GPU parallelization.
|
|
39
|
+
3. **WASM/CPU**: Universal fallback.
|
|
40
|
+
- **Semantic Queries**: Find data by _meaning_ at native speeds.
|
|
41
|
+
|
|
42
|
+
### 🔄 Modern Sync
|
|
43
|
+
|
|
44
|
+
- **Hybrid Transport**:
|
|
45
|
+
- **WebSocket**: Supports Cloudflare Durable Object Hibernation ($0 idle cost).
|
|
46
|
+
- **WebTransport**: UDP-like high-frequency streams (perfect for cursors/games).
|
|
47
|
+
- **Zero-Trust Auth (UCANs)**: Decentralized authorization via User Controlled Authorization Networks.
|
|
48
|
+
- **Lens**: Bidirectional schema migrations for infinite backward compatibility.
|
|
49
|
+
|
|
50
|
+
### 🎨 Graphics Ready
|
|
51
|
+
|
|
52
|
+
- **Direct Buffers**: Zero-copy bindings allow piping data directly into 3D engines like Three.js.
|
|
53
|
+
- **Spatial Indexing**: R-Tree support for massive 3D visualizations.
|
|
13
54
|
|
|
14
55
|
## Installation
|
|
15
56
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as ucans from 'ucans';
|
|
2
|
+
export class AuthManager {
|
|
3
|
+
keypair = null;
|
|
4
|
+
did = null;
|
|
5
|
+
async init() {
|
|
6
|
+
if (!this.keypair) {
|
|
7
|
+
this.keypair = await ucans.EdKeypair.create();
|
|
8
|
+
this.did = this.keypair.did();
|
|
9
|
+
}
|
|
10
|
+
return this.did;
|
|
11
|
+
}
|
|
12
|
+
getDID() {
|
|
13
|
+
if (!this.did)
|
|
14
|
+
throw new Error("AuthManager not initialized");
|
|
15
|
+
return this.did;
|
|
16
|
+
}
|
|
17
|
+
async issueRoomToken(roomAudience, roomName) {
|
|
18
|
+
if (!this.keypair)
|
|
19
|
+
await this.init();
|
|
20
|
+
// In a real app, this "Root" UCAN would come from a server or a stored root key.
|
|
21
|
+
// For this P2P/Star architecture, we are self-issuing a delegated capability
|
|
22
|
+
// or acting as a self-signed root for the session if the Relay permits "open" access valid signatures.
|
|
23
|
+
// Assuming the Relay acts as a gatekeeper that validates the signature is valid,
|
|
24
|
+
// and perhaps checks a "known list" or just logs the DID.
|
|
25
|
+
// Capability: "room/write" on "room://<roomName>"
|
|
26
|
+
const capability = {
|
|
27
|
+
with: { scheme: "room", hierPart: `//${roomName}` },
|
|
28
|
+
can: { namespace: "room", segments: ["write"] }
|
|
29
|
+
};
|
|
30
|
+
const ucan = await ucans.build({
|
|
31
|
+
audience: roomAudience, // The Relay's DID
|
|
32
|
+
issuer: this.keypair,
|
|
33
|
+
capabilities: [capability],
|
|
34
|
+
lifetimeInSeconds: 3600 // 1 hour token
|
|
35
|
+
});
|
|
36
|
+
return ucans.encode(ucan);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const auth = new AuthManager();
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// import sqlite3InitModule from '@sqlite.org/sqlite-wasm'; // moved to dynamic import
|
|
2
2
|
import { vectorEngine } from './ai.js';
|
|
3
|
+
import { schema as defaultLens } from '../schema/lens.js';
|
|
3
4
|
export class DashEngine {
|
|
4
5
|
db = null;
|
|
5
6
|
readyPromise;
|
|
6
7
|
listeners = new Set();
|
|
8
|
+
lens = defaultLens;
|
|
9
|
+
currentSchemaVersion = 1;
|
|
7
10
|
constructor() {
|
|
8
11
|
// SSR/Build safety: Only initialize in browser environments
|
|
9
12
|
if (typeof window !== 'undefined') {
|
|
@@ -199,18 +202,26 @@ export class DashEngine {
|
|
|
199
202
|
SELECT
|
|
200
203
|
item.id,
|
|
201
204
|
item.content,
|
|
202
|
-
distance
|
|
205
|
+
distance,
|
|
206
|
+
dash_metadata.value as _v
|
|
203
207
|
FROM dash_vec_idx
|
|
204
208
|
JOIN dash_items AS item ON item.id = dash_vec_idx.id
|
|
209
|
+
LEFT JOIN dash_metadata ON dash_metadata.key = 'schema_version_' || item.id
|
|
205
210
|
WHERE embedding MATCH ?
|
|
206
211
|
ORDER BY distance
|
|
207
212
|
LIMIT ?
|
|
208
213
|
`, [queryVector, limit]);
|
|
209
214
|
// Normalize distance to score (assuming Cosine Distance: score = 1 - distance)
|
|
210
|
-
return rows.map((row) =>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
return rows.map((row) => {
|
|
216
|
+
// Apply Lens Migration if version differs
|
|
217
|
+
// Default to v1 if no version metadata found
|
|
218
|
+
const version = row._v ? parseInt(row._v) : 1;
|
|
219
|
+
const migrated = this.lens.migrate(row, version, this.currentSchemaVersion);
|
|
220
|
+
return {
|
|
221
|
+
...migrated,
|
|
222
|
+
score: row.distance !== undefined ? 1 - row.distance : 0
|
|
223
|
+
};
|
|
224
|
+
});
|
|
214
225
|
}
|
|
215
226
|
catch (e) {
|
|
216
227
|
console.warn("Vector search failed, using fallback", e);
|
|
@@ -218,19 +229,25 @@ export class DashEngine {
|
|
|
218
229
|
}
|
|
219
230
|
}
|
|
220
231
|
async spatialQuery(bounds) {
|
|
221
|
-
|
|
232
|
+
const rows = this.execute(`
|
|
222
233
|
SELECT
|
|
223
234
|
map.item_id as id,
|
|
224
235
|
item.content,
|
|
225
|
-
idx.minX, idx.maxX, idx.minY, idx.maxY, idx.minZ, idx.maxZ
|
|
236
|
+
idx.minX, idx.maxX, idx.minY, idx.maxY, idx.minZ, idx.maxZ,
|
|
237
|
+
meta.value as _v
|
|
226
238
|
FROM dash_spatial_idx AS idx
|
|
227
239
|
JOIN dash_spatial_map AS map ON map.rowid = idx.id
|
|
228
240
|
JOIN dash_items AS item ON item.id = map.item_id
|
|
241
|
+
LEFT JOIN dash_metadata AS meta ON meta.key = 'schema_version_' || item.id
|
|
229
242
|
WHERE
|
|
230
243
|
minX >= ? AND maxX <= ? AND
|
|
231
244
|
minY >= ? AND maxY <= ? AND
|
|
232
245
|
minZ >= ? AND maxZ <= ?
|
|
233
246
|
`, [bounds.minX, bounds.maxX, bounds.minY, bounds.maxY, bounds.minZ, bounds.maxZ]);
|
|
247
|
+
return rows.map(row => {
|
|
248
|
+
const version = row._v ? parseInt(row._v) : 1;
|
|
249
|
+
return this.lens.migrate(row, version, this.currentSchemaVersion);
|
|
250
|
+
});
|
|
234
251
|
}
|
|
235
252
|
close() {
|
|
236
253
|
this.db?.close();
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { dash } from './engine/sqlite.js';
|
|
2
2
|
export { liveQuery, signal, effect, computed } from './reactivity/signal.js';
|
|
3
3
|
export { mcpServer } from './mcp/server.js';
|
|
4
|
+
export { YjsSqliteProvider } from './sync/provider.js';
|
|
5
|
+
export { backup, restore, generateKey, exportKey, importKey } from './sync/backup.js';
|
|
6
|
+
export type { CloudStorageAdapter } from './sync/backup.js';
|
|
7
|
+
export { HybridProvider } from './sync/hybrid-provider.js';
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { dash } from './engine/sqlite.js';
|
|
2
2
|
export { liveQuery, signal, effect, computed } from './reactivity/signal.js';
|
|
3
3
|
export { mcpServer } from './mcp/server.js';
|
|
4
|
+
// Sync exports
|
|
5
|
+
export { YjsSqliteProvider } from './sync/provider.js';
|
|
6
|
+
export { backup, restore, generateKey, exportKey, importKey } from './sync/backup.js';
|
|
7
|
+
export { HybridProvider } from './sync/hybrid-provider.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type LensOp = {
|
|
2
|
+
kind: 'rename';
|
|
3
|
+
source: string;
|
|
4
|
+
target: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'hoist';
|
|
7
|
+
host: string;
|
|
8
|
+
name: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'wrap';
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
export interface SchemaLens {
|
|
14
|
+
from: number;
|
|
15
|
+
to: number;
|
|
16
|
+
ops: LensOp[];
|
|
17
|
+
}
|
|
18
|
+
export declare class LensEngine {
|
|
19
|
+
private lenses;
|
|
20
|
+
private currentVersion;
|
|
21
|
+
constructor(currentVersion: number);
|
|
22
|
+
registerLens(from: number, to: number, ops: LensOp[]): void;
|
|
23
|
+
private findPath;
|
|
24
|
+
migrate(data: any, from: number, to?: number): any;
|
|
25
|
+
private normalizeOps;
|
|
26
|
+
private invertOp;
|
|
27
|
+
private applyOp;
|
|
28
|
+
}
|
|
29
|
+
export declare const schema: LensEngine;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Native Bidirectional Lens Implementation
|
|
2
|
+
// Inspired by Project Cambria
|
|
3
|
+
export class LensEngine {
|
|
4
|
+
lenses = [];
|
|
5
|
+
currentVersion;
|
|
6
|
+
constructor(currentVersion) {
|
|
7
|
+
this.currentVersion = currentVersion;
|
|
8
|
+
}
|
|
9
|
+
registerLens(from, to, ops) {
|
|
10
|
+
this.lenses.push({ from, to, ops });
|
|
11
|
+
}
|
|
12
|
+
// Find path from V_start to V_end
|
|
13
|
+
findPath(from, to) {
|
|
14
|
+
// BFS to find shortest path
|
|
15
|
+
const queue = [{ version: from, path: [] }];
|
|
16
|
+
const visited = new Set([from]);
|
|
17
|
+
while (queue.length > 0) {
|
|
18
|
+
const { version, path } = queue.shift();
|
|
19
|
+
if (version === to)
|
|
20
|
+
return path;
|
|
21
|
+
// Find neighbors
|
|
22
|
+
// Forward lenses
|
|
23
|
+
const forward = this.lenses.filter(l => l.from === version);
|
|
24
|
+
for (const lens of forward) {
|
|
25
|
+
if (!visited.has(lens.to)) {
|
|
26
|
+
visited.add(lens.to);
|
|
27
|
+
queue.push({ version: lens.to, path: [...path, lens] });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Reverse lenses (Implicit)
|
|
31
|
+
const backward = this.lenses.filter(l => l.to === version);
|
|
32
|
+
for (const lens of backward) {
|
|
33
|
+
if (!visited.has(lens.from)) {
|
|
34
|
+
visited.add(lens.from);
|
|
35
|
+
// Create synthetic reverse lens
|
|
36
|
+
const reverseLens = {
|
|
37
|
+
from: lens.to,
|
|
38
|
+
to: lens.from,
|
|
39
|
+
ops: [...lens.ops].reverse() // Ops must be reversed!
|
|
40
|
+
};
|
|
41
|
+
queue.push({ version: lens.from, path: [...path, reverseLens] });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`No translation path found from v${from} to v${to}`);
|
|
46
|
+
}
|
|
47
|
+
migrate(data, from, to = this.currentVersion) {
|
|
48
|
+
if (from === to)
|
|
49
|
+
return data;
|
|
50
|
+
const path = this.findPath(from, to);
|
|
51
|
+
let doc = JSON.parse(JSON.stringify(data)); // Deep clone to avoid mutation
|
|
52
|
+
for (const step of path) {
|
|
53
|
+
const isForward = step.from < step.to; // Heuristic: Assuming localized version jumps
|
|
54
|
+
// Actually we should just apply the ops in the step direction.
|
|
55
|
+
// But we need to know if the step is a "Forward Lens" or a "Reverse Lens"
|
|
56
|
+
// because ops like 'rename' need to swap source/target if reversed.
|
|
57
|
+
// The path finding creates "synthetic" lenses for reverse steps where from > to.
|
|
58
|
+
// So we just need to apply 'step.ops' carefully.
|
|
59
|
+
// But wait! in findPath, I just reversed the ARRAY of ops.
|
|
60
|
+
// I ALSO need to invert the OPERATORS themselves.
|
|
61
|
+
const opsToApply = this.normalizeOps(step);
|
|
62
|
+
for (const op of opsToApply) {
|
|
63
|
+
doc = this.applyOp(doc, op);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return doc;
|
|
67
|
+
}
|
|
68
|
+
normalizeOps(step) {
|
|
69
|
+
// If this lens was registered as from=1, to=2
|
|
70
|
+
// And we are using it as from=1, to=2. No change.
|
|
71
|
+
// If we are traversing 2->1.
|
|
72
|
+
// The findPath constructed a lens { from: 2, to: 1, ops: ops.reverse() }
|
|
73
|
+
// But the ops inside are still { kind: 'rename', source: 'old', target: 'new' }
|
|
74
|
+
// We need to invert them to { kind: 'rename', source: 'new', target: 'old' }
|
|
75
|
+
// We need to know the *registred* direction to know if inversion is needed?
|
|
76
|
+
// Actually findPath constructs a fresh object. We can check if it exists in this.lenses.
|
|
77
|
+
const registered = this.lenses.find(l => l.from === step.from && l.to === step.to);
|
|
78
|
+
if (registered) {
|
|
79
|
+
return step.ops; // Direct match, use as is
|
|
80
|
+
}
|
|
81
|
+
// Must be a reverse step constructed by findPath
|
|
82
|
+
return step.ops.map(op => this.invertOp(op));
|
|
83
|
+
}
|
|
84
|
+
invertOp(op) {
|
|
85
|
+
switch (op.kind) {
|
|
86
|
+
case 'rename': return { kind: 'rename', source: op.target, target: op.source };
|
|
87
|
+
// Invert Hoist: Wrap?
|
|
88
|
+
// Hoist: host='meta', name='key' (moves doc.meta.key -> doc.key)
|
|
89
|
+
// Inverse: Wrap (moves doc.key -> doc.meta.key)
|
|
90
|
+
// Use 'wrap' as simplified inverse of simple hoist
|
|
91
|
+
case 'hoist': return { kind: 'wrap', name: op.name }; // Simplified
|
|
92
|
+
case 'wrap': return { kind: 'hoist', host: 'container', name: op.name }; // Naive inverse
|
|
93
|
+
default: return op;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
applyOp(doc, op) {
|
|
97
|
+
switch (op.kind) {
|
|
98
|
+
case 'rename': {
|
|
99
|
+
if (doc[op.source] !== undefined) {
|
|
100
|
+
doc[op.target] = doc[op.source];
|
|
101
|
+
delete doc[op.source];
|
|
102
|
+
}
|
|
103
|
+
return doc;
|
|
104
|
+
}
|
|
105
|
+
case 'hoist': {
|
|
106
|
+
// Extract property from scalar host? Or object?
|
|
107
|
+
// Assume simple object flattening: { data: { val: 1 } } -> hoist 'data', 'val' -> { val: 1 }
|
|
108
|
+
// doc[op.host][op.name] -> doc[op.name]
|
|
109
|
+
if (doc[op.host] && doc[op.host][op.name] !== undefined) {
|
|
110
|
+
doc[op.name] = doc[op.host][op.name];
|
|
111
|
+
delete doc[op.host][op.name];
|
|
112
|
+
if (Object.keys(doc[op.host]).length === 0)
|
|
113
|
+
delete doc[op.host];
|
|
114
|
+
}
|
|
115
|
+
return doc;
|
|
116
|
+
}
|
|
117
|
+
default: return doc;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export const schema = new LensEngine(1);
|
|
122
|
+
// Default to v1 until configured
|
|
@@ -31,27 +31,41 @@ export class HybridProvider extends Observable {
|
|
|
31
31
|
}
|
|
32
32
|
connectWebSocket() {
|
|
33
33
|
// Protocol: switch http/https to ws/wss
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
this
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
34
|
+
// Protocol: switch http/https to ws/wss
|
|
35
|
+
// 1. Get Token
|
|
36
|
+
// Ideally await this before connecting, but construction is sync.
|
|
37
|
+
// We'll make connect async internally or fire-and-forget with token wait.
|
|
38
|
+
this.getAuthToken().then(token => {
|
|
39
|
+
const wsUrl = this.url.replace(/^http/, 'ws') + '/sync/' + this.roomName + '?token=' + token;
|
|
40
|
+
this.ws = new WebSocket(wsUrl);
|
|
41
|
+
this.ws.binaryType = 'arraybuffer';
|
|
42
|
+
this.ws.onopen = () => {
|
|
43
|
+
this.connected = true;
|
|
44
|
+
this.emit('status', [{ status: 'connected' }]);
|
|
45
|
+
this.sendSyncStep1();
|
|
46
|
+
};
|
|
47
|
+
// ... rest of event handlers
|
|
48
|
+
this.ws.onmessage = (event) => {
|
|
49
|
+
this.handleMessage(new Uint8Array(event.data));
|
|
50
|
+
};
|
|
51
|
+
this.ws.onclose = () => {
|
|
52
|
+
this.connected = false;
|
|
53
|
+
this.ws = null;
|
|
54
|
+
this.emit('status', [{ status: 'disconnected' }]);
|
|
55
|
+
// Reconnect logic
|
|
56
|
+
setTimeout(() => this.connectWebSocket(), 3000);
|
|
57
|
+
};
|
|
58
|
+
this.ws.onerror = (err) => {
|
|
59
|
+
console.error('WebSocket error', err);
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async getAuthToken() {
|
|
64
|
+
// Lazy load auth to avoid circular dependency issues if any
|
|
65
|
+
const { auth } = await import('../auth/manager.js');
|
|
66
|
+
// For MVP, we assume the Relay allows any DID to connect if they sign it.
|
|
67
|
+
// In prod, you'd know the Relay's DID.
|
|
68
|
+
return auth.issueRoomToken("did:web:relay.buley.dev", this.roomName);
|
|
55
69
|
}
|
|
56
70
|
async enterHighFrequencyMode() {
|
|
57
71
|
if (this.highFrequencyMode)
|
|
@@ -80,14 +94,38 @@ export class HybridProvider extends Observable {
|
|
|
80
94
|
this.send(encoding.toUint8Array(encoder));
|
|
81
95
|
}
|
|
82
96
|
async readLoop(readable) {
|
|
83
|
-
|
|
97
|
+
let reader;
|
|
98
|
+
try {
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
reader = readable.getReader({ mode: 'byob' });
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
reader = readable.getReader();
|
|
104
|
+
}
|
|
105
|
+
// Pre-allocate buffer (64KB) to emulate Zero-Copy / WASM Heap view
|
|
106
|
+
// In a future update, this could be a view into `sqlite3.wasm.memory`.
|
|
107
|
+
let buffer = new Uint8Array(65536);
|
|
84
108
|
try {
|
|
85
109
|
while (true) {
|
|
86
|
-
|
|
87
|
-
if (
|
|
110
|
+
let result;
|
|
111
|
+
if (reader.readAtLeast) {
|
|
112
|
+
// BYOB Reader
|
|
113
|
+
// We pass the view. The reader detaches it and returns a new view (potentially same backing store).
|
|
114
|
+
result = await reader.read(new Uint8Array(buffer.buffer, 0, buffer.byteLength));
|
|
115
|
+
if (result.value) {
|
|
116
|
+
buffer = result.value; // Update our reference to the valid buffer
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// Default Reader
|
|
121
|
+
result = await reader.read();
|
|
122
|
+
}
|
|
123
|
+
if (result.done)
|
|
88
124
|
break;
|
|
89
|
-
if (value) {
|
|
90
|
-
|
|
125
|
+
if (result.value) {
|
|
126
|
+
// Processing: In true zero-copy, we'd pass the offset/length to SQL directly.
|
|
127
|
+
// Here we pass the view.
|
|
128
|
+
this.handleMessage(result.value);
|
|
91
129
|
}
|
|
92
130
|
}
|
|
93
131
|
}
|
package/dist/src/sync/verify.js
CHANGED
|
@@ -3,8 +3,8 @@ import { HybridProvider } from './hybrid-provider.js';
|
|
|
3
3
|
async function verify() {
|
|
4
4
|
console.log('Starting verification...');
|
|
5
5
|
const doc = new Y.Doc();
|
|
6
|
-
// Use
|
|
7
|
-
const provider = new HybridProvider('
|
|
6
|
+
// Use deployed relay URL
|
|
7
|
+
const provider = new HybridProvider('https://dash-relay.taylorbuley.workers.dev', 'production-test-room', doc);
|
|
8
8
|
provider.on('status', (status) => {
|
|
9
9
|
console.log('Provider Status:', status);
|
|
10
10
|
});
|