forest-cli
Version:
The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.
259 lines • 23.5 kB
JavaScript
;
/**
* The diff engine: compares a remote canonical document against the locally
* edited one and produces ONLY server-whitelisted JSON Patch operations.
*
* Identity-based: items are matched by `id` then `name` (see `matchItems`),
* never by index. Renames are plain `replace .../name` ops because children
* are always addressed by the REMOTE item's key (stable across edits).
* Emission order per domain: adds (parents first) → replaces → removes.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.diffDomain = exports.remoteResolver = void 0;
const util_1 = require("util");
const uuid_1 = require("uuid");
const layout_file_1 = require("./layout-file");
const patch_rules_1 = require("./patch-rules");
/** Address segment for an item: prefer the stable id, else a path-safe name. */
function addressOf(item, jsonPath) {
if (item.id !== undefined && item.id !== null)
return String(item.id);
const { name } = item;
if (typeof name === 'string' && /^[^\s/:]+$/.test(name))
return name;
throw new layout_file_1.LayoutFileError(`${jsonPath}: item without a usable \`id\` (and the name contains characters forbidden in a path). ` +
'Do not remove the `id` fields generated by `forest layout pull`.');
}
/** Identity key used to match local and remote items. */
function identityOf(item) {
if (item.id !== undefined && item.id !== null)
return `id:${String(item.id)}`;
if (typeof item.name === 'string')
return `name:${item.name}`;
return undefined;
}
/**
* Match local items to remote ones by `id`, then by `name` — so a hand-authored
* item that omits its `id` updates the existing element instead of being seen as
* an add + a remove. Fails loudly on ambiguous input (duplicate local identities,
* or two locals resolving to the same remote).
*/
/**
* Build the local→remote resolver: a singleton (e.g. the main folder) matches its
* remote counterpart regardless of id/name; otherwise match by id, then by name.
* Exported so the sidecar upload (`workflow-sidecar.ts`) resolves its target with
* the EXACT same semantics as the domain patch (incl. last-wins on duplicate names).
*/
function remoteResolver(remoteItems, singletonFlag) {
const remoteById = new Map();
const remoteByName = new Map();
remoteItems.forEach(item => {
if (item.id !== undefined && item.id !== null)
remoteById.set(String(item.id), item);
if (typeof item.name === 'string')
remoteByName.set(item.name, item);
});
const remoteSingleton = singletonFlag
? remoteItems.find(item => item[singletonFlag] === true)
: undefined;
return local => (singletonFlag !== undefined && local[singletonFlag] === true ? remoteSingleton : undefined) ??
(local.id !== undefined && local.id !== null ? remoteById.get(String(local.id)) : undefined) ??
(typeof local.name === 'string' ? remoteByName.get(local.name) : undefined);
}
exports.remoteResolver = remoteResolver;
function matchItems(remoteItems, localItems, jsonPath, singletonFlag) {
const matchOf = remoteResolver(remoteItems, singletonFlag);
const seen = new Set();
const matched = new Set();
const added = [];
const common = [];
localItems.forEach(local => {
const key = identityOf(local);
if (key === undefined) {
throw new layout_file_1.LayoutFileError(`${jsonPath}: item without \`id\` or \`name\` — unable to identify it.`);
}
if (seen.has(key)) {
throw new layout_file_1.LayoutFileError(`${jsonPath}: two items share the same identity (${key}).`);
}
seen.add(key);
const remote = matchOf(local);
if (!remote) {
added.push(local);
}
else if (matched.has(remote)) {
throw new layout_file_1.LayoutFileError(`${jsonPath}: two items resolve to the same remote element (\`id\`/\`name\` collision).`);
}
else {
matched.add(remote);
common.push({ local, remote });
}
});
return { added, common, removed: remoteItems.filter(item => !matched.has(item)) };
}
function short(value) {
if (value === null)
return 'null';
if (value === undefined)
return '∅';
if (typeof value === 'object') {
const { name } = value;
if (typeof name === 'string')
return `{ name: « ${name} », … }`;
return Array.isArray(value) ? `[${value.length} items]` : '{…}';
}
const text = typeof value === 'string' ? `« ${value} »` : String(value);
return text.length > 60 ? `${text.slice(0, 57)}…` : text;
}
function emit(ctx, kind, op) {
ctx.bucket[kind].push(op);
}
function diffScalarLike(ctx, rule, remote, local) {
const path = `${ctx.pathPrefix}/${rule.prop}`;
const jsonPath = `${ctx.jsonPrefix}.${rule.prop}`;
const remoteValue = remote[rule.prop];
const localValue = local[rule.prop];
if ((0, util_1.isDeepStrictEqual)(remoteValue, localValue))
return;
const premiumPack = rule.premiumPack ?? ctx.premiumPack;
// Absent locally = "leave alone" (partial-authoring / patch semantics): the
// file need not carry every field. To clear a value, set it explicitly to
// `null` (which reaches the replace path below as a value, not `undefined`).
if (localValue === undefined)
return;
emit(ctx, 'replaces', {
domain: ctx.domain,
label: `${jsonPath}: ${short(remoteValue)} → ${short(localValue)}`,
op: 'replace',
path,
premiumPack,
value: localValue,
jsonPath,
});
}
/**
* Build the value sent in an `add` op. The server requires a client-supplied
* `id` on add (PRIMARY_KEYS) — so we KEEP the local id (stable across re-apply
* and cross-env copies) and generate a uuid only when none is present. Any
* per-rule `stripOnAdd` keys and `undefined` values are dropped; remaining
* write-required defaults are filled by the rule's own `addDefaults` hook.
*/
function prepareAddValue(item, rule) {
const cleaned = {};
Object.entries(item).forEach(([key, value]) => {
if (rule.stripOnAdd?.includes(key))
return;
if (value === undefined)
return;
cleaned[key] = value;
});
if (cleaned.id === undefined || cleaned.id === null)
cleaned.id = (0, uuid_1.v4)();
return rule.addDefaults ? rule.addDefaults(cleaned) : cleaned;
}
function diffKeyedArray(ctx, rule, remoteItems, localItems) {
// A root rule with `prop: ''` (folders/workflows) receives the document itself,
// which is `{}` when the domain is absent remotely — guard against non-arrays so
// `matchItems` never iterates a plain object.
const remotes = Array.isArray(remoteItems) ? remoteItems : [];
const locals = Array.isArray(localItems) ? localItems : [];
const arrayPath = rule.segment ? `${ctx.pathPrefix}/${rule.segment}` : ctx.pathPrefix;
const jsonPath = rule.prop ? `${ctx.jsonPrefix}.${rule.prop}` : ctx.jsonPrefix;
const premiumPack = rule.premiumPack ?? ctx.premiumPack;
const { added, common, removed } = matchItems(remotes, locals, jsonPath, rule.singletonFlag);
if (rule.fallbackReplaceWhole && (added.length > 0 || removed.length > 0)) {
emit(ctx, 'replaces', {
domain: ctx.domain,
label: `${jsonPath}: replacing the array (${locals.length} items)`,
op: 'replace',
path: arrayPath,
premiumPack,
value: locals,
jsonPath,
});
return;
}
added.forEach(item => {
if (!rule.addable) {
ctx.bucket.warnings.push(`${jsonPath}: cannot add « ${String(item.name ?? item.id)} » (items defined by the agent schema).`);
return;
}
emit(ctx, 'adds', {
domain: ctx.domain,
label: `${jsonPath}: adding ${short(item)}`,
op: 'add',
path: `${arrayPath}/-`,
premiumPack,
value: prepareAddValue(item, rule),
jsonPath,
});
});
removed.forEach(item => {
if (!rule.removable) {
ctx.bucket.warnings.push(`${jsonPath}: cannot remove « ${String(item.name ?? item.id)} » (items defined by the agent schema).`);
return;
}
emit(ctx, 'removes', {
domain: ctx.domain,
label: `${jsonPath}: removing ${short(item)}`,
op: 'remove',
path: `${arrayPath}/${addressOf(item, jsonPath)}`,
premiumPack,
jsonPath,
});
});
common.forEach(({ local, remote }) => {
const address = addressOf(remote, jsonPath);
const childCtx = {
...ctx,
pathPrefix: `${arrayPath}/${address}`,
premiumPack,
jsonPrefix: `${jsonPath}[${address}]`,
};
// eslint-disable-next-line no-use-before-define -- applyRules and diffKeyedArray are mutually recursive
applyRules(childCtx, rule.children, remote, local);
});
}
function applyRules(ctx, rules, remote, local) {
rules.forEach(rule => {
switch (rule.kind) {
case 'keyedArray': {
const remoteItems = (rule.prop ? remote[rule.prop] : remote);
const localItems = (rule.prop ? local[rule.prop] : local);
diffKeyedArray(ctx, rule, remoteItems ?? [], localItems ?? []);
break;
}
case 'object': {
const remoteChild = (remote[rule.prop] ?? {});
const localChild = (local[rule.prop] ?? {});
applyRules({
...ctx,
pathPrefix: `${ctx.pathPrefix}/${rule.segment}`,
jsonPrefix: `${ctx.jsonPrefix}.${rule.prop}`,
}, rule.children, remoteChild, localChild);
break;
}
case 'opaque':
case 'scalar': {
diffScalarLike(ctx, rule, remote, local);
break;
}
default:
break;
}
});
}
/** Diff one domain; returns whitelisted ops (adds → replaces → removes) + warnings. */
function diffDomain(domain, remote, local) {
const bucket = { adds: [], removes: [], replaces: [], warnings: [] };
const ctx = { bucket, domain, pathPrefix: '', jsonPrefix: domain };
applyRules(ctx, patch_rules_1.DOMAIN_RULES[domain].root, (remote ?? {}), (local ?? {}));
const ops = [...bucket.adds, ...bucket.replaces, ...bucket.removes];
const offending = ops.find(op => !(0, patch_rules_1.matchesWhitelist)(domain, op));
if (offending) {
throw new Error(`Internal bug: op generated outside the whitelist (${offending.op} ${offending.path}). ` +
'Please report this case — nothing was sent.');
}
return { ops, warnings: bucket.warnings };
}
exports.diffDomain = diffDomain;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlmZi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9sYXlvdXQvZGlmZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7O0dBUUc7OztBQUtILCtCQUF5QztBQUN6QywrQkFBb0M7QUFFcEMsK0NBQWdEO0FBQ2hELCtDQUErRDtBQVMvRCxnRkFBZ0Y7QUFDaEYsU0FBUyxTQUFTLENBQUMsSUFBVSxFQUFFLFFBQWdCO0lBQzdDLElBQUksSUFBSSxDQUFDLEVBQUUsS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJO1FBQUUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBRXRFLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFDdEIsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUVyRSxNQUFNLElBQUksNkJBQWUsQ0FDdkIsR0FBRyxRQUFRLHlGQUF5RjtRQUNsRyxrRUFBa0UsQ0FDckUsQ0FBQztBQUNKLENBQUM7QUFFRCx5REFBeUQ7QUFDekQsU0FBUyxVQUFVLENBQUMsSUFBVTtJQUM1QixJQUFJLElBQUksQ0FBQyxFQUFFLEtBQUssU0FBUyxJQUFJLElBQUksQ0FBQyxFQUFFLEtBQUssSUFBSTtRQUFFLE9BQU8sTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDOUUsSUFBSSxPQUFPLElBQUksQ0FBQyxJQUFJLEtBQUssUUFBUTtRQUFFLE9BQU8sUUFBUSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7SUFFOUQsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQVFEOzs7OztHQUtHO0FBQ0g7Ozs7O0dBS0c7QUFDSCxTQUFnQixjQUFjLENBQzVCLFdBQW1CLEVBQ25CLGFBQXNCO0lB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