UNPKG

convex

Version:

Client for the Convex Cloud

1,562 lines (1,542 loc) 151 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; } }); // src/react/index.ts var index_exports = {}; __export(index_exports, { AuthLoading: () => AuthLoading, Authenticated: () => Authenticated, ConvexProvider: () => ConvexProvider, ConvexProviderWithAuth: () => ConvexProviderWithAuth, ConvexReactClient: () => ConvexReactClient, Unauthenticated: () => Unauthenticated, insertAtBottomIfLoaded: () => insertAtBottomIfLoaded, insertAtPosition: () => insertAtPosition, insertAtTop: () => insertAtTop, optimisticallyUpdateValueInPaginatedQuery: () => optimisticallyUpdateValueInPaginatedQuery, resetPaginationId: () => resetPaginationId, useAction: () => useAction, useConvex: () => useConvex, useConvexAuth: () => useConvexAuth, useConvexConnectionState: () => useConvexConnectionState, 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, fromByteArrayUrlSafeNoPadding: () => fromByteArrayUrlSafeNoPadding, 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 arr2 = 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)]; arr2[curByte++] = tmp >> 16 & 255; arr2[curByte++] = tmp >> 8 & 255; arr2[curByte++] = tmp & 255; } if (placeHoldersLen === 2) { tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4; arr2[curByte++] = tmp & 255; } if (placeHoldersLen === 1) { tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2; arr2[curByte++] = tmp >> 8 & 255; arr2[curByte++] = tmp & 255; } return arr2; } 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(""); } function fromByteArrayUrlSafeNoPadding(uint8) { return fromByteArray(uint8).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); } // 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 { 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(n) { return Number.isNaN(n) || !Number.isFinite(n) || Object.is(n, -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/values/compare_utf8.ts function compareUTF8(a, b) { const aLength = a.length; const bLength = b.length; const length = Math.min(aLength, bLength); for (let i = 0; i < length; ) { const aCodePoint = a.codePointAt(i); const bCodePoint = b.codePointAt(i); if (aCodePoint !== bCodePoint) { if (aCodePoint < 128 && bCodePoint < 128) { return aCodePoint - bCodePoint; } const aLength2 = utf8Bytes(aCodePoint, aBytes); const bLength2 = utf8Bytes(bCodePoint, bBytes); return compareArrays(aBytes, aLength2, bBytes, bLength2); } i += utf16LengthForCodePoint(aCodePoint); } return aLength - bLength; } function compareArrays(a, aLength, b, bLength) { const length = Math.min(aLength, bLength); for (let i = 0; i < length; i++) { const aValue = a[i]; const bValue = b[i]; if (aValue !== bValue) { return aValue - bValue; } } return aLength - bLength; } function utf16LengthForCodePoint(aCodePoint) { return aCodePoint > 65535 ? 2 : 1; } var arr = () => Array.from({ length: 4 }, () => 0); var aBytes = arr(); var bBytes = arr(); function utf8Bytes(codePoint, bytes) { if (codePoint < 128) { bytes[0] = codePoint; return 1; } let count; let offset; if (codePoint <= 2047) { count = 1; offset = 192; } else if (codePoint <= 65535) { count = 2; offset = 224; } else if (codePoint <= 1114111) { count = 3; offset = 240; } else { throw new Error("Invalid code point"); } bytes[0] = (codePoint >> 6 * count) + offset; let i = 1; for (; count > 0; count--) { const temp = codePoint >> 6 * (count - 1); bytes[i++] = 128 | temp & 63; } return i; } // src/values/compare.ts function compareValues(k1, k2) { return compareAsTuples(makeComparable(k1), makeComparable(k2)); } function compareAsTuples(a, b) { if (a[0] === b[0]) { return compareSameTypeValues(a[1], b[1]); } else if (a[0] < b[0]) { return -1; } return 1; } function compareSameTypeValues(v1, v2) { if (v1 === void 0 || v1 === null) { return 0; } if (typeof v1 === "number") { if (typeof v2 !== "number") { throw new Error(`Unexpected type ${v2}`); } return compareNumbers(v1, v2); } if (typeof v1 === "string") { if (typeof v2 !== "string") { throw new Error(`Unexpected type ${v2}`); } return compareUTF8(v1, v2); } if (typeof v1 === "bigint" || typeof v1 === "boolean" || typeof v1 === "string") { return v1 < v2 ? -1 : v1 === v2 ? 0 : 1; } if (!Array.isArray(v1) || !Array.isArray(v2)) { throw new Error(`Unexpected type ${v1}`); } for (let i = 0; i < v1.length && i < v2.length; i++) { const cmp = compareAsTuples(v1[i], v2[i]); if (cmp !== 0) { return cmp; } } if (v1.length < v2.length) { return -1; } if (v1.length > v2.length) { return 1; } return 0; } function compareNumbers(v1, v2) { if (isNaN(v1) || isNaN(v2)) { const buffer1 = new ArrayBuffer(8); const buffer2 = new ArrayBuffer(8); new DataView(buffer1).setFloat64( 0, v1, /* little-endian */ true ); new DataView(buffer2).setFloat64( 0, v2, /* little-endian */ true ); const v1Bits = BigInt( new DataView(buffer1).getBigInt64( 0, /* little-endian */ true ) ); const v2Bits = BigInt( new DataView(buffer2).getBigInt64( 0, /* little-endian */ true ) ); const v1Sign = (v1Bits & 0x8000000000000000n) !== 0n; const v2Sign = (v2Bits & 0x8000000000000000n) !== 0n; if (isNaN(v1) !== isNaN(v2)) { if (isNaN(v1)) { return v1Sign ? -1 : 1; } return v2Sign ? 1 : -1; } if (v1Sign !== v2Sign) { return v1Sign ? -1 : 1; } return v1Bits < v2Bits ? -1 : v1Bits === v2Bits ? 0 : 1; } if (Object.is(v1, v2)) { return 0; } if (Object.is(v1, -0)) { return Object.is(v2, 0) ? -1 : -Math.sign(v2); } if (Object.is(v2, -0)) { return Object.is(v1, 0) ? 1 : Math.sign(v1); } return v1 < v2 ? -1 : 1; } function makeComparable(v) { if (v === void 0) { return [0, void 0]; } if (v === null) { return [1, null]; } if (typeof v === "bigint") { return [2, v]; } if (typeof v === "number") { return [3, v]; } if (typeof v === "boolean") { return [4, v]; } if (typeof v === "string") { return [5, v]; } if (v instanceof ArrayBuffer) { return [6, Array.from(new Uint8Array(v)).map(makeComparable)]; } if (Array.isArray(v)) { return [7, v.map(makeComparable)]; } const keys = Object.keys(v).sort(); const pojo = keys.map((k) => [k, v[k]]); return [8, pojo.map(makeComparable)]; } // src/react/use_queries.ts var import_react3 = __toESM(require_react(), 1); // src/index.ts var version = "1.26.2"; // 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 "?"; } } var DefaultLogger = class { _onLogLineFuncs; _verbose; constructor(options) { this._onLogLineFuncs = {}; this._verbose = options.verbose; } addLogLineListener(func) { let id = Math.random().toString(36).substring(2, 15); for (let i = 0; i < 10; i++) { if (this._onLogLineFuncs[id] === void 0) { break; } id = Math.random().toString(36).substring(2, 15); } this._onLogLineFuncs[id] = func; return () => { delete this._onLogLineFuncs[id]; }; } logVerbose(...args) { if (this._verbose) { for (const func of Object.values(this._onLogLineFuncs)) { func("debug", `${(/* @__PURE__ */ new Date()).toISOString()}`, ...args); } } } log(...args) { for (const func of Object.values(this._onLogLineFuncs)) { func("info", ...args); } } warn(...args) { for (const func of Object.values(this._onLogLineFuncs)) { func("warn", ...args); } } error(...args) { for (const func of Object.values(this._onLogLineFuncs)) { func("error", ...args); } } }; function instantiateDefaultLogger(options) { const logger = new DefaultLogger(options); logger.addLogLineListener((level, ...args) => { switch (level) { case "debug": console.debug(...args); break; case "info": console.log(...args); break; case "warn": console.warn(...args); break; case "error": console.error(...args); break; default: { level; console.log(...args); } } }); return logger; } function instantiateNoopLogger(options) { return new DefaultLogger(options); } function logForFunction(logger, 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) { logger.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); logger.log(`%c[CONVEX ${prefix}(${udfPath})] [${level}]`, INFO_COLOR, args); } else { logger.error(`[CONVEX ${prefix}(${udfPath})] ${message}`); } } function logFatalError(logger, message) { const errorMessage = `[CONVEX FATAL ERROR] ${message}`; logger.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, componentPath }; 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: { 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, componentPath: localQuery.componentPath }; 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 { constructor(logger, markConnectionStateDirty) { this.logger = logger; this.markConnectionStateDirty = markConnectionStateDirty; this.inflightRequests = /* @__PURE__ */ new Map(); this.requestsOlderThanRestart = /* @__PURE__ */ new Set(); } inflightRequests; requestsOlderThanRestart; inflightMutationsCount = 0; inflightActionsCount = 0; 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 } }); if (message.type === "Mutation") { this.inflightMutationsCount++; } else if (message.type === "Action") { this.inflightActionsCount++; } }); this.markConnectionStateDirty(); 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) { logForFunction(this.logger, "info", udfType, udfPath, line); } const status = requestInfo.status; let result; let onResolve; if (response.success) { result = { success: true, logLines: response.logLines, value: jsonToConvex(response.result) }; onResolve = () => status.onResult(result); } else { const errorMessage = response.result; const { errorData } = response; logForFunction(this.logger, "error", udfType, udfPath, errorMessage); result = { success: false, errorMessage, errorData: errorData !== void 0 ? jsonToConvex(errorData) : void 0, logLines: response.logLines }; onResolve = () => status.onResult(result); } if (response.type === "ActionResponse" || !response.success) { onResolve(); this.inflightRequests.delete(response.requestId); this.requestsOlderThanRestart.delete(response.requestId); if (requestInfo.message.type === "Action") { this.inflightActionsCount--; } else if (requestInfo.message.type === "Mutation") { this.inflightMutationsCount--; } this.markConnectionStateDirty(); return { requestId: response.requestId, result }; } requestInfo.status = { status: "Completed", result, ts: response.ts, onResolve }; return null; } // Remove and returns completed requests. removeCompleted(ts) { const completeRequests = /* @__PURE__ */ new Map(); for (const [requestId, requestInfo] of this.inflightRequests.entries()) { const status = requestInfo.status; if (status.status === "Completed" && status.ts.lessThanOrEqual(ts)) { status.onResolve(); completeRequests.set(requestId, status.result); if (requestInfo.message.type === "Mutation") { this.inflightMutationsCount--; } else if (requestInfo.message.type === "Action") { this.inflightActionsCount--; } this.inflightRequests.delete(requestId); this.requestsOlderThanRestart.delete(requestId); } } if (completeRequests.size > 0) { this.markConnectionStateDirty(); } 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 if (value.message.type === "Action") { this.inflightRequests.delete(requestId); this.requestsOlderThanRestart.delete(requestId); this.inflightActionsCount--; 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: [] }); } } this.markConnectionStateDirty(); 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); } /** * @returns The number of mutations currently in flight. */ inflightMutations() { return this.inflightMutationsCount; } /** * @returns The number of actions currently in flight. */ inflightActions() { return this.inflightActionsCount; } }; // src/server/functionName.ts var functionName = Symbol.for("functionName"); // src/server/components/paths.ts var toReferencePath = Symbol.for("toReferencePath"); function extractReferencePath(reference) { return reference[toReferencePath] ?? null; } function isFunctionHandle(s) { return s.startsWith("function://"); } 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 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 _OptimisticLocalStoreImpl { // 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 = []; } /** * Apply all optimistic updates on top of server query results */ 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; } /** * @internal */ rawQueryResult(queryToken) { return this.queryResults.get(queryToken); } 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)