@fitzzero/quickdraw-core 5.1.2 → 5.1.3
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/README.md +793 -57
- package/dist/{admin-DWWrT1h6.d.ts → admin-CS7JB_UH.d.ts} +2 -2
- package/dist/{adminSchemas-D4bFKsrb.d.ts → adminSchemas-29cGgrg5.d.ts} +1 -1
- package/dist/{auth-DXiSJZaf.d.ts → auth-DtdYjrZ9.d.ts} +11 -7
- package/dist/{caller-BT-Mv4bD.d.ts → caller-BTwGl7vm.d.ts} +183 -15
- package/dist/chunk-2FDSKLBO.js +82 -0
- package/dist/chunk-2FDSKLBO.js.map +1 -0
- package/dist/{chunk-GSIPUNJL.js → chunk-64I6MYBC.js} +139 -792
- package/dist/chunk-64I6MYBC.js.map +1 -0
- package/dist/chunk-7473HK24.js +17 -0
- package/dist/chunk-7473HK24.js.map +1 -0
- package/dist/chunk-7Y5ROPXL.js +33 -0
- package/dist/chunk-7Y5ROPXL.js.map +1 -0
- package/dist/chunk-A2RBQD66.js +7 -0
- package/dist/chunk-A2RBQD66.js.map +1 -0
- package/dist/chunk-ASAMUGHG.js +651 -0
- package/dist/chunk-ASAMUGHG.js.map +1 -0
- package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
- package/dist/chunk-CYE4UBOB.js.map +1 -0
- package/dist/chunk-DNP65TD5.js +832 -0
- package/dist/chunk-DNP65TD5.js.map +1 -0
- package/dist/{chunk-5CGO426C.js → chunk-GYLWEJHW.js} +5 -1
- package/dist/chunk-GYLWEJHW.js.map +1 -0
- package/dist/{chunk-AONAFOYU.js → chunk-I3JCFS7H.js} +25 -13
- package/dist/chunk-I3JCFS7H.js.map +1 -0
- package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
- package/dist/chunk-IIQ3WMJK.js.map +1 -0
- package/dist/{chunk-6K5BY47X.js → chunk-JVTONQFI.js} +1277 -277
- package/dist/chunk-JVTONQFI.js.map +1 -0
- package/dist/{chunk-RL4VKLBX.js → chunk-KKQANRLG.js} +2 -2
- package/dist/{chunk-RL4VKLBX.js.map → chunk-KKQANRLG.js.map} +1 -1
- package/dist/{chunk-GKH3LJ7N.js → chunk-MPQAT55F.js} +38 -205
- package/dist/chunk-MPQAT55F.js.map +1 -0
- package/dist/chunk-NX2V6SIL.js +202 -0
- package/dist/chunk-NX2V6SIL.js.map +1 -0
- package/dist/{chunk-7QCUX4TK.js → chunk-NZHA4TZD.js} +72 -175
- package/dist/chunk-NZHA4TZD.js.map +1 -0
- package/dist/{chunk-MBH6IDSM.js → chunk-OCZJ4EYV.js} +40 -17
- package/dist/chunk-OCZJ4EYV.js.map +1 -0
- package/dist/{chunk-RJ5I2VO3.js → chunk-PHCURHC7.js} +6 -6
- package/dist/chunk-PHCURHC7.js.map +1 -0
- package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
- package/dist/chunk-Q4OGQM5I.js.map +1 -0
- package/dist/chunk-QSU47XEX.js +13 -0
- package/dist/chunk-QSU47XEX.js.map +1 -0
- package/dist/{chunk-LCC7HNFP.js → chunk-RZXJTDH5.js} +2 -2
- package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
- package/dist/chunk-SN3OZVEQ.js.map +1 -0
- package/dist/{chunk-Z5ILULF2.js → chunk-SQ5VUG77.js} +4 -3
- package/dist/{chunk-Z5ILULF2.js.map → chunk-SQ5VUG77.js.map} +1 -1
- package/dist/chunk-STH5DLQP.js +1192 -0
- package/dist/chunk-STH5DLQP.js.map +1 -0
- package/dist/{chunk-NMXYENH4.js → chunk-VZFJTKIW.js} +6 -6
- package/dist/{chunk-753VJIUP.js → chunk-X4ZU4QH5.js} +43 -9
- package/dist/chunk-X4ZU4QH5.js.map +1 -0
- package/dist/chunk-XD7SRDUY.js +188 -0
- package/dist/chunk-XD7SRDUY.js.map +1 -0
- package/dist/chunk-Z7I6SONO.js +825 -0
- package/dist/chunk-Z7I6SONO.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +131 -113
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/core.d.ts +387 -0
- package/dist/client/core.js +109 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/index.d.ts +30 -193
- package/dist/client/index.js +56 -217
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-hWcPUVS9.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
- package/dist/{connection-CgijdGpT.d.ts → connection-2EeQ_Et0.d.ts} +16 -9
- package/dist/{createServer-Cp-Wk500.d.ts → createServer-Bx5HOcYK.d.ts} +37 -9
- package/dist/{createTestApp-BB_PkqQ7.d.ts → createTestApp-DAMANT79.d.ts} +36 -9
- package/dist/{envelope-DzWyIXjj.d.ts → envelope-BMBaxppV.d.ts} +200 -8
- package/dist/{http-DotQ5bj8.d.ts → http-YzLAar3w.d.ts} +15 -50
- package/dist/index.d.ts +7 -6
- package/dist/index.js +61 -28
- package/dist/index.js.map +1 -1
- package/dist/{infer-BmoZsL9a.d.ts → infer-Br62cnyW.d.ts} +39 -1
- package/dist/{metrics-DW80Exve.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
- package/dist/parser.js +2 -1
- package/dist/prisma/index.d.ts +13 -6
- package/dist/prisma/index.js +155 -9
- package/dist/prisma/index.js.map +1 -1
- package/dist/remote-N7OUAGOW.js +15 -0
- package/dist/remote-N7OUAGOW.js.map +1 -0
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/auth/index.js +5 -3
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/index.d.ts +140 -36
- package/dist/server/index.js +162 -55
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +34 -18
- package/dist/server/mcp/index.js +29 -12
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-UDLn1049.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
- package/dist/testing/client.d.ts +13 -13
- package/dist/testing/client.js +17 -10
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +49 -12
- package/dist/testing/index.js +102 -19
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +15 -9
- package/dist/tracked-BR5b2O-U.d.ts +43 -0
- package/dist/{types-CYliTP65.d.ts → types-CLRCQGnO.d.ts} +4 -3
- package/dist/types-tMagHbjy.d.ts +151 -0
- package/dist/utils/index.d.ts +16 -4
- package/dist/utils/index.js +6 -3
- package/package.json +5 -1
- package/dist/chunk-4YO5H5ON.js.map +0 -1
- package/dist/chunk-5CGO426C.js.map +0 -1
- package/dist/chunk-6K5BY47X.js.map +0 -1
- package/dist/chunk-753VJIUP.js.map +0 -1
- package/dist/chunk-7QCUX4TK.js.map +0 -1
- package/dist/chunk-AONAFOYU.js.map +0 -1
- package/dist/chunk-B2P7AZ25.js.map +0 -1
- package/dist/chunk-GKH3LJ7N.js.map +0 -1
- package/dist/chunk-GSIPUNJL.js.map +0 -1
- package/dist/chunk-MBH6IDSM.js.map +0 -1
- package/dist/chunk-RJ5I2VO3.js.map +0 -1
- package/dist/chunk-X5CWPX7H.js.map +0 -1
- package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
- /package/dist/{chunk-LCC7HNFP.js.map → chunk-RZXJTDH5.js.map} +0 -0
- /package/dist/{chunk-NMXYENH4.js.map → chunk-VZFJTKIW.js.map} +0 -0
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
adminSpecOf,
|
|
3
2
|
searchSpecOf
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
CLIENT_EVENTS,
|
|
7
|
-
MAX_SUBSCRIBE_IDS,
|
|
8
|
-
PROTOCOL_VERSION,
|
|
9
|
-
QUICKDRAW_VERSION,
|
|
10
|
-
SERVER_EVENTS,
|
|
11
|
-
isAuthenticationRefused,
|
|
12
|
-
isProtocolMismatch
|
|
13
|
-
} from "./chunk-753VJIUP.js";
|
|
3
|
+
} from "./chunk-XD7SRDUY.js";
|
|
14
4
|
import {
|
|
15
5
|
DEFAULT_COLLECTION_MAX_LIMIT
|
|
16
6
|
} from "./chunk-4D2DOGAW.js";
|
|
17
7
|
import {
|
|
18
8
|
ACCESS_LEVELS
|
|
19
9
|
} from "./chunk-AK23XSWD.js";
|
|
20
|
-
import {
|
|
21
|
-
createJsonParser
|
|
22
|
-
} from "./chunk-Z2LZ7IZ7.js";
|
|
23
10
|
import {
|
|
24
11
|
consoleLogger
|
|
25
12
|
} from "./chunk-DKE6QO26.js";
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_BACKOFF_MS,
|
|
15
|
+
INITIAL_CONNECTION_STATE,
|
|
16
|
+
hostLinksOf,
|
|
17
|
+
isServerDisconnect,
|
|
18
|
+
isSessionRefusal,
|
|
19
|
+
notifyEach,
|
|
20
|
+
retryAfterOf,
|
|
21
|
+
subscriptionLimits
|
|
22
|
+
} from "./chunk-STH5DLQP.js";
|
|
26
23
|
import {
|
|
27
24
|
KEY_ROOT,
|
|
28
25
|
collectionKey,
|
|
@@ -30,757 +27,23 @@ import {
|
|
|
30
27
|
methodKey,
|
|
31
28
|
methodKeyPrefix,
|
|
32
29
|
serviceKeyPrefix
|
|
33
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-IIQ3WMJK.js";
|
|
31
|
+
import {
|
|
32
|
+
adminSpecOf
|
|
33
|
+
} from "./chunk-MPQAT55F.js";
|
|
34
|
+
import {
|
|
35
|
+
CLIENT_EVENTS,
|
|
36
|
+
MAX_SCOPE_LENGTH,
|
|
37
|
+
SERVER_EVENTS
|
|
38
|
+
} from "./chunk-X4ZU4QH5.js";
|
|
34
39
|
import {
|
|
35
40
|
QuickdrawError,
|
|
36
|
-
fromWire
|
|
41
|
+
fromWire
|
|
42
|
+
} from "./chunk-Q4OGQM5I.js";
|
|
43
|
+
import {
|
|
37
44
|
isName,
|
|
38
45
|
isRecord
|
|
39
|
-
} from "./chunk-
|
|
40
|
-
|
|
41
|
-
// src/client/backoff.ts
|
|
42
|
-
var DEFAULT_BACKOFF_MS = 5e3;
|
|
43
|
-
var MIN_BACKOFF_MS = 250;
|
|
44
|
-
var MAX_BACKOFF_MS = 5 * 60 * 1e3;
|
|
45
|
-
var JITTER_RATIO = 0.5;
|
|
46
|
-
function retryAfterOf(error) {
|
|
47
|
-
const retryAfterMs = isRecord(error.data) ? error.data.retryAfterMs : void 0;
|
|
48
|
-
return typeof retryAfterMs === "number" ? retryAfterMs : void 0;
|
|
49
|
-
}
|
|
50
|
-
function waitFor(retryAfterMs) {
|
|
51
|
-
const wait2 = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) ? retryAfterMs : DEFAULT_BACKOFF_MS;
|
|
52
|
-
return Math.min(Math.max(wait2, MIN_BACKOFF_MS), MAX_BACKOFF_MS);
|
|
53
|
-
}
|
|
54
|
-
function createBackoff(onChange) {
|
|
55
|
-
const until = /* @__PURE__ */ new Map();
|
|
56
|
-
const timers = /* @__PURE__ */ new Map();
|
|
57
|
-
const windows = () => Object.freeze(Object.fromEntries(until));
|
|
58
|
-
function end(kind) {
|
|
59
|
-
timers.delete(kind);
|
|
60
|
-
until.delete(kind);
|
|
61
|
-
onChange(windows());
|
|
62
|
-
}
|
|
63
|
-
return Object.freeze({
|
|
64
|
-
report(kind, retryAfterMs) {
|
|
65
|
-
const wait2 = waitFor(retryAfterMs);
|
|
66
|
-
const delay = wait2 + Math.random() * wait2 * JITTER_RATIO;
|
|
67
|
-
const ends2 = Date.now() + delay;
|
|
68
|
-
if (ends2 <= (until.get(kind) ?? 0)) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
clearTimeout(timers.get(kind));
|
|
72
|
-
until.set(kind, ends2);
|
|
73
|
-
timers.set(
|
|
74
|
-
kind,
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
end(kind);
|
|
77
|
-
}, delay)
|
|
78
|
-
);
|
|
79
|
-
onChange(windows());
|
|
80
|
-
},
|
|
81
|
-
remaining(kind) {
|
|
82
|
-
const ends2 = until.get(kind);
|
|
83
|
-
return ends2 === void 0 ? 0 : Math.max(0, ends2 - Date.now());
|
|
84
|
-
},
|
|
85
|
-
windows,
|
|
86
|
-
clear() {
|
|
87
|
-
for (const timer of timers.values()) {
|
|
88
|
-
clearTimeout(timer);
|
|
89
|
-
}
|
|
90
|
-
timers.clear();
|
|
91
|
-
if (until.size > 0) {
|
|
92
|
-
until.clear();
|
|
93
|
-
onChange(windows());
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// src/client/watch.ts
|
|
100
|
-
function topicId(service, topic) {
|
|
101
|
-
return `${service}\0${topic}`;
|
|
102
|
-
}
|
|
103
|
-
function rethrowLater(error) {
|
|
104
|
-
queueMicrotask(() => {
|
|
105
|
-
throw error;
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
function notifyEach(listeners, notify) {
|
|
109
|
-
for (const listener of [...listeners]) {
|
|
110
|
-
try {
|
|
111
|
-
notify(listener);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
rethrowLater(error);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function oncePerKey(records) {
|
|
118
|
-
const keys = /* @__PURE__ */ new Set();
|
|
119
|
-
return [...records].filter(({ watch }) => {
|
|
120
|
-
if (watch.key === void 0) {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
const first = !keys.has(watch.key);
|
|
124
|
-
keys.add(watch.key);
|
|
125
|
-
return first;
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
function modelName(model) {
|
|
129
|
-
return `${model.charAt(0).toLowerCase()}${model.slice(1)}`;
|
|
130
|
-
}
|
|
131
|
-
function concerns(watch, frame) {
|
|
132
|
-
const { models } = watch;
|
|
133
|
-
if (models === void 0 || !Array.isArray(frame.models)) {
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
const changed3 = new Set(frame.models.map((model) => modelName(String(model))));
|
|
137
|
-
return models.some((model) => changed3.has(modelName(model)));
|
|
138
|
-
}
|
|
139
|
-
function tell(topic, frame) {
|
|
140
|
-
const concerned = [...topic.watches].filter(({ watch }) => concerns(watch, frame));
|
|
141
|
-
notifyEach(oncePerKey(concerned), ({ watch }) => {
|
|
142
|
-
watch.onChanged(frame);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
function tellJoined(topic) {
|
|
146
|
-
const waited = new Set([...topic.holds].map((hold2) => hold2.key));
|
|
147
|
-
const unjoined = new Set(topic.unjoinedReads);
|
|
148
|
-
topic.unjoinedReads.clear();
|
|
149
|
-
const owed = [...topic.watches].filter(
|
|
150
|
-
({ watch, told }) => !told || watch.key !== void 0 && unjoined.has(watch.key)
|
|
151
|
-
);
|
|
152
|
-
for (const record of owed) {
|
|
153
|
-
record.told = true;
|
|
154
|
-
}
|
|
155
|
-
const toTell = owed.filter(({ watch }) => watch.key === void 0 || !waited.has(watch.key));
|
|
156
|
-
notifyEach(oncePerKey(toTell), ({ watch }) => {
|
|
157
|
-
watch.onJoined?.();
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
function releaseHolds(topic) {
|
|
161
|
-
const holds = [...topic.holds];
|
|
162
|
-
topic.holds.clear();
|
|
163
|
-
for (const hold2 of holds) {
|
|
164
|
-
if (topic.state !== "joined" && hold2.key !== void 0) {
|
|
165
|
-
topic.unjoinedReads.add(hold2.key);
|
|
166
|
-
}
|
|
167
|
-
hold2.release();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function isChangedFrame(value) {
|
|
171
|
-
return isRecord(value) && typeof value.s === "string" && typeof value.topic === "string";
|
|
172
|
-
}
|
|
173
|
-
function retryIn(registry, topic, delayMs) {
|
|
174
|
-
clearTimeout(topic.retry);
|
|
175
|
-
topic.state = "waiting";
|
|
176
|
-
topic.retry = setTimeout(() => {
|
|
177
|
-
topic.retry = void 0;
|
|
178
|
-
if (registry.topics.get(topic.id) === topic) {
|
|
179
|
-
join(registry, topic);
|
|
180
|
-
}
|
|
181
|
-
}, delayMs);
|
|
182
|
-
}
|
|
183
|
-
function refused(registry, topic, reply) {
|
|
184
|
-
const { host } = registry;
|
|
185
|
-
const failure = fromWire(isRecord(reply) ? reply.e : void 0);
|
|
186
|
-
if (failure.code === "RATE_LIMITED") {
|
|
187
|
-
host.reportRateLimited("subscription", retryAfterOf(failure));
|
|
188
|
-
retryIn(registry, topic, host.backoffRemaining("subscription"));
|
|
189
|
-
} else {
|
|
190
|
-
topic.state = "refused";
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function answered(registry, topic, error, reply) {
|
|
194
|
-
if (error !== null) {
|
|
195
|
-
if (registry.host.socket.connected) {
|
|
196
|
-
retryIn(registry, topic, DEFAULT_BACKOFF_MS);
|
|
197
|
-
} else {
|
|
198
|
-
topic.state = "waiting";
|
|
199
|
-
}
|
|
200
|
-
} else if (isRecord(reply) && reply.ok === true) {
|
|
201
|
-
topic.state = "joined";
|
|
202
|
-
tellJoined(topic);
|
|
203
|
-
} else {
|
|
204
|
-
refused(registry, topic, reply);
|
|
205
|
-
}
|
|
206
|
-
releaseHolds(topic);
|
|
207
|
-
}
|
|
208
|
-
function join(registry, topic) {
|
|
209
|
-
const { host } = registry;
|
|
210
|
-
clearTimeout(topic.retry);
|
|
211
|
-
topic.retry = void 0;
|
|
212
|
-
topic.attempt += 1;
|
|
213
|
-
const wait2 = host.backoffRemaining("subscription");
|
|
214
|
-
if (!host.socket.connected) {
|
|
215
|
-
topic.state = "waiting";
|
|
216
|
-
releaseHolds(topic);
|
|
217
|
-
} else if (wait2 > 0) {
|
|
218
|
-
retryIn(registry, topic, wait2);
|
|
219
|
-
releaseHolds(topic);
|
|
220
|
-
} else {
|
|
221
|
-
topic.state = "joining";
|
|
222
|
-
const { attempt } = topic;
|
|
223
|
-
host.lane.send(CLIENT_EVENTS.watch, topic.frame, (error, reply) => {
|
|
224
|
-
if (registry.topics.get(topic.id) === topic && topic.attempt === attempt) {
|
|
225
|
-
answered(registry, topic, error, reply);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function leave(registry, topic) {
|
|
231
|
-
registry.topics.delete(topic.id);
|
|
232
|
-
clearTimeout(topic.retry);
|
|
233
|
-
clearTimeout(topic.leaving);
|
|
234
|
-
topic.leaving = void 0;
|
|
235
|
-
topic.attempt += 1;
|
|
236
|
-
releaseHolds(topic);
|
|
237
|
-
const sent = topic.state === "joining" || topic.state === "joined";
|
|
238
|
-
if (sent && registry.host.socket.connected) {
|
|
239
|
-
registry.host.socket.emit(CLIENT_EVENTS.unwatch, topic.frame);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
function waitForJoin(registry, wait2) {
|
|
243
|
-
const topic = registry.topics.get(topicId(wait2.service, wait2.topic));
|
|
244
|
-
if (topic === void 0) {
|
|
245
|
-
return void 0;
|
|
246
|
-
}
|
|
247
|
-
if (topic.state === "joining" && registry.host.socket.connected) {
|
|
248
|
-
return new Promise((resolve) => {
|
|
249
|
-
topic.holds.add({ key: wait2.key, release: resolve });
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
if (topic.state !== "joined" && wait2.key !== void 0) {
|
|
253
|
-
topic.unjoinedReads.add(wait2.key);
|
|
254
|
-
}
|
|
255
|
-
return void 0;
|
|
256
|
-
}
|
|
257
|
-
function startWatch(registry, watch) {
|
|
258
|
-
const id = topicId(watch.service, watch.topic);
|
|
259
|
-
const topic = registry.topics.get(id) ?? {
|
|
260
|
-
id,
|
|
261
|
-
frame: Object.freeze({ s: watch.service, topic: watch.topic }),
|
|
262
|
-
watches: /* @__PURE__ */ new Set(),
|
|
263
|
-
holds: /* @__PURE__ */ new Set(),
|
|
264
|
-
unjoinedReads: /* @__PURE__ */ new Set(),
|
|
265
|
-
state: "waiting",
|
|
266
|
-
retry: void 0,
|
|
267
|
-
leaving: void 0,
|
|
268
|
-
attempt: 0
|
|
269
|
-
};
|
|
270
|
-
registry.topics.set(id, topic);
|
|
271
|
-
const kept = topic.leaving !== void 0;
|
|
272
|
-
clearTimeout(topic.leaving);
|
|
273
|
-
topic.leaving = void 0;
|
|
274
|
-
const record = { watch, told: topic.state === "joined" };
|
|
275
|
-
topic.watches.add(record);
|
|
276
|
-
if (topic.watches.size === 1 && !kept) {
|
|
277
|
-
join(registry, topic);
|
|
278
|
-
}
|
|
279
|
-
return () => {
|
|
280
|
-
if (topic.watches.delete(record) && topic.watches.size === 0) {
|
|
281
|
-
topic.leaving = setTimeout(() => {
|
|
282
|
-
if (topic.watches.size === 0 && registry.topics.get(topic.id) === topic) {
|
|
283
|
-
leave(registry, topic);
|
|
284
|
-
}
|
|
285
|
-
}, 0);
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
function createTopics(host) {
|
|
290
|
-
const registry = { host, topics: /* @__PURE__ */ new Map() };
|
|
291
|
-
host.socket.on(SERVER_EVENTS.changed, (frame) => {
|
|
292
|
-
const topic = isChangedFrame(frame) ? registry.topics.get(topicId(frame.s, frame.topic)) : void 0;
|
|
293
|
-
if (topic !== void 0) {
|
|
294
|
-
tell(topic, frame);
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
host.socket.on("disconnect", () => {
|
|
298
|
-
for (const topic of registry.topics.values()) {
|
|
299
|
-
if (topic.state === "joined") {
|
|
300
|
-
topic.state = "waiting";
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
return Object.freeze({
|
|
305
|
-
watch: (watch) => startWatch(registry, watch),
|
|
306
|
-
waitForJoin: (wait2) => waitForJoin(registry, wait2),
|
|
307
|
-
rejoin() {
|
|
308
|
-
for (const topic of [...registry.topics.values()]) {
|
|
309
|
-
if (topic.watches.size === 0) {
|
|
310
|
-
leave(registry, topic);
|
|
311
|
-
} else {
|
|
312
|
-
join(registry, topic);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
stop() {
|
|
317
|
-
for (const topic of [...registry.topics.values()]) {
|
|
318
|
-
if (topic.watches.size === 0) {
|
|
319
|
-
leave(registry, topic);
|
|
320
|
-
} else {
|
|
321
|
-
clearTimeout(topic.retry);
|
|
322
|
-
topic.retry = void 0;
|
|
323
|
-
topic.attempt += 1;
|
|
324
|
-
topic.state = "waiting";
|
|
325
|
-
releaseHolds(topic);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// src/client/lane.ts
|
|
333
|
-
var DEFAULT_SUBSCRIPTION_LANE = 8;
|
|
334
|
-
function positiveInteger(value, fallback) {
|
|
335
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : fallback;
|
|
336
|
-
}
|
|
337
|
-
function subscriptionLimits(hello) {
|
|
338
|
-
const limits = hello?.limits;
|
|
339
|
-
const record = isRecord(limits) ? limits : {};
|
|
340
|
-
const lane = isRecord(record.subscriptions) ? record.subscriptions : {};
|
|
341
|
-
return {
|
|
342
|
-
maxInFlight: positiveInteger(lane.maxInFlight, DEFAULT_SUBSCRIPTION_LANE),
|
|
343
|
-
maxSubscribeIds: positiveInteger(record.maxSubscribeIds, MAX_SUBSCRIBE_IDS)
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
function notConnected() {
|
|
347
|
-
return new Error("The socket is not connected; the event was not sent");
|
|
348
|
-
}
|
|
349
|
-
function createSubscriptionLane(host) {
|
|
350
|
-
const queue = [];
|
|
351
|
-
let inFlight = 0;
|
|
352
|
-
let backoffTimer;
|
|
353
|
-
const answer = (pending, error, reply) => {
|
|
354
|
-
notifyEach([pending.done], (done) => {
|
|
355
|
-
done(error, reply);
|
|
356
|
-
});
|
|
357
|
-
};
|
|
358
|
-
function sendOne(pending) {
|
|
359
|
-
inFlight += 1;
|
|
360
|
-
const emitter = host.socket.timeout(host.timeoutMs());
|
|
361
|
-
emitter.emit(pending.event, pending.frame, (error, reply) => {
|
|
362
|
-
inFlight -= 1;
|
|
363
|
-
answer(pending, error, reply);
|
|
364
|
-
pump();
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
function waitForBackoff(wait2) {
|
|
368
|
-
backoffTimer ??= setTimeout(() => {
|
|
369
|
-
backoffTimer = void 0;
|
|
370
|
-
pump();
|
|
371
|
-
}, wait2);
|
|
372
|
-
}
|
|
373
|
-
function dropAll() {
|
|
374
|
-
for (const dropped of queue.splice(0)) {
|
|
375
|
-
answer(dropped, notConnected(), void 0);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function pump() {
|
|
379
|
-
const { maxInFlight } = subscriptionLimits(host.hello());
|
|
380
|
-
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
381
|
-
if (!host.socket.connected) {
|
|
382
|
-
dropAll();
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const wait2 = host.backoffRemaining("subscription");
|
|
386
|
-
if (wait2 > 0) {
|
|
387
|
-
waitForBackoff(wait2);
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
sendOne(queue.shift());
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return Object.freeze({
|
|
394
|
-
send(event, frame, done) {
|
|
395
|
-
queue.push({ event, frame, done });
|
|
396
|
-
pump();
|
|
397
|
-
},
|
|
398
|
-
inFlight: () => inFlight,
|
|
399
|
-
waiting: () => queue.length,
|
|
400
|
-
stop() {
|
|
401
|
-
clearTimeout(backoffTimer);
|
|
402
|
-
backoffTimer = void 0;
|
|
403
|
-
dropAll();
|
|
404
|
-
}
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// src/client/reload.ts
|
|
409
|
-
var RELOAD_KEY_PREFIX = "quickdraw:protocol-reload:";
|
|
410
|
-
function firstReload(scope, key) {
|
|
411
|
-
try {
|
|
412
|
-
const storage = scope.sessionStorage;
|
|
413
|
-
if (storage === void 0 || storage.getItem(key) !== null) {
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
|
-
storage.setItem(key, String(Date.now()));
|
|
417
|
-
return true;
|
|
418
|
-
} catch {
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
function reloadOncePerSession(mismatch) {
|
|
423
|
-
const scope = globalThis;
|
|
424
|
-
const location = scope.location;
|
|
425
|
-
if (typeof location?.reload !== "function") {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (firstReload(scope, `${RELOAD_KEY_PREFIX}${mismatch.expected}`)) {
|
|
429
|
-
location.reload();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// src/client/socket.ts
|
|
434
|
-
import { io } from "socket.io-client";
|
|
435
|
-
function createSocket(settings, credentials) {
|
|
436
|
-
return io(settings.url, {
|
|
437
|
-
forceNew: true,
|
|
438
|
-
withCredentials: true,
|
|
439
|
-
transports: ["websocket", "polling"],
|
|
440
|
-
...settings.binary === true ? {} : { parser: createJsonParser() },
|
|
441
|
-
...settings.socketOptions,
|
|
442
|
-
...settings.transports === void 0 ? {} : { transports: settings.transports },
|
|
443
|
-
autoConnect: false,
|
|
444
|
-
auth: (send2) => {
|
|
445
|
-
send2({ ...credentials(), qd: { protocol: PROTOCOL_VERSION, client: QUICKDRAW_VERSION } });
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// src/client/connection.ts
|
|
451
|
-
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
452
|
-
var HELLO_TIMEOUT_MARGIN_MS = 2e3;
|
|
453
|
-
var INITIAL_CONNECTION_STATE = Object.freeze({
|
|
454
|
-
status: "idle",
|
|
455
|
-
reconnecting: false,
|
|
456
|
-
hello: null,
|
|
457
|
-
serviceAccess: null,
|
|
458
|
-
refusal: null,
|
|
459
|
-
backoff: Object.freeze({})
|
|
460
|
-
});
|
|
461
|
-
function createStore() {
|
|
462
|
-
let state = INITIAL_CONNECTION_STATE;
|
|
463
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
464
|
-
return {
|
|
465
|
-
get: () => state,
|
|
466
|
-
set(patch2) {
|
|
467
|
-
state = Object.freeze({ ...state, ...patch2 });
|
|
468
|
-
for (const listener of [...listeners]) {
|
|
469
|
-
listener();
|
|
470
|
-
}
|
|
471
|
-
},
|
|
472
|
-
subscribe(listener) {
|
|
473
|
-
listeners.add(listener);
|
|
474
|
-
return () => {
|
|
475
|
-
listeners.delete(listener);
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
function credentialsOf(auth) {
|
|
481
|
-
if (typeof auth === "string") {
|
|
482
|
-
return auth === "" ? {} : { token: auth };
|
|
483
|
-
}
|
|
484
|
-
return auth ?? {};
|
|
485
|
-
}
|
|
486
|
-
function authKey(auth) {
|
|
487
|
-
const sorted = (_key, value) => isRecord(value) ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : 1)) : value;
|
|
488
|
-
return JSON.stringify(credentialsOf(auth), sorted);
|
|
489
|
-
}
|
|
490
|
-
function failBuffered(socket, error) {
|
|
491
|
-
const buffered = socket.sendBuffer.splice(0);
|
|
492
|
-
const { acks } = socket;
|
|
493
|
-
for (const packet of buffered) {
|
|
494
|
-
const id = packet.id === void 0 ? void 0 : String(packet.id);
|
|
495
|
-
const ack = id === void 0 ? void 0 : acks[id];
|
|
496
|
-
if (id !== void 0 && ack !== void 0) {
|
|
497
|
-
Reflect.deleteProperty(acks, id);
|
|
498
|
-
if (ack.withError === true) {
|
|
499
|
-
ack.call(socket, error);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
function isServerDisconnect(reason) {
|
|
505
|
-
return reason === "io server disconnect";
|
|
506
|
-
}
|
|
507
|
-
function isSessionRefusal(data) {
|
|
508
|
-
return isRecord(data) && (data.code === "UNAUTHENTICATED" || data.code === "FORBIDDEN");
|
|
509
|
-
}
|
|
510
|
-
function refusalOf(error) {
|
|
511
|
-
if (isProtocolMismatch(error.data)) {
|
|
512
|
-
return { code: error.data.code, expected: error.data.expected };
|
|
513
|
-
}
|
|
514
|
-
if (isAuthenticationRefused(error.data)) {
|
|
515
|
-
return { code: "UNAUTHENTICATED" };
|
|
516
|
-
}
|
|
517
|
-
return { code: "REFUSED", message: error.message };
|
|
518
|
-
}
|
|
519
|
-
var MAX_TIMEOUT_MS = 2147483647;
|
|
520
|
-
function isTimeLimit(value) {
|
|
521
|
-
return typeof value === "number" && value > 0 && value <= MAX_TIMEOUT_MS;
|
|
522
|
-
}
|
|
523
|
-
function checkOptions(options) {
|
|
524
|
-
if (typeof options.url !== "string" || options.url === "") {
|
|
525
|
-
throw new TypeError("createQuickdrawConnection: url must be the server's URL");
|
|
526
|
-
}
|
|
527
|
-
const { timeoutMs } = options;
|
|
528
|
-
if (timeoutMs !== void 0 && !isTimeLimit(timeoutMs)) {
|
|
529
|
-
throw new TypeError(
|
|
530
|
-
"createQuickdrawConnection: timeoutMs must be a number of milliseconds, above 0 and at most 2^31 - 1"
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
return timeoutMs;
|
|
534
|
-
}
|
|
535
|
-
function defaultTimeout(option, hello) {
|
|
536
|
-
if (option !== void 0) {
|
|
537
|
-
return option;
|
|
538
|
-
}
|
|
539
|
-
const limits = hello?.limits;
|
|
540
|
-
const serverMs = isRecord(limits) ? limits.callTimeoutMs : void 0;
|
|
541
|
-
const fromHello = typeof serverMs === "number" ? serverMs + HELLO_TIMEOUT_MARGIN_MS : void 0;
|
|
542
|
-
return isTimeLimit(fromHello) ? fromHello : DEFAULT_TIMEOUT_MS;
|
|
543
|
-
}
|
|
544
|
-
function createLifecycle(socket, store, backoff, session) {
|
|
545
|
-
let opened = false;
|
|
546
|
-
let switching = false;
|
|
547
|
-
let users = 0;
|
|
548
|
-
let closeTimer;
|
|
549
|
-
let rotateTimer;
|
|
550
|
-
function connect() {
|
|
551
|
-
socket.connect();
|
|
552
|
-
store.set({ status: "connecting", refusal: null });
|
|
553
|
-
}
|
|
554
|
-
function reconnect() {
|
|
555
|
-
if (!opened) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
switching = true;
|
|
559
|
-
socket.disconnect();
|
|
560
|
-
switching = false;
|
|
561
|
-
connect();
|
|
562
|
-
}
|
|
563
|
-
function open() {
|
|
564
|
-
clearTimeout(closeTimer);
|
|
565
|
-
closeTimer = void 0;
|
|
566
|
-
opened = true;
|
|
567
|
-
if (!socket.connected && !socket.active) {
|
|
568
|
-
connect();
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
function close() {
|
|
572
|
-
opened = false;
|
|
573
|
-
session.established = false;
|
|
574
|
-
session.topics.stop();
|
|
575
|
-
session.lane.stop();
|
|
576
|
-
clearTimeout(closeTimer);
|
|
577
|
-
closeTimer = void 0;
|
|
578
|
-
clearTimeout(rotateTimer);
|
|
579
|
-
rotateTimer = void 0;
|
|
580
|
-
backoff.clear();
|
|
581
|
-
failBuffered(
|
|
582
|
-
socket,
|
|
583
|
-
new QuickdrawError("CANCELLED", "The connection closed before the call was sent")
|
|
584
|
-
);
|
|
585
|
-
socket.disconnect();
|
|
586
|
-
store.set({ status: "idle", reconnecting: false });
|
|
587
|
-
}
|
|
588
|
-
function retain2() {
|
|
589
|
-
users += 1;
|
|
590
|
-
open();
|
|
591
|
-
let released = false;
|
|
592
|
-
return () => {
|
|
593
|
-
if (!released) {
|
|
594
|
-
released = true;
|
|
595
|
-
users -= 1;
|
|
596
|
-
if (users === 0) {
|
|
597
|
-
closeTimer = setTimeout(close, 0);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
function rotateWithin(withinMs) {
|
|
603
|
-
if (rotateTimer === void 0) {
|
|
604
|
-
rotateTimer = setTimeout(() => {
|
|
605
|
-
rotateTimer = void 0;
|
|
606
|
-
reconnect();
|
|
607
|
-
}, Math.random() * withinMs);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
function stoppedStatus() {
|
|
611
|
-
if (switching) {
|
|
612
|
-
return "connecting";
|
|
613
|
-
}
|
|
614
|
-
return opened ? "disconnected" : "idle";
|
|
615
|
-
}
|
|
616
|
-
return { open, close, retain: retain2, reconnect, rotateWithin, stoppedStatus };
|
|
617
|
-
}
|
|
618
|
-
function sayHello(hellos, entry, hello) {
|
|
619
|
-
if (hellos.has(entry) && entry.last !== hello) {
|
|
620
|
-
entry.last = hello;
|
|
621
|
-
entry.listener(hello);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
function addListener(set, listener) {
|
|
625
|
-
set.add(listener);
|
|
626
|
-
return () => {
|
|
627
|
-
set.delete(listener);
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
function listenForHellos(wiring, listener) {
|
|
631
|
-
const { hellos, store } = wiring;
|
|
632
|
-
const entry = { listener, last: void 0 };
|
|
633
|
-
hellos.add(entry);
|
|
634
|
-
const { status, hello } = store.get();
|
|
635
|
-
if (status === "connected" && hello !== null) {
|
|
636
|
-
queueMicrotask(() => {
|
|
637
|
-
notifyEach([entry], (current) => {
|
|
638
|
-
sayHello(hellos, current, hello);
|
|
639
|
-
});
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
return () => {
|
|
643
|
-
hellos.delete(entry);
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
function listenToSocket(wiring) {
|
|
647
|
-
const { socket, store, lifecycle, session } = wiring;
|
|
648
|
-
socket.on("connect", () => {
|
|
649
|
-
const again = session.established;
|
|
650
|
-
session.established = true;
|
|
651
|
-
store.set({ status: "connected", reconnecting: false, refusal: null });
|
|
652
|
-
session.topics.rejoin();
|
|
653
|
-
if (again) {
|
|
654
|
-
notifyEach(wiring.reconnected, (listener) => {
|
|
655
|
-
listener();
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
socket.on("disconnect", (reason) => {
|
|
660
|
-
const status = socket.active ? "connecting" : lifecycle.stoppedStatus();
|
|
661
|
-
const ended = isServerDisconnect(reason) ? { hello: null, serviceAccess: null } : {};
|
|
662
|
-
store.set({ status, reconnecting: status === "connecting" && session.established, ...ended });
|
|
663
|
-
});
|
|
664
|
-
socket.on("connect_error", (error) => {
|
|
665
|
-
if (socket.active) {
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
const refusal = refusalOf(error);
|
|
669
|
-
session.established = false;
|
|
670
|
-
const ended = isSessionRefusal(error.data);
|
|
671
|
-
if (ended) {
|
|
672
|
-
failBuffered(
|
|
673
|
-
socket,
|
|
674
|
-
new QuickdrawError("CANCELLED", "The session ended before the call was sent")
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
store.set({
|
|
678
|
-
status: "refused",
|
|
679
|
-
reconnecting: false,
|
|
680
|
-
refusal,
|
|
681
|
-
...ended ? { hello: null, serviceAccess: null } : {}
|
|
682
|
-
});
|
|
683
|
-
if (refusal.code === "PROTOCOL_MISMATCH") {
|
|
684
|
-
wiring.onProtocolMismatch(refusal);
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
function listenToPushes(wiring) {
|
|
689
|
-
const { socket, store, lifecycle } = wiring;
|
|
690
|
-
socket.on(SERVER_EVENTS.hello, (frame) => {
|
|
691
|
-
const grants = isRecord(frame) && isRecord(frame.serviceAccess) ? frame.serviceAccess : null;
|
|
692
|
-
store.set({ hello: frame, serviceAccess: grants });
|
|
693
|
-
notifyEach(wiring.hellos, (entry) => {
|
|
694
|
-
sayHello(wiring.hellos, entry, frame);
|
|
695
|
-
});
|
|
696
|
-
});
|
|
697
|
-
socket.on(SERVER_EVENTS.access, (frame) => {
|
|
698
|
-
if (isRecord(frame) && isRecord(frame.serviceAccess)) {
|
|
699
|
-
store.set({ serviceAccess: frame.serviceAccess });
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
socket.on(SERVER_EVENTS.rotate, (frame) => {
|
|
703
|
-
const withinMs = isRecord(frame) ? frame.withinMs : void 0;
|
|
704
|
-
if (typeof withinMs === "number" && Number.isFinite(withinMs) && withinMs >= 0) {
|
|
705
|
-
lifecycle.rotateWithin(withinMs);
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
function createQuickdrawConnection(options) {
|
|
710
|
-
const timeoutOption = checkOptions(options);
|
|
711
|
-
const store = createStore();
|
|
712
|
-
const backoff = createBackoff((windows) => {
|
|
713
|
-
store.set({ backoff: windows });
|
|
714
|
-
});
|
|
715
|
-
let auth = options.auth;
|
|
716
|
-
let nextId = 0;
|
|
717
|
-
const socket = createSocket(options, () => credentialsOf(auth));
|
|
718
|
-
const timeoutMs = () => defaultTimeout(timeoutOption, store.get().hello);
|
|
719
|
-
const subscriptionLane = createSubscriptionLane({
|
|
720
|
-
socket,
|
|
721
|
-
timeoutMs,
|
|
722
|
-
hello: () => store.get().hello,
|
|
723
|
-
backoffRemaining: backoff.remaining
|
|
724
|
-
});
|
|
725
|
-
const topics = createTopics({
|
|
726
|
-
socket,
|
|
727
|
-
lane: subscriptionLane,
|
|
728
|
-
backoffRemaining: backoff.remaining,
|
|
729
|
-
reportRateLimited: backoff.report
|
|
730
|
-
});
|
|
731
|
-
const session = { established: false, topics, lane: subscriptionLane };
|
|
732
|
-
const lifecycle = createLifecycle(socket, store, backoff, session);
|
|
733
|
-
const reconnected = /* @__PURE__ */ new Set();
|
|
734
|
-
const wiring = {
|
|
735
|
-
socket,
|
|
736
|
-
store,
|
|
737
|
-
lifecycle,
|
|
738
|
-
session,
|
|
739
|
-
reconnected,
|
|
740
|
-
hellos: /* @__PURE__ */ new Set(),
|
|
741
|
-
onProtocolMismatch: options.onProtocolMismatch ?? reloadOncePerSession
|
|
742
|
-
};
|
|
743
|
-
listenToSocket(wiring);
|
|
744
|
-
listenToPushes(wiring);
|
|
745
|
-
return Object.freeze({
|
|
746
|
-
url: options.url,
|
|
747
|
-
get timeoutMs() {
|
|
748
|
-
return timeoutMs();
|
|
749
|
-
},
|
|
750
|
-
socket,
|
|
751
|
-
subscriptionLane,
|
|
752
|
-
getState: store.get,
|
|
753
|
-
subscribe: store.subscribe,
|
|
754
|
-
open: lifecycle.open,
|
|
755
|
-
close: lifecycle.close,
|
|
756
|
-
retain: lifecycle.retain,
|
|
757
|
-
setAuth(next2) {
|
|
758
|
-
if (authKey(next2) === authKey(auth)) {
|
|
759
|
-
return false;
|
|
760
|
-
}
|
|
761
|
-
auth = next2;
|
|
762
|
-
session.established = false;
|
|
763
|
-
failBuffered(
|
|
764
|
-
socket,
|
|
765
|
-
new QuickdrawError("CANCELLED", "The credentials changed before the call was sent")
|
|
766
|
-
);
|
|
767
|
-
store.set({ hello: null, serviceAccess: null });
|
|
768
|
-
lifecycle.reconnect();
|
|
769
|
-
return true;
|
|
770
|
-
},
|
|
771
|
-
nextCallId() {
|
|
772
|
-
const id = nextId;
|
|
773
|
-
nextId = nextId >= Number.MAX_SAFE_INTEGER ? 0 : nextId + 1;
|
|
774
|
-
return id;
|
|
775
|
-
},
|
|
776
|
-
reportRateLimited: backoff.report,
|
|
777
|
-
backoffRemaining: backoff.remaining,
|
|
778
|
-
onReconnect: (listener) => addListener(reconnected, listener),
|
|
779
|
-
onHello: (listener) => listenForHellos(wiring, listener),
|
|
780
|
-
watch: topics.watch,
|
|
781
|
-
waitForJoin: topics.waitForJoin
|
|
782
|
-
});
|
|
783
|
-
}
|
|
46
|
+
} from "./chunk-QSU47XEX.js";
|
|
784
47
|
|
|
785
48
|
// src/client/loopGuard.ts
|
|
786
49
|
var MUTATION_LIMIT = 5;
|
|
@@ -1258,10 +521,10 @@ function shownOf(held) {
|
|
|
1258
521
|
}));
|
|
1259
522
|
}
|
|
1260
523
|
function refusedOf(held) {
|
|
1261
|
-
const
|
|
524
|
+
const refused2 = (held ?? []).flatMap(
|
|
1262
525
|
(addition) => addition.refusal === void 0 ? [] : [{ item: addition.item, error: addition.refusal.error, addition }]
|
|
1263
526
|
);
|
|
1264
|
-
return
|
|
527
|
+
return refused2.length === 0 ? NO_REFUSALS : refused2;
|
|
1265
528
|
}
|
|
1266
529
|
function newAddition(service, collection, scope, item, keep2) {
|
|
1267
530
|
return {
|
|
@@ -1290,7 +553,7 @@ function unasked(additions) {
|
|
|
1290
553
|
}
|
|
1291
554
|
function weigh(held, evidence) {
|
|
1292
555
|
const ending = [];
|
|
1293
|
-
let
|
|
556
|
+
let refused2 = false;
|
|
1294
557
|
for (const addition of held) {
|
|
1295
558
|
const outcome = verdict(addition, evidence);
|
|
1296
559
|
if (outcome === "end") {
|
|
@@ -1298,10 +561,10 @@ function weigh(held, evidence) {
|
|
|
1298
561
|
} else if (outcome === "refuse" && addition.unknown !== void 0) {
|
|
1299
562
|
addition.refusal = addition.unknown.refusal;
|
|
1300
563
|
addition.unknown = void 0;
|
|
1301
|
-
|
|
564
|
+
refused2 = true;
|
|
1302
565
|
}
|
|
1303
566
|
}
|
|
1304
|
-
return { ending, refused:
|
|
567
|
+
return { ending, refused: refused2 };
|
|
1305
568
|
}
|
|
1306
569
|
function createAdditions(queryClient) {
|
|
1307
570
|
const byScope = /* @__PURE__ */ new Map();
|
|
@@ -1617,13 +880,13 @@ function settleScope(layers, ref, evidence) {
|
|
|
1617
880
|
if (layers.additions.size === 0) {
|
|
1618
881
|
return;
|
|
1619
882
|
}
|
|
1620
|
-
const { ending, refused:
|
|
883
|
+
const { ending, refused: refused2 } = layers.additions.settle(
|
|
1621
884
|
ref.service,
|
|
1622
885
|
ref.collection,
|
|
1623
886
|
ref.scope,
|
|
1624
887
|
evidence
|
|
1625
888
|
);
|
|
1626
|
-
discard(layers, [], ending,
|
|
889
|
+
discard(layers, [], ending, refused2 ? [ref.service] : []);
|
|
1627
890
|
}
|
|
1628
891
|
function resetLayers(layers) {
|
|
1629
892
|
const services = new Set(layers.views.keys());
|
|
@@ -1643,7 +906,7 @@ function resetLayers(layers) {
|
|
|
1643
906
|
layers.count = 0;
|
|
1644
907
|
changed(layers, services);
|
|
1645
908
|
}
|
|
1646
|
-
function
|
|
909
|
+
function createStore(queryClient) {
|
|
1647
910
|
const layers = {
|
|
1648
911
|
byRow: /* @__PURE__ */ new Map(),
|
|
1649
912
|
additions: createAdditions(queryClient),
|
|
@@ -1708,7 +971,7 @@ function createStore2(queryClient) {
|
|
|
1708
971
|
function storeOf(queryClient) {
|
|
1709
972
|
let store = stores.get(queryClient);
|
|
1710
973
|
if (store === void 0) {
|
|
1711
|
-
store =
|
|
974
|
+
store = createStore(queryClient);
|
|
1712
975
|
stores.set(queryClient, store);
|
|
1713
976
|
}
|
|
1714
977
|
return store;
|
|
@@ -2485,13 +1748,87 @@ function setState(feeds, feed, state) {
|
|
|
2485
1748
|
listener();
|
|
2486
1749
|
});
|
|
2487
1750
|
}
|
|
2488
|
-
function
|
|
1751
|
+
function answered(feeds, feed, reply) {
|
|
2489
1752
|
const seed = Array.isArray(reply.seed) ? reply.seed : [];
|
|
2490
1753
|
const items = latest([...seed, ...feed.pending ?? []], capOf(feed));
|
|
2491
1754
|
feed.pending = void 0;
|
|
2492
1755
|
setState(feeds, feed, Object.freeze({ items, isLoading: false, error: null }));
|
|
2493
1756
|
}
|
|
1757
|
+
var HOST_RETRY_MS = 1e3;
|
|
1758
|
+
function subscribeHost(feeds, feed) {
|
|
1759
|
+
clearTimeout(feed.retry);
|
|
1760
|
+
feed.retry = void 0;
|
|
1761
|
+
feed.release?.();
|
|
1762
|
+
feed.release = void 0;
|
|
1763
|
+
feed.attempt += 1;
|
|
1764
|
+
const { attempt } = feed;
|
|
1765
|
+
const current = () => feed.attempt === attempt && feeds.registry.get(feed.key) === feed;
|
|
1766
|
+
const { s: service, stream } = feed.frame;
|
|
1767
|
+
const scope = feed.frame.scope ?? "";
|
|
1768
|
+
const refused2 = (error) => {
|
|
1769
|
+
feed.pending = void 0;
|
|
1770
|
+
setState(feeds, feed, Object.freeze({ items: [], isLoading: false, error }));
|
|
1771
|
+
};
|
|
1772
|
+
if (!isName(scope) || scope.length > MAX_SCOPE_LENGTH) {
|
|
1773
|
+
refused2(
|
|
1774
|
+
new QuickdrawError(
|
|
1775
|
+
"VALIDATION",
|
|
1776
|
+
`${service}.${stream} is scoped: its scope is a string of 1 to ${MAX_SCOPE_LENGTH} characters`
|
|
1777
|
+
)
|
|
1778
|
+
);
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
if (feed.room === void 0) {
|
|
1782
|
+
refused2(
|
|
1783
|
+
new QuickdrawError(
|
|
1784
|
+
"NOT_FOUND",
|
|
1785
|
+
`No app room serves scope "${scope}" of ${service}.${stream}`
|
|
1786
|
+
)
|
|
1787
|
+
);
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
feed.pending = [];
|
|
1791
|
+
feed.release = hostLinksOf(feeds.host.connection).subscribe(feed.room, {
|
|
1792
|
+
service,
|
|
1793
|
+
stream,
|
|
1794
|
+
scope,
|
|
1795
|
+
seeded: (items) => {
|
|
1796
|
+
if (current()) {
|
|
1797
|
+
answered(feeds, feed, { seed: items });
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
item: (item) => {
|
|
1801
|
+
if (current()) {
|
|
1802
|
+
receive(feeds, [service, stream, scope, item]);
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
refused: (error) => {
|
|
1806
|
+
if (!current()) {
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
if (error.code === "RATE_LIMITED") {
|
|
1810
|
+
feed.retry = setTimeout(
|
|
1811
|
+
() => {
|
|
1812
|
+
subscribeHost(feeds, feed);
|
|
1813
|
+
},
|
|
1814
|
+
HOST_RETRY_MS * (1 + Math.random() / 2)
|
|
1815
|
+
);
|
|
1816
|
+
} else {
|
|
1817
|
+
refused2(new QuickdrawError(error.code, error.message));
|
|
1818
|
+
}
|
|
1819
|
+
},
|
|
1820
|
+
lost: () => {
|
|
1821
|
+
if (current()) {
|
|
1822
|
+
feed.pending = [];
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
2494
1827
|
function subscribe(feeds, feed) {
|
|
1828
|
+
if (feed.hosted) {
|
|
1829
|
+
subscribeHost(feeds, feed);
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
2495
1832
|
const { host } = feeds;
|
|
2496
1833
|
clearTimeout(feed.retry);
|
|
2497
1834
|
feed.retry = void 0;
|
|
@@ -2503,7 +1840,7 @@ function subscribe(feeds, feed) {
|
|
|
2503
1840
|
return;
|
|
2504
1841
|
}
|
|
2505
1842
|
if (outcome.kind === "ok") {
|
|
2506
|
-
|
|
1843
|
+
answered(feeds, feed, outcome.reply);
|
|
2507
1844
|
} else if (outcome.kind === "refused") {
|
|
2508
1845
|
feed.pending = void 0;
|
|
2509
1846
|
setState(feeds, feed, Object.freeze({ items: [], isLoading: false, error: outcome.error }));
|
|
@@ -2521,12 +1858,19 @@ function closeFeed(feeds, feed) {
|
|
|
2521
1858
|
const { socket } = feeds.host.connection;
|
|
2522
1859
|
clearTimeout(feed.retry);
|
|
2523
1860
|
feed.attempt += 1;
|
|
1861
|
+
if (feed.hosted) {
|
|
1862
|
+
feed.release?.();
|
|
1863
|
+
feed.release = void 0;
|
|
1864
|
+
setState(feeds, feed, PENDING_STREAM);
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
2524
1867
|
if (feed.sent && socket.connected) {
|
|
2525
1868
|
socket.emit(CLIENT_EVENTS.streamUnsub, feed.frame);
|
|
2526
1869
|
}
|
|
2527
1870
|
setState(feeds, feed, PENDING_STREAM);
|
|
2528
1871
|
}
|
|
2529
1872
|
function openFeed(target, scope, key) {
|
|
1873
|
+
const { host } = target;
|
|
2530
1874
|
const frame = scope === void 0 ? { s: target.service, stream: target.stream } : { s: target.service, stream: target.stream, scope };
|
|
2531
1875
|
return {
|
|
2532
1876
|
key,
|
|
@@ -2536,7 +1880,10 @@ function openFeed(target, scope, key) {
|
|
|
2536
1880
|
pending: void 0,
|
|
2537
1881
|
sent: false,
|
|
2538
1882
|
attempt: 0,
|
|
2539
|
-
retry: void 0
|
|
1883
|
+
retry: void 0,
|
|
1884
|
+
hosted: host !== void 0,
|
|
1885
|
+
room: host?.(scope ?? ""),
|
|
1886
|
+
release: void 0
|
|
2540
1887
|
};
|
|
2541
1888
|
}
|
|
2542
1889
|
function hold(feeds, target, scope, max) {
|
|
@@ -2596,6 +1943,7 @@ function listen(feeds, key, listener) {
|
|
|
2596
1943
|
};
|
|
2597
1944
|
}
|
|
2598
1945
|
function createStreamStore(host) {
|
|
1946
|
+
hostLinksOf(host.connection);
|
|
2599
1947
|
const feeds = {
|
|
2600
1948
|
host,
|
|
2601
1949
|
registry: createRegistry((feed) => {
|
|
@@ -2615,7 +1963,9 @@ function createStreamStore(host) {
|
|
|
2615
1963
|
},
|
|
2616
1964
|
resume() {
|
|
2617
1965
|
for (const feed of feeds.registry.held()) {
|
|
2618
|
-
|
|
1966
|
+
if (!feed.hosted || feed.release === void 0) {
|
|
1967
|
+
subscribe(feeds, feed);
|
|
1968
|
+
}
|
|
2619
1969
|
}
|
|
2620
1970
|
},
|
|
2621
1971
|
forget() {
|
|
@@ -2630,6 +1980,10 @@ function createStreamStore(host) {
|
|
|
2630
1980
|
feed.retry = void 0;
|
|
2631
1981
|
feed.attempt += 1;
|
|
2632
1982
|
feed.pending = void 0;
|
|
1983
|
+
if (feed.hosted) {
|
|
1984
|
+
feed.release?.();
|
|
1985
|
+
feed.release = void 0;
|
|
1986
|
+
}
|
|
2633
1987
|
}
|
|
2634
1988
|
}
|
|
2635
1989
|
});
|
|
@@ -2716,15 +2070,15 @@ function loadPage(p) {
|
|
|
2716
2070
|
const limit = p.pageLimit();
|
|
2717
2071
|
const frame = { ...scopeFrame(p), cursor, ...limit === void 0 ? {} : { limit } };
|
|
2718
2072
|
p.write({ loadingMore: true });
|
|
2719
|
-
let
|
|
2073
|
+
let answered3 = false;
|
|
2720
2074
|
const page = new Promise((resolve) => {
|
|
2721
2075
|
request2(p.host, CLIENT_EVENTS.collectionSub, frame, (outcome) => {
|
|
2722
|
-
|
|
2076
|
+
answered3 = true;
|
|
2723
2077
|
p.page = void 0;
|
|
2724
2078
|
resolve(p.disposed ? false : pageLoaded(p, sent, outcome));
|
|
2725
2079
|
});
|
|
2726
2080
|
});
|
|
2727
|
-
if (!
|
|
2081
|
+
if (!answered3) {
|
|
2728
2082
|
p.page = page;
|
|
2729
2083
|
}
|
|
2730
2084
|
return page;
|
|
@@ -2897,7 +2251,7 @@ function applyLoad(p, reply, kept, readAt) {
|
|
|
2897
2251
|
observeItems(p, reply.items, reply.rev, readAt);
|
|
2898
2252
|
applied = { state: applySnapshot(base, reply, p.shape), reset: false, missing: [] };
|
|
2899
2253
|
} else {
|
|
2900
|
-
|
|
2254
|
+
refused(p, malformed("qd:col:sub"));
|
|
2901
2255
|
return;
|
|
2902
2256
|
}
|
|
2903
2257
|
for (const frame of kept) {
|
|
@@ -2921,7 +2275,7 @@ function applyLoad(p, reply, kept, readAt) {
|
|
|
2921
2275
|
reloaded
|
|
2922
2276
|
);
|
|
2923
2277
|
}
|
|
2924
|
-
function
|
|
2278
|
+
function refused(p, error) {
|
|
2925
2279
|
p.joined = false;
|
|
2926
2280
|
const drops = ["FORBIDDEN", "NOT_FOUND", "UNAUTHENTICATED"].includes(error.code);
|
|
2927
2281
|
write(p, drops ? { state: null, error, loadingMore: false } : { error });
|
|
@@ -2937,7 +2291,7 @@ function loaded(p, { generation, kind, readAt }, outcome) {
|
|
|
2937
2291
|
if (outcome.kind === "ok") {
|
|
2938
2292
|
applyLoad(p, outcome.reply, kept, readAt);
|
|
2939
2293
|
} else if (outcome.kind === "refused") {
|
|
2940
|
-
|
|
2294
|
+
refused(p, outcome.error);
|
|
2941
2295
|
} else if (outcome.kind === "retry") {
|
|
2942
2296
|
p.retryTimer = setTimeout(() => {
|
|
2943
2297
|
p.retryTimer = void 0;
|
|
@@ -3486,8 +2840,8 @@ function entityResultOf(value) {
|
|
|
3486
2840
|
if (isRecord(value) && value.ok === false) {
|
|
3487
2841
|
return value;
|
|
3488
2842
|
}
|
|
3489
|
-
const
|
|
3490
|
-
return
|
|
2843
|
+
const answered3 = isRecord(value) && value.ok === true && isRevision(value.rev) && (value.nm === true || Object.hasOwn(value, "d"));
|
|
2844
|
+
return answered3 ? value : { ok: false, e: { code: "INTERNAL", message: "The server's qd:sub reply is malformed" } };
|
|
3491
2845
|
}
|
|
3492
2846
|
|
|
3493
2847
|
// src/client/live/entityStore.ts
|
|
@@ -3569,7 +2923,7 @@ function apply(state, batch, results) {
|
|
|
3569
2923
|
commit(state, row, before, applyEntityResult(before, result, batch.readAt));
|
|
3570
2924
|
});
|
|
3571
2925
|
}
|
|
3572
|
-
function
|
|
2926
|
+
function answered2(state, batch, outcome) {
|
|
3573
2927
|
if (batch.epoch !== state.epoch || outcome.kind === "offline") {
|
|
3574
2928
|
return;
|
|
3575
2929
|
}
|
|
@@ -3588,7 +2942,7 @@ function send(state, service, ids) {
|
|
|
3588
2942
|
const batch = { service, ids, epoch: state.epoch, readAt: state.host.overlays.now() };
|
|
3589
2943
|
const frame = revs.some((rev) => rev !== null) ? { s: service, ids, revs } : { s: service, ids };
|
|
3590
2944
|
request2(state.host, CLIENT_EVENTS.sub, frame, (outcome) => {
|
|
3591
|
-
|
|
2945
|
+
answered2(state, batch, outcome);
|
|
3592
2946
|
});
|
|
3593
2947
|
}
|
|
3594
2948
|
function flush(state) {
|
|
@@ -4261,13 +3615,6 @@ function showCollection(state, options) {
|
|
|
4261
3615
|
}
|
|
4262
3616
|
|
|
4263
3617
|
export {
|
|
4264
|
-
DEFAULT_BACKOFF_MS,
|
|
4265
|
-
retryAfterOf,
|
|
4266
|
-
notifyEach,
|
|
4267
|
-
DEFAULT_SUBSCRIPTION_LANE,
|
|
4268
|
-
reloadOncePerSession,
|
|
4269
|
-
isTimeLimit,
|
|
4270
|
-
createQuickdrawConnection,
|
|
4271
3618
|
createMutationTrace,
|
|
4272
3619
|
loopGuardOf,
|
|
4273
3620
|
DEFAULT_INVALIDATION_WINDOW_MS,
|
|
@@ -4324,4 +3671,4 @@ export {
|
|
|
4324
3671
|
viewPredicate,
|
|
4325
3672
|
showCollection
|
|
4326
3673
|
};
|
|
4327
|
-
//# sourceMappingURL=chunk-
|
|
3674
|
+
//# sourceMappingURL=chunk-64I6MYBC.js.map
|