UNPKG

convex

Version:

Client for the Convex Cloud

1,506 lines (1,486 loc) 55.7 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( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // ../common/temp/node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js var require_base64_js = __commonJS({ "../common/temp/node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js"(exports) { "use strict"; exports.byteLength = byteLength; exports.toByteArray = toByteArray3; exports.fromByteArray = fromByteArray2; var lookup = []; var revLookup = []; var Arr = typeof Uint8Array !== "undefined" ? Uint8Array : Array; 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 len2 = b64.length; if (len2 % 4 > 0) { throw new Error("Invalid string. Length must be a multiple of 4"); } var validLen = b64.indexOf("="); if (validLen === -1) validLen = len2; var placeHoldersLen = validLen === len2 ? 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 toByteArray3(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 len2 = placeHoldersLen > 0 ? validLen - 4 : validLen; var i2; for (i2 = 0; i2 < len2; i2 += 4) { tmp = revLookup[b64.charCodeAt(i2)] << 18 | revLookup[b64.charCodeAt(i2 + 1)] << 12 | revLookup[b64.charCodeAt(i2 + 2)] << 6 | revLookup[b64.charCodeAt(i2 + 3)]; arr[curByte++] = tmp >> 16 & 255; arr[curByte++] = tmp >> 8 & 255; arr[curByte++] = tmp & 255; } if (placeHoldersLen === 2) { tmp = revLookup[b64.charCodeAt(i2)] << 2 | revLookup[b64.charCodeAt(i2 + 1)] >> 4; arr[curByte++] = tmp & 255; } if (placeHoldersLen === 1) { tmp = revLookup[b64.charCodeAt(i2)] << 10 | revLookup[b64.charCodeAt(i2 + 1)] << 4 | revLookup[b64.charCodeAt(i2 + 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 i2 = start; i2 < end; i2 += 3) { tmp = (uint8[i2] << 16 & 16711680) + (uint8[i2 + 1] << 8 & 65280) + (uint8[i2 + 2] & 255); output.push(tripletToBase64(tmp)); } return output.join(""); } function fromByteArray2(uint8) { var tmp; var len2 = uint8.length; var extraBytes = len2 % 3; var parts = []; var maxChunkLength = 16383; for (var i2 = 0, len22 = len2 - extraBytes; i2 < len22; i2 += maxChunkLength) { parts.push(encodeChunk(uint8, i2, i2 + maxChunkLength > len22 ? len22 : i2 + maxChunkLength)); } if (extraBytes === 1) { tmp = uint8[len2 - 1]; parts.push( lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "==" ); } else if (extraBytes === 2) { tmp = (uint8[len2 - 2] << 8) + uint8[len2 - 1]; parts.push( lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "=" ); } return parts.join(""); } } }); // ../common/temp/node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/browser.js var require_browser = __commonJS({ "../common/temp/node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/browser.js"(exports, module) { "use strict"; var getGlobal = function() { if (typeof self !== "undefined") { return self; } if (typeof window !== "undefined") { return window; } if (typeof global !== "undefined") { return global; } throw new Error("unable to locate global object"); }; var global = getGlobal(); module.exports = exports = global.fetch; if (global.fetch) { exports.default = global.fetch.bind(global); } exports.Headers = global.Headers; exports.Request = global.Request; exports.Response = global.Response; } }); // src/browser/index.ts var browser_exports = {}; __export(browser_exports, { ConvexHttpClient: () => ConvexHttpClient, InternalConvexClient: () => InternalConvexClient }); // src/index.ts var version = "0.6.0"; // src/values/values.ts var Base64 = __toESM(require_base64_js()); 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"); var GenericId = class { constructor(tableName, id) { this.tableName = tableName; this.id = id; } equals(other) { if (other instanceof GenericId) { return this.tableName === other.tableName && this.id === other.id; } return false; } static fromJSON(obj) { if (typeof obj.$id !== "string") { throw new Error( `Object ${JSON.stringify(obj)} isn't a valid Id: $id isn't a string.` ); } const parts = obj.$id.split("|"); if (parts.length !== 2) { throw new Error( `Object ${JSON.stringify(obj)} isn't a valid Id: Wrong number of parts.` ); } return new GenericId(parts[0], parts[1]); } toJSON() { const idString = `${this.tableName}|${this.id}`; return { $id: idString }; } toString() { return this.id; } inspect() { return `Id('${this.tableName}', '${this.id}')`; } }; 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 Base64.fromByteArray(bytes); } function slowBase64ToBigInt(encoded) { const integerBytes = Base64.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 Base64.fromByteArray(new Uint8Array(buffer)); } function modernBase64ToBigInt(encoded) { const integerBytes = Base64.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 = 64; var ALL_UNDERSCORES = /^_+$/; var IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/; function validateObjectField(k) { if (k.length === 0) { throw new Error("Empty field names are disallowed."); } 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.`); } if (ALL_UNDERSCORES.test(k)) { throw new Error(`Field name ${k} can't exclusively be underscores.`); } if (!IDENTIFIER_REGEX.test(k)) { throw new Error( `Field name ${k} must only contain alphanumeric characters or underscores and can't start with a number.` ); } } function jsonToConvexInternal(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 (value instanceof Array) { return value.map(jsonToConvexInternal); } 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 === "$id" || key === "$weakRef" || key === "$strongRef") { return GenericId.fromJSON(value); } if (key === "$bytes") { if (typeof value.$bytes !== "string") { throw new Error(`Malformed $bytes field on ${value}`); } return Base64.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 = Base64.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") { if (!(value.$set instanceof Array)) { throw new Error(`Malformed $set field on ${value}`); } return new Set(value.$set.map(jsonToConvexInternal)); } if (key === "$map") { if (!(value.$map instanceof Array)) { throw new Error(`Malformed $map field on ${value}`); } const map = /* @__PURE__ */ new Map(); for (const pair of value.$map) { if (!(pair instanceof Array) || pair.length !== 2) { throw new Error(`Malformed pair in $map ${value}`); } const k = jsonToConvexInternal(pair[0]); const v = jsonToConvexInternal(pair[1]); map.set(k, v); } return map; } } const out = {}; for (const [k, v] of Object.entries(value)) { validateObjectField(k); out[k] = jsonToConvexInternal(v); } return out; } function jsonToConvex(value) { return jsonToConvexInternal(value); } function stringifyValueForError(value) { return JSON.stringify(value, (_key, value2) => { if (value2 === void 0) { return "undefined"; } return value2; }); } function convexToJsonInternal(value, originalValue, context) { if (value === void 0) { throw new Error( `undefined is not a valid Convex value (present at path ${context} in original object ${stringifyValueForError( originalValue )}). To learn about Convex's supported types, see https://docs.convex.dev/using/types.` ); } if (value === null) { return value; } if (value instanceof GenericId) { return value.toJSON(); } 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: Base64.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: Base64.fromByteArray(new Uint8Array(value)) }; } if (value instanceof Array) { return value.map( (value2, i) => convexToJsonInternal(value2, originalValue, context + `[${i}]`) ); } if (value instanceof Set) { return { $set: [...value].map( (value2, i) => convexToJsonInternal(value2, originalValue, context + `.keys()[${i}]`) ) }; } if (value instanceof Map) { return { $map: [...value].map(([k, v], i) => { const jsonKey = convexToJsonInternal( k, originalValue, context + `.keys()[${i}]` ); const jsonValue = convexToJsonInternal( v, originalValue, context + `.values()[${i}]` ); return [jsonKey, jsonValue]; }) }; } if (typeof value !== "object") { throw new Error( `${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.` ); } const prototype = Object.getPrototypeOf(value); if (prototype !== null && prototype !== Object.prototype) { throw new Error( `${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.` ); } const out = {}; for (const [k, v] of Object.entries(value)) { validateObjectField(k); out[k] = convexToJsonInternal(v, originalValue, context + `.${k}`); } return out; } function convexToJson(value) { return convexToJsonInternal(value, value, ""); } // src/browser/sync/udf_path_utils.ts function canonicalizeUdfPath(udfPath) { const pieces = udfPath.split(":"); let moduleName; let functionName; if (pieces.length == 1) { moduleName = pieces[0]; functionName = "default"; } else { moduleName = pieces.slice(0, pieces.length - 1).join(":"); functionName = pieces[pieces.length - 1]; } if (!moduleName.endsWith(".js")) { moduleName = `${moduleName}.js`; } return `${moduleName}:${functionName}`; } function serializePathAndArgs(udfPath, args) { return JSON.stringify({ udfPath: canonicalizeUdfPath(udfPath), args: convexToJson(args) }); } // src/browser/sync/local_state.ts var LocalSyncState = class { constructor() { this.nextQueryId = 0; this.querySetVersion = 0; this.identityVersion = 0; this.querySet = /* @__PURE__ */ new Map(); this.queryIdToToken = /* @__PURE__ */ new Map(); } subscribe(udfPath, args, journal) { 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; const add = { type: "Add", queryId, udfPath: canonicalizedUdfPath, args: args.map(convexToJson), journal }; const modification = { type: "ModifyQuerySet", baseVersion, newVersion, modifications: [add] }; return { queryToken, modification, unsubscribe: () => this.removeSubscriber(queryToken) }; } } saveQueryJournals(transition) { for (const modification of transition.modifications) { switch (modification.type) { case "QueryUpdated": case "QueryFailed": { 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": { break; } default: { const _ = modification; throw new Error(`Invalid modification ${modification}`); } } } } 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; } setAuth(value) { this.auth = { tokenType: "User", value }; const baseVersion = this.identityVersion++; return { type: "Authenticate", baseVersion, ...this.auth }; } setAdminAuth(value) { this.auth = { tokenType: "Admin", value }; const baseVersion = this.identityVersion++; return { type: "Authenticate", baseVersion, ...this.auth }; } clearAuth() { this.auth = void 0; const baseVersion = this.identityVersion++; return { type: "Authenticate", tokenType: "None", baseVersion }; } 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() { const modifications = []; for (const localQuery of this.querySet.values()) { const add = { type: "Add", queryId: localQuery.id, udfPath: localQuery.canonicalizedUdfPath, args: localQuery.args.map(convexToJson), journal: localQuery.journal }; modifications.push(add); } this.querySetVersion = 1; const querySet = { type: "ModifyQuerySet", baseVersion: 0, newVersion: 1, modifications }; if (!this.auth) { this.identityVersion = 0; return [querySet, void 0]; } const authenticate = { type: "Authenticate", baseVersion: 0, ...this.auth }; this.identityVersion = 1; return [querySet, authenticate]; } 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); const baseVersion = this.querySetVersion; const newVersion = ++this.querySetVersion; const remove = { type: "Remove", queryId: localQuery.id }; return { type: "ModifyQuerySet", baseVersion, newVersion, modifications: [remove] }; } } }; // 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"; } } function logToConsole(type, source, udfPath, message) { const prefix = prefix_for_source(source); if (type == "info") { console.log(`%c[CONVEX ${prefix}(${udfPath})] ${message}`, INFO_COLOR); } else { console.error(`[CONVEX ${prefix}(${udfPath})] ${message}`); } } function logFatalError(message) { const errorMessage = `[CONVEX FATAL ERROR] ${message}`; console.error(errorMessage); return new Error(errorMessage); } function createError(source, udfPath, message) { const prefix = prefix_for_source(source); return new Error(`[CONVEX ${prefix}(${udfPath})] ${message}`); } // src/browser/sync/mutation_manager.ts var MutationManager = class { constructor() { this.inflightMutations = /* @__PURE__ */ new Map(); } request(udfPath, args, mutationId) { const message = { type: "Mutation", mutationId, udfPath, args: convexToJson(args) }; const result = new Promise((resolve, reject) => { this.inflightMutations.set(mutationId, { message, status: { status: "Requested", onResult: resolve, onFailure: reject } }); }); return { message, result }; } onResponse(response) { const mutationInfo = this.inflightMutations.get(response.mutationId); if (mutationInfo === void 0) { return null; } if (mutationInfo.status.status === "Requested") { const udfPath = mutationInfo.message.udfPath; for (const line of response.logLines) { logToConsole("info", "mutation", udfPath, line); } if (response.success) { const status = mutationInfo.status; const onResult = () => status.onResult(jsonToConvex(response.result)); mutationInfo.status = { status: "Committed", ts: response.ts, onResult }; return null; } else { this.inflightMutations.delete(response.mutationId); logToConsole("error", "mutation", udfPath, response.result); mutationInfo.status.onFailure( createError("mutation", udfPath, response.result) ); return response.mutationId; } } return null; } removeCompletedMutations(ts) { const completeMutations = /* @__PURE__ */ new Set(); for (const [mutationId, mutationInfo] of this.inflightMutations.entries()) { const status = mutationInfo.status; if (status.status == "Committed" && status.ts.lessThanOrEqual(ts)) { status.onResult(); completeMutations.add(mutationId); this.inflightMutations.delete(mutationId); } } return completeMutations; } hasUncommittedMutations() { for (const mutationInfo of this.inflightMutations.values()) { if (mutationInfo.status.status === "Requested") { return true; } } return false; } restart() { const allMessages = []; for (const value of this.inflightMutations.values()) { allMessages.push(value.message); } return allMessages; } hasInflightMutation() { return this.inflightMutations.size > 0; } }; // src/browser/sync/optimistic_updates_impl.ts var OptimisticLocalStoreImpl = class { constructor(queryResults) { this.queryResults = queryResults; this.modifiedQueries = []; } getQuery(name, args) { const query = this.queryResults.get(serializePathAndArgs(name, args)); if (query === void 0) { return void 0; } return OptimisticLocalStoreImpl.queryValue(query.result); } getAllQueries(name) { const queriesWithName = []; for (const query of this.queryResults.values()) { if (query.udfPath === canonicalizeUdfPath(name)) { queriesWithName.push({ args: query.args, value: OptimisticLocalStoreImpl.queryValue(query.result) }); } } return queriesWithName; } setQuery(name, args, value) { const queryToken = serializePathAndArgs(name, args); let result; if (value === void 0) { result = void 0; } else { result = { success: true, value }; } const query = { udfPath: name, args, 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 { 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 { throw createError("query", query.udfPath, result.errorMessage); } } }; // src/browser/long.ts var Long = class { static isLong(obj) { return (obj && obj.__isUnsignedLong__) === true; } constructor(low, high) { this.low = low | 0; this.high = high | 0; this.__isUnsignedLong__ = true; } 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 ); } 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); } 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(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 { 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}:${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 }); break; } case "QueryFailed": { const queryPath = this.queryPath(modification.queryId); if (queryPath) { for (const line of modification.logLines) { logToConsole("info", "query", queryPath, line); } } this.remoteQuerySet.set(modification.queryId, { success: false, errorMessage: modification.errorMessage }); break; } case "QueryRemoved": { this.remoteQuerySet.delete(modification.queryId); break; } default: { const _ = modification; throw new Error(`Invalid modification ${modification}`); } } } this.version = transition.endVersion; } remoteQueryResults() { return this.remoteQuerySet; } timestamp() { return this.version.ts; } }; // src/browser/sync/protocol.ts var Base642 = __toESM(require_base64_js()); function u64ToLong(encoded) { const integerBytes = Base642.toByteArray(encoded); return Long.fromBytesLE(Array.from(integerBytes)); } function parseServerMessage(encoded) { switch (encoded.type) { case "FatalError": 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; } // src/browser/sync/web_socket_manager.ts var CLOSE_NORMAL = 1e3; var CLOSE_NO_STATUS = 1005; function promisePair() { let resolvePromise; const promise = new Promise((resolve) => { resolvePromise = resolve; }); return { promise, resolve: resolvePromise }; } var WebSocketManager = class { constructor(uri, onOpen, onMessage, webSocketConstructor) { this.webSocketConstructor = webSocketConstructor; this.socket = { state: "disconnected" }; this.connectionCount = 0; this.lastCloseReason = "InitialConnect"; this.initialBackoff = 100; this.maxBackoff = 16e3; this.retries = 0; this.serverInactivityThreshold = 3e4; this.reconnectDueToServerInactivityTimeout = null; this.uri = uri; this.onOpen = onOpen; this.onMessage = onMessage; void this.connect(); } async connect() { if (this.socket.state === "closing" || this.socket.state === "stopping" || this.socket.state === "stopped") { return; } if (this.socket.state !== "disconnected") { throw new Error("Didn't start connection from disconnected state"); } const ws = new this.webSocketConstructor(this.uri); this.socket = { state: "connecting", ws }; ws.onopen = () => { if (this.socket.state !== "connecting") { throw new Error("onopen called with socket not in connecting state"); } this.socket = { state: "ready", ws }; this.onServerActivity(); this.onOpen({ connectionCount: this.connectionCount, lastCloseReason: this.lastCloseReason }); this.connectionCount += 1; this.lastCloseReason = null; }; ws.onerror = (error) => { const message = error.message; console.log(`WebSocket error: ${message}`); this.closeAndReconnect("WebSocketError"); }; ws.onmessage = (message) => { this.retries = 0; this.onServerActivity(); const serverMessage = parseServerMessage(JSON.parse(message.data)); this.onMessage(serverMessage); }; ws.onclose = (event) => { if (this.lastCloseReason === null) { this.lastCloseReason = event.reason ?? "OnCloseInvoked"; } if (event.code !== CLOSE_NORMAL && event.code !== CLOSE_NO_STATUS) { let msg = `WebSocket closed unexpectedly with code ${event.code}`; if (event.reason) { msg += `: ${event.reason}`; } console.error(msg); } if (this.socket.state === "stopping") { this.socket.promisePair.resolve(null); this.socket = { state: "stopped" }; return; } this.socket = { state: "disconnected" }; const backoff = this.nextBackoff(); console.log(`Attempting reconnect in ${backoff}ms`); setTimeout(() => this.connect(), backoff); }; } socketState() { return this.socket.state; } sendMessage(message) { if (this.socket.state === "ready") { const request = JSON.stringify(message); try { this.socket.ws.send(request); } catch (error) { console.log( `Failed to send message on WebSocket, reconnecting: ${error}` ); this.closeAndReconnect("FailedToSendMessage"); } } } onServerActivity() { if (this.reconnectDueToServerInactivityTimeout !== null) { clearTimeout(this.reconnectDueToServerInactivityTimeout); this.reconnectDueToServerInactivityTimeout = null; } this.reconnectDueToServerInactivityTimeout = setTimeout(() => { this.closeAndReconnect("InactiveServer"); }, this.serverInactivityThreshold); } closeAndReconnect(closeReason) { switch (this.socket.state) { case "disconnected": case "closing": case "stopping": case "stopped": return; case "connecting": case "ready": this.lastCloseReason = closeReason; this.socket.ws.close(); this.socket = { state: "closing" }; return; default: { const _ = this.socket; } } } async stop() { switch (this.socket.state) { case "stopped": return; case "connecting": case "ready": this.socket.ws.close(); this.socket = { state: "stopping", promisePair: promisePair() }; await this.socket.promisePair.promise; return; case "closing": this.socket = { state: "stopping", promisePair: promisePair() }; await this.socket.promisePair.promise; return; case "disconnected": this.socket = { state: "stopped" }; return; case "stopping": await this.socket.promisePair.promise; return; default: { const _ = this.socket; } } } nextBackoff() { const baseBackoff = this.initialBackoff * Math.pow(2, this.retries); this.retries += 1; const actualBackoff = Math.min(baseBackoff, this.maxBackoff); const jitter = actualBackoff * (Math.random() - 0.5); return actualBackoff + jitter; } }; // ../common/temp/node_modules/.pnpm/uuid@9.0.0/node_modules/uuid/dist/esm-browser/rng.js var getRandomValues; var rnds8 = new Uint8Array(16); function rng() { if (!getRandomValues) { getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto); if (!getRandomValues) { throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported"); } } return getRandomValues(rnds8); } // ../common/temp/node_modules/.pnpm/uuid@9.0.0/node_modules/uuid/dist/esm-browser/stringify.js var byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 256).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } // ../common/temp/node_modules/.pnpm/uuid@9.0.0/node_modules/uuid/dist/esm-browser/native.js var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto); var native_default = { randomUUID }; // ../common/temp/node_modules/.pnpm/uuid@9.0.0/node_modules/uuid/dist/esm-browser/v4.js function v4(options, buf, offset) { if (native_default.randomUUID && !buf && !options) { return native_default.randomUUID(); } options = options || {}; const rnds = options.random || (options.rng || rng)(); rnds[6] = rnds[6] & 15 | 64; rnds[8] = rnds[8] & 63 | 128; if (buf) { offset = offset || 0; for (let i = 0; i < 16; ++i) { buf[offset + i] = rnds[i]; } return buf; } return unsafeStringify(rnds); } var v4_default = v4; // src/browser/sync/action_manager.ts var ActionManager = class { constructor() { this.inflightActions = /* @__PURE__ */ new Map(); } request(udfPath, args, actionId) { const message = { type: "Action", actionId, udfPath, args: convexToJson(args) }; const result = new Promise((resolve, reject) => { this.inflightActions.set(actionId, { message, onResult: resolve, onFailure: reject }); }); return { message, result }; } onResponse(response) { const actionInfo = this.inflightActions.get(response.actionId); if (actionInfo === void 0) { return; } this.inflightActions.delete(response.actionId); const udfPath = actionInfo.message.udfPath; for (const line of response.logLines) { logToConsole("info", "action", udfPath, line); } if (response.success) { actionInfo.onResult(jsonToConvex(response.result)); } else { logToConsole("error", "action", udfPath, response.result); actionInfo.onFailure(createError("action", udfPath, response.result)); } } hasInflightActions() { return this.inflightActions.size > 0; } restart() { for (const [actionId, actionInfo] of this.inflightActions) { this.inflightActions.delete(actionId); const udfPath = actionInfo.message.udfPath; actionInfo.onFailure(createError("action", udfPath, "Transient error")); } } }; // src/browser/sync/client.ts var DEFAULT_OPTIONS = { unsavedChangesWarning: true }; var InternalConvexClient = class { constructor(clientConfig, onTransition, options) { options = { ...DEFAULT_OPTIONS, ...options }; let webSocketConstructor = options.webSocketConstructor; if (!webSocketConstructor && typeof WebSocket === "undefined") { throw new Error( "No WebSocket global variable defined! To use Convex in an environment without WebSocket try the HTTP client: https://docs.convex.dev/api/classes/browser.ConvexHttpClient" ); } webSocketConstructor = webSocketConstructor || WebSocket; const address = clientConfig.address; const i = address.search("://"); if (i == -1) { throw new Error("Provided address was not an absolute URL."); } const origin = address.substring(i + 3); const protocol = address.substring(0, i); let wsProtocol; if (protocol === "http") { wsProtocol = "ws"; } else if (protocol === "https") { wsProtocol = "wss"; } else { throw new Error(`Unknown parent protocol ${protocol}`); } const wsUri = `${wsProtocol}://${origin}/api/${version}/sync`; this.state = new LocalSyncState(); this.remoteQuerySet = new RemoteQuerySet( (queryId) => this.state.queryPath(queryId) ); this.mutationManager = new MutationManager(); this.actionManager = new ActionManager(); this.optimisticQueryResults = new OptimisticQueryResults(); this.onTransition = onTransition; this.nextMutationId = 0; this.nextActionId = 0; this.sessionId = v4_default(); const { unsavedChangesWarning } = options; if (typeof window === "undefined" && unsavedChangesWarning) { throw new Error( "unsavedChangesWarning enabled, but no window object found! Navigating away from the page could cause in-flight mutations to be dropped. Pass {unsavedChangesWarning: false} in Convex client options to disable this feature." ); } unsavedChangesWarning && window.addEventListener("beforeunload", (e) => { if (this.mutationManager.hasUncommittedMutations() || this.actionManager.hasInflightActions()) { e.preventDefault(); const confirmationMessage = "Are you sure you want to leave? Your changes may not be saved."; (e || window.event).returnValue = confirmationMessage; return confirmationMessage; } }); this.webSocketManager = new WebSocketManager( wsUri, (reconnectMetadata) => { this.webSocketManager.sendMessage({ ...reconnectMetadata, type: "Connect", sessionId: this.sessionId }); this.remoteQuerySet = new RemoteQuerySet( (queryId) => this.state.queryPath(queryId) ); const [querySetModification, authModification] = this.state.restart(); if (authModification) { this.webSocketManager.sendMessage(authModification); } this.webSocketManager.sendMessage(querySetModification); this.actionManager.restart(); for (const message of this.mutationManager.restart()) { this.webSocketManager.sendMessage(message); } }, (serverMessage) => { if (serverMessage.type == "Transition") { this.remoteQuerySet.transition(serverMessage); this.state.saveQueryJournals(serverMessage); const completedMutations = this.mutationManager.removeCompletedMutations( this.remoteQuerySet.timestamp() ); this.notifyOnQueryResultChanges(completedMutations); } else if (serverMessage.type == "MutationResponse") { const completedMutationId = this.mutationManager.onResponse(serverMessage); if (completedMutationId) { this.notifyOnQueryResultChanges(/* @__PURE__ */ new Set([completedMutationId])); } } else if (serverMessage.type == "ActionResponse") { this.actionManager.onResponse(serverMessage); } else if (serverMessage.type == "FatalError") { const error = logFatalError(serverMessage.error); void this.webSocketManager.stop(); throw error; } }, webSocketConstructor ); } notifyOnQueryResultChanges(completedMutations) { const remoteQueryResults = this.remoteQuerySet.remoteQueryResults(); const queryTokenToValue = /* @__PURE__ */ new Map(); for (const [queryId, result] of remoteQueryResults) { const queryToken = this.state.queryToken(queryId); if (queryToken !== null) { const query = { result, udfPath: this.state.queryPath(queryId), args: this.state.queryArgs(queryId) }; queryTokenToValue.set(queryToken, query); } } this.onTransition( this.optimisticQueryResults.ingestQueryResultsFromServer( queryTokenToValue, completedMutations ) ); } setAuth(value) { const message = this.state.setAuth(value); this.webSocketManager.sendMessage(message); } setAdminAuth(value) { const message = this.state.setAdminAuth(value); this.webSocketManager.sendMessage(message); } clearAuth() { const message = this.state.clearAuth(); this.webSocketManager.sendMessage(message); } subscribe(name, args, journal) { if (!Array.isArray(args)) { throw new Error( `Query arguments to \`InternalConvexClient.subcribe\` must be an array. Received ${args}.` ); } const { modification, queryToken, unsubscribe } = this.state.subscribe( name, args, journal ); if (modification !== null) { this.webSocketManager.sendMessage(modification); } return { queryToken, unsubscribe: () => { const modification2 = unsubscribe();