UNPKG

realm

Version:

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

257 lines 11.9 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. // //////////////////////////////////////////////////////////////////////////// Object.defineProperty(exports, "__esModule", { value: true }); exports.List = void 0; const internal_1 = require("./internal"); /** * Instances of this class will be returned when accessing object properties whose type is `"list"`. * * Lists mostly behave like normal Javascript Arrays, except for that they can * only store values of a single type (indicated by the `type` and `optional` * properties of the List), and can only be modified inside a {@link Realm.write | write} transaction. */ class List extends internal_1.OrderedCollection { /** @internal */ constructor(realm, internal, helpers) { if (arguments.length === 0 || !(internal instanceof internal_1.binding.List)) { throw new internal_1.IllegalConstructorError("List"); } super(realm, internal.asResults(), helpers); // Getting the `objectSchema` off the internal will throw if base type isn't object const baseType = this.results.type & ~960 /* binding.PropertyType.Flags */; const isEmbedded = baseType === 7 /* binding.PropertyType.Object */ && internal.objectSchema.tableType === 1 /* binding.TableType.Embedded */; Object.defineProperty(this, "internal", { enumerable: false, configurable: false, writable: false, value: internal, }); Object.defineProperty(this, "isEmbedded", { enumerable: false, configurable: false, writable: false, value: isEmbedded, }); } /** * Checks if this collection has not been deleted and is part of a valid Realm. * @returns `true` if the collection can be safely accessed. */ isValid() { return this.internal.isValid; } /** * Set an element of the ordered collection by index * @param index The index * @param value The value * @internal */ set(index, value) { const { realm, internal, isEmbedded, helpers: { toBinding }, } = this; internal_1.assert.inTransaction(realm); // TODO: Consider a more performant way to determine if the list is embedded internal.setAny(index, toBinding(value, isEmbedded ? { createObj: () => [internal.setEmbedded(index), true] } : undefined)); } /** * @returns The number of values in the list. */ get length() { return this.internal.size; } /** * @throws An {@link Error} as the length property cannot be assigned. */ set length(value) { throw new Error("Cannot assign to read only property 'length'"); } /** * Remove the **last** value from the list and return it. * @throws an {@link AssertionError} If not inside a write transaction. * @returns The last value or undefined if the list is empty. */ pop() { internal_1.assert.inTransaction(this.realm); const { internal, helpers: { fromBinding }, } = this; const lastIndex = internal.size - 1; if (lastIndex >= 0) { const result = fromBinding(internal.getAny(lastIndex)); internal.remove(lastIndex); return result; } } /** * Add one or more values to the _end_ of the list. * @param items - Values to add to the list. * @throws A {TypeError} if a value is not of a type which can be stored in * the list, or if an object being added to the list does not match the {@link ObjectSchema} for the list. * @throws An {@link AssertionError} if not inside a write transaction. * @returns The new length of the list after adding the values. */ push(...items) { internal_1.assert.inTransaction(this.realm); const { isEmbedded, internal, helpers: { toBinding }, } = this; const start = internal.size; for (const [offset, item] of items.entries()) { const index = start + offset; if (isEmbedded) { // Simply transforming to binding will insert the embedded object toBinding(item, { createObj: () => [internal.insertEmbedded(index), true] }); } else { internal.insertAny(index, toBinding(item)); } } return internal.size; } /** * Remove the **first** value from the list and return it. * @throws An {@link AssertionError} if not inside a write transaction. * @returns The first value or `undefined` if the list is empty. */ shift() { internal_1.assert.inTransaction(this.realm); const { internal, helpers: { fromBinding }, } = this; if (internal.size > 0) { const result = fromBinding(internal.getAny(0)); internal.remove(0); return result; } } /** * Add one or more values to the _beginning_ of the list. * @param items - Values to add to the list. * @throws A {TypeError} if a value is not of a type which can be stored in * the list, or if an object being added to the list does not match the {@link ObjectSchema} for the list. * @throws An {@link AssertionError} if not inside a write transaction. * @returns The new length of the list after adding the values. */ unshift(...items) { internal_1.assert.inTransaction(this.realm); const { isEmbedded, internal, helpers: { toBinding }, } = this; for (const [index, item] of items.entries()) { if (isEmbedded) { // Simply transforming to binding will insert the embedded object toBinding(item, { createObj: () => [internal.insertEmbedded(index), true] }); } else { internal.insertAny(index, toBinding(item)); } } return internal.size; } /** * Changes the contents of the list by removing value and/or inserting new value. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice Array.prototype.splice} * @param start - The start index. If greater than the length of the list, * the start index will be set to the length instead. If negative, then the start index * will be counted from the end of the list (e.g. `list.length - index`). * @param deleteCount - The number of values to remove from the list. * If not provided, then all values from the start index through the end of * the list will be removed. * @param items - Values to insert into the list starting at `index`. * @returns An array containing the value that were removed from the list. The * array is empty if no value were removed. */ splice(start, deleteCount, ...items) { // Comments in the code below is copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice internal_1.assert.inTransaction(this.realm); internal_1.assert.number(start, "start"); const { isEmbedded, internal, helpers: { fromBinding, toBinding }, } = this; // If negative, it will begin that many elements from the end of the array. if (start < 0) { start = internal.size + start; } // If greater than the length of the array, start will be set to the length of the array. if (start > internal.size) { start = internal.size; } // If deleteCount is omitted, or if its value is equal to or larger than array.length - start // (that is, if it is equal to or greater than the number of elements left in the array, starting at start), // then all the elements from start to the end of the array will be deleted. const end = typeof deleteCount === "number" ? Math.min(start + deleteCount, internal.size) : internal.size; // Get the elements that are about to be deleted const result = []; for (let i = start; i < end; i++) { result.push(fromBinding(internal.getAny(i))); } // Remove the elements from the list (backwards to avoid skipping elements as they're being deleted) for (let i = end - 1; i >= start; i--) { internal.remove(i); } // Insert any new elements for (const [offset, item] of items.entries()) { const index = start + offset; if (isEmbedded) { // Simply transforming to binding will insert the embedded object toBinding(item, { createObj: () => [internal.insertEmbedded(index), true] }); } else { internal.insertAny(index, toBinding(item)); } } return result; } /** * Removes the element of the list at the specified index. * @param index - The index of the element to remove. * @throws An {@link AssertionError} if not inside a write transaction or the input index is less than 0 * or greater than or equal to the size of the list. */ remove(index) { internal_1.assert.inTransaction(this.realm); internal_1.assert.number(index, "index"); (0, internal_1.assert)(index >= 0, "Index cannot be smaller than 0"); (0, internal_1.assert)(index < this.internal.size, "Index cannot be greater than the size of the list"); this.internal.remove(index); } /** * Moves one element of the list from one index to another. * @param from - The index of the element to move. * @param to - The destination index of the element. * @throws An {@link AssertionError} if not inside a write transaction or if any of the input indexes * is less than 0 or greater than or equal to the size of the list. */ move(from, to) { internal_1.assert.inTransaction(this.realm); internal_1.assert.number(from, "from"); internal_1.assert.number(to, "to"); const size = this.internal.size; (0, internal_1.assert)(from >= 0 && to >= 0, "Indexes cannot be smaller than 0"); (0, internal_1.assert)(from < size && to < size, "Indexes cannot be greater than the size of the list"); this.internal.move(from, to); } /** * Swaps the positions of the elements of the list at two indexes. * @param index1 - The index of the first element. * @param index2 - The index of the second element. * @throws An {@link AssertionError} if not inside a write transaction or if any of the input indexes * is less than 0 or greater than or equal to the size of the list. */ swap(index1, index2) { internal_1.assert.inTransaction(this.realm); internal_1.assert.number(index1, "index1"); internal_1.assert.number(index2, "index2"); const size = this.internal.size; (0, internal_1.assert)(index1 >= 0 && index2 >= 0, "Indexes cannot be smaller than 0"); (0, internal_1.assert)(index1 < size && index2 < size, "Indexes cannot be greater than the size of the list"); this.internal.swap(index1, index2); } } exports.List = List; //# sourceMappingURL=List.js.map