@evolu/common 7.4.1 → 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 +851 -430
  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 -824
  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 +206 -277
  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 -526
  246. package/src/Instances.ts +0 -92
  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 -949
@@ -1,52 +1,58 @@
1
+ /**
2
+ * Relay server for data synchronization.
3
+ *
4
+ * @module
5
+ */
6
+
1
7
  import {
2
8
  filterArray,
3
9
  firstInArray,
4
- isNonEmptyReadonlyArray,
10
+ isNonEmptyArray,
5
11
  mapArray,
6
12
  } from "../Array.js";
7
- import { ConsoleConfig, ConsoleDep } from "../Console.js";
8
- import { TimingSafeEqualDep } from "../Crypto.js";
9
- import { LazyValue } from "../Function.js";
10
- import { createInstances } from "../Instances.js";
11
- import { err, ok, Result } from "../Result.js";
12
- import { sql, SqliteDep, SqliteError } from "../Sqlite.js";
13
- import { createMutex, isAsync, MaybeAsync, Mutex } from "../Task.js";
14
- import { PositiveInt, SimpleName } from "../Type.js";
13
+ import { assert } from "../Assert.js";
14
+ import type { TimingSafeEqualDep } from "../Crypto.js";
15
+ import { err, getOk, ok } from "../Result.js";
16
+ import type { SqliteDep } from "../Sqlite.js";
17
+ import { sql } from "../Sqlite.js";
18
+ import { createMutexByKey } from "../Task.js";
19
+ import { Name, PositiveInt } from "../Type.js";
20
+ import { isPromiseLike, type Awaitable } from "../Types.js";
15
21
  import {
16
22
  OwnerId,
17
23
  ownerIdBytesToOwnerId,
18
- OwnerTransport,
24
+ // OwnerTransport,
19
25
  OwnerWriteKey,
20
26
  } from "./Owner.js";
21
- import { ProtocolInvalidDataError } from "./Protocol.js";
22
- import {
23
- createBaseSqliteStorage,
24
- CreateBaseSqliteStorageConfig,
27
+ import type {
25
28
  EncryptedDbChange,
26
- getOwnerUsage,
27
- getTimestampInsertStrategy,
28
29
  SqliteStorageDeps,
29
30
  Storage,
30
31
  StorageConfig,
31
32
  StorageQuotaError,
33
+ } from "./Storage.js";
34
+ import {
35
+ createBaseSqliteStorage,
36
+ getOwnerUsage,
37
+ getTimestampInsertStrategy,
32
38
  updateOwnerUsage,
33
39
  } from "./Storage.js";
34
40
  import { timestampToTimestampBytes } from "./Timestamp.js";
35
41
 
36
- export interface RelayConfig extends ConsoleConfig, StorageConfig {
42
+ export interface RelayConfig extends StorageConfig {
37
43
  /**
38
44
  * The relay name.
39
45
  *
40
46
  * Implementations can use this for identification purposes (e.g., database
41
47
  * file name, logging).
42
48
  */
43
- readonly name?: SimpleName;
49
+ readonly name?: Name;
44
50
 
45
51
  /**
46
52
  * Optional callback to check if an {@link OwnerId} is allowed to access the
47
53
  * relay. If this callback is not provided, all owners are allowed.
48
54
  *
49
- * The callback receives the {@link OwnerId} and returns a {@link MaybeAsync}
55
+ * The callback receives the {@link OwnerId} and returns a {@link Awaitable}
50
56
  * boolean: `true` to allow access, or `false` to deny.
51
57
  *
52
58
  * The callback can be synchronous (for SQLite or in-memory checks) or
@@ -60,7 +66,7 @@ export interface RelayConfig extends ConsoleConfig, StorageConfig {
60
66
  * relay access, not write permissions. Since all data is encrypted on the
61
67
  * relay, OwnerId exposure is safe.
62
68
  *
63
- * Owners specify which relays to connect to via {@link OwnerTransport}. In
69
+ * Owners specify which relays to connect to via `OwnerTransport`. In
64
70
  * WebSocket-based implementations, this check occurs before accepting the
65
71
  * connection, with the OwnerId typically extracted from the URL Path (e.g.,
66
72
  * `ws://localhost:4000/<ownerId>`). The relay requires the URL to be in the
@@ -85,7 +91,7 @@ export interface RelayConfig extends ConsoleConfig, StorageConfig {
85
91
  * Promise.resolve(ownerId === "6jy_2F4RT5qqeLgJ14_dnQ"),
86
92
  * ```
87
93
  */
88
- readonly isOwnerAllowed?: (ownerId: OwnerId) => MaybeAsync<boolean>;
94
+ readonly isOwnerAllowed?: (ownerId: OwnerId) => Awaitable<boolean>;
89
95
  }
90
96
 
91
97
  /**
@@ -97,21 +103,15 @@ export interface RelayConfig extends ConsoleConfig, StorageConfig {
97
103
  * decentralization and infinite horizontal scalability with minimal
98
104
  * infrastructure.
99
105
  */
100
- export interface Relay extends Disposable {}
106
+ export interface Relay extends AsyncDisposable {}
101
107
 
102
108
  export const createRelaySqliteStorage =
103
109
  (deps: SqliteStorageDeps & TimingSafeEqualDep) =>
104
- (config: CreateBaseSqliteStorageConfig): Storage => {
105
- const sqliteStorageBase = createBaseSqliteStorage(deps)(config);
106
-
107
- /**
108
- * Mutex instances are cached per OwnerId to prevent concurrent writes for
109
- * the same owner. Instances are never evicted, causing a memory leak
110
- * proportional to unique owner count. However, per-instance overhead should
111
- * be small. Monitor production memory usage to determine if
112
- * eviction/cleanup is needed.
113
- */
114
- const ownerMutexes = createInstances<OwnerId, Mutex>();
110
+ (config: StorageConfig): Storage => {
111
+ const sqliteStorageBase = createBaseSqliteStorage(deps);
112
+
113
+ /** Mutex keyed by OwnerId to prevent concurrent writes. */
114
+ const mutexByOwnerId = createMutexByKey<OwnerId>();
115
115
 
116
116
  return {
117
117
  ...sqliteStorageBase,
@@ -133,63 +133,46 @@ export const createRelaySqliteStorage =
133
133
  where ownerId = ${ownerId};
134
134
  `,
135
135
  );
136
- if (!selectWriteKey.ok) {
137
- config.onStorageError(selectWriteKey.error);
138
- return false;
139
- }
136
+ const { rows } = selectWriteKey;
140
137
 
141
- const { rows } = selectWriteKey.value;
142
-
143
- if (isNonEmptyReadonlyArray(rows)) {
138
+ if (isNonEmptyArray(rows)) {
144
139
  return deps.timingSafeEqual(rows[0].writeKey, writeKey);
145
140
  }
146
141
 
147
- const insertWriteKey = deps.sqlite.exec(sql`
142
+ deps.sqlite.exec(sql`
148
143
  insert into evolu_writeKey (ownerId, writeKey)
149
144
  values (${ownerId}, ${writeKey});
150
145
  `);
151
- if (!insertWriteKey.ok) {
152
- config.onStorageError(insertWriteKey.error);
153
- return false;
154
- }
155
146
 
156
147
  return true;
157
148
  },
158
149
 
159
150
  setWriteKey: (ownerId, writeKey) => {
160
- const upsertWriteKey = deps.sqlite.exec(sql`
151
+ deps.sqlite.exec(sql`
161
152
  insert into evolu_writeKey (ownerId, writeKey)
162
153
  values (${ownerId}, ${writeKey})
163
154
  on conflict (ownerId) do update
164
155
  set writeKey = excluded.writeKey;
165
156
  `);
166
- if (!upsertWriteKey.ok) {
167
- config.onStorageError(upsertWriteKey.error);
168
- return false;
169
- }
170
-
171
- return true;
172
157
  },
173
158
 
174
- writeMessages: async (ownerIdBytes, messages) => {
159
+ writeMessages: (ownerIdBytes, messages) => async (run) => {
175
160
  const ownerId = ownerIdBytesToOwnerId(ownerIdBytes);
176
161
  const messagesWithTimestampBytes = mapArray(messages, (m) => ({
177
162
  timestamp: timestampToTimestampBytes(m.timestamp),
178
163
  change: m.change,
179
164
  }));
180
165
 
181
- const result = await ownerMutexes
182
- .ensure(ownerId, createMutex)
183
- .withLock<void, SqliteError | StorageQuotaError>(async () => {
166
+ const result = await run(
167
+ mutexByOwnerId.withLock(ownerId, async () => {
184
168
  const existingTimestampsResult =
185
169
  sqliteStorageBase.getExistingTimestamps(
186
170
  ownerIdBytes,
187
171
  mapArray(messagesWithTimestampBytes, (m) => m.timestamp),
188
172
  );
189
- if (!existingTimestampsResult.ok) return existingTimestampsResult;
190
173
 
191
174
  const existingTimestampsSet = new Set(
192
- existingTimestampsResult.value.map((t) => t.toString()),
175
+ existingTimestampsResult.map((t) => t.toString()),
193
176
  );
194
177
  const newMessages = filterArray(
195
178
  messagesWithTimestampBytes,
@@ -197,33 +180,40 @@ export const createRelaySqliteStorage =
197
180
  );
198
181
 
199
182
  // Nothing to write
200
- if (!isNonEmptyReadonlyArray(newMessages)) {
183
+ if (!isNonEmptyArray(newMessages)) {
201
184
  return ok();
202
185
  }
203
186
 
204
- const usage = getOwnerUsage(deps)(
205
- ownerIdBytes,
206
- firstInArray(newMessages).timestamp,
187
+ const usage = getOk(
188
+ getOwnerUsage(deps)(
189
+ ownerIdBytes,
190
+ firstInArray(newMessages).timestamp,
191
+ ),
207
192
  );
208
- if (!usage.ok) return usage;
209
-
210
- const { storedBytes } = usage.value;
211
193
 
212
194
  const incomingBytes = newMessages.reduce(
213
195
  (sum, m) => sum + m.change.length,
214
196
  0,
215
197
  );
216
198
  const newStoredBytes = PositiveInt.orThrow(
217
- (storedBytes ?? 0) + incomingBytes,
199
+ (usage.storedBytes ?? 0) + incomingBytes,
218
200
  );
219
201
 
220
- const result = config.isOwnerWithinQuota(ownerId, newStoredBytes);
221
- const isWithinQuota = isAsync(result) ? await result : result;
202
+ const quotaResult = config.isOwnerWithinQuota(
203
+ ownerId,
204
+ newStoredBytes,
205
+ );
206
+ const isWithinQuota = isPromiseLike(quotaResult)
207
+ ? await quotaResult
208
+ : quotaResult;
222
209
  if (!isWithinQuota) {
223
- return err({ type: "StorageQuotaError", ownerId });
210
+ return err<StorageQuotaError>({
211
+ type: "StorageQuotaError",
212
+ ownerId,
213
+ });
224
214
  }
225
215
 
226
- let { firstTimestamp, lastTimestamp } = usage.value;
216
+ let { firstTimestamp, lastTimestamp } = usage;
227
217
 
228
218
  return deps.sqlite.transaction(() => {
229
219
  for (const { timestamp, change } of newMessages) {
@@ -235,42 +225,35 @@ export const createRelaySqliteStorage =
235
225
  lastTimestamp,
236
226
  );
237
227
 
238
- {
239
- const result = sqliteStorageBase.insertTimestamp(
240
- ownerIdBytes,
241
- timestamp,
242
- strategy,
243
- );
244
- if (!result.ok) return result;
245
- }
246
-
247
- {
248
- const result = deps.sqlite.exec(sql`
249
- insert into evolu_message ("ownerId", "timestamp", "change")
250
- values (${ownerIdBytes}, ${timestamp}, ${change})
251
- on conflict do nothing;
252
- `);
253
- if (!result.ok) return result;
254
- }
228
+ sqliteStorageBase.insertTimestamp(
229
+ ownerIdBytes,
230
+ timestamp,
231
+ strategy,
232
+ );
233
+
234
+ deps.sqlite.exec(sql`
235
+ insert into evolu_message
236
+ ("ownerId", "timestamp", "change")
237
+ values (${ownerIdBytes}, ${timestamp}, ${change})
238
+ on conflict do nothing;
239
+ `);
255
240
  }
256
241
 
257
- return updateOwnerUsage(deps)(
242
+ updateOwnerUsage(deps)(
258
243
  ownerIdBytes,
259
244
  newStoredBytes,
260
245
  firstTimestamp,
261
246
  lastTimestamp,
262
247
  );
248
+
249
+ return ok();
263
250
  });
264
- })();
265
-
266
- if (!result.ok && result.error.type !== "AbortError") {
267
- switch (result.error.type) {
268
- case "SqliteError":
269
- config.onStorageError(result.error);
270
- return err({ type: "StorageWriteError", ownerId });
271
- case "StorageQuotaError":
272
- return err({ type: "StorageQuotaError", ownerId });
273
- }
251
+ }),
252
+ );
253
+
254
+ if (!result.ok) {
255
+ if (result.error.type === "AbortError") return ok();
256
+ return result;
274
257
  }
275
258
 
276
259
  return ok();
@@ -284,51 +267,35 @@ export const createRelaySqliteStorage =
284
267
  from evolu_message
285
268
  where "ownerId" = ${ownerId} and "timestamp" = ${timestamp};
286
269
  `);
287
- if (!result.ok) {
288
- config.onStorageError(result.error);
289
- return null;
290
- }
291
270
 
292
- return result.value.rows[0]?.change;
271
+ const row = result.rows[0];
272
+ assert(row, "Every timestamp must have a change");
273
+ return row.change;
293
274
  },
294
275
 
295
276
  deleteOwner: (ownerId) => {
296
- const transactionResult = deps.sqlite.transaction(() => {
297
- const deleteWriteKey = deps.sqlite.exec(sql`
277
+ deps.sqlite.transaction(() => {
278
+ deps.sqlite.exec(sql`
298
279
  delete from evolu_writeKey where ownerId = ${ownerId};
299
280
  `);
300
- if (!deleteWriteKey.ok) return deleteWriteKey;
301
281
 
302
- const deleteMessages = deps.sqlite.exec(sql`
282
+ deps.sqlite.exec(sql`
303
283
  delete from evolu_message where ownerId = ${ownerId};
304
284
  `);
305
- if (!deleteMessages.ok) return deleteMessages;
306
285
 
307
- const deleteUsage = deps.sqlite.exec(sql`
286
+ deps.sqlite.exec(sql`
308
287
  delete from evolu_usage where ownerId = ${ownerId};
309
288
  `);
310
- if (!deleteUsage.ok) return deleteUsage;
311
289
 
312
- const deleteBaseOwner = sqliteStorageBase.deleteOwner(ownerId);
313
- if (!deleteBaseOwner) return err(null);
290
+ sqliteStorageBase.deleteOwner(ownerId);
314
291
 
315
292
  return ok();
316
293
  });
317
-
318
- if (!transactionResult.ok) {
319
- if (transactionResult.error)
320
- config.onStorageError(transactionResult.error);
321
- return false;
322
- }
323
-
324
- return true;
325
294
  },
326
295
  };
327
296
  };
328
297
 
329
- export const createRelayStorageTables = (
330
- deps: SqliteDep,
331
- ): Result<void, SqliteError> => {
298
+ export const createRelayStorageTables = (deps: SqliteDep): void => {
332
299
  for (const query of [
333
300
  sql`
334
301
  create table evolu_writeKey (
@@ -349,134 +316,6 @@ export const createRelayStorageTables = (
349
316
  strict;
350
317
  `,
351
318
  ]) {
352
- const result = deps.sqlite.exec(query);
353
- if (!result.ok) return result;
319
+ deps.sqlite.exec(query);
354
320
  }
355
-
356
- return ok();
357
321
  };
358
-
359
- export interface RelayLogger {
360
- readonly started: (enableLogging: boolean, port: number) => void;
361
- readonly storageError: (error: unknown) => void;
362
- readonly upgradeSocketError: (error: Error) => void;
363
- readonly invalidOrMissingOwnerIdInUrl: (url: string | undefined) => void;
364
- readonly unauthorizedOwner: (ownerId: OwnerId) => void;
365
- readonly connectionEstablished: (totalConnectionCount: number) => void;
366
- readonly connectionWebSocketError: (error: Error) => void;
367
- readonly relayOptionSubscribe: (
368
- ownerId: OwnerId,
369
- getSubscriberCount: LazyValue<number>,
370
- ) => void;
371
- readonly relayOptionUnsubscribe: (
372
- ownerId: OwnerId,
373
- getSubscriberCount: LazyValue<number>,
374
- ) => void;
375
- readonly relayOptionBroadcast: (
376
- ownerId: OwnerId,
377
- broadcastCount: number,
378
- subscriberCount: number,
379
- ) => void;
380
- readonly messageLength: (messageLength: number) => void;
381
- readonly applyProtocolMessageAsRelayError: (
382
- error: ProtocolInvalidDataError,
383
- ) => void;
384
- readonly responseLength: (responseLength: number) => void;
385
- readonly applyProtocolMessageAsRelayUnknownError: (error: unknown) => void;
386
- readonly connectionClosed: (totalConnectionCount: number) => void;
387
- readonly shuttingDown: () => void;
388
- readonly webSocketServerDisposed: () => void;
389
- readonly httpServerDisposed: () => void;
390
- }
391
-
392
- export const createRelayLogger = (deps: ConsoleDep): RelayLogger => ({
393
- started: (enableLogging, port) => {
394
- deps.console.enabled = true;
395
- deps.console.log(`Evolu Relay started on port ${port}`);
396
- deps.console.enabled = enableLogging;
397
- },
398
-
399
- storageError: (error) => {
400
- deps.console.error("[relay]", "storage", error);
401
- },
402
-
403
- upgradeSocketError: (error) => {
404
- deps.console.warn("[relay]", "socket error", { error });
405
- },
406
-
407
- invalidOrMissingOwnerIdInUrl: (url) => {
408
- deps.console.warn("[relay]", "invalid or missing ownerId in URL", { url });
409
- },
410
-
411
- unauthorizedOwner: (ownerId) => {
412
- deps.console.warn("[relay]", "unauthorized owner", { ownerId });
413
- },
414
-
415
- connectionEstablished: (totalConnectionCount) => {
416
- deps.console.log("[relay]", "connection", { totalConnectionCount });
417
- },
418
-
419
- connectionWebSocketError: (error) => {
420
- deps.console.error("[relay]", "error", { error });
421
- },
422
-
423
- relayOptionSubscribe: (ownerId, getSubscriberCount) => {
424
- if (deps.console.enabled)
425
- deps.console.log("[relay]", "subscribe", {
426
- ownerId,
427
- subscriberCount: getSubscriberCount(),
428
- });
429
- },
430
-
431
- relayOptionUnsubscribe: (ownerId, getSubscriberCount) => {
432
- if (deps.console.enabled)
433
- deps.console.log("[relay]", "unsubscribe", {
434
- ownerId,
435
- subscriberCount: getSubscriberCount(),
436
- });
437
- },
438
-
439
- relayOptionBroadcast: (ownerId, broadcastCount, totalSubscribers) => {
440
- deps.console.log("[relay]", "broadcast", {
441
- ownerId,
442
- broadcastCount,
443
- totalSubscribers,
444
- });
445
- },
446
-
447
- messageLength: (messageLength) => {
448
- deps.console.log("[relay]", "on message", { messageLength });
449
- },
450
-
451
- applyProtocolMessageAsRelayError: (error) => {
452
- deps.console.error("[relay]", "applyProtocolMessageAsRelay", error);
453
- },
454
-
455
- responseLength: (responseLength) => {
456
- deps.console.log("[relay]", "responseLength", { responseLength });
457
- },
458
-
459
- applyProtocolMessageAsRelayUnknownError: (error) => {
460
- deps.console.error(
461
- "[relay]",
462
- "applyProtocolMessageAsRelayUnknownError",
463
- error,
464
- );
465
- },
466
-
467
- connectionClosed: (totalConnectionCount) => {
468
- deps.console.log("[relay]", "close", { totalConnectionCount });
469
- },
470
-
471
- shuttingDown: () => {
472
- deps.console.log("Shutting down Evolu Relay...");
473
- },
474
-
475
- webSocketServerDisposed: () => {
476
- deps.console.log("Evolu Relay WebSocketServer disposed");
477
- },
478
-
479
- httpServerDisposed: () => {
480
- deps.console.log("Evolu Relay HTTP server disposed");
481
- },
482
- });