@wvdsh/sdk-js 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4189 +1,43 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __commonJS = (cb, mod) => function __require() {
8
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
- };
10
- var __export = (target, all) => {
11
- for (var name in all)
12
- __defProp(target, name, { get: all[name], enumerable: true });
13
- };
14
- var __copyProps = (to, from, except, desc) => {
15
- if (from && typeof from === "object" || typeof from === "function") {
16
- for (let key of __getOwnPropNames(from))
17
- if (!__hasOwnProp.call(to, key) && key !== except)
18
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
- // If the importer is in node compatibility mode or this is not an ESM
24
- // file that has been converted to a CommonJS file using a Babel-
25
- // compatible transform (i.e. "__esModule" has not been set), then set
26
- // "default" to the CommonJS "module.exports" for node compatibility.
27
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
- mod
29
- ));
30
-
31
- // node_modules/lodash.debounce/index.js
32
- var require_lodash = __commonJS({
33
- "node_modules/lodash.debounce/index.js"(exports, module) {
34
- "use strict";
35
- var FUNC_ERROR_TEXT = "Expected a function";
36
- var NAN = 0 / 0;
37
- var symbolTag = "[object Symbol]";
38
- var reTrim = /^\s+|\s+$/g;
39
- var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
40
- var reIsBinary = /^0b[01]+$/i;
41
- var reIsOctal = /^0o[0-7]+$/i;
42
- var freeParseInt = parseInt;
43
- var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
44
- var freeSelf = typeof self == "object" && self && self.Object === Object && self;
45
- var root = freeGlobal || freeSelf || Function("return this")();
46
- var objectProto = Object.prototype;
47
- var objectToString = objectProto.toString;
48
- var nativeMax = Math.max;
49
- var nativeMin = Math.min;
50
- var now = function() {
51
- return root.Date.now();
52
- };
53
- function debounce3(func, wait, options) {
54
- var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
55
- if (typeof func != "function") {
56
- throw new TypeError(FUNC_ERROR_TEXT);
57
- }
58
- wait = toNumber(wait) || 0;
59
- if (isObject(options)) {
60
- leading = !!options.leading;
61
- maxing = "maxWait" in options;
62
- maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
63
- trailing = "trailing" in options ? !!options.trailing : trailing;
64
- }
65
- function invokeFunc(time) {
66
- var args = lastArgs, thisArg = lastThis;
67
- lastArgs = lastThis = void 0;
68
- lastInvokeTime = time;
69
- result = func.apply(thisArg, args);
70
- return result;
71
- }
72
- function leadingEdge(time) {
73
- lastInvokeTime = time;
74
- timerId = setTimeout(timerExpired, wait);
75
- return leading ? invokeFunc(time) : result;
76
- }
77
- function remainingWait(time) {
78
- var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, result2 = wait - timeSinceLastCall;
79
- return maxing ? nativeMin(result2, maxWait - timeSinceLastInvoke) : result2;
80
- }
81
- function shouldInvoke(time) {
82
- var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
83
- return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
84
- }
85
- function timerExpired() {
86
- var time = now();
87
- if (shouldInvoke(time)) {
88
- return trailingEdge(time);
89
- }
90
- timerId = setTimeout(timerExpired, remainingWait(time));
91
- }
92
- function trailingEdge(time) {
93
- timerId = void 0;
94
- if (trailing && lastArgs) {
95
- return invokeFunc(time);
96
- }
97
- lastArgs = lastThis = void 0;
98
- return result;
99
- }
100
- function cancel() {
101
- if (timerId !== void 0) {
102
- clearTimeout(timerId);
103
- }
104
- lastInvokeTime = 0;
105
- lastArgs = lastCallTime = lastThis = timerId = void 0;
106
- }
107
- function flush() {
108
- return timerId === void 0 ? result : trailingEdge(now());
109
- }
110
- function debounced() {
111
- var time = now(), isInvoking = shouldInvoke(time);
112
- lastArgs = arguments;
113
- lastThis = this;
114
- lastCallTime = time;
115
- if (isInvoking) {
116
- if (timerId === void 0) {
117
- return leadingEdge(lastCallTime);
118
- }
119
- if (maxing) {
120
- timerId = setTimeout(timerExpired, wait);
121
- return invokeFunc(lastCallTime);
122
- }
123
- }
124
- if (timerId === void 0) {
125
- timerId = setTimeout(timerExpired, wait);
126
- }
127
- return result;
128
- }
129
- debounced.cancel = cancel;
130
- debounced.flush = flush;
131
- return debounced;
132
- }
133
- function isObject(value) {
134
- var type = typeof value;
135
- return !!value && (type == "object" || type == "function");
136
- }
137
- function isObjectLike(value) {
138
- return !!value && typeof value == "object";
139
- }
140
- function isSymbol(value) {
141
- return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
142
- }
143
- function toNumber(value) {
144
- if (typeof value == "number") {
145
- return value;
146
- }
147
- if (isSymbol(value)) {
148
- return NAN;
149
- }
150
- if (isObject(value)) {
151
- var other = typeof value.valueOf == "function" ? value.valueOf() : value;
152
- value = isObject(other) ? other + "" : other;
153
- }
154
- if (typeof value != "string") {
155
- return value === 0 ? value : +value;
156
- }
157
- value = value.replace(reTrim, "");
158
- var isBinary = reIsBinary.test(value);
159
- return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
160
- }
161
- module.exports = debounce3;
162
- }
163
- });
164
-
165
- // node_modules/convex/dist/esm/index.js
166
- var version = "1.30.0";
167
-
168
- // node_modules/convex/dist/esm/values/base64.js
169
- var base64_exports = {};
170
- __export(base64_exports, {
171
- byteLength: () => byteLength,
172
- fromByteArray: () => fromByteArray,
173
- fromByteArrayUrlSafeNoPadding: () => fromByteArrayUrlSafeNoPadding,
174
- toByteArray: () => toByteArray
175
- });
176
- var lookup = [];
177
- var revLookup = [];
178
- var Arr = Uint8Array;
179
- var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
180
- for (i = 0, len = code.length; i < len; ++i) {
181
- lookup[i] = code[i];
182
- revLookup[code.charCodeAt(i)] = i;
183
- }
184
- var i;
185
- var len;
186
- revLookup["-".charCodeAt(0)] = 62;
187
- revLookup["_".charCodeAt(0)] = 63;
188
- function getLens(b64) {
189
- var len = b64.length;
190
- if (len % 4 > 0) {
191
- throw new Error("Invalid string. Length must be a multiple of 4");
192
- }
193
- var validLen = b64.indexOf("=");
194
- if (validLen === -1) validLen = len;
195
- var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4;
196
- return [validLen, placeHoldersLen];
197
- }
198
- function byteLength(b64) {
199
- var lens = getLens(b64);
200
- var validLen = lens[0];
201
- var placeHoldersLen = lens[1];
202
- return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
203
- }
204
- function _byteLength(_b64, validLen, placeHoldersLen) {
205
- return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen;
206
- }
207
- function toByteArray(b64) {
208
- var tmp;
209
- var lens = getLens(b64);
210
- var validLen = lens[0];
211
- var placeHoldersLen = lens[1];
212
- var arr2 = new Arr(_byteLength(b64, validLen, placeHoldersLen));
213
- var curByte = 0;
214
- var len = placeHoldersLen > 0 ? validLen - 4 : validLen;
215
- var i;
216
- for (i = 0; i < len; i += 4) {
217
- tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)];
218
- arr2[curByte++] = tmp >> 16 & 255;
219
- arr2[curByte++] = tmp >> 8 & 255;
220
- arr2[curByte++] = tmp & 255;
221
- }
222
- if (placeHoldersLen === 2) {
223
- tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4;
224
- arr2[curByte++] = tmp & 255;
225
- }
226
- if (placeHoldersLen === 1) {
227
- tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2;
228
- arr2[curByte++] = tmp >> 8 & 255;
229
- arr2[curByte++] = tmp & 255;
230
- }
231
- return arr2;
232
- }
233
- function tripletToBase64(num) {
234
- return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63];
235
- }
236
- function encodeChunk(uint8, start, end) {
237
- var tmp;
238
- var output = [];
239
- for (var i = start; i < end; i += 3) {
240
- tmp = (uint8[i] << 16 & 16711680) + (uint8[i + 1] << 8 & 65280) + (uint8[i + 2] & 255);
241
- output.push(tripletToBase64(tmp));
242
- }
243
- return output.join("");
244
- }
245
- function fromByteArray(uint8) {
246
- var tmp;
247
- var len = uint8.length;
248
- var extraBytes = len % 3;
249
- var parts = [];
250
- var maxChunkLength = 16383;
251
- for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
252
- parts.push(
253
- encodeChunk(
254
- uint8,
255
- i,
256
- i + maxChunkLength > len2 ? len2 : i + maxChunkLength
257
- )
258
- );
259
- }
260
- if (extraBytes === 1) {
261
- tmp = uint8[len - 1];
262
- parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "==");
263
- } else if (extraBytes === 2) {
264
- tmp = (uint8[len - 2] << 8) + uint8[len - 1];
265
- parts.push(
266
- lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "="
267
- );
268
- }
269
- return parts.join("");
270
- }
271
- function fromByteArrayUrlSafeNoPadding(uint8) {
272
- return fromByteArray(uint8).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
273
- }
274
-
275
- // node_modules/convex/dist/esm/common/index.js
276
- function parseArgs(args) {
277
- if (args === void 0) {
278
- return {};
279
- }
280
- if (!isSimpleObject(args)) {
281
- throw new Error(
282
- `The arguments to a Convex function must be an object. Received: ${args}`
283
- );
284
- }
285
- return args;
286
- }
287
- function validateDeploymentUrl(deploymentUrl) {
288
- if (typeof deploymentUrl === "undefined") {
289
- throw new Error(
290
- `Client created with undefined deployment address. If you used an environment variable, check that it's set.`
291
- );
292
- }
293
- if (typeof deploymentUrl !== "string") {
294
- throw new Error(
295
- `Invalid deployment address: found ${deploymentUrl}".`
296
- );
297
- }
298
- if (!(deploymentUrl.startsWith("http:") || deploymentUrl.startsWith("https:"))) {
299
- throw new Error(
300
- `Invalid deployment address: Must start with "https://" or "http://". Found "${deploymentUrl}".`
301
- );
302
- }
303
- try {
304
- new URL(deploymentUrl);
305
- } catch {
306
- throw new Error(
307
- `Invalid deployment address: "${deploymentUrl}" is not a valid URL. If you believe this URL is correct, use the \`skipConvexDeploymentUrlCheck\` option to bypass this.`
308
- );
309
- }
310
- if (deploymentUrl.endsWith(".convex.site")) {
311
- throw new Error(
312
- `Invalid deployment address: "${deploymentUrl}" ends with .convex.site, which is used for HTTP Actions. Convex deployment URLs typically end with .convex.cloud? If you believe this URL is correct, use the \`skipConvexDeploymentUrlCheck\` option to bypass this.`
313
- );
314
- }
315
- }
316
- function isSimpleObject(value) {
317
- const isObject = typeof value === "object";
318
- const prototype = Object.getPrototypeOf(value);
319
- const isSimple = prototype === null || prototype === Object.prototype || // Objects generated from other contexts (e.g. across Node.js `vm` modules) will not satisfy the previous
320
- // conditions but are still simple objects.
321
- prototype?.constructor?.name === "Object";
322
- return isObject && isSimple;
323
- }
324
-
325
- // node_modules/convex/dist/esm/values/value.js
326
- var LITTLE_ENDIAN = true;
327
- var MIN_INT64 = BigInt("-9223372036854775808");
328
- var MAX_INT64 = BigInt("9223372036854775807");
329
- var ZERO = BigInt("0");
330
- var EIGHT = BigInt("8");
331
- var TWOFIFTYSIX = BigInt("256");
332
- function isSpecial(n) {
333
- return Number.isNaN(n) || !Number.isFinite(n) || Object.is(n, -0);
334
- }
335
- function slowBigIntToBase64(value) {
336
- if (value < ZERO) {
337
- value -= MIN_INT64 + MIN_INT64;
338
- }
339
- let hex = value.toString(16);
340
- if (hex.length % 2 === 1) hex = "0" + hex;
341
- const bytes = new Uint8Array(new ArrayBuffer(8));
342
- let i = 0;
343
- for (const hexByte of hex.match(/.{2}/g).reverse()) {
344
- bytes.set([parseInt(hexByte, 16)], i++);
345
- value >>= EIGHT;
346
- }
347
- return fromByteArray(bytes);
348
- }
349
- function slowBase64ToBigInt(encoded) {
350
- const integerBytes = toByteArray(encoded);
351
- if (integerBytes.byteLength !== 8) {
352
- throw new Error(
353
- `Received ${integerBytes.byteLength} bytes, expected 8 for $integer`
354
- );
355
- }
356
- let value = ZERO;
357
- let power = ZERO;
358
- for (const byte of integerBytes) {
359
- value += BigInt(byte) * TWOFIFTYSIX ** power;
360
- power++;
361
- }
362
- if (value > MAX_INT64) {
363
- value += MIN_INT64 + MIN_INT64;
364
- }
365
- return value;
366
- }
367
- function modernBigIntToBase64(value) {
368
- if (value < MIN_INT64 || MAX_INT64 < value) {
369
- throw new Error(
370
- `BigInt ${value} does not fit into a 64-bit signed integer.`
371
- );
372
- }
373
- const buffer = new ArrayBuffer(8);
374
- new DataView(buffer).setBigInt64(0, value, true);
375
- return fromByteArray(new Uint8Array(buffer));
376
- }
377
- function modernBase64ToBigInt(encoded) {
378
- const integerBytes = toByteArray(encoded);
379
- if (integerBytes.byteLength !== 8) {
380
- throw new Error(
381
- `Received ${integerBytes.byteLength} bytes, expected 8 for $integer`
382
- );
383
- }
384
- const intBytesView = new DataView(integerBytes.buffer);
385
- return intBytesView.getBigInt64(0, true);
386
- }
387
- var bigIntToBase64 = DataView.prototype.setBigInt64 ? modernBigIntToBase64 : slowBigIntToBase64;
388
- var base64ToBigInt = DataView.prototype.getBigInt64 ? modernBase64ToBigInt : slowBase64ToBigInt;
389
- var MAX_IDENTIFIER_LEN = 1024;
390
- function validateObjectField(k) {
391
- if (k.length > MAX_IDENTIFIER_LEN) {
392
- throw new Error(
393
- `Field name ${k} exceeds maximum field name length ${MAX_IDENTIFIER_LEN}.`
394
- );
395
- }
396
- if (k.startsWith("$")) {
397
- throw new Error(`Field name ${k} starts with a '$', which is reserved.`);
398
- }
399
- for (let i = 0; i < k.length; i += 1) {
400
- const charCode = k.charCodeAt(i);
401
- if (charCode < 32 || charCode >= 127) {
402
- throw new Error(
403
- `Field name ${k} has invalid character '${k[i]}': Field names can only contain non-control ASCII characters`
404
- );
405
- }
406
- }
407
- }
408
- function jsonToConvex(value) {
409
- if (value === null) {
410
- return value;
411
- }
412
- if (typeof value === "boolean") {
413
- return value;
414
- }
415
- if (typeof value === "number") {
416
- return value;
417
- }
418
- if (typeof value === "string") {
419
- return value;
420
- }
421
- if (Array.isArray(value)) {
422
- return value.map((value2) => jsonToConvex(value2));
423
- }
424
- if (typeof value !== "object") {
425
- throw new Error(`Unexpected type of ${value}`);
426
- }
427
- const entries = Object.entries(value);
428
- if (entries.length === 1) {
429
- const key = entries[0][0];
430
- if (key === "$bytes") {
431
- if (typeof value.$bytes !== "string") {
432
- throw new Error(`Malformed $bytes field on ${value}`);
433
- }
434
- return toByteArray(value.$bytes).buffer;
435
- }
436
- if (key === "$integer") {
437
- if (typeof value.$integer !== "string") {
438
- throw new Error(`Malformed $integer field on ${value}`);
439
- }
440
- return base64ToBigInt(value.$integer);
441
- }
442
- if (key === "$float") {
443
- if (typeof value.$float !== "string") {
444
- throw new Error(`Malformed $float field on ${value}`);
445
- }
446
- const floatBytes = toByteArray(value.$float);
447
- if (floatBytes.byteLength !== 8) {
448
- throw new Error(
449
- `Received ${floatBytes.byteLength} bytes, expected 8 for $float`
450
- );
451
- }
452
- const floatBytesView = new DataView(floatBytes.buffer);
453
- const float = floatBytesView.getFloat64(0, LITTLE_ENDIAN);
454
- if (!isSpecial(float)) {
455
- throw new Error(`Float ${float} should be encoded as a number`);
456
- }
457
- return float;
458
- }
459
- if (key === "$set") {
460
- throw new Error(
461
- `Received a Set which is no longer supported as a Convex type.`
462
- );
463
- }
464
- if (key === "$map") {
465
- throw new Error(
466
- `Received a Map which is no longer supported as a Convex type.`
467
- );
468
- }
469
- }
470
- const out = {};
471
- for (const [k, v2] of Object.entries(value)) {
472
- validateObjectField(k);
473
- out[k] = jsonToConvex(v2);
474
- }
475
- return out;
476
- }
477
- var MAX_VALUE_FOR_ERROR_LEN = 16384;
478
- function stringifyValueForError(value) {
479
- const str = JSON.stringify(value, (_key, value2) => {
480
- if (value2 === void 0) {
481
- return "undefined";
482
- }
483
- if (typeof value2 === "bigint") {
484
- return `${value2.toString()}n`;
485
- }
486
- return value2;
487
- });
488
- if (str.length > MAX_VALUE_FOR_ERROR_LEN) {
489
- const rest = "[...truncated]";
490
- let truncateAt = MAX_VALUE_FOR_ERROR_LEN - rest.length;
491
- const codePoint = str.codePointAt(truncateAt - 1);
492
- if (codePoint !== void 0 && codePoint > 65535) {
493
- truncateAt -= 1;
494
- }
495
- return str.substring(0, truncateAt) + rest;
496
- }
497
- return str;
498
- }
499
- function convexToJsonInternal(value, originalValue, context, includeTopLevelUndefined) {
500
- if (value === void 0) {
501
- const contextText = context && ` (present at path ${context} in original object ${stringifyValueForError(
502
- originalValue
503
- )})`;
504
- throw new Error(
505
- `undefined is not a valid Convex value${contextText}. To learn about Convex's supported types, see https://docs.convex.dev/using/types.`
506
- );
507
- }
508
- if (value === null) {
509
- return value;
510
- }
511
- if (typeof value === "bigint") {
512
- if (value < MIN_INT64 || MAX_INT64 < value) {
513
- throw new Error(
514
- `BigInt ${value} does not fit into a 64-bit signed integer.`
515
- );
516
- }
517
- return { $integer: bigIntToBase64(value) };
518
- }
519
- if (typeof value === "number") {
520
- if (isSpecial(value)) {
521
- const buffer = new ArrayBuffer(8);
522
- new DataView(buffer).setFloat64(0, value, LITTLE_ENDIAN);
523
- return { $float: fromByteArray(new Uint8Array(buffer)) };
524
- } else {
525
- return value;
526
- }
527
- }
528
- if (typeof value === "boolean") {
529
- return value;
530
- }
531
- if (typeof value === "string") {
532
- return value;
533
- }
534
- if (value instanceof ArrayBuffer) {
535
- return { $bytes: fromByteArray(new Uint8Array(value)) };
536
- }
537
- if (Array.isArray(value)) {
538
- return value.map(
539
- (value2, i) => convexToJsonInternal(value2, originalValue, context + `[${i}]`, false)
540
- );
541
- }
542
- if (value instanceof Set) {
543
- throw new Error(
544
- errorMessageForUnsupportedType(context, "Set", [...value], originalValue)
545
- );
546
- }
547
- if (value instanceof Map) {
548
- throw new Error(
549
- errorMessageForUnsupportedType(context, "Map", [...value], originalValue)
550
- );
551
- }
552
- if (!isSimpleObject(value)) {
553
- const theType = value?.constructor?.name;
554
- const typeName = theType ? `${theType} ` : "";
555
- throw new Error(
556
- errorMessageForUnsupportedType(context, typeName, value, originalValue)
557
- );
558
- }
559
- const out = {};
560
- const entries = Object.entries(value);
561
- entries.sort(([k1, _v1], [k2, _v2]) => k1 === k2 ? 0 : k1 < k2 ? -1 : 1);
562
- for (const [k, v2] of entries) {
563
- if (v2 !== void 0) {
564
- validateObjectField(k);
565
- out[k] = convexToJsonInternal(v2, originalValue, context + `.${k}`, false);
566
- } else if (includeTopLevelUndefined) {
567
- validateObjectField(k);
568
- out[k] = convexOrUndefinedToJsonInternal(
569
- v2,
570
- originalValue,
571
- context + `.${k}`
572
- );
573
- }
574
- }
575
- return out;
576
- }
577
- function errorMessageForUnsupportedType(context, typeName, value, originalValue) {
578
- if (context) {
579
- return `${typeName}${stringifyValueForError(
580
- value
581
- )} is not a supported Convex type (present at path ${context} in original object ${stringifyValueForError(
582
- originalValue
583
- )}). To learn about Convex's supported types, see https://docs.convex.dev/using/types.`;
584
- } else {
585
- return `${typeName}${stringifyValueForError(
586
- value
587
- )} is not a supported Convex type.`;
588
- }
589
- }
590
- function convexOrUndefinedToJsonInternal(value, originalValue, context) {
591
- if (value === void 0) {
592
- return { $undefined: null };
593
- } else {
594
- if (originalValue === void 0) {
595
- throw new Error(
596
- `Programming error. Current value is ${stringifyValueForError(
597
- value
598
- )} but original value is undefined`
599
- );
600
- }
601
- return convexToJsonInternal(value, originalValue, context, false);
602
- }
603
- }
604
- function convexToJson(value) {
605
- return convexToJsonInternal(value, value, "", false);
606
- }
607
-
608
- // node_modules/convex/dist/esm/values/errors.js
609
- var __defProp2 = Object.defineProperty;
610
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
611
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
612
- var _a;
613
- var _b;
614
- var IDENTIFYING_FIELD = /* @__PURE__ */ Symbol.for("ConvexError");
615
- var ConvexError = class extends (_b = Error, _a = IDENTIFYING_FIELD, _b) {
616
- constructor(data) {
617
- super(typeof data === "string" ? data : stringifyValueForError(data));
618
- __publicField(this, "name", "ConvexError");
619
- __publicField(this, "data");
620
- __publicField(this, _a, true);
621
- this.data = data;
622
- }
623
- };
624
-
625
- // node_modules/convex/dist/esm/values/compare_utf8.js
626
- var arr = () => Array.from({ length: 4 }, () => 0);
627
- var aBytes = arr();
628
- var bBytes = arr();
629
-
630
- // node_modules/convex/dist/esm/browser/logging.js
631
- var __defProp3 = Object.defineProperty;
632
- var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
633
- var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
634
- var INFO_COLOR = "color:rgb(0, 145, 255)";
635
- function prefix_for_source(source) {
636
- switch (source) {
637
- case "query":
638
- return "Q";
639
- case "mutation":
640
- return "M";
641
- case "action":
642
- return "A";
643
- case "any":
644
- return "?";
645
- }
646
- }
647
- var DefaultLogger = class {
648
- constructor(options) {
649
- __publicField2(this, "_onLogLineFuncs");
650
- __publicField2(this, "_verbose");
651
- this._onLogLineFuncs = {};
652
- this._verbose = options.verbose;
653
- }
654
- addLogLineListener(func) {
655
- let id = Math.random().toString(36).substring(2, 15);
656
- for (let i = 0; i < 10; i++) {
657
- if (this._onLogLineFuncs[id] === void 0) {
658
- break;
659
- }
660
- id = Math.random().toString(36).substring(2, 15);
661
- }
662
- this._onLogLineFuncs[id] = func;
663
- return () => {
664
- delete this._onLogLineFuncs[id];
665
- };
666
- }
667
- logVerbose(...args) {
668
- if (this._verbose) {
669
- for (const func of Object.values(this._onLogLineFuncs)) {
670
- func("debug", `${(/* @__PURE__ */ new Date()).toISOString()}`, ...args);
671
- }
672
- }
673
- }
674
- log(...args) {
675
- for (const func of Object.values(this._onLogLineFuncs)) {
676
- func("info", ...args);
677
- }
678
- }
679
- warn(...args) {
680
- for (const func of Object.values(this._onLogLineFuncs)) {
681
- func("warn", ...args);
682
- }
683
- }
684
- error(...args) {
685
- for (const func of Object.values(this._onLogLineFuncs)) {
686
- func("error", ...args);
687
- }
688
- }
689
- };
690
- function instantiateDefaultLogger(options) {
691
- const logger = new DefaultLogger(options);
692
- logger.addLogLineListener((level, ...args) => {
693
- switch (level) {
694
- case "debug":
695
- console.debug(...args);
696
- break;
697
- case "info":
698
- console.log(...args);
699
- break;
700
- case "warn":
701
- console.warn(...args);
702
- break;
703
- case "error":
704
- console.error(...args);
705
- break;
706
- default: {
707
- level;
708
- console.log(...args);
709
- }
710
- }
711
- });
712
- return logger;
713
- }
714
- function instantiateNoopLogger(options) {
715
- return new DefaultLogger(options);
716
- }
717
- function logForFunction(logger, type, source, udfPath, message) {
718
- const prefix = prefix_for_source(source);
719
- if (typeof message === "object") {
720
- message = `ConvexError ${JSON.stringify(message.errorData, null, 2)}`;
721
- }
722
- if (type === "info") {
723
- const match = message.match(/^\[.*?\] /);
724
- if (match === null) {
725
- logger.error(
726
- `[CONVEX ${prefix}(${udfPath})] Could not parse console.log`
727
- );
728
- return;
729
- }
730
- const level = message.slice(1, match[0].length - 2);
731
- const args = message.slice(match[0].length);
732
- logger.log(`%c[CONVEX ${prefix}(${udfPath})] [${level}]`, INFO_COLOR, args);
733
- } else {
734
- logger.error(`[CONVEX ${prefix}(${udfPath})] ${message}`);
735
- }
736
- }
737
- function logFatalError(logger, message) {
738
- const errorMessage = `[CONVEX FATAL ERROR] ${message}`;
739
- logger.error(errorMessage);
740
- return new Error(errorMessage);
741
- }
742
- function createHybridErrorStacktrace(source, udfPath, result) {
743
- const prefix = prefix_for_source(source);
744
- return `[CONVEX ${prefix}(${udfPath})] ${result.errorMessage}
745
- Called by client`;
746
- }
747
- function forwardData(result, error) {
748
- error.data = result.errorData;
749
- return error;
750
- }
751
-
752
- // node_modules/convex/dist/esm/browser/sync/udf_path_utils.js
753
- function canonicalizeUdfPath(udfPath) {
754
- const pieces = udfPath.split(":");
755
- let moduleName;
756
- let functionName2;
757
- if (pieces.length === 1) {
758
- moduleName = pieces[0];
759
- functionName2 = "default";
760
- } else {
761
- moduleName = pieces.slice(0, pieces.length - 1).join(":");
762
- functionName2 = pieces[pieces.length - 1];
763
- }
764
- if (moduleName.endsWith(".js")) {
765
- moduleName = moduleName.slice(0, -3);
766
- }
767
- return `${moduleName}:${functionName2}`;
768
- }
769
- function serializePathAndArgs(udfPath, args) {
770
- return JSON.stringify({
771
- udfPath: canonicalizeUdfPath(udfPath),
772
- args: convexToJson(args)
773
- });
774
- }
775
- function serializePaginatedPathAndArgs(udfPath, args, options) {
776
- const { initialNumItems, id } = options;
777
- const result = JSON.stringify({
778
- type: "paginated",
779
- udfPath: canonicalizeUdfPath(udfPath),
780
- args: convexToJson(args),
781
- options: convexToJson({ initialNumItems, id })
782
- });
783
- return result;
784
- }
785
- function serializedQueryTokenIsPaginated(token) {
786
- return JSON.parse(token).type === "paginated";
787
- }
788
-
789
- // node_modules/convex/dist/esm/browser/sync/local_state.js
790
- var __defProp4 = Object.defineProperty;
791
- var __defNormalProp3 = (obj, key, value) => key in obj ? __defProp4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
792
- var __publicField3 = (obj, key, value) => __defNormalProp3(obj, typeof key !== "symbol" ? key + "" : key, value);
793
- var LocalSyncState = class {
794
- constructor() {
795
- __publicField3(this, "nextQueryId");
796
- __publicField3(this, "querySetVersion");
797
- __publicField3(this, "querySet");
798
- __publicField3(this, "queryIdToToken");
799
- __publicField3(this, "identityVersion");
800
- __publicField3(this, "auth");
801
- __publicField3(this, "outstandingQueriesOlderThanRestart");
802
- __publicField3(this, "outstandingAuthOlderThanRestart");
803
- __publicField3(this, "paused");
804
- __publicField3(this, "pendingQuerySetModifications");
805
- this.nextQueryId = 0;
806
- this.querySetVersion = 0;
807
- this.identityVersion = 0;
808
- this.querySet = /* @__PURE__ */ new Map();
809
- this.queryIdToToken = /* @__PURE__ */ new Map();
810
- this.outstandingQueriesOlderThanRestart = /* @__PURE__ */ new Set();
811
- this.outstandingAuthOlderThanRestart = false;
812
- this.paused = false;
813
- this.pendingQuerySetModifications = /* @__PURE__ */ new Map();
814
- }
815
- hasSyncedPastLastReconnect() {
816
- return this.outstandingQueriesOlderThanRestart.size === 0 && !this.outstandingAuthOlderThanRestart;
817
- }
818
- markAuthCompletion() {
819
- this.outstandingAuthOlderThanRestart = false;
820
- }
821
- subscribe(udfPath, args, journal, componentPath) {
822
- const canonicalizedUdfPath = canonicalizeUdfPath(udfPath);
823
- const queryToken = serializePathAndArgs(canonicalizedUdfPath, args);
824
- const existingEntry = this.querySet.get(queryToken);
825
- if (existingEntry !== void 0) {
826
- existingEntry.numSubscribers += 1;
827
- return {
828
- queryToken,
829
- modification: null,
830
- unsubscribe: () => this.removeSubscriber(queryToken)
831
- };
832
- } else {
833
- const queryId = this.nextQueryId++;
834
- const query = {
835
- id: queryId,
836
- canonicalizedUdfPath,
837
- args,
838
- numSubscribers: 1,
839
- journal,
840
- componentPath
841
- };
842
- this.querySet.set(queryToken, query);
843
- this.queryIdToToken.set(queryId, queryToken);
844
- const baseVersion = this.querySetVersion;
845
- const newVersion = this.querySetVersion + 1;
846
- const add = {
847
- type: "Add",
848
- queryId,
849
- udfPath: canonicalizedUdfPath,
850
- args: [convexToJson(args)],
851
- journal,
852
- componentPath
853
- };
854
- if (this.paused) {
855
- this.pendingQuerySetModifications.set(queryId, add);
856
- } else {
857
- this.querySetVersion = newVersion;
858
- }
859
- const modification = {
860
- type: "ModifyQuerySet",
861
- baseVersion,
862
- newVersion,
863
- modifications: [add]
864
- };
865
- return {
866
- queryToken,
867
- modification,
868
- unsubscribe: () => this.removeSubscriber(queryToken)
869
- };
870
- }
871
- }
872
- transition(transition) {
873
- for (const modification of transition.modifications) {
874
- switch (modification.type) {
875
- case "QueryUpdated":
876
- case "QueryFailed": {
877
- this.outstandingQueriesOlderThanRestart.delete(modification.queryId);
878
- const journal = modification.journal;
879
- if (journal !== void 0) {
880
- const queryToken = this.queryIdToToken.get(modification.queryId);
881
- if (queryToken !== void 0) {
882
- this.querySet.get(queryToken).journal = journal;
883
- }
884
- }
885
- break;
886
- }
887
- case "QueryRemoved": {
888
- this.outstandingQueriesOlderThanRestart.delete(modification.queryId);
889
- break;
890
- }
891
- default: {
892
- modification;
893
- throw new Error(`Invalid modification ${modification.type}`);
894
- }
895
- }
896
- }
897
- }
898
- queryId(udfPath, args) {
899
- const canonicalizedUdfPath = canonicalizeUdfPath(udfPath);
900
- const queryToken = serializePathAndArgs(canonicalizedUdfPath, args);
901
- const existingEntry = this.querySet.get(queryToken);
902
- if (existingEntry !== void 0) {
903
- return existingEntry.id;
904
- }
905
- return null;
906
- }
907
- isCurrentOrNewerAuthVersion(version2) {
908
- return version2 >= this.identityVersion;
909
- }
910
- getAuth() {
911
- return this.auth;
912
- }
913
- setAuth(value) {
914
- this.auth = {
915
- tokenType: "User",
916
- value
917
- };
918
- const baseVersion = this.identityVersion;
919
- if (!this.paused) {
920
- this.identityVersion = baseVersion + 1;
921
- }
922
- return {
923
- type: "Authenticate",
924
- baseVersion,
925
- ...this.auth
926
- };
927
- }
928
- setAdminAuth(value, actingAs) {
929
- const auth = {
930
- tokenType: "Admin",
931
- value,
932
- impersonating: actingAs
933
- };
934
- this.auth = auth;
935
- const baseVersion = this.identityVersion;
936
- if (!this.paused) {
937
- this.identityVersion = baseVersion + 1;
938
- }
939
- return {
940
- type: "Authenticate",
941
- baseVersion,
942
- ...auth
943
- };
944
- }
945
- clearAuth() {
946
- this.auth = void 0;
947
- this.markAuthCompletion();
948
- const baseVersion = this.identityVersion;
949
- if (!this.paused) {
950
- this.identityVersion = baseVersion + 1;
951
- }
952
- return {
953
- type: "Authenticate",
954
- tokenType: "None",
955
- baseVersion
956
- };
957
- }
958
- hasAuth() {
959
- return !!this.auth;
960
- }
961
- isNewAuth(value) {
962
- return this.auth?.value !== value;
963
- }
964
- queryPath(queryId) {
965
- const pathAndArgs = this.queryIdToToken.get(queryId);
966
- if (pathAndArgs) {
967
- return this.querySet.get(pathAndArgs).canonicalizedUdfPath;
968
- }
969
- return null;
970
- }
971
- queryArgs(queryId) {
972
- const pathAndArgs = this.queryIdToToken.get(queryId);
973
- if (pathAndArgs) {
974
- return this.querySet.get(pathAndArgs).args;
975
- }
976
- return null;
977
- }
978
- queryToken(queryId) {
979
- return this.queryIdToToken.get(queryId) ?? null;
980
- }
981
- queryJournal(queryToken) {
982
- return this.querySet.get(queryToken)?.journal;
983
- }
984
- restart(oldRemoteQueryResults) {
985
- this.unpause();
986
- this.outstandingQueriesOlderThanRestart.clear();
987
- const modifications = [];
988
- for (const localQuery of this.querySet.values()) {
989
- const add = {
990
- type: "Add",
991
- queryId: localQuery.id,
992
- udfPath: localQuery.canonicalizedUdfPath,
993
- args: [convexToJson(localQuery.args)],
994
- journal: localQuery.journal,
995
- componentPath: localQuery.componentPath
996
- };
997
- modifications.push(add);
998
- if (!oldRemoteQueryResults.has(localQuery.id)) {
999
- this.outstandingQueriesOlderThanRestart.add(localQuery.id);
1000
- }
1001
- }
1002
- this.querySetVersion = 1;
1003
- const querySet = {
1004
- type: "ModifyQuerySet",
1005
- baseVersion: 0,
1006
- newVersion: 1,
1007
- modifications
1008
- };
1009
- if (!this.auth) {
1010
- this.identityVersion = 0;
1011
- return [querySet, void 0];
1012
- }
1013
- this.outstandingAuthOlderThanRestart = true;
1014
- const authenticate = {
1015
- type: "Authenticate",
1016
- baseVersion: 0,
1017
- ...this.auth
1018
- };
1019
- this.identityVersion = 1;
1020
- return [querySet, authenticate];
1021
- }
1022
- pause() {
1023
- this.paused = true;
1024
- }
1025
- resume() {
1026
- const querySet = this.pendingQuerySetModifications.size > 0 ? {
1027
- type: "ModifyQuerySet",
1028
- baseVersion: this.querySetVersion,
1029
- newVersion: ++this.querySetVersion,
1030
- modifications: Array.from(
1031
- this.pendingQuerySetModifications.values()
1032
- )
1033
- } : void 0;
1034
- const authenticate = this.auth !== void 0 ? {
1035
- type: "Authenticate",
1036
- baseVersion: this.identityVersion++,
1037
- ...this.auth
1038
- } : void 0;
1039
- this.unpause();
1040
- return [querySet, authenticate];
1041
- }
1042
- unpause() {
1043
- this.paused = false;
1044
- this.pendingQuerySetModifications.clear();
1045
- }
1046
- removeSubscriber(queryToken) {
1047
- const localQuery = this.querySet.get(queryToken);
1048
- if (localQuery.numSubscribers > 1) {
1049
- localQuery.numSubscribers -= 1;
1050
- return null;
1051
- } else {
1052
- this.querySet.delete(queryToken);
1053
- this.queryIdToToken.delete(localQuery.id);
1054
- this.outstandingQueriesOlderThanRestart.delete(localQuery.id);
1055
- const baseVersion = this.querySetVersion;
1056
- const newVersion = this.querySetVersion + 1;
1057
- const remove = {
1058
- type: "Remove",
1059
- queryId: localQuery.id
1060
- };
1061
- if (this.paused) {
1062
- if (this.pendingQuerySetModifications.has(localQuery.id)) {
1063
- this.pendingQuerySetModifications.delete(localQuery.id);
1064
- } else {
1065
- this.pendingQuerySetModifications.set(localQuery.id, remove);
1066
- }
1067
- } else {
1068
- this.querySetVersion = newVersion;
1069
- }
1070
- return {
1071
- type: "ModifyQuerySet",
1072
- baseVersion,
1073
- newVersion,
1074
- modifications: [remove]
1075
- };
1076
- }
1077
- }
1078
- };
1079
-
1080
- // node_modules/convex/dist/esm/browser/sync/request_manager.js
1081
- var __defProp5 = Object.defineProperty;
1082
- var __defNormalProp4 = (obj, key, value) => key in obj ? __defProp5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1083
- var __publicField4 = (obj, key, value) => __defNormalProp4(obj, typeof key !== "symbol" ? key + "" : key, value);
1084
- var RequestManager = class {
1085
- constructor(logger, markConnectionStateDirty) {
1086
- this.logger = logger;
1087
- this.markConnectionStateDirty = markConnectionStateDirty;
1088
- __publicField4(this, "inflightRequests");
1089
- __publicField4(this, "requestsOlderThanRestart");
1090
- __publicField4(this, "inflightMutationsCount", 0);
1091
- __publicField4(this, "inflightActionsCount", 0);
1092
- this.inflightRequests = /* @__PURE__ */ new Map();
1093
- this.requestsOlderThanRestart = /* @__PURE__ */ new Set();
1094
- }
1095
- request(message, sent) {
1096
- const result = new Promise((resolve) => {
1097
- const status = sent ? "Requested" : "NotSent";
1098
- this.inflightRequests.set(message.requestId, {
1099
- message,
1100
- status: { status, requestedAt: /* @__PURE__ */ new Date(), onResult: resolve }
1101
- });
1102
- if (message.type === "Mutation") {
1103
- this.inflightMutationsCount++;
1104
- } else if (message.type === "Action") {
1105
- this.inflightActionsCount++;
1106
- }
1107
- });
1108
- this.markConnectionStateDirty();
1109
- return result;
1110
- }
1111
- /**
1112
- * Update the state after receiving a response.
1113
- *
1114
- * @returns A RequestId if the request is complete and its optimistic update
1115
- * can be dropped, null otherwise.
1116
- */
1117
- onResponse(response) {
1118
- const requestInfo = this.inflightRequests.get(response.requestId);
1119
- if (requestInfo === void 0) {
1120
- return null;
1121
- }
1122
- if (requestInfo.status.status === "Completed") {
1123
- return null;
1124
- }
1125
- const udfType = requestInfo.message.type === "Mutation" ? "mutation" : "action";
1126
- const udfPath = requestInfo.message.udfPath;
1127
- for (const line of response.logLines) {
1128
- logForFunction(this.logger, "info", udfType, udfPath, line);
1129
- }
1130
- const status = requestInfo.status;
1131
- let result;
1132
- let onResolve;
1133
- if (response.success) {
1134
- result = {
1135
- success: true,
1136
- logLines: response.logLines,
1137
- value: jsonToConvex(response.result)
1138
- };
1139
- onResolve = () => status.onResult(result);
1140
- } else {
1141
- const errorMessage = response.result;
1142
- const { errorData } = response;
1143
- logForFunction(this.logger, "error", udfType, udfPath, errorMessage);
1144
- result = {
1145
- success: false,
1146
- errorMessage,
1147
- errorData: errorData !== void 0 ? jsonToConvex(errorData) : void 0,
1148
- logLines: response.logLines
1149
- };
1150
- onResolve = () => status.onResult(result);
1151
- }
1152
- if (response.type === "ActionResponse" || !response.success) {
1153
- onResolve();
1154
- this.inflightRequests.delete(response.requestId);
1155
- this.requestsOlderThanRestart.delete(response.requestId);
1156
- if (requestInfo.message.type === "Action") {
1157
- this.inflightActionsCount--;
1158
- } else if (requestInfo.message.type === "Mutation") {
1159
- this.inflightMutationsCount--;
1160
- }
1161
- this.markConnectionStateDirty();
1162
- return { requestId: response.requestId, result };
1163
- }
1164
- requestInfo.status = {
1165
- status: "Completed",
1166
- result,
1167
- ts: response.ts,
1168
- onResolve
1169
- };
1170
- return null;
1171
- }
1172
- // Remove and returns completed requests.
1173
- removeCompleted(ts) {
1174
- const completeRequests = /* @__PURE__ */ new Map();
1175
- for (const [requestId, requestInfo] of this.inflightRequests.entries()) {
1176
- const status = requestInfo.status;
1177
- if (status.status === "Completed" && status.ts.lessThanOrEqual(ts)) {
1178
- status.onResolve();
1179
- completeRequests.set(requestId, status.result);
1180
- if (requestInfo.message.type === "Mutation") {
1181
- this.inflightMutationsCount--;
1182
- } else if (requestInfo.message.type === "Action") {
1183
- this.inflightActionsCount--;
1184
- }
1185
- this.inflightRequests.delete(requestId);
1186
- this.requestsOlderThanRestart.delete(requestId);
1187
- }
1188
- }
1189
- if (completeRequests.size > 0) {
1190
- this.markConnectionStateDirty();
1191
- }
1192
- return completeRequests;
1193
- }
1194
- restart() {
1195
- this.requestsOlderThanRestart = new Set(this.inflightRequests.keys());
1196
- const allMessages = [];
1197
- for (const [requestId, value] of this.inflightRequests) {
1198
- if (value.status.status === "NotSent") {
1199
- value.status.status = "Requested";
1200
- allMessages.push(value.message);
1201
- continue;
1202
- }
1203
- if (value.message.type === "Mutation") {
1204
- allMessages.push(value.message);
1205
- } else if (value.message.type === "Action") {
1206
- this.inflightRequests.delete(requestId);
1207
- this.requestsOlderThanRestart.delete(requestId);
1208
- this.inflightActionsCount--;
1209
- if (value.status.status === "Completed") {
1210
- throw new Error("Action should never be in 'Completed' state");
1211
- }
1212
- value.status.onResult({
1213
- success: false,
1214
- errorMessage: "Connection lost while action was in flight",
1215
- logLines: []
1216
- });
1217
- }
1218
- }
1219
- this.markConnectionStateDirty();
1220
- return allMessages;
1221
- }
1222
- resume() {
1223
- const allMessages = [];
1224
- for (const [, value] of this.inflightRequests) {
1225
- if (value.status.status === "NotSent") {
1226
- value.status.status = "Requested";
1227
- allMessages.push(value.message);
1228
- continue;
1229
- }
1230
- }
1231
- return allMessages;
1232
- }
1233
- /**
1234
- * @returns true if there are any requests that have been requested but have
1235
- * not be completed yet.
1236
- */
1237
- hasIncompleteRequests() {
1238
- for (const requestInfo of this.inflightRequests.values()) {
1239
- if (requestInfo.status.status === "Requested") {
1240
- return true;
1241
- }
1242
- }
1243
- return false;
1244
- }
1245
- /**
1246
- * @returns true if there are any inflight requests, including ones that have
1247
- * completed on the server, but have not been applied.
1248
- */
1249
- hasInflightRequests() {
1250
- return this.inflightRequests.size > 0;
1251
- }
1252
- /**
1253
- * @returns true if there are any inflight requests, that have been hanging around
1254
- * since prior to the most recent restart.
1255
- */
1256
- hasSyncedPastLastReconnect() {
1257
- return this.requestsOlderThanRestart.size === 0;
1258
- }
1259
- timeOfOldestInflightRequest() {
1260
- if (this.inflightRequests.size === 0) {
1261
- return null;
1262
- }
1263
- let oldestInflightRequest = Date.now();
1264
- for (const request of this.inflightRequests.values()) {
1265
- if (request.status.status !== "Completed") {
1266
- if (request.status.requestedAt.getTime() < oldestInflightRequest) {
1267
- oldestInflightRequest = request.status.requestedAt.getTime();
1268
- }
1269
- }
1270
- }
1271
- return new Date(oldestInflightRequest);
1272
- }
1273
- /**
1274
- * @returns The number of mutations currently in flight.
1275
- */
1276
- inflightMutations() {
1277
- return this.inflightMutationsCount;
1278
- }
1279
- /**
1280
- * @returns The number of actions currently in flight.
1281
- */
1282
- inflightActions() {
1283
- return this.inflightActionsCount;
1284
- }
1285
- };
1286
-
1287
- // node_modules/convex/dist/esm/server/functionName.js
1288
- var functionName = /* @__PURE__ */ Symbol.for("functionName");
1289
-
1290
- // node_modules/convex/dist/esm/server/components/paths.js
1291
- var toReferencePath = /* @__PURE__ */ Symbol.for("toReferencePath");
1292
- function extractReferencePath(reference) {
1293
- return reference[toReferencePath] ?? null;
1294
- }
1295
- function isFunctionHandle(s) {
1296
- return s.startsWith("function://");
1297
- }
1298
- function getFunctionAddress(functionReference) {
1299
- let functionAddress;
1300
- if (typeof functionReference === "string") {
1301
- if (isFunctionHandle(functionReference)) {
1302
- functionAddress = { functionHandle: functionReference };
1303
- } else {
1304
- functionAddress = { name: functionReference };
1305
- }
1306
- } else if (functionReference[functionName]) {
1307
- functionAddress = { name: functionReference[functionName] };
1308
- } else {
1309
- const referencePath = extractReferencePath(functionReference);
1310
- if (!referencePath) {
1311
- throw new Error(`${functionReference} is not a functionReference`);
1312
- }
1313
- functionAddress = { reference: referencePath };
1314
- }
1315
- return functionAddress;
1316
- }
1317
-
1318
- // node_modules/convex/dist/esm/server/api.js
1319
- function getFunctionName(functionReference) {
1320
- const address = getFunctionAddress(functionReference);
1321
- if (address.name === void 0) {
1322
- if (address.functionHandle !== void 0) {
1323
- throw new Error(
1324
- `Expected function reference like "api.file.func" or "internal.file.func", but received function handle ${address.functionHandle}`
1325
- );
1326
- } else if (address.reference !== void 0) {
1327
- throw new Error(
1328
- `Expected function reference in the current component like "api.file.func" or "internal.file.func", but received reference ${address.reference}`
1329
- );
1330
- }
1331
- throw new Error(
1332
- `Expected function reference like "api.file.func" or "internal.file.func", but received ${JSON.stringify(address)}`
1333
- );
1334
- }
1335
- if (typeof functionReference === "string") return functionReference;
1336
- const name = functionReference[functionName];
1337
- if (!name) {
1338
- throw new Error(`${functionReference} is not a functionReference`);
1339
- }
1340
- return name;
1341
- }
1342
- function createApi(pathParts = []) {
1343
- const handler = {
1344
- get(_, prop) {
1345
- if (typeof prop === "string") {
1346
- const newParts = [...pathParts, prop];
1347
- return createApi(newParts);
1348
- } else if (prop === functionName) {
1349
- if (pathParts.length < 2) {
1350
- const found = ["api", ...pathParts].join(".");
1351
- throw new Error(
1352
- `API path is expected to be of the form \`api.moduleName.functionName\`. Found: \`${found}\``
1353
- );
1354
- }
1355
- const path = pathParts.slice(0, -1).join("/");
1356
- const exportName = pathParts[pathParts.length - 1];
1357
- if (exportName === "default") {
1358
- return path;
1359
- } else {
1360
- return path + ":" + exportName;
1361
- }
1362
- } else if (prop === Symbol.toStringTag) {
1363
- return "FunctionReference";
1364
- } else {
1365
- return void 0;
1366
- }
1367
- }
1368
- };
1369
- return new Proxy({}, handler);
1370
- }
1371
- var anyApi = createApi();
1372
-
1373
- // node_modules/convex/dist/esm/browser/sync/optimistic_updates_impl.js
1374
- var __defProp6 = Object.defineProperty;
1375
- var __defNormalProp5 = (obj, key, value) => key in obj ? __defProp6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1376
- var __publicField5 = (obj, key, value) => __defNormalProp5(obj, typeof key !== "symbol" ? key + "" : key, value);
1377
- var OptimisticLocalStoreImpl = class _OptimisticLocalStoreImpl {
1378
- constructor(queryResults) {
1379
- __publicField5(this, "queryResults");
1380
- __publicField5(this, "modifiedQueries");
1381
- this.queryResults = queryResults;
1382
- this.modifiedQueries = [];
1383
- }
1384
- getQuery(query, ...args) {
1385
- const queryArgs = parseArgs(args[0]);
1386
- const name = getFunctionName(query);
1387
- const queryResult = this.queryResults.get(
1388
- serializePathAndArgs(name, queryArgs)
1389
- );
1390
- if (queryResult === void 0) {
1391
- return void 0;
1392
- }
1393
- return _OptimisticLocalStoreImpl.queryValue(queryResult.result);
1394
- }
1395
- getAllQueries(query) {
1396
- const queriesWithName = [];
1397
- const name = getFunctionName(query);
1398
- for (const queryResult of this.queryResults.values()) {
1399
- if (queryResult.udfPath === canonicalizeUdfPath(name)) {
1400
- queriesWithName.push({
1401
- args: queryResult.args,
1402
- value: _OptimisticLocalStoreImpl.queryValue(queryResult.result)
1403
- });
1404
- }
1405
- }
1406
- return queriesWithName;
1407
- }
1408
- setQuery(queryReference, args, value) {
1409
- const queryArgs = parseArgs(args);
1410
- const name = getFunctionName(queryReference);
1411
- const queryToken = serializePathAndArgs(name, queryArgs);
1412
- let result;
1413
- if (value === void 0) {
1414
- result = void 0;
1415
- } else {
1416
- result = {
1417
- success: true,
1418
- value,
1419
- // It's an optimistic update, so there are no function logs to show.
1420
- logLines: []
1421
- };
1422
- }
1423
- const query = {
1424
- udfPath: name,
1425
- args: queryArgs,
1426
- result
1427
- };
1428
- this.queryResults.set(queryToken, query);
1429
- this.modifiedQueries.push(queryToken);
1430
- }
1431
- static queryValue(result) {
1432
- if (result === void 0) {
1433
- return void 0;
1434
- } else if (result.success) {
1435
- return result.value;
1436
- } else {
1437
- return void 0;
1438
- }
1439
- }
1440
- };
1441
- var OptimisticQueryResults = class {
1442
- constructor() {
1443
- __publicField5(this, "queryResults");
1444
- __publicField5(this, "optimisticUpdates");
1445
- this.queryResults = /* @__PURE__ */ new Map();
1446
- this.optimisticUpdates = [];
1447
- }
1448
- /**
1449
- * Apply all optimistic updates on top of server query results
1450
- */
1451
- ingestQueryResultsFromServer(serverQueryResults, optimisticUpdatesToDrop) {
1452
- this.optimisticUpdates = this.optimisticUpdates.filter((updateAndId) => {
1453
- return !optimisticUpdatesToDrop.has(updateAndId.mutationId);
1454
- });
1455
- const oldQueryResults = this.queryResults;
1456
- this.queryResults = new Map(serverQueryResults);
1457
- const localStore = new OptimisticLocalStoreImpl(this.queryResults);
1458
- for (const updateAndId of this.optimisticUpdates) {
1459
- updateAndId.update(localStore);
1460
- }
1461
- const changedQueries = [];
1462
- for (const [queryToken, query] of this.queryResults) {
1463
- const oldQuery = oldQueryResults.get(queryToken);
1464
- if (oldQuery === void 0 || oldQuery.result !== query.result) {
1465
- changedQueries.push(queryToken);
1466
- }
1467
- }
1468
- return changedQueries;
1469
- }
1470
- applyOptimisticUpdate(update, mutationId) {
1471
- this.optimisticUpdates.push({
1472
- update,
1473
- mutationId
1474
- });
1475
- const localStore = new OptimisticLocalStoreImpl(this.queryResults);
1476
- update(localStore);
1477
- return localStore.modifiedQueries;
1478
- }
1479
- /**
1480
- * "Raw" with respect to errors vs values, but query results still have
1481
- * optimistic updates applied.
1482
- *
1483
- * @internal
1484
- */
1485
- rawQueryResult(queryToken) {
1486
- const query = this.queryResults.get(queryToken);
1487
- if (query === void 0) {
1488
- return void 0;
1489
- }
1490
- return query.result;
1491
- }
1492
- queryResult(queryToken) {
1493
- const query = this.queryResults.get(queryToken);
1494
- if (query === void 0) {
1495
- return void 0;
1496
- }
1497
- const result = query.result;
1498
- if (result === void 0) {
1499
- return void 0;
1500
- } else if (result.success) {
1501
- return result.value;
1502
- } else {
1503
- if (result.errorData !== void 0) {
1504
- throw forwardData(
1505
- result,
1506
- new ConvexError(
1507
- createHybridErrorStacktrace("query", query.udfPath, result)
1508
- )
1509
- );
1510
- }
1511
- throw new Error(
1512
- createHybridErrorStacktrace("query", query.udfPath, result)
1513
- );
1514
- }
1515
- }
1516
- hasQueryResult(queryToken) {
1517
- return this.queryResults.get(queryToken) !== void 0;
1518
- }
1519
- /**
1520
- * @internal
1521
- */
1522
- queryLogs(queryToken) {
1523
- const query = this.queryResults.get(queryToken);
1524
- return query?.result?.logLines;
1525
- }
1526
- };
1527
-
1528
- // node_modules/convex/dist/esm/vendor/long.js
1529
- var __defProp7 = Object.defineProperty;
1530
- var __defNormalProp6 = (obj, key, value) => key in obj ? __defProp7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1531
- var __publicField6 = (obj, key, value) => __defNormalProp6(obj, typeof key !== "symbol" ? key + "" : key, value);
1532
- var Long = class _Long {
1533
- constructor(low, high) {
1534
- __publicField6(this, "low");
1535
- __publicField6(this, "high");
1536
- __publicField6(this, "__isUnsignedLong__");
1537
- this.low = low | 0;
1538
- this.high = high | 0;
1539
- this.__isUnsignedLong__ = true;
1540
- }
1541
- static isLong(obj) {
1542
- return (obj && obj.__isUnsignedLong__) === true;
1543
- }
1544
- // prettier-ignore
1545
- static fromBytesLE(bytes) {
1546
- return new _Long(
1547
- bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24,
1548
- bytes[4] | bytes[5] << 8 | bytes[6] << 16 | bytes[7] << 24
1549
- );
1550
- }
1551
- // prettier-ignore
1552
- toBytesLE() {
1553
- const hi = this.high;
1554
- const lo = this.low;
1555
- return [
1556
- lo & 255,
1557
- lo >>> 8 & 255,
1558
- lo >>> 16 & 255,
1559
- lo >>> 24,
1560
- hi & 255,
1561
- hi >>> 8 & 255,
1562
- hi >>> 16 & 255,
1563
- hi >>> 24
1564
- ];
1565
- }
1566
- static fromNumber(value) {
1567
- if (isNaN(value)) return UZERO;
1568
- if (value < 0) return UZERO;
1569
- if (value >= TWO_PWR_64_DBL) return MAX_UNSIGNED_VALUE;
1570
- return new _Long(value % TWO_PWR_32_DBL | 0, value / TWO_PWR_32_DBL | 0);
1571
- }
1572
- toString() {
1573
- return (BigInt(this.high) * BigInt(TWO_PWR_32_DBL) + BigInt(this.low)).toString();
1574
- }
1575
- equals(other) {
1576
- if (!_Long.isLong(other)) other = _Long.fromValue(other);
1577
- if (this.high >>> 31 === 1 && other.high >>> 31 === 1) return false;
1578
- return this.high === other.high && this.low === other.low;
1579
- }
1580
- notEquals(other) {
1581
- return !this.equals(other);
1582
- }
1583
- comp(other) {
1584
- if (!_Long.isLong(other)) other = _Long.fromValue(other);
1585
- if (this.equals(other)) return 0;
1586
- return other.high >>> 0 > this.high >>> 0 || other.high === this.high && other.low >>> 0 > this.low >>> 0 ? -1 : 1;
1587
- }
1588
- lessThanOrEqual(other) {
1589
- return this.comp(
1590
- /* validates */
1591
- other
1592
- ) <= 0;
1593
- }
1594
- static fromValue(val) {
1595
- if (typeof val === "number") return _Long.fromNumber(val);
1596
- return new _Long(val.low, val.high);
1597
- }
1598
- };
1599
- var UZERO = new Long(0, 0);
1600
- var TWO_PWR_16_DBL = 1 << 16;
1601
- var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
1602
- var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
1603
- var MAX_UNSIGNED_VALUE = new Long(4294967295 | 0, 4294967295 | 0);
1604
-
1605
- // node_modules/convex/dist/esm/browser/sync/remote_query_set.js
1606
- var __defProp8 = Object.defineProperty;
1607
- var __defNormalProp7 = (obj, key, value) => key in obj ? __defProp8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1608
- var __publicField7 = (obj, key, value) => __defNormalProp7(obj, typeof key !== "symbol" ? key + "" : key, value);
1609
- var RemoteQuerySet = class {
1610
- constructor(queryPath, logger) {
1611
- __publicField7(this, "version");
1612
- __publicField7(this, "remoteQuerySet");
1613
- __publicField7(this, "queryPath");
1614
- __publicField7(this, "logger");
1615
- this.version = { querySet: 0, ts: Long.fromNumber(0), identity: 0 };
1616
- this.remoteQuerySet = /* @__PURE__ */ new Map();
1617
- this.queryPath = queryPath;
1618
- this.logger = logger;
1619
- }
1620
- transition(transition) {
1621
- const start = transition.startVersion;
1622
- if (this.version.querySet !== start.querySet || this.version.ts.notEquals(start.ts) || this.version.identity !== start.identity) {
1623
- throw new Error(
1624
- `Invalid start version: ${start.ts.toString()}:${start.querySet}:${start.identity}, transitioning from ${this.version.ts.toString()}:${this.version.querySet}:${this.version.identity}`
1625
- );
1626
- }
1627
- for (const modification of transition.modifications) {
1628
- switch (modification.type) {
1629
- case "QueryUpdated": {
1630
- const queryPath = this.queryPath(modification.queryId);
1631
- if (queryPath) {
1632
- for (const line of modification.logLines) {
1633
- logForFunction(this.logger, "info", "query", queryPath, line);
1634
- }
1635
- }
1636
- const value = jsonToConvex(modification.value ?? null);
1637
- this.remoteQuerySet.set(modification.queryId, {
1638
- success: true,
1639
- value,
1640
- logLines: modification.logLines
1641
- });
1642
- break;
1643
- }
1644
- case "QueryFailed": {
1645
- const queryPath = this.queryPath(modification.queryId);
1646
- if (queryPath) {
1647
- for (const line of modification.logLines) {
1648
- logForFunction(this.logger, "info", "query", queryPath, line);
1649
- }
1650
- }
1651
- const { errorData } = modification;
1652
- this.remoteQuerySet.set(modification.queryId, {
1653
- success: false,
1654
- errorMessage: modification.errorMessage,
1655
- errorData: errorData !== void 0 ? jsonToConvex(errorData) : void 0,
1656
- logLines: modification.logLines
1657
- });
1658
- break;
1659
- }
1660
- case "QueryRemoved": {
1661
- this.remoteQuerySet.delete(modification.queryId);
1662
- break;
1663
- }
1664
- default: {
1665
- modification;
1666
- throw new Error(`Invalid modification ${modification.type}`);
1667
- }
1668
- }
1669
- }
1670
- this.version = transition.endVersion;
1671
- }
1672
- remoteQueryResults() {
1673
- return this.remoteQuerySet;
1674
- }
1675
- timestamp() {
1676
- return this.version.ts;
1677
- }
1678
- };
1679
-
1680
- // node_modules/convex/dist/esm/browser/sync/protocol.js
1681
- function u64ToLong(encoded) {
1682
- const integerBytes = base64_exports.toByteArray(encoded);
1683
- return Long.fromBytesLE(Array.from(integerBytes));
1684
- }
1685
- function longToU64(raw) {
1686
- const integerBytes = new Uint8Array(raw.toBytesLE());
1687
- return base64_exports.fromByteArray(integerBytes);
1688
- }
1689
- function parseServerMessage(encoded) {
1690
- switch (encoded.type) {
1691
- case "FatalError":
1692
- case "AuthError":
1693
- case "ActionResponse":
1694
- case "TransitionChunk":
1695
- case "Ping": {
1696
- return { ...encoded };
1697
- }
1698
- case "MutationResponse": {
1699
- if (encoded.success) {
1700
- return { ...encoded, ts: u64ToLong(encoded.ts) };
1701
- } else {
1702
- return { ...encoded };
1703
- }
1704
- }
1705
- case "Transition": {
1706
- return {
1707
- ...encoded,
1708
- startVersion: {
1709
- ...encoded.startVersion,
1710
- ts: u64ToLong(encoded.startVersion.ts)
1711
- },
1712
- endVersion: {
1713
- ...encoded.endVersion,
1714
- ts: u64ToLong(encoded.endVersion.ts)
1715
- }
1716
- };
1717
- }
1718
- default: {
1719
- encoded;
1720
- }
1721
- }
1722
- return void 0;
1723
- }
1724
- function encodeClientMessage(message) {
1725
- switch (message.type) {
1726
- case "Authenticate":
1727
- case "ModifyQuerySet":
1728
- case "Mutation":
1729
- case "Action":
1730
- case "Event": {
1731
- return { ...message };
1732
- }
1733
- case "Connect": {
1734
- if (message.maxObservedTimestamp !== void 0) {
1735
- return {
1736
- ...message,
1737
- maxObservedTimestamp: longToU64(message.maxObservedTimestamp)
1738
- };
1739
- } else {
1740
- return { ...message, maxObservedTimestamp: void 0 };
1741
- }
1742
- }
1743
- default: {
1744
- message;
1745
- }
1746
- }
1747
- return void 0;
1748
- }
1749
-
1750
- // node_modules/convex/dist/esm/browser/sync/web_socket_manager.js
1751
- var __defProp9 = Object.defineProperty;
1752
- var __defNormalProp8 = (obj, key, value) => key in obj ? __defProp9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1753
- var __publicField8 = (obj, key, value) => __defNormalProp8(obj, typeof key !== "symbol" ? key + "" : key, value);
1754
- var CLOSE_NORMAL = 1e3;
1755
- var CLOSE_GOING_AWAY = 1001;
1756
- var CLOSE_NO_STATUS = 1005;
1757
- var CLOSE_NOT_FOUND = 4040;
1758
- var firstTime;
1759
- function monotonicMillis() {
1760
- if (firstTime === void 0) {
1761
- firstTime = Date.now();
1762
- }
1763
- if (typeof performance === "undefined" || !performance.now) {
1764
- return Date.now();
1765
- }
1766
- return Math.round(firstTime + performance.now());
1767
- }
1768
- function prettyNow() {
1769
- return `t=${Math.round((monotonicMillis() - firstTime) / 100) / 10}s`;
1770
- }
1771
- var serverDisconnectErrors = {
1772
- // A known error, e.g. during a restart or push
1773
- InternalServerError: { timeout: 1e3 },
1774
- // ErrorMetadata::overloaded() messages that we realy should back off
1775
- SubscriptionsWorkerFullError: { timeout: 3e3 },
1776
- TooManyConcurrentRequests: { timeout: 3e3 },
1777
- CommitterFullError: { timeout: 3e3 },
1778
- AwsTooManyRequestsException: { timeout: 3e3 },
1779
- ExecuteFullError: { timeout: 3e3 },
1780
- SystemTimeoutError: { timeout: 3e3 },
1781
- ExpiredInQueue: { timeout: 3e3 },
1782
- // ErrorMetadata::feature_temporarily_unavailable() that typically indicate a deploy just happened
1783
- VectorIndexesUnavailable: { timeout: 1e3 },
1784
- SearchIndexesUnavailable: { timeout: 1e3 },
1785
- TableSummariesUnavailable: { timeout: 1e3 },
1786
- // More ErrorMetadata::overloaded()
1787
- VectorIndexTooLarge: { timeout: 3e3 },
1788
- SearchIndexTooLarge: { timeout: 3e3 },
1789
- TooManyWritesInTimePeriod: { timeout: 3e3 }
1790
- };
1791
- function classifyDisconnectError(s) {
1792
- if (s === void 0) return "Unknown";
1793
- for (const prefix of Object.keys(
1794
- serverDisconnectErrors
1795
- )) {
1796
- if (s.startsWith(prefix)) {
1797
- return prefix;
1798
- }
1799
- }
1800
- return "Unknown";
1801
- }
1802
- var WebSocketManager = class {
1803
- constructor(uri, callbacks, webSocketConstructor, logger, markConnectionStateDirty, debug) {
1804
- this.markConnectionStateDirty = markConnectionStateDirty;
1805
- this.debug = debug;
1806
- __publicField8(this, "socket");
1807
- __publicField8(this, "connectionCount");
1808
- __publicField8(this, "_hasEverConnected", false);
1809
- __publicField8(this, "lastCloseReason");
1810
- __publicField8(this, "transitionChunkBuffer", null);
1811
- __publicField8(this, "defaultInitialBackoff");
1812
- __publicField8(this, "maxBackoff");
1813
- __publicField8(this, "retries");
1814
- __publicField8(this, "serverInactivityThreshold");
1815
- __publicField8(this, "reconnectDueToServerInactivityTimeout");
1816
- __publicField8(this, "uri");
1817
- __publicField8(this, "onOpen");
1818
- __publicField8(this, "onResume");
1819
- __publicField8(this, "onMessage");
1820
- __publicField8(this, "webSocketConstructor");
1821
- __publicField8(this, "logger");
1822
- __publicField8(this, "onServerDisconnectError");
1823
- this.webSocketConstructor = webSocketConstructor;
1824
- this.socket = { state: "disconnected" };
1825
- this.connectionCount = 0;
1826
- this.lastCloseReason = "InitialConnect";
1827
- this.defaultInitialBackoff = 1e3;
1828
- this.maxBackoff = 16e3;
1829
- this.retries = 0;
1830
- this.serverInactivityThreshold = 6e4;
1831
- this.reconnectDueToServerInactivityTimeout = null;
1832
- this.uri = uri;
1833
- this.onOpen = callbacks.onOpen;
1834
- this.onResume = callbacks.onResume;
1835
- this.onMessage = callbacks.onMessage;
1836
- this.onServerDisconnectError = callbacks.onServerDisconnectError;
1837
- this.logger = logger;
1838
- this.connect();
1839
- }
1840
- setSocketState(state) {
1841
- this.socket = state;
1842
- this._logVerbose(
1843
- `socket state changed: ${this.socket.state}, paused: ${"paused" in this.socket ? this.socket.paused : void 0}`
1844
- );
1845
- this.markConnectionStateDirty();
1846
- }
1847
- assembleTransition(chunk) {
1848
- if (chunk.partNumber < 0 || chunk.partNumber >= chunk.totalParts || chunk.totalParts === 0 || this.transitionChunkBuffer && (this.transitionChunkBuffer.totalParts !== chunk.totalParts || this.transitionChunkBuffer.transitionId !== chunk.transitionId)) {
1849
- this.transitionChunkBuffer = null;
1850
- throw new Error("Invalid TransitionChunk");
1851
- }
1852
- if (this.transitionChunkBuffer === null) {
1853
- this.transitionChunkBuffer = {
1854
- chunks: [],
1855
- totalParts: chunk.totalParts,
1856
- transitionId: chunk.transitionId
1857
- };
1858
- }
1859
- if (chunk.partNumber !== this.transitionChunkBuffer.chunks.length) {
1860
- const expectedLength = this.transitionChunkBuffer.chunks.length;
1861
- this.transitionChunkBuffer = null;
1862
- throw new Error(
1863
- `TransitionChunk received out of order: expected part ${expectedLength}, got ${chunk.partNumber}`
1864
- );
1865
- }
1866
- this.transitionChunkBuffer.chunks.push(chunk.chunk);
1867
- if (this.transitionChunkBuffer.chunks.length === chunk.totalParts) {
1868
- const fullJson = this.transitionChunkBuffer.chunks.join("");
1869
- this.transitionChunkBuffer = null;
1870
- const transition = parseServerMessage(JSON.parse(fullJson));
1871
- if (transition.type !== "Transition") {
1872
- throw new Error(
1873
- `Expected Transition, got ${transition.type} after assembling chunks`
1874
- );
1875
- }
1876
- return transition;
1877
- }
1878
- return null;
1879
- }
1880
- connect() {
1881
- if (this.socket.state === "terminated") {
1882
- return;
1883
- }
1884
- if (this.socket.state !== "disconnected" && this.socket.state !== "stopped") {
1885
- throw new Error(
1886
- "Didn't start connection from disconnected state: " + this.socket.state
1887
- );
1888
- }
1889
- const ws = new this.webSocketConstructor(this.uri);
1890
- this._logVerbose("constructed WebSocket");
1891
- this.setSocketState({
1892
- state: "connecting",
1893
- ws,
1894
- paused: "no"
1895
- });
1896
- this.resetServerInactivityTimeout();
1897
- ws.onopen = () => {
1898
- this.logger.logVerbose("begin ws.onopen");
1899
- if (this.socket.state !== "connecting") {
1900
- throw new Error("onopen called with socket not in connecting state");
1901
- }
1902
- this.setSocketState({
1903
- state: "ready",
1904
- ws,
1905
- paused: this.socket.paused === "yes" ? "uninitialized" : "no"
1906
- });
1907
- this.resetServerInactivityTimeout();
1908
- if (this.socket.paused === "no") {
1909
- this._hasEverConnected = true;
1910
- this.onOpen({
1911
- connectionCount: this.connectionCount,
1912
- lastCloseReason: this.lastCloseReason,
1913
- clientTs: monotonicMillis()
1914
- });
1915
- }
1916
- if (this.lastCloseReason !== "InitialConnect") {
1917
- if (this.lastCloseReason) {
1918
- this.logger.log(
1919
- "WebSocket reconnected at",
1920
- prettyNow(),
1921
- "after disconnect due to",
1922
- this.lastCloseReason
1923
- );
1924
- } else {
1925
- this.logger.log("WebSocket reconnected at", prettyNow());
1926
- }
1927
- }
1928
- this.connectionCount += 1;
1929
- this.lastCloseReason = null;
1930
- };
1931
- ws.onerror = (error) => {
1932
- this.transitionChunkBuffer = null;
1933
- const message = error.message;
1934
- if (message) {
1935
- this.logger.log(`WebSocket error message: ${message}`);
1936
- }
1937
- };
1938
- ws.onmessage = (message) => {
1939
- this.resetServerInactivityTimeout();
1940
- const messageLength = message.data.length;
1941
- let serverMessage = parseServerMessage(JSON.parse(message.data));
1942
- this._logVerbose(`received ws message with type ${serverMessage.type}`);
1943
- if (serverMessage.type === "Ping") {
1944
- return;
1945
- }
1946
- if (serverMessage.type === "TransitionChunk") {
1947
- const transition = this.assembleTransition(serverMessage);
1948
- if (!transition) {
1949
- return;
1950
- }
1951
- serverMessage = transition;
1952
- this._logVerbose(
1953
- `assembled full ws message of type ${serverMessage.type}`
1954
- );
1955
- }
1956
- if (this.transitionChunkBuffer !== null) {
1957
- this.transitionChunkBuffer = null;
1958
- this.logger.log(
1959
- `Received unexpected ${serverMessage.type} while buffering TransitionChunks`
1960
- );
1961
- }
1962
- if (serverMessage.type === "Transition") {
1963
- this.reportLargeTransition({
1964
- messageLength,
1965
- transition: serverMessage
1966
- });
1967
- }
1968
- const response = this.onMessage(serverMessage);
1969
- if (response.hasSyncedPastLastReconnect) {
1970
- this.retries = 0;
1971
- this.markConnectionStateDirty();
1972
- }
1973
- };
1974
- ws.onclose = (event) => {
1975
- this._logVerbose("begin ws.onclose");
1976
- this.transitionChunkBuffer = null;
1977
- if (this.lastCloseReason === null) {
1978
- this.lastCloseReason = event.reason || `closed with code ${event.code}`;
1979
- }
1980
- if (event.code !== CLOSE_NORMAL && event.code !== CLOSE_GOING_AWAY && // This commonly gets fired on mobile apps when the app is backgrounded
1981
- event.code !== CLOSE_NO_STATUS && event.code !== CLOSE_NOT_FOUND) {
1982
- let msg = `WebSocket closed with code ${event.code}`;
1983
- if (event.reason) {
1984
- msg += `: ${event.reason}`;
1985
- }
1986
- this.logger.log(msg);
1987
- if (this.onServerDisconnectError && event.reason) {
1988
- this.onServerDisconnectError(msg);
1989
- }
1990
- }
1991
- const reason = classifyDisconnectError(event.reason);
1992
- this.scheduleReconnect(reason);
1993
- return;
1994
- };
1995
- }
1996
- /**
1997
- * @returns The state of the {@link Socket}.
1998
- */
1999
- socketState() {
2000
- return this.socket.state;
2001
- }
2002
- /**
2003
- * @param message - A ClientMessage to send.
2004
- * @returns Whether the message (might have been) sent.
2005
- */
2006
- sendMessage(message) {
2007
- const messageForLog = {
2008
- type: message.type,
2009
- ...message.type === "Authenticate" && message.tokenType === "User" ? {
2010
- value: `...${message.value.slice(-7)}`
2011
- } : {}
2012
- };
2013
- if (this.socket.state === "ready" && this.socket.paused === "no") {
2014
- const encodedMessage = encodeClientMessage(message);
2015
- const request = JSON.stringify(encodedMessage);
2016
- let sent = false;
2017
- try {
2018
- this.socket.ws.send(request);
2019
- sent = true;
2020
- } catch (error) {
2021
- this.logger.log(
2022
- `Failed to send message on WebSocket, reconnecting: ${error}`
2023
- );
2024
- this.closeAndReconnect("FailedToSendMessage");
2025
- }
2026
- this._logVerbose(
2027
- `${sent ? "sent" : "failed to send"} message with type ${message.type}: ${JSON.stringify(
2028
- messageForLog
2029
- )}`
2030
- );
2031
- return true;
2032
- }
2033
- this._logVerbose(
2034
- `message not sent (socket state: ${this.socket.state}, paused: ${"paused" in this.socket ? this.socket.paused : void 0}): ${JSON.stringify(
2035
- messageForLog
2036
- )}`
2037
- );
2038
- return false;
2039
- }
2040
- resetServerInactivityTimeout() {
2041
- if (this.socket.state === "terminated") {
2042
- return;
2043
- }
2044
- if (this.reconnectDueToServerInactivityTimeout !== null) {
2045
- clearTimeout(this.reconnectDueToServerInactivityTimeout);
2046
- this.reconnectDueToServerInactivityTimeout = null;
2047
- }
2048
- this.reconnectDueToServerInactivityTimeout = setTimeout(() => {
2049
- this.closeAndReconnect("InactiveServer");
2050
- }, this.serverInactivityThreshold);
2051
- }
2052
- scheduleReconnect(reason) {
2053
- this.socket = { state: "disconnected" };
2054
- const backoff = this.nextBackoff(reason);
2055
- this.markConnectionStateDirty();
2056
- this.logger.log(`Attempting reconnect in ${Math.round(backoff)}ms`);
2057
- setTimeout(() => this.connect(), backoff);
2058
- }
2059
- /**
2060
- * Close the WebSocket and schedule a reconnect.
2061
- *
2062
- * This should be used when we hit an error and would like to restart the session.
2063
- */
2064
- closeAndReconnect(closeReason) {
2065
- this._logVerbose(`begin closeAndReconnect with reason ${closeReason}`);
2066
- switch (this.socket.state) {
2067
- case "disconnected":
2068
- case "terminated":
2069
- case "stopped":
2070
- return;
2071
- case "connecting":
2072
- case "ready": {
2073
- this.lastCloseReason = closeReason;
2074
- void this.close();
2075
- this.scheduleReconnect("client");
2076
- return;
2077
- }
2078
- default: {
2079
- this.socket;
2080
- }
2081
- }
2082
- }
2083
- /**
2084
- * Close the WebSocket, being careful to clear the onclose handler to avoid re-entrant
2085
- * calls. Use this instead of directly calling `ws.close()`
2086
- *
2087
- * It is the callers responsibility to update the state after this method is called so that the
2088
- * closed socket is not accessible or used again after this method is called
2089
- */
2090
- close() {
2091
- this.transitionChunkBuffer = null;
2092
- switch (this.socket.state) {
2093
- case "disconnected":
2094
- case "terminated":
2095
- case "stopped":
2096
- return Promise.resolve();
2097
- case "connecting": {
2098
- const ws = this.socket.ws;
2099
- ws.onmessage = (_message) => {
2100
- this._logVerbose("Ignoring message received after close");
2101
- };
2102
- return new Promise((r) => {
2103
- ws.onclose = () => {
2104
- this._logVerbose("Closed after connecting");
2105
- r();
2106
- };
2107
- ws.onopen = () => {
2108
- this._logVerbose("Opened after connecting");
2109
- ws.close();
2110
- };
2111
- });
2112
- }
2113
- case "ready": {
2114
- this._logVerbose("ws.close called");
2115
- const ws = this.socket.ws;
2116
- ws.onmessage = (_message) => {
2117
- this._logVerbose("Ignoring message received after close");
2118
- };
2119
- const result = new Promise((r) => {
2120
- ws.onclose = () => {
2121
- r();
2122
- };
2123
- });
2124
- ws.close();
2125
- return result;
2126
- }
2127
- default: {
2128
- this.socket;
2129
- return Promise.resolve();
2130
- }
2131
- }
2132
- }
2133
- /**
2134
- * Close the WebSocket and do not reconnect.
2135
- * @returns A Promise that resolves when the WebSocket `onClose` callback is called.
2136
- */
2137
- terminate() {
2138
- if (this.reconnectDueToServerInactivityTimeout) {
2139
- clearTimeout(this.reconnectDueToServerInactivityTimeout);
2140
- }
2141
- switch (this.socket.state) {
2142
- case "terminated":
2143
- case "stopped":
2144
- case "disconnected":
2145
- case "connecting":
2146
- case "ready": {
2147
- const result = this.close();
2148
- this.setSocketState({ state: "terminated" });
2149
- return result;
2150
- }
2151
- default: {
2152
- this.socket;
2153
- throw new Error(
2154
- `Invalid websocket state: ${this.socket.state}`
2155
- );
2156
- }
2157
- }
2158
- }
2159
- stop() {
2160
- switch (this.socket.state) {
2161
- case "terminated":
2162
- return Promise.resolve();
2163
- case "connecting":
2164
- case "stopped":
2165
- case "disconnected":
2166
- case "ready": {
2167
- const result = this.close();
2168
- this.socket = { state: "stopped" };
2169
- return result;
2170
- }
2171
- default: {
2172
- this.socket;
2173
- return Promise.resolve();
2174
- }
2175
- }
2176
- }
2177
- /**
2178
- * Create a new WebSocket after a previous `stop()`, unless `terminate()` was
2179
- * called before.
2180
- */
2181
- tryRestart() {
2182
- switch (this.socket.state) {
2183
- case "stopped":
2184
- break;
2185
- case "terminated":
2186
- case "connecting":
2187
- case "ready":
2188
- case "disconnected":
2189
- this.logger.logVerbose("Restart called without stopping first");
2190
- return;
2191
- default: {
2192
- this.socket;
2193
- }
2194
- }
2195
- this.connect();
2196
- }
2197
- pause() {
2198
- switch (this.socket.state) {
2199
- case "disconnected":
2200
- case "stopped":
2201
- case "terminated":
2202
- return;
2203
- case "connecting":
2204
- case "ready": {
2205
- this.socket = { ...this.socket, paused: "yes" };
2206
- return;
2207
- }
2208
- default: {
2209
- this.socket;
2210
- return;
2211
- }
2212
- }
2213
- }
2214
- /**
2215
- * Resume the state machine if previously paused.
2216
- */
2217
- resume() {
2218
- switch (this.socket.state) {
2219
- case "connecting":
2220
- this.socket = { ...this.socket, paused: "no" };
2221
- return;
2222
- case "ready":
2223
- if (this.socket.paused === "uninitialized") {
2224
- this.socket = { ...this.socket, paused: "no" };
2225
- this.onOpen({
2226
- connectionCount: this.connectionCount,
2227
- lastCloseReason: this.lastCloseReason,
2228
- clientTs: monotonicMillis()
2229
- });
2230
- } else if (this.socket.paused === "yes") {
2231
- this.socket = { ...this.socket, paused: "no" };
2232
- this.onResume();
2233
- }
2234
- return;
2235
- case "terminated":
2236
- case "stopped":
2237
- case "disconnected":
2238
- return;
2239
- default: {
2240
- this.socket;
2241
- }
2242
- }
2243
- this.connect();
2244
- }
2245
- connectionState() {
2246
- return {
2247
- isConnected: this.socket.state === "ready",
2248
- hasEverConnected: this._hasEverConnected,
2249
- connectionCount: this.connectionCount,
2250
- connectionRetries: this.retries
2251
- };
2252
- }
2253
- _logVerbose(message) {
2254
- this.logger.logVerbose(message);
2255
- }
2256
- nextBackoff(reason) {
2257
- const initialBackoff = reason === "client" ? 100 : reason === "Unknown" ? this.defaultInitialBackoff : serverDisconnectErrors[reason].timeout;
2258
- const baseBackoff = initialBackoff * Math.pow(2, this.retries);
2259
- this.retries += 1;
2260
- const actualBackoff = Math.min(baseBackoff, this.maxBackoff);
2261
- const jitter = actualBackoff * (Math.random() - 0.5);
2262
- return actualBackoff + jitter;
2263
- }
2264
- reportLargeTransition({
2265
- transition,
2266
- messageLength
2267
- }) {
2268
- if (transition.clientClockSkew === void 0 || transition.serverTs === void 0) {
2269
- return;
2270
- }
2271
- const transitionTransitTime = monotonicMillis() - // client time now
2272
- // clientClockSkew = (server time + upstream latency) - client time
2273
- // clientClockSkew is "how many milliseconds behind (slow) is the client clock"
2274
- // but the latency of the Connect message inflates this, making it appear further behind
2275
- transition.clientClockSkew - transition.serverTs / 1e6;
2276
- const prettyTransitionTime = `${Math.round(transitionTransitTime)}ms`;
2277
- const prettyMessageMB = `${Math.round(messageLength / 1e4) / 100}MB`;
2278
- const bytesPerSecond = messageLength / (transitionTransitTime / 1e3);
2279
- const prettyBytesPerSecond = `${Math.round(bytesPerSecond / 1e4) / 100}MB per second`;
2280
- this._logVerbose(
2281
- `received ${prettyMessageMB} transition in ${prettyTransitionTime} at ${prettyBytesPerSecond}`
2282
- );
2283
- if (messageLength > 2e7) {
2284
- this.logger.log(
2285
- `received query results totaling more that 20MB (${prettyMessageMB}) which will take a long time to download on slower connections`
2286
- );
2287
- } else if (transitionTransitTime > 2e4) {
2288
- this.logger.log(
2289
- `received query results totaling ${prettyMessageMB} which took more than 20s to arrive (${prettyTransitionTime})`
2290
- );
2291
- }
2292
- if (this.debug) {
2293
- this.sendMessage({
2294
- type: "Event",
2295
- eventType: "ClientReceivedTransition",
2296
- event: { transitionTransitTime, messageLength }
2297
- });
2298
- }
2299
- }
2300
- };
2301
-
2302
- // node_modules/convex/dist/esm/browser/sync/session.js
2303
- function newSessionId() {
2304
- return uuidv4();
2305
- }
2306
- function uuidv4() {
2307
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2308
- const r = Math.random() * 16 | 0, v2 = c === "x" ? r : r & 3 | 8;
2309
- return v2.toString(16);
2310
- });
2311
- }
2312
-
2313
- // node_modules/convex/dist/esm/vendor/jwt-decode/index.js
2314
- var InvalidTokenError = class extends Error {
2315
- };
2316
- InvalidTokenError.prototype.name = "InvalidTokenError";
2317
- function b64DecodeUnicode(str) {
2318
- return decodeURIComponent(
2319
- atob(str).replace(/(.)/g, (_m, p) => {
2320
- let code2 = p.charCodeAt(0).toString(16).toUpperCase();
2321
- if (code2.length < 2) {
2322
- code2 = "0" + code2;
2323
- }
2324
- return "%" + code2;
2325
- })
2326
- );
2327
- }
2328
- function base64UrlDecode(str) {
2329
- let output = str.replace(/-/g, "+").replace(/_/g, "/");
2330
- switch (output.length % 4) {
2331
- case 0:
2332
- break;
2333
- case 2:
2334
- output += "==";
2335
- break;
2336
- case 3:
2337
- output += "=";
2338
- break;
2339
- default:
2340
- throw new Error("base64 string is not of the correct length");
2341
- }
2342
- try {
2343
- return b64DecodeUnicode(output);
2344
- } catch {
2345
- return atob(output);
2346
- }
2347
- }
2348
- function jwtDecode(token, options) {
2349
- if (typeof token !== "string") {
2350
- throw new InvalidTokenError("Invalid token specified: must be a string");
2351
- }
2352
- options || (options = {});
2353
- const pos = options.header === true ? 0 : 1;
2354
- const part = token.split(".")[pos];
2355
- if (typeof part !== "string") {
2356
- throw new InvalidTokenError(
2357
- `Invalid token specified: missing part #${pos + 1}`
2358
- );
2359
- }
2360
- let decoded;
2361
- try {
2362
- decoded = base64UrlDecode(part);
2363
- } catch (e) {
2364
- throw new InvalidTokenError(
2365
- `Invalid token specified: invalid base64 for part #${pos + 1} (${e.message})`
2366
- );
2367
- }
2368
- try {
2369
- return JSON.parse(decoded);
2370
- } catch (e) {
2371
- throw new InvalidTokenError(
2372
- `Invalid token specified: invalid json for part #${pos + 1} (${e.message})`
2373
- );
2374
- }
2375
- }
2376
-
2377
- // node_modules/convex/dist/esm/browser/sync/authentication_manager.js
2378
- var __defProp10 = Object.defineProperty;
2379
- var __defNormalProp9 = (obj, key, value) => key in obj ? __defProp10(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2380
- var __publicField9 = (obj, key, value) => __defNormalProp9(obj, typeof key !== "symbol" ? key + "" : key, value);
2381
- var MAXIMUM_REFRESH_DELAY = 20 * 24 * 60 * 60 * 1e3;
2382
- var MAX_TOKEN_CONFIRMATION_ATTEMPTS = 2;
2383
- var AuthenticationManager = class {
2384
- constructor(syncState, callbacks, config) {
2385
- __publicField9(this, "authState", { state: "noAuth" });
2386
- __publicField9(this, "configVersion", 0);
2387
- __publicField9(this, "syncState");
2388
- __publicField9(this, "authenticate");
2389
- __publicField9(this, "stopSocket");
2390
- __publicField9(this, "tryRestartSocket");
2391
- __publicField9(this, "pauseSocket");
2392
- __publicField9(this, "resumeSocket");
2393
- __publicField9(this, "clearAuth");
2394
- __publicField9(this, "logger");
2395
- __publicField9(this, "refreshTokenLeewaySeconds");
2396
- __publicField9(this, "tokenConfirmationAttempts", 0);
2397
- this.syncState = syncState;
2398
- this.authenticate = callbacks.authenticate;
2399
- this.stopSocket = callbacks.stopSocket;
2400
- this.tryRestartSocket = callbacks.tryRestartSocket;
2401
- this.pauseSocket = callbacks.pauseSocket;
2402
- this.resumeSocket = callbacks.resumeSocket;
2403
- this.clearAuth = callbacks.clearAuth;
2404
- this.logger = config.logger;
2405
- this.refreshTokenLeewaySeconds = config.refreshTokenLeewaySeconds;
2406
- }
2407
- async setConfig(fetchToken, onChange) {
2408
- this.resetAuthState();
2409
- this._logVerbose("pausing WS for auth token fetch");
2410
- this.pauseSocket();
2411
- const token = await this.fetchTokenAndGuardAgainstRace(fetchToken, {
2412
- forceRefreshToken: false
2413
- });
2414
- if (token.isFromOutdatedConfig) {
2415
- return;
2416
- }
2417
- if (token.value) {
2418
- this.setAuthState({
2419
- state: "waitingForServerConfirmationOfCachedToken",
2420
- config: { fetchToken, onAuthChange: onChange },
2421
- hasRetried: false
2422
- });
2423
- this.authenticate(token.value);
2424
- } else {
2425
- this.setAuthState({
2426
- state: "initialRefetch",
2427
- config: { fetchToken, onAuthChange: onChange }
2428
- });
2429
- await this.refetchToken();
2430
- }
2431
- this._logVerbose("resuming WS after auth token fetch");
2432
- this.resumeSocket();
2433
- }
2434
- onTransition(serverMessage) {
2435
- if (!this.syncState.isCurrentOrNewerAuthVersion(
2436
- serverMessage.endVersion.identity
2437
- )) {
2438
- return;
2439
- }
2440
- if (serverMessage.endVersion.identity <= serverMessage.startVersion.identity) {
2441
- return;
2442
- }
2443
- if (this.authState.state === "waitingForServerConfirmationOfCachedToken") {
2444
- this._logVerbose("server confirmed auth token is valid");
2445
- void this.refetchToken();
2446
- this.authState.config.onAuthChange(true);
2447
- return;
2448
- }
2449
- if (this.authState.state === "waitingForServerConfirmationOfFreshToken") {
2450
- this._logVerbose("server confirmed new auth token is valid");
2451
- this.scheduleTokenRefetch(this.authState.token);
2452
- this.tokenConfirmationAttempts = 0;
2453
- if (!this.authState.hadAuth) {
2454
- this.authState.config.onAuthChange(true);
2455
- }
2456
- }
2457
- }
2458
- onAuthError(serverMessage) {
2459
- if (serverMessage.authUpdateAttempted === false && (this.authState.state === "waitingForServerConfirmationOfFreshToken" || this.authState.state === "waitingForServerConfirmationOfCachedToken")) {
2460
- this._logVerbose("ignoring non-auth token expired error");
2461
- return;
2462
- }
2463
- const { baseVersion } = serverMessage;
2464
- if (!this.syncState.isCurrentOrNewerAuthVersion(baseVersion + 1)) {
2465
- this._logVerbose("ignoring auth error for previous auth attempt");
2466
- return;
2467
- }
2468
- void this.tryToReauthenticate(serverMessage);
2469
- return;
2470
- }
2471
- // This is similar to `refetchToken` defined below, in fact we
2472
- // don't represent them as different states, but it is different
2473
- // in that we pause the WebSocket so that mutations
2474
- // don't retry with bad auth.
2475
- async tryToReauthenticate(serverMessage) {
2476
- this._logVerbose(`attempting to reauthenticate: ${serverMessage.error}`);
2477
- if (
2478
- // No way to fetch another token, kaboom
2479
- this.authState.state === "noAuth" || // We failed on a fresh token. After a small number of retries, we give up
2480
- // and clear the auth state to avoid infinite retries.
2481
- this.authState.state === "waitingForServerConfirmationOfFreshToken" && this.tokenConfirmationAttempts >= MAX_TOKEN_CONFIRMATION_ATTEMPTS
2482
- ) {
2483
- this.logger.error(
2484
- `Failed to authenticate: "${serverMessage.error}", check your server auth config`
2485
- );
2486
- if (this.syncState.hasAuth()) {
2487
- this.syncState.clearAuth();
2488
- }
2489
- if (this.authState.state !== "noAuth") {
2490
- this.setAndReportAuthFailed(this.authState.config.onAuthChange);
2491
- }
2492
- return;
2493
- }
2494
- if (this.authState.state === "waitingForServerConfirmationOfFreshToken") {
2495
- this.tokenConfirmationAttempts++;
2496
- this._logVerbose(
2497
- `retrying reauthentication, ${MAX_TOKEN_CONFIRMATION_ATTEMPTS - this.tokenConfirmationAttempts} attempts remaining`
2498
- );
2499
- }
2500
- await this.stopSocket();
2501
- const token = await this.fetchTokenAndGuardAgainstRace(
2502
- this.authState.config.fetchToken,
2503
- {
2504
- forceRefreshToken: true
2505
- }
2506
- );
2507
- if (token.isFromOutdatedConfig) {
2508
- return;
2509
- }
2510
- if (token.value && this.syncState.isNewAuth(token.value)) {
2511
- this.authenticate(token.value);
2512
- this.setAuthState({
2513
- state: "waitingForServerConfirmationOfFreshToken",
2514
- config: this.authState.config,
2515
- token: token.value,
2516
- hadAuth: this.authState.state === "notRefetching" || this.authState.state === "waitingForScheduledRefetch"
2517
- });
2518
- } else {
2519
- this._logVerbose("reauthentication failed, could not fetch a new token");
2520
- if (this.syncState.hasAuth()) {
2521
- this.syncState.clearAuth();
2522
- }
2523
- this.setAndReportAuthFailed(this.authState.config.onAuthChange);
2524
- }
2525
- this.tryRestartSocket();
2526
- }
2527
- // Force refetch the token and schedule another refetch
2528
- // before the token expires - an active client should never
2529
- // need to reauthenticate.
2530
- async refetchToken() {
2531
- if (this.authState.state === "noAuth") {
2532
- return;
2533
- }
2534
- this._logVerbose("refetching auth token");
2535
- const token = await this.fetchTokenAndGuardAgainstRace(
2536
- this.authState.config.fetchToken,
2537
- {
2538
- forceRefreshToken: true
2539
- }
2540
- );
2541
- if (token.isFromOutdatedConfig) {
2542
- return;
2543
- }
2544
- if (token.value) {
2545
- if (this.syncState.isNewAuth(token.value)) {
2546
- this.setAuthState({
2547
- state: "waitingForServerConfirmationOfFreshToken",
2548
- hadAuth: this.syncState.hasAuth(),
2549
- token: token.value,
2550
- config: this.authState.config
2551
- });
2552
- this.authenticate(token.value);
2553
- } else {
2554
- this.setAuthState({
2555
- state: "notRefetching",
2556
- config: this.authState.config
2557
- });
2558
- }
2559
- } else {
2560
- this._logVerbose("refetching token failed");
2561
- if (this.syncState.hasAuth()) {
2562
- this.clearAuth();
2563
- }
2564
- this.setAndReportAuthFailed(this.authState.config.onAuthChange);
2565
- }
2566
- this._logVerbose(
2567
- "restarting WS after auth token fetch (if currently stopped)"
2568
- );
2569
- this.tryRestartSocket();
2570
- }
2571
- scheduleTokenRefetch(token) {
2572
- if (this.authState.state === "noAuth") {
2573
- return;
2574
- }
2575
- const decodedToken = this.decodeToken(token);
2576
- if (!decodedToken) {
2577
- this.logger.error(
2578
- "Auth token is not a valid JWT, cannot refetch the token"
2579
- );
2580
- return;
2581
- }
2582
- const { iat, exp } = decodedToken;
2583
- if (!iat || !exp) {
2584
- this.logger.error(
2585
- "Auth token does not have required fields, cannot refetch the token"
2586
- );
2587
- return;
2588
- }
2589
- const tokenValiditySeconds = exp - iat;
2590
- if (tokenValiditySeconds <= 2) {
2591
- this.logger.error(
2592
- "Auth token does not live long enough, cannot refetch the token"
2593
- );
2594
- return;
2595
- }
2596
- let delay = Math.min(
2597
- MAXIMUM_REFRESH_DELAY,
2598
- (tokenValiditySeconds - this.refreshTokenLeewaySeconds) * 1e3
2599
- );
2600
- if (delay <= 0) {
2601
- this.logger.warn(
2602
- `Refetching auth token immediately, configured leeway ${this.refreshTokenLeewaySeconds}s is larger than the token's lifetime ${tokenValiditySeconds}s`
2603
- );
2604
- delay = 0;
2605
- }
2606
- const refetchTokenTimeoutId = setTimeout(() => {
2607
- this._logVerbose("running scheduled token refetch");
2608
- void this.refetchToken();
2609
- }, delay);
2610
- this.setAuthState({
2611
- state: "waitingForScheduledRefetch",
2612
- refetchTokenTimeoutId,
2613
- config: this.authState.config
2614
- });
2615
- this._logVerbose(
2616
- `scheduled preemptive auth token refetching in ${delay}ms`
2617
- );
2618
- }
2619
- // Protects against simultaneous calls to `setConfig`
2620
- // while we're fetching a token
2621
- async fetchTokenAndGuardAgainstRace(fetchToken, fetchArgs) {
2622
- const originalConfigVersion = ++this.configVersion;
2623
- this._logVerbose(
2624
- `fetching token with config version ${originalConfigVersion}`
2625
- );
2626
- const token = await fetchToken(fetchArgs);
2627
- if (this.configVersion !== originalConfigVersion) {
2628
- this._logVerbose(
2629
- `stale config version, expected ${originalConfigVersion}, got ${this.configVersion}`
2630
- );
2631
- return { isFromOutdatedConfig: true };
2632
- }
2633
- return { isFromOutdatedConfig: false, value: token };
2634
- }
2635
- stop() {
2636
- this.resetAuthState();
2637
- this.configVersion++;
2638
- this._logVerbose(`config version bumped to ${this.configVersion}`);
2639
- }
2640
- setAndReportAuthFailed(onAuthChange) {
2641
- onAuthChange(false);
2642
- this.resetAuthState();
2643
- }
2644
- resetAuthState() {
2645
- this.setAuthState({ state: "noAuth" });
2646
- }
2647
- setAuthState(newAuth) {
2648
- const authStateForLog = newAuth.state === "waitingForServerConfirmationOfFreshToken" ? {
2649
- hadAuth: newAuth.hadAuth,
2650
- state: newAuth.state,
2651
- token: `...${newAuth.token.slice(-7)}`
2652
- } : { state: newAuth.state };
2653
- this._logVerbose(
2654
- `setting auth state to ${JSON.stringify(authStateForLog)}`
2655
- );
2656
- switch (newAuth.state) {
2657
- case "waitingForScheduledRefetch":
2658
- case "notRefetching":
2659
- case "noAuth":
2660
- this.tokenConfirmationAttempts = 0;
2661
- break;
2662
- case "waitingForServerConfirmationOfFreshToken":
2663
- case "waitingForServerConfirmationOfCachedToken":
2664
- case "initialRefetch":
2665
- break;
2666
- default: {
2667
- newAuth;
2668
- }
2669
- }
2670
- if (this.authState.state === "waitingForScheduledRefetch") {
2671
- clearTimeout(this.authState.refetchTokenTimeoutId);
2672
- this.syncState.markAuthCompletion();
2673
- }
2674
- this.authState = newAuth;
2675
- }
2676
- decodeToken(token) {
2677
- try {
2678
- return jwtDecode(token);
2679
- } catch (e) {
2680
- this._logVerbose(
2681
- `Error decoding token: ${e instanceof Error ? e.message : "Unknown error"}`
2682
- );
2683
- return null;
2684
- }
2685
- }
2686
- _logVerbose(message) {
2687
- this.logger.logVerbose(`${message} [v${this.configVersion}]`);
2688
- }
2689
- };
2690
-
2691
- // node_modules/convex/dist/esm/browser/sync/metrics.js
2692
- var markNames = [
2693
- "convexClientConstructed",
2694
- "convexWebSocketOpen",
2695
- "convexFirstMessageReceived"
2696
- ];
2697
- function mark(name, sessionId) {
2698
- const detail = { sessionId };
2699
- if (typeof performance === "undefined" || !performance.mark) return;
2700
- performance.mark(name, { detail });
2701
- }
2702
- function performanceMarkToJson(mark2) {
2703
- let name = mark2.name.slice("convex".length);
2704
- name = name.charAt(0).toLowerCase() + name.slice(1);
2705
- return {
2706
- name,
2707
- startTime: mark2.startTime
2708
- };
2709
- }
2710
- function getMarksReport(sessionId) {
2711
- if (typeof performance === "undefined" || !performance.getEntriesByName) {
2712
- return [];
2713
- }
2714
- const allMarks = [];
2715
- for (const name of markNames) {
2716
- const marks = performance.getEntriesByName(name).filter((entry) => entry.entryType === "mark").filter((mark2) => mark2.detail.sessionId === sessionId);
2717
- allMarks.push(...marks);
2718
- }
2719
- return allMarks.map(performanceMarkToJson);
2720
- }
2721
-
2722
- // node_modules/convex/dist/esm/browser/sync/client.js
2723
- var __defProp11 = Object.defineProperty;
2724
- var __defNormalProp10 = (obj, key, value) => key in obj ? __defProp11(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
2725
- var __publicField10 = (obj, key, value) => __defNormalProp10(obj, typeof key !== "symbol" ? key + "" : key, value);
2726
- var BaseConvexClient = class {
2727
- /**
2728
- * @param address - The url of your Convex deployment, often provided
2729
- * by an environment variable. E.g. `https://small-mouse-123.convex.cloud`.
2730
- * @param onTransition - A callback receiving an array of query tokens
2731
- * corresponding to query results that have changed -- additional handlers
2732
- * can be added via `addOnTransitionHandler`.
2733
- * @param options - See {@link BaseConvexClientOptions} for a full description.
2734
- */
2735
- constructor(address, onTransition, options) {
2736
- __publicField10(this, "address");
2737
- __publicField10(this, "state");
2738
- __publicField10(this, "requestManager");
2739
- __publicField10(this, "webSocketManager");
2740
- __publicField10(this, "authenticationManager");
2741
- __publicField10(this, "remoteQuerySet");
2742
- __publicField10(this, "optimisticQueryResults");
2743
- __publicField10(this, "_transitionHandlerCounter", 0);
2744
- __publicField10(this, "_nextRequestId");
2745
- __publicField10(this, "_onTransitionFns", /* @__PURE__ */ new Map());
2746
- __publicField10(this, "_sessionId");
2747
- __publicField10(this, "firstMessageReceived", false);
2748
- __publicField10(this, "debug");
2749
- __publicField10(this, "logger");
2750
- __publicField10(this, "maxObservedTimestamp");
2751
- __publicField10(this, "connectionStateSubscribers", /* @__PURE__ */ new Map());
2752
- __publicField10(this, "nextConnectionStateSubscriberId", 0);
2753
- __publicField10(this, "_lastPublishedConnectionState");
2754
- __publicField10(this, "markConnectionStateDirty", () => {
2755
- void Promise.resolve().then(() => {
2756
- const curConnectionState = this.connectionState();
2757
- if (JSON.stringify(curConnectionState) !== JSON.stringify(this._lastPublishedConnectionState)) {
2758
- this._lastPublishedConnectionState = curConnectionState;
2759
- for (const cb of this.connectionStateSubscribers.values()) {
2760
- cb(curConnectionState);
2761
- }
2762
- }
2763
- });
2764
- });
2765
- __publicField10(this, "mark", (name) => {
2766
- if (this.debug) {
2767
- mark(name, this.sessionId);
2768
- }
2769
- });
2770
- if (typeof address === "object") {
2771
- throw new Error(
2772
- "Passing a ClientConfig object is no longer supported. Pass the URL of the Convex deployment as a string directly."
2773
- );
2774
- }
2775
- if (options?.skipConvexDeploymentUrlCheck !== true) {
2776
- validateDeploymentUrl(address);
2777
- }
2778
- options = { ...options };
2779
- const authRefreshTokenLeewaySeconds = options.authRefreshTokenLeewaySeconds ?? 2;
2780
- let webSocketConstructor = options.webSocketConstructor;
2781
- if (!webSocketConstructor && typeof WebSocket === "undefined") {
2782
- throw new Error(
2783
- "No WebSocket global variable defined! To use Convex in an environment without WebSocket try the HTTP client: https://docs.convex.dev/api/classes/browser.ConvexHttpClient"
2784
- );
2785
- }
2786
- webSocketConstructor = webSocketConstructor || WebSocket;
2787
- this.debug = options.reportDebugInfoToConvex ?? false;
2788
- this.address = address;
2789
- this.logger = options.logger === false ? instantiateNoopLogger({ verbose: options.verbose ?? false }) : options.logger !== true && options.logger ? options.logger : instantiateDefaultLogger({ verbose: options.verbose ?? false });
2790
- const i = address.search("://");
2791
- if (i === -1) {
2792
- throw new Error("Provided address was not an absolute URL.");
2793
- }
2794
- const origin = address.substring(i + 3);
2795
- const protocol = address.substring(0, i);
2796
- let wsProtocol;
2797
- if (protocol === "http") {
2798
- wsProtocol = "ws";
2799
- } else if (protocol === "https") {
2800
- wsProtocol = "wss";
2801
- } else {
2802
- throw new Error(`Unknown parent protocol ${protocol}`);
2803
- }
2804
- const wsUri = `${wsProtocol}://${origin}/api/${version}/sync`;
2805
- this.state = new LocalSyncState();
2806
- this.remoteQuerySet = new RemoteQuerySet(
2807
- (queryId) => this.state.queryPath(queryId),
2808
- this.logger
2809
- );
2810
- this.requestManager = new RequestManager(
2811
- this.logger,
2812
- this.markConnectionStateDirty
2813
- );
2814
- const pauseSocket = () => {
2815
- this.webSocketManager.pause();
2816
- this.state.pause();
2817
- };
2818
- this.authenticationManager = new AuthenticationManager(
2819
- this.state,
2820
- {
2821
- authenticate: (token) => {
2822
- const message = this.state.setAuth(token);
2823
- this.webSocketManager.sendMessage(message);
2824
- return message.baseVersion;
2825
- },
2826
- stopSocket: () => this.webSocketManager.stop(),
2827
- tryRestartSocket: () => this.webSocketManager.tryRestart(),
2828
- pauseSocket,
2829
- resumeSocket: () => this.webSocketManager.resume(),
2830
- clearAuth: () => {
2831
- this.clearAuth();
2832
- }
2833
- },
2834
- {
2835
- logger: this.logger,
2836
- refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds
2837
- }
2838
- );
2839
- this.optimisticQueryResults = new OptimisticQueryResults();
2840
- this.addOnTransitionHandler((transition) => {
2841
- onTransition(transition.queries.map((q) => q.token));
2842
- });
2843
- this._nextRequestId = 0;
2844
- this._sessionId = newSessionId();
2845
- const { unsavedChangesWarning } = options;
2846
- if (typeof window === "undefined" || typeof window.addEventListener === "undefined") {
2847
- if (unsavedChangesWarning === true) {
2848
- throw new Error(
2849
- "unsavedChangesWarning requested, but window.addEventListener not found! Remove {unsavedChangesWarning: true} from Convex client options."
2850
- );
2851
- }
2852
- } else if (unsavedChangesWarning !== false) {
2853
- window.addEventListener("beforeunload", (e) => {
2854
- if (this.requestManager.hasIncompleteRequests()) {
2855
- e.preventDefault();
2856
- const confirmationMessage = "Are you sure you want to leave? Your changes may not be saved.";
2857
- (e || window.event).returnValue = confirmationMessage;
2858
- return confirmationMessage;
2859
- }
2860
- });
2861
- }
2862
- this.webSocketManager = new WebSocketManager(
2863
- wsUri,
2864
- {
2865
- onOpen: (reconnectMetadata) => {
2866
- this.mark("convexWebSocketOpen");
2867
- this.webSocketManager.sendMessage({
2868
- ...reconnectMetadata,
2869
- type: "Connect",
2870
- sessionId: this._sessionId,
2871
- maxObservedTimestamp: this.maxObservedTimestamp
2872
- });
2873
- const oldRemoteQueryResults = new Set(
2874
- this.remoteQuerySet.remoteQueryResults().keys()
2875
- );
2876
- this.remoteQuerySet = new RemoteQuerySet(
2877
- (queryId) => this.state.queryPath(queryId),
2878
- this.logger
2879
- );
2880
- const [querySetModification, authModification] = this.state.restart(
2881
- oldRemoteQueryResults
2882
- );
2883
- if (authModification) {
2884
- this.webSocketManager.sendMessage(authModification);
2885
- }
2886
- this.webSocketManager.sendMessage(querySetModification);
2887
- for (const message of this.requestManager.restart()) {
2888
- this.webSocketManager.sendMessage(message);
2889
- }
2890
- },
2891
- onResume: () => {
2892
- const [querySetModification, authModification] = this.state.resume();
2893
- if (authModification) {
2894
- this.webSocketManager.sendMessage(authModification);
2895
- }
2896
- if (querySetModification) {
2897
- this.webSocketManager.sendMessage(querySetModification);
2898
- }
2899
- for (const message of this.requestManager.resume()) {
2900
- this.webSocketManager.sendMessage(message);
2901
- }
2902
- },
2903
- onMessage: (serverMessage) => {
2904
- if (!this.firstMessageReceived) {
2905
- this.firstMessageReceived = true;
2906
- this.mark("convexFirstMessageReceived");
2907
- this.reportMarks();
2908
- }
2909
- switch (serverMessage.type) {
2910
- case "Transition": {
2911
- this.observedTimestamp(serverMessage.endVersion.ts);
2912
- this.authenticationManager.onTransition(serverMessage);
2913
- this.remoteQuerySet.transition(serverMessage);
2914
- this.state.transition(serverMessage);
2915
- const completedRequests = this.requestManager.removeCompleted(
2916
- this.remoteQuerySet.timestamp()
2917
- );
2918
- this.notifyOnQueryResultChanges(completedRequests);
2919
- break;
2920
- }
2921
- case "MutationResponse": {
2922
- if (serverMessage.success) {
2923
- this.observedTimestamp(serverMessage.ts);
2924
- }
2925
- const completedMutationInfo = this.requestManager.onResponse(serverMessage);
2926
- if (completedMutationInfo !== null) {
2927
- this.notifyOnQueryResultChanges(
2928
- /* @__PURE__ */ new Map([
2929
- [
2930
- completedMutationInfo.requestId,
2931
- completedMutationInfo.result
2932
- ]
2933
- ])
2934
- );
2935
- }
2936
- break;
2937
- }
2938
- case "ActionResponse": {
2939
- this.requestManager.onResponse(serverMessage);
2940
- break;
2941
- }
2942
- case "AuthError": {
2943
- this.authenticationManager.onAuthError(serverMessage);
2944
- break;
2945
- }
2946
- case "FatalError": {
2947
- const error = logFatalError(this.logger, serverMessage.error);
2948
- void this.webSocketManager.terminate();
2949
- throw error;
2950
- }
2951
- default: {
2952
- serverMessage;
2953
- }
2954
- }
2955
- return {
2956
- hasSyncedPastLastReconnect: this.hasSyncedPastLastReconnect()
2957
- };
2958
- },
2959
- onServerDisconnectError: options.onServerDisconnectError
2960
- },
2961
- webSocketConstructor,
2962
- this.logger,
2963
- this.markConnectionStateDirty,
2964
- this.debug
2965
- );
2966
- this.mark("convexClientConstructed");
2967
- if (options.expectAuth) {
2968
- pauseSocket();
2969
- }
2970
- }
2971
- /**
2972
- * Return true if there is outstanding work from prior to the time of the most recent restart.
2973
- * This indicates that the client has not proven itself to have gotten past the issue that
2974
- * potentially led to the restart. Use this to influence when to reset backoff after a failure.
2975
- */
2976
- hasSyncedPastLastReconnect() {
2977
- const hasSyncedPastLastReconnect = this.requestManager.hasSyncedPastLastReconnect() || this.state.hasSyncedPastLastReconnect();
2978
- return hasSyncedPastLastReconnect;
2979
- }
2980
- observedTimestamp(observedTs) {
2981
- if (this.maxObservedTimestamp === void 0 || this.maxObservedTimestamp.lessThanOrEqual(observedTs)) {
2982
- this.maxObservedTimestamp = observedTs;
2983
- }
2984
- }
2985
- getMaxObservedTimestamp() {
2986
- return this.maxObservedTimestamp;
2987
- }
2988
- /**
2989
- * Compute the current query results based on the remoteQuerySet and the
2990
- * current optimistic updates and call `onTransition` for all the changed
2991
- * queries.
2992
- *
2993
- * @param completedMutations - A set of mutation IDs whose optimistic updates
2994
- * are no longer needed.
2995
- */
2996
- notifyOnQueryResultChanges(completedRequests) {
2997
- const remoteQueryResults = this.remoteQuerySet.remoteQueryResults();
2998
- const queryTokenToValue = /* @__PURE__ */ new Map();
2999
- for (const [queryId, result] of remoteQueryResults) {
3000
- const queryToken = this.state.queryToken(queryId);
3001
- if (queryToken !== null) {
3002
- const query = {
3003
- result,
3004
- udfPath: this.state.queryPath(queryId),
3005
- args: this.state.queryArgs(queryId)
3006
- };
3007
- queryTokenToValue.set(queryToken, query);
3008
- }
3009
- }
3010
- const changedQueryTokens = this.optimisticQueryResults.ingestQueryResultsFromServer(
3011
- queryTokenToValue,
3012
- new Set(completedRequests.keys())
3013
- );
3014
- this.handleTransition({
3015
- queries: changedQueryTokens.map((token) => {
3016
- const optimisticResult = this.optimisticQueryResults.rawQueryResult(token);
3017
- return {
3018
- token,
3019
- modification: {
3020
- kind: "Updated",
3021
- result: optimisticResult
3022
- }
3023
- };
3024
- }),
3025
- reflectedMutations: Array.from(completedRequests).map(
3026
- ([requestId, result]) => ({
3027
- requestId,
3028
- result
3029
- })
3030
- ),
3031
- timestamp: this.remoteQuerySet.timestamp()
3032
- });
3033
- }
3034
- handleTransition(transition) {
3035
- for (const fn of this._onTransitionFns.values()) {
3036
- fn(transition);
3037
- }
3038
- }
3039
- /**
3040
- * Add a handler that will be called on a transition.
3041
- *
3042
- * Any external side effects (e.g. setting React state) should be handled here.
3043
- *
3044
- * @param fn
3045
- *
3046
- * @returns
3047
- */
3048
- addOnTransitionHandler(fn) {
3049
- const id = this._transitionHandlerCounter++;
3050
- this._onTransitionFns.set(id, fn);
3051
- return () => this._onTransitionFns.delete(id);
3052
- }
3053
- /**
3054
- * Get the current JWT auth token and decoded claims.
3055
- */
3056
- getCurrentAuthClaims() {
3057
- const authToken = this.state.getAuth();
3058
- let decoded = {};
3059
- if (authToken && authToken.tokenType === "User") {
3060
- try {
3061
- decoded = authToken ? jwtDecode(authToken.value) : {};
3062
- } catch {
3063
- decoded = {};
3064
- }
3065
- } else {
3066
- return void 0;
3067
- }
3068
- return { token: authToken.value, decoded };
3069
- }
3070
- /**
3071
- * Set the authentication token to be used for subsequent queries and mutations.
3072
- * `fetchToken` will be called automatically again if a token expires.
3073
- * `fetchToken` should return `null` if the token cannot be retrieved, for example
3074
- * when the user's rights were permanently revoked.
3075
- * @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token
3076
- * @param onChange - a callback that will be called when the authentication status changes
3077
- */
3078
- setAuth(fetchToken, onChange) {
3079
- void this.authenticationManager.setConfig(fetchToken, onChange);
3080
- }
3081
- hasAuth() {
3082
- return this.state.hasAuth();
3083
- }
3084
- /** @internal */
3085
- setAdminAuth(value, fakeUserIdentity) {
3086
- const message = this.state.setAdminAuth(value, fakeUserIdentity);
3087
- this.webSocketManager.sendMessage(message);
3088
- }
3089
- clearAuth() {
3090
- const message = this.state.clearAuth();
3091
- this.webSocketManager.sendMessage(message);
3092
- }
3093
- /**
3094
- * Subscribe to a query function.
3095
- *
3096
- * Whenever this query's result changes, the `onTransition` callback
3097
- * passed into the constructor will be called.
3098
- *
3099
- * @param name - The name of the query.
3100
- * @param args - An arguments object for the query. If this is omitted, the
3101
- * arguments will be `{}`.
3102
- * @param options - A {@link SubscribeOptions} options object for this query.
3103
-
3104
- * @returns An object containing a {@link QueryToken} corresponding to this
3105
- * query and an `unsubscribe` callback.
3106
- */
3107
- subscribe(name, args, options) {
3108
- const argsObject = parseArgs(args);
3109
- const { modification, queryToken, unsubscribe } = this.state.subscribe(
3110
- name,
3111
- argsObject,
3112
- options?.journal,
3113
- options?.componentPath
3114
- );
3115
- if (modification !== null) {
3116
- this.webSocketManager.sendMessage(modification);
3117
- }
3118
- return {
3119
- queryToken,
3120
- unsubscribe: () => {
3121
- const modification2 = unsubscribe();
3122
- if (modification2) {
3123
- this.webSocketManager.sendMessage(modification2);
3124
- }
3125
- }
3126
- };
3127
- }
3128
- /**
3129
- * A query result based only on the current, local state.
3130
- *
3131
- * The only way this will return a value is if we're already subscribed to the
3132
- * query or its value has been set optimistically.
3133
- */
3134
- localQueryResult(udfPath, args) {
3135
- const argsObject = parseArgs(args);
3136
- const queryToken = serializePathAndArgs(udfPath, argsObject);
3137
- return this.optimisticQueryResults.queryResult(queryToken);
3138
- }
3139
- /**
3140
- * Get query result by query token based on current, local state
3141
- *
3142
- * The only way this will return a value is if we're already subscribed to the
3143
- * query or its value has been set optimistically.
3144
- *
3145
- * @internal
3146
- */
3147
- localQueryResultByToken(queryToken) {
3148
- return this.optimisticQueryResults.queryResult(queryToken);
3149
- }
3150
- /**
3151
- * Whether local query result is available for a token.
3152
- *
3153
- * This method does not throw if the result is an error.
3154
- *
3155
- * @internal
3156
- */
3157
- hasLocalQueryResultByToken(queryToken) {
3158
- return this.optimisticQueryResults.hasQueryResult(queryToken);
3159
- }
3160
- /**
3161
- * @internal
3162
- */
3163
- localQueryLogs(udfPath, args) {
3164
- const argsObject = parseArgs(args);
3165
- const queryToken = serializePathAndArgs(udfPath, argsObject);
3166
- return this.optimisticQueryResults.queryLogs(queryToken);
3167
- }
3168
- /**
3169
- * Retrieve the current {@link QueryJournal} for this query function.
3170
- *
3171
- * If we have not yet received a result for this query, this will be `undefined`.
3172
- *
3173
- * @param name - The name of the query.
3174
- * @param args - The arguments object for this query.
3175
- * @returns The query's {@link QueryJournal} or `undefined`.
3176
- */
3177
- queryJournal(name, args) {
3178
- const argsObject = parseArgs(args);
3179
- const queryToken = serializePathAndArgs(name, argsObject);
3180
- return this.state.queryJournal(queryToken);
3181
- }
3182
- /**
3183
- * Get the current {@link ConnectionState} between the client and the Convex
3184
- * backend.
3185
- *
3186
- * @returns The {@link ConnectionState} with the Convex backend.
3187
- */
3188
- connectionState() {
3189
- const wsConnectionState = this.webSocketManager.connectionState();
3190
- return {
3191
- hasInflightRequests: this.requestManager.hasInflightRequests(),
3192
- isWebSocketConnected: wsConnectionState.isConnected,
3193
- hasEverConnected: wsConnectionState.hasEverConnected,
3194
- connectionCount: wsConnectionState.connectionCount,
3195
- connectionRetries: wsConnectionState.connectionRetries,
3196
- timeOfOldestInflightRequest: this.requestManager.timeOfOldestInflightRequest(),
3197
- inflightMutations: this.requestManager.inflightMutations(),
3198
- inflightActions: this.requestManager.inflightActions()
3199
- };
3200
- }
3201
- /**
3202
- * Subscribe to the {@link ConnectionState} between the client and the Convex
3203
- * backend, calling a callback each time it changes.
3204
- *
3205
- * Subscribed callbacks will be called when any part of ConnectionState changes.
3206
- * ConnectionState may grow in future versions (e.g. to provide a array of
3207
- * inflight requests) in which case callbacks would be called more frequently.
3208
- *
3209
- * @returns An unsubscribe function to stop listening.
3210
- */
3211
- subscribeToConnectionState(cb) {
3212
- const id = this.nextConnectionStateSubscriberId++;
3213
- this.connectionStateSubscribers.set(id, cb);
3214
- return () => {
3215
- this.connectionStateSubscribers.delete(id);
3216
- };
3217
- }
3218
- /**
3219
- * Execute a mutation function.
3220
- *
3221
- * @param name - The name of the mutation.
3222
- * @param args - An arguments object for the mutation. If this is omitted,
3223
- * the arguments will be `{}`.
3224
- * @param options - A {@link MutationOptions} options object for this mutation.
3225
-
3226
- * @returns - A promise of the mutation's result.
3227
- */
3228
- async mutation(name, args, options) {
3229
- const result = await this.mutationInternal(name, args, options);
3230
- if (!result.success) {
3231
- if (result.errorData !== void 0) {
3232
- throw forwardData(
3233
- result,
3234
- new ConvexError(
3235
- createHybridErrorStacktrace("mutation", name, result)
3236
- )
3237
- );
3238
- }
3239
- throw new Error(createHybridErrorStacktrace("mutation", name, result));
3240
- }
3241
- return result.value;
3242
- }
3243
- /**
3244
- * @internal
3245
- */
3246
- async mutationInternal(udfPath, args, options, componentPath) {
3247
- const { mutationPromise } = this.enqueueMutation(
3248
- udfPath,
3249
- args,
3250
- options,
3251
- componentPath
3252
- );
3253
- return mutationPromise;
3254
- }
3255
- /**
3256
- * @internal
3257
- */
3258
- enqueueMutation(udfPath, args, options, componentPath) {
3259
- const mutationArgs = parseArgs(args);
3260
- this.tryReportLongDisconnect();
3261
- const requestId = this.nextRequestId;
3262
- this._nextRequestId++;
3263
- if (options !== void 0) {
3264
- const optimisticUpdate = options.optimisticUpdate;
3265
- if (optimisticUpdate !== void 0) {
3266
- const wrappedUpdate = (localQueryStore) => {
3267
- const result = optimisticUpdate(
3268
- localQueryStore,
3269
- mutationArgs
3270
- );
3271
- if (result instanceof Promise) {
3272
- this.logger.warn(
3273
- "Optimistic update handler returned a Promise. Optimistic updates should be synchronous."
3274
- );
3275
- }
3276
- };
3277
- const changedQueryTokens = this.optimisticQueryResults.applyOptimisticUpdate(
3278
- wrappedUpdate,
3279
- requestId
3280
- );
3281
- const changedQueries = changedQueryTokens.map((token) => {
3282
- const localResult = this.localQueryResultByToken(token);
3283
- return {
3284
- token,
3285
- modification: {
3286
- kind: "Updated",
3287
- result: localResult === void 0 ? void 0 : {
3288
- success: true,
3289
- value: localResult,
3290
- logLines: []
3291
- }
3292
- }
3293
- };
3294
- });
3295
- this.handleTransition({
3296
- queries: changedQueries,
3297
- reflectedMutations: [],
3298
- timestamp: this.remoteQuerySet.timestamp()
3299
- });
3300
- }
3301
- }
3302
- const message = {
3303
- type: "Mutation",
3304
- requestId,
3305
- udfPath,
3306
- componentPath,
3307
- args: [convexToJson(mutationArgs)]
3308
- };
3309
- const mightBeSent = this.webSocketManager.sendMessage(message);
3310
- const mutationPromise = this.requestManager.request(message, mightBeSent);
3311
- return {
3312
- requestId,
3313
- mutationPromise
3314
- };
3315
- }
3316
- /**
3317
- * Execute an action function.
3318
- *
3319
- * @param name - The name of the action.
3320
- * @param args - An arguments object for the action. If this is omitted,
3321
- * the arguments will be `{}`.
3322
- * @returns A promise of the action's result.
3323
- */
3324
- async action(name, args) {
3325
- const result = await this.actionInternal(name, args);
3326
- if (!result.success) {
3327
- if (result.errorData !== void 0) {
3328
- throw forwardData(
3329
- result,
3330
- new ConvexError(createHybridErrorStacktrace("action", name, result))
3331
- );
3332
- }
3333
- throw new Error(createHybridErrorStacktrace("action", name, result));
3334
- }
3335
- return result.value;
3336
- }
3337
- /**
3338
- * @internal
3339
- */
3340
- async actionInternal(udfPath, args, componentPath) {
3341
- const actionArgs = parseArgs(args);
3342
- const requestId = this.nextRequestId;
3343
- this._nextRequestId++;
3344
- this.tryReportLongDisconnect();
3345
- const message = {
3346
- type: "Action",
3347
- requestId,
3348
- udfPath,
3349
- componentPath,
3350
- args: [convexToJson(actionArgs)]
3351
- };
3352
- const mightBeSent = this.webSocketManager.sendMessage(message);
3353
- return this.requestManager.request(message, mightBeSent);
3354
- }
3355
- /**
3356
- * Close any network handles associated with this client and stop all subscriptions.
3357
- *
3358
- * Call this method when you're done with an {@link BaseConvexClient} to
3359
- * dispose of its sockets and resources.
3360
- *
3361
- * @returns A `Promise` fulfilled when the connection has been completely closed.
3362
- */
3363
- async close() {
3364
- this.authenticationManager.stop();
3365
- return this.webSocketManager.terminate();
3366
- }
3367
- /**
3368
- * Return the address for this client, useful for creating a new client.
3369
- *
3370
- * Not guaranteed to match the address with which this client was constructed:
3371
- * it may be canonicalized.
3372
- */
3373
- get url() {
3374
- return this.address;
3375
- }
3376
- /**
3377
- * @internal
3378
- */
3379
- get nextRequestId() {
3380
- return this._nextRequestId;
3381
- }
3382
- /**
3383
- * @internal
3384
- */
3385
- get sessionId() {
3386
- return this._sessionId;
3387
- }
3388
- /**
3389
- * Reports performance marks to the server. This should only be called when
3390
- * we have a functional websocket.
3391
- */
3392
- reportMarks() {
3393
- if (this.debug) {
3394
- const report = getMarksReport(this.sessionId);
3395
- this.webSocketManager.sendMessage({
3396
- type: "Event",
3397
- eventType: "ClientConnect",
3398
- event: report
3399
- });
3400
- }
3401
- }
3402
- tryReportLongDisconnect() {
3403
- if (!this.debug) {
3404
- return;
3405
- }
3406
- const timeOfOldestRequest = this.connectionState().timeOfOldestInflightRequest;
3407
- if (timeOfOldestRequest === null || Date.now() - timeOfOldestRequest.getTime() <= 60 * 1e3) {
3408
- return;
3409
- }
3410
- const endpoint = `${this.address}/api/debug_event`;
3411
- fetch(endpoint, {
3412
- method: "POST",
3413
- headers: {
3414
- "Content-Type": "application/json",
3415
- "Convex-Client": `npm-${version}`
3416
- },
3417
- body: JSON.stringify({ event: "LongWebsocketDisconnect" })
3418
- }).then((response) => {
3419
- if (!response.ok) {
3420
- this.logger.warn(
3421
- "Analytics request failed with response:",
3422
- response.body
3423
- );
3424
- }
3425
- }).catch((error) => {
3426
- this.logger.warn("Analytics response failed with error:", error);
3427
- });
3428
- }
3429
- };
3430
-
3431
- // node_modules/convex/dist/esm/browser/sync/pagination.js
3432
- function asPaginationResult(value) {
3433
- if (typeof value !== "object" || value === null || !Array.isArray(value.page) || typeof value.isDone !== "boolean" || typeof value.continueCursor !== "string") {
3434
- throw new Error(`Not a valid paginated query result: ${value?.toString()}`);
3435
- }
3436
- return value;
3437
- }
3438
-
3439
- // node_modules/convex/dist/esm/browser/sync/paginated_query_client.js
3440
- var __defProp12 = Object.defineProperty;
3441
- var __defNormalProp11 = (obj, key, value) => key in obj ? __defProp12(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3442
- var __publicField11 = (obj, key, value) => __defNormalProp11(obj, typeof key !== "symbol" ? key + "" : key, value);
3443
- var PaginatedQueryClient = class {
3444
- constructor(client, onTransition) {
3445
- this.client = client;
3446
- this.onTransition = onTransition;
3447
- __publicField11(this, "paginatedQuerySet", /* @__PURE__ */ new Map());
3448
- __publicField11(this, "lastTransitionTs");
3449
- this.lastTransitionTs = Long.fromNumber(0);
3450
- this.client.addOnTransitionHandler(
3451
- (transition) => this.onBaseTransition(transition)
3452
- );
3453
- }
3454
- /**
3455
- * Subscribe to a paginated query.
3456
- *
3457
- * @param name - The name of the paginated query function
3458
- * @param args - Arguments for the query (excluding paginationOpts)
3459
- * @param options - Pagination options including initialNumItems
3460
- * @returns Object with paginatedQueryToken and unsubscribe function
3461
- */
3462
- subscribe(name, args, options) {
3463
- const canonicalizedUdfPath = canonicalizeUdfPath(name);
3464
- const token = serializePaginatedPathAndArgs(
3465
- canonicalizedUdfPath,
3466
- args,
3467
- options
3468
- );
3469
- const unsubscribe = () => this.removePaginatedQuerySubscriber(token);
3470
- const existingEntry = this.paginatedQuerySet.get(token);
3471
- if (existingEntry) {
3472
- existingEntry.numSubscribers += 1;
3473
- return {
3474
- paginatedQueryToken: token,
3475
- unsubscribe
3476
- };
3477
- }
3478
- this.paginatedQuerySet.set(token, {
3479
- token,
3480
- canonicalizedUdfPath,
3481
- args,
3482
- numSubscribers: 1,
3483
- options: { initialNumItems: options.initialNumItems },
3484
- nextPageKey: 0,
3485
- pageKeys: [],
3486
- pageKeyToQuery: /* @__PURE__ */ new Map(),
3487
- ongoingSplits: /* @__PURE__ */ new Map(),
3488
- skip: false,
3489
- id: options.id
3490
- });
3491
- this.addPageToPaginatedQuery(token, null, options.initialNumItems);
3492
- return {
3493
- paginatedQueryToken: token,
3494
- unsubscribe
3495
- };
3496
- }
3497
- /**
3498
- * Get current results for a paginated query based on local state.
3499
- *
3500
- * Throws an error when one of the pages has errored.
3501
- */
3502
- localQueryResult(name, args, options) {
3503
- const canonicalizedUdfPath = canonicalizeUdfPath(name);
3504
- const token = serializePaginatedPathAndArgs(
3505
- canonicalizedUdfPath,
3506
- args,
3507
- options
3508
- );
3509
- return this.localQueryResultByToken(token);
3510
- }
3511
- /**
3512
- * @internal
3513
- */
3514
- localQueryResultByToken(token) {
3515
- const paginatedQuery = this.paginatedQuerySet.get(token);
3516
- if (!paginatedQuery) {
3517
- return void 0;
3518
- }
3519
- const activePages = this.activePageQueryTokens(paginatedQuery);
3520
- if (activePages.length === 0) {
3521
- return {
3522
- results: [],
3523
- status: "LoadingFirstPage",
3524
- loadMore: (numItems) => {
3525
- return this.loadMoreOfPaginatedQuery(token, numItems);
3526
- }
3527
- };
3528
- }
3529
- let allResults = [];
3530
- let hasUndefined = false;
3531
- let isDone = false;
3532
- for (const pageToken of activePages) {
3533
- const result = this.client.localQueryResultByToken(pageToken);
3534
- if (result === void 0) {
3535
- hasUndefined = true;
3536
- isDone = false;
3537
- continue;
3538
- }
3539
- const paginationResult = asPaginationResult(result);
3540
- allResults = allResults.concat(paginationResult.page);
3541
- isDone = !!paginationResult.isDone;
3542
- }
3543
- let status;
3544
- if (hasUndefined) {
3545
- status = allResults.length === 0 ? "LoadingFirstPage" : "LoadingMore";
3546
- } else if (isDone) {
3547
- status = "Exhausted";
3548
- } else {
3549
- status = "CanLoadMore";
3550
- }
3551
- return {
3552
- results: allResults,
3553
- status,
3554
- loadMore: (numItems) => {
3555
- return this.loadMoreOfPaginatedQuery(token, numItems);
3556
- }
3557
- };
3558
- }
3559
- onBaseTransition(transition) {
3560
- const changedBaseTokens = transition.queries.map((q) => q.token);
3561
- const changed = this.queriesContainingTokens(changedBaseTokens);
3562
- let paginatedQueries = [];
3563
- if (changed.length > 0) {
3564
- this.processPaginatedQuerySplits(
3565
- changed,
3566
- (token) => this.client.localQueryResultByToken(token)
3567
- );
3568
- paginatedQueries = changed.map((token) => ({
3569
- token,
3570
- modification: {
3571
- kind: "Updated",
3572
- result: this.localQueryResultByToken(token)
3573
- }
3574
- }));
3575
- }
3576
- const extendedTransition = {
3577
- ...transition,
3578
- paginatedQueries
3579
- };
3580
- this.onTransition(extendedTransition);
3581
- }
3582
- /**
3583
- * Load more items for a paginated query.
3584
- *
3585
- * This *always* causes a transition, the status of the query
3586
- * has probably changed from "CanLoadMore" to "LoadingMore".
3587
- * Data might have changed too: maybe a subscription to this page
3588
- * query already exists (unlikely but possible) or this page query
3589
- * has an optimistic update providing some initial data.
3590
- *
3591
- * @internal
3592
- */
3593
- loadMoreOfPaginatedQuery(token, numItems) {
3594
- this.mustGetPaginatedQuery(token);
3595
- const lastPageToken = this.queryTokenForLastPageOfPaginatedQuery(token);
3596
- const lastPageResult = this.client.localQueryResultByToken(lastPageToken);
3597
- if (!lastPageResult) {
3598
- return false;
3599
- }
3600
- const paginationResult = asPaginationResult(lastPageResult);
3601
- if (paginationResult.isDone) {
3602
- return false;
3603
- }
3604
- this.addPageToPaginatedQuery(
3605
- token,
3606
- paginationResult.continueCursor,
3607
- numItems
3608
- );
3609
- const loadMoreTransition = {
3610
- timestamp: this.lastTransitionTs,
3611
- reflectedMutations: [],
3612
- queries: [],
3613
- paginatedQueries: [
3614
- {
3615
- token,
3616
- modification: {
3617
- kind: "Updated",
3618
- result: this.localQueryResultByToken(token)
3619
- }
3620
- }
3621
- ]
3622
- };
3623
- this.onTransition(loadMoreTransition);
3624
- return true;
3625
- }
3626
- /**
3627
- * @internal
3628
- */
3629
- queriesContainingTokens(queryTokens) {
3630
- if (queryTokens.length === 0) {
3631
- return [];
3632
- }
3633
- const changed = [];
3634
- const queryTokenSet = new Set(queryTokens);
3635
- for (const [paginatedToken, paginatedQuery] of this.paginatedQuerySet) {
3636
- for (const pageToken of this.allQueryTokens(paginatedQuery)) {
3637
- if (queryTokenSet.has(pageToken)) {
3638
- changed.push(paginatedToken);
3639
- break;
3640
- }
3641
- }
3642
- }
3643
- return changed;
3644
- }
3645
- /**
3646
- * @internal
3647
- */
3648
- processPaginatedQuerySplits(changed, getResult) {
3649
- for (const paginatedQueryToken of changed) {
3650
- const paginatedQuery = this.mustGetPaginatedQuery(paginatedQueryToken);
3651
- const { ongoingSplits, pageKeyToQuery, pageKeys } = paginatedQuery;
3652
- for (const [pageKey, [splitKey1, splitKey2]] of ongoingSplits) {
3653
- const bothNewPagesLoaded = getResult(pageKeyToQuery.get(splitKey1).queryToken) !== void 0 && getResult(pageKeyToQuery.get(splitKey2).queryToken) !== void 0;
3654
- if (bothNewPagesLoaded) {
3655
- this.completePaginatedQuerySplit(
3656
- paginatedQuery,
3657
- pageKey,
3658
- splitKey1,
3659
- splitKey2
3660
- );
3661
- }
3662
- }
3663
- for (const pageKey of pageKeys) {
3664
- if (ongoingSplits.has(pageKey)) {
3665
- continue;
3666
- }
3667
- const pageToken = pageKeyToQuery.get(pageKey).queryToken;
3668
- const pageResult = getResult(pageToken);
3669
- if (!pageResult) {
3670
- continue;
3671
- }
3672
- const result = asPaginationResult(pageResult);
3673
- const shouldSplit = result.splitCursor && (result.pageStatus === "SplitRecommended" || result.pageStatus === "SplitRequired" || // This client-driven page splitting condition will change in the future.
3674
- result.page.length > paginatedQuery.options.initialNumItems * 2);
3675
- if (shouldSplit) {
3676
- this.splitPaginatedQueryPage(
3677
- paginatedQuery,
3678
- pageKey,
3679
- result.splitCursor,
3680
- // we just checked
3681
- result.continueCursor
3682
- );
3683
- }
3684
- }
3685
- }
3686
- }
3687
- splitPaginatedQueryPage(paginatedQuery, pageKey, splitCursor, continueCursor) {
3688
- const splitKey1 = paginatedQuery.nextPageKey++;
3689
- const splitKey2 = paginatedQuery.nextPageKey++;
3690
- const paginationOpts = {
3691
- cursor: continueCursor,
3692
- numItems: paginatedQuery.options.initialNumItems,
3693
- id: paginatedQuery.id
3694
- };
3695
- const firstSubscription = this.client.subscribe(
3696
- paginatedQuery.canonicalizedUdfPath,
3697
- {
3698
- ...paginatedQuery.args,
3699
- paginationOpts: {
3700
- ...paginationOpts,
3701
- cursor: null,
3702
- // Start from beginning for first split
3703
- endCursor: splitCursor
3704
- }
3705
- }
3706
- );
3707
- paginatedQuery.pageKeyToQuery.set(splitKey1, firstSubscription);
3708
- const secondSubscription = this.client.subscribe(
3709
- paginatedQuery.canonicalizedUdfPath,
3710
- {
3711
- ...paginatedQuery.args,
3712
- paginationOpts: {
3713
- ...paginationOpts,
3714
- cursor: splitCursor,
3715
- endCursor: continueCursor
3716
- }
3717
- }
3718
- );
3719
- paginatedQuery.pageKeyToQuery.set(splitKey2, secondSubscription);
3720
- paginatedQuery.ongoingSplits.set(pageKey, [splitKey1, splitKey2]);
3721
- }
3722
- /**
3723
- * @internal
3724
- */
3725
- addPageToPaginatedQuery(token, continueCursor, numItems) {
3726
- const paginatedQuery = this.mustGetPaginatedQuery(token);
3727
- const pageKey = paginatedQuery.nextPageKey++;
3728
- const paginationOpts = {
3729
- cursor: continueCursor,
3730
- numItems,
3731
- id: paginatedQuery.id
3732
- };
3733
- const pageArgs = {
3734
- ...paginatedQuery.args,
3735
- paginationOpts
3736
- };
3737
- const subscription = this.client.subscribe(
3738
- paginatedQuery.canonicalizedUdfPath,
3739
- pageArgs
3740
- );
3741
- paginatedQuery.pageKeys.push(pageKey);
3742
- paginatedQuery.pageKeyToQuery.set(pageKey, subscription);
3743
- return subscription;
3744
- }
3745
- removePaginatedQuerySubscriber(token) {
3746
- const paginatedQuery = this.paginatedQuerySet.get(token);
3747
- if (!paginatedQuery) {
3748
- return;
3749
- }
3750
- paginatedQuery.numSubscribers -= 1;
3751
- if (paginatedQuery.numSubscribers > 0) {
3752
- return;
3753
- }
3754
- for (const subscription of paginatedQuery.pageKeyToQuery.values()) {
3755
- subscription.unsubscribe();
3756
- }
3757
- this.paginatedQuerySet.delete(token);
3758
- }
3759
- completePaginatedQuerySplit(paginatedQuery, pageKey, splitKey1, splitKey2) {
3760
- const originalQuery = paginatedQuery.pageKeyToQuery.get(pageKey);
3761
- paginatedQuery.pageKeyToQuery.delete(pageKey);
3762
- const pageIndex = paginatedQuery.pageKeys.indexOf(pageKey);
3763
- paginatedQuery.pageKeys.splice(pageIndex, 1, splitKey1, splitKey2);
3764
- paginatedQuery.ongoingSplits.delete(pageKey);
3765
- originalQuery.unsubscribe();
3766
- }
3767
- /** The query tokens for all active pages, in result order */
3768
- activePageQueryTokens(paginatedQuery) {
3769
- return paginatedQuery.pageKeys.map(
3770
- (pageKey) => paginatedQuery.pageKeyToQuery.get(pageKey).queryToken
3771
- );
3772
- }
3773
- allQueryTokens(paginatedQuery) {
3774
- return Array.from(paginatedQuery.pageKeyToQuery.values()).map(
3775
- (sub) => sub.queryToken
3776
- );
3777
- }
3778
- queryTokenForLastPageOfPaginatedQuery(token) {
3779
- const paginatedQuery = this.mustGetPaginatedQuery(token);
3780
- const lastPageKey = paginatedQuery.pageKeys[paginatedQuery.pageKeys.length - 1];
3781
- if (lastPageKey === void 0) {
3782
- throw new Error(`No pages for paginated query ${token}`);
3783
- }
3784
- return paginatedQuery.pageKeyToQuery.get(lastPageKey).queryToken;
3785
- }
3786
- mustGetPaginatedQuery(token) {
3787
- const paginatedQuery = this.paginatedQuerySet.get(token);
3788
- if (!paginatedQuery) {
3789
- throw new Error("paginated query no longer exists for token " + token);
3790
- }
3791
- return paginatedQuery;
3792
- }
3793
- };
3794
-
3795
- // node_modules/convex/dist/esm/browser/simple_client.js
3796
- var __defProp13 = Object.defineProperty;
3797
- var __defNormalProp12 = (obj, key, value) => key in obj ? __defProp13(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3798
- var __publicField12 = (obj, key, value) => __defNormalProp12(obj, typeof key !== "symbol" ? key + "" : key, value);
3799
- var defaultWebSocketConstructor;
3800
- var ConvexClient = class {
3801
- /**
3802
- * Construct a client and immediately initiate a WebSocket connection to the passed address.
3803
- *
3804
- * @public
3805
- */
3806
- constructor(address, options = {}) {
3807
- __publicField12(this, "listeners");
3808
- __publicField12(this, "_client");
3809
- __publicField12(this, "_paginatedClient");
3810
- __publicField12(this, "callNewListenersWithCurrentValuesTimer");
3811
- __publicField12(this, "_closed");
3812
- __publicField12(this, "_disabled");
3813
- if (options.skipConvexDeploymentUrlCheck !== true) {
3814
- validateDeploymentUrl(address);
3815
- }
3816
- const { disabled, ...baseOptions } = options;
3817
- this._closed = false;
3818
- this._disabled = !!disabled;
3819
- if (defaultWebSocketConstructor && !("webSocketConstructor" in baseOptions) && typeof WebSocket === "undefined") {
3820
- baseOptions.webSocketConstructor = defaultWebSocketConstructor;
3821
- }
3822
- if (typeof window === "undefined" && !("unsavedChangesWarning" in baseOptions)) {
3823
- baseOptions.unsavedChangesWarning = false;
3824
- }
3825
- if (!this.disabled) {
3826
- this._client = new BaseConvexClient(
3827
- address,
3828
- () => {
3829
- },
3830
- // NOP, let the paginated query client do it all
3831
- baseOptions
3832
- );
3833
- this._paginatedClient = new PaginatedQueryClient(
3834
- this._client,
3835
- (transition) => this._transition(transition)
3836
- );
3837
- }
3838
- this.listeners = /* @__PURE__ */ new Set();
3839
- }
3840
- /**
3841
- * Once closed no registered callbacks will fire again.
3842
- */
3843
- get closed() {
3844
- return this._closed;
3845
- }
3846
- get client() {
3847
- if (this._client) return this._client;
3848
- throw new Error("ConvexClient is disabled");
3849
- }
3850
- /**
3851
- * @internal
3852
- */
3853
- get paginatedClient() {
3854
- if (this._paginatedClient) return this._paginatedClient;
3855
- throw new Error("ConvexClient is disabled");
3856
- }
3857
- get disabled() {
3858
- return this._disabled;
3859
- }
3860
- /**
3861
- * Call a callback whenever a new result for a query is received. The callback
3862
- * will run soon after being registered if a result for the query is already
3863
- * in memory.
3864
- *
3865
- * The return value is an {@link Unsubscribe} object which is both a function
3866
- * an an object with properties. Both of the patterns below work with this object:
3867
- *
3868
- *```ts
3869
- * // call the return value as a function
3870
- * const unsubscribe = client.onUpdate(api.messages.list, {}, (messages) => {
3871
- * console.log(messages);
3872
- * });
3873
- * unsubscribe();
3874
- *
3875
- * // unpack the return value into its properties
3876
- * const {
3877
- * getCurrentValue,
3878
- * unsubscribe,
3879
- * } = client.onUpdate(api.messages.list, {}, (messages) => {
3880
- * console.log(messages);
3881
- * });
3882
- *```
3883
- *
3884
- * @param query - A {@link server.FunctionReference} for the public query to run.
3885
- * @param args - The arguments to run the query with.
3886
- * @param callback - Function to call when the query result updates.
3887
- * @param onError - Function to call when the query result updates with an error.
3888
- * If not provided, errors will be thrown instead of calling the callback.
3889
- *
3890
- * @return an {@link Unsubscribe} function to stop calling the onUpdate function.
3891
- */
3892
- onUpdate(query, args, callback, onError) {
3893
- if (this.disabled) {
3894
- return this.createDisabledUnsubscribe();
3895
- }
3896
- const { queryToken, unsubscribe } = this.client.subscribe(
3897
- getFunctionName(query),
3898
- args
3899
- );
3900
- const queryInfo = {
3901
- queryToken,
3902
- callback,
3903
- onError,
3904
- unsubscribe,
3905
- hasEverRun: false,
3906
- query,
3907
- args,
3908
- paginationOptions: void 0
3909
- };
3910
- this.listeners.add(queryInfo);
3911
- if (this.queryResultReady(queryToken) && this.callNewListenersWithCurrentValuesTimer === void 0) {
3912
- this.callNewListenersWithCurrentValuesTimer = setTimeout(
3913
- () => this.callNewListenersWithCurrentValues(),
3914
- 0
3915
- );
3916
- }
3917
- const unsubscribeProps = {
3918
- unsubscribe: () => {
3919
- if (this.closed) {
3920
- return;
3921
- }
3922
- this.listeners.delete(queryInfo);
3923
- unsubscribe();
3924
- },
3925
- getCurrentValue: () => this.client.localQueryResultByToken(queryToken),
3926
- getQueryLogs: () => this.client.localQueryLogs(queryToken)
3927
- };
3928
- const ret = unsubscribeProps.unsubscribe;
3929
- Object.assign(ret, unsubscribeProps);
3930
- return ret;
3931
- }
3932
- /**
3933
- * Call a callback whenever a new result for a paginated query is received.
3934
- *
3935
- * This is an experimental preview: the final API may change.
3936
- * In particular, caching behavior, page splitting, and required paginated query options
3937
- * may change.
3938
- *
3939
- * @param query - A {@link server.FunctionReference} for the public query to run.
3940
- * @param args - The arguments to run the query with.
3941
- * @param options - Options for the paginated query including initialNumItems and id.
3942
- * @param callback - Function to call when the query result updates.
3943
- * @param onError - Function to call when the query result updates with an error.
3944
- *
3945
- * @return an {@link Unsubscribe} function to stop calling the callback.
3946
- */
3947
- onPaginatedUpdate_experimental(query, args, options, callback, onError) {
3948
- if (this.disabled) {
3949
- return this.createDisabledUnsubscribe();
3950
- }
3951
- const paginationOptions = {
3952
- initialNumItems: options.initialNumItems,
3953
- id: -1
3954
- };
3955
- const { paginatedQueryToken, unsubscribe } = this.paginatedClient.subscribe(
3956
- getFunctionName(query),
3957
- args,
3958
- // Simple client doesn't use IDs, there's no expectation that these queries remain separate.
3959
- paginationOptions
3960
- );
3961
- const queryInfo = {
3962
- queryToken: paginatedQueryToken,
3963
- callback,
3964
- onError,
3965
- unsubscribe,
3966
- hasEverRun: false,
3967
- query,
3968
- args,
3969
- paginationOptions
3970
- };
3971
- this.listeners.add(queryInfo);
3972
- if (!!this.paginatedClient.localQueryResultByToken(paginatedQueryToken) && this.callNewListenersWithCurrentValuesTimer === void 0) {
3973
- this.callNewListenersWithCurrentValuesTimer = setTimeout(
3974
- () => this.callNewListenersWithCurrentValues(),
3975
- 0
3976
- );
3977
- }
3978
- const unsubscribeProps = {
3979
- unsubscribe: () => {
3980
- if (this.closed) {
3981
- return;
3982
- }
3983
- this.listeners.delete(queryInfo);
3984
- unsubscribe();
3985
- },
3986
- getCurrentValue: () => {
3987
- const result = this.paginatedClient.localQueryResult(
3988
- getFunctionName(query),
3989
- args,
3990
- paginationOptions
3991
- );
3992
- return result;
3993
- },
3994
- getQueryLogs: () => []
3995
- // Paginated queries don't aggregate their logs
3996
- };
3997
- const ret = unsubscribeProps.unsubscribe;
3998
- Object.assign(ret, unsubscribeProps);
3999
- return ret;
4000
- }
4001
- // Run all callbacks that have never been run before if they have a query
4002
- // result available now.
4003
- callNewListenersWithCurrentValues() {
4004
- this.callNewListenersWithCurrentValuesTimer = void 0;
4005
- this._transition({ queries: [], paginatedQueries: [] }, true);
4006
- }
4007
- queryResultReady(queryToken) {
4008
- return this.client.hasLocalQueryResultByToken(queryToken);
4009
- }
4010
- createDisabledUnsubscribe() {
4011
- const disabledUnsubscribe = () => {
4012
- };
4013
- const unsubscribeProps = {
4014
- unsubscribe: disabledUnsubscribe,
4015
- getCurrentValue: () => void 0,
4016
- getQueryLogs: () => void 0
4017
- };
4018
- Object.assign(disabledUnsubscribe, unsubscribeProps);
4019
- return disabledUnsubscribe;
4020
- }
4021
- async close() {
4022
- if (this.disabled) return;
4023
- this.listeners.clear();
4024
- this._closed = true;
4025
- if (this._paginatedClient) {
4026
- this._paginatedClient = void 0;
4027
- }
4028
- return this.client.close();
4029
- }
4030
- /**
4031
- * Get the current JWT auth token and decoded claims.
4032
- */
4033
- getAuth() {
4034
- if (this.disabled) return;
4035
- return this.client.getCurrentAuthClaims();
4036
- }
4037
- /**
4038
- * Set the authentication token to be used for subsequent queries and mutations.
4039
- * `fetchToken` will be called automatically again if a token expires.
4040
- * `fetchToken` should return `null` if the token cannot be retrieved, for example
4041
- * when the user's rights were permanently revoked.
4042
- * @param fetchToken - an async function returning the JWT (typically an OpenID Connect Identity Token)
4043
- * @param onChange - a callback that will be called when the authentication status changes
4044
- */
4045
- setAuth(fetchToken, onChange) {
4046
- if (this.disabled) return;
4047
- this.client.setAuth(
4048
- fetchToken,
4049
- onChange ?? (() => {
4050
- })
4051
- );
4052
- }
4053
- /**
4054
- * @internal
4055
- */
4056
- setAdminAuth(token, identity) {
4057
- if (this.closed) {
4058
- throw new Error("ConvexClient has already been closed.");
4059
- }
4060
- if (this.disabled) return;
4061
- this.client.setAdminAuth(token, identity);
4062
- }
4063
- /**
4064
- * @internal
4065
- */
4066
- _transition({
4067
- queries,
4068
- paginatedQueries
4069
- }, callNewListeners = false) {
4070
- const updatedQueries = [
4071
- ...queries.map((q) => q.token),
4072
- ...paginatedQueries.map((q) => q.token)
4073
- ];
4074
- for (const queryInfo of this.listeners) {
4075
- const { callback, queryToken, onError, hasEverRun } = queryInfo;
4076
- const isPaginatedQuery = serializedQueryTokenIsPaginated(queryToken);
4077
- const hasResultReady = isPaginatedQuery ? !!this.paginatedClient.localQueryResultByToken(queryToken) : this.client.hasLocalQueryResultByToken(queryToken);
4078
- if (updatedQueries.includes(queryToken) || callNewListeners && !hasEverRun && hasResultReady) {
4079
- queryInfo.hasEverRun = true;
4080
- let newValue;
4081
- try {
4082
- if (isPaginatedQuery) {
4083
- newValue = this.paginatedClient.localQueryResultByToken(queryToken);
4084
- } else {
4085
- newValue = this.client.localQueryResultByToken(queryToken);
4086
- }
4087
- } catch (error) {
4088
- if (!(error instanceof Error)) throw error;
4089
- if (onError) {
4090
- onError(
4091
- error,
4092
- "Second argument to onUpdate onError is reserved for later use"
4093
- );
4094
- } else {
4095
- void Promise.reject(error);
4096
- }
4097
- continue;
4098
- }
4099
- callback(
4100
- newValue,
4101
- "Second argument to onUpdate callback is reserved for later use"
4102
- );
4103
- }
4104
- }
4105
- }
4106
- /**
4107
- * Execute a mutation function.
4108
- *
4109
- * @param mutation - A {@link server.FunctionReference} for the public mutation
4110
- * to run.
4111
- * @param args - An arguments object for the mutation.
4112
- * @param options - A {@link MutationOptions} options object for the mutation.
4113
- * @returns A promise of the mutation's result.
4114
- */
4115
- async mutation(mutation, args, options) {
4116
- if (this.disabled) throw new Error("ConvexClient is disabled");
4117
- return await this.client.mutation(getFunctionName(mutation), args, options);
4118
- }
4119
- /**
4120
- * Execute an action function.
4121
- *
4122
- * @param action - A {@link server.FunctionReference} for the public action
4123
- * to run.
4124
- * @param args - An arguments object for the action.
4125
- * @returns A promise of the action's result.
4126
- */
4127
- async action(action, args) {
4128
- if (this.disabled) throw new Error("ConvexClient is disabled");
4129
- return await this.client.action(getFunctionName(action), args);
4130
- }
4131
- /**
4132
- * Fetch a query result once.
4133
- *
4134
- * @param query - A {@link server.FunctionReference} for the public query
4135
- * to run.
4136
- * @param args - An arguments object for the query.
4137
- * @returns A promise of the query's result.
4138
- */
4139
- async query(query, args) {
4140
- if (this.disabled) throw new Error("ConvexClient is disabled");
4141
- const value = this.client.localQueryResult(getFunctionName(query), args);
4142
- if (value !== void 0) return Promise.resolve(value);
4143
- return new Promise((resolve, reject) => {
4144
- const { unsubscribe } = this.onUpdate(
4145
- query,
4146
- args,
4147
- (value2) => {
4148
- unsubscribe();
4149
- resolve(value2);
4150
- },
4151
- (e) => {
4152
- unsubscribe();
4153
- reject(e);
4154
- }
4155
- );
4156
- });
4157
- }
4158
- /**
4159
- * Get the current {@link ConnectionState} between the client and the Convex
4160
- * backend.
4161
- *
4162
- * @returns The {@link ConnectionState} with the Convex backend.
4163
- */
4164
- connectionState() {
4165
- if (this.disabled) throw new Error("ConvexClient is disabled");
4166
- return this.client.connectionState();
4167
- }
4168
- /**
4169
- * Subscribe to the {@link ConnectionState} between the client and the Convex
4170
- * backend, calling a callback each time it changes.
4171
- *
4172
- * Subscribed callbacks will be called when any part of ConnectionState changes.
4173
- * ConnectionState may grow in future versions (e.g. to provide a array of
4174
- * inflight requests) in which case callbacks would be called more frequently.
4175
- *
4176
- * @returns An unsubscribe function to stop listening.
4177
- */
4178
- subscribeToConnectionState(cb) {
4179
- if (this.disabled) return () => {
4180
- };
4181
- return this.client.subscribeToConnectionState(cb);
4182
- }
4183
- };
1
+ // src/index.ts
2
+ import { ConvexClient } from "convex/browser";
4184
3
 
4185
4
  // src/services/lobby.ts
4186
- var import_lodash = __toESM(require_lodash(), 1);
5
+ import debounce from "lodash.debounce";
6
+
7
+ // src/constants.ts
8
+ import {
9
+ LOBBY_VISIBILITY,
10
+ LEADERBOARD_SORT_ORDER,
11
+ LEADERBOARD_DISPLAY_TYPE,
12
+ UGC_TYPE,
13
+ UGC_VISIBILITY,
14
+ GAME_ENGINE,
15
+ LOBBY_MESSAGE_MAX_LENGTH
16
+ } from "@wvdsh/api";
17
+ var LobbyKickedReason = {
18
+ KICKED: "KICKED",
19
+ ERROR: "ERROR"
20
+ };
21
+ var LobbyUserChangeType = {
22
+ JOINED: "JOINED",
23
+ LEFT: "LEFT"
24
+ };
25
+ var P2PPacketDropReason = {
26
+ QUEUE_FULL: "QUEUE_FULL",
27
+ PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
28
+ INVALID_PAYLOAD_SIZE: "INVALID_PAYLOAD_SIZE",
29
+ INVALID_CHANNEL: "INVALID_CHANNEL",
30
+ MALFORMED: "MALFORMED",
31
+ PEER_NOT_READY: "PEER_NOT_READY"
32
+ };
33
+ var AvatarSize = {
34
+ SMALL: 64,
35
+ // Lists, chat bubbles
36
+ MEDIUM: 128,
37
+ // Profile cards
38
+ LARGE: 256
39
+ // Large displays
40
+ };
4187
41
 
4188
42
  // src/events.ts
4189
43
  var WavedashEvents = {
@@ -4227,22 +81,8 @@ var WavedashEvents = {
4227
81
  // P2P_CONNECTION_REQUESTED: 'P2PConnectionRequested', // for now we always connect all lobby members
4228
82
  };
4229
83
 
4230
- // src/types.ts
4231
- var LobbyKickedReason = {
4232
- KICKED: "KICKED",
4233
- ERROR: "ERROR"
4234
- };
4235
- var LobbyUserChangeType = {
4236
- JOINED: "JOINED",
4237
- LEFT: "LEFT"
4238
- };
4239
-
4240
84
  // src/services/lobby.ts
4241
- import {
4242
- api,
4243
- IFRAME_MESSAGE_TYPE,
4244
- LOBBY_MESSAGE_MAX_LENGTH
4245
- } from "@wvdsh/api";
85
+ import { api, IFRAME_MESSAGE_TYPE } from "@wvdsh/api";
4246
86
  var LobbyManager = class {
4247
87
  constructor(sdk) {
4248
88
  // Track current lobby state
@@ -4265,7 +105,7 @@ var LobbyManager = class {
4265
105
  this.seenInviteIds = /* @__PURE__ */ new Set();
4266
106
  // Queue for serializing P2P connection updates to prevent race conditions
4267
107
  this.p2pUpdateQueue = Promise.resolve();
4268
- this.debouncedMetadataUpdate = (0, import_lodash.default)(
108
+ this.debouncedMetadataUpdate = debounce(
4269
109
  () => this.processPendingLobbyDataUpdates(),
4270
110
  50
4271
111
  );
@@ -6561,7 +2401,7 @@ var P2PManager = _P2PManager;
6561
2401
 
6562
2402
  // src/services/stats.ts
6563
2403
  import { api as api6 } from "@wvdsh/api";
6564
- var import_lodash2 = __toESM(require_lodash(), 1);
2404
+ import debounce2 from "lodash.debounce";
6565
2405
  var STORE_DEBOUNCE_MS = 1e3;
6566
2406
  var StatsManager = class {
6567
2407
  constructor(sdk) {
@@ -6585,7 +2425,7 @@ var StatsManager = class {
6585
2425
  // Debounced persist — used by storeNow in setters to batch rapid calls.
6586
2426
  // Leading+trailing: first call fires immediately, subsequent calls within
6587
2427
  // the window are batched into one trailing call.
6588
- this.debouncedPersist = (0, import_lodash2.default)(() => this.persist(), STORE_DEBOUNCE_MS, {
2428
+ this.debouncedPersist = debounce2(() => this.persist(), STORE_DEBOUNCE_MS, {
6589
2429
  leading: true,
6590
2430
  trailing: true
6591
2431
  });
@@ -6967,7 +2807,7 @@ var FullscreenManager = class {
6967
2807
  (data) => {
6968
2808
  this.sdk.gameEventManager.notifyGame(
6969
2809
  WavedashEvents.FULLSCREEN_CHANGED,
6970
- data.isFullscreen
2810
+ { isFullscreen: data.isFullscreen }
6971
2811
  );
6972
2812
  this.setState(data.isFullscreen);
6973
2813
  }
@@ -7049,7 +2889,6 @@ import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE4 } from "@wvdsh/api";
7049
2889
  var OverlayManager = class {
7050
2890
  constructor(sdk) {
7051
2891
  this.handleKeyDown = (event) => {
7052
- if (event.defaultPrevented) return;
7053
2892
  if (event.key === "Tab" && event.shiftKey) {
7054
2893
  event.preventDefault();
7055
2894
  this.toggleOverlay();
@@ -7099,10 +2938,6 @@ function getCdnImageUrl(r2Key, host, options) {
7099
2938
  }
7100
2939
 
7101
2940
  // src/services/friends.ts
7102
- var AVATAR_SIZE_SMALL = 0;
7103
- var AVATAR_SIZE_MEDIUM = 1;
7104
- var AVATAR_SIZE_LARGE = 2;
7105
- var AVATAR_DIMENSIONS = [64, 128, 256];
7106
2941
  var FriendsManager = class {
7107
2942
  constructor(sdk) {
7108
2943
  this.userCache = /* @__PURE__ */ new Map();
@@ -7123,20 +2958,20 @@ var FriendsManager = class {
7123
2958
  }
7124
2959
  }
7125
2960
  /**
7126
- * Returns CDN URL with size transformation for a cached user's avatar
2961
+ * Returns CDN URL with size transformation for a cached user's avatar.
7127
2962
  * @param userId - The user ID to get the avatar URL for
7128
- * @param size - Avatar size constant (AVATAR_SIZE_SMALL, AVATAR_SIZE_MEDIUM, or AVATAR_SIZE_LARGE)
2963
+ * @param size - Pixel size for width and height. Use a value from
2964
+ * `AvatarSize` (SMALL=64, MEDIUM=128, LARGE=256) or any custom pixel size.
7129
2965
  * @returns CDN URL with size transformation, or null if user not cached or has no avatar
7130
2966
  */
7131
- getUserAvatarUrl(userId, size = AVATAR_SIZE_MEDIUM) {
2967
+ getUserAvatarUrl(userId, size = AvatarSize.MEDIUM) {
7132
2968
  const user = this.userCache.get(userId);
7133
2969
  if (!user?.avatarR2Key) {
7134
2970
  return null;
7135
2971
  }
7136
- const dimension = AVATAR_DIMENSIONS[size] ?? AVATAR_DIMENSIONS[AVATAR_SIZE_MEDIUM];
7137
2972
  return getCdnImageUrl(user.avatarR2Key, this.sdk.uploadsHost, {
7138
- width: dimension,
7139
- height: dimension,
2973
+ width: size,
2974
+ height: size,
7140
2975
  fit: "cover",
7141
2976
  quality: "high",
7142
2977
  sharpen: 1
@@ -7295,16 +3130,7 @@ var IFrameMessenger = class {
7295
3130
  };
7296
3131
 
7297
3132
  // src/index.ts
7298
- import {
7299
- GAME_ENGINE,
7300
- IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE5,
7301
- LEADERBOARD_DISPLAY_TYPE,
7302
- LEADERBOARD_SORT_ORDER,
7303
- LOBBY_VISIBILITY,
7304
- UGC_TYPE,
7305
- UGC_VISIBILITY,
7306
- UrlParams
7307
- } from "@wvdsh/api";
3133
+ import { IFRAME_MESSAGE_TYPE as IFRAME_MESSAGE_TYPE5, UrlParams } from "@wvdsh/api";
7308
3134
 
7309
3135
  // src/utils/validation.ts
7310
3136
  var CONVEX_ID_REGEX = /^[0-9a-z]{31,37}$/;
@@ -7419,12 +3245,28 @@ var WavedashSDK = class extends EventTarget {
7419
3245
  this._eventsReady = false;
7420
3246
  this.sessionEndSent = false;
7421
3247
  this.gameFinishedLoading = false;
3248
+ // Expose constants for easy access `Wavedash.LobbyVisibility.PUBLIC` etc.
7422
3249
  this.Events = WavedashEvents;
3250
+ this.LobbyVisibility = LOBBY_VISIBILITY;
3251
+ this.LeaderboardSortOrder = LEADERBOARD_SORT_ORDER;
3252
+ this.LeaderboardDisplayType = LEADERBOARD_DISPLAY_TYPE;
3253
+ this.UGCType = UGC_TYPE;
3254
+ this.UGCVisibility = UGC_VISIBILITY;
3255
+ this.AvatarSize = AvatarSize;
3256
+ this.LobbyKickedReason = LobbyKickedReason;
3257
+ this.LobbyUserChangeType = LobbyUserChangeType;
3258
+ this.P2PPacketDropReason = P2PPacketDropReason;
7423
3259
  this.config = null;
7424
3260
  this.engineCallbackReceiver = "WavedashCallbackReceiver";
7425
3261
  this.engineInstance = null;
7426
3262
  this.gameplayJwt = null;
7427
3263
  this.gameplayJwtPromise = null;
3264
+ // ==============
3265
+ // Event listening
3266
+ // ==============
3267
+ // Wrappers stored so `off(event, listener)` can find the wrapped EventListener
3268
+ // we registered on the EventTarget for a given user-supplied payload listener.
3269
+ this.listenerWrappers = /* @__PURE__ */ new Map();
7428
3270
  this.convexClient = new ConvexClient(sdkConfig.convexCloudUrl, {
7429
3271
  expectAuth: true
7430
3272
  });
@@ -7505,6 +3347,47 @@ var WavedashSDK = class extends EventTarget {
7505
3347
  this._eventsReady = true;
7506
3348
  this.gameEventManager.flushEventQueue();
7507
3349
  }
3350
+ /**
3351
+ * Subscribe to a Wavedash event with a payload-typed listener.
3352
+ * Returns an unsubscribe function.
3353
+ *
3354
+ * const unsubscribeLobbyJoined = Wavedash.on(Wavedash.Events.LOBBY_JOINED, (payload) => {
3355
+ * // payload: LobbyJoinedPayload
3356
+ * });
3357
+ * unsubscribeLobbyJoined(); // later
3358
+ */
3359
+ on(event, listener) {
3360
+ const wrapped = (e) => {
3361
+ listener(e.detail);
3362
+ };
3363
+ let perEvent = this.listenerWrappers.get(event);
3364
+ if (!perEvent) {
3365
+ perEvent = /* @__PURE__ */ new Map();
3366
+ this.listenerWrappers.set(event, perEvent);
3367
+ }
3368
+ const prev = perEvent.get(listener);
3369
+ if (prev) this.removeEventListener(event, prev);
3370
+ perEvent.set(listener, wrapped);
3371
+ this.addEventListener(event, wrapped);
3372
+ return () => this.off(event, listener);
3373
+ }
3374
+ /**
3375
+ * Remove a listener previously registered with {@link on}.
3376
+ */
3377
+ off(event, listener) {
3378
+ const perEvent = this.listenerWrappers.get(event);
3379
+ const wrapped = perEvent?.get(listener);
3380
+ if (!perEvent || !wrapped) return;
3381
+ this.removeEventListener(event, wrapped);
3382
+ perEvent.delete(listener);
3383
+ if (perEvent.size === 0) this.listenerWrappers.delete(event);
3384
+ }
3385
+ addEventListener(type, listener, options) {
3386
+ super.addEventListener(type, listener, options);
3387
+ }
3388
+ removeEventListener(type, listener, options) {
3389
+ super.removeEventListener(type, listener, options);
3390
+ }
7508
3391
  // ==================
7509
3392
  // Entrypoint Helpers
7510
3393
  // ==================
@@ -7620,10 +3503,10 @@ var WavedashSDK = class extends EventTarget {
7620
3503
  * Get avatar URL for a cached user with size transformation.
7621
3504
  * Users are cached when seen via listFriends() or lobby membership.
7622
3505
  * @param userId - The user ID to get the avatar URL for
7623
- * @param size - Avatar size constant (AVATAR_SIZE_SMALL=0, AVATAR_SIZE_MEDIUM=1, AVATAR_SIZE_LARGE=2)
3506
+ * @param size - Avatar size constant (Wavedash.AvatarSize.SMALL=0, Wavedash.AvatarSize.MEDIUM=1, Wavedash.AvatarSize.LARGE=2)
7624
3507
  * @returns CDN URL with size transformation, or null if user not cached or has no avatar
7625
3508
  */
7626
- getUserAvatarUrl(userId, size = AVATAR_SIZE_MEDIUM) {
3509
+ getUserAvatarUrl(userId, size = this.AvatarSize.MEDIUM) {
7627
3510
  return this.apiCallSync(
7628
3511
  this.friendsManager,
7629
3512
  "getUserAvatarUrl",
@@ -8361,7 +4244,7 @@ var WavedashSDK = class extends EventTarget {
8361
4244
  }
8362
4245
  };
8363
4246
  function setupWavedashSDK() {
8364
- const existing = window.WavedashJS;
4247
+ const existing = window.Wavedash;
8365
4248
  if (existing) return existing;
8366
4249
  const raw = new URLSearchParams(window.location.search).get(
8367
4250
  UrlParams.SdkConfig
@@ -8381,17 +4264,10 @@ function setupWavedashSDK() {
8381
4264
  );
8382
4265
  }
8383
4266
  const sdk = new WavedashSDK(sdkConfig);
8384
- window.WavedashJS = sdk;
8385
4267
  window.Wavedash = sdk;
4268
+ window.WavedashJS = sdk;
8386
4269
  return sdk;
8387
4270
  }
8388
4271
  export {
8389
- AVATAR_SIZE_LARGE,
8390
- AVATAR_SIZE_MEDIUM,
8391
- AVATAR_SIZE_SMALL,
8392
- LobbyKickedReason,
8393
- LobbyUserChangeType,
8394
- WavedashEvents,
8395
- WavedashSDK,
8396
4272
  setupWavedashSDK
8397
4273
  };