orez 0.2.26 → 0.2.29

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 (172) hide show
  1. package/dist/cf-do/worker.d.ts.map +1 -1
  2. package/dist/cf-do/worker.js +9 -1
  3. package/dist/cf-do/worker.js.map +1 -1
  4. package/dist/pg-proxy-do-backend.d.ts +2 -0
  5. package/dist/pg-proxy-do-backend.d.ts.map +1 -1
  6. package/dist/pg-proxy-do-backend.js +49 -7
  7. package/dist/pg-proxy-do-backend.js.map +1 -1
  8. package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
  9. package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
  10. package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
  11. package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
  12. package/dist/pg-sqlite-compiler/index.d.ts +12 -0
  13. package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
  14. package/dist/pg-sqlite-compiler/index.js +59 -0
  15. package/dist/pg-sqlite-compiler/index.js.map +1 -0
  16. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
  17. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
  18. package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
  19. package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
  20. package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
  21. package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
  22. package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
  23. package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
  24. package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
  25. package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
  26. package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
  27. package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
  28. package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
  29. package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
  30. package/dist/pg-sqlite-compiler/passes/index.js +39 -0
  31. package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
  32. package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
  33. package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
  34. package/dist/pg-sqlite-compiler/passes/types.js +103 -0
  35. package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
  36. package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
  37. package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
  38. package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
  39. package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
  40. package/dist/pg-sqlite-compiler/types.d.ts +55 -0
  41. package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
  42. package/dist/pg-sqlite-compiler/types.js +2 -0
  43. package/dist/pg-sqlite-compiler/types.js.map +1 -0
  44. package/package.json +8 -4
  45. package/src/admin/admin-data.test.ts +0 -348
  46. package/src/admin/http-proxy.ts +0 -252
  47. package/src/admin/log-store.ts +0 -192
  48. package/src/admin/server.ts +0 -471
  49. package/src/admin/ui.ts +0 -1322
  50. package/src/bench/proxy-throughput.bench.ts +0 -343
  51. package/src/bench/serial-mutations.bench.ts +0 -270
  52. package/src/browser.ts +0 -203
  53. package/src/cf-do/.wrangler/cache/cf.json +0 -1
  54. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
  55. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  56. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  57. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
  58. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
  59. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
  60. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
  61. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
  62. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
  63. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
  64. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
  65. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
  66. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
  67. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
  68. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
  69. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
  70. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
  71. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
  72. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
  73. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  74. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  75. package/src/cf-do/ARCHITECTURE.md +0 -83
  76. package/src/cf-do/watermark.test.ts +0 -103
  77. package/src/cf-do/watermark.ts +0 -118
  78. package/src/cf-do/worker.ts +0 -1033
  79. package/src/cf-do/wrangler.toml +0 -11
  80. package/src/cf-pglite/README.md +0 -19
  81. package/src/change-tracking.ts +0 -25
  82. package/src/child-process.test.ts +0 -147
  83. package/src/child-process.ts +0 -90
  84. package/src/cli-entry.ts +0 -72
  85. package/src/cli.test.ts +0 -38
  86. package/src/cli.ts +0 -1214
  87. package/src/config.ts +0 -150
  88. package/src/do-sql-tracking.test.ts +0 -19
  89. package/src/do-sql-tracking.ts +0 -19
  90. package/src/index.ts +0 -1215
  91. package/src/integration/integration.test.ts +0 -517
  92. package/src/integration/native-binary.guard.test.ts +0 -13
  93. package/src/integration/native-startup.test.ts +0 -44
  94. package/src/integration/replication-latency.test.ts +0 -428
  95. package/src/integration/restore-live-stress.test.ts +0 -433
  96. package/src/integration/restore-reset.test.ts +0 -400
  97. package/src/integration/restore.test.ts +0 -274
  98. package/src/integration/test-permissions.ts +0 -147
  99. package/src/load-config.ts +0 -46
  100. package/src/log.ts +0 -96
  101. package/src/mutex.ts +0 -47
  102. package/src/pg-proxy-browser.singledb.test.ts +0 -233
  103. package/src/pg-proxy-browser.ts +0 -2022
  104. package/src/pg-proxy-do-backend.test.ts +0 -3890
  105. package/src/pg-proxy-do-backend.ts +0 -7157
  106. package/src/pg-proxy.ts +0 -1087
  107. package/src/pglite-ipc.test.ts +0 -116
  108. package/src/pglite-ipc.ts +0 -266
  109. package/src/pglite-manager.ts +0 -557
  110. package/src/pglite-web-proxy.test.ts +0 -57
  111. package/src/pglite-web-proxy.ts +0 -221
  112. package/src/pglite-web-worker.ts +0 -152
  113. package/src/pglite-worker-thread.ts +0 -253
  114. package/src/port.ts +0 -25
  115. package/src/process-title.ts +0 -9
  116. package/src/recovery.ts +0 -155
  117. package/src/replication/change-tracker.test.ts +0 -357
  118. package/src/replication/change-tracker.ts +0 -279
  119. package/src/replication/handler.test.ts +0 -511
  120. package/src/replication/handler.ts +0 -1190
  121. package/src/replication/pgoutput-encoder.test.ts +0 -697
  122. package/src/replication/pgoutput-encoder.ts +0 -373
  123. package/src/replication/tcp-replication.test.ts +0 -876
  124. package/src/replication/zero-compat.test.ts +0 -1150
  125. package/src/restore-stress.test.ts +0 -188
  126. package/src/s3-local.ts +0 -203
  127. package/src/shim/hooks.mjs +0 -120
  128. package/src/shim/register.mjs +0 -4
  129. package/src/sqlite-mode/apply-mode.ts +0 -224
  130. package/src/sqlite-mode/index.ts +0 -15
  131. package/src/sqlite-mode/native-binary.ts +0 -89
  132. package/src/sqlite-mode/package-resolve.ts +0 -17
  133. package/src/sqlite-mode/resolve-mode.ts +0 -80
  134. package/src/sqlite-mode/shim-template.ts +0 -159
  135. package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
  136. package/src/sqlite-mode/types.ts +0 -30
  137. package/src/vite-plugin.ts +0 -67
  138. package/src/wasm-sqlite.test.ts +0 -537
  139. package/src/worker/browser-admin.ts +0 -52
  140. package/src/worker/browser-build-config.test.ts +0 -71
  141. package/src/worker/browser-build-config.ts +0 -109
  142. package/src/worker/browser-embed-admin.test.ts +0 -75
  143. package/src/worker/browser-embed.ts +0 -345
  144. package/src/worker/cf-patches.ts +0 -384
  145. package/src/worker/embed-integration.test.ts +0 -321
  146. package/src/worker/index.ts +0 -138
  147. package/src/worker/shims/fastify.test.ts +0 -255
  148. package/src/worker/shims/fastify.ts +0 -306
  149. package/src/worker/shims/http-service.test.ts +0 -355
  150. package/src/worker/shims/http-service.ts +0 -293
  151. package/src/worker/shims/node-stub.ts +0 -290
  152. package/src/worker/shims/oxfmt.ts +0 -3
  153. package/src/worker/shims/postgres-browser.ts +0 -59
  154. package/src/worker/shims/postgres-socket.test.ts +0 -576
  155. package/src/worker/shims/postgres-socket.ts +0 -310
  156. package/src/worker/shims/postgres.test.ts +0 -364
  157. package/src/worker/shims/postgres.ts +0 -1454
  158. package/src/worker/shims/sqlite-browser.test.ts +0 -233
  159. package/src/worker/shims/sqlite-browser.ts +0 -175
  160. package/src/worker/shims/sqlite.test.ts +0 -786
  161. package/src/worker/shims/sqlite.ts +0 -978
  162. package/src/worker/shims/stream-browser.ts +0 -15
  163. package/src/worker/shims/ws-browser.test.ts +0 -205
  164. package/src/worker/shims/ws-browser.ts +0 -248
  165. package/src/worker/shims/ws.test.ts +0 -288
  166. package/src/worker/shims/ws.ts +0 -467
  167. package/src/worker/shims/zero-process-env.ts +0 -11
  168. package/src/worker/types.ts +0 -75
  169. package/src/worker/worker-integration.test.ts +0 -223
  170. package/src/worker/worker.test.ts +0 -136
  171. package/src/worker/zero-cache-embed-cf.ts +0 -463
  172. package/src/worker/zero-cache-embed.ts +0 -277
@@ -1,15 +0,0 @@
1
- /**
2
- * stream shim — re-exports readable-stream with missing Node.js stream functions.
3
- *
4
- * readable-stream/stream-browserify don't include getDefaultHighWaterMark
5
- * which zero-cache uses (added in Node.js 18+).
6
- */
7
-
8
- // @ts-expect-error — readable-stream is CJS
9
- export * from 'readable-stream'
10
- // @ts-expect-error — readable-stream is CJS
11
- export { default } from 'readable-stream'
12
-
13
- export function getDefaultHighWaterMark(objectMode: boolean): number {
14
- return objectMode ? 16 : 16 * 1024
15
- }
@@ -1,205 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
-
3
- import { messagePortToWs, browserWsToWs, createInProcessPair } from './ws-browser.js'
4
-
5
- describe('messagePortToWs', () => {
6
- // create a mock MessagePort pair
7
- function createMockPorts() {
8
- type Handler = (event: MessageEvent) => void
9
- let handler1: Handler | null = null
10
- let handler2: Handler | null = null
11
- let closed1 = false
12
- let closed2 = false
13
-
14
- const port1 = {
15
- set onmessage(h: Handler | null) {
16
- handler1 = h
17
- },
18
- get onmessage() {
19
- return handler1
20
- },
21
- onmessageerror: null as Handler | null,
22
- postMessage(data: unknown) {
23
- if (closed2) return
24
- // deliver to port2's handler
25
- handler2?.({ data } as MessageEvent)
26
- },
27
- close() {
28
- closed1 = true
29
- },
30
- }
31
-
32
- const port2 = {
33
- set onmessage(h: Handler | null) {
34
- handler2 = h
35
- },
36
- get onmessage() {
37
- return handler2
38
- },
39
- onmessageerror: null as Handler | null,
40
- postMessage(data: unknown) {
41
- if (closed1) return
42
- handler1?.({ data } as MessageEvent)
43
- },
44
- close() {
45
- closed2 = true
46
- },
47
- }
48
-
49
- return [port1, port2] as [typeof port1, typeof port2]
50
- }
51
-
52
- it('wraps a MessagePort as WebSocket-like', () => {
53
- const [port1] = createMockPorts()
54
- const ws = messagePortToWs(port1 as any)
55
- expect(ws.readyState).toBe(1) // OPEN
56
- expect(ws.send).toBeInstanceOf(Function)
57
- expect(ws.close).toBeInstanceOf(Function)
58
- })
59
-
60
- it('send() calls port.postMessage', () => {
61
- const [port1] = createMockPorts()
62
- const spy = vi.spyOn(port1, 'postMessage')
63
- const ws = messagePortToWs(port1 as any)
64
-
65
- ws.send('hello')
66
- expect(spy).toHaveBeenCalledWith('hello')
67
- })
68
-
69
- it('send() invokes the ws callback after posting', () => {
70
- const [port1] = createMockPorts()
71
- const ws = messagePortToWs(port1 as any)
72
- const callback = vi.fn()
73
-
74
- ws.send('hello', callback)
75
-
76
- expect(callback).toHaveBeenCalledWith()
77
- })
78
-
79
- it('send() reports an error to the callback after close', () => {
80
- const [port1] = createMockPorts()
81
- const ws = messagePortToWs(port1 as any)
82
- const callback = vi.fn()
83
-
84
- ws.close()
85
- ws.send('ignored', callback)
86
-
87
- expect(callback).toHaveBeenCalledWith(expect.any(Error))
88
- })
89
-
90
- it('forwards port messages as ws message events', () => {
91
- const [port1, port2] = createMockPorts()
92
- const ws1 = messagePortToWs(port1 as any)
93
-
94
- const handler = vi.fn()
95
- ws1.addEventListener('message', handler)
96
-
97
- // send from port2 → should arrive at ws1
98
- port2.postMessage('world')
99
- expect(handler).toHaveBeenCalledWith({ data: 'world' })
100
- })
101
-
102
- it('close() sets readyState to CLOSED and fires event', () => {
103
- const [port1] = createMockPorts()
104
- const ws = messagePortToWs(port1 as any)
105
-
106
- const closeHandler = vi.fn()
107
- ws.addEventListener('close', closeHandler)
108
-
109
- ws.close(1000)
110
- expect(ws.readyState).toBe(3) // CLOSED
111
- expect(closeHandler).toHaveBeenCalledWith(
112
- expect.objectContaining({ code: 1000, wasClean: true })
113
- )
114
- })
115
-
116
- it('send() is no-op after close', () => {
117
- const [port1] = createMockPorts()
118
- const spy = vi.spyOn(port1, 'postMessage')
119
- const ws = messagePortToWs(port1 as any)
120
-
121
- ws.close()
122
- ws.send('ignored')
123
- // postMessage should not be called after close
124
- expect(spy).not.toHaveBeenCalled()
125
- })
126
-
127
- it('removeEventListener works', () => {
128
- const [port1] = createMockPorts()
129
- const ws = messagePortToWs(port1 as any)
130
-
131
- const handler = vi.fn()
132
- ws.addEventListener('message', handler)
133
- ws.removeEventListener('message', handler)
134
-
135
- // simulate message — handler should not be called
136
- ;(port1 as any).onmessage?.({ data: 'test' })
137
- // the handler was removed from ws listeners but port.onmessage still fires
138
- // however ws.addEventListener wraps the handler, so the original shouldn't fire
139
- // ... actually the port.onmessage fires the ws internal handler which checks listeners
140
- // this is an implementation detail. just verify no error is thrown.
141
- })
142
- })
143
-
144
- describe('browserWsToWs', () => {
145
- it('wraps a browser WebSocket', () => {
146
- const mockWs = {
147
- readyState: 1,
148
- send: vi.fn(),
149
- close: vi.fn(),
150
- addEventListener: vi.fn(),
151
- removeEventListener: vi.fn(),
152
- }
153
-
154
- const ws = browserWsToWs(mockWs as any)
155
- expect(ws.readyState).toBe(1)
156
-
157
- ws.send('test')
158
- expect(mockWs.send).toHaveBeenCalledWith('test')
159
-
160
- ws.close(1000, 'bye')
161
- expect(mockWs.close).toHaveBeenCalledWith(1000, 'bye')
162
- })
163
- })
164
-
165
- describe('createInProcessPair', () => {
166
- it('creates a connected pair', () => {
167
- const [client, server] = createInProcessPair()
168
- expect(client.readyState).toBe(1)
169
- expect(server.readyState).toBe(1)
170
- })
171
-
172
- it('messages flow between client and server', async () => {
173
- const [client, server] = createInProcessPair()
174
-
175
- // MessageChannel delivers messages asynchronously in Node.js
176
- const serverReceived = new Promise<string>((resolve) => {
177
- server.addEventListener('message', (event: any) => {
178
- resolve(event.data)
179
- })
180
- })
181
-
182
- client.send('hello from client')
183
- expect(await serverReceived).toBe('hello from client')
184
-
185
- const clientReceived = new Promise<string>((resolve) => {
186
- client.addEventListener('message', (event: any) => {
187
- resolve(event.data)
188
- })
189
- })
190
-
191
- server.send('hello from server')
192
- expect(await clientReceived).toBe('hello from server')
193
- })
194
-
195
- it('close sets readyState', () => {
196
- const [client] = createInProcessPair()
197
-
198
- const closeHandler = vi.fn()
199
- client.addEventListener('close', closeHandler)
200
-
201
- client.close(1000)
202
- expect(client.readyState).toBe(3)
203
- expect(closeHandler).toHaveBeenCalled()
204
- })
205
- })
@@ -1,248 +0,0 @@
1
- /**
2
- * browser WebSocket/MessagePort adapter for the ws shim.
3
- *
4
- * wraps a browser WebSocket or MessagePort to match the interface
5
- * that the existing ws shim (orez/worker/shims/ws) expects. this lets
6
- * zero-cache handle WebSocket connections in browser Web Workers.
7
- *
8
- * usage:
9
- * import { messagePortToWs, browserWsToWs } from 'orez/worker/shims/ws-browser'
10
- *
11
- * // from a MessageChannel (worker ↔ main thread)
12
- * const channel = new MessageChannel()
13
- * const serverWs = messagePortToWs(channel.port2)
14
- *
15
- * // from a browser WebSocket
16
- * const ws = new WebSocket('ws://...')
17
- * const shimWs = browserWsToWs(ws)
18
- */
19
-
20
- // the interface that the ws shim expects (same as CF WebSocket)
21
- // on/off are needed because createWebSocketStream uses ws.on('message')
22
- interface WsCompatible {
23
- // ws constants — zero-cache uses ws.OPEN / ws.CONNECTING in switch statements
24
- CONNECTING: number
25
- OPEN: number
26
- CLOSING: number
27
- CLOSED: number
28
- readyState: number
29
- send(
30
- data: string | ArrayBuffer | ArrayBufferView,
31
- callback?: (err?: Error) => void
32
- ): void
33
- close(code?: number, reason?: string): void
34
- addEventListener(type: string, handler: (event: any) => void): void
35
- removeEventListener(type: string, handler: (event: any) => void): void
36
- on(type: string, handler: (event: any) => void): void
37
- off(type: string, handler: (event: any) => void): void
38
- }
39
-
40
- /**
41
- * wrap a MessagePort to look like a WebSocket.
42
- *
43
- * MessagePort uses postMessage/onmessage, while the ws shim expects
44
- * send/addEventListener('message'). this bridges the two.
45
- *
46
- * messages that arrive before any listener is registered are buffered
47
- * and replayed when the first 'message' listener is added. this prevents
48
- * a race where the port starts receiving data before the connection
49
- * handler (e.g. createWebSocketStream / proxyInbound) has set up its
50
- * listener — which would silently drop messages.
51
- */
52
- export function messagePortToWs(port: MessagePort): WsCompatible {
53
- // separate listener sets for on() vs addEventListener() to match real ws behavior.
54
- // in the ws package, on() is EventEmitter-style and addEventListener() is DOM-style.
55
- // createWebSocketStream uses ws.on('message') exclusively for inbound data.
56
- // streamOut uses ws.addEventListener('message') for ack handling.
57
- // if they share the same set, ack messages reach both handlers and
58
- // handleMessage tries to parse acks as protocol messages, closing the connection.
59
- const onListeners = new Map<string, Set<(event: any) => void>>()
60
- const domListeners = new Map<string, Set<(event: any) => void>>()
61
- let closed = false
62
-
63
- // buffer messages until a 'message' listener is registered (either kind)
64
- const pendingMessages: any[] = []
65
-
66
- function addOnListener(type: string, handler: (event: any) => void) {
67
- if (!onListeners.has(type)) onListeners.set(type, new Set())
68
- onListeners.get(type)!.add(handler)
69
- if (type === 'message' && pendingMessages.length > 0) {
70
- const queued = pendingMessages.splice(0)
71
- for (const event of queued) handler(event)
72
- }
73
- }
74
-
75
- function removeOnListener(type: string, handler: (event: any) => void) {
76
- onListeners.get(type)?.delete(handler)
77
- }
78
-
79
- function addDomListener(type: string, handler: (event: any) => void) {
80
- if (!domListeners.has(type)) domListeners.set(type, new Set())
81
- domListeners.get(type)!.add(handler)
82
- if (type === 'message' && pendingMessages.length > 0) {
83
- const queued = pendingMessages.splice(0)
84
- for (const event of queued) handler(event)
85
- }
86
- }
87
-
88
- function removeDomListener(type: string, handler: (event: any) => void) {
89
- domListeners.get(type)?.delete(handler)
90
- }
91
-
92
- function emit(type: string, event: any) {
93
- const onHandlers = onListeners.get(type)
94
- const domHandlers = domListeners.get(type)
95
- const hasAny =
96
- (onHandlers && onHandlers.size > 0) || (domHandlers && domHandlers.size > 0)
97
- if (!hasAny) {
98
- if (type === 'message') pendingMessages.push(event)
99
- return
100
- }
101
- if (onHandlers) for (const h of onHandlers) h(event)
102
- if (domHandlers) for (const h of domHandlers) h(event)
103
- }
104
-
105
- // forward port messages → ws 'message' events
106
- // filter out control messages from sync-ws-patch.js (__close, __open)
107
- port.onmessage = (event: MessageEvent) => {
108
- const data = event.data
109
- // control messages from sync-ws-patch.js — handle as close/open events
110
- if (
111
- data &&
112
- typeof data === 'object' &&
113
- !ArrayBuffer.isView(data) &&
114
- !(data instanceof ArrayBuffer)
115
- ) {
116
- if (data.__close) {
117
- closed = true
118
- port.close()
119
- emit('close', {
120
- code: data.code ?? 1000,
121
- reason: data.reason ?? '',
122
- wasClean: true,
123
- })
124
- return
125
- }
126
- if (data.__open) {
127
- // already open, ignore
128
- return
129
- }
130
- }
131
- emit('message', { data })
132
- }
133
-
134
- port.onmessageerror = (event: MessageEvent) => {
135
- emit('error', { message: 'MessagePort error', error: event })
136
- }
137
-
138
- return {
139
- // ws constants — zero-cache uses ws.OPEN / ws.CONNECTING in switch statements
140
- CONNECTING: 0,
141
- OPEN: 1,
142
- CLOSING: 2,
143
- CLOSED: 3,
144
-
145
- get readyState() {
146
- return closed ? 3 : 1 // CLOSED or OPEN
147
- },
148
-
149
- send(data: string | ArrayBuffer | ArrayBufferView, callback?: (err?: Error) => void) {
150
- if (closed) {
151
- callback?.(new Error('WebSocket is closed'))
152
- return
153
- }
154
-
155
- try {
156
- port.postMessage(data)
157
- } catch (error) {
158
- const err = error instanceof Error ? error : new Error(String(error))
159
- if (callback) {
160
- callback(err)
161
- return
162
- }
163
- throw err
164
- }
165
-
166
- callback?.()
167
- },
168
-
169
- close(code?: number, _reason?: string) {
170
- if (closed) return
171
- closed = true
172
- port.close()
173
- emit('close', { code: code ?? 1000, reason: '', wasClean: true })
174
- },
175
-
176
- addEventListener: addDomListener,
177
- removeEventListener: removeDomListener,
178
- on: addOnListener,
179
- off: removeOnListener,
180
- }
181
- }
182
-
183
- /**
184
- * wrap a browser WebSocket to match the ws shim's expected interface.
185
- *
186
- * browser WebSocket and the ws shim's CFWebSocket interface are very
187
- * similar but have subtle differences. this normalizes them.
188
- */
189
- export function browserWsToWs(ws: WebSocket): WsCompatible {
190
- return {
191
- CONNECTING: 0,
192
- OPEN: 1,
193
- CLOSING: 2,
194
- CLOSED: 3,
195
-
196
- get readyState() {
197
- return ws.readyState
198
- },
199
-
200
- send(data: string | ArrayBuffer | ArrayBufferView, callback?: (err?: Error) => void) {
201
- try {
202
- ws.send(data)
203
- } catch (error) {
204
- const err = error instanceof Error ? error : new Error(String(error))
205
- if (callback) {
206
- callback(err)
207
- return
208
- }
209
- throw err
210
- }
211
-
212
- callback?.()
213
- },
214
-
215
- close(code?: number, reason?: string) {
216
- ws.close(code, reason)
217
- },
218
-
219
- addEventListener(type: string, handler: (event: any) => void) {
220
- ws.addEventListener(type, handler)
221
- },
222
-
223
- removeEventListener(type: string, handler: (event: any) => void) {
224
- ws.removeEventListener(type, handler)
225
- },
226
-
227
- on(type: string, handler: (event: any) => void) {
228
- ws.addEventListener(type, handler)
229
- },
230
-
231
- off(type: string, handler: (event: any) => void) {
232
- ws.removeEventListener(type, handler)
233
- },
234
- }
235
- }
236
-
237
- /**
238
- * create a connected pair of WebSocket-like objects for in-process
239
- * communication (e.g., when zero-cache and Zero client are in the
240
- * same browser context). uses MessageChannel internally.
241
- *
242
- * returns [client, server] — client goes to Zero client, server
243
- * goes to the browser embed's handleWebSocket().
244
- */
245
- export function createInProcessPair(): [WsCompatible, WsCompatible] {
246
- const channel = new MessageChannel()
247
- return [messagePortToWs(channel.port1), messagePortToWs(channel.port2)]
248
- }