UNPKG

@instantdb/core

Version:

Instant's core local abstraction

502 lines 17.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.version = exports.Storage = exports.Auth = exports.InstantCoreDatabase = exports.WindowNetworkListener = exports.IndexedDBStorage = exports.weakHash = exports.getOps = exports.i = exports.InstantAPIError = exports.lookup = exports.txInit = exports.tx = exports.id = exports.init_experimental = void 0; exports.init = init; exports.coerceQuery = coerceQuery; const Reactor_js_1 = __importDefault(require("./Reactor.js")); const instatx_js_1 = require("./instatx.js"); Object.defineProperty(exports, "tx", { enumerable: true, get: function () { return instatx_js_1.tx; } }); Object.defineProperty(exports, "txInit", { enumerable: true, get: function () { return instatx_js_1.txInit; } }); Object.defineProperty(exports, "lookup", { enumerable: true, get: function () { return instatx_js_1.lookup; } }); Object.defineProperty(exports, "getOps", { enumerable: true, get: function () { return instatx_js_1.getOps; } }); const weakHash_js_1 = __importDefault(require("./utils/weakHash.js")); exports.weakHash = weakHash_js_1.default; const uuid_js_1 = __importDefault(require("./utils/uuid.js")); exports.id = uuid_js_1.default; const IndexedDBStorage_js_1 = __importDefault(require("./IndexedDBStorage.js")); exports.IndexedDBStorage = IndexedDBStorage_js_1.default; const WindowNetworkListener_js_1 = __importDefault(require("./WindowNetworkListener.js")); exports.WindowNetworkListener = WindowNetworkListener_js_1.default; const schema_js_1 = require("./schema.js"); Object.defineProperty(exports, "i", { enumerable: true, get: function () { return schema_js_1.i; } }); const devtool_js_1 = require("./devtool.js"); const version_js_1 = __importDefault(require("./version.js")); exports.version = version_js_1.default; const fetch_js_1 = require("./utils/fetch.js"); Object.defineProperty(exports, "InstantAPIError", { enumerable: true, get: function () { return fetch_js_1.InstantAPIError; } }); const defaultOpenDevtool = true; // consts const defaultConfig = { apiURI: 'https://api.instantdb.com', websocketURI: 'wss://api.instantdb.com/runtime/session', }; // hmr function initSchemaHashStore() { var _a; globalThis.__instantDbSchemaHashStore = (_a = globalThis.__instantDbSchemaHashStore) !== null && _a !== void 0 ? _a : new WeakMap(); return globalThis.__instantDbSchemaHashStore; } function initGlobalInstantCoreStore() { var _a; globalThis.__instantDbStore = (_a = globalThis.__instantDbStore) !== null && _a !== void 0 ? _a : {}; return globalThis.__instantDbStore; } function reactorKey(config) { // @ts-expect-error const adminToken = config.__adminToken; return (config.appId + '_' + (config.websocketURI || 'default_ws_uri') + '_' + (config.apiURI || 'default_api_uri') + '_' + (adminToken || 'client_only')); } const globalInstantCoreStore = initGlobalInstantCoreStore(); const schemaHashStore = initSchemaHashStore(); /** * Functions to log users in and out. * * @see https://instantdb.com/docs/auth */ class Auth { constructor(db) { this.db = db; /** * Sends a magic code to the user's email address. * * Once you send the magic code, see {@link auth.signInWithMagicCode} to let the * user verify. * * @see https://instantdb.com/docs/auth * @example * db.auth.sendMagicCode({email: "example@gmail.com"}) * .catch((err) => console.error(err.body?.message)) */ this.sendMagicCode = (params) => { return this.db.sendMagicCode(params); }; /** * Verify a magic code that was sent to the user's email address. * * @see https://instantdb.com/docs/auth * * @example * db.auth.signInWithMagicCode({email: "example@gmail.com", code: "123456"}) * .catch((err) => console.error(err.body?.message)) */ this.signInWithMagicCode = (params) => { return this.db.signInWithMagicCode(params); }; /** * Sign in a user with a refresh token * * @see https://instantdb.com/docs/backend#frontend-auth-sign-in-with-token * * @example * // Get the token from your backend * const token = await fetch('/signin', ...); * //Sign in * db.auth.signInWithToken(token); */ this.signInWithToken = (token) => { return this.db.signInWithCustomToken(token); }; /** * Create an authorization url to sign in with an external provider * * @see https://instantdb.com/docs/auth * * @example * // Get the authorization url from your backend * const url = db.auth.createAuthorizationUrl({ * clientName: "google", * redirectURL: window.location.href, * }); * * // Put it in a sign in link * <a href={url}>Log in with Google</a> */ this.createAuthorizationURL = (params) => { return this.db.createAuthorizationURL(params); }; /** * Sign in with the id_token from an external provider like Google * * @see https://instantdb.com/docs/auth * @example * db.auth * .signInWithIdToken({ * // Token from external service * idToken: id_token, * // The name you gave the client when you registered it with Instant * clientName: "google", * // The nonce, if any, that you used when you initiated the auth flow * // with the external service. * nonce: your_nonce * }) * .catch((err) => console.error(err.body?.message)); * */ this.signInWithIdToken = (params) => { return this.db.signInWithIdToken(params); }; /** * Sign in with the id_token from an external provider like Google * * @see https://instantdb.com/docs/auth * @example * db.auth * .exchangeOAuthCode({ * // code received in redirect from OAuth callback * code: code * // The PKCE code_verifier, if any, that you used when you * // initiated the auth flow * codeVerifier: your_code_verifier * }) * .catch((err) => console.error(err.body?.message)); * */ this.exchangeOAuthCode = (params) => { return this.db.exchangeCodeForToken(params); }; /** * OpenID Discovery path for use with tools like * expo-auth-session that use auto-discovery of * OAuth parameters. * * @see https://instantdb.com/docs/auth * @example * const discovery = useAutoDiscovery( * db.auth.issuerURI() * ); */ this.issuerURI = () => { return this.db.issuerURI(); }; /** * Sign out the current user */ this.signOut = (opts = { invalidateToken: true }) => { return this.db.signOut(opts); }; } } exports.Auth = Auth; /** * Functions to manage file storage. */ class Storage { constructor(db) { this.db = db; /** * Uploads file at the provided path. * * @see https://instantdb.com/docs/storage * @example * const [file] = e.target.files; // result of file input * const data = await db.storage.uploadFile('photos/demo.png', file); */ this.uploadFile = (path, file, opts = {}) => { return this.db.uploadFile(path, file, opts); }; /** * Deletes a file by path name. * * @see https://instantdb.com/docs/storage * @example * await db.storage.delete('photos/demo.png'); */ this.delete = (pathname) => { return this.db.deleteFile(pathname); }; // Deprecated Storage API (Jan 2025) // --------------------------------- /** * @deprecated. Use `db.storage.uploadFile` instead * remove in the future. */ this.upload = (pathname, file) => { return this.db.upload(pathname, file); }; /** * @deprecated Use `db.storage.uploadFile` instead */ this.put = this.upload; /** * @deprecated. getDownloadUrl will be removed in the future. * Use `useQuery` instead to query and fetch for valid urls * * db.useQuery({ * $files: { * $: { * where: { * path: "moop.png" * } * } * } * }) */ this.getDownloadUrl = (pathname) => { return this.db.getDownloadUrl(pathname); }; } } exports.Storage = Storage; // util function coerceQuery(o) { // stringify and parse to remove undefined values return JSON.parse(JSON.stringify(o)); } class InstantCoreDatabase { constructor(reactor) { this.tx = (0, instatx_js_1.txInit)(); this._reactor = reactor; this.auth = new Auth(this._reactor); this.storage = new Storage(this._reactor); } /** * Use this to write data! You can create, update, delete, and link objects * * @see https://instantdb.com/docs/instaml * * @example * // Create a new object in the `goals` namespace * const goalId = id(); * db.transact(db.tx.goals[goalId].update({title: "Get fit"})) * * // Update the title * db.transact(db.tx.goals[goalId].update({title: "Get super fit"})) * * // Delete it * db.transact(db.tx.goals[goalId].delete()) * * // Or create an association: * todoId = id(); * db.transact([ * db.tx.todos[todoId].update({ title: 'Go on a run' }), * db.tx.goals[goalId].link({todos: todoId}), * ]) */ transact(chunks) { return this._reactor.pushTx(chunks); } getLocalId(name) { return this._reactor.getLocalId(name); } /** * Use this to query your data! * * @see https://instantdb.com/docs/instaql * * @example * // listen to all goals * db.subscribeQuery({ goals: {} }, (resp) => { * console.log(resp.data.goals) * }) * * // goals where the title is "Get Fit" * db.subscribeQuery( * { goals: { $: { where: { title: "Get Fit" } } } }, * (resp) => { * console.log(resp.data.goals) * } * ) * * // all goals, _alongside_ their todos * db.subscribeQuery({ goals: { todos: {} } }, (resp) => { * console.log(resp.data.goals) * }); */ subscribeQuery(query, cb, opts) { return this._reactor.subscribeQuery(query, cb, opts); } /** * Listen for the logged in state. This is useful * for deciding when to show a login screen. * * @see https://instantdb.com/docs/auth * @example * const unsub = db.subscribeAuth((auth) => { * if (auth.user) { * console.log('logged in as', auth.user.email) * } else { * console.log('logged out') * } * }) */ subscribeAuth(cb) { return this._reactor.subscribeAuth(cb); } /** * One time query for the logged in state. This is useful * for scenarios where you want to know the current auth * state without subscribing to changes. * * @see https://instantdb.com/docs/auth * @example * const user = await db.getAuth(); * console.log('logged in as', user.email) */ getAuth() { return this._reactor.getAuth(); } /** * Listen for connection status changes to Instant. This is useful * for building things like connectivity indicators * * @see https://www.instantdb.com/docs/patterns#connection-status * @example * const unsub = db.subscribeConnectionStatus((status) => { * const connectionState = * status === 'connecting' || status === 'opened' * ? 'authenticating' * : status === 'authenticated' * ? 'connected' * : status === 'closed' * ? 'closed' * : status === 'errored' * ? 'errored' * : 'unexpected state'; * * console.log('Connection status:', connectionState); * }); */ subscribeConnectionStatus(cb) { return this._reactor.subscribeConnectionStatus(cb); } /** * Join a room to publish and subscribe to topics and presence. * * @see https://instantdb.com/docs/presence-and-topics * @example * // init * const db = init(); * const room = db.joinRoom(roomType, roomId); * // usage * const unsubscribeTopic = room.subscribeTopic("foo", console.log); * const unsubscribePresence = room.subscribePresence({}, console.log); * room.publishTopic("hello", { message: "hello world!" }); * room.publishPresence({ name: "joe" }); * // later * unsubscribePresence(); * unsubscribeTopic(); * room.leaveRoom(); */ joinRoom(roomType = '_defaultRoomType', roomId = '_defaultRoomId', opts) { const leaveRoom = this._reactor.joinRoom(roomId, opts === null || opts === void 0 ? void 0 : opts.initialPresence); return { leaveRoom, subscribeTopic: (topic, onEvent) => this._reactor.subscribeTopic(roomId, topic, onEvent), subscribePresence: (opts, onChange) => this._reactor.subscribePresence(roomType, roomId, opts, onChange), publishTopic: (topic, data) => this._reactor.publishTopic({ roomType, roomId, topic, data }), publishPresence: (data) => this._reactor.publishPresence(roomType, roomId, data), getPresence: (opts) => this._reactor.getPresence(roomType, roomId, opts), }; } shutdown() { delete globalInstantCoreStore[reactorKey(this._reactor.config)]; this._reactor.shutdown(); } /** * Use this for one-off queries. * Returns local data if available, otherwise fetches from the server. * Because we want to avoid stale data, this method will throw an error * if the user is offline or there is no active connection to the server. * * @see https://instantdb.com/docs/instaql * * @example * * const resp = await db.queryOnce({ goals: {} }); * console.log(resp.data.goals) */ queryOnce(query, opts) { return this._reactor.queryOnce(query, opts); } } exports.InstantCoreDatabase = InstantCoreDatabase; function schemaHash(schema) { if (!schema) { return '0'; } if (schemaHashStore.get(schema)) { return schemaHashStore.get(schema); } const hash = (0, weakHash_js_1.default)(schema); schemaHashStore.set(schema, hash); return hash; } function schemaChanged(existingClient, newSchema) { return (schemaHash(existingClient._reactor.config.schema) !== schemaHash(newSchema)); } /** * * The first step: init your application! * * Visit https://instantdb.com/dash to get your `appId` :) * * @example * import { init } from "@instantdb/core" * * const db = init({ appId: "my-app-id" }) * * // You can also provide a schema for type safety and editor autocomplete! * * import { init } from "@instantdb/core" * import schema from ""../instant.schema.ts"; * * const db = init({ appId: "my-app-id", schema }) * * // To learn more: https://instantdb.com/docs/modeling-data */ function init(config, Storage, NetworkListener, versions) { const existingClient = globalInstantCoreStore[reactorKey(config)]; if (existingClient) { if (schemaChanged(existingClient, config.schema)) { existingClient._reactor.updateSchema(config.schema); } return existingClient; } const reactor = new Reactor_js_1.default(Object.assign(Object.assign(Object.assign({}, defaultConfig), config), { cardinalityInference: config.schema ? true : false }), Storage || IndexedDBStorage_js_1.default, NetworkListener || WindowNetworkListener_js_1.default, Object.assign(Object.assign({}, (versions || {})), { '@instantdb/core': version_js_1.default })); const client = new InstantCoreDatabase(reactor); globalInstantCoreStore[reactorKey(config)] = client; handleDevtool(config.appId, config.devtool); return client; } function handleDevtool(appId, devtool) { if (typeof window === 'undefined' || typeof window.location === 'undefined' || typeof document === 'undefined') { return; } if (typeof devtool === 'boolean' && !devtool) { return; } const config = Object.assign({ position: 'bottom-right', allowedHosts: ['localhost'] }, (typeof devtool === 'object' ? devtool : {})); if (!config.allowedHosts.includes(window.location.hostname)) { return; } (0, devtool_js_1.createDevtool)(appId, config); } /** * @deprecated * `init_experimental` is deprecated. You can replace it with `init`. * * @example * * // Before * import { init_experimental } from "@instantdb/core" * const db = init_experimental({ ... }); * * // After * import { init } from "@instantdb/core" * const db = init({ ... }); */ const init_experimental = init; exports.init_experimental = init_experimental; //# sourceMappingURL=index.js.map