UNPKG

convex

Version:

Client for the Convex Cloud

1,562 lines (1,539 loc) 123 kB
"use strict"; var convex = (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // external-global-plugin:react var require_react = __commonJS({ "external-global-plugin:react"(exports, module) { module.exports = window.React; } }); // external-global-plugin:react-dom var require_react_dom = __commonJS({ "external-global-plugin:react-dom"(exports, module) { module.exports = window.ReactDOM; } }); // src/react/index.ts var react_exports = {}; __export(react_exports, { AuthLoading: () => AuthLoading, Authenticated: () => Authenticated, ConvexProvider: () => ConvexProvider, ConvexProviderWithAuth: () => ConvexProviderWithAuth, ConvexReactClient: () => ConvexReactClient, Unauthenticated: () => Unauthenticated, optimisticallyUpdateValueInPaginatedQuery: () => optimisticallyUpdateValueInPaginatedQuery, resetPaginationId: () => resetPaginationId, useAction: () => useAction, useConvex: () => useConvex, useConvexAuth: () => useConvexAuth, useMutation: () => useMutation, usePaginatedQuery: () => usePaginatedQuery, usePreloadedQuery: () => usePreloadedQuery, useQueries: () => useQueries, useQuery: () => useQuery, useSubscription: () => useSubscription }); // src/react/use_paginated_query.ts var import_react4 = __toESM(require_react(), 1); // src/values/base64.ts var base64_exports = {}; __export(base64_exports, { byteLength: () => byteLength, fromByteArray: () => fromByteArray, toByteArray: () => toByteArray }); var lookup = []; var revLookup = []; var Arr = Uint8Array; var code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; for (i = 0, len = code.length; i < len; ++i) { lookup[i] = code[i]; revLookup[code.charCodeAt(i)] = i; } var i; var len; revLookup["-".charCodeAt(0)] = 62; revLookup["_".charCodeAt(0)] = 63; function getLens(b64) { var len = b64.length; if (len % 4 > 0) { throw new Error("Invalid string. Length must be a multiple of 4"); } var validLen = b64.indexOf("="); if (validLen === -1) validLen = len; var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4; return [validLen, placeHoldersLen]; } function byteLength(b64) { var lens = getLens(b64); var validLen = lens[0]; var placeHoldersLen = lens[1]; return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; } function _byteLength(_b64, validLen, placeHoldersLen) { return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; } function toByteArray(b64) { var tmp; var lens = getLens(b64); var validLen = lens[0]; var placeHoldersLen = lens[1]; var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)); var curByte = 0; var len = placeHoldersLen > 0 ? validLen - 4 : validLen; var i; for (i = 0; i < len; i += 4) { tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)]; arr[curByte++] = tmp >> 16 & 255; arr[curByte++] = tmp >> 8 & 255; arr[curByte++] = tmp & 255; } if (placeHoldersLen === 2) { tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4; arr[curByte++] = tmp & 255; } if (placeHoldersLen === 1) { tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2; arr[curByte++] = tmp >> 8 & 255; arr[curByte++] = tmp & 255; } return arr; } function tripletToBase64(num) { return lookup[num >> 18 & 63] + lookup[num >> 12 & 63] + lookup[num >> 6 & 63] + lookup[num & 63]; } function encodeChunk(uint8, start, end) { var tmp; var output = []; for (var i = start; i < end; i += 3) { tmp = (uint8[i] << 16 & 16711680) + (uint8[i + 1] << 8 & 65280) + (uint8[i + 2] & 255); output.push(tripletToBase64(tmp)); } return output.join(""); } function fromByteArray(uint8) { var tmp; var len = uint8.length; var extraBytes = len % 3; var parts = []; var maxChunkLength = 16383; for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { parts.push( encodeChunk( uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength ) ); } if (extraBytes === 1) { tmp = uint8[len - 1]; parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "=="); } else if (extraBytes === 2) { tmp = (uint8[len - 2] << 8) + uint8[len - 1]; parts.push( lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "=" ); } return parts.join(""); } // src/common/index.ts function parseArgs(args) { if (args === void 0) { return {}; } if (!isSimpleObject(args)) { throw new Error( `The arguments to a Convex function must be an object. Received: ${args}` ); } return args; } function validateDeploymentUrl(deploymentUrl) { if (typeof deploymentUrl === "undefined") { throw new Error( `Client created with undefined deployment address. If you used an environment variable, check that it's set.` ); } if (typeof deploymentUrl !== "string") { throw new Error( `Invalid deployment address: found ${deploymentUrl}".` ); } if (!(deploymentUrl.startsWith("http:") || deploymentUrl.startsWith("https:"))) { throw new Error( `Invalid deployment address: Must start with "https://" or "http://". Found "${deploymentUrl}".` ); } try { new URL(deploymentUrl); } catch (err) { throw new Error( `Invalid deployment address: "${deploymentUrl}" is not a valid URL. If you believe this URL is correct, use the \`skipConvexDeploymentUrlCheck\` option to bypass this.` ); } if (deploymentUrl.endsWith(".convex.site")) { throw new Error( `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.` ); } } function isSimpleObject(value) { const isObject = typeof value === "object"; const prototype = Object.getPrototypeOf(value); const isSimple = prototype === null || prototype === Object.prototype || // Objects generated from other contexts (e.g. across Node.js `vm` modules) will not satisfy the previous // conditions but are still simple objects. prototype?.constructor?.name === "Object"; return isObject && isSimple; } // src/values/value.ts var LITTLE_ENDIAN = true; var MIN_INT64 = BigInt("-9223372036854775808"); var MAX_INT64 = BigInt("9223372036854775807"); var ZERO = BigInt("0"); var EIGHT = BigInt("8"); var TWOFIFTYSIX = BigInt("256"); function isSpecial(n2) { return Number.isNaN(n2) || !Number.isFinite(n2) || Object.is(n2, -0); } function slowBigIntToBase64(value) { if (value < ZERO) { value -= MIN_INT64 + MIN_INT64; } let hex = value.toString(16); if (hex.length % 2 === 1) hex = "0" + hex; const bytes = new Uint8Array(new ArrayBuffer(8)); let i = 0; for (const hexByte of hex.match(/.{2}/g).reverse()) { bytes.set([parseInt(hexByte, 16)], i++); value >>= EIGHT; } return fromByteArray(bytes); } function slowBase64ToBigInt(encoded) { const integerBytes = toByteArray(encoded); if (integerBytes.byteLength !== 8) { throw new Error( `Received ${integerBytes.byteLength} bytes, expected 8 for $integer` ); } let value = ZERO; let power = ZERO; for (const byte of integerBytes) { value += BigInt(byte) * TWOFIFTYSIX ** power; power++; } if (value > MAX_INT64) { value += MIN_INT64 + MIN_INT64; } return value; } function modernBigIntToBase64(value) { if (value < MIN_INT64 || MAX_INT64 < value) { throw new Error( `BigInt ${value} does not fit into a 64-bit signed integer.` ); } const buffer = new ArrayBuffer(8); new DataView(buffer).setBigInt64(0, value, true); return fromByteArray(new Uint8Array(buffer)); } function modernBase64ToBigInt(encoded) { const integerBytes = toByteArray(encoded); if (integerBytes.byteLength !== 8) { throw new Error( `Received ${integerBytes.byteLength} bytes, expected 8 for $integer` ); } const intBytesView = new DataView(integerBytes.buffer); return intBytesView.getBigInt64(0, true); } var bigIntToBase64 = DataView.prototype.setBigInt64 ? modernBigIntToBase64 : slowBigIntToBase64; var base64ToBigInt = DataView.prototype.getBigInt64 ? modernBase64ToBigInt : slowBase64ToBigInt; var MAX_IDENTIFIER_LEN = 1024; function validateObjectField(k) { if (k.length > MAX_IDENTIFIER_LEN) { throw new Error( `Field name ${k} exceeds maximum field name length ${MAX_IDENTIFIER_LEN}.` ); } if (k.startsWith("$")) { throw new Error(`Field name ${k} starts with a '$', which is reserved.`); } for (let i = 0; i < k.length; i += 1) { const charCode = k.charCodeAt(i); if (charCode < 32 || charCode >= 127) { throw new Error( `Field name ${k} has invalid character '${k[i]}': Field names can only contain non-control ASCII characters` ); } } } function jsonToConvex(value) { if (value === null) { return value; } if (typeof value === "boolean") { return value; } if (typeof value === "number") { return value; } if (typeof value === "string") { return value; } if (Array.isArray(value)) { return value.map((value2) => jsonToConvex(value2)); } if (typeof value !== "object") { throw new Error(`Unexpected type of ${value}`); } const entries = Object.entries(value); if (entries.length === 1) { const key = entries[0][0]; if (key === "$bytes") { if (typeof value.$bytes !== "string") { throw new Error(`Malformed $bytes field on ${value}`); } return toByteArray(value.$bytes).buffer; } if (key === "$integer") { if (typeof value.$integer !== "string") { throw new Error(`Malformed $integer field on ${value}`); } return base64ToBigInt(value.$integer); } if (key === "$float") { if (typeof value.$float !== "string") { throw new Error(`Malformed $float field on ${value}`); } const floatBytes = toByteArray(value.$float); if (floatBytes.byteLength !== 8) { throw new Error( `Received ${floatBytes.byteLength} bytes, expected 8 for $float` ); } const floatBytesView = new DataView(floatBytes.buffer); const float = floatBytesView.getFloat64(0, LITTLE_ENDIAN); if (!isSpecial(float)) { throw new Error(`Float ${float} should be encoded as a number`); } return float; } if (key === "$set") { throw new Error( `Received a Set which is no longer supported as a Convex type.` ); } if (key === "$map") { throw new Error( `Received a Map which is no longer supported as a Convex type.` ); } } const out = {}; for (const [k, v] of Object.entries(value)) { validateObjectField(k); out[k] = jsonToConvex(v); } return out; } function stringifyValueForError(value) { return JSON.stringify(value, (_key, value2) => { if (value2 === void 0) { return "undefined"; } if (typeof value2 === "bigint") { return `${value2.toString()}n`; } return value2; }); } function convexToJsonInternal(value, originalValue, context, includeTopLevelUndefined) { if (value === void 0) { const contextText = context && ` (present at path ${context} in original object ${stringifyValueForError( originalValue )})`; throw new Error( `undefined is not a valid Convex value${contextText}. To learn about Convex's supported types, see https://docs.convex.dev/using/types.` ); } if (value === null) { return value; } if (typeof value === "bigint") { if (value < MIN_INT64 || MAX_INT64 < value) { throw new Error( `BigInt ${value} does not fit into a 64-bit signed integer.` ); } return { $integer: bigIntToBase64(value) }; } if (typeof value === "number") { if (isSpecial(value)) { const buffer = new ArrayBuffer(8); new DataView(buffer).setFloat64(0, value, LITTLE_ENDIAN); return { $float: fromByteArray(new Uint8Array(buffer)) }; } else { return value; } } if (typeof value === "boolean") { return value; } if (typeof value === "string") { return value; } if (value instanceof ArrayBuffer) { return { $bytes: fromByteArray(new Uint8Array(value)) }; } if (Array.isArray(value)) { return value.map( (value2, i) => convexToJsonInternal(value2, originalValue, context + `[${i}]`, false) ); } if (value instanceof Set) { throw new Error( errorMessageForUnsupportedType(context, "Set", [...value], originalValue) ); } if (value instanceof Map) { throw new Error( errorMessageForUnsupportedType(context, "Map", [...value], originalValue) ); } if (!isSimpleObject(value)) { const theType = value?.constructor?.name; const typeName = theType ? `${theType} ` : ""; throw new Error( errorMessageForUnsupportedType(context, typeName, value, originalValue) ); } const out = {}; const entries = Object.entries(value); entries.sort(([k1, _v1], [k2, _v2]) => k1 === k2 ? 0 : k1 < k2 ? -1 : 1); for (const [k, v] of entries) { if (v !== void 0) { validateObjectField(k); out[k] = convexToJsonInternal(v, originalValue, context + `.${k}`, false); } else if (includeTopLevelUndefined) { validateObjectField(k); out[k] = convexOrUndefinedToJsonInternal( v, originalValue, context + `.${k}` ); } } return out; } function errorMessageForUnsupportedType(context, typeName, value, originalValue) { if (context) { return `${typeName}${stringifyValueForError( value )} is not a supported Convex type (present at path ${context} in original object ${stringifyValueForError( originalValue )}). To learn about Convex's supported types, see https://docs.convex.dev/using/types.`; } else { return `${typeName}${stringifyValueForError( value )} is not a supported Convex type.`; } } function convexOrUndefinedToJsonInternal(value, originalValue, context) { if (value === void 0) { return { $undefined: null }; } else { if (originalValue === void 0) { throw new Error( `Programming error. Current value is ${stringifyValueForError( value )} but original value is undefined` ); } return convexToJsonInternal(value, originalValue, context, false); } } function convexToJson(value) { return convexToJsonInternal(value, value, "", false); } // src/values/errors.ts var IDENTIFYING_FIELD = Symbol.for("ConvexError"); var ConvexError = class extends Error { name = "ConvexError"; data; [IDENTIFYING_FIELD] = true; constructor(data) { super(typeof data === "string" ? data : stringifyValueForError(data)); this.data = data; } }; // src/react/use_queries.ts var import_react3 = __toESM(require_react(), 1); // src/index.ts var version = "1.15.0"; // src/browser/logging.ts var INFO_COLOR = "color:rgb(0, 145, 255)"; function prefix_for_source(source) { switch (source) { case "query": return "Q"; case "mutation": return "M"; case "action": return "A"; case "any": return "?"; } } function logToConsole(type, source, udfPath, message) { const prefix = prefix_for_source(source); if (typeof message === "object") { message = `ConvexError ${JSON.stringify(message.errorData, null, 2)}`; } if (type === "info") { const match = message.match(/^\[.*?\] /); if (match === null) { console.error( `[CONVEX ${prefix}(${udfPath})] Could not parse console.log` ); return; } const level = message.slice(1, match[0].length - 2); const args = message.slice(match[0].length); console.log( `%c[CONVEX ${prefix}(${udfPath})] [${level}]`, INFO_COLOR, args ); } else { console.error(`[CONVEX ${prefix}(${udfPath})] ${message}`); } } function logFatalError(message) { const errorMessage = `[CONVEX FATAL ERROR] ${message}`; console.error(errorMessage); return new Error(errorMessage); } function createHybridErrorStacktrace(source, udfPath, result) { const prefix = prefix_for_source(source); return `[CONVEX ${prefix}(${udfPath})] ${result.errorMessage} Called by client`; } function forwardData(result, error) { error.data = result.errorData; return error; } // src/browser/sync/udf_path_utils.ts function canonicalizeUdfPath(udfPath) { const pieces = udfPath.split(":"); let moduleName; let functionName2; if (pieces.length === 1) { moduleName = pieces[0]; functionName2 = "default"; } else { moduleName = pieces.slice(0, pieces.length - 1).join(":"); functionName2 = pieces[pieces.length - 1]; } if (moduleName.endsWith(".js")) { moduleName = moduleName.slice(0, -3); } return `${moduleName}:${functionName2}`; } function serializePathAndArgs(udfPath, args) { return JSON.stringify({ udfPath: canonicalizeUdfPath(udfPath), args: convexToJson(args) }); } // src/browser/sync/local_state.ts var LocalSyncState = class { nextQueryId; querySetVersion; querySet; queryIdToToken; identityVersion; auth; outstandingQueriesOlderThanRestart; outstandingAuthOlderThanRestart; paused; pendingQuerySetModifications; constructor() { this.nextQueryId = 0; this.querySetVersion = 0; this.identityVersion = 0; this.querySet = /* @__PURE__ */ new Map(); this.queryIdToToken = /* @__PURE__ */ new Map(); this.outstandingQueriesOlderThanRestart = /* @__PURE__ */ new Set(); this.outstandingAuthOlderThanRestart = false; this.paused = false; this.pendingQuerySetModifications = /* @__PURE__ */ new Map(); } hasSyncedPastLastReconnect() { return this.outstandingQueriesOlderThanRestart.size === 0 && !this.outstandingAuthOlderThanRestart; } markAuthCompletion() { this.outstandingAuthOlderThanRestart = false; } subscribe(udfPath, args, journal, componentPath) { const canonicalizedUdfPath = canonicalizeUdfPath(udfPath); const queryToken = serializePathAndArgs(canonicalizedUdfPath, args); const existingEntry = this.querySet.get(queryToken); if (existingEntry !== void 0) { existingEntry.numSubscribers += 1; return { queryToken, modification: null, unsubscribe: () => this.removeSubscriber(queryToken) }; } else { const queryId = this.nextQueryId++; const query = { id: queryId, canonicalizedUdfPath, args, numSubscribers: 1, journal }; this.querySet.set(queryToken, query); this.queryIdToToken.set(queryId, queryToken); const baseVersion = this.querySetVersion; const newVersion = this.querySetVersion + 1; const add = { type: "Add", queryId, udfPath: canonicalizedUdfPath, args: [convexToJson(args)], journal, componentPath }; if (this.paused) { this.pendingQuerySetModifications.set(queryId, add); } else { this.querySetVersion = newVersion; } const modification = { type: "ModifyQuerySet", baseVersion, newVersion, modifications: [add] }; return { queryToken, modification, unsubscribe: () => this.removeSubscriber(queryToken) }; } } transition(transition) { for (const modification of transition.modifications) { switch (modification.type) { case "QueryUpdated": case "QueryFailed": { this.outstandingQueriesOlderThanRestart.delete(modification.queryId); const journal = modification.journal; if (journal !== void 0) { const queryToken = this.queryIdToToken.get(modification.queryId); if (queryToken !== void 0) { this.querySet.get(queryToken).journal = journal; } } break; } case "QueryRemoved": { this.outstandingQueriesOlderThanRestart.delete(modification.queryId); break; } default: { const _ = modification; throw new Error(`Invalid modification ${modification.type}`); } } } } queryId(udfPath, args) { const canonicalizedUdfPath = canonicalizeUdfPath(udfPath); const queryToken = serializePathAndArgs(canonicalizedUdfPath, args); const existingEntry = this.querySet.get(queryToken); if (existingEntry !== void 0) { return existingEntry.id; } return null; } isCurrentOrNewerAuthVersion(version2) { return version2 >= this.identityVersion; } setAuth(value) { this.auth = { tokenType: "User", value }; const baseVersion = this.identityVersion; if (!this.paused) { this.identityVersion = baseVersion + 1; } return { type: "Authenticate", baseVersion, ...this.auth }; } setAdminAuth(value, actingAs) { const auth = { tokenType: "Admin", value, impersonating: actingAs }; this.auth = auth; const baseVersion = this.identityVersion; if (!this.paused) { this.identityVersion = baseVersion + 1; } return { type: "Authenticate", baseVersion, ...auth }; } clearAuth() { this.auth = void 0; this.markAuthCompletion(); const baseVersion = this.identityVersion; if (!this.paused) { this.identityVersion = baseVersion + 1; } return { type: "Authenticate", tokenType: "None", baseVersion }; } hasAuth() { return !!this.auth; } isNewAuth(value) { return this.auth?.value !== value; } queryPath(queryId) { const pathAndArgs = this.queryIdToToken.get(queryId); if (pathAndArgs) { return this.querySet.get(pathAndArgs).canonicalizedUdfPath; } return null; } queryArgs(queryId) { const pathAndArgs = this.queryIdToToken.get(queryId); if (pathAndArgs) { return this.querySet.get(pathAndArgs).args; } return null; } queryToken(queryId) { return this.queryIdToToken.get(queryId) ?? null; } queryJournal(queryToken) { return this.querySet.get(queryToken)?.journal; } restart(oldRemoteQueryResults) { this.unpause(); this.outstandingQueriesOlderThanRestart.clear(); const modifications = []; for (const localQuery of this.querySet.values()) { const add = { type: "Add", queryId: localQuery.id, udfPath: localQuery.canonicalizedUdfPath, args: [convexToJson(localQuery.args)], journal: localQuery.journal }; modifications.push(add); if (!oldRemoteQueryResults.has(localQuery.id)) { this.outstandingQueriesOlderThanRestart.add(localQuery.id); } } this.querySetVersion = 1; const querySet = { type: "ModifyQuerySet", baseVersion: 0, newVersion: 1, modifications }; if (!this.auth) { this.identityVersion = 0; return [querySet, void 0]; } this.outstandingAuthOlderThanRestart = true; const authenticate = { type: "Authenticate", baseVersion: 0, ...this.auth }; this.identityVersion = 1; return [querySet, authenticate]; } pause() { this.paused = true; } resume() { const querySet = this.pendingQuerySetModifications.size > 0 ? { type: "ModifyQuerySet", baseVersion: this.querySetVersion, newVersion: ++this.querySetVersion, modifications: Array.from( this.pendingQuerySetModifications.values() ) } : void 0; const authenticate = this.auth !== void 0 ? { type: "Authenticate", baseVersion: this.identityVersion++, ...this.auth } : void 0; this.unpause(); return [querySet, authenticate]; } unpause() { this.paused = false; this.pendingQuerySetModifications.clear(); } removeSubscriber(queryToken) { const localQuery = this.querySet.get(queryToken); if (localQuery.numSubscribers > 1) { localQuery.numSubscribers -= 1; return null; } else { this.querySet.delete(queryToken); this.queryIdToToken.delete(localQuery.id); this.outstandingQueriesOlderThanRestart.delete(localQuery.id); const baseVersion = this.querySetVersion; const newVersion = this.querySetVersion + 1; const remove = { type: "Remove", queryId: localQuery.id }; if (this.paused) { if (this.pendingQuerySetModifications.has(localQuery.id)) { this.pendingQuerySetModifications.delete(localQuery.id); } else { this.pendingQuerySetModifications.set(localQuery.id, remove); } } else { this.querySetVersion = newVersion; } return { type: "ModifyQuerySet", baseVersion, newVersion, modifications: [remove] }; } } }; // src/browser/sync/request_manager.ts var RequestManager = class { inflightRequests; requestsOlderThanRestart; constructor() { this.inflightRequests = /* @__PURE__ */ new Map(); this.requestsOlderThanRestart = /* @__PURE__ */ new Set(); } request(message, sent) { const result = new Promise((resolve) => { const status = sent ? "Requested" : "NotSent"; this.inflightRequests.set(message.requestId, { message, status: { status, requestedAt: /* @__PURE__ */ new Date(), onResult: resolve } }); }); return result; } /** * Update the state after receiving a response. * * @returns A RequestId if the request is complete and its optimistic update * can be dropped, null otherwise. */ onResponse(response) { const requestInfo = this.inflightRequests.get(response.requestId); if (requestInfo === void 0) { return null; } if (requestInfo.status.status === "Completed") { return null; } const udfType = requestInfo.message.type === "Mutation" ? "mutation" : "action"; const udfPath = requestInfo.message.udfPath; for (const line of response.logLines) { logToConsole("info", udfType, udfPath, line); } const status = requestInfo.status; let onResolve; if (response.success) { onResolve = () => status.onResult({ success: true, logLines: response.logLines, value: jsonToConvex(response.result) }); } else { const errorMessage = response.result; const { errorData } = response; logToConsole("error", udfType, udfPath, errorMessage); onResolve = () => status.onResult({ success: false, errorMessage, errorData: errorData !== void 0 ? jsonToConvex(errorData) : void 0, logLines: response.logLines }); } if (response.type === "ActionResponse" || !response.success) { onResolve(); this.inflightRequests.delete(response.requestId); this.requestsOlderThanRestart.delete(response.requestId); return response.requestId; } requestInfo.status = { status: "Completed", ts: response.ts, onResolve }; return null; } // Remove and returns completed requests. removeCompleted(ts) { const completeRequests = /* @__PURE__ */ new Set(); for (const [requestId, requestInfo] of this.inflightRequests.entries()) { const status = requestInfo.status; if (status.status === "Completed" && status.ts.lessThanOrEqual(ts)) { status.onResolve(); completeRequests.add(requestId); this.inflightRequests.delete(requestId); this.requestsOlderThanRestart.delete(requestId); } } return completeRequests; } restart() { this.requestsOlderThanRestart = new Set(this.inflightRequests.keys()); const allMessages = []; for (const [requestId, value] of this.inflightRequests) { if (value.status.status === "NotSent") { value.status.status = "Requested"; allMessages.push(value.message); continue; } if (value.message.type === "Mutation") { allMessages.push(value.message); } else { this.inflightRequests.delete(requestId); this.requestsOlderThanRestart.delete(requestId); if (value.status.status === "Completed") { throw new Error("Action should never be in 'Completed' state"); } value.status.onResult({ success: false, errorMessage: "Connection lost while action was in flight", logLines: [] }); } } return allMessages; } resume() { const allMessages = []; for (const [, value] of this.inflightRequests) { if (value.status.status === "NotSent") { value.status.status = "Requested"; allMessages.push(value.message); continue; } } return allMessages; } /** * @returns true if there are any requests that have been requested but have * not be completed yet. */ hasIncompleteRequests() { for (const requestInfo of this.inflightRequests.values()) { if (requestInfo.status.status === "Requested") { return true; } } return false; } /** * @returns true if there are any inflight requests, including ones that have * completed on the server, but have not been applied. */ hasInflightRequests() { return this.inflightRequests.size > 0; } /** * @returns true if there are any inflight requests, that have been hanging around * since prior to the most recent restart. */ hasSyncedPastLastReconnect() { return this.requestsOlderThanRestart.size === 0; } timeOfOldestInflightRequest() { if (this.inflightRequests.size === 0) { return null; } let oldestInflightRequest = Date.now(); for (const request of this.inflightRequests.values()) { if (request.status.status !== "Completed") { if (request.status.requestedAt.getTime() < oldestInflightRequest) { oldestInflightRequest = request.status.requestedAt.getTime(); } } } return new Date(oldestInflightRequest); } }; // src/server/components/index.ts var toReferencePath = Symbol.for("toReferencePath"); function extractReferencePath(reference) { return reference[toReferencePath] ?? null; } function isFunctionHandle(s) { return s.startsWith("function://"); } var InstalledComponent = class { /** * @internal */ _definition; /** * @internal */ _name; /** * @internal */ [toReferencePath]; constructor(definition, name) { this._definition = definition; this._name = name; this[toReferencePath] = `_reference/childComponent/${name}`; } get exports() { return createExports(this._name, []); } }; function createExports(name, pathParts) { const handler = { get(_, prop) { if (typeof prop === "string") { const newParts = [...pathParts, prop]; return createExports(name, newParts); } else if (prop === toReferencePath) { let reference = `_reference/childComponent/${name}`; for (const part of pathParts) { reference += `/${part}`; } return reference; } else { return void 0; } } }; return new Proxy({}, handler); } // src/server/impl/actions_impl.ts function getFunctionAddress(functionReference) { let functionAddress; if (typeof functionReference === "string") { if (isFunctionHandle(functionReference)) { functionAddress = { functionHandle: functionReference }; } else { functionAddress = { name: functionReference }; } } else if (functionReference[functionName]) { functionAddress = { name: functionReference[functionName] }; } else { const referencePath = extractReferencePath(functionReference); if (!referencePath) { throw new Error(`${functionReference} is not a functionReference`); } functionAddress = { reference: referencePath }; } return functionAddress; } // src/server/api.ts var functionName = Symbol.for("functionName"); function getFunctionName(functionReference) { const address = getFunctionAddress(functionReference); if (address.name === void 0) { if (address.functionHandle !== void 0) { throw new Error( `Expected function reference like "api.file.func" or "internal.file.func", but received function handle ${address.functionHandle}` ); } else if (address.reference !== void 0) { throw new Error( `Expected function reference in the current component like "api.file.func" or "internal.file.func", but received reference ${address.reference}` ); } throw new Error( `Expected function reference like "api.file.func" or "internal.file.func", but received ${JSON.stringify(address)}` ); } if (typeof functionReference === "string") return functionReference; const name = functionReference[functionName]; if (!name) { throw new Error(`${functionReference} is not a functionReference`); } return name; } function makeFunctionReference(name) { return { [functionName]: name }; } function createApi(pathParts = []) { const handler = { get(_, prop) { if (typeof prop === "string") { const newParts = [...pathParts, prop]; return createApi(newParts); } else if (prop === functionName) { if (pathParts.length < 2) { const found = ["api", ...pathParts].join("."); throw new Error( `API path is expected to be of the form \`api.moduleName.functionName\`. Found: \`${found}\`` ); } const path = pathParts.slice(0, -1).join("/"); const exportName = pathParts[pathParts.length - 1]; if (exportName === "default") { return path; } else { return path + ":" + exportName; } } else if (prop === Symbol.toStringTag) { return "FunctionReference"; } else { return void 0; } } }; return new Proxy({}, handler); } var anyApi = createApi(); // src/browser/sync/optimistic_updates_impl.ts var OptimisticLocalStoreImpl = class { // A references of the query results in OptimisticQueryResults queryResults; // All of the queries modified by this class modifiedQueries; constructor(queryResults) { this.queryResults = queryResults; this.modifiedQueries = []; } getQuery(query, ...args) { const queryArgs = parseArgs(args[0]); const name = getFunctionName(query); const queryResult = this.queryResults.get( serializePathAndArgs(name, queryArgs) ); if (queryResult === void 0) { return void 0; } return OptimisticLocalStoreImpl.queryValue(queryResult.result); } getAllQueries(query) { const queriesWithName = []; const name = getFunctionName(query); for (const queryResult of this.queryResults.values()) { if (queryResult.udfPath === canonicalizeUdfPath(name)) { queriesWithName.push({ args: queryResult.args, value: OptimisticLocalStoreImpl.queryValue(queryResult.result) }); } } return queriesWithName; } setQuery(queryReference, args, value) { const queryArgs = parseArgs(args); const name = getFunctionName(queryReference); const queryToken = serializePathAndArgs(name, queryArgs); let result; if (value === void 0) { result = void 0; } else { result = { success: true, value, // It's an optimistic update, so there are no function logs to show. logLines: [] }; } const query = { udfPath: name, args: queryArgs, result }; this.queryResults.set(queryToken, query); this.modifiedQueries.push(queryToken); } static queryValue(result) { if (result === void 0) { return void 0; } else if (result.success) { return result.value; } else { return void 0; } } }; var OptimisticQueryResults = class { queryResults; optimisticUpdates; constructor() { this.queryResults = /* @__PURE__ */ new Map(); this.optimisticUpdates = []; } ingestQueryResultsFromServer(serverQueryResults, optimisticUpdatesToDrop) { this.optimisticUpdates = this.optimisticUpdates.filter((updateAndId) => { return !optimisticUpdatesToDrop.has(updateAndId.mutationId); }); const oldQueryResults = this.queryResults; this.queryResults = new Map(serverQueryResults); const localStore = new OptimisticLocalStoreImpl(this.queryResults); for (const updateAndId of this.optimisticUpdates) { updateAndId.update(localStore); } const changedQueries = []; for (const [queryToken, query] of this.queryResults) { const oldQuery = oldQueryResults.get(queryToken); if (oldQuery === void 0 || oldQuery.result !== query.result) { changedQueries.push(queryToken); } } return changedQueries; } applyOptimisticUpdate(update, mutationId) { this.optimisticUpdates.push({ update, mutationId }); const localStore = new OptimisticLocalStoreImpl(this.queryResults); update(localStore); return localStore.modifiedQueries; } queryResult(queryToken) { const query = this.queryResults.get(queryToken); if (query === void 0) { return void 0; } const result = query.result; if (result === void 0) { return void 0; } else if (result.success) { return result.value; } else { if (result.errorData !== void 0) { throw forwardData( result, new ConvexError( createHybridErrorStacktrace("query", query.udfPath, result) ) ); } throw new Error( createHybridErrorStacktrace("query", query.udfPath, result) ); } } hasQueryResult(queryToken) { return this.queryResults.get(queryToken) !== void 0; } /** * @internal */ queryLogs(queryToken) { const query = this.queryResults.get(queryToken); return query?.result?.logLines; } }; // src/browser/long.ts var Long = class { low; high; __isUnsignedLong__; static isLong(obj) { return (obj && obj.__isUnsignedLong__) === true; } constructor(low, high) { this.low = low | 0; this.high = high | 0; this.__isUnsignedLong__ = true; } // prettier-ignore static fromBytesLE(bytes) { return new Long( bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24, bytes[4] | bytes[5] << 8 | bytes[6] << 16 | bytes[7] << 24 ); } // prettier-ignore toBytesLE() { const hi = this.high; const lo = this.low; return [ lo & 255, lo >>> 8 & 255, lo >>> 16 & 255, lo >>> 24, hi & 255, hi >>> 8 & 255, hi >>> 16 & 255, hi >>> 24 ]; } static fromNumber(value) { if (isNaN(value)) return UZERO; if (value < 0) return UZERO; if (value >= TWO_PWR_64_DBL) return MAX_UNSIGNED_VALUE; return new Long(value % TWO_PWR_32_DBL | 0, value / TWO_PWR_32_DBL | 0); } toString() { return (BigInt(this.high) * BigInt(TWO_PWR_32_DBL) + BigInt(this.low)).toString(); } equals(other) { if (!Long.isLong(other)) other = Long.fromValue(other); if (this.high >>> 31 === 1 && other.high >>> 31 === 1) return false; return this.high === other.high && this.low === other.low; } notEquals(other) { return !this.equals(other); } comp(other) { if (!Long.isLong(other)) other = Long.fromValue(other); if (this.equals(other)) return 0; return other.high >>> 0 > this.high >>> 0 || other.high === this.high && other.low >>> 0 > this.low >>> 0 ? -1 : 1; } lessThanOrEqual(other) { return this.comp( /* validates */ other ) <= 0; } static fromValue(val) { if (typeof val === "number") return Long.fromNumber(val); return new Long(val.low, val.high); } }; var UZERO = new Long(0, 0); var TWO_PWR_16_DBL = 1 << 16; var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL; var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL; var MAX_UNSIGNED_VALUE = new Long(4294967295 | 0, 4294967295 | 0); // src/browser/sync/remote_query_set.ts var RemoteQuerySet = class { version; remoteQuerySet; queryPath; constructor(queryPath) { this.version = { querySet: 0, ts: Long.fromNumber(0), identity: 0 }; this.remoteQuerySet = /* @__PURE__ */ new Map(); this.queryPath = queryPath; } transition(transition) { const start = transition.startVersion; if (this.version.querySet !== start.querySet || this.version.ts.notEquals(start.ts) || this.version.identity !== start.identity) { throw new Error( `Invalid start version: ${start.ts.toString()}:${start.querySet}` ); } for (const modification of transition.modifications) { switch (modification.type) { case "QueryUpdated": { const queryPath = this.queryPath(modification.queryId); if (queryPath) { for (const line of modification.logLines) { logToConsole("info", "query", queryPath, line); } } const value = jsonToConvex(modification.value ?? null); this.remoteQuerySet.set(modification.queryId, { success: true, value, logLines: modification.logLines }); break; } case "QueryFailed": { const queryPath = this.queryPath(modification.queryId); if (queryPath) { for (const line of modification.logLines) { logToConsole("info", "query", queryPath, line); } } const { errorData } = modification; this.remoteQuerySet.set(modification.queryId, { success: false, errorMessage: modification.errorMessage, errorData: errorData !== void 0 ? jsonToConvex(errorData) : void 0, logLines: modification.logLines }); break; } case "QueryRemoved": { this.remoteQuerySet.delete(modification.queryId); break; } default: { const _ = modification; throw new Error(`Invalid modification ${modification.type}`); } } } this.version = transition.endVersion; } remoteQueryResults() { return this.remoteQuerySet; } timestamp() { return this.version.ts; } }; // src/browser/sync/protocol.ts function u64ToLong(encoded) { const integerBytes = base64_exports.toByteArray(encoded); return Long.fromBytesLE(Array.from(integerBytes)); } function longToU64(raw) { const integerBytes = new Uint8Array(raw.toBytesLE()); return base64_exports.fromByteArray(integerBytes); } function parseServerMessage(encoded) { switch (encoded.type) { case "FatalError": case "AuthError": case "ActionResponse": case "Ping": { return { ...encoded }; } case "MutationResponse": { if (encoded.success) { return { ...encoded, ts: u64ToLong(encoded.ts) }; } else { return { ...encoded }; } } case "Transition": { return { ...encoded, startVersion: { ...encoded.startVersion, ts: u64ToLong(encoded.startVersion.ts) }, endVersion: { ...encoded.endVersion, ts: u64ToLong(encoded.endVersion.ts) } }; } default: { const _exhaustivenessCheck = encoded; } } return void 0; } function encodeClientMessage(message) { switch (message.type) { case "Authenticate": case "ModifyQuerySet": case "Mutation": case "Action": case "Event": { return { ...message }; } case "Connect": { if (message.maxObservedTimestamp !== void 0) { return { ...message, maxObservedTimestamp: longToU64(message.maxObservedTimestamp) }; } else { return { ...message, maxObservedTimestamp: void 0 }; } } default: { const _exhaustivenessCheck = message; } } return void 0; } // src/browser/sync/web_socket_manager.ts var CLOSE_NORMAL = 1e3; var CLOSE_GOING_AWAY = 1001; var CLOSE_NO_STATUS = 1005; var CLOSE_NOT_FOUND = 4040; var WebSocketManager = class { socket; connectionCount; lastCloseReason; /** Upon HTTPS/WSS failure, the first jittered backoff duration, in ms. */ initialBackoff; /** We backoff exponentially, but we need to cap that--this is the jittered max. */ maxBackoff; /** How many times have we failed consecutively? */ retries; /** How long before lack of server response causes us to initiate a reconnect, * in ms */ serverInactivityThreshold; reconnectDueToServerInactivityTimeout; uri; onOpen; onResume; onMessage; webSocketConstructor; verbose; constructor(uri, callbacks, webSocketConstructor, verbose) { this.webSocketConstructor = webSocketConstructor; this.socket = { state: "disconnected" }; this.connectionCount = 0; this.lastCloseReason = "InitialConnect"; this.initialBackoff = 100; this.maxBackoff = 16e3;