convex
Version:
Client for the Convex Cloud
242 lines (241 loc) • 9.28 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 client_exports = {};
__export(client_exports, {
InternalConvexClient: () => InternalConvexClient
});
module.exports = __toCommonJS(client_exports);
var import__ = require("../../index.js");
var import_local_state = require("./local_state.js");
var import_mutation_manager = require("./mutation_manager.js");
var import_optimistic_updates_impl = require("./optimistic_updates_impl.js");
var import_remote_query_set = require("./remote_query_set.js");
var import_udf_path_utils = require("./udf_path_utils.js");
var import_web_socket_manager = require("./web_socket_manager.js");
var import_uuid = require("uuid");
var import_logging = require("../logging.js");
var import_action_manager = require("./action_manager.js");
const DEFAULT_OPTIONS = {
unsavedChangesWarning: true
};
class InternalConvexClient {
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/${import__.version}/sync`;
this.state = new import_local_state.LocalSyncState();
this.remoteQuerySet = new import_remote_query_set.RemoteQuerySet(
(queryId) => this.state.queryPath(queryId)
);
this.mutationManager = new import_mutation_manager.MutationManager();
this.actionManager = new import_action_manager.ActionManager();
this.optimisticQueryResults = new import_optimistic_updates_impl.OptimisticQueryResults();
this.onTransition = onTransition;
this.nextMutationId = 0;
this.nextActionId = 0;
this.sessionId = (0, import_uuid.v4)();
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 import_web_socket_manager.WebSocketManager(
wsUri,
(reconnectMetadata) => {
this.webSocketManager.sendMessage({
...reconnectMetadata,
type: "Connect",
sessionId: this.sessionId
});
this.remoteQuerySet = new import_remote_query_set.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 = (0, import_logging.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();
if (modification2) {
this.webSocketManager.sendMessage(modification2);
}
}
};
}
localQueryResult(udfPath, args) {
const queryToken = (0, import_udf_path_utils.serializePathAndArgs)(udfPath, args);
return this.optimisticQueryResults.queryResult(queryToken);
}
queryJournal(name, args) {
const queryToken = (0, import_udf_path_utils.serializePathAndArgs)(name, args);
return this.state.queryJournal(queryToken);
}
connectionState() {
return {
hasInflightMutation: this.mutationManager.hasInflightMutation(),
hasInflightAction: this.actionManager.hasInflightActions(),
isWebSocketConnected: this.webSocketManager.socketState() === "ready"
};
}
async mutate(udfPath, args, optimisticUpdate = null) {
const mutationId = this.nextMutationId;
this.nextMutationId++;
if (optimisticUpdate !== null) {
const wrappedUpdate = (localQueryStore) => {
optimisticUpdate(localQueryStore, ...args);
};
const changedQueries = this.optimisticQueryResults.applyOptimisticUpdate(
wrappedUpdate,
mutationId
);
this.onTransition(changedQueries);
}
const { message, result } = this.mutationManager.request(
udfPath,
args,
mutationId
);
this.webSocketManager.sendMessage(message);
return result;
}
async action(udfPath, args) {
const actionId = this.nextActionId;
this.nextActionId++;
const { message, result } = this.actionManager.request(
udfPath,
args,
actionId
);
this.webSocketManager.sendMessage(message);
return result;
}
async close() {
return this.webSocketManager.stop();
}
}
//# sourceMappingURL=client.js.map