@evolu/common 8.14.0 → 8.15.1

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 (207) hide show
  1. package/dist/src/Crypto.d.ts +2 -1
  2. package/dist/src/Crypto.d.ts.map +1 -1
  3. package/dist/src/Redacted.d.ts +19 -39
  4. package/dist/src/Redacted.d.ts.map +1 -1
  5. package/dist/src/Redacted.js +18 -33
  6. package/dist/src/Type.d.ts +161 -0
  7. package/dist/src/Type.d.ts.map +1 -1
  8. package/dist/src/Type.js +152 -2
  9. package/dist/src/WebSocket.d.ts +16 -1
  10. package/dist/src/WebSocket.d.ts.map +1 -1
  11. package/dist/src/WebSocket.js +14 -2
  12. package/dist/src/intl/_en.d.ts +5 -1
  13. package/dist/src/intl/_en.d.ts.map +1 -1
  14. package/dist/src/intl/_en.js +4 -0
  15. package/dist/src/intl/ar.d.ts +5 -1
  16. package/dist/src/intl/ar.d.ts.map +1 -1
  17. package/dist/src/intl/ar.js +4 -0
  18. package/dist/src/intl/bn.d.ts +5 -1
  19. package/dist/src/intl/bn.d.ts.map +1 -1
  20. package/dist/src/intl/bn.js +4 -0
  21. package/dist/src/intl/ca.d.ts +5 -1
  22. package/dist/src/intl/ca.d.ts.map +1 -1
  23. package/dist/src/intl/ca.js +4 -0
  24. package/dist/src/intl/cs.d.ts +5 -1
  25. package/dist/src/intl/cs.d.ts.map +1 -1
  26. package/dist/src/intl/cs.js +4 -0
  27. package/dist/src/intl/da.d.ts +5 -1
  28. package/dist/src/intl/da.d.ts.map +1 -1
  29. package/dist/src/intl/da.js +4 -0
  30. package/dist/src/intl/de.d.ts +5 -1
  31. package/dist/src/intl/de.d.ts.map +1 -1
  32. package/dist/src/intl/de.js +4 -0
  33. package/dist/src/intl/el.d.ts +5 -1
  34. package/dist/src/intl/el.d.ts.map +1 -1
  35. package/dist/src/intl/el.js +4 -0
  36. package/dist/src/intl/es.d.ts +5 -1
  37. package/dist/src/intl/es.d.ts.map +1 -1
  38. package/dist/src/intl/es.js +4 -0
  39. package/dist/src/intl/fa.d.ts +5 -1
  40. package/dist/src/intl/fa.d.ts.map +1 -1
  41. package/dist/src/intl/fa.js +4 -0
  42. package/dist/src/intl/fi.d.ts +5 -1
  43. package/dist/src/intl/fi.d.ts.map +1 -1
  44. package/dist/src/intl/fi.js +4 -0
  45. package/dist/src/intl/fil.d.ts +5 -1
  46. package/dist/src/intl/fil.d.ts.map +1 -1
  47. package/dist/src/intl/fil.js +4 -0
  48. package/dist/src/intl/fr.d.ts +5 -1
  49. package/dist/src/intl/fr.d.ts.map +1 -1
  50. package/dist/src/intl/fr.js +4 -0
  51. package/dist/src/intl/he.d.ts +5 -1
  52. package/dist/src/intl/he.d.ts.map +1 -1
  53. package/dist/src/intl/he.js +4 -0
  54. package/dist/src/intl/hi.d.ts +5 -1
  55. package/dist/src/intl/hi.d.ts.map +1 -1
  56. package/dist/src/intl/hi.js +4 -0
  57. package/dist/src/intl/hr.d.ts +5 -1
  58. package/dist/src/intl/hr.d.ts.map +1 -1
  59. package/dist/src/intl/hr.js +4 -0
  60. package/dist/src/intl/hu.d.ts +3 -1
  61. package/dist/src/intl/hu.d.ts.map +1 -1
  62. package/dist/src/intl/hu.js +2 -0
  63. package/dist/src/intl/id.d.ts +5 -1
  64. package/dist/src/intl/id.d.ts.map +1 -1
  65. package/dist/src/intl/id.js +4 -0
  66. package/dist/src/intl/it.d.ts +5 -1
  67. package/dist/src/intl/it.d.ts.map +1 -1
  68. package/dist/src/intl/it.js +4 -0
  69. package/dist/src/intl/ja.d.ts +5 -1
  70. package/dist/src/intl/ja.d.ts.map +1 -1
  71. package/dist/src/intl/ja.js +4 -0
  72. package/dist/src/intl/ko.d.ts +5 -1
  73. package/dist/src/intl/ko.d.ts.map +1 -1
  74. package/dist/src/intl/ko.js +4 -0
  75. package/dist/src/intl/ml.d.ts +5 -1
  76. package/dist/src/intl/ml.d.ts.map +1 -1
  77. package/dist/src/intl/ml.js +4 -0
  78. package/dist/src/intl/mr.d.ts +5 -1
  79. package/dist/src/intl/mr.d.ts.map +1 -1
  80. package/dist/src/intl/mr.js +4 -0
  81. package/dist/src/intl/ms.d.ts +5 -1
  82. package/dist/src/intl/ms.d.ts.map +1 -1
  83. package/dist/src/intl/ms.js +4 -0
  84. package/dist/src/intl/nb.d.ts +3 -1
  85. package/dist/src/intl/nb.d.ts.map +1 -1
  86. package/dist/src/intl/nb.js +2 -0
  87. package/dist/src/intl/nl.d.ts +5 -1
  88. package/dist/src/intl/nl.d.ts.map +1 -1
  89. package/dist/src/intl/nl.js +4 -0
  90. package/dist/src/intl/pa.d.ts +5 -1
  91. package/dist/src/intl/pa.d.ts.map +1 -1
  92. package/dist/src/intl/pa.js +4 -0
  93. package/dist/src/intl/pl.d.ts +4 -0
  94. package/dist/src/intl/pl.d.ts.map +1 -1
  95. package/dist/src/intl/pl.js +4 -0
  96. package/dist/src/intl/pt-BR.d.ts +5 -1
  97. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  98. package/dist/src/intl/pt-BR.js +4 -0
  99. package/dist/src/intl/pt.d.ts +5 -1
  100. package/dist/src/intl/pt.d.ts.map +1 -1
  101. package/dist/src/intl/pt.js +4 -0
  102. package/dist/src/intl/ro.d.ts +5 -1
  103. package/dist/src/intl/ro.d.ts.map +1 -1
  104. package/dist/src/intl/ro.js +4 -0
  105. package/dist/src/intl/sk.d.ts +5 -1
  106. package/dist/src/intl/sk.d.ts.map +1 -1
  107. package/dist/src/intl/sk.js +4 -0
  108. package/dist/src/intl/sl.d.ts +5 -1
  109. package/dist/src/intl/sl.d.ts.map +1 -1
  110. package/dist/src/intl/sl.js +4 -0
  111. package/dist/src/intl/sv.d.ts +5 -1
  112. package/dist/src/intl/sv.d.ts.map +1 -1
  113. package/dist/src/intl/sv.js +4 -0
  114. package/dist/src/intl/sw.d.ts +2 -0
  115. package/dist/src/intl/sw.d.ts.map +1 -1
  116. package/dist/src/intl/sw.js +2 -0
  117. package/dist/src/intl/ta.d.ts +5 -1
  118. package/dist/src/intl/ta.d.ts.map +1 -1
  119. package/dist/src/intl/ta.js +4 -0
  120. package/dist/src/intl/te.d.ts +5 -1
  121. package/dist/src/intl/te.d.ts.map +1 -1
  122. package/dist/src/intl/te.js +4 -0
  123. package/dist/src/intl/th.d.ts +5 -1
  124. package/dist/src/intl/th.d.ts.map +1 -1
  125. package/dist/src/intl/th.js +4 -0
  126. package/dist/src/intl/tr.d.ts +5 -1
  127. package/dist/src/intl/tr.d.ts.map +1 -1
  128. package/dist/src/intl/tr.js +4 -0
  129. package/dist/src/intl/uk.d.ts +5 -1
  130. package/dist/src/intl/uk.d.ts.map +1 -1
  131. package/dist/src/intl/uk.js +4 -0
  132. package/dist/src/intl/ur.d.ts +5 -1
  133. package/dist/src/intl/ur.d.ts.map +1 -1
  134. package/dist/src/intl/ur.js +4 -0
  135. package/dist/src/intl/vi.d.ts +5 -1
  136. package/dist/src/intl/vi.d.ts.map +1 -1
  137. package/dist/src/intl/vi.js +4 -0
  138. package/dist/src/intl/zh-CN.d.ts +5 -1
  139. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  140. package/dist/src/intl/zh-CN.js +4 -0
  141. package/dist/src/intl/zh-TW.d.ts +5 -1
  142. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  143. package/dist/src/intl/zh-TW.js +4 -0
  144. package/dist/src/local-first/Evolu.d.ts +4 -0
  145. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  146. package/dist/src/local-first/Evolu.js +6 -1
  147. package/dist/src/local-first/Schema.d.ts +3 -0
  148. package/dist/src/local-first/Schema.d.ts.map +1 -1
  149. package/dist/src/local-first/Schema.js +9 -5
  150. package/dist/src/local-first/Shared.js +1 -3
  151. package/package.json +1 -1
  152. package/src/Crypto.ts +2 -1
  153. package/src/Redacted.test.ts +231 -0
  154. package/src/Redacted.ts +36 -47
  155. package/src/Type.test.ts +166 -0
  156. package/src/Type.ts +198 -2
  157. package/src/WebSocket.ts +33 -4
  158. package/src/intl/_en.ts +10 -0
  159. package/src/intl/ar.ts +8 -0
  160. package/src/intl/bn.ts +10 -0
  161. package/src/intl/ca.ts +10 -0
  162. package/src/intl/cs.ts +10 -0
  163. package/src/intl/da.ts +10 -0
  164. package/src/intl/de.ts +10 -0
  165. package/src/intl/el.ts +10 -0
  166. package/src/intl/es.ts +10 -0
  167. package/src/intl/fa.ts +10 -0
  168. package/src/intl/fi.ts +10 -0
  169. package/src/intl/fil.ts +10 -0
  170. package/src/intl/fr.ts +10 -0
  171. package/src/intl/he.ts +10 -0
  172. package/src/intl/hi.ts +10 -0
  173. package/src/intl/hr.ts +10 -0
  174. package/src/intl/hu.ts +6 -0
  175. package/src/intl/id.ts +10 -0
  176. package/src/intl/intl.test.ts +88 -0
  177. package/src/intl/it.ts +10 -0
  178. package/src/intl/ja.ts +10 -0
  179. package/src/intl/ko.ts +10 -0
  180. package/src/intl/ml.ts +10 -0
  181. package/src/intl/mr.ts +10 -0
  182. package/src/intl/ms.ts +8 -0
  183. package/src/intl/nb.ts +6 -0
  184. package/src/intl/nl.ts +10 -0
  185. package/src/intl/pa.ts +10 -0
  186. package/src/intl/pl.ts +12 -0
  187. package/src/intl/pt-BR.ts +10 -0
  188. package/src/intl/pt.ts +8 -0
  189. package/src/intl/ro.ts +10 -0
  190. package/src/intl/sk.ts +8 -0
  191. package/src/intl/sl.ts +10 -0
  192. package/src/intl/sv.ts +10 -0
  193. package/src/intl/sw.ts +4 -0
  194. package/src/intl/ta.ts +10 -0
  195. package/src/intl/te.ts +10 -0
  196. package/src/intl/th.ts +10 -0
  197. package/src/intl/tr.ts +10 -0
  198. package/src/intl/uk.ts +10 -0
  199. package/src/intl/ur.ts +8 -0
  200. package/src/intl/vi.ts +8 -0
  201. package/src/intl/zh-CN.ts +10 -0
  202. package/src/intl/zh-TW.ts +10 -0
  203. package/src/local-first/Evolu.test.ts +62 -8
  204. package/src/local-first/Evolu.ts +11 -1
  205. package/src/local-first/Schema.test.ts +80 -1
  206. package/src/local-first/Schema.ts +15 -5
  207. package/src/local-first/Shared.ts +1 -3
package/src/intl/te.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatTypeOfError = (
@@ -258,6 +260,10 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
258
260
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
259
261
  `విలువ ${safelyStringifyUnknownValue(error.value)} చెల్లుబాటు అయ్యే Name కాదు.`;
260
262
 
263
+ /** EmailErrorను తెలుగులో ఫార్మాట్ చేస్తుంది. */
264
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
265
+ `విలువ ${safelyStringifyUnknownValue(error.value)} చెల్లుబాటు అయ్యే ఇమెయిల్ చిరునామా కాదు.`;
266
+
261
267
  /** MnemonicErrorను తెలుగులో ఫార్మాట్ చేస్తుంది. */
262
268
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
263
269
  `విలువ ${safelyStringifyUnknownValue(error.value)} చెల్లుబాటు అయ్యే ఆంగ్ల BIP39 mnemonic కాదు.`;
@@ -270,6 +276,10 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
270
276
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
271
277
  `విలువ ${safelyStringifyUnknownValue(error.value)} పట్టిక ${error.table} కు చెల్లుబాటు అయ్యే Id కాదు.`;
272
278
 
279
+ /** UuidErrorను తెలుగులో ఫార్మాట్ చేస్తుంది. */
280
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
281
+ `విలువ ${safelyStringifyUnknownValue(error.value)} చిన్న అక్షరాలలోని ప్రామాణిక UUID కాదు.`;
282
+
273
283
  /** NonNegativeErrorను తెలుగులో ఫార్మాట్ చేస్తుంది. */
274
284
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
275
285
  error,
package/src/intl/th.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatTypeOfError = (
@@ -256,6 +258,10 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
256
258
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
257
259
  `ค่า ${safelyStringifyUnknownValue(error.value)} ไม่ใช่ Name ที่ถูกต้อง`;
258
260
 
261
+ /** จัดรูปแบบ EmailError เป็นภาษาไทย */
262
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
263
+ `ค่า ${safelyStringifyUnknownValue(error.value)} ไม่ใช่ที่อยู่อีเมลที่ถูกต้อง`;
264
+
259
265
  /** จัดรูปแบบ MnemonicError เป็นภาษาไทย */
260
266
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
261
267
  `ค่า ${safelyStringifyUnknownValue(error.value)} ไม่ใช่ mnemonic BIP39 ภาษาอังกฤษที่ถูกต้อง`;
@@ -268,6 +274,10 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
268
274
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
269
275
  `ค่า ${safelyStringifyUnknownValue(error.value)} ไม่ใช่ Id ที่ถูกต้องสำหรับตาราง ${error.table}`;
270
276
 
277
+ /** จัดรูปแบบ UuidError เป็นภาษาไทย */
278
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
279
+ `ค่า ${safelyStringifyUnknownValue(error.value)} ไม่ใช่ UUID ตัวพิมพ์เล็กรูปแบบมาตรฐาน`;
280
+
271
281
  /** จัดรูปแบบ NonNegativeError เป็นภาษาไทย */
272
282
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
273
283
  error,
package/src/intl/tr.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatValueMustBe = (value: unknown, expected: string): string =>
@@ -258,6 +260,10 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
258
260
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
259
261
  `Değer ${safelyStringifyUnknownValue(error.value)} geçerli bir Name değildir.`;
260
262
 
263
+ /** Formats an EmailError in Turkish. */
264
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
265
+ `Değer ${safelyStringifyUnknownValue(error.value)} geçerli bir e-posta adresi değildir.`;
266
+
261
267
  /** Formats a MnemonicError in Turkish. */
262
268
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
263
269
  `Değer ${safelyStringifyUnknownValue(error.value)} geçerli bir İngilizce BIP39 anımsatıcı değildir.`;
@@ -270,6 +276,10 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
270
276
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
271
277
  `Değer ${safelyStringifyUnknownValue(error.value)} ${error.table} tablosu için geçerli bir Id değildir.`;
272
278
 
279
+ /** Formats a UuidError in Turkish. */
280
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
281
+ `Değer ${safelyStringifyUnknownValue(error.value)} kurallı bir küçük harfli UUID değildir.`;
282
+
273
283
  /** Formats a NonNegativeError in Turkish. */
274
284
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
275
285
  error,
package/src/intl/uk.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const typeOfNames = {
@@ -262,6 +264,10 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
262
264
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
263
265
  `Значення ${safelyStringifyUnknownValue(error.value)} не є допустимим Name.`;
264
266
 
267
+ /** Форматує EmailError українською. */
268
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
269
+ `Значення ${safelyStringifyUnknownValue(error.value)} не є допустимою адресою електронної пошти.`;
270
+
265
271
  /** Форматує MnemonicError українською. */
266
272
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
267
273
  `Значення ${safelyStringifyUnknownValue(error.value)} не є допустимою англійською мнемонічною фразою BIP39.`;
@@ -274,6 +280,10 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
274
280
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
275
281
  `Значення ${safelyStringifyUnknownValue(error.value)} не є допустимим Id для таблиці ${error.table}.`;
276
282
 
283
+ /** Форматує UuidError українською. */
284
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
285
+ `Значення ${safelyStringifyUnknownValue(error.value)} не є канонічним UUID у нижньому регістрі.`;
286
+
277
287
  /** Форматує NonNegativeError українською. */
278
288
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
279
289
  error,
package/src/intl/ur.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatTypeOfError = (
@@ -224,6 +226,9 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
224
226
  /** NameError کو اردو میں فارمیٹ کرتا ہے۔ */
225
227
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
226
228
  `قدر ${safelyStringifyUnknownValue(error.value)} درست Name نہیں ہے۔`;
229
+ /** EmailError کو اردو میں فارمیٹ کرتا ہے۔ */
230
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
231
+ `قدر ${safelyStringifyUnknownValue(error.value)} درست ای میل ایڈریس نہیں ہے۔`;
227
232
  /** MnemonicError کو اردو میں فارمیٹ کرتا ہے۔ */
228
233
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
229
234
  `قدر ${safelyStringifyUnknownValue(error.value)} درست انگریزی BIP39 mnemonic نہیں ہے۔`;
@@ -233,6 +238,9 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
233
238
  /** TableIdError کو اردو میں فارمیٹ کرتا ہے۔ */
234
239
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
235
240
  `قدر ${safelyStringifyUnknownValue(error.value)} جدول ${error.table} کے لیے درست Id نہیں ہے۔`;
241
+ /** UuidError کو اردو میں فارمیٹ کرتا ہے۔ */
242
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
243
+ `قدر ${safelyStringifyUnknownValue(error.value)} چھوٹے حروف والا canonical UUID نہیں ہے۔`;
236
244
  /** NonNegativeError کو اردو میں فارمیٹ کرتا ہے۔ */
237
245
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
238
246
  error,
package/src/intl/vi.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatTypeOfError = (
@@ -225,6 +227,9 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
225
227
  /** Định dạng NameError bằng tiếng Việt. */
226
228
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
227
229
  `Giá trị ${safelyStringifyUnknownValue(error.value)} không phải là Name hợp lệ.`;
230
+ /** Định dạng EmailError bằng tiếng Việt. */
231
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
232
+ `Giá trị ${safelyStringifyUnknownValue(error.value)} không phải là địa chỉ email hợp lệ.`;
228
233
  /** Định dạng MnemonicError bằng tiếng Việt. */
229
234
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
230
235
  `Giá trị ${safelyStringifyUnknownValue(error.value)} không phải là cụm từ gợi nhớ BIP39 tiếng Anh hợp lệ.`;
@@ -234,6 +239,9 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
234
239
  /** Định dạng TableIdError bằng tiếng Việt. */
235
240
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
236
241
  `Giá trị ${safelyStringifyUnknownValue(error.value)} không phải là Id hợp lệ cho bảng ${error.table}.`;
242
+ /** Định dạng UuidError bằng tiếng Việt. */
243
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
244
+ `Giá trị ${safelyStringifyUnknownValue(error.value)} không phải là UUID chữ thường chính tắc.`;
237
245
  /** Định dạng NonNegativeError bằng tiếng Việt. */
238
246
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
239
247
  error,
package/src/intl/zh-CN.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatTypeOfError = (
@@ -254,6 +256,10 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
254
256
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
255
257
  `值 ${safelyStringifyUnknownValue(error.value)} 不是有效的 Name。`;
256
258
 
259
+ /** 以简体中文格式化 EmailError。 */
260
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
261
+ `值 ${safelyStringifyUnknownValue(error.value)} 不是有效的电子邮件地址。`;
262
+
257
263
  /** 以简体中文格式化 MnemonicError。 */
258
264
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
259
265
  `值 ${safelyStringifyUnknownValue(error.value)} 不是有效的英文 BIP39 助记词。`;
@@ -266,6 +272,10 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
266
272
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
267
273
  `值 ${safelyStringifyUnknownValue(error.value)} 不是表 ${error.table} 的有效 Id。`;
268
274
 
275
+ /** 以简体中文格式化 UuidError。 */
276
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
277
+ `值 ${safelyStringifyUnknownValue(error.value)} 不是规范的小写 UUID。`;
278
+
269
279
  /** 以简体中文格式化 NonNegativeError。 */
270
280
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
271
281
  error,
package/src/intl/zh-TW.ts CHANGED
@@ -23,6 +23,7 @@ import type {
23
23
  DateIsoFromDateError,
24
24
  DecimalStringError,
25
25
  DiscriminatedUnionError,
26
+ EmailError,
26
27
  EvoluTypeError,
27
28
  FiniteError,
28
29
  GreaterThanError,
@@ -74,6 +75,7 @@ import type {
74
75
  TypeOfError,
75
76
  UInt64Error,
76
77
  UnionError,
78
+ UuidError,
77
79
  } from "../Type.ts";
78
80
 
79
81
  const formatTypeOfError = (
@@ -254,6 +256,10 @@ export const formatBase64UrlError: TypeErrorFormatter<Base64UrlError> = (
254
256
  export const formatNameError: TypeErrorFormatter<NameError> = (error) =>
255
257
  `值 ${safelyStringifyUnknownValue(error.value)} 不是有效的 Name。`;
256
258
 
259
+ /** 以繁體中文格式化 EmailError。 */
260
+ export const formatEmailError: TypeErrorFormatter<EmailError> = (error) =>
261
+ `值 ${safelyStringifyUnknownValue(error.value)} 不是有效的電子郵件地址。`;
262
+
257
263
  /** 以繁體中文格式化 MnemonicError。 */
258
264
  export const formatMnemonicError: TypeErrorFormatter<MnemonicError> = (error) =>
259
265
  `值 ${safelyStringifyUnknownValue(error.value)} 不是有效的英文 BIP39 助記詞。`;
@@ -266,6 +272,10 @@ export const formatIdError: TypeErrorFormatter<IdError> = (error) =>
266
272
  export const formatTableIdError: TypeErrorFormatter<TableIdError> = (error) =>
267
273
  `值 ${safelyStringifyUnknownValue(error.value)} 不是資料表 ${error.table} 的有效 Id。`;
268
274
 
275
+ /** 以繁體中文格式化 UuidError。 */
276
+ export const formatUuidError: TypeErrorFormatter<UuidError> = (error) =>
277
+ `值 ${safelyStringifyUnknownValue(error.value)} 不是標準小寫 UUID。`;
278
+
269
279
  /** 以繁體中文格式化 NonNegativeError。 */
270
280
  export const formatNonNegativeError: TypeErrorFormatter<NonNegativeError> = (
271
281
  error,
@@ -35,6 +35,8 @@ import {
35
35
  createOwnerSecret,
36
36
  createOwnerWebSocketTransport,
37
37
  createSharedOwner,
38
+ createSharedReadonlyOwner,
39
+ type Owner,
38
40
  testAppOwner,
39
41
  } from "./Owner.ts";
40
42
  import {
@@ -124,6 +126,13 @@ const testOwnerTransport = createOwnerWebSocketTransport({
124
126
  ownerId: testAppOwner.id,
125
127
  });
126
128
 
129
+ /** {@link testAppOwner} as useOwner posts it, without its type and mnemonic. */
130
+ const testPostedAppOwner: Owner = {
131
+ id: testAppOwner.id,
132
+ encryptionKey: testAppOwner.encryptionKey,
133
+ writeKey: testAppOwner.writeKey,
134
+ };
135
+
127
136
  const createQuery = createQueryBuilder(Schema);
128
137
 
129
138
  const todoTitleQuery = createQuery((db) =>
@@ -1178,7 +1187,7 @@ describe("Evolu", () => {
1178
1187
  {
1179
1188
  action: "add",
1180
1189
  owner: {
1181
- owner: testAppOwner,
1190
+ owner: testPostedAppOwner,
1182
1191
  transports: [{ type: "WebSocket", url: "wss://free.evoluhq.com" }],
1183
1192
  },
1184
1193
  },
@@ -1260,7 +1269,14 @@ describe("Evolu", () => {
1260
1269
  const { run, evoluInputs } = setup;
1261
1270
  const evolu = await run.ok(testCreateEvolu);
1262
1271
  const sharedOwner = createSharedOwner(createOwnerSecret(run.deps));
1263
- const owner = { owner: sharedOwner, transports: [testOwnerTransport] };
1272
+ const owner = {
1273
+ owner: {
1274
+ id: sharedOwner.id,
1275
+ encryptionKey: sharedOwner.encryptionKey,
1276
+ writeKey: sharedOwner.writeKey,
1277
+ },
1278
+ transports: [testOwnerTransport],
1279
+ };
1264
1280
  assertNotSame(sharedOwner.id, evolu.appOwner.id);
1265
1281
 
1266
1282
  const unuse = evolu.useOwner(sharedOwner, [testOwnerTransport]);
@@ -1340,7 +1356,7 @@ describe("Evolu", () => {
1340
1356
  actions: [
1341
1357
  {
1342
1358
  owner: {
1343
- owner: testAppOwner,
1359
+ owner: testPostedAppOwner,
1344
1360
  transports: [testOwnerTransport],
1345
1361
  },
1346
1362
  action: "add",
@@ -1376,7 +1392,45 @@ describe("Evolu", () => {
1376
1392
  actions: [
1377
1393
  {
1378
1394
  owner: {
1379
- owner: testAppOwner,
1395
+ owner: testPostedAppOwner,
1396
+ transports: [testOwnerTransport],
1397
+ },
1398
+ action: "add",
1399
+ },
1400
+ ],
1401
+ },
1402
+ ]);
1403
+ });
1404
+
1405
+ it("posts only the owner id and keys", async () => {
1406
+ await using setup = await setupRunWithEvoluDeps();
1407
+ const { run, evoluInputs } = setup;
1408
+ const evolu = await run.ok(testCreateEvolu);
1409
+ const readonlyOwner = createSharedReadonlyOwner(
1410
+ createSharedOwner(createOwnerSecret(run.deps)),
1411
+ );
1412
+
1413
+ evolu.useOwner(testAppOwner, [testOwnerTransport]);
1414
+ evolu.useOwner(readonlyOwner, [testOwnerTransport]);
1415
+ await testWaitForWorkerMessage();
1416
+
1417
+ assertEqual(evoluInputs, [
1418
+ {
1419
+ type: "UseOwner",
1420
+ actions: [
1421
+ {
1422
+ owner: {
1423
+ owner: testPostedAppOwner,
1424
+ transports: [testOwnerTransport],
1425
+ },
1426
+ action: "add",
1427
+ },
1428
+ {
1429
+ owner: {
1430
+ owner: {
1431
+ id: readonlyOwner.id,
1432
+ encryptionKey: readonlyOwner.encryptionKey,
1433
+ },
1380
1434
  transports: [testOwnerTransport],
1381
1435
  },
1382
1436
  action: "add",
@@ -1404,14 +1458,14 @@ describe("Evolu", () => {
1404
1458
  actions: [
1405
1459
  {
1406
1460
  owner: {
1407
- owner: testAppOwner,
1461
+ owner: testPostedAppOwner,
1408
1462
  transports: [testOwnerTransport],
1409
1463
  },
1410
1464
  action: "add",
1411
1465
  },
1412
1466
  {
1413
1467
  owner: {
1414
- owner: testAppOwner,
1468
+ owner: testPostedAppOwner,
1415
1469
  transports: [testOwnerTransport],
1416
1470
  },
1417
1471
  action: "remove",
@@ -1447,7 +1501,7 @@ describe("Evolu", () => {
1447
1501
  actions: [
1448
1502
  {
1449
1503
  owner: {
1450
- owner: testAppOwner,
1504
+ owner: testPostedAppOwner,
1451
1505
  transports: [testOwnerTransport],
1452
1506
  },
1453
1507
  action: "remove",
@@ -1480,7 +1534,7 @@ describe("Evolu", () => {
1480
1534
  actions: [
1481
1535
  {
1482
1536
  owner: {
1483
- owner: testAppOwner,
1537
+ owner: testPostedAppOwner,
1484
1538
  transports: [testOwnerTransport],
1485
1539
  },
1486
1540
  action: "add",
@@ -195,6 +195,10 @@ export interface EvoluConfig {
195
195
  /**
196
196
  * Use the `indexes` option to define SQLite indexes.
197
197
  *
198
+ * Unique indexes are rejected with an assertion because concurrent offline
199
+ * writes can violate uniqueness during synchronization. See
200
+ * [Uniqueness](https://www.evolu.dev/docs/schema#uniqueness).
201
+ *
198
202
  * Table and column names are not typed because Kysely doesn't support it.
199
203
  *
200
204
  * https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
@@ -1772,8 +1776,14 @@ export const createEvolu =
1772
1776
  "useOwner requires explicit non-empty transports when config.transports is empty.",
1773
1777
  );
1774
1778
 
1779
+ // Post only what workers use. Forwarding the caller's object would copy
1780
+ // an AppOwner's mnemonic into every worker.
1781
+ const { id, encryptionKey } = owner;
1775
1782
  const syncOwner: SyncOwner = {
1776
- owner,
1783
+ owner:
1784
+ "writeKey" in owner
1785
+ ? { id, encryptionKey, writeKey: owner.writeKey }
1786
+ : { id, encryptionKey },
1777
1787
  transports: effectiveTransports,
1778
1788
  };
1779
1789
 
@@ -4,6 +4,7 @@ import {
4
4
  assertErr,
5
5
  assertOk,
6
6
  assertSame,
7
+ assertThrowsInstanceOf,
7
8
  assertType,
8
9
  createEvolu,
9
10
  createQueryBuilder,
@@ -27,7 +28,7 @@ import {
27
28
  type TimestampBytes,
28
29
  } from "../index.ts";
29
30
  import type { EvoluPlatformDeps } from "./Evolu.ts";
30
- import type { ValidateSchema } from "./Schema.ts";
31
+ import { evoluSchemaToSqliteSchema, type ValidateSchema } from "./Schema.ts";
31
32
 
32
33
  test("history queries select and filter by ownerId bytes", () => {
33
34
  const createQuery = createQueryBuilder(testEvoluSchema);
@@ -44,6 +45,84 @@ test("history queries select and filter by ownerId bytes", () => {
44
45
  >();
45
46
  });
46
47
 
48
+ test("evoluSchemaToSqliteSchema compiles ordinary indexes", () => {
49
+ const sqliteSchema = evoluSchemaToSqliteSchema(testEvoluSchema, (create) => [
50
+ create("todoTitle").on("todo").column("title"),
51
+ create("todoProjectIdTitle").on("todo").columns(["projectId", "title"]),
52
+ create("todoTitleNotNull")
53
+ .on("todo")
54
+ .column("title")
55
+ .where("title", "is not", null),
56
+ ]);
57
+
58
+ assertEqual(sqliteSchema.indexes, [
59
+ {
60
+ name: "todoTitle",
61
+ sql: 'create index "todoTitle" on "todo" ("title")',
62
+ },
63
+ {
64
+ name: "todoProjectIdTitle",
65
+ sql: 'create index "todoProjectIdTitle" on "todo" ("projectId", "title")',
66
+ },
67
+ {
68
+ name: "todoTitleNotNull",
69
+ sql: 'create index "todoTitleNotNull" on "todo" ("title") where "title" is not null',
70
+ },
71
+ ]);
72
+ });
73
+
74
+ test("evoluSchemaToSqliteSchema rejects unique indexes", () => {
75
+ const error = assertThrowsInstanceOf(
76
+ () =>
77
+ evoluSchemaToSqliteSchema(testEvoluSchema, (create) => [
78
+ create("todoUniqueTitle").on("todo").column("title").unique(),
79
+ ]),
80
+ Error,
81
+ );
82
+
83
+ assertEqual(
84
+ error.message,
85
+ "Unique indexes are not supported because they can prevent synchronization.",
86
+ );
87
+ });
88
+
89
+ test("evoluSchemaToSqliteSchema rejects composite unique indexes", () => {
90
+ const error = assertThrowsInstanceOf(
91
+ () =>
92
+ evoluSchemaToSqliteSchema(testEvoluSchema, (create) => [
93
+ create("todoUniqueProjectIdTitle")
94
+ .on("todo")
95
+ .columns(["projectId", "title"])
96
+ .unique(),
97
+ ]),
98
+ Error,
99
+ );
100
+
101
+ assertEqual(
102
+ error.message,
103
+ "Unique indexes are not supported because they can prevent synchronization.",
104
+ );
105
+ });
106
+
107
+ test("evoluSchemaToSqliteSchema rejects partial unique indexes", () => {
108
+ const error = assertThrowsInstanceOf(
109
+ () =>
110
+ evoluSchemaToSqliteSchema(testEvoluSchema, (create) => [
111
+ create("todoUniqueTitleNotNull")
112
+ .on("todo")
113
+ .column("title")
114
+ .where("title", "is not", null)
115
+ .unique(),
116
+ ]),
117
+ Error,
118
+ );
119
+
120
+ assertEqual(
121
+ error.message,
122
+ "Unique indexes are not supported because they can prevent synchronization.",
123
+ );
124
+ });
125
+
47
126
  test("testEvoluSchema supports project-linked and independent todos", () => {
48
127
  assertType<TestEvoluSchema, typeof testEvoluSchema>();
49
128
  assertType<ValidateSchema<TestEvoluSchema>, TestEvoluSchema>();
@@ -6,7 +6,7 @@
6
6
 
7
7
  import * as Kysely from "kysely";
8
8
  import type { StandardSchemaV1 } from "@standard-schema/spec";
9
- import { assertNonNullable } from "../Assert.ts";
9
+ import { assert, assertNonNullable } from "../Assert.ts";
10
10
  import { getOwnProp, mapObject, type ReadonlyRecord } from "../Object.ts";
11
11
  import {
12
12
  eqSqliteIndex,
@@ -299,6 +299,9 @@ export type ValidateSchema<S extends EvoluSchema> =
299
299
  /**
300
300
  * Defines SQLite indexes with Kysely's index builder.
301
301
  *
302
+ * Unique indexes are not supported because concurrent offline writes can
303
+ * violate uniqueness during synchronization.
304
+ *
302
305
  * @group SQLite
303
306
  */
304
307
  export type IndexesConfig = (
@@ -800,10 +803,17 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
800
803
  );
801
804
 
802
805
  const indexes = indexesConfig
803
- ? indexesConfig(createIndex).map((index): SqliteIndex => ({
804
- name: index.toOperationNode().name.name,
805
- sql: index.compile().sql,
806
- }))
806
+ ? indexesConfig(createIndex).map((index): SqliteIndex => {
807
+ const node = index.toOperationNode();
808
+ assert(
809
+ !node.unique,
810
+ "Unique indexes are not supported because they can prevent synchronization.",
811
+ );
812
+ return {
813
+ name: node.name.name,
814
+ sql: index.compile().sql,
815
+ };
816
+ })
807
817
  : [];
808
818
 
809
819
  return { tables, indexes };
@@ -3578,9 +3578,7 @@ const createEvoluTenant =
3578
3578
  // - Detect DbWorker and port liveness so a worker-only crash resumes the queue.
3579
3579
  // Defer panicked-worker restart until failure detection and recovery are
3580
3580
  // defined, accounting for SQLite WASM's detection limits. A mutation that
3581
- // throws is answered, but other SQLite operations are expected not to throw;
3582
- // user-defined UNIQUE indexes, which can make replicated writes fail, are
3583
- // planned to be forbidden.
3581
+ // throws is answered, but other SQLite operations are expected not to throw.
3584
3582
  // - Split worker protocol types and the EvoluTenant implementation into focused
3585
3583
  // modules.
3586
3584
  // - Remove the obsolete commented protocol block above.