@syncular/client 0.15.44 → 0.15.46
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 +37 -5
- package/dist/browser-storage-persistence.d.ts +20 -0
- package/dist/browser-storage-persistence.js +34 -0
- package/dist/bun-database.d.ts +1 -1
- package/dist/bun-database.js +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +6 -6
- package/dist/database.d.ts +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/http.d.ts +6 -2
- package/dist/http.js +68 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/invalidation.d.ts +1 -1
- package/dist/leader-lock.d.ts +2 -2
- package/dist/multi-tab.js +1 -1
- package/dist/naming.d.ts +1 -1
- package/dist/naming.js +1 -1
- package/dist/node-database.js +1 -1
- package/dist/query-guard.d.ts +1 -1
- package/dist/query-guard.js +1 -1
- package/dist/remote.d.ts +76 -0
- package/dist/remote.js +441 -0
- package/dist/schema.d.ts +3 -3
- package/dist/sql-tag.d.ts +1 -1
- package/dist/transport.d.ts +12 -1
- package/dist/transport.js +1 -1
- package/dist/wasm-database.d.ts +6 -4
- package/dist/wasm-database.js +13 -8
- package/dist/window.d.ts +1 -1
- package/dist/window.js +1 -1
- package/dist/worker-entry.js +1 -1
- package/dist/worker-host.d.ts +5 -5
- package/dist/worker-host.js +2 -2
- package/dist/worker-protocol.d.ts +3 -3
- package/dist/worker-protocol.js +1 -1
- package/package.json +3 -3
- package/src/browser-storage-persistence.ts +52 -0
- package/src/bun-database.ts +1 -1
- package/src/client.ts +6 -6
- package/src/database.ts +1 -1
- package/src/devtools.ts +1 -1
- package/src/http.ts +100 -8
- package/src/index.ts +4 -2
- package/src/invalidation.ts +1 -1
- package/src/leader-lock.ts +2 -2
- package/src/multi-tab.ts +1 -1
- package/src/naming.ts +1 -1
- package/src/node-database.ts +1 -1
- package/src/query-guard.ts +1 -1
- package/src/remote.ts +724 -0
- package/src/schema.ts +3 -3
- package/src/sql-tag.ts +1 -1
- package/src/transport.ts +20 -1
- package/src/wasm-database.ts +13 -8
- package/src/window.ts +1 -1
- package/src/worker-entry.ts +1 -1
- package/src/worker-host.ts +6 -6
- package/src/worker-protocol.ts +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The worker RPC protocol
|
|
2
|
+
* The worker RPC protocol: the whole
|
|
3
3
|
* client core runs in a Web Worker; the UI thread talks to it through
|
|
4
4
|
* this thin, multiplexed postMessage protocol. Exactly SIX message types
|
|
5
5
|
* (`init`, `call`, `ready`, `result`, `error`, `event`) — every logical
|
|
@@ -30,14 +30,14 @@ import type { CommitOutcome, CommitOutcomeQuery, ResolveCommitOutcomeInput } fro
|
|
|
30
30
|
import type { ClientSchema } from './schema.js';
|
|
31
31
|
import type { SubscriptionRecord } from './state.js';
|
|
32
32
|
import type { WindowBase } from './window.js';
|
|
33
|
-
/** The handle exists but this tab lost the leader election
|
|
33
|
+
/** The handle exists but this tab lost the leader election. */
|
|
34
34
|
export declare const NOT_LEADER_CODE = "client.not_leader";
|
|
35
35
|
/** The worker (or its RPC channel) failed outside protocol semantics. */
|
|
36
36
|
export declare const WORKER_FAILED_CODE = "client.worker_failed";
|
|
37
37
|
/** A page/worker bundle identity changed and a full host reload is required. */
|
|
38
38
|
export declare const WORKER_RESTART_REQUIRED_CODE = "client.worker_restart_required";
|
|
39
39
|
export type WorkerDatabaseInit = {
|
|
40
|
-
/**
|
|
40
|
+
/** Reload-persistent OPFS mode; origin eviction policy stays page-owned. */
|
|
41
41
|
readonly mode: 'persistent';
|
|
42
42
|
readonly name: string;
|
|
43
43
|
/** Optional pool directory override (default `.syncular/<name>`). */
|
package/dist/worker-protocol.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// Client-local error codes (never wire codes; §10 stays server-owned)
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
|
-
/** The handle exists but this tab lost the leader election
|
|
4
|
+
/** The handle exists but this tab lost the leader election. */
|
|
5
5
|
export const NOT_LEADER_CODE = 'client.not_leader';
|
|
6
6
|
/** The worker (or its RPC channel) failed outside protocol semantics. */
|
|
7
7
|
export const WORKER_FAILED_CODE = 'client.worker_failed';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncular/client",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.46",
|
|
4
4
|
"description": "Syncular TypeScript client core — offline-first sync over SQLite (WASM/OPFS, Bun, Node)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Benjamin Kniffler",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
},
|
|
90
90
|
"dependencies": {
|
|
91
91
|
"@sqlite.org/sqlite-wasm": "^3.53.0-build1",
|
|
92
|
-
"@syncular/core": "0.15.
|
|
92
|
+
"@syncular/core": "0.15.46"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
95
|
"better-sqlite3": ">=11"
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@syncular/server": "0.15.
|
|
103
|
+
"@syncular/server": "0.15.46",
|
|
104
104
|
"@types/better-sqlite3": "^7.6.13",
|
|
105
105
|
"better-sqlite3": "^12.11.1"
|
|
106
106
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser storage durability for the origin that owns Syncular's OPFS
|
|
3
|
+
* database. Persistence is requested by the page because `persist()` is a
|
|
4
|
+
* Window-only API and browsers may evaluate the request against user
|
|
5
|
+
* engagement.
|
|
6
|
+
*/
|
|
7
|
+
export type BrowserStoragePersistence =
|
|
8
|
+
| { readonly state: 'persistent' }
|
|
9
|
+
| {
|
|
10
|
+
readonly state: 'best-effort';
|
|
11
|
+
readonly reason:
|
|
12
|
+
| 'not-granted'
|
|
13
|
+
| 'unavailable'
|
|
14
|
+
| 'check-failed'
|
|
15
|
+
| 'request-failed';
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Check whether the current origin is protected from automatic eviction. */
|
|
19
|
+
export async function checkBrowserStoragePersistence(): Promise<BrowserStoragePersistence> {
|
|
20
|
+
const storage =
|
|
21
|
+
typeof navigator === 'undefined' ? undefined : navigator.storage;
|
|
22
|
+
if (storage === undefined || typeof storage.persisted !== 'function') {
|
|
23
|
+
return { state: 'best-effort', reason: 'unavailable' };
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return (await storage.persisted())
|
|
27
|
+
? { state: 'persistent' }
|
|
28
|
+
: { state: 'best-effort', reason: 'not-granted' };
|
|
29
|
+
} catch {
|
|
30
|
+
return { state: 'best-effort', reason: 'check-failed' };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Request eviction-resistant storage for the current origin. Call this from a
|
|
36
|
+
* user action near the first important offline write. A best-effort result is
|
|
37
|
+
* an explicit durability state; the OPFS database remains usable.
|
|
38
|
+
*/
|
|
39
|
+
export async function requestBrowserStoragePersistence(): Promise<BrowserStoragePersistence> {
|
|
40
|
+
const storage =
|
|
41
|
+
typeof navigator === 'undefined' ? undefined : navigator.storage;
|
|
42
|
+
if (storage === undefined || typeof storage.persist !== 'function') {
|
|
43
|
+
return { state: 'best-effort', reason: 'unavailable' };
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return (await storage.persist())
|
|
47
|
+
? { state: 'persistent' }
|
|
48
|
+
: { state: 'best-effort', reason: 'not-granted' };
|
|
49
|
+
} catch {
|
|
50
|
+
return { state: 'best-effort', reason: 'request-failed' };
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/bun-database.ts
CHANGED
package/src/client.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SyncClient
|
|
2
|
+
* SyncClient implements the client side of SPEC.md §§3–8.
|
|
3
3
|
*
|
|
4
4
|
* A plain library running on whatever thread it is constructed on
|
|
5
|
-
*
|
|
5
|
+
* Storage is behind `ClientDatabase`, network
|
|
6
6
|
* behind `SyncTransport`/`SegmentDownloader`/`RealtimeConnector`, multi-tab
|
|
7
7
|
* ownership behind `LeaderLock`. One combined push+pull request per
|
|
8
8
|
* `sync()` round (§7.2); local reads go straight to the database.
|
|
@@ -708,7 +708,7 @@ export class SyncClient {
|
|
|
708
708
|
this.#config.onSyncNeeded?.('startup');
|
|
709
709
|
this.#config.onSyncIntent?.({ kind: 'interactive' });
|
|
710
710
|
}
|
|
711
|
-
//
|
|
711
|
+
// Console introspection is a no-op outside a dev page.
|
|
712
712
|
this.#devtoolsUnregister = registerDevtools({
|
|
713
713
|
kind: 'client',
|
|
714
714
|
ref: this,
|
|
@@ -881,7 +881,7 @@ export class SyncClient {
|
|
|
881
881
|
return this.#clientId;
|
|
882
882
|
}
|
|
883
883
|
|
|
884
|
-
/** The underlying database
|
|
884
|
+
/** The underlying database: raw SQL is the local query API. */
|
|
885
885
|
get database(): ClientDatabase {
|
|
886
886
|
this.#requireActive();
|
|
887
887
|
return this.#db;
|
|
@@ -960,7 +960,7 @@ export class SyncClient {
|
|
|
960
960
|
});
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
-
// -- live-query invalidation
|
|
963
|
+
// -- live-query invalidation ----------------------------------------------
|
|
964
964
|
|
|
965
965
|
/**
|
|
966
966
|
* Subscribe to fine-grained invalidation. The callback fires ONCE per
|
|
@@ -2725,7 +2725,7 @@ export class SyncClient {
|
|
|
2725
2725
|
|
|
2726
2726
|
/**
|
|
2727
2727
|
* One request/response round trip (§8.7): over the socket whenever it
|
|
2728
|
-
* is connected (
|
|
2728
|
+
* is connected (the socket IS the sync-round
|
|
2729
2729
|
* transport, not a fallback pair), otherwise through the configured
|
|
2730
2730
|
* `SyncTransport` seam (loopback/conformance hosts, HTTP-only
|
|
2731
2731
|
* producers).
|
package/src/database.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Storage abstraction
|
|
2
|
+
* Storage abstraction: the client core runs on any SQLite that
|
|
3
3
|
* implements this minimal synchronous surface. Tests use bun:sqlite
|
|
4
4
|
* (`./bun-database`); browsers use sqlite-wasm + OPFS (`./wasm-database`).
|
|
5
5
|
* Methods are synchronous because both backends execute synchronously once
|
package/src/devtools.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The client-side introspection registry
|
|
2
|
+
* The client-side introspection registry: every live
|
|
3
3
|
* `SyncClient` / `SyncClientHandle` on a page registers itself on
|
|
4
4
|
* `globalThis.__SYNCULAR__`, so a first integration debugs from the console
|
|
5
5
|
* instead of hand-exposing the client:
|
package/src/http.ts
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* Browser transport bindings (§1.1, §5.4/§5.5, §8.1): fetch-based sync
|
|
3
3
|
* transport, segment download with signed-URL preference and direct-serve
|
|
4
4
|
* fallback, and a WebSocket realtime connector. Core tests never use these
|
|
5
|
-
* (the loopback doctrine);
|
|
5
|
+
* (the loopback doctrine); the browser fixture exercises them in a real browser.
|
|
6
6
|
*/
|
|
7
7
|
import type { BlobTransport } from './blob';
|
|
8
8
|
import { SSP2_CONTENT_TYPE } from './content-type';
|
|
9
9
|
import { ClientSyncError } from './errors';
|
|
10
10
|
import type {
|
|
11
11
|
RealtimeConnector,
|
|
12
|
+
RemoteOperationTransport,
|
|
13
|
+
RemoteOperationRealtimeConnector,
|
|
12
14
|
SegmentDownloader,
|
|
13
15
|
SyncTransport,
|
|
14
16
|
} from './transport';
|
|
@@ -57,6 +59,78 @@ export function httpSyncTransport(
|
|
|
57
59
|
};
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
/** POST one registered authoritative operation to `<mount>/operations`. */
|
|
63
|
+
export function httpRemoteOperationTransport(
|
|
64
|
+
operationsUrl: string,
|
|
65
|
+
options?: HttpTransportOptions,
|
|
66
|
+
): RemoteOperationTransport {
|
|
67
|
+
const doFetch = options?.fetch ?? fetch;
|
|
68
|
+
return async (request) => {
|
|
69
|
+
const response = await doFetch(operationsUrl, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/vnd.syncular.operations.v1+json',
|
|
73
|
+
...options?.headers,
|
|
74
|
+
},
|
|
75
|
+
body: request.slice().buffer as ArrayBuffer,
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) await throwHttpError(response);
|
|
78
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** WebSocket connector for registered query snapshots. */
|
|
83
|
+
export function webSocketRemoteOperationConnector(
|
|
84
|
+
realtimeUrl: string,
|
|
85
|
+
): RemoteOperationRealtimeConnector {
|
|
86
|
+
return (handlers) =>
|
|
87
|
+
new Promise((resolve, reject) => {
|
|
88
|
+
const socket = new WebSocket(realtimeUrl);
|
|
89
|
+
let opened = false;
|
|
90
|
+
socket.binaryType = 'arraybuffer';
|
|
91
|
+
socket.onopen = () => {
|
|
92
|
+
opened = true;
|
|
93
|
+
resolve({
|
|
94
|
+
send: (bytes) => socket.send(bytes.slice().buffer as ArrayBuffer),
|
|
95
|
+
close: () => socket.close(),
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
socket.onmessage = (event) => {
|
|
99
|
+
if (event.data instanceof ArrayBuffer) {
|
|
100
|
+
handlers.onMessage(new Uint8Array(event.data));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
socket.onerror = () => {
|
|
104
|
+
if (!opened) {
|
|
105
|
+
reject(
|
|
106
|
+
new ClientSyncError(
|
|
107
|
+
'sync.transport_failed',
|
|
108
|
+
'remote operation realtime socket failed to connect',
|
|
109
|
+
true,
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
socket.close();
|
|
115
|
+
} catch {
|
|
116
|
+
handlers.onClose?.();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
socket.onclose = () => {
|
|
120
|
+
if (!opened) {
|
|
121
|
+
reject(
|
|
122
|
+
new ClientSyncError(
|
|
123
|
+
'sync.transport_failed',
|
|
124
|
+
'remote operation realtime socket closed while connecting',
|
|
125
|
+
true,
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
handlers.onClose?.();
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
60
134
|
/**
|
|
61
135
|
* §5.5 direct endpoint with the `X-Syncular-Scopes` re-authorization
|
|
62
136
|
* header, plus the §5.4 `fetchUrl` capability (advertises accept bit 3).
|
|
@@ -224,8 +298,10 @@ export function webSocketRealtimeConnector(
|
|
|
224
298
|
return (handlers) =>
|
|
225
299
|
new Promise((resolve, reject) => {
|
|
226
300
|
const socket = new WebSocket(realtimeUrl);
|
|
301
|
+
let opened = false;
|
|
227
302
|
socket.binaryType = 'arraybuffer';
|
|
228
303
|
socket.onopen = () => {
|
|
304
|
+
opened = true;
|
|
229
305
|
resolve({
|
|
230
306
|
send: (text) => socket.send(text),
|
|
231
307
|
sendBytes: (bytes) => {
|
|
@@ -239,15 +315,31 @@ export function webSocketRealtimeConnector(
|
|
|
239
315
|
else handlers.onBinary(new Uint8Array(event.data as ArrayBuffer));
|
|
240
316
|
};
|
|
241
317
|
socket.onerror = () => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
318
|
+
if (!opened) {
|
|
319
|
+
reject(
|
|
320
|
+
new ClientSyncError(
|
|
321
|
+
'sync.transport_failed',
|
|
322
|
+
'realtime socket failed to connect',
|
|
323
|
+
true,
|
|
324
|
+
),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
socket.close();
|
|
329
|
+
} catch {
|
|
330
|
+
handlers.onClose?.();
|
|
331
|
+
}
|
|
249
332
|
};
|
|
250
333
|
socket.onclose = () => {
|
|
334
|
+
if (!opened) {
|
|
335
|
+
reject(
|
|
336
|
+
new ClientSyncError(
|
|
337
|
+
'sync.transport_failed',
|
|
338
|
+
'realtime socket closed while connecting',
|
|
339
|
+
true,
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
251
343
|
handlers.onClose?.();
|
|
252
344
|
};
|
|
253
345
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @syncular/client
|
|
3
|
-
*
|
|
2
|
+
* @syncular/client is the TypeScript client protocol core.
|
|
3
|
+
* SPEC.md is normative.
|
|
4
4
|
*
|
|
5
5
|
* Browser-safe root: database backends live behind subpath exports
|
|
6
6
|
* (`./bun` for bun:sqlite tests, `./wasm` for sqlite-wasm + OPFS); the
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
export * from './apply';
|
|
12
12
|
export * from './availability';
|
|
13
13
|
export * from './blob';
|
|
14
|
+
export * from './browser-storage-persistence';
|
|
14
15
|
export * from './client';
|
|
15
16
|
export * from './content-type';
|
|
16
17
|
export * from './database';
|
|
@@ -29,6 +30,7 @@ export * from './outbox';
|
|
|
29
30
|
export * from './outcomes';
|
|
30
31
|
export * from './query-guard';
|
|
31
32
|
export * from './reactive-store';
|
|
33
|
+
export * from './remote';
|
|
32
34
|
export * from './realtime-supervisor';
|
|
33
35
|
export * from './schema';
|
|
34
36
|
export * from './sql-tag';
|
package/src/invalidation.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Revisioned client-local observation events (SPEC §7.5
|
|
2
|
+
* Revisioned client-local observation events (SPEC §7.5).
|
|
3
3
|
*
|
|
4
4
|
* The core records observer domains while it owns the SQLite transaction,
|
|
5
5
|
* increments the persisted local revision in that same transaction, and emits
|
package/src/leader-lock.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Multi-tab ownership seam
|
|
2
|
+
* Multi-tab ownership seam: exactly one core instance owns the
|
|
3
3
|
* local database. The interface is the whole B3 deliverable — cross-tab
|
|
4
4
|
* follower fanout is post-gate. Browsers use Web Locks; tests use the
|
|
5
5
|
* no-op single-owner lock.
|
|
@@ -15,7 +15,7 @@ export interface LeaderLock {
|
|
|
15
15
|
* Resolves immediately: the lease when leadership was free, `undefined`
|
|
16
16
|
* when another owner holds it. The worker handle uses this so a second
|
|
17
17
|
* tab gets a clear not-leader state instead of blocking forever
|
|
18
|
-
* (followers are post-gate
|
|
18
|
+
* (followers are post-gate).
|
|
19
19
|
*/
|
|
20
20
|
tryAcquire?(name: string): Promise<LeaderLease | undefined>;
|
|
21
21
|
}
|
package/src/multi-tab.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Multi-tab followers
|
|
2
|
+
* Multi-tab followers: one core per origin, N tabs.
|
|
3
3
|
*
|
|
4
4
|
* The leader tab holds the Web Locks lease and runs the worker core (the
|
|
5
5
|
* existing worker-host path, unchanged). Every OTHER tab is a FOLLOWER: it
|
package/src/naming.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The pinned snake→camel naming map
|
|
2
|
+
* The pinned snake→camel naming map. The
|
|
3
3
|
* client-side copy of the typegen algorithm (kept in lockstep by shared
|
|
4
4
|
* test vectors; the Rust core carries the same function). Used by `mutate`
|
|
5
5
|
* to accept BOTH casings for value keys: the canonical camelCase the
|
package/src/node-database.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ClientDatabase` on better-sqlite3 — the Electron-main / plain-Node
|
|
3
|
-
* backend
|
|
3
|
+
* backend. Semantics mirror `./bun-database`
|
|
4
4
|
* exactly (synchronous exec/query/transaction with the shared savepoint
|
|
5
5
|
* helper, and the same §5.3 sqlite-image ATTACH path), so the core behaves
|
|
6
6
|
* identically whether it runs on bun:sqlite (tests), sqlite-wasm (browser)
|
package/src/query-guard.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The raw-query guard
|
|
2
|
+
* The raw-query guard. `client.query()` and the React
|
|
3
3
|
* `useRawSql` hook are the untrusted raw-SQL tier: an app hands us a SQL
|
|
4
4
|
* string and we run it against the local database. Two rules make that safe
|
|
5
5
|
* to expose, enforced HERE in the core (previously they lived in the
|