@evolu/common 6.0.1-preview.8 → 7.0.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 (184) hide show
  1. package/README.md +2 -2
  2. package/dist/src/Array.d.ts +256 -12
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +215 -9
  5. package/dist/src/Assert.d.ts +0 -13
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +0 -15
  8. package/dist/src/Brand.d.ts +75 -0
  9. package/dist/src/Brand.d.ts.map +1 -0
  10. package/dist/src/Brand.js +1 -0
  11. package/dist/src/Buffer.d.ts +1 -1
  12. package/dist/src/Buffer.d.ts.map +1 -1
  13. package/dist/src/Buffer.js +8 -7
  14. package/dist/src/Cache.d.ts +44 -0
  15. package/dist/src/Cache.d.ts.map +1 -0
  16. package/dist/src/Cache.js +52 -0
  17. package/dist/src/Callbacks.d.ts +45 -12
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Callbacks.js +14 -7
  20. package/dist/src/Console.d.ts +31 -6
  21. package/dist/src/Console.d.ts.map +1 -1
  22. package/dist/src/Console.js +72 -9
  23. package/dist/src/Crypto.d.ts +56 -42
  24. package/dist/src/Crypto.d.ts.map +1 -1
  25. package/dist/src/Crypto.js +40 -53
  26. package/dist/src/Evolu/Db.d.ts +162 -74
  27. package/dist/src/Evolu/Db.d.ts.map +1 -1
  28. package/dist/src/Evolu/Db.js +284 -702
  29. package/dist/src/Evolu/Diff.d.ts +3 -3
  30. package/dist/src/Evolu/Diff.d.ts.map +1 -1
  31. package/dist/src/Evolu/Diff.js +7 -5
  32. package/dist/src/Evolu/Evolu.d.ts +214 -134
  33. package/dist/src/Evolu/Evolu.d.ts.map +1 -1
  34. package/dist/src/Evolu/Evolu.js +189 -180
  35. package/dist/src/Evolu/Internal.d.ts +0 -2
  36. package/dist/src/Evolu/Internal.d.ts.map +1 -1
  37. package/dist/src/Evolu/Internal.js +0 -2
  38. package/dist/src/Evolu/LocalAuth.d.ts +150 -0
  39. package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
  40. package/dist/src/Evolu/LocalAuth.js +174 -0
  41. package/dist/src/Evolu/Owner.d.ts +273 -120
  42. package/dist/src/Evolu/Owner.d.ts.map +1 -1
  43. package/dist/src/Evolu/Owner.js +130 -104
  44. package/dist/src/Evolu/Platform.d.ts +9 -7
  45. package/dist/src/Evolu/Platform.d.ts.map +1 -1
  46. package/dist/src/Evolu/Protocol.d.ts +268 -240
  47. package/dist/src/Evolu/Protocol.d.ts.map +1 -1
  48. package/dist/src/Evolu/Protocol.js +600 -454
  49. package/dist/src/Evolu/Public.d.ts +6 -8
  50. package/dist/src/Evolu/Public.d.ts.map +1 -1
  51. package/dist/src/Evolu/Public.js +2 -3
  52. package/dist/src/Evolu/PublicKysely.d.ts.map +1 -1
  53. package/dist/src/Evolu/PublicKysely.js +3 -4
  54. package/dist/src/Evolu/Query.d.ts +2 -1
  55. package/dist/src/Evolu/Query.d.ts.map +1 -1
  56. package/dist/src/Evolu/Query.js +1 -1
  57. package/dist/src/Evolu/Relay.d.ts +91 -8
  58. package/dist/src/Evolu/Relay.d.ts.map +1 -1
  59. package/dist/src/Evolu/Relay.js +214 -88
  60. package/dist/src/Evolu/Schema.d.ts +125 -47
  61. package/dist/src/Evolu/Schema.d.ts.map +1 -1
  62. package/dist/src/Evolu/Schema.js +175 -31
  63. package/dist/src/Evolu/Storage.d.ts +249 -27
  64. package/dist/src/Evolu/Storage.d.ts.map +1 -1
  65. package/dist/src/Evolu/Storage.js +198 -92
  66. package/dist/src/Evolu/Sync.d.ts +68 -13
  67. package/dist/src/Evolu/Sync.d.ts.map +1 -1
  68. package/dist/src/Evolu/Sync.js +469 -20
  69. package/dist/src/Evolu/Timestamp.d.ts +83 -30
  70. package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
  71. package/dist/src/Evolu/Timestamp.js +79 -34
  72. package/dist/src/Identicon.d.ts +35 -0
  73. package/dist/src/Identicon.d.ts.map +1 -0
  74. package/dist/src/Identicon.js +143 -0
  75. package/dist/src/Instances.d.ts +34 -0
  76. package/dist/src/Instances.d.ts.map +1 -0
  77. package/dist/src/Instances.js +44 -0
  78. package/dist/src/Number.d.ts +4 -3
  79. package/dist/src/Number.d.ts.map +1 -1
  80. package/dist/src/Number.js +5 -4
  81. package/dist/src/Object.d.ts +10 -4
  82. package/dist/src/Object.d.ts.map +1 -1
  83. package/dist/src/Object.js +9 -3
  84. package/dist/src/Platform.d.ts +20 -0
  85. package/dist/src/Platform.d.ts.map +1 -0
  86. package/dist/src/Platform.js +22 -0
  87. package/dist/src/Random.d.ts +3 -2
  88. package/dist/src/Random.d.ts.map +1 -1
  89. package/dist/src/Relation.d.ts +79 -0
  90. package/dist/src/Relation.d.ts.map +1 -0
  91. package/dist/src/Relation.js +127 -0
  92. package/dist/src/Resources.d.ts +118 -0
  93. package/dist/src/Resources.d.ts.map +1 -0
  94. package/dist/src/Resources.js +197 -0
  95. package/dist/src/Result.d.ts +184 -52
  96. package/dist/src/Result.d.ts.map +1 -1
  97. package/dist/src/Result.js +30 -241
  98. package/dist/src/Skiplist.js +2 -1
  99. package/dist/src/Sqlite.d.ts +89 -5
  100. package/dist/src/Sqlite.d.ts.map +1 -1
  101. package/dist/src/Sqlite.js +136 -9
  102. package/dist/src/Task.d.ts +586 -0
  103. package/dist/src/Task.d.ts.map +1 -0
  104. package/dist/src/Task.js +469 -0
  105. package/dist/src/Time.d.ts +66 -1
  106. package/dist/src/Time.d.ts.map +1 -1
  107. package/dist/src/Time.js +99 -5
  108. package/dist/src/Type.d.ts +676 -343
  109. package/dist/src/Type.d.ts.map +1 -1
  110. package/dist/src/Type.js +718 -467
  111. package/dist/src/Types.d.ts +1 -75
  112. package/dist/src/Types.d.ts.map +1 -1
  113. package/dist/src/WebSocket.d.ts +5 -2
  114. package/dist/src/WebSocket.d.ts.map +1 -1
  115. package/dist/src/WebSocket.js +12 -18
  116. package/dist/src/Worker.d.ts +39 -11
  117. package/dist/src/Worker.d.ts.map +1 -1
  118. package/dist/src/Worker.js +22 -4
  119. package/dist/src/index.d.ts +8 -3
  120. package/dist/src/index.d.ts.map +1 -1
  121. package/dist/src/index.js +8 -3
  122. package/package.json +15 -14
  123. package/src/Array.ts +301 -19
  124. package/src/Assert.ts +0 -21
  125. package/src/Brand.ts +75 -0
  126. package/src/Buffer.ts +7 -7
  127. package/src/Cache.ts +85 -0
  128. package/src/Callbacks.ts +62 -22
  129. package/src/Console.ts +91 -11
  130. package/src/Crypto.ts +94 -90
  131. package/src/Evolu/Db.ts +519 -1026
  132. package/src/Evolu/Diff.ts +7 -5
  133. package/src/Evolu/Evolu.ts +477 -361
  134. package/src/Evolu/Internal.ts +0 -2
  135. package/src/Evolu/LocalAuth.ts +463 -0
  136. package/src/Evolu/Owner.ts +355 -228
  137. package/src/Evolu/Platform.ts +9 -9
  138. package/src/Evolu/Protocol.ts +842 -742
  139. package/src/Evolu/Public.ts +7 -14
  140. package/src/Evolu/PublicKysely.ts +4 -5
  141. package/src/Evolu/Query.ts +3 -2
  142. package/src/Evolu/Relay.ts +406 -103
  143. package/src/Evolu/Schema.ts +323 -91
  144. package/src/Evolu/Storage.ts +559 -137
  145. package/src/Evolu/Sync.ts +819 -36
  146. package/src/Evolu/Timestamp.ts +90 -58
  147. package/src/Identicon.ts +197 -0
  148. package/src/Instances.ts +90 -0
  149. package/src/Number.ts +6 -10
  150. package/src/Object.ts +13 -5
  151. package/src/Platform.ts +26 -0
  152. package/src/Random.ts +3 -2
  153. package/src/Relation.ts +234 -0
  154. package/src/Resources.ts +367 -0
  155. package/src/Result.ts +191 -54
  156. package/src/Skiplist.ts +1 -1
  157. package/src/Sqlite.ts +152 -17
  158. package/src/Task.ts +901 -0
  159. package/src/Time.ts +180 -5
  160. package/src/Type.ts +1135 -730
  161. package/src/Types.ts +1 -77
  162. package/src/WebSocket.ts +27 -25
  163. package/src/Worker.ts +72 -23
  164. package/src/index.ts +8 -3
  165. package/dist/src/Evolu/Config.d.ts +0 -69
  166. package/dist/src/Evolu/Config.d.ts.map +0 -1
  167. package/dist/src/Evolu/Config.js +0 -9
  168. package/dist/src/Evolu/Kysely.d.ts +0 -6
  169. package/dist/src/Evolu/Kysely.d.ts.map +0 -1
  170. package/dist/src/Evolu/Kysely.js +0 -21
  171. package/dist/src/ManyToManyMap.d.ts +0 -26
  172. package/dist/src/ManyToManyMap.d.ts.map +0 -1
  173. package/dist/src/ManyToManyMap.js +0 -92
  174. package/dist/src/NanoId.d.ts +0 -27
  175. package/dist/src/NanoId.d.ts.map +0 -1
  176. package/dist/src/NanoId.js +0 -6
  177. package/dist/src/Promise.d.ts +0 -180
  178. package/dist/src/Promise.d.ts.map +0 -1
  179. package/dist/src/Promise.js +0 -176
  180. package/src/Evolu/Config.ts +0 -83
  181. package/src/Evolu/Kysely.ts +0 -38
  182. package/src/ManyToManyMap.ts +0 -140
  183. package/src/NanoId.ts +0 -39
  184. package/src/Promise.ts +0 -295
@@ -4,15 +4,13 @@
4
4
  * @module
5
5
  */
6
6
  export { createEvolu } from "./Evolu.js";
7
- export type { Evolu, EvoluConfigWithInitialData, EvoluDeps, EvoluError, } from "./Evolu.js";
7
+ export type { Evolu, EvoluConfig, EvoluDeps, EvoluError } from "./Evolu.js";
8
8
  export * from "./Owner.js";
9
- export { binaryIdToId, idToBinaryId } from "./Protocol.js";
10
- export type { BinaryId } from "./Protocol.js";
9
+ export * from "./LocalAuth.js";
11
10
  export * as kysely from "./PublicKysely.js";
12
11
  export type { InferRow, Query, QueryRows, Row } from "./Query.js";
13
- export { formatValidMutationSizeError } from "./Schema.js";
14
- export type { EvoluSchema, ValidMutationSizeError } from "./Schema.js";
15
- export type { NetworkError, PaymentRequiredError, ServerError, SyncState, SyncStateInitial, SyncStateIsNotSynced, SyncStateIsSynced, SyncStateIsSyncing, } from "./Sync.js";
16
- export { binaryTimestampToTimestamp, Timestamp, timestampToBinaryTimestamp, } from "./Timestamp.js";
17
- export type { BinaryTimestamp, TimestampCounterOverflowError, TimestampDriftError, TimestampDuplicateNodeError, TimestampError, TimestampTimeOutOfRangeError, } from "./Timestamp.js";
12
+ export type { EvoluSchema } from "./Schema.js";
13
+ export type { NetworkError, PaymentRequiredError, ServerError, SyncOwner, SyncState, SyncStateInitial, SyncStateIsNotSynced, SyncStateIsSynced, SyncStateIsSyncing, } from "./Sync.js";
14
+ export { Timestamp, timestampBytesToTimestamp, timestampToTimestampBytes, } from "./Timestamp.js";
15
+ export type { TimestampBytes, TimestampCounterOverflowError, TimestampDriftError, TimestampError, TimestampTimeOutOfRangeError, } from "./Timestamp.js";
18
16
  //# sourceMappingURL=Public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Public.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Public.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EACV,KAAK,EACL,0BAA0B,EAC1B,SAAS,EACT,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAC3D,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,0BAA0B,EAC1B,SAAS,EACT,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,eAAe,EACf,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,cAAc,EACd,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"Public.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Public.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5E,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,SAAS,EACT,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,6BAA6B,EAC7B,mBAAmB,EACnB,cAAc,EACd,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC"}
@@ -5,7 +5,6 @@
5
5
  */
6
6
  export { createEvolu } from "./Evolu.js";
7
7
  export * from "./Owner.js";
8
- export { binaryIdToId, idToBinaryId } from "./Protocol.js";
8
+ export * from "./LocalAuth.js";
9
9
  export * as kysely from "./PublicKysely.js";
10
- export { formatValidMutationSizeError } from "./Schema.js";
11
- export { binaryTimestampToTimestamp, Timestamp, timestampToBinaryTimestamp, } from "./Timestamp.js";
10
+ export { Timestamp, timestampBytesToTimestamp, timestampToTimestampBytes, } from "./Timestamp.js";
@@ -1 +1 @@
1
- {"version":3,"file":"PublicKysely.d.ts","sourceRoot":"","sources":["../../../src/Evolu/PublicKysely.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAGnB,UAAU,EAGV,UAAU,EAEV,eAAe,EACf,QAAQ,EAIT,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,YAAY,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAGH,wBAAgB,aAAa,CAAC,CAAC,EAC3B,IAAI,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACpC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAIhC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAGH,wBAAgB,cAAc,CAAC,CAAC,EAC5B,IAAI,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACpC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAIhC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAGH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,EACzE,GAAG,EAAE,CAAC,GACL,UAAU,CACX,QAAQ,CAAC;KACN,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CAC7D,CAAC,CACH,CAIA;AAEH,UAAU,4BAA4B,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IACtE,IAAI,oBAAoB,IAAI,IAAI,CAAC;IACjC,eAAe,IAAI,eAAe,CAAC;CACpC;AAeD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAoBrC"}
1
+ {"version":3,"file":"PublicKysely.d.ts","sourceRoot":"","sources":["../../../src/Evolu/PublicKysely.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAGnB,UAAU,EAGV,UAAU,EAEV,eAAe,EACf,QAAQ,EAIT,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,YAAY,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAGH,wBAAgB,aAAa,CAAC,CAAC,EAC3B,IAAI,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACpC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAIhC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAGH,wBAAgB,cAAc,CAAC,CAAC,EAC5B,IAAI,EAAE,4BAA4B,CAAC,CAAC,CAAC,GACpC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAIhC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAGH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,EACzE,GAAG,EAAE,CAAC,GACL,UAAU,CACX,QAAQ,CAAC;KACN,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CAC7D,CAAC,CACH,CAIA;AAEH,UAAU,4BAA4B,CAAC,CAAC,CAAE,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IACtE,IAAI,oBAAoB,IAAI,IAAI,CAAC;IACjC,eAAe,IAAI,eAAe,CAAC;CACpC;AAeD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAmBrC"}
@@ -48,7 +48,7 @@ export { sql } from "kysely";
48
48
  * from "person"
49
49
  * ```
50
50
  */
51
- // SQL formatting breaks generated SQL for some reason.
51
+ // Kysely expects strict AST.
52
52
  // prettier-ignore
53
53
  export function jsonArrayFrom(expr) {
54
54
  return sql `(select ${sql.lit(kyselyJsonIdentifier)} || coalesce(json_group_array(json_object(${sql.join(getSqliteJsonObjectArgs(expr.toOperationNode(), 'agg'))})), '[]') from ${expr} as agg)`;
@@ -100,7 +100,7 @@ export function jsonArrayFrom(expr) {
100
100
  * from "person";
101
101
  * ```
102
102
  */
103
- // SQL formatting breaks generated SQL for some reason.
103
+ // Kysely expects strict AST.
104
104
  // prettier-ignore
105
105
  export function jsonObjectFrom(expr) {
106
106
  return sql `(select ${sql.lit(kyselyJsonIdentifier)} || json_object(${sql.join(getSqliteJsonObjectArgs(expr.toOperationNode(), 'obj'))}) from ${expr} as obj)`;
@@ -145,7 +145,7 @@ export function jsonObjectFrom(expr) {
145
145
  * from "person"
146
146
  * ```
147
147
  */
148
- // SQL formatting breaks generated SQL for some reason.
148
+ // Kysely expects strict AST.
149
149
  // prettier-ignore
150
150
  export function jsonBuildObject(obj) {
151
151
  return sql `${sql.lit(kyselyJsonIdentifier)} || json_object(${sql.join(Object.keys(obj).flatMap((k) => [sql.lit(k), obj[k]]))})`;
@@ -160,7 +160,6 @@ function getSqliteJsonObjectArgs(node, table) {
160
160
  }
161
161
  export function getJsonObjectArgs(node, table) {
162
162
  const args = [];
163
- args.push(kyselyJsonIdentifier, kyselyJsonIdentifier);
164
163
  for (const { selection: s } of node.selections ?? []) {
165
164
  if (ReferenceNode.is(s) && ColumnNode.is(s.column)) {
166
165
  args.push(colName(s.column.column.name), colRef(table, s.column.column.name));
@@ -1,6 +1,7 @@
1
+ import { Brand } from "../Brand.js";
1
2
  import { SqliteQuery, SqliteRow, SqliteValue } from "../Sqlite.js";
2
3
  import { Store, StoreSubscribe } from "../Store.js";
3
- import { Brand, Simplify } from "../Types.js";
4
+ import { Simplify } from "../Types.js";
4
5
  /**
5
6
  * A type-safe SQL query.
6
7
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Query.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,WAAW,EAEX,SAAS,EACT,WAAW,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,MAAM,GAC7C,KAAK,CAAC,OAAO,CAAC,GAAG;IACf;;;;;;;;;;;OAWG;IACH,GAAG,EAAE,CAAC,CAAC;CACR,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,GAAG,EAAE,OAAO,WAAW,KAAG,KAAK,CAAC,CAAC,CAYzE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,GAAG,EAC5C,OAAO,KAAK,CAAC,CAAC,CAAC,KACd,WAoBF,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAE7E,MAAM,WAAW,GAAG;IAClB,QAAQ,EAAE,GAAG,EAAE,MAAM,GACjB,WAAW,GACX,GAAG,GACH,aAAa,CAAC,GAAG,CAAC,CAAC;CACxB;AAGD,eAAO,MAAM,SAAS,EAAE,aAAa,CAAC,GAAG,CAAM,CAAC;AAEhD,gCAAgC;AAChC,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,aAAa,CACxD,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CACtB,CAAC;AAEF,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnE,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,OAAO,IAAI;KACjD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK;CACnE,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,SAAS,OAAO,IAAI;KACzD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CAC5E,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,CACZ,WAAW,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,KACnE,IAAI,CAAC;IACV,QAAQ,CAAC,GAAG,EAAE,MAAM,YAAY,CAAC;CAClC;AAED,eAAO,MAAM,oBAAoB,QAAO,cAWvC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,cAAc,CAAC;IAE5C,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAE/B,GAAG,EAAE,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;CACjC;AAED,eAAO,MAAM,uBAAuB,GAClC,WAAW,KAAK,CAAC,YAAY,CAAC,KAC7B,iBAwBF,CAAC"}
1
+ {"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,OAAO,EAEL,WAAW,EAEX,SAAS,EACT,WAAW,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,MAAM,GAC7C,KAAK,CAAC,OAAO,CAAC,GAAG;IACf;;;;;;;;;;;OAWG;IACH,GAAG,EAAE,CAAC,CAAC;CACR,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,GAAG,EAAE,OAAO,WAAW,KAAG,KAAK,CAAC,CAAC,CAYzE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,GAAG,EAC5C,OAAO,KAAK,CAAC,CAAC,CAAC,KACd,WAoBF,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAE7E,MAAM,WAAW,GAAG;IAClB,QAAQ,EAAE,GAAG,EAAE,MAAM,GACjB,WAAW,GACX,GAAG,GACH,aAAa,CAAC,GAAG,CAAC,CAAC;CACxB;AAGD,eAAO,MAAM,SAAS,EAAE,aAAa,CAAC,GAAG,CAAM,CAAC;AAEhD,gCAAgC;AAChC,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,aAAa,CACxD,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CACtB,CAAC;AAEF,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnE,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,OAAO,IAAI;KACjD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK;CACnE,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,SAAS,OAAO,IAAI;KACzD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CAC5E,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,CACZ,WAAW,EAAE,aAAa,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,KACnE,IAAI,CAAC;IACV,QAAQ,CAAC,GAAG,EAAE,MAAM,YAAY,CAAC;CAClC;AAED,eAAO,MAAM,oBAAoB,QAAO,cAWvC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,cAAc,CAAC;IAE5C,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAE/B,GAAG,EAAE,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;CACjC;AAED,eAAO,MAAM,uBAAuB,GAClC,WAAW,KAAK,CAAC,YAAY,CAAC,KAC7B,iBAwBF,CAAC"}
@@ -9,7 +9,7 @@ export const serializeQuery = (query) => {
9
9
  ? ["b", bytesToHex(v)]
10
10
  : ["j", v]);
11
11
  const options = query.options
12
- ? objectToEntries(query.options).sort(([a], [b]) => a.localeCompare(b))
12
+ ? objectToEntries(query.options).toSorted(([a], [b]) => a.localeCompare(b))
13
13
  : [];
14
14
  return JSON.stringify([query.sql, params, options]);
15
15
  };
@@ -1,15 +1,98 @@
1
- import { ConsoleConfig } from "../Console.js";
1
+ import { ConsoleConfig, ConsoleDep } from "../Console.js";
2
2
  import { TimingSafeEqualDep } from "../Crypto.js";
3
+ import { LazyValue } from "../Function.js";
3
4
  import { Result } from "../Result.js";
4
- import { SqliteError } from "../Sqlite.js";
5
+ import { SqliteDep, SqliteError } from "../Sqlite.js";
6
+ import { MaybeAsync } from "../Task.js";
5
7
  import { SimpleName } from "../Type.js";
6
- import { Storage } from "./Protocol.js";
7
- import { CreateSqliteStorageBaseOptions, SqliteStorageDeps } from "./Storage.js";
8
+ import { OwnerId } from "./Owner.js";
9
+ import { ProtocolInvalidDataError } from "./Protocol.js";
10
+ import { CreateBaseSqliteStorageConfig, SqliteStorageDeps, Storage, StorageConfig } from "./Storage.js";
11
+ export interface RelayConfig extends ConsoleConfig, StorageConfig {
12
+ /**
13
+ * The relay name.
14
+ *
15
+ * Implementations can use this for identification purposes (e.g., database
16
+ * file name, logging).
17
+ */
18
+ readonly name?: SimpleName;
19
+ /**
20
+ * Optional callback to check if an {@link OwnerId} is allowed to access the
21
+ * relay. If this callback is not provided, all owners are allowed.
22
+ *
23
+ * The callback receives the {@link OwnerId} and returns a {@link MaybeAsync}
24
+ * boolean: `true` to allow access, or `false` to deny.
25
+ *
26
+ * The callback can be synchronous (for SQLite or in-memory checks) or
27
+ * asynchronous (for calling remote APIs).
28
+ *
29
+ * The callback returns a boolean rather than an error type because error
30
+ * handling and logging are the responsibility of the callback
31
+ * implementation.
32
+ *
33
+ * OwnerId is used rather than short-lived tokens because this only controls
34
+ * relay access, not write permissions. Since all data is encrypted on the
35
+ * relay, OwnerId exposure is safe.
36
+ *
37
+ * Owners specify which relays to connect to via {@link OwnerTransport}. In
38
+ * WebSocket-based implementations, this check occurs before accepting the
39
+ * connection, with the OwnerId typically extracted from the URL Path (e.g.,
40
+ * `ws://localhost:4000/<ownerId>`). The relay requires the URL to be in the
41
+ * correct format for OwnerId extraction.
42
+ *
43
+ * ### Example
44
+ *
45
+ * ```ts
46
+ * // Client
47
+ * const transport = createOwnerWebSocketTransport({
48
+ * url: "wss://relay.evolu.dev",
49
+ * ownerId: owner.id,
50
+ * });
51
+ *
52
+ * const evolu = createEvolu(deps)(Schema, {
53
+ * transports: [transport],
54
+ * });
55
+ *
56
+ *
57
+ * // Relay
58
+ * isOwnerAllowed: (ownerId) =>
59
+ * Promise.resolve(ownerId === "6jy_2F4RT5qqeLgJ14_dnQ"),
60
+ * ```
61
+ */
62
+ readonly isOwnerAllowed?: (ownerId: OwnerId) => MaybeAsync<boolean>;
63
+ }
64
+ /**
65
+ * A completely interchangeable server for syncing and backing up encrypted data
66
+ * between Evolu clients.
67
+ *
68
+ * Unlike traditional servers, relays are blind by design—they transmit
69
+ * encrypted data without understanding its shape or meaning. This enables true
70
+ * decentralization and infinite horizontal scalability with minimal
71
+ * infrastructure.
72
+ */
8
73
  export interface Relay extends Disposable {
9
74
  }
10
- export interface RelayConfig extends ConsoleConfig {
11
- readonly name?: SimpleName;
75
+ export declare const createRelaySqliteStorage: (deps: SqliteStorageDeps & TimingSafeEqualDep) => (config: CreateBaseSqliteStorageConfig) => Storage;
76
+ export declare const createRelayStorageTables: (deps: SqliteDep) => Result<void, SqliteError>;
77
+ export interface RelayLogger {
78
+ readonly started: (enableLogging: boolean, port: number) => void;
79
+ readonly storageError: (error: unknown) => void;
80
+ readonly upgradeSocketError: (error: Error) => void;
81
+ readonly invalidOrMissingOwnerIdInUrl: (url: string | undefined) => void;
82
+ readonly unauthorizedOwner: (ownerId: OwnerId) => void;
83
+ readonly connectionEstablished: (totalConnectionCount: number) => void;
84
+ readonly connectionWebSocketError: (error: Error) => void;
85
+ readonly relayOptionSubscribe: (ownerId: OwnerId, getSubscriberCount: LazyValue<number>) => void;
86
+ readonly relayOptionUnsubscribe: (ownerId: OwnerId, getSubscriberCount: LazyValue<number>) => void;
87
+ readonly relayOptionBroadcast: (ownerId: OwnerId, broadcastCount: number, subscriberCount: number) => void;
88
+ readonly messageLength: (messageLength: number) => void;
89
+ readonly applyProtocolMessageAsRelayError: (error: ProtocolInvalidDataError) => void;
90
+ readonly responseLength: (responseLength: number) => void;
91
+ readonly applyProtocolMessageAsRelayUnknownError: (error: unknown) => void;
92
+ readonly connectionClosed: (totalConnectionCount: number) => void;
93
+ readonly shuttingDown: () => void;
94
+ readonly webSocketServerDisposed: () => void;
95
+ readonly httpServerDisposed: () => void;
12
96
  }
13
- export type RelaySqliteStorageDeps = SqliteStorageDeps & TimingSafeEqualDep;
14
- export declare const createRelayStorage: (deps: RelaySqliteStorageDeps) => (options: CreateSqliteStorageBaseOptions) => Result<Storage, SqliteError>;
97
+ export declare const createRelayLogger: (deps: ConsoleDep) => RelayLogger;
15
98
  //# sourceMappingURL=Relay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAW,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAO,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAqB,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAEL,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAGtB,MAAM,WAAW,KAAM,SAAQ,UAAU;CAAG;AAE5C,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAE5E,eAAO,MAAM,kBAAkB,GAC5B,MAAM,sBAAsB,MAC5B,SAAS,8BAA8B,KAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CA6JrE,CAAC"}
1
+ {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"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"}
@@ -1,39 +1,26 @@
1
- import { isNonEmptyReadonlyArray } from "../Array.js";
1
+ import { filterArray, firstInArray, isNonEmptyReadonlyArray, mapArray, } from "../Array.js";
2
+ import { createInstances } from "../Instances.js";
2
3
  import { err, ok } from "../Result.js";
3
4
  import { sql } from "../Sqlite.js";
4
- import { createSqliteStorageBase, } from "./Storage.js";
5
- import { timestampToBinaryTimestamp } from "./Timestamp.js";
6
- export const createRelayStorage = (deps) => (options) => {
7
- const sqliteStorageBase = createSqliteStorageBase(deps)(options);
8
- if (!sqliteStorageBase.ok)
9
- return sqliteStorageBase;
10
- for (const query of [
11
- sql `
12
- create table if not exists evolu_writeKey (
13
- "ownerId" blob not null,
14
- "writeKey" blob not null,
15
- primary key ("ownerId")
16
- )
17
- strict;
18
- `,
19
- sql `
20
- create table if not exists evolu_message (
21
- "ownerId" blob not null,
22
- "timestamp" blob not null,
23
- "change" blob not null,
24
- primary key ("ownerId", "timestamp")
25
- )
26
- strict;
27
- `,
28
- ]) {
29
- const result = deps.sqlite.exec(query);
30
- if (!result.ok)
31
- return result;
32
- }
33
- return ok({
34
- ...sqliteStorageBase.value,
5
+ import { createMutex, isAsync } from "../Task.js";
6
+ import { PositiveInt } from "../Type.js";
7
+ import { ownerIdBytesToOwnerId, } from "./Owner.js";
8
+ import { createBaseSqliteStorage, getOwnerUsage, getTimestampInsertStrategy, updateOwnerUsage, } from "./Storage.js";
9
+ import { timestampToTimestampBytes } from "./Timestamp.js";
10
+ export const createRelaySqliteStorage = (deps) => (config) => {
11
+ const sqliteStorageBase = createBaseSqliteStorage(deps)(config);
12
+ /**
13
+ * Mutex instances are cached per OwnerId to prevent concurrent writes for
14
+ * the same owner. Instances are never evicted, causing a memory leak
15
+ * proportional to unique owner count. However, per-instance overhead should
16
+ * be small. Monitor production memory usage to determine if
17
+ * eviction/cleanup is needed.
18
+ */
19
+ const ownerMutexes = createInstances();
20
+ return {
21
+ ...sqliteStorageBase,
35
22
  /**
36
- * Lazily authorizes the initiator's {@link WriteKey} for the given
23
+ * Lazily authorizes the initiator's {@link OwnerWriteKey} for the given
37
24
  * {@link OwnerId}.
38
25
  *
39
26
  * - If the {@link OwnerId} does not exist, it is created and associated with
@@ -43,27 +30,27 @@ export const createRelayStorage = (deps) => (options) => {
43
30
  */
44
31
  validateWriteKey: (ownerId, writeKey) => {
45
32
  const selectWriteKey = deps.sqlite.exec(sql `
46
- select writeKey
47
- from evolu_writeKey
48
- where ownerId = ${ownerId};
49
- `);
33
+ select writeKey
34
+ from evolu_writeKey
35
+ where ownerId = ${ownerId};
36
+ `);
50
37
  if (!selectWriteKey.ok) {
51
- options.onStorageError(selectWriteKey.error);
38
+ config.onStorageError(selectWriteKey.error);
52
39
  return false;
53
40
  }
54
41
  const { rows } = selectWriteKey.value;
55
- if (!isNonEmptyReadonlyArray(rows)) {
56
- const insertWriteKey = deps.sqlite.exec(sql `
57
- insert into evolu_writeKey (ownerId, writeKey)
58
- values (${ownerId}, ${writeKey});
59
- `);
60
- if (!insertWriteKey.ok) {
61
- options.onStorageError(insertWriteKey.error);
62
- return false;
63
- }
64
- return true;
42
+ if (isNonEmptyReadonlyArray(rows)) {
43
+ return deps.timingSafeEqual(rows[0].writeKey, writeKey);
65
44
  }
66
- return deps.timingSafeEqual(rows[0].writeKey, writeKey);
45
+ const insertWriteKey = deps.sqlite.exec(sql `
46
+ insert into evolu_writeKey (ownerId, writeKey)
47
+ values (${ownerId}, ${writeKey});
48
+ `);
49
+ if (!insertWriteKey.ok) {
50
+ config.onStorageError(insertWriteKey.error);
51
+ return false;
52
+ }
53
+ return true;
67
54
  },
68
55
  setWriteKey: (ownerId, writeKey) => {
69
56
  const upsertWriteKey = deps.sqlite.exec(sql `
@@ -73,37 +60,75 @@ export const createRelayStorage = (deps) => (options) => {
73
60
  set writeKey = excluded.writeKey;
74
61
  `);
75
62
  if (!upsertWriteKey.ok) {
76
- options.onStorageError(upsertWriteKey.error);
63
+ config.onStorageError(upsertWriteKey.error);
77
64
  return false;
78
65
  }
79
66
  return true;
80
67
  },
81
- writeMessages: (ownerId, messages) => {
82
- const result = deps.sqlite.transaction(() => {
83
- for (const message of messages) {
84
- const insertTimestampResult = sqliteStorageBase.value.insertTimestamp(ownerId, timestampToBinaryTimestamp(message.timestamp));
85
- if (!insertTimestampResult.ok)
86
- return insertTimestampResult;
87
- const insertMessage = deps.sqlite.exec(sql `
88
- insert into evolu_message ("ownerId", "timestamp", "change")
89
- values
90
- (
91
- ${ownerId},
92
- ${timestampToBinaryTimestamp(message.timestamp)},
93
- ${message.change}
94
- )
95
- on conflict do nothing;
96
- `);
97
- if (!insertMessage.ok)
98
- return insertMessage;
68
+ writeMessages: async (ownerIdBytes, messages) => {
69
+ const ownerId = ownerIdBytesToOwnerId(ownerIdBytes);
70
+ const messagesWithTimestampBytes = mapArray(messages, (m) => ({
71
+ timestamp: timestampToTimestampBytes(m.timestamp),
72
+ change: m.change,
73
+ }));
74
+ const result = await ownerMutexes
75
+ .ensure(ownerId, createMutex)
76
+ .withLock(async () => {
77
+ const existingTimestampsResult = sqliteStorageBase.getExistingTimestamps(ownerIdBytes, mapArray(messagesWithTimestampBytes, (m) => m.timestamp));
78
+ if (!existingTimestampsResult.ok)
79
+ return existingTimestampsResult;
80
+ const existingTimestampsSet = new Set(existingTimestampsResult.value.map((t) => t.toString()));
81
+ const newMessages = filterArray(messagesWithTimestampBytes, (m) => !existingTimestampsSet.has(m.timestamp.toString()));
82
+ // Nothing to write
83
+ if (!isNonEmptyReadonlyArray(newMessages)) {
84
+ return ok();
85
+ }
86
+ const usage = getOwnerUsage(deps)(ownerIdBytes, firstInArray(newMessages).timestamp);
87
+ if (!usage.ok)
88
+ return usage;
89
+ const { storedBytes } = usage.value;
90
+ const incomingBytes = newMessages.reduce((sum, m) => sum + m.change.length, 0);
91
+ const newStoredBytes = PositiveInt.orThrow((storedBytes ?? 0) + incomingBytes);
92
+ const withinQuotaResult = config.isOwnerWithinQuota(ownerId, newStoredBytes);
93
+ const isWithinQuota = isAsync(withinQuotaResult)
94
+ ? await withinQuotaResult
95
+ : withinQuotaResult;
96
+ if (!isWithinQuota) {
97
+ return err({ type: "StorageQuotaError", ownerId });
98
+ }
99
+ let { firstTimestamp, lastTimestamp } = usage.value;
100
+ return deps.sqlite.transaction(() => {
101
+ for (const { timestamp, change } of newMessages) {
102
+ let strategy;
103
+ [strategy, firstTimestamp, lastTimestamp] =
104
+ getTimestampInsertStrategy(timestamp, firstTimestamp, lastTimestamp);
105
+ const insertTimestampResult = sqliteStorageBase.insertTimestamp(ownerIdBytes, timestamp, strategy);
106
+ if (!insertTimestampResult.ok)
107
+ return insertTimestampResult;
108
+ const insertMessage = deps.sqlite.exec(sql `
109
+ insert into evolu_message ("ownerId", "timestamp", "change")
110
+ values (${ownerIdBytes}, ${timestamp}, ${change})
111
+ on conflict do nothing;
112
+ `);
113
+ if (!insertMessage.ok)
114
+ return insertMessage;
115
+ }
116
+ const updateUsage = updateOwnerUsage(deps)(ownerIdBytes, newStoredBytes, firstTimestamp, lastTimestamp);
117
+ if (!updateUsage.ok)
118
+ return updateUsage;
119
+ return ok();
120
+ });
121
+ })();
122
+ if (!result.ok && result.error.type !== "AbortError") {
123
+ switch (result.error.type) {
124
+ case "SqliteError":
125
+ config.onStorageError(result.error);
126
+ return err({ type: "StorageWriteError", ownerId });
127
+ case "StorageQuotaError":
128
+ return err({ type: "StorageQuotaError", ownerId });
99
129
  }
100
- return ok();
101
- });
102
- if (!result.ok) {
103
- options.onStorageError(result.error);
104
- return false;
105
130
  }
106
- return true;
131
+ return ok();
107
132
  },
108
133
  readDbChange: (ownerId, timestamp) => {
109
134
  const result = deps.sqlite.exec(sql `
@@ -112,34 +137,135 @@ export const createRelayStorage = (deps) => (options) => {
112
137
  where "ownerId" = ${ownerId} and "timestamp" = ${timestamp};
113
138
  `);
114
139
  if (!result.ok) {
115
- options.onStorageError(result.error);
140
+ config.onStorageError(result.error);
116
141
  return null;
117
142
  }
118
143
  return result.value.rows[0]?.change;
119
144
  },
120
145
  deleteOwner: (ownerId) => {
121
- const result = deps.sqlite.transaction(() => {
122
- const del1 = deps.sqlite.exec(sql `
146
+ const transactionResult = deps.sqlite.transaction(() => {
147
+ const deleteWriteKey = deps.sqlite.exec(sql `
123
148
  delete from evolu_writeKey where ownerId = ${ownerId};
124
149
  `);
125
- if (!del1.ok)
126
- return del1;
127
- const del2 = deps.sqlite.exec(sql `
150
+ if (!deleteWriteKey.ok)
151
+ return deleteWriteKey;
152
+ const deleteMessages = deps.sqlite.exec(sql `
128
153
  delete from evolu_message where ownerId = ${ownerId};
129
154
  `);
130
- if (!del2.ok)
131
- return del2;
132
- const del3 = sqliteStorageBase.value.deleteOwner(ownerId);
133
- if (!del3)
155
+ if (!deleteMessages.ok)
156
+ return deleteMessages;
157
+ const deleteUsage = deps.sqlite.exec(sql `
158
+ delete from evolu_usage where ownerId = ${ownerId};
159
+ `);
160
+ if (!deleteUsage.ok)
161
+ return deleteUsage;
162
+ const deleteBaseOwner = sqliteStorageBase.deleteOwner(ownerId);
163
+ if (!deleteBaseOwner)
134
164
  return err(null);
135
165
  return ok();
136
166
  });
137
- if (!result.ok) {
138
- if (result.error)
139
- options.onStorageError(result.error);
167
+ if (!transactionResult.ok) {
168
+ if (transactionResult.error)
169
+ config.onStorageError(transactionResult.error);
140
170
  return false;
141
171
  }
142
172
  return true;
143
173
  },
144
- });
174
+ };
175
+ };
176
+ export const createRelayStorageTables = (deps) => {
177
+ for (const query of [
178
+ sql `
179
+ create table evolu_writeKey (
180
+ "ownerId" blob not null,
181
+ "writeKey" blob not null,
182
+ primary key ("ownerId")
183
+ )
184
+ strict;
185
+ `,
186
+ sql `
187
+ create table evolu_message (
188
+ "ownerId" blob not null,
189
+ "timestamp" blob not null,
190
+ "change" blob not null,
191
+ primary key ("ownerId", "timestamp")
192
+ )
193
+ strict;
194
+ `,
195
+ ]) {
196
+ const result = deps.sqlite.exec(query);
197
+ if (!result.ok)
198
+ return result;
199
+ }
200
+ return ok();
145
201
  };
202
+ export const createRelayLogger = (deps) => ({
203
+ started: (enableLogging, port) => {
204
+ deps.console.enabled = true;
205
+ deps.console.log(`Evolu Relay started on port ${port}`);
206
+ deps.console.enabled = enableLogging;
207
+ },
208
+ storageError: (error) => {
209
+ deps.console.error("[relay]", "storage", error);
210
+ },
211
+ upgradeSocketError: (error) => {
212
+ deps.console.warn("[relay]", "socket error", { error });
213
+ },
214
+ invalidOrMissingOwnerIdInUrl: (url) => {
215
+ deps.console.warn("[relay]", "invalid or missing ownerId in URL", { url });
216
+ },
217
+ unauthorizedOwner: (ownerId) => {
218
+ deps.console.warn("[relay]", "unauthorized owner", { ownerId });
219
+ },
220
+ connectionEstablished: (totalConnectionCount) => {
221
+ deps.console.log("[relay]", "connection", { totalConnectionCount });
222
+ },
223
+ connectionWebSocketError: (error) => {
224
+ deps.console.error("[relay]", "error", { error });
225
+ },
226
+ relayOptionSubscribe: (ownerId, getSubscriberCount) => {
227
+ if (deps.console.enabled)
228
+ deps.console.log("[relay]", "subscribe", {
229
+ ownerId,
230
+ subscriberCount: getSubscriberCount(),
231
+ });
232
+ },
233
+ relayOptionUnsubscribe: (ownerId, getSubscriberCount) => {
234
+ if (deps.console.enabled)
235
+ deps.console.log("[relay]", "unsubscribe", {
236
+ ownerId,
237
+ subscriberCount: getSubscriberCount(),
238
+ });
239
+ },
240
+ relayOptionBroadcast: (ownerId, broadcastCount, totalSubscribers) => {
241
+ deps.console.log("[relay]", "broadcast", {
242
+ ownerId,
243
+ broadcastCount,
244
+ totalSubscribers,
245
+ });
246
+ },
247
+ messageLength: (messageLength) => {
248
+ deps.console.log("[relay]", "on message", { messageLength });
249
+ },
250
+ applyProtocolMessageAsRelayError: (error) => {
251
+ deps.console.error("[relay]", "applyProtocolMessageAsRelay", error);
252
+ },
253
+ responseLength: (responseLength) => {
254
+ deps.console.log("[relay]", "responseLength", { responseLength });
255
+ },
256
+ applyProtocolMessageAsRelayUnknownError: (error) => {
257
+ deps.console.error("[relay]", "applyProtocolMessageAsRelayUnknownError", error);
258
+ },
259
+ connectionClosed: (totalConnectionCount) => {
260
+ deps.console.log("[relay]", "close", { totalConnectionCount });
261
+ },
262
+ shuttingDown: () => {
263
+ deps.console.log("Shutting down Evolu Relay...");
264
+ },
265
+ webSocketServerDisposed: () => {
266
+ deps.console.log("Evolu Relay WebSocketServer disposed");
267
+ },
268
+ httpServerDisposed: () => {
269
+ deps.console.log("Evolu Relay HTTP server disposed");
270
+ },
271
+ });