UNPKG

@sinclair/typebox

Version:

Json Schema Type Builder with Static Type Resolution for TypeScript

175 lines (173 loc) 6.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Patch = exports.Diff = exports.ValueDeltaSymbolError = exports.ValueDeltaError = exports.Edit = exports.Delete = exports.Update = exports.Insert = void 0; const index_1 = require("../guard/index"); const index_2 = require("../pointer/index"); const index_3 = require("../clone/index"); const index_4 = require("../../type/error/index"); const index_5 = require("../../type/literal/index"); const index_6 = require("../../type/object/index"); const index_7 = require("../../type/string/index"); const index_8 = require("../../type/unknown/index"); const index_9 = require("../../type/union/index"); exports.Insert = (0, index_6.Object)({ type: (0, index_5.Literal)('insert'), path: (0, index_7.String)(), value: (0, index_8.Unknown)(), }); exports.Update = (0, index_6.Object)({ type: (0, index_5.Literal)('update'), path: (0, index_7.String)(), value: (0, index_8.Unknown)(), }); exports.Delete = (0, index_6.Object)({ type: (0, index_5.Literal)('delete'), path: (0, index_7.String)(), }); exports.Edit = (0, index_9.Union)([exports.Insert, exports.Update, exports.Delete]); // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ class ValueDeltaError extends index_4.TypeBoxError { constructor(value, message) { super(message); this.value = value; } } exports.ValueDeltaError = ValueDeltaError; class ValueDeltaSymbolError extends ValueDeltaError { constructor(value) { super(value, 'Cannot diff objects with symbol keys'); this.value = value; } } exports.ValueDeltaSymbolError = ValueDeltaSymbolError; // ------------------------------------------------------------------ // Command Factory // ------------------------------------------------------------------ function CreateUpdate(path, value) { return { type: 'update', path, value }; } function CreateInsert(path, value) { return { type: 'insert', path, value }; } function CreateDelete(path) { return { type: 'delete', path }; } // ------------------------------------------------------------------ // Diffing Generators // ------------------------------------------------------------------ function* ObjectType(path, current, next) { if (!(0, index_1.IsPlainObject)(next)) return yield CreateUpdate(path, next); const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)]; const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)]; for (const key of currentKeys) { if ((0, index_1.IsSymbol)(key)) throw new ValueDeltaSymbolError(key); if ((0, index_1.IsUndefined)(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${globalThis.String(key)}`, undefined); } for (const key of nextKeys) { if ((0, index_1.IsUndefined)(current[key]) || (0, index_1.IsUndefined)(next[key])) continue; if ((0, index_1.IsSymbol)(key)) throw new ValueDeltaSymbolError(key); yield* Visit(`${path}/${globalThis.String(key)}`, current[key], next[key]); } for (const key of nextKeys) { if ((0, index_1.IsSymbol)(key)) throw new ValueDeltaSymbolError(key); if ((0, index_1.IsUndefined)(current[key])) yield CreateInsert(`${path}/${globalThis.String(key)}`, next[key]); } for (const key of currentKeys.reverse()) { if ((0, index_1.IsSymbol)(key)) throw new ValueDeltaSymbolError(key); if ((0, index_1.IsUndefined)(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${globalThis.String(key)}`); } } function* ArrayType(path, current, next) { if (!(0, index_1.IsArray)(next)) return yield CreateUpdate(path, next); for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]); } for (let i = 0; i < next.length; i++) { if (i < current.length) continue; yield CreateInsert(`${path}/${i}`, next[i]); } for (let i = current.length - 1; i >= 0; i--) { if (i < next.length) continue; yield CreateDelete(`${path}/${i}`); } } function* TypedArrayType(path, current, next) { if (!(0, index_1.IsTypedArray)(next) || current.length !== next.length || globalThis.Object.getPrototypeOf(current).constructor.name !== globalThis.Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next); for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]); } } function* ValueType(path, current, next) { if (current === next) return; yield CreateUpdate(path, next); } function* Visit(path, current, next) { if ((0, index_1.IsPlainObject)(current)) return yield* ObjectType(path, current, next); if ((0, index_1.IsArray)(current)) return yield* ArrayType(path, current, next); if ((0, index_1.IsTypedArray)(current)) return yield* TypedArrayType(path, current, next); if ((0, index_1.IsValueType)(current)) return yield* ValueType(path, current, next); throw new ValueDeltaError(current, 'Unable to create diff edits for unknown value'); } // ------------------------------------------------------------------ // Diff // ------------------------------------------------------------------ function Diff(current, next) { return [...Visit('', current, next)]; } exports.Diff = Diff; // ------------------------------------------------------------------ // Patch // ------------------------------------------------------------------ function IsRootUpdate(edits) { return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update'; } function IsIdentity(edits) { return edits.length === 0; } function Patch(current, edits) { if (IsRootUpdate(edits)) { return (0, index_3.Clone)(edits[0].value); } if (IsIdentity(edits)) { return (0, index_3.Clone)(current); } const clone = (0, index_3.Clone)(current); for (const edit of edits) { switch (edit.type) { case 'insert': { index_2.ValuePointer.Set(clone, edit.path, edit.value); break; } case 'update': { index_2.ValuePointer.Set(clone, edit.path, edit.value); break; } case 'delete': { index_2.ValuePointer.Delete(clone, edit.path); break; } } } return clone; } exports.Patch = Patch;