UNPKG

mahler

Version:

A automated task composer and HTN based planner for building autonomous system agents

127 lines 4.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Distance = void 0; exports.diff = diff; const pointer_1 = require("./pointer"); const path_1 = require("./path"); const target_1 = require("./target"); const utils_1 = require("./utils"); function applyPatch(s, t) { if (t != null && !Array.isArray(t) && typeof t === 'object') { const result = { ...s }; for (const [key, value] of Object.entries(t)) { if (value === target_1.UNDEFINED) { delete result[key]; } else { result[key] = applyPatch(result[key], value); } } return result; } return t; } /** * getOperations returns all the possible operations that are applicable given a * new target state. This means, for instance, that if a target state creates a new * value under /a/b/c, this function must report a 'create' operation on that path, but also * 'update' operations on '/a/b', '/a', and '/' as any of those can result in same outcome */ function* getOperations(s, t) { // We store target, path pair in a quee so we can visit the full target // object, ordered by level, without recursion const queue = [ { tgt: t, ref: [] }, ]; // The target object const patched = applyPatch(s, t); // The list of operations to return while (queue.length > 0) { const { tgt, ref, isLeaf } = queue.shift(); const path = path_1.Path.from(ref); const sValue = pointer_1.Pointer.from(s, path); const tValue = tgt !== target_1.UNDEFINED ? pointer_1.Pointer.from(patched, path) : undefined; // If the target is DELETED, and the source value still // exists we need to add a delete operation if (tgt === target_1.UNDEFINED && sValue != null) { yield { op: 'delete', path, isLeaf: isLeaf == null }; } else if (tgt !== target_1.UNDEFINED) { // If the source value does not exist, then we add a `create` // operation if (sValue == null) { yield { op: 'create', path, target: tValue, isLeaf: true }; } // If the source value does exist, we do a deep comparison compare the source to the patched // version and if they don't match, we add an `update` operation else if (!(0, utils_1.deepEqual)(sValue, tValue)) { yield { op: 'update', path, source: sValue, target: tValue, isLeaf: // If the source or target are not objects, or they are arrays, then // we wont continue recursing so the object is a leaf typeof sValue !== 'object' || Array.isArray(sValue) || typeof tValue !== 'object' || Array.isArray(tValue), }; } } if (tgt != null && !Array.isArray(tgt) && typeof tgt === 'object' && // Only expand the target if the source exists sValue != null) { // Add target keys to stack to recurse for (const key of Object.keys(tgt)) { const value = tgt[key]; const newPath = ref.concat(key); queue.push({ tgt: value, ref: newPath }); } } // if tgt is DELETE, then we should also add rules to delete the current // properties recursively if (tgt === target_1.UNDEFINED && sValue != null && !Array.isArray(sValue) && typeof sValue === 'object') { for (const key of Object.keys(sValue)) { const newPath = ref.concat(key); // We set isLeaf to false here because we know that the source // comes from a previous iteration queue.push({ tgt: target_1.UNDEFINED, ref: newPath, isLeaf: false }); } } } } /** * Calculates the list of changes between the current state and the target * * Returns only the leaf operations. */ function diff(s, t) { const ops = [...getOperations(s, t)]; return ops.filter(({ isLeaf }) => isLeaf).map(({ isLeaf, ...op }) => op); } function from(src, tgt) { const target = applyPatch(src, tgt); return Object.assign((s) => { // NOTE: we return an array here, but we could easily // return an iterator instead for better memory usage return [...getOperations(s, tgt)].map(({ isLeaf, ...op }) => { delete op.source; return op; }); }, { get target() { return target; }, }); } exports.Distance = { from, }; //# sourceMappingURL=distance.js.map