@datasworn/core
Version:
Typings and JSON schema common to Datasworn. This is a pre-release package, provided for developer feedback. It will almost certainly receive breaking changes.
305 lines (304 loc) • 12 kB
JavaScript
;
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a, _ObjectGlobber_getPlainObjectPaths, _ObjectGlobber_getMapPaths;
Object.defineProperty(exports, "__esModule", { value: true });
const arrayIs_js_1 = require("./arrayIs.js");
const CONST_js_1 = __importDefault(require("../IdElements/CONST.js"));
const TypeGuard_js_1 = __importDefault(require("../IdElements/TypeGuard.js"));
// TODO: make this friendly to lookups in Map objects.
/**
* @internal
*/
class ObjectGlobber extends Array {
constructor(...items) {
super(...items.map(_a.replaceGlobString));
}
/** Does this path contain any wildcard or globstar elements? */
get wildcard() {
return this.some(_a.isGlobElement);
}
toJSON() {
return this.map(_a.replaceGlobSymbol);
}
clone() {
return new _a(...this);
}
partitionStaticPath() {
if (!this.wildcard)
return [this.clone(), new _a()];
const firstGlobIndex = this.findIndex(_a.isGlobElement);
return [
new _a(...this.slice(0, firstGlobIndex)),
new _a(...this.slice(firstGlobIndex))
];
}
nearestStaticPath() {
if (!this.wildcard)
return this;
const pathElements = new _a();
for (const element of this) {
if (element === _a.WILDCARD ||
element === _a.GLOBSTAR)
break;
else
pathElements.push(element);
}
return pathElements;
}
step(steps = 1) {
if (steps < 0)
throw new Error("steps parameter can't be less than 0");
return new _a(...this.slice(1));
}
getParent() {
return new _a(...this.slice(0, this.length - 1));
}
getMatches(from, matchTest, matches = [], includeArrays = false) {
if (!this.wildcard) {
const match = this.walk(from);
if ((typeof matchTest === 'function' &&
matchTest(match, this.last(), this.last())) ||
!matchTest)
matches.push(match);
// else matches.push(match)
return matches;
}
matches.push(..._a.getMatches(from, this, { includeArrays, matchTest }));
return matches;
}
walk(from, forEach) {
if (this.wildcard)
throw new Error(`Path contains wildcard/globstar elements. Use ${this.constructor.name}#${this.getMatches.name} instead.`);
return _a.walk(from, this, forEach);
}
is(path) {
if (path instanceof _a)
return false;
return this.every((valueA, i) => {
const valueB = path[i];
if (Array.isArray(valueA) && Array.isArray(valueB))
return (0, arrayIs_js_1.arrayIs)(valueA, valueB);
return Object.is(valueA, valueB);
});
}
static isGlobElement(element) {
return (element === _a.WILDCARD || element === _a.GLOBSTAR);
}
first() {
return this[0];
}
last() {
return this[this.length - 1];
}
head() {
return this.slice(1);
}
tail() {
return this.slice(0, -1);
}
static getMatches(from, keys, { matchTest, includeArrays = false } = { includeArrays: false }) {
const nextKey = keys[0];
const nextPath = keys.slice(1);
// console.log('next:', nextKey, nextPath)
if (nextPath.length === 0)
return _a.getKeyMatches(from, nextKey, {
includeArrays,
matchTest
});
const matches = _a.getKeyMatches(from, nextKey, {
includeArrays
});
const results = [];
for (const match of matches) {
results.push(..._a.getMatches(match, nextPath, {
matchTest,
includeArrays
}));
}
return results;
}
static getKeyMatches(from, matchedKey, { forEachMatch, matchTest, includeArrays = false } = {
includeArrays: false
}) {
if (!_a.isPropertyKey(matchedKey))
throw new Error(`Expected a number, string, or symbol key, but got ${typeof matchedKey}`);
const results = [];
const hasMatchTest = typeof matchTest === 'function';
function iterateWildcardMatch(key, value, results) {
if (!hasMatchTest || matchTest(value, key, matchedKey))
results.push(value);
}
function iterateGlobstarMatch(key, value, results) {
iterateWildcardMatch(key, value, results);
if (_a.isWalkable(value, includeArrays)) {
results.push(..._a.getKeyMatches(value, matchedKey, {
matchTest,
includeArrays
}));
}
}
switch (matchedKey) {
case _a.WILDCARD.description:
case _a.WILDCARD:
if (from instanceof Map) {
for (const [key, value] of from)
iterateWildcardMatch(key, value, results);
}
else
for (const key in from) {
if (!Object.hasOwn(from, key))
continue;
const value = from[key];
iterateWildcardMatch(key, value, results);
}
break;
case _a.GLOBSTAR.description:
case _a.GLOBSTAR:
if (from instanceof Map) {
for (const [key, value] of from)
iterateGlobstarMatch(key, value, results);
}
else
for (const key in from) {
// console.log(realKey)
if (!Object.hasOwn(from, key))
continue;
const value = from[key];
iterateGlobstarMatch(key, value, results);
}
break;
default: {
let value;
if (from instanceof Map)
value = from.get(matchedKey);
else
value = from[matchedKey];
if (typeof value === 'undefined' &&
!_a.implicitKeys.includes(matchedKey))
throw new Error(`Unable to find key ${typeof matchedKey === 'symbol'
? matchedKey.toString()
: JSON.stringify(matchedKey)}`);
results.push(value);
break;
}
}
return results;
}
/** Is this value an object with recursable keys? */
static isWalkable(value, includeArrays = false) {
if (!includeArrays && Array.isArray(value))
return false;
return typeof value === 'object' && !Object.is(value, null);
}
/** Is the value valid as an object property key? */
static isPropertyKey(key) {
return (typeof key === 'string' ||
typeof key === 'number' ||
typeof key === 'symbol');
}
static walk(from, path, forEach) {
if (path.length === 0)
return from;
if (typeof forEach === 'function')
forEach(from, path);
const [currentKey, ...nextPath] = path;
if (!_a.isPropertyKey(currentKey))
throw new Error(`Expected a number, string, or symbol key, but got ${typeof currentKey}`);
if (typeof from !== 'object')
throw new Error(`Expected an object but got ${typeof from}`);
if (from == null)
throw new Error(`Expected an object but got ${JSON.stringify(from)}`);
let nextObject;
if (Array.isArray(from)) {
const currentIndex = Number(currentKey);
if (!Number.isInteger(currentIndex))
throw new Error(`Expected an array index but got ${currentIndex}`);
nextObject = from[currentIndex];
}
else if (from instanceof Map) {
nextObject = from.get(currentKey);
}
else
nextObject = from[currentKey];
if (path.length === 0)
return nextObject;
else
return _a.walk(nextObject, nextPath);
}
/** Return all object paths in a given object. Paths that contain only a primitive value (boolean, number, string) are omitted. */
static getObjectPaths(object, includeArrays = false, currentPath = new _a()) {
const results = [];
if (object instanceof Map)
results.push(...__classPrivateFieldGet(_a, _a, "m", _ObjectGlobber_getMapPaths).call(_a, object, includeArrays, currentPath));
else
results.push(...__classPrivateFieldGet(_a, _a, "m", _ObjectGlobber_getPlainObjectPaths).call(_a, object, includeArrays, currentPath));
return results;
}
/** Replace a wildcard/globstar string with a Symbol */
static replaceGlobString(item) {
switch (true) {
case TypeGuard_js_1.default.Wildcard(item):
return _a.WILDCARD;
case TypeGuard_js_1.default.Globstar(item):
return _a.GLOBSTAR;
default:
return item;
}
}
/** Replace a wildcard or globstar Symbol with a string representation. */
static replaceGlobSymbol(item) {
switch (item) {
case _a.WILDCARD:
case _a.GLOBSTAR:
return item.description();
default:
return item;
}
}
}
_a = ObjectGlobber, _ObjectGlobber_getPlainObjectPaths = function _ObjectGlobber_getPlainObjectPaths(object, includeArrays = false, currentPath = new _a()) {
const results = [];
for (const k in object) {
if (!Object.hasOwn(object, k))
continue;
const nextObject = object[k];
if (typeof nextObject !== 'object')
continue;
if (Object.is(nextObject, null))
continue;
if (!includeArrays && Array.isArray(nextObject))
continue;
const nextPath = new _a(...currentPath, k);
results.push(nextPath, ..._a.getObjectPaths(nextObject, includeArrays, nextPath));
}
return results;
}, _ObjectGlobber_getMapPaths = function _ObjectGlobber_getMapPaths(object, includeArrays = false, currentPath = new _a()) {
const results = [];
for (const [k, nextObject] of object) {
if (typeof nextObject !== 'object')
continue;
if (Object.is(nextObject, null))
continue;
if (!includeArrays && Array.isArray(nextObject))
continue;
const nextPath = new _a(...currentPath, k);
results.push(nextPath, ..._a.getObjectPaths(nextObject, includeArrays, nextPath));
}
return results;
};
/** Keys that are part of the real object path, but not part of the ID */
ObjectGlobber.implicitKeys = ['contents', 'collections'];
(function (ObjectGlobber) {
/** Represents a glob wildcard path element, usually expressed as `*` */
ObjectGlobber.WILDCARD = Symbol(CONST_js_1.default.WildcardString);
/** Represents a globstar (recursive wildcard) path element, usually expressed as `**` */
ObjectGlobber.GLOBSTAR = Symbol(CONST_js_1.default.GlobstarString);
})(ObjectGlobber || (ObjectGlobber = {}));
exports.default = ObjectGlobber;