diff --git a/dist/browser.js b/dist/browser.js index 39861fd..d00ab68 100644 --- a/dist/browser.js +++ b/dist/browser.js @@ -241,7 +241,7 @@ class AceBaseBase extends EventEmitter { } module.exports = { AceBaseBase, AceBaseSettings }; -},{"./data-reference":7,"./type-mappings":16,"events":42}],5:[function(require,module,exports){ +},{"./data-reference":7,"./type-mappings":16,"events":41}],5:[function(require,module,exports){ class Api { // interface for local and web api's @@ -467,11 +467,11 @@ class DataReference { /** * Returns a new reference to a child node - * @param {string} childPath Child key or path + * @param {string|number} childPath Child key, index or path * @returns {DataReference} reference to the child */ child(childPath) { - childPath = childPath.replace(/^\/|\/$/g, ""); + childPath = typeof childPath === 'number' ? childPath : childPath.replace(/^\/|\/$/g, ""); const currentPath = PathInfo.fillVariables2(this.path, this.vars); const targetPath = PathInfo.getChildPath(currentPath, childPath); return new DataReference(this.db, targetPath); // `${this.path}/${childPath}` @@ -2607,7 +2607,7 @@ function cloneObject(original, stack) { } } if (typeof stack === "undefined") { stack = [original]; } - const clone = {}; + const clone = original instanceof Array ? [] : {}; Object.keys(original).forEach(key => { let val = original[key]; if (typeof val === "function") { @@ -2685,7 +2685,7 @@ module.exports = { }; }).call(this,require("buffer").Buffer) -},{"./data-snapshot":8,"./path-reference":13,"buffer":41}],18:[function(require,module,exports){ +},{"./data-snapshot":8,"./path-reference":13,"buffer":40}],18:[function(require,module,exports){ /* The MIT License (MIT) @@ -2888,7 +2888,7 @@ for (var map in colors.maps) { defineProps(colors, init()); -},{"./custom/trap":19,"./custom/zalgo":20,"./maps/america":23,"./maps/rainbow":24,"./maps/random":25,"./maps/zebra":26,"./styles":27,"./system/supports-colors":29,"util":47}],19:[function(require,module,exports){ +},{"./custom/trap":19,"./custom/zalgo":20,"./maps/america":23,"./maps/rainbow":24,"./maps/random":25,"./maps/zebra":26,"./styles":27,"./system/supports-colors":29,"util":46}],19:[function(require,module,exports){ module['exports'] = function runTheTrap(text, options) { var result = ''; text = text || 'Run the trap, drop the bass'; @@ -3332,7 +3332,7 @@ module.exports = function(flag, argv) { }; }).call(this,require('_process')) -},{"_process":45}],29:[function(require,module,exports){ +},{"_process":44}],29:[function(require,module,exports){ (function (process){ /* The MIT License (MIT) @@ -3487,7 +3487,7 @@ module.exports = { }; }).call(this,require('_process')) -},{"./has-flag.js":28,"_process":45,"os":44}],30:[function(require,module,exports){ +},{"./has-flag.js":28,"_process":44,"os":43}],30:[function(require,module,exports){ const { AceBase, AceBaseLocalSettings } = require('./acebase-local'); const { CustomStorageSettings, CustomStorageTransaction, CustomStorageHelpers, ICustomStorageNode, ICustomStorageNodeMetaData } = require('./storage-custom'); @@ -3584,44 +3584,6 @@ class BrowserAceBase extends AceBase { }); return new AceBase(dbname, { logLevel: settings.logLevel, storage: storageSettings }); } - - /** - * Creates an AceBase database instance using LocalStorage as storage engine - * @param {string} dbname Name of the database - * @param {object} [settings] optional settings - * @param {string} [settings.logLevel] what level to use for logging to the console - * @param {boolean} [settings.temp] whether to use sessionStorage instead of localStorage - * @param {any} [settings.provider] Alternate localStorage provider. Eg using 'node-localstorage' - */ - static WithLocalStorage(dbname, settings) { - - settings = settings || {}; - if (!settings.logLevel) { settings.logLevel = 'error'; } - - // Determine whether to use localStorage or sessionStorage - const localStorage = settings.provider ? settings.provider : settings.temp ? window.localStorage : window.sessionStorage; - - // Setup our CustomStorageSettings - const storageSettings = new CustomStorageSettings({ - name: 'LocalStorage', - locking: true, - ready() { - // LocalStorage is always ready - return Promise.resolve(); - }, - getTransaction(target) { - // Create an instance of our transaction class - const context = { - debug: true, - dbname, - localStorage - } - const transaction = new LocalStorageTransaction(context, target); - return Promise.resolve(transaction); - } - }); - return new AceBase(dbname, { logLevel: settings.logLevel, storage: storageSettings }); - } } class IndexedDBStorageTransaction extends CustomStorageTransaction { @@ -3852,6 +3814,100 @@ class IndexedDBStorageTransaction extends CustomStorageTransaction { } +module.exports = { BrowserAceBase }; +},{"./acebase-local":31,"./storage-custom":38}],31:[function(require,module,exports){ +/** + ________________________________________________________________________________ + + ___ ______ + / _ \ | ___ \ + / /_\ \ ___ ___| |_/ / __ _ ___ ___ + | _ |/ __/ _ \ ___ \/ _` / __|/ _ \ + | | | | (_| __/ |_/ / (_| \__ \ __/ + \_| |_/\___\___\____/ \__,_|___/\___| + + Copyright 2018 by Ewout Stortenbeker (me@appy.one) + Published under MIT license + ________________________________________________________________________________ + + */ +const { AceBaseBase, AceBaseSettings } = require('acebase-core'); +const { StorageSettings } = require('./storage'); +const { LocalApi } = require('./api-local'); +const { CustomStorageSettings, CustomStorageTransaction, CustomStorageHelpers, ICustomStorageNode, ICustomStorageNodeMetaData } = require('./storage-custom'); + +class AceBaseLocalSettings { + /** + * + * @param {{ logLevel: 'verbose'|'log'|'warn'|'error', storage: StorageSettings }} options + */ + constructor(options) { + if (!options) { options = {}; } + this.logLevel = options.logLevel || 'log'; + this.storage = options.storage; ////new StorageOptions(options.storage); + } +} + +class AceBase extends AceBaseBase { + + /** + * + * @param {string} dbname | Name of the database to open or create + * @param {AceBaseLocalSettings} options | + */ + constructor(dbname, options) { + options = new AceBaseLocalSettings(options); + super(dbname, options); + const apiSettings = { + db: this, + storage: options.storage, + logLevel: options.logLevel + }; + this.api = new LocalApi(dbname, apiSettings, ready => { + this.emit("ready"); + }); + } + + /** + * Creates an AceBase database instance using LocalStorage or SessionStorage as storage engine. When running in non-browser environments, set + * settings.provider to a custom LocalStorage provider, eg 'node-localstorage' + * @param {string} dbname Name of the database + * @param {object} [settings] optional settings + * @param {string} [settings.logLevel] what level to use for logging to the console + * @param {boolean} [settings.temp] whether to use sessionStorage instead of localStorage + * @param {any} [settings.provider] Alternate localStorage provider for running in non-browser environments. Eg using 'node-localstorage' + */ + static WithLocalStorage(dbname, settings) { + + settings = settings || {}; + if (!settings.logLevel) { settings.logLevel = 'error'; } + + // Determine whether to use localStorage or sessionStorage + const localStorage = settings.provider ? settings.provider : settings.temp ? window.localStorage : window.sessionStorage; + + // Setup our CustomStorageSettings + const storageSettings = new CustomStorageSettings({ + name: 'LocalStorage', + locking: true, + ready() { + // LocalStorage is always ready + return Promise.resolve(); + }, + getTransaction(target) { + // Create an instance of our transaction class + const context = { + debug: true, + dbname, + localStorage + } + const transaction = new LocalStorageTransaction(context, target); + return Promise.resolve(transaction); + } + }); + return new AceBase(dbname, { logLevel: settings.logLevel, storage: storageSettings }); + } +} + // Setup CustomStorageTransaction for browser's LocalStorage class LocalStorageTransaction extends CustomStorageTransaction { @@ -3964,69 +4020,14 @@ class LocalStorageTransaction extends CustomStorageTransaction { } } -module.exports = { BrowserAceBase }; -},{"./acebase-local":31,"./storage-custom":38}],31:[function(require,module,exports){ -/** - ________________________________________________________________________________ - - ___ ______ - / _ \ | ___ \ - / /_\ \ ___ ___| |_/ / __ _ ___ ___ - | _ |/ __/ _ \ ___ \/ _` / __|/ _ \ - | | | | (_| __/ |_/ / (_| \__ \ __/ - \_| |_/\___\___\____/ \__,_|___/\___| - - Copyright 2018 by Ewout Stortenbeker (me@appy.one) - Published under MIT license - ________________________________________________________________________________ - - */ -const { AceBaseBase, AceBaseSettings } = require('acebase-core'); -const { StorageSettings } = require('./storage'); -const { LocalApi } = require('./api-local'); - -class AceBaseLocalSettings { - /** - * - * @param {{ logLevel: 'verbose'|'log'|'warn'|'error', storage: StorageSettings }} options - */ - constructor(options) { - if (!options) { options = {}; } - this.logLevel = options.logLevel || 'log'; - this.storage = options.storage; ////new StorageOptions(options.storage); - } -} - -class AceBase extends AceBaseBase { - - /** - * - * @param {string} dbname | Name of the database to open or create - * @param {AceBaseLocalSettings} options | - */ - constructor(dbname, options) { - options = new AceBaseLocalSettings(options); - super(dbname, options); - const apiSettings = { - db: this, - storage: options.storage, - logLevel: options.logLevel - }; - this.api = new LocalApi(dbname, apiSettings, ready => { - this.emit("ready"); - }); - } -} - module.exports = { AceBase, AceBaseLocalSettings }; -},{"./api-local":32,"./storage":40,"acebase-core":11}],32:[function(require,module,exports){ +},{"./api-local":32,"./storage":39,"./storage-custom":38,"acebase-core":11}],32:[function(require,module,exports){ const { Api, Utils } = require('acebase-core'); const { AceBase } = require('./acebase-local'); const { StorageSettings } = require('./storage'); const { AceBaseStorage, AceBaseStorageSettings } = require('./storage-acebase'); const { SQLiteStorage, SQLiteStorageSettings } = require('./storage-sqlite'); const { MSSQLStorage, MSSQLStorageSettings } = require('./storage-mssql'); -const { LocalStorage, LocalStorageSettings } = require('./storage-localstorage'); const { CustomStorage, CustomStorageSettings } = require('./storage-custom'); const { Node } = require('./node'); const { DataIndex } = require('./data-index'); @@ -4050,9 +4051,6 @@ class LocalApi extends Api { else if (MSSQLStorageSettings && (settings.storage instanceof MSSQLStorageSettings || settings.storage.type === 'mssql')) { this.storage = new MSSQLStorage(dbname, settings.storage); } - else if (LocalStorageSettings && (settings.storage instanceof LocalStorageSettings || settings.storage.type === 'localstorage')) { - this.storage = new LocalStorage(dbname, settings.storage); - } else if (CustomStorageSettings && (settings.storage instanceof CustomStorageSettings || settings.storage.type === 'custom')) { this.storage = new CustomStorage(dbname, settings.storage); } @@ -4852,7 +4850,7 @@ class LocalApi extends Api { key: typeof childInfo.key === 'string' ? childInfo.key : childInfo.index, type: childInfo.valueTypeName, value: childInfo.value, - // address is now only added when storage is acebase. Not when eg sqlite, mssql, localstorage + // address is now only added when storage is acebase. Not when eg sqlite, mssql address: typeof childInfo.address === 'object' && 'pageNr' in childInfo.address ? { pageNr: childInfo.address.pageNr, recordNr: childInfo.address.recordNr } : undefined }); } @@ -4907,7 +4905,7 @@ class LocalApi extends Api { } module.exports = { LocalApi }; -},{"./acebase-local":31,"./data-index":41,"./node":37,"./storage":40,"./storage-acebase":41,"./storage-custom":38,"./storage-localstorage":39,"./storage-mssql":41,"./storage-sqlite":41,"acebase-core":11}],33:[function(require,module,exports){ +},{"./acebase-local":31,"./data-index":40,"./node":37,"./storage":39,"./storage-acebase":40,"./storage-custom":38,"./storage-mssql":40,"./storage-sqlite":40,"acebase-core":11}],33:[function(require,module,exports){ /* * This file is used to create a browser bundle, (re)generate it with: npm run browserify @@ -4931,7 +4929,6 @@ module.exports = { LocalApi }; const { DataReference, DataSnapshot, EventSubscription, PathReference, TypeMappings, TypeMappingOptions } = require('acebase-core'); const { AceBaseLocalSettings } = require('./acebase-local'); const { BrowserAceBase } = require('./acebase-browser'); -const { LocalStorageSettings } = require('./storage-localstorage'); const { CustomStorageSettings, CustomStorageTransaction, CustomStorageHelpers } = require('./storage-custom'); const acebase = { @@ -4943,7 +4940,6 @@ const acebase = { PathReference, TypeMappings, TypeMappingOptions, - LocalStorageSettings, CustomStorageSettings, CustomStorageTransaction, CustomStorageHelpers @@ -4955,7 +4951,7 @@ window.acebase = acebase; window.AceBase = BrowserAceBase; // Expose classes for module imports: module.exports = acebase; -},{"./acebase-browser":30,"./acebase-local":31,"./storage-custom":38,"./storage-localstorage":39,"acebase-core":11}],34:[function(require,module,exports){ +},{"./acebase-browser":30,"./acebase-local":31,"./storage-custom":38,"acebase-core":11}],34:[function(require,module,exports){ const { VALUE_TYPES, getValueTypeName } = require('./node-value-types'); const { PathInfo } = require('acebase-core'); @@ -5636,7 +5632,7 @@ module.exports = { Node, NodeInfo }; -},{"./node-info":34,"./node-value-types":36,"./storage":40,"colors":22}],38:[function(require,module,exports){ +},{"./node-info":34,"./node-value-types":36,"./storage":39,"colors":22}],38:[function(require,module,exports){ const { debug, ID, PathReference, PathInfo, ascii85 } = require('acebase-core'); const { NodeInfo } = require('./node-info'); const { NodeLocker } = require('./node-lock'); @@ -6168,7 +6164,7 @@ class CustomStorage extends Storage { * Creates or updates a node in its own record. DOES NOT CHECK if path exists in parent node, or if parent paths exist! Calling code needs to do this * @param {string} path * @param {any} value - * @param {object} options] + * @param {object} options * @param {CustomStorageTransaction} options.transaction * @param {boolean} [options.merge=false] * @param {string} [options.revision] @@ -6186,9 +6182,19 @@ class CustomStorage extends Storage { // Get info about current node at path const currentRow = await this._readNode(path, { transaction }); + + if (options.merge && currentRow) { + if (currentRow.type === VALUE_TYPES.ARRAY && !(value instanceof Array) && typeof value === 'object' && Object.keys(value).some(key => isNaN(key))) { + throw new Error(`Cannot merge existing array of path "${path}" with an object`); + } + if (value instanceof Array && currentRow.type !== VALUE_TYPES.ARRAY) { + throw new Error(`Cannot merge existing object of path "${path}" with an array`); + } + } + const revision = options.revision || ID.generate(); let mainNode = { - type: VALUE_TYPES.OBJECT, + type: currentRow && currentRow.type === VALUE_TYPES.ARRAY ? VALUE_TYPES.ARRAY : VALUE_TYPES.OBJECT, value: {} }; const childNodeValues = {}; @@ -6269,6 +6275,7 @@ class CustomStorage extends Storage { } // Insert or update node + const isArray = mainNode.type === VALUE_TYPES.ARRAY; if (currentRow) { // update this.debug.log(`Node "/${path}" is being ${options.merge ? 'updated' : 'overwritten'}`.cyan); @@ -6293,7 +6300,7 @@ class CustomStorage extends Storage { throw new Error(`${this._customImplementation.info} childrenOf did not call checkCallback before addCallback`); } const key = PathInfo.get(childPath).key; - keys.push(key); + keys.push(key.toString()); // .toString to make sure all keys are compared as strings return true; // Keep streaming } await transaction.childrenOf(path, { metadata: false, value: false }, includeChildCheck, addChildPath); @@ -6312,12 +6319,27 @@ class CustomStorage extends Storage { const changes = { insert: children.new.filter(key => !children.current.includes(key)), - update: children.new.filter(key => children.current.includes(key)), + update: [], delete: options && options.merge ? Object.keys(value).filter(key => value[key] === null) : children.current.filter(key => !children.new.includes(key)), }; + changes.update = children.new.filter(key => children.current.includes(key) && !changes.delete.includes(key)); + + if (isArray && options.merge && (changes.insert.length > 0 || changes.delete.length > 0)) { + // deletes or inserts of individual array entries are not allowed, unless it is the last entry: + // - deletes would cause the paths of following items to change, which is unwanted because the actual data does not change, + // eg: removing index 3 on array of size 10 causes entries with index 4 to 9 to 'move' to indexes 3 to 8 + // - inserts might introduce gaps in indexes, + // eg: adding to index 7 on an array of size 3 causes entries with indexes 3 to 6 to go 'missing' + const newArrayKeys = changes.update.concat(changes.insert); + const isExhaustive = newArrayKeys.every((k, index, arr) => arr.includes(index.toString())); + if (!isExhaustive) { + throw new Error(`Elements cannot be inserted beyond, or removed before the end of an array. Rewrite the whole array at path "${path}" or change your schema to use an object collection instead`); + } + } // (over)write all child nodes that must be stored in their own record const writePromises = Object.keys(childNodeValues).map(key => { + if (isArray) { key = parseInt(key); } const childPath = pathInfo.childPath(key); // PathInfo.getChildPath(path, key); const childValue = childNodeValues[key]; return this._writeNode(childPath, childValue, { transaction, revision, merge: false }); @@ -6328,6 +6350,7 @@ class CustomStorage extends Storage { const movingNodes = keys.filter(key => key in mainNode.value); // moving from dedicated to inline value const deleteDedicatedKeys = changes.delete.concat(movingNodes); const deletePromises = deleteDedicatedKeys.map(key => { + if (isArray) { key = parseInt(key); } const childPath = pathInfo.childPath(key); //PathInfo.getChildPath(path, key); return this._deleteNode(childPath, { transaction }); }); @@ -6354,7 +6377,17 @@ class CustomStorage extends Storage { // write all child nodes that must be stored in their own record this.debug.log(`Node "/${path}" is being created`.cyan); + if (isArray) { + // Check if the array is "intact" (all entries have an index from 0 to the end with no gaps) + const arrayKeys = Object.keys(mainNode.value).concat(Object.keys(childNodeValues)); + const isExhaustive = arrayKeys.every((k, index, arr) => arr.includes(index.toString())); + if (!isExhaustive) { + throw new Error(`Cannot store arrays with missing entries`); + } + } + const promises = Object.keys(childNodeValues).map(key => { + if (isArray) { key = parseInt(key); } const childPath = PathInfo.getChildPath(path, key); const childValue = childNodeValues[key]; return this._writeNode(childPath, childValue, { transaction, revision, merge: false }); @@ -7088,1016 +7121,7 @@ module.exports = { ICustomStorageNodeMetaData, ICustomStorageNode } -},{"./node-info":34,"./node-lock":35,"./node-value-types":36,"./storage":40,"acebase-core":11}],39:[function(require,module,exports){ -const { debug, ID, PathReference, PathInfo, ascii85 } = require('acebase-core'); -const { NodeInfo } = require('./node-info'); -const { VALUE_TYPES } = require('./node-value-types'); -const { Storage, StorageSettings, NodeNotFoundError } = require('./storage'); - -class LocalStorageSettings extends StorageSettings { - constructor(settings) { - super(settings); - settings = settings || {}; - this.session = settings.session === true; // Whether to use sessionStorage instead of localStorage - this.provider = typeof settings.provider === 'object' ? settings.provider : null; - } -}; - -class LocalStorageNodeAddress { - constructor(containerPath) { - this.path = containerPath; - } -} - -class LocalStorageNodeInfo extends NodeInfo { - constructor(info) { - super(info); - - /** @type {LocalStorageNodeAddress} */ - this.address; // no assignment, only typedef - - /** @type {string} */ - this.revision = info.revision; - /** @type {number} */ - this.revision_nr = info.revision_nr; - /** @type {Date} */ - this.created = info.created; - /** @type {Date} */ - this.modified = info.modified; - } -} - -class LocalStorage extends Storage { - - /** - * - * @param {string} dbname - * @param {LocalStorageSettings} settings - */ - constructor(dbname, settings) { - super(dbname, settings); - - this._init(); - } - - _init() { - if (this.settings.provider !== null && typeof this.settings.provider === 'object') { - // Custom localStorage implementation. Implemented for testing on Node.js without having to add dependency to project - this._localStorage = this.settings.provider; - } - else { - if (!this.settings.session && typeof localStorage === 'undefined') { - throw new Error(`No localStorage available. If you are on Node: npm i node-localstorage`); - } - if (this.settings.session === true && typeof sessionStorage === 'undefined') { - throw new Error(`No sessionStorage available`); - } - this._localStorage = this.settings.session === true ? sessionStorage : localStorage; - } - - this.debug.log(`Database "${this.name}" details:`.intro); - this.debug.log(`- Type: LocalStorage`); - this.debug.log(`- Max inline value size: ${this.settings.maxInlineValueSize}`.intro); - - // Create root node if it's not there yet - return this.getNodeInfo('') - .then(info => { - if (!info.exists) { - return this._writeNode('', {}); - } - }) - .then(() => { - return this.indexes.supported && this.indexes.load(); - }) - .then(() => { - this.emit('ready'); - }); - } - - get _keyPrefix() { - return `${this.name}.acebase::`; - } - _getPathFromKey(key) { - return key.slice(this._keyPrefix.length); - } - _getKeyFromPath(path) { - return `${this._keyPrefix}${path}`; - } - - /** - * - * @param {string} path - * @param {object} info - * @param {number} info.type - * @param {any} info.value - * @param {string} info.revision - * @param {number} info.revision_nr - * @param {number} info.created - * @param {number} info.modified - */ - _storeNode(path, info) { - // serialize the value to store - const getTypedChildValue = val => { - if (val === null) { - throw new Error(`Not allowed to store null values. remove the property`); - } - else if (['string','number','boolean'].includes(typeof val)) { - return val; - } - else if (val instanceof Date) { - return { type: VALUE_TYPES.DATETIME, value: val.getTime() }; - } - else if (val instanceof PathReference) { - return { type: VALUE_TYPES.REFERENCE, value: child.path }; - } - else if (val instanceof ArrayBuffer) { - return { type: VALUE_TYPES.BINARY, value: ascii85.encode(val) }; - } - else if (typeof val === 'object') { - console.assert(Object.keys(val).length === 0, 'child object stored in parent can only be empty'); - return val; - } - } - - const unprocessed = `Caller should have pre-processed the value by converting it to a string`; - if (info.type === VALUE_TYPES.ARRAY && info.value instanceof Array) { - // Convert array to object with numeric properties - // NOTE: caller should have done this already - console.warn(`Unprocessed array. ${unprocessed}`); - const obj = {}; - for (let i = 0; i < info.value.length; i++) { - obj[i] = info.value[i]; - } - info.value = obj; - } - if (info.type === VALUE_TYPES.BINARY && typeof info.value !== 'string') { - console.warn(`Unprocessed binary value. ${unprocessed}`); - info.value = ascii85.encode(info.value); - } - if (info.type === VALUE_TYPES.REFERENCE && info.value instanceof PathReference) { - console.warn(`Unprocessed path reference. ${unprocessed}`); - info.value = info.value.path; - } - if ([VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(info.type)) { - const original = info.value; - info.value = {}; - // If original is an array, it'll automatically be converted to an object now - Object.keys(original).forEach(key => { - info.value[key] = getTypedChildValue(original[key]); - }); - } - - // Now stringify it for storage - const json = JSON.stringify(info); - this._localStorage.setItem(this._getKeyFromPath(path), json); - } - - _readNode(path) { - // deserialize a stored value (always an object with "type", "value", "revision", "revision_nr", "created", "modified") - let val = this._localStorage.getItem(this._getKeyFromPath(path)); - if (val === null) { return null; } - val = JSON.parse(val); - - const getTypedChildValue = val => { - // Typed value stored in parent record - if (val.type === VALUE_TYPES.BINARY) { - // binary stored in a parent record as a string - return ascii85.decode(val.value); - } - else if (val.type === VALUE_TYPES.DATETIME) { - // Date value stored as number - return new Date(val.value); - } - else if (val.type === VALUE_TYPES.REFERENCE) { - // Path reference stored as string - return new PathReference(val.value); - } - else { - throw new Error(`Unhandled child value type ${val.type}`); - } - } - - const node = { - type: val.type, - value: val.value, - revision: val.revision, - revision_nr: val.revision_nr, - created: val.created, - modified: val.modified - }; - - switch (val.type) { - - // case VALUE_TYPES.ARRAY: { - // // Array is stored as object with numeric properties - // // check if any value needs to be converted - // const arr = val.value; - // for (let i = 0; i < arr.length; i++) { - // let item = arr[i]; - // if (typeof item === 'object' && 'type' in object) { - // arr[i] = getTypedChildValue(item); - // } - // } - // return { type: val.type, value: arr }; - // } - - case VALUE_TYPES.ARRAY: - case VALUE_TYPES.OBJECT: { - // check if any value needs to be converted - // NOTE: Arrays are stored with numeric properties - const obj = val.value; - Object.keys(obj).forEach(key => { - let item = obj[key]; - if (typeof item === 'object' && 'type' in item) { - obj[key] = getTypedChildValue(item); - } - }); - node.value = obj; - break; - } - - case VALUE_TYPES.BINARY: { - node.value = ascii85.decode(val.value); - break; - } - - case VALUE_TYPES.STRING: { - node.value = val.value; - break; - } - - case VALUE_TYPES.REFERENCE: { - node.value = new PathReference(val.value); - break; - } - - default: - throw new Error(`Invalid standalone record value type`); // should never happen - } - return node; - } - - _getTypeFromStoredValue(val) { - let type; - if (typeof val === 'string') { - type = VALUE_TYPES.STRING; - } - else if (typeof val === 'number') { - type = VALUE_TYPES.NUMBER; - } - else if (typeof val === 'boolean') { - type = VALUE_TYPES.BOOLEAN; - } - else if (val instanceof Array) { - type = VALUE_TYPES.ARRAY; - } - else if (typeof val === 'object') { - if ('type' in val) { - type = val.type; - val = val.value; - if (type === VALUE_TYPES.DATETIME) { - val = new Date(val); - } - else if (type === VALUE_TYPES.REFERENCE) { - val = new PathReference(val); - } - } - else { - type = VALUE_TYPES.OBJECT; - } - } - else { - throw new Error(`Unknown value type`); - } - return { type, value: val }; - } - - - /** - * Creates or updates a node in its own record. DOES NOT CHECK if path exists in parent node, or if parent paths exist! Calling code needs to do this - * @param {string} path - * @param {any} value - * @param {object} [options] - * @returns {Promise} - */ - _writeNode(path, value, options = { merge: false, revision: null }) { - if (this.valueFitsInline(value) && path !== '') { - throw new Error(`invalid value to store in its own node`); - } - else if (path === '' && (typeof value !== 'object' || value instanceof Array)) { - throw new Error(`Invalid root node value. Must be an object`) - } - - // Get info about current node at path - const currentRow = this._readNode(path); - const newRevision = (options && options.revision) || ID.generate(); - let mainNode = { - type: VALUE_TYPES.OBJECT, - value: {} - }; - const childNodeValues = {}; - if (value instanceof Array) { - mainNode.type = VALUE_TYPES.ARRAY; - // Convert array to object with numeric properties - const obj = {}; - for (let i = 0; i < value.length; i++) { - obj[i] = value[i]; - } - value = obj; - } - else if (value instanceof PathReference) { - mainNode.type = VALUE_TYPES.REFERENCE; - mainNode.value = value.path; - } - else if (value instanceof ArrayBuffer) { - mainNode.type = VALUE_TYPES.BINARY; - mainNode.value = ascii85.encode(value); - } - else if (typeof value === 'string') { - mainNode.type = VALUE_TYPES.STRING; - mainNode.value = value; - } - - const currentIsObjectOrArray = currentRow ? [VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(currentRow.type) : false; - const newIsObjectOrArray = [VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(mainNode.type); - const children = { - current: [], - new: [] - } - - let currentObject = null; - if (currentIsObjectOrArray) { - currentObject = currentRow.value; - children.current = Object.keys(currentObject); - // if (currentObject instanceof Array) { // ALWAYS FALSE BECAUSE THEY ARE STORED AS OBJECTS WITH NUMERIC PROPERTIES - // // Convert array to object with numeric properties - // const obj = {}; - // for (let i = 0; i < value.length; i++) { - // obj[i] = value[i]; - // } - // currentObject = obj; - // } - if (newIsObjectOrArray) { - mainNode.value = currentObject; - } - } - if (newIsObjectOrArray) { - // Object or array. Determine which properties can be stored in the main node, - // and which should be stored in their own nodes - Object.keys(value).forEach(key => { - const val = value[key]; - delete mainNode.value[key]; // key is being overwritten, moved from inline to dedicated, or deleted. TODO: check if this needs to be done SQLite & MSSQL implementations too - if (val === null) { // || typeof val === 'undefined' - // This key is being removed - return; - } - else if (typeof val === "undefined") { - if (this.settings.removeVoidProperties === true) { - delete value[key]; // Kill the property in the passed object as well, to prevent differences in stored and working values - return; - } - else { - throw new Error(`Property "${key}" has invalid value. Cannot store undefined values. Set removeVoidProperties option to true to automatically remove undefined properties`); - } - } - // Where to store this value? - if (this.valueFitsInline(val)) { - // Store in main node - mainNode.value[key] = val; - } - else { - // Store in child node - childNodeValues[key] = val; - } - }); - } - - // Insert or update node - if (currentRow) { - // update - this.debug.log(`Node "/${path}" is being ${options.merge ? 'updated' : 'overwritten'}`.cyan); - - // If existing is an array or object, we have to find out which children are affected - if (currentIsObjectOrArray || newIsObjectOrArray) { - - // Get current child nodes in dedicated child records - const pathInfo = PathInfo.get(path); - const keys = []; - for (let i = 0; i < this._localStorage.length; i++) { - const key = this._localStorage.key(i); - if (!key.startsWith(this._keyPrefix)) { continue; } - let otherPath = this._getPathFromKey(key); - if (pathInfo.isParentOf(otherPath)) { - const key = PathInfo.get(otherPath).key; - keys.push(key); - } - } - children.current = children.current.concat(keys); - if (newIsObjectOrArray) { - if (options && options.merge) { - children.new = children.current.slice(); - } - Object.keys(value).forEach(key => { - if (!children.new.includes(key)) { - children.new.push(key); - } - }); - } - - const changes = { - insert: children.new.filter(key => !children.current.includes(key)), - update: children.new.filter(key => children.current.includes(key)), - delete: options && options.merge ? Object.keys(value).filter(key => value[key] === null) : children.current.filter(key => !children.new.includes(key)), - }; - - // (over)write all child nodes that must be stored in their own record - Object.keys(childNodeValues).map(key => { - const childPath = PathInfo.getChildPath(path, key); - const childValue = childNodeValues[key]; - this._writeNode(childPath, childValue, { revision: newRevision, merge: false }); - }); - - // Delete all child nodes that were stored in their own record, but are being removed - // Also delete nodes that are being moved from a dedicated record to inline - const movingNodes = keys.filter(key => key in mainNode.value); // moving from dedicated to inline value - const deleteDedicatedKeys = changes.delete.concat(movingNodes); - deleteDedicatedKeys.forEach(key => { - const childPath = PathInfo.getChildPath(path, key); - this._deleteNode(childPath); - }); - } - - // Update main node - this._storeNode(path, { - type: mainNode.type, - value: mainNode.value, - revision: currentRow.revision, - revision_nr: currentRow.revision_nr + 1, - created: currentRow.created, - modified: Date.now() - }); - } - else { - // Current node does not exist, create it and any child nodes - // write all child nodes that must be stored in their own record - this.debug.log(`Node "/${path}" is being created`.cyan); - - Object.keys(childNodeValues).map(key => { - const childPath = PathInfo.getChildPath(path, key); - const childValue = childNodeValues[key]; - this._writeNode(childPath, childValue, { revision: newRevision, merge: false }); - }); - - // Create current node - this._storeNode(path, { - type: mainNode.type, - value: mainNode.value, - revision: newRevision, - revision_nr: 1, - created: Date.now(), - modified: Date.now() - }); - } - } - - /** - * Deletes (dedicated) node and all subnodes without checking for existence. Use with care - all removed nodes will lose their revision stats! DOES NOT REMOVE INLINE CHILD NODES! - * @param {string} path - */ - _deleteNode(path) { - const pathInfo = PathInfo.get(path); - this.debug.log(`Node "/${path}" is being deleted`.cyan); - this._localStorage.removeItem(this._getKeyFromPath(path)); // Remove main node - for (let i = 0; i < this._localStorage.length; i++) { - const key = this._localStorage.key(i); - if (!key.startsWith(this._keyPrefix)) { continue; } - let otherPath = this._getPathFromKey(key); - if (pathInfo.isAncestorOf(otherPath)) { - this.debug.log(`Node "/${otherPath}" is being deleted`.cyan); - localStorage.removeItem(this._getKeyFromPath(otherPath)); // Remove child node - } - } - } - - /** - * Enumerates all children of a given Node for reflection purposes - * @param {string} path - * @param {string[]|number[]} [options.keyFilter] - */ - getChildren(path, options = { keyFilter: undefined, tid: undefined }) { - // return generator - var callback; //, resolve, reject; - const generator = { - /** - * - * @param {(child: NodeInfo) => boolean} valueCallback callback function to run for each child. Return false to stop iterating - * @returns {Promise} returns a promise that resolves with a boolean indicating if all children have been enumerated, or was canceled by the valueCallback function - */ - next(valueCallback) { - callback = valueCallback; - return start(); - } - }; - const start = () => { - let lock, canceled = false; - const tid = (options && options.tid) || ID.generate(); - return this.nodeLocker.lock(path, tid, false, 'getChildren') - .then(l => { - lock = l; - - let row = this._localStorage.getItem(this._getKeyFromPath(path)); - if (!row) { throw new NodeNotFoundError(`Node "/${path}" does not exist`); } - row = JSON.parse(row); - - if (![VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(row.type)) { - // No children - return; - } - const isArray = row.type === VALUE_TYPES.ARRAY; - const value = row.value; - let keys = Object.keys(value); - if (options.keyFilter) { - keys = keys.filter(key => options.keyFilter.includes(key)); - } - const pathInfo = PathInfo.get(path); - keys.length > 0 && keys.every(key => { - let child = this._getTypeFromStoredValue(value[key]); - - const info = new LocalStorageNodeInfo({ - path: pathInfo.childPath(key), - key: isArray ? null : key, - index: isArray ? key : null, - type: child.type, - address: null, - exists: true, - value: child.value, - revision: row.revision, - revision_nr: row.revision_nr, - created: row.created, - modified: row.modified - }); - - canceled = callback(info) === false; - return !canceled; // stop .every loop if canceled - }); - if (canceled) { - return; - } - - // Go on... get other children - const childRows = []; - for (let i = 0; i < this._localStorage.length; i++) { - const key = this._localStorage.key(i); - if (!key.startsWith(this._keyPrefix)) { continue; } - let otherPath = this._getPathFromKey(key); - if (pathInfo.isParentOf(otherPath)) { - const key = PathInfo.get(otherPath).key; - if (options.keyFilter && !options.keyFilter.includes(key)) { - continue; // ignore this one - } - - let row = this._readNode(otherPath); - childRows.push({ - type: row.type, - path: otherPath, - revision: row.revision, - revision_nr: row.revision_nr, - created: row.created, - modified: row.modified - }); - } - } - - const handleNextChild = i => { - const row = childRows[i]; - if (!row) { return; } - - const key = PathInfo.get(row.path).key; - if (options.keyFilter && !options.keyFilter.includes(key)) { - return handleNextChild(i+1); - } - - const info = new LocalStorageNodeInfo({ - path: row.path, - type: row.type, - key: isArray ? null : key, - index: isArray ? key : null, - address: new LocalStorageNodeAddress(row.path), //new SqlNodeAddress(row.path), - exists: true, - value: null, // not loaded - revision: row.revision, - revision_nr: row.revision_nr, - created: new Date(row.created), - modified: new Date(row.modified) - }); - - canceled = callback(info) === false; - if (!canceled) { - return handleNextChild(i+1); - } - } - return handleNextChild(0); - }) - .then(() => { - lock.release(); - return canceled; - }) - .catch(err => { - lock.release(); - throw err; - }); - }; // start() - return generator; - } - - getNode(path, options = { include: undefined, exclude: undefined, child_objects: true, tid: undefined }) { - // path = path.replace(/'/g, ''); // prevent sql injection, remove single quotes - - const tid = (options && options.tid )|| ID.generate(); - let lock; - return this.nodeLocker.lock(path, tid, false, 'getNode') - .then(l => { - lock = l; - - // Get path, path/* and path[* - const filtered = options && (options.include || options.exclude || options.child_objects === false); - const pathInfo = PathInfo.get(path); - const targetRow = this._readNode(path); - if (!targetRow) { - // Lookup parent node - if (path === '') { return { value: null }; } // path is root. There is no parent. - return lock.moveToParent() - .then(parentLock => { - lock = parentLock; - let parentNode = this._readNode(pathInfo.parentPath); - if (parentNode && [VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(parentNode.type) && pathInfo.key in parentNode) { - return { revision: parentNode.revision, value: parentNode.value[pathInfo.key] }; - } - return { value: null }; - }); - } - - const includeCheck = options.include - ? new RegExp('^' + options.include.map(p => '(?:' + p.replace(/\*/g, '[^/\\[]+') + ')').join('|') + '(?:$|[/\\[])') - : null; - const excludeCheck = options.exclude - ? new RegExp('^' + options.exclude.map(p => '(?:' + p.replace(/\*/g, '[^/\\[]+') + ')').join('|') + '(?:$|[/\\[])') - : null; - - const childRows = []; - for (let i = 0; i < this._localStorage.length; i++) { - const key = this._localStorage.key(i); - if (!key.startsWith(this._keyPrefix)) { continue; } - let otherPath = this._getPathFromKey(key); - let include = false; - if (pathInfo.isAncestorOf(otherPath)) { - - // Apply include & exclude filters - let checkPath = otherPath.slice(path.length); - if (checkPath[0] === '/') { checkPath = checkPath.slice(1); } - include = (includeCheck ? includeCheck.test(checkPath) : true) - && (excludeCheck ? !excludeCheck.test(checkPath) : true); - } - - // Apply child_objects filter - if (include - && options.child_objects === false - && (pathInfo.isParentOf(otherPath) && [VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(childNode.type) - || PathInfo.getPathKeys(otherPath).length > pathInfo.pathKeys.length + 1) - ) { - include = false; - } - - if (include) { - const childRow = this._readNode(otherPath); - childRow.path = otherPath; - childRows.push(childRow); - } - } - - this.debug.log(`Read node "/${path}" and ${filtered ? '(filtered) ' : ''}children from ${childRows.length + 1} records`.magenta); - - const result = { - revision: targetRow ? targetRow.revision : null, - value: targetRow.value - }; - - const objectToArray = obj => { - // Convert object value to array - const arr = []; - Object.keys(obj).forEach(key => { - let index = parseInt(key); - arr[index] = obj[index]; - }); - return arr; - }; - - if (targetRow.type === VALUE_TYPES.ARRAY) { - result.value = objectToArray(result.value); - } - - if (targetRow.type === VALUE_TYPES.OBJECT || targetRow.type === VALUE_TYPES.ARRAY) { - // target node is an object or array - // merge with other found (child) records - const targetPathKeys = PathInfo.getPathKeys(path); - let value = targetRow.value; - for (let i = 0; i < childRows.length; i++) { - const otherRow = childRows[i]; - const pathKeys = PathInfo.getPathKeys(otherRow.path); - const trailKeys = pathKeys.slice(targetPathKeys.length); - let parent = value; - for (let j = 0 ; j < trailKeys.length; j++) { - console.assert(typeof parent === 'object', 'parent must be an object/array to have children!!'); - const key = trailKeys[j]; - const isLast = j === trailKeys.length-1; - const nodeType = isLast - ? otherRow.type - : typeof trailKeys[j+1] === 'number' - ? VALUE_TYPES.ARRAY - : VALUE_TYPES.OBJECT; - let nodeValue; - if (!isLast) { - nodeValue = nodeType === VALUE_TYPES.OBJECT ? {} : []; - } - else { - nodeValue = otherRow.value; - if (nodeType === VALUE_TYPES.ARRAY) { - nodeValue = objectToArray(nodeValue); - } - } - if (key in parent) { - // Merge with parent - console.assert(typeof parent[key] === typeof nodeValue && [VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(nodeType), 'Merging child values can only be done if existing and current values are both an array or object'); - Object.keys(nodeValue).forEach(childKey => { - console.assert(!(childKey in parent[key]), 'child key is in parent value already?! HOW?!'); - parent[key][childKey] = nodeValue[childKey]; - }); - } - else { - parent[key] = nodeValue; - } - parent = parent[key]; - } - } - } - else if (childRows.length > 0) { - throw new Error(`multiple records found for non-object value!`); - } - - // Post process filters to remove any data that got though because they were - // not stored in dedicated records. This will happen with smaller values because - // they are stored inline in their parent node. - // eg: - // { number: 1, small_string: 'small string', bool: true, obj: {}, arr: [] } - // All properties of this object are stored inline, - // if exclude: ['obj'], or child_objects: false was passed, these will still - // have to be removed from the value - - if (options.child_objects === false) { - Object.keys(result.value).forEach(key => { - if (typeof result.value[key] === 'object' && result.value[key].constructor === Object) { - // This can only happen if the object was empty - console.assert(Object.keys(result.value[key]).length === 0); - delete result.value[key]; - } - }) - } - - if (options.exclude) { - const process = (obj, keys) => { - if (typeof obj !== 'object') { return; } - const key = keys[0]; - if (key === '*') { - Object.keys(obj).forEach(k => { - process(obj[k], keys.slice(1)); - }); - } - else if (keys.length > 1) { - key in obj && process(obj[key], keys.slice(1)); - } - else { - delete obj[key]; - } - }; - options.exclude.forEach(path => { - const checkKeys = PathInfo.getPathKeys(path); - process(result.value, checkKeys); - }); - } - return result; - }) - .then(result => { - lock.release(); - return result; - }) - .catch(err => { - lock.release(); - throw err; - }); - } - - /** - * - * @param {string} path - * @param {*} options - * @returns {Promise} - */ - getNodeInfo(path, options = { tid: undefined }) { - const pathInfo = PathInfo.get(path); - const tid = (options && options.tid) || ID.generate(); - let lock; - return this.nodeLocker.lock(path, tid, false, 'getNodeInfo') - .then(l => { - lock = l; - - const node = this._readNode(path); - const info = new LocalStorageNodeInfo({ - path, - key: typeof pathInfo.key === 'string' ? pathInfo.key : null, - index: typeof pathInfo.key === 'number' ? pathInfo.key : null, - type: node ? node.type : 0, - exists: node !== null, - address: node ? new LocalStorageNodeAddress(path) : null, - created: node ? new Date(node.created) : null, - modified: node ? new Date(node.modified) : null, - revision: node ? node.revision : null, - revision_nr: node ? node.revision_nr : null - }); - - if (node || path === '') { - return info; - } - - // Try parent node - return lock.moveToParent() - .then(parentLock => { - lock = parentLock; - const parent = this._readNode(pathInfo.parentPath); - if (parent && [VALUE_TYPES.OBJECT, VALUE_TYPES.ARRAY].includes(parent.type) && pathInfo.key in parent.value) { - // Stored in parent node - info.exists = true; - info.value = parent.value[pathInfo.key]; - info.address = null; - info.type = parent.type; - info.created = new Date(parent.created); - info.modified = new Date(parent.modified); - info.revision = parent.revision; - info.revision_nr = parent.revision_nr; - } - else { - // Parent doesn't exist, so the node we're looking for cannot exist either - info.address = null; - } - return info; - }) - }) - .then(info => { - lock.release(); - return info; - }) - .catch(err => { - lock && lock.release(); - throw err; - }); - } - - // TODO: Move to Storage base class? - removeNode(path, options = { tid: undefined }) { - if (path === '') { - return Promise.reject(new Error(`Cannot remove the root node`)); - } - - const pathInfo = PathInfo.get(path); - const tid = (options && options.tid) || ID.generate(); - return this.nodeLocker.lock(pathInfo.parentPath, tid, true, 'removeNode') - .then(lock => { - return this.updateNode(pathInfo.parentPath, { [pathInfo.key]: null }, { tid }) - .then(result => { - lock.release(); - return result; - }) - .catch(err => { - lock.release(); - throw err; - }); - }); - } - - // TODO: Move to Storage base class? - setNode(path, value, options = { assert_revision: undefined, tid: undefined }) { - const pathInfo = PathInfo.get(path); - - let lock; - const tid = (options && options.tid) || ID.generate(); - return this.nodeLocker.lock(path, tid, true, 'setNode') - .then(l => { - lock = l; - - if (path === '') { - if (value === null || typeof value !== 'object' || value instanceof Array || value instanceof ArrayBuffer || ('buffer' in value && value.buffer instanceof ArrayBuffer)) { - return Promise.reject(new Error(`Invalid value for root node: ${value}`)); - } - - return this._writeNodeWithTracking('', value, { merge: false, tid }) - } - - if (options && typeof options.assert_revision !== 'undefined') { - return this.getNodeInfo(path, { tid: lock.tid }) - .then(info => { - if (info.revision !== options.assert_revision) { - throw new NodeRevisionError(`revision '${info.revision}' does not match requested revision '${options.assert_revision}'`); - } - if (info.address && info.address.path === path && !this.valueFitsInline(value)) { - // Overwrite node - return this._writeNodeWithTracking(path, value, { merge: false, tid }); - } - else { - // Update parent node - return lock.moveToParent() - .then(parentLock => { - lock = parentLock; - return this._writeNodeWithTracking(pathInfo.parentPath, { [pathInfo.key]: value }, { merge: true, tid }); - }); - } - }) - } - else { - // Delegate operation to update on parent node - return lock.moveToParent() - .then(parentLock => { - lock = parentLock; - return this.updateNode(pathInfo.parentPath, { [pathInfo.key]: value }, { tid }); - }); - } - }) - .then(result => { - lock.release(); - return result; - }) - .catch(err => { - lock.release(); - throw err; - }); - } - - // TODO: Move to Storage base class? - updateNode(path, updates, options = { tid: undefined }) { - - if (typeof updates !== 'object') { // || Object.keys(updates).length === 0 - return Promise.reject(new Error(`invalid updates argument`)); //. Must be a non-empty object or array - } - - const tid = (options && options.tid) || ID.generate(); - let lock; - return this.nodeLocker.lock(path, tid, true, 'updateNode') - .then(l => { - lock = l; - // Get info about current node - return this.getNodeInfo(path, { tid: lock.tid }); - }) - .then(nodeInfo => { - const pathInfo = PathInfo.get(path); - if (nodeInfo.exists && nodeInfo.address && nodeInfo.address.path === path) { - // Node exists and is stored in its own record. - // Update it - return this._writeNodeWithTracking(path, updates, { merge: true, tid }); - } - else if (nodeInfo.exists) { - // Node exists, but is stored in its parent node. - const pathInfo = PathInfo.get(path); - return lock.moveToParent() - .then(parentLock => { - lock = parentLock; - return this._writeNodeWithTracking(pathInfo.parentPath, { [pathInfo.key]: value }, { merge: true, tid }); - }); - } - else { - // The node does not exist, it's parent doesn't have it either. Update the parent instead - return lock.moveToParent() - .then(parentLock => { - lock = parentLock; - return this.updateNode(pathInfo.parentPath, { [pathInfo.key]: updates }, { tid }); - }); - } - }) - .then(result => { - lock.release(); - return result; - }) - .catch(err => { - lock.release(); - throw err; - }); - } -} - -module.exports = { - LocalStorageNodeAddress, - LocalStorageNodeInfo, - LocalStorage, - LocalStorageSettings -} -},{"./node-info":34,"./node-value-types":36,"./storage":40,"acebase-core":11}],40:[function(require,module,exports){ +},{"./node-info":34,"./node-lock":35,"./node-value-types":36,"./storage":39,"acebase-core":11}],39:[function(require,module,exports){ (function (process){ const { Utils, DebugLogger, PathInfo, ID, PathReference, ascii85 } = require('acebase-core'); const { NodeLocker } = require('./node-lock'); @@ -8749,6 +7773,7 @@ class Storage extends EventEmitter { return this._writeNode(path, value, options); } + // TODO: Doublecheck if this has been fixed already: // TODO: FIX indexes on higher path not being updated. // Now, updates on an indexed property does not update the index! // issue example: @@ -9643,9 +8668,9 @@ module.exports = { NodeRevisionError }; }).call(this,require('_process')) -},{"./data-index":41,"./node-info":34,"./node-lock":35,"./node-value-types":36,"./promise-fs":41,"_process":45,"acebase-core":11,"colors":22,"events":42}],41:[function(require,module,exports){ +},{"./data-index":40,"./node-info":34,"./node-lock":35,"./node-value-types":36,"./promise-fs":40,"_process":44,"acebase-core":11,"colors":22,"events":41}],40:[function(require,module,exports){ -},{}],42:[function(require,module,exports){ +},{}],41:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -10170,7 +9195,7 @@ function functionBindPolyfill(context) { }; } -},{}],43:[function(require,module,exports){ +},{}],42:[function(require,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { @@ -10195,7 +9220,7 @@ if (typeof Object.create === 'function') { } } -},{}],44:[function(require,module,exports){ +},{}],43:[function(require,module,exports){ exports.endianness = function () { return 'LE' }; exports.hostname = function () { @@ -10246,7 +9271,7 @@ exports.homedir = function () { return '/' }; -},{}],45:[function(require,module,exports){ +},{}],44:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -10432,14 +9457,14 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],46:[function(require,module,exports){ +},{}],45:[function(require,module,exports){ module.exports = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function'; } -},{}],47:[function(require,module,exports){ +},{}],46:[function(require,module,exports){ (function (process,global){ // Copyright Joyent, Inc. and other Node contributors. // @@ -11029,5 +10054,5 @@ function hasOwnProperty(obj, prop) { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":46,"_process":45,"inherits":43}]},{},[33])(33) +},{"./support/isBuffer":45,"_process":44,"inherits":42}]},{},[33])(33) }); diff --git a/dist/browser.min.js b/dist/browser.min.js index 8539356..8ecb8b9 100644 --- a/dist/browser.min.js +++ b/dist/browser.min.js @@ -1 +1 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.acebase=f()}})((function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,(function(r){var n=e[i][1][r];return o(n||r)}),p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i=7&&stringLength<=10)return true;return false};cuid.fingerprint=fingerprint;module.exports=cuid},{"./lib/fingerprint.js":2,"./lib/pad.js":3}],2:[function(require,module,exports){var pad=require("./pad.js");var env=typeof window==="object"?window:self;var globalCount=Object.keys(env).length;var mimeTypesLength=navigator.mimeTypes?navigator.mimeTypes.length:0;var clientId=pad((mimeTypesLength+navigator.userAgent.length).toString(36)+globalCount.toString(36),4);module.exports=function fingerprint(){return clientId}},{"./pad.js":3}],3:[function(require,module,exports){module.exports=function pad(num,size){var s="000000000"+num;return s.substr(s.length-size)}},{}],4:[function(require,module,exports){const{EventEmitter:EventEmitter}=require("events");const{DataReference:DataReference,DataReferenceQuery:DataReferenceQuery}=require("./data-reference");const{TypeMappings:TypeMappings}=require("./type-mappings");class AceBaseSettings{constructor(options){this.logLevel=options.logLevel||"log"}}class AceBaseBase extends EventEmitter{constructor(dbname,options){super();if(!options){options={}}this.once("ready",()=>{this._ready=true});this.types=new TypeMappings(this)}ready(callback=undefined){if(this._ready===true){callback&&callback();return Promise.resolve()}else{let resolve;const promise=new Promise(res=>resolve=res);this.on("ready",()=>{resolve();callback&&callback()});return promise}}get isReady(){return this._ready===true}ref(path){return new DataReference(this,path)}get root(){return this.ref("")}query(path){const ref=new DataReference(this,path);return new DataReferenceQuery(ref)}get indexes(){return{get:()=>{return this.api.getIndexes()},create:(path,key,options)=>{return this.api.createIndex(path,key,options)}}}}module.exports={AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings}},{"./data-reference":7,"./type-mappings":16,events:42}],5:[function(require,module,exports){class Api{stats(options=undefined){}subscribe(path,event,callback){}unsubscribe(path,event,callback){}update(path,updates){}set(path,value){}get(path,options){}exists(path){}query(path,query,options){}createIndex(path,key){}getIndexes(){}}module.exports={Api:Api}},{}],6:[function(require,module,exports){const c=function(input,length,result){var i,j,n,b=[0,0,0,0,0];for(i=0;i";return ret},decode:function(input){if(!input.startsWith("<~")||!input.endsWith("~>")){throw new Error("Invalid input string")}input=input.substr(2,input.length-4);var n=input.length,r=[],b=[0,0,0,0,0],i,j,t,x,y,d;for(i=0;i>>=8;y=t&255;t>>>=8;r.push(t>>>8,t&255,y,x);for(j=d;j<5;++j,r.pop());i+=4}const data=new Uint8Array(r);return data.buffer.slice(data.byteOffset,data.byteOffset+data.byteLength)}};module.exports=ascii85},{}],7:[function(require,module,exports){const{DataSnapshot:DataSnapshot}=require("./data-snapshot");const{EventStream:EventStream,EventPublisher:EventPublisher}=require("./subscription");const{ID:ID}=require("./id");const debug=require("./debug");const{PathInfo:PathInfo}=require("./path-info");class DataRetrievalOptions{constructor(options){if(!options){options={}}if(typeof options.include!=="undefined"&&!(options.include instanceof Array)){throw new TypeError(`options.include must be an array`)}if(typeof options.exclude!=="undefined"&&!(options.exclude instanceof Array)){throw new TypeError(`options.exclude must be an array`)}if(typeof options.child_objects!=="undefined"&&typeof options.child_objects!=="boolean"){throw new TypeError(`options.child_objects must be a boolean`)}if(typeof options.allow_cache!=="undefined"&&typeof options.allow_cache!=="boolean"){throw new TypeError(`options.allow_cache must be a boolean`)}this.include=options.include||undefined;this.exclude=options.exclude||undefined;this.child_objects=typeof options.child_objects==="boolean"?options.child_objects:undefined;this.allow_cache=typeof options.allow_cache==="boolean"?options.allow_cache:undefined}}class QueryDataRetrievalOptions extends DataRetrievalOptions{constructor(options){super(options);if(typeof options.snapshots!=="undefined"&&typeof options.snapshots!=="boolean"){throw new TypeError(`options.snapshots must be an array`)}this.snapshots=typeof options.snapshots==="boolean"?options.snapshots:undefined}}const _private=Symbol("private");class DataReference{constructor(db,path,vars){if(!path){path=""}path=path.replace(/^\/|\/$/g,"");const pathInfo=PathInfo.get(path);const key=pathInfo.key;const callbacks=[];this[_private]={get path(){return path},get key(){return key},get callbacks(){return callbacks},vars:vars||{}};this.db=db}get path(){return this[_private].path}get key(){return this[_private].key}get parent(){let currentPath=PathInfo.fillVariables2(this.path,this.vars);const info=PathInfo.get(currentPath);if(info.parentPath===null){return null}return new DataReference(this.db,info.parentPath)}get vars(){return this[_private].vars}child(childPath){childPath=childPath.replace(/^\/|\/$/g,"");const currentPath=PathInfo.fillVariables2(this.path,this.vars);const targetPath=PathInfo.getChildPath(currentPath,childPath);return new DataReference(this.db,targetPath)}set(value,onComplete=undefined){if(this.isWildcardPath){throw new Error(`Cannot set the value of a path with wildcards and/or variables`)}if(this.parent===null){throw new Error(`Cannot set the root object. Use update, or set individual child properties`)}if(typeof value==="undefined"){throw new TypeError(`Cannot store value undefined`)}value=this.db.types.serialize(this.path,value);return this.db.api.set(this.path,value).then(res=>{if(typeof onComplete==="function"){try{onComplete(null,this)}catch(err){console.error(`Error in onComplete callback:`,err)}}}).catch(err=>{if(typeof onComplete==="function"){try{onComplete(err)}catch(err){console.error(`Error in onComplete callback:`,err)}}else{throw err}}).then(()=>{return this})}update(updates,onComplete=undefined){if(this.isWildcardPath){throw new Error(`Cannot update the value of a path with wildcards and/or variables`)}let promise;if(typeof updates!=="object"||updates instanceof Array||updates instanceof ArrayBuffer||updates instanceof Date){promise=this.set(updates)}else{updates=this.db.types.serialize(this.path,updates);promise=this.db.api.update(this.path,updates)}return promise.then(()=>{if(typeof onComplete==="function"){try{onComplete(null,this)}catch(err){console.error(`Error in onComplete callback:`,err)}}}).catch(err=>{if(typeof onComplete==="function"){try{onComplete(err)}catch(err){console.error(`Error in onComplete callback:`,err)}}else{throw err}}).then(()=>{return this})}transaction(callback){if(this.isWildcardPath){throw new Error(`Cannot start a transaction on a path with wildcards and/or variables`)}let cb=currentValue=>{currentValue=this.db.types.deserialize(this.path,currentValue);const snap=new DataSnapshot(this,currentValue);let newValue;try{newValue=callback(snap)}catch(err){return}if(newValue instanceof Promise){return newValue.then(val=>{return this.db.types.serialize(this.path,val)})}else{return this.db.types.serialize(this.path,newValue)}};return this.db.api.transaction(this.path,cb).then(result=>{return this})}on(event,callback,cancelCallbackOrContext,context){if(this.path===""&&["value","notify_value","child_changed","notify_child_changed"].includes(event)){console.warn(`WARNING: Listening for value and child_changed events on the root node is a bad practice`)}const cancelCallback=typeof cancelCallbackOrContext==="function"&&cancelCallbackOrContext;context=typeof cancelCallbackOrContext==="object"?cancelCallbackOrContext:context;const useCallback=typeof callback==="function";let eventPublisher=null;const eventStream=new EventStream(publisher=>{eventPublisher=publisher});let cb={subscr:eventStream,original:callback,ours:(err,path,newValue,oldValue)=>{if(err){debug.error(`Error getting data for event ${event} on path "${path}"`,err);return}let ref=this.db.ref(path);ref[_private].vars=PathInfo.extractVariables(this.path,path);let callbackObject;if(event.startsWith("notify_")){callbackObject=ref}else{const isRemoved=event==="child_removed";const val=this.db.types.deserialize(path,isRemoved?oldValue:newValue);const snap=new DataSnapshot(ref,val,isRemoved);callbackObject=snap}useCallback&&callback.call(context||null,callbackObject);let keep=eventPublisher.publish(callbackObject);if(!keep&&!useCallback){let callbacks=this[_private].callbacks;callbacks.splice(callbacks.indexOf(cb),1);this.db.api.unsubscribe(this.path,event,cb.ours)}}};this[_private].callbacks.push(cb);let authorized=this.db.api.subscribe(this.path,event,cb.ours);const allSubscriptionsStoppedCallback=()=>{let callbacks=this[_private].callbacks;callbacks.splice(callbacks.indexOf(cb),1);this.db.api.unsubscribe(this.path,event,cb.ours)};if(authorized instanceof Promise){authorized.then(()=>{eventPublisher.start(allSubscriptionsStoppedCallback)}).catch(err=>{let callbacks=this[_private].callbacks;callbacks.splice(callbacks.indexOf(cb),1);this.db.api.unsubscribe(this.path,event,cb.ours);eventPublisher.cancel(err.message);cancelCallback&&cancelCallback(err.message)})}else{eventPublisher.start(allSubscriptionsStoppedCallback)}if(callback&&!this.isWildcardPath){if(event==="value"){this.get(snap=>{eventPublisher.publish(snap);useCallback&&callback(snap)})}else if(event==="child_added"){this.get(snap=>{const val=snap.val();if(val===null||typeof val!=="object"){return}Object.keys(val).forEach(key=>{let childSnap=new DataSnapshot(this.child(key),val[key]);eventPublisher.publish(childSnap);useCallback&&callback(childSnap)})})}else if(event==="notify_child_added"){const step=100;let limit=step,skip=0;const more=()=>{this.db.api.reflect(this.path,"children",{limit:limit,skip:skip}).then(children=>{children.list.forEach(child=>{const childRef=this.child(child.key);eventPublisher.publish(childRef);useCallback&&callback(childRef)});if(children.more){skip+=step;more()}})};more()}}return eventStream}off(event=undefined,callback=undefined){const callbacks=this[_private].callbacks;if(callback){const cb=callbacks.find(cb=>cb.original===callback);if(!cb){debug.error(`Can't find specified callback to unsubscribe from (path: "${this.path}", event: ${event}, callback: ${callback})`);return}callbacks.splice(callbacks.indexOf(cb),1);callback=cb.ours;cb.subscr.unsubscribe(callback)}else{callbacks.splice(0,callbacks.length).forEach(cb=>{cb.subscr.unsubscribe()})}this.db.api.unsubscribe(this.path,event,callback);return this}get(optionsOrCallback=undefined,callback=undefined){if(this.isWildcardPath){throw new Error(`Cannot get the value of a path with wildcards and/or variables. Use .query() instead`)}callback=typeof optionsOrCallback==="function"?optionsOrCallback:typeof callback==="function"?callback:undefined;const options=typeof optionsOrCallback==="object"?optionsOrCallback:new DataRetrievalOptions({allow_cache:true});if(typeof options.allow_cache==="undefined"){options.allow_cache=true}const promise=this.db.api.get(this.path,options).then(value=>{value=this.db.types.deserialize(this.path,value);const snapshot=new DataSnapshot(this,value);return snapshot});if(callback){promise.then(callback);return}else{return promise}}once(event,options){if(event==="value"&&!this.isWildcardPath){return this.get(options)}return new Promise((resolve,reject)=>{const callback=snap=>{this.off(event,snap);resolve(snap)};this.on(event,callback)})}push(value=undefined,onComplete=undefined){if(this.isWildcardPath){throw new Error(`Cannot push to a path with wildcards and/or variables`)}const id=ID.generate();const ref=this.child(id);ref.__pushed=true;if(typeof value!=="undefined"){return ref.set(value,onComplete).then(res=>ref)}else{return ref}}remove(){if(this.isWildcardPath){throw new Error(`Cannot remove a path with wildcards and/or variables. Use query().remove instead`)}if(this.parent===null){throw new Error(`Cannot remove the top node`)}return this.set(null)}exists(){if(this.isWildcardPath){throw new Error(`Cannot push to a path with wildcards and/or variables`)}return this.db.api.exists(this.path)}get isWildcardPath(){return this.path.indexOf("*")>=0||this.path.indexOf("$")>=0}query(){return new DataReferenceQuery(this)}reflect(type,args){if(this.pathHasVariables){throw new Error(`Cannot reflect on a path with wildcards and/or variables`)}return this.db.api.reflect(this.path,type,args)}export(stream,options={format:"json"}){return this.db.api.export(this.path,stream,options)}}class DataReferenceQuery{constructor(ref){this.ref=ref;this[_private]={filters:[],skip:0,take:0,order:[]}}filter(key,op,compare){if((op==="in"||op==="!in")&&(!(compare instanceof Array)||compare.length===0)){throw new Error(`${op} filter for ${key} must supply an Array compare argument containing at least 1 value`)}if((op==="between"||op==="!between")&&(!(compare instanceof Array)||compare.length!==2)){throw new Error(`${op} filter for ${key} must supply an Array compare argument containing 2 values`)}if((op==="matches"||op==="!matches")&&!(compare instanceof RegExp)){throw new Error(`${op} filter for ${key} must supply a RegExp compare argument`)}if((op==="contains"||op==="!contains")&&(typeof compare==="object"&&!(compare instanceof Array)&&!(compare instanceof Date)||compare instanceof Array&&compare.length===0)){throw new Error(`${op} filter for ${key} must supply a simple value or (non-zero length) array compare argument`)}this[_private].filters.push({key:key,op:op,compare:compare});return this}where(key,op,compare){return this.filter(key,op,compare)}take(n){this[_private].take=n;return this}skip(n){this[_private].skip=n;return this}sort(key,ascending=true){if(typeof key!=="string"){throw`key must be a string`}this[_private].order.push({key:key,ascending:ascending});return this}order(key,ascending=true){return this.sort(key,ascending)}get(optionsOrCallback=undefined,callback=undefined){callback=typeof optionsOrCallback==="function"?optionsOrCallback:typeof callback==="function"?callback:undefined;const options=typeof optionsOrCallback==="object"?optionsOrCallback:new QueryDataRetrievalOptions({snapshots:true,allow_cache:true});if(typeof options.snapshots==="undefined"){options.snapshots=true}if(typeof options.allow_cache==="undefined"){options.allow_cache=true}options.eventHandler=ev=>{if(!this._events||!this._events[ev.name]){return false}const listeners=this._events[ev.name];if(typeof listeners!=="object"||listeners.length===0){return false}if(["add","change","remove"].includes(ev.name)){const ref=new DataReference(this.ref.db,ev.path);const eventData={name:ev.name};if(options.snapshots&&ev.name!=="remove"){const val=db.types.deserialize(ev.path,ev.value);eventData.snapshot=new DataSnapshot(ref,val,false)}else{eventData.ref=ref}ev=eventData}listeners.forEach(callback=>{try{callback(ev)}catch(e){}})};options.monitor={add:false,change:false,remove:false};if(this._events){if(this._events["add"]&&this._events["add"].length>0){options.monitor.add=true}if(this._events["change"]&&this._events["change"].length>0){options.monitor.change=true}if(this._events["remove"]&&this._events["remove"].length>0){options.monitor.remove=true}}const db=this.ref.db;return db.api.query(this.ref.path,this[_private],options).catch(err=>{throw new Error(err)}).then(results=>{results.forEach((result,index)=>{if(options.snapshots){const val=db.types.deserialize(result.path,result.val);results[index]=new DataSnapshot(db.ref(result.path),val)}else{results[index]=db.ref(result)}});if(options.snapshots){return DataSnapshotsArray.from(results)}else{return DataReferencesArray.from(results)}}).then(results=>{callback&&callback(results);return results})}getRefs(callback=undefined){return this.get({snapshots:false},callback)}remove(callback){return this.get({snapshots:false}).then(refs=>{const promises=[];return Promise.all(refs.map(ref=>ref.remove())).then(()=>{callback&&callback()})})}on(event,callback){if(!this._events){this._events={}}if(!this._events[event]){this._events[event]=[]}this._events[event].push(callback);return this}off(event,callback){if(!this._events){return this}if(typeof event==="undefined"){this._events={};return this}if(!this._events[event]){return this}if(typeof callback==="undefined"){delete this._events[event];return this}const index=!this._events[event].indexOf(callback);if(!~index){return this}this._events[event].splice(index,1);return this}}class DataSnapshotsArray extends Array{static from(snaps){const arr=new DataSnapshotsArray(snaps.length);snaps.forEach((snap,i)=>arr[i]=snap);return arr}getValues(){return this.map(snap=>snap.val())}}class DataReferencesArray extends Array{static from(refs){const arr=new DataReferencesArray(refs.length);refs.forEach((ref,i)=>arr[i]=ref);return arr}getPaths(){return this.map(ref=>ref.path)}}module.exports={DataReference:DataReference,DataReferenceQuery:DataReferenceQuery,DataRetrievalOptions:DataRetrievalOptions,QueryDataRetrievalOptions:QueryDataRetrievalOptions}},{"./data-snapshot":8,"./debug":9,"./id":10,"./path-info":12,"./subscription":14}],8:[function(require,module,exports){const{DataReference:DataReference}=require("./data-reference");const{getPathKeys:getPathKeys}=require("./path-info");const getChild=(snapshot,path)=>{if(!snapshot.exists()){return null}let child=snapshot.val();getPathKeys(path).every(key=>{child=child[key];return typeof child!=="undefined"});return child||null};const getChildren=snapshot=>{if(!snapshot.exists()){return[]}let value=snapshot.val();if(value instanceof Array){return new Array(value.length).map((v,i)=>i)}if(typeof value==="object"){return Object.keys(value)}return[]};class DataSnapshot{constructor(ref,value,isRemoved=false){this.ref=ref;this.val=()=>{return value};this.exists=()=>{if(isRemoved){return false}return value!==null&&typeof value!=="undefined"}}child(path){let child=getChild(this,path);return new DataSnapshot(this.ref.child(path),child)}hasChild(path){return getChild(this,path)!==null}hasChildren(){return getChildren(this).length>0}numChildren(){return getChildren(this).length}forEach(callback){const value=this.val();return getChildren(this).every((key,i)=>{const snap=new DataSnapshot(this.ref.child(key),value[key]);return callback(snap)})}get key(){return this.ref.key}}module.exports={DataSnapshot:DataSnapshot}},{"./data-reference":7,"./path-info":12}],9:[function(require,module,exports){class DebugLogger{constructor(level="log",prefix=""){this.prefix=prefix;this.setLevel(level)}setLevel(level){const prefix=this.prefix?this.prefix:"";this.level=level;this.verbose=["verbose"].includes(level)?console.log.bind(console,prefix):()=>{};this.log=["verbose","log"].includes(level)?console.log.bind(console,prefix):()=>{};this.warn=["verbose","log","warn"].includes(level)?console.warn.bind(console,prefix):()=>{};this.error=["verbose","log","warn","error"].includes(level)?console.error.bind(console,prefix):()=>{};this.write=console.log.bind(console)}}module.exports=DebugLogger},{}],10:[function(require,module,exports){const cuid=require("cuid");class ID{static generate(){return cuid().slice(1)}}module.exports={ID:ID}},{cuid:1}],11:[function(require,module,exports){const{AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings}=require("./acebase-base");const{Api:Api}=require("./api");const{DataReference:DataReference,DataReferenceQuery:DataReferenceQuery,DataRetrievalOptions:DataRetrievalOptions,QueryDataRetrievalOptions:QueryDataRetrievalOptions}=require("./data-reference");const{DataSnapshot:DataSnapshot}=require("./data-snapshot");const DebugLogger=require("./debug");const{ID:ID}=require("./id");const{PathReference:PathReference}=require("./path-reference");const{EventStream:EventStream,EventPublisher:EventPublisher,EventSubscription:EventSubscription}=require("./subscription");const Transport=require("./transport");const{TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions}=require("./type-mappings");const Utils=require("./utils");const{PathInfo:PathInfo}=require("./path-info");const ascii85=require("./ascii85");module.exports={AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings,Api:Api,DataReference:DataReference,DataReferenceQuery:DataReferenceQuery,DataRetrievalOptions:DataRetrievalOptions,QueryDataRetrievalOptions:QueryDataRetrievalOptions,DataSnapshot:DataSnapshot,DebugLogger:DebugLogger,ID:ID,PathReference:PathReference,EventStream:EventStream,EventPublisher:EventPublisher,EventSubscription:EventSubscription,Transport:Transport,TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions,Utils:Utils,PathInfo:PathInfo,ascii85:ascii85}},{"./acebase-base":4,"./api":5,"./ascii85":6,"./data-reference":7,"./data-snapshot":8,"./debug":9,"./id":10,"./path-info":12,"./path-reference":13,"./subscription":14,"./transport":15,"./type-mappings":16,"./utils":17}],12:[function(require,module,exports){function getPathKeys(path){if(path.length===0){return[]}let keys=path.replace(/\[/g,"/[").split("/");keys.forEach((key,index)=>{if(key.startsWith("[")){keys[index]=parseInt(key.substr(1,key.length-2))}});return keys}function getPathInfo(path){if(path.length===0){return{parent:null,key:""}}const i=Math.max(path.lastIndexOf("/"),path.lastIndexOf("["));const parentPath=i<0?"":path.substr(0,i);let key=i<0?path:path.substr(i);if(key.startsWith("[")){key=parseInt(key.substr(1,key.length-2))}else if(key.startsWith("/")){key=key.substr(1)}if(parentPath===path){parentPath=null}return{parent:parentPath,key:key}}function getChildPath(path,key){if(path.length===0){if(typeof key==="number"){throw new TypeError("Cannot add array index to root path!")}return key}if(typeof key==="number"){return`${path}[${key}]`}return`${path}/${key}`}class PathInfo{static get(path){return new PathInfo(path)}static getChildPath(path,childKey){return getChildPath(path,childKey)}static getPathKeys(path){return getPathKeys(path)}constructor(path){this.path=path}get key(){return getPathInfo(this.path).key}get parentPath(){return getPathInfo(this.path).parent}childPath(childKey){return getChildPath(`${this.path}`,childKey)}get pathKeys(){return getPathKeys(this.path)}static extractVariables(varPath,fullPath){if(!varPath.includes("*")&&!varPath.includes("$")){return[]}const keys=getPathKeys(varPath);const pathKeys=getPathKeys(fullPath);let count=0;const variables={get length(){return count}};keys.forEach((key,index)=>{const pathKey=pathKeys[index];if(key==="*"){variables[count++]=pathKey}else if(typeof key==="string"&&key[0]==="$"){variables[count++]=pathKey;variables[key]=pathKey;const varName=key.slice(1);if(typeof variables[varName]==="undefined"){variables[varName]=pathKey}}});return variables}static fillVariables(varPath,fullPath){if(varPath.indexOf("*")<0&&varPath.indexOf("$")<0){return varPath}const keys=getPathKeys(varPath);const pathKeys=getPathKeys(fullPath);let merged=keys.map((key,index)=>{if(key===pathKeys[index]||index>=pathKeys.length){return key}else if(typeof key==="string"&&(key==="*"||key[0]==="$")){return pathKeys[index]}else{throw new Error(`Path "${fullPath}" cannot be used to fill variables of path "${this.path}" because they do not match`)}});let mergedPath="";merged.forEach(key=>{if(typeof key==="number"){mergedPath+=`[${key}]`}else{if(mergedPath.length>0){mergedPath+="/"}mergedPath+=key}});return mergedPath}static fillVariables2(varPath,vars){if(typeof vars!=="object"||Object.keys(vars).length===0){return varPath}let pathKeys=getPathKeys(varPath);let n=0;const targetPath=pathKeys.reduce((path,key)=>{if(key==="*"||key.startsWith("$")){key=vars[n++]}if(typeof key==="number"){return`${path}[${key}]`}else{return`${path}/${key}`}},"");return targetPath}equals(otherPath){if(this.path===otherPath){return true}const keys=getPathKeys(this.path);const otherKeys=getPathKeys(otherPath);if(keys.length!==otherKeys.length){return false}return keys.every((key,index)=>{const otherKey=otherKeys[index];return otherKey===key||typeof otherKey==="string"&&(otherKey==="*"||otherKey[0]==="$")||typeof key==="string"&&(key==="*"||key[0]==="$")})}isAncestorOf(descendantPath){if(descendantPath===""||this.path===descendantPath){return false}if(this.path===""){return true}const ancestorKeys=getPathKeys(this.path);const descendantKeys=getPathKeys(descendantPath);if(ancestorKeys.length>=descendantKeys.length){return false}return ancestorKeys.every((key,index)=>{const otherKey=descendantKeys[index];return otherKey===key||typeof otherKey==="string"&&(otherKey==="*"||otherKey[0]==="$")||typeof key==="string"&&(key==="*"||key[0]==="$")})}isDescendantOf(ancestorPath){if(this.path===""||this.path===ancestorPath){return false}if(ancestorPath===""){return true}const ancestorKeys=getPathKeys(ancestorPath);const descendantKeys=getPathKeys(this.path);if(ancestorKeys.length>=descendantKeys.length){return false}return ancestorKeys.every((key,index)=>{const otherKey=descendantKeys[index];return otherKey===key||typeof otherKey==="string"&&(otherKey==="*"||otherKey[0]==="$")||typeof key==="string"&&(key==="*"||key[0]==="$")})}isChildOf(otherPath){if(this.path===""){return false}const parentInfo=PathInfo.get(this.parentPath);return parentInfo.equals(otherPath)}isParentOf(otherPath){if(otherPath===""){return false}const parentInfo=PathInfo.get(PathInfo.get(otherPath).parentPath);return parentInfo.equals(this.path)}}module.exports={getPathInfo:getPathInfo,getChildPath:getChildPath,getPathKeys:getPathKeys,PathInfo:PathInfo}},{}],13:[function(require,module,exports){class PathReference{constructor(path){this.path=path}}module.exports={PathReference:PathReference}},{}],14:[function(require,module,exports){class EventSubscription{constructor(stop){this.stop=stop;this._internal={state:"init",cancelReason:undefined,activatePromises:[]}}activated(callback=undefined){if(callback){this._internal.activatePromises.push({callback:callback});if(this._internal.state==="active"){callback(true)}else if(this._internal.state==="canceled"){callback(false,this._internal.cancelReason)}}return new Promise((resolve,reject)=>{if(this._internal.state==="active"){return resolve()}else if(this._internal.state==="canceled"&&!callback){return reject(new Error(this._internal.cancelReason))}this._internal.activatePromises.push({resolve:resolve,reject:callback?()=>{}:reject})})}_setActivationState(activated,cancelReason){this._internal.cancelReason=cancelReason;this._internal.state=activated?"active":"canceled";while(this._internal.activatePromises.length>0){const p=this._internal.activatePromises.shift();if(activated){p.callback&&p.callback(true);p.resolve&&p.resolve()}else{p.callback&&p.callback(false,cancelReason);p.reject&&p.reject(cancelReason)}}}}class EventPublisher{constructor(publish,start,cancel){this.publish=publish;this.start=start;this.cancel=cancel}}class EventStream{constructor(eventPublisherCallback){const subscribers=[];let noMoreSubscribersCallback;let activationState;const _stoppedState="stopped (no more subscribers)";this.subscribe=(callback,activationCallback)=>{if(typeof callback!=="function"){throw new TypeError("callback must be a function")}else if(activationState===_stoppedState){throw new Error("stream can't be used anymore because all subscribers were stopped")}const sub={callback:callback,activationCallback:function(activated,cancelReason){activationCallback&&activationCallback(activated,cancelReason);this.subscription._setActivationState(activated,cancelReason)},subscription:new EventSubscription((function stop(){subscribers.splice(subscribers.indexOf(this),1);checkActiveSubscribers()}))};subscribers.push(sub);if(typeof activationState!=="undefined"){if(activationState===true){activationCallback&&activationCallback(true);sub.subscription._setActivationState(true)}else if(typeof activationState==="string"){activationCallback&&activationCallback(false,activationState);sub.subscription._setActivationState(false,activationState)}}return sub.subscription};const checkActiveSubscribers=()=>{if(subscribers.length===0){noMoreSubscribersCallback&&noMoreSubscribersCallback();activationState=_stoppedState}};this.unsubscribe=(callback=undefined)=>{const remove=callback?subscribers.filter(sub=>sub.callback===callback):subscribers;remove.forEach(sub=>{const i=subscribers.indexOf(sub);subscribers.splice(i,1)});checkActiveSubscribers()};this.stop=()=>{subscribers.splice(0);checkActiveSubscribers()};const publish=val=>{subscribers.forEach(sub=>{try{sub.callback(val)}catch(err){debug.error(`Error running subscriber callback: ${err.message}`)}});return subscribers.length>0};const start=allSubscriptionsStoppedCallback=>{activationState=true;noMoreSubscribersCallback=allSubscriptionsStoppedCallback;subscribers.forEach(sub=>{sub.activationCallback&&sub.activationCallback(true)})};const cancel=reason=>{activationState=reason;subscribers.forEach(sub=>{sub.activationCallback&&sub.activationCallback(false,reason||new Error("unknown reason"))});subscribers.splice()};const publisher=new EventPublisher(publish,start,cancel);eventPublisherCallback(publisher)}}module.exports={EventStream:EventStream,EventPublisher:EventPublisher,EventSubscription:EventSubscription}},{}],15:[function(require,module,exports){const{PathReference:PathReference}=require("./path-reference");const{cloneObject:cloneObject}=require("./utils");const ascii85=require("./ascii85");module.exports={deserialize(data){if(data.map===null||typeof data.map==="undefined"){return data.val}const deserializeValue=(type,val)=>{if(type==="date"){return new Date(val)}else if(type==="binary"){return ascii85.decode(val)}else if(type==="reference"){return new PathReference(val)}else if(type==="regexp"){return new RegExp(val.pattern,val.flags)}return val};if(typeof data.map==="string"){return deserializeValue(data.map,data.val)}Object.keys(data.map).forEach(path=>{const type=data.map[path];const keys=path.replace(/\[/g,"/[").split("/");keys.forEach((key,index)=>{if(key.startsWith("[")){keys[index]=parseInt(key.substr(1,key.length-2))}});let parent=data;let key="val";let val=data.val;keys.forEach(k=>{key=k;parent=val;val=val[key]});parent[key]=deserializeValue(type,val)});return data.val},serialize(obj){if(obj===null||typeof obj!=="object"||obj instanceof Date||obj instanceof ArrayBuffer||obj instanceof PathReference){const ser=this.serialize({value:obj});return{map:ser.map.value,val:ser.val.value}}obj=cloneObject(obj);const process=(obj,mappings,prefix)=>{Object.keys(obj).forEach(key=>{const val=obj[key];const path=prefix.length===0?key:`${prefix}/${key}`;if(val instanceof Date){obj[key]=val.toISOString();mappings[path]="date"}else if(val instanceof ArrayBuffer){obj[key]=ascii85.encode(val);mappings[path]="binary"}else if(val instanceof PathReference){obj[key]=val.path;mappings[path]="reference"}else if(val instanceof RegExp){obj[key]={pattern:val.source,flags:val.flags};mappings[path]="regexp"}else if(typeof val==="object"&&val!==null){process(val,mappings,path)}})};const mappings={};process(obj,mappings,"");return{map:mappings,val:obj}}}},{"./ascii85":6,"./path-reference":13,"./utils":17}],16:[function(require,module,exports){const{cloneObject:cloneObject}=require("./utils");const{PathInfo:PathInfo}=require("./path-info");const{AceBaseBase:AceBaseBase}=require("./acebase-base");const{DataReference:DataReference}=require("./data-reference");const{DataSnapshot:DataSnapshot}=require("./data-snapshot");const get=(mappings,path)=>{path=path.replace(/^\/|\/$/g,"");const keys=PathInfo.getPathKeys(path);const mappedPath=Object.keys(mappings).find(mpath=>{const mkeys=PathInfo.getPathKeys(mpath);if(mkeys.length!==keys.length){return false}return mkeys.every((mkey,index)=>{if(mkey==="*"||mkey[0]==="$"){return true}return mkey===keys[index]})});const mapping=mappings[mappedPath];return mapping};const map=(mappings,path)=>{const targetPath=PathInfo.get(path).parentPath;if(targetPath===null){return}return get(mappings,targetPath)};const mapDeep=(mappings,entryPath)=>{entryPath=entryPath.replace(/^\/|\/$/g,"");const pathInfo=PathInfo.get(entryPath);const startPath=pathInfo.parentPath;const keys=startPath?PathInfo.getPathKeys(startPath):[];const matches=Object.keys(mappings).reduce((m,mpath)=>{const mkeys=PathInfo.getPathKeys(mpath);if(mkeys.length{if(index>=keys.length){return false}else if(mkey==="*"||mkey[0]==="$"||mkey===keys[index]){return true}else{isMatch=false;return false}})}if(isMatch){const mapping=mappings[mpath];m.push({path:mpath,type:mapping})}return m},[]);return matches};const process=(db,mappings,path,obj,action)=>{if(obj===null||typeof obj!=="object"){return obj}const keys=PathInfo.getPathKeys(path);const m=mapDeep(mappings,path);const changes=[];m.sort((a,b)=>PathInfo.getPathKeys(a.path).length>PathInfo.getPathKeys(b.path).length?-1:1);m.forEach(mapping=>{const mkeys=PathInfo.getPathKeys(mapping.path);mkeys.push("*");const mTrailKeys=mkeys.slice(keys.length);if(mTrailKeys.length===0){const vars=PathInfo.extractVariables(mapping.path,path);const ref=new DataReference(db,path,vars);if(action==="serialize"){obj=mapping.type.serialize(obj,ref)}else if(action==="deserialize"){const snap=new DataSnapshot(ref,obj);obj=mapping.type.deserialize(snap)}return}const process=(parentPath,parent,keys)=>{if(obj===null||typeof obj!=="object"){return obj}const key=keys[0];let children=[];if(key==="*"||key[0]==="$"){if(parent instanceof Array){children=parent.map((val,index)=>({key:index,val:val}))}else{children=Object.keys(parent).map(k=>({key:k,val:parent[k]}))}}else{const child=parent[key];if(typeof child==="object"){children.push({key:key,val:child})}}children.forEach(child=>{const childPath=PathInfo.getChildPath(parentPath,child.key);const vars=PathInfo.extractVariables(mapping.path,childPath);const ref=new DataReference(db,childPath,vars);if(keys.length===1){if(action==="serialize"){changes.push({parent:parent,key:child.key,original:parent[child.key]});parent[child.key]=mapping.type.serialize(child.val,ref)}else if(action==="deserialize"){const snap=new DataSnapshot(ref,child.val);parent[child.key]=mapping.type.deserialize(snap)}}else{process(childPath,child.val,keys.slice(1))}})};process(path,obj,mTrailKeys)});if(action==="serialize"){obj=cloneObject(obj);if(changes.length>0){changes.forEach(change=>{change.parent[change.key]=change.original})}}return obj};class TypeMappingOptions{constructor(options){if(!options){options={}}this.serializer=options.serializer;this.creator=options.creator}}const _mappings=Symbol("mappings");class TypeMappings{constructor(db){this.db=db;this[_mappings]={}}get mappings(){return this[_mappings]}map(path){return map(this[_mappings],path)}bind(path,type,options={}){if(typeof path!=="string"){throw new TypeError("path must be a string")}if(typeof type!=="function"){throw new TypeError("constructor must be a function")}if(typeof options.serializer==="undefined"){}else if(typeof options.serializer==="string"){if(typeof type.prototype[options.serializer]==="function"){options.serializer=type.prototype[options.serializer]}else{throw new TypeError(`${type.name}.prototype.${options.serializer} is not a function, cannot use it as serializer`)}}else if(typeof options.serializer!=="function"){throw new TypeError(`serializer for class ${type.name} must be a function, or the name of a prototype method`)}if(typeof options.creator==="undefined"){if(typeof type.create==="function"){options.creator=type.create}}else if(typeof options.creator==="string"){if(typeof type[options.creator]==="function"){options.creator=type[options.creator]}else{throw new TypeError(`${type.name}.${options.creator} is not a function, cannot use it as creator`)}}else if(typeof options.creator!=="function"){throw new TypeError(`creator for class ${type.name} must be a function, or the name of a static method`)}path=path.replace(/^\/|\/$/g,"");this[_mappings][path]={db:this.db,type:type,creator:options.creator,serializer:options.serializer,deserialize(snap){let obj;if(this.creator){obj=this.creator.call(this.type,snap)}else{obj=new this.type(snap)}return obj},serialize(obj,ref){if(this.serializer){obj=this.serializer.call(obj,ref,obj)}else if(obj&&typeof obj.serialize==="function"){obj=obj.serialize(ref,obj)}return obj}}}serialize(path,obj){return process(this.db,this[_mappings],path,obj,"serialize")}deserialize(path,obj){return process(this.db,this[_mappings],path,obj,"deserialize")}}module.exports={TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions}},{"./acebase-base":4,"./data-reference":7,"./data-snapshot":8,"./path-info":12,"./utils":17}],17:[function(require,module,exports){(function(Buffer){const{PathReference:PathReference}=require("./path-reference");function numberToBytes(number){const bytes=new Uint8Array(8);const view=new DataView(bytes.buffer);view.setFloat64(0,number);return new Array(...bytes)}function bytesToNumber(bytes){if(bytes.length<8){throw new TypeError("must be 8 bytes")}const bin=new Uint8Array(bytes);const view=new DataView(bin.buffer);const nr=view.getFloat64(0);return nr}function encodeString(str){if(typeof TextEncoder!=="undefined"){const encoder=new TextEncoder;return encoder.encode(str)}else if(typeof Buffer==="function"){const buf=Buffer.from(str,"utf-8");return new Uint8Array(buf.buffer,buf.byteOffset,buf.byteLength)}else{let arr=[];for(let i=0;i128){if((code&55296)===55296){const nextCode=str.charCodeAt(i+1);if((nextCode&56320)!==56320){throw new Error("follow-up utf-16 character does not start with 0xDC00")}i++;const p1=code&1023;const p2=nextCode&1023;code=65536|p1<<10|p2}if(code<2048){const b1=192|code>>6&31;const b2=128|code&63;arr.push(b1,b2)}else if(code<65536){const b1=224|code>>12&15;const b2=128|code>>6&63;const b3=128|code&63;arr.push(b1,b2,b3)}else if(code<2097152){const b1=240|code>>18&7;const b2=128|code>>12&63;const b3=128|code>>6&63;const b4=128|code&63;arr.push(b1,b2,b3,b4)}else{throw new Error(`Cannot convert character ${str.charAt(i)} (code ${code}) to utf-8`)}}else{arr.push(code<128?code:63)}}return new Uint8Array(arr)}}function decodeString(buffer){if(typeof TextDecoder!=="undefined"){const decoder=new TextDecoder;if(buffer instanceof Uint8Array){return decoder.decode(buffer)}const buf=Uint8Array.from(buffer);return decoder.decode(buf)}else if(typeof Buffer==="function"){if(buffer instanceof Buffer){return buffer.toString("utf-8")}else if(buffer instanceof Array){const typedArray=Uint8Array.from(buffer);const buf=Buffer.from(typedArray.buffer,typedArray.byteOffset,typedArray.byteOffset+typedArray.byteLength);return buf.toString("utf-8")}else if("buffer"in buffer&&buffer["buffer"]instanceof ArrayBuffer){const buf=Buffer.from(buffer["buffer"],buffer.byteOffset,buffer.byteOffset+buffer.byteLength);return buf.toString("utf-8")}else{throw new Error(`Unsupported buffer argument`)}}else{if(!(buffer instanceof Uint8Array)&&"buffer"in buffer&&buffer["buffer"]instanceof ArrayBuffer){buffer=new Uint8Array(buffer["buffer"],buffer.byteOffset,buffer.byteLength)}if(buffer instanceof Buffer||buffer instanceof Array||buffer instanceof Uint8Array){let str="";for(let i=0;i128){if((code&240)===240){const b1=code,b2=buffer[i+1],b3=buffer[i+2],b4=buffer[i+3];code=(b1&7)<<18|(b2&63)<<12|(b3&63)<<6|b4&63;i+=3}else if((code&224)===224){const b1=code,b2=buffer[i+1],b3=buffer[i+2];code=(b1&15)<<12|(b2&63)<<6|b3&63;i+=2}else if((code&192)===192){const b1=code,b2=buffer[i+1];code=(b1&31)<<6|b2&63;i++}else{throw new Error(`invalid utf-8 data`)}}if(code>=65536){code^=65536;const p1=55296|code>>10;const p2=56320|code&1023;str+=String.fromCharCode(p1);str+=String.fromCharCode(p2)}else{str+=String.fromCharCode(code)}}return str}else{throw new Error(`Unsupported buffer argument`)}}}function concatTypedArrays(a,b){const c=new a.constructor(a.length+b.length);c.set(a);c.set(b,a.length);return c}function cloneObject(original,stack){const{DataSnapshot:DataSnapshot}=require("./data-snapshot");if(original instanceof DataSnapshot){throw new TypeError(`Object to clone is a DataSnapshot (path "${original.ref.path}")`)}const checkAndFixTypedArray=obj=>{if(obj!==null&&typeof obj==="object"&&typeof obj.constructor==="function"&&typeof obj.constructor.name==="string"&&["Buffer","Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","BigUint64Array","BigInt64Array"].includes(obj.constructor.name)){obj=obj.buffer.slice(obj.byteOffset,obj.byteOffset+obj.byteLength)}return obj};original=checkAndFixTypedArray(original);if(typeof original!=="object"||original===null||original instanceof Date||original instanceof ArrayBuffer||original instanceof PathReference||original instanceof RegExp){return original}const cloneValue=val=>{if(stack.indexOf(val)>=0){throw new ReferenceError(`object contains a circular reference`)}val=checkAndFixTypedArray(val);if(val===null||val instanceof Date||val instanceof ArrayBuffer||val instanceof PathReference||val instanceof RegExp){return val}else if(val instanceof Array){stack.push(val);val=val.map(item=>cloneValue(item));stack.pop();return val}else if(typeof val==="object"){stack.push(val);val=cloneObject(val,stack);stack.pop();return val}else{return val}};if(typeof stack==="undefined"){stack=[original]}const clone={};Object.keys(original).forEach(key=>{let val=original[key];if(typeof val==="function"){return}clone[key]=cloneValue(val)});return clone}function compareValues(oldVal,newVal){const voids=[undefined,null];if(oldVal===newVal){return"identical"}else if(voids.indexOf(oldVal)>=0&&voids.indexOf(newVal)<0){return"added"}else if(voids.indexOf(oldVal)<0&&voids.indexOf(newVal)>=0){return"removed"}else if(typeof oldVal!==typeof newVal){return"changed"}else if(typeof oldVal==="object"){const isArray=oldVal instanceof Array;const oldKeys=isArray?Object.keys(oldVal).map(v=>parseInt(v)):Object.keys(oldVal);const newKeys=isArray?Object.keys(newVal).map(v=>parseInt(v)):Object.keys(newVal);const removedKeys=oldKeys.filter(key=>newKeys.indexOf(key)<0);const addedKeys=newKeys.filter(key=>oldKeys.indexOf(key)<0);const changedKeys=newKeys.reduce((changed,key)=>{if(oldKeys.indexOf(key)>=0){const val1=oldVal[key];const val2=newVal[key];const c=compareValues(val1,val2);if(c!=="identical"){changed.push({key:key,change:c})}}return changed},[]);if(addedKeys.length===0&&removedKeys.length===0&&changedKeys.length===0){return"identical"}else{return{added:addedKeys,removed:removedKeys,changed:changedKeys}}}else if(oldVal!==newVal){return"changed"}return"identical"}const getChildValues=(childKey,oldValue,newValue)=>{oldValue=oldValue===null?null:oldValue[childKey];if(typeof oldValue==="undefined"){oldValue=null}newValue=newValue===null?null:newValue[childKey];if(typeof newValue==="undefined"){newValue=null}return{oldValue:oldValue,newValue:newValue}};module.exports={numberToBytes:numberToBytes,bytesToNumber:bytesToNumber,concatTypedArrays:concatTypedArrays,cloneObject:cloneObject,compareValues:compareValues,getChildValues:getChildValues,encodeString:encodeString,decodeString:decodeString}}).call(this,require("buffer").Buffer)},{"./data-snapshot":8,"./path-reference":13,buffer:41}],18:[function(require,module,exports){var colors={};module["exports"]=colors;colors.themes={};var util=require("util");var ansiStyles=colors.styles=require("./styles");var defineProps=Object.defineProperties;var newLineRegex=new RegExp(/[\r\n]+/g);colors.supportsColor=require("./system/supports-colors").supportsColor;if(typeof colors.enabled==="undefined"){colors.enabled=colors.supportsColor()!==false}colors.enable=function(){colors.enabled=true};colors.disable=function(){colors.enabled=false};colors.stripColors=colors.strip=function(str){return(""+str).replace(/\x1B\[\d+m/g,"")};var stylize=colors.stylize=function stylize(str,style){if(!colors.enabled){return str+""}return ansiStyles[style].open+str+ansiStyles[style].close};var matchOperatorsRe=/[|\\{}()[\]^$+*?.]/g;var escapeStringRegexp=function(str){if(typeof str!=="string"){throw new TypeError("Expected a string")}return str.replace(matchOperatorsRe,"\\$&")};function build(_styles){var builder=function builder(){return applyStyle.apply(builder,arguments)};builder._styles=_styles;builder.__proto__=proto;return builder}var styles=function(){var ret={};ansiStyles.grey=ansiStyles.gray;Object.keys(ansiStyles).forEach((function(key){ansiStyles[key].closeRe=new RegExp(escapeStringRegexp(ansiStyles[key].close),"g");ret[key]={get:function(){return build(this._styles.concat(key))}}}));return ret}();var proto=defineProps((function colors(){}),styles);function applyStyle(){var args=Array.prototype.slice.call(arguments);var str=args.map((function(arg){if(arg!=undefined&&arg.constructor===String){return arg}else{return util.inspect(arg)}})).join(" ");if(!colors.enabled||!str){return str}var newLinesPresent=str.indexOf("\n")!=-1;var nestedStyles=this._styles;var i=nestedStyles.length;while(i--){var code=ansiStyles[nestedStyles[i]];str=code.open+str.replace(code.closeRe,code.open)+code.close;if(newLinesPresent){str=str.replace(newLineRegex,(function(match){return code.close+match+code.open}))}}return str}colors.setTheme=function(theme){if(typeof theme==="string"){console.log("colors.setTheme now only accepts an object, not a string. "+"If you are trying to set a theme from a file, it is now your (the "+"caller's) responsibility to require the file. The old syntax "+"looked like colors.setTheme(__dirname + "+"'/../themes/generic-logging.js'); The new syntax looks like "+"colors.setTheme(require(__dirname + "+"'/../themes/generic-logging.js'));");return}for(var style in theme){(function(style){colors[style]=function(str){if(typeof theme[style]==="object"){var out=str;for(var i in theme[style]){out=colors[theme[style][i]](out)}return out}return colors[theme[style]](str)}})(style)}};function init(){var ret={};Object.keys(styles).forEach((function(name){ret[name]={get:function(){return build([name])}}}));return ret}var sequencer=function sequencer(map,str){var exploded=str.split("");exploded=exploded.map(map);return exploded.join("")};colors.trap=require("./custom/trap");colors.zalgo=require("./custom/zalgo");colors.maps={};colors.maps.america=require("./maps/america")(colors);colors.maps.zebra=require("./maps/zebra")(colors);colors.maps.rainbow=require("./maps/rainbow")(colors);colors.maps.random=require("./maps/random")(colors);for(var map in colors.maps){(function(map){colors[map]=function(str){return sequencer(colors.maps[map],str)}})(map)}defineProps(colors,init())},{"./custom/trap":19,"./custom/zalgo":20,"./maps/america":23,"./maps/rainbow":24,"./maps/random":25,"./maps/zebra":26,"./styles":27,"./system/supports-colors":29,util:47}],19:[function(require,module,exports){module["exports"]=function runTheTrap(text,options){var result="";text=text||"Run the trap, drop the bass";text=text.split("");var trap={a:["@","Ą","Ⱥ","Ʌ","Δ","Λ","Д"],b:["ß","Ɓ","Ƀ","ɮ","β","฿"],c:["©","Ȼ","Ͼ"],d:["Ð","Ɗ","Ԁ","ԁ","Ԃ","ԃ"],e:["Ë","ĕ","Ǝ","ɘ","Σ","ξ","Ҽ","੬"],f:["Ӻ"],g:["ɢ"],h:["Ħ","ƕ","Ң","Һ","Ӈ","Ԋ"],i:["༏"],j:["Ĵ"],k:["ĸ","Ҡ","Ӄ","Ԟ"],l:["Ĺ"],m:["ʍ","Ӎ","ӎ","Ԡ","ԡ","൩"],n:["Ñ","ŋ","Ɲ","Ͷ","Π","Ҋ"],o:["Ø","õ","ø","Ǿ","ʘ","Ѻ","ם","۝","๏"],p:["Ƿ","Ҏ"],q:["্"],r:["®","Ʀ","Ȑ","Ɍ","ʀ","Я"],s:["§","Ϟ","ϟ","Ϩ"],t:["Ł","Ŧ","ͳ"],u:["Ʊ","Ս"],v:["ט"],w:["Ш","Ѡ","Ѽ","൰"],x:["Ҳ","Ӿ","Ӽ","ӽ"],y:["¥","Ұ","Ӌ"],z:["Ƶ","ɀ"]};text.forEach((function(c){c=c.toLowerCase();var chars=trap[c]||[" "];var rand=Math.floor(Math.random()*chars.length);if(typeof trap[c]!=="undefined"){result+=trap[c][rand]}else{result+=c}}));return result}},{}],20:[function(require,module,exports){module["exports"]=function zalgo(text,options){text=text||" he is here ";var soul={up:["̍","̎","̄","̅","̿","̑","̆","̐","͒","͗","͑","̇","̈","̊","͂","̓","̈","͊","͋","͌","̃","̂","̌","͐","̀","́","̋","̏","̒","̓","̔","̽","̉","ͣ","ͤ","ͥ","ͦ","ͧ","ͨ","ͩ","ͪ","ͫ","ͬ","ͭ","ͮ","ͯ","̾","͛","͆","̚"],down:["̖","̗","̘","̙","̜","̝","̞","̟","̠","̤","̥","̦","̩","̪","̫","̬","̭","̮","̯","̰","̱","̲","̳","̹","̺","̻","̼","ͅ","͇","͈","͉","͍","͎","͓","͔","͕","͖","͙","͚","̣"],mid:["̕","̛","̀","́","͘","̡","̢","̧","̨","̴","̵","̶","͜","͝","͞","͟","͠","͢","̸","̷","͡"," ҉"]};var all=[].concat(soul.up,soul.down,soul.mid);function randomNumber(range){var r=Math.floor(Math.random()*range);return r}function isChar(character){var bool=false;all.filter((function(i){bool=i===character}));return bool}function heComes(text,options){var result="";var counts;var l;options=options||{};options["up"]=typeof options["up"]!=="undefined"?options["up"]:true;options["mid"]=typeof options["mid"]!=="undefined"?options["mid"]:true;options["down"]=typeof options["down"]!=="undefined"?options["down"]:true;options["size"]=typeof options["size"]!=="undefined"?options["size"]:"maxi";text=text.split("");for(l in text){if(isChar(l)){continue}result=result+text[l];counts={up:0,down:0,mid:0};switch(options.size){case"mini":counts.up=randomNumber(8);counts.mid=randomNumber(2);counts.down=randomNumber(8);break;case"maxi":counts.up=randomNumber(16)+3;counts.mid=randomNumber(4)+1;counts.down=randomNumber(64)+3;break;default:counts.up=randomNumber(8)+1;counts.mid=randomNumber(6)/2;counts.down=randomNumber(8)+1;break}var arr=["up","mid","down"];for(var d in arr){var index=arr[d];for(var i=0;i<=counts[index];i++){if(options[index]){result=result+soul[index][randomNumber(soul[index].length)]}}}}return result}return heComes(text,options)}},{}],21:[function(require,module,exports){var colors=require("./colors");module["exports"]=function(){var addProperty=function(color,func){String.prototype.__defineGetter__(color,func)};addProperty("strip",(function(){return colors.strip(this)}));addProperty("stripColors",(function(){return colors.strip(this)}));addProperty("trap",(function(){return colors.trap(this)}));addProperty("zalgo",(function(){return colors.zalgo(this)}));addProperty("zebra",(function(){return colors.zebra(this)}));addProperty("rainbow",(function(){return colors.rainbow(this)}));addProperty("random",(function(){return colors.random(this)}));addProperty("america",(function(){return colors.america(this)}));var x=Object.keys(colors.styles);x.forEach((function(style){addProperty(style,(function(){return colors.stylize(this,style)}))}));function applyTheme(theme){var stringPrototypeBlacklist=["__defineGetter__","__defineSetter__","__lookupGetter__","__lookupSetter__","charAt","constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf","charCodeAt","indexOf","lastIndexOf","length","localeCompare","match","repeat","replace","search","slice","split","substring","toLocaleLowerCase","toLocaleUpperCase","toLowerCase","toUpperCase","trim","trimLeft","trimRight"];Object.keys(theme).forEach((function(prop){if(stringPrototypeBlacklist.indexOf(prop)!==-1){console.log("warn: ".red+("String.prototype"+prop).magenta+" is probably something you don't want to override. "+"Ignoring style name")}else{if(typeof theme[prop]==="string"){colors[prop]=colors[theme[prop]]}else{var tmp=colors[theme[prop][0]];for(var t=1;t=2,has16m:level>=3}}function supportsColor(stream){if(forceColor===false){return 0}if(hasFlag("color=16m")||hasFlag("color=full")||hasFlag("color=truecolor")){return 3}if(hasFlag("color=256")){return 2}if(stream&&!stream.isTTY&&forceColor!==true){return 0}var min=forceColor?1:0;if(process.platform==="win32"){var osRelease=os.release().split(".");if(Number(process.versions.node.split(".")[0])>=8&&Number(osRelease[0])>=10&&Number(osRelease[2])>=10586){return Number(osRelease[2])>=14931?3:2}return 1}if("CI"in env){if(["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some((function(sign){return sign in env}))||env.CI_NAME==="codeship"){return 1}return min}if("TEAMCITY_VERSION"in env){return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION)?1:0}if("TERM_PROGRAM"in env){var version=parseInt((env.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(env.TERM_PROGRAM){case"iTerm.app":return version>=3?3:2;case"Hyper":return 3;case"Apple_Terminal":return 2}}if(/-256(color)?$/i.test(env.TERM)){return 2}if(/^screen|^xterm|^vt100|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)){return 1}if("COLORTERM"in env){return 1}if(env.TERM==="dumb"){return min}return min}function getSupportLevel(stream){var level=supportsColor(stream);return translateLevel(level)}module.exports={supportsColor:getSupportLevel,stdout:getSupportLevel(process.stdout),stderr:getSupportLevel(process.stderr)}}).call(this,require("_process"))},{"./has-flag.js":28,_process:45,os:44}],30:[function(require,module,exports){const{AceBase:AceBase,AceBaseLocalSettings:AceBaseLocalSettings}=require("./acebase-local");const{CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers,ICustomStorageNode:ICustomStorageNode,ICustomStorageNodeMetaData:ICustomStorageNodeMetaData}=require("./storage-custom");const deprecatedConstructorError=`Using AceBase constructor in the browser to use localStorage is deprecated!\nSwitch to:\nIndexedDB implementation (FASTER, MORE RELIABLE):\n let db = AceBase.WithIndexedDB(name, settings)\nOr, new LocalStorage implementation:\n let db = AceBase.WithLocalStorage(name, settings)\nOr, write your own CustomStorage adapter:\n let myCustomStorage = new CustomStorageSettings({ ... });\n let db = new AceBase(name, { storage: myCustomStorage })`;class BrowserAceBase extends AceBase{constructor(name,settings){if(typeof settings!=="object"||typeof settings.storage!=="object"){throw new Error(deprecatedConstructorError)}super(name,settings)}static WithIndexedDB(dbname,settings){settings=settings||{};if(!settings.logLevel){settings.logLevel="error"}const IndexedDB=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;let request=IndexedDB.open(`${dbname}.acebase`,1);let readyResolve,readyReject,readyPromise=new Promise((rs,rj)=>{readyResolve=rs;readyReject=rj});request.onupgradeneeded=e=>{let db=request.result;db.createObjectStore("nodes",{keyPath:"path"});db.createObjectStore("content")};let db;request.onsuccess=e=>{db=request.result;readyResolve()};request.onerror=e=>{readyReject(e)};const storageSettings=new CustomStorageSettings({name:"IndexedDB",locking:false,ready(){return readyPromise},async getTransaction(target){const context={debug:true,db:db};const transaction=new IndexedDBStorageTransaction(context,target);await transaction.start();return transaction}});return new AceBase(dbname,{logLevel:settings.logLevel,storage:storageSettings})}static WithLocalStorage(dbname,settings){settings=settings||{};if(!settings.logLevel){settings.logLevel="error"}const localStorage=settings.provider?settings.provider:settings.temp?window.localStorage:window.sessionStorage;const storageSettings=new CustomStorageSettings({name:"LocalStorage",locking:true,ready(){return Promise.resolve()},getTransaction(target){const context={debug:true,dbname:dbname,localStorage:localStorage};const transaction=new LocalStorageTransaction(context,target);return Promise.resolve(transaction)}});return new AceBase(dbname,{logLevel:settings.logLevel,storage:storageSettings})}}class IndexedDBStorageTransaction extends CustomStorageTransaction{constructor(context,target){super(target);this.context=context}start(){return new Promise((resolve,reject)=>{this._tx=this.context.db.transaction(["nodes","content"],this.target.write?"readwrite":"readonly");resolve()})}commit(){const tx=this._tx;tx.commit&&tx.commit()}rollback(err){const tx=this._tx;tx.abort&&tx.abort()}get(path){const tx=this._tx;const request=tx.objectStore("nodes").get(path);return new Promise((resolve,reject)=>{request.onsuccess=event=>{const data=request.result;if(!data){return resolve(null)}const node=data.metadata;const contentReq=tx.objectStore("content").get(path);contentReq.onsuccess=e=>{node.value=contentReq.result;resolve(node)};contentReq.onerror=e=>reject(e)};request.onerror=e=>{console.error(`IndexedDB get error`,e);reject(e)}})}set(path,node){const copy={};const value=node.value;Object.assign(copy,node);delete copy.value;const metadata=copy;const obj={path:path,metadata:metadata};return new Promise((resolve,reject)=>{const tx=this._tx;const request=tx.objectStore("nodes").put(obj);request.onerror=e=>reject(e);request.onsuccess=e=>{const contentReq=tx.objectStore("content").put(value,path);contentReq.onsuccess=e=>resolve();contentReq.onerror=e=>{tx.abort();reject(e)}}})}remove(path){const tx=this._tx;return new Promise((resolve,reject)=>{const r1=tx.objectStore("content").delete(path);r1.onerror=e=>reject(e);r1.onsuccess=e=>{const r2=tx.objectStore("nodes").delete(path);r2.onerror=e=>{tx.abort();reject(e)};r2.onsuccess=e=>resolve()}})}childrenOf(path,include,checkCallback,addCallback){const pathInfo=CustomStorageHelpers.PathInfo.get(path);const tx=this._tx;const store=tx.objectStore("nodes");const query=IDBKeyRange.lowerBound(path,true);return new Promise((resolve,reject)=>{const cursor=include.metadata?store.openCursor(query):store.openKeyCursor(query);cursor.onerror=e=>reject(e);cursor.onsuccess=async e=>{const otherPath=cursor.result?cursor.result.key:null;let keepGoing=true;if(otherPath===null){keepGoing=false}else if(!pathInfo.isAncestorOf(otherPath)){keepGoing=false}else if(pathInfo.isParentOf(otherPath)&&checkCallback(otherPath)){let node;if(include.metadata){const valueCursor=cursor;const data=valueCursor.result.value;node=data.metadata;if(include.value){const req=tx.objectStore("content").get(otherPath);await new Promise((resolve,reject)=>{req.onerror=e=>reject(e);req.onsuccess=e=>{node.value=req.result;resolve()}})}}keepGoing=addCallback(otherPath,node)}if(keepGoing){try{cursor.result.continue()}catch(err){keepGoing=false}}if(!keepGoing){resolve()}}})}descendantsOf(path,include,checkCallback,addCallback){const pathInfo=CustomStorageHelpers.PathInfo.get(path);const tx=this._tx;const store=tx.objectStore("nodes");const query=IDBKeyRange.lowerBound(path,true);return new Promise((resolve,reject)=>{const cursor=include.metadata?store.openCursor(query):store.openKeyCursor(query);cursor.onerror=e=>reject(e);cursor.onsuccess=async e=>{const otherPath=cursor.result?cursor.result.key:null;let keepGoing=true;if(otherPath===null){keepGoing=false}else if(!pathInfo.isAncestorOf(otherPath)){keepGoing=false}else if(checkCallback(otherPath)){let node;if(include.metadata){const valueCursor=cursor;const data=valueCursor.result.value;node=data.metadata;if(include.value){const req=tx.objectStore("content").get(otherPath);await new Promise((resolve,reject)=>{req.onerror=e=>reject(e);req.onsuccess=e=>{node.value=req.result;resolve()}})}}keepGoing=addCallback(otherPath,node)}if(keepGoing){try{cursor.result.continue()}catch(err){keepGoing=false}}if(!keepGoing){resolve()}}})}}class LocalStorageTransaction extends CustomStorageTransaction{constructor(context,target){super(target);this.context=context;this._storageKeysPrefix=`${this.context.dbname}.acebase::`}commit(){return Promise.resolve()}rollback(err){return Promise.resolve()}get(path){return new Promise(resolve=>{const json=this.context.localStorage.getItem(this.getStorageKeyForPath(path));const val=JSON.parse(json);resolve(val)})}set(path,val){return new Promise(resolve=>{const json=JSON.stringify(val);this.context.localStorage.setItem(this.getStorageKeyForPath(path),json);resolve()})}remove(path){return new Promise(resolve=>{this.context.localStorage.removeItem(this.getStorageKeyForPath(path));resolve()})}childrenOf(path,include,checkCallback,addCallback){return new Promise(resolve=>{const pathInfo=CustomStorageHelpers.PathInfo.get(path);for(let i=0;i{const pathInfo=CustomStorageHelpers.PathInfo.get(path);for(let i=0;i{this.emit("ready")})}}module.exports={AceBase:AceBase,AceBaseLocalSettings:AceBaseLocalSettings}},{"./api-local":32,"./storage":40,"acebase-core":11}],32:[function(require,module,exports){const{Api:Api,Utils:Utils}=require("acebase-core");const{AceBase:AceBase}=require("./acebase-local");const{StorageSettings:StorageSettings}=require("./storage");const{AceBaseStorage:AceBaseStorage,AceBaseStorageSettings:AceBaseStorageSettings}=require("./storage-acebase");const{SQLiteStorage:SQLiteStorage,SQLiteStorageSettings:SQLiteStorageSettings}=require("./storage-sqlite");const{MSSQLStorage:MSSQLStorage,MSSQLStorageSettings:MSSQLStorageSettings}=require("./storage-mssql");const{LocalStorage:LocalStorage,LocalStorageSettings:LocalStorageSettings}=require("./storage-localstorage");const{CustomStorage:CustomStorage,CustomStorageSettings:CustomStorageSettings}=require("./storage-custom");const{Node:Node}=require("./node");const{DataIndex:DataIndex}=require("./data-index");class LocalApi extends Api{constructor(dbname="default",settings,readyCallback){super();this.db=settings.db;if(typeof settings.storage==="object"){settings.storage.logLevel=settings.logLevel;if(SQLiteStorageSettings&&(settings.storage instanceof SQLiteStorageSettings||settings.storage.type==="sqlite")){this.storage=new SQLiteStorage(dbname,settings.storage)}else if(MSSQLStorageSettings&&(settings.storage instanceof MSSQLStorageSettings||settings.storage.type==="mssql")){this.storage=new MSSQLStorage(dbname,settings.storage)}else if(LocalStorageSettings&&(settings.storage instanceof LocalStorageSettings||settings.storage.type==="localstorage")){this.storage=new LocalStorage(dbname,settings.storage)}else if(CustomStorageSettings&&(settings.storage instanceof CustomStorageSettings||settings.storage.type==="custom")){this.storage=new CustomStorage(dbname,settings.storage)}else{const storageSettings=settings.storage instanceof AceBaseStorageSettings?settings.storage:new AceBaseStorageSettings(settings.storage);this.storage=new AceBaseStorage(dbname,storageSettings)}}else{settings.storage=new AceBaseStorageSettings({logLevel:settings.logLevel});this.storage=new AceBaseStorage(dbname,settings.storage)}this.storage.on("ready",readyCallback)}stats(options){return Promise.resolve(this.storage.stats)}subscribe(path,event,callback){this.storage.subscriptions.add(path,event,callback)}unsubscribe(path,event=undefined,callback=undefined){this.storage.subscriptions.remove(path,event,callback)}set(path,value,flags=undefined){return Node.update(this.storage,path,value,{merge:false})}update(path,updates,flags=undefined){return Node.update(this.storage,path,updates,{merge:true})}get(path,options){return Node.getValue(this.storage,path,options)}transaction(path,callback){return Node.transaction(this.storage,path,callback)}exists(path){return Node.exists(this.storage,path)}query2(path,query,options={snapshots:false,include:undefined,exclude:undefined,child_objects:undefined}){}query(path,query,options={snapshots:false,include:undefined,exclude:undefined,child_objects:undefined,eventHandler:event=>{}}){if(typeof options!=="object"){options={}}if(typeof options.snapshots==="undefined"){options.snapshots=false}const sortMatches=matches=>{matches.sort((a,b)=>{const compare=i=>{const o=query.order[i];let left=a.val[o.key];let right=b.val[o.key];if(typeof left==="undefined"&&typeof right!=="undefined"){return o.ascending?-1:1}if(typeof left!=="undefined"&&typeof right==="undefined"){return o.ascending?1:-1}if(typeof left==="undefined"&&typeof right==="undefined"){return 0}if(left==right){if(iright){return o.ascending?1:-1}};return compare(0)})};const loadResultsData=(preResults,options)=>{if(preResults.length===0){return Promise.resolve([])}const maxBatchSize=50;let batches=[];const items=preResults.map((result,index)=>({path:result.path,index:index}));while(items.length>0){let batchItems=items.splice(0,maxBatchSize);batches.push(batchItems)}const results=[];const nextBatch=()=>{const batch=batches.shift();return Promise.all(batch.map(item=>{const{path:path,index:index}=item;return Node.getValue(this.storage,path,options).then(val=>{if(val===null){this.storage.debug.warn(`Indexed result "/${path}" does not have a record!`);return}const result={path:path,val:val};if(stepsExecuted.sorted){results[index]=result}else{results.push(result);if(!stepsExecuted.skipped&&results.length>query.skip+query.take){sortMatches(results);results.pop()}}})})).then(()=>{if(batches.length>0){return nextBatch()}})};return nextBatch().then(()=>{return results})};const isWildcardPath=path.indexOf("*")>=0||path.indexOf("$")>=0;const availableIndexes=this.storage.indexes.get(path);const usingIndexes=[];query.filters.forEach(filter=>{if(filter.index){return}const indexesOnKey=availableIndexes.filter(index=>index.key===filter.key).filter(index=>{return index.validOperators.includes(filter.op)});if(indexesOnKey.length>=1){const otherFilterKeys=query.filters.filter(f=>f!==filter).map(f=>f.key);const sortKeys=query.order.map(o=>o.key).filter(key=>key!==filter.key);const beneficialIndexes=indexesOnKey.map(index=>{const availableKeys=index.includeKeys.concat(index.key);const forOtherFilters=availableKeys.filter(key=>otherFilterKeys.indexOf(key)>=0);const forSorting=availableKeys.filter(key=>sortKeys.indexOf(key)>=0);const forBoth=forOtherFilters.concat(forSorting.filter(index=>forOtherFilters.indexOf(index)<0));const points={filters:forOtherFilters.length,sorting:forSorting.length*(query.take>0?forSorting.length:1),both:forBoth.length*forBoth.length,get total(){return this.filters+this.sorting+this.both}};return{index:index,points:points.total,filterKeys:forOtherFilters,sortKeys:forSorting}});beneficialIndexes.sort((a,b)=>a.points>b.points?-1:1);const bestBenificialIndex=beneficialIndexes[0];filter.index=bestBenificialIndex.index;bestBenificialIndex.filterKeys.forEach(key=>{query.filters.filter(f=>f!==filter&&f.key===key).forEach(f=>{if(!DataIndex.validOperators.includes(f.op)){return}f.indexUsage="filter";f.index=bestBenificialIndex.index})});bestBenificialIndex.sortKeys.forEach(key=>{query.order.filter(s=>s.key===key).forEach(s=>{s.index=bestBenificialIndex.index})})}if(filter.index){usingIndexes.push({index:filter.index,description:filter.index.description})}});if(query.order.length>0&&query.take>0){query.order.forEach(sort=>{if(sort.index){return}sort.index=availableIndexes.filter(index=>index.key===sort.key).find(index=>index.type==="normal")})}const indexDescriptions=usingIndexes.map(index=>index.description).join(", ");usingIndexes.length>0&&this.storage.debug.log(`Using indexes for query: ${indexDescriptions}`);const tableScanFilters=query.filters.filter(filter=>!filter.index);const specialOpsRegex=/^[a-z]+\:/i;if(tableScanFilters.some(filter=>specialOpsRegex.test(filter.op))){const f=tableScanFilters.find(filter=>specialOpsRegex.test(filter.op));const err=new Error(`query contains operator "${f.op}" which requires a special index that was not found on path "${path}", key "${f.key}"`);return Promise.reject(err)}const allowedTableScanOperators=["<","<=","==","!=",">=",">","like","!like","in","!in","matches","!matches","between","!between","has","!has","contains","!contains","exists","!exists"];for(let i=0;i0){const keys=tableScanFilters.reduce((keys,f)=>{if(keys.indexOf(f.key)<0){keys.push(f.key)}return keys},[]).map(key=>`"${key}"`);const err=new Error(`This wildcard path query on "/${path}" requires index(es) on key(s): ${keys.join(", ")}. Create the index(es) and retry`);return Promise.reject(err)}const indexScanPromises=[];query.filters.forEach(filter=>{if(filter.index&&filter.indexUsage!=="filter"){let promise=filter.index.query(filter.op,filter.compare).then(results=>{options.eventHandler&&options.eventHandler({name:"stats",type:"index_query",source:filter.index.description,stats:results.stats});if(results.hints.length>0){options.eventHandler&&options.eventHandler({name:"hints",type:"index_query",source:filter.index.description,hints:results.hints})}return results});const resultFilters=query.filters.filter(f=>f.index===filter.index&&f.indexUsage==="filter");if(resultFilters.length>0){promise=promise.then(results=>{resultFilters.forEach(filter=>{results=results.filterMetadata(filter.key,filter.op,filter.compare)});return results})}indexScanPromises.push(promise)}});const stepsExecuted={filtered:query.filters.length===0,skipped:query.skip===0,taken:query.take===0,sorted:query.order.length===0,preDataLoaded:false,dataLoaded:false};if(query.filters.length===0&&query.take===0){this.storage.debug.error(`Filterless queries must use .take to limit the results. Defaulting to 100 for query on path "${path}"`);query.take=100}if(query.filters.length===0&&query.order.length>0&&query.order[0].index){const sortIndex=query.order[0].index;this.storage.debug.log(`Using index for sorting: ${sortIndex.description}`);const promise=sortIndex.take(query.skip,query.take,query.order[0].ascending).then(results=>{options.eventHandler&&options.eventHandler({name:"stats",type:"sort_index_take",source:filter.index.description,stats:results.stats});if(results.hints.length>0){options.eventHandler&&options.eventHandler({name:"hints",type:"sort_index_take",source:filter.index.description,hints:results.hints})}return results});indexScanPromises.push(promise);stepsExecuted.skipped=true;stepsExecuted.taken=true;stepsExecuted.sorted=true}return Promise.all(indexScanPromises).then(indexResultSets=>{let indexedResults=[];if(indexResultSets.length===1){const resultSet=indexResultSets[0];indexedResults=resultSet.map(match=>{const result={key:match.key,path:match.path,val:{[resultSet.filterKey]:match.value}};match.metadata&&Object.assign(result.val,match.metadata);return result});stepsExecuted.filtered=true}else if(indexResultSets.length>1){indexResultSets.sort((a,b)=>a.length{const result={key:match.key,path:match.path,val:{[shortestSet.filterKey]:match.value}};const matchedInAllSets=otherSets.every(set=>set.findIndex(m=>match.path===match.path)>=0);if(matchedInAllSets){match.metadata&&Object.assign(result.val,match.metadata);otherSets.forEach(set=>{const otherResult=set.find(r=>r.path===result.path);result.val[set.filterKey]=otherResult.value;otherResult.metadata&&Object.assign(result.val,otherResult.metadata)});results.push(result)}return results},[]);stepsExecuted.filtered=true}if(isWildcardPath||indexScanPromises.length>0&&tableScanFilters.length===0){if(query.order.length===0||query.order.every(o=>o.index)){stepsExecuted.preDataLoaded=true;if(!stepsExecuted.sorted&&query.order.length>0){sortMatches(indexedResults)}stepsExecuted.sorted=true;if(!stepsExecuted.skipped&&query.skip>0){indexedResults=indexedResults.slice(query.skip)}if(!stepsExecuted.taken&&query.take>0){indexedResults=indexedResults.slice(0,query.take)}stepsExecuted.skipped=true;stepsExecuted.taken=true;if(!options.snapshots){return indexedResults}const childOptions={include:options.include,exclude:options.exclude,child_objects:options.child_objects};return loadResultsData(indexedResults,childOptions).then(results=>{stepsExecuted.dataLoaded=true;return results})}if(options.snapshots||!stepsExecuted.sorted){const loadPartialResults=query.order.length>0;const childOptions=loadPartialResults?{include:query.order.map(order=>order.key)}:{include:options.include,exclude:options.exclude,child_objects:options.child_objects};return loadResultsData(indexedResults,childOptions).then(results=>{if(query.order.length>0){sortMatches(results)}stepsExecuted.sorted=true;if(query.skip>0){results=results.slice(query.skip)}if(query.take>0){results=results.slice(0,query.take)}stepsExecuted.skipped=true;stepsExecuted.taken=true;if(options.snapshots&&loadPartialResults){return loadResultsData(results,{include:options.include,exclude:options.exclude,child_objects:options.child_objects})}return results})}else{return indexedResults}}let indexKeyFilter;if(indexedResults.length>0){indexKeyFilter=indexedResults.map(result=>result.key)}const promises=[];let matches=[];let preliminaryStop=false;const loadPartialData=query.order.length>0;const childOptions=loadPartialData?{include:query.order.map(order=>order.key)}:{include:options.include,exclude:options.exclude,child_objects:options.child_objects};return Node.getChildren(this.storage,path,indexKeyFilter).next(child=>{if(child.type===Node.VALUE_TYPES.OBJECT){if(!child.address){return}if(preliminaryStop){return false}const p=Node.matches(this.storage,child.address.path,tableScanFilters).then(isMatch=>{if(!isMatch){return null}const childPath=child.address.path;if(options.snapshots||query.order.length>0){return Node.getValue(this.storage,childPath,childOptions).then(val=>{return{path:childPath,val:val}})}else{return{path:childPath}}}).then(result=>{if(result!==null){matches.push(result);if(query.take>0&&matches.length>query.take+query.skip){if(query.order.length>0){sortMatches(matches)}else{preliminaryStop=true}matches.pop()}}});promises.push(p)}}).catch(reason=>{this.storage.debug.warn(`Error getting child stream: ${reason}`);return[]}).then(()=>{return Promise.all(promises).then(()=>{stepsExecuted.preDataLoaded=loadPartialData;stepsExecuted.dataLoaded=!loadPartialData;if(query.order.length>0){sortMatches(matches)}stepsExecuted.sorted=true;if(query.skip>0){matches=matches.slice(query.skip)}stepsExecuted.skipped=true;if(query.take>0){matches=matches.slice(0,query.take)}stepsExecuted.taken=true;if(!stepsExecuted.dataLoaded){return loadResultsData(matches,{include:options.include,exclude:options.exclude,child_objects:options.child_objects}).then(results=>{stepsExecuted.dataLoaded=true;return results})}return matches})})}).then(matches=>{if(!stepsExecuted.sorted&&query.order.length>0){sortMatches(matches)}if(!options.snapshots){matches=matches.map(match=>match.path)}if(!stepsExecuted.skipped&&query.skip>0){matches=matches.slice(query.skip)}if(!stepsExecuted.taken&&query.take>0){matches=matches.slice(0,query.take)}if(options.monitor===true){options.monitor={add:true,change:true,remove:true}}if(typeof options.monitor==="object"&&(options.monitor.add||options.monitor.change||options.monitor.remove)){const matchedPaths=options.snapshots?matches.map(match=>match.path):matches.slice();const ref=this.db.ref(path);const removeMatch=path=>{const index=matchedPaths.indexOf(path);if(index<0){return}matchedPaths.splice(index,1)};const addMatch=path=>{if(matchedPaths.includes(path)){return}matchedPaths.push(path)};const stopMonitoring=()=>{this.unsubscribe(ref.path,"notify_child_changed",childChangedCallback);this.unsubscribe(ref.path,"notify_child_added",childAddedCallback);this.unsubscribe(ref.path,"notify_child_removed",childRemovedCallback)};const childChangedCallback=(err,path,newValue,oldValue)=>{const wasMatch=matchedPaths.includes(path);let keepMonitoring=true;const checkKeys=[];query.filters.forEach(f=>!checkKeys.includes(f.key)&&checkKeys.push(f.key));const seenKeys=[];typeof oldValue==="object"&&Object.keys(oldValue).forEach(key=>!seenKeys.includes(key)&&seenKeys.push(key));typeof newValue==="object"&&Object.keys(newValue).forEach(key=>!seenKeys.includes(key)&&seenKeys.push(key));const missingKeys=[];let isMatch=seenKeys.every(key=>{if(!checkKeys.includes(key)){return true}const filters=query.filters.filter(filter=>filter.key===key);return filters.every(filter=>{if(allowedTableScanOperators.includes(filter.op)){return this.storage.test(newValue[key],filter.op,filter.compare)}if(filter.index.constructor.name==="FullTextDataIndex"&&filter.index.localeKey&&!seenKeys.includes(filter.index.localeKey)){missingKeys.push(filter.index.localeKey);return true}return filter.index.test(newValue,filter.op,filter.compare)})});if(isMatch){missingKeys.push(...checkKeys.filter(key=>!seenKeys.includes(key)));let promise=Promise.resolve(true);if(!wasMatch&&missingKeys.length>0){const filterQueue=query.filters.filter(f=>missingKeys.includes(f.key));const simpleFilters=filterQueue.filter(f=>allowedTableScanOperators.includes(f.op));const indexFilters=filterQueue.filter(f=>!allowedTableScanOperators.includes(f.op));const processFilters=()=>{const checkIndexFilters=()=>{const keysToLoad=indexFilters.reduce((keys,filter)=>{if(!keys.includes(filter.key)){keys.push(filter.key)}if(filter.index.constructor.name==="FullTextDataIndex"&&filter.index.localeKey&&!keys.includes(filter.index.localeKey)){keys.push(filter.index.localeKey)}return keys},[]);return Node.getValue(this.storage,path,{include:keysToLoad}).then(val=>{if(val===null){return false}return indexFilters.every(filter=>filter.index.test(val,filter.op,filter.compare))})};if(simpleFilters.length>0){return Node.matches(this.storage,path,simpleFilters).then(isMatch=>{if(isMatch){if(indexFilters.length===0){return true}return checkIndexFilters()}return false})}else{return checkIndexFilters()}};promise=processFilters()}return promise.then(isMatch=>{if(isMatch){if(!wasMatch){addMatch(path)}let gotValue=value=>{if(wasMatch&&options.monitor.change){keepMonitoring=options.eventHandler({name:"change",path:path,value:value})}else if(!wasMatch&&options.monitor.add){keepMonitoring=options.eventHandler({name:"add",path:path,value:value})}if(keepMonitoring===false){stopMonitoring()}};if(options.snapshots){const loadOptions={include:options.include,exclude:options.exclude,child_objects:options.child_objects};return this.storage.getNodeValue(path,loadOptions).then(gotValue)}else{return gotValue(newValue)}}else if(wasMatch){removeMatch(path);if(options.monitor.remove){keepMonitoring=options.eventHandler({name:"remove",path:path,value:oldValue})}}if(keepMonitoring===false){stopMonitoring()}})}else{if(wasMatch){removeMatch(path);if(options.monitor.remove){keepMonitoring=options.eventHandler({name:"remove",path:path,value:oldValue});if(keepMonitoring===false){stopMonitoring()}}}}};const childAddedCallback=(err,path,newValue,oldValue)=>{let isMatch=query.filters.every(filter=>{if(allowedTableScanOperators.includes(filter.op)){return this.storage.test(newValue[filter.key],filter.op,filter.compare)}else{return filter.index.test(newValue,filter.op,filter.compare)}});let keepMonitoring=true;if(isMatch){addMatch(path);if(options.monitor.add){keepMonitoring=options.eventHandler({name:"add",path:path,value:options.snapshots?newValue:null})}}if(keepMonitoring===false){stopMonitoring()}};const childRemovedCallback=(err,path,newValue,oldValue)=>{let keepMonitoring=true;removeMatch(path);if(options.monitor.remove){keepMonitoring=options.eventHandler({name:"remove",path:path,value:options.snapshots?oldValue:null})}if(keepMonitoring===false){stopMonitoring()}};if(options.monitor.add||options.monitor.change||options.monitor.remove){this.subscribe(ref.path,"notify_child_changed",childChangedCallback)}if(options.monitor.remove){this.subscribe(ref.path,"notify_child_removed",childRemovedCallback)}if(options.monitor.add){this.subscribe(ref.path,"notify_child_added",childAddedCallback)}}return matches})}createIndex(path,key,options){return this.storage.indexes.create(path,key,options)}getIndexes(){return Promise.resolve(this.storage.indexes.list())}reflect(path,type,args){const getChildren=(path,limit=50,skip=0)=>{if(typeof limit==="string"){limit=parseInt(limit)}if(typeof skip==="string"){skip=parseInt(skip)}const children=[];let n=0,stop=skip+limit;return Node.getChildren(this.storage,path).next(childInfo=>{n++;if(limit===0||n<=stop&&n>skip){children.push({key:typeof childInfo.key==="string"?childInfo.key:childInfo.index,type:childInfo.valueTypeName,value:childInfo.value,address:typeof childInfo.address==="object"&&"pageNr"in childInfo.address?{pageNr:childInfo.address.pageNr,recordNr:childInfo.address.recordNr}:undefined})}if(limit>0&&n>stop){return false}}).then(()=>{return{more:limit!==0&&n>stop,list:children}})};switch(type){case"children":{return getChildren(path,args.limit,args.skip)}case"info":{const info={key:"",exists:false,type:"unknown",value:undefined,children:{more:false,list:[]}};return Node.getInfo(this.storage,path).then(nodeInfo=>{info.key=nodeInfo.key;info.exists=nodeInfo.exists;info.type=nodeInfo.valueTypeName;info.value=nodeInfo.value;let hasChildren=nodeInfo.exists&&nodeInfo.address&&[Node.VALUE_TYPES.OBJECT,Node.VALUE_TYPES.ARRAY].includes(nodeInfo.type);if(hasChildren){return getChildren(path,args.child_limit,args.child_skip)}}).then(children=>{info.children=children;return info})}}}export(path,stream,options={format:"json"}){return this.storage.exportNode(path,stream,options)}}module.exports={LocalApi:LocalApi}},{"./acebase-local":31,"./data-index":41,"./node":37,"./storage":40,"./storage-acebase":41,"./storage-custom":38,"./storage-localstorage":39,"./storage-mssql":41,"./storage-sqlite":41,"acebase-core":11}],33:[function(require,module,exports){const{DataReference:DataReference,DataSnapshot:DataSnapshot,EventSubscription:EventSubscription,PathReference:PathReference,TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions}=require("acebase-core");const{AceBaseLocalSettings:AceBaseLocalSettings}=require("./acebase-local");const{BrowserAceBase:BrowserAceBase}=require("./acebase-browser");const{LocalStorageSettings:LocalStorageSettings}=require("./storage-localstorage");const{CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers}=require("./storage-custom");const acebase={AceBase:BrowserAceBase,AceBaseLocalSettings:AceBaseLocalSettings,DataReference:DataReference,DataSnapshot:DataSnapshot,EventSubscription:EventSubscription,PathReference:PathReference,TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions,LocalStorageSettings:LocalStorageSettings,CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers};window.acebase=acebase;window.AceBase=BrowserAceBase;module.exports=acebase},{"./acebase-browser":30,"./acebase-local":31,"./storage-custom":38,"./storage-localstorage":39,"acebase-core":11}],34:[function(require,module,exports){const{VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}=require("./node-value-types");const{PathInfo:PathInfo}=require("acebase-core");class NodeInfo{constructor(info){this.path=info.path;this.type=info.type;this.index=info.index;this.key=info.key;this.exists=info.exists;this.address=info.address;this.value=info.value;if(typeof this.path==="string"&&(typeof this.key==="undefined"&&typeof this.index==="undefined")){let pathInfo=PathInfo.get(this.path);if(typeof pathInfo.key==="number"){this.index=pathInfo.key}else{this.key=pathInfo.key}}if(typeof this.exists==="undefined"){this.exists=true}}get valueType(){return this.type}get valueTypeName(){return getValueTypeName(this.valueType)}toString(){if(!this.exists){return`"${this.path}" doesn't exist`}if(this.address){return`"${this.path}" is ${this.valueTypeName} stored at ${this.address.pageNr},${this.address.recordNr}`}else{return`"${this.path}" is ${this.valueTypeName} with value ${this.value}`}}}module.exports={NodeInfo:NodeInfo}},{"./node-value-types":36,"acebase-core":11}],35:[function(require,module,exports){const{PathInfo:PathInfo}=require("acebase-core");const SECOND=1e3;const MINUTE=6e4;const DEBUG_MODE=false;const LOCK_TIMEOUT=DEBUG_MODE?15*MINUTE:90*SECOND;const LOCK_STATE={PENDING:"pending",LOCKED:"locked",EXPIRED:"expired",DONE:"done"};class NodeLocker{constructor(){this._locks=[]}_allowLock(path,tid,forWriting){const pathInfo=PathInfo.get(path);const existing=this._locks.find(otherLock=>otherLock.tid===tid&&otherLock.state===LOCK_STATE.LOCKED&&(otherLock.path===path||pathInfo.isDescendantOf(otherLock.path))&&(otherLock.forWriting||!forWriting));if(typeof existing==="object"){return{allow:true}}const conflict=this._locks.filter(otherLock=>otherLock.tid!==tid&&otherLock.state===LOCK_STATE.LOCKED).find(otherLock=>{return(forWriting||otherLock.forWriting)&&(path===otherLock.path||pathInfo.isDescendantOf(otherLock.path))});const clashes=typeof conflict!=="undefined";return{allow:!clashes,conflict:conflict}}_processLockQueue(){const pending=this._locks.filter(lock=>lock.state===LOCK_STATE.PENDING&&(lock.waitingFor===null||lock.waitingFor.state!==LOCK_STATE.LOCKED)).sort((a,b)=>{if(a.priority&&!b.priority){return-1}else if(!a.priority&&b.priority){return 1}return a.requested{const check=this._allowLock(lock.path,lock.tid,lock.forWriting);lock.waitingFor=check.conflict||null;if(check.allow){this.lock(lock).then(lock.resolve).catch(lock.reject)}})}lock(path,tid,forWriting=true,comment="",options={withPriority:false,noTimeout:false}){let lock,proceed;if(path instanceof NodeLock){lock=path;lock.comment=`(retry: ${lock.comment})`;proceed=true}else if(this._locks.findIndex(l=>l.tid===tid&&l.state===LOCK_STATE.EXPIRED)>=0){return Promise.reject(new Error(`lock on tid ${tid} has expired, not allowed to continue`))}else{lock=new NodeLock(this,path,tid,forWriting,options.withPriority===true);lock.comment=comment;this._locks.push(lock);const check=this._allowLock(path,tid,forWriting);lock.waitingFor=check.conflict||null;proceed=check.allow}if(proceed){lock.state=LOCK_STATE.LOCKED;if(typeof lock.granted==="number"){}else{lock.granted=Date.now();if(options.noTimeout!==true){lock.expires=Date.now()+LOCK_TIMEOUT;lock.timeout=setTimeout(()=>{if(lock.state!==LOCK_STATE.LOCKED){return}console.error(`lock :: ${lock.forWriting?"write":"read"} lock on path "/${lock.path}" by tid ${lock.tid} took too long, ${lock.comment}`);lock.state=LOCK_STATE.EXPIRED;this._processLockQueue()},LOCK_TIMEOUT)}}return Promise.resolve(lock)}else{console.assert(lock.state===LOCK_STATE.PENDING);const p=new Promise((resolve,reject)=>{lock.resolve=resolve;lock.reject=reject});return p}}unlock(lockOrId,comment,processQueue=true){let lock,i;if(lockOrId instanceof NodeLock){lock=lockOrId;i=this._locks.indexOf(lock)}else{let id=lockOrId;i=this._locks.findIndex(l=>l.id===id);lock=this._locks[i]}if(i<0){const msg=`lock on "/${lock.path}" for tid ${lock.tid} wasn't found; ${comment}`;return Promise.reject(new Error(msg))}lock.state=LOCK_STATE.DONE;clearTimeout(lock.timeout);this._locks.splice(i,1);processQueue&&this._processLockQueue();return Promise.resolve(lock)}list(){return this._locks||[]}isAllowed(path,tid,forWriting){return this._allowLock(path,tid,forWriting).allow}}let lastid=0;class NodeLock{static get LOCK_STATE(){return LOCK_STATE}constructor(locker,path,tid,forWriting,priority=false){this.locker=locker;this.path=path;this.tid=tid;this.forWriting=forWriting;this.priority=priority;this.state=LOCK_STATE.PENDING;this.requested=Date.now();this.granted=undefined;this.expires=undefined;this.comment="";this.waitingFor=null;this.id=++lastid}release(comment){return this.locker.unlock(this,comment||this.comment)}moveToParent(){const parentPath=PathInfo.get(this.path).parentPath;const allowed=this.locker.isAllowed(parentPath,this.tid,this.forWriting);if(allowed){this.waitingFor=null;this.path=parentPath;this.comment=`moved to parent: ${this.comment}`;return Promise.resolve(this)}else{this.locker.unlock(this,`moveLockToParent: ${this.comment}`,false);return this.locker.lock(parentPath,this.tid,this.forWriting,`moved to parent (queued): ${this.comment}`,{withPriority:true}).then(newLock=>{return newLock})}}moveTo(otherPath,forWriting){const allowed=this.locker.isAllowed(otherPath,this.tid,forWriting);if(allowed){this.waitingFor=null;this.path=otherPath;this.forWriting=forWriting;this.comment=`moved to "/${otherPath}": ${this.comment}`;return Promise.resolve(this)}else{this.locker.unlock(this,`moving to "/${otherPath}": ${this.comment}`,false);return this.locker.lock(otherPath,this.tid,forWriting,`moved to "/${otherPath}" (queued): ${this.comment}`,{withPriority:true}).then(newLock=>{return newLock})}}}module.exports={NodeLocker:NodeLocker,NodeLock:NodeLock}},{"acebase-core":11}],36:[function(require,module,exports){const VALUE_TYPES={OBJECT:1,ARRAY:2,NUMBER:3,BOOLEAN:4,STRING:5,DATETIME:6,BINARY:8,REFERENCE:9};function getValueTypeName(valueType){switch(valueType){case VALUE_TYPES.ARRAY:return"array";case VALUE_TYPES.BINARY:return"binary";case VALUE_TYPES.BOOLEAN:return"boolean";case VALUE_TYPES.DATETIME:return"date";case VALUE_TYPES.NUMBER:return"number";case VALUE_TYPES.OBJECT:return"object";case VALUE_TYPES.REFERENCE:return"reference";case VALUE_TYPES.STRING:return"string";default:"unknown"}}module.exports={VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}},{}],37:[function(require,module,exports){const{Storage:Storage}=require("./storage");const{NodeInfo:NodeInfo}=require("./node-info");const{VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}=require("./node-value-types");const colors=require("colors");class Node{static get VALUE_TYPES(){return VALUE_TYPES}static getInfo(storage,path,options={no_cache:false}){if(options&&!options.no_cache){let cachedInfo=storage.nodeCache.find(path);if(cachedInfo){return Promise.resolve(cachedInfo)}}return storage.getNodeInfo(path).then(info=>{if(options&&!options.no_cache){storage.nodeCache.update(info)}return info})}static update(storage,path,value,options={merge:true}){if(options.merge){return storage.updateNode(path,value)}else{return storage.setNode(path,value)}}static exists(storage,path){return storage.getNodeInfo(path).then(nodeInfo=>{return nodeInfo.exists})}static getValue(storage,path,options={include:undefined,exclude:undefined,child_objects:true}){if(!options){options={}}if(typeof options.include!=="undefined"&&!(options.include instanceof Array)){throw new TypeError(`options.include must be an array of key names`)}if(typeof options.exclude!=="undefined"&&!(options.exclude instanceof Array)){throw new TypeError(`options.exclude must be an array of key names`)}if(["undefined","boolean"].indexOf(typeof options.child_objects)<0){throw new TypeError(`options.child_objects must be a boolean`)}return storage.getNodeValue(path,options)}static getChildInfo(storage,path,childKeyOrIndex){let childInfo;return storage.getChildren(path,{keyFilter:[childKeyOrIndex]}).next(info=>{childInfo=info}).then(()=>{return childInfo})}static getChildren(storage,path,keyFilter=undefined){return storage.getChildren(path,{keyFilter:keyFilter})}static remove(storage,path){return storage.removeNode(path)}static set(storage,path,value){return Node.update(storage,path,value,{merge:false})}static transaction(storage,path,callback){return storage.transactNode(path,callback)}static matches(storage,path,criteria,options){return storage.matchNode(path,criteria,options)}}class NodeChange{static get CHANGE_TYPE(){return{UPDATE:"update",DELETE:"delete",INSERT:"insert"}}constructor(keyOrIndex,changeType,oldValue,newValue){this.keyOrIndex=keyOrIndex;this.changeType=changeType;this.oldValue=oldValue;this.newValue=newValue}}class NodeChangeTracker{constructor(path){this.path=path;this._changes=[];this._oldValue=undefined;this._newValue=undefined}addDelete(keyOrIndex,oldValue){this._changes.push(new NodeChange(keyOrIndex,NodeChange.CHANGE_TYPE.DELETE,oldValue,null))}addUpdate(keyOrIndex,oldValue,newValue){this._changes.push(new NodeChange(keyOrIndex,NodeChange.CHANGE_TYPE.UPDATE,oldValue,newValue))}addInsert(keyOrIndex,newValue){this._changes.push(new NodeChange(keyOrIndex,NodeChange.CHANGE_TYPE.INSERT,null,newValue))}add(keyOrIndex,currentValue,newValue){if(currentValue===null){if(newValue===null){throw new Error(`Wrong logic for node change on "${this.nodeInfo.path}/${keyOrIndex}" - both old and new values are null`)}this.addInsert(keyOrIndex,newValue)}else if(newValue===null){this.addDelete(keyOrIndex,currentValue)}else{this.addUpdate(keyOrIndex,currentValue,newValue)}}get updates(){return this._changes.filter(change=>change.changeType===NodeChange.CHANGE_TYPE.UPDATE)}get deletes(){return this._changes.filter(change=>change.changeType===NodeChange.CHANGE_TYPE.DELETE)}get inserts(){return this._changes.filter(change=>change.changeType===NodeChange.CHANGE_TYPE.INSERT)}get all(){return this._changes}get totalChanges(){return this._changes.length}get(keyOrIndex){return this._changes.find(change=>change.keyOrIndex===keyOrIndex)}hasChanged(keyOrIndex){return!!this.get(keyOrIndex)}get newValue(){if(typeof this._newValue==="object"){return this._newValue}if(typeof this._oldValue==="undefined"){throw new TypeError(`oldValue is not set`)}let newValue={};Object.keys(this.oldValue).forEach(key=>newValue[key]=oldValue[key]);this.deletes.forEach(change=>delete newValue[change.key]);this.updates.forEach(change=>newValue[change.key]=change.newValue);this.inserts.forEach(change=>newValue[change.key]=change.newValue);return newValue}set newValue(value){this._newValue=value}get oldValue(){if(typeof this._oldValue==="object"){return this._oldValue}if(typeof this._newValue==="undefined"){throw new TypeError(`newValue is not set`)}let oldValue={};Object.keys(this.newValue).forEach(key=>oldValue[key]=newValue[key]);this.deletes.forEach(change=>oldValue[change.key]=change.oldValue);this.updates.forEach(change=>oldValue[change.key]=change.oldValue);this.inserts.forEach(change=>delete oldValue[change.key]);return oldValue}set oldValue(value){this._oldValue=value}get typeChanged(){return typeof this.oldValue!==typeof this.newValue||this.oldValue instanceof Array&&!(this.newValue instanceof Array)||this.newValue instanceof Array&&!(this.oldValue instanceof Array)}static create(path,oldValue,newValue){const changes=new NodeChangeTracker(path);changes.oldValue=oldValue;changes.newValue=newValue;typeof oldValue==="object"&&Object.keys(oldValue).forEach(key=>{if(typeof newValue==="object"&&key in newValue&&newValue!==null){changes.add(key,oldValue[key],newValue[key])}else{changes.add(key,oldValue[key],null)}});typeof newValue==="object"&&Object.keys(newValue).forEach(key=>{if(typeof oldValue!=="object"||!(key in oldValue)||oldValue[key]===null){changes.add(key,null,newValue[key])}});return changes}}module.exports={Node:Node,NodeInfo:NodeInfo}},{"./node-info":34,"./node-value-types":36,"./storage":40,colors:22}],38:[function(require,module,exports){const{debug:debug,ID:ID,PathReference:PathReference,PathInfo:PathInfo,ascii85:ascii85}=require("acebase-core");const{NodeInfo:NodeInfo}=require("./node-info");const{NodeLocker:NodeLocker}=require("./node-lock");const{VALUE_TYPES:VALUE_TYPES}=require("./node-value-types");const{Storage:Storage,StorageSettings:StorageSettings,NodeNotFoundError:NodeNotFoundError}=require("./storage");class ICustomStorageNodeMetaData{constructor(){this.revision="";this.revision_nr=0;this.created=0;this.modified=0;this.type=0}}class ICustomStorageNode extends ICustomStorageNodeMetaData{constructor(){super();this.value=null}}class CustomStorageTransaction{constructor(target){this.target={get originalPath(){return target.path},path:target.path,get write(){return target.write}};this.id=ID.generate()}get(path){throw new Error(`CustomStorageTransaction.get must be overridden by subclass`)}set(path,node){throw new Error(`CustomStorageTransaction.set must be overridden by subclass`)}remove(path){throw new Error(`CustomStorageTransaction.remove must be overridden by subclass`)}childrenOf(path,include,checkCallback,addCallback){throw new Error(`CustomStorageTransaction.childrenOf must be overridden by subclass`)}descendantsOf(path,include,checkCallback,addCallback){throw new Error(`CustomStorageTransaction.descendantsOf must be overridden by subclass`)}getMultiple(paths){const map=new Map;return Promise.all(paths.map(path=>this.get(path).then(val=>map.set(path,val)))).then(done=>map)}async setMultiple(nodes){await Promise.all(paths.map(({path:path,node:node})=>this.set(path,node)))}async removeMultiple(paths){await Promise.all(paths.map(path=>this.remove(path)))}rollback(reason){throw new Error(`CustomStorageTransaction.rollback must be overridden by subclass`)}commit(){throw new Error(`CustomStorageTransaction.rollback must be overridden by subclass`)}async moveToParentPath(targetPath){const currentPath=this._lock&&this._lock.path||this.target.path;if(currentPath===targetPath){return targetPath}const pathInfo=CustomStorageHelpers.PathInfo.get(targetPath);if(pathInfo.isParentOf(currentPath)){if(this._lock){this._lock=await this._lock.moveToParent()}}else{throw new Error(`Locking issue. Locked path "${this._lock.path}" is not a child/descendant of "${targetPath}"`)}this.target.path=targetPath;return targetPath}}class CustomStorageSettings extends StorageSettings{constructor(settings){super(settings);settings=settings||{};if(typeof settings.ready!=="function"){throw new Error(`ready must be a function`)}if(typeof settings.getTransaction!=="function"){throw new Error(`getTransaction must be a function`)}this.name=settings.name;this.info=`${this.name||"CustomStorage"} realtime database`;this.locking=settings.locking!==false;this.ready=settings.ready;const useLocking=this.locking;const nodeLocker=useLocking?new NodeLocker:null;this.getTransaction=async({path:path,write:write})=>{const transaction=await settings.getTransaction({path:path,write:write});console.assert(typeof transaction.id==="string",`transaction id not set`);const rollback=transaction.rollback;const commit=transaction.commit;transaction.commit=async()=>{const ret=await commit.call(transaction);if(useLocking){await transaction._lock.release("commit")}return ret};transaction.rollback=async reason=>{const ret=await rollback.call(transaction,reason);if(useLocking){await transaction._lock.release("rollback")}return ret};if(useLocking){transaction._lock=await nodeLocker.lock(path,transaction.id,write,`${this.name}::getTransaction`)}return transaction}}}class CustomStorageNodeAddress{constructor(containerPath){this.path=containerPath}}class CustomStorageNodeInfo extends NodeInfo{constructor(info){super(info);this.address;this.revision=info.revision;this.revision_nr=info.revision_nr;this.created=info.created;this.modified=info.modified}}class CustomStorageHelpers{static ChildPathsSql(path,columnName="path"){const where=path===""?`${columnName} <> '' AND ${columnName} NOT LIKE '%/%'`:`(${columnName} LIKE '${path}/%' OR ${columnName} LIKE '${path}[%') AND ${columnName} NOT LIKE '${path}/%/%' AND ${columnName} NOT LIKE '${path}[%]/%' AND ${columnName} NOT LIKE '${path}[%][%'`;return where}static ChildPathsRegex(path){return new RegExp(`^${path}(?:/[^/[]+|[[0-9]+])$`)}static DescendantPathsSql(path,columnName="path"){const where=path===""?`${columnName} <> ''`:`${columnName} LIKE '${path}/%' OR ${columnName} LIKE '${path}[%'`;return where}static DescendantPathsRegex(path){return new RegExp(`^${path}(?:/[^/[]+|[[0-9]+])`)}static get PathInfo(){return PathInfo}}class CustomStorage extends Storage{constructor(dbname,settings){super(dbname,settings);this._init()}async _init(){this._customImplementation=this.settings;this.debug.log(`Database "${this.name}" details:`.intro);this.debug.log(`- Type: CustomStorage`);this.debug.log(`- Path: ${this.settings.path}`);this.debug.log(`- Max inline value size: ${this.settings.maxInlineValueSize}`.intro);this.debug.log(`- Autoremove undefined props: ${this.settings.removeVoidProperties}`);await this._customImplementation.ready();const transaction=await this._customImplementation.getTransaction({path:"",write:true});const info=await this.getNodeInfo("",{transaction:transaction});if(!info.exists){await this._writeNode("",{},{transaction:transaction})}await transaction.commit();if(this.indexes.supported){await this.indexes.load()}this.emit("ready")}_storeNode(path,node,options){const getTypedChildValue=val=>{if(val===null){throw new Error(`Not allowed to store null values. remove the property`)}else if(["string","number","boolean"].includes(typeof val)){return val}else if(val instanceof Date){return{type:VALUE_TYPES.DATETIME,value:val.getTime()}}else if(val instanceof PathReference){return{type:VALUE_TYPES.REFERENCE,value:child.path}}else if(val instanceof ArrayBuffer){return{type:VALUE_TYPES.BINARY,value:ascii85.encode(val)}}else if(typeof val==="object"){console.assert(Object.keys(val).length===0,"child object stored in parent can only be empty");return val}};const unprocessed=`Caller should have pre-processed the value by converting it to a string`;if(node.type===VALUE_TYPES.ARRAY&&node.value instanceof Array){console.warn(`Unprocessed array. ${unprocessed}`);const obj={};for(let i=0;i{node.value[key]=getTypedChildValue(original[key])})}return options.transaction.set(path,node)}_processReadNodeValue(node){const getTypedChildValue=val=>{if(val.type===VALUE_TYPES.BINARY){return ascii85.decode(val.value)}else if(val.type===VALUE_TYPES.DATETIME){return new Date(val.value)}else if(val.type===VALUE_TYPES.REFERENCE){return new PathReference(val.value)}else{throw new Error(`Unhandled child value type ${val.type}`)}};switch(node.type){case VALUE_TYPES.ARRAY:case VALUE_TYPES.OBJECT:{const obj=node.value;Object.keys(obj).forEach(key=>{let item=obj[key];if(typeof item==="object"&&"type"in item){obj[key]=getTypedChildValue(item)}});node.value=obj;break}case VALUE_TYPES.BINARY:{node.value=ascii85.decode(node.value);break}case VALUE_TYPES.REFERENCE:{node.value=new PathReference(node.value);break}case VALUE_TYPES.STRING:{break}default:throw new Error(`Invalid standalone record value type`)}}async _readNode(path,options){let node=await options.transaction.get(path);if(node===null){return null}if(typeof node!=="object"){throw new Error(`CustomStorage get function must return an ICustomStorageNode object. Use JSON.parse if your set function stored it as a string`)}this._processReadNodeValue(node);return node}_getTypeFromStoredValue(val){let type;if(typeof val==="string"){type=VALUE_TYPES.STRING}else if(typeof val==="number"){type=VALUE_TYPES.NUMBER}else if(typeof val==="boolean"){type=VALUE_TYPES.BOOLEAN}else if(val instanceof Array){type=VALUE_TYPES.ARRAY}else if(typeof val==="object"){if("type"in val){type=val.type;val=val.value;if(type===VALUE_TYPES.DATETIME){val=new Date(val)}else if(type===VALUE_TYPES.REFERENCE){val=new PathReference(val)}}else{type=VALUE_TYPES.OBJECT}}else{throw new Error(`Unknown value type`)}return{type:type,value:val}}async _writeNode(path,value,options){if(this.valueFitsInline(value)&&path!==""){throw new Error(`invalid value to store in its own node`)}else if(path===""&&(typeof value!=="object"||value instanceof Array)){throw new Error(`Invalid root node value. Must be an object`)}const transaction=options.transaction;const currentRow=await this._readNode(path,{transaction:transaction});const revision=options.revision||ID.generate();let mainNode={type:VALUE_TYPES.OBJECT,value:{}};const childNodeValues={};if(value instanceof Array){mainNode.type=VALUE_TYPES.ARRAY;const obj={};for(let i=0;i{const val=value[key];delete mainNode.value[key];if(val===null){return}else if(typeof val==="undefined"){if(this.settings.removeVoidProperties===true){delete value[key];return}else{throw new Error(`Property "${key}" has invalid value. Cannot store undefined values. Set removeVoidProperties option to true to automatically remove undefined properties`)}}if(this.valueFitsInline(val)){mainNode.value[key]=val}else{childNodeValues[key]=val}})}if(currentRow){this.debug.log(`Node "/${path}" is being ${options.merge?"updated":"overwritten"}`.cyan);if(currentIsObjectOrArray||newIsObjectOrArray){const pathInfo=PathInfo.get(path);const keys=[];let checkExecuted=false;const includeChildCheck=childPath=>{checkExecuted=true;if(!pathInfo.isParentOf(childPath)){throw new Error(`"${childPath}" is not a child of "${path}" - childrenOf must only check and return paths that are children`)}return true};const addChildPath=childPath=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} childrenOf did not call checkCallback before addCallback`)}const key=PathInfo.get(childPath).key;keys.push(key);return true};await transaction.childrenOf(path,{metadata:false,value:false},includeChildCheck,addChildPath);children.current=children.current.concat(keys);if(newIsObjectOrArray){if(options&&options.merge){children.new=children.current.slice()}Object.keys(value).forEach(key=>{if(!children.new.includes(key)){children.new.push(key)}})}const changes={insert:children.new.filter(key=>!children.current.includes(key)),update:children.new.filter(key=>children.current.includes(key)),delete:options&&options.merge?Object.keys(value).filter(key=>value[key]===null):children.current.filter(key=>!children.new.includes(key))};const writePromises=Object.keys(childNodeValues).map(key=>{const childPath=pathInfo.childPath(key);const childValue=childNodeValues[key];return this._writeNode(childPath,childValue,{transaction:transaction,revision:revision,merge:false})});const movingNodes=keys.filter(key=>key in mainNode.value);const deleteDedicatedKeys=changes.delete.concat(movingNodes);const deletePromises=deleteDedicatedKeys.map(key=>{const childPath=pathInfo.childPath(key);return this._deleteNode(childPath,{transaction:transaction})});const promises=writePromises.concat(deletePromises);await Promise.all(promises)}return await this._storeNode(path,{type:mainNode.type,value:mainNode.value,revision:currentRow.revision,revision_nr:currentRow.revision_nr+1,created:currentRow.created,modified:Date.now()},{transaction:transaction})}else{this.debug.log(`Node "/${path}" is being created`.cyan);const promises=Object.keys(childNodeValues).map(key=>{const childPath=PathInfo.getChildPath(path,key);const childValue=childNodeValues[key];return this._writeNode(childPath,childValue,{transaction:transaction,revision:revision,merge:false})});const p=this._storeNode(path,{type:mainNode.type,value:mainNode.value,revision:revision,revision_nr:1,created:Date.now(),modified:Date.now()},{transaction:transaction});promises.push(p);return Promise.all(promises)}}async _deleteNode(path,options){const pathInfo=PathInfo.get(path);this.debug.log(`Node "/${path}" is being deleted`.cyan);const deletePaths=[path];let checkExecuted=false;const includeDescendantCheck=descPath=>{checkExecuted=true;if(!pathInfo.isAncestorOf(descPath)){throw new Error(`"${descPath}" is not a descendant of "${path}" - descendantsOf must only check and return paths that are descendants`)}return true};const addDescendant=descPath=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} descendantsOf did not call checkCallback before addCallback`)}deletePaths.push(descPath);return true};const transaction=options.transaction;await transaction.descendantsOf(path,{metadata:false,value:false},includeDescendantCheck,addDescendant);this.debug.log(`Nodes ${deletePaths.map(p=>`"/${p}"`).join(",")} are being deleted`.cyan);return transaction.removeMultiple(deletePaths)}getChildren(path,options){options=options||{};var callback;const generator={next(valueCallback){callback=valueCallback;return start()}};const start=async()=>{const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:false});try{let canceled=false;await(async()=>{let node=await this._readNode(path,{transaction:transaction});if(!node){throw new NodeNotFoundError(`Node "/${path}" does not exist`)}if(![VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(node.type)){return}const isArray=node.type===VALUE_TYPES.ARRAY;const value=node.value;let keys=Object.keys(value);if(options.keyFilter){keys=keys.filter(key=>options.keyFilter.includes(key))}const pathInfo=PathInfo.get(path);keys.length>0&&keys.every(key=>{let child=this._getTypeFromStoredValue(value[key]);const info=new CustomStorageNodeInfo({path:pathInfo.childPath(key),key:isArray?null:key,index:isArray?key:null,type:child.type,address:null,exists:true,value:child.value,revision:node.revision,revision_nr:node.revision_nr,created:node.created,modified:node.modified});canceled=callback(info)===false;return!canceled});if(canceled){return}let checkExecuted=false;const includeChildCheck=childPath=>{checkExecuted=true;if(!pathInfo.isParentOf(childPath)){throw new Error(`"${childPath}" is not a child of "${path}" - childrenOf must only check and return paths that are children`)}if(options.keyFilter){const key=PathInfo.get(childPath).key;return options.keyFilter.includes(key)}return true};const addChildNode=(childPath,node)=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} childrenOf did not call checkCallback before addCallback`)}const key=PathInfo.get(childPath).key;const info=new CustomStorageNodeInfo({path:childPath,type:node.type,key:isArray?null:key,index:isArray?key:null,address:new CustomStorageNodeAddress(childPath),exists:true,value:null,revision:node.revision,revision_nr:node.revision_nr,created:new Date(node.created),modified:new Date(node.modified)});canceled=callback(info)===false;return!canceled};await transaction.childrenOf(path,{metadata:true,value:false},includeChildCheck,addChildNode)})();if(!options.transaction){await transaction.commit()}return canceled}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}};return generator}async getNode(path,options){options=options||{};const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:false});try{const node=await(async()=>{const filtered=options.include||options.exclude||options.child_objects===false;const pathInfo=PathInfo.get(path);const targetNode=await this._readNode(path,{transaction:transaction});if(!targetNode){if(path===""){return{value:null}}const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);let parentNode=await this._readNode(pathInfo.parentPath,{transaction:transaction});if(parentNode&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(parentNode.type)&&pathInfo.key in parentNode){const childValueInfo=this._getTypeFromStoredValue(parentNode.value[pathInfo.key]);return{revision:parentNode.revision,revision_nr:parentNode.revision_nr,created:parentNode.created,modified:parentNode.modified,type:childValueInfo.type,value:childValueInfo.value}}return{value:null}}const includeCheck=options.include?new RegExp("^"+options.include.map(p=>"(?:"+p.replace(/\*/g,"[^/\\[]+")+")").join("|")+"(?:$|[/\\[])"):null;const excludeCheck=options.exclude?new RegExp("^"+options.exclude.map(p=>"(?:"+p.replace(/\*/g,"[^/\\[]+")+")").join("|")+"(?:$|[/\\[])"):null;let checkExecuted=false;const includeDescendantCheck=descPath=>{checkExecuted=true;if(!pathInfo.isAncestorOf(descPath)){throw new Error(`"${descPath}" is not a descendant of "${path}" - descendantsOf must only check and return paths that are descendants`)}if(!filtered){return true}let checkPath=descPath.slice(path.length);if(checkPath[0]==="/"){checkPath=checkPath.slice(1)}let include=(includeCheck?includeCheck.test(checkPath):true)&&(excludeCheck?!excludeCheck.test(checkPath):true);if(include&&options.child_objects===false&&(pathInfo.isParentOf(descPath)&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(childNode.type)||PathInfo.getPathKeys(descPath).length>pathInfo.pathKeys.length+1)){include=false}return include};const descRows=[];const addDescendant=(descPath,node)=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} descendantsOf did not call checkCallback before addCallback`)}this._processReadNodeValue(node);node.path=descPath;descRows.push(node);return true};await transaction.descendantsOf(path,{metadata:true,value:true},includeDescendantCheck,addDescendant);this.debug.log(`Read node "/${path}" and ${filtered?"(filtered) ":""}descendants from ${descRows.length+1} records`.magenta);const result=targetNode;const objectToArray=obj=>{const arr=[];Object.keys(obj).forEach(key=>{let index=parseInt(key);arr[index]=obj[index]});return arr};if(targetNode.type===VALUE_TYPES.ARRAY){result.value=objectToArray(result.value)}if(targetNode.type===VALUE_TYPES.OBJECT||targetNode.type===VALUE_TYPES.ARRAY){const targetPathKeys=PathInfo.getPathKeys(path);let value=targetNode.value;for(let i=0;i{console.assert(!(childKey in parent[key]),"child key is in parent value already?! HOW?!");parent[key][childKey]=nodeValue[childKey]})}else{parent[key]=nodeValue}parent=parent[key]}}}else if(descRows.length>0){throw new Error(`multiple records found for non-object value!`)}if(options.child_objects===false){Object.keys(result.value).forEach(key=>{if(typeof result.value[key]==="object"&&result.value[key].constructor===Object){console.assert(Object.keys(result.value[key]).length===0);delete result.value[key]}})}if(options.exclude){const process=(obj,keys)=>{if(typeof obj!=="object"){return}const key=keys[0];if(key==="*"){Object.keys(obj).forEach(k=>{process(obj[k],keys.slice(1))})}else if(keys.length>1){key in obj&&process(obj[key],keys.slice(1))}else{delete obj[key]}};options.exclude.forEach(path=>{const checkKeys=PathInfo.getPathKeys(path);process(result.value,checkKeys)})}return result})();if(!options.transaction){await transaction.commit()}return node}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async getNodeInfo(path,options){options=options||{};const pathInfo=PathInfo.get(path);const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:true});try{const node=await this._readNode(path,{transaction:transaction});const info=new CustomStorageNodeInfo({path:path,key:typeof pathInfo.key==="string"?pathInfo.key:null,index:typeof pathInfo.key==="number"?pathInfo.key:null,type:node?node.type:0,exists:node!==null,address:node?new CustomStorageNodeAddress(path):null,created:node?new Date(node.created):null,modified:node?new Date(node.modified):null,revision:node?node.revision:null,revision_nr:node?node.revision_nr:null});if(!node&&path!==""){const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);const parent=await this._readNode(pathInfo.parentPath,{transaction:transaction});if(parent&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(parent.type)&&pathInfo.key in parent.value){info.exists=true;info.value=parent.value[pathInfo.key];info.address=null;info.type=parent.type;info.created=new Date(parent.created);info.modified=new Date(parent.modified);info.revision=parent.revision;info.revision_nr=parent.revision_nr}else{info.address=null}}if(!options.transaction){await transaction.commit()}return info}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async removeNode(path,options){if(path===""){return Promise.reject(new Error(`Cannot remove the root node`))}options=options||{};const pathInfo=PathInfo.get(path);const transaction=options.transaction||this._customImplementation.getTransaction({path:path,write:true});try{await this.updateNode(pathInfo.parentPath,{[pathInfo.key]:null},{transaction:transaction});if(!options.transaction){await transaction.commit()}}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async setNode(path,value,options){const pathInfo=PathInfo.get(path);options=options||{};const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:true});try{if(path===""){if(value===null||typeof value!=="object"||value instanceof Array||value instanceof ArrayBuffer||"buffer"in value&&value.buffer instanceof ArrayBuffer){throw new Error(`Invalid value for root node: ${value}`)}await this._writeNodeWithTracking("",value,{merge:false,transaction:transaction})}else if(typeof options.assert_revision!=="undefined"){const info=await this.getNodeInfo(path,{transaction:transaction});if(info.revision!==options.assert_revision){throw new NodeRevisionError(`revision '${info.revision}' does not match requested revision '${options.assert_revision}'`)}if(info.address&&info.address.path===path&&!this.valueFitsInline(value)){await this._writeNodeWithTracking(path,value,{merge:false,transaction:transaction})}else{const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this._writeNodeWithTracking(pathInfo.parentPath,{[pathInfo.key]:value},{merge:true,transaction:transaction})}}else{const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this.updateNode(pathInfo.parentPath,{[pathInfo.key]:value},{transaction:transaction})}if(!options.transaction){await transaction.commit()}}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async updateNode(path,updates,options){if(typeof updates!=="object"){throw new Error(`invalid updates argument`)}options=options||{};const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:true});try{const nodeInfo=await this.getNodeInfo(path,{transaction:transaction});const pathInfo=PathInfo.get(path);if(nodeInfo.exists&&nodeInfo.address&&nodeInfo.address.path===path){await this._writeNodeWithTracking(path,updates,{transaction:transaction,merge:true})}else if(nodeInfo.exists){const pathInfo=PathInfo.get(path);const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this._writeNodeWithTracking(pathInfo.parentPath,{[pathInfo.key]:updates},{transaction:transaction,merge:true})}else{const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this.updateNode(pathInfo.parentPath,{[pathInfo.key]:updates},{transaction:transaction})}if(!options.transaction){await transaction.commit()}}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}}module.exports={CustomStorageNodeAddress:CustomStorageNodeAddress,CustomStorageNodeInfo:CustomStorageNodeInfo,CustomStorage:CustomStorage,CustomStorageSettings:CustomStorageSettings,CustomStorageHelpers:CustomStorageHelpers,CustomStorageTransaction:CustomStorageTransaction,ICustomStorageNodeMetaData:ICustomStorageNodeMetaData,ICustomStorageNode:ICustomStorageNode}},{"./node-info":34,"./node-lock":35,"./node-value-types":36,"./storage":40,"acebase-core":11}],39:[function(require,module,exports){const{debug:debug,ID:ID,PathReference:PathReference,PathInfo:PathInfo,ascii85:ascii85}=require("acebase-core");const{NodeInfo:NodeInfo}=require("./node-info");const{VALUE_TYPES:VALUE_TYPES}=require("./node-value-types");const{Storage:Storage,StorageSettings:StorageSettings,NodeNotFoundError:NodeNotFoundError}=require("./storage");class LocalStorageSettings extends StorageSettings{constructor(settings){super(settings);settings=settings||{};this.session=settings.session===true;this.provider=typeof settings.provider==="object"?settings.provider:null}}class LocalStorageNodeAddress{constructor(containerPath){this.path=containerPath}}class LocalStorageNodeInfo extends NodeInfo{constructor(info){super(info);this.address;this.revision=info.revision;this.revision_nr=info.revision_nr;this.created=info.created;this.modified=info.modified}}class LocalStorage extends Storage{constructor(dbname,settings){super(dbname,settings);this._init()}_init(){if(this.settings.provider!==null&&typeof this.settings.provider==="object"){this._localStorage=this.settings.provider}else{if(!this.settings.session&&typeof localStorage==="undefined"){throw new Error(`No localStorage available. If you are on Node: npm i node-localstorage`)}if(this.settings.session===true&&typeof sessionStorage==="undefined"){throw new Error(`No sessionStorage available`)}this._localStorage=this.settings.session===true?sessionStorage:localStorage}this.debug.log(`Database "${this.name}" details:`.intro);this.debug.log(`- Type: LocalStorage`);this.debug.log(`- Max inline value size: ${this.settings.maxInlineValueSize}`.intro);return this.getNodeInfo("").then(info=>{if(!info.exists){return this._writeNode("",{})}}).then(()=>{return this.indexes.supported&&this.indexes.load()}).then(()=>{this.emit("ready")})}get _keyPrefix(){return`${this.name}.acebase::`}_getPathFromKey(key){return key.slice(this._keyPrefix.length)}_getKeyFromPath(path){return`${this._keyPrefix}${path}`}_storeNode(path,info){const getTypedChildValue=val=>{if(val===null){throw new Error(`Not allowed to store null values. remove the property`)}else if(["string","number","boolean"].includes(typeof val)){return val}else if(val instanceof Date){return{type:VALUE_TYPES.DATETIME,value:val.getTime()}}else if(val instanceof PathReference){return{type:VALUE_TYPES.REFERENCE,value:child.path}}else if(val instanceof ArrayBuffer){return{type:VALUE_TYPES.BINARY,value:ascii85.encode(val)}}else if(typeof val==="object"){console.assert(Object.keys(val).length===0,"child object stored in parent can only be empty");return val}};const unprocessed=`Caller should have pre-processed the value by converting it to a string`;if(info.type===VALUE_TYPES.ARRAY&&info.value instanceof Array){console.warn(`Unprocessed array. ${unprocessed}`);const obj={};for(let i=0;i{info.value[key]=getTypedChildValue(original[key])})}const json=JSON.stringify(info);this._localStorage.setItem(this._getKeyFromPath(path),json)}_readNode(path){let val=this._localStorage.getItem(this._getKeyFromPath(path));if(val===null){return null}val=JSON.parse(val);const getTypedChildValue=val=>{if(val.type===VALUE_TYPES.BINARY){return ascii85.decode(val.value)}else if(val.type===VALUE_TYPES.DATETIME){return new Date(val.value)}else if(val.type===VALUE_TYPES.REFERENCE){return new PathReference(val.value)}else{throw new Error(`Unhandled child value type ${val.type}`)}};const node={type:val.type,value:val.value,revision:val.revision,revision_nr:val.revision_nr,created:val.created,modified:val.modified};switch(val.type){case VALUE_TYPES.ARRAY:case VALUE_TYPES.OBJECT:{const obj=val.value;Object.keys(obj).forEach(key=>{let item=obj[key];if(typeof item==="object"&&"type"in item){obj[key]=getTypedChildValue(item)}});node.value=obj;break}case VALUE_TYPES.BINARY:{node.value=ascii85.decode(val.value);break}case VALUE_TYPES.STRING:{node.value=val.value;break}case VALUE_TYPES.REFERENCE:{node.value=new PathReference(val.value);break}default:throw new Error(`Invalid standalone record value type`)}return node}_getTypeFromStoredValue(val){let type;if(typeof val==="string"){type=VALUE_TYPES.STRING}else if(typeof val==="number"){type=VALUE_TYPES.NUMBER}else if(typeof val==="boolean"){type=VALUE_TYPES.BOOLEAN}else if(val instanceof Array){type=VALUE_TYPES.ARRAY}else if(typeof val==="object"){if("type"in val){type=val.type;val=val.value;if(type===VALUE_TYPES.DATETIME){val=new Date(val)}else if(type===VALUE_TYPES.REFERENCE){val=new PathReference(val)}}else{type=VALUE_TYPES.OBJECT}}else{throw new Error(`Unknown value type`)}return{type:type,value:val}}_writeNode(path,value,options={merge:false,revision:null}){if(this.valueFitsInline(value)&&path!==""){throw new Error(`invalid value to store in its own node`)}else if(path===""&&(typeof value!=="object"||value instanceof Array)){throw new Error(`Invalid root node value. Must be an object`)}const currentRow=this._readNode(path);const newRevision=options&&options.revision||ID.generate();let mainNode={type:VALUE_TYPES.OBJECT,value:{}};const childNodeValues={};if(value instanceof Array){mainNode.type=VALUE_TYPES.ARRAY;const obj={};for(let i=0;i{const val=value[key];delete mainNode.value[key];if(val===null){return}else if(typeof val==="undefined"){if(this.settings.removeVoidProperties===true){delete value[key];return}else{throw new Error(`Property "${key}" has invalid value. Cannot store undefined values. Set removeVoidProperties option to true to automatically remove undefined properties`)}}if(this.valueFitsInline(val)){mainNode.value[key]=val}else{childNodeValues[key]=val}})}if(currentRow){this.debug.log(`Node "/${path}" is being ${options.merge?"updated":"overwritten"}`.cyan);if(currentIsObjectOrArray||newIsObjectOrArray){const pathInfo=PathInfo.get(path);const keys=[];for(let i=0;i{if(!children.new.includes(key)){children.new.push(key)}})}const changes={insert:children.new.filter(key=>!children.current.includes(key)),update:children.new.filter(key=>children.current.includes(key)),delete:options&&options.merge?Object.keys(value).filter(key=>value[key]===null):children.current.filter(key=>!children.new.includes(key))};Object.keys(childNodeValues).map(key=>{const childPath=PathInfo.getChildPath(path,key);const childValue=childNodeValues[key];this._writeNode(childPath,childValue,{revision:newRevision,merge:false})});const movingNodes=keys.filter(key=>key in mainNode.value);const deleteDedicatedKeys=changes.delete.concat(movingNodes);deleteDedicatedKeys.forEach(key=>{const childPath=PathInfo.getChildPath(path,key);this._deleteNode(childPath)})}this._storeNode(path,{type:mainNode.type,value:mainNode.value,revision:currentRow.revision,revision_nr:currentRow.revision_nr+1,created:currentRow.created,modified:Date.now()})}else{this.debug.log(`Node "/${path}" is being created`.cyan);Object.keys(childNodeValues).map(key=>{const childPath=PathInfo.getChildPath(path,key);const childValue=childNodeValues[key];this._writeNode(childPath,childValue,{revision:newRevision,merge:false})});this._storeNode(path,{type:mainNode.type,value:mainNode.value,revision:newRevision,revision_nr:1,created:Date.now(),modified:Date.now()})}}_deleteNode(path){const pathInfo=PathInfo.get(path);this.debug.log(`Node "/${path}" is being deleted`.cyan);this._localStorage.removeItem(this._getKeyFromPath(path));for(let i=0;i{let lock,canceled=false;const tid=options&&options.tid||ID.generate();return this.nodeLocker.lock(path,tid,false,"getChildren").then(l=>{lock=l;let row=this._localStorage.getItem(this._getKeyFromPath(path));if(!row){throw new NodeNotFoundError(`Node "/${path}" does not exist`)}row=JSON.parse(row);if(![VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(row.type)){return}const isArray=row.type===VALUE_TYPES.ARRAY;const value=row.value;let keys=Object.keys(value);if(options.keyFilter){keys=keys.filter(key=>options.keyFilter.includes(key))}const pathInfo=PathInfo.get(path);keys.length>0&&keys.every(key=>{let child=this._getTypeFromStoredValue(value[key]);const info=new LocalStorageNodeInfo({path:pathInfo.childPath(key),key:isArray?null:key,index:isArray?key:null,type:child.type,address:null,exists:true,value:child.value,revision:row.revision,revision_nr:row.revision_nr,created:row.created,modified:row.modified});canceled=callback(info)===false;return!canceled});if(canceled){return}const childRows=[];for(let i=0;i{const row=childRows[i];if(!row){return}const key=PathInfo.get(row.path).key;if(options.keyFilter&&!options.keyFilter.includes(key)){return handleNextChild(i+1)}const info=new LocalStorageNodeInfo({path:row.path,type:row.type,key:isArray?null:key,index:isArray?key:null,address:new LocalStorageNodeAddress(row.path),exists:true,value:null,revision:row.revision,revision_nr:row.revision_nr,created:new Date(row.created),modified:new Date(row.modified)});canceled=callback(info)===false;if(!canceled){return handleNextChild(i+1)}};return handleNextChild(0)}).then(()=>{lock.release();return canceled}).catch(err=>{lock.release();throw err})};return generator}getNode(path,options={include:undefined,exclude:undefined,child_objects:true,tid:undefined}){const tid=options&&options.tid||ID.generate();let lock;return this.nodeLocker.lock(path,tid,false,"getNode").then(l=>{lock=l;const filtered=options&&(options.include||options.exclude||options.child_objects===false);const pathInfo=PathInfo.get(path);const targetRow=this._readNode(path);if(!targetRow){if(path===""){return{value:null}}return lock.moveToParent().then(parentLock=>{lock=parentLock;let parentNode=this._readNode(pathInfo.parentPath);if(parentNode&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(parentNode.type)&&pathInfo.key in parentNode){return{revision:parentNode.revision,value:parentNode.value[pathInfo.key]}}return{value:null}})}const includeCheck=options.include?new RegExp("^"+options.include.map(p=>"(?:"+p.replace(/\*/g,"[^/\\[]+")+")").join("|")+"(?:$|[/\\[])"):null;const excludeCheck=options.exclude?new RegExp("^"+options.exclude.map(p=>"(?:"+p.replace(/\*/g,"[^/\\[]+")+")").join("|")+"(?:$|[/\\[])"):null;const childRows=[];for(let i=0;ipathInfo.pathKeys.length+1)){include=false}if(include){const childRow=this._readNode(otherPath);childRow.path=otherPath;childRows.push(childRow)}}this.debug.log(`Read node "/${path}" and ${filtered?"(filtered) ":""}children from ${childRows.length+1} records`.magenta);const result={revision:targetRow?targetRow.revision:null,value:targetRow.value};const objectToArray=obj=>{const arr=[];Object.keys(obj).forEach(key=>{let index=parseInt(key);arr[index]=obj[index]});return arr};if(targetRow.type===VALUE_TYPES.ARRAY){result.value=objectToArray(result.value)}if(targetRow.type===VALUE_TYPES.OBJECT||targetRow.type===VALUE_TYPES.ARRAY){const targetPathKeys=PathInfo.getPathKeys(path);let value=targetRow.value;for(let i=0;i{console.assert(!(childKey in parent[key]),"child key is in parent value already?! HOW?!");parent[key][childKey]=nodeValue[childKey]})}else{parent[key]=nodeValue}parent=parent[key]}}}else if(childRows.length>0){throw new Error(`multiple records found for non-object value!`)}if(options.child_objects===false){Object.keys(result.value).forEach(key=>{if(typeof result.value[key]==="object"&&result.value[key].constructor===Object){console.assert(Object.keys(result.value[key]).length===0);delete result.value[key]}})}if(options.exclude){const process=(obj,keys)=>{if(typeof obj!=="object"){return}const key=keys[0];if(key==="*"){Object.keys(obj).forEach(k=>{process(obj[k],keys.slice(1))})}else if(keys.length>1){key in obj&&process(obj[key],keys.slice(1))}else{delete obj[key]}};options.exclude.forEach(path=>{const checkKeys=PathInfo.getPathKeys(path);process(result.value,checkKeys)})}return result}).then(result=>{lock.release();return result}).catch(err=>{lock.release();throw err})}getNodeInfo(path,options={tid:undefined}){const pathInfo=PathInfo.get(path);const tid=options&&options.tid||ID.generate();let lock;return this.nodeLocker.lock(path,tid,false,"getNodeInfo").then(l=>{lock=l;const node=this._readNode(path);const info=new LocalStorageNodeInfo({path:path,key:typeof pathInfo.key==="string"?pathInfo.key:null,index:typeof pathInfo.key==="number"?pathInfo.key:null,type:node?node.type:0,exists:node!==null,address:node?new LocalStorageNodeAddress(path):null,created:node?new Date(node.created):null,modified:node?new Date(node.modified):null,revision:node?node.revision:null,revision_nr:node?node.revision_nr:null});if(node||path===""){return info}return lock.moveToParent().then(parentLock=>{lock=parentLock;const parent=this._readNode(pathInfo.parentPath);if(parent&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(parent.type)&&pathInfo.key in parent.value){info.exists=true;info.value=parent.value[pathInfo.key];info.address=null;info.type=parent.type;info.created=new Date(parent.created);info.modified=new Date(parent.modified);info.revision=parent.revision;info.revision_nr=parent.revision_nr}else{info.address=null}return info})}).then(info=>{lock.release();return info}).catch(err=>{lock&&lock.release();throw err})}removeNode(path,options={tid:undefined}){if(path===""){return Promise.reject(new Error(`Cannot remove the root node`))}const pathInfo=PathInfo.get(path);const tid=options&&options.tid||ID.generate();return this.nodeLocker.lock(pathInfo.parentPath,tid,true,"removeNode").then(lock=>{return this.updateNode(pathInfo.parentPath,{[pathInfo.key]:null},{tid:tid}).then(result=>{lock.release();return result}).catch(err=>{lock.release();throw err})})}setNode(path,value,options={assert_revision:undefined,tid:undefined}){const pathInfo=PathInfo.get(path);let lock;const tid=options&&options.tid||ID.generate();return this.nodeLocker.lock(path,tid,true,"setNode").then(l=>{lock=l;if(path===""){if(value===null||typeof value!=="object"||value instanceof Array||value instanceof ArrayBuffer||"buffer"in value&&value.buffer instanceof ArrayBuffer){return Promise.reject(new Error(`Invalid value for root node: ${value}`))}return this._writeNodeWithTracking("",value,{merge:false,tid:tid})}if(options&&typeof options.assert_revision!=="undefined"){return this.getNodeInfo(path,{tid:lock.tid}).then(info=>{if(info.revision!==options.assert_revision){throw new NodeRevisionError(`revision '${info.revision}' does not match requested revision '${options.assert_revision}'`)}if(info.address&&info.address.path===path&&!this.valueFitsInline(value)){return this._writeNodeWithTracking(path,value,{merge:false,tid:tid})}else{return lock.moveToParent().then(parentLock=>{lock=parentLock;return this._writeNodeWithTracking(pathInfo.parentPath,{[pathInfo.key]:value},{merge:true,tid:tid})})}})}else{return lock.moveToParent().then(parentLock=>{lock=parentLock;return this.updateNode(pathInfo.parentPath,{[pathInfo.key]:value},{tid:tid})})}}).then(result=>{lock.release();return result}).catch(err=>{lock.release();throw err})}updateNode(path,updates,options={tid:undefined}){if(typeof updates!=="object"){return Promise.reject(new Error(`invalid updates argument`))}const tid=options&&options.tid||ID.generate();let lock;return this.nodeLocker.lock(path,tid,true,"updateNode").then(l=>{lock=l;return this.getNodeInfo(path,{tid:lock.tid})}).then(nodeInfo=>{const pathInfo=PathInfo.get(path);if(nodeInfo.exists&&nodeInfo.address&&nodeInfo.address.path===path){return this._writeNodeWithTracking(path,updates,{merge:true,tid:tid})}else if(nodeInfo.exists){const pathInfo=PathInfo.get(path);return lock.moveToParent().then(parentLock=>{lock=parentLock;return this._writeNodeWithTracking(pathInfo.parentPath,{[pathInfo.key]:value},{merge:true,tid:tid})})}else{return lock.moveToParent().then(parentLock=>{lock=parentLock;return this.updateNode(pathInfo.parentPath,{[pathInfo.key]:updates},{tid:tid})})}}).then(result=>{lock.release();return result}).catch(err=>{lock.release();throw err})}}module.exports={LocalStorageNodeAddress:LocalStorageNodeAddress,LocalStorageNodeInfo:LocalStorageNodeInfo,LocalStorage:LocalStorage,LocalStorageSettings:LocalStorageSettings}},{"./node-info":34,"./node-value-types":36,"./storage":40,"acebase-core":11}],40:[function(require,module,exports){(function(process){const{Utils:Utils,DebugLogger:DebugLogger,PathInfo:PathInfo,ID:ID,PathReference:PathReference,ascii85:ascii85}=require("acebase-core");const{NodeLocker:NodeLocker}=require("./node-lock");const{VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}=require("./node-value-types");const{NodeInfo:NodeInfo}=require("./node-info");const{EventEmitter:EventEmitter}=require("events");const{cloneObject:cloneObject,compareValues:compareValues,getChildValues:getChildValues,encodeString:encodeString}=Utils;const colors=require("colors");class NodeNotFoundError extends Error{}class NodeRevisionError extends Error{}class ClusterSettings{constructor(settings){settings=settings||{};this.enabled=settings.enabled===true;this.isMaster=settings.isMaster===true;this.master=this.isMaster?null:settings.master;this.workers=this.isMaster?settings.workers:null}}class ClusterManager extends EventEmitter{constructor(settings){super();this.settings=new ClusterSettings(settings);if(!settings.enabled){}else if(settings.isMaster){settings.workers.forEach(worker=>{worker.on("message",data=>{const{id:id,request:request}=data;if(typeof request==="object"&&request.type==="ping"){worker.send({id:id,result:"pong"})}else{const reply=result=>{worker.send({id:id,result:result})};const broadcast=msg=>{console.assert(!("id"in msg),"message to broadcast cannot have id property, it will confuse workers because they think it is a reply to their request");settings.workers.forEach(otherWorker=>{if(otherWorker!==worker){otherWorker.send(msg)}})};this.emit("worker_request",{request:request,reply:reply,broadcast:broadcast})}})});this.request=msg=>{throw new Error(`request can only be called by worker processes!`)}}else{const master=settings.master;const requests={};this.request=msg=>{return new Promise((resolve,reject)=>{const id=ID.generate();requests[id]=resolve;master.send({id:id,request:msg})})};master.on("message",data=>{if(typeof data.id!=="undefined"){let resolve=requests[data.id];delete requests[data.id];resolve(data.result)}else{this.emit("master_notification",data)}});this.request({type:"ping"}).then(result=>{console.log(`PING master process result: ${result}`)})}}get isMaster(){return this.settings.isMaster}get enabled(){return this.settings.enabled}}class StorageSettings{constructor(settings){settings=settings||{};this.maxInlineValueSize=typeof settings.maxInlineValueSize==="number"?settings.maxInlineValueSize:50;this.removeVoidProperties=settings.removeVoidProperties===true;this.cluster=new ClusterSettings(settings.cluster);this.path=settings.path||".";if(this.path.endsWith("/")){this.path=this.path.slice(0,-1)}this.logLevel=settings.logLevel||"log";this.info=settings.info||"realtime database"}}class Storage extends EventEmitter{constructor(name,settings){super();this.name=name;this.settings=settings;this.debug=new DebugLogger(settings.logLevel,`[${name}]`);colors.setTheme({art:["magenta","bold"],intro:["dim"]});const logo=" ___ ______ ".art+"\n"+" / _ \\ | ___ \\ ".art+"\n"+" / /_\\ \\ ___ ___| |_/ / __ _ ___ ___ ".art+"\n"+" | _ |/ __/ _ \\ ___ \\/ _` / __|/ _ \\".art+"\n"+" | | | | (_| __/ |_/ / (_| \\__ \\ __/".art+"\n"+" \\_| |_/\\___\\___\\____/ \\__,_|___/\\___|".art+"\n"+(settings.info?"".padStart(40-settings.info.length," ")+settings.info.magenta+"\n":"");this.debug.write(logo);this.nodeCache={find(path){return null},update(path,info){}};this.nodeLocker=new NodeLocker;this.cluster=new ClusterManager(settings.cluster);const{DataIndex:DataIndex,ArrayIndex:ArrayIndex,FullTextIndex:FullTextIndex,GeoIndex:GeoIndex}=require("./data-index");const _indexes=[];const storage=this;this.indexes={get supported(){const pfs=require("./promise-fs");return pfs&&pfs.hasFileSystem},create(path,key,options={rebuild:false,type:undefined,include:undefined}){path=path.replace(/\/\*$/,"");const rebuild=options&&options.rebuild===true;const indexType=options&&options.type||"normal";let includeKeys=options&&options.include||[];if(typeof includeKeys==="string"){includeKeys=[includeKeys]}const existingIndex=_indexes.find(index=>index.path===path&&index.key===key&&index.type===indexType&&index.includeKeys.length===includeKeys.length&&index.includeKeys.every((key,index)=>includeKeys[index]===key));if(existingIndex&&rebuild!==true){storage.debug.log(`Index on "/${path}/*/${key}" already exists`.inverse);return Promise.resolve(existingIndex)}const index=existingIndex||(()=>{switch(indexType){case"array":return new ArrayIndex(storage,path,key,{include:options.include,config:options.config});case"fulltext":return new FullTextIndex(storage,path,key,{include:options.include,config:options.config});case"geo":return new GeoIndex(storage,path,key,{include:options.include,config:options.config});default:return new DataIndex(storage,path,key,{include:options.include,config:options.config})}})();if(!existingIndex){_indexes.push(index)}return index.build().then(()=>{return index}).catch(err=>{storage.debug.error(`Index build on "/${path}/*/${key}" failed: ${err.message} (code: ${err.code})`.red);if(!existingIndex){_indexes.splice(_indexes.indexOf(index),1)}throw err})},get(path,key=null){return _indexes.filter(index=>index.path===path&&(key===null||key===index.key))},getAll(targetPath,options={parentPaths:true,childPaths:true}){const pathKeys=PathInfo.getPathKeys(targetPath);return _indexes.filter(index=>{const indexKeys=PathInfo.getPathKeys(index.path+"/*");if(options.parentPaths&&indexKeys.every((key,i)=>{return key==="*"||pathKeys[i]===key})&&[index.key].concat(...index.includeKeys).includes(pathKeys[indexKeys.length])){return true}else if(indexKeys.length{return[key,"*"].includes(indexKeys[i])})})},list(){return _indexes.slice()},load(){_indexes.splice(0);const pfs=require("./promise-fs");if(!pfs||!pfs.readdir){return Promise.resolve()}return pfs.readdir(`${storage.settings.path}/${storage.name}.acebase`).then(files=>{const promises=[];files.forEach(fileName=>{if(fileName.endsWith(".idx")){const p=DataIndex.readFromFile(storage,fileName).then(index=>{_indexes.push(index)}).catch(err=>{storage.debug.error(err)});promises.push(p)}});return Promise.all(promises)}).catch(err=>{if(err.code!=="ENOENT"){storage.debug.error(err)}})}};const _subs={};const _supportedEvents=["value","child_added","child_changed","child_removed"];_supportedEvents.push(..._supportedEvents.map(event=>`notify_${event}`));this.subscriptions={add(path,type,callback){if(_supportedEvents.indexOf(type)<0){throw new TypeError(`Invalid event type "${type}"`)}let pathSubs=_subs[path];if(!pathSubs){pathSubs=_subs[path]=[]}pathSubs.push({created:Date.now(),type:type,callback:callback})},remove(path,type=undefined,callback=undefined){let pathSubs=_subs[path];if(!pathSubs){return}while(true){const i=pathSubs.findIndex(ps=>(type?ps.type===type:true)&&(callback?ps.callback===callback:true));if(i<0){break}pathSubs.splice(i,1)}},hasValueSubscribersForPath(path){const valueNeeded=this.getValueSubscribersForPath(path);return!!valueNeeded},getValueSubscribersForPath(path){const pathInfo=new PathInfo(path);const valueSubscribers=[];Object.keys(_subs).forEach(subscriptionPath=>{if(pathInfo.equals(subscriptionPath)||pathInfo.isDescendantOf(subscriptionPath)){let pathSubs=_subs[subscriptionPath];const eventPath=PathInfo.fillVariables(subscriptionPath,path);pathSubs.forEach(sub=>{let dataPath=null;if(sub.type==="value"||sub.type==="notify_value"){dataPath=eventPath}else if((sub.type==="child_changed"||sub.type==="notify_child_changed")&&path!==eventPath){let childKey=PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}else if(["child_added","child_removed","notify_child_added","notify_child_removed"].includes(sub.type)&&pathInfo.isChildOf(eventPath)){let childKey=PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}if(dataPath!==null&&valueSubscribers.findIndex(s=>s.type===sub.type&&s.path===eventPath)<0){valueSubscribers.push({type:sub.type,eventPath:eventPath,dataPath:dataPath,subscriptionPath:subscriptionPath})}})}});return valueSubscribers},getAllSubscribersForPath(path){const pathInfo=PathInfo.get(path);const subscribers=[];Object.keys(_subs).forEach(subscriptionPath=>{if(pathInfo.equals(subscriptionPath)||pathInfo.isDescendantOf(subscriptionPath)||pathInfo.isAncestorOf(subscriptionPath)){let pathSubs=_subs[subscriptionPath];const eventPath=PathInfo.fillVariables(subscriptionPath,path);pathSubs.forEach(sub=>{let dataPath=null;if(sub.type==="value"||sub.type==="notify_value"){dataPath=eventPath}else if(sub.type==="child_changed"||sub.type==="notify_child_changed"){let childKey=path===eventPath||pathInfo.isAncestorOf(eventPath)?"*":PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}else if(["child_added","child_removed","notify_child_added","notify_child_removed"].includes(sub.type)&&(pathInfo.isChildOf(eventPath)||path===eventPath||pathInfo.isAncestorOf(eventPath))){let childKey=path===eventPath||pathInfo.isAncestorOf(eventPath)?"*":PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}if(dataPath!==null){subscribers.push({type:sub.type,eventPath:eventPath,dataPath:dataPath,subscriptionPath:subscriptionPath})}})}});return subscribers},trigger(event,path,dataPath,oldValue,newValue){const pathSubscriptions=_subs[path]||[];pathSubscriptions.filter(sub=>sub.type===event).forEach(sub=>{sub.callback(null,dataPath,newValue,oldValue)})}}}get path(){return`${this.settings.path}/${this.name}.acebase`}valueFitsInline(value){const encoding="utf8";if(typeof value==="number"||typeof value==="boolean"||value instanceof Date){return true}else if(typeof value==="string"){if(value.length>this.settings.maxInlineValueSize){return false}const encoded=encodeString(value);return encoded.lengththis.settings.maxInlineValueSize){return false}const encoded=encodeString(value.path);return encoded.length0){hasValueSubscribers=true;let eventPaths=valueSubscribers.map(sub=>{return{path:sub.dataPath,keys:PathInfo.getPathKeys(sub.dataPath)}}).sort((a,b)=>{if(a.keys.lengthb.keys.length)return 1;return 0});let first=eventPaths[0];topEventPath=first.path;if(valueSubscribers.filter(sub=>sub.dataPath===topEventPath).every(sub=>sub.type.startsWith("notify_"))){hasValueSubscribers=false}topEventPath=PathInfo.fillVariables(topEventPath,path)}const writeNode=()=>{if(typeof options._customWriteFunction==="function"){return options._customWriteFunction()}return this._writeNode(path,value,options)};const indexes=this.indexes.getAll(path,{childPaths:true,parentPaths:true}).map(index=>({index:index,keys:PathInfo.getPathKeys(index.path)})).sort((a,b)=>{if(a.keys.lengthb.keys.length){return 1}return 0}).map(obj=>obj.index);if(eventSubscriptions.length===0&&indexes.length===0){return writeNode()}let keysFilter=[];if(indexes.length>0){indexes.sort((a,b)=>{if(typeof a._pathKeys==="undefined"){a._pathKeys=PathInfo.getPathKeys(a.path)}if(typeof b._pathKeys==="undefined"){b._pathKeys=PathInfo.getPathKeys(b.path)}if(a._pathKeys.lengthb._pathKeys.length)return 1;return 0});const topIndex=indexes[0];let topIndexPath=topIndex.path===path?path:PathInfo.fillVariables(`${topIndex.path}/*`,path);if(topIndexPath.lengthindex.path===topIndex.path).forEach(index=>{let keys=[index.key].concat(index.includeKeys);keys.forEach(key=>!keysFilter.includes(key)&&keysFilter.push(key))})}}if(!hasValueSubscribers&&options.merge===true&&keysFilter.length===0){keysFilter=Object.keys(value);if(topEventPath!==path){let trailPath=path.slice(topEventPath.length);keysFilter=keysFilter.map(key=>`${trailPath}/${key}`)}}return this.getNodeInfo(topEventPath,{transaction:transaction,tid:tid}).then(eventNodeInfo=>{if(!eventNodeInfo.exists){return null}let valueOptions={transaction:transaction,tid:tid};if(keysFilter.length>0){valueOptions.include=keysFilter}if(topEventPath===""&&typeof valueOptions.include==="undefined"){this.debug.warn(`WARNING: One or more value event listeners on the root node are causing the entire database value to be read to facilitate change tracking. Using "value", "notify_value", "child_changed" and "notify_child_changed" events on the root node are a bad practice because of the significant performance impact`)}return this.getNodeValue(topEventPath,valueOptions)}).then(currentValue=>{topEventData=currentValue;return writeNode()}).then(result=>{let newTopEventData=cloneObject(topEventData);if(newTopEventData===null){newTopEventData=path===topEventPath?value:{}}let modifiedData=newTopEventData;if(path!==topEventPath){let trailPath=path.slice(topEventPath.length).replace(/^\//,"");let trailKeys=PathInfo.getPathKeys(trailPath);while(trailKeys.length>0){let childKey=trailKeys.shift();if(!options.merge&&trailKeys.length===0){modifiedData[childKey]=value}else{if(!(childKey in modifiedData)){modifiedData[childKey]={}}modifiedData=modifiedData[childKey]}}}if(options.merge){Object.keys(value).forEach(key=>{let newValue=value[key];if(newValue!==null){modifiedData[key]=newValue}else{delete modifiedData[key]}})}else if(path===topEventPath){newTopEventData=modifiedData=value}const indexUpdates=[];indexes.map(index=>({index:index,keys:PathInfo.getPathKeys(index.path)})).sort((a,b)=>{if(a.keys.lengthb.keys.length){return-1}return 0}).forEach(({index:index})=>{let pathKeys=PathInfo.getPathKeys(topEventPath);let indexPathKeys=PathInfo.getPathKeys(index.path+"/*");let trailKeys=indexPathKeys.slice(pathKeys.length);let oldValue=topEventData;let newValue=newTopEventData;if(trailKeys.length===0){console.assert(pathKeys.length===indexPathKeys.length,"check logic");const p=index.handleRecordUpdate(topEventPath,oldValue,newValue);indexUpdates.push(p);return}const getAllIndexUpdates=(path,oldValue,newValue)=>{if(oldValue===null&&newValue===null){return[]}let pathKeys=PathInfo.getPathKeys(path);let indexPathKeys=PathInfo.getPathKeys(index.path+"/*");let trailKeys=indexPathKeys.slice(pathKeys.length);if(trailKeys.length===0){console.assert(pathKeys.length===indexPathKeys.length,"check logic");return[{path:path,oldValue:oldValue,newValue:newValue}]}let results=[];let trailPath="";while(trailKeys.length>0){let subKey=trailKeys.shift();if(subKey==="*"){let allKeys=oldValue===null?[]:Object.keys(oldValue);newValue!==null&&Object.keys(newValue).forEach(key=>{if(allKeys.indexOf(key)<0){allKeys.push(key)}});allKeys.forEach(key=>{let childPath=PathInfo.getChildPath(trailPath,key);let childValues=getChildValues(key,oldValue,newValue);let subTrailPath=PathInfo.getChildPath(path,childPath);let childResults=getAllIndexUpdates(subTrailPath,childValues.oldValue,childValues.newValue);results=results.concat(childResults)});break}else{let values=getChildValues(subKey,oldValue,newValue);oldValue=values.oldValue;newValue=values.newValue;if(oldValue===null&&newValue===null){break}trailPath=PathInfo.getChildPath(trailPath,subKey)}}return results};let results=getAllIndexUpdates(topEventPath,oldValue,newValue);results.forEach(result=>{const p=index.handleRecordUpdate(result.path,result.oldValue,result.newValue);indexUpdates.push(p)})});const callSubscriberWithValues=(sub,oldValue,newValue,variables=[])=>{let trigger=true;let type=sub.type;if(type.startsWith("notify_")){type=type.slice("notify_".length)}if(type==="child_changed"&&(oldValue===null||newValue===null)){trigger=false}else if(type==="value"||type==="child_changed"){let changes=compareValues(oldValue,newValue);trigger=changes!=="identical"}else if(type==="child_added"){trigger=oldValue===null&&newValue!==null}else if(type==="child_removed"){trigger=oldValue!==null&&newValue===null}let dataPath=sub.dataPath;variables.forEach((variable,i)=>{const safeVarName=variable.name==="*"?"\\*":variable.name.replace("$","\\$");dataPath=dataPath.replace(new RegExp(`(^|/)${safeVarName}([/[]|$)`),`$1${variable.value}$2`)});trigger&&this.subscriptions.trigger(sub.type,sub.subscriptionPath,dataPath,oldValue,newValue)};const triggerAllEvents=()=>{eventSubscriptions.map(sub=>{const keys=PathInfo.getPathKeys(sub.dataPath);return{sub:sub,keys:keys}}).sort((a,b)=>{if(a.keys.lengthb.keys.length){return-1}return 0}).forEach(({sub:sub})=>{const process=(currentPath,oldValue,newValue,variables=[])=>{let trailPath=sub.dataPath.slice(currentPath.length).replace(/^\//,"");let trailKeys=PathInfo.getPathKeys(trailPath);while(trailKeys.length>0){let subKey=trailKeys.shift();if(typeof subKey==="string"&&(subKey==="*"||subKey[0]==="$")){let allKeys=oldValue===null?[]:Object.keys(oldValue);newValue!==null&&Object.keys(newValue).forEach(key=>{if(allKeys.indexOf(key)<0){allKeys.push(key)}});allKeys.forEach(key=>{const childValues=getChildValues(key,oldValue,newValue);const vars=variables.concat({name:subKey,value:key});if(trailKeys.length===0){callSubscriberWithValues(sub,childValues.oldValue,childValues.newValue,vars)}else{process(`${currentPath}/${subKey}`,childValues.oldValue,childValues.newValue,vars)}});return}else{currentPath=PathInfo.getChildPath(currentPath,subKey);let childValues=getChildValues(subKey,oldValue,newValue);oldValue=childValues.oldValue;newValue=childValues.newValue}}callSubscriberWithValues(sub,oldValue,newValue,variables)};process(topEventPath,topEventData,newTopEventData)})};if(options.waitForIndexUpdates===false){indexUpdates.splice(0)}return Promise.all(indexUpdates).then(()=>{process.nextTick(triggerAllEvents);return result})})}getChildren(path,options={keyFilter:undefined,tid:undefined}){throw new Error(`This method must be implemented by subclass`)}getNodeValue(path,options={include:undefined,exclude:undefined,child_objects:true,tid:undefined}){return this.getNode(path,options).then(node=>{return node.value})}getNode(path,options={include:undefined,exclude:undefined,child_objects:true,tid:undefined}){throw new Error(`This method must be implemented by subclass`)}getNodeInfo(path,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}removeNode(path,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}setNode(path,value,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}updateNode(path,updates,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}transactNode(path,callback,options={no_lock:false}){let checkRevision;const tid=ID.generate();const lockPromise=options&&options.no_lock===true?Promise.resolve({tid:tid,release(){}}):this.nodeLocker.lock(path,tid,true,"transactNode");return lockPromise.then(lock=>{let changed=false,changeCallback=(err,path)=>{changed=true};if(options&&options.no_lock){this.subscriptions.add(path,"notify_value",changeCallback)}return this.getNode(path,{tid:tid}).then(node=>{checkRevision=node.revision;let newValue;try{newValue=callback(node.value)}catch(err){this.debug.error(`Error in transaction callback: ${err.message}`)}if(newValue instanceof Promise){return newValue.catch(err=>{this.debug.error(`Error in transaction callback: ${err.message}`)})}return newValue}).then(newValue=>{if(typeof newValue==="undefined"){return}if(options&&options.no_lock){this.subscriptions.remove(path,"notify_value",changeCallback)}if(changed){return Promise.reject(new NodeRevisionError(`Node changed`))}return this.setNode(path,newValue,{assert_revision:checkRevision,tid:lock.tid})}).then(result=>{lock.release();return result}).catch(err=>{lock.release();if(err instanceof NodeRevisionError){console.warn(`node value changed, running again. Error: ${err.message}`);return this.transactNode(path,callback,options)}else{throw err}})})}matchNode(path,criteria,options={tid:undefined}){const tid=options&&options.tid||ID.generate();const checkNode=(path,criteria)=>{if(criteria.length===0){return Promise.resolve(true)}const criteriaKeys=criteria.reduce((keys,cr)=>{if(keys.indexOf(cr.key)<0){keys.push(cr.key)}return keys},[]);const unseenKeys=criteriaKeys.slice();let isMatch=true;let delayedMatchPromises=[];return this.getChildren(path,{tid:tid,keyFilter:criteriaKeys}).next(childInfo=>{unseenKeys.includes(childInfo.key)&&unseenKeys.splice(unseenKeys.indexOf(childInfo.key),1);const keyCriteria=criteria.filter(cr=>cr.key===childInfo.key).map(cr=>({op:cr.op,compare:cr.compare}));const result=checkChild(childInfo,keyCriteria);isMatch=result.isMatch;delayedMatchPromises.push(...result.promises);if(!isMatch||unseenKeys.length===0){return false}}).then(()=>{if(isMatch){return Promise.all(delayedMatchPromises).then(results=>{isMatch=results.every(res=>res.isMatch)})}}).then(()=>{if(!isMatch){return false}isMatch=unseenKeys.every(key=>{const child=new NodeInfo({key:key,exists:false});const keyCriteria=criteria.filter(cr=>cr.key===key).map(cr=>({op:cr.op,compare:cr.compare}));const result=checkChild(child,keyCriteria);return result.isMatch});return isMatch}).catch(err=>{this.debug.error(`Error matching on "${path}": `,err);throw err})};const checkChild=(child,criteria)=>{const promises=[];const isMatch=criteria.every(f=>{let proceed=true;if(f.op==="!exists"||f.op==="=="&&(typeof f.compare==="undefined"||f.compare===null)){proceed=!child.exists}else if(f.op==="exists"||f.op==="!="&&(typeof f.compare==="undefined"||f.compare===null)){proceed=child.exists}else if(!child.exists){proceed=false}else{if(child.address){if(child.valueType===VALUE_TYPES.OBJECT&&["has","!has"].indexOf(f.op)>=0){const op=f.op==="has"?"exists":"!exists";const p=checkNode(child.path,[{key:f.compare,op:op}]).then(isMatch=>{return{key:child.key,isMatch:isMatch}});promises.push(p);proceed=true}else if(child.valueType===VALUE_TYPES.ARRAY&&["contains","!contains"].indexOf(f.op)>=0){const p=this.getNodeValue(child.path,{tid:tid}).then(arr=>{const isMatch=f.op==="contains"?f.compare instanceof Array?f.compare.every(val=>arr.includes(val)):arr.includes(f.compare):f.compare instanceof Array?!f.compare.some(val=>arr.includes(val)):!arr.includes(f.compare);return{key:child.key,isMatch:isMatch}});promises.push(p);proceed=true}else if(child.valueType===VALUE_TYPES.STRING){const p=this.getNodeValue(child.path,{tid:tid}).then(val=>{return{key:child.key,isMatch:this.test(val,f.op,f.compare)}});promises.push(p);proceed=true}else{proceed=false}}else if(child.type===VALUE_TYPES.OBJECT&&["has","!has"].indexOf(f.op)>=0){const has=f.compare in child.value;proceed=has&&f.op==="has"||!has&&f.op==="!has"}else if(child.type===VALUE_TYPES.ARRAY&&["contains","!contains"].indexOf(f.op)>=0){const contains=child.value.indexOf(f.compare)>=0;proceed=contains&&f.op==="contains"||!contains&&f.op==="!contains"}else{const ret=this.test(child.value,f.op,f.compare);if(ret instanceof Promise){promises.push(ret);ret=true}proceed=ret}}return proceed});return{isMatch:isMatch,promises:promises}};return checkNode(path,criteria)}test(val,op,compare){if(op==="<"){return val"){return val>compare}if(op===">="){return val>=compare}if(op==="in"){return compare.indexOf(val)>=0}if(op==="!in"){return compare.indexOf(val)<0}if(op==="like"||op==="!like"){const pattern="^"+compare.replace(/[-[\]{}()+.,\\^$|#\s]/g,"\\$&").replace(/\?/g,".").replace(/\*/g,".*?")+"$";const re=new RegExp(pattern,"i");const isMatch=re.test(val.toString());return op==="like"?isMatch:!isMatch}if(op==="matches"){return compare.test(val.toString())}if(op==="!matches"){return!compare.test(val.toString())}if(op==="between"){return val>=compare[0]&&val<=compare[1]}if(op==="!between"){return valcompare[1]}if(op==="has"||op==="!has"){const has=typeof val==="object"&&compare in val;return op==="has"?has:!has}if(op==="contains"||op==="!contains"){const includes=typeof val==="object"&&val instanceof Array&&val.includes(compare);return op==="contains"?includes:!includes}return false}exportNode(path,stream,options={format:"json"}){if(options&&options.format!=="json"){throw new Error(`Only json output is currently supported`)}const stringifyValue=(type,val)=>{const escape=str=>str.replace(/\\/i,"\\\\").replace(/"/g,'\\"');if(type===VALUE_TYPES.DATETIME){val=`"${val.toISOString()}"`}else if(type===VALUE_TYPES.STRING){val=`"${escape(val)}"`}else if(type===VALUE_TYPES.ARRAY){val=`[]`}else if(type===VALUE_TYPES.OBJECT){val=`{}`}else if(type===VALUE_TYPES.BINARY){val=`"${escape(ascii85.encode(val))}"`}else if(type===VALUE_TYPES.REFERENCE){val=`"${val.path}"`}return val};const queue=[];let outputCount=0;let objStart="",objEnd="";const buffer={output:"",enable:false,promise:null};return this.getNodeInfo(path).then(nodeInfo=>{if(!nodeInfo.exists){stream.write("null")}else if(nodeInfo.type===VALUE_TYPES.OBJECT){objStart="{";objEnd="}"}else if(nodeInfo.type===VALUE_TYPES.ARRAY){objStart="{";objEnd="}"}else{return this.getNodeValue(path).then(value=>{const val=stringifyValue(nodeInfo.type,value);return stream.write(val)})}let p=Promise.resolve();if(objStart){p=stream.write(objStart);if(!(p instanceof Promise)){p=Promise.resolve()}}return p.then(()=>{return this.getChildren(path).next(childInfo=>{if(childInfo.address){queue.push(childInfo)}else{const val=stringifyValue(childInfo.type,childInfo.value);const comma=outputCount>0?",":"";const key=typeof childInfo.index==="number"?`"${childInfo.index}"`:`"${childInfo.key}"`;const output=`${comma}${key}:${val}`;outputCount++;if(buffer.enable){buffer.output+=output}else{const flush=output=>{const p=stream.write(output);if(p instanceof Promise){buffer.enable=true;buffer.promise=p.then(()=>{const buffered=buffer.output;buffer.enable=false;buffer.output="";buffer.promise=null;if(buffered.length>0){return flush(buffered)}});return buffer.promise}};flush(output)}}})})}).then(()=>{return buffer.promise}).then(()=>{const next=()=>{if(queue.length===0){return}const childInfo=queue.shift();const comma=outputCount>0?",":"";const key=typeof childInfo.index==="number"?`"${childInfo.index}"`:`"${childInfo.key}"`;let p=stream.write(`${comma}${key}:`);outputCount++;if(!(p instanceof Promise)){p=Promise.resolve(p)}return p.then(()=>{return this.exportNode(childInfo.address.path,stream)}).then(()=>{return next()})};return next()}).then(()=>{if(objEnd){return stream.write(objEnd)}})}}module.exports={Storage:Storage,StorageSettings:StorageSettings,NodeNotFoundError:NodeNotFoundError,NodeRevisionError:NodeRevisionError}}).call(this,require("_process"))},{"./data-index":41,"./node-info":34,"./node-lock":35,"./node-value-types":36,"./promise-fs":41,_process:45,"acebase-core":11,colors:22,events:42}],41:[function(require,module,exports){},{}],42:[function(require,module,exports){var objectCreate=Object.create||objectCreatePolyfill;var objectKeys=Object.keys||objectKeysPolyfill;var bind=Function.prototype.bind||functionBindPolyfill;function EventEmitter(){if(!this._events||!Object.prototype.hasOwnProperty.call(this,"_events")){this._events=objectCreate(null);this._eventsCount=0}this._maxListeners=this._maxListeners||undefined}module.exports=EventEmitter;EventEmitter.EventEmitter=EventEmitter;EventEmitter.prototype._events=undefined;EventEmitter.prototype._maxListeners=undefined;var defaultMaxListeners=10;var hasDefineProperty;try{var o={};if(Object.defineProperty)Object.defineProperty(o,"x",{value:0});hasDefineProperty=o.x===0}catch(err){hasDefineProperty=false}if(hasDefineProperty){Object.defineProperty(EventEmitter,"defaultMaxListeners",{enumerable:true,get:function(){return defaultMaxListeners},set:function(arg){if(typeof arg!=="number"||arg<0||arg!==arg)throw new TypeError('"defaultMaxListeners" must be a positive number');defaultMaxListeners=arg}})}else{EventEmitter.defaultMaxListeners=defaultMaxListeners}EventEmitter.prototype.setMaxListeners=function setMaxListeners(n){if(typeof n!=="number"||n<0||isNaN(n))throw new TypeError('"n" argument must be a positive number');this._maxListeners=n;return this};function $getMaxListeners(that){if(that._maxListeners===undefined)return EventEmitter.defaultMaxListeners;return that._maxListeners}EventEmitter.prototype.getMaxListeners=function getMaxListeners(){return $getMaxListeners(this)};function emitNone(handler,isFn,self){if(isFn)handler.call(self);else{var len=handler.length;var listeners=arrayClone(handler,len);for(var i=0;i1)er=arguments[1];if(er instanceof Error){throw er}else{var err=new Error('Unhandled "error" event. ('+er+")");err.context=er;throw err}return false}handler=events[type];if(!handler)return false;var isFn=typeof handler==="function";len=arguments.length;switch(len){case 1:emitNone(handler,isFn,this);break;case 2:emitOne(handler,isFn,this,arguments[1]);break;case 3:emitTwo(handler,isFn,this,arguments[1],arguments[2]);break;case 4:emitThree(handler,isFn,this,arguments[1],arguments[2],arguments[3]);break;default:args=new Array(len-1);for(i=1;i0&&existing.length>m){existing.warned=true;var w=new Error("Possible EventEmitter memory leak detected. "+existing.length+' "'+String(type)+'" listeners '+"added. Use emitter.setMaxListeners() to "+"increase limit.");w.name="MaxListenersExceededWarning";w.emitter=target;w.type=type;w.count=existing.length;if(typeof console==="object"&&console.warn){console.warn("%s: %s",w.name,w.message)}}}}return target}EventEmitter.prototype.addListener=function addListener(type,listener){return _addListener(this,type,listener,false)};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.prependListener=function prependListener(type,listener){return _addListener(this,type,listener,true)};function onceWrapper(){if(!this.fired){this.target.removeListener(this.type,this.wrapFn);this.fired=true;switch(arguments.length){case 0:return this.listener.call(this.target);case 1:return this.listener.call(this.target,arguments[0]);case 2:return this.listener.call(this.target,arguments[0],arguments[1]);case 3:return this.listener.call(this.target,arguments[0],arguments[1],arguments[2]);default:var args=new Array(arguments.length);for(var i=0;i=0;i--){if(list[i]===listener||list[i].listener===listener){originalListener=list[i].listener;position=i;break}}if(position<0)return this;if(position===0)list.shift();else spliceOne(list,position);if(list.length===1)events[type]=list[0];if(events.removeListener)this.emit("removeListener",type,originalListener||listener)}return this};EventEmitter.prototype.removeAllListeners=function removeAllListeners(type){var listeners,events,i;events=this._events;if(!events)return this;if(!events.removeListener){if(arguments.length===0){this._events=objectCreate(null);this._eventsCount=0}else if(events[type]){if(--this._eventsCount===0)this._events=objectCreate(null);else delete events[type]}return this}if(arguments.length===0){var keys=objectKeys(events);var key;for(i=0;i=0;i--){this.removeListener(type,listeners[i])}}return this};function _listeners(target,type,unwrap){var events=target._events;if(!events)return[];var evlistener=events[type];if(!evlistener)return[];if(typeof evlistener==="function")return unwrap?[evlistener.listener||evlistener]:[evlistener];return unwrap?unwrapListeners(evlistener):arrayClone(evlistener,evlistener.length)}EventEmitter.prototype.listeners=function listeners(type){return _listeners(this,type,true)};EventEmitter.prototype.rawListeners=function rawListeners(type){return _listeners(this,type,false)};EventEmitter.listenerCount=function(emitter,type){if(typeof emitter.listenerCount==="function"){return emitter.listenerCount(type)}else{return listenerCount.call(emitter,type)}};EventEmitter.prototype.listenerCount=listenerCount;function listenerCount(type){var events=this._events;if(events){var evlistener=events[type];if(typeof evlistener==="function"){return 1}else if(evlistener){return evlistener.length}}return 0}EventEmitter.prototype.eventNames=function eventNames(){return this._eventsCount>0?Reflect.ownKeys(this._events):[]};function spliceOne(list,index){for(var i=index,k=i+1,n=list.length;k1){for(var i=1;i=len)return x;switch(x){case"%s":return String(args[i++]);case"%d":return Number(args[i++]);case"%j":try{return JSON.stringify(args[i++])}catch(_){return"[Circular]"}default:return x}}));for(var x=args[i];i=3)ctx.depth=arguments[2];if(arguments.length>=4)ctx.colors=arguments[3];if(isBoolean(opts)){ctx.showHidden=opts}else if(opts){exports._extend(ctx,opts)}if(isUndefined(ctx.showHidden))ctx.showHidden=false;if(isUndefined(ctx.depth))ctx.depth=2;if(isUndefined(ctx.colors))ctx.colors=false;if(isUndefined(ctx.customInspect))ctx.customInspect=true;if(ctx.colors)ctx.stylize=stylizeWithColor;return formatValue(ctx,obj,ctx.depth)}exports.inspect=inspect;inspect.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]};inspect.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"};function stylizeWithColor(str,styleType){var style=inspect.styles[styleType];if(style){return"["+inspect.colors[style][0]+"m"+str+"["+inspect.colors[style][1]+"m"}else{return str}}function stylizeNoColor(str,styleType){return str}function arrayToHash(array){var hash={};array.forEach((function(val,idx){hash[val]=true}));return hash}function formatValue(ctx,value,recurseTimes){if(ctx.customInspect&&value&&isFunction(value.inspect)&&value.inspect!==exports.inspect&&!(value.constructor&&value.constructor.prototype===value)){var ret=value.inspect(recurseTimes,ctx);if(!isString(ret)){ret=formatValue(ctx,ret,recurseTimes)}return ret}var primitive=formatPrimitive(ctx,value);if(primitive){return primitive}var keys=Object.keys(value);var visibleKeys=arrayToHash(keys);if(ctx.showHidden){keys=Object.getOwnPropertyNames(value)}if(isError(value)&&(keys.indexOf("message")>=0||keys.indexOf("description")>=0)){return formatError(value)}if(keys.length===0){if(isFunction(value)){var name=value.name?": "+value.name:"";return ctx.stylize("[Function"+name+"]","special")}if(isRegExp(value)){return ctx.stylize(RegExp.prototype.toString.call(value),"regexp")}if(isDate(value)){return ctx.stylize(Date.prototype.toString.call(value),"date")}if(isError(value)){return formatError(value)}}var base="",array=false,braces=["{","}"];if(isArray(value)){array=true;braces=["[","]"]}if(isFunction(value)){var n=value.name?": "+value.name:"";base=" [Function"+n+"]"}if(isRegExp(value)){base=" "+RegExp.prototype.toString.call(value)}if(isDate(value)){base=" "+Date.prototype.toUTCString.call(value)}if(isError(value)){base=" "+formatError(value)}if(keys.length===0&&(!array||value.length==0)){return braces[0]+base+braces[1]}if(recurseTimes<0){if(isRegExp(value)){return ctx.stylize(RegExp.prototype.toString.call(value),"regexp")}else{return ctx.stylize("[Object]","special")}}ctx.seen.push(value);var output;if(array){output=formatArray(ctx,value,recurseTimes,visibleKeys,keys)}else{output=keys.map((function(key){return formatProperty(ctx,value,recurseTimes,visibleKeys,key,array)}))}ctx.seen.pop();return reduceToSingleString(output,base,braces)}function formatPrimitive(ctx,value){if(isUndefined(value))return ctx.stylize("undefined","undefined");if(isString(value)){var simple="'"+JSON.stringify(value).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return ctx.stylize(simple,"string")}if(isNumber(value))return ctx.stylize(""+value,"number");if(isBoolean(value))return ctx.stylize(""+value,"boolean");if(isNull(value))return ctx.stylize("null","null")}function formatError(value){return"["+Error.prototype.toString.call(value)+"]"}function formatArray(ctx,value,recurseTimes,visibleKeys,keys){var output=[];for(var i=0,l=value.length;i-1){if(array){str=str.split("\n").map((function(line){return" "+line})).join("\n").substr(2)}else{str="\n"+str.split("\n").map((function(line){return" "+line})).join("\n")}}}else{str=ctx.stylize("[Circular]","special")}}if(isUndefined(name)){if(array&&key.match(/^\d+$/)){return str}name=JSON.stringify(""+key);if(name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)){name=name.substr(1,name.length-2);name=ctx.stylize(name,"name")}else{name=name.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'");name=ctx.stylize(name,"string")}}return name+": "+str}function reduceToSingleString(output,base,braces){var numLinesEst=0;var length=output.reduce((function(prev,cur){numLinesEst++;if(cur.indexOf("\n")>=0)numLinesEst++;return prev+cur.replace(/\u001b\[\d\d?m/g,"").length+1}),0);if(length>60){return braces[0]+(base===""?"":base+"\n ")+" "+output.join(",\n ")+" "+braces[1]}return braces[0]+base+" "+output.join(", ")+" "+braces[1]}function isArray(ar){return Array.isArray(ar)}exports.isArray=isArray;function isBoolean(arg){return typeof arg==="boolean"}exports.isBoolean=isBoolean;function isNull(arg){return arg===null}exports.isNull=isNull;function isNullOrUndefined(arg){return arg==null}exports.isNullOrUndefined=isNullOrUndefined;function isNumber(arg){return typeof arg==="number"}exports.isNumber=isNumber;function isString(arg){return typeof arg==="string"}exports.isString=isString;function isSymbol(arg){return typeof arg==="symbol"}exports.isSymbol=isSymbol;function isUndefined(arg){return arg===void 0}exports.isUndefined=isUndefined;function isRegExp(re){return isObject(re)&&objectToString(re)==="[object RegExp]"}exports.isRegExp=isRegExp;function isObject(arg){return typeof arg==="object"&&arg!==null}exports.isObject=isObject;function isDate(d){return isObject(d)&&objectToString(d)==="[object Date]"}exports.isDate=isDate;function isError(e){return isObject(e)&&(objectToString(e)==="[object Error]"||e instanceof Error)}exports.isError=isError;function isFunction(arg){return typeof arg==="function"}exports.isFunction=isFunction;function isPrimitive(arg){return arg===null||typeof arg==="boolean"||typeof arg==="number"||typeof arg==="string"||typeof arg==="symbol"||typeof arg==="undefined"}exports.isPrimitive=isPrimitive;exports.isBuffer=require("./support/isBuffer");function objectToString(o){return Object.prototype.toString.call(o)}function pad(n){return n<10?"0"+n.toString(10):n.toString(10)}var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function timestamp(){var d=new Date;var time=[pad(d.getHours()),pad(d.getMinutes()),pad(d.getSeconds())].join(":");return[d.getDate(),months[d.getMonth()],time].join(" ")}exports.log=function(){console.log("%s - %s",timestamp(),exports.format.apply(exports,arguments))};exports.inherits=require("inherits");exports._extend=function(origin,add){if(!add||!isObject(add))return origin;var keys=Object.keys(add);var i=keys.length;while(i--){origin[keys[i]]=add[keys[i]]}return origin};function hasOwnProperty(obj,prop){return Object.prototype.hasOwnProperty.call(obj,prop)}}).call(this,require("_process"),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./support/isBuffer":46,_process:45,inherits:43}]},{},[33])(33)})); \ No newline at end of file +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.acebase=f()}})((function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,(function(r){var n=e[i][1][r];return o(n||r)}),p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i=7&&stringLength<=10)return true;return false};cuid.fingerprint=fingerprint;module.exports=cuid},{"./lib/fingerprint.js":2,"./lib/pad.js":3}],2:[function(require,module,exports){var pad=require("./pad.js");var env=typeof window==="object"?window:self;var globalCount=Object.keys(env).length;var mimeTypesLength=navigator.mimeTypes?navigator.mimeTypes.length:0;var clientId=pad((mimeTypesLength+navigator.userAgent.length).toString(36)+globalCount.toString(36),4);module.exports=function fingerprint(){return clientId}},{"./pad.js":3}],3:[function(require,module,exports){module.exports=function pad(num,size){var s="000000000"+num;return s.substr(s.length-size)}},{}],4:[function(require,module,exports){const{EventEmitter:EventEmitter}=require("events");const{DataReference:DataReference,DataReferenceQuery:DataReferenceQuery}=require("./data-reference");const{TypeMappings:TypeMappings}=require("./type-mappings");class AceBaseSettings{constructor(options){this.logLevel=options.logLevel||"log"}}class AceBaseBase extends EventEmitter{constructor(dbname,options){super();if(!options){options={}}this.once("ready",()=>{this._ready=true});this.types=new TypeMappings(this)}ready(callback=undefined){if(this._ready===true){callback&&callback();return Promise.resolve()}else{let resolve;const promise=new Promise(res=>resolve=res);this.on("ready",()=>{resolve();callback&&callback()});return promise}}get isReady(){return this._ready===true}ref(path){return new DataReference(this,path)}get root(){return this.ref("")}query(path){const ref=new DataReference(this,path);return new DataReferenceQuery(ref)}get indexes(){return{get:()=>{return this.api.getIndexes()},create:(path,key,options)=>{return this.api.createIndex(path,key,options)}}}}module.exports={AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings}},{"./data-reference":7,"./type-mappings":16,events:41}],5:[function(require,module,exports){class Api{stats(options=undefined){}subscribe(path,event,callback){}unsubscribe(path,event,callback){}update(path,updates){}set(path,value){}get(path,options){}exists(path){}query(path,query,options){}createIndex(path,key){}getIndexes(){}}module.exports={Api:Api}},{}],6:[function(require,module,exports){const c=function(input,length,result){var i,j,n,b=[0,0,0,0,0];for(i=0;i";return ret},decode:function(input){if(!input.startsWith("<~")||!input.endsWith("~>")){throw new Error("Invalid input string")}input=input.substr(2,input.length-4);var n=input.length,r=[],b=[0,0,0,0,0],i,j,t,x,y,d;for(i=0;i>>=8;y=t&255;t>>>=8;r.push(t>>>8,t&255,y,x);for(j=d;j<5;++j,r.pop());i+=4}const data=new Uint8Array(r);return data.buffer.slice(data.byteOffset,data.byteOffset+data.byteLength)}};module.exports=ascii85},{}],7:[function(require,module,exports){const{DataSnapshot:DataSnapshot}=require("./data-snapshot");const{EventStream:EventStream,EventPublisher:EventPublisher}=require("./subscription");const{ID:ID}=require("./id");const debug=require("./debug");const{PathInfo:PathInfo}=require("./path-info");class DataRetrievalOptions{constructor(options){if(!options){options={}}if(typeof options.include!=="undefined"&&!(options.include instanceof Array)){throw new TypeError(`options.include must be an array`)}if(typeof options.exclude!=="undefined"&&!(options.exclude instanceof Array)){throw new TypeError(`options.exclude must be an array`)}if(typeof options.child_objects!=="undefined"&&typeof options.child_objects!=="boolean"){throw new TypeError(`options.child_objects must be a boolean`)}if(typeof options.allow_cache!=="undefined"&&typeof options.allow_cache!=="boolean"){throw new TypeError(`options.allow_cache must be a boolean`)}this.include=options.include||undefined;this.exclude=options.exclude||undefined;this.child_objects=typeof options.child_objects==="boolean"?options.child_objects:undefined;this.allow_cache=typeof options.allow_cache==="boolean"?options.allow_cache:undefined}}class QueryDataRetrievalOptions extends DataRetrievalOptions{constructor(options){super(options);if(typeof options.snapshots!=="undefined"&&typeof options.snapshots!=="boolean"){throw new TypeError(`options.snapshots must be an array`)}this.snapshots=typeof options.snapshots==="boolean"?options.snapshots:undefined}}const _private=Symbol("private");class DataReference{constructor(db,path,vars){if(!path){path=""}path=path.replace(/^\/|\/$/g,"");const pathInfo=PathInfo.get(path);const key=pathInfo.key;const callbacks=[];this[_private]={get path(){return path},get key(){return key},get callbacks(){return callbacks},vars:vars||{}};this.db=db}get path(){return this[_private].path}get key(){return this[_private].key}get parent(){let currentPath=PathInfo.fillVariables2(this.path,this.vars);const info=PathInfo.get(currentPath);if(info.parentPath===null){return null}return new DataReference(this.db,info.parentPath)}get vars(){return this[_private].vars}child(childPath){childPath=typeof childPath==="number"?childPath:childPath.replace(/^\/|\/$/g,"");const currentPath=PathInfo.fillVariables2(this.path,this.vars);const targetPath=PathInfo.getChildPath(currentPath,childPath);return new DataReference(this.db,targetPath)}set(value,onComplete=undefined){if(this.isWildcardPath){throw new Error(`Cannot set the value of a path with wildcards and/or variables`)}if(this.parent===null){throw new Error(`Cannot set the root object. Use update, or set individual child properties`)}if(typeof value==="undefined"){throw new TypeError(`Cannot store value undefined`)}value=this.db.types.serialize(this.path,value);return this.db.api.set(this.path,value).then(res=>{if(typeof onComplete==="function"){try{onComplete(null,this)}catch(err){console.error(`Error in onComplete callback:`,err)}}}).catch(err=>{if(typeof onComplete==="function"){try{onComplete(err)}catch(err){console.error(`Error in onComplete callback:`,err)}}else{throw err}}).then(()=>{return this})}update(updates,onComplete=undefined){if(this.isWildcardPath){throw new Error(`Cannot update the value of a path with wildcards and/or variables`)}let promise;if(typeof updates!=="object"||updates instanceof Array||updates instanceof ArrayBuffer||updates instanceof Date){promise=this.set(updates)}else{updates=this.db.types.serialize(this.path,updates);promise=this.db.api.update(this.path,updates)}return promise.then(()=>{if(typeof onComplete==="function"){try{onComplete(null,this)}catch(err){console.error(`Error in onComplete callback:`,err)}}}).catch(err=>{if(typeof onComplete==="function"){try{onComplete(err)}catch(err){console.error(`Error in onComplete callback:`,err)}}else{throw err}}).then(()=>{return this})}transaction(callback){if(this.isWildcardPath){throw new Error(`Cannot start a transaction on a path with wildcards and/or variables`)}let cb=currentValue=>{currentValue=this.db.types.deserialize(this.path,currentValue);const snap=new DataSnapshot(this,currentValue);let newValue;try{newValue=callback(snap)}catch(err){return}if(newValue instanceof Promise){return newValue.then(val=>{return this.db.types.serialize(this.path,val)})}else{return this.db.types.serialize(this.path,newValue)}};return this.db.api.transaction(this.path,cb).then(result=>{return this})}on(event,callback,cancelCallbackOrContext,context){if(this.path===""&&["value","notify_value","child_changed","notify_child_changed"].includes(event)){console.warn(`WARNING: Listening for value and child_changed events on the root node is a bad practice`)}const cancelCallback=typeof cancelCallbackOrContext==="function"&&cancelCallbackOrContext;context=typeof cancelCallbackOrContext==="object"?cancelCallbackOrContext:context;const useCallback=typeof callback==="function";let eventPublisher=null;const eventStream=new EventStream(publisher=>{eventPublisher=publisher});let cb={subscr:eventStream,original:callback,ours:(err,path,newValue,oldValue)=>{if(err){debug.error(`Error getting data for event ${event} on path "${path}"`,err);return}let ref=this.db.ref(path);ref[_private].vars=PathInfo.extractVariables(this.path,path);let callbackObject;if(event.startsWith("notify_")){callbackObject=ref}else{const isRemoved=event==="child_removed";const val=this.db.types.deserialize(path,isRemoved?oldValue:newValue);const snap=new DataSnapshot(ref,val,isRemoved);callbackObject=snap}useCallback&&callback.call(context||null,callbackObject);let keep=eventPublisher.publish(callbackObject);if(!keep&&!useCallback){let callbacks=this[_private].callbacks;callbacks.splice(callbacks.indexOf(cb),1);this.db.api.unsubscribe(this.path,event,cb.ours)}}};this[_private].callbacks.push(cb);let authorized=this.db.api.subscribe(this.path,event,cb.ours);const allSubscriptionsStoppedCallback=()=>{let callbacks=this[_private].callbacks;callbacks.splice(callbacks.indexOf(cb),1);this.db.api.unsubscribe(this.path,event,cb.ours)};if(authorized instanceof Promise){authorized.then(()=>{eventPublisher.start(allSubscriptionsStoppedCallback)}).catch(err=>{let callbacks=this[_private].callbacks;callbacks.splice(callbacks.indexOf(cb),1);this.db.api.unsubscribe(this.path,event,cb.ours);eventPublisher.cancel(err.message);cancelCallback&&cancelCallback(err.message)})}else{eventPublisher.start(allSubscriptionsStoppedCallback)}if(callback&&!this.isWildcardPath){if(event==="value"){this.get(snap=>{eventPublisher.publish(snap);useCallback&&callback(snap)})}else if(event==="child_added"){this.get(snap=>{const val=snap.val();if(val===null||typeof val!=="object"){return}Object.keys(val).forEach(key=>{let childSnap=new DataSnapshot(this.child(key),val[key]);eventPublisher.publish(childSnap);useCallback&&callback(childSnap)})})}else if(event==="notify_child_added"){const step=100;let limit=step,skip=0;const more=()=>{this.db.api.reflect(this.path,"children",{limit:limit,skip:skip}).then(children=>{children.list.forEach(child=>{const childRef=this.child(child.key);eventPublisher.publish(childRef);useCallback&&callback(childRef)});if(children.more){skip+=step;more()}})};more()}}return eventStream}off(event=undefined,callback=undefined){const callbacks=this[_private].callbacks;if(callback){const cb=callbacks.find(cb=>cb.original===callback);if(!cb){debug.error(`Can't find specified callback to unsubscribe from (path: "${this.path}", event: ${event}, callback: ${callback})`);return}callbacks.splice(callbacks.indexOf(cb),1);callback=cb.ours;cb.subscr.unsubscribe(callback)}else{callbacks.splice(0,callbacks.length).forEach(cb=>{cb.subscr.unsubscribe()})}this.db.api.unsubscribe(this.path,event,callback);return this}get(optionsOrCallback=undefined,callback=undefined){if(this.isWildcardPath){throw new Error(`Cannot get the value of a path with wildcards and/or variables. Use .query() instead`)}callback=typeof optionsOrCallback==="function"?optionsOrCallback:typeof callback==="function"?callback:undefined;const options=typeof optionsOrCallback==="object"?optionsOrCallback:new DataRetrievalOptions({allow_cache:true});if(typeof options.allow_cache==="undefined"){options.allow_cache=true}const promise=this.db.api.get(this.path,options).then(value=>{value=this.db.types.deserialize(this.path,value);const snapshot=new DataSnapshot(this,value);return snapshot});if(callback){promise.then(callback);return}else{return promise}}once(event,options){if(event==="value"&&!this.isWildcardPath){return this.get(options)}return new Promise((resolve,reject)=>{const callback=snap=>{this.off(event,snap);resolve(snap)};this.on(event,callback)})}push(value=undefined,onComplete=undefined){if(this.isWildcardPath){throw new Error(`Cannot push to a path with wildcards and/or variables`)}const id=ID.generate();const ref=this.child(id);ref.__pushed=true;if(typeof value!=="undefined"){return ref.set(value,onComplete).then(res=>ref)}else{return ref}}remove(){if(this.isWildcardPath){throw new Error(`Cannot remove a path with wildcards and/or variables. Use query().remove instead`)}if(this.parent===null){throw new Error(`Cannot remove the top node`)}return this.set(null)}exists(){if(this.isWildcardPath){throw new Error(`Cannot push to a path with wildcards and/or variables`)}return this.db.api.exists(this.path)}get isWildcardPath(){return this.path.indexOf("*")>=0||this.path.indexOf("$")>=0}query(){return new DataReferenceQuery(this)}reflect(type,args){if(this.pathHasVariables){throw new Error(`Cannot reflect on a path with wildcards and/or variables`)}return this.db.api.reflect(this.path,type,args)}export(stream,options={format:"json"}){return this.db.api.export(this.path,stream,options)}}class DataReferenceQuery{constructor(ref){this.ref=ref;this[_private]={filters:[],skip:0,take:0,order:[]}}filter(key,op,compare){if((op==="in"||op==="!in")&&(!(compare instanceof Array)||compare.length===0)){throw new Error(`${op} filter for ${key} must supply an Array compare argument containing at least 1 value`)}if((op==="between"||op==="!between")&&(!(compare instanceof Array)||compare.length!==2)){throw new Error(`${op} filter for ${key} must supply an Array compare argument containing 2 values`)}if((op==="matches"||op==="!matches")&&!(compare instanceof RegExp)){throw new Error(`${op} filter for ${key} must supply a RegExp compare argument`)}if((op==="contains"||op==="!contains")&&(typeof compare==="object"&&!(compare instanceof Array)&&!(compare instanceof Date)||compare instanceof Array&&compare.length===0)){throw new Error(`${op} filter for ${key} must supply a simple value or (non-zero length) array compare argument`)}this[_private].filters.push({key:key,op:op,compare:compare});return this}where(key,op,compare){return this.filter(key,op,compare)}take(n){this[_private].take=n;return this}skip(n){this[_private].skip=n;return this}sort(key,ascending=true){if(typeof key!=="string"){throw`key must be a string`}this[_private].order.push({key:key,ascending:ascending});return this}order(key,ascending=true){return this.sort(key,ascending)}get(optionsOrCallback=undefined,callback=undefined){callback=typeof optionsOrCallback==="function"?optionsOrCallback:typeof callback==="function"?callback:undefined;const options=typeof optionsOrCallback==="object"?optionsOrCallback:new QueryDataRetrievalOptions({snapshots:true,allow_cache:true});if(typeof options.snapshots==="undefined"){options.snapshots=true}if(typeof options.allow_cache==="undefined"){options.allow_cache=true}options.eventHandler=ev=>{if(!this._events||!this._events[ev.name]){return false}const listeners=this._events[ev.name];if(typeof listeners!=="object"||listeners.length===0){return false}if(["add","change","remove"].includes(ev.name)){const ref=new DataReference(this.ref.db,ev.path);const eventData={name:ev.name};if(options.snapshots&&ev.name!=="remove"){const val=db.types.deserialize(ev.path,ev.value);eventData.snapshot=new DataSnapshot(ref,val,false)}else{eventData.ref=ref}ev=eventData}listeners.forEach(callback=>{try{callback(ev)}catch(e){}})};options.monitor={add:false,change:false,remove:false};if(this._events){if(this._events["add"]&&this._events["add"].length>0){options.monitor.add=true}if(this._events["change"]&&this._events["change"].length>0){options.monitor.change=true}if(this._events["remove"]&&this._events["remove"].length>0){options.monitor.remove=true}}const db=this.ref.db;return db.api.query(this.ref.path,this[_private],options).catch(err=>{throw new Error(err)}).then(results=>{results.forEach((result,index)=>{if(options.snapshots){const val=db.types.deserialize(result.path,result.val);results[index]=new DataSnapshot(db.ref(result.path),val)}else{results[index]=db.ref(result)}});if(options.snapshots){return DataSnapshotsArray.from(results)}else{return DataReferencesArray.from(results)}}).then(results=>{callback&&callback(results);return results})}getRefs(callback=undefined){return this.get({snapshots:false},callback)}remove(callback){return this.get({snapshots:false}).then(refs=>{const promises=[];return Promise.all(refs.map(ref=>ref.remove())).then(()=>{callback&&callback()})})}on(event,callback){if(!this._events){this._events={}}if(!this._events[event]){this._events[event]=[]}this._events[event].push(callback);return this}off(event,callback){if(!this._events){return this}if(typeof event==="undefined"){this._events={};return this}if(!this._events[event]){return this}if(typeof callback==="undefined"){delete this._events[event];return this}const index=!this._events[event].indexOf(callback);if(!~index){return this}this._events[event].splice(index,1);return this}}class DataSnapshotsArray extends Array{static from(snaps){const arr=new DataSnapshotsArray(snaps.length);snaps.forEach((snap,i)=>arr[i]=snap);return arr}getValues(){return this.map(snap=>snap.val())}}class DataReferencesArray extends Array{static from(refs){const arr=new DataReferencesArray(refs.length);refs.forEach((ref,i)=>arr[i]=ref);return arr}getPaths(){return this.map(ref=>ref.path)}}module.exports={DataReference:DataReference,DataReferenceQuery:DataReferenceQuery,DataRetrievalOptions:DataRetrievalOptions,QueryDataRetrievalOptions:QueryDataRetrievalOptions}},{"./data-snapshot":8,"./debug":9,"./id":10,"./path-info":12,"./subscription":14}],8:[function(require,module,exports){const{DataReference:DataReference}=require("./data-reference");const{getPathKeys:getPathKeys}=require("./path-info");const getChild=(snapshot,path)=>{if(!snapshot.exists()){return null}let child=snapshot.val();getPathKeys(path).every(key=>{child=child[key];return typeof child!=="undefined"});return child||null};const getChildren=snapshot=>{if(!snapshot.exists()){return[]}let value=snapshot.val();if(value instanceof Array){return new Array(value.length).map((v,i)=>i)}if(typeof value==="object"){return Object.keys(value)}return[]};class DataSnapshot{constructor(ref,value,isRemoved=false){this.ref=ref;this.val=()=>{return value};this.exists=()=>{if(isRemoved){return false}return value!==null&&typeof value!=="undefined"}}child(path){let child=getChild(this,path);return new DataSnapshot(this.ref.child(path),child)}hasChild(path){return getChild(this,path)!==null}hasChildren(){return getChildren(this).length>0}numChildren(){return getChildren(this).length}forEach(callback){const value=this.val();return getChildren(this).every((key,i)=>{const snap=new DataSnapshot(this.ref.child(key),value[key]);return callback(snap)})}get key(){return this.ref.key}}module.exports={DataSnapshot:DataSnapshot}},{"./data-reference":7,"./path-info":12}],9:[function(require,module,exports){class DebugLogger{constructor(level="log",prefix=""){this.prefix=prefix;this.setLevel(level)}setLevel(level){const prefix=this.prefix?this.prefix:"";this.level=level;this.verbose=["verbose"].includes(level)?console.log.bind(console,prefix):()=>{};this.log=["verbose","log"].includes(level)?console.log.bind(console,prefix):()=>{};this.warn=["verbose","log","warn"].includes(level)?console.warn.bind(console,prefix):()=>{};this.error=["verbose","log","warn","error"].includes(level)?console.error.bind(console,prefix):()=>{};this.write=console.log.bind(console)}}module.exports=DebugLogger},{}],10:[function(require,module,exports){const cuid=require("cuid");class ID{static generate(){return cuid().slice(1)}}module.exports={ID:ID}},{cuid:1}],11:[function(require,module,exports){const{AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings}=require("./acebase-base");const{Api:Api}=require("./api");const{DataReference:DataReference,DataReferenceQuery:DataReferenceQuery,DataRetrievalOptions:DataRetrievalOptions,QueryDataRetrievalOptions:QueryDataRetrievalOptions}=require("./data-reference");const{DataSnapshot:DataSnapshot}=require("./data-snapshot");const DebugLogger=require("./debug");const{ID:ID}=require("./id");const{PathReference:PathReference}=require("./path-reference");const{EventStream:EventStream,EventPublisher:EventPublisher,EventSubscription:EventSubscription}=require("./subscription");const Transport=require("./transport");const{TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions}=require("./type-mappings");const Utils=require("./utils");const{PathInfo:PathInfo}=require("./path-info");const ascii85=require("./ascii85");module.exports={AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings,Api:Api,DataReference:DataReference,DataReferenceQuery:DataReferenceQuery,DataRetrievalOptions:DataRetrievalOptions,QueryDataRetrievalOptions:QueryDataRetrievalOptions,DataSnapshot:DataSnapshot,DebugLogger:DebugLogger,ID:ID,PathReference:PathReference,EventStream:EventStream,EventPublisher:EventPublisher,EventSubscription:EventSubscription,Transport:Transport,TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions,Utils:Utils,PathInfo:PathInfo,ascii85:ascii85}},{"./acebase-base":4,"./api":5,"./ascii85":6,"./data-reference":7,"./data-snapshot":8,"./debug":9,"./id":10,"./path-info":12,"./path-reference":13,"./subscription":14,"./transport":15,"./type-mappings":16,"./utils":17}],12:[function(require,module,exports){function getPathKeys(path){if(path.length===0){return[]}let keys=path.replace(/\[/g,"/[").split("/");keys.forEach((key,index)=>{if(key.startsWith("[")){keys[index]=parseInt(key.substr(1,key.length-2))}});return keys}function getPathInfo(path){if(path.length===0){return{parent:null,key:""}}const i=Math.max(path.lastIndexOf("/"),path.lastIndexOf("["));const parentPath=i<0?"":path.substr(0,i);let key=i<0?path:path.substr(i);if(key.startsWith("[")){key=parseInt(key.substr(1,key.length-2))}else if(key.startsWith("/")){key=key.substr(1)}if(parentPath===path){parentPath=null}return{parent:parentPath,key:key}}function getChildPath(path,key){if(path.length===0){if(typeof key==="number"){throw new TypeError("Cannot add array index to root path!")}return key}if(typeof key==="number"){return`${path}[${key}]`}return`${path}/${key}`}class PathInfo{static get(path){return new PathInfo(path)}static getChildPath(path,childKey){return getChildPath(path,childKey)}static getPathKeys(path){return getPathKeys(path)}constructor(path){this.path=path}get key(){return getPathInfo(this.path).key}get parentPath(){return getPathInfo(this.path).parent}childPath(childKey){return getChildPath(`${this.path}`,childKey)}get pathKeys(){return getPathKeys(this.path)}static extractVariables(varPath,fullPath){if(!varPath.includes("*")&&!varPath.includes("$")){return[]}const keys=getPathKeys(varPath);const pathKeys=getPathKeys(fullPath);let count=0;const variables={get length(){return count}};keys.forEach((key,index)=>{const pathKey=pathKeys[index];if(key==="*"){variables[count++]=pathKey}else if(typeof key==="string"&&key[0]==="$"){variables[count++]=pathKey;variables[key]=pathKey;const varName=key.slice(1);if(typeof variables[varName]==="undefined"){variables[varName]=pathKey}}});return variables}static fillVariables(varPath,fullPath){if(varPath.indexOf("*")<0&&varPath.indexOf("$")<0){return varPath}const keys=getPathKeys(varPath);const pathKeys=getPathKeys(fullPath);let merged=keys.map((key,index)=>{if(key===pathKeys[index]||index>=pathKeys.length){return key}else if(typeof key==="string"&&(key==="*"||key[0]==="$")){return pathKeys[index]}else{throw new Error(`Path "${fullPath}" cannot be used to fill variables of path "${this.path}" because they do not match`)}});let mergedPath="";merged.forEach(key=>{if(typeof key==="number"){mergedPath+=`[${key}]`}else{if(mergedPath.length>0){mergedPath+="/"}mergedPath+=key}});return mergedPath}static fillVariables2(varPath,vars){if(typeof vars!=="object"||Object.keys(vars).length===0){return varPath}let pathKeys=getPathKeys(varPath);let n=0;const targetPath=pathKeys.reduce((path,key)=>{if(key==="*"||key.startsWith("$")){key=vars[n++]}if(typeof key==="number"){return`${path}[${key}]`}else{return`${path}/${key}`}},"");return targetPath}equals(otherPath){if(this.path===otherPath){return true}const keys=getPathKeys(this.path);const otherKeys=getPathKeys(otherPath);if(keys.length!==otherKeys.length){return false}return keys.every((key,index)=>{const otherKey=otherKeys[index];return otherKey===key||typeof otherKey==="string"&&(otherKey==="*"||otherKey[0]==="$")||typeof key==="string"&&(key==="*"||key[0]==="$")})}isAncestorOf(descendantPath){if(descendantPath===""||this.path===descendantPath){return false}if(this.path===""){return true}const ancestorKeys=getPathKeys(this.path);const descendantKeys=getPathKeys(descendantPath);if(ancestorKeys.length>=descendantKeys.length){return false}return ancestorKeys.every((key,index)=>{const otherKey=descendantKeys[index];return otherKey===key||typeof otherKey==="string"&&(otherKey==="*"||otherKey[0]==="$")||typeof key==="string"&&(key==="*"||key[0]==="$")})}isDescendantOf(ancestorPath){if(this.path===""||this.path===ancestorPath){return false}if(ancestorPath===""){return true}const ancestorKeys=getPathKeys(ancestorPath);const descendantKeys=getPathKeys(this.path);if(ancestorKeys.length>=descendantKeys.length){return false}return ancestorKeys.every((key,index)=>{const otherKey=descendantKeys[index];return otherKey===key||typeof otherKey==="string"&&(otherKey==="*"||otherKey[0]==="$")||typeof key==="string"&&(key==="*"||key[0]==="$")})}isChildOf(otherPath){if(this.path===""){return false}const parentInfo=PathInfo.get(this.parentPath);return parentInfo.equals(otherPath)}isParentOf(otherPath){if(otherPath===""){return false}const parentInfo=PathInfo.get(PathInfo.get(otherPath).parentPath);return parentInfo.equals(this.path)}}module.exports={getPathInfo:getPathInfo,getChildPath:getChildPath,getPathKeys:getPathKeys,PathInfo:PathInfo}},{}],13:[function(require,module,exports){class PathReference{constructor(path){this.path=path}}module.exports={PathReference:PathReference}},{}],14:[function(require,module,exports){class EventSubscription{constructor(stop){this.stop=stop;this._internal={state:"init",cancelReason:undefined,activatePromises:[]}}activated(callback=undefined){if(callback){this._internal.activatePromises.push({callback:callback});if(this._internal.state==="active"){callback(true)}else if(this._internal.state==="canceled"){callback(false,this._internal.cancelReason)}}return new Promise((resolve,reject)=>{if(this._internal.state==="active"){return resolve()}else if(this._internal.state==="canceled"&&!callback){return reject(new Error(this._internal.cancelReason))}this._internal.activatePromises.push({resolve:resolve,reject:callback?()=>{}:reject})})}_setActivationState(activated,cancelReason){this._internal.cancelReason=cancelReason;this._internal.state=activated?"active":"canceled";while(this._internal.activatePromises.length>0){const p=this._internal.activatePromises.shift();if(activated){p.callback&&p.callback(true);p.resolve&&p.resolve()}else{p.callback&&p.callback(false,cancelReason);p.reject&&p.reject(cancelReason)}}}}class EventPublisher{constructor(publish,start,cancel){this.publish=publish;this.start=start;this.cancel=cancel}}class EventStream{constructor(eventPublisherCallback){const subscribers=[];let noMoreSubscribersCallback;let activationState;const _stoppedState="stopped (no more subscribers)";this.subscribe=(callback,activationCallback)=>{if(typeof callback!=="function"){throw new TypeError("callback must be a function")}else if(activationState===_stoppedState){throw new Error("stream can't be used anymore because all subscribers were stopped")}const sub={callback:callback,activationCallback:function(activated,cancelReason){activationCallback&&activationCallback(activated,cancelReason);this.subscription._setActivationState(activated,cancelReason)},subscription:new EventSubscription((function stop(){subscribers.splice(subscribers.indexOf(this),1);checkActiveSubscribers()}))};subscribers.push(sub);if(typeof activationState!=="undefined"){if(activationState===true){activationCallback&&activationCallback(true);sub.subscription._setActivationState(true)}else if(typeof activationState==="string"){activationCallback&&activationCallback(false,activationState);sub.subscription._setActivationState(false,activationState)}}return sub.subscription};const checkActiveSubscribers=()=>{if(subscribers.length===0){noMoreSubscribersCallback&&noMoreSubscribersCallback();activationState=_stoppedState}};this.unsubscribe=(callback=undefined)=>{const remove=callback?subscribers.filter(sub=>sub.callback===callback):subscribers;remove.forEach(sub=>{const i=subscribers.indexOf(sub);subscribers.splice(i,1)});checkActiveSubscribers()};this.stop=()=>{subscribers.splice(0);checkActiveSubscribers()};const publish=val=>{subscribers.forEach(sub=>{try{sub.callback(val)}catch(err){debug.error(`Error running subscriber callback: ${err.message}`)}});return subscribers.length>0};const start=allSubscriptionsStoppedCallback=>{activationState=true;noMoreSubscribersCallback=allSubscriptionsStoppedCallback;subscribers.forEach(sub=>{sub.activationCallback&&sub.activationCallback(true)})};const cancel=reason=>{activationState=reason;subscribers.forEach(sub=>{sub.activationCallback&&sub.activationCallback(false,reason||new Error("unknown reason"))});subscribers.splice()};const publisher=new EventPublisher(publish,start,cancel);eventPublisherCallback(publisher)}}module.exports={EventStream:EventStream,EventPublisher:EventPublisher,EventSubscription:EventSubscription}},{}],15:[function(require,module,exports){const{PathReference:PathReference}=require("./path-reference");const{cloneObject:cloneObject}=require("./utils");const ascii85=require("./ascii85");module.exports={deserialize(data){if(data.map===null||typeof data.map==="undefined"){return data.val}const deserializeValue=(type,val)=>{if(type==="date"){return new Date(val)}else if(type==="binary"){return ascii85.decode(val)}else if(type==="reference"){return new PathReference(val)}else if(type==="regexp"){return new RegExp(val.pattern,val.flags)}return val};if(typeof data.map==="string"){return deserializeValue(data.map,data.val)}Object.keys(data.map).forEach(path=>{const type=data.map[path];const keys=path.replace(/\[/g,"/[").split("/");keys.forEach((key,index)=>{if(key.startsWith("[")){keys[index]=parseInt(key.substr(1,key.length-2))}});let parent=data;let key="val";let val=data.val;keys.forEach(k=>{key=k;parent=val;val=val[key]});parent[key]=deserializeValue(type,val)});return data.val},serialize(obj){if(obj===null||typeof obj!=="object"||obj instanceof Date||obj instanceof ArrayBuffer||obj instanceof PathReference){const ser=this.serialize({value:obj});return{map:ser.map.value,val:ser.val.value}}obj=cloneObject(obj);const process=(obj,mappings,prefix)=>{Object.keys(obj).forEach(key=>{const val=obj[key];const path=prefix.length===0?key:`${prefix}/${key}`;if(val instanceof Date){obj[key]=val.toISOString();mappings[path]="date"}else if(val instanceof ArrayBuffer){obj[key]=ascii85.encode(val);mappings[path]="binary"}else if(val instanceof PathReference){obj[key]=val.path;mappings[path]="reference"}else if(val instanceof RegExp){obj[key]={pattern:val.source,flags:val.flags};mappings[path]="regexp"}else if(typeof val==="object"&&val!==null){process(val,mappings,path)}})};const mappings={};process(obj,mappings,"");return{map:mappings,val:obj}}}},{"./ascii85":6,"./path-reference":13,"./utils":17}],16:[function(require,module,exports){const{cloneObject:cloneObject}=require("./utils");const{PathInfo:PathInfo}=require("./path-info");const{AceBaseBase:AceBaseBase}=require("./acebase-base");const{DataReference:DataReference}=require("./data-reference");const{DataSnapshot:DataSnapshot}=require("./data-snapshot");const get=(mappings,path)=>{path=path.replace(/^\/|\/$/g,"");const keys=PathInfo.getPathKeys(path);const mappedPath=Object.keys(mappings).find(mpath=>{const mkeys=PathInfo.getPathKeys(mpath);if(mkeys.length!==keys.length){return false}return mkeys.every((mkey,index)=>{if(mkey==="*"||mkey[0]==="$"){return true}return mkey===keys[index]})});const mapping=mappings[mappedPath];return mapping};const map=(mappings,path)=>{const targetPath=PathInfo.get(path).parentPath;if(targetPath===null){return}return get(mappings,targetPath)};const mapDeep=(mappings,entryPath)=>{entryPath=entryPath.replace(/^\/|\/$/g,"");const pathInfo=PathInfo.get(entryPath);const startPath=pathInfo.parentPath;const keys=startPath?PathInfo.getPathKeys(startPath):[];const matches=Object.keys(mappings).reduce((m,mpath)=>{const mkeys=PathInfo.getPathKeys(mpath);if(mkeys.length{if(index>=keys.length){return false}else if(mkey==="*"||mkey[0]==="$"||mkey===keys[index]){return true}else{isMatch=false;return false}})}if(isMatch){const mapping=mappings[mpath];m.push({path:mpath,type:mapping})}return m},[]);return matches};const process=(db,mappings,path,obj,action)=>{if(obj===null||typeof obj!=="object"){return obj}const keys=PathInfo.getPathKeys(path);const m=mapDeep(mappings,path);const changes=[];m.sort((a,b)=>PathInfo.getPathKeys(a.path).length>PathInfo.getPathKeys(b.path).length?-1:1);m.forEach(mapping=>{const mkeys=PathInfo.getPathKeys(mapping.path);mkeys.push("*");const mTrailKeys=mkeys.slice(keys.length);if(mTrailKeys.length===0){const vars=PathInfo.extractVariables(mapping.path,path);const ref=new DataReference(db,path,vars);if(action==="serialize"){obj=mapping.type.serialize(obj,ref)}else if(action==="deserialize"){const snap=new DataSnapshot(ref,obj);obj=mapping.type.deserialize(snap)}return}const process=(parentPath,parent,keys)=>{if(obj===null||typeof obj!=="object"){return obj}const key=keys[0];let children=[];if(key==="*"||key[0]==="$"){if(parent instanceof Array){children=parent.map((val,index)=>({key:index,val:val}))}else{children=Object.keys(parent).map(k=>({key:k,val:parent[k]}))}}else{const child=parent[key];if(typeof child==="object"){children.push({key:key,val:child})}}children.forEach(child=>{const childPath=PathInfo.getChildPath(parentPath,child.key);const vars=PathInfo.extractVariables(mapping.path,childPath);const ref=new DataReference(db,childPath,vars);if(keys.length===1){if(action==="serialize"){changes.push({parent:parent,key:child.key,original:parent[child.key]});parent[child.key]=mapping.type.serialize(child.val,ref)}else if(action==="deserialize"){const snap=new DataSnapshot(ref,child.val);parent[child.key]=mapping.type.deserialize(snap)}}else{process(childPath,child.val,keys.slice(1))}})};process(path,obj,mTrailKeys)});if(action==="serialize"){obj=cloneObject(obj);if(changes.length>0){changes.forEach(change=>{change.parent[change.key]=change.original})}}return obj};class TypeMappingOptions{constructor(options){if(!options){options={}}this.serializer=options.serializer;this.creator=options.creator}}const _mappings=Symbol("mappings");class TypeMappings{constructor(db){this.db=db;this[_mappings]={}}get mappings(){return this[_mappings]}map(path){return map(this[_mappings],path)}bind(path,type,options={}){if(typeof path!=="string"){throw new TypeError("path must be a string")}if(typeof type!=="function"){throw new TypeError("constructor must be a function")}if(typeof options.serializer==="undefined"){}else if(typeof options.serializer==="string"){if(typeof type.prototype[options.serializer]==="function"){options.serializer=type.prototype[options.serializer]}else{throw new TypeError(`${type.name}.prototype.${options.serializer} is not a function, cannot use it as serializer`)}}else if(typeof options.serializer!=="function"){throw new TypeError(`serializer for class ${type.name} must be a function, or the name of a prototype method`)}if(typeof options.creator==="undefined"){if(typeof type.create==="function"){options.creator=type.create}}else if(typeof options.creator==="string"){if(typeof type[options.creator]==="function"){options.creator=type[options.creator]}else{throw new TypeError(`${type.name}.${options.creator} is not a function, cannot use it as creator`)}}else if(typeof options.creator!=="function"){throw new TypeError(`creator for class ${type.name} must be a function, or the name of a static method`)}path=path.replace(/^\/|\/$/g,"");this[_mappings][path]={db:this.db,type:type,creator:options.creator,serializer:options.serializer,deserialize(snap){let obj;if(this.creator){obj=this.creator.call(this.type,snap)}else{obj=new this.type(snap)}return obj},serialize(obj,ref){if(this.serializer){obj=this.serializer.call(obj,ref,obj)}else if(obj&&typeof obj.serialize==="function"){obj=obj.serialize(ref,obj)}return obj}}}serialize(path,obj){return process(this.db,this[_mappings],path,obj,"serialize")}deserialize(path,obj){return process(this.db,this[_mappings],path,obj,"deserialize")}}module.exports={TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions}},{"./acebase-base":4,"./data-reference":7,"./data-snapshot":8,"./path-info":12,"./utils":17}],17:[function(require,module,exports){(function(Buffer){const{PathReference:PathReference}=require("./path-reference");function numberToBytes(number){const bytes=new Uint8Array(8);const view=new DataView(bytes.buffer);view.setFloat64(0,number);return new Array(...bytes)}function bytesToNumber(bytes){if(bytes.length<8){throw new TypeError("must be 8 bytes")}const bin=new Uint8Array(bytes);const view=new DataView(bin.buffer);const nr=view.getFloat64(0);return nr}function encodeString(str){if(typeof TextEncoder!=="undefined"){const encoder=new TextEncoder;return encoder.encode(str)}else if(typeof Buffer==="function"){const buf=Buffer.from(str,"utf-8");return new Uint8Array(buf.buffer,buf.byteOffset,buf.byteLength)}else{let arr=[];for(let i=0;i128){if((code&55296)===55296){const nextCode=str.charCodeAt(i+1);if((nextCode&56320)!==56320){throw new Error("follow-up utf-16 character does not start with 0xDC00")}i++;const p1=code&1023;const p2=nextCode&1023;code=65536|p1<<10|p2}if(code<2048){const b1=192|code>>6&31;const b2=128|code&63;arr.push(b1,b2)}else if(code<65536){const b1=224|code>>12&15;const b2=128|code>>6&63;const b3=128|code&63;arr.push(b1,b2,b3)}else if(code<2097152){const b1=240|code>>18&7;const b2=128|code>>12&63;const b3=128|code>>6&63;const b4=128|code&63;arr.push(b1,b2,b3,b4)}else{throw new Error(`Cannot convert character ${str.charAt(i)} (code ${code}) to utf-8`)}}else{arr.push(code<128?code:63)}}return new Uint8Array(arr)}}function decodeString(buffer){if(typeof TextDecoder!=="undefined"){const decoder=new TextDecoder;if(buffer instanceof Uint8Array){return decoder.decode(buffer)}const buf=Uint8Array.from(buffer);return decoder.decode(buf)}else if(typeof Buffer==="function"){if(buffer instanceof Buffer){return buffer.toString("utf-8")}else if(buffer instanceof Array){const typedArray=Uint8Array.from(buffer);const buf=Buffer.from(typedArray.buffer,typedArray.byteOffset,typedArray.byteOffset+typedArray.byteLength);return buf.toString("utf-8")}else if("buffer"in buffer&&buffer["buffer"]instanceof ArrayBuffer){const buf=Buffer.from(buffer["buffer"],buffer.byteOffset,buffer.byteOffset+buffer.byteLength);return buf.toString("utf-8")}else{throw new Error(`Unsupported buffer argument`)}}else{if(!(buffer instanceof Uint8Array)&&"buffer"in buffer&&buffer["buffer"]instanceof ArrayBuffer){buffer=new Uint8Array(buffer["buffer"],buffer.byteOffset,buffer.byteLength)}if(buffer instanceof Buffer||buffer instanceof Array||buffer instanceof Uint8Array){let str="";for(let i=0;i128){if((code&240)===240){const b1=code,b2=buffer[i+1],b3=buffer[i+2],b4=buffer[i+3];code=(b1&7)<<18|(b2&63)<<12|(b3&63)<<6|b4&63;i+=3}else if((code&224)===224){const b1=code,b2=buffer[i+1],b3=buffer[i+2];code=(b1&15)<<12|(b2&63)<<6|b3&63;i+=2}else if((code&192)===192){const b1=code,b2=buffer[i+1];code=(b1&31)<<6|b2&63;i++}else{throw new Error(`invalid utf-8 data`)}}if(code>=65536){code^=65536;const p1=55296|code>>10;const p2=56320|code&1023;str+=String.fromCharCode(p1);str+=String.fromCharCode(p2)}else{str+=String.fromCharCode(code)}}return str}else{throw new Error(`Unsupported buffer argument`)}}}function concatTypedArrays(a,b){const c=new a.constructor(a.length+b.length);c.set(a);c.set(b,a.length);return c}function cloneObject(original,stack){const{DataSnapshot:DataSnapshot}=require("./data-snapshot");if(original instanceof DataSnapshot){throw new TypeError(`Object to clone is a DataSnapshot (path "${original.ref.path}")`)}const checkAndFixTypedArray=obj=>{if(obj!==null&&typeof obj==="object"&&typeof obj.constructor==="function"&&typeof obj.constructor.name==="string"&&["Buffer","Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","BigUint64Array","BigInt64Array"].includes(obj.constructor.name)){obj=obj.buffer.slice(obj.byteOffset,obj.byteOffset+obj.byteLength)}return obj};original=checkAndFixTypedArray(original);if(typeof original!=="object"||original===null||original instanceof Date||original instanceof ArrayBuffer||original instanceof PathReference||original instanceof RegExp){return original}const cloneValue=val=>{if(stack.indexOf(val)>=0){throw new ReferenceError(`object contains a circular reference`)}val=checkAndFixTypedArray(val);if(val===null||val instanceof Date||val instanceof ArrayBuffer||val instanceof PathReference||val instanceof RegExp){return val}else if(val instanceof Array){stack.push(val);val=val.map(item=>cloneValue(item));stack.pop();return val}else if(typeof val==="object"){stack.push(val);val=cloneObject(val,stack);stack.pop();return val}else{return val}};if(typeof stack==="undefined"){stack=[original]}const clone=original instanceof Array?[]:{};Object.keys(original).forEach(key=>{let val=original[key];if(typeof val==="function"){return}clone[key]=cloneValue(val)});return clone}function compareValues(oldVal,newVal){const voids=[undefined,null];if(oldVal===newVal){return"identical"}else if(voids.indexOf(oldVal)>=0&&voids.indexOf(newVal)<0){return"added"}else if(voids.indexOf(oldVal)<0&&voids.indexOf(newVal)>=0){return"removed"}else if(typeof oldVal!==typeof newVal){return"changed"}else if(typeof oldVal==="object"){const isArray=oldVal instanceof Array;const oldKeys=isArray?Object.keys(oldVal).map(v=>parseInt(v)):Object.keys(oldVal);const newKeys=isArray?Object.keys(newVal).map(v=>parseInt(v)):Object.keys(newVal);const removedKeys=oldKeys.filter(key=>newKeys.indexOf(key)<0);const addedKeys=newKeys.filter(key=>oldKeys.indexOf(key)<0);const changedKeys=newKeys.reduce((changed,key)=>{if(oldKeys.indexOf(key)>=0){const val1=oldVal[key];const val2=newVal[key];const c=compareValues(val1,val2);if(c!=="identical"){changed.push({key:key,change:c})}}return changed},[]);if(addedKeys.length===0&&removedKeys.length===0&&changedKeys.length===0){return"identical"}else{return{added:addedKeys,removed:removedKeys,changed:changedKeys}}}else if(oldVal!==newVal){return"changed"}return"identical"}const getChildValues=(childKey,oldValue,newValue)=>{oldValue=oldValue===null?null:oldValue[childKey];if(typeof oldValue==="undefined"){oldValue=null}newValue=newValue===null?null:newValue[childKey];if(typeof newValue==="undefined"){newValue=null}return{oldValue:oldValue,newValue:newValue}};module.exports={numberToBytes:numberToBytes,bytesToNumber:bytesToNumber,concatTypedArrays:concatTypedArrays,cloneObject:cloneObject,compareValues:compareValues,getChildValues:getChildValues,encodeString:encodeString,decodeString:decodeString}}).call(this,require("buffer").Buffer)},{"./data-snapshot":8,"./path-reference":13,buffer:40}],18:[function(require,module,exports){var colors={};module["exports"]=colors;colors.themes={};var util=require("util");var ansiStyles=colors.styles=require("./styles");var defineProps=Object.defineProperties;var newLineRegex=new RegExp(/[\r\n]+/g);colors.supportsColor=require("./system/supports-colors").supportsColor;if(typeof colors.enabled==="undefined"){colors.enabled=colors.supportsColor()!==false}colors.enable=function(){colors.enabled=true};colors.disable=function(){colors.enabled=false};colors.stripColors=colors.strip=function(str){return(""+str).replace(/\x1B\[\d+m/g,"")};var stylize=colors.stylize=function stylize(str,style){if(!colors.enabled){return str+""}return ansiStyles[style].open+str+ansiStyles[style].close};var matchOperatorsRe=/[|\\{}()[\]^$+*?.]/g;var escapeStringRegexp=function(str){if(typeof str!=="string"){throw new TypeError("Expected a string")}return str.replace(matchOperatorsRe,"\\$&")};function build(_styles){var builder=function builder(){return applyStyle.apply(builder,arguments)};builder._styles=_styles;builder.__proto__=proto;return builder}var styles=function(){var ret={};ansiStyles.grey=ansiStyles.gray;Object.keys(ansiStyles).forEach((function(key){ansiStyles[key].closeRe=new RegExp(escapeStringRegexp(ansiStyles[key].close),"g");ret[key]={get:function(){return build(this._styles.concat(key))}}}));return ret}();var proto=defineProps((function colors(){}),styles);function applyStyle(){var args=Array.prototype.slice.call(arguments);var str=args.map((function(arg){if(arg!=undefined&&arg.constructor===String){return arg}else{return util.inspect(arg)}})).join(" ");if(!colors.enabled||!str){return str}var newLinesPresent=str.indexOf("\n")!=-1;var nestedStyles=this._styles;var i=nestedStyles.length;while(i--){var code=ansiStyles[nestedStyles[i]];str=code.open+str.replace(code.closeRe,code.open)+code.close;if(newLinesPresent){str=str.replace(newLineRegex,(function(match){return code.close+match+code.open}))}}return str}colors.setTheme=function(theme){if(typeof theme==="string"){console.log("colors.setTheme now only accepts an object, not a string. "+"If you are trying to set a theme from a file, it is now your (the "+"caller's) responsibility to require the file. The old syntax "+"looked like colors.setTheme(__dirname + "+"'/../themes/generic-logging.js'); The new syntax looks like "+"colors.setTheme(require(__dirname + "+"'/../themes/generic-logging.js'));");return}for(var style in theme){(function(style){colors[style]=function(str){if(typeof theme[style]==="object"){var out=str;for(var i in theme[style]){out=colors[theme[style][i]](out)}return out}return colors[theme[style]](str)}})(style)}};function init(){var ret={};Object.keys(styles).forEach((function(name){ret[name]={get:function(){return build([name])}}}));return ret}var sequencer=function sequencer(map,str){var exploded=str.split("");exploded=exploded.map(map);return exploded.join("")};colors.trap=require("./custom/trap");colors.zalgo=require("./custom/zalgo");colors.maps={};colors.maps.america=require("./maps/america")(colors);colors.maps.zebra=require("./maps/zebra")(colors);colors.maps.rainbow=require("./maps/rainbow")(colors);colors.maps.random=require("./maps/random")(colors);for(var map in colors.maps){(function(map){colors[map]=function(str){return sequencer(colors.maps[map],str)}})(map)}defineProps(colors,init())},{"./custom/trap":19,"./custom/zalgo":20,"./maps/america":23,"./maps/rainbow":24,"./maps/random":25,"./maps/zebra":26,"./styles":27,"./system/supports-colors":29,util:46}],19:[function(require,module,exports){module["exports"]=function runTheTrap(text,options){var result="";text=text||"Run the trap, drop the bass";text=text.split("");var trap={a:["@","Ą","Ⱥ","Ʌ","Δ","Λ","Д"],b:["ß","Ɓ","Ƀ","ɮ","β","฿"],c:["©","Ȼ","Ͼ"],d:["Ð","Ɗ","Ԁ","ԁ","Ԃ","ԃ"],e:["Ë","ĕ","Ǝ","ɘ","Σ","ξ","Ҽ","੬"],f:["Ӻ"],g:["ɢ"],h:["Ħ","ƕ","Ң","Һ","Ӈ","Ԋ"],i:["༏"],j:["Ĵ"],k:["ĸ","Ҡ","Ӄ","Ԟ"],l:["Ĺ"],m:["ʍ","Ӎ","ӎ","Ԡ","ԡ","൩"],n:["Ñ","ŋ","Ɲ","Ͷ","Π","Ҋ"],o:["Ø","õ","ø","Ǿ","ʘ","Ѻ","ם","۝","๏"],p:["Ƿ","Ҏ"],q:["্"],r:["®","Ʀ","Ȑ","Ɍ","ʀ","Я"],s:["§","Ϟ","ϟ","Ϩ"],t:["Ł","Ŧ","ͳ"],u:["Ʊ","Ս"],v:["ט"],w:["Ш","Ѡ","Ѽ","൰"],x:["Ҳ","Ӿ","Ӽ","ӽ"],y:["¥","Ұ","Ӌ"],z:["Ƶ","ɀ"]};text.forEach((function(c){c=c.toLowerCase();var chars=trap[c]||[" "];var rand=Math.floor(Math.random()*chars.length);if(typeof trap[c]!=="undefined"){result+=trap[c][rand]}else{result+=c}}));return result}},{}],20:[function(require,module,exports){module["exports"]=function zalgo(text,options){text=text||" he is here ";var soul={up:["̍","̎","̄","̅","̿","̑","̆","̐","͒","͗","͑","̇","̈","̊","͂","̓","̈","͊","͋","͌","̃","̂","̌","͐","̀","́","̋","̏","̒","̓","̔","̽","̉","ͣ","ͤ","ͥ","ͦ","ͧ","ͨ","ͩ","ͪ","ͫ","ͬ","ͭ","ͮ","ͯ","̾","͛","͆","̚"],down:["̖","̗","̘","̙","̜","̝","̞","̟","̠","̤","̥","̦","̩","̪","̫","̬","̭","̮","̯","̰","̱","̲","̳","̹","̺","̻","̼","ͅ","͇","͈","͉","͍","͎","͓","͔","͕","͖","͙","͚","̣"],mid:["̕","̛","̀","́","͘","̡","̢","̧","̨","̴","̵","̶","͜","͝","͞","͟","͠","͢","̸","̷","͡"," ҉"]};var all=[].concat(soul.up,soul.down,soul.mid);function randomNumber(range){var r=Math.floor(Math.random()*range);return r}function isChar(character){var bool=false;all.filter((function(i){bool=i===character}));return bool}function heComes(text,options){var result="";var counts;var l;options=options||{};options["up"]=typeof options["up"]!=="undefined"?options["up"]:true;options["mid"]=typeof options["mid"]!=="undefined"?options["mid"]:true;options["down"]=typeof options["down"]!=="undefined"?options["down"]:true;options["size"]=typeof options["size"]!=="undefined"?options["size"]:"maxi";text=text.split("");for(l in text){if(isChar(l)){continue}result=result+text[l];counts={up:0,down:0,mid:0};switch(options.size){case"mini":counts.up=randomNumber(8);counts.mid=randomNumber(2);counts.down=randomNumber(8);break;case"maxi":counts.up=randomNumber(16)+3;counts.mid=randomNumber(4)+1;counts.down=randomNumber(64)+3;break;default:counts.up=randomNumber(8)+1;counts.mid=randomNumber(6)/2;counts.down=randomNumber(8)+1;break}var arr=["up","mid","down"];for(var d in arr){var index=arr[d];for(var i=0;i<=counts[index];i++){if(options[index]){result=result+soul[index][randomNumber(soul[index].length)]}}}}return result}return heComes(text,options)}},{}],21:[function(require,module,exports){var colors=require("./colors");module["exports"]=function(){var addProperty=function(color,func){String.prototype.__defineGetter__(color,func)};addProperty("strip",(function(){return colors.strip(this)}));addProperty("stripColors",(function(){return colors.strip(this)}));addProperty("trap",(function(){return colors.trap(this)}));addProperty("zalgo",(function(){return colors.zalgo(this)}));addProperty("zebra",(function(){return colors.zebra(this)}));addProperty("rainbow",(function(){return colors.rainbow(this)}));addProperty("random",(function(){return colors.random(this)}));addProperty("america",(function(){return colors.america(this)}));var x=Object.keys(colors.styles);x.forEach((function(style){addProperty(style,(function(){return colors.stylize(this,style)}))}));function applyTheme(theme){var stringPrototypeBlacklist=["__defineGetter__","__defineSetter__","__lookupGetter__","__lookupSetter__","charAt","constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf","charCodeAt","indexOf","lastIndexOf","length","localeCompare","match","repeat","replace","search","slice","split","substring","toLocaleLowerCase","toLocaleUpperCase","toLowerCase","toUpperCase","trim","trimLeft","trimRight"];Object.keys(theme).forEach((function(prop){if(stringPrototypeBlacklist.indexOf(prop)!==-1){console.log("warn: ".red+("String.prototype"+prop).magenta+" is probably something you don't want to override. "+"Ignoring style name")}else{if(typeof theme[prop]==="string"){colors[prop]=colors[theme[prop]]}else{var tmp=colors[theme[prop][0]];for(var t=1;t=2,has16m:level>=3}}function supportsColor(stream){if(forceColor===false){return 0}if(hasFlag("color=16m")||hasFlag("color=full")||hasFlag("color=truecolor")){return 3}if(hasFlag("color=256")){return 2}if(stream&&!stream.isTTY&&forceColor!==true){return 0}var min=forceColor?1:0;if(process.platform==="win32"){var osRelease=os.release().split(".");if(Number(process.versions.node.split(".")[0])>=8&&Number(osRelease[0])>=10&&Number(osRelease[2])>=10586){return Number(osRelease[2])>=14931?3:2}return 1}if("CI"in env){if(["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some((function(sign){return sign in env}))||env.CI_NAME==="codeship"){return 1}return min}if("TEAMCITY_VERSION"in env){return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION)?1:0}if("TERM_PROGRAM"in env){var version=parseInt((env.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(env.TERM_PROGRAM){case"iTerm.app":return version>=3?3:2;case"Hyper":return 3;case"Apple_Terminal":return 2}}if(/-256(color)?$/i.test(env.TERM)){return 2}if(/^screen|^xterm|^vt100|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)){return 1}if("COLORTERM"in env){return 1}if(env.TERM==="dumb"){return min}return min}function getSupportLevel(stream){var level=supportsColor(stream);return translateLevel(level)}module.exports={supportsColor:getSupportLevel,stdout:getSupportLevel(process.stdout),stderr:getSupportLevel(process.stderr)}}).call(this,require("_process"))},{"./has-flag.js":28,_process:44,os:43}],30:[function(require,module,exports){const{AceBase:AceBase,AceBaseLocalSettings:AceBaseLocalSettings}=require("./acebase-local");const{CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers,ICustomStorageNode:ICustomStorageNode,ICustomStorageNodeMetaData:ICustomStorageNodeMetaData}=require("./storage-custom");const deprecatedConstructorError=`Using AceBase constructor in the browser to use localStorage is deprecated!\nSwitch to:\nIndexedDB implementation (FASTER, MORE RELIABLE):\n let db = AceBase.WithIndexedDB(name, settings)\nOr, new LocalStorage implementation:\n let db = AceBase.WithLocalStorage(name, settings)\nOr, write your own CustomStorage adapter:\n let myCustomStorage = new CustomStorageSettings({ ... });\n let db = new AceBase(name, { storage: myCustomStorage })`;class BrowserAceBase extends AceBase{constructor(name,settings){if(typeof settings!=="object"||typeof settings.storage!=="object"){throw new Error(deprecatedConstructorError)}super(name,settings)}static WithIndexedDB(dbname,settings){settings=settings||{};if(!settings.logLevel){settings.logLevel="error"}const IndexedDB=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;let request=IndexedDB.open(`${dbname}.acebase`,1);let readyResolve,readyReject,readyPromise=new Promise((rs,rj)=>{readyResolve=rs;readyReject=rj});request.onupgradeneeded=e=>{let db=request.result;db.createObjectStore("nodes",{keyPath:"path"});db.createObjectStore("content")};let db;request.onsuccess=e=>{db=request.result;readyResolve()};request.onerror=e=>{readyReject(e)};const storageSettings=new CustomStorageSettings({name:"IndexedDB",locking:false,ready(){return readyPromise},async getTransaction(target){const context={debug:true,db:db};const transaction=new IndexedDBStorageTransaction(context,target);await transaction.start();return transaction}});return new AceBase(dbname,{logLevel:settings.logLevel,storage:storageSettings})}}class IndexedDBStorageTransaction extends CustomStorageTransaction{constructor(context,target){super(target);this.context=context}start(){return new Promise((resolve,reject)=>{this._tx=this.context.db.transaction(["nodes","content"],this.target.write?"readwrite":"readonly");resolve()})}commit(){const tx=this._tx;tx.commit&&tx.commit()}rollback(err){const tx=this._tx;tx.abort&&tx.abort()}get(path){const tx=this._tx;const request=tx.objectStore("nodes").get(path);return new Promise((resolve,reject)=>{request.onsuccess=event=>{const data=request.result;if(!data){return resolve(null)}const node=data.metadata;const contentReq=tx.objectStore("content").get(path);contentReq.onsuccess=e=>{node.value=contentReq.result;resolve(node)};contentReq.onerror=e=>reject(e)};request.onerror=e=>{console.error(`IndexedDB get error`,e);reject(e)}})}set(path,node){const copy={};const value=node.value;Object.assign(copy,node);delete copy.value;const metadata=copy;const obj={path:path,metadata:metadata};return new Promise((resolve,reject)=>{const tx=this._tx;const request=tx.objectStore("nodes").put(obj);request.onerror=e=>reject(e);request.onsuccess=e=>{const contentReq=tx.objectStore("content").put(value,path);contentReq.onsuccess=e=>resolve();contentReq.onerror=e=>{tx.abort();reject(e)}}})}remove(path){const tx=this._tx;return new Promise((resolve,reject)=>{const r1=tx.objectStore("content").delete(path);r1.onerror=e=>reject(e);r1.onsuccess=e=>{const r2=tx.objectStore("nodes").delete(path);r2.onerror=e=>{tx.abort();reject(e)};r2.onsuccess=e=>resolve()}})}childrenOf(path,include,checkCallback,addCallback){const pathInfo=CustomStorageHelpers.PathInfo.get(path);const tx=this._tx;const store=tx.objectStore("nodes");const query=IDBKeyRange.lowerBound(path,true);return new Promise((resolve,reject)=>{const cursor=include.metadata?store.openCursor(query):store.openKeyCursor(query);cursor.onerror=e=>reject(e);cursor.onsuccess=async e=>{const otherPath=cursor.result?cursor.result.key:null;let keepGoing=true;if(otherPath===null){keepGoing=false}else if(!pathInfo.isAncestorOf(otherPath)){keepGoing=false}else if(pathInfo.isParentOf(otherPath)&&checkCallback(otherPath)){let node;if(include.metadata){const valueCursor=cursor;const data=valueCursor.result.value;node=data.metadata;if(include.value){const req=tx.objectStore("content").get(otherPath);await new Promise((resolve,reject)=>{req.onerror=e=>reject(e);req.onsuccess=e=>{node.value=req.result;resolve()}})}}keepGoing=addCallback(otherPath,node)}if(keepGoing){try{cursor.result.continue()}catch(err){keepGoing=false}}if(!keepGoing){resolve()}}})}descendantsOf(path,include,checkCallback,addCallback){const pathInfo=CustomStorageHelpers.PathInfo.get(path);const tx=this._tx;const store=tx.objectStore("nodes");const query=IDBKeyRange.lowerBound(path,true);return new Promise((resolve,reject)=>{const cursor=include.metadata?store.openCursor(query):store.openKeyCursor(query);cursor.onerror=e=>reject(e);cursor.onsuccess=async e=>{const otherPath=cursor.result?cursor.result.key:null;let keepGoing=true;if(otherPath===null){keepGoing=false}else if(!pathInfo.isAncestorOf(otherPath)){keepGoing=false}else if(checkCallback(otherPath)){let node;if(include.metadata){const valueCursor=cursor;const data=valueCursor.result.value;node=data.metadata;if(include.value){const req=tx.objectStore("content").get(otherPath);await new Promise((resolve,reject)=>{req.onerror=e=>reject(e);req.onsuccess=e=>{node.value=req.result;resolve()}})}}keepGoing=addCallback(otherPath,node)}if(keepGoing){try{cursor.result.continue()}catch(err){keepGoing=false}}if(!keepGoing){resolve()}}})}}module.exports={BrowserAceBase:BrowserAceBase}},{"./acebase-local":31,"./storage-custom":38}],31:[function(require,module,exports){const{AceBaseBase:AceBaseBase,AceBaseSettings:AceBaseSettings}=require("acebase-core");const{StorageSettings:StorageSettings}=require("./storage");const{LocalApi:LocalApi}=require("./api-local");const{CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers,ICustomStorageNode:ICustomStorageNode,ICustomStorageNodeMetaData:ICustomStorageNodeMetaData}=require("./storage-custom");class AceBaseLocalSettings{constructor(options){if(!options){options={}}this.logLevel=options.logLevel||"log";this.storage=options.storage}}class AceBase extends AceBaseBase{constructor(dbname,options){options=new AceBaseLocalSettings(options);super(dbname,options);const apiSettings={db:this,storage:options.storage,logLevel:options.logLevel};this.api=new LocalApi(dbname,apiSettings,ready=>{this.emit("ready")})}static WithLocalStorage(dbname,settings){settings=settings||{};if(!settings.logLevel){settings.logLevel="error"}const localStorage=settings.provider?settings.provider:settings.temp?window.localStorage:window.sessionStorage;const storageSettings=new CustomStorageSettings({name:"LocalStorage",locking:true,ready(){return Promise.resolve()},getTransaction(target){const context={debug:true,dbname:dbname,localStorage:localStorage};const transaction=new LocalStorageTransaction(context,target);return Promise.resolve(transaction)}});return new AceBase(dbname,{logLevel:settings.logLevel,storage:storageSettings})}}class LocalStorageTransaction extends CustomStorageTransaction{constructor(context,target){super(target);this.context=context;this._storageKeysPrefix=`${this.context.dbname}.acebase::`}commit(){return Promise.resolve()}rollback(err){return Promise.resolve()}get(path){return new Promise(resolve=>{const json=this.context.localStorage.getItem(this.getStorageKeyForPath(path));const val=JSON.parse(json);resolve(val)})}set(path,val){return new Promise(resolve=>{const json=JSON.stringify(val);this.context.localStorage.setItem(this.getStorageKeyForPath(path),json);resolve()})}remove(path){return new Promise(resolve=>{this.context.localStorage.removeItem(this.getStorageKeyForPath(path));resolve()})}childrenOf(path,include,checkCallback,addCallback){return new Promise(resolve=>{const pathInfo=CustomStorageHelpers.PathInfo.get(path);for(let i=0;i{const pathInfo=CustomStorageHelpers.PathInfo.get(path);for(let i=0;i{}}){if(typeof options!=="object"){options={}}if(typeof options.snapshots==="undefined"){options.snapshots=false}const sortMatches=matches=>{matches.sort((a,b)=>{const compare=i=>{const o=query.order[i];let left=a.val[o.key];let right=b.val[o.key];if(typeof left==="undefined"&&typeof right!=="undefined"){return o.ascending?-1:1}if(typeof left!=="undefined"&&typeof right==="undefined"){return o.ascending?1:-1}if(typeof left==="undefined"&&typeof right==="undefined"){return 0}if(left==right){if(iright){return o.ascending?1:-1}};return compare(0)})};const loadResultsData=(preResults,options)=>{if(preResults.length===0){return Promise.resolve([])}const maxBatchSize=50;let batches=[];const items=preResults.map((result,index)=>({path:result.path,index:index}));while(items.length>0){let batchItems=items.splice(0,maxBatchSize);batches.push(batchItems)}const results=[];const nextBatch=()=>{const batch=batches.shift();return Promise.all(batch.map(item=>{const{path:path,index:index}=item;return Node.getValue(this.storage,path,options).then(val=>{if(val===null){this.storage.debug.warn(`Indexed result "/${path}" does not have a record!`);return}const result={path:path,val:val};if(stepsExecuted.sorted){results[index]=result}else{results.push(result);if(!stepsExecuted.skipped&&results.length>query.skip+query.take){sortMatches(results);results.pop()}}})})).then(()=>{if(batches.length>0){return nextBatch()}})};return nextBatch().then(()=>{return results})};const isWildcardPath=path.indexOf("*")>=0||path.indexOf("$")>=0;const availableIndexes=this.storage.indexes.get(path);const usingIndexes=[];query.filters.forEach(filter=>{if(filter.index){return}const indexesOnKey=availableIndexes.filter(index=>index.key===filter.key).filter(index=>{return index.validOperators.includes(filter.op)});if(indexesOnKey.length>=1){const otherFilterKeys=query.filters.filter(f=>f!==filter).map(f=>f.key);const sortKeys=query.order.map(o=>o.key).filter(key=>key!==filter.key);const beneficialIndexes=indexesOnKey.map(index=>{const availableKeys=index.includeKeys.concat(index.key);const forOtherFilters=availableKeys.filter(key=>otherFilterKeys.indexOf(key)>=0);const forSorting=availableKeys.filter(key=>sortKeys.indexOf(key)>=0);const forBoth=forOtherFilters.concat(forSorting.filter(index=>forOtherFilters.indexOf(index)<0));const points={filters:forOtherFilters.length,sorting:forSorting.length*(query.take>0?forSorting.length:1),both:forBoth.length*forBoth.length,get total(){return this.filters+this.sorting+this.both}};return{index:index,points:points.total,filterKeys:forOtherFilters,sortKeys:forSorting}});beneficialIndexes.sort((a,b)=>a.points>b.points?-1:1);const bestBenificialIndex=beneficialIndexes[0];filter.index=bestBenificialIndex.index;bestBenificialIndex.filterKeys.forEach(key=>{query.filters.filter(f=>f!==filter&&f.key===key).forEach(f=>{if(!DataIndex.validOperators.includes(f.op)){return}f.indexUsage="filter";f.index=bestBenificialIndex.index})});bestBenificialIndex.sortKeys.forEach(key=>{query.order.filter(s=>s.key===key).forEach(s=>{s.index=bestBenificialIndex.index})})}if(filter.index){usingIndexes.push({index:filter.index,description:filter.index.description})}});if(query.order.length>0&&query.take>0){query.order.forEach(sort=>{if(sort.index){return}sort.index=availableIndexes.filter(index=>index.key===sort.key).find(index=>index.type==="normal")})}const indexDescriptions=usingIndexes.map(index=>index.description).join(", ");usingIndexes.length>0&&this.storage.debug.log(`Using indexes for query: ${indexDescriptions}`);const tableScanFilters=query.filters.filter(filter=>!filter.index);const specialOpsRegex=/^[a-z]+\:/i;if(tableScanFilters.some(filter=>specialOpsRegex.test(filter.op))){const f=tableScanFilters.find(filter=>specialOpsRegex.test(filter.op));const err=new Error(`query contains operator "${f.op}" which requires a special index that was not found on path "${path}", key "${f.key}"`);return Promise.reject(err)}const allowedTableScanOperators=["<","<=","==","!=",">=",">","like","!like","in","!in","matches","!matches","between","!between","has","!has","contains","!contains","exists","!exists"];for(let i=0;i0){const keys=tableScanFilters.reduce((keys,f)=>{if(keys.indexOf(f.key)<0){keys.push(f.key)}return keys},[]).map(key=>`"${key}"`);const err=new Error(`This wildcard path query on "/${path}" requires index(es) on key(s): ${keys.join(", ")}. Create the index(es) and retry`);return Promise.reject(err)}const indexScanPromises=[];query.filters.forEach(filter=>{if(filter.index&&filter.indexUsage!=="filter"){let promise=filter.index.query(filter.op,filter.compare).then(results=>{options.eventHandler&&options.eventHandler({name:"stats",type:"index_query",source:filter.index.description,stats:results.stats});if(results.hints.length>0){options.eventHandler&&options.eventHandler({name:"hints",type:"index_query",source:filter.index.description,hints:results.hints})}return results});const resultFilters=query.filters.filter(f=>f.index===filter.index&&f.indexUsage==="filter");if(resultFilters.length>0){promise=promise.then(results=>{resultFilters.forEach(filter=>{results=results.filterMetadata(filter.key,filter.op,filter.compare)});return results})}indexScanPromises.push(promise)}});const stepsExecuted={filtered:query.filters.length===0,skipped:query.skip===0,taken:query.take===0,sorted:query.order.length===0,preDataLoaded:false,dataLoaded:false};if(query.filters.length===0&&query.take===0){this.storage.debug.error(`Filterless queries must use .take to limit the results. Defaulting to 100 for query on path "${path}"`);query.take=100}if(query.filters.length===0&&query.order.length>0&&query.order[0].index){const sortIndex=query.order[0].index;this.storage.debug.log(`Using index for sorting: ${sortIndex.description}`);const promise=sortIndex.take(query.skip,query.take,query.order[0].ascending).then(results=>{options.eventHandler&&options.eventHandler({name:"stats",type:"sort_index_take",source:filter.index.description,stats:results.stats});if(results.hints.length>0){options.eventHandler&&options.eventHandler({name:"hints",type:"sort_index_take",source:filter.index.description,hints:results.hints})}return results});indexScanPromises.push(promise);stepsExecuted.skipped=true;stepsExecuted.taken=true;stepsExecuted.sorted=true}return Promise.all(indexScanPromises).then(indexResultSets=>{let indexedResults=[];if(indexResultSets.length===1){const resultSet=indexResultSets[0];indexedResults=resultSet.map(match=>{const result={key:match.key,path:match.path,val:{[resultSet.filterKey]:match.value}};match.metadata&&Object.assign(result.val,match.metadata);return result});stepsExecuted.filtered=true}else if(indexResultSets.length>1){indexResultSets.sort((a,b)=>a.length{const result={key:match.key,path:match.path,val:{[shortestSet.filterKey]:match.value}};const matchedInAllSets=otherSets.every(set=>set.findIndex(m=>match.path===match.path)>=0);if(matchedInAllSets){match.metadata&&Object.assign(result.val,match.metadata);otherSets.forEach(set=>{const otherResult=set.find(r=>r.path===result.path);result.val[set.filterKey]=otherResult.value;otherResult.metadata&&Object.assign(result.val,otherResult.metadata)});results.push(result)}return results},[]);stepsExecuted.filtered=true}if(isWildcardPath||indexScanPromises.length>0&&tableScanFilters.length===0){if(query.order.length===0||query.order.every(o=>o.index)){stepsExecuted.preDataLoaded=true;if(!stepsExecuted.sorted&&query.order.length>0){sortMatches(indexedResults)}stepsExecuted.sorted=true;if(!stepsExecuted.skipped&&query.skip>0){indexedResults=indexedResults.slice(query.skip)}if(!stepsExecuted.taken&&query.take>0){indexedResults=indexedResults.slice(0,query.take)}stepsExecuted.skipped=true;stepsExecuted.taken=true;if(!options.snapshots){return indexedResults}const childOptions={include:options.include,exclude:options.exclude,child_objects:options.child_objects};return loadResultsData(indexedResults,childOptions).then(results=>{stepsExecuted.dataLoaded=true;return results})}if(options.snapshots||!stepsExecuted.sorted){const loadPartialResults=query.order.length>0;const childOptions=loadPartialResults?{include:query.order.map(order=>order.key)}:{include:options.include,exclude:options.exclude,child_objects:options.child_objects};return loadResultsData(indexedResults,childOptions).then(results=>{if(query.order.length>0){sortMatches(results)}stepsExecuted.sorted=true;if(query.skip>0){results=results.slice(query.skip)}if(query.take>0){results=results.slice(0,query.take)}stepsExecuted.skipped=true;stepsExecuted.taken=true;if(options.snapshots&&loadPartialResults){return loadResultsData(results,{include:options.include,exclude:options.exclude,child_objects:options.child_objects})}return results})}else{return indexedResults}}let indexKeyFilter;if(indexedResults.length>0){indexKeyFilter=indexedResults.map(result=>result.key)}const promises=[];let matches=[];let preliminaryStop=false;const loadPartialData=query.order.length>0;const childOptions=loadPartialData?{include:query.order.map(order=>order.key)}:{include:options.include,exclude:options.exclude,child_objects:options.child_objects};return Node.getChildren(this.storage,path,indexKeyFilter).next(child=>{if(child.type===Node.VALUE_TYPES.OBJECT){if(!child.address){return}if(preliminaryStop){return false}const p=Node.matches(this.storage,child.address.path,tableScanFilters).then(isMatch=>{if(!isMatch){return null}const childPath=child.address.path;if(options.snapshots||query.order.length>0){return Node.getValue(this.storage,childPath,childOptions).then(val=>{return{path:childPath,val:val}})}else{return{path:childPath}}}).then(result=>{if(result!==null){matches.push(result);if(query.take>0&&matches.length>query.take+query.skip){if(query.order.length>0){sortMatches(matches)}else{preliminaryStop=true}matches.pop()}}});promises.push(p)}}).catch(reason=>{this.storage.debug.warn(`Error getting child stream: ${reason}`);return[]}).then(()=>{return Promise.all(promises).then(()=>{stepsExecuted.preDataLoaded=loadPartialData;stepsExecuted.dataLoaded=!loadPartialData;if(query.order.length>0){sortMatches(matches)}stepsExecuted.sorted=true;if(query.skip>0){matches=matches.slice(query.skip)}stepsExecuted.skipped=true;if(query.take>0){matches=matches.slice(0,query.take)}stepsExecuted.taken=true;if(!stepsExecuted.dataLoaded){return loadResultsData(matches,{include:options.include,exclude:options.exclude,child_objects:options.child_objects}).then(results=>{stepsExecuted.dataLoaded=true;return results})}return matches})})}).then(matches=>{if(!stepsExecuted.sorted&&query.order.length>0){sortMatches(matches)}if(!options.snapshots){matches=matches.map(match=>match.path)}if(!stepsExecuted.skipped&&query.skip>0){matches=matches.slice(query.skip)}if(!stepsExecuted.taken&&query.take>0){matches=matches.slice(0,query.take)}if(options.monitor===true){options.monitor={add:true,change:true,remove:true}}if(typeof options.monitor==="object"&&(options.monitor.add||options.monitor.change||options.monitor.remove)){const matchedPaths=options.snapshots?matches.map(match=>match.path):matches.slice();const ref=this.db.ref(path);const removeMatch=path=>{const index=matchedPaths.indexOf(path);if(index<0){return}matchedPaths.splice(index,1)};const addMatch=path=>{if(matchedPaths.includes(path)){return}matchedPaths.push(path)};const stopMonitoring=()=>{this.unsubscribe(ref.path,"notify_child_changed",childChangedCallback);this.unsubscribe(ref.path,"notify_child_added",childAddedCallback);this.unsubscribe(ref.path,"notify_child_removed",childRemovedCallback)};const childChangedCallback=(err,path,newValue,oldValue)=>{const wasMatch=matchedPaths.includes(path);let keepMonitoring=true;const checkKeys=[];query.filters.forEach(f=>!checkKeys.includes(f.key)&&checkKeys.push(f.key));const seenKeys=[];typeof oldValue==="object"&&Object.keys(oldValue).forEach(key=>!seenKeys.includes(key)&&seenKeys.push(key));typeof newValue==="object"&&Object.keys(newValue).forEach(key=>!seenKeys.includes(key)&&seenKeys.push(key));const missingKeys=[];let isMatch=seenKeys.every(key=>{if(!checkKeys.includes(key)){return true}const filters=query.filters.filter(filter=>filter.key===key);return filters.every(filter=>{if(allowedTableScanOperators.includes(filter.op)){return this.storage.test(newValue[key],filter.op,filter.compare)}if(filter.index.constructor.name==="FullTextDataIndex"&&filter.index.localeKey&&!seenKeys.includes(filter.index.localeKey)){missingKeys.push(filter.index.localeKey);return true}return filter.index.test(newValue,filter.op,filter.compare)})});if(isMatch){missingKeys.push(...checkKeys.filter(key=>!seenKeys.includes(key)));let promise=Promise.resolve(true);if(!wasMatch&&missingKeys.length>0){const filterQueue=query.filters.filter(f=>missingKeys.includes(f.key));const simpleFilters=filterQueue.filter(f=>allowedTableScanOperators.includes(f.op));const indexFilters=filterQueue.filter(f=>!allowedTableScanOperators.includes(f.op));const processFilters=()=>{const checkIndexFilters=()=>{const keysToLoad=indexFilters.reduce((keys,filter)=>{if(!keys.includes(filter.key)){keys.push(filter.key)}if(filter.index.constructor.name==="FullTextDataIndex"&&filter.index.localeKey&&!keys.includes(filter.index.localeKey)){keys.push(filter.index.localeKey)}return keys},[]);return Node.getValue(this.storage,path,{include:keysToLoad}).then(val=>{if(val===null){return false}return indexFilters.every(filter=>filter.index.test(val,filter.op,filter.compare))})};if(simpleFilters.length>0){return Node.matches(this.storage,path,simpleFilters).then(isMatch=>{if(isMatch){if(indexFilters.length===0){return true}return checkIndexFilters()}return false})}else{return checkIndexFilters()}};promise=processFilters()}return promise.then(isMatch=>{if(isMatch){if(!wasMatch){addMatch(path)}let gotValue=value=>{if(wasMatch&&options.monitor.change){keepMonitoring=options.eventHandler({name:"change",path:path,value:value})}else if(!wasMatch&&options.monitor.add){keepMonitoring=options.eventHandler({name:"add",path:path,value:value})}if(keepMonitoring===false){stopMonitoring()}};if(options.snapshots){const loadOptions={include:options.include,exclude:options.exclude,child_objects:options.child_objects};return this.storage.getNodeValue(path,loadOptions).then(gotValue)}else{return gotValue(newValue)}}else if(wasMatch){removeMatch(path);if(options.monitor.remove){keepMonitoring=options.eventHandler({name:"remove",path:path,value:oldValue})}}if(keepMonitoring===false){stopMonitoring()}})}else{if(wasMatch){removeMatch(path);if(options.monitor.remove){keepMonitoring=options.eventHandler({name:"remove",path:path,value:oldValue});if(keepMonitoring===false){stopMonitoring()}}}}};const childAddedCallback=(err,path,newValue,oldValue)=>{let isMatch=query.filters.every(filter=>{if(allowedTableScanOperators.includes(filter.op)){return this.storage.test(newValue[filter.key],filter.op,filter.compare)}else{return filter.index.test(newValue,filter.op,filter.compare)}});let keepMonitoring=true;if(isMatch){addMatch(path);if(options.monitor.add){keepMonitoring=options.eventHandler({name:"add",path:path,value:options.snapshots?newValue:null})}}if(keepMonitoring===false){stopMonitoring()}};const childRemovedCallback=(err,path,newValue,oldValue)=>{let keepMonitoring=true;removeMatch(path);if(options.monitor.remove){keepMonitoring=options.eventHandler({name:"remove",path:path,value:options.snapshots?oldValue:null})}if(keepMonitoring===false){stopMonitoring()}};if(options.monitor.add||options.monitor.change||options.monitor.remove){this.subscribe(ref.path,"notify_child_changed",childChangedCallback)}if(options.monitor.remove){this.subscribe(ref.path,"notify_child_removed",childRemovedCallback)}if(options.monitor.add){this.subscribe(ref.path,"notify_child_added",childAddedCallback)}}return matches})}createIndex(path,key,options){return this.storage.indexes.create(path,key,options)}getIndexes(){return Promise.resolve(this.storage.indexes.list())}reflect(path,type,args){const getChildren=(path,limit=50,skip=0)=>{if(typeof limit==="string"){limit=parseInt(limit)}if(typeof skip==="string"){skip=parseInt(skip)}const children=[];let n=0,stop=skip+limit;return Node.getChildren(this.storage,path).next(childInfo=>{n++;if(limit===0||n<=stop&&n>skip){children.push({key:typeof childInfo.key==="string"?childInfo.key:childInfo.index,type:childInfo.valueTypeName,value:childInfo.value,address:typeof childInfo.address==="object"&&"pageNr"in childInfo.address?{pageNr:childInfo.address.pageNr,recordNr:childInfo.address.recordNr}:undefined})}if(limit>0&&n>stop){return false}}).then(()=>{return{more:limit!==0&&n>stop,list:children}})};switch(type){case"children":{return getChildren(path,args.limit,args.skip)}case"info":{const info={key:"",exists:false,type:"unknown",value:undefined,children:{more:false,list:[]}};return Node.getInfo(this.storage,path).then(nodeInfo=>{info.key=nodeInfo.key;info.exists=nodeInfo.exists;info.type=nodeInfo.valueTypeName;info.value=nodeInfo.value;let hasChildren=nodeInfo.exists&&nodeInfo.address&&[Node.VALUE_TYPES.OBJECT,Node.VALUE_TYPES.ARRAY].includes(nodeInfo.type);if(hasChildren){return getChildren(path,args.child_limit,args.child_skip)}}).then(children=>{info.children=children;return info})}}}export(path,stream,options={format:"json"}){return this.storage.exportNode(path,stream,options)}}module.exports={LocalApi:LocalApi}},{"./acebase-local":31,"./data-index":40,"./node":37,"./storage":39,"./storage-acebase":40,"./storage-custom":38,"./storage-mssql":40,"./storage-sqlite":40,"acebase-core":11}],33:[function(require,module,exports){const{DataReference:DataReference,DataSnapshot:DataSnapshot,EventSubscription:EventSubscription,PathReference:PathReference,TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions}=require("acebase-core");const{AceBaseLocalSettings:AceBaseLocalSettings}=require("./acebase-local");const{BrowserAceBase:BrowserAceBase}=require("./acebase-browser");const{CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers}=require("./storage-custom");const acebase={AceBase:BrowserAceBase,AceBaseLocalSettings:AceBaseLocalSettings,DataReference:DataReference,DataSnapshot:DataSnapshot,EventSubscription:EventSubscription,PathReference:PathReference,TypeMappings:TypeMappings,TypeMappingOptions:TypeMappingOptions,CustomStorageSettings:CustomStorageSettings,CustomStorageTransaction:CustomStorageTransaction,CustomStorageHelpers:CustomStorageHelpers};window.acebase=acebase;window.AceBase=BrowserAceBase;module.exports=acebase},{"./acebase-browser":30,"./acebase-local":31,"./storage-custom":38,"acebase-core":11}],34:[function(require,module,exports){const{VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}=require("./node-value-types");const{PathInfo:PathInfo}=require("acebase-core");class NodeInfo{constructor(info){this.path=info.path;this.type=info.type;this.index=info.index;this.key=info.key;this.exists=info.exists;this.address=info.address;this.value=info.value;if(typeof this.path==="string"&&(typeof this.key==="undefined"&&typeof this.index==="undefined")){let pathInfo=PathInfo.get(this.path);if(typeof pathInfo.key==="number"){this.index=pathInfo.key}else{this.key=pathInfo.key}}if(typeof this.exists==="undefined"){this.exists=true}}get valueType(){return this.type}get valueTypeName(){return getValueTypeName(this.valueType)}toString(){if(!this.exists){return`"${this.path}" doesn't exist`}if(this.address){return`"${this.path}" is ${this.valueTypeName} stored at ${this.address.pageNr},${this.address.recordNr}`}else{return`"${this.path}" is ${this.valueTypeName} with value ${this.value}`}}}module.exports={NodeInfo:NodeInfo}},{"./node-value-types":36,"acebase-core":11}],35:[function(require,module,exports){const{PathInfo:PathInfo}=require("acebase-core");const SECOND=1e3;const MINUTE=6e4;const DEBUG_MODE=false;const LOCK_TIMEOUT=DEBUG_MODE?15*MINUTE:90*SECOND;const LOCK_STATE={PENDING:"pending",LOCKED:"locked",EXPIRED:"expired",DONE:"done"};class NodeLocker{constructor(){this._locks=[]}_allowLock(path,tid,forWriting){const pathInfo=PathInfo.get(path);const existing=this._locks.find(otherLock=>otherLock.tid===tid&&otherLock.state===LOCK_STATE.LOCKED&&(otherLock.path===path||pathInfo.isDescendantOf(otherLock.path))&&(otherLock.forWriting||!forWriting));if(typeof existing==="object"){return{allow:true}}const conflict=this._locks.filter(otherLock=>otherLock.tid!==tid&&otherLock.state===LOCK_STATE.LOCKED).find(otherLock=>{return(forWriting||otherLock.forWriting)&&(path===otherLock.path||pathInfo.isDescendantOf(otherLock.path))});const clashes=typeof conflict!=="undefined";return{allow:!clashes,conflict:conflict}}_processLockQueue(){const pending=this._locks.filter(lock=>lock.state===LOCK_STATE.PENDING&&(lock.waitingFor===null||lock.waitingFor.state!==LOCK_STATE.LOCKED)).sort((a,b)=>{if(a.priority&&!b.priority){return-1}else if(!a.priority&&b.priority){return 1}return a.requested{const check=this._allowLock(lock.path,lock.tid,lock.forWriting);lock.waitingFor=check.conflict||null;if(check.allow){this.lock(lock).then(lock.resolve).catch(lock.reject)}})}lock(path,tid,forWriting=true,comment="",options={withPriority:false,noTimeout:false}){let lock,proceed;if(path instanceof NodeLock){lock=path;lock.comment=`(retry: ${lock.comment})`;proceed=true}else if(this._locks.findIndex(l=>l.tid===tid&&l.state===LOCK_STATE.EXPIRED)>=0){return Promise.reject(new Error(`lock on tid ${tid} has expired, not allowed to continue`))}else{lock=new NodeLock(this,path,tid,forWriting,options.withPriority===true);lock.comment=comment;this._locks.push(lock);const check=this._allowLock(path,tid,forWriting);lock.waitingFor=check.conflict||null;proceed=check.allow}if(proceed){lock.state=LOCK_STATE.LOCKED;if(typeof lock.granted==="number"){}else{lock.granted=Date.now();if(options.noTimeout!==true){lock.expires=Date.now()+LOCK_TIMEOUT;lock.timeout=setTimeout(()=>{if(lock.state!==LOCK_STATE.LOCKED){return}console.error(`lock :: ${lock.forWriting?"write":"read"} lock on path "/${lock.path}" by tid ${lock.tid} took too long, ${lock.comment}`);lock.state=LOCK_STATE.EXPIRED;this._processLockQueue()},LOCK_TIMEOUT)}}return Promise.resolve(lock)}else{console.assert(lock.state===LOCK_STATE.PENDING);const p=new Promise((resolve,reject)=>{lock.resolve=resolve;lock.reject=reject});return p}}unlock(lockOrId,comment,processQueue=true){let lock,i;if(lockOrId instanceof NodeLock){lock=lockOrId;i=this._locks.indexOf(lock)}else{let id=lockOrId;i=this._locks.findIndex(l=>l.id===id);lock=this._locks[i]}if(i<0){const msg=`lock on "/${lock.path}" for tid ${lock.tid} wasn't found; ${comment}`;return Promise.reject(new Error(msg))}lock.state=LOCK_STATE.DONE;clearTimeout(lock.timeout);this._locks.splice(i,1);processQueue&&this._processLockQueue();return Promise.resolve(lock)}list(){return this._locks||[]}isAllowed(path,tid,forWriting){return this._allowLock(path,tid,forWriting).allow}}let lastid=0;class NodeLock{static get LOCK_STATE(){return LOCK_STATE}constructor(locker,path,tid,forWriting,priority=false){this.locker=locker;this.path=path;this.tid=tid;this.forWriting=forWriting;this.priority=priority;this.state=LOCK_STATE.PENDING;this.requested=Date.now();this.granted=undefined;this.expires=undefined;this.comment="";this.waitingFor=null;this.id=++lastid}release(comment){return this.locker.unlock(this,comment||this.comment)}moveToParent(){const parentPath=PathInfo.get(this.path).parentPath;const allowed=this.locker.isAllowed(parentPath,this.tid,this.forWriting);if(allowed){this.waitingFor=null;this.path=parentPath;this.comment=`moved to parent: ${this.comment}`;return Promise.resolve(this)}else{this.locker.unlock(this,`moveLockToParent: ${this.comment}`,false);return this.locker.lock(parentPath,this.tid,this.forWriting,`moved to parent (queued): ${this.comment}`,{withPriority:true}).then(newLock=>{return newLock})}}moveTo(otherPath,forWriting){const allowed=this.locker.isAllowed(otherPath,this.tid,forWriting);if(allowed){this.waitingFor=null;this.path=otherPath;this.forWriting=forWriting;this.comment=`moved to "/${otherPath}": ${this.comment}`;return Promise.resolve(this)}else{this.locker.unlock(this,`moving to "/${otherPath}": ${this.comment}`,false);return this.locker.lock(otherPath,this.tid,forWriting,`moved to "/${otherPath}" (queued): ${this.comment}`,{withPriority:true}).then(newLock=>{return newLock})}}}module.exports={NodeLocker:NodeLocker,NodeLock:NodeLock}},{"acebase-core":11}],36:[function(require,module,exports){const VALUE_TYPES={OBJECT:1,ARRAY:2,NUMBER:3,BOOLEAN:4,STRING:5,DATETIME:6,BINARY:8,REFERENCE:9};function getValueTypeName(valueType){switch(valueType){case VALUE_TYPES.ARRAY:return"array";case VALUE_TYPES.BINARY:return"binary";case VALUE_TYPES.BOOLEAN:return"boolean";case VALUE_TYPES.DATETIME:return"date";case VALUE_TYPES.NUMBER:return"number";case VALUE_TYPES.OBJECT:return"object";case VALUE_TYPES.REFERENCE:return"reference";case VALUE_TYPES.STRING:return"string";default:"unknown"}}module.exports={VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}},{}],37:[function(require,module,exports){const{Storage:Storage}=require("./storage");const{NodeInfo:NodeInfo}=require("./node-info");const{VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}=require("./node-value-types");const colors=require("colors");class Node{static get VALUE_TYPES(){return VALUE_TYPES}static getInfo(storage,path,options={no_cache:false}){if(options&&!options.no_cache){let cachedInfo=storage.nodeCache.find(path);if(cachedInfo){return Promise.resolve(cachedInfo)}}return storage.getNodeInfo(path).then(info=>{if(options&&!options.no_cache){storage.nodeCache.update(info)}return info})}static update(storage,path,value,options={merge:true}){if(options.merge){return storage.updateNode(path,value)}else{return storage.setNode(path,value)}}static exists(storage,path){return storage.getNodeInfo(path).then(nodeInfo=>{return nodeInfo.exists})}static getValue(storage,path,options={include:undefined,exclude:undefined,child_objects:true}){if(!options){options={}}if(typeof options.include!=="undefined"&&!(options.include instanceof Array)){throw new TypeError(`options.include must be an array of key names`)}if(typeof options.exclude!=="undefined"&&!(options.exclude instanceof Array)){throw new TypeError(`options.exclude must be an array of key names`)}if(["undefined","boolean"].indexOf(typeof options.child_objects)<0){throw new TypeError(`options.child_objects must be a boolean`)}return storage.getNodeValue(path,options)}static getChildInfo(storage,path,childKeyOrIndex){let childInfo;return storage.getChildren(path,{keyFilter:[childKeyOrIndex]}).next(info=>{childInfo=info}).then(()=>{return childInfo})}static getChildren(storage,path,keyFilter=undefined){return storage.getChildren(path,{keyFilter:keyFilter})}static remove(storage,path){return storage.removeNode(path)}static set(storage,path,value){return Node.update(storage,path,value,{merge:false})}static transaction(storage,path,callback){return storage.transactNode(path,callback)}static matches(storage,path,criteria,options){return storage.matchNode(path,criteria,options)}}class NodeChange{static get CHANGE_TYPE(){return{UPDATE:"update",DELETE:"delete",INSERT:"insert"}}constructor(keyOrIndex,changeType,oldValue,newValue){this.keyOrIndex=keyOrIndex;this.changeType=changeType;this.oldValue=oldValue;this.newValue=newValue}}class NodeChangeTracker{constructor(path){this.path=path;this._changes=[];this._oldValue=undefined;this._newValue=undefined}addDelete(keyOrIndex,oldValue){this._changes.push(new NodeChange(keyOrIndex,NodeChange.CHANGE_TYPE.DELETE,oldValue,null))}addUpdate(keyOrIndex,oldValue,newValue){this._changes.push(new NodeChange(keyOrIndex,NodeChange.CHANGE_TYPE.UPDATE,oldValue,newValue))}addInsert(keyOrIndex,newValue){this._changes.push(new NodeChange(keyOrIndex,NodeChange.CHANGE_TYPE.INSERT,null,newValue))}add(keyOrIndex,currentValue,newValue){if(currentValue===null){if(newValue===null){throw new Error(`Wrong logic for node change on "${this.nodeInfo.path}/${keyOrIndex}" - both old and new values are null`)}this.addInsert(keyOrIndex,newValue)}else if(newValue===null){this.addDelete(keyOrIndex,currentValue)}else{this.addUpdate(keyOrIndex,currentValue,newValue)}}get updates(){return this._changes.filter(change=>change.changeType===NodeChange.CHANGE_TYPE.UPDATE)}get deletes(){return this._changes.filter(change=>change.changeType===NodeChange.CHANGE_TYPE.DELETE)}get inserts(){return this._changes.filter(change=>change.changeType===NodeChange.CHANGE_TYPE.INSERT)}get all(){return this._changes}get totalChanges(){return this._changes.length}get(keyOrIndex){return this._changes.find(change=>change.keyOrIndex===keyOrIndex)}hasChanged(keyOrIndex){return!!this.get(keyOrIndex)}get newValue(){if(typeof this._newValue==="object"){return this._newValue}if(typeof this._oldValue==="undefined"){throw new TypeError(`oldValue is not set`)}let newValue={};Object.keys(this.oldValue).forEach(key=>newValue[key]=oldValue[key]);this.deletes.forEach(change=>delete newValue[change.key]);this.updates.forEach(change=>newValue[change.key]=change.newValue);this.inserts.forEach(change=>newValue[change.key]=change.newValue);return newValue}set newValue(value){this._newValue=value}get oldValue(){if(typeof this._oldValue==="object"){return this._oldValue}if(typeof this._newValue==="undefined"){throw new TypeError(`newValue is not set`)}let oldValue={};Object.keys(this.newValue).forEach(key=>oldValue[key]=newValue[key]);this.deletes.forEach(change=>oldValue[change.key]=change.oldValue);this.updates.forEach(change=>oldValue[change.key]=change.oldValue);this.inserts.forEach(change=>delete oldValue[change.key]);return oldValue}set oldValue(value){this._oldValue=value}get typeChanged(){return typeof this.oldValue!==typeof this.newValue||this.oldValue instanceof Array&&!(this.newValue instanceof Array)||this.newValue instanceof Array&&!(this.oldValue instanceof Array)}static create(path,oldValue,newValue){const changes=new NodeChangeTracker(path);changes.oldValue=oldValue;changes.newValue=newValue;typeof oldValue==="object"&&Object.keys(oldValue).forEach(key=>{if(typeof newValue==="object"&&key in newValue&&newValue!==null){changes.add(key,oldValue[key],newValue[key])}else{changes.add(key,oldValue[key],null)}});typeof newValue==="object"&&Object.keys(newValue).forEach(key=>{if(typeof oldValue!=="object"||!(key in oldValue)||oldValue[key]===null){changes.add(key,null,newValue[key])}});return changes}}module.exports={Node:Node,NodeInfo:NodeInfo}},{"./node-info":34,"./node-value-types":36,"./storage":39,colors:22}],38:[function(require,module,exports){const{debug:debug,ID:ID,PathReference:PathReference,PathInfo:PathInfo,ascii85:ascii85}=require("acebase-core");const{NodeInfo:NodeInfo}=require("./node-info");const{NodeLocker:NodeLocker}=require("./node-lock");const{VALUE_TYPES:VALUE_TYPES}=require("./node-value-types");const{Storage:Storage,StorageSettings:StorageSettings,NodeNotFoundError:NodeNotFoundError}=require("./storage");class ICustomStorageNodeMetaData{constructor(){this.revision="";this.revision_nr=0;this.created=0;this.modified=0;this.type=0}}class ICustomStorageNode extends ICustomStorageNodeMetaData{constructor(){super();this.value=null}}class CustomStorageTransaction{constructor(target){this.target={get originalPath(){return target.path},path:target.path,get write(){return target.write}};this.id=ID.generate()}get(path){throw new Error(`CustomStorageTransaction.get must be overridden by subclass`)}set(path,node){throw new Error(`CustomStorageTransaction.set must be overridden by subclass`)}remove(path){throw new Error(`CustomStorageTransaction.remove must be overridden by subclass`)}childrenOf(path,include,checkCallback,addCallback){throw new Error(`CustomStorageTransaction.childrenOf must be overridden by subclass`)}descendantsOf(path,include,checkCallback,addCallback){throw new Error(`CustomStorageTransaction.descendantsOf must be overridden by subclass`)}getMultiple(paths){const map=new Map;return Promise.all(paths.map(path=>this.get(path).then(val=>map.set(path,val)))).then(done=>map)}async setMultiple(nodes){await Promise.all(paths.map(({path:path,node:node})=>this.set(path,node)))}async removeMultiple(paths){await Promise.all(paths.map(path=>this.remove(path)))}rollback(reason){throw new Error(`CustomStorageTransaction.rollback must be overridden by subclass`)}commit(){throw new Error(`CustomStorageTransaction.rollback must be overridden by subclass`)}async moveToParentPath(targetPath){const currentPath=this._lock&&this._lock.path||this.target.path;if(currentPath===targetPath){return targetPath}const pathInfo=CustomStorageHelpers.PathInfo.get(targetPath);if(pathInfo.isParentOf(currentPath)){if(this._lock){this._lock=await this._lock.moveToParent()}}else{throw new Error(`Locking issue. Locked path "${this._lock.path}" is not a child/descendant of "${targetPath}"`)}this.target.path=targetPath;return targetPath}}class CustomStorageSettings extends StorageSettings{constructor(settings){super(settings);settings=settings||{};if(typeof settings.ready!=="function"){throw new Error(`ready must be a function`)}if(typeof settings.getTransaction!=="function"){throw new Error(`getTransaction must be a function`)}this.name=settings.name;this.info=`${this.name||"CustomStorage"} realtime database`;this.locking=settings.locking!==false;this.ready=settings.ready;const useLocking=this.locking;const nodeLocker=useLocking?new NodeLocker:null;this.getTransaction=async({path:path,write:write})=>{const transaction=await settings.getTransaction({path:path,write:write});console.assert(typeof transaction.id==="string",`transaction id not set`);const rollback=transaction.rollback;const commit=transaction.commit;transaction.commit=async()=>{const ret=await commit.call(transaction);if(useLocking){await transaction._lock.release("commit")}return ret};transaction.rollback=async reason=>{const ret=await rollback.call(transaction,reason);if(useLocking){await transaction._lock.release("rollback")}return ret};if(useLocking){transaction._lock=await nodeLocker.lock(path,transaction.id,write,`${this.name}::getTransaction`)}return transaction}}}class CustomStorageNodeAddress{constructor(containerPath){this.path=containerPath}}class CustomStorageNodeInfo extends NodeInfo{constructor(info){super(info);this.address;this.revision=info.revision;this.revision_nr=info.revision_nr;this.created=info.created;this.modified=info.modified}}class CustomStorageHelpers{static ChildPathsSql(path,columnName="path"){const where=path===""?`${columnName} <> '' AND ${columnName} NOT LIKE '%/%'`:`(${columnName} LIKE '${path}/%' OR ${columnName} LIKE '${path}[%') AND ${columnName} NOT LIKE '${path}/%/%' AND ${columnName} NOT LIKE '${path}[%]/%' AND ${columnName} NOT LIKE '${path}[%][%'`;return where}static ChildPathsRegex(path){return new RegExp(`^${path}(?:/[^/[]+|[[0-9]+])$`)}static DescendantPathsSql(path,columnName="path"){const where=path===""?`${columnName} <> ''`:`${columnName} LIKE '${path}/%' OR ${columnName} LIKE '${path}[%'`;return where}static DescendantPathsRegex(path){return new RegExp(`^${path}(?:/[^/[]+|[[0-9]+])`)}static get PathInfo(){return PathInfo}}class CustomStorage extends Storage{constructor(dbname,settings){super(dbname,settings);this._init()}async _init(){this._customImplementation=this.settings;this.debug.log(`Database "${this.name}" details:`.intro);this.debug.log(`- Type: CustomStorage`);this.debug.log(`- Path: ${this.settings.path}`);this.debug.log(`- Max inline value size: ${this.settings.maxInlineValueSize}`.intro);this.debug.log(`- Autoremove undefined props: ${this.settings.removeVoidProperties}`);await this._customImplementation.ready();const transaction=await this._customImplementation.getTransaction({path:"",write:true});const info=await this.getNodeInfo("",{transaction:transaction});if(!info.exists){await this._writeNode("",{},{transaction:transaction})}await transaction.commit();if(this.indexes.supported){await this.indexes.load()}this.emit("ready")}_storeNode(path,node,options){const getTypedChildValue=val=>{if(val===null){throw new Error(`Not allowed to store null values. remove the property`)}else if(["string","number","boolean"].includes(typeof val)){return val}else if(val instanceof Date){return{type:VALUE_TYPES.DATETIME,value:val.getTime()}}else if(val instanceof PathReference){return{type:VALUE_TYPES.REFERENCE,value:child.path}}else if(val instanceof ArrayBuffer){return{type:VALUE_TYPES.BINARY,value:ascii85.encode(val)}}else if(typeof val==="object"){console.assert(Object.keys(val).length===0,"child object stored in parent can only be empty");return val}};const unprocessed=`Caller should have pre-processed the value by converting it to a string`;if(node.type===VALUE_TYPES.ARRAY&&node.value instanceof Array){console.warn(`Unprocessed array. ${unprocessed}`);const obj={};for(let i=0;i{node.value[key]=getTypedChildValue(original[key])})}return options.transaction.set(path,node)}_processReadNodeValue(node){const getTypedChildValue=val=>{if(val.type===VALUE_TYPES.BINARY){return ascii85.decode(val.value)}else if(val.type===VALUE_TYPES.DATETIME){return new Date(val.value)}else if(val.type===VALUE_TYPES.REFERENCE){return new PathReference(val.value)}else{throw new Error(`Unhandled child value type ${val.type}`)}};switch(node.type){case VALUE_TYPES.ARRAY:case VALUE_TYPES.OBJECT:{const obj=node.value;Object.keys(obj).forEach(key=>{let item=obj[key];if(typeof item==="object"&&"type"in item){obj[key]=getTypedChildValue(item)}});node.value=obj;break}case VALUE_TYPES.BINARY:{node.value=ascii85.decode(node.value);break}case VALUE_TYPES.REFERENCE:{node.value=new PathReference(node.value);break}case VALUE_TYPES.STRING:{break}default:throw new Error(`Invalid standalone record value type`)}}async _readNode(path,options){let node=await options.transaction.get(path);if(node===null){return null}if(typeof node!=="object"){throw new Error(`CustomStorage get function must return an ICustomStorageNode object. Use JSON.parse if your set function stored it as a string`)}this._processReadNodeValue(node);return node}_getTypeFromStoredValue(val){let type;if(typeof val==="string"){type=VALUE_TYPES.STRING}else if(typeof val==="number"){type=VALUE_TYPES.NUMBER}else if(typeof val==="boolean"){type=VALUE_TYPES.BOOLEAN}else if(val instanceof Array){type=VALUE_TYPES.ARRAY}else if(typeof val==="object"){if("type"in val){type=val.type;val=val.value;if(type===VALUE_TYPES.DATETIME){val=new Date(val)}else if(type===VALUE_TYPES.REFERENCE){val=new PathReference(val)}}else{type=VALUE_TYPES.OBJECT}}else{throw new Error(`Unknown value type`)}return{type:type,value:val}}async _writeNode(path,value,options){if(this.valueFitsInline(value)&&path!==""){throw new Error(`invalid value to store in its own node`)}else if(path===""&&(typeof value!=="object"||value instanceof Array)){throw new Error(`Invalid root node value. Must be an object`)}const transaction=options.transaction;const currentRow=await this._readNode(path,{transaction:transaction});if(options.merge&¤tRow){if(currentRow.type===VALUE_TYPES.ARRAY&&!(value instanceof Array)&&typeof value==="object"&&Object.keys(value).some(key=>isNaN(key))){throw new Error(`Cannot merge existing array of path "${path}" with an object`)}if(value instanceof Array&¤tRow.type!==VALUE_TYPES.ARRAY){throw new Error(`Cannot merge existing object of path "${path}" with an array`)}}const revision=options.revision||ID.generate();let mainNode={type:currentRow&¤tRow.type===VALUE_TYPES.ARRAY?VALUE_TYPES.ARRAY:VALUE_TYPES.OBJECT,value:{}};const childNodeValues={};if(value instanceof Array){mainNode.type=VALUE_TYPES.ARRAY;const obj={};for(let i=0;i{const val=value[key];delete mainNode.value[key];if(val===null){return}else if(typeof val==="undefined"){if(this.settings.removeVoidProperties===true){delete value[key];return}else{throw new Error(`Property "${key}" has invalid value. Cannot store undefined values. Set removeVoidProperties option to true to automatically remove undefined properties`)}}if(this.valueFitsInline(val)){mainNode.value[key]=val}else{childNodeValues[key]=val}})}const isArray=mainNode.type===VALUE_TYPES.ARRAY;if(currentRow){this.debug.log(`Node "/${path}" is being ${options.merge?"updated":"overwritten"}`.cyan);if(currentIsObjectOrArray||newIsObjectOrArray){const pathInfo=PathInfo.get(path);const keys=[];let checkExecuted=false;const includeChildCheck=childPath=>{checkExecuted=true;if(!pathInfo.isParentOf(childPath)){throw new Error(`"${childPath}" is not a child of "${path}" - childrenOf must only check and return paths that are children`)}return true};const addChildPath=childPath=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} childrenOf did not call checkCallback before addCallback`)}const key=PathInfo.get(childPath).key;keys.push(key.toString());return true};await transaction.childrenOf(path,{metadata:false,value:false},includeChildCheck,addChildPath);children.current=children.current.concat(keys);if(newIsObjectOrArray){if(options&&options.merge){children.new=children.current.slice()}Object.keys(value).forEach(key=>{if(!children.new.includes(key)){children.new.push(key)}})}const changes={insert:children.new.filter(key=>!children.current.includes(key)),update:[],delete:options&&options.merge?Object.keys(value).filter(key=>value[key]===null):children.current.filter(key=>!children.new.includes(key))};changes.update=children.new.filter(key=>children.current.includes(key)&&!changes.delete.includes(key));if(isArray&&options.merge&&(changes.insert.length>0||changes.delete.length>0)){const newArrayKeys=changes.update.concat(changes.insert);const isExhaustive=newArrayKeys.every((k,index,arr)=>arr.includes(index.toString()));if(!isExhaustive){throw new Error(`Elements cannot be inserted beyond, or removed before the end of an array. Rewrite the whole array at path "${path}" or change your schema to use an object collection instead`)}}const writePromises=Object.keys(childNodeValues).map(key=>{if(isArray){key=parseInt(key)}const childPath=pathInfo.childPath(key);const childValue=childNodeValues[key];return this._writeNode(childPath,childValue,{transaction:transaction,revision:revision,merge:false})});const movingNodes=keys.filter(key=>key in mainNode.value);const deleteDedicatedKeys=changes.delete.concat(movingNodes);const deletePromises=deleteDedicatedKeys.map(key=>{if(isArray){key=parseInt(key)}const childPath=pathInfo.childPath(key);return this._deleteNode(childPath,{transaction:transaction})});const promises=writePromises.concat(deletePromises);await Promise.all(promises)}return await this._storeNode(path,{type:mainNode.type,value:mainNode.value,revision:currentRow.revision,revision_nr:currentRow.revision_nr+1,created:currentRow.created,modified:Date.now()},{transaction:transaction})}else{this.debug.log(`Node "/${path}" is being created`.cyan);if(isArray){const arrayKeys=Object.keys(mainNode.value).concat(Object.keys(childNodeValues));const isExhaustive=arrayKeys.every((k,index,arr)=>arr.includes(index.toString()));if(!isExhaustive){throw new Error(`Cannot store arrays with missing entries`)}}const promises=Object.keys(childNodeValues).map(key=>{if(isArray){key=parseInt(key)}const childPath=PathInfo.getChildPath(path,key);const childValue=childNodeValues[key];return this._writeNode(childPath,childValue,{transaction:transaction,revision:revision,merge:false})});const p=this._storeNode(path,{type:mainNode.type,value:mainNode.value,revision:revision,revision_nr:1,created:Date.now(),modified:Date.now()},{transaction:transaction});promises.push(p);return Promise.all(promises)}}async _deleteNode(path,options){const pathInfo=PathInfo.get(path);this.debug.log(`Node "/${path}" is being deleted`.cyan);const deletePaths=[path];let checkExecuted=false;const includeDescendantCheck=descPath=>{checkExecuted=true;if(!pathInfo.isAncestorOf(descPath)){throw new Error(`"${descPath}" is not a descendant of "${path}" - descendantsOf must only check and return paths that are descendants`)}return true};const addDescendant=descPath=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} descendantsOf did not call checkCallback before addCallback`)}deletePaths.push(descPath);return true};const transaction=options.transaction;await transaction.descendantsOf(path,{metadata:false,value:false},includeDescendantCheck,addDescendant);this.debug.log(`Nodes ${deletePaths.map(p=>`"/${p}"`).join(",")} are being deleted`.cyan);return transaction.removeMultiple(deletePaths)}getChildren(path,options){options=options||{};var callback;const generator={next(valueCallback){callback=valueCallback;return start()}};const start=async()=>{const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:false});try{let canceled=false;await(async()=>{let node=await this._readNode(path,{transaction:transaction});if(!node){throw new NodeNotFoundError(`Node "/${path}" does not exist`)}if(![VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(node.type)){return}const isArray=node.type===VALUE_TYPES.ARRAY;const value=node.value;let keys=Object.keys(value);if(options.keyFilter){keys=keys.filter(key=>options.keyFilter.includes(key))}const pathInfo=PathInfo.get(path);keys.length>0&&keys.every(key=>{let child=this._getTypeFromStoredValue(value[key]);const info=new CustomStorageNodeInfo({path:pathInfo.childPath(key),key:isArray?null:key,index:isArray?key:null,type:child.type,address:null,exists:true,value:child.value,revision:node.revision,revision_nr:node.revision_nr,created:node.created,modified:node.modified});canceled=callback(info)===false;return!canceled});if(canceled){return}let checkExecuted=false;const includeChildCheck=childPath=>{checkExecuted=true;if(!pathInfo.isParentOf(childPath)){throw new Error(`"${childPath}" is not a child of "${path}" - childrenOf must only check and return paths that are children`)}if(options.keyFilter){const key=PathInfo.get(childPath).key;return options.keyFilter.includes(key)}return true};const addChildNode=(childPath,node)=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} childrenOf did not call checkCallback before addCallback`)}const key=PathInfo.get(childPath).key;const info=new CustomStorageNodeInfo({path:childPath,type:node.type,key:isArray?null:key,index:isArray?key:null,address:new CustomStorageNodeAddress(childPath),exists:true,value:null,revision:node.revision,revision_nr:node.revision_nr,created:new Date(node.created),modified:new Date(node.modified)});canceled=callback(info)===false;return!canceled};await transaction.childrenOf(path,{metadata:true,value:false},includeChildCheck,addChildNode)})();if(!options.transaction){await transaction.commit()}return canceled}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}};return generator}async getNode(path,options){options=options||{};const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:false});try{const node=await(async()=>{const filtered=options.include||options.exclude||options.child_objects===false;const pathInfo=PathInfo.get(path);const targetNode=await this._readNode(path,{transaction:transaction});if(!targetNode){if(path===""){return{value:null}}const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);let parentNode=await this._readNode(pathInfo.parentPath,{transaction:transaction});if(parentNode&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(parentNode.type)&&pathInfo.key in parentNode){const childValueInfo=this._getTypeFromStoredValue(parentNode.value[pathInfo.key]);return{revision:parentNode.revision,revision_nr:parentNode.revision_nr,created:parentNode.created,modified:parentNode.modified,type:childValueInfo.type,value:childValueInfo.value}}return{value:null}}const includeCheck=options.include?new RegExp("^"+options.include.map(p=>"(?:"+p.replace(/\*/g,"[^/\\[]+")+")").join("|")+"(?:$|[/\\[])"):null;const excludeCheck=options.exclude?new RegExp("^"+options.exclude.map(p=>"(?:"+p.replace(/\*/g,"[^/\\[]+")+")").join("|")+"(?:$|[/\\[])"):null;let checkExecuted=false;const includeDescendantCheck=descPath=>{checkExecuted=true;if(!pathInfo.isAncestorOf(descPath)){throw new Error(`"${descPath}" is not a descendant of "${path}" - descendantsOf must only check and return paths that are descendants`)}if(!filtered){return true}let checkPath=descPath.slice(path.length);if(checkPath[0]==="/"){checkPath=checkPath.slice(1)}let include=(includeCheck?includeCheck.test(checkPath):true)&&(excludeCheck?!excludeCheck.test(checkPath):true);if(include&&options.child_objects===false&&(pathInfo.isParentOf(descPath)&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(childNode.type)||PathInfo.getPathKeys(descPath).length>pathInfo.pathKeys.length+1)){include=false}return include};const descRows=[];const addDescendant=(descPath,node)=>{if(!checkExecuted){throw new Error(`${this._customImplementation.info} descendantsOf did not call checkCallback before addCallback`)}this._processReadNodeValue(node);node.path=descPath;descRows.push(node);return true};await transaction.descendantsOf(path,{metadata:true,value:true},includeDescendantCheck,addDescendant);this.debug.log(`Read node "/${path}" and ${filtered?"(filtered) ":""}descendants from ${descRows.length+1} records`.magenta);const result=targetNode;const objectToArray=obj=>{const arr=[];Object.keys(obj).forEach(key=>{let index=parseInt(key);arr[index]=obj[index]});return arr};if(targetNode.type===VALUE_TYPES.ARRAY){result.value=objectToArray(result.value)}if(targetNode.type===VALUE_TYPES.OBJECT||targetNode.type===VALUE_TYPES.ARRAY){const targetPathKeys=PathInfo.getPathKeys(path);let value=targetNode.value;for(let i=0;i{console.assert(!(childKey in parent[key]),"child key is in parent value already?! HOW?!");parent[key][childKey]=nodeValue[childKey]})}else{parent[key]=nodeValue}parent=parent[key]}}}else if(descRows.length>0){throw new Error(`multiple records found for non-object value!`)}if(options.child_objects===false){Object.keys(result.value).forEach(key=>{if(typeof result.value[key]==="object"&&result.value[key].constructor===Object){console.assert(Object.keys(result.value[key]).length===0);delete result.value[key]}})}if(options.exclude){const process=(obj,keys)=>{if(typeof obj!=="object"){return}const key=keys[0];if(key==="*"){Object.keys(obj).forEach(k=>{process(obj[k],keys.slice(1))})}else if(keys.length>1){key in obj&&process(obj[key],keys.slice(1))}else{delete obj[key]}};options.exclude.forEach(path=>{const checkKeys=PathInfo.getPathKeys(path);process(result.value,checkKeys)})}return result})();if(!options.transaction){await transaction.commit()}return node}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async getNodeInfo(path,options){options=options||{};const pathInfo=PathInfo.get(path);const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:true});try{const node=await this._readNode(path,{transaction:transaction});const info=new CustomStorageNodeInfo({path:path,key:typeof pathInfo.key==="string"?pathInfo.key:null,index:typeof pathInfo.key==="number"?pathInfo.key:null,type:node?node.type:0,exists:node!==null,address:node?new CustomStorageNodeAddress(path):null,created:node?new Date(node.created):null,modified:node?new Date(node.modified):null,revision:node?node.revision:null,revision_nr:node?node.revision_nr:null});if(!node&&path!==""){const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);const parent=await this._readNode(pathInfo.parentPath,{transaction:transaction});if(parent&&[VALUE_TYPES.OBJECT,VALUE_TYPES.ARRAY].includes(parent.type)&&pathInfo.key in parent.value){info.exists=true;info.value=parent.value[pathInfo.key];info.address=null;info.type=parent.type;info.created=new Date(parent.created);info.modified=new Date(parent.modified);info.revision=parent.revision;info.revision_nr=parent.revision_nr}else{info.address=null}}if(!options.transaction){await transaction.commit()}return info}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async removeNode(path,options){if(path===""){return Promise.reject(new Error(`Cannot remove the root node`))}options=options||{};const pathInfo=PathInfo.get(path);const transaction=options.transaction||this._customImplementation.getTransaction({path:path,write:true});try{await this.updateNode(pathInfo.parentPath,{[pathInfo.key]:null},{transaction:transaction});if(!options.transaction){await transaction.commit()}}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async setNode(path,value,options){const pathInfo=PathInfo.get(path);options=options||{};const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:true});try{if(path===""){if(value===null||typeof value!=="object"||value instanceof Array||value instanceof ArrayBuffer||"buffer"in value&&value.buffer instanceof ArrayBuffer){throw new Error(`Invalid value for root node: ${value}`)}await this._writeNodeWithTracking("",value,{merge:false,transaction:transaction})}else if(typeof options.assert_revision!=="undefined"){const info=await this.getNodeInfo(path,{transaction:transaction});if(info.revision!==options.assert_revision){throw new NodeRevisionError(`revision '${info.revision}' does not match requested revision '${options.assert_revision}'`)}if(info.address&&info.address.path===path&&!this.valueFitsInline(value)){await this._writeNodeWithTracking(path,value,{merge:false,transaction:transaction})}else{const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this._writeNodeWithTracking(pathInfo.parentPath,{[pathInfo.key]:value},{merge:true,transaction:transaction})}}else{const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this.updateNode(pathInfo.parentPath,{[pathInfo.key]:value},{transaction:transaction})}if(!options.transaction){await transaction.commit()}}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}async updateNode(path,updates,options){if(typeof updates!=="object"){throw new Error(`invalid updates argument`)}options=options||{};const transaction=options.transaction||await this._customImplementation.getTransaction({path:path,write:true});try{const nodeInfo=await this.getNodeInfo(path,{transaction:transaction});const pathInfo=PathInfo.get(path);if(nodeInfo.exists&&nodeInfo.address&&nodeInfo.address.path===path){await this._writeNodeWithTracking(path,updates,{transaction:transaction,merge:true})}else if(nodeInfo.exists){const pathInfo=PathInfo.get(path);const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this._writeNodeWithTracking(pathInfo.parentPath,{[pathInfo.key]:updates},{transaction:transaction,merge:true})}else{const lockPath=await transaction.moveToParentPath(pathInfo.parentPath);console.assert(lockPath===pathInfo.parentPath,`transaction.moveToParentPath() did not move to the right parent path of "${path}"`);await this.updateNode(pathInfo.parentPath,{[pathInfo.key]:updates},{transaction:transaction})}if(!options.transaction){await transaction.commit()}}catch(err){if(!options.transaction){await transaction.rollback(err)}throw err}}}module.exports={CustomStorageNodeAddress:CustomStorageNodeAddress,CustomStorageNodeInfo:CustomStorageNodeInfo,CustomStorage:CustomStorage,CustomStorageSettings:CustomStorageSettings,CustomStorageHelpers:CustomStorageHelpers,CustomStorageTransaction:CustomStorageTransaction,ICustomStorageNodeMetaData:ICustomStorageNodeMetaData,ICustomStorageNode:ICustomStorageNode}},{"./node-info":34,"./node-lock":35,"./node-value-types":36,"./storage":39,"acebase-core":11}],39:[function(require,module,exports){(function(process){const{Utils:Utils,DebugLogger:DebugLogger,PathInfo:PathInfo,ID:ID,PathReference:PathReference,ascii85:ascii85}=require("acebase-core");const{NodeLocker:NodeLocker}=require("./node-lock");const{VALUE_TYPES:VALUE_TYPES,getValueTypeName:getValueTypeName}=require("./node-value-types");const{NodeInfo:NodeInfo}=require("./node-info");const{EventEmitter:EventEmitter}=require("events");const{cloneObject:cloneObject,compareValues:compareValues,getChildValues:getChildValues,encodeString:encodeString}=Utils;const colors=require("colors");class NodeNotFoundError extends Error{}class NodeRevisionError extends Error{}class ClusterSettings{constructor(settings){settings=settings||{};this.enabled=settings.enabled===true;this.isMaster=settings.isMaster===true;this.master=this.isMaster?null:settings.master;this.workers=this.isMaster?settings.workers:null}}class ClusterManager extends EventEmitter{constructor(settings){super();this.settings=new ClusterSettings(settings);if(!settings.enabled){}else if(settings.isMaster){settings.workers.forEach(worker=>{worker.on("message",data=>{const{id:id,request:request}=data;if(typeof request==="object"&&request.type==="ping"){worker.send({id:id,result:"pong"})}else{const reply=result=>{worker.send({id:id,result:result})};const broadcast=msg=>{console.assert(!("id"in msg),"message to broadcast cannot have id property, it will confuse workers because they think it is a reply to their request");settings.workers.forEach(otherWorker=>{if(otherWorker!==worker){otherWorker.send(msg)}})};this.emit("worker_request",{request:request,reply:reply,broadcast:broadcast})}})});this.request=msg=>{throw new Error(`request can only be called by worker processes!`)}}else{const master=settings.master;const requests={};this.request=msg=>{return new Promise((resolve,reject)=>{const id=ID.generate();requests[id]=resolve;master.send({id:id,request:msg})})};master.on("message",data=>{if(typeof data.id!=="undefined"){let resolve=requests[data.id];delete requests[data.id];resolve(data.result)}else{this.emit("master_notification",data)}});this.request({type:"ping"}).then(result=>{console.log(`PING master process result: ${result}`)})}}get isMaster(){return this.settings.isMaster}get enabled(){return this.settings.enabled}}class StorageSettings{constructor(settings){settings=settings||{};this.maxInlineValueSize=typeof settings.maxInlineValueSize==="number"?settings.maxInlineValueSize:50;this.removeVoidProperties=settings.removeVoidProperties===true;this.cluster=new ClusterSettings(settings.cluster);this.path=settings.path||".";if(this.path.endsWith("/")){this.path=this.path.slice(0,-1)}this.logLevel=settings.logLevel||"log";this.info=settings.info||"realtime database"}}class Storage extends EventEmitter{constructor(name,settings){super();this.name=name;this.settings=settings;this.debug=new DebugLogger(settings.logLevel,`[${name}]`);colors.setTheme({art:["magenta","bold"],intro:["dim"]});const logo=" ___ ______ ".art+"\n"+" / _ \\ | ___ \\ ".art+"\n"+" / /_\\ \\ ___ ___| |_/ / __ _ ___ ___ ".art+"\n"+" | _ |/ __/ _ \\ ___ \\/ _` / __|/ _ \\".art+"\n"+" | | | | (_| __/ |_/ / (_| \\__ \\ __/".art+"\n"+" \\_| |_/\\___\\___\\____/ \\__,_|___/\\___|".art+"\n"+(settings.info?"".padStart(40-settings.info.length," ")+settings.info.magenta+"\n":"");this.debug.write(logo);this.nodeCache={find(path){return null},update(path,info){}};this.nodeLocker=new NodeLocker;this.cluster=new ClusterManager(settings.cluster);const{DataIndex:DataIndex,ArrayIndex:ArrayIndex,FullTextIndex:FullTextIndex,GeoIndex:GeoIndex}=require("./data-index");const _indexes=[];const storage=this;this.indexes={get supported(){const pfs=require("./promise-fs");return pfs&&pfs.hasFileSystem},create(path,key,options={rebuild:false,type:undefined,include:undefined}){path=path.replace(/\/\*$/,"");const rebuild=options&&options.rebuild===true;const indexType=options&&options.type||"normal";let includeKeys=options&&options.include||[];if(typeof includeKeys==="string"){includeKeys=[includeKeys]}const existingIndex=_indexes.find(index=>index.path===path&&index.key===key&&index.type===indexType&&index.includeKeys.length===includeKeys.length&&index.includeKeys.every((key,index)=>includeKeys[index]===key));if(existingIndex&&rebuild!==true){storage.debug.log(`Index on "/${path}/*/${key}" already exists`.inverse);return Promise.resolve(existingIndex)}const index=existingIndex||(()=>{switch(indexType){case"array":return new ArrayIndex(storage,path,key,{include:options.include,config:options.config});case"fulltext":return new FullTextIndex(storage,path,key,{include:options.include,config:options.config});case"geo":return new GeoIndex(storage,path,key,{include:options.include,config:options.config});default:return new DataIndex(storage,path,key,{include:options.include,config:options.config})}})();if(!existingIndex){_indexes.push(index)}return index.build().then(()=>{return index}).catch(err=>{storage.debug.error(`Index build on "/${path}/*/${key}" failed: ${err.message} (code: ${err.code})`.red);if(!existingIndex){_indexes.splice(_indexes.indexOf(index),1)}throw err})},get(path,key=null){return _indexes.filter(index=>index.path===path&&(key===null||key===index.key))},getAll(targetPath,options={parentPaths:true,childPaths:true}){const pathKeys=PathInfo.getPathKeys(targetPath);return _indexes.filter(index=>{const indexKeys=PathInfo.getPathKeys(index.path+"/*");if(options.parentPaths&&indexKeys.every((key,i)=>{return key==="*"||pathKeys[i]===key})&&[index.key].concat(...index.includeKeys).includes(pathKeys[indexKeys.length])){return true}else if(indexKeys.length{return[key,"*"].includes(indexKeys[i])})})},list(){return _indexes.slice()},load(){_indexes.splice(0);const pfs=require("./promise-fs");if(!pfs||!pfs.readdir){return Promise.resolve()}return pfs.readdir(`${storage.settings.path}/${storage.name}.acebase`).then(files=>{const promises=[];files.forEach(fileName=>{if(fileName.endsWith(".idx")){const p=DataIndex.readFromFile(storage,fileName).then(index=>{_indexes.push(index)}).catch(err=>{storage.debug.error(err)});promises.push(p)}});return Promise.all(promises)}).catch(err=>{if(err.code!=="ENOENT"){storage.debug.error(err)}})}};const _subs={};const _supportedEvents=["value","child_added","child_changed","child_removed"];_supportedEvents.push(..._supportedEvents.map(event=>`notify_${event}`));this.subscriptions={add(path,type,callback){if(_supportedEvents.indexOf(type)<0){throw new TypeError(`Invalid event type "${type}"`)}let pathSubs=_subs[path];if(!pathSubs){pathSubs=_subs[path]=[]}pathSubs.push({created:Date.now(),type:type,callback:callback})},remove(path,type=undefined,callback=undefined){let pathSubs=_subs[path];if(!pathSubs){return}while(true){const i=pathSubs.findIndex(ps=>(type?ps.type===type:true)&&(callback?ps.callback===callback:true));if(i<0){break}pathSubs.splice(i,1)}},hasValueSubscribersForPath(path){const valueNeeded=this.getValueSubscribersForPath(path);return!!valueNeeded},getValueSubscribersForPath(path){const pathInfo=new PathInfo(path);const valueSubscribers=[];Object.keys(_subs).forEach(subscriptionPath=>{if(pathInfo.equals(subscriptionPath)||pathInfo.isDescendantOf(subscriptionPath)){let pathSubs=_subs[subscriptionPath];const eventPath=PathInfo.fillVariables(subscriptionPath,path);pathSubs.forEach(sub=>{let dataPath=null;if(sub.type==="value"||sub.type==="notify_value"){dataPath=eventPath}else if((sub.type==="child_changed"||sub.type==="notify_child_changed")&&path!==eventPath){let childKey=PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}else if(["child_added","child_removed","notify_child_added","notify_child_removed"].includes(sub.type)&&pathInfo.isChildOf(eventPath)){let childKey=PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}if(dataPath!==null&&valueSubscribers.findIndex(s=>s.type===sub.type&&s.path===eventPath)<0){valueSubscribers.push({type:sub.type,eventPath:eventPath,dataPath:dataPath,subscriptionPath:subscriptionPath})}})}});return valueSubscribers},getAllSubscribersForPath(path){const pathInfo=PathInfo.get(path);const subscribers=[];Object.keys(_subs).forEach(subscriptionPath=>{if(pathInfo.equals(subscriptionPath)||pathInfo.isDescendantOf(subscriptionPath)||pathInfo.isAncestorOf(subscriptionPath)){let pathSubs=_subs[subscriptionPath];const eventPath=PathInfo.fillVariables(subscriptionPath,path);pathSubs.forEach(sub=>{let dataPath=null;if(sub.type==="value"||sub.type==="notify_value"){dataPath=eventPath}else if(sub.type==="child_changed"||sub.type==="notify_child_changed"){let childKey=path===eventPath||pathInfo.isAncestorOf(eventPath)?"*":PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}else if(["child_added","child_removed","notify_child_added","notify_child_removed"].includes(sub.type)&&(pathInfo.isChildOf(eventPath)||path===eventPath||pathInfo.isAncestorOf(eventPath))){let childKey=path===eventPath||pathInfo.isAncestorOf(eventPath)?"*":PathInfo.getPathKeys(path.slice(eventPath.length).replace(/^\//,""))[0];dataPath=PathInfo.getChildPath(eventPath,childKey)}if(dataPath!==null){subscribers.push({type:sub.type,eventPath:eventPath,dataPath:dataPath,subscriptionPath:subscriptionPath})}})}});return subscribers},trigger(event,path,dataPath,oldValue,newValue){const pathSubscriptions=_subs[path]||[];pathSubscriptions.filter(sub=>sub.type===event).forEach(sub=>{sub.callback(null,dataPath,newValue,oldValue)})}}}get path(){return`${this.settings.path}/${this.name}.acebase`}valueFitsInline(value){const encoding="utf8";if(typeof value==="number"||typeof value==="boolean"||value instanceof Date){return true}else if(typeof value==="string"){if(value.length>this.settings.maxInlineValueSize){return false}const encoded=encodeString(value);return encoded.lengththis.settings.maxInlineValueSize){return false}const encoded=encodeString(value.path);return encoded.length0){hasValueSubscribers=true;let eventPaths=valueSubscribers.map(sub=>{return{path:sub.dataPath,keys:PathInfo.getPathKeys(sub.dataPath)}}).sort((a,b)=>{if(a.keys.lengthb.keys.length)return 1;return 0});let first=eventPaths[0];topEventPath=first.path;if(valueSubscribers.filter(sub=>sub.dataPath===topEventPath).every(sub=>sub.type.startsWith("notify_"))){hasValueSubscribers=false}topEventPath=PathInfo.fillVariables(topEventPath,path)}const writeNode=()=>{if(typeof options._customWriteFunction==="function"){return options._customWriteFunction()}return this._writeNode(path,value,options)};const indexes=this.indexes.getAll(path,{childPaths:true,parentPaths:true}).map(index=>({index:index,keys:PathInfo.getPathKeys(index.path)})).sort((a,b)=>{if(a.keys.lengthb.keys.length){return 1}return 0}).map(obj=>obj.index);if(eventSubscriptions.length===0&&indexes.length===0){return writeNode()}let keysFilter=[];if(indexes.length>0){indexes.sort((a,b)=>{if(typeof a._pathKeys==="undefined"){a._pathKeys=PathInfo.getPathKeys(a.path)}if(typeof b._pathKeys==="undefined"){b._pathKeys=PathInfo.getPathKeys(b.path)}if(a._pathKeys.lengthb._pathKeys.length)return 1;return 0});const topIndex=indexes[0];let topIndexPath=topIndex.path===path?path:PathInfo.fillVariables(`${topIndex.path}/*`,path);if(topIndexPath.lengthindex.path===topIndex.path).forEach(index=>{let keys=[index.key].concat(index.includeKeys);keys.forEach(key=>!keysFilter.includes(key)&&keysFilter.push(key))})}}if(!hasValueSubscribers&&options.merge===true&&keysFilter.length===0){keysFilter=Object.keys(value);if(topEventPath!==path){let trailPath=path.slice(topEventPath.length);keysFilter=keysFilter.map(key=>`${trailPath}/${key}`)}}return this.getNodeInfo(topEventPath,{transaction:transaction,tid:tid}).then(eventNodeInfo=>{if(!eventNodeInfo.exists){return null}let valueOptions={transaction:transaction,tid:tid};if(keysFilter.length>0){valueOptions.include=keysFilter}if(topEventPath===""&&typeof valueOptions.include==="undefined"){this.debug.warn(`WARNING: One or more value event listeners on the root node are causing the entire database value to be read to facilitate change tracking. Using "value", "notify_value", "child_changed" and "notify_child_changed" events on the root node are a bad practice because of the significant performance impact`)}return this.getNodeValue(topEventPath,valueOptions)}).then(currentValue=>{topEventData=currentValue;return writeNode()}).then(result=>{let newTopEventData=cloneObject(topEventData);if(newTopEventData===null){newTopEventData=path===topEventPath?value:{}}let modifiedData=newTopEventData;if(path!==topEventPath){let trailPath=path.slice(topEventPath.length).replace(/^\//,"");let trailKeys=PathInfo.getPathKeys(trailPath);while(trailKeys.length>0){let childKey=trailKeys.shift();if(!options.merge&&trailKeys.length===0){modifiedData[childKey]=value}else{if(!(childKey in modifiedData)){modifiedData[childKey]={}}modifiedData=modifiedData[childKey]}}}if(options.merge){Object.keys(value).forEach(key=>{let newValue=value[key];if(newValue!==null){modifiedData[key]=newValue}else{delete modifiedData[key]}})}else if(path===topEventPath){newTopEventData=modifiedData=value}const indexUpdates=[];indexes.map(index=>({index:index,keys:PathInfo.getPathKeys(index.path)})).sort((a,b)=>{if(a.keys.lengthb.keys.length){return-1}return 0}).forEach(({index:index})=>{let pathKeys=PathInfo.getPathKeys(topEventPath);let indexPathKeys=PathInfo.getPathKeys(index.path+"/*");let trailKeys=indexPathKeys.slice(pathKeys.length);let oldValue=topEventData;let newValue=newTopEventData;if(trailKeys.length===0){console.assert(pathKeys.length===indexPathKeys.length,"check logic");const p=index.handleRecordUpdate(topEventPath,oldValue,newValue);indexUpdates.push(p);return}const getAllIndexUpdates=(path,oldValue,newValue)=>{if(oldValue===null&&newValue===null){return[]}let pathKeys=PathInfo.getPathKeys(path);let indexPathKeys=PathInfo.getPathKeys(index.path+"/*");let trailKeys=indexPathKeys.slice(pathKeys.length);if(trailKeys.length===0){console.assert(pathKeys.length===indexPathKeys.length,"check logic");return[{path:path,oldValue:oldValue,newValue:newValue}]}let results=[];let trailPath="";while(trailKeys.length>0){let subKey=trailKeys.shift();if(subKey==="*"){let allKeys=oldValue===null?[]:Object.keys(oldValue);newValue!==null&&Object.keys(newValue).forEach(key=>{if(allKeys.indexOf(key)<0){allKeys.push(key)}});allKeys.forEach(key=>{let childPath=PathInfo.getChildPath(trailPath,key);let childValues=getChildValues(key,oldValue,newValue);let subTrailPath=PathInfo.getChildPath(path,childPath);let childResults=getAllIndexUpdates(subTrailPath,childValues.oldValue,childValues.newValue);results=results.concat(childResults)});break}else{let values=getChildValues(subKey,oldValue,newValue);oldValue=values.oldValue;newValue=values.newValue;if(oldValue===null&&newValue===null){break}trailPath=PathInfo.getChildPath(trailPath,subKey)}}return results};let results=getAllIndexUpdates(topEventPath,oldValue,newValue);results.forEach(result=>{const p=index.handleRecordUpdate(result.path,result.oldValue,result.newValue);indexUpdates.push(p)})});const callSubscriberWithValues=(sub,oldValue,newValue,variables=[])=>{let trigger=true;let type=sub.type;if(type.startsWith("notify_")){type=type.slice("notify_".length)}if(type==="child_changed"&&(oldValue===null||newValue===null)){trigger=false}else if(type==="value"||type==="child_changed"){let changes=compareValues(oldValue,newValue);trigger=changes!=="identical"}else if(type==="child_added"){trigger=oldValue===null&&newValue!==null}else if(type==="child_removed"){trigger=oldValue!==null&&newValue===null}let dataPath=sub.dataPath;variables.forEach((variable,i)=>{const safeVarName=variable.name==="*"?"\\*":variable.name.replace("$","\\$");dataPath=dataPath.replace(new RegExp(`(^|/)${safeVarName}([/[]|$)`),`$1${variable.value}$2`)});trigger&&this.subscriptions.trigger(sub.type,sub.subscriptionPath,dataPath,oldValue,newValue)};const triggerAllEvents=()=>{eventSubscriptions.map(sub=>{const keys=PathInfo.getPathKeys(sub.dataPath);return{sub:sub,keys:keys}}).sort((a,b)=>{if(a.keys.lengthb.keys.length){return-1}return 0}).forEach(({sub:sub})=>{const process=(currentPath,oldValue,newValue,variables=[])=>{let trailPath=sub.dataPath.slice(currentPath.length).replace(/^\//,"");let trailKeys=PathInfo.getPathKeys(trailPath);while(trailKeys.length>0){let subKey=trailKeys.shift();if(typeof subKey==="string"&&(subKey==="*"||subKey[0]==="$")){let allKeys=oldValue===null?[]:Object.keys(oldValue);newValue!==null&&Object.keys(newValue).forEach(key=>{if(allKeys.indexOf(key)<0){allKeys.push(key)}});allKeys.forEach(key=>{const childValues=getChildValues(key,oldValue,newValue);const vars=variables.concat({name:subKey,value:key});if(trailKeys.length===0){callSubscriberWithValues(sub,childValues.oldValue,childValues.newValue,vars)}else{process(`${currentPath}/${subKey}`,childValues.oldValue,childValues.newValue,vars)}});return}else{currentPath=PathInfo.getChildPath(currentPath,subKey);let childValues=getChildValues(subKey,oldValue,newValue);oldValue=childValues.oldValue;newValue=childValues.newValue}}callSubscriberWithValues(sub,oldValue,newValue,variables)};process(topEventPath,topEventData,newTopEventData)})};if(options.waitForIndexUpdates===false){indexUpdates.splice(0)}return Promise.all(indexUpdates).then(()=>{process.nextTick(triggerAllEvents);return result})})}getChildren(path,options={keyFilter:undefined,tid:undefined}){throw new Error(`This method must be implemented by subclass`)}getNodeValue(path,options={include:undefined,exclude:undefined,child_objects:true,tid:undefined}){return this.getNode(path,options).then(node=>{return node.value})}getNode(path,options={include:undefined,exclude:undefined,child_objects:true,tid:undefined}){throw new Error(`This method must be implemented by subclass`)}getNodeInfo(path,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}removeNode(path,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}setNode(path,value,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}updateNode(path,updates,options={tid:undefined}){throw new Error(`This method must be implemented by subclass`)}transactNode(path,callback,options={no_lock:false}){let checkRevision;const tid=ID.generate();const lockPromise=options&&options.no_lock===true?Promise.resolve({tid:tid,release(){}}):this.nodeLocker.lock(path,tid,true,"transactNode");return lockPromise.then(lock=>{let changed=false,changeCallback=(err,path)=>{changed=true};if(options&&options.no_lock){this.subscriptions.add(path,"notify_value",changeCallback)}return this.getNode(path,{tid:tid}).then(node=>{checkRevision=node.revision;let newValue;try{newValue=callback(node.value)}catch(err){this.debug.error(`Error in transaction callback: ${err.message}`)}if(newValue instanceof Promise){return newValue.catch(err=>{this.debug.error(`Error in transaction callback: ${err.message}`)})}return newValue}).then(newValue=>{if(typeof newValue==="undefined"){return}if(options&&options.no_lock){this.subscriptions.remove(path,"notify_value",changeCallback)}if(changed){return Promise.reject(new NodeRevisionError(`Node changed`))}return this.setNode(path,newValue,{assert_revision:checkRevision,tid:lock.tid})}).then(result=>{lock.release();return result}).catch(err=>{lock.release();if(err instanceof NodeRevisionError){console.warn(`node value changed, running again. Error: ${err.message}`);return this.transactNode(path,callback,options)}else{throw err}})})}matchNode(path,criteria,options={tid:undefined}){const tid=options&&options.tid||ID.generate();const checkNode=(path,criteria)=>{if(criteria.length===0){return Promise.resolve(true)}const criteriaKeys=criteria.reduce((keys,cr)=>{if(keys.indexOf(cr.key)<0){keys.push(cr.key)}return keys},[]);const unseenKeys=criteriaKeys.slice();let isMatch=true;let delayedMatchPromises=[];return this.getChildren(path,{tid:tid,keyFilter:criteriaKeys}).next(childInfo=>{unseenKeys.includes(childInfo.key)&&unseenKeys.splice(unseenKeys.indexOf(childInfo.key),1);const keyCriteria=criteria.filter(cr=>cr.key===childInfo.key).map(cr=>({op:cr.op,compare:cr.compare}));const result=checkChild(childInfo,keyCriteria);isMatch=result.isMatch;delayedMatchPromises.push(...result.promises);if(!isMatch||unseenKeys.length===0){return false}}).then(()=>{if(isMatch){return Promise.all(delayedMatchPromises).then(results=>{isMatch=results.every(res=>res.isMatch)})}}).then(()=>{if(!isMatch){return false}isMatch=unseenKeys.every(key=>{const child=new NodeInfo({key:key,exists:false});const keyCriteria=criteria.filter(cr=>cr.key===key).map(cr=>({op:cr.op,compare:cr.compare}));const result=checkChild(child,keyCriteria);return result.isMatch});return isMatch}).catch(err=>{this.debug.error(`Error matching on "${path}": `,err);throw err})};const checkChild=(child,criteria)=>{const promises=[];const isMatch=criteria.every(f=>{let proceed=true;if(f.op==="!exists"||f.op==="=="&&(typeof f.compare==="undefined"||f.compare===null)){proceed=!child.exists}else if(f.op==="exists"||f.op==="!="&&(typeof f.compare==="undefined"||f.compare===null)){proceed=child.exists}else if(!child.exists){proceed=false}else{if(child.address){if(child.valueType===VALUE_TYPES.OBJECT&&["has","!has"].indexOf(f.op)>=0){const op=f.op==="has"?"exists":"!exists";const p=checkNode(child.path,[{key:f.compare,op:op}]).then(isMatch=>{return{key:child.key,isMatch:isMatch}});promises.push(p);proceed=true}else if(child.valueType===VALUE_TYPES.ARRAY&&["contains","!contains"].indexOf(f.op)>=0){const p=this.getNodeValue(child.path,{tid:tid}).then(arr=>{const isMatch=f.op==="contains"?f.compare instanceof Array?f.compare.every(val=>arr.includes(val)):arr.includes(f.compare):f.compare instanceof Array?!f.compare.some(val=>arr.includes(val)):!arr.includes(f.compare);return{key:child.key,isMatch:isMatch}});promises.push(p);proceed=true}else if(child.valueType===VALUE_TYPES.STRING){const p=this.getNodeValue(child.path,{tid:tid}).then(val=>{return{key:child.key,isMatch:this.test(val,f.op,f.compare)}});promises.push(p);proceed=true}else{proceed=false}}else if(child.type===VALUE_TYPES.OBJECT&&["has","!has"].indexOf(f.op)>=0){const has=f.compare in child.value;proceed=has&&f.op==="has"||!has&&f.op==="!has"}else if(child.type===VALUE_TYPES.ARRAY&&["contains","!contains"].indexOf(f.op)>=0){const contains=child.value.indexOf(f.compare)>=0;proceed=contains&&f.op==="contains"||!contains&&f.op==="!contains"}else{const ret=this.test(child.value,f.op,f.compare);if(ret instanceof Promise){promises.push(ret);ret=true}proceed=ret}}return proceed});return{isMatch:isMatch,promises:promises}};return checkNode(path,criteria)}test(val,op,compare){if(op==="<"){return val"){return val>compare}if(op===">="){return val>=compare}if(op==="in"){return compare.indexOf(val)>=0}if(op==="!in"){return compare.indexOf(val)<0}if(op==="like"||op==="!like"){const pattern="^"+compare.replace(/[-[\]{}()+.,\\^$|#\s]/g,"\\$&").replace(/\?/g,".").replace(/\*/g,".*?")+"$";const re=new RegExp(pattern,"i");const isMatch=re.test(val.toString());return op==="like"?isMatch:!isMatch}if(op==="matches"){return compare.test(val.toString())}if(op==="!matches"){return!compare.test(val.toString())}if(op==="between"){return val>=compare[0]&&val<=compare[1]}if(op==="!between"){return valcompare[1]}if(op==="has"||op==="!has"){const has=typeof val==="object"&&compare in val;return op==="has"?has:!has}if(op==="contains"||op==="!contains"){const includes=typeof val==="object"&&val instanceof Array&&val.includes(compare);return op==="contains"?includes:!includes}return false}exportNode(path,stream,options={format:"json"}){if(options&&options.format!=="json"){throw new Error(`Only json output is currently supported`)}const stringifyValue=(type,val)=>{const escape=str=>str.replace(/\\/i,"\\\\").replace(/"/g,'\\"');if(type===VALUE_TYPES.DATETIME){val=`"${val.toISOString()}"`}else if(type===VALUE_TYPES.STRING){val=`"${escape(val)}"`}else if(type===VALUE_TYPES.ARRAY){val=`[]`}else if(type===VALUE_TYPES.OBJECT){val=`{}`}else if(type===VALUE_TYPES.BINARY){val=`"${escape(ascii85.encode(val))}"`}else if(type===VALUE_TYPES.REFERENCE){val=`"${val.path}"`}return val};const queue=[];let outputCount=0;let objStart="",objEnd="";const buffer={output:"",enable:false,promise:null};return this.getNodeInfo(path).then(nodeInfo=>{if(!nodeInfo.exists){stream.write("null")}else if(nodeInfo.type===VALUE_TYPES.OBJECT){objStart="{";objEnd="}"}else if(nodeInfo.type===VALUE_TYPES.ARRAY){objStart="{";objEnd="}"}else{return this.getNodeValue(path).then(value=>{const val=stringifyValue(nodeInfo.type,value);return stream.write(val)})}let p=Promise.resolve();if(objStart){p=stream.write(objStart);if(!(p instanceof Promise)){p=Promise.resolve()}}return p.then(()=>{return this.getChildren(path).next(childInfo=>{if(childInfo.address){queue.push(childInfo)}else{const val=stringifyValue(childInfo.type,childInfo.value);const comma=outputCount>0?",":"";const key=typeof childInfo.index==="number"?`"${childInfo.index}"`:`"${childInfo.key}"`;const output=`${comma}${key}:${val}`;outputCount++;if(buffer.enable){buffer.output+=output}else{const flush=output=>{const p=stream.write(output);if(p instanceof Promise){buffer.enable=true;buffer.promise=p.then(()=>{const buffered=buffer.output;buffer.enable=false;buffer.output="";buffer.promise=null;if(buffered.length>0){return flush(buffered)}});return buffer.promise}};flush(output)}}})})}).then(()=>{return buffer.promise}).then(()=>{const next=()=>{if(queue.length===0){return}const childInfo=queue.shift();const comma=outputCount>0?",":"";const key=typeof childInfo.index==="number"?`"${childInfo.index}"`:`"${childInfo.key}"`;let p=stream.write(`${comma}${key}:`);outputCount++;if(!(p instanceof Promise)){p=Promise.resolve(p)}return p.then(()=>{return this.exportNode(childInfo.address.path,stream)}).then(()=>{return next()})};return next()}).then(()=>{if(objEnd){return stream.write(objEnd)}})}}module.exports={Storage:Storage,StorageSettings:StorageSettings,NodeNotFoundError:NodeNotFoundError,NodeRevisionError:NodeRevisionError}}).call(this,require("_process"))},{"./data-index":40,"./node-info":34,"./node-lock":35,"./node-value-types":36,"./promise-fs":40,_process:44,"acebase-core":11,colors:22,events:41}],40:[function(require,module,exports){},{}],41:[function(require,module,exports){var objectCreate=Object.create||objectCreatePolyfill;var objectKeys=Object.keys||objectKeysPolyfill;var bind=Function.prototype.bind||functionBindPolyfill;function EventEmitter(){if(!this._events||!Object.prototype.hasOwnProperty.call(this,"_events")){this._events=objectCreate(null);this._eventsCount=0}this._maxListeners=this._maxListeners||undefined}module.exports=EventEmitter;EventEmitter.EventEmitter=EventEmitter;EventEmitter.prototype._events=undefined;EventEmitter.prototype._maxListeners=undefined;var defaultMaxListeners=10;var hasDefineProperty;try{var o={};if(Object.defineProperty)Object.defineProperty(o,"x",{value:0});hasDefineProperty=o.x===0}catch(err){hasDefineProperty=false}if(hasDefineProperty){Object.defineProperty(EventEmitter,"defaultMaxListeners",{enumerable:true,get:function(){return defaultMaxListeners},set:function(arg){if(typeof arg!=="number"||arg<0||arg!==arg)throw new TypeError('"defaultMaxListeners" must be a positive number');defaultMaxListeners=arg}})}else{EventEmitter.defaultMaxListeners=defaultMaxListeners}EventEmitter.prototype.setMaxListeners=function setMaxListeners(n){if(typeof n!=="number"||n<0||isNaN(n))throw new TypeError('"n" argument must be a positive number');this._maxListeners=n;return this};function $getMaxListeners(that){if(that._maxListeners===undefined)return EventEmitter.defaultMaxListeners;return that._maxListeners}EventEmitter.prototype.getMaxListeners=function getMaxListeners(){return $getMaxListeners(this)};function emitNone(handler,isFn,self){if(isFn)handler.call(self);else{var len=handler.length;var listeners=arrayClone(handler,len);for(var i=0;i1)er=arguments[1];if(er instanceof Error){throw er}else{var err=new Error('Unhandled "error" event. ('+er+")");err.context=er;throw err}return false}handler=events[type];if(!handler)return false;var isFn=typeof handler==="function";len=arguments.length;switch(len){case 1:emitNone(handler,isFn,this);break;case 2:emitOne(handler,isFn,this,arguments[1]);break;case 3:emitTwo(handler,isFn,this,arguments[1],arguments[2]);break;case 4:emitThree(handler,isFn,this,arguments[1],arguments[2],arguments[3]);break;default:args=new Array(len-1);for(i=1;i0&&existing.length>m){existing.warned=true;var w=new Error("Possible EventEmitter memory leak detected. "+existing.length+' "'+String(type)+'" listeners '+"added. Use emitter.setMaxListeners() to "+"increase limit.");w.name="MaxListenersExceededWarning";w.emitter=target;w.type=type;w.count=existing.length;if(typeof console==="object"&&console.warn){console.warn("%s: %s",w.name,w.message)}}}}return target}EventEmitter.prototype.addListener=function addListener(type,listener){return _addListener(this,type,listener,false)};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.prependListener=function prependListener(type,listener){return _addListener(this,type,listener,true)};function onceWrapper(){if(!this.fired){this.target.removeListener(this.type,this.wrapFn);this.fired=true;switch(arguments.length){case 0:return this.listener.call(this.target);case 1:return this.listener.call(this.target,arguments[0]);case 2:return this.listener.call(this.target,arguments[0],arguments[1]);case 3:return this.listener.call(this.target,arguments[0],arguments[1],arguments[2]);default:var args=new Array(arguments.length);for(var i=0;i=0;i--){if(list[i]===listener||list[i].listener===listener){originalListener=list[i].listener;position=i;break}}if(position<0)return this;if(position===0)list.shift();else spliceOne(list,position);if(list.length===1)events[type]=list[0];if(events.removeListener)this.emit("removeListener",type,originalListener||listener)}return this};EventEmitter.prototype.removeAllListeners=function removeAllListeners(type){var listeners,events,i;events=this._events;if(!events)return this;if(!events.removeListener){if(arguments.length===0){this._events=objectCreate(null);this._eventsCount=0}else if(events[type]){if(--this._eventsCount===0)this._events=objectCreate(null);else delete events[type]}return this}if(arguments.length===0){var keys=objectKeys(events);var key;for(i=0;i=0;i--){this.removeListener(type,listeners[i])}}return this};function _listeners(target,type,unwrap){var events=target._events;if(!events)return[];var evlistener=events[type];if(!evlistener)return[];if(typeof evlistener==="function")return unwrap?[evlistener.listener||evlistener]:[evlistener];return unwrap?unwrapListeners(evlistener):arrayClone(evlistener,evlistener.length)}EventEmitter.prototype.listeners=function listeners(type){return _listeners(this,type,true)};EventEmitter.prototype.rawListeners=function rawListeners(type){return _listeners(this,type,false)};EventEmitter.listenerCount=function(emitter,type){if(typeof emitter.listenerCount==="function"){return emitter.listenerCount(type)}else{return listenerCount.call(emitter,type)}};EventEmitter.prototype.listenerCount=listenerCount;function listenerCount(type){var events=this._events;if(events){var evlistener=events[type];if(typeof evlistener==="function"){return 1}else if(evlistener){return evlistener.length}}return 0}EventEmitter.prototype.eventNames=function eventNames(){return this._eventsCount>0?Reflect.ownKeys(this._events):[]};function spliceOne(list,index){for(var i=index,k=i+1,n=list.length;k1){for(var i=1;i=len)return x;switch(x){case"%s":return String(args[i++]);case"%d":return Number(args[i++]);case"%j":try{return JSON.stringify(args[i++])}catch(_){return"[Circular]"}default:return x}}));for(var x=args[i];i=3)ctx.depth=arguments[2];if(arguments.length>=4)ctx.colors=arguments[3];if(isBoolean(opts)){ctx.showHidden=opts}else if(opts){exports._extend(ctx,opts)}if(isUndefined(ctx.showHidden))ctx.showHidden=false;if(isUndefined(ctx.depth))ctx.depth=2;if(isUndefined(ctx.colors))ctx.colors=false;if(isUndefined(ctx.customInspect))ctx.customInspect=true;if(ctx.colors)ctx.stylize=stylizeWithColor;return formatValue(ctx,obj,ctx.depth)}exports.inspect=inspect;inspect.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]};inspect.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"};function stylizeWithColor(str,styleType){var style=inspect.styles[styleType];if(style){return"["+inspect.colors[style][0]+"m"+str+"["+inspect.colors[style][1]+"m"}else{return str}}function stylizeNoColor(str,styleType){return str}function arrayToHash(array){var hash={};array.forEach((function(val,idx){hash[val]=true}));return hash}function formatValue(ctx,value,recurseTimes){if(ctx.customInspect&&value&&isFunction(value.inspect)&&value.inspect!==exports.inspect&&!(value.constructor&&value.constructor.prototype===value)){var ret=value.inspect(recurseTimes,ctx);if(!isString(ret)){ret=formatValue(ctx,ret,recurseTimes)}return ret}var primitive=formatPrimitive(ctx,value);if(primitive){return primitive}var keys=Object.keys(value);var visibleKeys=arrayToHash(keys);if(ctx.showHidden){keys=Object.getOwnPropertyNames(value)}if(isError(value)&&(keys.indexOf("message")>=0||keys.indexOf("description")>=0)){return formatError(value)}if(keys.length===0){if(isFunction(value)){var name=value.name?": "+value.name:"";return ctx.stylize("[Function"+name+"]","special")}if(isRegExp(value)){return ctx.stylize(RegExp.prototype.toString.call(value),"regexp")}if(isDate(value)){return ctx.stylize(Date.prototype.toString.call(value),"date")}if(isError(value)){return formatError(value)}}var base="",array=false,braces=["{","}"];if(isArray(value)){array=true;braces=["[","]"]}if(isFunction(value)){var n=value.name?": "+value.name:"";base=" [Function"+n+"]"}if(isRegExp(value)){base=" "+RegExp.prototype.toString.call(value)}if(isDate(value)){base=" "+Date.prototype.toUTCString.call(value)}if(isError(value)){base=" "+formatError(value)}if(keys.length===0&&(!array||value.length==0)){return braces[0]+base+braces[1]}if(recurseTimes<0){if(isRegExp(value)){return ctx.stylize(RegExp.prototype.toString.call(value),"regexp")}else{return ctx.stylize("[Object]","special")}}ctx.seen.push(value);var output;if(array){output=formatArray(ctx,value,recurseTimes,visibleKeys,keys)}else{output=keys.map((function(key){return formatProperty(ctx,value,recurseTimes,visibleKeys,key,array)}))}ctx.seen.pop();return reduceToSingleString(output,base,braces)}function formatPrimitive(ctx,value){if(isUndefined(value))return ctx.stylize("undefined","undefined");if(isString(value)){var simple="'"+JSON.stringify(value).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return ctx.stylize(simple,"string")}if(isNumber(value))return ctx.stylize(""+value,"number");if(isBoolean(value))return ctx.stylize(""+value,"boolean");if(isNull(value))return ctx.stylize("null","null")}function formatError(value){return"["+Error.prototype.toString.call(value)+"]"}function formatArray(ctx,value,recurseTimes,visibleKeys,keys){var output=[];for(var i=0,l=value.length;i-1){if(array){str=str.split("\n").map((function(line){return" "+line})).join("\n").substr(2)}else{str="\n"+str.split("\n").map((function(line){return" "+line})).join("\n")}}}else{str=ctx.stylize("[Circular]","special")}}if(isUndefined(name)){if(array&&key.match(/^\d+$/)){return str}name=JSON.stringify(""+key);if(name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)){name=name.substr(1,name.length-2);name=ctx.stylize(name,"name")}else{name=name.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'");name=ctx.stylize(name,"string")}}return name+": "+str}function reduceToSingleString(output,base,braces){var numLinesEst=0;var length=output.reduce((function(prev,cur){numLinesEst++;if(cur.indexOf("\n")>=0)numLinesEst++;return prev+cur.replace(/\u001b\[\d\d?m/g,"").length+1}),0);if(length>60){return braces[0]+(base===""?"":base+"\n ")+" "+output.join(",\n ")+" "+braces[1]}return braces[0]+base+" "+output.join(", ")+" "+braces[1]}function isArray(ar){return Array.isArray(ar)}exports.isArray=isArray;function isBoolean(arg){return typeof arg==="boolean"}exports.isBoolean=isBoolean;function isNull(arg){return arg===null}exports.isNull=isNull;function isNullOrUndefined(arg){return arg==null}exports.isNullOrUndefined=isNullOrUndefined;function isNumber(arg){return typeof arg==="number"}exports.isNumber=isNumber;function isString(arg){return typeof arg==="string"}exports.isString=isString;function isSymbol(arg){return typeof arg==="symbol"}exports.isSymbol=isSymbol;function isUndefined(arg){return arg===void 0}exports.isUndefined=isUndefined;function isRegExp(re){return isObject(re)&&objectToString(re)==="[object RegExp]"}exports.isRegExp=isRegExp;function isObject(arg){return typeof arg==="object"&&arg!==null}exports.isObject=isObject;function isDate(d){return isObject(d)&&objectToString(d)==="[object Date]"}exports.isDate=isDate;function isError(e){return isObject(e)&&(objectToString(e)==="[object Error]"||e instanceof Error)}exports.isError=isError;function isFunction(arg){return typeof arg==="function"}exports.isFunction=isFunction;function isPrimitive(arg){return arg===null||typeof arg==="boolean"||typeof arg==="number"||typeof arg==="string"||typeof arg==="symbol"||typeof arg==="undefined"}exports.isPrimitive=isPrimitive;exports.isBuffer=require("./support/isBuffer");function objectToString(o){return Object.prototype.toString.call(o)}function pad(n){return n<10?"0"+n.toString(10):n.toString(10)}var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function timestamp(){var d=new Date;var time=[pad(d.getHours()),pad(d.getMinutes()),pad(d.getSeconds())].join(":");return[d.getDate(),months[d.getMonth()],time].join(" ")}exports.log=function(){console.log("%s - %s",timestamp(),exports.format.apply(exports,arguments))};exports.inherits=require("inherits");exports._extend=function(origin,add){if(!add||!isObject(add))return origin;var keys=Object.keys(add);var i=keys.length;while(i--){origin[keys[i]]=add[keys[i]]}return origin};function hasOwnProperty(obj,prop){return Object.prototype.hasOwnProperty.call(obj,prop)}}).call(this,require("_process"),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./support/isBuffer":45,_process:44,inherits:42}]},{},[33])(33)})); \ No newline at end of file