convex
Version:
Client for the Convex Cloud
1,562 lines (1,539 loc) • 123 kB
JavaScript
"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;