syncorejs 0.1.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.
Files changed (227) hide show
  1. package/README.md +30 -0
  2. package/dist/_vendor/core/_virtual/_rolldown/runtime.mjs +27 -0
  3. package/dist/_vendor/core/cli.d.mts +5 -0
  4. package/dist/_vendor/core/cli.d.mts.map +1 -0
  5. package/dist/_vendor/core/cli.mjs +1196 -0
  6. package/dist/_vendor/core/cli.mjs.map +1 -0
  7. package/dist/_vendor/core/index.d.mts +7 -0
  8. package/dist/_vendor/core/index.mjs +25 -0
  9. package/dist/_vendor/core/index.mjs.map +1 -0
  10. package/dist/_vendor/core/runtime/devtools.d.mts +15 -0
  11. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -0
  12. package/dist/_vendor/core/runtime/devtools.mjs +300 -0
  13. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -0
  14. package/dist/_vendor/core/runtime/functions.d.mts +123 -0
  15. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -0
  16. package/dist/_vendor/core/runtime/functions.mjs +71 -0
  17. package/dist/_vendor/core/runtime/functions.mjs.map +1 -0
  18. package/dist/_vendor/core/runtime/id.d.mts +13 -0
  19. package/dist/_vendor/core/runtime/id.d.mts.map +1 -0
  20. package/dist/_vendor/core/runtime/id.mjs +28 -0
  21. package/dist/_vendor/core/runtime/id.mjs.map +1 -0
  22. package/dist/_vendor/core/runtime/runtime.d.mts +370 -0
  23. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -0
  24. package/dist/_vendor/core/runtime/runtime.mjs +1143 -0
  25. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -0
  26. package/dist/_vendor/devtools-protocol/index.d.ts +230 -0
  27. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -0
  28. package/dist/_vendor/devtools-protocol/index.js +0 -0
  29. package/dist/_vendor/next/config.d.ts +17 -0
  30. package/dist/_vendor/next/config.d.ts.map +1 -0
  31. package/dist/_vendor/next/config.js +73 -0
  32. package/dist/_vendor/next/config.js.map +1 -0
  33. package/dist/_vendor/next/index.d.ts +80 -0
  34. package/dist/_vendor/next/index.d.ts.map +1 -0
  35. package/dist/_vendor/next/index.js +81 -0
  36. package/dist/_vendor/next/index.js.map +1 -0
  37. package/dist/_vendor/platform-expo/index.d.ts +97 -0
  38. package/dist/_vendor/platform-expo/index.d.ts.map +1 -0
  39. package/dist/_vendor/platform-expo/index.js +197 -0
  40. package/dist/_vendor/platform-expo/index.js.map +1 -0
  41. package/dist/_vendor/platform-expo/react.d.ts +26 -0
  42. package/dist/_vendor/platform-expo/react.d.ts.map +1 -0
  43. package/dist/_vendor/platform-expo/react.js +30 -0
  44. package/dist/_vendor/platform-expo/react.js.map +1 -0
  45. package/dist/_vendor/platform-node/index.d.mts +145 -0
  46. package/dist/_vendor/platform-node/index.d.mts.map +1 -0
  47. package/dist/_vendor/platform-node/index.mjs +405 -0
  48. package/dist/_vendor/platform-node/index.mjs.map +1 -0
  49. package/dist/_vendor/platform-node/ipc-react.d.mts +25 -0
  50. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -0
  51. package/dist/_vendor/platform-node/ipc-react.mjs +21 -0
  52. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -0
  53. package/dist/_vendor/platform-node/ipc.d.mts +75 -0
  54. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -0
  55. package/dist/_vendor/platform-node/ipc.mjs +343 -0
  56. package/dist/_vendor/platform-node/ipc.mjs.map +1 -0
  57. package/dist/_vendor/platform-web/index.d.ts +123 -0
  58. package/dist/_vendor/platform-web/index.d.ts.map +1 -0
  59. package/dist/_vendor/platform-web/index.js +309 -0
  60. package/dist/_vendor/platform-web/index.js.map +1 -0
  61. package/dist/_vendor/platform-web/indexeddb.d.ts +25 -0
  62. package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -0
  63. package/dist/_vendor/platform-web/indexeddb.js +125 -0
  64. package/dist/_vendor/platform-web/indexeddb.js.map +1 -0
  65. package/dist/_vendor/platform-web/opfs.d.ts +27 -0
  66. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -0
  67. package/dist/_vendor/platform-web/opfs.js +146 -0
  68. package/dist/_vendor/platform-web/opfs.js.map +1 -0
  69. package/dist/_vendor/platform-web/persistence.d.ts +27 -0
  70. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -0
  71. package/dist/_vendor/platform-web/persistence.js +23 -0
  72. package/dist/_vendor/platform-web/persistence.js.map +1 -0
  73. package/dist/_vendor/platform-web/react.d.ts +35 -0
  74. package/dist/_vendor/platform-web/react.d.ts.map +1 -0
  75. package/dist/_vendor/platform-web/react.js +42 -0
  76. package/dist/_vendor/platform-web/react.js.map +1 -0
  77. package/dist/_vendor/platform-web/sqljs.js +133 -0
  78. package/dist/_vendor/platform-web/sqljs.js.map +1 -0
  79. package/dist/_vendor/platform-web/worker.d.ts +78 -0
  80. package/dist/_vendor/platform-web/worker.d.ts.map +1 -0
  81. package/dist/_vendor/platform-web/worker.js +307 -0
  82. package/dist/_vendor/platform-web/worker.js.map +1 -0
  83. package/dist/_vendor/react/index.d.ts +58 -0
  84. package/dist/_vendor/react/index.d.ts.map +1 -0
  85. package/dist/_vendor/react/index.js +151 -0
  86. package/dist/_vendor/react/index.js.map +1 -0
  87. package/dist/_vendor/schema/definition.d.ts +98 -0
  88. package/dist/_vendor/schema/definition.d.ts.map +1 -0
  89. package/dist/_vendor/schema/definition.js +84 -0
  90. package/dist/_vendor/schema/definition.js.map +1 -0
  91. package/dist/_vendor/schema/index.d.ts +4 -0
  92. package/dist/_vendor/schema/index.js +4 -0
  93. package/dist/_vendor/schema/planner.d.ts +42 -0
  94. package/dist/_vendor/schema/planner.d.ts.map +1 -0
  95. package/dist/_vendor/schema/planner.js +131 -0
  96. package/dist/_vendor/schema/planner.js.map +1 -0
  97. package/dist/_vendor/schema/validators.d.ts +194 -0
  98. package/dist/_vendor/schema/validators.d.ts.map +1 -0
  99. package/dist/_vendor/schema/validators.js +158 -0
  100. package/dist/_vendor/schema/validators.js.map +1 -0
  101. package/dist/_vendor/svelte/index.d.ts +43 -0
  102. package/dist/_vendor/svelte/index.d.ts.map +1 -0
  103. package/dist/_vendor/svelte/index.js +75 -0
  104. package/dist/_vendor/svelte/index.js.map +1 -0
  105. package/dist/browser-react.d.ts +2 -0
  106. package/dist/browser-react.js +2 -0
  107. package/dist/browser.d.ts +12 -0
  108. package/dist/browser.d.ts.map +1 -0
  109. package/dist/browser.js +10 -0
  110. package/dist/browser.js.map +1 -0
  111. package/dist/cli.d.ts +2 -0
  112. package/dist/cli.js +11 -0
  113. package/dist/cli.js.map +1 -0
  114. package/dist/core/src/cli.d.ts +5 -0
  115. package/dist/core/src/cli.d.ts.map +1 -0
  116. package/dist/core/src/cli.js +1196 -0
  117. package/dist/core/src/cli.js.map +1 -0
  118. package/dist/core/src/index.js +7 -0
  119. package/dist/core/src/runtime/devtools.d.ts +7 -0
  120. package/dist/core/src/runtime/devtools.d.ts.map +1 -0
  121. package/dist/core/src/runtime/devtools.js +300 -0
  122. package/dist/core/src/runtime/devtools.js.map +1 -0
  123. package/dist/core/src/runtime/functions.d.ts +123 -0
  124. package/dist/core/src/runtime/functions.d.ts.map +1 -0
  125. package/dist/core/src/runtime/functions.js +71 -0
  126. package/dist/core/src/runtime/functions.js.map +1 -0
  127. package/dist/core/src/runtime/id.d.ts +13 -0
  128. package/dist/core/src/runtime/id.d.ts.map +1 -0
  129. package/dist/core/src/runtime/id.js +28 -0
  130. package/dist/core/src/runtime/id.js.map +1 -0
  131. package/dist/core/src/runtime/runtime.d.ts +371 -0
  132. package/dist/core/src/runtime/runtime.d.ts.map +1 -0
  133. package/dist/core/src/runtime/runtime.js +1143 -0
  134. package/dist/core/src/runtime/runtime.js.map +1 -0
  135. package/dist/devtools-protocol/src/index.d.ts +201 -0
  136. package/dist/devtools-protocol/src/index.d.ts.map +1 -0
  137. package/dist/expo-react.d.ts +2 -0
  138. package/dist/expo-react.js +2 -0
  139. package/dist/expo.d.ts +2 -0
  140. package/dist/expo.js +2 -0
  141. package/dist/index.d.ts +7 -0
  142. package/dist/index.js +8 -0
  143. package/dist/next/src/config.d.ts +17 -0
  144. package/dist/next/src/config.d.ts.map +1 -0
  145. package/dist/next/src/config.js +73 -0
  146. package/dist/next/src/config.js.map +1 -0
  147. package/dist/next/src/index.d.ts +80 -0
  148. package/dist/next/src/index.d.ts.map +1 -0
  149. package/dist/next/src/index.js +82 -0
  150. package/dist/next/src/index.js.map +1 -0
  151. package/dist/next-config.d.ts +2 -0
  152. package/dist/next-config.js +2 -0
  153. package/dist/next.d.ts +3 -0
  154. package/dist/next.js +3 -0
  155. package/dist/node-ipc-react.d.ts +2 -0
  156. package/dist/node-ipc-react.js +2 -0
  157. package/dist/node-ipc.d.ts +2 -0
  158. package/dist/node-ipc.js +2 -0
  159. package/dist/node.d.ts +4 -0
  160. package/dist/node.js +3 -0
  161. package/dist/platform-expo/src/index.d.ts +96 -0
  162. package/dist/platform-expo/src/index.d.ts.map +1 -0
  163. package/dist/platform-expo/src/index.js +198 -0
  164. package/dist/platform-expo/src/index.js.map +1 -0
  165. package/dist/platform-expo/src/react.d.ts +26 -0
  166. package/dist/platform-expo/src/react.d.ts.map +1 -0
  167. package/dist/platform-expo/src/react.js +30 -0
  168. package/dist/platform-expo/src/react.js.map +1 -0
  169. package/dist/platform-node/src/index.d.ts +145 -0
  170. package/dist/platform-node/src/index.d.ts.map +1 -0
  171. package/dist/platform-node/src/index.js +407 -0
  172. package/dist/platform-node/src/index.js.map +1 -0
  173. package/dist/platform-node/src/ipc-react.d.ts +25 -0
  174. package/dist/platform-node/src/ipc-react.d.ts.map +1 -0
  175. package/dist/platform-node/src/ipc-react.js +21 -0
  176. package/dist/platform-node/src/ipc-react.js.map +1 -0
  177. package/dist/platform-node/src/ipc.d.ts +76 -0
  178. package/dist/platform-node/src/ipc.d.ts.map +1 -0
  179. package/dist/platform-node/src/ipc.js +344 -0
  180. package/dist/platform-node/src/ipc.js.map +1 -0
  181. package/dist/platform-web/src/index.d.ts +106 -0
  182. package/dist/platform-web/src/index.d.ts.map +1 -0
  183. package/dist/platform-web/src/index.js +311 -0
  184. package/dist/platform-web/src/index.js.map +1 -0
  185. package/dist/platform-web/src/indexeddb.js +125 -0
  186. package/dist/platform-web/src/indexeddb.js.map +1 -0
  187. package/dist/platform-web/src/opfs.js +146 -0
  188. package/dist/platform-web/src/opfs.js.map +1 -0
  189. package/dist/platform-web/src/persistence.d.ts +20 -0
  190. package/dist/platform-web/src/persistence.d.ts.map +1 -0
  191. package/dist/platform-web/src/persistence.js +23 -0
  192. package/dist/platform-web/src/persistence.js.map +1 -0
  193. package/dist/platform-web/src/react.d.ts +35 -0
  194. package/dist/platform-web/src/react.d.ts.map +1 -0
  195. package/dist/platform-web/src/react.js +42 -0
  196. package/dist/platform-web/src/react.js.map +1 -0
  197. package/dist/platform-web/src/sqljs.js +133 -0
  198. package/dist/platform-web/src/sqljs.js.map +1 -0
  199. package/dist/platform-web/src/worker.d.ts +79 -0
  200. package/dist/platform-web/src/worker.d.ts.map +1 -0
  201. package/dist/platform-web/src/worker.js +308 -0
  202. package/dist/platform-web/src/worker.js.map +1 -0
  203. package/dist/react/src/index.d.ts +59 -0
  204. package/dist/react/src/index.d.ts.map +1 -0
  205. package/dist/react/src/index.js +151 -0
  206. package/dist/react/src/index.js.map +1 -0
  207. package/dist/react.d.ts +2 -0
  208. package/dist/react.js +2 -0
  209. package/dist/schema/src/definition.d.ts +98 -0
  210. package/dist/schema/src/definition.d.ts.map +1 -0
  211. package/dist/schema/src/definition.js +84 -0
  212. package/dist/schema/src/definition.js.map +1 -0
  213. package/dist/schema/src/planner.d.ts +42 -0
  214. package/dist/schema/src/planner.d.ts.map +1 -0
  215. package/dist/schema/src/planner.js +131 -0
  216. package/dist/schema/src/planner.js.map +1 -0
  217. package/dist/schema/src/validators.d.ts +194 -0
  218. package/dist/schema/src/validators.d.ts.map +1 -0
  219. package/dist/schema/src/validators.js +158 -0
  220. package/dist/schema/src/validators.js.map +1 -0
  221. package/dist/svelte/src/index.d.ts +44 -0
  222. package/dist/svelte/src/index.d.ts.map +1 -0
  223. package/dist/svelte/src/index.js +75 -0
  224. package/dist/svelte/src/index.js.map +1 -0
  225. package/dist/svelte.d.ts +2 -0
  226. package/dist/svelte.js +2 -0
  227. package/package.json +152 -0
@@ -0,0 +1,311 @@
1
+ import { SyncoreRuntime } from "../../core/src/runtime/runtime.js";
2
+ import { createDevtoolsRequestHandler } from "../../core/src/runtime/devtools.js";
3
+ import "../../core/src/index.js";
4
+ import "./indexeddb.js";
5
+ import "./opfs.js";
6
+ import { createWebPersistence } from "./persistence.js";
7
+ import { SqlJsDriver } from "./sqljs.js";
8
+ import { attachWebWorkerRuntime } from "./worker.js";
9
+ //#region ../platform-web/src/index.ts
10
+ /**
11
+ * Create a full Syncore runtime directly in the browser.
12
+ *
13
+ * Most React apps should use a worker runtime instead so queries and SQLite work
14
+ * stay off the main thread.
15
+ */
16
+ async function createWebSyncoreRuntime(options) {
17
+ const persistence = options.persistence ?? await createWebPersistence({
18
+ ...options.persistenceMode ? { mode: options.persistenceMode } : {},
19
+ ...options.persistenceDatabaseName ? { indexedDbDatabaseName: options.persistenceDatabaseName } : {},
20
+ opfsRootDirectoryName: options.opfsRootDirectoryName ?? options.databaseName ?? "syncore"
21
+ });
22
+ const driver = options.driver ?? await SqlJsDriver.create({
23
+ databaseName: options.databaseName ?? "syncore",
24
+ persistence,
25
+ ...options.wasmUrl ? { wasmUrl: options.wasmUrl } : {},
26
+ ...options.locateFile ? { locateFile: options.locateFile } : {}
27
+ });
28
+ const storage = options.storage ?? new BrowserFileStorageAdapter(persistence, options.storageNamespace ?? options.databaseName ?? "syncore");
29
+ const appName = resolveWebAppName();
30
+ const origin = resolveWebOrigin();
31
+ const sessionLabel = resolveWebSessionLabel();
32
+ const autoDevtools = options.devtools === void 0 && shouldAutoConnectDevtools() ? (() => {
33
+ const sinkOptions = { url: resolveDefaultDevtoolsUrl() };
34
+ if (appName) sinkOptions.appName = appName;
35
+ if (origin) sinkOptions.origin = origin;
36
+ if (sessionLabel) sinkOptions.sessionLabel = sessionLabel;
37
+ return createBrowserWebSocketDevtoolsSink(sinkOptions);
38
+ })() : void 0;
39
+ const resolvedDevtools = options.devtools === false ? void 0 : options.devtools ?? autoDevtools;
40
+ const runtime = new SyncoreRuntime({
41
+ schema: options.schema,
42
+ functions: options.functions,
43
+ driver,
44
+ storage,
45
+ platform: options.platform ?? "browser",
46
+ ...options.capabilities ? { capabilities: options.capabilities } : {},
47
+ ...resolvedDevtools ? { devtools: resolvedDevtools } : {},
48
+ ...options.experimentalPlugins ? { experimentalPlugins: options.experimentalPlugins } : {},
49
+ ...options.scheduler ? { scheduler: options.scheduler } : {}
50
+ });
51
+ if (autoDevtools) {
52
+ autoDevtools.attachRuntime(() => runtime.getDevtoolsSnapshot());
53
+ autoDevtools.attachRequestHandler(createDevtoolsRequestHandler({
54
+ driver,
55
+ schema: options.schema,
56
+ functions: options.functions,
57
+ runtime
58
+ }));
59
+ }
60
+ return runtime;
61
+ }
62
+ /**
63
+ * Attach a Syncore runtime to a browser Worker endpoint.
64
+ */
65
+ function createWebWorkerRuntime(options) {
66
+ return attachWebWorkerRuntime({
67
+ endpoint: options.endpoint,
68
+ createRuntime: () => createWebSyncoreRuntime(options)
69
+ });
70
+ }
71
+ /**
72
+ * Attach a Syncore runtime to a browser Worker endpoint.
73
+ */
74
+ function createBrowserWorkerRuntime(options) {
75
+ return createWebWorkerRuntime(options);
76
+ }
77
+ /**
78
+ * Create a client directly from a browser Syncore runtime.
79
+ */
80
+ function createWebSyncoreClient(runtime) {
81
+ return runtime.createClient();
82
+ }
83
+ /**
84
+ * Create a full Syncore runtime directly in the browser.
85
+ */
86
+ function createBrowserSyncoreRuntime(options) {
87
+ return createWebSyncoreRuntime(options);
88
+ }
89
+ /**
90
+ * Create a client directly from a browser Syncore runtime.
91
+ */
92
+ function createBrowserSyncoreClient(runtime) {
93
+ return createWebSyncoreClient(runtime);
94
+ }
95
+ function createBrowserWebSocketDevtoolsSink(options) {
96
+ let socket;
97
+ let disposed = false;
98
+ let connectTimer;
99
+ let getSnapshot;
100
+ let onRequest;
101
+ const pendingMessages = [];
102
+ let latestHello;
103
+ const connect = () => {
104
+ if (disposed || typeof WebSocket === "undefined") return;
105
+ socket = new WebSocket(options.url);
106
+ socket.onopen = () => {
107
+ if (latestHello) sendNow({
108
+ type: "hello",
109
+ runtimeId: latestHello.runtimeId,
110
+ platform: latestHello.platform,
111
+ ...options.appName ? { appName: options.appName } : {},
112
+ ...options.origin ? { origin: options.origin } : {},
113
+ ...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
114
+ });
115
+ if (getSnapshot) sendNow({
116
+ type: "snapshot",
117
+ snapshot: withSnapshotMeta(getSnapshot(), options)
118
+ });
119
+ flushPendingMessages();
120
+ };
121
+ socket.onmessage = (event) => {
122
+ if (typeof event.data !== "string") return;
123
+ const message = JSON.parse(event.data);
124
+ if (message.type === "ping") send({ type: "pong" });
125
+ else if (message.type === "request" && onRequest) onRequest(message.payload).then((responsePayload) => {
126
+ const runtimeId = latestHello?.runtimeId ?? getSnapshot?.().runtimeId;
127
+ if (!runtimeId) return;
128
+ send({
129
+ type: "response",
130
+ requestId: message.requestId,
131
+ runtimeId,
132
+ payload: responsePayload
133
+ });
134
+ }).catch((err) => {
135
+ const runtimeId = latestHello?.runtimeId ?? getSnapshot?.().runtimeId;
136
+ if (!runtimeId) return;
137
+ send({
138
+ type: "response",
139
+ requestId: message.requestId,
140
+ runtimeId,
141
+ payload: {
142
+ kind: "error",
143
+ message: err instanceof Error ? err.message : "Unknown error"
144
+ }
145
+ });
146
+ });
147
+ };
148
+ socket.onclose = scheduleReconnect;
149
+ socket.onerror = () => {
150
+ socket?.close();
151
+ };
152
+ };
153
+ const scheduleReconnect = () => {
154
+ if (disposed || connectTimer) return;
155
+ connectTimer = setTimeout(() => {
156
+ connectTimer = void 0;
157
+ connect();
158
+ }, options.reconnectDelayMs ?? 1200);
159
+ };
160
+ const sendNow = (message) => {
161
+ if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify(message));
162
+ };
163
+ const flushPendingMessages = () => {
164
+ while (pendingMessages.length > 0) {
165
+ const nextMessage = pendingMessages.shift();
166
+ if (nextMessage) sendNow(nextMessage);
167
+ }
168
+ };
169
+ const send = (message) => {
170
+ if (socket?.readyState === WebSocket.OPEN) {
171
+ sendNow(message);
172
+ return;
173
+ }
174
+ pendingMessages.push(message);
175
+ };
176
+ connect();
177
+ return {
178
+ emit(event) {
179
+ if (event.type === "runtime.connected") {
180
+ latestHello = {
181
+ runtimeId: event.runtimeId,
182
+ platform: event.platform
183
+ };
184
+ send({
185
+ type: "hello",
186
+ runtimeId: event.runtimeId,
187
+ platform: event.platform,
188
+ ...options.appName ? { appName: options.appName } : {},
189
+ ...options.origin ? { origin: options.origin } : {},
190
+ ...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
191
+ });
192
+ }
193
+ send({
194
+ type: "event",
195
+ event
196
+ });
197
+ if (getSnapshot) send({
198
+ type: "snapshot",
199
+ snapshot: withSnapshotMeta(getSnapshot(), options)
200
+ });
201
+ },
202
+ attachRuntime(snapshotGetter) {
203
+ getSnapshot = snapshotGetter;
204
+ if (socket?.readyState === WebSocket.OPEN) send({
205
+ type: "snapshot",
206
+ snapshot: withSnapshotMeta(getSnapshot(), options)
207
+ });
208
+ },
209
+ attachRequestHandler(handler) {
210
+ onRequest = handler;
211
+ },
212
+ dispose() {
213
+ disposed = true;
214
+ if (connectTimer) clearTimeout(connectTimer);
215
+ socket?.close();
216
+ }
217
+ };
218
+ }
219
+ function withSnapshotMeta(snapshot, options) {
220
+ return {
221
+ ...snapshot,
222
+ ...options.appName ? { appName: options.appName } : {},
223
+ ...options.origin ? { origin: options.origin } : {},
224
+ ...options.sessionLabel ? { sessionLabel: options.sessionLabel } : {}
225
+ };
226
+ }
227
+ function shouldAutoConnectDevtools() {
228
+ if (typeof globalThis === "undefined") return false;
229
+ try {
230
+ return globalThis.location?.hostname === "localhost" || globalThis.location?.hostname === "127.0.0.1" ? true : Boolean(globalThis.location?.hostname?.endsWith?.(".local"));
231
+ } catch {
232
+ return false;
233
+ }
234
+ }
235
+ function resolveDefaultDevtoolsUrl() {
236
+ return "ws://127.0.0.1:4311";
237
+ }
238
+ function resolveWebOrigin() {
239
+ try {
240
+ return globalThis.location?.origin;
241
+ } catch {
242
+ return;
243
+ }
244
+ }
245
+ function resolveWebAppName() {
246
+ try {
247
+ return globalThis.location?.hostname ?? globalThis.document?.title ?? void 0;
248
+ } catch {
249
+ return;
250
+ }
251
+ }
252
+ function resolveWebSessionLabel() {
253
+ try {
254
+ if (typeof navigator === "undefined") return;
255
+ return navigator.userAgent.includes("Firefox") ? "Firefox" : navigator.userAgent.includes("Chrome") ? "Chrome" : navigator.userAgent.includes("Safari") ? "Safari" : navigator.userAgent;
256
+ } catch {
257
+ return;
258
+ }
259
+ }
260
+ /**
261
+ * Browser file/blob storage built on top of Syncore web persistence.
262
+ */
263
+ var BrowserFileStorageAdapter = class {
264
+ constructor(persistence, namespace) {
265
+ this.persistence = persistence;
266
+ this.namespace = namespace;
267
+ }
268
+ async put(id, input) {
269
+ const bytes = normalizeBinary(input.data);
270
+ await this.persistence.putFile(this.namespace, id, bytes, input.contentType ?? null);
271
+ return {
272
+ id,
273
+ path: `${this.persistence.storageProtocol}://${this.namespace}/${id}`,
274
+ size: bytes.byteLength,
275
+ contentType: input.contentType ?? null
276
+ };
277
+ }
278
+ async get(id) {
279
+ const file = await this.persistence.getFile(this.namespace, id);
280
+ if (!file) return null;
281
+ return {
282
+ id,
283
+ path: `${this.persistence.storageProtocol}://${this.namespace}/${id}`,
284
+ size: file.size,
285
+ contentType: file.contentType
286
+ };
287
+ }
288
+ async read(id) {
289
+ return (await this.persistence.getFile(this.namespace, id))?.bytes ?? null;
290
+ }
291
+ async delete(id) {
292
+ await this.persistence.deleteFile(this.namespace, id);
293
+ }
294
+ async list() {
295
+ return (await this.persistence.listFiles(this.namespace)).map((file) => ({
296
+ id: file.id,
297
+ path: `${this.persistence.storageProtocol}://${this.namespace}/${file.id}`,
298
+ size: file.size,
299
+ contentType: file.contentType
300
+ }));
301
+ }
302
+ };
303
+ function normalizeBinary(data) {
304
+ if (typeof data === "string") return new TextEncoder().encode(data);
305
+ if (data instanceof Uint8Array) return data;
306
+ return new Uint8Array(data);
307
+ }
308
+ //#endregion
309
+ export { BrowserFileStorageAdapter, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerRuntime, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerRuntime };
310
+
311
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../platform-web/src/index.ts"],"sourcesContent":["import {\n type AnySyncoreSchema,\n createDevtoolsRequestHandler,\n type DevtoolsRequestHandler,\n type DevtoolsSink,\n SyncoreRuntime,\n type SchedulerOptions,\n type SyncoreCapabilities,\n type SyncoreExperimentalPlugin,\n type SyncoreRuntimeOptions,\n type SyncoreStorageAdapter,\n type StorageObject,\n type StorageWriteInput\n} from \"@syncore/core\";\nimport {\n type SyncoreDevtoolsMessage,\n type SyncoreDevtoolsRequest,\n type SyncoreDevtoolsSnapshot\n} from \"@syncore/devtools-protocol\";\nimport {\n createWebPersistence,\n type SyncoreWebPersistence,\n type WebPersistenceMode\n} from \"./persistence.js\";\nimport { SqlJsDriver } from \"./sqljs.js\";\nimport {\n attachWebWorkerRuntime,\n type SyncoreWorkerMessageEndpoint\n} from \"./worker.js\";\nexport * from \"./worker.js\";\nexport * from \"./persistence.js\";\nexport * from \"./indexeddb.js\";\nexport * from \"./opfs.js\";\n\nexport type WebSyncoreSchema = AnySyncoreSchema;\nexport type BrowserSyncoreSchema = WebSyncoreSchema;\n\n/**\n * Options for constructing a browser Syncore runtime.\n *\n * Use this when you want to host the full runtime in a browser tab or worker.\n */\nexport interface CreateWebRuntimeOptions {\n /** The schema for the local Syncore app. */\n schema: WebSyncoreSchema;\n\n /** The generated function registry for the local Syncore app. */\n functions: SyncoreRuntimeOptions<WebSyncoreSchema>[\"functions\"];\n\n /** Optional platform capabilities exposed to function handlers. */\n capabilities?: SyncoreCapabilities;\n\n /** Optional custom SQL driver. Defaults to SQL.js with local persistence. */\n driver?: SyncoreRuntimeOptions<WebSyncoreSchema>[\"driver\"];\n\n /** Optional custom file/blob storage adapter. */\n storage?: SyncoreStorageAdapter;\n\n /** Optional experimental plugins for runtime hooks. */\n experimentalPlugins?: Array<SyncoreExperimentalPlugin<WebSyncoreSchema>>;\n\n /** Optional explicit persistence implementation. */\n persistence?: SyncoreWebPersistence;\n\n /** Which browser persistence mode to use when Syncore creates one for you. */\n persistenceMode?: WebPersistenceMode;\n\n /** Logical database name for SQL.js and local storage namespaces. */\n databaseName?: string;\n\n /** Optional IndexedDB database name for persistence metadata. */\n persistenceDatabaseName?: string;\n\n /** Optional OPFS directory name for persistent files. */\n opfsRootDirectoryName?: string;\n\n /** Optional namespace for file/blob storage. */\n storageNamespace?: string;\n\n /** Optional direct wasm URL for SQL.js. */\n wasmUrl?: string;\n\n /** Optional callback for resolving SQL.js support files. */\n locateFile?: (fileName: string) => string;\n\n /** Optional runtime platform label shown in devtools snapshots. */\n platform?: string;\n\n /** Optional devtools sink used during development. */\n devtools?: DevtoolsSink | false;\n\n /** Optional scheduler configuration for jobs and recurring work. */\n scheduler?: SchedulerOptions;\n}\n\n/**\n * Options for hosting a Syncore runtime inside a browser Worker.\n */\nexport interface CreateWebWorkerRuntimeOptions extends CreateWebRuntimeOptions {\n /** The message endpoint exposed by the current worker global. */\n endpoint: SyncoreWorkerMessageEndpoint;\n}\n\n/**\n * Options for constructing a browser Syncore runtime.\n */\nexport type CreateBrowserRuntimeOptions = CreateWebRuntimeOptions;\n\n/**\n * Options for hosting a Syncore runtime inside a browser Worker.\n */\nexport type CreateBrowserWorkerRuntimeOptions = CreateWebWorkerRuntimeOptions;\n\n/**\n * Create a full Syncore runtime directly in the browser.\n *\n * Most React apps should use a worker runtime instead so queries and SQLite work\n * stay off the main thread.\n */\nexport async function createWebSyncoreRuntime(\n options: CreateWebRuntimeOptions\n): Promise<SyncoreRuntime<WebSyncoreSchema>> {\n const persistence =\n options.persistence ??\n (await createWebPersistence({\n ...(options.persistenceMode ? { mode: options.persistenceMode } : {}),\n ...(options.persistenceDatabaseName\n ? { indexedDbDatabaseName: options.persistenceDatabaseName }\n : {}),\n opfsRootDirectoryName:\n options.opfsRootDirectoryName ?? options.databaseName ?? \"syncore\"\n }));\n const driver =\n options.driver ??\n (await SqlJsDriver.create({\n databaseName: options.databaseName ?? \"syncore\",\n persistence,\n ...(options.wasmUrl ? { wasmUrl: options.wasmUrl } : {}),\n ...(options.locateFile ? { locateFile: options.locateFile } : {})\n }));\n const storage =\n options.storage ??\n new BrowserFileStorageAdapter(\n persistence,\n options.storageNamespace ?? options.databaseName ?? \"syncore\"\n );\n const appName = resolveWebAppName();\n const origin = resolveWebOrigin();\n const sessionLabel = resolveWebSessionLabel();\n const autoDevtools =\n options.devtools === undefined && shouldAutoConnectDevtools()\n ? (() => {\n const sinkOptions: BrowserWebSocketDevtoolsSinkOptions = {\n url: resolveDefaultDevtoolsUrl()\n };\n if (appName) {\n sinkOptions.appName = appName;\n }\n if (origin) {\n sinkOptions.origin = origin;\n }\n if (sessionLabel) {\n sinkOptions.sessionLabel = sessionLabel;\n }\n return createBrowserWebSocketDevtoolsSink(sinkOptions);\n })()\n : undefined;\n const resolvedDevtools =\n options.devtools === false ? undefined : (options.devtools ?? autoDevtools);\n\n const runtime = new SyncoreRuntime({\n schema: options.schema,\n functions: options.functions,\n driver,\n storage,\n platform: options.platform ?? \"browser\",\n ...(options.capabilities ? { capabilities: options.capabilities } : {}),\n ...(resolvedDevtools ? { devtools: resolvedDevtools } : {}),\n ...(options.experimentalPlugins\n ? { experimentalPlugins: options.experimentalPlugins }\n : {}),\n ...(options.scheduler ? { scheduler: options.scheduler } : {})\n });\n\n if (autoDevtools) {\n autoDevtools.attachRuntime(() => runtime.getDevtoolsSnapshot());\n autoDevtools.attachRequestHandler(\n createDevtoolsRequestHandler({\n driver,\n schema: options.schema,\n functions: options.functions,\n runtime\n })\n );\n }\n\n return runtime;\n}\n\n/**\n * Attach a Syncore runtime to a browser Worker endpoint.\n */\nexport function createWebWorkerRuntime(options: CreateWebWorkerRuntimeOptions) {\n return attachWebWorkerRuntime({\n endpoint: options.endpoint,\n createRuntime: () => createWebSyncoreRuntime(options)\n });\n}\n\n/**\n * Attach a Syncore runtime to a browser Worker endpoint.\n */\nexport function createBrowserWorkerRuntime(\n options: CreateBrowserWorkerRuntimeOptions\n) {\n return createWebWorkerRuntime(options);\n}\n\n/**\n * Create a client directly from a browser Syncore runtime.\n */\nexport function createWebSyncoreClient(\n runtime: SyncoreRuntime<WebSyncoreSchema>\n) {\n return runtime.createClient();\n}\n\n/**\n * Create a full Syncore runtime directly in the browser.\n */\nexport function createBrowserSyncoreRuntime(\n options: CreateBrowserRuntimeOptions\n) {\n return createWebSyncoreRuntime(options);\n}\n\n/**\n * Create a client directly from a browser Syncore runtime.\n */\nexport function createBrowserSyncoreClient(\n runtime: SyncoreRuntime<BrowserSyncoreSchema>\n) {\n return createWebSyncoreClient(runtime);\n}\n\nexport interface BrowserWebSocketDevtoolsSinkOptions {\n url: string;\n reconnectDelayMs?: number;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n}\n\nexport interface BrowserWebSocketDevtoolsSink extends DevtoolsSink {\n attachRuntime(getSnapshot: () => SyncoreDevtoolsSnapshot): void;\n attachRequestHandler(handler: DevtoolsRequestHandler): void;\n dispose(): void;\n}\n\nexport function createBrowserWebSocketDevtoolsSink(\n options: BrowserWebSocketDevtoolsSinkOptions\n): BrowserWebSocketDevtoolsSink {\n let socket: WebSocket | undefined;\n let disposed = false;\n let connectTimer: ReturnType<typeof setTimeout> | undefined;\n let getSnapshot: (() => SyncoreDevtoolsSnapshot) | undefined;\n let onRequest: DevtoolsRequestHandler | undefined;\n const pendingMessages: SyncoreDevtoolsMessage[] = [];\n let latestHello:\n | {\n runtimeId: string;\n platform: string;\n }\n | undefined;\n\n const connect = () => {\n if (disposed || typeof WebSocket === \"undefined\") {\n return;\n }\n socket = new WebSocket(options.url);\n socket.onopen = () => {\n if (latestHello) {\n sendNow({\n type: \"hello\",\n runtimeId: latestHello.runtimeId,\n platform: latestHello.platform,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {})\n });\n }\n if (getSnapshot) {\n sendNow({\n type: \"snapshot\",\n snapshot: withSnapshotMeta(getSnapshot(), options)\n });\n }\n flushPendingMessages();\n };\n socket.onmessage = (event) => {\n if (typeof event.data !== \"string\") {\n return;\n }\n const message = JSON.parse(event.data) as\n | SyncoreDevtoolsMessage\n | SyncoreDevtoolsRequest;\n if (message.type === \"ping\") {\n send({ type: \"pong\" });\n } else if (message.type === \"request\" && onRequest) {\n onRequest(message.payload)\n .then((responsePayload) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSnapshot?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"response\",\n requestId: message.requestId,\n runtimeId,\n payload: responsePayload\n });\n })\n .catch((err) => {\n const runtimeId =\n latestHello?.runtimeId ?? getSnapshot?.().runtimeId;\n if (!runtimeId) {\n return;\n }\n send({\n type: \"response\",\n requestId: message.requestId,\n runtimeId,\n payload: {\n kind: \"error\",\n message: err instanceof Error ? err.message : \"Unknown error\"\n }\n });\n });\n }\n };\n socket.onclose = scheduleReconnect;\n socket.onerror = () => {\n socket?.close();\n };\n };\n\n const scheduleReconnect = () => {\n if (disposed || connectTimer) {\n return;\n }\n connectTimer = setTimeout(() => {\n connectTimer = undefined;\n connect();\n }, options.reconnectDelayMs ?? 1200);\n };\n\n const sendNow = (message: SyncoreDevtoolsMessage) => {\n if (socket?.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(message));\n }\n };\n\n const flushPendingMessages = () => {\n while (pendingMessages.length > 0) {\n const nextMessage = pendingMessages.shift();\n if (nextMessage) {\n sendNow(nextMessage);\n }\n }\n };\n\n const send = (message: SyncoreDevtoolsMessage) => {\n if (socket?.readyState === WebSocket.OPEN) {\n sendNow(message);\n return;\n }\n pendingMessages.push(message);\n };\n\n connect();\n\n return {\n emit(event) {\n if (event.type === \"runtime.connected\") {\n latestHello = {\n runtimeId: event.runtimeId,\n platform: event.platform\n };\n send({\n type: \"hello\",\n runtimeId: event.runtimeId,\n platform: event.platform,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel\n ? { sessionLabel: options.sessionLabel }\n : {})\n });\n }\n send({ type: \"event\", event });\n if (getSnapshot) {\n send({\n type: \"snapshot\",\n snapshot: withSnapshotMeta(getSnapshot(), options)\n });\n }\n },\n attachRuntime(snapshotGetter) {\n getSnapshot = snapshotGetter;\n if (socket?.readyState === WebSocket.OPEN) {\n send({\n type: \"snapshot\",\n snapshot: withSnapshotMeta(getSnapshot(), options)\n });\n }\n },\n attachRequestHandler(handler) {\n onRequest = handler;\n },\n dispose() {\n disposed = true;\n if (connectTimer) {\n clearTimeout(connectTimer);\n }\n socket?.close();\n }\n };\n}\n\nfunction withSnapshotMeta(\n snapshot: SyncoreDevtoolsSnapshot,\n options: BrowserWebSocketDevtoolsSinkOptions\n): SyncoreDevtoolsSnapshot {\n return {\n ...snapshot,\n ...(options.appName ? { appName: options.appName } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n ...(options.sessionLabel ? { sessionLabel: options.sessionLabel } : {})\n };\n}\n\nfunction shouldAutoConnectDevtools(): boolean {\n if (typeof globalThis === \"undefined\") {\n return false;\n }\n try {\n return globalThis.location?.hostname === \"localhost\" ||\n globalThis.location?.hostname === \"127.0.0.1\"\n ? true\n : Boolean(globalThis.location?.hostname?.endsWith?.(\".local\"));\n } catch {\n return false;\n }\n}\n\nfunction resolveDefaultDevtoolsUrl(): string {\n return \"ws://127.0.0.1:4311\";\n}\n\nfunction resolveWebOrigin(): string | undefined {\n try {\n return globalThis.location?.origin;\n } catch {\n return undefined;\n }\n}\n\nfunction resolveWebAppName(): string | undefined {\n try {\n return (\n globalThis.location?.hostname ?? globalThis.document?.title ?? undefined\n );\n } catch {\n return undefined;\n }\n}\n\nfunction resolveWebSessionLabel(): string | undefined {\n try {\n if (typeof navigator === \"undefined\") {\n return undefined;\n }\n return navigator.userAgent.includes(\"Firefox\")\n ? \"Firefox\"\n : navigator.userAgent.includes(\"Chrome\")\n ? \"Chrome\"\n : navigator.userAgent.includes(\"Safari\")\n ? \"Safari\"\n : navigator.userAgent;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Browser file/blob storage built on top of Syncore web persistence.\n */\nexport class BrowserFileStorageAdapter implements SyncoreStorageAdapter {\n constructor(\n private readonly persistence: SyncoreWebPersistence,\n private readonly namespace: string\n ) {}\n\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\n const bytes = normalizeBinary(input.data);\n await this.persistence.putFile(\n this.namespace,\n id,\n bytes,\n input.contentType ?? null\n );\n return {\n id,\n path: `${this.persistence.storageProtocol}://${this.namespace}/${id}`,\n size: bytes.byteLength,\n contentType: input.contentType ?? null\n };\n }\n\n async get(id: string): Promise<StorageObject | null> {\n const file = await this.persistence.getFile(this.namespace, id);\n if (!file) {\n return null;\n }\n return {\n id,\n path: `${this.persistence.storageProtocol}://${this.namespace}/${id}`,\n size: file.size,\n contentType: file.contentType\n };\n }\n\n async read(id: string): Promise<Uint8Array | null> {\n const file = await this.persistence.getFile(this.namespace, id);\n return file?.bytes ?? null;\n }\n\n async delete(id: string): Promise<void> {\n await this.persistence.deleteFile(this.namespace, id);\n }\n\n async list(): Promise<StorageObject[]> {\n const files = await this.persistence.listFiles(this.namespace);\n return files.map((file) => ({\n id: file.id,\n path: `${this.persistence.storageProtocol}://${this.namespace}/${file.id}`,\n size: file.size,\n contentType: file.contentType\n }));\n }\n}\n\nfunction normalizeBinary(data: StorageWriteInput[\"data\"]): Uint8Array {\n if (typeof data === \"string\") {\n return new TextEncoder().encode(data);\n }\n if (data instanceof Uint8Array) {\n return data;\n }\n return new Uint8Array(data);\n}\n"],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ //#region ../platform-web/src/indexeddb.ts
2
+ var SyncoreIndexedDbPersistence = class {
3
+ storageProtocol = "idb";
4
+ databaseName;
5
+ constructor(options) {
6
+ this.databaseName = options?.databaseName ?? "syncore-web";
7
+ }
8
+ async loadDatabase(key) {
9
+ const record = await this.getRecord("databases", key);
10
+ if (!record) return null;
11
+ return new Uint8Array(record.bytes);
12
+ }
13
+ async saveDatabase(key, bytes) {
14
+ await this.putRecord("databases", {
15
+ key,
16
+ bytes: sliceToArrayBuffer(bytes),
17
+ updatedAt: Date.now()
18
+ });
19
+ }
20
+ async getFile(namespace, id) {
21
+ const record = await this.getRecord("files", createNamespacedKey(namespace, id));
22
+ if (!record) return null;
23
+ return {
24
+ id,
25
+ bytes: new Uint8Array(record.bytes),
26
+ contentType: record.contentType,
27
+ size: record.size
28
+ };
29
+ }
30
+ async putFile(namespace, id, bytes, contentType) {
31
+ await this.putRecord("files", {
32
+ key: createNamespacedKey(namespace, id),
33
+ bytes: sliceToArrayBuffer(bytes),
34
+ contentType,
35
+ size: bytes.byteLength,
36
+ updatedAt: Date.now()
37
+ });
38
+ }
39
+ async deleteFile(namespace, id) {
40
+ await this.deleteRecord("files", createNamespacedKey(namespace, id));
41
+ }
42
+ async listFiles(namespace) {
43
+ const prefix = `${namespace}:`;
44
+ return (await this.listRecords("files")).filter((record) => record.key.startsWith(prefix)).map((record) => ({
45
+ id: record.key.slice(prefix.length),
46
+ bytes: new Uint8Array(record.bytes),
47
+ contentType: record.contentType,
48
+ size: record.size
49
+ }));
50
+ }
51
+ async getDatabase() {
52
+ const indexedDb = globalThis.indexedDB;
53
+ if (!indexedDb) throw new Error("IndexedDB is not available in this environment.");
54
+ return new Promise((resolve, reject) => {
55
+ const request = indexedDb.open(this.databaseName, 1);
56
+ request.onupgradeneeded = () => {
57
+ const database = request.result;
58
+ if (!database.objectStoreNames.contains("databases")) database.createObjectStore("databases", { keyPath: "key" });
59
+ if (!database.objectStoreNames.contains("files")) database.createObjectStore("files", { keyPath: "key" });
60
+ };
61
+ request.onsuccess = () => resolve(request.result);
62
+ request.onerror = () => reject(request.error ?? /* @__PURE__ */ new Error("Failed to open IndexedDB."));
63
+ });
64
+ }
65
+ async getRecord(storeName, key) {
66
+ const database = await this.getDatabase();
67
+ try {
68
+ return await new Promise((resolve, reject) => {
69
+ const request = database.transaction(storeName, "readonly").objectStore(storeName).get(key);
70
+ request.onsuccess = () => resolve(request.result ?? null);
71
+ request.onerror = () => reject(request.error ?? /* @__PURE__ */ new Error(`Failed to read ${storeName}/${key}.`));
72
+ });
73
+ } finally {
74
+ database.close();
75
+ }
76
+ }
77
+ async putRecord(storeName, record) {
78
+ const database = await this.getDatabase();
79
+ try {
80
+ await new Promise((resolve, reject) => {
81
+ const transaction = database.transaction(storeName, "readwrite");
82
+ transaction.oncomplete = () => resolve();
83
+ transaction.onerror = () => reject(transaction.error ?? /* @__PURE__ */ new Error(`Failed to write ${storeName}/${record.key}.`));
84
+ transaction.objectStore(storeName).put(record);
85
+ });
86
+ } finally {
87
+ database.close();
88
+ }
89
+ }
90
+ async deleteRecord(storeName, key) {
91
+ const database = await this.getDatabase();
92
+ try {
93
+ await new Promise((resolve, reject) => {
94
+ const transaction = database.transaction(storeName, "readwrite");
95
+ transaction.oncomplete = () => resolve();
96
+ transaction.onerror = () => reject(transaction.error ?? /* @__PURE__ */ new Error(`Failed to delete ${storeName}/${key}.`));
97
+ transaction.objectStore(storeName).delete(key);
98
+ });
99
+ } finally {
100
+ database.close();
101
+ }
102
+ }
103
+ async listRecords(storeName) {
104
+ const database = await this.getDatabase();
105
+ try {
106
+ return await new Promise((resolve, reject) => {
107
+ const request = database.transaction(storeName, "readonly").objectStore(storeName).getAll();
108
+ request.onsuccess = () => resolve(request.result ?? []);
109
+ request.onerror = () => reject(request.error ?? /* @__PURE__ */ new Error(`Failed to list records from ${storeName}.`));
110
+ });
111
+ } finally {
112
+ database.close();
113
+ }
114
+ }
115
+ };
116
+ function createNamespacedKey(namespace, id) {
117
+ return `${namespace}:${id}`;
118
+ }
119
+ function sliceToArrayBuffer(bytes) {
120
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
121
+ }
122
+ //#endregion
123
+ export { SyncoreIndexedDbPersistence };
124
+
125
+ //# sourceMappingURL=indexeddb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexeddb.js","names":[],"sources":["../../../../platform-web/src/indexeddb.ts"],"sourcesContent":["import type { SyncoreWebPersistence, StoredWebFile } from \"./persistence.js\";\r\n\r\nexport interface IndexedDbPersistenceOptions {\r\n databaseName?: string;\r\n}\r\n\r\ntype StoredDatabaseRecord = {\r\n key: string;\r\n bytes: ArrayBuffer;\r\n updatedAt: number;\r\n};\r\n\r\ntype StoredFileRecord = {\r\n key: string;\r\n bytes: ArrayBuffer;\r\n contentType: string | null;\r\n size: number;\r\n updatedAt: number;\r\n};\r\n\r\nexport class SyncoreIndexedDbPersistence implements SyncoreWebPersistence {\r\n readonly storageProtocol = \"idb\" as const;\r\n private readonly databaseName: string;\r\n\r\n constructor(options?: IndexedDbPersistenceOptions) {\r\n this.databaseName = options?.databaseName ?? \"syncore-web\";\r\n }\r\n\r\n async loadDatabase(key: string): Promise<Uint8Array | null> {\r\n const record = await this.getRecord<StoredDatabaseRecord>(\"databases\", key);\r\n if (!record) {\r\n return null;\r\n }\r\n return new Uint8Array(record.bytes);\r\n }\r\n\r\n async saveDatabase(key: string, bytes: Uint8Array): Promise<void> {\r\n await this.putRecord<StoredDatabaseRecord>(\"databases\", {\r\n key,\r\n bytes: sliceToArrayBuffer(bytes),\r\n updatedAt: Date.now()\r\n });\r\n }\r\n\r\n async getFile(\r\n namespace: string,\r\n id: string\r\n ): Promise<StoredWebFile | null> {\r\n const record = await this.getRecord<StoredFileRecord>(\r\n \"files\",\r\n createNamespacedKey(namespace, id)\r\n );\r\n if (!record) {\r\n return null;\r\n }\r\n return {\r\n id,\r\n bytes: new Uint8Array(record.bytes),\r\n contentType: record.contentType,\r\n size: record.size\r\n };\r\n }\r\n\r\n async putFile(\r\n namespace: string,\r\n id: string,\r\n bytes: Uint8Array,\r\n contentType: string | null\r\n ): Promise<void> {\r\n await this.putRecord<StoredFileRecord>(\"files\", {\r\n key: createNamespacedKey(namespace, id),\r\n bytes: sliceToArrayBuffer(bytes),\r\n contentType,\r\n size: bytes.byteLength,\r\n updatedAt: Date.now()\r\n });\r\n }\r\n\r\n async deleteFile(namespace: string, id: string): Promise<void> {\r\n await this.deleteRecord(\"files\", createNamespacedKey(namespace, id));\r\n }\r\n\r\n async listFiles(namespace: string): Promise<StoredWebFile[]> {\r\n const prefix = `${namespace}:`;\r\n const records = await this.listRecords<StoredFileRecord>(\"files\");\r\n return records\r\n .filter((record) => record.key.startsWith(prefix))\r\n .map((record) => ({\r\n id: record.key.slice(prefix.length),\r\n bytes: new Uint8Array(record.bytes),\r\n contentType: record.contentType,\r\n size: record.size\r\n }));\r\n }\r\n\r\n private async getDatabase(): Promise<IDBDatabase> {\r\n const indexedDb = globalThis.indexedDB;\r\n if (!indexedDb) {\r\n throw new Error(\"IndexedDB is not available in this environment.\");\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDb.open(this.databaseName, 1);\r\n request.onupgradeneeded = () => {\r\n const database = request.result;\r\n if (!database.objectStoreNames.contains(\"databases\")) {\r\n database.createObjectStore(\"databases\", { keyPath: \"key\" });\r\n }\r\n if (!database.objectStoreNames.contains(\"files\")) {\r\n database.createObjectStore(\"files\", { keyPath: \"key\" });\r\n }\r\n };\r\n request.onsuccess = () => resolve(request.result);\r\n request.onerror = () =>\r\n reject(request.error ?? new Error(\"Failed to open IndexedDB.\"));\r\n });\r\n }\r\n\r\n private async getRecord<TRecord>(\r\n storeName: \"databases\" | \"files\",\r\n key: string\r\n ): Promise<TRecord | null> {\r\n const database = await this.getDatabase();\r\n try {\r\n return await new Promise<TRecord | null>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readonly\");\r\n const request = transaction.objectStore(storeName).get(key);\r\n request.onsuccess = () => resolve((request.result as TRecord | undefined) ?? null);\r\n request.onerror = () =>\r\n reject(request.error ?? new Error(`Failed to read ${storeName}/${key}.`));\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n\r\n private async putRecord<TRecord extends { key: string }>(\r\n storeName: \"databases\" | \"files\",\r\n record: TRecord\r\n ): Promise<void> {\r\n const database = await this.getDatabase();\r\n try {\r\n await new Promise<void>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readwrite\");\r\n transaction.oncomplete = () => resolve();\r\n transaction.onerror = () =>\r\n reject(transaction.error ?? new Error(`Failed to write ${storeName}/${record.key}.`));\r\n transaction.objectStore(storeName).put(record);\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n\r\n private async deleteRecord(\r\n storeName: \"databases\" | \"files\",\r\n key: string\r\n ): Promise<void> {\r\n const database = await this.getDatabase();\r\n try {\r\n await new Promise<void>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readwrite\");\r\n transaction.oncomplete = () => resolve();\r\n transaction.onerror = () =>\r\n reject(transaction.error ?? new Error(`Failed to delete ${storeName}/${key}.`));\r\n transaction.objectStore(storeName).delete(key);\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n\r\n private async listRecords<TRecord>(\r\n storeName: \"databases\" | \"files\"\r\n ): Promise<TRecord[]> {\r\n const database = await this.getDatabase();\r\n try {\r\n return await new Promise<TRecord[]>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readonly\");\r\n const request = transaction.objectStore(storeName).getAll();\r\n request.onsuccess = () => resolve((request.result as TRecord[] | undefined) ?? []);\r\n request.onerror = () =>\r\n reject(request.error ?? new Error(`Failed to list records from ${storeName}.`));\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n}\r\n\r\nfunction createNamespacedKey(namespace: string, id: string): string {\r\n return `${namespace}:${id}`;\r\n}\r\n\r\nfunction sliceToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\r\n return bytes.buffer.slice(\r\n bytes.byteOffset,\r\n bytes.byteOffset + bytes.byteLength\r\n ) as ArrayBuffer;\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ //#region ../platform-web/src/opfs.ts
2
+ var SyncoreOpfsPersistence = class {
3
+ storageProtocol = "opfs";
4
+ rootDirectoryPromise;
5
+ constructor(options = {}) {
6
+ this.options = options;
7
+ }
8
+ async loadDatabase(key) {
9
+ const handle = await this.getOptionalFileHandle(["databases"], `${encodePathComponent(key)}.sqlite`);
10
+ if (!handle) return null;
11
+ return readFileBytes(handle);
12
+ }
13
+ async saveDatabase(key, bytes) {
14
+ await writeBytes(await (await this.ensureDirectory(["databases"])).getFileHandle(`${encodePathComponent(key)}.sqlite`, { create: true }), bytes);
15
+ }
16
+ async getFile(namespace, id) {
17
+ const directory = await this.getOptionalDirectory(["files", encodePathComponent(namespace)]);
18
+ if (!directory) return null;
19
+ const fileName = `${encodePathComponent(id)}.bin`;
20
+ const metadataName = `${encodePathComponent(id)}.meta.json`;
21
+ const fileHandle = await this.getOptionalFileHandleFromDirectory(directory, fileName);
22
+ if (!fileHandle) return null;
23
+ const [bytes, metadata] = await Promise.all([readFileBytes(fileHandle), this.readMetadata(directory, metadataName)]);
24
+ return {
25
+ id,
26
+ bytes,
27
+ size: bytes.byteLength,
28
+ contentType: metadata?.contentType ?? null
29
+ };
30
+ }
31
+ async putFile(namespace, id, bytes, contentType) {
32
+ const directory = await this.ensureDirectory(["files", encodePathComponent(namespace)]);
33
+ const encodedId = encodePathComponent(id);
34
+ await writeBytes(await directory.getFileHandle(`${encodedId}.bin`, { create: true }), bytes);
35
+ await writeText(await directory.getFileHandle(`${encodedId}.meta.json`, { create: true }), JSON.stringify({ contentType }));
36
+ }
37
+ async deleteFile(namespace, id) {
38
+ const directory = await this.getOptionalDirectory(["files", encodePathComponent(namespace)]);
39
+ if (!directory) return;
40
+ const encodedId = encodePathComponent(id);
41
+ await removeEntryIfExists(directory, `${encodedId}.bin`);
42
+ await removeEntryIfExists(directory, `${encodedId}.meta.json`);
43
+ }
44
+ async listFiles(namespace) {
45
+ const directory = await this.getOptionalDirectory(["files", encodePathComponent(namespace)]);
46
+ if (!directory) return [];
47
+ const files = [];
48
+ const iterableDirectory = directory;
49
+ for await (const [name, handle] of iterableDirectory.entries()) {
50
+ if (handle.kind !== "file" || !name.endsWith(".bin")) continue;
51
+ const encodedId = name.slice(0, -4);
52
+ const id = decodeURIComponent(encodedId);
53
+ const bytes = await readFileBytes(handle);
54
+ const metadata = await this.readMetadata(directory, `${encodedId}.meta.json`);
55
+ files.push({
56
+ id,
57
+ bytes,
58
+ size: bytes.byteLength,
59
+ contentType: metadata?.contentType ?? null
60
+ });
61
+ }
62
+ return files;
63
+ }
64
+ async ensureDirectory(pathSegments) {
65
+ let directory = await this.getRootDirectory();
66
+ for (const segment of pathSegments) directory = await directory.getDirectoryHandle(segment, { create: true });
67
+ return directory;
68
+ }
69
+ async getOptionalDirectory(pathSegments) {
70
+ try {
71
+ let directory = await this.getRootDirectory();
72
+ for (const segment of pathSegments) directory = await directory.getDirectoryHandle(segment);
73
+ return directory;
74
+ } catch (error) {
75
+ if (isNotFoundError(error)) return null;
76
+ throw error;
77
+ }
78
+ }
79
+ async getOptionalFileHandle(pathSegments, fileName) {
80
+ const directory = await this.getOptionalDirectory(pathSegments);
81
+ if (!directory) return null;
82
+ return this.getOptionalFileHandleFromDirectory(directory, fileName);
83
+ }
84
+ async getOptionalFileHandleFromDirectory(directory, fileName) {
85
+ try {
86
+ return await directory.getFileHandle(fileName);
87
+ } catch (error) {
88
+ if (isNotFoundError(error)) return null;
89
+ throw error;
90
+ }
91
+ }
92
+ async readMetadata(directory, fileName) {
93
+ const handle = await this.getOptionalFileHandleFromDirectory(directory, fileName);
94
+ if (!handle) return null;
95
+ const bytes = await readFileBytes(handle);
96
+ return JSON.parse(new TextDecoder().decode(bytes));
97
+ }
98
+ async getRootDirectory() {
99
+ if (!this.rootDirectoryPromise) this.rootDirectoryPromise = (async () => {
100
+ const storageManager = getOpfsStorageManager();
101
+ if (!storageManager?.getDirectory) throw new Error("OPFS is not available in this environment.");
102
+ return (await storageManager.getDirectory()).getDirectoryHandle(this.options.rootDirectoryName ?? "syncore", { create: true });
103
+ })();
104
+ return this.rootDirectoryPromise;
105
+ }
106
+ };
107
+ async function readFileBytes(handle) {
108
+ const file = await handle.getFile();
109
+ return new Uint8Array(await file.arrayBuffer());
110
+ }
111
+ async function writeBytes(handle, bytes) {
112
+ const writable = await handle.createWritable();
113
+ try {
114
+ await writable.write(sliceToArrayBuffer(bytes));
115
+ await writable.truncate(bytes.byteLength);
116
+ } finally {
117
+ await writable.close();
118
+ }
119
+ }
120
+ async function writeText(handle, value) {
121
+ await writeBytes(handle, new TextEncoder().encode(value));
122
+ }
123
+ async function removeEntryIfExists(directory, name) {
124
+ try {
125
+ await directory.removeEntry(name);
126
+ } catch (error) {
127
+ if (!isNotFoundError(error)) throw error;
128
+ }
129
+ }
130
+ function encodePathComponent(value) {
131
+ return encodeURIComponent(value);
132
+ }
133
+ function sliceToArrayBuffer(bytes) {
134
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
135
+ }
136
+ function getOpfsStorageManager() {
137
+ if (typeof navigator === "undefined") return;
138
+ return navigator.storage;
139
+ }
140
+ function isNotFoundError(error) {
141
+ return typeof error === "object" && error !== null && "name" in error && error.name === "NotFoundError";
142
+ }
143
+ //#endregion
144
+ export { SyncoreOpfsPersistence };
145
+
146
+ //# sourceMappingURL=opfs.js.map