UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

239 lines (213 loc) 5.29 kB
/** * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact schukai GmbH. * * SPDX-License-Identifier: AGPL-3.0 */ import { internalSymbol } from "../constants.mjs"; import { instanceSymbol } from "../constants.mjs"; import { Base } from "../types/base.mjs"; import { parseDataURL } from "../types/dataurl.mjs"; import { isString } from "../types/is.mjs"; import { ProxyObserver } from "../types/proxyobserver.mjs"; import { validateObject } from "../types/validate.mjs"; import { extend } from "./extend.mjs"; import { Pathfinder } from "./pathfinder.mjs"; export { Datasource }; /** * This callback can be passed to a datasource and is used to adapt data structures. * * @callback Monster.Data.Datasource~exampleCallback * @param {*} value Value * @param {string} key Key * @see Monster.Data.Datasource */ /** * @private * @type {symbol} * @license AGPLv3 * @since 1.24.0 */ const internalDataSymbol = Symbol.for( "@schukai/monster/data/datasource/@@data", ); /** * The datasource class is the basis for dealing with different data sources. * It provides a unified interface for accessing data * @externalExample ../../example/data/datasource.mjs * @license AGPLv3 * @since 1.22.0 * @copyright schukai GmbH * @summary The datasource class encapsulates the access to data objects. */ class Datasource extends Base { /** * creates a new datasource * */ constructor() { super(); this[internalSymbol] = new ProxyObserver({ options: extend({}, this.defaults), }); this[internalDataSymbol] = new ProxyObserver({}); } /** * attach a new observer * * @param {Observer} observer * @return {Datasource} */ attachObserver(observer) { this[internalDataSymbol].attachObserver(observer); return this; } /** * detach a observer * * @param {Observer} observer * @return {Datasource} */ detachObserver(observer) { this[internalDataSymbol].detachObserver(observer); return this; } /** * @param {Observer} observer * @return {boolean} */ containsObserver(observer) { return this[internalDataSymbol].containsObserver(observer); } /** * Derived classes can override and extend this method as follows. * * ``` * get defaults() { * return Object.assign({}, super.defaults, { * myValue:true * }); * } * ``` */ get defaults() { return {}; } /** * Set option * * @param {string} path * @param {*} value * @return {Datasource} */ setOption(path, value) { new Pathfinder(this[internalSymbol].getSubject()["options"]).setVia( path, value, ); return this; } /** * @param {string|object} options * @return {Datasource} * @throws {Error} the options does not contain a valid json definition */ setOptions(options) { if (isString(options)) { options = parseOptionsJSON(options); } extend( this[internalSymbol].getSubject()["options"], this.defaults, options, ); return this; } /** * nested options can be specified by path `a.b.c` * * @param {string} path * @param {*} defaultValue * @return {*} */ getOption(path, defaultValue) { let value; try { value = new Pathfinder( this[internalSymbol].getRealSubject()["options"], ).getVia(path); } catch (e) {} if (value === undefined) return defaultValue; return value; } /** * @throws {Error} this method must be implemented by derived classes. * @return {Promise} */ read() { throw new Error("this method must be implemented by derived classes"); } /** * @throws {Error} this method must be implemented by derived classes. * @return {Promise} */ write() { throw new Error("this method must be implemented by derived classes"); } /** * Returns real object * * @return {Object|Array} */ get() { return this[internalDataSymbol].getRealSubject(); } /** * @param {Object|Array} data * @return {Datasource} */ set(data) { this[internalDataSymbol].setSubject(data); return this; } /** * This method is called by the `instanceof` operator. * @return {symbol} * @since 2.1.0 */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/data/datasource"); } } /** * @private * @param {String} data * @return {Object} * @throws {Error} the options does not contain a valid json definition */ function parseOptionsJSON(data) { if (isString(data)) { // the configuration can be specified as a data url. try { const dataUrl = parseDataURL(data); data = dataUrl.content; } catch (e) {} try { const obj = JSON.parse(data); validateObject(obj); return obj; } catch (e) { throw new Error( `the options does not contain a valid json definition (actual: ${data}).`, ); } } return {}; }