UNPKG

convex

Version:

Client for the Convex Cloud

422 lines (421 loc) 14.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; 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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var http_client_exports = {}; __export(http_client_exports, { ConvexHttpClient: () => ConvexHttpClient, STATUS_CODE_BAD_REQUEST: () => STATUS_CODE_BAD_REQUEST, STATUS_CODE_OK: () => STATUS_CODE_OK, STATUS_CODE_UDF_FAILED: () => STATUS_CODE_UDF_FAILED, setFetch: () => setFetch }); module.exports = __toCommonJS(http_client_exports); var import_api = require("../server/api.js"); var import_common = require("../common/index.js"); var import__ = require("../index.js"); var import_values = require("../values/index.js"); var import_logging = require("./logging.js"); const STATUS_CODE_OK = 200; const STATUS_CODE_BAD_REQUEST = 400; const STATUS_CODE_UDF_FAILED = 560; let specifiedFetch = void 0; function setFetch(f) { specifiedFetch = f; } class ConvexHttpClient { /** * Create a new {@link ConvexHttpClient}. * * @param address - The url of your Convex deployment, often provided * by an environment variable. E.g. `https://small-mouse-123.convex.cloud`. * @param skipConvexDeploymentUrlCheck - Skip validating that the Convex deployment URL looks like * `https://happy-animal-123.convex.cloud` or localhost. This can be useful if running a self-hosted * Convex backend that uses a different URL. */ constructor(address, skipConvexDeploymentUrlCheck) { __publicField(this, "address"); __publicField(this, "auth"); __publicField(this, "adminAuth"); __publicField(this, "encodedTsPromise"); __publicField(this, "debug"); __publicField(this, "fetchOptions"); if (skipConvexDeploymentUrlCheck !== true) { (0, import_common.validateDeploymentUrl)(address); } this.address = address; this.debug = true; } /** * Obtain the {@link ConvexHttpClient}'s URL to its backend. * @deprecated Use url, which returns the url without /api at the end. * * @returns The URL to the Convex backend, including the client's API version. */ backendUrl() { return `${this.address}/api`; } /** * Return the address for this client, useful for creating a new client. * * Not guaranteed to match the address with which this client was constructed: * it may be canonicalized. */ get url() { return this.address; } /** * Set the authentication token to be used for subsequent queries and mutations. * * Should be called whenever the token changes (i.e. due to expiration and refresh). * * @param value - JWT-encoded OpenID Connect identity token. */ setAuth(value) { this.clearAuth(); this.auth = value; } /** * @internal */ setAdminAuth(token, actingAsIdentity) { this.clearAuth(); if (actingAsIdentity !== void 0) { const bytes = new TextEncoder().encode(JSON.stringify(actingAsIdentity)); const actingAsIdentityEncoded = btoa(String.fromCodePoint(...bytes)); this.adminAuth = `${token}:${actingAsIdentityEncoded}`; } else { this.adminAuth = token; } } /** * Clear the current authentication token if set. */ clearAuth() { this.auth = void 0; this.adminAuth = void 0; } /** * Sets whether the result log lines should be printed on the console or not. * * @internal */ setDebug(debug) { this.debug = debug; } /** * Used to customize the fetch behavior in some runtimes. * * @internal */ setFetchOptions(fetchOptions) { this.fetchOptions = fetchOptions; } /** * This API is experimental: it may change or disappear. * * Execute a Convex query function at the same timestamp as every other * consistent query execution run by this HTTP client. * * This doesn't make sense for long-lived ConvexHttpClients as Convex * backends can read a limited amount into the past: beyond 30 seconds * in the past may not be available. * * Create a new client to use a consistent time. * * @param name - The name of the query. * @param args - The arguments object for the query. If this is omitted, * the arguments will be `{}`. * @returns A promise of the query's result. * * @deprecated This API is experimental: it may change or disappear. */ async consistentQuery(query, ...args) { const queryArgs = (0, import_common.parseArgs)(args[0]); const timestampPromise = this.getTimestamp(); return await this.queryInner(query, queryArgs, { timestampPromise }); } async getTimestamp() { if (this.encodedTsPromise) { return this.encodedTsPromise; } return this.encodedTsPromise = this.getTimestampInner(); } async getTimestampInner() { const localFetch = specifiedFetch || fetch; const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${import__.version}` }; const response = await localFetch(`${this.address}/api/query_ts`, { ...this.fetchOptions, method: "POST", headers, credentials: "include" }); if (!response.ok) { throw new Error(await response.text()); } const { ts } = await response.json(); return ts; } /** * Execute a Convex query function. * * @param name - The name of the query. * @param args - The arguments object for the query. If this is omitted, * the arguments will be `{}`. * @returns A promise of the query's result. */ async query(query, ...args) { const queryArgs = (0, import_common.parseArgs)(args[0]); return await this.queryInner(query, queryArgs, {}); } async queryInner(query, queryArgs, options) { const name = (0, import_api.getFunctionName)(query); const args = [(0, import_values.convexToJson)(queryArgs)]; const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${import__.version}` }; if (this.adminAuth) { headers["Authorization"] = `Convex ${this.adminAuth}`; } else if (this.auth) { headers["Authorization"] = `Bearer ${this.auth}`; } const localFetch = specifiedFetch || fetch; const timestamp = options.timestampPromise ? await options.timestampPromise : void 0; const body = JSON.stringify({ path: name, format: "convex_encoded_json", args, ...timestamp ? { ts: timestamp } : {} }); const endpoint = timestamp ? `${this.address}/api/query_at_ts` : `${this.address}/api/query`; const response = await localFetch(endpoint, { ...this.fetchOptions, body, method: "POST", headers, credentials: "include" }); if (!response.ok && response.status !== STATUS_CODE_UDF_FAILED) { throw new Error(await response.text()); } const respJSON = await response.json(); if (this.debug) { for (const line of respJSON.logLines ?? []) { (0, import_logging.logToConsole)("info", "query", name, line); } } switch (respJSON.status) { case "success": return (0, import_values.jsonToConvex)(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new import_values.ConvexError(respJSON.errorMessage) ); } throw new Error(respJSON.errorMessage); default: throw new Error(`Invalid response: ${JSON.stringify(respJSON)}`); } } /** * Execute a Convex mutation function. * * @param name - The name of the mutation. * @param args - The arguments object for the mutation. If this is omitted, * the arguments will be `{}`. * @returns A promise of the mutation's result. */ async mutation(mutation, ...args) { const mutationArgs = (0, import_common.parseArgs)(args[0]); const name = (0, import_api.getFunctionName)(mutation); const body = JSON.stringify({ path: name, format: "convex_encoded_json", args: [(0, import_values.convexToJson)(mutationArgs)] }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${import__.version}` }; if (this.adminAuth) { headers["Authorization"] = `Convex ${this.adminAuth}`; } else if (this.auth) { headers["Authorization"] = `Bearer ${this.auth}`; } const localFetch = specifiedFetch || fetch; const response = await localFetch(`${this.address}/api/mutation`, { ...this.fetchOptions, body, method: "POST", headers, credentials: "include" }); if (!response.ok && response.status !== STATUS_CODE_UDF_FAILED) { throw new Error(await response.text()); } const respJSON = await response.json(); if (this.debug) { for (const line of respJSON.logLines ?? []) { (0, import_logging.logToConsole)("info", "mutation", name, line); } } switch (respJSON.status) { case "success": return (0, import_values.jsonToConvex)(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new import_values.ConvexError(respJSON.errorMessage) ); } throw new Error(respJSON.errorMessage); default: throw new Error(`Invalid response: ${JSON.stringify(respJSON)}`); } } /** * Execute a Convex action function. * * @param name - The name of the action. * @param args - The arguments object for the action. If this is omitted, * the arguments will be `{}`. * @returns A promise of the action's result. */ async action(action, ...args) { const actionArgs = (0, import_common.parseArgs)(args[0]); const name = (0, import_api.getFunctionName)(action); const body = JSON.stringify({ path: name, format: "convex_encoded_json", args: [(0, import_values.convexToJson)(actionArgs)] }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${import__.version}` }; if (this.adminAuth) { headers["Authorization"] = `Convex ${this.adminAuth}`; } else if (this.auth) { headers["Authorization"] = `Bearer ${this.auth}`; } const localFetch = specifiedFetch || fetch; const response = await localFetch(`${this.address}/api/action`, { ...this.fetchOptions, body, method: "POST", headers, credentials: "include" }); if (!response.ok && response.status !== STATUS_CODE_UDF_FAILED) { throw new Error(await response.text()); } const respJSON = await response.json(); if (this.debug) { for (const line of respJSON.logLines ?? []) { (0, import_logging.logToConsole)("info", "action", name, line); } } switch (respJSON.status) { case "success": return (0, import_values.jsonToConvex)(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new import_values.ConvexError(respJSON.errorMessage) ); } throw new Error(respJSON.errorMessage); default: throw new Error(`Invalid response: ${JSON.stringify(respJSON)}`); } } /** * Execute a Convex function of an unknown type. * * @param name - The name of the function. * @param args - The arguments object for the function. If this is omitted, * the arguments will be `{}`. * @returns A promise of the function's result. * * @internal */ async function(anyFunction, componentPath, ...args) { const functionArgs = (0, import_common.parseArgs)(args[0]); const name = typeof anyFunction === "string" ? anyFunction : (0, import_api.getFunctionName)(anyFunction); const body = JSON.stringify({ componentPath, path: name, format: "convex_encoded_json", args: (0, import_values.convexToJson)(functionArgs) }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${import__.version}` }; if (this.adminAuth) { headers["Authorization"] = `Convex ${this.adminAuth}`; } else if (this.auth) { headers["Authorization"] = `Bearer ${this.auth}`; } const localFetch = specifiedFetch || fetch; const response = await localFetch(`${this.address}/api/function`, { ...this.fetchOptions, body, method: "POST", headers, credentials: "include" }); if (!response.ok && response.status !== STATUS_CODE_UDF_FAILED) { throw new Error(await response.text()); } const respJSON = await response.json(); if (this.debug) { for (const line of respJSON.logLines ?? []) { (0, import_logging.logToConsole)("info", "any", name, line); } } switch (respJSON.status) { case "success": return (0, import_values.jsonToConvex)(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new import_values.ConvexError(respJSON.errorMessage) ); } throw new Error(respJSON.errorMessage); default: throw new Error(`Invalid response: ${JSON.stringify(respJSON)}`); } } } function forwardErrorData(errorData, error) { error.data = (0, import_values.jsonToConvex)(errorData); return error; } //# sourceMappingURL=http_client.js.map