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.
- package/dist/cjs/createUseQuery.cjs +28 -16
- package/dist/cjs/createUseQuery.native.js +42 -30
- package/dist/cjs/createUseQuery.native.js.map +1 -1
- package/dist/cjs/createZeroClient.cjs +13 -1
- package/dist/cjs/createZeroClient.native.js +13 -1
- package/dist/cjs/createZeroClient.native.js.map +1 -1
- package/dist/cjs/httpPull/auth.test.cjs +197 -0
- package/dist/cjs/httpPull/auth.test.native.js +279 -0
- package/dist/cjs/httpPull/auth.test.native.js.map +1 -0
- package/dist/cjs/httpPull/churn.test.cjs +132 -0
- package/dist/cjs/httpPull/churn.test.native.js +155 -0
- package/dist/cjs/httpPull/churn.test.native.js.map +1 -0
- package/dist/cjs/httpPull/fixtureSchema.cjs +76 -0
- package/dist/cjs/httpPull/fixtureSchema.native.js +82 -0
- package/dist/cjs/httpPull/fixtureSchema.native.js.map +1 -0
- package/dist/cjs/httpPull/fixtureServer.cjs +340 -0
- package/dist/cjs/httpPull/fixtureServer.native.js +534 -0
- package/dist/cjs/httpPull/fixtureServer.native.js.map +1 -0
- package/dist/cjs/httpPull/integration.test.cjs +53 -0
- package/dist/cjs/httpPull/integration.test.native.js +60 -0
- package/dist/cjs/httpPull/integration.test.native.js.map +1 -0
- package/dist/cjs/httpPull/rebase.test.cjs +360 -0
- package/dist/cjs/httpPull/rebase.test.native.js +420 -0
- package/dist/cjs/httpPull/rebase.test.native.js.map +1 -0
- package/dist/cjs/httpPull/relations.test.cjs +107 -0
- package/dist/cjs/httpPull/relations.test.native.js +119 -0
- package/dist/cjs/httpPull/relations.test.native.js.map +1 -0
- package/dist/cjs/httpPull/testHarness.cjs +100 -0
- package/dist/cjs/httpPull/testHarness.native.js +112 -0
- package/dist/cjs/httpPull/testHarness.native.js.map +1 -0
- package/dist/cjs/httpPull/transport.test.cjs +568 -0
- package/dist/cjs/httpPull/transport.test.native.js +655 -0
- package/dist/cjs/httpPull/transport.test.native.js.map +1 -0
- package/dist/cjs/httpPullTransport.cjs +432 -0
- package/dist/cjs/httpPullTransport.native.js +695 -0
- package/dist/cjs/httpPullTransport.native.js.map +1 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/multiInstanceNested.test.cjs +26 -0
- package/dist/cjs/multiInstanceNested.test.native.js +34 -0
- package/dist/cjs/multiInstanceNested.test.native.js.map +1 -1
- package/dist/esm/createUseQuery.mjs +28 -16
- package/dist/esm/createUseQuery.mjs.map +1 -1
- package/dist/esm/createUseQuery.native.js +42 -30
- package/dist/esm/createUseQuery.native.js.map +1 -1
- package/dist/esm/createZeroClient.mjs +13 -1
- package/dist/esm/createZeroClient.mjs.map +1 -1
- package/dist/esm/createZeroClient.native.js +13 -1
- package/dist/esm/createZeroClient.native.js.map +1 -1
- package/dist/esm/httpPull/auth.test.mjs +198 -0
- package/dist/esm/httpPull/auth.test.mjs.map +1 -0
- package/dist/esm/httpPull/auth.test.native.js +277 -0
- package/dist/esm/httpPull/auth.test.native.js.map +1 -0
- package/dist/esm/httpPull/churn.test.mjs +133 -0
- package/dist/esm/httpPull/churn.test.mjs.map +1 -0
- package/dist/esm/httpPull/churn.test.native.js +153 -0
- package/dist/esm/httpPull/churn.test.native.js.map +1 -0
- package/dist/esm/httpPull/fixtureSchema.mjs +50 -0
- package/dist/esm/httpPull/fixtureSchema.mjs.map +1 -0
- package/dist/esm/httpPull/fixtureSchema.native.js +53 -0
- package/dist/esm/httpPull/fixtureSchema.native.js.map +1 -0
- package/dist/esm/httpPull/fixtureServer.mjs +315 -0
- package/dist/esm/httpPull/fixtureServer.mjs.map +1 -0
- package/dist/esm/httpPull/fixtureServer.native.js +506 -0
- package/dist/esm/httpPull/fixtureServer.native.js.map +1 -0
- package/dist/esm/httpPull/integration.test.mjs +54 -0
- package/dist/esm/httpPull/integration.test.mjs.map +1 -0
- package/dist/esm/httpPull/integration.test.native.js +58 -0
- package/dist/esm/httpPull/integration.test.native.js.map +1 -0
- package/dist/esm/httpPull/rebase.test.mjs +361 -0
- package/dist/esm/httpPull/rebase.test.mjs.map +1 -0
- package/dist/esm/httpPull/rebase.test.native.js +418 -0
- package/dist/esm/httpPull/rebase.test.native.js.map +1 -0
- package/dist/esm/httpPull/relations.test.mjs +108 -0
- package/dist/esm/httpPull/relations.test.mjs.map +1 -0
- package/dist/esm/httpPull/relations.test.native.js +117 -0
- package/dist/esm/httpPull/relations.test.native.js.map +1 -0
- package/dist/esm/httpPull/testHarness.mjs +72 -0
- package/dist/esm/httpPull/testHarness.mjs.map +1 -0
- package/dist/esm/httpPull/testHarness.native.js +81 -0
- package/dist/esm/httpPull/testHarness.native.js.map +1 -0
- package/dist/esm/httpPull/transport.test.mjs +569 -0
- package/dist/esm/httpPull/transport.test.mjs.map +1 -0
- package/dist/esm/httpPull/transport.test.native.js +653 -0
- package/dist/esm/httpPull/transport.test.native.js.map +1 -0
- package/dist/esm/httpPullTransport.mjs +406 -0
- package/dist/esm/httpPullTransport.mjs.map +1 -0
- package/dist/esm/httpPullTransport.native.js +666 -0
- package/dist/esm/httpPullTransport.native.js.map +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +1 -0
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/multiInstanceNested.test.mjs +27 -1
- package/dist/esm/multiInstanceNested.test.mjs.map +1 -1
- package/dist/esm/multiInstanceNested.test.native.js +35 -1
- package/dist/esm/multiInstanceNested.test.native.js.map +1 -1
- package/package.json +2 -2
- package/src/createUseQuery.tsx +40 -22
- package/src/createZeroClient.tsx +19 -0
- package/src/httpPull/auth.test.ts +208 -0
- package/src/httpPull/churn.test.ts +147 -0
- package/src/httpPull/fixtureSchema.ts +82 -0
- package/src/httpPull/fixtureServer.ts +391 -0
- package/src/httpPull/integration.test.ts +57 -0
- package/src/httpPull/rebase.test.ts +368 -0
- package/src/httpPull/relations.test.ts +135 -0
- package/src/httpPull/testHarness.ts +95 -0
- package/src/httpPull/transport.test.ts +577 -0
- package/src/httpPullTransport.ts +559 -0
- package/src/index.ts +1 -0
- package/src/multiInstanceNested.test.tsx +25 -1
- package/types/createUseQuery.d.ts.map +1 -1
- package/types/createZeroClient.d.ts +3 -1
- package/types/createZeroClient.d.ts.map +1 -1
- package/types/httpPull/auth.test.d.ts +2 -0
- package/types/httpPull/auth.test.d.ts.map +1 -0
- package/types/httpPull/churn.test.d.ts +2 -0
- package/types/httpPull/churn.test.d.ts.map +1 -0
- package/types/httpPull/fixtureSchema.d.ts +111 -0
- package/types/httpPull/fixtureSchema.d.ts.map +1 -0
- package/types/httpPull/fixtureServer.d.ts +14 -0
- package/types/httpPull/fixtureServer.d.ts.map +1 -0
- package/types/httpPull/integration.test.d.ts +2 -0
- package/types/httpPull/integration.test.d.ts.map +1 -0
- package/types/httpPull/rebase.test.d.ts +2 -0
- package/types/httpPull/rebase.test.d.ts.map +1 -0
- package/types/httpPull/relations.test.d.ts +2 -0
- package/types/httpPull/relations.test.d.ts.map +1 -0
- package/types/httpPull/testHarness.d.ts +32 -0
- package/types/httpPull/testHarness.d.ts.map +1 -0
- package/types/httpPull/transport.test.d.ts +2 -0
- package/types/httpPull/transport.test.d.ts.map +1 -0
- package/types/httpPullTransport.d.ts +13 -0
- package/types/httpPullTransport.d.ts.map +1 -0
- package/types/index.d.ts +1 -0
- 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;
|
|
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;
|
|
1
|
+
{"version":3,"file":"createZeroClient.d.ts","sourceRoot":"","sources":["../src/createZeroClient.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../src/httpPull/auth.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"churn.test.d.ts","sourceRoot":"","sources":["../../src/httpPull/churn.test.ts"],"names":[],"mappings":""}
|