convex
Version:
Client for the Convex Cloud
260 lines (259 loc) • 8.19 kB
JavaScript
"use strict";
import { validateDeploymentUrl } from "../common/index.js";
import {
BaseConvexClient
} from "./index.js";
import { getFunctionName } from "../server/api.js";
let defaultWebSocketConstructor;
export function setDefaultWebSocketConstructor(ws) {
defaultWebSocketConstructor = ws;
}
export class ConvexClient {
/**
* Once closed no registered callbacks will fire again.
*/
get closed() {
return this._closed;
}
get client() {
if (this._client)
return this._client;
throw new Error("ConvexClient is disabled");
}
/**
* Construct a client and immediately initiate a WebSocket connection to the passed address.
*
* @public
*/
constructor(address, options = {}) {
if (options.skipConvexDeploymentUrlCheck !== true) {
validateDeploymentUrl(address);
}
const { disabled, ...baseOptions } = options;
this._closed = false;
this.disabled = !!disabled;
if (defaultWebSocketConstructor && !("webSocketConstructor" in baseOptions) && typeof WebSocket === "undefined") {
baseOptions.webSocketConstructor = defaultWebSocketConstructor;
}
if (typeof window === "undefined" && !("unsavedChangesWarning" in baseOptions)) {
baseOptions.unsavedChangesWarning = false;
}
if (!this.disabled) {
this._client = new BaseConvexClient(
address,
(updatedQueries) => this._transition(updatedQueries),
baseOptions
);
}
this.listeners = /* @__PURE__ */ new Set();
}
/**
* Call a callback whenever a new result for a query is received. The callback
* will run soon after being registered if a result for the query is already
* in memory.
*
* The return value is an {@link Unsubscribe} object which is both a function
* an an object with properties. Both of the patterns below work with this object:
*
*```ts
* // call the return value as a function
* const unsubscribe = client.onUpdate(api.messages.list, {}, (messages) => {
* console.log(messages);
* });
* unsubscribe();
*
* // unpack the return value into its properties
* const {
* getCurrentValue,
* unsubscribe,
* } = client.onUpdate(api.messages.list, {}, (messages) => {
* console.log(messages);
* });
*```
*
* @param query - A {@link server.FunctionReference} for the public query to run.
* @param args - The arguments to run the query with.
* @param callback - Function to call when the query result updates.
* @param onError - Function to call when the query result updates with an error.
* If not provided, errors will be thrown instead of calling the callback.
*
* @return an {@link Unsubscribe} function to stop calling the onUpdate function.
*/
onUpdate(query, args, callback, onError) {
if (this.disabled) {
const disabledUnsubscribe = () => {
};
const unsubscribeProps2 = {
unsubscribe: disabledUnsubscribe,
getCurrentValue: () => void 0,
getQueryLogs: () => void 0
};
Object.assign(disabledUnsubscribe, unsubscribeProps2);
return disabledUnsubscribe;
}
const { queryToken, unsubscribe } = this.client.subscribe(
getFunctionName(query),
args
);
const queryInfo = {
queryToken,
callback,
onError,
unsubscribe,
hasEverRun: false,
query,
args
};
this.listeners.add(queryInfo);
if (this.queryResultReady(queryToken) && this.callNewListenersWithCurrentValuesTimer === void 0) {
this.callNewListenersWithCurrentValuesTimer = setTimeout(
() => this.callNewListenersWithCurrentValues(),
0
);
}
const unsubscribeProps = {
unsubscribe: () => {
if (this.closed) {
return;
}
this.listeners.delete(queryInfo);
unsubscribe();
},
getCurrentValue: () => this.client.localQueryResultByToken(queryToken),
getQueryLogs: () => this.client.localQueryLogs(queryToken)
};
const ret = unsubscribeProps.unsubscribe;
Object.assign(ret, unsubscribeProps);
return ret;
}
// Run all callbacks that have never been run before if they have a query
// result available now.
callNewListenersWithCurrentValues() {
this.callNewListenersWithCurrentValuesTimer = void 0;
this._transition([], true);
}
queryResultReady(queryToken) {
return this.client.hasLocalQueryResultByToken(queryToken);
}
async close() {
if (this.disabled)
return;
this.listeners.clear();
this._closed = true;
return this.client.close();
}
/**
* Set the authentication token to be used for subsequent queries and mutations.
* `fetchToken` will be called automatically again if a token expires.
* `fetchToken` should return `null` if the token cannot be retrieved, for example
* when the user's rights were permanently revoked.
* @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token
* @param onChange - a callback that will be called when the authentication status changes
*/
setAuth(fetchToken, onChange) {
this.client.setAuth(
fetchToken,
onChange ?? (() => {
})
);
}
/**
* @internal
*/
setAdminAuth(token, identity) {
if (this.closed) {
throw new Error("ConvexClient has already been closed.");
}
if (this.disabled)
return;
this.client.setAdminAuth(token, identity);
}
/**
* @internal
*/
_transition(updatedQueries, callNewListeners = false) {
for (const queryInfo of this.listeners) {
const { callback, queryToken, onError, hasEverRun } = queryInfo;
if (updatedQueries.includes(queryToken) || callNewListeners && !hasEverRun && this.client.hasLocalQueryResultByToken(queryToken)) {
queryInfo.hasEverRun = true;
let newValue;
try {
newValue = this.client.localQueryResultByToken(queryToken);
} catch (error) {
if (!(error instanceof Error))
throw error;
if (onError) {
onError(
error,
"Second argument to onUpdate onError is reserved for later use"
);
} else {
void Promise.reject(error);
}
continue;
}
callback(
newValue,
"Second argument to onUpdate callback is reserved for later use"
);
}
}
}
/**
* Execute a mutation function.
*
* @param mutation - A {@link server.FunctionReference} for the public mutation
* to run.
* @param args - An arguments object for the mutation.
* @param options - A {@link MutationOptions} options object for the mutation.
* @returns A promise of the mutation's result.
*/
async mutation(mutation, args) {
if (this.disabled)
throw new Error("ConvexClient is disabled");
return await this.client.mutation(getFunctionName(mutation), args);
}
/**
* Execute an action function.
*
* @param action - A {@link server.FunctionReference} for the public action
* to run.
* @param args - An arguments object for the action.
* @returns A promise of the action's result.
*/
async action(action, args) {
if (this.disabled)
throw new Error("ConvexClient is disabled");
return await this.client.action(getFunctionName(action), args);
}
/**
* Fetch a query result once.
*
* @param query - A {@link server.FunctionReference} for the public query
* to run.
* @param args - An arguments object for the query.
* @returns A promise of the query's result.
*/
async query(query, args) {
if (this.disabled)
throw new Error("ConvexClient is disabled");
const value = this.client.localQueryResult(getFunctionName(query), args);
if (value !== void 0)
return value;
return new Promise((resolve, reject) => {
const { unsubscribe } = this.onUpdate(
query,
args,
(value2) => {
unsubscribe();
resolve(value2);
},
(e) => {
unsubscribe();
reject(e);
}
);
});
}
}
//# sourceMappingURL=simple_client.js.map