on-zero 0.4.25 → 0.4.27

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 (140) hide show
  1. package/dist/cjs/createUseQuery.cjs +28 -16
  2. package/dist/cjs/createUseQuery.native.js +42 -30
  3. package/dist/cjs/createUseQuery.native.js.map +1 -1
  4. package/dist/cjs/createZeroClient.cjs +13 -1
  5. package/dist/cjs/createZeroClient.native.js +13 -1
  6. package/dist/cjs/createZeroClient.native.js.map +1 -1
  7. package/dist/cjs/httpPull/auth.test.cjs +197 -0
  8. package/dist/cjs/httpPull/auth.test.native.js +279 -0
  9. package/dist/cjs/httpPull/auth.test.native.js.map +1 -0
  10. package/dist/cjs/httpPull/churn.test.cjs +132 -0
  11. package/dist/cjs/httpPull/churn.test.native.js +155 -0
  12. package/dist/cjs/httpPull/churn.test.native.js.map +1 -0
  13. package/dist/cjs/httpPull/fixtureSchema.cjs +76 -0
  14. package/dist/cjs/httpPull/fixtureSchema.native.js +82 -0
  15. package/dist/cjs/httpPull/fixtureSchema.native.js.map +1 -0
  16. package/dist/cjs/httpPull/fixtureServer.cjs +340 -0
  17. package/dist/cjs/httpPull/fixtureServer.native.js +534 -0
  18. package/dist/cjs/httpPull/fixtureServer.native.js.map +1 -0
  19. package/dist/cjs/httpPull/integration.test.cjs +53 -0
  20. package/dist/cjs/httpPull/integration.test.native.js +60 -0
  21. package/dist/cjs/httpPull/integration.test.native.js.map +1 -0
  22. package/dist/cjs/httpPull/rebase.test.cjs +360 -0
  23. package/dist/cjs/httpPull/rebase.test.native.js +420 -0
  24. package/dist/cjs/httpPull/rebase.test.native.js.map +1 -0
  25. package/dist/cjs/httpPull/relations.test.cjs +107 -0
  26. package/dist/cjs/httpPull/relations.test.native.js +119 -0
  27. package/dist/cjs/httpPull/relations.test.native.js.map +1 -0
  28. package/dist/cjs/httpPull/testHarness.cjs +100 -0
  29. package/dist/cjs/httpPull/testHarness.native.js +112 -0
  30. package/dist/cjs/httpPull/testHarness.native.js.map +1 -0
  31. package/dist/cjs/httpPull/transport.test.cjs +568 -0
  32. package/dist/cjs/httpPull/transport.test.native.js +655 -0
  33. package/dist/cjs/httpPull/transport.test.native.js.map +1 -0
  34. package/dist/cjs/httpPullTransport.cjs +432 -0
  35. package/dist/cjs/httpPullTransport.native.js +695 -0
  36. package/dist/cjs/httpPullTransport.native.js.map +1 -0
  37. package/dist/cjs/index.cjs +1 -0
  38. package/dist/cjs/index.native.js +1 -0
  39. package/dist/cjs/index.native.js.map +1 -1
  40. package/dist/cjs/multiInstanceNested.test.cjs +26 -0
  41. package/dist/cjs/multiInstanceNested.test.native.js +34 -0
  42. package/dist/cjs/multiInstanceNested.test.native.js.map +1 -1
  43. package/dist/esm/createUseQuery.mjs +28 -16
  44. package/dist/esm/createUseQuery.mjs.map +1 -1
  45. package/dist/esm/createUseQuery.native.js +42 -30
  46. package/dist/esm/createUseQuery.native.js.map +1 -1
  47. package/dist/esm/createZeroClient.mjs +13 -1
  48. package/dist/esm/createZeroClient.mjs.map +1 -1
  49. package/dist/esm/createZeroClient.native.js +13 -1
  50. package/dist/esm/createZeroClient.native.js.map +1 -1
  51. package/dist/esm/httpPull/auth.test.mjs +198 -0
  52. package/dist/esm/httpPull/auth.test.mjs.map +1 -0
  53. package/dist/esm/httpPull/auth.test.native.js +277 -0
  54. package/dist/esm/httpPull/auth.test.native.js.map +1 -0
  55. package/dist/esm/httpPull/churn.test.mjs +133 -0
  56. package/dist/esm/httpPull/churn.test.mjs.map +1 -0
  57. package/dist/esm/httpPull/churn.test.native.js +153 -0
  58. package/dist/esm/httpPull/churn.test.native.js.map +1 -0
  59. package/dist/esm/httpPull/fixtureSchema.mjs +50 -0
  60. package/dist/esm/httpPull/fixtureSchema.mjs.map +1 -0
  61. package/dist/esm/httpPull/fixtureSchema.native.js +53 -0
  62. package/dist/esm/httpPull/fixtureSchema.native.js.map +1 -0
  63. package/dist/esm/httpPull/fixtureServer.mjs +315 -0
  64. package/dist/esm/httpPull/fixtureServer.mjs.map +1 -0
  65. package/dist/esm/httpPull/fixtureServer.native.js +506 -0
  66. package/dist/esm/httpPull/fixtureServer.native.js.map +1 -0
  67. package/dist/esm/httpPull/integration.test.mjs +54 -0
  68. package/dist/esm/httpPull/integration.test.mjs.map +1 -0
  69. package/dist/esm/httpPull/integration.test.native.js +58 -0
  70. package/dist/esm/httpPull/integration.test.native.js.map +1 -0
  71. package/dist/esm/httpPull/rebase.test.mjs +361 -0
  72. package/dist/esm/httpPull/rebase.test.mjs.map +1 -0
  73. package/dist/esm/httpPull/rebase.test.native.js +418 -0
  74. package/dist/esm/httpPull/rebase.test.native.js.map +1 -0
  75. package/dist/esm/httpPull/relations.test.mjs +108 -0
  76. package/dist/esm/httpPull/relations.test.mjs.map +1 -0
  77. package/dist/esm/httpPull/relations.test.native.js +117 -0
  78. package/dist/esm/httpPull/relations.test.native.js.map +1 -0
  79. package/dist/esm/httpPull/testHarness.mjs +72 -0
  80. package/dist/esm/httpPull/testHarness.mjs.map +1 -0
  81. package/dist/esm/httpPull/testHarness.native.js +81 -0
  82. package/dist/esm/httpPull/testHarness.native.js.map +1 -0
  83. package/dist/esm/httpPull/transport.test.mjs +569 -0
  84. package/dist/esm/httpPull/transport.test.mjs.map +1 -0
  85. package/dist/esm/httpPull/transport.test.native.js +653 -0
  86. package/dist/esm/httpPull/transport.test.native.js.map +1 -0
  87. package/dist/esm/httpPullTransport.mjs +406 -0
  88. package/dist/esm/httpPullTransport.mjs.map +1 -0
  89. package/dist/esm/httpPullTransport.native.js +666 -0
  90. package/dist/esm/httpPullTransport.native.js.map +1 -0
  91. package/dist/esm/index.js +1 -0
  92. package/dist/esm/index.js.map +1 -1
  93. package/dist/esm/index.mjs +1 -0
  94. package/dist/esm/index.mjs.map +1 -1
  95. package/dist/esm/index.native.js +1 -0
  96. package/dist/esm/index.native.js.map +1 -1
  97. package/dist/esm/multiInstanceNested.test.mjs +27 -1
  98. package/dist/esm/multiInstanceNested.test.mjs.map +1 -1
  99. package/dist/esm/multiInstanceNested.test.native.js +35 -1
  100. package/dist/esm/multiInstanceNested.test.native.js.map +1 -1
  101. package/package.json +2 -2
  102. package/src/createUseQuery.tsx +40 -22
  103. package/src/createZeroClient.tsx +19 -0
  104. package/src/httpPull/auth.test.ts +208 -0
  105. package/src/httpPull/churn.test.ts +147 -0
  106. package/src/httpPull/fixtureSchema.ts +82 -0
  107. package/src/httpPull/fixtureServer.ts +391 -0
  108. package/src/httpPull/integration.test.ts +57 -0
  109. package/src/httpPull/rebase.test.ts +368 -0
  110. package/src/httpPull/relations.test.ts +135 -0
  111. package/src/httpPull/testHarness.ts +95 -0
  112. package/src/httpPull/transport.test.ts +577 -0
  113. package/src/httpPullTransport.ts +559 -0
  114. package/src/index.ts +1 -0
  115. package/src/multiInstanceNested.test.tsx +25 -1
  116. package/types/createUseQuery.d.ts.map +1 -1
  117. package/types/createZeroClient.d.ts +3 -1
  118. package/types/createZeroClient.d.ts.map +1 -1
  119. package/types/httpPull/auth.test.d.ts +2 -0
  120. package/types/httpPull/auth.test.d.ts.map +1 -0
  121. package/types/httpPull/churn.test.d.ts +2 -0
  122. package/types/httpPull/churn.test.d.ts.map +1 -0
  123. package/types/httpPull/fixtureSchema.d.ts +111 -0
  124. package/types/httpPull/fixtureSchema.d.ts.map +1 -0
  125. package/types/httpPull/fixtureServer.d.ts +14 -0
  126. package/types/httpPull/fixtureServer.d.ts.map +1 -0
  127. package/types/httpPull/integration.test.d.ts +2 -0
  128. package/types/httpPull/integration.test.d.ts.map +1 -0
  129. package/types/httpPull/rebase.test.d.ts +2 -0
  130. package/types/httpPull/rebase.test.d.ts.map +1 -0
  131. package/types/httpPull/relations.test.d.ts +2 -0
  132. package/types/httpPull/relations.test.d.ts.map +1 -0
  133. package/types/httpPull/testHarness.d.ts +32 -0
  134. package/types/httpPull/testHarness.d.ts.map +1 -0
  135. package/types/httpPull/transport.test.d.ts +2 -0
  136. package/types/httpPull/transport.test.d.ts.map +1 -0
  137. package/types/httpPullTransport.d.ts +13 -0
  138. package/types/httpPullTransport.d.ts.map +1 -0
  139. package/types/index.d.ts +1 -0
  140. package/types/index.d.ts.map +1 -1
@@ -0,0 +1,559 @@
1
+ // http-pull transport: runs a stock @rocicorp/zero client over stateless HTTP
2
+ // by intercepting its /sync/v51/connect WebSocket with a shim that translates
3
+ // pull responses into v51 pokes. ported from the orez zero-http spike — the
4
+ // wire contract (lexicographic string cookies, gotQueriesPatch poke-part
5
+ // ordering, FIFO push serialization, updateAuth, 401→Unauthorized frame,
6
+ // teardown drain) is pinned by the tests in ./httpPull/ and documented in
7
+ // ~/orez/plans/zero-http.md. do not "simplify" any of it without re-running
8
+ // those tests against a stock zero client.
9
+
10
+ type WebSocketProtocols = string | string[] | undefined
11
+ type SocketEventType = 'open' | 'message' | 'close' | 'error'
12
+ type SocketListener = ((event: any) => void) | { handleEvent(event: any): void }
13
+
14
+ type WebSocketConstructor = {
15
+ new (url: string | URL, protocols?: WebSocketProtocols): any
16
+ CONNECTING?: number
17
+ OPEN?: number
18
+ CLOSING?: number
19
+ CLOSED?: number
20
+ }
21
+
22
+ type DesiredQueryPatchOp =
23
+ | { op: 'clear' }
24
+ | { op: 'put' | 'del'; hash: string; [key: string]: unknown }
25
+
26
+ type GotQueryPatchOp = { op: 'clear' } | { op: 'put' | 'del'; hash: string }
27
+
28
+ type PullResponse =
29
+ | {
30
+ cookie: number
31
+ lastMutationIDChanges: Record<string, number>
32
+ rowsPatch: unknown[]
33
+ unchanged?: false
34
+ }
35
+ | {
36
+ cookie: number | null
37
+ unchanged: true
38
+ }
39
+
40
+ type TransportState = {
41
+ readonly origin: URL
42
+ readonly originString: string
43
+ readonly fetch: typeof fetch
44
+ readonly nativeWebSocket: WebSocketConstructor | undefined
45
+ readonly sockets: Set<ZeroHttpSocket>
46
+ readonly pullIntervalMs: number | undefined
47
+ nextPokeID: number
48
+ }
49
+
50
+ const COOKIE_WIDTH = 20
51
+
52
+ export type HttpPullTransport = {
53
+ pull(): Promise<void>
54
+ readonly connections: number
55
+ uninstall(): void
56
+ }
57
+
58
+ export type HttpPullTransportOptions = {
59
+ origin: string
60
+ fetch?: typeof fetch
61
+ // when set, every open connection also pulls on this interval so
62
+ // server-initiated changes arrive without a client-side trigger
63
+ pullIntervalMs?: number
64
+ }
65
+
66
+ export function installHttpPullTransport(
67
+ opts: HttpPullTransportOptions,
68
+ ): HttpPullTransport {
69
+ const previousWebSocket = globalThis.WebSocket as WebSocketConstructor | undefined
70
+ const fetchImpl = opts.fetch ?? globalThis.fetch
71
+ if (!fetchImpl) {
72
+ throw new Error('installHttpPullTransport requires a fetch implementation')
73
+ }
74
+
75
+ const state: TransportState = {
76
+ origin: new URL(opts.origin),
77
+ originString: trimTrailingSlash(new URL(opts.origin).toString()),
78
+ fetch: fetchImpl,
79
+ nativeWebSocket: previousWebSocket,
80
+ sockets: new Set(),
81
+ pullIntervalMs: opts.pullIntervalMs,
82
+ nextPokeID: 0,
83
+ }
84
+
85
+ const Shim = class {
86
+ static CONNECTING = 0
87
+ static OPEN = 1
88
+ static CLOSING = 2
89
+ static CLOSED = 3
90
+
91
+ constructor(url: string | URL, protocols?: WebSocketProtocols) {
92
+ if (shouldIntercept(state.origin, url)) {
93
+ return new ZeroHttpSocket(state, url, protocols)
94
+ }
95
+ if (!state.nativeWebSocket) {
96
+ throw new Error(`No native WebSocket available for ${String(url)}`)
97
+ }
98
+ return new state.nativeWebSocket(url, protocols)
99
+ }
100
+ }
101
+
102
+ globalThis.WebSocket = Shim as unknown as typeof WebSocket
103
+
104
+ return {
105
+ pull: async () => {
106
+ await Promise.all([...state.sockets].map((socket) => socket.pull()))
107
+ },
108
+ get connections() {
109
+ return state.sockets.size
110
+ },
111
+ uninstall: () => {
112
+ if (globalThis.WebSocket === (Shim as unknown as typeof WebSocket)) {
113
+ globalThis.WebSocket = previousWebSocket as typeof WebSocket
114
+ }
115
+ },
116
+ }
117
+ }
118
+
119
+ // per-origin idempotent install for app usage (ProvideZero rotates zero
120
+ // instances against the same server; installing per rotation would chain
121
+ // shims unboundedly). installed transports live for the page lifetime.
122
+ const transportsByOrigin = new Map<string, HttpPullTransport>()
123
+
124
+ export function ensureHttpPullTransport(
125
+ opts: HttpPullTransportOptions,
126
+ ): HttpPullTransport {
127
+ const key = trimTrailingSlash(new URL(opts.origin).toString())
128
+ const existing = transportsByOrigin.get(key)
129
+ if (existing) return existing
130
+ const transport = installHttpPullTransport(opts)
131
+ transportsByOrigin.set(key, transport)
132
+ return transport
133
+ }
134
+
135
+ class ZeroHttpSocket {
136
+ readonly CONNECTING = 0
137
+ readonly OPEN = 1
138
+ readonly CLOSING = 2
139
+ readonly CLOSED = 3
140
+
141
+ readonly url: string
142
+ readyState = this.CONNECTING
143
+
144
+ private readonly connectURL: URL
145
+ private authToken: string | undefined
146
+ private readonly listeners: Record<SocketEventType, Set<SocketListener>> = {
147
+ open: new Set(),
148
+ message: new Set(),
149
+ close: new Set(),
150
+ error: new Set(),
151
+ }
152
+ private readonly clientID: string
153
+ private readonly clientGroupID: string
154
+ private readonly wsid: string
155
+ private cookie: string | null
156
+ private pendingGotQueriesPatch: GotQueryPatchOp[] = []
157
+ private pullInFlight: Promise<void> | undefined
158
+ private pullAfterCurrent = false
159
+ private pushChain: Promise<void> = Promise.resolve()
160
+ private nextLocalCookieID = 0
161
+ private openTimer: ReturnType<typeof setTimeout> | undefined
162
+ private pullTimer: ReturnType<typeof setInterval> | undefined
163
+
164
+ constructor(
165
+ private readonly state: TransportState,
166
+ url: string | URL,
167
+ protocols?: WebSocketProtocols,
168
+ ) {
169
+ this.connectURL = toHttpURL(url)
170
+ this.url = String(url)
171
+ this.clientID = this.connectURL.searchParams.get('clientID') ?? ''
172
+ this.clientGroupID = this.connectURL.searchParams.get('clientGroupID') ?? ''
173
+ this.wsid = this.connectURL.searchParams.get('wsid') ?? `zero-http-${Date.now()}`
174
+ const baseCookie = this.connectURL.searchParams.get('baseCookie')
175
+ this.cookie = baseCookie ? baseCookie : null
176
+
177
+ const decoded = decodeSecProtocol(protocols)
178
+ this.authToken = decoded.authToken
179
+ this.queueDesiredQueries(decoded.initConnectionMessage?.[1])
180
+
181
+ this.state.sockets.add(this)
182
+ this.openTimer = setTimeout(() => this.open(), 0)
183
+ }
184
+
185
+ addEventListener(type: SocketEventType, listener: SocketListener | null) {
186
+ if (listener) this.listeners[type]?.add(listener)
187
+ }
188
+
189
+ removeEventListener(type: SocketEventType, listener: SocketListener | null) {
190
+ if (listener) this.listeners[type]?.delete(listener)
191
+ }
192
+
193
+ dispatchEvent(event: { type: SocketEventType }) {
194
+ this.emit(event.type, event)
195
+ return true
196
+ }
197
+
198
+ send(data: string) {
199
+ if (this.readyState !== this.OPEN) {
200
+ throw new Error('cannot send on a socket that is not open')
201
+ }
202
+ const message = JSON.parse(data) as [string, any]
203
+ switch (message[0]) {
204
+ case 'initConnection':
205
+ case 'changeDesiredQueries':
206
+ this.queueDesiredQueries(message[1])
207
+ this.requestPullAfterCurrent()
208
+ return
209
+ case 'updateAuth':
210
+ this.authToken = (message[1] as { auth?: string }).auth
211
+ return
212
+ case 'push':
213
+ this.enqueuePush(message[1])
214
+ return
215
+ case 'ping':
216
+ this.emitMessage(['pong', {}])
217
+ return
218
+ case 'pull':
219
+ this.run(this.answerMutationRecoveryPull(message[1]))
220
+ return
221
+ case 'deleteClients':
222
+ case 'ackMutationResponses':
223
+ return
224
+ default:
225
+ throw new Error(`unsupported zero-http upstream message ${message[0]}`)
226
+ }
227
+ }
228
+
229
+ close(code = 1000, reason = '') {
230
+ if (this.readyState === this.CLOSED) return
231
+ if (this.openTimer) clearTimeout(this.openTimer)
232
+ if (this.pullTimer) clearInterval(this.pullTimer)
233
+ this.readyState = this.CLOSED
234
+ this.state.sockets.delete(this)
235
+ this.emit('close', { code, reason, wasClean: code <= 1001 })
236
+ }
237
+
238
+ pull(): Promise<void> {
239
+ if (this.readyState === this.CLOSED) return Promise.resolve()
240
+ if (this.pullInFlight) return this.pullInFlight
241
+ this.pullInFlight = this.fetchPull(this.clientGroupID, this.cookie)
242
+ .then((response) => {
243
+ if (response.unchanged) {
244
+ this.emitGotQueriesPatch(response.cookie)
245
+ return
246
+ }
247
+ this.emitPoke(response)
248
+ })
249
+ .catch((error) => {
250
+ this.fail(error)
251
+ throw error
252
+ })
253
+ .finally(async () => {
254
+ const pullAgain = this.pullAfterCurrent
255
+ this.pullAfterCurrent = false
256
+ this.pullInFlight = undefined
257
+ if (pullAgain && this.readyState !== this.CLOSED) await this.pull()
258
+ })
259
+ return this.pullInFlight
260
+ }
261
+
262
+ private open() {
263
+ if (this.readyState !== this.CONNECTING) return
264
+ this.readyState = this.OPEN
265
+ this.emit('open', {})
266
+ this.emitMessage(['connected', { wsid: this.wsid, timestamp: Date.now() }])
267
+ setTimeout(() => this.run(this.pull()), 0)
268
+ if (this.state.pullIntervalMs) {
269
+ this.pullTimer = setInterval(() => {
270
+ this.run(this.pull())
271
+ }, this.state.pullIntervalMs)
272
+ }
273
+ }
274
+
275
+ private queueDesiredQueries(body: unknown) {
276
+ const desiredQueriesPatch = (body as { desiredQueriesPatch?: unknown })
277
+ ?.desiredQueriesPatch
278
+ if (!Array.isArray(desiredQueriesPatch)) return
279
+ this.pendingGotQueriesPatch.push(...gotQueriesPatch(desiredQueriesPatch))
280
+ }
281
+
282
+ private async push(body: unknown) {
283
+ const response = (await this.postJSON('/push', body)) as {
284
+ pushResponse?: unknown
285
+ }
286
+ this.emitMessage(['pushResponse', response.pushResponse])
287
+ this.requestPullAfterCurrent()
288
+ }
289
+
290
+ private enqueuePush(body: unknown) {
291
+ const nextPush = this.pushChain.then(async () => {
292
+ if (this.readyState === this.CLOSED) return
293
+ await this.push(body)
294
+ })
295
+ this.pushChain = nextPush.catch(() => {})
296
+ this.run(nextPush)
297
+ }
298
+
299
+ private requestPullAfterCurrent() {
300
+ if (this.pullInFlight) {
301
+ this.pullAfterCurrent = true
302
+ return
303
+ }
304
+ this.run(this.pull())
305
+ }
306
+
307
+ private async answerMutationRecoveryPull(body: {
308
+ clientGroupID: string
309
+ cookie: string | null
310
+ requestID: string
311
+ }) {
312
+ const response = await this.fetchPull(body.clientGroupID, body.cookie)
313
+ const cookie = toWebSocketCookie(response.cookie)
314
+ this.emitMessage([
315
+ 'pull',
316
+ {
317
+ requestID: body.requestID,
318
+ cookie: cookie ?? this.cookie ?? '0',
319
+ lastMutationIDChanges: response.unchanged ? {} : response.lastMutationIDChanges,
320
+ },
321
+ ])
322
+ }
323
+
324
+ private async fetchPull(clientGroupID: string, cookie: string | null) {
325
+ return (await this.postJSON('/pull', {
326
+ clientID: this.clientID,
327
+ clientGroupID,
328
+ cookie: toHttpCookie(cookie),
329
+ })) as PullResponse
330
+ }
331
+
332
+ private async postJSON(path: '/pull' | '/push', body: unknown) {
333
+ const response = await this.state.fetch(`${this.state.originString}${path}`, {
334
+ method: 'POST',
335
+ headers: {
336
+ authorization: this.authToken ? `Bearer ${this.authToken}` : '',
337
+ 'content-type': 'application/json',
338
+ },
339
+ body: JSON.stringify(body),
340
+ })
341
+ if (!response.ok) {
342
+ throw new ZeroHttpResponseError(path, response.status)
343
+ }
344
+ return response.json()
345
+ }
346
+
347
+ private run(promise: Promise<void>) {
348
+ void promise.catch((error) => this.fail(error))
349
+ }
350
+
351
+ private fail(error: unknown) {
352
+ if (this.readyState === this.CLOSED) return
353
+ if (isAuthHTTPError(error)) {
354
+ this.emitMessage([
355
+ 'error',
356
+ {
357
+ kind: 'Unauthorized',
358
+ message: error.message,
359
+ origin: 'server',
360
+ },
361
+ ])
362
+ if (this.readyState !== this.CLOSED) this.close(1000, error.message)
363
+ return
364
+ }
365
+ this.emit('error', { error })
366
+ this.close(1011, errorMessage(error))
367
+ }
368
+
369
+ private emitPoke(response: Exclude<PullResponse, { unchanged: true }>) {
370
+ const nextCookie = toWebSocketCookie(response.cookie)
371
+ if (isStaleCookie(this.cookie, response.cookie)) {
372
+ throw new Error(
373
+ `zero-http pull returned stale cookie ${response.cookie} for ${this.cookie}`,
374
+ )
375
+ }
376
+
377
+ const pokeID = `zero-http-${++this.state.nextPokeID}`
378
+ const gotQueries = this.pendingGotQueriesPatch
379
+ this.pendingGotQueriesPatch = []
380
+
381
+ this.emitMessage([
382
+ 'pokeStart',
383
+ {
384
+ pokeID,
385
+ baseCookie: this.cookie,
386
+ schemaVersions: {
387
+ minSupportedVersion: 1,
388
+ maxSupportedVersion: 1,
389
+ },
390
+ timestamp: Date.now(),
391
+ },
392
+ ])
393
+ this.emitMessage([
394
+ 'pokePart',
395
+ {
396
+ pokeID,
397
+ lastMutationIDChanges: response.lastMutationIDChanges,
398
+ rowsPatch: response.rowsPatch,
399
+ },
400
+ ])
401
+ if (gotQueries.length > 0) {
402
+ this.emitMessage([
403
+ 'pokePart',
404
+ {
405
+ pokeID,
406
+ gotQueriesPatch: gotQueries,
407
+ },
408
+ ])
409
+ }
410
+ this.emitMessage(['pokeEnd', { pokeID, cookie: nextCookie }])
411
+ this.cookie = nextCookie
412
+ }
413
+
414
+ private emitGotQueriesPatch(cookie: number | null) {
415
+ if (this.pendingGotQueriesPatch.length === 0) return
416
+
417
+ const serverCookie = cookie ?? toHttpCookie(this.cookie)
418
+ if (serverCookie === null) return
419
+ const nextCookie = toLocalWebSocketCookie(serverCookie, ++this.nextLocalCookieID)
420
+ const pokeID = `zero-http-${++this.state.nextPokeID}`
421
+ const gotQueries = this.pendingGotQueriesPatch
422
+ this.pendingGotQueriesPatch = []
423
+
424
+ this.emitMessage([
425
+ 'pokeStart',
426
+ {
427
+ pokeID,
428
+ baseCookie: this.cookie,
429
+ schemaVersions: {
430
+ minSupportedVersion: 1,
431
+ maxSupportedVersion: 1,
432
+ },
433
+ timestamp: Date.now(),
434
+ },
435
+ ])
436
+ this.emitMessage([
437
+ 'pokePart',
438
+ {
439
+ pokeID,
440
+ gotQueriesPatch: gotQueries,
441
+ },
442
+ ])
443
+ this.emitMessage(['pokeEnd', { pokeID, cookie: nextCookie }])
444
+ this.cookie = nextCookie
445
+ }
446
+
447
+ private emitMessage(message: unknown) {
448
+ if (this.readyState !== this.OPEN) return
449
+ this.emit('message', { data: JSON.stringify(message) })
450
+ }
451
+
452
+ private emit(type: SocketEventType, event: any) {
453
+ const handler = (this as unknown as Record<string, unknown>)[`on${type}`]
454
+ if (typeof handler === 'function') handler.call(this, event)
455
+ for (const listener of this.listeners[type]) {
456
+ if (typeof listener === 'function') listener(event)
457
+ else listener.handleEvent(event)
458
+ }
459
+ }
460
+ }
461
+
462
+ function shouldIntercept(origin: URL, url: string | URL) {
463
+ const candidate = toHttpURL(url)
464
+ if (candidate.origin !== origin.origin) return false
465
+ return candidate.pathname === `${trimTrailingSlash(origin.pathname)}/sync/v51/connect`
466
+ }
467
+
468
+ function toHttpURL(url: string | URL) {
469
+ const parsed = new URL(url)
470
+ if (parsed.protocol === 'ws:') parsed.protocol = 'http:'
471
+ if (parsed.protocol === 'wss:') parsed.protocol = 'https:'
472
+ return parsed
473
+ }
474
+
475
+ function trimTrailingSlash(value: string) {
476
+ return value.endsWith('/') ? value.slice(0, -1) : value
477
+ }
478
+
479
+ function decodeSecProtocol(protocols: WebSocketProtocols):
480
+ | {
481
+ authToken?: string
482
+ initConnectionMessage?: [string, Record<string, unknown>]
483
+ }
484
+ | Record<string, never> {
485
+ const protocol = Array.isArray(protocols) ? protocols[0] : protocols
486
+ if (!protocol) return {}
487
+ try {
488
+ const decoded = decodeURIComponent(protocol)
489
+ const json = new TextDecoder().decode(
490
+ Uint8Array.from(globalThis.atob(decoded), (char) => char.charCodeAt(0)),
491
+ )
492
+ const parsed = JSON.parse(json) as {
493
+ authToken?: string
494
+ initConnectionMessage?: unknown
495
+ }
496
+ return {
497
+ authToken: parsed.authToken,
498
+ initConnectionMessage: Array.isArray(parsed.initConnectionMessage)
499
+ ? (parsed.initConnectionMessage as [string, Record<string, unknown>])
500
+ : undefined,
501
+ }
502
+ } catch {
503
+ return {}
504
+ }
505
+ }
506
+
507
+ function gotQueriesPatch(patch: DesiredQueryPatchOp[]) {
508
+ const got: GotQueryPatchOp[] = []
509
+ for (const op of patch) {
510
+ if (op.op === 'clear') got.push({ op: 'clear' })
511
+ else if (op.hash) got.push({ op: op.op, hash: op.hash })
512
+ }
513
+ return got
514
+ }
515
+
516
+ function toHttpCookie(cookie: string | null): number | null {
517
+ if (cookie === null || cookie === '') return null
518
+ const parsed = Number(cookie.slice(0, COOKIE_WIDTH))
519
+ if (!Number.isFinite(parsed)) {
520
+ throw new Error(`zero-http cookie is not numeric: ${cookie}`)
521
+ }
522
+ return parsed
523
+ }
524
+
525
+ function toWebSocketCookie(cookie: number | null): string | null {
526
+ return cookie === null ? null : String(cookie).padStart(COOKIE_WIDTH, '0')
527
+ }
528
+
529
+ function toLocalWebSocketCookie(cookie: number, localID: number): string {
530
+ return `${String(cookie).padStart(COOKIE_WIDTH, '0')}#${String(localID).padStart(
531
+ 6,
532
+ '0',
533
+ )}`
534
+ }
535
+
536
+ function isStaleCookie(current: string | null, next: number) {
537
+ const currentNumber = toHttpCookie(current)
538
+ return currentNumber !== null && next <= currentNumber
539
+ }
540
+
541
+ function errorMessage(error: unknown) {
542
+ return error instanceof Error ? error.message : String(error)
543
+ }
544
+
545
+ class ZeroHttpResponseError extends Error {
546
+ constructor(
547
+ readonly path: '/pull' | '/push',
548
+ readonly status: number,
549
+ ) {
550
+ super(`zero-http ${path} failed with ${status}`)
551
+ }
552
+ }
553
+
554
+ function isAuthHTTPError(error: unknown): error is ZeroHttpResponseError {
555
+ return (
556
+ error instanceof ZeroHttpResponseError &&
557
+ (error.status === 401 || error.status === 403)
558
+ )
559
+ }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export { setAuthData, setEnvironment } from './state'
10
10
 
11
11
  export * from './combineZeroClients'
12
12
  export * from './createZeroClient'
13
+ export * from './httpPullTransport'
13
14
  export * from './createUseQuery'
14
15
  export * from './resolveQuery'
15
16
  export * from './run'
@@ -6,7 +6,7 @@
6
6
  // path — they must materialize directly on the owning instance.
7
7
 
8
8
  import { createSchema, string, table } from '@rocicorp/zero'
9
- import { act } from 'react'
9
+ import { act, StrictMode } from 'react'
10
10
  import { createRoot, type Root } from 'react-dom/client'
11
11
  import { afterEach, beforeEach, expect, test } from 'vitest'
12
12
 
@@ -182,6 +182,30 @@ test('inner provider unmount/remount does not break outer-instance subscriptions
182
182
  await waitFor(() => probe.data?.name === 'v3', 'v3 visible after inner remount')
183
183
  })
184
184
 
185
+ test('StrictMode: direct views survive the effect double-invoke', async () => {
186
+ // StrictMode (and any suspense hide/reveal) runs effect cleanup + re-setup
187
+ // with unchanged deps: the cleanup destroys the materialized view while the
188
+ // memoized store identity survives, so without re-materialization on
189
+ // resubscribe the snapshot freezes at its mount-time value forever — the
190
+ // "control queries strand at result type unknown" bug (soot 3e6ecd10a)
191
+ await render(
192
+ <StrictMode>
193
+ <control.ProvideZero server={null} userID="t4-ctl">
194
+ <project.ProvideZero server={null} userID="t4-prj">
195
+ <ControlUserProbe id="u4" />
196
+ </project.ProvideZero>
197
+ </control.ProvideZero>
198
+ </StrictMode>,
199
+ )
200
+ await waitFor(() => probe.renders > 0, 'probe mount')
201
+
202
+ await seedControl({ id: 'u4', name: 'strict-v1' })
203
+ await waitFor(() => probe.data?.name === 'strict-v1', 'update visible under StrictMode')
204
+
205
+ await seedControl({ id: 'u4', name: 'strict-v2' })
206
+ await waitFor(() => probe.data?.name === 'strict-v2', 'second update still live')
207
+ })
208
+
185
209
  test('direct-path views re-materialize when the owning instance rotates', async () => {
186
210
  const App = ({ userID }: { userID: string }) => (
187
211
  <control.ProvideZero server={null} userID={userID}>
@@ -1 +1 @@
1
- {"version":3,"file":"createUseQuery.d.ts","sourceRoot":"","sources":["../src/createUseQuery.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAML,KAAK,OAAO,EACb,MAAM,OAAO,CAAA;AAGd,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAEhE,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,KAAK,EACL,MAAM,IAAI,UAAU,EACrB,MAAM,gBAAgB,CAAA;AAGvB,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,YAAY,CAAA;AAE7D,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;CAC9C,CAAA;AAED,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5D,MAAM,MAAM,WAAW,CAAC,OAAO,IAAI,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAA;AAExF,YAAY,EAAE,YAAY,EAAE,CAAA;AAE5B,MAAM,MAAM,YAAY,CAAC,MAAM,SAAS,UAAU,IAAI;IAEpD,CAAC,IAAI,EAAE,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,EAC5D,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EACtD,MAAM,EAAE,IAAI,EACZ,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,GAClC,WAAW,CAAC,OAAO,CAAC,CAAC;IAGxB,CAAC,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,EACtD,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EACtD,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,GAClC,WAAW,CAAC,OAAO,CAAC,CAAA;CACxB,CAAA;AAmBD,wBAAgB,cAAc,CAAC,MAAM,SAAS,UAAU,EAAE,EACxD,eAAe,EACf,aAAa,GACd,EAAE;IACD,eAAe,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC1C,aAAa,EAAE,gBAAgB,CAAA;CAChC,GAAG,YAAY,CAAC,MAAM,CAAC,CAkCvB;AAoCD,KAAK,kBAAkB,GAAG;IACxB,WAAW,CACT,KAAK,EAAE,GAAG,EACV,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,GACtB;QACD,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAA;QAC9D,OAAO,IAAI,IAAI,CAAA;KAChB,CAAA;CACF,CAAA;AAiDD,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,UAAU,EAAE,EAC9D,eAAe,EACf,aAAa,EACb,OAAO,EACP,WAAW,GACZ,EAAE;IACD,eAAe,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC1C,aAAa,EAAE,gBAAgB,CAAA;IAE/B,OAAO,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAA;IAExC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAC7B,GAAG,YAAY,CAAC,MAAM,CAAC,CAwDvB"}
1
+ {"version":3,"file":"createUseQuery.d.ts","sourceRoot":"","sources":["../src/createUseQuery.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAML,KAAK,OAAO,EACb,MAAM,OAAO,CAAA;AAGd,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAEhE,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,KAAK,EACL,MAAM,IAAI,UAAU,EACrB,MAAM,gBAAgB,CAAA;AAGvB,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,OAAO,GAAG,YAAY,CAAA;AAE7D,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;CAC9C,CAAA;AAED,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5D,MAAM,MAAM,WAAW,CAAC,OAAO,IAAI,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAA;AAExF,YAAY,EAAE,YAAY,EAAE,CAAA;AAE5B,MAAM,MAAM,YAAY,CAAC,MAAM,SAAS,UAAU,IAAI;IAEpD,CAAC,IAAI,EAAE,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,EAC5D,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EACtD,MAAM,EAAE,IAAI,EACZ,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,GAClC,WAAW,CAAC,OAAO,CAAC,CAAC;IAGxB,CAAC,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,EACtD,EAAE,EAAE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EACtD,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,GAClC,WAAW,CAAC,OAAO,CAAC,CAAA;CACxB,CAAA;AAmBD,wBAAgB,cAAc,CAAC,MAAM,SAAS,UAAU,EAAE,EACxD,eAAe,EACf,aAAa,GACd,EAAE;IACD,eAAe,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC1C,aAAa,EAAE,gBAAgB,CAAA;CAChC,GAAG,YAAY,CAAC,MAAM,CAAC,CAkCvB;AAoCD,KAAK,kBAAkB,GAAG;IACxB,WAAW,CACT,KAAK,EAAE,GAAG,EACV,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,GACtB;QACD,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAA;QAC9D,OAAO,IAAI,IAAI,CAAA;KAChB,CAAA;CACF,CAAA;AAoED,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,UAAU,EAAE,EAC9D,eAAe,EACf,aAAa,EACb,OAAO,EACP,WAAW,GACZ,EAAE;IACD,eAAe,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC1C,aAAa,EAAE,gBAAgB,CAAA;IAE/B,OAAO,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAA;IAExC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CAC7B,GAAG,YAAY,CAAC,MAAM,CAAC,CAuDvB"}
@@ -18,10 +18,12 @@ export declare function createZeroClient<Schema extends ZeroSchema, Models exten
18
18
  }): {
19
19
  instanceName: string;
20
20
  zeroEvents: import("@take-out/helpers").Emitter<ZeroEvent | null>;
21
- ProvideZero: ({ children, authData: authDataIn, disable, ...props }: Omit<ZeroOptions<Schema, GetZeroMutators<Models>>, "schema" | "mutators"> & {
21
+ ProvideZero: ({ children, authData: authDataIn, disable, transport, pullIntervalMs, ...props }: Omit<ZeroOptions<Schema, GetZeroMutators<Models>>, "schema" | "mutators"> & {
22
22
  children: ReactNode;
23
23
  authData?: AuthData | null;
24
24
  disable?: boolean;
25
+ transport?: "http-pull";
26
+ pullIntervalMs?: number;
25
27
  }) => string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
26
28
  ControlQueries: ({ children, action, whenDisabled, }: {
27
29
  children: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"createZeroClient.d.ts","sourceRoot":"","sources":["../src/createZeroClient.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA8B,IAAI,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG/E,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AAGd,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAA;AAMzB,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAOhE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAClF,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAQ,WAAW,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEzF,KAAK,cAAc,GAAG;IAAE,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAEvE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;AAMpF,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,iBAAiB,GAAG,kBAAkB,CAAA;AAmBtF,wBAAgB,gBAAgB,CAC9B,MAAM,SAAS,UAAU,EACzB,MAAM,SAAS,aAAa,EAC5B,EACA,MAAM,EACN,MAAM,EACN,cAAc,EACd,kBAAiC,EACjC,YAAwB,GACzB,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,cAAc,CAAA;IAC9B,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IAIvC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;;;yEA6PI,IAAI,CAAC,WAAW,CAAC,MAAM,0BAAe,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG;QAClE,QAAQ,EAAE,SAAS,CAAA;QACnB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;QAC1B,OAAO,CAAC,EAAE,OAAO,CAAA;KAClB;0DA8NE;QACD,QAAQ,EAAE,SAAS,CAAA;QACnB,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;QAC7B,YAAY,CAAC,EAAE,OAAO,GAAG,YAAY,CAAA;KACtC;;;2BAnTU,oCAAY,CAAC,MAAM,GAAG,EAAE,CAAC,WACvB,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,yCAG9C,OAAO,GAAG,IAAI;iCAJR,oCAAY,CAAC,MAAM,GAAG,EAAE,CAAC,WACvB,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,yCAG9C,OAAO,GAAG,IAAI;;;SAqQF,IAAI,EAAE,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,MACxE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,UAC9C,IAAI,YACF,cAAc,GACvB;YAAE,OAAO,EAAE,MAAM,IAAI,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;SAAE;SAClC,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,MAClE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,YAC5C,cAAc,GACvB;YAAE,OAAO,EAAE,MAAM,IAAI,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;SAAE;;;SAejC,IAAI,EAAE,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,MACzE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,UAC9C,IAAI,GACX,UAAU,CAAC,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;SACxB,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,MACnE,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,GACrD,UAAU,CAAC,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;;EA+B3C"}
1
+ {"version":3,"file":"createZeroClient.d.ts","sourceRoot":"","sources":["../src/createZeroClient.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../src/httpPull/auth.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=churn.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"churn.test.d.ts","sourceRoot":"","sources":["../../src/httpPull/churn.test.ts"],"names":[],"mappings":""}