UNPKG

@genkit-ai/ai

Version:

Genkit AI framework generative AI APIs.

256 lines 6.9 kB
function escapeToken(token) { return token.replace(/~/g, "~0").replace(/\//g, "~1"); } function unescapeToken(token) { return token.replace(/~1/g, "/").replace(/~0/g, "~"); } const FORBIDDEN_TOKENS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]); function parsePointer(pointer) { if (pointer === "") return []; if (pointer[0] !== "/") { throw new Error(`Invalid JSON Pointer: "${pointer}" must start with "/".`); } const tokens = pointer.slice(1).split("/").map(unescapeToken); for (const token of tokens) { if (FORBIDDEN_TOKENS.has(token)) { throw new Error( `Invalid JSON Pointer: "${pointer}" contains forbidden token "${token}".` ); } } return tokens; } function isObject(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function definedKeys(obj) { return Object.keys(obj).filter((key) => obj[key] !== void 0); } function deepEqual(a, b) { if (a === b) return true; if (typeof a !== typeof b) return false; if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (!deepEqual(a[i], b[i])) return false; } return true; } if (isObject(a) && isObject(b)) { const aKeys = definedKeys(a); const bKeys = definedKeys(b); if (aKeys.length !== bKeys.length) return false; for (const key of aKeys) { if (b[key] === void 0) return false; if (!deepEqual(a[key], b[key])) return false; } return true; } return false; } function clone(value) { if (value === void 0) return value; if (typeof structuredClone === "function") { return structuredClone(value); } return JSON.parse(JSON.stringify(value)); } function diff(from, to) { const patch = []; diffRecursive(from, to, "", patch); return patch; } function diffRecursive(from, to, pointer, patch) { if (deepEqual(from, to)) return; if (isObject(from) && isObject(to)) { const keys = /* @__PURE__ */ new Set([...Object.keys(from), ...Object.keys(to)]); for (const key of keys) { const childPointer = `${pointer}/${escapeToken(key)}`; const inFrom = from[key] !== void 0; const inTo = to[key] !== void 0; if (inFrom && !inTo) { patch.push({ op: "remove", path: childPointer }); } else if (!inFrom && inTo) { patch.push({ op: "add", path: childPointer, value: clone(to[key]) }); } else if (inFrom && inTo) { diffRecursive(from[key], to[key], childPointer, patch); } } return; } if (Array.isArray(from) && Array.isArray(to)) { const min = Math.min(from.length, to.length); for (let i = 0; i < min; i++) { diffRecursive(from[i], to[i], `${pointer}/${i}`, patch); } if (to.length > from.length) { for (let i = from.length; i < to.length; i++) { patch.push({ op: "add", path: `${pointer}/-`, value: clone(to[i]) }); } } else if (from.length > to.length) { for (let i = from.length - 1; i >= to.length; i--) { patch.push({ op: "remove", path: `${pointer}/${i}` }); } } return; } patch.push({ op: "replace", path: pointer, value: clone(to) }); } function applyPatch(document, patch) { let doc = clone(document); for (const op of patch) { doc = applyOperation(doc, op); } return doc; } function applyOperation(doc, op) { const tokens = parsePointer(op.path); if (tokens.length === 0) { switch (op.op) { case "add": case "replace": return clone(op.value); case "remove": return void 0; case "test": if (!deepEqual(doc, op.value)) { throw new Error(`JSON Patch 'test' failed at root.`); } return doc; case "move": case "copy": { const value = clone(getValue(doc, parsePointer(op.from))); return value; } default: throw new Error(`Unsupported JSON Patch op: ${op.op}`); } } if (doc == null && (op.op === "add" || op.op === "replace")) { doc = {}; } switch (op.op) { case "add": setValue( doc, tokens, clone(op.value), /* isAdd= */ true ); return doc; case "replace": setValue( doc, tokens, clone(op.value), /* isAdd= */ false ); return doc; case "remove": removeValue(doc, tokens); return doc; case "test": { const actual = getValue(doc, tokens); if (!deepEqual(actual, op.value)) { throw new Error(`JSON Patch 'test' failed at "${op.path}".`); } return doc; } case "move": { const fromTokens = parsePointer(op.from); const value = clone(getValue(doc, fromTokens)); removeValue(doc, fromTokens); setValue( doc, tokens, value, /* isAdd= */ true ); return doc; } case "copy": { const value = clone(getValue(doc, parsePointer(op.from))); setValue( doc, tokens, value, /* isAdd= */ true ); return doc; } default: throw new Error(`Unsupported JSON Patch op: ${op.op}`); } } function getValue(doc, tokens) { let cur = doc; for (const token of tokens) { if (cur == null) return void 0; cur = Array.isArray(cur) ? cur[Number(token)] : cur[token]; } return cur; } function setValue(doc, tokens, value, isAdd) { const parent = ensureParent(doc, tokens); if (parent == null) return; const last = tokens[tokens.length - 1]; if (Array.isArray(parent)) { if (last === "-") { parent.push(value); return; } const idx = Number(last); if (Number.isNaN(idx)) return; if (isAdd) { parent.splice(idx, 0, value); } else { parent[idx] = value; } return; } parent[last] = value; } function removeValue(doc, tokens) { const parent = getValue(doc, tokens.slice(0, -1)); if (parent == null) return; const last = tokens[tokens.length - 1]; if (Array.isArray(parent)) { const idx = Number(last); if (!Number.isNaN(idx) && idx >= 0 && idx < parent.length) { parent.splice(idx, 1); } return; } if (isObject(parent)) { delete parent[last]; } } function ensureParent(doc, tokens) { let cur = doc; for (let i = 0; i < tokens.length - 1; i++) { const token = tokens[i]; if (cur == null) return void 0; const next = Array.isArray(cur) ? cur[Number(token)] : cur[token]; if (next == null || typeof next !== "object") { const created = {}; if (Array.isArray(cur)) { cur[Number(token)] = created; } else { cur[token] = created; } cur = created; } else { cur = next; } } return cur; } export { applyPatch, diff }; //# sourceMappingURL=json-patch.mjs.map