syncorejs 0.2.2 → 0.2.4
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/dist/_vendor/cli/app.d.mts.map +1 -1
- package/dist/_vendor/cli/app.mjs +8 -5
- package/dist/_vendor/cli/app.mjs.map +1 -1
- package/dist/_vendor/cli/context.mjs.map +1 -1
- package/dist/_vendor/cli/dev-session.mjs.map +1 -1
- package/dist/_vendor/cli/doctor.mjs.map +1 -1
- package/dist/_vendor/cli/errors.mjs.map +1 -1
- package/dist/_vendor/cli/help.mjs.map +1 -1
- package/dist/_vendor/cli/index.mjs +9 -2
- package/dist/_vendor/cli/index.mjs.map +1 -1
- package/dist/_vendor/cli/messages.mjs.map +1 -1
- package/dist/_vendor/cli/preflight.mjs.map +1 -1
- package/dist/_vendor/cli/project.mjs +20 -20
- package/dist/_vendor/cli/project.mjs.map +1 -1
- package/dist/_vendor/cli/render.mjs.map +1 -1
- package/dist/_vendor/cli/targets.mjs.map +1 -1
- package/dist/_vendor/core/cli.d.mts +8 -2
- package/dist/_vendor/core/cli.d.mts.map +1 -1
- package/dist/_vendor/core/cli.mjs +510 -71
- package/dist/_vendor/core/cli.mjs.map +1 -1
- package/dist/_vendor/core/devtools-auth.mjs.map +1 -1
- package/dist/_vendor/core/index.d.mts +3 -3
- package/dist/_vendor/core/runtime/components.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/components.mjs.map +1 -1
- package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/devtools.mjs +261 -23
- package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
- package/dist/_vendor/core/runtime/functions.d.mts +388 -6
- package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/functions.mjs +72 -1
- package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
- package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/id.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +12 -6
- package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +123 -20
- package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +56 -8
- package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +49 -14
- package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +4 -7
- package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs +81 -2
- package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +100 -13
- package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +42 -7
- package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -1
- package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +4 -0
- package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -1
- package/dist/_vendor/core/runtime/runtime.d.mts +1100 -12
- package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
- package/dist/_vendor/core/runtime/runtime.mjs +63 -0
- package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
- package/dist/_vendor/core/transport.d.mts +2 -0
- package/dist/_vendor/core/transport.d.mts.map +1 -1
- package/dist/_vendor/core/transport.mjs +61 -27
- package/dist/_vendor/core/transport.mjs.map +1 -1
- package/dist/_vendor/devtools-protocol/index.d.ts +223 -4
- package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
- package/dist/_vendor/devtools-protocol/index.js.map +1 -1
- package/dist/_vendor/next/config.d.ts +3 -4
- package/dist/_vendor/next/config.d.ts.map +1 -1
- package/dist/_vendor/next/config.js +37 -19
- package/dist/_vendor/next/config.js.map +1 -1
- package/dist/_vendor/next/index.d.ts +109 -29
- package/dist/_vendor/next/index.d.ts.map +1 -1
- package/dist/_vendor/next/index.js +86 -18
- package/dist/_vendor/next/index.js.map +1 -1
- package/dist/_vendor/platform-expo/index.d.ts +146 -27
- package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
- package/dist/_vendor/platform-expo/index.js +81 -10
- package/dist/_vendor/platform-expo/index.js.map +1 -1
- package/dist/_vendor/platform-expo/react.js.map +1 -1
- package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
- package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
- package/dist/_vendor/platform-node/index.d.mts +174 -9
- package/dist/_vendor/platform-node/index.d.mts.map +1 -1
- package/dist/_vendor/platform-node/index.mjs +251 -95
- package/dist/_vendor/platform-node/index.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc-react.mjs +4 -0
- package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
- package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
- package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
- package/dist/_vendor/platform-web/external-change.d.ts +41 -0
- package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
- package/dist/_vendor/platform-web/external-change.js +30 -0
- package/dist/_vendor/platform-web/external-change.js.map +1 -1
- package/dist/_vendor/platform-web/index.d.ts +312 -37
- package/dist/_vendor/platform-web/index.d.ts.map +1 -1
- package/dist/_vendor/platform-web/index.js +247 -25
- package/dist/_vendor/platform-web/index.js.map +1 -1
- package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
- package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
- package/dist/_vendor/platform-web/indexeddb.js +10 -0
- package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
- package/dist/_vendor/platform-web/opfs.d.ts +16 -1
- package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
- package/dist/_vendor/platform-web/opfs.js +41 -3
- package/dist/_vendor/platform-web/opfs.js.map +1 -1
- package/dist/_vendor/platform-web/persistence.d.ts +85 -1
- package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
- package/dist/_vendor/platform-web/persistence.js +15 -0
- package/dist/_vendor/platform-web/persistence.js.map +1 -1
- package/dist/_vendor/platform-web/react.d.ts +1 -2
- package/dist/_vendor/platform-web/react.d.ts.map +1 -1
- package/dist/_vendor/platform-web/react.js +11 -5
- package/dist/_vendor/platform-web/react.js.map +1 -1
- package/dist/_vendor/platform-web/sqljs.js +10 -1
- package/dist/_vendor/platform-web/sqljs.js.map +1 -1
- package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
- package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
- package/dist/_vendor/platform-web/worker.d.ts +60 -9
- package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
- package/dist/_vendor/platform-web/worker.js +37 -4
- package/dist/_vendor/platform-web/worker.js.map +1 -1
- package/dist/_vendor/react/index.d.ts +197 -13
- package/dist/_vendor/react/index.d.ts.map +1 -1
- package/dist/_vendor/react/index.js +209 -17
- package/dist/_vendor/react/index.js.map +1 -1
- package/dist/_vendor/schema/definition.d.ts +129 -0
- package/dist/_vendor/schema/definition.d.ts.map +1 -1
- package/dist/_vendor/schema/definition.js +99 -0
- package/dist/_vendor/schema/definition.js.map +1 -1
- package/dist/_vendor/schema/planner.d.ts.map +1 -1
- package/dist/_vendor/schema/planner.js.map +1 -1
- package/dist/_vendor/schema/validators.d.ts +180 -4
- package/dist/_vendor/schema/validators.d.ts.map +1 -1
- package/dist/_vendor/schema/validators.js +35 -1
- package/dist/_vendor/schema/validators.js.map +1 -1
- package/dist/_vendor/svelte/index.d.ts +207 -7
- package/dist/_vendor/svelte/index.d.ts.map +1 -1
- package/dist/_vendor/svelte/index.js +201 -6
- package/dist/_vendor/svelte/index.js.map +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +24 -21
|
@@ -1,27 +1,111 @@
|
|
|
1
1
|
//#region src/persistence.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A binary file record stored in web persistence (OPFS or IndexedDB).
|
|
4
|
+
* Returned by `SyncoreWebPersistence.getFile` and `listFiles`.
|
|
5
|
+
*/
|
|
2
6
|
interface StoredWebFile {
|
|
7
|
+
/** Unique file identifier within its namespace. */
|
|
3
8
|
id: string;
|
|
9
|
+
/** Raw file bytes. */
|
|
4
10
|
bytes: Uint8Array;
|
|
11
|
+
/** MIME type, or `null` if none was recorded at write time. */
|
|
5
12
|
contentType: string | null;
|
|
13
|
+
/** File size in bytes. */
|
|
6
14
|
size: number;
|
|
7
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* A byte range from a web persistence file, plus total object metadata.
|
|
18
|
+
*/
|
|
19
|
+
interface StoredWebFileRange {
|
|
20
|
+
/** Unique file identifier within its namespace. */
|
|
21
|
+
id: string;
|
|
22
|
+
/** Raw bytes for the requested range. */
|
|
23
|
+
bytes: Uint8Array;
|
|
24
|
+
/** MIME type, or `null` if none was recorded at write time. */
|
|
25
|
+
contentType: string | null;
|
|
26
|
+
/** Total file size in bytes. */
|
|
27
|
+
size: number;
|
|
28
|
+
/** Offset used for this range. */
|
|
29
|
+
offset: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Metadata for a web persistence file without the raw bytes.
|
|
33
|
+
*/
|
|
34
|
+
interface StoredWebFileMetadata {
|
|
35
|
+
/** Unique file identifier within its namespace. */
|
|
36
|
+
id: string;
|
|
37
|
+
/** MIME type, or `null` if none was recorded at write time. */
|
|
38
|
+
contentType: string | null;
|
|
39
|
+
/** Total file size in bytes. */
|
|
40
|
+
size: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Abstraction over browser storage backends (OPFS or IndexedDB).
|
|
44
|
+
*
|
|
45
|
+
* Handles both the SQLite database blob and binary file objects. All
|
|
46
|
+
* implementations must persist data across page reloads.
|
|
47
|
+
*
|
|
48
|
+
* The concrete implementation is chosen by `createWebPersistence` based on
|
|
49
|
+
* browser capabilities and the requested `WebPersistenceMode`.
|
|
50
|
+
*/
|
|
8
51
|
interface SyncoreWebPersistence {
|
|
52
|
+
/** The storage protocol used: `"opfs"` (Origin Private File System) or `"idb"` (IndexedDB). */
|
|
9
53
|
readonly storageProtocol: "idb" | "opfs";
|
|
54
|
+
/** Load the serialized SQLite database for `key`, or `null` if none has been saved yet. */
|
|
10
55
|
loadDatabase(key: string): Promise<Uint8Array | null>;
|
|
56
|
+
/** Persist the serialized SQLite database bytes for `key`. */
|
|
11
57
|
saveDatabase(key: string, bytes: Uint8Array): Promise<void>;
|
|
58
|
+
/** Retrieve a stored file from `namespace` by `id`, or `null` if not found. */
|
|
12
59
|
getFile(namespace: string, id: string): Promise<StoredWebFile | null>;
|
|
60
|
+
/** Retrieve a byte range without loading the whole file when supported. */
|
|
61
|
+
getFileRange?(namespace: string, id: string, offset: number, length: number): Promise<StoredWebFileRange | null>;
|
|
62
|
+
/** Write a file into `namespace` under `id`, replacing any existing entry. */
|
|
13
63
|
putFile(namespace: string, id: string, bytes: Uint8Array, contentType: string | null): Promise<void>;
|
|
64
|
+
/** Delete a file from `namespace` by `id`. No-op if the file does not exist. */
|
|
14
65
|
deleteFile(namespace: string, id: string): Promise<void>;
|
|
66
|
+
/** List all stored files in `namespace`. */
|
|
15
67
|
listFiles(namespace: string): Promise<StoredWebFile[]>;
|
|
68
|
+
/** List stored file metadata without loading file bytes when supported. */
|
|
69
|
+
listFileMetadata?(namespace: string): Promise<StoredWebFileMetadata[]>;
|
|
16
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Which browser storage backend Syncore should use for SQLite persistence.
|
|
73
|
+
*
|
|
74
|
+
* - `"opfs"` — Origin Private File System. Fastest option; available in
|
|
75
|
+
* Chrome 102+, Safari 15.2+, and modern Firefox. **Required** for
|
|
76
|
+
* multi-tab coordination using `SharedArrayBuffer`.
|
|
77
|
+
* - `"indexeddb"` — Falls back to IndexedDB for browsers without OPFS.
|
|
78
|
+
* Slower due to serialization overhead but universally available.
|
|
79
|
+
* - `"auto"` *(default)* — Picks `"opfs"` when available, otherwise
|
|
80
|
+
* `"indexeddb"`.
|
|
81
|
+
*/
|
|
17
82
|
type WebPersistenceMode = "auto" | "indexeddb" | "opfs";
|
|
83
|
+
/** Options for {@link createWebPersistence}. */
|
|
18
84
|
interface CreateWebPersistenceOptions {
|
|
85
|
+
/** Persistence backend to use. Defaults to `"auto"`. */
|
|
19
86
|
mode?: WebPersistenceMode;
|
|
87
|
+
/** Custom IndexedDB database name. Defaults to the Syncore database name. */
|
|
20
88
|
indexedDbDatabaseName?: string;
|
|
89
|
+
/** Root directory name inside the OPFS bucket. Defaults to the Syncore database name. */
|
|
21
90
|
opfsRootDirectoryName?: string;
|
|
22
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Create the appropriate web persistence backend based on browser capabilities
|
|
94
|
+
* and the requested mode.
|
|
95
|
+
*
|
|
96
|
+
* Call this if you need a `SyncoreWebPersistence` instance outside of
|
|
97
|
+
* `createWebSyncoreRuntime` (e.g. in the Expo adapter). In a standard
|
|
98
|
+
* browser setup, `createWebSyncoreRuntime` calls this automatically.
|
|
99
|
+
*/
|
|
23
100
|
declare function createWebPersistence(options?: CreateWebPersistenceOptions): Promise<SyncoreWebPersistence>;
|
|
101
|
+
/**
|
|
102
|
+
* Return `true` if the Origin Private File System API is available in the
|
|
103
|
+
* current browser context.
|
|
104
|
+
*
|
|
105
|
+
* Used internally to decide whether to prefer OPFS over IndexedDB in `"auto"`
|
|
106
|
+
* mode. Also useful in application code for displaying conditional UI.
|
|
107
|
+
*/
|
|
24
108
|
declare function isOpfsAvailable(): boolean;
|
|
25
109
|
//#endregion
|
|
26
|
-
export { CreateWebPersistenceOptions, StoredWebFile, SyncoreWebPersistence, WebPersistenceMode, createWebPersistence, isOpfsAvailable };
|
|
110
|
+
export { CreateWebPersistenceOptions, StoredWebFile, StoredWebFileMetadata, StoredWebFileRange, SyncoreWebPersistence, WebPersistenceMode, createWebPersistence, isOpfsAvailable };
|
|
27
111
|
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","names":[],"sources":["../src/persistence.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","names":[],"sources":["../src/persistence.ts"],"mappings":";;AAOA;;;UAAiB,aAAA;EAEf;EAAA,EAAA;EAEO;EAAP,KAAA,EAAO,UAAU;EAIjB;EAFA,WAAA;EAEI;EAAJ,IAAA;AAAA;;;;UAMe,kBAAA;EAIR;EAFP,EAAA;EAMA;EAJA,KAAA,EAAO,UAAU;EAMX;EAJN,WAAA;EAUe;EARf,IAAA;;EAEA,MAAA;AAAA;;;;UAMe,qBAAA;EAkBA;EAhBf,EAAA;;EAEA,WAAA;EAkB2B;EAhB3B,IAAA;AAAA;;;;;;;;;;UAYe,qBAAA;EA4BuB;EAAA,SA1B7B,eAAA;EA0BoC;EAxB7C,YAAA,CAAa,GAAA,WAAc,OAAA,CAAQ,UAAA;EAAnC;EAEA,YAAA,CAAa,GAAA,UAAa,KAAA,EAAO,UAAA,GAAa,OAAA;EAFnB;EAI3B,OAAA,CAAQ,SAAA,UAAmB,EAAA,WAAa,OAAA,CAAQ,aAAA;EAFhD;EAIA,YAAA,EACE,SAAA,UACA,EAAA,UACA,MAAA,UACA,MAAA,WACC,OAAA,CAAQ,kBAAA;EATsB;EAWjC,OAAA,CACE,SAAA,UACA,EAAA,UACA,KAAA,EAAO,UAAA,EACP,WAAA,kBACC,OAAA;EAhB2C;EAkB9C,UAAA,CAAW,SAAA,UAAmB,EAAA,WAAa,OAAA;EAhBnC;EAkBR,SAAA,CAAU,SAAA,WAAoB,OAAA,CAAQ,aAAA;EAlBE;EAoBxC,gBAAA,EAAkB,SAAA,WAAoB,OAAA,CAAQ,qBAAA;AAAA;;;;;;;;;;;;KAcpC,kBAAA;;UAGK,2BAAA;EArBJ;EAuBX,IAAA,GAAO,kBAAkB;EAvBkB;EAyB3C,qBAAA;EAvBU;EAyBV,qBAAA;AAAA;;;;;;AAvBmE;AAcrE;;iBAoBsB,oBAAA,CACpB,OAAA,GAAS,2BAAA,GACR,OAAA,CAAQ,qBAAA;;AAtBmB;AAG9B;;;;;iBAuDgB,eAAA,CAAA"}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { SyncoreIndexedDbPersistence } from "./indexeddb.js";
|
|
2
2
|
import { SyncoreOpfsPersistence } from "./opfs.js";
|
|
3
3
|
//#region src/persistence.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create the appropriate web persistence backend based on browser capabilities
|
|
6
|
+
* and the requested mode.
|
|
7
|
+
*
|
|
8
|
+
* Call this if you need a `SyncoreWebPersistence` instance outside of
|
|
9
|
+
* `createWebSyncoreRuntime` (e.g. in the Expo adapter). In a standard
|
|
10
|
+
* browser setup, `createWebSyncoreRuntime` calls this automatically.
|
|
11
|
+
*/
|
|
4
12
|
async function createWebPersistence(options = {}) {
|
|
5
13
|
const mode = options.mode ?? "auto";
|
|
6
14
|
if (mode === "opfs") {
|
|
@@ -10,6 +18,13 @@ async function createWebPersistence(options = {}) {
|
|
|
10
18
|
if (mode === "auto" && isOpfsAvailable()) return new SyncoreOpfsPersistence(options.opfsRootDirectoryName ? { rootDirectoryName: options.opfsRootDirectoryName } : void 0);
|
|
11
19
|
return new SyncoreIndexedDbPersistence(options.indexedDbDatabaseName ? { databaseName: options.indexedDbDatabaseName } : void 0);
|
|
12
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Return `true` if the Origin Private File System API is available in the
|
|
23
|
+
* current browser context.
|
|
24
|
+
*
|
|
25
|
+
* Used internally to decide whether to prefer OPFS over IndexedDB in `"auto"`
|
|
26
|
+
* mode. Also useful in application code for displaying conditional UI.
|
|
27
|
+
*/
|
|
13
28
|
function isOpfsAvailable() {
|
|
14
29
|
return Boolean(getOpfsStorageManager()?.getDirectory);
|
|
15
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.js","names":[],"sources":["../src/persistence.ts"],"sourcesContent":["import { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\nimport { SyncoreOpfsPersistence } from \"./opfs.js\";\n\nexport interface StoredWebFile {\n id: string;\n bytes: Uint8Array;\n contentType: string | null;\n size: number;\n}\n\nexport interface SyncoreWebPersistence {\n readonly storageProtocol: \"idb\" | \"opfs\";\n loadDatabase(key: string): Promise<Uint8Array | null>;\n saveDatabase(key: string, bytes: Uint8Array): Promise<void>;\n getFile(namespace: string, id: string): Promise<StoredWebFile | null>;\n putFile(\n namespace: string,\n id: string,\n bytes: Uint8Array,\n contentType: string | null\n ): Promise<void>;\n deleteFile(namespace: string, id: string): Promise<void>;\n listFiles(namespace: string): Promise<StoredWebFile[]>;\n}\n\nexport type WebPersistenceMode = \"auto\" | \"indexeddb\" | \"opfs\";\n\nexport interface CreateWebPersistenceOptions {\n mode?: WebPersistenceMode;\n indexedDbDatabaseName?: string;\n opfsRootDirectoryName?: string;\n}\n\nexport async function createWebPersistence(\n options: CreateWebPersistenceOptions = {}\n): Promise<SyncoreWebPersistence> {\n const mode = options.mode ?? \"auto\";\n\n if (mode === \"opfs\") {\n if (!isOpfsAvailable()) {\n throw new Error(\"OPFS is not available in this environment.\");\n }\n return new SyncoreOpfsPersistence(\n options.opfsRootDirectoryName\n ? { rootDirectoryName: options.opfsRootDirectoryName }\n : undefined\n );\n }\n\n if (mode === \"auto\" && isOpfsAvailable()) {\n return new SyncoreOpfsPersistence(\n options.opfsRootDirectoryName\n ? { rootDirectoryName: options.opfsRootDirectoryName }\n : undefined\n );\n }\n\n return new SyncoreIndexedDbPersistence(\n options.indexedDbDatabaseName\n ? { databaseName: options.indexedDbDatabaseName }\n : undefined\n );\n}\n\nexport function isOpfsAvailable(): boolean {\n return Boolean(getOpfsStorageManager()?.getDirectory);\n}\n\ntype OpfsStorageManager = StorageManager & {\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\n};\n\nfunction getOpfsStorageManager(): OpfsStorageManager | undefined {\n if (typeof navigator === \"undefined\") {\n return undefined;\n }\n return navigator.storage as OpfsStorageManager | undefined;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"persistence.js","names":[],"sources":["../src/persistence.ts"],"sourcesContent":["import { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\nimport { SyncoreOpfsPersistence } from \"./opfs.js\";\n\n/**\n * A binary file record stored in web persistence (OPFS or IndexedDB).\n * Returned by `SyncoreWebPersistence.getFile` and `listFiles`.\n */\nexport interface StoredWebFile {\n /** Unique file identifier within its namespace. */\n id: string;\n /** Raw file bytes. */\n bytes: Uint8Array;\n /** MIME type, or `null` if none was recorded at write time. */\n contentType: string | null;\n /** File size in bytes. */\n size: number;\n}\n\n/**\n * A byte range from a web persistence file, plus total object metadata.\n */\nexport interface StoredWebFileRange {\n /** Unique file identifier within its namespace. */\n id: string;\n /** Raw bytes for the requested range. */\n bytes: Uint8Array;\n /** MIME type, or `null` if none was recorded at write time. */\n contentType: string | null;\n /** Total file size in bytes. */\n size: number;\n /** Offset used for this range. */\n offset: number;\n}\n\n/**\n * Metadata for a web persistence file without the raw bytes.\n */\nexport interface StoredWebFileMetadata {\n /** Unique file identifier within its namespace. */\n id: string;\n /** MIME type, or `null` if none was recorded at write time. */\n contentType: string | null;\n /** Total file size in bytes. */\n size: number;\n}\n\n/**\n * Abstraction over browser storage backends (OPFS or IndexedDB).\n *\n * Handles both the SQLite database blob and binary file objects. All\n * implementations must persist data across page reloads.\n *\n * The concrete implementation is chosen by `createWebPersistence` based on\n * browser capabilities and the requested `WebPersistenceMode`.\n */\nexport interface SyncoreWebPersistence {\n /** The storage protocol used: `\"opfs\"` (Origin Private File System) or `\"idb\"` (IndexedDB). */\n readonly storageProtocol: \"idb\" | \"opfs\";\n /** Load the serialized SQLite database for `key`, or `null` if none has been saved yet. */\n loadDatabase(key: string): Promise<Uint8Array | null>;\n /** Persist the serialized SQLite database bytes for `key`. */\n saveDatabase(key: string, bytes: Uint8Array): Promise<void>;\n /** Retrieve a stored file from `namespace` by `id`, or `null` if not found. */\n getFile(namespace: string, id: string): Promise<StoredWebFile | null>;\n /** Retrieve a byte range without loading the whole file when supported. */\n getFileRange?(\n namespace: string,\n id: string,\n offset: number,\n length: number\n ): Promise<StoredWebFileRange | null>;\n /** Write a file into `namespace` under `id`, replacing any existing entry. */\n putFile(\n namespace: string,\n id: string,\n bytes: Uint8Array,\n contentType: string | null\n ): Promise<void>;\n /** Delete a file from `namespace` by `id`. No-op if the file does not exist. */\n deleteFile(namespace: string, id: string): Promise<void>;\n /** List all stored files in `namespace`. */\n listFiles(namespace: string): Promise<StoredWebFile[]>;\n /** List stored file metadata without loading file bytes when supported. */\n listFileMetadata?(namespace: string): Promise<StoredWebFileMetadata[]>;\n}\n\n/**\n * Which browser storage backend Syncore should use for SQLite persistence.\n *\n * - `\"opfs\"` — Origin Private File System. Fastest option; available in\n * Chrome 102+, Safari 15.2+, and modern Firefox. **Required** for\n * multi-tab coordination using `SharedArrayBuffer`.\n * - `\"indexeddb\"` — Falls back to IndexedDB for browsers without OPFS.\n * Slower due to serialization overhead but universally available.\n * - `\"auto\"` *(default)* — Picks `\"opfs\"` when available, otherwise\n * `\"indexeddb\"`.\n */\nexport type WebPersistenceMode = \"auto\" | \"indexeddb\" | \"opfs\";\n\n/** Options for {@link createWebPersistence}. */\nexport interface CreateWebPersistenceOptions {\n /** Persistence backend to use. Defaults to `\"auto\"`. */\n mode?: WebPersistenceMode;\n /** Custom IndexedDB database name. Defaults to the Syncore database name. */\n indexedDbDatabaseName?: string;\n /** Root directory name inside the OPFS bucket. Defaults to the Syncore database name. */\n opfsRootDirectoryName?: string;\n}\n\n/**\n * Create the appropriate web persistence backend based on browser capabilities\n * and the requested mode.\n *\n * Call this if you need a `SyncoreWebPersistence` instance outside of\n * `createWebSyncoreRuntime` (e.g. in the Expo adapter). In a standard\n * browser setup, `createWebSyncoreRuntime` calls this automatically.\n */\nexport async function createWebPersistence(\n options: CreateWebPersistenceOptions = {}\n): Promise<SyncoreWebPersistence> {\n const mode = options.mode ?? \"auto\";\n\n if (mode === \"opfs\") {\n if (!isOpfsAvailable()) {\n throw new Error(\"OPFS is not available in this environment.\");\n }\n return new SyncoreOpfsPersistence(\n options.opfsRootDirectoryName\n ? { rootDirectoryName: options.opfsRootDirectoryName }\n : undefined\n );\n }\n\n if (mode === \"auto\" && isOpfsAvailable()) {\n return new SyncoreOpfsPersistence(\n options.opfsRootDirectoryName\n ? { rootDirectoryName: options.opfsRootDirectoryName }\n : undefined\n );\n }\n\n return new SyncoreIndexedDbPersistence(\n options.indexedDbDatabaseName\n ? { databaseName: options.indexedDbDatabaseName }\n : undefined\n );\n}\n\n/**\n * Return `true` if the Origin Private File System API is available in the\n * current browser context.\n *\n * Used internally to decide whether to prefer OPFS over IndexedDB in `\"auto\"`\n * mode. Also useful in application code for displaying conditional UI.\n */\nexport function isOpfsAvailable(): boolean {\n return Boolean(getOpfsStorageManager()?.getDirectory);\n}\n\ntype OpfsStorageManager = StorageManager & {\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\n};\n\nfunction getOpfsStorageManager(): OpfsStorageManager | undefined {\n if (typeof navigator === \"undefined\") {\n return undefined;\n }\n return navigator.storage as OpfsStorageManager | undefined;\n}\n"],"mappings":";;;;;;;;;;;AAqHA,eAAsB,qBACpB,UAAuC,CAAC,GACR;CAChC,MAAM,OAAO,QAAQ,QAAQ;CAE7B,IAAI,SAAS,QAAQ;EACnB,IAAI,CAAC,gBAAgB,GACnB,MAAM,IAAI,MAAM,4CAA4C;EAE9D,OAAO,IAAI,uBACT,QAAQ,wBACJ,EAAE,mBAAmB,QAAQ,sBAAsB,IACnD,KAAA,CACN;CACF;CAEA,IAAI,SAAS,UAAU,gBAAgB,GACrC,OAAO,IAAI,uBACT,QAAQ,wBACJ,EAAE,mBAAmB,QAAQ,sBAAsB,IACnD,KAAA,CACN;CAGF,OAAO,IAAI,4BACT,QAAQ,wBACJ,EAAE,cAAc,QAAQ,sBAAsB,IAC9C,KAAA,CACN;AACF;;;;;;;;AASA,SAAgB,kBAA2B;CACzC,OAAO,QAAQ,sBAAsB,GAAG,YAAY;AACtD;AAMA,SAAS,wBAAwD;CAC/D,IAAI,OAAO,cAAc,aACvB;CAEF,OAAO,UAAU;AACnB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CreateWebWorkerClientProviderOptions } from "./worker.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
-
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
3
|
|
|
5
4
|
//#region src/react.d.ts
|
|
6
5
|
/**
|
|
@@ -29,7 +28,7 @@ declare function SyncoreWebProvider({
|
|
|
29
28
|
/**
|
|
30
29
|
* Start a worker-backed Syncore client and provide it to React descendants.
|
|
31
30
|
*/
|
|
32
|
-
declare function SyncoreBrowserProvider(props: SyncoreBrowserProviderProps):
|
|
31
|
+
declare function SyncoreBrowserProvider(props: SyncoreBrowserProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
33
32
|
//#endregion
|
|
34
33
|
export { SyncoreBrowserProvider, SyncoreBrowserProviderProps, SyncoreWebProvider, SyncoreWebProviderProps };
|
|
35
34
|
//# sourceMappingURL=react.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.tsx"],"mappings":";;;;;;AAgBA;UAAiB,uBAAA,SAAgC,oCAAA;;EAE/C,QAAA,EAAU,SAAA;EAGC;EAAX,QAAA,GAAW,SAAA;AAAA;;;;KAMD,2BAAA,GAA8B,uBAAuB;;;;iBAKjD,kBAAA,CAAA;EACd,QAAA;EACA,SAAA;EACA,UAAA;EACA,UAAA;EACA;AAAA,GACC,uBAAA,GAA0B,SAAA;AAX7B;;;AAAA,iBAoEgB,sBAAA,CAAuB,KAAA,EAAO,2BAA2B,+BAAA,GAAA,CAAA,OAAA"}
|
|
@@ -10,11 +10,14 @@ import { jsx } from "react/jsx-runtime";
|
|
|
10
10
|
function SyncoreWebProvider({ children, workerUrl, workerType, workerName, fallback = null }) {
|
|
11
11
|
const bootingClient = useMemo(() => createUnavailableSyncoreClient({
|
|
12
12
|
kind: "starting",
|
|
13
|
-
reason: "booting"
|
|
13
|
+
reason: "booting",
|
|
14
|
+
capabilities: { storage: {
|
|
15
|
+
available: false,
|
|
16
|
+
reason: "Syncore worker is booting."
|
|
17
|
+
} }
|
|
14
18
|
}), []);
|
|
15
19
|
const [client, setClient] = useState(bootingClient);
|
|
16
20
|
useEffect(() => {
|
|
17
|
-
let disposed = false;
|
|
18
21
|
let managedClient;
|
|
19
22
|
setClient(bootingClient);
|
|
20
23
|
try {
|
|
@@ -23,16 +26,19 @@ function SyncoreWebProvider({ children, workerUrl, workerType, workerName, fallb
|
|
|
23
26
|
...workerType ? { workerType } : {},
|
|
24
27
|
...workerName ? { workerName } : {}
|
|
25
28
|
});
|
|
26
|
-
|
|
29
|
+
setClient(managedClient.client);
|
|
27
30
|
} catch (error) {
|
|
28
|
-
|
|
31
|
+
setClient(createUnavailableSyncoreClient({
|
|
29
32
|
kind: "unavailable",
|
|
30
33
|
reason: "worker-unavailable",
|
|
34
|
+
capabilities: { storage: {
|
|
35
|
+
available: false,
|
|
36
|
+
reason: "Syncore worker is unavailable."
|
|
37
|
+
} },
|
|
31
38
|
...error instanceof Error ? { error } : {}
|
|
32
39
|
}));
|
|
33
40
|
}
|
|
34
41
|
return () => {
|
|
35
|
-
disposed = true;
|
|
36
42
|
managedClient?.dispose();
|
|
37
43
|
};
|
|
38
44
|
}, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import {\n createUnavailableSyncoreClient,\n type SyncoreClient\n} from \"@syncore/core\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport {\n createSyncoreWebWorkerClient,\n type CreateWebWorkerClientProviderOptions,\n type ManagedWebWorkerClient\n} from \"./worker.js\";\n\n/**\n * Props for {@link SyncoreWebProvider}.\n */\nexport interface SyncoreWebProviderProps extends CreateWebWorkerClientProviderOptions {\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /** Optional fallback content rendered before the worker client is ready. */\n fallback?: ReactNode;\n}\n\n/**\n * Props for {@link SyncoreBrowserProvider}.\n */\nexport type SyncoreBrowserProviderProps = SyncoreWebProviderProps;\n\n/**\n * Start a worker-backed Syncore client and provide it to React descendants.\n */\nexport function SyncoreWebProvider({\n children,\n workerUrl,\n workerType,\n workerName,\n fallback = null\n}: SyncoreWebProviderProps): ReactNode {\n const bootingClient = useMemo(\n () =>\n createUnavailableSyncoreClient({\n kind: \"starting\",\n reason: \"booting\"\n }),\n []\n );\n const [client, setClient] = useState<SyncoreClient>(bootingClient);\n\n useEffect(() => {\n let
|
|
1
|
+
{"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import {\n createUnavailableSyncoreClient,\n type SyncoreClient\n} from \"@syncore/core\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport {\n createSyncoreWebWorkerClient,\n type CreateWebWorkerClientProviderOptions,\n type ManagedWebWorkerClient\n} from \"./worker.js\";\n\n/**\n * Props for {@link SyncoreWebProvider}.\n */\nexport interface SyncoreWebProviderProps extends CreateWebWorkerClientProviderOptions {\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /** Optional fallback content rendered before the worker client is ready. */\n fallback?: ReactNode;\n}\n\n/**\n * Props for {@link SyncoreBrowserProvider}.\n */\nexport type SyncoreBrowserProviderProps = SyncoreWebProviderProps;\n\n/**\n * Start a worker-backed Syncore client and provide it to React descendants.\n */\nexport function SyncoreWebProvider({\n children,\n workerUrl,\n workerType,\n workerName,\n fallback = null\n}: SyncoreWebProviderProps): ReactNode {\n const bootingClient = useMemo(\n () =>\n createUnavailableSyncoreClient({\n kind: \"starting\",\n reason: \"booting\",\n capabilities: {\n storage: { available: false, reason: \"Syncore worker is booting.\" }\n }\n }),\n []\n );\n const [client, setClient] = useState<SyncoreClient>(bootingClient);\n\n useEffect(() => {\n let managedClient: ManagedWebWorkerClient | undefined;\n\n setClient(bootingClient);\n\n try {\n managedClient = createSyncoreWebWorkerClient({\n workerUrl,\n ...(workerType ? { workerType } : {}),\n ...(workerName ? { workerName } : {})\n });\n setClient(managedClient.client);\n } catch (error) {\n setClient(\n createUnavailableSyncoreClient({\n kind: \"unavailable\",\n reason: \"worker-unavailable\",\n capabilities: {\n storage: {\n available: false,\n reason: \"Syncore worker is unavailable.\"\n }\n },\n ...(error instanceof Error ? { error } : {})\n })\n );\n }\n\n return () => {\n managedClient?.dispose();\n };\n }, [bootingClient, workerName, workerType, workerUrl]);\n\n return (\n <SyncoreProvider client={client}>\n {children ?? fallback}\n </SyncoreProvider>\n );\n}\n\n/**\n * Start a worker-backed Syncore client and provide it to React descendants.\n */\nexport function SyncoreBrowserProvider(props: SyncoreBrowserProviderProps) {\n return <SyncoreWebProvider {...props} />;\n}\n"],"mappings":";;;;;;;;;AAgCA,SAAgB,mBAAmB,EACjC,UACA,WACA,YACA,YACA,WAAW,QAC0B;CACrC,MAAM,gBAAgB,cAElB,+BAA+B;EAC7B,MAAM;EACN,QAAQ;EACR,cAAc,EACZ,SAAS;GAAE,WAAW;GAAO,QAAQ;EAA6B,EACpE;CACF,CAAC,GACH,CAAC,CACH;CACA,MAAM,CAAC,QAAQ,aAAa,SAAwB,aAAa;CAEjE,gBAAgB;EACd,IAAI;EAEJ,UAAU,aAAa;EAEvB,IAAI;GACF,gBAAgB,6BAA6B;IAC3C;IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;IACnC,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;GACrC,CAAC;GACD,UAAU,cAAc,MAAM;EAChC,SAAS,OAAO;GACd,UACE,+BAA+B;IAC7B,MAAM;IACN,QAAQ;IACR,cAAc,EACZ,SAAS;KACP,WAAW;KACX,QAAQ;IACV,EACF;IACA,GAAI,iBAAiB,QAAQ,EAAE,MAAM,IAAI,CAAC;GAC5C,CAAC,CACH;EACF;EAEA,aAAa;GACX,eAAe,QAAQ;EACzB;CACF,GAAG;EAAC;EAAe;EAAY;EAAY;CAAS,CAAC;CAErD,OACE,oBAAC,iBAAD;EAAyB;YACtB,YAAY;CACE,CAAA;AAErB;;;;AAKA,SAAgB,uBAAuB,OAAoC;CACzE,OAAO,oBAAC,oBAAD,EAAoB,GAAI,MAAQ,CAAA;AACzC"}
|
|
@@ -2,6 +2,10 @@ import { SyncoreIndexedDbPersistence } from "./indexeddb.js";
|
|
|
2
2
|
import initSqlJs from "sql.js";
|
|
3
3
|
//#region src/sqljs.ts
|
|
4
4
|
var SqlJsDriver = class SqlJsDriver {
|
|
5
|
+
database;
|
|
6
|
+
persistence;
|
|
7
|
+
databaseName;
|
|
8
|
+
createDatabase;
|
|
5
9
|
transactionDepth = 0;
|
|
6
10
|
closed = false;
|
|
7
11
|
constructor(database, persistence, databaseName, createDatabase) {
|
|
@@ -12,7 +16,9 @@ var SqlJsDriver = class SqlJsDriver {
|
|
|
12
16
|
}
|
|
13
17
|
static async create(options) {
|
|
14
18
|
const persistence = options.persistence ?? new SyncoreIndexedDbPersistence();
|
|
15
|
-
const
|
|
19
|
+
const hasExplicitWasmResolver = Boolean(options.locateFile ?? options.wasmUrl);
|
|
20
|
+
if (isBrowserLikeRuntime() && !hasExplicitWasmResolver) throw new Error("SqlJsDriver requires an explicit wasmUrl or locateFile in browser runtimes. Use createWebSyncoreRuntime/createBrowserWorkerRuntime for the default Syncore web resolver.");
|
|
21
|
+
const SQL = await initSqlJs(!hasExplicitWasmResolver ? void 0 : { locateFile: options.locateFile ?? (() => options.wasmUrl) });
|
|
16
22
|
const existingBytes = await persistence.loadDatabase(options.databaseName);
|
|
17
23
|
return new SqlJsDriver(existingBytes ? new SQL.Database(existingBytes) : new SQL.Database(), persistence, options.databaseName, (bytes) => bytes ? new SQL.Database(bytes) : new SQL.Database());
|
|
18
24
|
}
|
|
@@ -130,6 +136,9 @@ var SqlJsDriver = class SqlJsDriver {
|
|
|
130
136
|
previousDatabase.close();
|
|
131
137
|
}
|
|
132
138
|
};
|
|
139
|
+
function isBrowserLikeRuntime() {
|
|
140
|
+
return typeof window !== "undefined" || globalThis.WorkerGlobalScope !== void 0;
|
|
141
|
+
}
|
|
133
142
|
function normalizeParams(values) {
|
|
134
143
|
return values.map((value) => {
|
|
135
144
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqljs.js","names":[],"sources":["../src/sqljs.ts"],"sourcesContent":["import initSqlJs from \"sql.js\";\nimport type { RunResult, SyncoreSqlDriver } from \"@syncore/core\";\nimport { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\ntype SqlJsValue = initSqlJs.SqlValue;\n\nexport interface CreateSqlJsDriverOptions {\n databaseName: string;\n persistence?: SyncoreWebPersistence;\n wasmUrl?: string;\n locateFile?: (fileName: string) => string;\n}\n\nexport class SqlJsDriver implements SyncoreSqlDriver {\n private transactionDepth = 0;\n private closed = false;\n\n constructor(\n private database: SqlJsDatabase,\n private readonly persistence: SyncoreWebPersistence,\n private readonly databaseName: string,\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase\n ) {}\n\n static async create(options: CreateSqlJsDriverOptions): Promise<SqlJsDriver> {\n const persistence =\n options.persistence ?? new SyncoreIndexedDbPersistence();\n const SQL = await initSqlJs(\n typeof window === \"undefined\" && !options.locateFile && !options.wasmUrl\n ? undefined\n : {\n locateFile:\n options.locateFile ?? (() => options.wasmUrl ?? \"/sql-wasm.wasm\")\n }\n );\n const existingBytes = await persistence.loadDatabase(options.databaseName);\n const database = existingBytes\n ? new SQL.Database(existingBytes)\n : new SQL.Database();\n return new SqlJsDriver(\n database,\n persistence,\n options.databaseName,\n (bytes) => (bytes ? new SQL.Database(bytes) : new SQL.Database())\n );\n }\n\n async exec(sql: string): Promise<void> {\n this.ensureOpen();\n this.database.run(sql);\n await this.persistIfNeeded();\n }\n\n async run(sql: string, params: unknown[] = []): Promise<RunResult> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.run(normalizeParams(params));\n const lastInsertRowid = readScalarNumber(\n this.database,\n \"SELECT last_insert_rowid()\"\n );\n const result = {\n changes: this.database.getRowsModified(),\n ...(lastInsertRowid !== null ? { lastInsertRowid } : {})\n };\n await this.persistIfNeeded();\n return result;\n } finally {\n statement.free();\n }\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.bind(normalizeParams(params));\n if (!statement.step()) {\n return undefined;\n }\n return statement.getAsObject() as T;\n } finally {\n statement.free();\n }\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.bind(normalizeParams(params));\n const rows: T[] = [];\n while (statement.step()) {\n rows.push(statement.getAsObject() as T);\n }\n return rows;\n } finally {\n statement.free();\n }\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n this.database.run(\"BEGIN IMMEDIATE\");\n try {\n const result = await callback();\n this.database.run(\"COMMIT\");\n await this.persistNow();\n return result;\n } catch (error) {\n this.database.run(\"ROLLBACK\");\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.database.run(`SAVEPOINT ${safeName}`);\n this.transactionDepth += 1;\n try {\n const result = await callback();\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n this.database.run(`ROLLBACK TO SAVEPOINT ${safeName}`);\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n await this.persistNow();\n this.database.close();\n this.closed = true;\n }\n\n async reloadFromPersistence(): Promise<boolean> {\n this.ensureOpen();\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (!bytes) {\n return false;\n }\n const nextDatabase = this.createDatabase(bytes);\n const previousDatabase = this.database;\n this.database = nextDatabase;\n previousDatabase.close();\n return true;\n }\n\n createDatabaseFromBytes(bytes?: Uint8Array): SqlJsDatabase {\n return this.createDatabase(bytes);\n }\n\n private async persistIfNeeded(): Promise<void> {\n if (this.transactionDepth === 0) {\n await this.persistNow();\n }\n }\n\n private async persistNow(): Promise<void> {\n this.ensureOpen();\n await this.persistence.saveDatabase(\n this.databaseName,\n this.database.export()\n );\n }\n\n private ensureOpen(): void {\n if (this.closed) {\n throw new Error(\"The sql.js driver is already closed.\");\n }\n }\n\n replaceDatabase(database: SqlJsDatabase): void {\n this.ensureOpen();\n const previousDatabase = this.database;\n this.database = database;\n previousDatabase.close();\n }\n}\n\nfunction normalizeParams(values: unknown[]): SqlJsValue[] {\n return values.map((value) => {\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n if (\n value === null ||\n typeof value === \"number\" ||\n typeof value === \"string\" ||\n value instanceof Uint8Array\n ) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n return JSON.stringify(value);\n });\n}\n\nfunction readScalarNumber(database: SqlJsDatabase, sql: string): number | null {\n const rows = database.exec(sql);\n const firstSet = rows[0];\n const firstRow = firstSet?.values[0];\n const firstValue = firstRow?.[0];\n if (typeof firstValue === \"number\") {\n return firstValue;\n }\n if (typeof firstValue === \"string\") {\n const parsed = Number(firstValue);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqljs.js","names":["scope"],"sources":["../src/sqljs.ts"],"sourcesContent":["import initSqlJs from \"sql.js\";\nimport type { RunResult, SyncoreSqlDriver } from \"@syncore/core\";\nimport { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\ntype SqlJsValue = initSqlJs.SqlValue;\n\nexport interface CreateSqlJsDriverOptions {\n databaseName: string;\n persistence?: SyncoreWebPersistence;\n wasmUrl?: string;\n locateFile?: (fileName: string) => string;\n}\n\nexport class SqlJsDriver implements SyncoreSqlDriver {\n private transactionDepth = 0;\n private closed = false;\n\n constructor(\n private database: SqlJsDatabase,\n private readonly persistence: SyncoreWebPersistence,\n private readonly databaseName: string,\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase\n ) {}\n\n static async create(options: CreateSqlJsDriverOptions): Promise<SqlJsDriver> {\n const persistence =\n options.persistence ?? new SyncoreIndexedDbPersistence();\n const hasExplicitWasmResolver = Boolean(options.locateFile ?? options.wasmUrl);\n if (isBrowserLikeRuntime() && !hasExplicitWasmResolver) {\n throw new Error(\n \"SqlJsDriver requires an explicit wasmUrl or locateFile in browser runtimes. \" +\n \"Use createWebSyncoreRuntime/createBrowserWorkerRuntime for the default Syncore web resolver.\"\n );\n }\n const SQL = await initSqlJs(\n !hasExplicitWasmResolver\n ? undefined\n : {\n locateFile:\n options.locateFile ?? (() => options.wasmUrl as string)\n }\n );\n const existingBytes = await persistence.loadDatabase(options.databaseName);\n const database = existingBytes\n ? new SQL.Database(existingBytes)\n : new SQL.Database();\n return new SqlJsDriver(\n database,\n persistence,\n options.databaseName,\n (bytes) => (bytes ? new SQL.Database(bytes) : new SQL.Database())\n );\n }\n\n async exec(sql: string): Promise<void> {\n this.ensureOpen();\n this.database.run(sql);\n await this.persistIfNeeded();\n }\n\n async run(sql: string, params: unknown[] = []): Promise<RunResult> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.run(normalizeParams(params));\n const lastInsertRowid = readScalarNumber(\n this.database,\n \"SELECT last_insert_rowid()\"\n );\n const result = {\n changes: this.database.getRowsModified(),\n ...(lastInsertRowid !== null ? { lastInsertRowid } : {})\n };\n await this.persistIfNeeded();\n return result;\n } finally {\n statement.free();\n }\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.bind(normalizeParams(params));\n if (!statement.step()) {\n return undefined;\n }\n return statement.getAsObject() as T;\n } finally {\n statement.free();\n }\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.bind(normalizeParams(params));\n const rows: T[] = [];\n while (statement.step()) {\n rows.push(statement.getAsObject() as T);\n }\n return rows;\n } finally {\n statement.free();\n }\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n this.database.run(\"BEGIN IMMEDIATE\");\n try {\n const result = await callback();\n this.database.run(\"COMMIT\");\n await this.persistNow();\n return result;\n } catch (error) {\n this.database.run(\"ROLLBACK\");\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.database.run(`SAVEPOINT ${safeName}`);\n this.transactionDepth += 1;\n try {\n const result = await callback();\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n this.database.run(`ROLLBACK TO SAVEPOINT ${safeName}`);\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n await this.persistNow();\n this.database.close();\n this.closed = true;\n }\n\n async reloadFromPersistence(): Promise<boolean> {\n this.ensureOpen();\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (!bytes) {\n return false;\n }\n const nextDatabase = this.createDatabase(bytes);\n const previousDatabase = this.database;\n this.database = nextDatabase;\n previousDatabase.close();\n return true;\n }\n\n createDatabaseFromBytes(bytes?: Uint8Array): SqlJsDatabase {\n return this.createDatabase(bytes);\n }\n\n private async persistIfNeeded(): Promise<void> {\n if (this.transactionDepth === 0) {\n await this.persistNow();\n }\n }\n\n private async persistNow(): Promise<void> {\n this.ensureOpen();\n await this.persistence.saveDatabase(\n this.databaseName,\n this.database.export()\n );\n }\n\n private ensureOpen(): void {\n if (this.closed) {\n throw new Error(\"The sql.js driver is already closed.\");\n }\n }\n\n replaceDatabase(database: SqlJsDatabase): void {\n this.ensureOpen();\n const previousDatabase = this.database;\n this.database = database;\n previousDatabase.close();\n }\n}\n\nfunction isBrowserLikeRuntime(): boolean {\n const scope = globalThis as typeof globalThis & {\n WorkerGlobalScope?: unknown;\n };\n return typeof window !== \"undefined\" || scope.WorkerGlobalScope !== undefined;\n}\n\nfunction normalizeParams(values: unknown[]): SqlJsValue[] {\n return values.map((value) => {\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n if (\n value === null ||\n typeof value === \"number\" ||\n typeof value === \"string\" ||\n value instanceof Uint8Array\n ) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n return JSON.stringify(value);\n });\n}\n\nfunction readScalarNumber(database: SqlJsDatabase, sql: string): number | null {\n const rows = database.exec(sql);\n const firstSet = rows[0];\n const firstRow = firstSet?.values[0];\n const firstValue = firstRow?.[0];\n if (typeof firstValue === \"number\") {\n return firstValue;\n }\n if (typeof firstValue === \"string\") {\n const parsed = Number(firstValue);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n}\n"],"mappings":";;;AAeA,IAAa,cAAb,MAAa,YAAwC;CAKzC;CACS;CACA;CACA;CAPnB,mBAA2B;CAC3B,SAAiB;CAEjB,YACE,UACA,aACA,cACA,gBACA;EAJQ,KAAA,WAAA;EACS,KAAA,cAAA;EACA,KAAA,eAAA;EACA,KAAA,iBAAA;CAChB;CAEH,aAAa,OAAO,SAAyD;EAC3E,MAAM,cACJ,QAAQ,eAAe,IAAI,4BAA4B;EACzD,MAAM,0BAA0B,QAAQ,QAAQ,cAAc,QAAQ,OAAO;EAC7E,IAAI,qBAAqB,KAAK,CAAC,yBAC7B,MAAM,IAAI,MACR,0KAEF;EAEF,MAAM,MAAM,MAAM,UAChB,CAAC,0BACG,KAAA,IACA,EACE,YACE,QAAQ,qBAAqB,QAAQ,SACzC,CACN;EACA,MAAM,gBAAgB,MAAM,YAAY,aAAa,QAAQ,YAAY;EAIzE,OAAO,IAAI,YAHM,gBACb,IAAI,IAAI,SAAS,aAAa,IAC9B,IAAI,IAAI,SAAS,GAGnB,aACA,QAAQ,eACP,UAAW,QAAQ,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,IAAI,SAAS,CACjE;CACF;CAEA,MAAM,KAAK,KAA4B;EACrC,KAAK,WAAW;EAChB,KAAK,SAAS,IAAI,GAAG;EACrB,MAAM,KAAK,gBAAgB;CAC7B;CAEA,MAAM,IAAI,KAAa,SAAoB,CAAC,GAAuB;EACjE,KAAK,WAAW;EAChB,MAAM,YAAY,KAAK,SAAS,QAAQ,GAAG;EAC3C,IAAI;GACF,UAAU,IAAI,gBAAgB,MAAM,CAAC;GACrC,MAAM,kBAAkB,iBACtB,KAAK,UACL,4BACF;GACA,MAAM,SAAS;IACb,SAAS,KAAK,SAAS,gBAAgB;IACvC,GAAI,oBAAoB,OAAO,EAAE,gBAAgB,IAAI,CAAC;GACxD;GACA,MAAM,KAAK,gBAAgB;GAC3B,OAAO;EACT,UAAU;GACR,UAAU,KAAK;EACjB;CACF;CAEA,MAAM,IAAO,KAAa,SAAoB,CAAC,GAA2B;EACxE,KAAK,WAAW;EAChB,MAAM,YAAY,KAAK,SAAS,QAAQ,GAAG;EAC3C,IAAI;GACF,UAAU,KAAK,gBAAgB,MAAM,CAAC;GACtC,IAAI,CAAC,UAAU,KAAK,GAClB;GAEF,OAAO,UAAU,YAAY;EAC/B,UAAU;GACR,UAAU,KAAK;EACjB;CACF;CAEA,MAAM,IAAO,KAAa,SAAoB,CAAC,GAAiB;EAC9D,KAAK,WAAW;EAChB,MAAM,YAAY,KAAK,SAAS,QAAQ,GAAG;EAC3C,IAAI;GACF,UAAU,KAAK,gBAAgB,MAAM,CAAC;GACtC,MAAM,OAAY,CAAC;GACnB,OAAO,UAAU,KAAK,GACpB,KAAK,KAAK,UAAU,YAAY,CAAM;GAExC,OAAO;EACT,UAAU;GACR,UAAU,KAAK;EACjB;CACF;CAEA,MAAM,gBAAmB,UAAwC;EAC/D,KAAK,WAAW;EAChB,IAAI,KAAK,mBAAmB,GAC1B,OAAO,KAAK,cAAc,UAAU,KAAK,oBAAoB,QAAQ;EAGvE,KAAK,oBAAoB;EACzB,KAAK,SAAS,IAAI,iBAAiB;EACnC,IAAI;GACF,MAAM,SAAS,MAAM,SAAS;GAC9B,KAAK,SAAS,IAAI,QAAQ;GAC1B,MAAM,KAAK,WAAW;GACtB,OAAO;EACT,SAAS,OAAO;GACd,KAAK,SAAS,IAAI,UAAU;GAC5B,MAAM;EACR,UAAU;GACR,KAAK,oBAAoB;EAC3B;CACF;CAEA,MAAM,cAAiB,MAAc,UAAwC;EAC3E,KAAK,WAAW;EAChB,MAAM,WAAW,KAAK,WAAW,kBAAkB,GAAG;EACtD,KAAK,SAAS,IAAI,aAAa,UAAU;EACzC,KAAK,oBAAoB;EACzB,IAAI;GACF,MAAM,SAAS,MAAM,SAAS;GAC9B,KAAK,SAAS,IAAI,qBAAqB,UAAU;GACjD,OAAO;EACT,SAAS,OAAO;GACd,KAAK,SAAS,IAAI,yBAAyB,UAAU;GACrD,KAAK,SAAS,IAAI,qBAAqB,UAAU;GACjD,MAAM;EACR,UAAU;GACR,KAAK,oBAAoB;EAC3B;CACF;CAEA,MAAM,QAAuB;EAC3B,IAAI,KAAK,QACP;EAEF,MAAM,KAAK,WAAW;EACtB,KAAK,SAAS,MAAM;EACpB,KAAK,SAAS;CAChB;CAEA,MAAM,wBAA0C;EAC9C,KAAK,WAAW;EAChB,MAAM,QAAQ,MAAM,KAAK,YAAY,aAAa,KAAK,YAAY;EACnE,IAAI,CAAC,OACH,OAAO;EAET,MAAM,eAAe,KAAK,eAAe,KAAK;EAC9C,MAAM,mBAAmB,KAAK;EAC9B,KAAK,WAAW;EAChB,iBAAiB,MAAM;EACvB,OAAO;CACT;CAEA,wBAAwB,OAAmC;EACzD,OAAO,KAAK,eAAe,KAAK;CAClC;CAEA,MAAc,kBAAiC;EAC7C,IAAI,KAAK,qBAAqB,GAC5B,MAAM,KAAK,WAAW;CAE1B;CAEA,MAAc,aAA4B;EACxC,KAAK,WAAW;EAChB,MAAM,KAAK,YAAY,aACrB,KAAK,cACL,KAAK,SAAS,OAAO,CACvB;CACF;CAEA,aAA2B;EACzB,IAAI,KAAK,QACP,MAAM,IAAI,MAAM,sCAAsC;CAE1D;CAEA,gBAAgB,UAA+B;EAC7C,KAAK,WAAW;EAChB,MAAM,mBAAmB,KAAK;EAC9B,KAAK,WAAW;EAChB,iBAAiB,MAAM;CACzB;AACF;AAEA,SAAS,uBAAgC;CAIvC,OAAO,OAAO,WAAW,eAAeA,WAAM,sBAAsB,KAAA;AACtE;AAEA,SAAS,gBAAgB,QAAiC;CACxD,OAAO,OAAO,KAAK,UAAU;EAC3B,IAAI,OAAO,UAAU,WACnB,OAAO,QAAQ,IAAI;EAErB,IACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,iBAAiB,YAEjB,OAAO;EAET,IAAI,iBAAiB,aACnB,OAAO,IAAI,WAAW,KAAK;EAE7B,OAAO,KAAK,UAAU,KAAK;CAC7B,CAAC;AACH;AAEA,SAAS,iBAAiB,UAAyB,KAA4B;CAI7E,MAAM,cAHO,SAAS,KAAK,GACP,EAAE,IACK,OAAO,MACJ;CAC9B,IAAI,OAAO,eAAe,UACxB,OAAO;CAET,IAAI,OAAO,eAAe,UAAU;EAClC,MAAM,SAAS,OAAO,UAAU;EAChC,OAAO,OAAO,MAAM,MAAM,IAAI,OAAO;CACvC;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-sqljs-wasm.js","names":[],"sources":["../src/web-sqljs-wasm.ts"],"sourcesContent":["export function resolveDefaultWebSqlJsWasmUrl(): string {\n return new URL(\"sql.js/dist/sql-wasm.wasm\", import.meta.url).toString();\n}\n"],"mappings":";AAAA,SAAgB,gCAAwC;CACtD,OAAO,IAAI,IAAI,6BAA6B,OAAO,KAAK,GAAG,EAAE,SAAS;AACxE"}
|
|
@@ -1,52 +1,103 @@
|
|
|
1
1
|
import { AttachRuntimeBridgeOptions, AttachedRuntimeBridge, BridgeQueryWatch, SyncoreBridgeClient, SyncoreBridgeMessageEndpoint, SyncoreDataModel } from "../core/index.d.mts";
|
|
2
2
|
|
|
3
3
|
//#region src/worker.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Schema type constraint for worker-side Syncore runtimes.
|
|
6
|
+
*
|
|
7
|
+
* Pass any schema produced by `defineSchema()` where this type is expected.
|
|
8
|
+
* Defaults to the unconstrained `SyncoreDataModel` when omitted.
|
|
9
|
+
*/
|
|
4
10
|
type WebWorkerSyncoreSchema<TSchema extends SyncoreDataModel = SyncoreDataModel> = TSchema;
|
|
11
|
+
/** Message endpoint shape required by the browser worker bridge (alias of `SyncoreBridgeMessageEndpoint`). */
|
|
5
12
|
type SyncoreWorkerMessageEndpoint = SyncoreBridgeMessageEndpoint;
|
|
13
|
+
/** Live-query subscription handle returned by `SyncoreWebWorkerClient.watchQuery`. */
|
|
6
14
|
type WorkerQueryWatch<TValue> = BridgeQueryWatch<TValue>;
|
|
15
|
+
/**
|
|
16
|
+
* Syncore client that communicates with a runtime running in a browser Worker
|
|
17
|
+
* over the `postMessage` bridge.
|
|
18
|
+
*
|
|
19
|
+
* Use `createSyncoreWebWorkerClient()` or `createManagedWebWorkerClient()` to
|
|
20
|
+
* create instances. Prefer the React hooks (`useQuery`, `useMutation`, etc.)
|
|
21
|
+
* over calling this directly in React apps.
|
|
22
|
+
*/
|
|
7
23
|
declare class SyncoreWebWorkerClient extends SyncoreBridgeClient {
|
|
8
24
|
query: SyncoreBridgeClient["query"];
|
|
9
25
|
mutation: SyncoreBridgeClient["mutation"];
|
|
10
26
|
action: SyncoreBridgeClient["action"];
|
|
11
27
|
watchQuery: SyncoreBridgeClient["watchQuery"];
|
|
12
28
|
}
|
|
29
|
+
/** Options for attaching a runtime to a worker bridge endpoint. Alias of `AttachRuntimeBridgeOptions`. */
|
|
13
30
|
type AttachWebWorkerRuntimeOptions<TSchema extends WebWorkerSyncoreSchema = WebWorkerSyncoreSchema> = AttachRuntimeBridgeOptions<TSchema>;
|
|
31
|
+
/** Handle returned by `attachWebWorkerRuntime` for controlling the attached bridge. */
|
|
14
32
|
type AttachedWebWorkerRuntime = AttachedRuntimeBridge;
|
|
15
33
|
/**
|
|
16
|
-
* A
|
|
34
|
+
* A browser Worker and its associated Syncore client, bundled for easy
|
|
35
|
+
* lifecycle management.
|
|
36
|
+
*
|
|
37
|
+
* Returned by `createSyncoreWebWorkerClient` and `createManagedWebWorkerClient`.
|
|
38
|
+
* Call `dispose()` to terminate the worker and release its resources.
|
|
17
39
|
*/
|
|
18
40
|
interface ManagedWebWorkerClient {
|
|
41
|
+
/** The Syncore client connected to the worker. */
|
|
19
42
|
client: SyncoreWebWorkerClient;
|
|
43
|
+
/** The underlying `Worker` instance. Useful for low-level control. */
|
|
20
44
|
worker: Worker;
|
|
45
|
+
/** Terminate the worker and dispose the client. Call on app unmount or navigation. */
|
|
21
46
|
dispose(): void;
|
|
22
47
|
}
|
|
23
48
|
/**
|
|
24
|
-
* Options for creating a worker
|
|
49
|
+
* Options for creating a managed worker Syncore client via
|
|
50
|
+
* `createSyncoreWebWorkerClient`.
|
|
25
51
|
*/
|
|
26
52
|
interface CreateWebWorkerClientProviderOptions {
|
|
27
|
-
/** The worker module URL passed to `new Worker(...)`. */
|
|
53
|
+
/** The worker module URL passed to `new Worker(...)`. Typically an `import.meta.url`-relative `URL`. */
|
|
28
54
|
workerUrl: URL | string;
|
|
29
|
-
/**
|
|
55
|
+
/** Worker module type. Defaults to `"module"` (ESM worker). */
|
|
30
56
|
workerType?: WorkerOptions["type"];
|
|
31
|
-
/** Optional
|
|
57
|
+
/** Optional label shown in browser devtools’ Sources panel. */
|
|
32
58
|
workerName?: string;
|
|
33
59
|
}
|
|
34
60
|
/**
|
|
35
|
-
* Create a
|
|
61
|
+
* Create a {@link SyncoreWebWorkerClient} from a low-level message endpoint.
|
|
62
|
+
*
|
|
63
|
+
* Use this when you already have a `Worker` or `MessagePort` reference and
|
|
64
|
+
* want to wrap it manually. For the common case of spawning a new Worker from
|
|
65
|
+
* a URL, use `createSyncoreWebWorkerClient` instead.
|
|
36
66
|
*/
|
|
37
67
|
declare function createWebWorkerClient(endpoint: SyncoreWorkerMessageEndpoint): SyncoreWebWorkerClient;
|
|
38
68
|
/**
|
|
39
|
-
* Create
|
|
69
|
+
* Create a {@link ManagedWebWorkerClient} using a provided Worker factory.
|
|
70
|
+
*
|
|
71
|
+
* Useful when you need control over how the Worker is constructed (e.g. to
|
|
72
|
+
* pass constructor options not exposed by `CreateWebWorkerClientProviderOptions`).
|
|
73
|
+
* For the common URL-based case, use `createSyncoreWebWorkerClient`.
|
|
40
74
|
*/
|
|
41
75
|
declare function createManagedWebWorkerClient(options: {
|
|
42
76
|
createWorker: () => Worker;
|
|
43
77
|
}): ManagedWebWorkerClient;
|
|
44
78
|
/**
|
|
45
|
-
* Create a
|
|
79
|
+
* Create a {@link ManagedWebWorkerClient} by spawning a new Worker from a URL.
|
|
80
|
+
*
|
|
81
|
+
* This is the standard way to create a main-thread client in a browser app.
|
|
82
|
+
* Pass the URL of your `syncore.worker.ts` file (which calls
|
|
83
|
+
* `createWebWorkerRuntime`) and connect the returned `client` to your React
|
|
84
|
+
* `SyncoreProvider` or Svelte context.
|
|
85
|
+
*
|
|
86
|
+
* ```ts
|
|
87
|
+
* // main.ts (or React root)
|
|
88
|
+
* import { createSyncoreWebWorkerClient } from "syncorejs/browser";
|
|
89
|
+
*
|
|
90
|
+
* const { client, dispose } = createSyncoreWebWorkerClient({
|
|
91
|
+
* workerUrl: new URL("./syncore.worker.ts", import.meta.url),
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
46
94
|
*/
|
|
47
95
|
declare function createSyncoreWebWorkerClient(options: CreateWebWorkerClientProviderOptions): ManagedWebWorkerClient;
|
|
48
96
|
/**
|
|
49
|
-
*
|
|
97
|
+
* Wire a Syncore runtime factory to a worker message endpoint.
|
|
98
|
+
*
|
|
99
|
+
* Called internally by `createWebWorkerRuntime`. Exposed for cases where you
|
|
100
|
+
* need to attach a runtime to a custom bridge endpoint (e.g. a `MessagePort`).
|
|
50
101
|
*/
|
|
51
102
|
declare function attachWebWorkerRuntime(options: AttachWebWorkerRuntimeOptions): AttachedWebWorkerRuntime;
|
|
52
103
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","names":[],"sources":["../src/worker.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.ts","names":[],"sources":["../src/worker.ts"],"mappings":";;;;;AAgBA;;;;KAAY,sBAAA,iBACM,gBAAA,GAAmB,gBAAA,IACjC,OAAA;;KAEQ,4BAAA,GAA+B,4BAA4B;;KAE3D,gBAAA,WAA2B,gBAAgB,CAAC,MAAA;;;;;AAJ7C;AAEX;;;cAYa,sBAAA,SAA+B,mBAAA;EAClC,KAAA,EAAO,mBAAA;EACP,QAAA,EAAU,mBAAA;EACV,MAAA,EAAQ,mBAAA;EACR,UAAA,EAAY,mBAAA;AAAA;;KAIV,6BAAA,iBACM,sBAAA,GAAyB,sBAAA,IACvC,0BAAA,CAA2B,OAAA;;KAEnB,wBAAA,GAA2B,qBAAqB;AAtBE;AAU9D;;;;;;AAV8D,UA+B7C,sBAAA;EArB2B;EAuB1C,MAAA,EAAQ,sBAAA;EAvBqD;EAyB7D,MAAA,EAAQ,MAAM;EAxBN;EA0BR,OAAA;AAAA;;;;;UAOe,oCAAA;EA9BwB;EAgCvC,SAAA,EAAW,GAAA;EA5BD;EA8BV,UAAA,GAAa,aAAa;EA9Ba;EAgCvC,UAAA;AAAA;;;;;;;;iBAUc,qBAAA,CACd,QAAA,EAAU,4BAAA,GACT,sBAAsB;;;AA1Ca;AAEtC;;;;iBAmDgB,4BAAA,CAA6B,OAAA;EAC3C,YAAA,QAAoB,MAAA;AAAA,IAClB,sBAAsB;;;;;;;;;AAtCjB;AAOT;;;;;;;;iBA6DgB,4BAAA,CACd,OAAA,EAAS,oCAAA,GACR,sBAAsB;;AAzDb;AAUZ;;;;iBA+DgB,sBAAA,CACd,OAAA,EAAS,6BAAA,GACR,wBAAwB"}
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
import { SyncoreBridgeClient, attachRuntimeBridge } from "../core/index.mjs";
|
|
2
2
|
//#region src/worker.ts
|
|
3
|
+
/**
|
|
4
|
+
* Syncore client that communicates with a runtime running in a browser Worker
|
|
5
|
+
* over the `postMessage` bridge.
|
|
6
|
+
*
|
|
7
|
+
* Use `createSyncoreWebWorkerClient()` or `createManagedWebWorkerClient()` to
|
|
8
|
+
* create instances. Prefer the React hooks (`useQuery`, `useMutation`, etc.)
|
|
9
|
+
* over calling this directly in React apps.
|
|
10
|
+
*/
|
|
3
11
|
var SyncoreWebWorkerClient = class extends SyncoreBridgeClient {};
|
|
4
12
|
/**
|
|
5
|
-
* Create a
|
|
13
|
+
* Create a {@link SyncoreWebWorkerClient} from a low-level message endpoint.
|
|
14
|
+
*
|
|
15
|
+
* Use this when you already have a `Worker` or `MessagePort` reference and
|
|
16
|
+
* want to wrap it manually. For the common case of spawning a new Worker from
|
|
17
|
+
* a URL, use `createSyncoreWebWorkerClient` instead.
|
|
6
18
|
*/
|
|
7
19
|
function createWebWorkerClient(endpoint) {
|
|
8
20
|
return new SyncoreWebWorkerClient(endpoint);
|
|
9
21
|
}
|
|
10
22
|
/**
|
|
11
|
-
* Create
|
|
23
|
+
* Create a {@link ManagedWebWorkerClient} using a provided Worker factory.
|
|
24
|
+
*
|
|
25
|
+
* Useful when you need control over how the Worker is constructed (e.g. to
|
|
26
|
+
* pass constructor options not exposed by `CreateWebWorkerClientProviderOptions`).
|
|
27
|
+
* For the common URL-based case, use `createSyncoreWebWorkerClient`.
|
|
12
28
|
*/
|
|
13
29
|
function createManagedWebWorkerClient(options) {
|
|
14
30
|
const worker = options.createWorker();
|
|
@@ -23,7 +39,21 @@ function createManagedWebWorkerClient(options) {
|
|
|
23
39
|
};
|
|
24
40
|
}
|
|
25
41
|
/**
|
|
26
|
-
* Create a
|
|
42
|
+
* Create a {@link ManagedWebWorkerClient} by spawning a new Worker from a URL.
|
|
43
|
+
*
|
|
44
|
+
* This is the standard way to create a main-thread client in a browser app.
|
|
45
|
+
* Pass the URL of your `syncore.worker.ts` file (which calls
|
|
46
|
+
* `createWebWorkerRuntime`) and connect the returned `client` to your React
|
|
47
|
+
* `SyncoreProvider` or Svelte context.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* // main.ts (or React root)
|
|
51
|
+
* import { createSyncoreWebWorkerClient } from "syncorejs/browser";
|
|
52
|
+
*
|
|
53
|
+
* const { client, dispose } = createSyncoreWebWorkerClient({
|
|
54
|
+
* workerUrl: new URL("./syncore.worker.ts", import.meta.url),
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
27
57
|
*/
|
|
28
58
|
function createSyncoreWebWorkerClient(options) {
|
|
29
59
|
return createManagedWebWorkerClient({ createWorker: () => new Worker(options.workerUrl, {
|
|
@@ -32,7 +62,10 @@ function createSyncoreWebWorkerClient(options) {
|
|
|
32
62
|
}) });
|
|
33
63
|
}
|
|
34
64
|
/**
|
|
35
|
-
*
|
|
65
|
+
* Wire a Syncore runtime factory to a worker message endpoint.
|
|
66
|
+
*
|
|
67
|
+
* Called internally by `createWebWorkerRuntime`. Exposed for cases where you
|
|
68
|
+
* need to attach a runtime to a custom bridge endpoint (e.g. a `MessagePort`).
|
|
36
69
|
*/
|
|
37
70
|
function attachWebWorkerRuntime(options) {
|
|
38
71
|
return attachRuntimeBridge(options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","names":[],"sources":["../src/worker.ts"],"sourcesContent":["import {\n attachRuntimeBridge,\n type AttachRuntimeBridgeOptions,\n type AttachedRuntimeBridge,\n type BridgeQueryWatch,\n SyncoreBridgeClient,\n type SyncoreDataModel,\n type SyncoreBridgeMessageEndpoint,\n} from \"@syncore/core\";\n\nexport type WebWorkerSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\nexport type SyncoreWorkerMessageEndpoint = SyncoreBridgeMessageEndpoint;\nexport type WorkerQueryWatch<TValue> = BridgeQueryWatch<TValue>;\n\nexport class SyncoreWebWorkerClient extends SyncoreBridgeClient {\n declare query: SyncoreBridgeClient[\"query\"];\n declare mutation: SyncoreBridgeClient[\"mutation\"];\n declare action: SyncoreBridgeClient[\"action\"];\n declare watchQuery: SyncoreBridgeClient[\"watchQuery\"];\n}\n\nexport type AttachWebWorkerRuntimeOptions<\n TSchema extends WebWorkerSyncoreSchema = WebWorkerSyncoreSchema\n> = AttachRuntimeBridgeOptions<TSchema>;\nexport type AttachedWebWorkerRuntime = AttachedRuntimeBridge;\n\n/**\n * A
|
|
1
|
+
{"version":3,"file":"worker.js","names":[],"sources":["../src/worker.ts"],"sourcesContent":["import {\n attachRuntimeBridge,\n type AttachRuntimeBridgeOptions,\n type AttachedRuntimeBridge,\n type BridgeQueryWatch,\n SyncoreBridgeClient,\n type SyncoreDataModel,\n type SyncoreBridgeMessageEndpoint,\n} from \"@syncore/core\";\n\n/**\n * Schema type constraint for worker-side Syncore runtimes.\n *\n * Pass any schema produced by `defineSchema()` where this type is expected.\n * Defaults to the unconstrained `SyncoreDataModel` when omitted.\n */\nexport type WebWorkerSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\n/** Message endpoint shape required by the browser worker bridge (alias of `SyncoreBridgeMessageEndpoint`). */\nexport type SyncoreWorkerMessageEndpoint = SyncoreBridgeMessageEndpoint;\n/** Live-query subscription handle returned by `SyncoreWebWorkerClient.watchQuery`. */\nexport type WorkerQueryWatch<TValue> = BridgeQueryWatch<TValue>;\n\n/**\n * Syncore client that communicates with a runtime running in a browser Worker\n * over the `postMessage` bridge.\n *\n * Use `createSyncoreWebWorkerClient()` or `createManagedWebWorkerClient()` to\n * create instances. Prefer the React hooks (`useQuery`, `useMutation`, etc.)\n * over calling this directly in React apps.\n */\nexport class SyncoreWebWorkerClient extends SyncoreBridgeClient {\n declare query: SyncoreBridgeClient[\"query\"];\n declare mutation: SyncoreBridgeClient[\"mutation\"];\n declare action: SyncoreBridgeClient[\"action\"];\n declare watchQuery: SyncoreBridgeClient[\"watchQuery\"];\n}\n\n/** Options for attaching a runtime to a worker bridge endpoint. Alias of `AttachRuntimeBridgeOptions`. */\nexport type AttachWebWorkerRuntimeOptions<\n TSchema extends WebWorkerSyncoreSchema = WebWorkerSyncoreSchema\n> = AttachRuntimeBridgeOptions<TSchema>;\n/** Handle returned by `attachWebWorkerRuntime` for controlling the attached bridge. */\nexport type AttachedWebWorkerRuntime = AttachedRuntimeBridge;\n\n/**\n * A browser Worker and its associated Syncore client, bundled for easy\n * lifecycle management.\n *\n * Returned by `createSyncoreWebWorkerClient` and `createManagedWebWorkerClient`.\n * Call `dispose()` to terminate the worker and release its resources.\n */\nexport interface ManagedWebWorkerClient {\n /** The Syncore client connected to the worker. */\n client: SyncoreWebWorkerClient;\n /** The underlying `Worker` instance. Useful for low-level control. */\n worker: Worker;\n /** Terminate the worker and dispose the client. Call on app unmount or navigation. */\n dispose(): void;\n}\n\n/**\n * Options for creating a managed worker Syncore client via\n * `createSyncoreWebWorkerClient`.\n */\nexport interface CreateWebWorkerClientProviderOptions {\n /** The worker module URL passed to `new Worker(...)`. Typically an `import.meta.url`-relative `URL`. */\n workerUrl: URL | string;\n /** Worker module type. Defaults to `\"module\"` (ESM worker). */\n workerType?: WorkerOptions[\"type\"];\n /** Optional label shown in browser devtools’ Sources panel. */\n workerName?: string;\n}\n\n/**\n * Create a {@link SyncoreWebWorkerClient} from a low-level message endpoint.\n *\n * Use this when you already have a `Worker` or `MessagePort` reference and\n * want to wrap it manually. For the common case of spawning a new Worker from\n * a URL, use `createSyncoreWebWorkerClient` instead.\n */\nexport function createWebWorkerClient(\n endpoint: SyncoreWorkerMessageEndpoint\n): SyncoreWebWorkerClient {\n return new SyncoreWebWorkerClient(endpoint);\n}\n\n/**\n * Create a {@link ManagedWebWorkerClient} using a provided Worker factory.\n *\n * Useful when you need control over how the Worker is constructed (e.g. to\n * pass constructor options not exposed by `CreateWebWorkerClientProviderOptions`).\n * For the common URL-based case, use `createSyncoreWebWorkerClient`.\n */\nexport function createManagedWebWorkerClient(options: {\n createWorker: () => Worker;\n}): ManagedWebWorkerClient {\n const worker = options.createWorker();\n const client = createWebWorkerClient(worker);\n return {\n client,\n worker,\n dispose() {\n client.dispose();\n worker.terminate();\n }\n };\n}\n\n/**\n * Create a {@link ManagedWebWorkerClient} by spawning a new Worker from a URL.\n *\n * This is the standard way to create a main-thread client in a browser app.\n * Pass the URL of your `syncore.worker.ts` file (which calls\n * `createWebWorkerRuntime`) and connect the returned `client` to your React\n * `SyncoreProvider` or Svelte context.\n *\n * ```ts\n * // main.ts (or React root)\n * import { createSyncoreWebWorkerClient } from \"syncorejs/browser\";\n *\n * const { client, dispose } = createSyncoreWebWorkerClient({\n * workerUrl: new URL(\"./syncore.worker.ts\", import.meta.url),\n * });\n * ```\n */\nexport function createSyncoreWebWorkerClient(\n options: CreateWebWorkerClientProviderOptions\n): ManagedWebWorkerClient {\n return createManagedWebWorkerClient({\n createWorker: () =>\n new Worker(options.workerUrl, {\n type: options.workerType ?? \"module\",\n ...(options.workerName ? { name: options.workerName } : {})\n })\n });\n}\n\n/**\n * Wire a Syncore runtime factory to a worker message endpoint.\n *\n * Called internally by `createWebWorkerRuntime`. Exposed for cases where you\n * need to attach a runtime to a custom bridge endpoint (e.g. a `MessagePort`).\n */\nexport function attachWebWorkerRuntime(\n options: AttachWebWorkerRuntimeOptions\n): AttachedWebWorkerRuntime {\n return attachRuntimeBridge(options);\n}\n"],"mappings":";;;;;;;;;;AAgCA,IAAa,yBAAb,cAA4C,oBAAoB,CAKhE;;;;;;;;AA6CA,SAAgB,sBACd,UACwB;CACxB,OAAO,IAAI,uBAAuB,QAAQ;AAC5C;;;;;;;;AASA,SAAgB,6BAA6B,SAElB;CACzB,MAAM,SAAS,QAAQ,aAAa;CACpC,MAAM,SAAS,sBAAsB,MAAM;CAC3C,OAAO;EACL;EACA;EACA,UAAU;GACR,OAAO,QAAQ;GACf,OAAO,UAAU;EACnB;CACF;AACF;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,6BACd,SACwB;CACxB,OAAO,6BAA6B,EAClC,oBACE,IAAI,OAAO,QAAQ,WAAW;EAC5B,MAAM,QAAQ,cAAc;EAC5B,GAAI,QAAQ,aAAa,EAAE,MAAM,QAAQ,WAAW,IAAI,CAAC;CAC3D,CAAC,EACL,CAAC;AACH;;;;;;;AAQA,SAAgB,uBACd,SAC0B;CAC1B,OAAO,oBAAoB,OAAO;AACpC"}
|