@dabble/patches 0.1.1

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.
Files changed (120) hide show
  1. package/README.md +632 -0
  2. package/dist/client/PatchDoc.d.ts +85 -0
  3. package/dist/client/PatchDoc.js +299 -0
  4. package/dist/client/index.d.ts +2 -0
  5. package/dist/client/index.js +1 -0
  6. package/dist/event-signal.d.ts +31 -0
  7. package/dist/event-signal.js +40 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +1 -0
  10. package/dist/json-patch/JSONPatch.d.ts +126 -0
  11. package/dist/json-patch/JSONPatch.js +221 -0
  12. package/dist/json-patch/applyPatch.d.ts +11 -0
  13. package/dist/json-patch/applyPatch.js +37 -0
  14. package/dist/json-patch/composePatch.d.ts +2 -0
  15. package/dist/json-patch/composePatch.js +38 -0
  16. package/dist/json-patch/createJSONPatch.d.ts +35 -0
  17. package/dist/json-patch/createJSONPatch.js +41 -0
  18. package/dist/json-patch/index.d.ts +9 -0
  19. package/dist/json-patch/index.js +8 -0
  20. package/dist/json-patch/invertPatch.d.ts +2 -0
  21. package/dist/json-patch/invertPatch.js +31 -0
  22. package/dist/json-patch/ops/add.d.ts +2 -0
  23. package/dist/json-patch/ops/add.js +52 -0
  24. package/dist/json-patch/ops/bitmask.d.ts +14 -0
  25. package/dist/json-patch/ops/bitmask.js +48 -0
  26. package/dist/json-patch/ops/copy.d.ts +2 -0
  27. package/dist/json-patch/ops/copy.js +34 -0
  28. package/dist/json-patch/ops/increment.d.ts +5 -0
  29. package/dist/json-patch/ops/increment.js +21 -0
  30. package/dist/json-patch/ops/index.d.ts +22 -0
  31. package/dist/json-patch/ops/index.js +25 -0
  32. package/dist/json-patch/ops/move.d.ts +2 -0
  33. package/dist/json-patch/ops/move.js +211 -0
  34. package/dist/json-patch/ops/remove.d.ts +2 -0
  35. package/dist/json-patch/ops/remove.js +31 -0
  36. package/dist/json-patch/ops/replace.d.ts +2 -0
  37. package/dist/json-patch/ops/replace.js +44 -0
  38. package/dist/json-patch/ops/test.d.ts +2 -0
  39. package/dist/json-patch/ops/test.js +22 -0
  40. package/dist/json-patch/ops/text.d.ts +2 -0
  41. package/dist/json-patch/ops/text.js +57 -0
  42. package/dist/json-patch/patchProxy.d.ts +41 -0
  43. package/dist/json-patch/patchProxy.js +125 -0
  44. package/dist/json-patch/state.d.ts +2 -0
  45. package/dist/json-patch/state.js +8 -0
  46. package/dist/json-patch/transformPatch.d.ts +19 -0
  47. package/dist/json-patch/transformPatch.js +37 -0
  48. package/dist/json-patch/types.d.ts +52 -0
  49. package/dist/json-patch/types.js +1 -0
  50. package/dist/json-patch/utils/deepEqual.d.ts +1 -0
  51. package/dist/json-patch/utils/deepEqual.js +33 -0
  52. package/dist/json-patch/utils/exit.d.ts +2 -0
  53. package/dist/json-patch/utils/exit.js +4 -0
  54. package/dist/json-patch/utils/get.d.ts +2 -0
  55. package/dist/json-patch/utils/get.js +6 -0
  56. package/dist/json-patch/utils/getOpData.d.ts +2 -0
  57. package/dist/json-patch/utils/getOpData.js +10 -0
  58. package/dist/json-patch/utils/getType.d.ts +3 -0
  59. package/dist/json-patch/utils/getType.js +6 -0
  60. package/dist/json-patch/utils/index.d.ts +14 -0
  61. package/dist/json-patch/utils/index.js +14 -0
  62. package/dist/json-patch/utils/log.d.ts +2 -0
  63. package/dist/json-patch/utils/log.js +7 -0
  64. package/dist/json-patch/utils/ops.d.ts +14 -0
  65. package/dist/json-patch/utils/ops.js +103 -0
  66. package/dist/json-patch/utils/paths.d.ts +9 -0
  67. package/dist/json-patch/utils/paths.js +53 -0
  68. package/dist/json-patch/utils/pluck.d.ts +5 -0
  69. package/dist/json-patch/utils/pluck.js +30 -0
  70. package/dist/json-patch/utils/shallowCopy.d.ts +1 -0
  71. package/dist/json-patch/utils/shallowCopy.js +20 -0
  72. package/dist/json-patch/utils/softWrites.d.ts +7 -0
  73. package/dist/json-patch/utils/softWrites.js +18 -0
  74. package/dist/json-patch/utils/toArrayIndex.d.ts +1 -0
  75. package/dist/json-patch/utils/toArrayIndex.js +12 -0
  76. package/dist/json-patch/utils/toKeys.d.ts +1 -0
  77. package/dist/json-patch/utils/toKeys.js +15 -0
  78. package/dist/json-patch/utils/updateArrayIndexes.d.ts +5 -0
  79. package/dist/json-patch/utils/updateArrayIndexes.js +38 -0
  80. package/dist/json-patch/utils/updateArrayPath.d.ts +5 -0
  81. package/dist/json-patch/utils/updateArrayPath.js +45 -0
  82. package/dist/net/AbstractTransport.d.ts +47 -0
  83. package/dist/net/AbstractTransport.js +37 -0
  84. package/dist/net/PatchesOfflineFirst.d.ts +3 -0
  85. package/dist/net/PatchesOfflineFirst.js +3 -0
  86. package/dist/net/PatchesRealtime.d.ts +90 -0
  87. package/dist/net/PatchesRealtime.js +257 -0
  88. package/dist/net/index.d.ts +9 -0
  89. package/dist/net/index.js +8 -0
  90. package/dist/net/protocol/JSONRPCClient.d.ts +55 -0
  91. package/dist/net/protocol/JSONRPCClient.js +106 -0
  92. package/dist/net/protocol/types.d.ts +142 -0
  93. package/dist/net/protocol/types.js +1 -0
  94. package/dist/net/webrtc/WebRTCAwareness.d.ts +81 -0
  95. package/dist/net/webrtc/WebRTCAwareness.js +119 -0
  96. package/dist/net/webrtc/WebRTCTransport.d.ts +80 -0
  97. package/dist/net/webrtc/WebRTCTransport.js +157 -0
  98. package/dist/net/websocket/PatchesWebSocket.d.ts +107 -0
  99. package/dist/net/websocket/PatchesWebSocket.js +144 -0
  100. package/dist/net/websocket/SignalingService.d.ts +91 -0
  101. package/dist/net/websocket/SignalingService.js +140 -0
  102. package/dist/net/websocket/WebSocketTransport.d.ts +47 -0
  103. package/dist/net/websocket/WebSocketTransport.js +138 -0
  104. package/dist/persist/IndexedDBStore.d.ts +72 -0
  105. package/dist/persist/IndexedDBStore.js +283 -0
  106. package/dist/persist/index.d.ts +2 -0
  107. package/dist/persist/index.js +1 -0
  108. package/dist/server/BranchManager.d.ts +40 -0
  109. package/dist/server/BranchManager.js +138 -0
  110. package/dist/server/HistoryManager.d.ts +63 -0
  111. package/dist/server/HistoryManager.js +92 -0
  112. package/dist/server/PatchServer.d.ts +129 -0
  113. package/dist/server/PatchServer.js +358 -0
  114. package/dist/server/index.d.ts +4 -0
  115. package/dist/server/index.js +3 -0
  116. package/dist/types.d.ts +158 -0
  117. package/dist/types.js +1 -0
  118. package/dist/utils.d.ts +36 -0
  119. package/dist/utils.js +83 -0
  120. package/package.json +78 -0
@@ -0,0 +1,52 @@
1
+ export interface JSONPatchOpHandler {
2
+ like: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test';
3
+ apply(state: State, path: string, valueOrFrom: any): string | void;
4
+ transform(state: State, other: JSONPatchOp, ops: JSONPatchOp[]): JSONPatchOp[];
5
+ invert(state: State, op: JSONPatchOp, value: any, changedObj: any, isIndex: boolean): JSONPatchOp;
6
+ compose?(state: State, value1: any, value2: any): any;
7
+ }
8
+ export interface JSONPatchOpHandlerMap {
9
+ [key: string]: JSONPatchOpHandler;
10
+ }
11
+ export interface ApplyJSONPatchOptions {
12
+ /**
13
+ * Do not reject patches if error occurs (partial patching)
14
+ */
15
+ partial?: boolean;
16
+ /**
17
+ * Throw an exception if an error occurs when patching
18
+ */
19
+ strict?: boolean;
20
+ /**
21
+ * Stop on error and return the original object (without throwing an exception)
22
+ */
23
+ rigid?: boolean;
24
+ /**
25
+ * Don't log errors when they occurs during patching, if strict is not true, errors will be logged if this is false
26
+ */
27
+ silent?: boolean;
28
+ /**
29
+ * Saves the patch that caused the error to this property of the options object
30
+ */
31
+ error?: JSONPatchOp;
32
+ /**
33
+ * Apply changes at a given path prefix
34
+ */
35
+ atPath?: string;
36
+ }
37
+ export interface JSONPatchOp {
38
+ op: string;
39
+ path: string;
40
+ from?: string;
41
+ value?: any;
42
+ soft?: boolean;
43
+ }
44
+ export interface Root {
45
+ '': any;
46
+ }
47
+ export type State = {
48
+ root: Root;
49
+ types: JSONPatchOpHandlerMap;
50
+ cache: Set<any> | null;
51
+ };
52
+ export type Runner = (state: State) => any;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function deepEqual(a: any, b: any): boolean;
@@ -0,0 +1,33 @@
1
+ export function deepEqual(a, b) {
2
+ if (a === b) {
3
+ return true;
4
+ }
5
+ if (!(a && b) || typeof a !== 'object' || typeof b !== 'object') {
6
+ return false;
7
+ }
8
+ if (a.length !== b.length) {
9
+ return false;
10
+ }
11
+ if (Array.isArray(a)) {
12
+ if (!Array.isArray(b)) {
13
+ return false;
14
+ }
15
+ for (let i = 0, imax = a.length; i < imax; i++) {
16
+ if (!deepEqual(a[i], b[i])) {
17
+ return false;
18
+ }
19
+ }
20
+ return true;
21
+ }
22
+ const aKeys = Object.keys(a);
23
+ if (aKeys.length !== Object.keys(b).length) {
24
+ return false;
25
+ }
26
+ for (let j = 0, jmax = aKeys.length; j < jmax; j++) {
27
+ const key = aKeys[j];
28
+ if (!deepEqual(a[key], b[key])) {
29
+ return false;
30
+ }
31
+ }
32
+ return true;
33
+ }
@@ -0,0 +1,2 @@
1
+ import type { ApplyJSONPatchOptions, JSONPatchOp, State } from '../types.js';
2
+ export declare function exit(state: State, object: any, patch: JSONPatchOp, opts: ApplyJSONPatchOptions): any;
@@ -0,0 +1,4 @@
1
+ export function exit(state, object, patch, opts) {
2
+ opts.error = patch;
3
+ return opts.partial && state.root ? state.root[''] : object;
4
+ }
@@ -0,0 +1,2 @@
1
+ import type { State } from '../types.js';
2
+ export declare function get(state: State, path: string): any;
@@ -0,0 +1,6 @@
1
+ import { getOpData } from './getOpData.js';
2
+ export function get(state, path) {
3
+ // eslint-disable-next-line no-unused-vars
4
+ const [keys, lastKey, target] = getOpData(state, path);
5
+ return target ? target[lastKey] : undefined;
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { State } from '../types.js';
2
+ export declare function getOpData(state: State, path: string, createMissingObjects?: boolean): any[];
@@ -0,0 +1,10 @@
1
+ import { EMPTY, pluck } from './pluck.js';
2
+ import { toKeys } from './toKeys.js';
3
+ export function getOpData(state, path, createMissingObjects) {
4
+ const keys = toKeys(path);
5
+ const lastKey = keys[keys.length - 1];
6
+ let target = pluck(state, keys);
7
+ if (createMissingObjects)
8
+ target = target || EMPTY;
9
+ return [keys, lastKey, target];
10
+ }
@@ -0,0 +1,3 @@
1
+ import type { JSONPatchOp, State } from '../types.js';
2
+ export declare function getType(state: State, patch: JSONPatchOp): import("../types.js").JSONPatchOpHandler;
3
+ export declare function getTypeLike(state: State, patch: JSONPatchOp): "replace" | "add" | "remove" | "move" | "copy" | "test";
@@ -0,0 +1,6 @@
1
+ export function getType(state, patch) {
2
+ return state.types?.[patch.op];
3
+ }
4
+ export function getTypeLike(state, patch) {
5
+ return state.types?.[patch.op]?.like;
6
+ }
@@ -0,0 +1,14 @@
1
+ export * from './deepEqual.js';
2
+ export * from './get.js';
3
+ export * from './getOpData.js';
4
+ export * from './getType.js';
5
+ export * from './log.js';
6
+ export * from './ops.js';
7
+ export * from './paths.js';
8
+ export * from './pluck.js';
9
+ export * from './shallowCopy.js';
10
+ export * from './softWrites.js';
11
+ export * from './toArrayIndex.js';
12
+ export * from './toKeys.js';
13
+ export * from './updateArrayIndexes.js';
14
+ export * from './updateArrayPath.js';
@@ -0,0 +1,14 @@
1
+ export * from './deepEqual.js';
2
+ export * from './get.js';
3
+ export * from './getOpData.js';
4
+ export * from './getType.js';
5
+ export * from './log.js';
6
+ export * from './ops.js';
7
+ export * from './paths.js';
8
+ export * from './pluck.js';
9
+ export * from './shallowCopy.js';
10
+ export * from './softWrites.js';
11
+ export * from './toArrayIndex.js';
12
+ export * from './toKeys.js';
13
+ export * from './updateArrayIndexes.js';
14
+ export * from './updateArrayPath.js';
@@ -0,0 +1,2 @@
1
+ export declare function verbose(value: boolean): void;
2
+ export declare function log(...rest: any[]): void;
@@ -0,0 +1,7 @@
1
+ let displayLogs = false;
2
+ export function verbose(value) {
3
+ displayLogs = value;
4
+ }
5
+ export function log(...rest) {
6
+ displayLogs && console.log(...rest);
7
+ }
@@ -0,0 +1,14 @@
1
+ import type { JSONPatchOp, State } from '../types.js';
2
+ /**
3
+ * Check whether this operation is an add operation of some sort (add, copy, move).
4
+ */
5
+ export declare function isAdd(state: State, op: JSONPatchOp, pathName: 'from' | 'path'): boolean;
6
+ /**
7
+ * Transforms an array of ops, returning the original if there is no change, filtering out ops that are dropped.
8
+ */
9
+ export declare function mapAndFilterOps(ops: JSONPatchOp[], iterator: (op: JSONPatchOp, index: number, breakAfter: (keepRest?: boolean) => {}) => JSONPatchOp | JSONPatchOp[] | null): JSONPatchOp[];
10
+ /**
11
+ * Remove operations that apply to a value which was removed.
12
+ */
13
+ export declare function updateRemovedOps(state: State, thisPath: string, otherOps: JSONPatchOp[], isRemove?: boolean, updatableObject?: boolean, opOp?: string, customHandler?: (op: JSONPatchOp) => any): JSONPatchOp[];
14
+ export declare function transformRemove(state: State, thisPath: string, otherOps: JSONPatchOp[], isRemove?: boolean): JSONPatchOp[];
@@ -0,0 +1,103 @@
1
+ import { getTypeLike } from './getType.js';
2
+ import { log } from './log.js';
3
+ import { isArrayPath } from './paths.js';
4
+ import { updateArrayIndexes } from './updateArrayIndexes.js';
5
+ /**
6
+ * Check whether this operation is an add operation of some sort (add, copy, move).
7
+ */
8
+ export function isAdd(state, op, pathName) {
9
+ const like = getTypeLike(state, op);
10
+ return (like === 'add' || like === 'copy' || like === 'move') && pathName === 'path';
11
+ }
12
+ /**
13
+ * Transforms an array of ops, returning the original if there is no change, filtering out ops that are dropped.
14
+ */
15
+ export function mapAndFilterOps(ops, iterator) {
16
+ let changed = false;
17
+ const mapped = [];
18
+ let shouldBreak = false;
19
+ let keepRest;
20
+ const breakAfter = (keep) => (shouldBreak = true) && (keepRest = keep);
21
+ for (let i = 0; i < ops.length; i++) {
22
+ const original = ops[i];
23
+ // If an op was copied or moved to the same path, it is a no-op and should be removed
24
+ if (original.from === original.path) {
25
+ if (!changed)
26
+ changed = true;
27
+ continue;
28
+ }
29
+ let value = iterator(original, i, breakAfter);
30
+ if (value && !Array.isArray(value) && value.from === value.path)
31
+ value = null;
32
+ if (!changed && value !== original)
33
+ changed = true;
34
+ if (Array.isArray(value))
35
+ mapped.push(...value);
36
+ else if (value)
37
+ mapped.push(value);
38
+ if (shouldBreak) {
39
+ if (keepRest)
40
+ mapped.push(...ops.slice(i + 1));
41
+ break;
42
+ }
43
+ }
44
+ return changed ? mapped : ops;
45
+ }
46
+ /**
47
+ * Remove operations that apply to a value which was removed.
48
+ */
49
+ export function updateRemovedOps(state, thisPath, otherOps, isRemove = false, updatableObject = false, opOp, customHandler) {
50
+ const softPrefixes = new Set();
51
+ return mapAndFilterOps(otherOps, (op, index, breakAfter) => {
52
+ const opLike = getTypeLike(state, op);
53
+ const canMergeCustom = customHandler && opOp === op.op;
54
+ if (thisPath === op.path && opLike !== 'remove' && !canMergeCustom && !op.soft) {
55
+ // Once an operation sets this value again, we can assume the following ops were working on that and not the
56
+ // old value so they can be kept
57
+ if (op.op !== 'test') {
58
+ breakAfter(true); // stop and keep the remaining ops as-is
59
+ }
60
+ return op;
61
+ }
62
+ const { path, from } = op;
63
+ if (path === thisPath && canMergeCustom) {
64
+ const customOp = customHandler(op);
65
+ if (customOp)
66
+ return customOp;
67
+ }
68
+ if (isRemove && !updatableObject && from === thisPath) {
69
+ // Because of the check above, moves and copies will only hit here when the "from" field matches
70
+ if (opLike === 'move') {
71
+ // We need the rest of the otherOps to be adjusted against this "move"
72
+ breakAfter();
73
+ return transformRemove(state, op.path, otherOps.slice(index + 1));
74
+ }
75
+ else if (opLike === 'copy') {
76
+ // We need future ops on the copied object to be removed
77
+ breakAfter();
78
+ let rest = transformRemove(state, thisPath, otherOps.slice(index + 1));
79
+ rest = transformRemove(state, op.path, rest);
80
+ return rest;
81
+ }
82
+ }
83
+ if (op.soft && path === thisPath) {
84
+ softPrefixes.add(path);
85
+ return null;
86
+ }
87
+ const samePath = (!updatableObject && path === thisPath) || (!softPrefixes.has(thisPath) && path.startsWith(`${thisPath}/`));
88
+ const sameFrom = (!updatableObject && from === thisPath) || (!softPrefixes.has(thisPath) && from?.startsWith(`${thisPath}/`));
89
+ if (samePath || sameFrom) {
90
+ log('Removing', op);
91
+ return null;
92
+ }
93
+ return op;
94
+ });
95
+ }
96
+ export function transformRemove(state, thisPath, otherOps, isRemove) {
97
+ if (isArrayPath(thisPath, state)) {
98
+ return updateArrayIndexes(state, thisPath, otherOps, -1, isRemove);
99
+ }
100
+ else {
101
+ return updateRemovedOps(state, thisPath, otherOps, isRemove);
102
+ }
103
+ }
@@ -0,0 +1,9 @@
1
+ import type { State } from '../types.js';
2
+ export declare function getPrefix(path: string): string;
3
+ export declare function getProp(path: string): string;
4
+ export declare function getPrefixAndProp(path: string): [string, string];
5
+ export declare function getPropAfter(path: string, index: number): string;
6
+ export declare function isArrayPath(path: string, state?: State): boolean;
7
+ export declare function getArrayPrefixAndIndex(state: State, path: string, pathLength?: number): [string, number];
8
+ export declare function getArrayIndex(state: State, path: string, pathLength?: number): number;
9
+ export declare function getIndexAndEnd(state: State, path: string | undefined, maxLength: number): number[];
@@ -0,0 +1,53 @@
1
+ import { getOpData } from './getOpData.js';
2
+ const arrayPathExp = /\/(0|[1-9]\d*)$/;
3
+ const EMPTY = [];
4
+ export function getPrefix(path) {
5
+ const lastSlash = path.lastIndexOf('/');
6
+ return path.slice(0, lastSlash + 1);
7
+ }
8
+ export function getProp(path) {
9
+ const lastSlash = path.lastIndexOf('/');
10
+ return path.slice(lastSlash + 1);
11
+ }
12
+ export function getPrefixAndProp(path) {
13
+ const prefix = getPrefix(path);
14
+ return [prefix, path.slice(prefix.length)];
15
+ }
16
+ export function getPropAfter(path, index) {
17
+ const lastSlash = path.indexOf('/', index);
18
+ return path.slice(index, lastSlash === -1 ? undefined : lastSlash);
19
+ }
20
+ export function isArrayPath(path, state) {
21
+ if (!arrayPathExp.test(path))
22
+ return false;
23
+ if (!state || !state.root || !state.root[''])
24
+ return true;
25
+ // Double-check if this is an array or not
26
+ const [_, __, target] = getOpData(state, path);
27
+ return Array.isArray(target) || target == null;
28
+ }
29
+ export function getArrayPrefixAndIndex(state, path, pathLength) {
30
+ if (pathLength)
31
+ path = path.slice(0, path.indexOf('/', pathLength));
32
+ if (!arrayPathExp.test(path))
33
+ return EMPTY;
34
+ const [_, __, target] = getOpData(state, path);
35
+ if (!Array.isArray(target))
36
+ return EMPTY;
37
+ const [prefix, indexStr] = getPrefixAndProp(path);
38
+ const index = parseInt(indexStr);
39
+ return [prefix, index];
40
+ }
41
+ export function getArrayIndex(state, path, pathLength) {
42
+ return getArrayPrefixAndIndex(state, path, pathLength)[1];
43
+ }
44
+ export function getIndexAndEnd(state, path, maxLength) {
45
+ if (!path)
46
+ return [];
47
+ const prop = getPropAfter(path, maxLength);
48
+ const end = maxLength + prop.length;
49
+ if (!isArrayPath(path.slice(0, end), state))
50
+ return [];
51
+ const index = parseInt(prop);
52
+ return [index, end];
53
+ }
@@ -0,0 +1,5 @@
1
+ import type { State } from '../types.js';
2
+ export declare const EMPTY: {};
3
+ export declare function pluck(state: State, keys: string[]): any;
4
+ export declare function pluckWithShallowCopy(state: State, keys: string[], createMissingObjects?: boolean): any;
5
+ export declare function getValue(state: State, value: any, addKey?: string, addValue?: any): any;
@@ -0,0 +1,30 @@
1
+ import { shallowCopy } from './shallowCopy.js';
2
+ export const EMPTY = {};
3
+ export function pluck(state, keys) {
4
+ let object = state.root;
5
+ for (let i = 0, imax = keys.length - 1; i < imax; i++) {
6
+ const key = keys[i];
7
+ if (!object[key]) {
8
+ return null;
9
+ }
10
+ object = object[key];
11
+ }
12
+ return object;
13
+ }
14
+ export function pluckWithShallowCopy(state, keys, createMissingObjects) {
15
+ let object = state.root;
16
+ for (let i = 0, imax = keys.length - 1; i < imax; i++) {
17
+ const key = keys[i];
18
+ object = object[key] = createMissingObjects && !object[key] ? getValue(state, EMPTY) : getValue(state, object[key]);
19
+ }
20
+ return object;
21
+ }
22
+ export function getValue(state, value, addKey, addValue) {
23
+ if (!state.cache?.has(value)) {
24
+ value = shallowCopy(value);
25
+ state.cache?.add(value);
26
+ }
27
+ if (addKey)
28
+ value[addKey] = addValue;
29
+ return value;
30
+ }
@@ -0,0 +1 @@
1
+ export declare function shallowCopy(obj: any): any;
@@ -0,0 +1,20 @@
1
+ export function shallowCopy(obj) {
2
+ if (!obj || typeof obj !== 'object') {
3
+ return obj;
4
+ }
5
+ if (Array.isArray(obj)) {
6
+ const len = obj.length;
7
+ const ary = new Array(len);
8
+ for (let i = 0; i < len; i++) {
9
+ ary[i] = obj[i];
10
+ }
11
+ return ary;
12
+ }
13
+ const keys = Object.keys(obj);
14
+ const copy = {};
15
+ for (let j = 0, jmax = keys.length; j < jmax; j++) {
16
+ const key = keys[j];
17
+ copy[key] = obj[key];
18
+ }
19
+ return copy;
20
+ }
@@ -0,0 +1,7 @@
1
+ import type { JSONPatchOp } from '../types.js';
2
+ export declare function isEmptyObject(value: any): boolean;
3
+ /**
4
+ * If other objects were added to this same path, assume they are maps/hashes/lookups and don't overwrite, allow
5
+ * subsequent ops to merge onto the first map created. `soft` will also do this for any value that already exists.
6
+ */
7
+ export declare function updateSoftWrites(overPath: string, ops: JSONPatchOp[]): JSONPatchOp[];
@@ -0,0 +1,18 @@
1
+ import { log } from './log.js';
2
+ import { mapAndFilterOps } from './ops.js';
3
+ export function isEmptyObject(value) {
4
+ return Boolean(value && typeof value === 'object' && Object.keys(value).length === 0);
5
+ }
6
+ /**
7
+ * If other objects were added to this same path, assume they are maps/hashes/lookups and don't overwrite, allow
8
+ * subsequent ops to merge onto the first map created. `soft` will also do this for any value that already exists.
9
+ */
10
+ export function updateSoftWrites(overPath, ops) {
11
+ return mapAndFilterOps(ops, op => {
12
+ if (op.op === 'add' && op.path === overPath && isEmptyObject(op.value)) {
13
+ log('Removing empty object', op);
14
+ return null;
15
+ }
16
+ return op;
17
+ });
18
+ }
@@ -0,0 +1 @@
1
+ export declare function toArrayIndex(array: any[], str: string): number;
@@ -0,0 +1,12 @@
1
+ export function toArrayIndex(array, str) {
2
+ if (str === '-') {
3
+ return array.length;
4
+ }
5
+ for (let i = 0, imax = str.length; i < imax; i++) {
6
+ const ch = str.charCodeAt(i);
7
+ if (57 < ch || ch < 48) {
8
+ return Infinity;
9
+ }
10
+ }
11
+ return +str;
12
+ }
@@ -0,0 +1 @@
1
+ export declare function toKeys(path: string): string[];
@@ -0,0 +1,15 @@
1
+ function esc(m) {
2
+ return m === '~0' ? '~' : '/';
3
+ }
4
+ export function toKeys(path) {
5
+ const keys = path.split('/');
6
+ if (!path.includes('~')) {
7
+ return keys;
8
+ }
9
+ for (let i = 0, imax = keys.length; i < imax; i++) {
10
+ if (keys[i].includes('~')) {
11
+ keys[i] = keys[i].replace(/~[01]/g, esc);
12
+ }
13
+ }
14
+ return keys;
15
+ }
@@ -0,0 +1,5 @@
1
+ import type { JSONPatchOp, State } from '../types.js';
2
+ /**
3
+ * Update array indexes to account for values being added or removed from an array.
4
+ */
5
+ export declare function updateArrayIndexes(state: State, thisPath: string, otherOps: JSONPatchOp[], modifier: 1 | -1, isRemove?: boolean): JSONPatchOp[];
@@ -0,0 +1,38 @@
1
+ import { getTypeLike } from './getType.js';
2
+ import { log } from './log.js';
3
+ import { isAdd, mapAndFilterOps, transformRemove } from './ops.js';
4
+ import { getPrefixAndProp } from './paths.js';
5
+ import { updateArrayPath } from './updateArrayPath.js';
6
+ /**
7
+ * Update array indexes to account for values being added or removed from an array.
8
+ */
9
+ export function updateArrayIndexes(state, thisPath, otherOps, modifier, isRemove) {
10
+ const [arrayPrefix, indexStr] = getPrefixAndProp(thisPath);
11
+ const index = parseInt(indexStr);
12
+ log('Shifting array indexes', thisPath, modifier);
13
+ // Check ops for any that need to be replaced
14
+ return mapAndFilterOps(otherOps, (op, i, breakAfter) => {
15
+ if (isRemove && thisPath === op.from) {
16
+ const opLike = getTypeLike(state, op);
17
+ if (opLike === 'move') {
18
+ // We need the rest of the otherOps to be adjusted against this "move"
19
+ breakAfter();
20
+ return transformRemove(state, op.path, otherOps.slice(i + 1));
21
+ }
22
+ else if (opLike === 'copy') {
23
+ // We need future ops on the copied object to be removed
24
+ breakAfter();
25
+ let rest = transformRemove(state, thisPath, otherOps.slice(i + 1));
26
+ rest = transformRemove(state, op.path, rest);
27
+ return rest;
28
+ }
29
+ }
30
+ if (op.soft && isAdd(state, op, 'path') && op.path === thisPath) {
31
+ breakAfter(true);
32
+ return null;
33
+ }
34
+ // check for items from the same array that will be affected
35
+ op = updateArrayPath(state, op, 'from', arrayPrefix, index, modifier);
36
+ return op && updateArrayPath(state, op, 'path', arrayPrefix, index, modifier);
37
+ });
38
+ }
@@ -0,0 +1,5 @@
1
+ import type { JSONPatchOp, State } from '../types.js';
2
+ /**
3
+ * Adjust ops within an array
4
+ */
5
+ export declare function updateArrayPath(state: State, otherOp: JSONPatchOp, pathName: 'from' | 'path', thisPrefix: string, thisIndex: number, modifier: 1 | -1): JSONPatchOp | [JSONPatchOp, JSONPatchOp] | null;
@@ -0,0 +1,45 @@
1
+ import { getTypeLike } from './getType.js';
2
+ import { isAdd } from './ops.js';
3
+ import { getIndexAndEnd } from './paths.js';
4
+ import { getValue } from './pluck.js';
5
+ /**
6
+ * Adjust ops within an array
7
+ */
8
+ export function updateArrayPath(state, otherOp, pathName, thisPrefix, thisIndex, modifier) {
9
+ const path = otherOp[pathName];
10
+ if (!path || !path.startsWith(thisPrefix))
11
+ return otherOp;
12
+ const [otherIndex, end] = getIndexAndEnd(state, path, thisPrefix.length);
13
+ const opLike = getTypeLike(state, otherOp);
14
+ // A bit of complex logic to handle moves upwards in an array. Since an item is removed earier in the array and added later, the other index is like it was one less (or this index was one more), so we correct it
15
+ if (opLike === 'move' &&
16
+ pathName === 'path' &&
17
+ otherOp.from?.startsWith(thisPrefix) &&
18
+ getIndexAndEnd(state, otherOp.from, thisPrefix.length)[0] < otherIndex) {
19
+ thisIndex -= 1;
20
+ }
21
+ if (otherIndex < thisIndex)
22
+ return otherOp;
23
+ // When this is a removed item and the op is a subpath or a non-add, remove it.
24
+ if (otherIndex === thisIndex && modifier === -1) {
25
+ if (end === path.length) {
26
+ // If we are adding to the location something got removed, continue adding it.
27
+ if (isAdd(state, otherOp, pathName))
28
+ return otherOp;
29
+ if (otherOp.op === 'replace')
30
+ return getValue(state, otherOp, 'op', 'add');
31
+ // If we are replacing an item which was removed, add it (don't replace something else in the array)
32
+ if (opLike === 'replace')
33
+ return [{ op: 'add', path: otherOp.path, value: null }, otherOp];
34
+ }
35
+ return null;
36
+ }
37
+ else if (isAdd(state, otherOp, pathName) && otherIndex === thisIndex && end === path.length) {
38
+ if (otherOp.soft)
39
+ return null;
40
+ return otherOp;
41
+ }
42
+ const newPath = thisPrefix + (otherIndex + modifier) + path.slice(end);
43
+ otherOp = getValue(state, otherOp, pathName, newPath);
44
+ return otherOp;
45
+ }
@@ -0,0 +1,47 @@
1
+ import type { ConnectionState, Transport } from '../net/protocol/types.js';
2
+ /**
3
+ * Abstract base class that implements common functionality for various transport implementations.
4
+ * Provides state management and event signaling for connection state changes and message reception.
5
+ * Concrete transport implementations must extend this class and implement the abstract methods.
6
+ */
7
+ export declare abstract class AbstractTransport implements Transport {
8
+ private _state;
9
+ /**
10
+ * Signal that emits when the connection state changes.
11
+ * Subscribers will receive the new connection state as an argument.
12
+ */
13
+ readonly onStateChange: import("../event-signal.js").Signal<(state: ConnectionState) => void>;
14
+ /**
15
+ * Signal that emits when a message is received from the transport.
16
+ * Subscribers will receive the message data as a string.
17
+ */
18
+ readonly onMessage: import("../event-signal.js").Signal<(data: string) => void>;
19
+ /**
20
+ * Gets the current connection state of the transport.
21
+ * @returns The current connection state ('connecting', 'connected', 'disconnected', or 'error')
22
+ */
23
+ get state(): ConnectionState;
24
+ /**
25
+ * Sets the connection state and emits a state change event.
26
+ * This method is protected and should only be called by subclasses.
27
+ * @param state - The new connection state
28
+ */
29
+ protected set state(state: ConnectionState);
30
+ /**
31
+ * Establishes the connection for this transport.
32
+ * Must be implemented by concrete subclasses.
33
+ * @returns A promise that resolves when the connection is established
34
+ */
35
+ abstract connect(): Promise<void>;
36
+ /**
37
+ * Terminates the connection for this transport.
38
+ * Must be implemented by concrete subclasses.
39
+ */
40
+ abstract disconnect(): void;
41
+ /**
42
+ * Sends data through this transport.
43
+ * Must be implemented by concrete subclasses.
44
+ * @param data - The string data to send
45
+ */
46
+ abstract send(data: string): void;
47
+ }