UNPKG

@instantdb/core

Version:
647 lines • 23.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.StorageInterface = exports.createInstantRouteHandler = exports.StoreInterface = exports.InstantStream = exports.SSEConnection = exports.SyncTableCallbackEventType = exports.getInfiniteQueryInitialSnapshot = exports.InstantError = exports.version = exports.Streams = exports.Storage = exports.Auth = exports.InstantCoreDatabase = exports.WindowNetworkListener = exports.IndexedDBStorage = exports.coerceToDate = exports.weakHash = exports.getOps = exports.i = exports.setInstantWarningsEnabled = exports.InstantAPIError = exports.Reactor = exports.FrameworkClient = exports.TransactionValidationError = exports.parseSchemaFromJSON = exports.validateTransactions = exports.QueryValidationError = exports.validateQuery = 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")); exports.Reactor = Reactor_js_1.default; 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 id_ts_1 = __importDefault(require("./utils/id.js")); exports.id = id_ts_1.default; const IndexedDBStorage_ts_1 = __importDefault(require("./IndexedDBStorage.js")); exports.IndexedDBStorage = IndexedDBStorage_ts_1.default; const dates_js_1 = require("./utils/dates.js"); Object.defineProperty(exports, "coerceToDate", { enumerable: true, get: function () { return dates_js_1.coerceToDate; } }); 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_ts_1 = __importDefault(require("./version.js")); exports.version = version_ts_1.default; const queryValidation_ts_1 = require("./queryValidation.js"); Object.defineProperty(exports, "validateQuery", { enumerable: true, get: function () { return queryValidation_ts_1.validateQuery; } }); Object.defineProperty(exports, "QueryValidationError", { enumerable: true, get: function () { return queryValidation_ts_1.QueryValidationError; } }); const transactionValidation_ts_1 = require("./transactionValidation.js"); Object.defineProperty(exports, "validateTransactions", { enumerable: true, get: function () { return transactionValidation_ts_1.validateTransactions; } }); Object.defineProperty(exports, "TransactionValidationError", { enumerable: true, get: function () { return transactionValidation_ts_1.TransactionValidationError; } }); const warningToggle_ts_1 = require("./warningToggle.js"); Object.defineProperty(exports, "setInstantWarningsEnabled", { enumerable: true, get: function () { return warningToggle_ts_1.setInstantWarningsEnabled; } }); const PersistedObject_ts_1 = require("./utils/PersistedObject.js"); Object.defineProperty(exports, "StoreInterface", { enumerable: true, get: function () { return PersistedObject_ts_1.StoreInterface; } }); const createRouteHandler_ts_1 = require("./createRouteHandler.js"); Object.defineProperty(exports, "createInstantRouteHandler", { enumerable: true, get: function () { return createRouteHandler_ts_1.createInstantRouteHandler; } }); const parseSchemaFromJSON_ts_1 = require("./parseSchemaFromJSON.js"); Object.defineProperty(exports, "parseSchemaFromJSON", { enumerable: true, get: function () { return parseSchemaFromJSON_ts_1.parseSchemaFromJSON; } }); const framework_ts_1 = require("./framework.js"); Object.defineProperty(exports, "FrameworkClient", { enumerable: true, get: function () { return framework_ts_1.FrameworkClient; } }); const fetch_js_1 = require("./utils/fetch.js"); Object.defineProperty(exports, "InstantAPIError", { enumerable: true, get: function () { return fetch_js_1.InstantAPIError; } }); const InstantError_ts_1 = require("./InstantError.js"); Object.defineProperty(exports, "InstantError", { enumerable: true, get: function () { return InstantError_ts_1.InstantError; } }); const Connection_ts_1 = require("./Connection.js"); Object.defineProperty(exports, "SSEConnection", { enumerable: true, get: function () { return Connection_ts_1.SSEConnection; } }); const SyncTable_ts_1 = require("./SyncTable.js"); Object.defineProperty(exports, "SyncTableCallbackEventType", { enumerable: true, get: function () { return SyncTable_ts_1.CallbackEventType; } }); const Stream_ts_1 = require("./Stream.js"); Object.defineProperty(exports, "InstantStream", { enumerable: true, get: function () { return Stream_ts_1.InstantStream; } }); const infiniteQuery_ts_1 = require("./infiniteQuery.js"); Object.defineProperty(exports, "getInfiniteQueryInitialSnapshot", { enumerable: true, get: function () { return infiniteQuery_ts_1.getInfiniteQueryInitialSnapshot; } }); const defaultOpenDevtool = true; // consts const defaultConfig = { apiURI: 'https://api.instantdb.com', websocketURI: 'wss://api.instantdb.com/runtime/session', }; // hmr function initSchemaHashStore() { globalThis.__instantDbSchemaHashStore = globalThis.__instantDbSchemaHashStore ?? new WeakMap(); return globalThis.__instantDbSchemaHashStore; } function initGlobalInstantCoreStore() { globalThis.__instantDbStore = globalThis.__instantDbStore ?? {}; 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') + '_' + config.useDateObjects); } const globalInstantCoreStore = initGlobalInstantCoreStore(); const schemaHashStore = initSchemaHashStore(); /** * Functions to log users in and out. * * @see https://instantdb.com/docs/auth */ class Auth { db; 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)) */ 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)) */ 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); */ signInWithToken = (token) => { return this.db.signInWithCustomToken(token); }; /** * Sign in as guest, creating a new user without email * * @see https://instantdb.com/docs/auth * * @example * db.auth.signInAsGuest(); */ signInAsGuest = () => { return this.db.signInAsGuest(); }; /** * 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> */ 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)); * */ 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)); * */ 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() * ); */ issuerURI = () => { return this.db.issuerURI(); }; /** * Sign out the current user */ signOut = (opts = { invalidateToken: true }) => { return this.db.signOut(opts); }; } exports.Auth = Auth; /** * Functions to manage file storage. */ class Storage { db; 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); */ uploadFile = (path, file, opts = {}) => { return this.db.uploadFile(path, file, opts); }; /** * Deletes a file by path name. * * @deprecated Use `db.transact` to delete files instead: * @example * // Delete by id * db.transact(db.tx.$files[fileId].delete()); * * // Delete by path * db.transact(db.tx.$files[lookup('path', 'photos/demo.png')].delete()); * * // Delete multiple files * db.transact(fileIds.map(id => db.tx.$files[id].delete())); * * @see https://instantdb.com/docs/storage */ delete = (pathname) => { return this.db.deleteFile(pathname); }; // Deprecated Storage API (Jan 2025) // --------------------------------- /** * @deprecated. Use `db.storage.uploadFile` instead * remove in the future. */ upload = (pathname, file) => { return this.db.upload(pathname, file); }; /** * @deprecated Use `db.storage.uploadFile` instead */ 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" * } * } * } * }) */ getDownloadUrl = (pathname) => { return this.db.getDownloadUrl(pathname); }; } exports.Storage = Storage; /** * Functions to manage streams. */ class Streams { db; constructor(db) { this.db = db; } /** * Creates a new ReadableStream for the given clientId. * * @example * const stream = db.streams.createReadStream({clientId: clientId}) * for await (const chunk of stream) { * console.log(chunk); * } */ createReadStream = (opts) => { return this.db.createReadStream(opts); }; /** * Creates a new WritableStream for the given clientId. * * @example * const writeStream = db.streams.createWriteStream({clientId: clientId}) * const writer = writeStream.getWriter(); * writer.write('Hello world'); * writer.close(); */ createWriteStream = (opts) => { return this.db.createWriteStream(opts); }; } exports.Streams = Streams; // util function coerceQuery(o) { // stringify and parse to remove undefined values return JSON.parse(JSON.stringify(o)); } class InstantCoreDatabase { _reactor; auth; storage; streams; tx = (0, instatx_js_1.txInit)(); constructor(reactor) { this._reactor = reactor; this.auth = new Auth(this._reactor); this.storage = new Storage(this._reactor); this.streams = new Streams(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); } /** * Subscribe to a query and incrementally load more items * * Only one top level namespace in the query is allowed. * @example * const { unsubscribe, loadNextPage } = db.subscribeInfiniteQuery({ * posts: { * $: { * limit: 20, // Load 20 posts at a time * order: { * createdAt: 'desc', * }, * }, * }, * (resp) => { * console.log(resp.data.posts); * } * }); */ subscribeInfiniteQuery(query, cb, opts) { return (0, infiniteQuery_ts_1.subscribeInfiniteQuery)(this, 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(roomType, roomId, opts?.initialPresence); return { leaveRoom, subscribeTopic: (topic, onEvent) => this._reactor.subscribeTopic(roomType, 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); } /** * @deprecated This is an experimental function that is not yet ready for production use. * Use this function to sync an entire namespace. * It has many limitations that will be removed in the future: * 1. Must be used with an admin token * 2. Does not support permissions * 3. Does not support where clauses * 4. Does not support links * It also does not support multiple top-level namespaces. For example, * {posts: {}, users: {}} is invalid. Only `posts` or `users` is allowed, but not both. */ _syncTableExperimental(query, cb) { return this._reactor.subscribeTable(query, cb); } } exports.InstantCoreDatabase = InstantCoreDatabase; function schemaHash(schema) { if (!schema) { return '0'; } const fromStore = schemaHashStore.get(schema); if (fromStore) { return fromStore; } 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( // Allows config with missing `useDateObjects`, but keeps `UseDates` // as a non-nullable in the InstantConfig type. config, Store, NetworkListener, versions, EventSourceImpl) { const configStrict = { ...config, appId: config.appId?.trim(), useDateObjects: (config.useDateObjects ?? false), }; const existingClient = globalInstantCoreStore[reactorKey(configStrict)]; if (existingClient) { if (schemaChanged(existingClient, configStrict.schema)) { existingClient._reactor.updateSchema(configStrict.schema); } return existingClient; } const reactor = new Reactor_js_1.default({ ...defaultConfig, ...configStrict, cardinalityInference: configStrict.schema ? true : false, }, Store || IndexedDBStorage_ts_1.default, NetworkListener || WindowNetworkListener_js_1.default, { ...(versions || {}), '@instantdb/core': version_ts_1.default }, EventSourceImpl); const client = new InstantCoreDatabase(reactor); globalInstantCoreStore[reactorKey(configStrict)] = client; handleDevtool(configStrict.appId, configStrict.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 = { 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; /** @deprecated Use StoreInterface instead */ exports.StorageInterface = PersistedObject_ts_1.StoreInterface; //# sourceMappingURL=index.js.map