@fitzzero/quickdraw-core 5.1.1 → 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 +1308 -219
- package/dist/{admin-DKwkIh5i.d.ts → admin-CS7JB_UH.d.ts} +2 -2
- package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-29cGgrg5.d.ts} +6 -4
- package/dist/{auth-BhWqdyb-.d.ts → auth-DtdYjrZ9.d.ts} +51 -21
- package/dist/{caller-CXTFswBO.d.ts → caller-BTwGl7vm.d.ts} +296 -33
- package/dist/chunk-2FDSKLBO.js +82 -0
- package/dist/chunk-2FDSKLBO.js.map +1 -0
- package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
- package/dist/chunk-4D2DOGAW.js.map +1 -0
- package/dist/{chunk-OECZPRPW.js → chunk-64I6MYBC.js} +162 -775
- 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-AK23XSWD.js +16 -0
- package/dist/chunk-AK23XSWD.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-DKE6QO26.js +136 -0
- package/dist/chunk-DKE6QO26.js.map +1 -0
- package/dist/chunk-DNP65TD5.js +832 -0
- package/dist/chunk-DNP65TD5.js.map +1 -0
- package/dist/{chunk-H23KEFBA.js → chunk-DUBU3BB3.js} +30 -1
- package/dist/chunk-DUBU3BB3.js.map +1 -0
- package/dist/chunk-GYLWEJHW.js +19 -0
- package/dist/chunk-GYLWEJHW.js.map +1 -0
- package/dist/{chunk-SHEGLZEP.js → chunk-I3JCFS7H.js} +34 -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-RZTP4IEW.js → chunk-JVTONQFI.js} +6339 -4226
- package/dist/chunk-JVTONQFI.js.map +1 -0
- package/dist/{chunk-QEKVYG5F.js → chunk-KKQANRLG.js} +2 -2
- package/dist/chunk-KKQANRLG.js.map +1 -0
- package/dist/{chunk-CDKG4TFA.js → chunk-MPQAT55F.js} +38 -205
- package/dist/chunk-MPQAT55F.js.map +1 -0
- package/dist/chunk-NO4M6PO5.js +101 -0
- package/dist/chunk-NO4M6PO5.js.map +1 -0
- package/dist/{chunk-TBMKJ26R.js → chunk-NW6U45HX.js} +3 -1
- package/dist/chunk-NW6U45HX.js.map +1 -0
- package/dist/chunk-NX2V6SIL.js +202 -0
- package/dist/chunk-NX2V6SIL.js.map +1 -0
- package/dist/{chunk-ZIVPHYRG.js → chunk-NZHA4TZD.js} +72 -175
- package/dist/chunk-NZHA4TZD.js.map +1 -0
- package/dist/{chunk-2PWMUVC7.js → chunk-OCZJ4EYV.js} +518 -27
- package/dist/chunk-OCZJ4EYV.js.map +1 -0
- package/dist/{chunk-REX5YPHX.js → chunk-PHCURHC7.js} +76 -21
- 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-DD4SKCNO.js → chunk-RZXJTDH5.js} +13 -2
- package/dist/chunk-RZXJTDH5.js.map +1 -0
- package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
- package/dist/chunk-SN3OZVEQ.js.map +1 -0
- package/dist/{chunk-4DIQHASK.js → chunk-SQ5VUG77.js} +28 -8
- package/dist/chunk-SQ5VUG77.js.map +1 -0
- package/dist/chunk-STH5DLQP.js +1192 -0
- package/dist/chunk-STH5DLQP.js.map +1 -0
- package/dist/{chunk-QCHKWIRS.js → chunk-VZFJTKIW.js} +13 -13
- package/dist/{chunk-QCHKWIRS.js.map → chunk-VZFJTKIW.js.map} +1 -1
- package/dist/{chunk-VSQBHPWH.js → chunk-X4ZU4QH5.js} +54 -22
- 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-XH2IZVLT.js +62 -0
- package/dist/chunk-XH2IZVLT.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 +132 -112
- 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/index.d.ts +52 -203
- package/dist/client/index.js +59 -218
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
- package/dist/{connection-Cr1BU07W.d.ts → connection-2EeQ_Et0.d.ts} +22 -11
- package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Bx5HOcYK.d.ts} +67 -17
- package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-DAMANT79.d.ts} +46 -13
- package/dist/{envelope-Cp3YcVxC.d.ts → envelope-BMBaxppV.d.ts} +216 -10
- package/dist/{http-Djj2Inuu.d.ts → http-YzLAar3w.d.ts} +76 -60
- package/dist/index.d.ts +8 -7
- package/dist/index.js +82 -25
- package/dist/index.js.map +1 -1
- package/dist/{infer-Teo1xpsd.d.ts → infer-Br62cnyW.d.ts} +82 -11
- package/dist/logger-Yz93KzdV.d.ts +30 -0
- package/dist/{metrics-BdBu5o8G.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
- package/dist/origins-kvpxnVcW.d.ts +8 -0
- package/dist/parser.js +2 -1
- package/dist/prisma/index.d.ts +14 -7
- package/dist/prisma/index.js +279 -70
- package/dist/prisma/index.js.map +1 -1
- package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
- package/dist/rateLimit-5RKBFLUR.js.map +1 -0
- package/dist/remote-N7OUAGOW.js +15 -0
- package/dist/remote-N7OUAGOW.js.map +1 -0
- package/dist/server/auth/index.d.ts +82 -33
- package/dist/server/auth/index.js +277 -270
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/express/index.d.ts +9 -2
- package/dist/server/express/index.js +1 -1
- package/dist/server/index.d.ts +268 -84
- package/dist/server/index.js +664 -418
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +89 -27
- package/dist/server/mcp/index.js +281 -62
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-DVa8W-yR.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
- package/dist/testing/client.d.ts +14 -14
- package/dist/testing/client.js +19 -10
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +50 -13
- package/dist/testing/index.js +113 -21
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +17 -9
- package/dist/tracked-BR5b2O-U.d.ts +43 -0
- package/dist/{types-Cu0vmx7c.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-2PWMUVC7.js.map +0 -1
- package/dist/chunk-4DIQHASK.js.map +0 -1
- package/dist/chunk-4YO5H5ON.js.map +0 -1
- package/dist/chunk-B2P7AZ25.js.map +0 -1
- package/dist/chunk-CDKG4TFA.js.map +0 -1
- package/dist/chunk-DD4SKCNO.js.map +0 -1
- package/dist/chunk-H23KEFBA.js.map +0 -1
- package/dist/chunk-OECZPRPW.js.map +0 -1
- package/dist/chunk-QEKVYG5F.js.map +0 -1
- package/dist/chunk-REX5YPHX.js.map +0 -1
- package/dist/chunk-RT6OILOI.js.map +0 -1
- package/dist/chunk-RZTP4IEW.js.map +0 -1
- package/dist/chunk-SHEGLZEP.js.map +0 -1
- package/dist/chunk-TBMKJ26R.js.map +0 -1
- package/dist/chunk-VSQBHPWH.js.map +0 -1
- package/dist/chunk-X5CWPX7H.js.map +0 -1
- package/dist/chunk-YSWH776Y.js +0 -13
- package/dist/chunk-YSWH776Y.js.map +0 -1
- package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
- package/dist/chunk-ZIVPHYRG.js.map +0 -1
- package/dist/logger-BY28acW-.d.ts +0 -16
- /package/dist/{rateLimit-ZMIZJUZF.js.map → client/core.js.map} +0 -0
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
adminSpecOf,
|
|
3
2
|
searchSpecOf
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
ACCESS_LEVELS,
|
|
7
|
-
CLIENT_EVENTS,
|
|
8
|
-
MAX_SUBSCRIBE_IDS,
|
|
9
|
-
PROTOCOL_VERSION,
|
|
10
|
-
QUICKDRAW_VERSION,
|
|
11
|
-
SERVER_EVENTS,
|
|
12
|
-
isAuthenticationRefused,
|
|
13
|
-
isProtocolMismatch
|
|
14
|
-
} from "./chunk-VSQBHPWH.js";
|
|
3
|
+
} from "./chunk-XD7SRDUY.js";
|
|
15
4
|
import {
|
|
16
5
|
DEFAULT_COLLECTION_MAX_LIMIT
|
|
17
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4D2DOGAW.js";
|
|
18
7
|
import {
|
|
19
|
-
|
|
20
|
-
} from "./chunk-
|
|
8
|
+
ACCESS_LEVELS
|
|
9
|
+
} from "./chunk-AK23XSWD.js";
|
|
21
10
|
import {
|
|
22
11
|
consoleLogger
|
|
23
|
-
} from "./chunk-
|
|
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";
|
|
24
23
|
import {
|
|
25
24
|
KEY_ROOT,
|
|
26
25
|
collectionKey,
|
|
@@ -28,738 +27,23 @@ import {
|
|
|
28
27
|
methodKey,
|
|
29
28
|
methodKeyPrefix,
|
|
30
29
|
serviceKeyPrefix
|
|
31
|
-
} 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";
|
|
32
39
|
import {
|
|
33
40
|
QuickdrawError,
|
|
34
|
-
fromWire
|
|
41
|
+
fromWire
|
|
42
|
+
} from "./chunk-Q4OGQM5I.js";
|
|
43
|
+
import {
|
|
35
44
|
isName,
|
|
36
45
|
isRecord
|
|
37
|
-
} from "./chunk-
|
|
38
|
-
|
|
39
|
-
// src/client/backoff.ts
|
|
40
|
-
var DEFAULT_BACKOFF_MS = 5e3;
|
|
41
|
-
var MIN_BACKOFF_MS = 250;
|
|
42
|
-
var MAX_BACKOFF_MS = 5 * 60 * 1e3;
|
|
43
|
-
var JITTER_RATIO = 0.5;
|
|
44
|
-
function retryAfterOf(error) {
|
|
45
|
-
const retryAfterMs = isRecord(error.data) ? error.data.retryAfterMs : void 0;
|
|
46
|
-
return typeof retryAfterMs === "number" ? retryAfterMs : void 0;
|
|
47
|
-
}
|
|
48
|
-
function waitFor(retryAfterMs) {
|
|
49
|
-
const wait2 = typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) ? retryAfterMs : DEFAULT_BACKOFF_MS;
|
|
50
|
-
return Math.min(Math.max(wait2, MIN_BACKOFF_MS), MAX_BACKOFF_MS);
|
|
51
|
-
}
|
|
52
|
-
function createBackoff(onChange) {
|
|
53
|
-
const until = /* @__PURE__ */ new Map();
|
|
54
|
-
const timers = /* @__PURE__ */ new Map();
|
|
55
|
-
const windows = () => Object.freeze(Object.fromEntries(until));
|
|
56
|
-
function end(kind) {
|
|
57
|
-
timers.delete(kind);
|
|
58
|
-
until.delete(kind);
|
|
59
|
-
onChange(windows());
|
|
60
|
-
}
|
|
61
|
-
return Object.freeze({
|
|
62
|
-
report(kind, retryAfterMs) {
|
|
63
|
-
const wait2 = waitFor(retryAfterMs);
|
|
64
|
-
const delay = wait2 + Math.random() * wait2 * JITTER_RATIO;
|
|
65
|
-
const ends2 = Date.now() + delay;
|
|
66
|
-
if (ends2 <= (until.get(kind) ?? 0)) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
clearTimeout(timers.get(kind));
|
|
70
|
-
until.set(kind, ends2);
|
|
71
|
-
timers.set(
|
|
72
|
-
kind,
|
|
73
|
-
setTimeout(() => {
|
|
74
|
-
end(kind);
|
|
75
|
-
}, delay)
|
|
76
|
-
);
|
|
77
|
-
onChange(windows());
|
|
78
|
-
},
|
|
79
|
-
remaining(kind) {
|
|
80
|
-
const ends2 = until.get(kind);
|
|
81
|
-
return ends2 === void 0 ? 0 : Math.max(0, ends2 - Date.now());
|
|
82
|
-
},
|
|
83
|
-
windows,
|
|
84
|
-
clear() {
|
|
85
|
-
for (const timer of timers.values()) {
|
|
86
|
-
clearTimeout(timer);
|
|
87
|
-
}
|
|
88
|
-
timers.clear();
|
|
89
|
-
if (until.size > 0) {
|
|
90
|
-
until.clear();
|
|
91
|
-
onChange(windows());
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// src/client/watch.ts
|
|
98
|
-
function topicId(service, topic) {
|
|
99
|
-
return `${service}\0${topic}`;
|
|
100
|
-
}
|
|
101
|
-
function rethrowLater(error) {
|
|
102
|
-
queueMicrotask(() => {
|
|
103
|
-
throw error;
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
function notifyEach(listeners, notify) {
|
|
107
|
-
for (const listener of [...listeners]) {
|
|
108
|
-
try {
|
|
109
|
-
notify(listener);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
rethrowLater(error);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function oncePerKey(records) {
|
|
116
|
-
const keys = /* @__PURE__ */ new Set();
|
|
117
|
-
return [...records].filter(({ watch }) => {
|
|
118
|
-
if (watch.key === void 0) {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
const first = !keys.has(watch.key);
|
|
122
|
-
keys.add(watch.key);
|
|
123
|
-
return first;
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
function modelName(model) {
|
|
127
|
-
return `${model.charAt(0).toLowerCase()}${model.slice(1)}`;
|
|
128
|
-
}
|
|
129
|
-
function concerns(watch, frame) {
|
|
130
|
-
const { models } = watch;
|
|
131
|
-
if (models === void 0 || !Array.isArray(frame.models)) {
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
const changed3 = new Set(frame.models.map((model) => modelName(String(model))));
|
|
135
|
-
return models.some((model) => changed3.has(modelName(model)));
|
|
136
|
-
}
|
|
137
|
-
function tell(topic, frame) {
|
|
138
|
-
const concerned = [...topic.watches].filter(({ watch }) => concerns(watch, frame));
|
|
139
|
-
notifyEach(oncePerKey(concerned), ({ watch }) => {
|
|
140
|
-
watch.onChanged(frame);
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
function tellJoined(topic) {
|
|
144
|
-
const waited = new Set([...topic.holds].map((hold2) => hold2.key));
|
|
145
|
-
const unjoined = new Set(topic.unjoinedReads);
|
|
146
|
-
topic.unjoinedReads.clear();
|
|
147
|
-
const owed = [...topic.watches].filter(
|
|
148
|
-
({ watch, told }) => !told || watch.key !== void 0 && unjoined.has(watch.key)
|
|
149
|
-
);
|
|
150
|
-
for (const record of owed) {
|
|
151
|
-
record.told = true;
|
|
152
|
-
}
|
|
153
|
-
const toTell = owed.filter(({ watch }) => watch.key === void 0 || !waited.has(watch.key));
|
|
154
|
-
notifyEach(oncePerKey(toTell), ({ watch }) => {
|
|
155
|
-
watch.onJoined?.();
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
function releaseHolds(topic) {
|
|
159
|
-
const holds = [...topic.holds];
|
|
160
|
-
topic.holds.clear();
|
|
161
|
-
for (const hold2 of holds) {
|
|
162
|
-
if (topic.state !== "joined" && hold2.key !== void 0) {
|
|
163
|
-
topic.unjoinedReads.add(hold2.key);
|
|
164
|
-
}
|
|
165
|
-
hold2.release();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
function isChangedFrame(value) {
|
|
169
|
-
return isRecord(value) && typeof value.s === "string" && typeof value.topic === "string";
|
|
170
|
-
}
|
|
171
|
-
function retryIn(registry, topic, delayMs) {
|
|
172
|
-
clearTimeout(topic.retry);
|
|
173
|
-
topic.state = "waiting";
|
|
174
|
-
topic.retry = setTimeout(() => {
|
|
175
|
-
topic.retry = void 0;
|
|
176
|
-
if (registry.topics.get(topic.id) === topic) {
|
|
177
|
-
join(registry, topic);
|
|
178
|
-
}
|
|
179
|
-
}, delayMs);
|
|
180
|
-
}
|
|
181
|
-
function refused(registry, topic, reply) {
|
|
182
|
-
const { host } = registry;
|
|
183
|
-
const failure = fromWire(isRecord(reply) ? reply.e : void 0);
|
|
184
|
-
if (failure.code === "RATE_LIMITED") {
|
|
185
|
-
host.reportRateLimited("subscription", retryAfterOf(failure));
|
|
186
|
-
retryIn(registry, topic, host.backoffRemaining("subscription"));
|
|
187
|
-
} else {
|
|
188
|
-
topic.state = "refused";
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
function answered(registry, topic, error, reply) {
|
|
192
|
-
if (error !== null) {
|
|
193
|
-
if (registry.host.socket.connected) {
|
|
194
|
-
retryIn(registry, topic, DEFAULT_BACKOFF_MS);
|
|
195
|
-
} else {
|
|
196
|
-
topic.state = "waiting";
|
|
197
|
-
}
|
|
198
|
-
} else if (isRecord(reply) && reply.ok === true) {
|
|
199
|
-
topic.state = "joined";
|
|
200
|
-
tellJoined(topic);
|
|
201
|
-
} else {
|
|
202
|
-
refused(registry, topic, reply);
|
|
203
|
-
}
|
|
204
|
-
releaseHolds(topic);
|
|
205
|
-
}
|
|
206
|
-
function join(registry, topic) {
|
|
207
|
-
const { host } = registry;
|
|
208
|
-
clearTimeout(topic.retry);
|
|
209
|
-
topic.retry = void 0;
|
|
210
|
-
topic.attempt += 1;
|
|
211
|
-
const wait2 = host.backoffRemaining("subscription");
|
|
212
|
-
if (!host.socket.connected) {
|
|
213
|
-
topic.state = "waiting";
|
|
214
|
-
releaseHolds(topic);
|
|
215
|
-
} else if (wait2 > 0) {
|
|
216
|
-
retryIn(registry, topic, wait2);
|
|
217
|
-
releaseHolds(topic);
|
|
218
|
-
} else {
|
|
219
|
-
topic.state = "joining";
|
|
220
|
-
const { attempt } = topic;
|
|
221
|
-
host.lane.send(CLIENT_EVENTS.watch, topic.frame, (error, reply) => {
|
|
222
|
-
if (registry.topics.get(topic.id) === topic && topic.attempt === attempt) {
|
|
223
|
-
answered(registry, topic, error, reply);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
function leave(registry, topic) {
|
|
229
|
-
registry.topics.delete(topic.id);
|
|
230
|
-
clearTimeout(topic.retry);
|
|
231
|
-
clearTimeout(topic.leaving);
|
|
232
|
-
topic.leaving = void 0;
|
|
233
|
-
topic.attempt += 1;
|
|
234
|
-
releaseHolds(topic);
|
|
235
|
-
const sent = topic.state === "joining" || topic.state === "joined";
|
|
236
|
-
if (sent && registry.host.socket.connected) {
|
|
237
|
-
registry.host.socket.emit(CLIENT_EVENTS.unwatch, topic.frame);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function waitForJoin(registry, wait2) {
|
|
241
|
-
const topic = registry.topics.get(topicId(wait2.service, wait2.topic));
|
|
242
|
-
if (topic === void 0) {
|
|
243
|
-
return void 0;
|
|
244
|
-
}
|
|
245
|
-
if (topic.state === "joining" && registry.host.socket.connected) {
|
|
246
|
-
return new Promise((resolve) => {
|
|
247
|
-
topic.holds.add({ key: wait2.key, release: resolve });
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (topic.state !== "joined" && wait2.key !== void 0) {
|
|
251
|
-
topic.unjoinedReads.add(wait2.key);
|
|
252
|
-
}
|
|
253
|
-
return void 0;
|
|
254
|
-
}
|
|
255
|
-
function startWatch(registry, watch) {
|
|
256
|
-
const id = topicId(watch.service, watch.topic);
|
|
257
|
-
const topic = registry.topics.get(id) ?? {
|
|
258
|
-
id,
|
|
259
|
-
frame: Object.freeze({ s: watch.service, topic: watch.topic }),
|
|
260
|
-
watches: /* @__PURE__ */ new Set(),
|
|
261
|
-
holds: /* @__PURE__ */ new Set(),
|
|
262
|
-
unjoinedReads: /* @__PURE__ */ new Set(),
|
|
263
|
-
state: "waiting",
|
|
264
|
-
retry: void 0,
|
|
265
|
-
leaving: void 0,
|
|
266
|
-
attempt: 0
|
|
267
|
-
};
|
|
268
|
-
registry.topics.set(id, topic);
|
|
269
|
-
const kept = topic.leaving !== void 0;
|
|
270
|
-
clearTimeout(topic.leaving);
|
|
271
|
-
topic.leaving = void 0;
|
|
272
|
-
const record = { watch, told: topic.state === "joined" };
|
|
273
|
-
topic.watches.add(record);
|
|
274
|
-
if (topic.watches.size === 1 && !kept) {
|
|
275
|
-
join(registry, topic);
|
|
276
|
-
}
|
|
277
|
-
return () => {
|
|
278
|
-
if (topic.watches.delete(record) && topic.watches.size === 0) {
|
|
279
|
-
topic.leaving = setTimeout(() => {
|
|
280
|
-
if (topic.watches.size === 0 && registry.topics.get(topic.id) === topic) {
|
|
281
|
-
leave(registry, topic);
|
|
282
|
-
}
|
|
283
|
-
}, 0);
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
function createTopics(host) {
|
|
288
|
-
const registry = { host, topics: /* @__PURE__ */ new Map() };
|
|
289
|
-
host.socket.on(SERVER_EVENTS.changed, (frame) => {
|
|
290
|
-
const topic = isChangedFrame(frame) ? registry.topics.get(topicId(frame.s, frame.topic)) : void 0;
|
|
291
|
-
if (topic !== void 0) {
|
|
292
|
-
tell(topic, frame);
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
host.socket.on("disconnect", () => {
|
|
296
|
-
for (const topic of registry.topics.values()) {
|
|
297
|
-
if (topic.state === "joined") {
|
|
298
|
-
topic.state = "waiting";
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
return Object.freeze({
|
|
303
|
-
watch: (watch) => startWatch(registry, watch),
|
|
304
|
-
waitForJoin: (wait2) => waitForJoin(registry, wait2),
|
|
305
|
-
rejoin() {
|
|
306
|
-
for (const topic of [...registry.topics.values()]) {
|
|
307
|
-
if (topic.watches.size === 0) {
|
|
308
|
-
leave(registry, topic);
|
|
309
|
-
} else {
|
|
310
|
-
join(registry, topic);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
stop() {
|
|
315
|
-
for (const topic of [...registry.topics.values()]) {
|
|
316
|
-
if (topic.watches.size === 0) {
|
|
317
|
-
leave(registry, topic);
|
|
318
|
-
} else {
|
|
319
|
-
clearTimeout(topic.retry);
|
|
320
|
-
topic.retry = void 0;
|
|
321
|
-
topic.attempt += 1;
|
|
322
|
-
topic.state = "waiting";
|
|
323
|
-
releaseHolds(topic);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// src/client/lane.ts
|
|
331
|
-
var DEFAULT_SUBSCRIPTION_LANE = 8;
|
|
332
|
-
function positiveInteger(value, fallback) {
|
|
333
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : fallback;
|
|
334
|
-
}
|
|
335
|
-
function subscriptionLimits(hello) {
|
|
336
|
-
const limits = hello?.limits;
|
|
337
|
-
const record = isRecord(limits) ? limits : {};
|
|
338
|
-
const lane = isRecord(record.subscriptions) ? record.subscriptions : {};
|
|
339
|
-
return {
|
|
340
|
-
maxInFlight: positiveInteger(lane.maxInFlight, DEFAULT_SUBSCRIPTION_LANE),
|
|
341
|
-
maxSubscribeIds: positiveInteger(record.maxSubscribeIds, MAX_SUBSCRIBE_IDS)
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
function notConnected() {
|
|
345
|
-
return new Error("The socket is not connected; the event was not sent");
|
|
346
|
-
}
|
|
347
|
-
function createSubscriptionLane(host) {
|
|
348
|
-
const queue = [];
|
|
349
|
-
let inFlight = 0;
|
|
350
|
-
let backoffTimer;
|
|
351
|
-
const answer = (pending, error, reply) => {
|
|
352
|
-
notifyEach([pending.done], (done) => {
|
|
353
|
-
done(error, reply);
|
|
354
|
-
});
|
|
355
|
-
};
|
|
356
|
-
function sendOne(pending) {
|
|
357
|
-
inFlight += 1;
|
|
358
|
-
const emitter = host.socket.timeout(host.timeoutMs());
|
|
359
|
-
emitter.emit(pending.event, pending.frame, (error, reply) => {
|
|
360
|
-
inFlight -= 1;
|
|
361
|
-
answer(pending, error, reply);
|
|
362
|
-
pump();
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
function waitForBackoff(wait2) {
|
|
366
|
-
backoffTimer ??= setTimeout(() => {
|
|
367
|
-
backoffTimer = void 0;
|
|
368
|
-
pump();
|
|
369
|
-
}, wait2);
|
|
370
|
-
}
|
|
371
|
-
function dropAll() {
|
|
372
|
-
for (const dropped of queue.splice(0)) {
|
|
373
|
-
answer(dropped, notConnected(), void 0);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
function pump() {
|
|
377
|
-
const { maxInFlight } = subscriptionLimits(host.hello());
|
|
378
|
-
while (queue.length > 0 && inFlight < maxInFlight) {
|
|
379
|
-
if (!host.socket.connected) {
|
|
380
|
-
dropAll();
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
const wait2 = host.backoffRemaining("subscription");
|
|
384
|
-
if (wait2 > 0) {
|
|
385
|
-
waitForBackoff(wait2);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
sendOne(queue.shift());
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return Object.freeze({
|
|
392
|
-
send(event, frame, done) {
|
|
393
|
-
queue.push({ event, frame, done });
|
|
394
|
-
pump();
|
|
395
|
-
},
|
|
396
|
-
inFlight: () => inFlight,
|
|
397
|
-
waiting: () => queue.length,
|
|
398
|
-
stop() {
|
|
399
|
-
clearTimeout(backoffTimer);
|
|
400
|
-
backoffTimer = void 0;
|
|
401
|
-
dropAll();
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// src/client/reload.ts
|
|
407
|
-
var RELOAD_KEY_PREFIX = "quickdraw:protocol-reload:";
|
|
408
|
-
function firstReload(scope, key) {
|
|
409
|
-
try {
|
|
410
|
-
const storage = scope.sessionStorage;
|
|
411
|
-
if (storage === void 0 || storage.getItem(key) !== null) {
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
storage.setItem(key, String(Date.now()));
|
|
415
|
-
return true;
|
|
416
|
-
} catch {
|
|
417
|
-
return false;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
function reloadOncePerSession(mismatch) {
|
|
421
|
-
const scope = globalThis;
|
|
422
|
-
const location = scope.location;
|
|
423
|
-
if (typeof location?.reload !== "function") {
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
if (firstReload(scope, `${RELOAD_KEY_PREFIX}${mismatch.expected}`)) {
|
|
427
|
-
location.reload();
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// src/client/socket.ts
|
|
432
|
-
import { io } from "socket.io-client";
|
|
433
|
-
function createSocket(settings, credentials) {
|
|
434
|
-
return io(settings.url, {
|
|
435
|
-
forceNew: true,
|
|
436
|
-
withCredentials: true,
|
|
437
|
-
transports: ["websocket", "polling"],
|
|
438
|
-
...settings.binary === true ? {} : { parser: createJsonParser() },
|
|
439
|
-
...settings.socketOptions,
|
|
440
|
-
...settings.transports === void 0 ? {} : { transports: settings.transports },
|
|
441
|
-
autoConnect: false,
|
|
442
|
-
auth: (send2) => {
|
|
443
|
-
send2({ ...credentials(), qd: { protocol: PROTOCOL_VERSION, client: QUICKDRAW_VERSION } });
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// src/client/connection.ts
|
|
449
|
-
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
450
|
-
var HELLO_TIMEOUT_MARGIN_MS = 2e3;
|
|
451
|
-
var INITIAL_CONNECTION_STATE = Object.freeze({
|
|
452
|
-
status: "idle",
|
|
453
|
-
reconnecting: false,
|
|
454
|
-
hello: null,
|
|
455
|
-
serviceAccess: null,
|
|
456
|
-
refusal: null,
|
|
457
|
-
backoff: Object.freeze({})
|
|
458
|
-
});
|
|
459
|
-
function createStore() {
|
|
460
|
-
let state = INITIAL_CONNECTION_STATE;
|
|
461
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
462
|
-
return {
|
|
463
|
-
get: () => state,
|
|
464
|
-
set(patch2) {
|
|
465
|
-
state = Object.freeze({ ...state, ...patch2 });
|
|
466
|
-
for (const listener of [...listeners]) {
|
|
467
|
-
listener();
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
subscribe(listener) {
|
|
471
|
-
listeners.add(listener);
|
|
472
|
-
return () => {
|
|
473
|
-
listeners.delete(listener);
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
function credentialsOf(auth) {
|
|
479
|
-
if (typeof auth === "string") {
|
|
480
|
-
return auth === "" ? {} : { token: auth };
|
|
481
|
-
}
|
|
482
|
-
return auth ?? {};
|
|
483
|
-
}
|
|
484
|
-
function authKey(auth) {
|
|
485
|
-
const sorted = (_key, value) => isRecord(value) ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : 1)) : value;
|
|
486
|
-
return JSON.stringify(credentialsOf(auth), sorted);
|
|
487
|
-
}
|
|
488
|
-
function failBuffered(socket, error) {
|
|
489
|
-
const buffered = socket.sendBuffer.splice(0);
|
|
490
|
-
const { acks } = socket;
|
|
491
|
-
for (const packet of buffered) {
|
|
492
|
-
const id = packet.id === void 0 ? void 0 : String(packet.id);
|
|
493
|
-
const ack = id === void 0 ? void 0 : acks[id];
|
|
494
|
-
if (id !== void 0 && ack !== void 0) {
|
|
495
|
-
Reflect.deleteProperty(acks, id);
|
|
496
|
-
if (ack.withError === true) {
|
|
497
|
-
ack.call(socket, error);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
function refusalOf(error) {
|
|
503
|
-
if (isProtocolMismatch(error.data)) {
|
|
504
|
-
return { code: error.data.code, expected: error.data.expected };
|
|
505
|
-
}
|
|
506
|
-
if (isAuthenticationRefused(error.data)) {
|
|
507
|
-
return { code: "UNAUTHENTICATED" };
|
|
508
|
-
}
|
|
509
|
-
return { code: "REFUSED", message: error.message };
|
|
510
|
-
}
|
|
511
|
-
var MAX_TIMEOUT_MS = 2147483647;
|
|
512
|
-
function isTimeLimit(value) {
|
|
513
|
-
return typeof value === "number" && value > 0 && value <= MAX_TIMEOUT_MS;
|
|
514
|
-
}
|
|
515
|
-
function checkOptions(options) {
|
|
516
|
-
if (typeof options.url !== "string" || options.url === "") {
|
|
517
|
-
throw new TypeError("createQuickdrawConnection: url must be the server's URL");
|
|
518
|
-
}
|
|
519
|
-
const { timeoutMs } = options;
|
|
520
|
-
if (timeoutMs !== void 0 && !isTimeLimit(timeoutMs)) {
|
|
521
|
-
throw new TypeError(
|
|
522
|
-
"createQuickdrawConnection: timeoutMs must be a number of milliseconds, above 0 and at most 2^31 - 1"
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
return timeoutMs;
|
|
526
|
-
}
|
|
527
|
-
function defaultTimeout(option, hello) {
|
|
528
|
-
if (option !== void 0) {
|
|
529
|
-
return option;
|
|
530
|
-
}
|
|
531
|
-
const limits = hello?.limits;
|
|
532
|
-
const serverMs = isRecord(limits) ? limits.callTimeoutMs : void 0;
|
|
533
|
-
const fromHello = typeof serverMs === "number" ? serverMs + HELLO_TIMEOUT_MARGIN_MS : void 0;
|
|
534
|
-
return isTimeLimit(fromHello) ? fromHello : DEFAULT_TIMEOUT_MS;
|
|
535
|
-
}
|
|
536
|
-
function createLifecycle(socket, store, backoff, session) {
|
|
537
|
-
let opened = false;
|
|
538
|
-
let switching = false;
|
|
539
|
-
let users = 0;
|
|
540
|
-
let closeTimer;
|
|
541
|
-
let rotateTimer;
|
|
542
|
-
function connect() {
|
|
543
|
-
socket.connect();
|
|
544
|
-
store.set({ status: "connecting", refusal: null });
|
|
545
|
-
}
|
|
546
|
-
function reconnect() {
|
|
547
|
-
if (!opened) {
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
switching = true;
|
|
551
|
-
socket.disconnect();
|
|
552
|
-
switching = false;
|
|
553
|
-
connect();
|
|
554
|
-
}
|
|
555
|
-
function open() {
|
|
556
|
-
clearTimeout(closeTimer);
|
|
557
|
-
closeTimer = void 0;
|
|
558
|
-
opened = true;
|
|
559
|
-
if (!socket.connected && !socket.active) {
|
|
560
|
-
connect();
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
function close() {
|
|
564
|
-
opened = false;
|
|
565
|
-
session.established = false;
|
|
566
|
-
session.topics.stop();
|
|
567
|
-
session.lane.stop();
|
|
568
|
-
clearTimeout(closeTimer);
|
|
569
|
-
closeTimer = void 0;
|
|
570
|
-
clearTimeout(rotateTimer);
|
|
571
|
-
rotateTimer = void 0;
|
|
572
|
-
backoff.clear();
|
|
573
|
-
failBuffered(
|
|
574
|
-
socket,
|
|
575
|
-
new QuickdrawError("CANCELLED", "The connection closed before the call was sent")
|
|
576
|
-
);
|
|
577
|
-
socket.disconnect();
|
|
578
|
-
store.set({ status: "idle", reconnecting: false });
|
|
579
|
-
}
|
|
580
|
-
function retain2() {
|
|
581
|
-
users += 1;
|
|
582
|
-
open();
|
|
583
|
-
let released = false;
|
|
584
|
-
return () => {
|
|
585
|
-
if (!released) {
|
|
586
|
-
released = true;
|
|
587
|
-
users -= 1;
|
|
588
|
-
if (users === 0) {
|
|
589
|
-
closeTimer = setTimeout(close, 0);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
function rotateWithin(withinMs) {
|
|
595
|
-
if (rotateTimer === void 0) {
|
|
596
|
-
rotateTimer = setTimeout(() => {
|
|
597
|
-
rotateTimer = void 0;
|
|
598
|
-
reconnect();
|
|
599
|
-
}, Math.random() * withinMs);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
function stoppedStatus() {
|
|
603
|
-
if (switching) {
|
|
604
|
-
return "connecting";
|
|
605
|
-
}
|
|
606
|
-
return opened ? "disconnected" : "idle";
|
|
607
|
-
}
|
|
608
|
-
return { open, close, retain: retain2, reconnect, rotateWithin, stoppedStatus };
|
|
609
|
-
}
|
|
610
|
-
function sayHello(hellos, entry, hello) {
|
|
611
|
-
if (hellos.has(entry) && entry.last !== hello) {
|
|
612
|
-
entry.last = hello;
|
|
613
|
-
entry.listener(hello);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
function addListener(set, listener) {
|
|
617
|
-
set.add(listener);
|
|
618
|
-
return () => {
|
|
619
|
-
set.delete(listener);
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
function listenForHellos(wiring, listener) {
|
|
623
|
-
const { hellos, store } = wiring;
|
|
624
|
-
const entry = { listener, last: void 0 };
|
|
625
|
-
hellos.add(entry);
|
|
626
|
-
const { status, hello } = store.get();
|
|
627
|
-
if (status === "connected" && hello !== null) {
|
|
628
|
-
queueMicrotask(() => {
|
|
629
|
-
notifyEach([entry], (current) => {
|
|
630
|
-
sayHello(hellos, current, hello);
|
|
631
|
-
});
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
return () => {
|
|
635
|
-
hellos.delete(entry);
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
function listenToSocket(wiring) {
|
|
639
|
-
const { socket, store, lifecycle, session } = wiring;
|
|
640
|
-
socket.on("connect", () => {
|
|
641
|
-
const again = session.established;
|
|
642
|
-
session.established = true;
|
|
643
|
-
store.set({ status: "connected", reconnecting: false, refusal: null });
|
|
644
|
-
session.topics.rejoin();
|
|
645
|
-
if (again) {
|
|
646
|
-
notifyEach(wiring.reconnected, (listener) => {
|
|
647
|
-
listener();
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
});
|
|
651
|
-
socket.on("disconnect", () => {
|
|
652
|
-
const status = socket.active ? "connecting" : lifecycle.stoppedStatus();
|
|
653
|
-
store.set({ status, reconnecting: status === "connecting" && session.established });
|
|
654
|
-
});
|
|
655
|
-
socket.on("connect_error", (error) => {
|
|
656
|
-
if (socket.active) {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
const refusal = refusalOf(error);
|
|
660
|
-
session.established = false;
|
|
661
|
-
store.set({ status: "refused", reconnecting: false, refusal });
|
|
662
|
-
if (refusal.code === "PROTOCOL_MISMATCH") {
|
|
663
|
-
wiring.onProtocolMismatch(refusal);
|
|
664
|
-
}
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
function listenToPushes(wiring) {
|
|
668
|
-
const { socket, store, lifecycle } = wiring;
|
|
669
|
-
socket.on(SERVER_EVENTS.hello, (frame) => {
|
|
670
|
-
const grants = isRecord(frame) && isRecord(frame.serviceAccess) ? frame.serviceAccess : null;
|
|
671
|
-
store.set({ hello: frame, serviceAccess: grants });
|
|
672
|
-
notifyEach(wiring.hellos, (entry) => {
|
|
673
|
-
sayHello(wiring.hellos, entry, frame);
|
|
674
|
-
});
|
|
675
|
-
});
|
|
676
|
-
socket.on(SERVER_EVENTS.access, (frame) => {
|
|
677
|
-
if (isRecord(frame) && isRecord(frame.serviceAccess)) {
|
|
678
|
-
store.set({ serviceAccess: frame.serviceAccess });
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
socket.on(SERVER_EVENTS.rotate, (frame) => {
|
|
682
|
-
const withinMs = isRecord(frame) ? frame.withinMs : void 0;
|
|
683
|
-
if (typeof withinMs === "number" && Number.isFinite(withinMs) && withinMs >= 0) {
|
|
684
|
-
lifecycle.rotateWithin(withinMs);
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
function createQuickdrawConnection(options) {
|
|
689
|
-
const timeoutOption = checkOptions(options);
|
|
690
|
-
const store = createStore();
|
|
691
|
-
const backoff = createBackoff((windows) => {
|
|
692
|
-
store.set({ backoff: windows });
|
|
693
|
-
});
|
|
694
|
-
let auth = options.auth;
|
|
695
|
-
let nextId = 0;
|
|
696
|
-
const socket = createSocket(options, () => credentialsOf(auth));
|
|
697
|
-
const timeoutMs = () => defaultTimeout(timeoutOption, store.get().hello);
|
|
698
|
-
const subscriptionLane = createSubscriptionLane({
|
|
699
|
-
socket,
|
|
700
|
-
timeoutMs,
|
|
701
|
-
hello: () => store.get().hello,
|
|
702
|
-
backoffRemaining: backoff.remaining
|
|
703
|
-
});
|
|
704
|
-
const topics = createTopics({
|
|
705
|
-
socket,
|
|
706
|
-
lane: subscriptionLane,
|
|
707
|
-
backoffRemaining: backoff.remaining,
|
|
708
|
-
reportRateLimited: backoff.report
|
|
709
|
-
});
|
|
710
|
-
const session = { established: false, topics, lane: subscriptionLane };
|
|
711
|
-
const lifecycle = createLifecycle(socket, store, backoff, session);
|
|
712
|
-
const reconnected = /* @__PURE__ */ new Set();
|
|
713
|
-
const wiring = {
|
|
714
|
-
socket,
|
|
715
|
-
store,
|
|
716
|
-
lifecycle,
|
|
717
|
-
session,
|
|
718
|
-
reconnected,
|
|
719
|
-
hellos: /* @__PURE__ */ new Set(),
|
|
720
|
-
onProtocolMismatch: options.onProtocolMismatch ?? reloadOncePerSession
|
|
721
|
-
};
|
|
722
|
-
listenToSocket(wiring);
|
|
723
|
-
listenToPushes(wiring);
|
|
724
|
-
return Object.freeze({
|
|
725
|
-
url: options.url,
|
|
726
|
-
get timeoutMs() {
|
|
727
|
-
return timeoutMs();
|
|
728
|
-
},
|
|
729
|
-
socket,
|
|
730
|
-
subscriptionLane,
|
|
731
|
-
getState: store.get,
|
|
732
|
-
subscribe: store.subscribe,
|
|
733
|
-
open: lifecycle.open,
|
|
734
|
-
close: lifecycle.close,
|
|
735
|
-
retain: lifecycle.retain,
|
|
736
|
-
setAuth(next2) {
|
|
737
|
-
if (authKey(next2) === authKey(auth)) {
|
|
738
|
-
return false;
|
|
739
|
-
}
|
|
740
|
-
auth = next2;
|
|
741
|
-
session.established = false;
|
|
742
|
-
failBuffered(
|
|
743
|
-
socket,
|
|
744
|
-
new QuickdrawError("CANCELLED", "The credentials changed before the call was sent")
|
|
745
|
-
);
|
|
746
|
-
store.set({ hello: null, serviceAccess: null });
|
|
747
|
-
lifecycle.reconnect();
|
|
748
|
-
return true;
|
|
749
|
-
},
|
|
750
|
-
nextCallId() {
|
|
751
|
-
const id = nextId;
|
|
752
|
-
nextId = nextId >= Number.MAX_SAFE_INTEGER ? 0 : nextId + 1;
|
|
753
|
-
return id;
|
|
754
|
-
},
|
|
755
|
-
reportRateLimited: backoff.report,
|
|
756
|
-
backoffRemaining: backoff.remaining,
|
|
757
|
-
onReconnect: (listener) => addListener(reconnected, listener),
|
|
758
|
-
onHello: (listener) => listenForHellos(wiring, listener),
|
|
759
|
-
watch: topics.watch,
|
|
760
|
-
waitForJoin: topics.waitForJoin
|
|
761
|
-
});
|
|
762
|
-
}
|
|
46
|
+
} from "./chunk-QSU47XEX.js";
|
|
763
47
|
|
|
764
48
|
// src/client/loopGuard.ts
|
|
765
49
|
var MUTATION_LIMIT = 5;
|
|
@@ -1237,10 +521,10 @@ function shownOf(held) {
|
|
|
1237
521
|
}));
|
|
1238
522
|
}
|
|
1239
523
|
function refusedOf(held) {
|
|
1240
|
-
const
|
|
524
|
+
const refused2 = (held ?? []).flatMap(
|
|
1241
525
|
(addition) => addition.refusal === void 0 ? [] : [{ item: addition.item, error: addition.refusal.error, addition }]
|
|
1242
526
|
);
|
|
1243
|
-
return
|
|
527
|
+
return refused2.length === 0 ? NO_REFUSALS : refused2;
|
|
1244
528
|
}
|
|
1245
529
|
function newAddition(service, collection, scope, item, keep2) {
|
|
1246
530
|
return {
|
|
@@ -1269,7 +553,7 @@ function unasked(additions) {
|
|
|
1269
553
|
}
|
|
1270
554
|
function weigh(held, evidence) {
|
|
1271
555
|
const ending = [];
|
|
1272
|
-
let
|
|
556
|
+
let refused2 = false;
|
|
1273
557
|
for (const addition of held) {
|
|
1274
558
|
const outcome = verdict(addition, evidence);
|
|
1275
559
|
if (outcome === "end") {
|
|
@@ -1277,10 +561,10 @@ function weigh(held, evidence) {
|
|
|
1277
561
|
} else if (outcome === "refuse" && addition.unknown !== void 0) {
|
|
1278
562
|
addition.refusal = addition.unknown.refusal;
|
|
1279
563
|
addition.unknown = void 0;
|
|
1280
|
-
|
|
564
|
+
refused2 = true;
|
|
1281
565
|
}
|
|
1282
566
|
}
|
|
1283
|
-
return { ending, refused:
|
|
567
|
+
return { ending, refused: refused2 };
|
|
1284
568
|
}
|
|
1285
569
|
function createAdditions(queryClient) {
|
|
1286
570
|
const byScope = /* @__PURE__ */ new Map();
|
|
@@ -1596,13 +880,13 @@ function settleScope(layers, ref, evidence) {
|
|
|
1596
880
|
if (layers.additions.size === 0) {
|
|
1597
881
|
return;
|
|
1598
882
|
}
|
|
1599
|
-
const { ending, refused:
|
|
883
|
+
const { ending, refused: refused2 } = layers.additions.settle(
|
|
1600
884
|
ref.service,
|
|
1601
885
|
ref.collection,
|
|
1602
886
|
ref.scope,
|
|
1603
887
|
evidence
|
|
1604
888
|
);
|
|
1605
|
-
discard(layers, [], ending,
|
|
889
|
+
discard(layers, [], ending, refused2 ? [ref.service] : []);
|
|
1606
890
|
}
|
|
1607
891
|
function resetLayers(layers) {
|
|
1608
892
|
const services = new Set(layers.views.keys());
|
|
@@ -1622,7 +906,7 @@ function resetLayers(layers) {
|
|
|
1622
906
|
layers.count = 0;
|
|
1623
907
|
changed(layers, services);
|
|
1624
908
|
}
|
|
1625
|
-
function
|
|
909
|
+
function createStore(queryClient) {
|
|
1626
910
|
const layers = {
|
|
1627
911
|
byRow: /* @__PURE__ */ new Map(),
|
|
1628
912
|
additions: createAdditions(queryClient),
|
|
@@ -1687,7 +971,7 @@ function createStore2(queryClient) {
|
|
|
1687
971
|
function storeOf(queryClient) {
|
|
1688
972
|
let store = stores.get(queryClient);
|
|
1689
973
|
if (store === void 0) {
|
|
1690
|
-
store =
|
|
974
|
+
store = createStore(queryClient);
|
|
1691
975
|
stores.set(queryClient, store);
|
|
1692
976
|
}
|
|
1693
977
|
return store;
|
|
@@ -1745,6 +1029,25 @@ function createSession(connection, queryClient) {
|
|
|
1745
1029
|
});
|
|
1746
1030
|
}
|
|
1747
1031
|
});
|
|
1032
|
+
const end = () => {
|
|
1033
|
+
queryClient.removeQueries({ queryKey: [KEY_ROOT] });
|
|
1034
|
+
resetOverlays(queryClient);
|
|
1035
|
+
loadedFor.set(queryClient, { userId: null });
|
|
1036
|
+
notifyEach(listeners, (listener) => {
|
|
1037
|
+
listener({ switched: true, first: false });
|
|
1038
|
+
});
|
|
1039
|
+
};
|
|
1040
|
+
const { socket } = connection;
|
|
1041
|
+
socket.on("disconnect", (reason) => {
|
|
1042
|
+
if (isServerDisconnect(reason)) {
|
|
1043
|
+
end();
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
socket.on("connect_error", (error) => {
|
|
1047
|
+
if (!socket.active && isRecord(error) && isSessionRefusal(error.data)) {
|
|
1048
|
+
end();
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1748
1051
|
return Object.freeze({
|
|
1749
1052
|
onHello(listener) {
|
|
1750
1053
|
listeners.add(listener);
|
|
@@ -2445,13 +1748,87 @@ function setState(feeds, feed, state) {
|
|
|
2445
1748
|
listener();
|
|
2446
1749
|
});
|
|
2447
1750
|
}
|
|
2448
|
-
function
|
|
1751
|
+
function answered(feeds, feed, reply) {
|
|
2449
1752
|
const seed = Array.isArray(reply.seed) ? reply.seed : [];
|
|
2450
1753
|
const items = latest([...seed, ...feed.pending ?? []], capOf(feed));
|
|
2451
1754
|
feed.pending = void 0;
|
|
2452
1755
|
setState(feeds, feed, Object.freeze({ items, isLoading: false, error: null }));
|
|
2453
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
|
+
}
|
|
2454
1827
|
function subscribe(feeds, feed) {
|
|
1828
|
+
if (feed.hosted) {
|
|
1829
|
+
subscribeHost(feeds, feed);
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
2455
1832
|
const { host } = feeds;
|
|
2456
1833
|
clearTimeout(feed.retry);
|
|
2457
1834
|
feed.retry = void 0;
|
|
@@ -2463,7 +1840,7 @@ function subscribe(feeds, feed) {
|
|
|
2463
1840
|
return;
|
|
2464
1841
|
}
|
|
2465
1842
|
if (outcome.kind === "ok") {
|
|
2466
|
-
|
|
1843
|
+
answered(feeds, feed, outcome.reply);
|
|
2467
1844
|
} else if (outcome.kind === "refused") {
|
|
2468
1845
|
feed.pending = void 0;
|
|
2469
1846
|
setState(feeds, feed, Object.freeze({ items: [], isLoading: false, error: outcome.error }));
|
|
@@ -2481,12 +1858,19 @@ function closeFeed(feeds, feed) {
|
|
|
2481
1858
|
const { socket } = feeds.host.connection;
|
|
2482
1859
|
clearTimeout(feed.retry);
|
|
2483
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
|
+
}
|
|
2484
1867
|
if (feed.sent && socket.connected) {
|
|
2485
1868
|
socket.emit(CLIENT_EVENTS.streamUnsub, feed.frame);
|
|
2486
1869
|
}
|
|
2487
1870
|
setState(feeds, feed, PENDING_STREAM);
|
|
2488
1871
|
}
|
|
2489
1872
|
function openFeed(target, scope, key) {
|
|
1873
|
+
const { host } = target;
|
|
2490
1874
|
const frame = scope === void 0 ? { s: target.service, stream: target.stream } : { s: target.service, stream: target.stream, scope };
|
|
2491
1875
|
return {
|
|
2492
1876
|
key,
|
|
@@ -2496,7 +1880,10 @@ function openFeed(target, scope, key) {
|
|
|
2496
1880
|
pending: void 0,
|
|
2497
1881
|
sent: false,
|
|
2498
1882
|
attempt: 0,
|
|
2499
|
-
retry: void 0
|
|
1883
|
+
retry: void 0,
|
|
1884
|
+
hosted: host !== void 0,
|
|
1885
|
+
room: host?.(scope ?? ""),
|
|
1886
|
+
release: void 0
|
|
2500
1887
|
};
|
|
2501
1888
|
}
|
|
2502
1889
|
function hold(feeds, target, scope, max) {
|
|
@@ -2556,6 +1943,7 @@ function listen(feeds, key, listener) {
|
|
|
2556
1943
|
};
|
|
2557
1944
|
}
|
|
2558
1945
|
function createStreamStore(host) {
|
|
1946
|
+
hostLinksOf(host.connection);
|
|
2559
1947
|
const feeds = {
|
|
2560
1948
|
host,
|
|
2561
1949
|
registry: createRegistry((feed) => {
|
|
@@ -2575,7 +1963,9 @@ function createStreamStore(host) {
|
|
|
2575
1963
|
},
|
|
2576
1964
|
resume() {
|
|
2577
1965
|
for (const feed of feeds.registry.held()) {
|
|
2578
|
-
|
|
1966
|
+
if (!feed.hosted || feed.release === void 0) {
|
|
1967
|
+
subscribe(feeds, feed);
|
|
1968
|
+
}
|
|
2579
1969
|
}
|
|
2580
1970
|
},
|
|
2581
1971
|
forget() {
|
|
@@ -2590,6 +1980,10 @@ function createStreamStore(host) {
|
|
|
2590
1980
|
feed.retry = void 0;
|
|
2591
1981
|
feed.attempt += 1;
|
|
2592
1982
|
feed.pending = void 0;
|
|
1983
|
+
if (feed.hosted) {
|
|
1984
|
+
feed.release?.();
|
|
1985
|
+
feed.release = void 0;
|
|
1986
|
+
}
|
|
2593
1987
|
}
|
|
2594
1988
|
}
|
|
2595
1989
|
});
|
|
@@ -2676,15 +2070,15 @@ function loadPage(p) {
|
|
|
2676
2070
|
const limit = p.pageLimit();
|
|
2677
2071
|
const frame = { ...scopeFrame(p), cursor, ...limit === void 0 ? {} : { limit } };
|
|
2678
2072
|
p.write({ loadingMore: true });
|
|
2679
|
-
let
|
|
2073
|
+
let answered3 = false;
|
|
2680
2074
|
const page = new Promise((resolve) => {
|
|
2681
2075
|
request2(p.host, CLIENT_EVENTS.collectionSub, frame, (outcome) => {
|
|
2682
|
-
|
|
2076
|
+
answered3 = true;
|
|
2683
2077
|
p.page = void 0;
|
|
2684
2078
|
resolve(p.disposed ? false : pageLoaded(p, sent, outcome));
|
|
2685
2079
|
});
|
|
2686
2080
|
});
|
|
2687
|
-
if (!
|
|
2081
|
+
if (!answered3) {
|
|
2688
2082
|
p.page = page;
|
|
2689
2083
|
}
|
|
2690
2084
|
return page;
|
|
@@ -2857,7 +2251,7 @@ function applyLoad(p, reply, kept, readAt) {
|
|
|
2857
2251
|
observeItems(p, reply.items, reply.rev, readAt);
|
|
2858
2252
|
applied = { state: applySnapshot(base, reply, p.shape), reset: false, missing: [] };
|
|
2859
2253
|
} else {
|
|
2860
|
-
|
|
2254
|
+
refused(p, malformed("qd:col:sub"));
|
|
2861
2255
|
return;
|
|
2862
2256
|
}
|
|
2863
2257
|
for (const frame of kept) {
|
|
@@ -2881,7 +2275,7 @@ function applyLoad(p, reply, kept, readAt) {
|
|
|
2881
2275
|
reloaded
|
|
2882
2276
|
);
|
|
2883
2277
|
}
|
|
2884
|
-
function
|
|
2278
|
+
function refused(p, error) {
|
|
2885
2279
|
p.joined = false;
|
|
2886
2280
|
const drops = ["FORBIDDEN", "NOT_FOUND", "UNAUTHENTICATED"].includes(error.code);
|
|
2887
2281
|
write(p, drops ? { state: null, error, loadingMore: false } : { error });
|
|
@@ -2897,7 +2291,7 @@ function loaded(p, { generation, kind, readAt }, outcome) {
|
|
|
2897
2291
|
if (outcome.kind === "ok") {
|
|
2898
2292
|
applyLoad(p, outcome.reply, kept, readAt);
|
|
2899
2293
|
} else if (outcome.kind === "refused") {
|
|
2900
|
-
|
|
2294
|
+
refused(p, outcome.error);
|
|
2901
2295
|
} else if (outcome.kind === "retry") {
|
|
2902
2296
|
p.retryTimer = setTimeout(() => {
|
|
2903
2297
|
p.retryTimer = void 0;
|
|
@@ -3446,8 +2840,8 @@ function entityResultOf(value) {
|
|
|
3446
2840
|
if (isRecord(value) && value.ok === false) {
|
|
3447
2841
|
return value;
|
|
3448
2842
|
}
|
|
3449
|
-
const
|
|
3450
|
-
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" } };
|
|
3451
2845
|
}
|
|
3452
2846
|
|
|
3453
2847
|
// src/client/live/entityStore.ts
|
|
@@ -3529,7 +2923,7 @@ function apply(state, batch, results) {
|
|
|
3529
2923
|
commit(state, row, before, applyEntityResult(before, result, batch.readAt));
|
|
3530
2924
|
});
|
|
3531
2925
|
}
|
|
3532
|
-
function
|
|
2926
|
+
function answered2(state, batch, outcome) {
|
|
3533
2927
|
if (batch.epoch !== state.epoch || outcome.kind === "offline") {
|
|
3534
2928
|
return;
|
|
3535
2929
|
}
|
|
@@ -3548,7 +2942,7 @@ function send(state, service, ids) {
|
|
|
3548
2942
|
const batch = { service, ids, epoch: state.epoch, readAt: state.host.overlays.now() };
|
|
3549
2943
|
const frame = revs.some((rev) => rev !== null) ? { s: service, ids, revs } : { s: service, ids };
|
|
3550
2944
|
request2(state.host, CLIENT_EVENTS.sub, frame, (outcome) => {
|
|
3551
|
-
|
|
2945
|
+
answered2(state, batch, outcome);
|
|
3552
2946
|
});
|
|
3553
2947
|
}
|
|
3554
2948
|
function flush(state) {
|
|
@@ -4221,13 +3615,6 @@ function showCollection(state, options) {
|
|
|
4221
3615
|
}
|
|
4222
3616
|
|
|
4223
3617
|
export {
|
|
4224
|
-
DEFAULT_BACKOFF_MS,
|
|
4225
|
-
retryAfterOf,
|
|
4226
|
-
notifyEach,
|
|
4227
|
-
DEFAULT_SUBSCRIPTION_LANE,
|
|
4228
|
-
reloadOncePerSession,
|
|
4229
|
-
isTimeLimit,
|
|
4230
|
-
createQuickdrawConnection,
|
|
4231
3618
|
createMutationTrace,
|
|
4232
3619
|
loopGuardOf,
|
|
4233
3620
|
DEFAULT_INVALIDATION_WINDOW_MS,
|
|
@@ -4284,4 +3671,4 @@ export {
|
|
|
4284
3671
|
viewPredicate,
|
|
4285
3672
|
showCollection
|
|
4286
3673
|
};
|
|
4287
|
-
//# sourceMappingURL=chunk-
|
|
3674
|
+
//# sourceMappingURL=chunk-64I6MYBC.js.map
|