@react-native-ohos/realm
Version:
Realm by MongoDB is an offline-first mobile database: an alternative to SQLite and key-value stores
255 lines • 11 kB
JavaScript
;
////////////////////////////////////////////////////////////////////////////
//
// 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.Dictionary = void 0;
const assert_1 = require("./assert");
const binding_1 = require("./binding");
const indirect_1 = require("./indirect");
const Collection_1 = require("./Collection");
const errors_1 = require("./errors");
const JSONCacheMap_1 = require("./JSONCacheMap");
const TypeHelpers_1 = require("./TypeHelpers");
const Object_1 = require("./Object");
const Results_1 = require("./collection-accessors/Results");
/* eslint-disable jsdoc/multiline-blocks -- We need this to have @ts-expect-error located correctly in the .d.ts bundle */
const REALM = Symbol("Dictionary#realm");
const INTERNAL = Symbol("Dictionary#internal");
const DEFAULT_PROPERTY_DESCRIPTOR = { configurable: true, enumerable: true };
const PROXY_HANDLER = {
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
if (typeof value === "undefined" && typeof prop === "string") {
return target[Collection_1.COLLECTION_ACCESSOR].get(target[INTERNAL], prop);
}
else {
return value;
}
},
set(target, prop, value) {
if (typeof prop === "string") {
target[Collection_1.COLLECTION_ACCESSOR].set(target[INTERNAL], prop, value);
return true;
}
else {
(0, assert_1.assert)(typeof prop !== "symbol", "Symbols cannot be used as keys of a dictionary");
return false;
}
},
deleteProperty(target, prop) {
// We're intentionally not checking !Reflect.has(target, prop) below to allow deletes to propagate for any key
if (typeof prop === "string") {
const internal = target[INTERNAL];
internal.tryErase(prop);
// We consider any key without a value as "deletable", the same way `const foo = {}; delete foo.bar;` returns true
return true;
}
else {
return false;
}
},
ownKeys(target) {
const internal = target[INTERNAL];
const result = Reflect.ownKeys(target);
const keys = internal.keys.snapshot();
for (let i = 0; i < keys.size(); i++) {
const key = keys.getAny(i);
assert_1.assert.string(key, "dictionary key");
result.push(key);
}
return result;
},
getOwnPropertyDescriptor(target, prop) {
const internal = target[INTERNAL];
if (typeof prop === "string" && internal.contains(prop)) {
return {
...DEFAULT_PROPERTY_DESCRIPTOR,
get: PROXY_HANDLER.get?.bind(null, target, prop, null),
set: PROXY_HANDLER.set?.bind(null, target, prop, null),
};
}
else {
return Reflect.getOwnPropertyDescriptor(target, prop);
}
},
};
/**
* Instances of this class are returned when accessing object properties whose type is `"Dictionary"`
*
* Dictionaries behave mostly like a JavaScript object i.e., as a key/value pair
* where the key is a string.
*/
class Dictionary extends Collection_1.Collection {
/**
* The representation in the binding.
* @internal
*/
[INTERNAL];
/**
* Create a `Results` wrapping a set of query `Results` from the binding.
* @internal
*/
constructor(realm, internal, accessor, typeHelpers) {
if (arguments.length === 0 || !(internal instanceof binding_1.binding.Dictionary)) {
throw new errors_1.IllegalConstructorError("Dictionary");
}
super(accessor, typeHelpers, (listener, keyPaths) => {
return this[INTERNAL].addKeyBasedNotificationCallback(({ deletions, insertions, modifications }) => {
try {
listener(proxied, {
deletions: deletions.map((value) => {
assert_1.assert.string(value);
return value;
}),
insertions: insertions.map((value) => {
assert_1.assert.string(value);
return value;
}),
modifications: modifications.map((value) => {
assert_1.assert.string(value);
return value;
}),
});
}
catch (err) {
// Scheduling a throw on the event loop,
// since throwing synchronously here would result in an abort in the calling C++
setImmediate(() => {
throw err;
});
}
}, keyPaths ? realm.internal.createKeyPathArray(internal.objectSchema.name, keyPaths) : keyPaths);
});
const proxied = new Proxy(this, PROXY_HANDLER);
Object.defineProperty(this, REALM, {
enumerable: false,
configurable: false,
writable: false,
value: realm,
});
this[INTERNAL] = internal;
return proxied;
}
/**
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries Array.prototype.entries}
* @returns An iterator with all entries in the dictionary.
*/
*[Symbol.iterator]() {
yield* this.entries();
}
/**
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/keys Array.prototype.keys}
* @returns An iterator with all values in the dictionary.
* @since 10.5.0
* @ts-expect-error We're exposing methods in the end-users namespace of keys */
*keys() {
const snapshot = this[INTERNAL].keys.snapshot();
const size = snapshot.size();
for (let i = 0; i < size; i++) {
const key = snapshot.getAny(i);
assert_1.assert.string(key);
yield key;
}
}
/**
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values Array.prototype.values}
* @returns An iterator with all values in the dictionary.
* @since 10.5.0
* @ts-expect-error We're exposing methods in the end-users namespace of values */
*values() {
const realm = this[REALM];
const values = this[INTERNAL].values;
const itemType = (0, TypeHelpers_1.toItemType)(values.type);
const typeHelpers = this[Collection_1.COLLECTION_TYPE_HELPERS];
const accessor = (0, Results_1.createResultsAccessor)({ realm, typeHelpers, itemType });
const results = new indirect_1.indirect.Results(realm, values, accessor, typeHelpers);
for (const value of results.values()) {
yield value;
}
}
/**
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries Array.prototype.entries}
* @returns An iterator with all key/value pairs in the dictionary.
* @since 10.5.0
* @ts-expect-error We're exposing methods in the end-users namespace of entries */
*entries() {
const keys = this[INTERNAL].keys.snapshot();
const snapshot = this[INTERNAL].values.snapshot();
const size = keys.size();
(0, assert_1.assert)(size === snapshot.size(), "Expected keys and values to equal in size");
const realm = this[REALM];
const itemType = (0, TypeHelpers_1.toItemType)(snapshot.type);
const typeHelpers = this[Collection_1.COLLECTION_TYPE_HELPERS];
const accessor = (0, Results_1.createResultsAccessor)({ realm, typeHelpers, itemType });
const results = new indirect_1.indirect.Results(realm, snapshot, accessor, typeHelpers);
for (let i = 0; i < size; i++) {
const key = keys.getAny(i);
const value = results[i];
yield [key, value];
}
}
/**
* Checks if this dictionary has not been deleted and is part of a valid Realm.
* @returns `true` if the dictionary can be safely accessed.
* @since 0.14.0
* @ts-expect-error We're exposing methods in the end-users namespace of keys */
isValid() {
return this[INTERNAL].isValid;
}
/**
* Adds one or more elements with the specified key and value to the dictionary or updates value if key exists.
* @param elementsOrKey - The element to add or the key of the element to add.
* @param value - The value of the element to add.
* @throws An {@link AssertionError} if not inside a write transaction, if using symbol as keys, or if any value violates type constraints.
* @returns The dictionary.
* @since 10.6.0
*/
set(elementsOrKey, value) {
assert_1.assert.inTransaction(this[REALM]);
const elements = typeof elementsOrKey === "object" ? elementsOrKey : { [elementsOrKey]: value };
(0, assert_1.assert)(Object.getOwnPropertySymbols(elements).length === 0, "Symbols cannot be used as keys of a dictionary");
for (const [key, value] of Object.entries(elements)) {
this[key] = value;
}
return this;
}
/**
* Removes elements from the dictionary, with the keys provided.
* This does not throw if the keys are already missing from the dictionary.
* @param key - The key to be removed.
* @throws An {@link AssertionError} if not inside a write transaction.
* @returns The dictionary
* @since 10.6.0
* @ts-expect-error We're exposing methods in the end-users namespace of keys */
remove(key) {
assert_1.assert.inTransaction(this[REALM]);
const internal = this[INTERNAL];
const keys = typeof key === "string" ? [key] : key;
for (const k of keys) {
internal.tryErase(k);
}
return this;
}
/** @internal */
toJSON(_, cache = new JSONCacheMap_1.JSONCacheMap()) {
return Object.fromEntries(Object.entries(this).map(([k, v]) => [k, v instanceof Object_1.RealmObject ? v.toJSON(k, cache) : v]));
}
}
exports.Dictionary = Dictionary;
(0, indirect_1.injectIndirect)("Dictionary", Dictionary);
//# sourceMappingURL=Dictionary.js.map