UNPKG

convex

Version:

Client for the Convex Cloud

328 lines (327 loc) 10 kB
"use strict"; import { getFunctionName } from "../server/api.js"; import { parseArgs, validateDeploymentUrl } from "../common/index.js"; import { version } from "../index.js"; import { ConvexError, convexToJson, jsonToConvex } from "../values/index.js"; import { logToConsole } from "./logging.js"; export const STATUS_CODE_OK = 200; export const STATUS_CODE_BAD_REQUEST = 400; export const STATUS_CODE_UDF_FAILED = 560; let specifiedFetch = void 0; export function setFetch(f) { specifiedFetch = f; } export 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) { if (skipConvexDeploymentUrlCheck !== true) { validateDeploymentUrl(address); } this.address = `${address}/api`; this.debug = true; } /** * Obtain the {@link ConvexHttpClient}'s URL to its backend. * * @returns The URL to the Convex backend, including the client's API version. */ backendUrl() { 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; } /** * 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 = parseArgs(args[0]); const name = getFunctionName(query); const body = JSON.stringify({ path: name, format: "convex_encoded_json", args: [convexToJson(queryArgs)] }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${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}/query`, { ...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 ?? []) { logToConsole("info", "query", name, line); } } switch (respJSON.status) { case "success": return jsonToConvex(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new 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 = parseArgs(args[0]); const name = getFunctionName(mutation); const body = JSON.stringify({ path: name, format: "convex_encoded_json", args: [convexToJson(mutationArgs)] }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${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}/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 ?? []) { logToConsole("info", "mutation", name, line); } } switch (respJSON.status) { case "success": return jsonToConvex(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new 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 = parseArgs(args[0]); const name = getFunctionName(action); const body = JSON.stringify({ path: name, format: "convex_encoded_json", args: [convexToJson(actionArgs)] }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${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}/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 ?? []) { logToConsole("info", "action", name, line); } } switch (respJSON.status) { case "success": return jsonToConvex(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new 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, ...args) { const functionArgs = parseArgs(args[0]); const name = typeof anyFunction === "string" ? anyFunction : getFunctionName(anyFunction); const body = JSON.stringify({ path: name, format: "convex_encoded_json", args: convexToJson(functionArgs) }); const headers = { "Content-Type": "application/json", "Convex-Client": `npm-${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}/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 ?? []) { logToConsole("info", "any", name, line); } } switch (respJSON.status) { case "success": return jsonToConvex(respJSON.value); case "error": if (respJSON.errorData !== void 0) { throw forwardErrorData( respJSON.errorData, new ConvexError(respJSON.errorMessage) ); } throw new Error(respJSON.errorMessage); default: throw new Error(`Invalid response: ${JSON.stringify(respJSON)}`); } } } function forwardErrorData(errorData, error) { error.data = jsonToConvex(errorData); return error; } //# sourceMappingURL=http_client.js.map