@evolu/common 7.4.0 → 8.0.0-next.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 (251) hide show
  1. package/README.md +4 -61
  2. package/dist/src/Array.d.ts +343 -102
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +181 -85
  5. package/dist/src/Assert.d.ts +57 -11
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +47 -11
  8. package/dist/src/BigInt.d.ts +6 -1
  9. package/dist/src/BigInt.d.ts.map +1 -1
  10. package/dist/src/BigInt.js +5 -0
  11. package/dist/src/Brand.d.ts +40 -12
  12. package/dist/src/Brand.d.ts.map +1 -1
  13. package/dist/src/Brand.js +5 -0
  14. package/dist/src/Buffer.d.ts +6 -1
  15. package/dist/src/Buffer.d.ts.map +1 -1
  16. package/dist/src/Buffer.js +7 -2
  17. package/dist/src/Cache.d.ts +10 -2
  18. package/dist/src/Cache.d.ts.map +1 -1
  19. package/dist/src/Cache.js +6 -0
  20. package/dist/src/Callbacks.d.ts +10 -7
  21. package/dist/src/Callbacks.d.ts.map +1 -1
  22. package/dist/src/Callbacks.js +23 -13
  23. package/dist/src/Console.d.ts +361 -69
  24. package/dist/src/Console.d.ts.map +1 -1
  25. package/dist/src/Console.js +217 -117
  26. package/dist/src/Crypto.d.ts +70 -21
  27. package/dist/src/Crypto.d.ts.map +1 -1
  28. package/dist/src/Crypto.js +72 -27
  29. package/dist/src/Eq.d.ts +22 -2
  30. package/dist/src/Eq.d.ts.map +1 -1
  31. package/dist/src/Eq.js +21 -1
  32. package/dist/src/Error.d.ts +23 -12
  33. package/dist/src/Error.d.ts.map +1 -1
  34. package/dist/src/Error.js +27 -11
  35. package/dist/src/Function.d.ts +128 -56
  36. package/dist/src/Function.d.ts.map +1 -1
  37. package/dist/src/Function.js +118 -14
  38. package/dist/src/Identicon.d.ts +5 -0
  39. package/dist/src/Identicon.d.ts.map +1 -1
  40. package/dist/src/Identicon.js +6 -1
  41. package/dist/src/Lookup.d.ts +160 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +192 -0
  44. package/dist/src/Microtask.d.ts +21 -0
  45. package/dist/src/Microtask.d.ts.map +1 -0
  46. package/dist/src/Microtask.js +37 -0
  47. package/dist/src/Number.d.ts +18 -4
  48. package/dist/src/Number.d.ts.map +1 -1
  49. package/dist/src/Number.js +23 -1
  50. package/dist/src/Object.d.ts +110 -5
  51. package/dist/src/Object.d.ts.map +1 -1
  52. package/dist/src/Object.js +108 -5
  53. package/dist/src/Option.d.ts +58 -0
  54. package/dist/src/Option.d.ts.map +1 -0
  55. package/dist/src/Option.js +43 -0
  56. package/dist/src/Order.d.ts +5 -0
  57. package/dist/src/Order.d.ts.map +1 -1
  58. package/dist/src/Order.js +8 -3
  59. package/dist/src/Platform.d.ts +36 -2
  60. package/dist/src/Platform.d.ts.map +1 -1
  61. package/dist/src/Platform.js +10 -6
  62. package/dist/src/Polyfills.d.ts +27 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +299 -0
  65. package/dist/src/Random.d.ts +18 -7
  66. package/dist/src/Random.d.ts.map +1 -1
  67. package/dist/src/Random.js +9 -7
  68. package/dist/src/Redacted.d.ts +7 -4
  69. package/dist/src/Redacted.d.ts.map +1 -1
  70. package/dist/src/Redacted.js +5 -0
  71. package/dist/src/Ref.d.ts +31 -16
  72. package/dist/src/Ref.d.ts.map +1 -1
  73. package/dist/src/Ref.js +35 -7
  74. package/dist/src/RefCount.d.ts +62 -0
  75. package/dist/src/RefCount.d.ts.map +1 -0
  76. package/dist/src/RefCount.js +83 -0
  77. package/dist/src/Relation.d.ts +84 -29
  78. package/dist/src/Relation.d.ts.map +1 -1
  79. package/dist/src/Relation.js +83 -105
  80. package/dist/src/Resource.d.ts +263 -0
  81. package/dist/src/Resource.d.ts.map +1 -0
  82. package/dist/src/Resource.js +389 -0
  83. package/dist/src/Result.d.ts +390 -374
  84. package/dist/src/Result.d.ts.map +1 -1
  85. package/dist/src/Result.js +142 -70
  86. package/dist/src/Schedule.d.ts +953 -0
  87. package/dist/src/Schedule.d.ts.map +1 -0
  88. package/dist/src/Schedule.js +1199 -0
  89. package/dist/src/Set.d.ts +181 -0
  90. package/dist/src/Set.d.ts.map +1 -0
  91. package/dist/src/Set.js +137 -0
  92. package/dist/src/Skiplist.d.ts +6 -1
  93. package/dist/src/Skiplist.d.ts.map +1 -1
  94. package/dist/src/Skiplist.js +5 -0
  95. package/dist/src/Sqlite.d.ts +160 -60
  96. package/dist/src/Sqlite.d.ts.map +1 -1
  97. package/dist/src/Sqlite.js +290 -190
  98. package/dist/src/Store.d.ts +36 -28
  99. package/dist/src/Store.d.ts.map +1 -1
  100. package/dist/src/Store.js +55 -15
  101. package/dist/src/String.d.ts +5 -0
  102. package/dist/src/String.d.ts.map +1 -1
  103. package/dist/src/String.js +5 -0
  104. package/dist/src/Task.d.ts +2263 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1488 -348
  107. package/dist/src/Test.d.ts +122 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +66 -0
  110. package/dist/src/Time.d.ts +201 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +190 -86
  113. package/dist/src/Tracer.d.ts +48 -0
  114. package/dist/src/Tracer.d.ts.map +1 -0
  115. package/dist/src/Tracer.js +6 -0
  116. package/dist/src/Type.d.ts +582 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +362 -240
  119. package/dist/src/Types.d.ts +160 -6
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/Types.js +22 -2
  122. package/dist/src/WebSocket.d.ts +113 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +210 -118
  125. package/dist/src/Worker.d.ts +245 -60
  126. package/dist/src/Worker.d.ts.map +1 -1
  127. package/dist/src/Worker.js +220 -64
  128. package/dist/src/index.d.ts +25 -3
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +19 -3
  131. package/dist/src/local-first/Db.d.ts +24 -213
  132. package/dist/src/local-first/Db.d.ts.map +1 -1
  133. package/dist/src/local-first/Db.js +622 -293
  134. package/dist/src/local-first/Error.d.ts +12 -0
  135. package/dist/src/local-first/Error.d.ts.map +1 -0
  136. package/dist/src/local-first/Error.js +6 -0
  137. package/dist/src/local-first/Evolu.d.ts +330 -285
  138. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  139. package/dist/src/local-first/Evolu.js +472 -402
  140. package/dist/src/local-first/LocalAuth.d.ts +10 -10
  141. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  142. package/dist/src/local-first/LocalAuth.js +6 -1
  143. package/dist/src/local-first/Owner.d.ts +70 -27
  144. package/dist/src/local-first/Owner.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.js +27 -14
  146. package/dist/src/local-first/Protocol.d.ts +58 -50
  147. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  148. package/dist/src/local-first/Protocol.js +253 -155
  149. package/dist/src/local-first/Query.d.ts +133 -43
  150. package/dist/src/local-first/Query.d.ts.map +1 -1
  151. package/dist/src/local-first/Query.js +139 -90
  152. package/dist/src/local-first/Relay.d.ts +18 -38
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -171
  155. package/dist/src/local-first/Schema.d.ts +139 -144
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +78 -163
  158. package/dist/src/local-first/Shared.d.ts +125 -0
  159. package/dist/src/local-first/Shared.d.ts.map +1 -0
  160. package/dist/src/local-first/Shared.js +569 -0
  161. package/dist/src/local-first/Storage.d.ts +50 -53
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +121 -173
  164. package/dist/src/local-first/Timestamp.d.ts +28 -35
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +24 -25
  167. package/dist/src/local-first/index.d.ts +2 -15
  168. package/dist/src/local-first/index.d.ts.map +1 -1
  169. package/dist/src/local-first/index.js +2 -15
  170. package/package.json +36 -26
  171. package/src/Array.ts +467 -106
  172. package/src/Assert.ts +87 -12
  173. package/src/BigInt.ts +7 -1
  174. package/src/Brand.ts +41 -12
  175. package/src/Buffer.ts +10 -4
  176. package/src/Cache.ts +11 -2
  177. package/src/Callbacks.ts +33 -23
  178. package/src/Console.ts +567 -178
  179. package/src/Crypto.ts +111 -62
  180. package/src/Eq.ts +30 -3
  181. package/src/Error.ts +50 -34
  182. package/src/Function.ts +140 -68
  183. package/src/Identicon.ts +7 -1
  184. package/src/Lookup.ts +415 -0
  185. package/src/Microtask.ts +58 -0
  186. package/src/Number.ts +40 -7
  187. package/src/Object.ts +155 -16
  188. package/src/Option.ts +74 -0
  189. package/src/Order.ts +11 -3
  190. package/src/Platform.ts +43 -7
  191. package/src/Polyfills.ts +465 -0
  192. package/src/Random.ts +25 -11
  193. package/src/Redacted.ts +8 -4
  194. package/src/Ref.ts +72 -21
  195. package/src/RefCount.ts +170 -0
  196. package/src/Relation.ts +188 -136
  197. package/src/Resource.ts +864 -0
  198. package/src/Result.ts +548 -381
  199. package/src/Schedule.ts +1471 -0
  200. package/src/Set.ts +247 -0
  201. package/src/Skiplist.ts +7 -1
  202. package/src/Sqlite.ts +409 -304
  203. package/src/Store.ts +94 -48
  204. package/src/String.ts +6 -0
  205. package/src/Task.ts +3685 -740
  206. package/src/Test.ts +162 -0
  207. package/src/Time.ts +348 -145
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +1064 -630
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +311 -178
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +871 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -823
  217. package/src/local-first/LocalAuth.ts +10 -10
  218. package/src/local-first/Owner.ts +94 -42
  219. package/src/local-first/Protocol.ts +440 -303
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +91 -252
  222. package/src/local-first/Schema.ts +284 -394
  223. package/src/local-first/Shared.ts +932 -0
  224. package/src/local-first/Storage.ts +209 -282
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/LICENSE +0 -21
  228. package/dist/src/Instances.d.ts +0 -34
  229. package/dist/src/Instances.d.ts.map +0 -1
  230. package/dist/src/Instances.js +0 -44
  231. package/dist/src/Resources.d.ts +0 -118
  232. package/dist/src/Resources.d.ts.map +0 -1
  233. package/dist/src/Resources.js +0 -197
  234. package/dist/src/local-first/Platform.d.ts +0 -25
  235. package/dist/src/local-first/Platform.d.ts.map +0 -1
  236. package/dist/src/local-first/Platform.js +0 -1
  237. package/dist/src/local-first/Public.d.ts +0 -22
  238. package/dist/src/local-first/Public.d.ts.map +0 -1
  239. package/dist/src/local-first/Public.js +0 -15
  240. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  241. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  242. package/dist/src/local-first/PublicKysely.js +0 -184
  243. package/dist/src/local-first/Sync.d.ts +0 -112
  244. package/dist/src/local-first/Sync.d.ts.map +0 -1
  245. package/dist/src/local-first/Sync.js +0 -529
  246. package/src/Instances.ts +0 -90
  247. package/src/Resources.ts +0 -367
  248. package/src/local-first/Platform.ts +0 -27
  249. package/src/local-first/Public.ts +0 -42
  250. package/src/local-first/PublicKysely.ts +0 -239
  251. package/src/local-first/Sync.ts +0 -960
@@ -1,440 +1,510 @@
1
- import { pack } from "msgpackr";
2
- import { dedupeArray, isNonEmptyArray, isNonEmptyReadonlyArray, } from "../Array.js";
3
- import { assert, assertNonEmptyReadonlyArray } from "../Assert.js";
1
+ /**
2
+ * Local-first platform.
3
+ *
4
+ * @module
5
+ */
6
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
7
+ if (value !== null && value !== void 0) {
8
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
9
+ var dispose, inner;
10
+ if (async) {
11
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
12
+ dispose = value[Symbol.asyncDispose];
13
+ }
14
+ if (dispose === void 0) {
15
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
16
+ dispose = value[Symbol.dispose];
17
+ if (async) inner = dispose;
18
+ }
19
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
20
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
21
+ env.stack.push({ value: value, dispose: dispose, async: async });
22
+ }
23
+ else if (async) {
24
+ env.stack.push({ async: true });
25
+ }
26
+ return value;
27
+ };
28
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
29
+ return function (env) {
30
+ function fail(e) {
31
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
32
+ env.hasError = true;
33
+ }
34
+ var r, s = 0;
35
+ function next() {
36
+ while (r = env.stack.pop()) {
37
+ try {
38
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
39
+ if (r.dispose) {
40
+ var result = r.dispose.call(r.value);
41
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
42
+ }
43
+ else s |= 1;
44
+ }
45
+ catch (e) {
46
+ fail(e);
47
+ }
48
+ }
49
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
50
+ if (env.hasError) throw env.error;
51
+ }
52
+ return next();
53
+ };
54
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
55
+ var e = new Error(message);
56
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
57
+ });
58
+ import { emptyArray, isNonEmptyArray, mapArray, } from "../Array.js";
59
+ import { assert, assertNonEmptyReadonlyArray, assertNotDisposed, } from "../Assert.js";
4
60
  import { createCallbacks } from "../Callbacks.js";
5
- import { eqArrayNumber } from "../Eq.js";
6
- import { exhaustiveCheck } from "../Function.js";
7
- import { createInstances } from "../Instances.js";
61
+ import { createConsole } from "../Console.js";
62
+ import { createUnknownError } from "../Error.js";
63
+ import { exhaustiveCheck, todo } from "../Function.js";
64
+ import { createMicrotaskBatch } from "../Microtask.js";
65
+ import { createRefCountByKey } from "../RefCount.js";
8
66
  import { err, ok } from "../Result.js";
9
- import { isSqlMutation, SqliteBoolean, sqliteBooleanToBoolean, } from "../Sqlite.js";
67
+ import { isNonEmptySet } from "../Set.js";
68
+ import { SqliteBoolean, sqliteBooleanToBoolean } from "../Sqlite.js";
10
69
  import { createStore } from "../Store.js";
11
- import { createId, } from "../Type.js";
12
- import { defaultDbConfig } from "./Db.js";
13
- import { applyPatches, createSubscribedQueries, emptyRows, serializeQuery, } from "./Query.js";
14
- import { evoluSchemaToDbSchema, insertable, kysely, updateable, upsertable, } from "./Schema.js";
70
+ import {} from "../Task.js";
71
+ import { brand, createId, createIdFromString, Name, UrlSafeString, } from "../Type.js";
72
+ import { createOwnerWebSocketTransport } from "./Owner.js";
73
+ import { applyPatches } from "./Query.js";
74
+ import { evoluSchemaToSqliteSchema } from "./Schema.js";
15
75
  import { DbChange } from "./Storage.js";
16
- import { initialSyncState } from "./Sync.js";
17
- const evoluInstances = createInstances();
18
76
  /**
19
- * Unique identifier for the current browser tab or app instance, lazily
20
- * initialized on first use to distinguish between multiple tabs.
21
- */
22
- let tabId = null;
23
- /**
24
- * Creates an {@link Evolu} instance for a platform configured with the specified
25
- * {@link EvoluSchema} and optional {@link EvoluConfig} providing a typed
26
- * interface for querying, mutating, and syncing your application's data.
27
- *
28
- * ### Example
29
- *
30
- * ```ts
31
- * const TodoId = id("Todo");
32
- * type TodoId = InferType<typeof TodoId>;
77
+ * Application name.
33
78
  *
34
- * const TodoCategoryId = id("TodoCategory");
35
- * type TodoCategoryId = InferType<typeof TodoCategoryId>;
79
+ * Evolu uses AppName as the base prefix for {@link Evolu.name}. The final
80
+ * instance name is derived per {@link AppOwner} as
81
+ * `${appName}-${createIdFromString(appOwner.id)}`.
36
82
  *
37
- * const NonEmptyString50 = maxLength(50, NonEmptyString);
38
- * type NonEmptyString50 = InferType<typeof NonEmptyString50>;
39
- *
40
- * const Schema = {
41
- * todo: {
42
- * id: TodoId,
43
- * title: NonEmptyString1000,
44
- * isCompleted: nullOr(SqliteBoolean),
45
- * categoryId: nullOr(TodoCategoryId),
46
- * },
47
- * todoCategory: {
48
- * id: TodoCategoryId,
49
- * name: NonEmptyString50,
50
- * },
51
- * };
52
- *
53
- * const evolu = createEvolu(evoluReactDeps)(Schema);
54
- * ```
83
+ * Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
84
+ * `_`) and must be between 1 and 41 characters.
85
+ */
86
+ export const AppName = /*#__PURE__*/ brand("AppName", UrlSafeString, (value) => value.length >= 1 && value.length <= 41
87
+ ? ok(value)
88
+ : err({ type: "AppName", value }));
89
+ export const testAppName = /*#__PURE__*/ AppName.orThrow("AppName");
90
+ /**
91
+ * Creates shared dependencies used by all {@link createEvolu} instances on a
92
+ * platform.
55
93
  *
56
- * ### Instance Caching
94
+ * Call this once per platform and reuse the returned deps when creating
95
+ * multiple Evolu instances. The returned deps object owns long-lived resources
96
+ * such as worker channels and the shared {@link EvoluErrorDep.evoluError}
97
+ * store.
57
98
  *
58
- * `createEvolu` caches instances using {@link Instances} by {@link EvoluConfig}
59
- * name to enable hot reloading and prevent database corruption from multiple
60
- * connections. For testing, use unique instance names to ensure proper
61
- * isolation.
99
+ * Dispose it only during app shutdown.
62
100
  */
63
- export const createEvolu = (deps) => (schema, config) => evoluInstances.ensure(config?.name ?? defaultDbConfig.name, () => createEvoluInstance(deps)(schema, config), (evolu) => {
64
- // Hot reloading. Note that indexes are intentionally omitted.
65
- evolu.ensureSchema(schema);
66
- });
67
- const createEvoluInstance = (deps) => (schema, config) => {
68
- deps.console.enabled = config?.enableLogging ?? false;
69
- const { indexes, reloadUrl = "/", ...partialDbConfig } = config ?? {};
70
- const dbConfig = { ...defaultDbConfig, ...partialDbConfig };
71
- deps.console.log("[evolu]", "createEvoluInstance", {
72
- name: dbConfig.name,
73
- });
74
- const errorStore = createStore(null);
75
- const rowsStore = createStore(new Map());
76
- const { promise: appOwner, resolve: resolveAppOwner } = Promise.withResolvers();
77
- if (config?.externalAppOwner) {
78
- resolveAppOwner(config.externalAppOwner);
79
- }
80
- // TODO: Update it for the owner-api
81
- const _syncStore = createStore(initialSyncState);
82
- const subscribedQueries = createSubscribedQueries(rowsStore);
83
- const loadingPromises = createLoadingPromises(subscribedQueries);
84
- const onCompleteCallbacks = createCallbacks(deps);
85
- const exportCallbacks = createCallbacks(deps);
86
- const dbWorker = deps.createDbWorker(dbConfig.name);
87
- const getTabId = () => {
88
- tabId ??= createId(deps);
89
- return tabId;
90
- };
91
- // Worker responses are delivered to all tabs. Each case must handle this
92
- // properly (e.g., AppOwner promise resolves only once, tabId filtering).
93
- dbWorker.onMessage((message) => {
101
+ export const createEvoluDeps = (deps) => {
102
+ const { createMessageChannel, sharedWorker } = deps;
103
+ const console = deps.console ?? createConsole();
104
+ const stack = new DisposableStack();
105
+ stack.use(sharedWorker);
106
+ const evoluError = stack.use(createStore(null));
107
+ const tabChannel = stack.use(createMessageChannel());
108
+ tabChannel.port2.onMessage = (message) => {
94
109
  switch (message.type) {
95
- case "onError": {
96
- errorStore.set(message.error);
97
- break;
98
- }
99
- case "onGetAppOwner": {
100
- resolveAppOwner(message.appOwner);
101
- break;
102
- }
103
- case "onQueryPatches": {
104
- if (message.tabId !== getTabId())
105
- return;
106
- const state = rowsStore.get();
107
- const nextState = new Map([
108
- ...state,
109
- ...message.queryPatches.map(({ query, patches }) => [
110
- query,
111
- applyPatches(patches, state.get(query) ?? emptyRows),
112
- ]),
113
- ]);
114
- for (const { query } of message.queryPatches) {
115
- loadingPromises.resolve(query, nextState.get(query) ?? emptyRows);
116
- }
117
- if (deps.flushSync && message.onCompleteIds.length > 0) {
118
- deps.flushSync(() => {
119
- rowsStore.set(nextState);
120
- });
121
- }
122
- else {
123
- rowsStore.set(nextState);
124
- }
125
- for (const id of message.onCompleteIds) {
126
- onCompleteCallbacks.execute(id);
110
+ case "OnConsoleEntry":
111
+ console.write(message.entry);
112
+ // Fallback channel for unexpected errors without EvoluError typing.
113
+ if (message.entry.method === "error") {
114
+ evoluError.set(createUnknownError(message.entry.args));
127
115
  }
128
116
  break;
129
- }
130
- case "refreshQueries": {
131
- if (message.tabId && message.tabId === getTabId())
132
- return;
133
- const loadingPromisesQueries = loadingPromises.getQueries();
134
- loadingPromises.releaseUnsubscribedOnMutation();
135
- const queries = dedupeArray([
136
- ...loadingPromisesQueries,
137
- ...subscribedQueries.get(),
138
- ]);
139
- if (isNonEmptyReadonlyArray(queries)) {
140
- dbWorker.postMessage({ type: "query", tabId: getTabId(), queries });
141
- }
117
+ case "OnError":
118
+ evoluError.set(message.error);
119
+ // Keep typed errors visible in logs as operational failures.
120
+ console.error(message.error);
142
121
  break;
143
- }
144
- case "onReset": {
145
- if (message.reload) {
146
- deps.reloadApp(reloadUrl);
147
- }
148
- else {
149
- onCompleteCallbacks.execute(message.onCompleteId);
150
- }
151
- break;
152
- }
153
- case "onExport": {
154
- exportCallbacks.execute(message.onCompleteId, message.file);
155
- break;
156
- }
157
122
  default:
158
123
  exhaustiveCheck(message);
159
124
  }
160
- });
161
- const dbSchema = evoluSchemaToDbSchema(schema, indexes);
162
- const mutationTypesCache = new Map();
163
- // Lazy create mutation Types like this: `insertable(Schema.todo)`
164
- const getMutationType = (table, kind) => {
165
- let types = mutationTypesCache.get(kind);
166
- if (!types) {
167
- types = new Map();
168
- mutationTypesCache.set(kind, types);
169
- }
170
- let type = types.get(table);
171
- if (!type) {
172
- type = { insert: insertable, update: updateable, upsert: upsertable }[kind](schema[table]);
173
- types.set(table, type);
174
- }
175
- return type;
176
- };
177
- dbWorker.postMessage({ type: "init", config: dbConfig, dbSchema });
178
- // We can't use `init` to get AppOwner because `init` runs only once per n tabs.
179
- dbWorker.postMessage({ type: "getAppOwner" });
180
- const loadQueryMicrotaskQueue = [];
181
- const mutateMicrotaskQueue = [];
182
- const useOwnerMicrotaskQueue = [];
183
- const createMutation = (kind) => (table, props, options) => {
184
- const result = getMutationType(table, kind).fromUnknown(props);
185
- const id = kind === "insert"
186
- ? createId(deps)
187
- : props.id;
188
- if (options?.onlyValidate !== true) {
189
- if (!result.ok) {
190
- // Mark the transaction as invalid by pushing null
191
- mutateMicrotaskQueue.push([null, undefined]);
192
- }
193
- else {
194
- const { id: _, isDeleted, ...values } = result.value;
195
- const dbChange = {
196
- table,
197
- id,
198
- values,
199
- isInsert: kind === "insert" || kind === "upsert",
200
- isDelete: SqliteBoolean.is(isDeleted)
201
- ? sqliteBooleanToBoolean(isDeleted)
202
- : null,
203
- };
204
- assert(DbChange.is(dbChange), `Invalid DbChange for table '${table}': Please check schema type errors.`);
205
- mutateMicrotaskQueue.push([
206
- { ...dbChange, ownerId: options?.ownerId },
207
- options?.onComplete,
208
- ]);
209
- }
210
- if (mutateMicrotaskQueue.length === 1) {
211
- queueMicrotask(processMutationQueue);
212
- }
213
- }
214
- if (result.ok)
215
- return ok({ id });
216
- return err(result.error);
217
125
  };
218
- const processMutationQueue = () => {
219
- const changes = [];
220
- const onCompletes = [];
221
- for (const [change, onComplete] of mutateMicrotaskQueue) {
222
- if (change !== null)
223
- changes.push(change);
224
- if (onComplete)
225
- onCompletes.push(onComplete);
226
- }
227
- const queueLength = mutateMicrotaskQueue.length;
228
- mutateMicrotaskQueue.length = 0;
229
- // Don't process any mutations if there was a validation error.
230
- // All mutations within a queue run as a single transaction.
231
- if (changes.length !== queueLength) {
232
- return;
233
- }
234
- const onCompleteIds = onCompletes.map(onCompleteCallbacks.register);
235
- loadingPromises.releaseUnsubscribedOnMutation();
236
- if (!isNonEmptyArray(changes))
237
- return;
238
- dbWorker.postMessage({
239
- type: "mutate",
240
- tabId: getTabId(),
241
- changes,
242
- onCompleteIds,
243
- subscribedQueries: subscribedQueries.get(),
244
- });
245
- };
246
- const evolu = {
247
- subscribeError: errorStore.subscribe,
248
- getError: errorStore.get,
249
- createQuery,
250
- loadQuery: (query) => {
251
- const { promise, isNew } = loadingPromises.get(query);
252
- if (isNew) {
253
- loadQueryMicrotaskQueue.push(query);
254
- if (loadQueryMicrotaskQueue.length === 1) {
255
- queueMicrotask(() => {
256
- const queries = dedupeArray(loadQueryMicrotaskQueue);
257
- loadQueryMicrotaskQueue.length = 0;
258
- assertNonEmptyReadonlyArray(queries);
259
- deps.console.log("[evolu]", "loadQuery", { queries });
260
- dbWorker.postMessage({
261
- type: "query",
262
- tabId: getTabId(),
263
- queries,
264
- });
265
- });
266
- }
267
- }
268
- return promise;
269
- },
270
- loadQueries: (queries) => queries.map(evolu.loadQuery),
271
- subscribeQuery: (query) => (listener) => {
272
- // Call the listener only if the result has been changed.
273
- let previousRows = null;
274
- const unsubscribe = subscribedQueries.subscribe(query)(() => {
275
- const rows = evolu.getQueryRows(query);
276
- if (previousRows === rows)
277
- return;
278
- previousRows = rows;
279
- listener();
280
- });
281
- return () => {
282
- previousRows = null;
283
- unsubscribe();
284
- };
285
- },
286
- getQueryRows: (query) => (rowsStore.get().get(query) ?? emptyRows),
287
- appOwner,
288
- // TODO: Update it for the owner-api
289
- // subscribeSyncState: syncStore.subscribe,
290
- // getSyncState: syncStore.get,
291
- insert: createMutation("insert"),
292
- update: createMutation("update"),
293
- upsert: createMutation("upsert"),
294
- resetAppOwner: (options) => {
295
- const { promise, resolve } = Promise.withResolvers();
296
- const onCompleteId = onCompleteCallbacks.register(resolve);
297
- dbWorker.postMessage({
298
- type: "reset",
299
- onCompleteId,
300
- reload: options?.reload ?? true,
301
- });
302
- return promise;
303
- },
304
- restoreAppOwner: (mnemonic, options) => {
305
- const { promise, resolve } = Promise.withResolvers();
306
- const onCompleteId = onCompleteCallbacks.register(resolve);
307
- dbWorker.postMessage({
308
- type: "reset",
309
- onCompleteId,
310
- reload: options?.reload ?? true,
311
- restore: { mnemonic, dbSchema },
312
- });
313
- return promise;
314
- },
315
- reloadApp: () => {
316
- deps.reloadApp(reloadUrl);
317
- },
318
- ensureSchema: (schema) => {
319
- mutationTypesCache.clear();
320
- const dbSchema = evoluSchemaToDbSchema(schema);
321
- dbWorker.postMessage({ type: "ensureDbSchema", dbSchema });
322
- },
323
- exportDatabase: () => {
324
- const { promise, resolve } = Promise.withResolvers();
325
- const onCompleteId = exportCallbacks.register(resolve);
326
- dbWorker.postMessage({ type: "export", onCompleteId });
327
- return promise;
328
- },
329
- useOwner: (owner) => {
330
- const scheduleOwnerQueueProcessing = () => {
331
- if (useOwnerMicrotaskQueue.length !== 1)
332
- return;
333
- queueMicrotask(() => {
334
- const queue = [...useOwnerMicrotaskQueue];
335
- useOwnerMicrotaskQueue.length = 0;
336
- const result = [];
337
- const skipIndices = new Set();
338
- for (let i = 0; i < queue.length; i++) {
339
- if (skipIndices.has(i))
340
- continue;
341
- const [currentOwner, currentUse, currentOwnerSerialized] = queue[i];
342
- // Look for opposite action with same owner
343
- for (let j = i + 1; j < queue.length; j++) {
344
- if (skipIndices.has(j))
345
- continue;
346
- const [, otherUse, otherOwnerSerialized] = queue[j];
347
- if (currentUse !== otherUse &&
348
- eqArrayNumber(currentOwnerSerialized, otherOwnerSerialized)) {
349
- // Found cancel-out pair, skip both
350
- skipIndices.add(i).add(j);
351
- break;
352
- }
353
- }
354
- if (!skipIndices.has(i)) {
355
- result.push([currentOwner, currentUse, currentOwnerSerialized]);
356
- }
357
- }
358
- for (const [owner, use] of result) {
359
- dbWorker.postMessage({ type: "useOwner", owner, use });
360
- }
361
- });
362
- };
363
- useOwnerMicrotaskQueue.push([owner, true, pack(owner)]);
364
- scheduleOwnerQueueProcessing();
365
- const unuse = () => {
366
- useOwnerMicrotaskQueue.push([owner, false, pack(owner)]);
367
- scheduleOwnerQueueProcessing();
368
- };
369
- return unuse;
370
- },
371
- /** Disposal is not implemented yet. */
372
- [Symbol.dispose]: () => {
373
- throw new Error("Evolu instance disposal is not yet implemented");
374
- },
126
+ sharedWorker.port.postMessage({
127
+ type: "InitTab",
128
+ consoleLevel: console.getLevel(),
129
+ port: tabChannel.port1.native,
130
+ }, [tabChannel.port1.native]);
131
+ const moved = stack.move();
132
+ return {
133
+ ...deps,
134
+ evoluError,
135
+ [Symbol.dispose]: () => moved.dispose(),
375
136
  };
376
- return evolu;
377
- };
378
- export const createQuery = (queryCallback, options) => {
379
- const compiledQuery = queryCallback(kysely).compile();
380
- if (isSqlMutation(compiledQuery.sql))
381
- throw new Error("SQL mutation (INSERT, UPDATE, DELETE, etc.) isn't allowed in the Evolu `createQuery` function. Kysely suggests it because there is no read-only Kysely yet, and removing such an API is not possible. For mutations, use Evolu Mutation API.");
382
- return serializeQuery({
383
- sql: compiledQuery.sql,
384
- parameters: compiledQuery.parameters,
385
- ...(options && { options }),
386
- });
387
137
  };
388
- const createLoadingPromises = (subscribedQueries) => {
389
- const loadingPromiseMap = new Map();
390
- return {
391
- get: (query) => {
392
- let loadingPromise = loadingPromiseMap.get(query);
393
- const isNew = !loadingPromise;
394
- if (!loadingPromise) {
395
- const { promise, resolve } = Promise.withResolvers();
396
- loadingPromise = { resolve, promise, releaseOnResolve: false };
397
- loadingPromiseMap.set(query, loadingPromise);
138
+ /**
139
+ * Creates an {@link Evolu} instance from {@link EvoluSchema} and
140
+ * {@link EvoluConfig}.
141
+ */
142
+ export const createEvolu = (schema, config) => async (run) => {
143
+ const env_1 = { stack: [], error: void 0, hasError: false };
144
+ try {
145
+ const { appName, appOwner, memoryOnly = false, transports = [{ type: "WebSocket", url: "wss://free.evoluhq.com" }], } = config;
146
+ const name = Name.orThrow(`${appName}-${createIdFromString(appOwner.id)}`);
147
+ const console = run.deps.console.child(name).child("Evolu");
148
+ console.info("createEvolu");
149
+ const stack = __addDisposableResource(env_1, new AsyncDisposableStack(), true);
150
+ const rowsByQueryMapStore = stack.use(createStore(new Map()));
151
+ const subscribedQueriesRefCount = stack.use(createRefCountByKey());
152
+ /**
153
+ * Settle pending query loads during disposal so awaiting callers and React
154
+ * `use` thenables do not hang forever during teardown.
155
+ */
156
+ const loadingPromisesByQuery = stack.adopt(new Map(), (loadingPromisesByQuery) => {
157
+ for (const loadingPromise of loadingPromisesByQuery.values()) {
158
+ if (loadingPromise.promise.status === "fulfilled")
159
+ continue;
160
+ fulfillLoadingPromise(loadingPromise, emptyArray);
398
161
  }
399
- return {
400
- promise: loadingPromise.promise,
401
- isNew,
402
- };
403
- },
404
- resolve: (query, rows) => {
405
- const loadingPromise = loadingPromiseMap.get(query);
406
- if (!loadingPromise)
407
- return;
162
+ loadingPromisesByQuery.clear();
163
+ });
164
+ const fulfillLoadingPromise = (loadingPromise, rows) => {
165
+ /**
166
+ * Pending promises must be resolved in place to preserve identity for
167
+ * current awaiters. Fulfilled promises are replaced with a new resolved
168
+ * promise so future loads see the latest rows.
169
+ */
408
170
  if (loadingPromise.promise.status !== "fulfilled") {
409
171
  loadingPromise.resolve(rows);
410
172
  }
411
173
  else {
412
174
  loadingPromise.promise = Promise.resolve(rows);
413
175
  }
414
- // Set status and value fields for React's `use` Hook to unwrap synchronously.
415
- // While undocumented in React docs, React still uses these properties internally,
416
- // and Evolu's own promise caching logic depends on checking `promise.status`.
417
- // https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
176
+ /** See {@link LoadingPromise.promise}. */
418
177
  void Object.assign(loadingPromise.promise, {
419
178
  status: "fulfilled",
420
179
  value: rows,
421
180
  });
422
- if (loadingPromise.releaseOnResolve) {
423
- loadingPromiseMap.delete(query);
424
- }
425
- },
426
- releaseUnsubscribedOnMutation: () => {
427
- [...loadingPromiseMap.entries()]
428
- .filter(([query]) => !subscribedQueries.has(query))
429
- .forEach(([query, loadingPromise]) => {
181
+ };
182
+ /**
183
+ * Mutations and refreshes invalidate query snapshots. Keep loading promises
184
+ * only for actively subscribed queries and release unsubscribed ones.
185
+ *
186
+ * Fulfilled promises can be dropped immediately because no awaiter is
187
+ * waiting on them. Pending promises must stay alive until they resolve so
188
+ * current awaiters keep the same promise identity.
189
+ */
190
+ const releaseUnsubscribedLoadingPromises = () => {
191
+ for (const [query, loadingPromise] of loadingPromisesByQuery) {
192
+ if (subscribedQueriesRefCount.has(query))
193
+ continue;
430
194
  if (loadingPromise.promise.status === "fulfilled") {
431
- loadingPromiseMap.delete(query);
195
+ loadingPromisesByQuery.delete(query);
432
196
  }
433
197
  else {
434
198
  loadingPromise.releaseOnResolve = true;
435
199
  }
200
+ }
201
+ };
202
+ const onMutateCompleteCallbacks = stack.use(createCallbacks(run.deps));
203
+ let exportDatabasePending = null;
204
+ stack.defer(() => {
205
+ exportDatabasePending?.reject({ type: "EvoluDisposedError" });
206
+ exportDatabasePending = null;
207
+ });
208
+ const mutateBatch = stack.use(createMicrotaskBatch((items) => {
209
+ console.debug("mutateBatch", { changeCount: items.length });
210
+ releaseUnsubscribedLoadingPromises();
211
+ postMessage({
212
+ type: "Mutate",
213
+ changes: mapArray(items, (item) => item.change),
214
+ onCompleteIds: items.flatMap((item) => item.onComplete
215
+ ? [onMutateCompleteCallbacks.register(item.onComplete)]
216
+ : []),
217
+ subscribedQueries: subscribedQueriesRefCount.keys(),
436
218
  });
437
- },
438
- getQueries: () => Array.from(loadingPromiseMap.keys()),
439
- };
219
+ }));
220
+ const queryBatch = stack.use(createMicrotaskBatch((queries) => {
221
+ const dedupedQueries = new Set(queries);
222
+ assert(isNonEmptySet(dedupedQueries), "Expected non-empty query batch.");
223
+ console.debug("queryBatch", { queryCount: dedupedQueries.size });
224
+ postMessage({ type: "Query", queries: dedupedQueries });
225
+ }));
226
+ const useOwnerBatch = stack.use(createMicrotaskBatch((actions) => postMessage({ type: "UseOwner", actions })));
227
+ let postMessage;
228
+ // Scope worker/channel wiring and keep only postMessage outside.
229
+ {
230
+ const { createDbWorker, createMessageChannel, sharedWorker } = run.deps;
231
+ const dbWorkerChannel = stack.use(createMessageChannel());
232
+ const evoluChannel = stack.use(createMessageChannel());
233
+ evoluChannel.port1.onMessage = (message) => {
234
+ switch (message.type) {
235
+ case "OnPatchesByQuery": {
236
+ console.debug("onPatchesByQuery", {
237
+ queryCount: message.patchesByQuery.size,
238
+ onCompleteCount: message.onCompleteIds.length,
239
+ });
240
+ const state = rowsByQueryMapStore.get();
241
+ const nextRowsByQueryMap = new Map(state);
242
+ for (const [query, patches] of message.patchesByQuery) {
243
+ nextRowsByQueryMap.set(query, applyPatches(patches, state.get(query) ?? emptyArray));
244
+ }
245
+ for (const query of message.patchesByQuery.keys()) {
246
+ const loadingPromise = loadingPromisesByQuery.get(query);
247
+ if (!loadingPromise)
248
+ continue;
249
+ const rows = nextRowsByQueryMap.get(query);
250
+ assert(rows, "Expected patched query rows to exist.");
251
+ fulfillLoadingPromise(loadingPromise, rows);
252
+ /**
253
+ * Release promises flagged during mutation when they finish
254
+ * resolving. This keeps in-flight promise identity stable and
255
+ * prevents stale cache entries after completion.
256
+ */
257
+ if (loadingPromise.releaseOnResolve) {
258
+ loadingPromisesByQuery.delete(query);
259
+ }
260
+ }
261
+ if (run.deps.flushSync && message.onCompleteIds.length > 0) {
262
+ run.deps.flushSync(() => {
263
+ rowsByQueryMapStore.set(nextRowsByQueryMap);
264
+ });
265
+ }
266
+ else {
267
+ rowsByQueryMapStore.set(nextRowsByQueryMap);
268
+ }
269
+ for (const onCompleteId of message.onCompleteIds) {
270
+ onMutateCompleteCallbacks.execute(onCompleteId);
271
+ }
272
+ break;
273
+ }
274
+ case "RefreshQueries": {
275
+ releaseUnsubscribedLoadingPromises();
276
+ const queries = new Set([
277
+ ...loadingPromisesByQuery.keys(),
278
+ ...subscribedQueriesRefCount.keys(),
279
+ ]);
280
+ if (isNonEmptySet(queries))
281
+ postMessage({ type: "Query", queries });
282
+ break;
283
+ }
284
+ case "OnExport": {
285
+ assert(exportDatabasePending, "OnExport received without pending export.");
286
+ exportDatabasePending.resolve(message.file);
287
+ exportDatabasePending = null;
288
+ break;
289
+ }
290
+ default:
291
+ exhaustiveCheck(message);
292
+ }
293
+ };
294
+ sharedWorker.port.postMessage({
295
+ type: "CreateEvolu",
296
+ name,
297
+ evoluPort: evoluChannel.port2.native,
298
+ dbWorkerPort: dbWorkerChannel.port2.native,
299
+ }, [evoluChannel.port2.native, dbWorkerChannel.port2.native]);
300
+ /**
301
+ * No stack.use because Evolu instances don't dispose DbWorker because
302
+ * it's SharedWorder responsibility. DbWorker can be used by another tab.
303
+ * That's required because SQLite WASM needs a single web worker.
304
+ */
305
+ createDbWorker().postMessage({
306
+ type: "Init",
307
+ name,
308
+ consoleLevel: console.getLevel(),
309
+ sqliteSchema: evoluSchemaToSqliteSchema(schema, config.indexes),
310
+ encryptionKey: appOwner.encryptionKey,
311
+ memoryOnly,
312
+ port: dbWorkerChannel.port1.native,
313
+ }, [dbWorkerChannel.port1.native]);
314
+ postMessage = evoluChannel.port1.postMessage;
315
+ stack.defer(() => {
316
+ postMessage({ type: "Dispose" });
317
+ });
318
+ }
319
+ const createMutation = (kind) => (table, values, options) => {
320
+ assertNotDisposed(moved);
321
+ const { id = createId(run.deps), isDeleted, ...changeValues } = values;
322
+ const dbChange = {
323
+ table,
324
+ id,
325
+ values: changeValues,
326
+ isInsert: kind === "insert" || kind === "upsert",
327
+ isDelete: SqliteBoolean.is(isDeleted)
328
+ ? sqliteBooleanToBoolean(isDeleted)
329
+ : null,
330
+ };
331
+ assert(DbChange.is(dbChange), `Invalid DbChange for table '${String(table)}'.`);
332
+ mutateBatch.push({
333
+ change: { ...dbChange, ownerId: options?.ownerId ?? appOwner.id },
334
+ onComplete: options?.onComplete,
335
+ });
336
+ return { id };
337
+ };
338
+ const loadQuery = (query) => {
339
+ assertNotDisposed(moved);
340
+ const loadingPromise = loadingPromisesByQuery.get(query);
341
+ if (loadingPromise) {
342
+ return loadingPromise.promise;
343
+ }
344
+ const { promise, resolve } = Promise.withResolvers();
345
+ const typedPromise = promise;
346
+ typedPromise.status = "pending";
347
+ loadingPromisesByQuery.set(query, {
348
+ promise: typedPromise,
349
+ resolve,
350
+ releaseOnResolve: false,
351
+ });
352
+ queryBatch.push(query);
353
+ return typedPromise;
354
+ };
355
+ const getQueryRows = (query) => {
356
+ assertNotDisposed(moved);
357
+ return (rowsByQueryMapStore.get().get(query) ??
358
+ emptyArray);
359
+ };
360
+ const useOwner = (owner, ownerTransports) => {
361
+ assertNotDisposed(moved);
362
+ const effectiveTransports = ownerTransports ?? transports;
363
+ assertNonEmptyReadonlyArray(effectiveTransports, "useOwner requires explicit non-empty transports when config.transports is empty.");
364
+ const syncOwner = {
365
+ owner,
366
+ transports: effectiveTransports,
367
+ };
368
+ useOwnerBatch.push({
369
+ owner: syncOwner,
370
+ action: "add",
371
+ });
372
+ let isUsed = true;
373
+ return () => {
374
+ assert(isUsed, "UnuseOwner can be called only once.");
375
+ isUsed = false;
376
+ useOwnerBatch.push({
377
+ owner: syncOwner,
378
+ action: "remove",
379
+ });
380
+ };
381
+ };
382
+ const moved = stack.move();
383
+ if (isNonEmptyArray(transports))
384
+ useOwner(appOwner);
385
+ return ok({
386
+ name,
387
+ appOwner,
388
+ insert: createMutation("insert"),
389
+ update: createMutation("update"),
390
+ upsert: createMutation("upsert"),
391
+ loadQuery,
392
+ loadQueries: (queries) => queries.map((query) => loadQuery(query)),
393
+ subscribeQuery: (query) => (listener) => {
394
+ assertNotDisposed(moved);
395
+ subscribedQueriesRefCount.increment(query);
396
+ let isSubscribed = true;
397
+ let previousRows = null;
398
+ const unsubscribe = rowsByQueryMapStore.subscribe(() => {
399
+ const rows = getQueryRows(query);
400
+ if (previousRows === rows)
401
+ return;
402
+ previousRows = rows;
403
+ listener();
404
+ });
405
+ return () => {
406
+ assert(isSubscribed, "subscribeQuery unsubscribe can be called only once.");
407
+ isSubscribed = false;
408
+ previousRows = null;
409
+ unsubscribe();
410
+ if (moved.disposed)
411
+ return;
412
+ subscribedQueriesRefCount.decrement(query);
413
+ };
414
+ },
415
+ getQueryRows,
416
+ exportDatabase: () => {
417
+ assertNotDisposed(moved);
418
+ if (!exportDatabasePending) {
419
+ exportDatabasePending =
420
+ Promise.withResolvers();
421
+ postMessage({ type: "Export" });
422
+ }
423
+ return exportDatabasePending.promise;
424
+ },
425
+ deleteDatabase: () => {
426
+ assertNotDisposed(moved);
427
+ todo();
428
+ },
429
+ deleteOwner: (owner) => {
430
+ assertNotDisposed(moved);
431
+ void owner;
432
+ todo();
433
+ },
434
+ useOwner,
435
+ [Symbol.asyncDispose]: () => {
436
+ console.info("dispose");
437
+ return moved.disposeAsync();
438
+ },
439
+ });
440
+ }
441
+ catch (e_1) {
442
+ env_1.error = e_1;
443
+ env_1.hasError = true;
444
+ }
445
+ finally {
446
+ const result_1 = __disposeResources(env_1);
447
+ if (result_1)
448
+ await result_1;
449
+ }
440
450
  };
451
+ // case "onReset": {
452
+ // if (message.reload) {
453
+ // deps.reloadApp(reloadUrl);
454
+ // } else {
455
+ // onCompleteCallbacks.execute(message.onCompleteId);
456
+ // }
457
+ // break;
458
+ // }
459
+ // resetAppOwner: (_options) => {
460
+ // const { promise, resolve } = Promise.withResolvers<undefined>();
461
+ // const _onCompleteId = onCompleteCallbacks.register(resolve);
462
+ // // dbWorker.postMessage({
463
+ // // type: "reset",
464
+ // // onCompleteId,
465
+ // // reload: options?.reload ?? true,
466
+ // // });
467
+ // return promise;
468
+ // },
469
+ // restoreAppOwner: (_mnemonic, _options) => {
470
+ // const { promise, resolve } = Promise.withResolvers<undefined>();
471
+ // const _onCompleteId = onCompleteCallbacks.register(resolve);
472
+ // // dbWorker.postMessage({
473
+ // // type: "reset",
474
+ // // onCompleteId,
475
+ // // reload: options?.reload ?? true,
476
+ // // restore: { mnemonic, sqliteSchema },
477
+ // // });
478
+ // return promise;
479
+ // },
480
+ // reloadApp: () => {
481
+ // // TODO:
482
+ // // deps.reloadApp(reloadUrl);
483
+ // },
484
+ // ensureSchema: (schema) => {
485
+ // mutationTypesCache.clear();
486
+ // const sqliteSchema = evoluSchemaToSqliteSchema(schema);
487
+ // dbWorker.postMessage({ type: "ensureSqliteSchema", sqliteSchema });
488
+ // },
489
+ // /**
490
+ // * Delete {@link AppOwner} and all their data from the current device. After
491
+ // * the deletion, Evolu will purge the application state. For browsers, this
492
+ // * will reload all tabs using Evolu. For native apps, it will restart the
493
+ // * app.
494
+ // *
495
+ // * Reloading can be turned off via options if you want to provide a different
496
+ // * UX.
497
+ // */
498
+ // readonly resetAppOwner: (options?: {
499
+ // readonly reload?: boolean;
500
+ // }) => Promise<void>;
501
+ // /**
502
+ // * Restore {@link AppOwner} with all their synced data. It uses
503
+ // * {@link Evolu.resetAppOwner}, so be careful.
504
+ // */
505
+ // readonly restoreAppOwner: (
506
+ // mnemonic: Mnemonic,
507
+ // options?: {
508
+ // readonly reload?: boolean;
509
+ // },
510
+ // ) => Promise<void>;