ts-patch-mongoose 3.1.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,10 @@ Patch history (audit log) & events plugin for mongoose
12
12
  [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_ts-patch-mongoose&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-patch-mongoose)
13
13
  [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_ts-patch-mongoose&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-patch-mongoose)
14
14
  [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ilovepixelart_ts-patch-mongoose&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=ilovepixelart_ts-patch-mongoose)
15
+ \
16
+ [![Socket Badge](https://badge.socket.dev/npm/package/ts-patch-mongoose)](https://socket.dev/npm/package/ts-patch-mongoose)
17
+ [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/ilovepixelart/ts-patch-mongoose/badge)](https://securityscorecards.dev/viewer/?uri=github.com/ilovepixelart/ts-patch-mongoose)
18
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12473/badge)](https://www.bestpractices.dev/en/projects/12473)
15
19
 
16
20
  ## Motivation
17
21
 
@@ -24,10 +28,12 @@ I need to track changes of mongoose models and save them as patch history (audit
24
28
  ```json
25
29
  {
26
30
  "node": "20.x || 22.x || 24.x",
27
- "mongoose": ">=6.6.0 || 7.x || 8.x || 9.x",
31
+ "mongoose": ">=6.6.0 <10"
28
32
  }
29
33
  ```
30
34
 
35
+ CI tests against mongoose `6.12.2`, `7.6.4`, `8.23.0`, and `9.4.1`.
36
+
31
37
  ## Features
32
38
 
33
39
  - Track changes in mongoose models
@@ -109,12 +115,12 @@ const BookSchema = new Schema<Book>({
109
115
  }
110
116
  }, { timestamps: true })
111
117
 
112
- BookSchema.plugin(patchHistoryPlugin, {
118
+ BookSchema.plugin(patchHistoryPlugin, {
113
119
  // Provide your event constants to plugin
114
120
  eventCreated: BOOK_CREATED,
115
121
  eventUpdated: BOOK_UPDATED,
116
122
  eventDeleted: BOOK_DELETED,
117
-
123
+
118
124
  // You can omit some properties in case you don't want to save them to patch history
119
125
  omit: ['__v', 'createdAt', 'updatedAt'],
120
126
 
package/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var mongoose = require('mongoose');
4
- var jsonpatch = require('fast-json-patch');
5
4
  var EventEmitter = require('node:events');
6
- var powerAssign = require('power-assign');
7
5
 
8
6
  const HistorySchema = new mongoose.Schema(
9
7
  {
@@ -250,6 +248,94 @@ class PatchEventEmitter extends EventEmitter {
250
248
  }
251
249
  const em = new PatchEventEmitter();
252
250
 
251
+ const escapeToken = (key) => {
252
+ if (!key.includes("/") && !key.includes("~")) return key;
253
+ return key.replaceAll("~", "~0").replaceAll("/", "~1");
254
+ };
255
+ const joinPath = (base, key) => `${base}/${escapeToken(key)}`;
256
+ const cloneValue = (value) => {
257
+ if (value === void 0) return null;
258
+ if (value === null || typeof value !== "object") return value;
259
+ return JSON.parse(JSON.stringify(value));
260
+ };
261
+ const isContainer = (value) => {
262
+ return typeof value === "object" && value !== null;
263
+ };
264
+ const keysOf = (value) => {
265
+ if (Array.isArray(value)) {
266
+ const indices = [];
267
+ for (let i = 0; i < value.length; i++) indices.push(String(i));
268
+ return indices;
269
+ }
270
+ return Object.keys(value);
271
+ };
272
+ const normalizeTarget = (target) => {
273
+ if (!isContainer(target) || Array.isArray(target)) return target;
274
+ const withToJSON = target;
275
+ return typeof withToJSON.toJSON === "function" ? withToJSON.toJSON() : target;
276
+ };
277
+ const emitTest = (path, value, invertible, out) => {
278
+ if (invertible) out.push({ op: "test", path, value: cloneValue(value) });
279
+ };
280
+ const emitReplace = (path, source, target, invertible, out) => {
281
+ emitTest(path, source, invertible, out);
282
+ out.push({ op: "replace", path, value: cloneValue(target) });
283
+ };
284
+ const emitRemove = (path, source, invertible, out) => {
285
+ emitTest(path, source, invertible, out);
286
+ out.push({ op: "remove", path });
287
+ };
288
+ const shouldTreatAsRemoval = (sourceChild, targetChild, sourceIsArray) => {
289
+ return targetChild === void 0 && sourceChild !== void 0 && !sourceIsArray;
290
+ };
291
+ const diffSourceKey = (scope, key) => {
292
+ const { source, target, targetKeySet, basePath, sourceIsArray, invertible, out } = scope;
293
+ const childPath = joinPath(basePath, key);
294
+ const sourceChild = source[key];
295
+ if (!targetKeySet.has(key)) {
296
+ emitRemove(childPath, sourceChild, invertible, out);
297
+ return;
298
+ }
299
+ const targetChild = target[key];
300
+ if (shouldTreatAsRemoval(sourceChild, targetChild, sourceIsArray)) {
301
+ emitRemove(childPath, sourceChild, invertible, out);
302
+ return;
303
+ }
304
+ diff(sourceChild, targetChild, childPath, invertible, out);
305
+ };
306
+ const diffAddedKeys = (target, sourceKeys, targetKeys, basePath, out) => {
307
+ const sourceKeySet = new Set(sourceKeys);
308
+ for (const key of targetKeys) {
309
+ if (sourceKeySet.has(key)) continue;
310
+ const targetChild = target[key];
311
+ if (targetChild === void 0) continue;
312
+ out.push({ op: "add", path: joinPath(basePath, key), value: cloneValue(targetChild) });
313
+ }
314
+ };
315
+ const diff = (source, target, basePath, invertible, out) => {
316
+ if (source === target) return;
317
+ const resolvedTarget = normalizeTarget(target);
318
+ const sourceIsArray = Array.isArray(source);
319
+ const targetIsArray = Array.isArray(resolvedTarget);
320
+ if (!isContainer(source) || !isContainer(resolvedTarget) || sourceIsArray !== targetIsArray) {
321
+ emitReplace(basePath, source, resolvedTarget, invertible, out);
322
+ return;
323
+ }
324
+ const sourceKeys = keysOf(source);
325
+ const targetKeys = keysOf(resolvedTarget);
326
+ const targetKeySet = new Set(targetKeys);
327
+ const scope = { source, target: resolvedTarget, targetKeySet, basePath, sourceIsArray, invertible, out };
328
+ for (const key of Array.from(sourceKeys).reverse()) {
329
+ diffSourceKey(scope, key);
330
+ }
331
+ diffAddedKeys(resolvedTarget, sourceKeys, targetKeys, basePath, out);
332
+ };
333
+ const compare = (source, target, invertible = false) => {
334
+ const out = [];
335
+ diff(source, target, "", invertible, out);
336
+ return out;
337
+ };
338
+
253
339
  const isPlainObject = (val) => {
254
340
  if (Object.prototype.toString.call(val) !== "[object Object]") return false;
255
341
  const prot = Object.getPrototypeOf(val);
@@ -380,7 +466,7 @@ const updatePatch = async (opts, context, current, original) => {
380
466
  const currentObject = getJsonOmit(opts, current);
381
467
  const originalObject = getJsonOmit(opts, original);
382
468
  if (isEmpty(originalObject) || isEmpty(currentObject)) return;
383
- const patch = jsonpatch.compare(originalObject, currentObject, true);
469
+ const patch = compare(originalObject, currentObject, true);
384
470
  if (isEmpty(patch)) return;
385
471
  emitEvent(context, opts.eventUpdated, { oldDoc: original, doc: current, patch });
386
472
  if (history) {
@@ -469,6 +555,186 @@ const saveHooksInitialize = (schema, opts) => {
469
555
  });
470
556
  };
471
557
 
558
+ const hasOwn = Object.prototype.hasOwnProperty;
559
+ const parseSegment = (segment) => {
560
+ const asNumber2 = Number(segment);
561
+ return Number.isInteger(asNumber2) && String(asNumber2) === segment ? asNumber2 : segment;
562
+ };
563
+ const parsePath = (path) => {
564
+ const [first, ...rest] = path.split(".").map(parseSegment);
565
+ let leaf = first ?? path;
566
+ const crumbs = [];
567
+ for (const key of rest) {
568
+ crumbs.push({ key: leaf, nextNumeric: typeof key === "number" });
569
+ leaf = key;
570
+ }
571
+ return { leaf, crumbs };
572
+ };
573
+ const deepEqualJson = (a, b) => {
574
+ try {
575
+ return JSON.stringify(a) === JSON.stringify(b);
576
+ } catch {
577
+ return a === b;
578
+ }
579
+ };
580
+ const ensureContainer = (parent, key, hintNumeric) => {
581
+ const existing = parent[key];
582
+ if (existing !== null && typeof existing === "object") {
583
+ return existing;
584
+ }
585
+ const created = hintNumeric ? [] : {};
586
+ parent[key] = created;
587
+ return created;
588
+ };
589
+ const setAtPath = (doc, path, value) => {
590
+ const { leaf, crumbs } = parsePath(path);
591
+ let cursor = doc;
592
+ for (const crumb of crumbs) {
593
+ cursor = ensureContainer(cursor, crumb.key, crumb.nextNumeric);
594
+ }
595
+ cursor[leaf] = value;
596
+ };
597
+ const getAtPath = (doc, path) => {
598
+ const { leaf, crumbs } = parsePath(path);
599
+ let cursor = doc;
600
+ for (const crumb of crumbs) {
601
+ const next = cursor[crumb.key];
602
+ if (next === null || typeof next !== "object") return void 0;
603
+ cursor = next;
604
+ }
605
+ return { container: cursor, leaf, exists: hasOwn.call(cursor, leaf) };
606
+ };
607
+ const unsetAtPath = (doc, path) => {
608
+ const located = getAtPath(doc, path);
609
+ if (!located) return;
610
+ if (Array.isArray(located.container)) {
611
+ located.container[located.leaf] = void 0;
612
+ } else {
613
+ delete located.container[located.leaf];
614
+ }
615
+ };
616
+ const asNumber = (value) => typeof value === "number" ? value : 0;
617
+ const toArray = (value) => Array.isArray(value) ? value : void 0;
618
+ const shouldReplaceForMin = (current, candidate) => {
619
+ if (candidate === void 0) return false;
620
+ if (current === void 0) return true;
621
+ return candidate < current;
622
+ };
623
+ const shouldReplaceForMax = (current, candidate) => {
624
+ if (candidate === void 0) return false;
625
+ if (current === void 0) return true;
626
+ return candidate > current;
627
+ };
628
+ const operators = {
629
+ $set: (doc, path, value) => setAtPath(doc, path, value),
630
+ $unset: (doc, path) => unsetAtPath(doc, path),
631
+ $inc: (doc, path, delta) => {
632
+ const located = getAtPath(doc, path);
633
+ const current = located?.exists ? asNumber(located.container[located.leaf]) : 0;
634
+ setAtPath(doc, path, current + asNumber(delta));
635
+ },
636
+ $mul: (doc, path, factor) => {
637
+ const located = getAtPath(doc, path);
638
+ const current = located?.exists ? asNumber(located.container[located.leaf]) : 0;
639
+ setAtPath(doc, path, current * asNumber(factor));
640
+ },
641
+ $min: (doc, path, candidate) => {
642
+ const located = getAtPath(doc, path);
643
+ if (!located?.exists) {
644
+ setAtPath(doc, path, candidate);
645
+ return;
646
+ }
647
+ if (shouldReplaceForMin(located.container[located.leaf], candidate)) {
648
+ setAtPath(doc, path, candidate);
649
+ }
650
+ },
651
+ $max: (doc, path, candidate) => {
652
+ const located = getAtPath(doc, path);
653
+ if (!located?.exists) {
654
+ setAtPath(doc, path, candidate);
655
+ return;
656
+ }
657
+ if (shouldReplaceForMax(located.container[located.leaf], candidate)) {
658
+ setAtPath(doc, path, candidate);
659
+ }
660
+ },
661
+ $rename: (doc, path, newPath) => {
662
+ if (typeof newPath !== "string") return;
663
+ const located = getAtPath(doc, path);
664
+ if (!located?.exists) return;
665
+ const value = located.container[located.leaf];
666
+ unsetAtPath(doc, path);
667
+ setAtPath(doc, newPath, value);
668
+ },
669
+ $currentDate: (doc, path, spec) => {
670
+ const wantTimestamp = typeof spec === "object" && spec !== null && spec.$type === "timestamp";
671
+ setAtPath(doc, path, wantTimestamp ? Date.now() : /* @__PURE__ */ new Date());
672
+ },
673
+ $push: (doc, path, pushSpec) => {
674
+ const located = getAtPath(doc, path);
675
+ const existing = located?.exists ? toArray(located.container[located.leaf]) ?? [] : [];
676
+ const next = [...existing];
677
+ if (typeof pushSpec === "object" && pushSpec !== null && "$each" in pushSpec && Array.isArray(pushSpec.$each)) {
678
+ next.push(...pushSpec.$each);
679
+ } else {
680
+ next.push(pushSpec);
681
+ }
682
+ setAtPath(doc, path, next);
683
+ },
684
+ $addToSet: (doc, path, item) => {
685
+ const located = getAtPath(doc, path);
686
+ const existing = located?.exists ? toArray(located.container[located.leaf]) ?? [] : [];
687
+ const next = [...existing];
688
+ const rawItems = typeof item === "object" && item !== null && "$each" in item ? item.$each : item;
689
+ const toAdd = Array.isArray(rawItems) ? rawItems : [rawItems];
690
+ for (const candidate of toAdd) {
691
+ if (!next.some((existingEntry) => deepEqualJson(existingEntry, candidate))) {
692
+ next.push(candidate);
693
+ }
694
+ }
695
+ setAtPath(doc, path, next);
696
+ },
697
+ $pull: (doc, path, matcher) => {
698
+ const located = getAtPath(doc, path);
699
+ const existing = located?.exists ? toArray(located.container[located.leaf]) : void 0;
700
+ if (!existing) return;
701
+ const filtered = existing.filter((entry) => !deepEqualJson(entry, matcher));
702
+ setAtPath(doc, path, filtered);
703
+ },
704
+ $pullAll: (doc, path, values) => {
705
+ const located = getAtPath(doc, path);
706
+ const existing = located?.exists ? toArray(located.container[located.leaf]) : void 0;
707
+ if (!existing || !Array.isArray(values)) return;
708
+ const filtered = existing.filter((entry) => !values.some((target) => deepEqualJson(entry, target)));
709
+ setAtPath(doc, path, filtered);
710
+ },
711
+ $pop: (doc, path, direction) => {
712
+ const located = getAtPath(doc, path);
713
+ const existing = located?.exists ? toArray(located.container[located.leaf]) : void 0;
714
+ if (!existing || existing.length === 0) return;
715
+ const next = direction === -1 ? existing.slice(1) : existing.slice(0, -1);
716
+ setAtPath(doc, path, next);
717
+ }
718
+ };
719
+ const applyOperator = (doc, operator, fields) => {
720
+ const fn = operators[operator];
721
+ if (!fn || fields === null || typeof fields !== "object") return;
722
+ for (const [path, argument] of Object.entries(fields)) {
723
+ fn(doc, path, argument);
724
+ }
725
+ };
726
+ const applyUpdate = (doc, update) => {
727
+ const result = { ...doc };
728
+ for (const [key, value] of Object.entries(update)) {
729
+ if (key.startsWith("$")) {
730
+ applyOperator(result, key, value);
731
+ } else {
732
+ setAtPath(result, key, value);
733
+ }
734
+ }
735
+ return result;
736
+ };
737
+
472
738
  const updateMethods = ["update", "updateOne", "replaceOne", "updateMany", "findOneAndUpdate", "findOneAndReplace", "findByIdAndUpdate"];
473
739
  const trackChangedFields = (fields, updated, changed) => {
474
740
  if (!fields) return;
@@ -477,30 +743,14 @@ const trackChangedFields = (fields, updated, changed) => {
477
743
  changed.set(root, updated[root]);
478
744
  }
479
745
  };
480
- const applyPullAll = (updated, fields, changed) => {
481
- for (const [field, values] of Object.entries(fields)) {
482
- const arr = updated[field];
483
- if (Array.isArray(arr)) {
484
- const filtered = arr.filter((item) => !values.some((v) => JSON.stringify(v) === JSON.stringify(item)));
485
- updated[field] = filtered;
486
- changed.set(field, filtered);
487
- }
488
- }
489
- };
490
746
  const assignUpdate = (document, update, commands) => {
491
- let updated = powerAssign.assign(document.toObject(toObjectOptions), update);
747
+ let updated = applyUpdate(document.toObject(toObjectOptions), update);
492
748
  const changedByCommand = /* @__PURE__ */ new Map();
493
749
  for (const command of commands) {
494
750
  const [op = ""] = Object.keys(command);
495
751
  const fields = command[op];
496
- try {
497
- updated = powerAssign.assign(updated, command);
498
- trackChangedFields(fields, updated, changedByCommand);
499
- } catch {
500
- if (op === "$pullAll" && fields) {
501
- applyPullAll(updated, fields, changedByCommand);
502
- }
503
- }
752
+ updated = applyUpdate(updated, command);
753
+ trackChangedFields(fields, updated, changedByCommand);
504
754
  }
505
755
  const doc = document.set(updated).toObject(toObjectOptions);
506
756
  for (const [field, value] of changedByCommand) {
@@ -555,7 +805,9 @@ const updateHooksInitialize = (schema, opts) => {
555
805
  const updateQuery = this.getUpdate();
556
806
  const { update, commands } = splitUpdateAndCommands(updateQuery);
557
807
  const filter = this.getFilter();
558
- const candidates = [update, assignUpdate(model.hydrate({}), update, commands), filter];
808
+ const simulated = assignUpdate(model.hydrate({}), update, commands);
809
+ const simulatedFilter = Object.fromEntries(Object.entries(simulated).filter(([, v]) => v !== void 0));
810
+ const candidates = [update, simulatedFilter, filter];
559
811
  let current = null;
560
812
  for (const query of candidates) {
561
813
  if (current || isEmpty(query)) continue;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,27 @@
1
1
  import { Types, Query, HydratedDocument, Schema } from 'mongoose';
2
- import { Operation } from 'fast-json-patch';
3
2
  import EventEmitter from 'node:events';
4
3
 
4
+ interface AddOperation<T> {
5
+ op: 'add';
6
+ path: string;
7
+ value: T;
8
+ }
9
+ interface RemoveOperation {
10
+ op: 'remove';
11
+ path: string;
12
+ }
13
+ interface ReplaceOperation<T> {
14
+ op: 'replace';
15
+ path: string;
16
+ value: T;
17
+ }
18
+ interface TestOperation<T> {
19
+ op: 'test';
20
+ path: string;
21
+ value: T;
22
+ }
23
+ type Operation = AddOperation<unknown> | RemoveOperation | ReplaceOperation<unknown> | TestOperation<unknown>;
24
+
5
25
  interface History {
6
26
  op: string;
7
27
  modelName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/em.ts","../src/ms.ts","../src/helpers.ts","../src/index.ts"],"mappings":";;;;AAGM,UAAW,OAAO;;;;kBAIR,KAAK,CAAC,QAAQ;;;;;;YAMpB,SAAS;gBACL,IAAI;gBACJ,IAAI;;AAGZ,UAAW,UAAU;aAChB,gBAAgB;UACnB,gBAAgB;YACd,SAAS;;AAGb,UAAW,YAAY;;;;;kBAKb,gBAAgB;kBAChB,gBAAgB;;;;AAK1B,KAAM,WAAW,MAAM,KAAK;;cAAiC,YAAY;;AAEzE,KAAM,IAAI,GAAG,MAAM;AAEnB,KAAM,QAAQ,GAAG,MAAM;AAEvB,UAAW,aAAa;;;;;;oBAMZ,gBAAgB,QAAQ,OAAO,CAAC,IAAI,IAAI,IAAI;sBAC1C,gBAAgB,QAAQ,OAAO;wBAC7B,gBAAgB,QAAQ,OAAO,CAAC,QAAQ,IAAI,QAAQ;;;uBAGrD,gBAAgB,UAAU,OAAO;sBAClC,KAAK;;;ACnDzB,cAAM,iBAAkB,SAAQ,YAAY;;AAC5C,cAAM,EAAE,mBAA0B;;ACKlC,cAAa,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCZ,KAAM,IAAI,gBAAgB,KAAK;AAE/B,KAAM,QAAQ,sCAAsC,IAAI,kBAAkB,IAAI;;AC6FpF,cAAa,kBAAkB,QAAe,QAAQ,oBAAoB,KAAK,cAAY,OAAO;;AC1HlG,cAAa,kBAAkB,cAAe,MAAM,WAAW,aAAa","names":[]}
1
+ {"version":3,"file":"index.d.cts","sources":["../src/json-patch.ts","../src/types.ts","../src/em.ts","../src/ms.ts","../src/helpers.ts","../src/index.ts"],"mappings":";;;AAAM,UAAW,YAAY;;;;;AAMvB,UAAW,eAAe;;;;AAK1B,UAAW,gBAAgB;;;;;AAM3B,UAAW,aAAa;;;;;AAMxB,KAAM,SAAS,GAAG,YAAY,YAAY,eAAe,GAAG,gBAAgB,YAAY,aAAa;;ACpBrG,UAAW,OAAO;;;;kBAIR,KAAK,CAAC,QAAQ;;;;;;YAMpB,SAAS;gBACL,IAAI;gBACJ,IAAI;;AAGZ,UAAW,UAAU;aAChB,gBAAgB;UACnB,gBAAgB;YACd,SAAS;;AAGb,UAAW,YAAY;;;;;kBAKb,gBAAgB;kBAChB,gBAAgB;;;;AAK1B,KAAM,WAAW,MAAM,KAAK;;cAAiC,YAAY;;AAEzE,KAAM,IAAI,GAAG,MAAM;AAEnB,KAAM,QAAQ,GAAG,MAAM;AAEvB,UAAW,aAAa;;;;;;oBAMZ,gBAAgB,QAAQ,OAAO,CAAC,IAAI,IAAI,IAAI;sBAC1C,gBAAgB,QAAQ,OAAO;wBAC7B,gBAAgB,QAAQ,OAAO,CAAC,QAAQ,IAAI,QAAQ;;;uBAGrD,gBAAgB,UAAU,OAAO;sBAClC,KAAK;;;ACnDzB,cAAM,iBAAkB,SAAQ,YAAY;;AAC5C,cAAM,EAAE,mBAA0B;;ACKlC,cAAa,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCZ,KAAM,IAAI,gBAAgB,KAAK;AAE/B,KAAM,QAAQ,sCAAsC,IAAI,kBAAkB,IAAI;;AC6FpF,cAAa,kBAAkB,QAAe,QAAQ,oBAAoB,KAAK,cAAY,OAAO;;AC1HlG,cAAa,kBAAkB,cAAe,MAAM,WAAW,aAAa","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,7 +1,27 @@
1
1
  import { Types, Query, HydratedDocument, Schema } from 'mongoose';
2
- import { Operation } from 'fast-json-patch';
3
2
  import EventEmitter from 'node:events';
4
3
 
4
+ interface AddOperation<T> {
5
+ op: 'add';
6
+ path: string;
7
+ value: T;
8
+ }
9
+ interface RemoveOperation {
10
+ op: 'remove';
11
+ path: string;
12
+ }
13
+ interface ReplaceOperation<T> {
14
+ op: 'replace';
15
+ path: string;
16
+ value: T;
17
+ }
18
+ interface TestOperation<T> {
19
+ op: 'test';
20
+ path: string;
21
+ value: T;
22
+ }
23
+ type Operation = AddOperation<unknown> | RemoveOperation | ReplaceOperation<unknown> | TestOperation<unknown>;
24
+
5
25
  interface History {
6
26
  op: string;
7
27
  modelName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sources":["../src/types.ts","../src/em.ts","../src/ms.ts","../src/helpers.ts","../src/index.ts"],"mappings":";;;;AAGM,UAAW,OAAO;;;;kBAIR,KAAK,CAAC,QAAQ;;;;;;YAMpB,SAAS;gBACL,IAAI;gBACJ,IAAI;;AAGZ,UAAW,UAAU;aAChB,gBAAgB;UACnB,gBAAgB;YACd,SAAS;;AAGb,UAAW,YAAY;;;;;kBAKb,gBAAgB;kBAChB,gBAAgB;;;;AAK1B,KAAM,WAAW,MAAM,KAAK;;cAAiC,YAAY;;AAEzE,KAAM,IAAI,GAAG,MAAM;AAEnB,KAAM,QAAQ,GAAG,MAAM;AAEvB,UAAW,aAAa;;;;;;oBAMZ,gBAAgB,QAAQ,OAAO,CAAC,IAAI,IAAI,IAAI;sBAC1C,gBAAgB,QAAQ,OAAO;wBAC7B,gBAAgB,QAAQ,OAAO,CAAC,QAAQ,IAAI,QAAQ;;;uBAGrD,gBAAgB,UAAU,OAAO;sBAClC,KAAK;;;ACnDzB,cAAM,iBAAkB,SAAQ,YAAY;;AAC5C,cAAM,EAAE,mBAA0B;;ACKlC,cAAa,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCZ,KAAM,IAAI,gBAAgB,KAAK;AAE/B,KAAM,QAAQ,sCAAsC,IAAI,kBAAkB,IAAI;;AC6FpF,cAAa,kBAAkB,QAAe,QAAQ,oBAAoB,KAAK,cAAY,OAAO;;AC1HlG,cAAa,kBAAkB,cAAe,MAAM,WAAW,aAAa","names":[]}
1
+ {"version":3,"file":"index.d.mts","sources":["../src/json-patch.ts","../src/types.ts","../src/em.ts","../src/ms.ts","../src/helpers.ts","../src/index.ts"],"mappings":";;;AAAM,UAAW,YAAY;;;;;AAMvB,UAAW,eAAe;;;;AAK1B,UAAW,gBAAgB;;;;;AAM3B,UAAW,aAAa;;;;;AAMxB,KAAM,SAAS,GAAG,YAAY,YAAY,eAAe,GAAG,gBAAgB,YAAY,aAAa;;ACpBrG,UAAW,OAAO;;;;kBAIR,KAAK,CAAC,QAAQ;;;;;;YAMpB,SAAS;gBACL,IAAI;gBACJ,IAAI;;AAGZ,UAAW,UAAU;aAChB,gBAAgB;UACnB,gBAAgB;YACd,SAAS;;AAGb,UAAW,YAAY;;;;;kBAKb,gBAAgB;kBAChB,gBAAgB;;;;AAK1B,KAAM,WAAW,MAAM,KAAK;;cAAiC,YAAY;;AAEzE,KAAM,IAAI,GAAG,MAAM;AAEnB,KAAM,QAAQ,GAAG,MAAM;AAEvB,UAAW,aAAa;;;;;;oBAMZ,gBAAgB,QAAQ,OAAO,CAAC,IAAI,IAAI,IAAI;sBAC1C,gBAAgB,QAAQ,OAAO;wBAC7B,gBAAgB,QAAQ,OAAO,CAAC,QAAQ,IAAI,QAAQ;;;uBAGrD,gBAAgB,UAAU,OAAO;sBAClC,KAAK;;;ACnDzB,cAAM,iBAAkB,SAAQ,YAAY;;AAC5C,cAAM,EAAE,mBAA0B;;ACKlC,cAAa,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCZ,KAAM,IAAI,gBAAgB,KAAK;AAE/B,KAAM,QAAQ,sCAAsC,IAAI,kBAAkB,IAAI;;AC6FpF,cAAa,kBAAkB,QAAe,QAAQ,oBAAoB,KAAK,cAAY,OAAO;;AC1HlG,cAAa,kBAAkB,cAAe,MAAM,WAAW,aAAa","names":[]}