UNPKG

@react-native-ohos/realm

Version:

Realm by MongoDB is an offline-first mobile database: an alternative to SQLite and key-value stores

1,024 lines 45.6 kB
"use strict"; //////////////////////////////////////////////////////////////////////////// // // Copyright 2022 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////// var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Realm = void 0; const binding_1 = require("./binding"); const assert_1 = require("./assert"); const errors_1 = require("./errors"); const debug_1 = require("./debug"); const flags_1 = require("./flags"); const indirect_1 = require("./indirect"); const platform_1 = require("./platform"); const Object_1 = require("./Object"); const Results_1 = require("./Results"); const schema_1 = require("./schema"); const ClassMap_1 = require("./ClassMap"); const Configuration_1 = require("./Configuration"); const SyncConfiguration_1 = require("./app-services/SyncConfiguration"); const Logger_1 = require("./Logger"); const List_1 = require("./List"); const ProgressRealmPromise_1 = require("./ProgressRealmPromise"); const Object_2 = require("./Object"); const RealmListeners_1 = require("./RealmListeners"); const SubscriptionSet_1 = require("./app-services/SubscriptionSet"); const SyncSession_1 = require("./app-services/SyncSession"); const array_buffer_1 = require("./type-helpers/array-buffer"); const symbols_1 = require("./symbols"); const Results_2 = require("./collection-accessors/Results"); const debug = (0, debug_1.extendDebug)("Realm"); /** * Asserts the event passed as string is a valid RealmEvent value. * @throws A {@link TypeAssertionError} if an unexpected name is passed via {@link name}. * @param name - The name of the event. * @internal */ function assertRealmEvent(name) { const values = Object.values(RealmListeners_1.RealmEvent); if (!values.includes(name)) { throw new errors_1.TypeAssertionError("One of " + values.join(", "), name); } } /** * The Realm database. */ class Realm { static defaultPath = Realm.normalizePath("default.realm"); static internals = new Set(); /** * Sets the log level. * @param level - The log level to be used by the logger. The default value is `info`. * @param category - The category to set the log level for. If omitted, the log level is set for all categories (`"Realm"`). * @note The log level can be changed during the lifetime of the application. * @since 12.0.0 * @example * Realm.setLogLevel("all"); */ static setLogLevel(level, category = "Realm") { (0, assert_1.assert)(Logger_1.LOG_CATEGORIES.includes(category), `Unexpected log category: '${category}'`); const categoryRef = binding_1.binding.LogCategoryRef.getCategory(category); categoryRef.setDefaultLevelThreshold((0, Logger_1.toBindingLoggerLevel)(level)); } static setLogger(loggerCallback) { assert_1.assert.function(loggerCallback); binding_1.binding.Logger.setDefaultLogger((0, Logger_1.toBindingLogger)(loggerCallback)); } /** * Closes all Realms, cancels all pending {@link Realm.open} calls, clears internal caches, resets the logger and collects garbage. * Call this method to free up the event loop and allow Node.js to perform a graceful exit. */ static shutdown() { // Close any realms not already closed for (const realmRef of Realm.internals) { const realm = realmRef.deref(); if (realm && !realm.isClosed) { realm.close(); } } Realm.internals.clear(); binding_1.binding.RealmCoordinator.clearAllCaches(); binding_1.binding.App.clearCachedApps(); ProgressRealmPromise_1.ProgressRealmPromise.cancelAll(); binding_1.binding.Logger.setDefaultLogger(null); platform_1.garbageCollection.collect(); } /** * Clears the state by closing and deleting any Realm in the default directory and logout all users. * NOTE: Not a part of the public API and it's primarily used from the library's tests. * @private */ static clearTestState() { (0, assert_1.assert)(flags_1.flags.ALLOW_CLEAR_TEST_STATE, "Set the flags.ALLOW_CLEAR_TEST_STATE = true before calling this."); Realm.shutdown(); // Delete all Realm files in the default directory const defaultDirectoryPath = platform_1.fs.getDefaultDirectoryPath(); platform_1.fs.removeRealmFilesFromDirectory(defaultDirectoryPath); } /** * Delete the Realm file for the given configuration. * @param config - The configuration for the Realm being deleted. * @throws An {@link Error} if anything in the provided {@link config} is invalid. */ static deleteFile(config) { (0, Configuration_1.validateConfiguration)(config); const path = Realm.determinePath(config); platform_1.fs.removeFile(path); platform_1.fs.removeFile(path + ".lock"); platform_1.fs.removeFile(path + ".fresh.lock"); platform_1.fs.removeFile(path + ".note"); platform_1.fs.removeDirectory(path + ".management"); } static exists(arg = {}) { const config = typeof arg === "string" ? { path: arg } : arg; (0, Configuration_1.validateConfiguration)(config); const path = Realm.determinePath(config); return platform_1.fs.exists(path); } static open(arg = {}) { const config = typeof arg === "string" ? { path: arg } : arg; return new ProgressRealmPromise_1.ProgressRealmPromise(config); } /** * Get the current schema version of the Realm at the given path. * @param path - The path to the file where the Realm database is stored. * @param encryptionKey - Required only when accessing encrypted Realms. * @throws An {@link Error} if passing an invalid or non-matching encryption key. * @returns Version of the schema as an integer, or `-1` if no Realm exists at {@link path}. * @since 0.11.0 */ static schemaVersion(path, encryptionKey) { const notFound = "18446744073709551615"; // std::numeric_limit<uint64_t>::max() = 0xffffffffffffffff as string const config = { path }; const absolutePath = Realm.determinePath(config); const schemaVersion = binding_1.binding.Realm.getSchemaVersion({ path: absolutePath, encryptionKey: Realm.determineEncryptionKey(encryptionKey), }); // no easy way to compare uint64_t in TypeScript return notFound === schemaVersion.toString() ? -1 : binding_1.binding.Int64.intToNum(schemaVersion); } /** * Creates a template object for a Realm model class where all optional fields are undefined * and all required fields have the default value for the given data type, either the value * set by the default property in the schema or the default value for the datatype if the schema * doesn't specify one, i.e. 0, false and "". * @param objectSchema - Schema describing the object that should be created. */ static createTemplateObject(objectSchema) { (0, schema_1.validateObjectSchema)(objectSchema); const normalizedSchema = (0, schema_1.normalizeObjectSchema)(objectSchema); const result = {}; for (const [key, property] of Object.entries(normalizedSchema.properties)) { // If a default value is explicitly set, always set the property if (typeof property.default !== "undefined") { result[key] = property.default; continue; } // if optional is set, it wil take precedence over any `?` set on the type parameter if (property.optional) { continue; } // Set the default value for all required primitive types. // Lists are always treated as empty if not specified and references to objects are always optional switch (property.type) { case "bool": result[key] = false; break; case "int": result[key] = 0; break; case "float": result[key] = 0.0; break; case "double": result[key] = 0.0; break; case "string": result[key] = ""; break; case "data": result[key] = new ArrayBuffer(0); break; case "date": result[key] = new Date(0); break; } } return result; } /** * Copy any Realm files (i.e. `*.realm`) bundled with the application from the application * directory into the application's documents directory, so that they can be opened and used * by Realm. If the file already exists in the documents directory, it will not be * overwritten, so this can safely be called multiple times. * * This should be called before opening the Realm, in order to move the bundled Realm * files into a place where they can be written to. * @example * ``` * // Given a bundled file, example.realm, this will copy example.realm (and any other .realm files) * // from the app bundle into the app's documents directory. If the file already exists, it will * // not be overwritten, so it is safe to call this every time the app starts. * Realm.copyBundledRealmFiles(); * * const realm = await Realm.open({ * // This will open example.realm from the documents directory, with the bundled data in. * path: "example.realm" * }); * ``` * * This is only implemented for React Native. * @throws an {@link Error} If an I/O error occurred or method is not implemented. */ static copyBundledRealmFiles() { platform_1.fs.copyBundledRealmFiles(); } /** * TODO: Consider breaking this by ensuring a ".realm" suffix (coordinating with other SDK teams in the process) */ static normalizePath(path) { if (typeof path === "undefined") { return Realm.defaultPath; } else if (path.length === 0) { throw new Error("Unexpected empty path"); } else if (platform_1.fs.isAbsolutePath(path)) { return path; } else { return platform_1.fs.joinPaths(platform_1.fs.getDefaultDirectoryPath(), path); } } /** * @note When the path is relative and the config contains a sync object, Core will replace any existing file extension * or add the ".realm" suffix. */ static determinePath(config) { if (config.sync && !config.openSyncedRealmLocally) { if (config.path && platform_1.fs.isAbsolutePath(config.path)) { return Realm.normalizePath(config.path); } else { const bindingSyncConfig = (0, SyncConfiguration_1.toBindingSyncConfig)(config.sync); return config.sync.user.internal.pathForRealm(bindingSyncConfig, config.path); } } else { return Realm.normalizePath(config.path); } } static determineEncryptionKey(encryptionKey) { if (typeof encryptionKey === "undefined") { return encryptionKey; } else { return (0, array_buffer_1.toArrayBuffer)(encryptionKey, false); } } static extractRealmSchemaExtras(schemas) { const extras = {}; for (const schema of schemas) { extras[schema.name] = this.extractObjectSchemaExtras(schema); } return extras; } /** @internal */ static extractObjectSchemaExtras(schema) { const defaults = {}; const presentations = {}; for (const [name, propertySchema] of Object.entries(schema.properties)) { defaults[name] = propertySchema.default; presentations[name] = propertySchema.presentation; } return { constructor: schema.ctor, defaults, presentations }; } /** @internal */ static transformConfig(config) { const normalizedSchema = config.schema && (0, schema_1.normalizeRealmSchema)(config.schema); const schemaExtras = Realm.extractRealmSchemaExtras(normalizedSchema || []); const path = Realm.determinePath(config); const { fifoFilesFallbackPath, shouldCompact, inMemory } = config; const bindingSchema = normalizedSchema && (0, schema_1.toBindingSchema)(normalizedSchema); return { schemaExtras, bindingConfig: { path, cache: true, fifoFilesFallbackPath, schema: bindingSchema, inMemory: inMemory === true, schemaMode: Realm.determineSchemaMode(config), schemaVersion: config.schema ? binding_1.binding.Int64.numToInt(typeof config.schemaVersion === "number" ? config.schemaVersion : 0) : undefined, migrationFunction: config.onMigration ? Realm.wrapMigration(schemaExtras, config.onMigration) : undefined, shouldCompactOnLaunchFunction: shouldCompact ? (totalBytes, usedBytes) => { return shouldCompact(Number(totalBytes), Number(usedBytes)); } : undefined, disableFormatUpgrade: config.disableFormatUpgrade, encryptionKey: Realm.determineEncryptionKey(config.encryptionKey), syncConfig: config.sync ? (0, SyncConfiguration_1.toBindingSyncConfig)(config.sync) : undefined, forceSyncHistory: config.openSyncedRealmLocally, automaticallyHandleBacklinksInMigrations: config.migrationOptions?.resolveEmbeddedConstraints ?? false, }, }; } static determineSchemaMode(config) { const { readOnly, deleteRealmIfMigrationNeeded, onMigration, sync } = config; (0, assert_1.assert)(!readOnly || !deleteRealmIfMigrationNeeded, "Cannot set 'deleteRealmIfMigrationNeeded' when 'readOnly' is set."); (0, assert_1.assert)(!onMigration || !deleteRealmIfMigrationNeeded, "Cannot set 'deleteRealmIfMigrationNeeded' when 'onMigration' is set."); if (readOnly) { return 1 /* binding.SchemaMode.Immutable */; } else if (deleteRealmIfMigrationNeeded) { return 3 /* binding.SchemaMode.SoftResetFile */; } else if (sync) { return 6 /* binding.SchemaMode.AdditiveExplicit */; } else { return undefined; } } static wrapMigration(schemaExtras, onMigration) { return (oldRealmInternal, newRealmInternal) => { try { const oldRealm = new Realm(null, { internal: oldRealmInternal, schemaExtras }); const newRealm = new Realm(null, { internal: newRealmInternal, schemaExtras }); onMigration(oldRealm, newRealm); } finally { oldRealmInternal.close(); oldRealmInternal.$resetSharedPtr(); newRealmInternal.$resetSharedPtr(); } }; } /** * The Realms's representation in the binding. * @internal */ internal; /** * The sync session if this is a synced Realm */ syncSession; schemaExtras = {}; classes; changeListeners = new RealmListeners_1.RealmListeners(this, RealmListeners_1.RealmEvent.Change); beforeNotifyListeners = new RealmListeners_1.RealmListeners(this, RealmListeners_1.RealmEvent.BeforeNotify); schemaListeners = new RealmListeners_1.RealmListeners(this, RealmListeners_1.RealmEvent.Schema); /** @internal */ currentUpdateMode; constructor(arg, internalConfig = {}) { const config = typeof arg === "string" ? { path: arg } : arg || {}; // Calling `Realm.exists()` before `binding.Realm.getSharedRealm()` is necessary to capture // the correct value when this constructor was called since `binding.Realm.getSharedRealm()` // will open the realm. This is needed when deciding whether to update initial subscriptions. const realmExists = internalConfig.realmExists ?? Realm.exists(config); if (arg !== null) { (0, assert_1.assert)(!internalConfig.schemaExtras, "Expected either a configuration or schemaExtras"); (0, Configuration_1.validateConfiguration)(config); const { bindingConfig, schemaExtras } = Realm.transformConfig(config); debug("open", bindingConfig); this.schemaExtras = schemaExtras; platform_1.fs.ensureDirectoryForFile(bindingConfig.path); this.internal = internalConfig.internal ?? binding_1.binding.Realm.getSharedRealm(bindingConfig); if (flags_1.flags.ALLOW_CLEAR_TEST_STATE) { Realm.internals.add(new binding_1.binding.WeakRef(this.internal)); } binding_1.binding.Helpers.setBindingContext(this.internal, { didChange: (r) => { r.verifyOpen(); this.changeListeners.notify(); }, schemaDidChange: (r) => { r.verifyOpen(); this.classes = new ClassMap_1.ClassMap(this, this.internal.schema, this.schema); this.schemaListeners.notify(this.schema); }, beforeNotify: (r) => { r.verifyOpen(); this.beforeNotifyListeners.notify(); }, }); } else { const { internal, schemaExtras } = internalConfig; assert_1.assert.instanceOf(internal, binding_1.binding.Realm, "internal"); this.internal = internal; this.schemaExtras = schemaExtras || {}; } Object.defineProperty(this, "classes", { enumerable: false, configurable: false, writable: true, }); Object.defineProperty(this, "internal", { enumerable: false, configurable: false, writable: false, }); this.classes = new ClassMap_1.ClassMap(this, this.internal.schema, this.schema); const syncSession = this.internal.syncSession; this.syncSession = syncSession ? new SyncSession_1.SyncSession(syncSession) : null; const initialSubscriptions = config.sync?.initialSubscriptions; if (initialSubscriptions && !config.openSyncedRealmLocally) { // Do not call `Realm.exists()` here in case the realm has been opened by this point in time. this.handleInitialSubscriptions(initialSubscriptions, realmExists); } } /** * Indicates if this Realm contains any objects. * @returns `true` if empty, `false` otherwise. * @readonly * @since 1.10.0 */ get isEmpty() { return this.internal.isEmpty; } /** * The path to the file where this Realm is stored. * @returns A string containing the path to the file where this Realm is stored. * @readonly * @since 0.12.0 */ get path() { return this.internal.config.path; } /** * Indicates if this Realm was opened as read-only. * @returns `true` if this Realm is read-only, `false` otherwise. * @readonly * @since 0.12.0 */ get isReadOnly() { return this.internal.config.schemaMode === 1 /* binding.SchemaMode.Immutable */; } /** * Indicates if this Realm was opened in-memory. * @returns `true` if this Realm is in-memory, `false` otherwise. * @readonly */ get isInMemory() { return this.internal.config.inMemory; } /** * A normalized representation of the schema provided in the {@link Configuration} when this Realm was constructed. * @returns An array of {@link CanonicalObjectSchema} describing all objects in this Realm. * @readonly * @since 0.12.0 */ get schema() { const schemas = (0, schema_1.fromBindingRealmSchema)(this.internal.schema); // Stitch in the constructors and defaults stored in this.schemaExtras for (const objectSchema of schemas) { const extras = this.schemaExtras[objectSchema.name]; if (extras) { objectSchema.ctor = extras.constructor; } for (const property of Object.values(objectSchema.properties)) { property.default = extras ? extras.defaults[property.name] : undefined; property.presentation = extras ? extras.presentations[property.name] : undefined; } } return schemas; } /** * The current schema version of the Realm. * @returns The schema version of this Realm, as a `number`. * @readonly * @since 0.12.0 */ get schemaVersion() { return Number(this.internal.schemaVersion); } /** * Indicates if this Realm is in a write transaction. * @returns `true` if in a write transaction, `false` otherwise. * @readonly * @since 1.10.3 */ get isInTransaction() { // TODO: Consider keeping a local state in JS for this return this.internal.isInTransaction; } /** * Indicates if this Realm is in migration. * @returns `true` if in migration, `false` otherwise * @readonly * @since 12.3.0 */ get isInMigration() { // TODO: Consider keeping a local state in JS for this return this.internal.isInMigration; } /** * Indicates if this Realm has been closed. * @returns `true` if closed, `false` otherwise. * @readonly * @since 2.1.0 */ get isClosed() { // TODO: Consider keeping a local state in JS for this return this.internal.isClosed; } /** * The latest set of flexible sync subscriptions. * @returns A {@link SubscriptionSet} object. * @throws An {@link Error} if flexible sync is not enabled for this app. */ get subscriptions() { const { syncConfig } = this.internal.config; (0, assert_1.assert)(syncConfig, "`subscriptions` can only be accessed if flexible sync is enabled, but sync is " + "currently disabled for your app. Add a flexible sync config when opening the " + "Realm, for example: { sync: { user, flexible: true } }."); (0, assert_1.assert)(syncConfig.flxSyncRequested, "`subscriptions` can only be accessed if flexible sync is enabled, but partition " + "based sync is currently enabled for your Realm. Modify your sync config to remove any `partitionValue` " + "and enable flexible sync, for example: { sync: { user, flexible: true } }"); return new SubscriptionSet_1.SubscriptionSet(this, this.internal.latestSubscriptionSet); } /** * Closes this Realm so it may be re-opened with a newer schema version. * All objects and collections from this Realm are no longer valid after calling this method. * The method is idempotent. */ close() { this.internal.close(); } create(type, values, mode = Object_2.UpdateMode.Never) { // Supporting a boolean overload for mode if (mode === true) { mode = Object_2.UpdateMode.All; } else if (mode === false) { mode = Object_2.UpdateMode.Never; } // Implements https://github.com/realm/realm-js/blob/v11/src/js_realm.hpp#L1260-L1321 if (values instanceof Object_1.RealmObject && !values[symbols_1.OBJECT_INTERNAL]) { throw new Error("Cannot create an object from a detached RealmObject instance"); } if (!Object.values(Object_2.UpdateMode).includes(mode)) { throw new Error(`Unsupported 'updateMode'. Only '${Object_2.UpdateMode.Never}', '${Object_2.UpdateMode.Modified}' or '${Object_2.UpdateMode.All}' is supported.`); } this.internal.verifyOpen(); const helpers = this.classes.getHelpers(type); this.currentUpdateMode = mode; let realmObject; try { realmObject = Object_1.RealmObject.create(this, values, mode, { helpers }); } finally { this.currentUpdateMode = undefined; } return isAsymmetric(helpers.objectSchema) ? undefined : realmObject; } //FIXME: any should not be used, but we are staying compatible with previous versions /** * Deletes the provided Realm object, or each one inside the provided collection. * @param subject - The Realm object to delete, or a collection containing multiple Realm objects to delete. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any delete(subject) { assert_1.assert.inTransaction(this, "Can only delete objects within a transaction."); assert_1.assert.object(subject, "subject"); if (subject instanceof Object_1.RealmObject) { assert_1.assert.isSameRealm(subject[symbols_1.OBJECT_REALM].internal, this.internal, "Can't delete an object from another Realm"); const { objectSchema } = this.classes.getHelpers(subject); const obj = subject[symbols_1.OBJECT_INTERNAL]; assert_1.assert.isValid(obj, "Object is invalid. Either it has been previously deleted or the Realm it belongs to has been closed."); const table = binding_1.binding.Helpers.getTable(this.internal, objectSchema.tableKey); table.removeObject(obj.key); } else if (subject instanceof List_1.List) { subject.internal.deleteAll(); } else if (subject instanceof Results_1.Results) { subject.internal.clear(); } else if (Array.isArray(subject) || Symbol.iterator in subject) { //@ts-expect-error the above check is good enough for (const object of subject) { assert_1.assert.instanceOf(object, Object_1.RealmObject); assert_1.assert.isSameRealm(object[symbols_1.OBJECT_REALM].internal, this.internal, "Can't delete an object from another Realm"); const { objectSchema } = this.classes.getHelpers(object); const table = binding_1.binding.Helpers.getTable(this.internal, objectSchema.tableKey); table.removeObject(object[symbols_1.OBJECT_INTERNAL].key); } } else { throw new Error("Can only delete objects, lists and results."); } } /** * Deletes a Realm model, including all of its objects. * If called outside a migration function, {@link schema} and {@link schemaVersion} are updated. * @param name - The model name. */ deleteModel(name) { assert_1.assert.inTransaction(this, "Can only delete objects within a transaction."); binding_1.binding.Helpers.deleteDataForObject(this.internal, name); if (!this.internal.isInMigration) { const newSchema = this.internal.schema.filter((objectSchema) => objectSchema.name !== name); this.internal.updateSchema(newSchema, binding_1.binding.Int64.add(this.internal.schemaVersion, binding_1.binding.Int64.numToInt(1)), null, null, true); } } /** * **WARNING:** This will delete **all** objects in the Realm! */ deleteAll() { assert_1.assert.inTransaction(this, "Can only delete objects within a transaction."); for (const objectSchema of this.internal.schema) { const table = binding_1.binding.Helpers.getTable(this.internal, objectSchema.tableKey); table.clear(); } } objectForPrimaryKey(type, primaryKey) { // Implements https://github.com/realm/realm-js/blob/v11/src/js_realm.hpp#L1240-L1258 const { objectSchema, properties, wrapObject } = this.classes.getHelpers(type); if (!objectSchema.primaryKey) { throw new Error(`Expected a primary key on '${objectSchema.name}'`); } if (isAsymmetric(objectSchema)) { throw new Error("You cannot query an asymmetric object."); } const table = binding_1.binding.Helpers.getTable(this.internal, objectSchema.tableKey); const value = properties.get(objectSchema.primaryKey).toBinding(primaryKey); try { const objKey = table.findPrimaryKey(value); if (binding_1.binding.isEmptyObjKey(objKey)) { return null; } else { const obj = table.getObject(objKey); return wrapObject(obj); } } catch (err) { // TODO: Match on something else than the error message, when exposed by the binding if (err instanceof Error && err.message.startsWith("No object with key")) { throw new Error(`No '${objectSchema.name}' with key '${primaryKey}'`); } else { throw err; } } } _objectForObjectKey(type, objectKey) { const { objectSchema, wrapObject } = this.classes.getHelpers(type); if (isEmbedded(objectSchema)) { throw new Error("You cannot query an embedded object."); } else if (isAsymmetric(objectSchema)) { throw new Error("You cannot query an asymmetric object."); } assert_1.assert.numericString(objectKey); const table = binding_1.binding.Helpers.getTable(this.internal, objectSchema.tableKey); try { const objKey = binding_1.binding.stringToObjKey(objectKey); const obj = table.tryGetObject(objKey); const result = obj && wrapObject(obj); return result === null ? undefined : result; } catch (err) { if (err instanceof binding_1.binding.InvalidObjKey) { return undefined; } else { throw err; } } } objects(type) { const { internal, classes } = this; const { objectSchema, wrapObject } = classes.getHelpers(type); if (isEmbedded(objectSchema)) { throw new Error("You cannot query an embedded object."); } else if (isAsymmetric(objectSchema)) { throw new Error("You cannot query an asymmetric object."); } const table = binding_1.binding.Helpers.getTable(internal, objectSchema.tableKey); const results = binding_1.binding.Results.fromTable(internal, table); const typeHelpers = { fromBinding(value) { return wrapObject(value); }, toBinding(value) { assert_1.assert.instanceOf(value, Object_1.RealmObject); return value[symbols_1.OBJECT_INTERNAL]; }, }; const accessor = (0, Results_2.createResultsAccessor)({ realm: this, typeHelpers, itemType: 7 /* binding.PropertyType.Object */ }); return new Results_1.Results(this, results, accessor, typeHelpers); } /** * Add a listener {@link callback} for the specified {@link eventName}. * @param eventName - The name of event that should cause the callback to be called. * @param callback - Function to be called when a change event occurs. * Each callback will only be called once per event, regardless of the number of times * it was added. * @throws An {@link Error} if an invalid event {@link eventName} is supplied, if Realm is closed or if {@link callback} is not a function. */ addListener(eventName, callback) { assert_1.assert.open(this); assert_1.assert.function(callback); if (eventName === "change") { this.changeListeners.add(callback); } else if (eventName === "schema") { this.schemaListeners.add(callback); } else if (eventName === "beforenotify") { this.beforeNotifyListeners.add(callback); } else { throw new Error(`Unknown event name '${eventName}': only 'change', 'schema' and 'beforenotify' are supported.`); } } /** * Remove the listener {@link callback} for the specified event {@link eventName}. * @param eventName - The event name. * @param callback - Function that was previously added as a listener for this event through the {@link addListener} method. * @throws an {@link Error} If an invalid event {@link eventName} is supplied, if Realm is closed or if {@link callback} is not a function. */ removeListener(eventName, callback) { assert_1.assert.open(this); assert_1.assert.function(callback); assertRealmEvent(eventName); if (eventName === RealmListeners_1.RealmEvent.Change) { this.changeListeners.remove(callback); } else if (eventName === RealmListeners_1.RealmEvent.Schema) { this.schemaListeners.remove(callback); } else if (eventName === RealmListeners_1.RealmEvent.BeforeNotify) { this.beforeNotifyListeners.remove(callback); } else { assert_1.assert.never(eventName, "eventName"); } } /** * Remove all event listeners (restricted to the event {@link eventName}, if provided). * @param eventName - The name of the event whose listeners should be removed. * @throws An {@link Error} when invalid event {@link eventName} is supplied. */ removeAllListeners(eventName) { assert_1.assert.open(this); if (typeof eventName === "undefined") { this.changeListeners.removeAll(); this.schemaListeners.removeAll(); this.beforeNotifyListeners.removeAll(); } else { assert_1.assert.string(eventName, "eventName"); assertRealmEvent(eventName); if (eventName === RealmListeners_1.RealmEvent.Change) { this.changeListeners.removeAll(); } else if (eventName === RealmListeners_1.RealmEvent.Schema) { this.schemaListeners.removeAll(); } else if (eventName === RealmListeners_1.RealmEvent.BeforeNotify) { this.beforeNotifyListeners.removeAll(); } else { assert_1.assert.never(eventName, "eventName"); } } } /** * Synchronously call the provided {@link callback} inside a write transaction. If an exception happens inside a transaction, * you’ll lose the changes in that transaction, but the Realm itself won’t be affected (or corrupted). * More precisely, {@link beginTransaction} and {@link commitTransaction} will be called * automatically. If any exception is thrown during the transaction {@link cancelTransaction} will * be called instead of {@link commitTransaction} and the exception will be re-thrown to the caller of {@link write}. * * Nested transactions (calling {@link write} within {@link write}) is not possible. * @param callback - Function to be called inside a write transaction. * @returns Returned value from the callback. */ write(callback) { let result = undefined; this.internal.beginTransaction(); try { result = callback(); } catch (err) { this.internal.cancelTransaction(); throw err; } this.internal.commitTransaction(); return result; } /** * Initiate a write transaction. * * When doing a transaction, it is highly recommended to do error handling. * If you don't handle errors, your data might become inconsistent. Error handling * will often involve canceling the transaction. * @throws An {@link Error} if already in write transaction * @see {@link cancelTransaction} * @see {@link commitTransaction} * @example * realm.beginTransaction(); * try { * realm.create('Person', { name: 'Arthur Dent', origin: 'Earth' }); * realm.create('Person', { name: 'Ford Prefect', origin: 'Betelgeuse Five' }); * realm.commitTransaction(); * } catch (e) { * realm.cancelTransaction(); * throw e; * } */ beginTransaction() { this.internal.beginTransaction(); } /** * Commit a write transaction. * @see {@link beginTransaction} */ commitTransaction() { this.internal.commitTransaction(); } /** * Cancel a write transaction. * @see {@link beginTransaction} */ cancelTransaction() { this.internal.cancelTransaction(); } /** * Replaces all string columns in this Realm with a string enumeration column and compacts the * database file. * * Cannot be called from a write transaction. * * Compaction will not occur if other {@link Realm} instances exist. * * While compaction is in progress, attempts by other threads or processes to open the database will * wait. * * Be warned that resource requirements for compaction is proportional to the amount of live data in * the database. Compaction works by writing the database contents to a temporary database file and * then replacing the database with the temporary one. * @returns `true` if compaction succeeds, `false` if not. */ compact() { assert_1.assert.outTransaction(this, "Cannot compact a Realm within a transaction."); return this.internal.compact(); } /** * Writes a compacted copy of the Realm with the given configuration. * * The destination file cannot already exist. * All conversions between synced and non-synced Realms are supported, and will be * performed according to the {@link config} parameter, which describes the desired output. * * Note that if this method is called from within a write transaction, the current data is written, * not the data from the point when the previous write transaction was committed. * @param config - Realm configuration that describes the output realm. */ writeCopyTo(config) { assert_1.assert.outTransaction(this, "Can only convert Realms outside a transaction."); (0, Configuration_1.validateConfiguration)(config); const { bindingConfig } = Realm.transformConfig(config); this.internal.convert(bindingConfig); } /** * Update the schema of the Realm. * @param schema The schema which the Realm should be updated to use. * @internal */ _updateSchema(schema) { (0, schema_1.validateRealmSchema)(schema); const normalizedSchema = (0, schema_1.normalizeRealmSchema)(schema); const bindingSchema = (0, schema_1.toBindingSchema)(normalizedSchema); if (!this.isInTransaction) { throw new Error("Can only create object schema within a transaction."); } this.internal.updateSchema(bindingSchema, binding_1.binding.Int64.add(this.internal.schemaVersion, binding_1.binding.Int64.numToInt(1)), null, null, true); // Note: The schema change listener is fired immediately after the call to // `this.internal.updateSchema()` (thus before `_updateSchema()` has // returned). Therefore, `this.classes` is updated in the `schemaDidChange` // callback and not here. } /** @internal */ getClassHelpers(arg) { return this.classes.getHelpers(arg); } /** * Update subscriptions with the initial subscriptions if needed. * @param initialSubscriptions The initial subscriptions. * @param realmExists Whether the realm already exists. */ handleInitialSubscriptions(initialSubscriptions, realmExists) { const shouldUpdateSubscriptions = initialSubscriptions.rerunOnOpen || !realmExists; if (shouldUpdateSubscriptions) { debug("handling initial subscriptions, %O", { rerunOnOpen: initialSubscriptions.rerunOnOpen, realmExists }); this.subscriptions.updateNoWait(initialSubscriptions.update); } } } exports.Realm = Realm; (0, indirect_1.injectIndirect)("Realm", Realm); /** * @param objectSchema - The schema of the object. * @returns `true` if the object is marked for asymmetric sync, otherwise `false`. */ function isAsymmetric(objectSchema) { return objectSchema.tableType === 2 /* binding.TableType.TopLevelAsymmetric */; } /** * @param objectSchema - The schema of the object. * @returns `true` if the object is marked as embedded, otherwise `false`. */ function isEmbedded(objectSchema) { return objectSchema.tableType === 1 /* binding.TableType.Embedded */; } // Declare the Realm namespace for backwards compatibility // This declaration needs to happen in the same file which declares "Realm" // @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-namespaces-with-classes-functions-and-enums const ns = __importStar(require("./namespace")); // Needed to avoid complaints about a self-reference var RealmItself = Realm; // eslint-disable-next-line @typescript-eslint/no-namespace (function (Realm) { Realm.Realm = RealmItself; Realm.flags = ns.flags; Realm.Object = ns.RealmObject; Realm.App = ns.App; Realm.Auth = ns.Auth; Realm.BSON = ns.BSON; Realm.Types = ns.Types; Realm.Services = ns.Services; Realm.index = ns.index; Realm.mapTo = ns.mapTo; Realm.kmToRadians = ns.kmToRadians; Realm.miToRadians = ns.miToRadians; Realm.AssertionError = ns.AssertionError; Realm.ClientResetMode = ns.ClientResetMode; Realm.Collection = ns.Collection; Realm.CompensatingWriteError = ns.CompensatingWriteError; Realm.ConnectionState = ns.ConnectionState; Realm.Counter = ns.Counter; Realm.Credentials = ns.Credentials; Realm.Dictionary = ns.Dictionary; Realm.List = ns.List; Realm.MetadataMode = ns.MetadataMode; Realm.MongoDB = ns.MongoDB; Realm.NumericLogLevel = ns.NumericLogLevel; Realm.OpenRealmBehaviorType = ns.OpenRealmBehaviorType; Realm.OpenRealmTimeOutBehavior = ns.OpenRealmTimeOutBehavior; Realm.OrderedCollection = ns.OrderedCollection; Realm.ProgressDirection = ns.ProgressDirection; Realm.ProgressMode = ns.ProgressMode; Realm.ProgressRealmPromise = ns.ProgressRealmPromise; Realm.PropertySchemaParseError = ns.PropertySchemaParseError; Realm.ProviderType = ns.ProviderType; Realm.ProxyType = ns.ProxyType; Realm.RealmEvent = ns.RealmEvent; Realm.Results = ns.Results; Realm.SchemaParseError = ns.SchemaParseError; Realm.SessionState = ns.SessionState; Realm.SessionStopPolicy = ns.SessionStopPolicy; Realm.Set = ns.RealmSet; Realm.SubscriptionSetState = ns.SubscriptionSetState; Realm.SyncError = ns.SyncError; Realm.TypeAssertionError = ns.TypeAssertionError; Realm.UpdateMode = ns.UpdateMode; Realm.User = ns.User; Realm.UserState = ns.UserState; Realm.WaitForSync = ns.WaitForSync; /** @deprecated Use another {@link ns.ClientResetMode | ClientResetMode} than {@link ns.ClientResetMode.Manual | ClientResetMode.Manual}. */ Realm.ClientResetError = ns.ClientResetError; /** @deprecated See https://www.mongodb.com/docs/atlas/app-services/reference/push-notifications/ */ Realm.PushClient = ns.PushClient; })(Realm = exports.Realm || (exports.Realm = {})); exports.Realm = Realm; // Set default logger and log level. Realm.setLogger(Logger_1.defaultLogger); Realm.setLogLevel(Logger_1.defaultLoggerLevel); //# sourceMappingURL=Realm.js.map