@evolu/common 8.7.0 → 8.9.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 (302) hide show
  1. package/dist/src/Array.d.ts +21 -21
  2. package/dist/src/Array.js +5 -5
  3. package/dist/src/Assert.d.ts +498 -141
  4. package/dist/src/Assert.d.ts.map +1 -1
  5. package/dist/src/Assert.js +648 -99
  6. package/dist/src/{Buffer.d.ts → Binary.d.ts} +85 -12
  7. package/dist/src/Binary.d.ts.map +1 -0
  8. package/dist/src/{Buffer.js → Binary.js} +206 -24
  9. package/dist/src/Console.d.ts +2 -10
  10. package/dist/src/Console.d.ts.map +1 -1
  11. package/dist/src/Console.js +6 -12
  12. package/dist/src/Crypto.d.ts +5 -5
  13. package/dist/src/Crypto.js +1 -1
  14. package/dist/src/Eq.d.ts +35 -40
  15. package/dist/src/Eq.d.ts.map +1 -1
  16. package/dist/src/Eq.js +51 -87
  17. package/dist/src/Function.d.ts +9 -14
  18. package/dist/src/Function.d.ts.map +1 -1
  19. package/dist/src/Function.js +7 -9
  20. package/dist/src/Http.d.ts +6 -7
  21. package/dist/src/Http.d.ts.map +1 -1
  22. package/dist/src/LeakDetector.d.ts.map +1 -1
  23. package/dist/src/LeakDetector.js +1 -0
  24. package/dist/src/LockManager.d.ts +1 -1
  25. package/dist/src/LockManager.js +1 -1
  26. package/dist/src/Lookup.d.ts +3 -2
  27. package/dist/src/Lookup.d.ts.map +1 -1
  28. package/dist/src/Lookup.js +4 -3
  29. package/dist/src/Number.d.ts +1 -1
  30. package/dist/src/Number.d.ts.map +1 -1
  31. package/dist/src/Number.js +1 -1
  32. package/dist/src/Object.d.ts +4 -4
  33. package/dist/src/Object.d.ts.map +1 -1
  34. package/dist/src/Object.js +5 -4
  35. package/dist/src/Option.d.ts +1 -2
  36. package/dist/src/Option.d.ts.map +1 -1
  37. package/dist/src/Option.js +1 -2
  38. package/dist/src/Platform.js +2 -0
  39. package/dist/src/Polyfills.d.ts +6 -0
  40. package/dist/src/Polyfills.d.ts.map +1 -1
  41. package/dist/src/Polyfills.js +10 -1
  42. package/dist/src/Random.d.ts +1 -1
  43. package/dist/src/Result.d.ts +49 -54
  44. package/dist/src/Result.d.ts.map +1 -1
  45. package/dist/src/Result.js +17 -22
  46. package/dist/src/Set.d.ts +6 -6
  47. package/dist/src/Set.js +2 -2
  48. package/dist/src/Sqlite.js +1 -1
  49. package/dist/src/String.js +3 -2
  50. package/dist/src/Task.d.ts +32 -32
  51. package/dist/src/Task.d.ts.map +1 -1
  52. package/dist/src/Task.js +20 -22
  53. package/dist/src/Test.d.ts +31 -3
  54. package/dist/src/Test.d.ts.map +1 -1
  55. package/dist/src/Test.js +46 -3
  56. package/dist/src/Time.d.ts +3 -11
  57. package/dist/src/Time.d.ts.map +1 -1
  58. package/dist/src/Time.js +10 -18
  59. package/dist/src/Type.d.ts +87 -85
  60. package/dist/src/Type.d.ts.map +1 -1
  61. package/dist/src/Type.js +70 -63
  62. package/dist/src/Types.d.ts +12 -12
  63. package/dist/src/Types.js +1 -1
  64. package/dist/src/WebSocket.d.ts +4 -7
  65. package/dist/src/WebSocket.d.ts.map +1 -1
  66. package/dist/src/Worker.d.ts +1 -1
  67. package/dist/src/Worker.js +7 -7
  68. package/dist/src/index.d.ts +1 -1
  69. package/dist/src/index.js +1 -1
  70. package/dist/src/intl/_en.d.ts.map +1 -1
  71. package/dist/src/intl/_en.js +1 -1
  72. package/dist/src/intl/ar.d.ts.map +1 -1
  73. package/dist/src/intl/ar.js +1 -1
  74. package/dist/src/intl/bn.d.ts.map +1 -1
  75. package/dist/src/intl/bn.js +1 -1
  76. package/dist/src/intl/ca.d.ts.map +1 -1
  77. package/dist/src/intl/ca.js +1 -1
  78. package/dist/src/intl/cs.d.ts.map +1 -1
  79. package/dist/src/intl/cs.js +1 -1
  80. package/dist/src/intl/da.d.ts.map +1 -1
  81. package/dist/src/intl/da.js +1 -1
  82. package/dist/src/intl/de.d.ts.map +1 -1
  83. package/dist/src/intl/de.js +1 -1
  84. package/dist/src/intl/el.d.ts.map +1 -1
  85. package/dist/src/intl/el.js +1 -1
  86. package/dist/src/intl/es.d.ts.map +1 -1
  87. package/dist/src/intl/es.js +1 -1
  88. package/dist/src/intl/fa.d.ts.map +1 -1
  89. package/dist/src/intl/fa.js +1 -1
  90. package/dist/src/intl/fi.d.ts.map +1 -1
  91. package/dist/src/intl/fi.js +1 -1
  92. package/dist/src/intl/fil.d.ts.map +1 -1
  93. package/dist/src/intl/fil.js +1 -1
  94. package/dist/src/intl/fr.d.ts.map +1 -1
  95. package/dist/src/intl/fr.js +1 -1
  96. package/dist/src/intl/he.d.ts.map +1 -1
  97. package/dist/src/intl/he.js +1 -1
  98. package/dist/src/intl/hi.d.ts.map +1 -1
  99. package/dist/src/intl/hi.js +1 -1
  100. package/dist/src/intl/hr.d.ts.map +1 -1
  101. package/dist/src/intl/hr.js +1 -1
  102. package/dist/src/intl/hu.d.ts.map +1 -1
  103. package/dist/src/intl/hu.js +1 -1
  104. package/dist/src/intl/id.d.ts.map +1 -1
  105. package/dist/src/intl/id.js +1 -1
  106. package/dist/src/intl/it.d.ts.map +1 -1
  107. package/dist/src/intl/it.js +1 -1
  108. package/dist/src/intl/ja.d.ts.map +1 -1
  109. package/dist/src/intl/ja.js +1 -1
  110. package/dist/src/intl/ko.d.ts.map +1 -1
  111. package/dist/src/intl/ko.js +1 -1
  112. package/dist/src/intl/ml.d.ts.map +1 -1
  113. package/dist/src/intl/ml.js +1 -1
  114. package/dist/src/intl/mr.d.ts.map +1 -1
  115. package/dist/src/intl/mr.js +1 -1
  116. package/dist/src/intl/ms.d.ts.map +1 -1
  117. package/dist/src/intl/ms.js +1 -1
  118. package/dist/src/intl/nb.d.ts.map +1 -1
  119. package/dist/src/intl/nb.js +1 -1
  120. package/dist/src/intl/nl.d.ts.map +1 -1
  121. package/dist/src/intl/nl.js +1 -1
  122. package/dist/src/intl/pa.d.ts.map +1 -1
  123. package/dist/src/intl/pa.js +1 -1
  124. package/dist/src/intl/pl.d.ts.map +1 -1
  125. package/dist/src/intl/pl.js +1 -1
  126. package/dist/src/intl/pt-BR.d.ts.map +1 -1
  127. package/dist/src/intl/pt-BR.js +1 -1
  128. package/dist/src/intl/pt.d.ts.map +1 -1
  129. package/dist/src/intl/pt.js +1 -1
  130. package/dist/src/intl/ro.d.ts.map +1 -1
  131. package/dist/src/intl/ro.js +1 -1
  132. package/dist/src/intl/sk.d.ts.map +1 -1
  133. package/dist/src/intl/sk.js +1 -1
  134. package/dist/src/intl/sl.d.ts.map +1 -1
  135. package/dist/src/intl/sl.js +1 -1
  136. package/dist/src/intl/sv.d.ts.map +1 -1
  137. package/dist/src/intl/sv.js +1 -1
  138. package/dist/src/intl/sw.d.ts.map +1 -1
  139. package/dist/src/intl/sw.js +1 -1
  140. package/dist/src/intl/ta.d.ts.map +1 -1
  141. package/dist/src/intl/ta.js +1 -1
  142. package/dist/src/intl/te.d.ts.map +1 -1
  143. package/dist/src/intl/te.js +1 -1
  144. package/dist/src/intl/th.d.ts.map +1 -1
  145. package/dist/src/intl/th.js +1 -1
  146. package/dist/src/intl/tr.d.ts.map +1 -1
  147. package/dist/src/intl/tr.js +1 -1
  148. package/dist/src/intl/uk.d.ts.map +1 -1
  149. package/dist/src/intl/uk.js +1 -1
  150. package/dist/src/intl/ur.d.ts.map +1 -1
  151. package/dist/src/intl/ur.js +1 -1
  152. package/dist/src/intl/vi.d.ts.map +1 -1
  153. package/dist/src/intl/vi.js +1 -1
  154. package/dist/src/intl/zh-CN.d.ts.map +1 -1
  155. package/dist/src/intl/zh-CN.js +1 -1
  156. package/dist/src/intl/zh-TW.d.ts.map +1 -1
  157. package/dist/src/intl/zh-TW.js +1 -1
  158. package/dist/src/local-first/Evolu.d.ts +12 -12
  159. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  160. package/dist/src/local-first/Evolu.js +2 -2
  161. package/dist/src/local-first/Protocol.d.ts +14 -76
  162. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  163. package/dist/src/local-first/Protocol.js +29 -206
  164. package/dist/src/local-first/Query.d.ts +11 -11
  165. package/dist/src/local-first/Query.js +6 -6
  166. package/dist/src/local-first/Relay.d.ts +2 -2
  167. package/dist/src/local-first/Schema.d.ts +3 -5
  168. package/dist/src/local-first/Schema.d.ts.map +1 -1
  169. package/dist/src/local-first/Schema.js +2 -4
  170. package/dist/src/local-first/Shared.d.ts.map +1 -1
  171. package/dist/src/local-first/Shared.js +5 -5
  172. package/dist/src/local-first/Storage.js +4 -4
  173. package/dist/src/local-first/Timestamp.d.ts +9 -0
  174. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  175. package/dist/src/local-first/Timestamp.js +13 -10
  176. package/package.json +7 -4
  177. package/src/Array.test.ts +809 -0
  178. package/src/Array.ts +21 -21
  179. package/src/Assert.test.ts +1232 -0
  180. package/src/Assert.ts +1023 -227
  181. package/src/BigInt.test.ts +28 -0
  182. package/src/Binary.test.ts +1128 -0
  183. package/src/{Buffer.ts → Binary.ts} +273 -46
  184. package/src/Brand.test.ts +90 -0
  185. package/src/Cache.test.ts +183 -0
  186. package/src/Callbacks.test.ts +66 -0
  187. package/src/Console.test.ts +809 -0
  188. package/src/Console.ts +9 -17
  189. package/src/Crypto.test.ts +138 -0
  190. package/src/Crypto.ts +5 -5
  191. package/src/Eq.test.ts +333 -0
  192. package/src/Eq.ts +110 -176
  193. package/src/Error.test.ts +118 -0
  194. package/src/Function.test.ts +204 -0
  195. package/src/Function.ts +10 -15
  196. package/src/Http.test.ts +816 -0
  197. package/src/Http.ts +6 -7
  198. package/src/LeakDetector.test.ts +210 -0
  199. package/src/LeakDetector.ts +1 -0
  200. package/src/LockManager.ts +2 -2
  201. package/src/Lookup.test.ts +418 -0
  202. package/src/Lookup.ts +4 -3
  203. package/src/Microtask.test.ts +140 -0
  204. package/src/Number.test.ts +218 -0
  205. package/src/Number.ts +2 -2
  206. package/src/Object.test.ts +293 -0
  207. package/src/Object.ts +5 -4
  208. package/src/Option.test.ts +104 -0
  209. package/src/Option.ts +1 -2
  210. package/src/Order.test.ts +56 -0
  211. package/src/Platform.ts +2 -0
  212. package/src/Polyfills.ts +9 -4
  213. package/src/Random.test.ts +89 -0
  214. package/src/Random.ts +1 -1
  215. package/src/Ref.test.ts +139 -0
  216. package/src/RefCount.test.ts +184 -0
  217. package/src/Relation.test.ts +505 -0
  218. package/src/Resource.test.ts +4623 -0
  219. package/src/Result.test.ts +1829 -0
  220. package/src/Result.ts +49 -54
  221. package/src/Schedule.test.ts +1554 -0
  222. package/src/Set.test.ts +279 -0
  223. package/src/Set.ts +6 -6
  224. package/src/Sqlite.ts +1 -1
  225. package/src/Store.test.ts +288 -0
  226. package/src/String.test.ts +54 -0
  227. package/src/String.ts +3 -3
  228. package/src/Task.test.ts +10820 -0
  229. package/src/Task.ts +38 -43
  230. package/src/Test.test.ts +88 -0
  231. package/src/Test.ts +49 -3
  232. package/src/Time.test.ts +926 -0
  233. package/src/Time.ts +13 -21
  234. package/src/Type.base64.test.ts +88 -0
  235. package/src/Type.test.ts +20465 -0
  236. package/src/Type.ts +114 -111
  237. package/src/Types.test.ts +324 -0
  238. package/src/Types.ts +13 -13
  239. package/src/WebSocket.ts +4 -7
  240. package/src/Worker.test.ts +755 -0
  241. package/src/Worker.ts +13 -18
  242. package/src/index.ts +1 -1
  243. package/src/intl/_en.ts +1 -1
  244. package/src/intl/ar.ts +1 -1
  245. package/src/intl/bn.ts +1 -1
  246. package/src/intl/ca.ts +1 -1
  247. package/src/intl/cs.ts +1 -1
  248. package/src/intl/da.ts +1 -1
  249. package/src/intl/de.ts +1 -1
  250. package/src/intl/el.ts +1 -1
  251. package/src/intl/es.ts +1 -1
  252. package/src/intl/fa.ts +1 -1
  253. package/src/intl/fi.ts +1 -1
  254. package/src/intl/fil.ts +1 -1
  255. package/src/intl/fr.ts +1 -1
  256. package/src/intl/he.ts +1 -1
  257. package/src/intl/hi.ts +1 -1
  258. package/src/intl/hr.ts +1 -1
  259. package/src/intl/hu.ts +1 -1
  260. package/src/intl/id.ts +1 -1
  261. package/src/intl/intl.test.ts +3106 -0
  262. package/src/intl/it.ts +1 -1
  263. package/src/intl/ja.ts +1 -1
  264. package/src/intl/ko.ts +1 -1
  265. package/src/intl/ml.ts +1 -1
  266. package/src/intl/mr.ts +1 -1
  267. package/src/intl/ms.ts +1 -1
  268. package/src/intl/nb.ts +1 -1
  269. package/src/intl/nl.ts +1 -1
  270. package/src/intl/pa.ts +1 -1
  271. package/src/intl/pl.ts +1 -1
  272. package/src/intl/pt-BR.ts +1 -1
  273. package/src/intl/pt.ts +1 -1
  274. package/src/intl/ro.ts +1 -1
  275. package/src/intl/sk.ts +1 -1
  276. package/src/intl/sl.ts +1 -1
  277. package/src/intl/sv.ts +1 -1
  278. package/src/intl/sw.ts +1 -1
  279. package/src/intl/ta.ts +1 -1
  280. package/src/intl/te.ts +1 -1
  281. package/src/intl/th.ts +1 -1
  282. package/src/intl/tr.ts +1 -1
  283. package/src/intl/uk.ts +1 -1
  284. package/src/intl/ur.ts +1 -1
  285. package/src/intl/vi.ts +1 -1
  286. package/src/intl/zh-CN.ts +1 -1
  287. package/src/intl/zh-TW.ts +1 -1
  288. package/src/local-first/Evolu.test.ts +1921 -0
  289. package/src/local-first/Evolu.ts +18 -14
  290. package/src/local-first/Owner.test.ts +83 -0
  291. package/src/local-first/Protocol.test.ts +1037 -0
  292. package/src/local-first/Protocol.ts +58 -261
  293. package/src/local-first/Query.test.ts +349 -0
  294. package/src/local-first/Query.ts +11 -11
  295. package/src/local-first/Relay.ts +2 -2
  296. package/src/local-first/Schema.ts +3 -5
  297. package/src/local-first/Shared.test.ts +1828 -0
  298. package/src/local-first/Shared.ts +9 -8
  299. package/src/local-first/Storage.ts +4 -4
  300. package/src/local-first/Timestamp.test.ts +374 -0
  301. package/src/local-first/Timestamp.ts +22 -10
  302. package/dist/src/Buffer.d.ts.map +0 -1
@@ -0,0 +1,755 @@
1
+ import { describe, it } from "node:test";
2
+
3
+ import {
4
+ assertEqual,
5
+ assertFalse,
6
+ assertNonNullable,
7
+ assertNotNull,
8
+ assertSame,
9
+ assertThrowsInstanceOf,
10
+ assertTrue,
11
+ } from "./Assert.ts";
12
+ import { constVoid } from "./Function.ts";
13
+ import type { NativeMessagePort } from "./Worker.ts";
14
+ import {
15
+ createWorker,
16
+ createSharedWorker,
17
+ createBroadcastChannel,
18
+ testCreateWorker,
19
+ testCreateSharedWorker,
20
+ testCreateMessageChannel,
21
+ testCreateMessagePort,
22
+ testCreateBroadcastChannel,
23
+ testWaitForWorkerMessage,
24
+ } from "./Worker.ts";
25
+
26
+ const expectArrayAfterWorkerMessage = async <T>(
27
+ actual: ReadonlyArray<T>,
28
+ expected: ReadonlyArray<T>,
29
+ ): Promise<void> => {
30
+ await testWaitForWorkerMessage();
31
+ assertEqual(actual, expected);
32
+ };
33
+
34
+ describe("createWorker", () => {
35
+ it("messages are queued and delivered asynchronously after worker self onMessage is assigned", async () => {
36
+ let self!: { onMessage: ((message: string) => void) | null };
37
+
38
+ const worker = createWorker<string>((nextSelf) => {
39
+ self = nextSelf;
40
+ });
41
+
42
+ worker.postMessage("queued");
43
+
44
+ const received: Array<string> = [];
45
+ self.onMessage = (message) => {
46
+ received.push(message);
47
+ };
48
+
49
+ await expectArrayAfterWorkerMessage(received, ["queued"]);
50
+ });
51
+ });
52
+
53
+ describe("createSharedWorker", () => {
54
+ it("messages are queued and delivered asynchronously after worker-side port onMessage is assigned", async () => {
55
+ let workerPort!: { onMessage: ((message: string) => void) | null };
56
+
57
+ const worker = createSharedWorker<string>((self) => {
58
+ self.onConnect = (port) => {
59
+ workerPort = port;
60
+ };
61
+ });
62
+
63
+ worker.port.postMessage("queued");
64
+
65
+ const received: Array<string> = [];
66
+ workerPort.onMessage = (message) => {
67
+ received.push(message);
68
+ };
69
+
70
+ await expectArrayAfterWorkerMessage(received, ["queued"]);
71
+ });
72
+ });
73
+
74
+ describe("testCreateMessageChannel", () => {
75
+ it("native ports are object tokens for WeakMap compatibility", () => {
76
+ const channel = testCreateMessageChannel<string, number>();
77
+ assertEqual(typeof channel.port1.native, "object");
78
+ assertNotNull(channel.port1.native);
79
+ assertEqual(typeof channel.port2.native, "object");
80
+ assertNotNull(channel.port2.native);
81
+ });
82
+
83
+ it("port1 postMessage delivers to port2 onMessage asynchronously", async () => {
84
+ const channel = testCreateMessageChannel<string, number>();
85
+ const received: Array<string> = [];
86
+ channel.port2.onMessage = (msg) => {
87
+ received.push(msg);
88
+ };
89
+ channel.port1.postMessage("hello");
90
+
91
+ await expectArrayAfterWorkerMessage(received, ["hello"]);
92
+ });
93
+
94
+ it("port2 postMessage delivers to port1 onMessage asynchronously", async () => {
95
+ const channel = testCreateMessageChannel<string, number>();
96
+ const received: Array<number> = [];
97
+ channel.port1.onMessage = (msg) => {
98
+ received.push(msg);
99
+ };
100
+ channel.port2.postMessage(42);
101
+
102
+ await expectArrayAfterWorkerMessage(received, [42]);
103
+ });
104
+
105
+ it("messages are queued until onMessage is assigned and then flushed asynchronously", async () => {
106
+ const channel = testCreateMessageChannel<string, number>();
107
+ channel.port1.postMessage("a");
108
+ channel.port1.postMessage("b");
109
+ const received: Array<string> = [];
110
+ channel.port2.onMessage = (msg) => {
111
+ received.push(msg);
112
+ };
113
+
114
+ await expectArrayAfterWorkerMessage(received, ["a", "b"]);
115
+ });
116
+
117
+ it("messages remain queued when dispatch runs before onMessage is assigned", async () => {
118
+ const channel = testCreateMessageChannel<string>();
119
+ const received: Array<string> = [];
120
+
121
+ channel.port1.postMessage("buffered");
122
+ await testWaitForWorkerMessage();
123
+
124
+ channel.port2.onMessage = (msg) => {
125
+ received.push(msg);
126
+ };
127
+ await expectArrayAfterWorkerMessage(received, ["buffered"]);
128
+ });
129
+
130
+ it("messages sent after onMessage is assigned are delivered asynchronously", async () => {
131
+ const channel = testCreateMessageChannel<string>();
132
+ const received: Array<string> = [];
133
+ channel.port2.onMessage = (msg) => {
134
+ received.push(msg);
135
+ };
136
+ channel.port1.postMessage("first");
137
+ channel.port1.postMessage("second");
138
+
139
+ await expectArrayAfterWorkerMessage(received, ["first", "second"]);
140
+ });
141
+
142
+ it("setting onMessage to null stops future asynchronous delivery", async () => {
143
+ const channel = testCreateMessageChannel<string>();
144
+ const received: Array<string> = [];
145
+ channel.port2.onMessage = (msg) => {
146
+ received.push(msg);
147
+ };
148
+ channel.port1.postMessage("delivered");
149
+ await expectArrayAfterWorkerMessage(received, ["delivered"]);
150
+
151
+ channel.port2.onMessage = null;
152
+ channel.port1.postMessage("queued");
153
+ await testWaitForWorkerMessage();
154
+
155
+ assertEqual(received, ["delivered"]);
156
+ });
157
+
158
+ it("dispose nulls out handlers", () => {
159
+ const channel = testCreateMessageChannel<string>();
160
+ channel.port1.onMessage = constVoid;
161
+ channel.port2.onMessage = constVoid;
162
+ assertNotNull(channel.port1.onMessage);
163
+ assertNotNull(channel.port2.onMessage);
164
+ channel[Symbol.dispose]();
165
+ assertSame(channel.port1.onMessage, null);
166
+ assertSame(channel.port2.onMessage, null);
167
+ });
168
+
169
+ it("dispose before scheduled flush drops queued messages", async () => {
170
+ const channel = testCreateMessageChannel<string>();
171
+ const received: Array<string> = [];
172
+ const clearTimeout = globalThis.clearTimeout;
173
+
174
+ globalThis.clearTimeout = () => undefined;
175
+
176
+ try {
177
+ channel.port2.onMessage = (message) => {
178
+ received.push(message);
179
+ };
180
+ channel.port1.postMessage("queued");
181
+ channel.port2[Symbol.dispose]();
182
+
183
+ await testWaitForWorkerMessage();
184
+
185
+ assertEqual(received, []);
186
+ } finally {
187
+ globalThis.clearTimeout = clearTimeout;
188
+ }
189
+ });
190
+
191
+ it("dispose during flush stops remaining queued messages", async () => {
192
+ const channel = testCreateMessageChannel<string>();
193
+ const received: Array<string> = [];
194
+
195
+ channel.port2.onMessage = (message) => {
196
+ received.push(message);
197
+ channel.port2[Symbol.dispose]();
198
+ };
199
+
200
+ channel.port1.postMessage("first");
201
+ channel.port1.postMessage("second");
202
+
203
+ await expectArrayAfterWorkerMessage(received, ["first"]);
204
+ });
205
+
206
+ it("isDisposed reflects disposal state", () => {
207
+ const channel = testCreateMessageChannel<string>();
208
+
209
+ assertFalse(channel.isDisposed());
210
+ channel[Symbol.dispose]();
211
+ assertTrue(channel.isDisposed());
212
+ });
213
+
214
+ it("each channel creates independent ports", () => {
215
+ const channel1 = testCreateMessageChannel<string>();
216
+ const channel2 = testCreateMessageChannel<string>();
217
+ assertFalse(Object.is(channel1.port1.native, channel2.port1.native));
218
+ assertFalse(Object.is(channel1.port2.native, channel2.port2.native));
219
+ });
220
+
221
+ it("bidirectional communication works asynchronously", async () => {
222
+ const channel = testCreateMessageChannel<string, number>();
223
+ const strings: Array<string> = [];
224
+ const numbers: Array<number> = [];
225
+
226
+ channel.port2.onMessage = (msg) => {
227
+ strings.push(msg);
228
+ };
229
+ channel.port1.onMessage = (msg) => {
230
+ numbers.push(msg);
231
+ };
232
+
233
+ channel.port1.postMessage("hello");
234
+ channel.port2.postMessage(42);
235
+
236
+ await expectArrayAfterWorkerMessage(strings, ["hello"]);
237
+ assertEqual(numbers, [42]);
238
+ });
239
+
240
+ it("disposed port ignores onMessage reassignment and repeated dispose", () => {
241
+ const channel = testCreateMessageChannel<string>();
242
+
243
+ channel.port2[Symbol.dispose]();
244
+ channel.port2.onMessage = constVoid;
245
+ channel.port2[Symbol.dispose]();
246
+
247
+ assertSame(channel.port2.onMessage, null);
248
+ });
249
+
250
+ it("sending to a disposed peer is ignored", async () => {
251
+ const channel = testCreateMessageChannel<string>();
252
+ const received: Array<string> = [];
253
+
254
+ channel.port2.onMessage = (message) => {
255
+ received.push(message);
256
+ };
257
+ channel.port2[Symbol.dispose]();
258
+ channel.port1.postMessage("ignored");
259
+
260
+ await testWaitForWorkerMessage();
261
+
262
+ assertEqual(received, []);
263
+ });
264
+
265
+ it("non-port transferables are ignored", async () => {
266
+ const channel = testCreateMessageChannel<string>();
267
+ const received: Array<string> = [];
268
+
269
+ channel.port2.onMessage = (message) => {
270
+ received.push(message);
271
+ };
272
+ channel.port1.postMessage("hello", [{} as NativeMessagePort]);
273
+ channel.port1.postMessage("world", [new ArrayBuffer(8)]);
274
+
275
+ await expectArrayAfterWorkerMessage(received, ["hello", "world"]);
276
+ });
277
+
278
+ it("transferred native ports can be wrapped after transfer", async () => {
279
+ const channel =
280
+ testCreateMessageChannel<NativeMessagePort<never, string>>();
281
+ const transferredChannel = testCreateMessageChannel<never, string>();
282
+ let transferredNative: NativeMessagePort<never, string> | undefined;
283
+
284
+ channel.port2.onMessage = (nativePort) => {
285
+ transferredNative = nativePort;
286
+ };
287
+
288
+ channel.port1.postMessage(transferredChannel.port1.native, [
289
+ transferredChannel.port1.native,
290
+ ]);
291
+
292
+ await testWaitForWorkerMessage();
293
+ assertNonNullable(transferredNative);
294
+
295
+ transferredChannel.port1[Symbol.dispose]();
296
+
297
+ const wrappedPort = testCreateMessagePort<never, string>(transferredNative);
298
+ const received: Array<string> = [];
299
+ wrappedPort.onMessage = (message) => {
300
+ received.push(message);
301
+ };
302
+
303
+ transferredChannel.port2.postMessage("hello");
304
+
305
+ await expectArrayAfterWorkerMessage(received, ["hello"]);
306
+ });
307
+ });
308
+
309
+ describe("testCreateMessagePort", () => {
310
+ it("looks up port by native token from channel", () => {
311
+ const channel = testCreateMessageChannel<string, number>();
312
+ const port = testCreateMessagePort<string, number>(channel.port1.native);
313
+ assertSame(port.native, channel.port1.native);
314
+ });
315
+
316
+ it("looks up port2 by native token", () => {
317
+ const channel = testCreateMessageChannel<string, number>();
318
+ const port = testCreateMessagePort<number, string>(channel.port2.native);
319
+ assertSame(port.native, channel.port2.native);
320
+ });
321
+
322
+ it("transferred ports remain usable after original channel dispose", async () => {
323
+ const channel = testCreateMessageChannel<string, number>();
324
+ const transferredPort1 = testCreateMessagePort<string, number>(
325
+ channel.port1.native,
326
+ );
327
+ const transferredPort2 = testCreateMessagePort<number, string>(
328
+ channel.port2.native,
329
+ );
330
+
331
+ const received: Array<string> = [];
332
+ transferredPort2.onMessage = (message) => {
333
+ received.push(message);
334
+ };
335
+
336
+ channel[Symbol.dispose]();
337
+ transferredPort1.postMessage("hello");
338
+
339
+ await expectArrayAfterWorkerMessage(received, ["hello"]);
340
+ });
341
+
342
+ it("disposed wrapper postMessage is ignored", async () => {
343
+ const channel = testCreateMessageChannel<string, number>();
344
+ const transferredPort1 = testCreateMessagePort<string, number>(
345
+ channel.port1.native,
346
+ );
347
+ const transferredPort2 = testCreateMessagePort<number, string>(
348
+ channel.port2.native,
349
+ );
350
+
351
+ const received: Array<string> = [];
352
+ transferredPort2.onMessage = (message) => {
353
+ received.push(message);
354
+ };
355
+
356
+ channel.port1[Symbol.dispose]();
357
+ channel.port1.postMessage("ignored");
358
+ transferredPort1.postMessage("delivered");
359
+
360
+ await expectArrayAfterWorkerMessage(received, ["delivered"]);
361
+ });
362
+
363
+ it("throws for disposed native port", () => {
364
+ const channel = testCreateMessageChannel<string>();
365
+ const native = channel.port1.native;
366
+
367
+ channel.port1[Symbol.dispose]();
368
+
369
+ const error = assertThrowsInstanceOf(
370
+ () => testCreateMessagePort(native),
371
+ Error,
372
+ );
373
+ assertTrue(error.message.includes("Unknown native port"));
374
+ });
375
+
376
+ it("throws for unknown native port", () => {
377
+ const unknownNative = {} as NativeMessagePort;
378
+ const error = assertThrowsInstanceOf(
379
+ () => testCreateMessagePort(unknownNative),
380
+ Error,
381
+ );
382
+ assertTrue(error.message.includes("Unknown native port"));
383
+ });
384
+ });
385
+
386
+ describe("createBroadcastChannel", () => {
387
+ it("postMessage delivers to other channels with the same name asynchronously", async () => {
388
+ using channel1 = createBroadcastChannel<string>("test-channel");
389
+ using channel2 = createBroadcastChannel<string>("test-channel");
390
+ const received: Array<string> = [];
391
+
392
+ channel2.onMessage = (message) => {
393
+ received.push(message);
394
+ };
395
+ channel1.postMessage("hello");
396
+
397
+ assertEqual(received, []);
398
+ await expectArrayAfterWorkerMessage(received, ["hello"]);
399
+ });
400
+
401
+ it("postMessage does not deliver to the sending channel", async () => {
402
+ using channel = createBroadcastChannel<string>("test-channel-self");
403
+ const received: Array<string> = [];
404
+
405
+ channel.onMessage = (message) => {
406
+ received.push(message);
407
+ };
408
+ channel.postMessage("ignored");
409
+
410
+ await testWaitForWorkerMessage();
411
+
412
+ assertEqual(received, []);
413
+ });
414
+
415
+ it("postMessage fans out to all other channels with the same name", async () => {
416
+ using channel1 = createBroadcastChannel<string>("test-channel-fanout");
417
+ using channel2 = createBroadcastChannel<string>("test-channel-fanout");
418
+ using channel3 = createBroadcastChannel<string>("test-channel-fanout");
419
+ const received2: Array<string> = [];
420
+ const received3: Array<string> = [];
421
+
422
+ channel2.onMessage = (message) => {
423
+ received2.push(message);
424
+ };
425
+ channel3.onMessage = (message) => {
426
+ received3.push(message);
427
+ };
428
+ channel1.postMessage("hello");
429
+
430
+ await testWaitForWorkerMessage();
431
+
432
+ assertEqual(received2, ["hello"]);
433
+ assertEqual(received3, ["hello"]);
434
+ });
435
+
436
+ it("channels with different names are isolated", async () => {
437
+ using channel1 = createBroadcastChannel<string>("test-channel-a");
438
+ using channel2 = createBroadcastChannel<string>("test-channel-b");
439
+ const received: Array<string> = [];
440
+
441
+ channel2.onMessage = (message) => {
442
+ received.push(message);
443
+ };
444
+ channel1.postMessage("ignored");
445
+
446
+ await testWaitForWorkerMessage();
447
+
448
+ assertEqual(received, []);
449
+ });
450
+
451
+ it("messages posted before onMessage is assigned are delivered if assigned before dispatch", async () => {
452
+ using channel1 = createBroadcastChannel<string>("test-channel-queued");
453
+ using channel2 = createBroadcastChannel<string>("test-channel-queued");
454
+ const received: Array<string> = [];
455
+
456
+ channel1.postMessage("queued");
457
+ channel2.onMessage = (message) => {
458
+ received.push(message);
459
+ };
460
+
461
+ await expectArrayAfterWorkerMessage(received, ["queued"]);
462
+ });
463
+
464
+ it("messages are dropped when no onMessage is assigned at dispatch", async () => {
465
+ using channel1 = createBroadcastChannel<string>("test-channel-drop");
466
+ using channel2 = createBroadcastChannel<string>("test-channel-drop");
467
+ const received: Array<string> = [];
468
+
469
+ channel1.postMessage("dropped");
470
+ await testWaitForWorkerMessage();
471
+ channel2.onMessage = (message) => {
472
+ received.push(message);
473
+ };
474
+
475
+ await testWaitForWorkerMessage();
476
+
477
+ assertEqual(received, []);
478
+ });
479
+
480
+ it("setting onMessage to null before dispatch drops the message", async () => {
481
+ using channel1 = createBroadcastChannel<string>("test-channel-null");
482
+ using channel2 = createBroadcastChannel<string>("test-channel-null");
483
+ const received: Array<string> = [];
484
+
485
+ channel2.onMessage = (message) => {
486
+ received.push(message);
487
+ };
488
+ channel1.postMessage("dropped");
489
+ channel2.onMessage = null;
490
+
491
+ await testWaitForWorkerMessage();
492
+
493
+ assertEqual(received, []);
494
+ });
495
+
496
+ it("dispose removes channel from future delivery", async () => {
497
+ using channel1 = createBroadcastChannel<string>("test-channel-dispose");
498
+ const channel2 = createBroadcastChannel<string>("test-channel-dispose");
499
+ const received: Array<string> = [];
500
+
501
+ channel2.onMessage = (message) => {
502
+ received.push(message);
503
+ };
504
+ channel2[Symbol.dispose]();
505
+ channel1.postMessage("ignored");
506
+
507
+ await testWaitForWorkerMessage();
508
+
509
+ assertEqual(received, []);
510
+ });
511
+
512
+ it("dispose before scheduled dispatch drops the message", async () => {
513
+ using channel1 = createBroadcastChannel<string>(
514
+ "test-channel-dispose-queued",
515
+ );
516
+ const channel2 = createBroadcastChannel<string>(
517
+ "test-channel-dispose-queued",
518
+ );
519
+ const received: Array<string> = [];
520
+
521
+ channel2.onMessage = (message) => {
522
+ received.push(message);
523
+ };
524
+ channel1.postMessage("queued");
525
+ channel2[Symbol.dispose]();
526
+
527
+ await testWaitForWorkerMessage();
528
+
529
+ assertEqual(received, []);
530
+ });
531
+
532
+ it("disposed channel postMessage asserts not disposed", () => {
533
+ const channel = createBroadcastChannel<string>(
534
+ "test-channel-disposed-send",
535
+ );
536
+
537
+ channel[Symbol.dispose]();
538
+
539
+ const error = assertThrowsInstanceOf(
540
+ () => channel.postMessage("ignored"),
541
+ Error,
542
+ );
543
+ assertTrue(error.message.includes("Cannot use a disposed object."));
544
+ });
545
+
546
+ it("disposed channel ignores onMessage reassignment", async () => {
547
+ using channel1 = createBroadcastChannel<string>(
548
+ "test-channel-disposed-send",
549
+ );
550
+ using channel2 = createBroadcastChannel<string>(
551
+ "test-channel-disposed-send",
552
+ );
553
+ const received: Array<string> = [];
554
+
555
+ channel1.onMessage = (message) => {
556
+ received.push(message);
557
+ };
558
+ channel1[Symbol.dispose]();
559
+ channel1.onMessage = (message) => {
560
+ received.push(message);
561
+ };
562
+ channel2.postMessage("ignored");
563
+
564
+ await testWaitForWorkerMessage();
565
+
566
+ assertSame(channel1.onMessage, null);
567
+ assertEqual(received, []);
568
+ });
569
+ });
570
+
571
+ describe("testCreateWorker", () => {
572
+ it("worker and self communicate through ports asynchronously", async () => {
573
+ const worker = testCreateWorker<string, number>();
574
+ const workerReceived: Array<number> = [];
575
+ const selfReceived: Array<string> = [];
576
+
577
+ worker.onMessage = (msg) => {
578
+ workerReceived.push(msg);
579
+ };
580
+ worker.self.onMessage = (msg) => {
581
+ selfReceived.push(msg);
582
+ };
583
+
584
+ worker.postMessage("to-self");
585
+ worker.self.postMessage(123);
586
+
587
+ await testWaitForWorkerMessage();
588
+
589
+ assertEqual(selfReceived, ["to-self"]);
590
+ assertEqual(workerReceived, [123]);
591
+ });
592
+
593
+ it("messages are queued until onMessage is assigned and then flushed asynchronously", async () => {
594
+ const worker = testCreateWorker<string>();
595
+ worker.postMessage("queued");
596
+
597
+ const received: Array<string> = [];
598
+ worker.self.onMessage = (msg) => {
599
+ received.push(msg);
600
+ };
601
+
602
+ await expectArrayAfterWorkerMessage(received, ["queued"]);
603
+ });
604
+
605
+ it("worker dispose clears handlers", () => {
606
+ const worker = testCreateWorker<string>();
607
+ worker.onMessage = constVoid;
608
+ worker.self.onMessage = constVoid;
609
+
610
+ worker[Symbol.dispose]();
611
+
612
+ assertSame(worker.onMessage, null);
613
+ assertSame(worker.self.onMessage, null);
614
+ });
615
+
616
+ it("self dispose clears self handler", () => {
617
+ const worker = testCreateWorker<string>();
618
+ worker.self.onMessage = constVoid;
619
+
620
+ worker.self[Symbol.dispose]();
621
+
622
+ assertSame(worker.self.onMessage, null);
623
+ });
624
+ });
625
+
626
+ describe("testCreateSharedWorker", () => {
627
+ it("connect triggers onConnect with worker port", () => {
628
+ const worker = testCreateSharedWorker<string>();
629
+ let connected = false;
630
+ worker.self.onConnect = () => {
631
+ connected = true;
632
+ };
633
+ worker.connect();
634
+ assertTrue(connected);
635
+ });
636
+
637
+ it("connect throws when onConnect is null", () => {
638
+ const worker = testCreateSharedWorker<string>();
639
+ const error = assertThrowsInstanceOf(() => worker.connect(), Error);
640
+ assertTrue(
641
+ error.message.includes(
642
+ "onConnect must be set before receiving connections",
643
+ ),
644
+ );
645
+ });
646
+
647
+ it("worker and self communicate through ports asynchronously", async () => {
648
+ const worker = testCreateSharedWorker<string, number>();
649
+ const workerReceived: Array<string> = [];
650
+ const clientReceived: Array<number> = [];
651
+
652
+ worker.self.onConnect = (port) => {
653
+ port.onMessage = (msg) => {
654
+ workerReceived.push(msg);
655
+ };
656
+ port.postMessage(99);
657
+ };
658
+
659
+ worker.port.onMessage = (msg) => {
660
+ clientReceived.push(msg);
661
+ };
662
+ worker.connect();
663
+
664
+ worker.port.postMessage("hello");
665
+
666
+ await testWaitForWorkerMessage();
667
+
668
+ assertEqual(workerReceived, ["hello"]);
669
+ assertEqual(clientReceived, [99]);
670
+ });
671
+
672
+ it("messages sent before connect are queued and delivered asynchronously", async () => {
673
+ const worker = testCreateSharedWorker<string>();
674
+ worker.port.postMessage("before-connect");
675
+
676
+ const received: Array<string> = [];
677
+ worker.self.onConnect = (port) => {
678
+ port.onMessage = (msg) => {
679
+ received.push(msg);
680
+ };
681
+ };
682
+ worker.connect();
683
+
684
+ await expectArrayAfterWorkerMessage(received, ["before-connect"]);
685
+ });
686
+
687
+ it("worker dispose disposes channel", () => {
688
+ const worker = testCreateSharedWorker<string>();
689
+ worker.port.onMessage = constVoid;
690
+ assertNotNull(worker.port.onMessage);
691
+ worker[Symbol.dispose]();
692
+ assertSame(worker.port.onMessage, null);
693
+ });
694
+
695
+ it("self dispose nulls onConnect", () => {
696
+ const worker = testCreateSharedWorker<string>();
697
+ worker.self.onConnect = constVoid;
698
+ assertNotNull(worker.self.onConnect);
699
+ worker.self[Symbol.dispose]();
700
+ assertSame(worker.self.onConnect, null);
701
+ });
702
+ });
703
+
704
+ describe("testCreateBroadcastChannel", () => {
705
+ it("forwards postMessage and onMessage to the underlying channel", async () => {
706
+ using channel1 = testCreateBroadcastChannel<string>("test-channel-helper");
707
+ using channel2 = testCreateBroadcastChannel<string>("test-channel-helper");
708
+ const received: Array<string> = [];
709
+
710
+ channel2.onMessage = (message) => {
711
+ received.push(message);
712
+ };
713
+ channel1.postMessage("hello");
714
+
715
+ await expectArrayAfterWorkerMessage(received, ["hello"]);
716
+
717
+ assertNotNull(channel2.onMessage);
718
+ });
719
+
720
+ it("isDisposed reflects disposal state", () => {
721
+ const channel = testCreateBroadcastChannel<string>("test-channel-state");
722
+
723
+ assertFalse(channel.isDisposed());
724
+ channel[Symbol.dispose]();
725
+ assertTrue(channel.isDisposed());
726
+ });
727
+ });
728
+
729
+ describe("testWaitForWorkerMessage", () => {
730
+ it("waits for scheduled worker message delivery", async () => {
731
+ const channel = testCreateMessageChannel<string>();
732
+ const received: Array<string> = [];
733
+ channel.port2.onMessage = (message) => {
734
+ received.push(message);
735
+ };
736
+ channel.port1.postMessage("delivered");
737
+
738
+ assertEqual(received, []);
739
+ await testWaitForWorkerMessage();
740
+ assertEqual(received, ["delivered"]);
741
+ });
742
+
743
+ it("waits for delivery scheduled during the idle checkpoint", async () => {
744
+ const idle = testWaitForWorkerMessage();
745
+ const channel = testCreateMessageChannel<string>();
746
+ const received: Array<string> = [];
747
+ channel.port2.onMessage = (message) => {
748
+ received.push(message);
749
+ };
750
+ channel.port1.postMessage("delivered");
751
+
752
+ await idle;
753
+ assertEqual(received, ["delivered"]);
754
+ });
755
+ });