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,432 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
21
|
+
value: true
|
|
22
|
+
}), mod);
|
|
23
|
+
var httpPullTransport_exports = {};
|
|
24
|
+
__export(httpPullTransport_exports, {
|
|
25
|
+
ensureHttpPullTransport: () => ensureHttpPullTransport,
|
|
26
|
+
installHttpPullTransport: () => installHttpPullTransport
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(httpPullTransport_exports);
|
|
29
|
+
const COOKIE_WIDTH = 20;
|
|
30
|
+
function installHttpPullTransport(opts) {
|
|
31
|
+
const previousWebSocket = globalThis.WebSocket;
|
|
32
|
+
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
33
|
+
if (!fetchImpl) {
|
|
34
|
+
throw new Error("installHttpPullTransport requires a fetch implementation");
|
|
35
|
+
}
|
|
36
|
+
const state = {
|
|
37
|
+
origin: new URL(opts.origin),
|
|
38
|
+
originString: trimTrailingSlash(new URL(opts.origin).toString()),
|
|
39
|
+
fetch: fetchImpl,
|
|
40
|
+
nativeWebSocket: previousWebSocket,
|
|
41
|
+
sockets: /* @__PURE__ */new Set(),
|
|
42
|
+
pullIntervalMs: opts.pullIntervalMs,
|
|
43
|
+
nextPokeID: 0
|
|
44
|
+
};
|
|
45
|
+
const Shim = class {
|
|
46
|
+
static CONNECTING = 0;
|
|
47
|
+
static OPEN = 1;
|
|
48
|
+
static CLOSING = 2;
|
|
49
|
+
static CLOSED = 3;
|
|
50
|
+
constructor(url, protocols) {
|
|
51
|
+
if (shouldIntercept(state.origin, url)) {
|
|
52
|
+
return new ZeroHttpSocket(state, url, protocols);
|
|
53
|
+
}
|
|
54
|
+
if (!state.nativeWebSocket) {
|
|
55
|
+
throw new Error(`No native WebSocket available for ${String(url)}`);
|
|
56
|
+
}
|
|
57
|
+
return new state.nativeWebSocket(url, protocols);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
globalThis.WebSocket = Shim;
|
|
61
|
+
return {
|
|
62
|
+
pull: async () => {
|
|
63
|
+
await Promise.all([...state.sockets].map(socket => socket.pull()));
|
|
64
|
+
},
|
|
65
|
+
get connections() {
|
|
66
|
+
return state.sockets.size;
|
|
67
|
+
},
|
|
68
|
+
uninstall: () => {
|
|
69
|
+
if (globalThis.WebSocket === Shim) {
|
|
70
|
+
globalThis.WebSocket = previousWebSocket;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const transportsByOrigin = /* @__PURE__ */new Map();
|
|
76
|
+
function ensureHttpPullTransport(opts) {
|
|
77
|
+
const key = trimTrailingSlash(new URL(opts.origin).toString());
|
|
78
|
+
const existing = transportsByOrigin.get(key);
|
|
79
|
+
if (existing) return existing;
|
|
80
|
+
const transport = installHttpPullTransport(opts);
|
|
81
|
+
transportsByOrigin.set(key, transport);
|
|
82
|
+
return transport;
|
|
83
|
+
}
|
|
84
|
+
class ZeroHttpSocket {
|
|
85
|
+
constructor(state, url, protocols) {
|
|
86
|
+
this.state = state;
|
|
87
|
+
this.connectURL = toHttpURL(url);
|
|
88
|
+
this.url = String(url);
|
|
89
|
+
this.clientID = this.connectURL.searchParams.get("clientID") ?? "";
|
|
90
|
+
this.clientGroupID = this.connectURL.searchParams.get("clientGroupID") ?? "";
|
|
91
|
+
this.wsid = this.connectURL.searchParams.get("wsid") ?? `zero-http-${Date.now()}`;
|
|
92
|
+
const baseCookie = this.connectURL.searchParams.get("baseCookie");
|
|
93
|
+
this.cookie = baseCookie ? baseCookie : null;
|
|
94
|
+
const decoded = decodeSecProtocol(protocols);
|
|
95
|
+
this.authToken = decoded.authToken;
|
|
96
|
+
this.queueDesiredQueries(decoded.initConnectionMessage?.[1]);
|
|
97
|
+
this.state.sockets.add(this);
|
|
98
|
+
this.openTimer = setTimeout(() => this.open(), 0);
|
|
99
|
+
}
|
|
100
|
+
CONNECTING = 0;
|
|
101
|
+
OPEN = 1;
|
|
102
|
+
CLOSING = 2;
|
|
103
|
+
CLOSED = 3;
|
|
104
|
+
url;
|
|
105
|
+
readyState = this.CONNECTING;
|
|
106
|
+
connectURL;
|
|
107
|
+
authToken;
|
|
108
|
+
listeners = {
|
|
109
|
+
open: /* @__PURE__ */new Set(),
|
|
110
|
+
message: /* @__PURE__ */new Set(),
|
|
111
|
+
close: /* @__PURE__ */new Set(),
|
|
112
|
+
error: /* @__PURE__ */new Set()
|
|
113
|
+
};
|
|
114
|
+
clientID;
|
|
115
|
+
clientGroupID;
|
|
116
|
+
wsid;
|
|
117
|
+
cookie;
|
|
118
|
+
pendingGotQueriesPatch = [];
|
|
119
|
+
pullInFlight;
|
|
120
|
+
pullAfterCurrent = false;
|
|
121
|
+
pushChain = Promise.resolve();
|
|
122
|
+
nextLocalCookieID = 0;
|
|
123
|
+
openTimer;
|
|
124
|
+
pullTimer;
|
|
125
|
+
addEventListener(type, listener) {
|
|
126
|
+
if (listener) this.listeners[type]?.add(listener);
|
|
127
|
+
}
|
|
128
|
+
removeEventListener(type, listener) {
|
|
129
|
+
if (listener) this.listeners[type]?.delete(listener);
|
|
130
|
+
}
|
|
131
|
+
dispatchEvent(event) {
|
|
132
|
+
this.emit(event.type, event);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
send(data) {
|
|
136
|
+
if (this.readyState !== this.OPEN) {
|
|
137
|
+
throw new Error("cannot send on a socket that is not open");
|
|
138
|
+
}
|
|
139
|
+
const message = JSON.parse(data);
|
|
140
|
+
switch (message[0]) {
|
|
141
|
+
case "initConnection":
|
|
142
|
+
case "changeDesiredQueries":
|
|
143
|
+
this.queueDesiredQueries(message[1]);
|
|
144
|
+
this.requestPullAfterCurrent();
|
|
145
|
+
return;
|
|
146
|
+
case "updateAuth":
|
|
147
|
+
this.authToken = message[1].auth;
|
|
148
|
+
return;
|
|
149
|
+
case "push":
|
|
150
|
+
this.enqueuePush(message[1]);
|
|
151
|
+
return;
|
|
152
|
+
case "ping":
|
|
153
|
+
this.emitMessage(["pong", {}]);
|
|
154
|
+
return;
|
|
155
|
+
case "pull":
|
|
156
|
+
this.run(this.answerMutationRecoveryPull(message[1]));
|
|
157
|
+
return;
|
|
158
|
+
case "deleteClients":
|
|
159
|
+
case "ackMutationResponses":
|
|
160
|
+
return;
|
|
161
|
+
default:
|
|
162
|
+
throw new Error(`unsupported zero-http upstream message ${message[0]}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
close(code = 1e3, reason = "") {
|
|
166
|
+
if (this.readyState === this.CLOSED) return;
|
|
167
|
+
if (this.openTimer) clearTimeout(this.openTimer);
|
|
168
|
+
if (this.pullTimer) clearInterval(this.pullTimer);
|
|
169
|
+
this.readyState = this.CLOSED;
|
|
170
|
+
this.state.sockets.delete(this);
|
|
171
|
+
this.emit("close", {
|
|
172
|
+
code,
|
|
173
|
+
reason,
|
|
174
|
+
wasClean: code <= 1001
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
pull() {
|
|
178
|
+
if (this.readyState === this.CLOSED) return Promise.resolve();
|
|
179
|
+
if (this.pullInFlight) return this.pullInFlight;
|
|
180
|
+
this.pullInFlight = this.fetchPull(this.clientGroupID, this.cookie).then(response => {
|
|
181
|
+
if (response.unchanged) {
|
|
182
|
+
this.emitGotQueriesPatch(response.cookie);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
this.emitPoke(response);
|
|
186
|
+
}).catch(error => {
|
|
187
|
+
this.fail(error);
|
|
188
|
+
throw error;
|
|
189
|
+
}).finally(async () => {
|
|
190
|
+
const pullAgain = this.pullAfterCurrent;
|
|
191
|
+
this.pullAfterCurrent = false;
|
|
192
|
+
this.pullInFlight = void 0;
|
|
193
|
+
if (pullAgain && this.readyState !== this.CLOSED) await this.pull();
|
|
194
|
+
});
|
|
195
|
+
return this.pullInFlight;
|
|
196
|
+
}
|
|
197
|
+
open() {
|
|
198
|
+
if (this.readyState !== this.CONNECTING) return;
|
|
199
|
+
this.readyState = this.OPEN;
|
|
200
|
+
this.emit("open", {});
|
|
201
|
+
this.emitMessage(["connected", {
|
|
202
|
+
wsid: this.wsid,
|
|
203
|
+
timestamp: Date.now()
|
|
204
|
+
}]);
|
|
205
|
+
setTimeout(() => this.run(this.pull()), 0);
|
|
206
|
+
if (this.state.pullIntervalMs) {
|
|
207
|
+
this.pullTimer = setInterval(() => {
|
|
208
|
+
this.run(this.pull());
|
|
209
|
+
}, this.state.pullIntervalMs);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
queueDesiredQueries(body) {
|
|
213
|
+
const desiredQueriesPatch = body?.desiredQueriesPatch;
|
|
214
|
+
if (!Array.isArray(desiredQueriesPatch)) return;
|
|
215
|
+
this.pendingGotQueriesPatch.push(...gotQueriesPatch(desiredQueriesPatch));
|
|
216
|
+
}
|
|
217
|
+
async push(body) {
|
|
218
|
+
const response = await this.postJSON("/push", body);
|
|
219
|
+
this.emitMessage(["pushResponse", response.pushResponse]);
|
|
220
|
+
this.requestPullAfterCurrent();
|
|
221
|
+
}
|
|
222
|
+
enqueuePush(body) {
|
|
223
|
+
const nextPush = this.pushChain.then(async () => {
|
|
224
|
+
if (this.readyState === this.CLOSED) return;
|
|
225
|
+
await this.push(body);
|
|
226
|
+
});
|
|
227
|
+
this.pushChain = nextPush.catch(() => {});
|
|
228
|
+
this.run(nextPush);
|
|
229
|
+
}
|
|
230
|
+
requestPullAfterCurrent() {
|
|
231
|
+
if (this.pullInFlight) {
|
|
232
|
+
this.pullAfterCurrent = true;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.run(this.pull());
|
|
236
|
+
}
|
|
237
|
+
async answerMutationRecoveryPull(body) {
|
|
238
|
+
const response = await this.fetchPull(body.clientGroupID, body.cookie);
|
|
239
|
+
const cookie = toWebSocketCookie(response.cookie);
|
|
240
|
+
this.emitMessage(["pull", {
|
|
241
|
+
requestID: body.requestID,
|
|
242
|
+
cookie: cookie ?? this.cookie ?? "0",
|
|
243
|
+
lastMutationIDChanges: response.unchanged ? {} : response.lastMutationIDChanges
|
|
244
|
+
}]);
|
|
245
|
+
}
|
|
246
|
+
async fetchPull(clientGroupID, cookie) {
|
|
247
|
+
return await this.postJSON("/pull", {
|
|
248
|
+
clientID: this.clientID,
|
|
249
|
+
clientGroupID,
|
|
250
|
+
cookie: toHttpCookie(cookie)
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async postJSON(path, body) {
|
|
254
|
+
const response = await this.state.fetch(`${this.state.originString}${path}`, {
|
|
255
|
+
method: "POST",
|
|
256
|
+
headers: {
|
|
257
|
+
authorization: this.authToken ? `Bearer ${this.authToken}` : "",
|
|
258
|
+
"content-type": "application/json"
|
|
259
|
+
},
|
|
260
|
+
body: JSON.stringify(body)
|
|
261
|
+
});
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
throw new ZeroHttpResponseError(path, response.status);
|
|
264
|
+
}
|
|
265
|
+
return response.json();
|
|
266
|
+
}
|
|
267
|
+
run(promise) {
|
|
268
|
+
void promise.catch(error => this.fail(error));
|
|
269
|
+
}
|
|
270
|
+
fail(error) {
|
|
271
|
+
if (this.readyState === this.CLOSED) return;
|
|
272
|
+
if (isAuthHTTPError(error)) {
|
|
273
|
+
this.emitMessage(["error", {
|
|
274
|
+
kind: "Unauthorized",
|
|
275
|
+
message: error.message,
|
|
276
|
+
origin: "server"
|
|
277
|
+
}]);
|
|
278
|
+
if (this.readyState !== this.CLOSED) this.close(1e3, error.message);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
this.emit("error", {
|
|
282
|
+
error
|
|
283
|
+
});
|
|
284
|
+
this.close(1011, errorMessage(error));
|
|
285
|
+
}
|
|
286
|
+
emitPoke(response) {
|
|
287
|
+
const nextCookie = toWebSocketCookie(response.cookie);
|
|
288
|
+
if (isStaleCookie(this.cookie, response.cookie)) {
|
|
289
|
+
throw new Error(`zero-http pull returned stale cookie ${response.cookie} for ${this.cookie}`);
|
|
290
|
+
}
|
|
291
|
+
const pokeID = `zero-http-${++this.state.nextPokeID}`;
|
|
292
|
+
const gotQueries = this.pendingGotQueriesPatch;
|
|
293
|
+
this.pendingGotQueriesPatch = [];
|
|
294
|
+
this.emitMessage(["pokeStart", {
|
|
295
|
+
pokeID,
|
|
296
|
+
baseCookie: this.cookie,
|
|
297
|
+
schemaVersions: {
|
|
298
|
+
minSupportedVersion: 1,
|
|
299
|
+
maxSupportedVersion: 1
|
|
300
|
+
},
|
|
301
|
+
timestamp: Date.now()
|
|
302
|
+
}]);
|
|
303
|
+
this.emitMessage(["pokePart", {
|
|
304
|
+
pokeID,
|
|
305
|
+
lastMutationIDChanges: response.lastMutationIDChanges,
|
|
306
|
+
rowsPatch: response.rowsPatch
|
|
307
|
+
}]);
|
|
308
|
+
if (gotQueries.length > 0) {
|
|
309
|
+
this.emitMessage(["pokePart", {
|
|
310
|
+
pokeID,
|
|
311
|
+
gotQueriesPatch: gotQueries
|
|
312
|
+
}]);
|
|
313
|
+
}
|
|
314
|
+
this.emitMessage(["pokeEnd", {
|
|
315
|
+
pokeID,
|
|
316
|
+
cookie: nextCookie
|
|
317
|
+
}]);
|
|
318
|
+
this.cookie = nextCookie;
|
|
319
|
+
}
|
|
320
|
+
emitGotQueriesPatch(cookie) {
|
|
321
|
+
if (this.pendingGotQueriesPatch.length === 0) return;
|
|
322
|
+
const serverCookie = cookie ?? toHttpCookie(this.cookie);
|
|
323
|
+
if (serverCookie === null) return;
|
|
324
|
+
const nextCookie = toLocalWebSocketCookie(serverCookie, ++this.nextLocalCookieID);
|
|
325
|
+
const pokeID = `zero-http-${++this.state.nextPokeID}`;
|
|
326
|
+
const gotQueries = this.pendingGotQueriesPatch;
|
|
327
|
+
this.pendingGotQueriesPatch = [];
|
|
328
|
+
this.emitMessage(["pokeStart", {
|
|
329
|
+
pokeID,
|
|
330
|
+
baseCookie: this.cookie,
|
|
331
|
+
schemaVersions: {
|
|
332
|
+
minSupportedVersion: 1,
|
|
333
|
+
maxSupportedVersion: 1
|
|
334
|
+
},
|
|
335
|
+
timestamp: Date.now()
|
|
336
|
+
}]);
|
|
337
|
+
this.emitMessage(["pokePart", {
|
|
338
|
+
pokeID,
|
|
339
|
+
gotQueriesPatch: gotQueries
|
|
340
|
+
}]);
|
|
341
|
+
this.emitMessage(["pokeEnd", {
|
|
342
|
+
pokeID,
|
|
343
|
+
cookie: nextCookie
|
|
344
|
+
}]);
|
|
345
|
+
this.cookie = nextCookie;
|
|
346
|
+
}
|
|
347
|
+
emitMessage(message) {
|
|
348
|
+
if (this.readyState !== this.OPEN) return;
|
|
349
|
+
this.emit("message", {
|
|
350
|
+
data: JSON.stringify(message)
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
emit(type, event) {
|
|
354
|
+
const handler = this[`on${type}`];
|
|
355
|
+
if (typeof handler === "function") handler.call(this, event);
|
|
356
|
+
for (const listener of this.listeners[type]) {
|
|
357
|
+
if (typeof listener === "function") listener(event);else listener.handleEvent(event);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function shouldIntercept(origin, url) {
|
|
362
|
+
const candidate = toHttpURL(url);
|
|
363
|
+
if (candidate.origin !== origin.origin) return false;
|
|
364
|
+
return candidate.pathname === `${trimTrailingSlash(origin.pathname)}/sync/v51/connect`;
|
|
365
|
+
}
|
|
366
|
+
function toHttpURL(url) {
|
|
367
|
+
const parsed = new URL(url);
|
|
368
|
+
if (parsed.protocol === "ws:") parsed.protocol = "http:";
|
|
369
|
+
if (parsed.protocol === "wss:") parsed.protocol = "https:";
|
|
370
|
+
return parsed;
|
|
371
|
+
}
|
|
372
|
+
function trimTrailingSlash(value) {
|
|
373
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
374
|
+
}
|
|
375
|
+
function decodeSecProtocol(protocols) {
|
|
376
|
+
const protocol = Array.isArray(protocols) ? protocols[0] : protocols;
|
|
377
|
+
if (!protocol) return {};
|
|
378
|
+
try {
|
|
379
|
+
const decoded = decodeURIComponent(protocol);
|
|
380
|
+
const json = new TextDecoder().decode(Uint8Array.from(globalThis.atob(decoded), char => char.charCodeAt(0)));
|
|
381
|
+
const parsed = JSON.parse(json);
|
|
382
|
+
return {
|
|
383
|
+
authToken: parsed.authToken,
|
|
384
|
+
initConnectionMessage: Array.isArray(parsed.initConnectionMessage) ? parsed.initConnectionMessage : void 0
|
|
385
|
+
};
|
|
386
|
+
} catch {
|
|
387
|
+
return {};
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function gotQueriesPatch(patch) {
|
|
391
|
+
const got = [];
|
|
392
|
+
for (const op of patch) {
|
|
393
|
+
if (op.op === "clear") got.push({
|
|
394
|
+
op: "clear"
|
|
395
|
+
});else if (op.hash) got.push({
|
|
396
|
+
op: op.op,
|
|
397
|
+
hash: op.hash
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return got;
|
|
401
|
+
}
|
|
402
|
+
function toHttpCookie(cookie) {
|
|
403
|
+
if (cookie === null || cookie === "") return null;
|
|
404
|
+
const parsed = Number(cookie.slice(0, COOKIE_WIDTH));
|
|
405
|
+
if (!Number.isFinite(parsed)) {
|
|
406
|
+
throw new Error(`zero-http cookie is not numeric: ${cookie}`);
|
|
407
|
+
}
|
|
408
|
+
return parsed;
|
|
409
|
+
}
|
|
410
|
+
function toWebSocketCookie(cookie) {
|
|
411
|
+
return cookie === null ? null : String(cookie).padStart(COOKIE_WIDTH, "0");
|
|
412
|
+
}
|
|
413
|
+
function toLocalWebSocketCookie(cookie, localID) {
|
|
414
|
+
return `${String(cookie).padStart(COOKIE_WIDTH, "0")}#${String(localID).padStart(6, "0")}`;
|
|
415
|
+
}
|
|
416
|
+
function isStaleCookie(current, next) {
|
|
417
|
+
const currentNumber = toHttpCookie(current);
|
|
418
|
+
return currentNumber !== null && next <= currentNumber;
|
|
419
|
+
}
|
|
420
|
+
function errorMessage(error) {
|
|
421
|
+
return error instanceof Error ? error.message : String(error);
|
|
422
|
+
}
|
|
423
|
+
class ZeroHttpResponseError extends Error {
|
|
424
|
+
constructor(path, status) {
|
|
425
|
+
super(`zero-http ${path} failed with ${status}`);
|
|
426
|
+
this.path = path;
|
|
427
|
+
this.status = status;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function isAuthHTTPError(error) {
|
|
431
|
+
return error instanceof ZeroHttpResponseError && (error.status === 401 || error.status === 403);
|
|
432
|
+
}
|