@derivesome/core 1.0.0 → 1.0.2
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/.package.json.~undo-tree~ +9 -2
- package/dist/cjs/array-proxy.d.ts.map +1 -1
- package/dist/cjs/array-proxy.js +7 -1
- package/dist/cjs/array-proxy.js.map +1 -1
- package/dist/cjs/common/array.d.ts.map +1 -1
- package/dist/cjs/common/array.js.map +1 -1
- package/dist/cjs/common/diff.d.ts +1 -1
- package/dist/cjs/common/diff.js +5 -5
- package/dist/cjs/common/diff.js.map +1 -1
- package/dist/cjs/common/has.d.ts +2 -2
- package/dist/cjs/common/has.d.ts.map +1 -1
- package/dist/cjs/common/has.js +2 -2
- package/dist/cjs/common/has.js.map +1 -1
- package/dist/cjs/common/index.d.ts +11 -11
- package/dist/cjs/common/match.d.ts +3 -10
- package/dist/cjs/common/match.d.ts.map +1 -1
- package/dist/cjs/common/match.js +29 -29
- package/dist/cjs/common/match.js.map +1 -1
- package/dist/cjs/common/patch.d.ts +1 -1
- package/dist/cjs/common/patch.js +5 -5
- package/dist/cjs/common/types.d.ts +3 -5
- package/dist/cjs/common/types.d.ts.map +1 -1
- package/dist/cjs/common/unique-array.js +1 -1
- package/dist/cjs/common/unique-array.js.map +1 -1
- package/dist/cjs/context.d.ts +2 -1
- package/dist/cjs/context.d.ts.map +1 -1
- package/dist/cjs/context.js +14 -0
- package/dist/cjs/context.js.map +1 -1
- package/dist/cjs/derived.d.ts.map +1 -1
- package/dist/cjs/derived.js +1 -1
- package/dist/cjs/effect.d.ts +2 -2
- package/dist/cjs/effect.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +7 -7
- package/dist/cjs/pubsub.d.ts +3 -2
- package/dist/cjs/pubsub.d.ts.map +1 -1
- package/dist/cjs/pubsub.js +9 -0
- package/dist/cjs/pubsub.js.map +1 -1
- package/dist/cjs/utils/findRefs.d.ts.map +1 -1
- package/dist/cjs/utils/findRefs.js +3 -3
- package/dist/cjs/utils/findRefs.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +2 -1
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/nextTick.d.ts +2 -0
- package/dist/cjs/utils/nextTick.d.ts.map +1 -0
- package/dist/cjs/utils/nextTick.js +13 -0
- package/dist/cjs/utils/nextTick.js.map +1 -0
- package/dist/esm/array-proxy.d.ts.map +1 -1
- package/dist/esm/array-proxy.js +7 -1
- package/dist/esm/array-proxy.js.map +1 -1
- package/dist/esm/common/array.d.ts.map +1 -1
- package/dist/esm/common/array.js.map +1 -1
- package/dist/esm/common/diff.d.ts +1 -1
- package/dist/esm/common/diff.js +6 -6
- package/dist/esm/common/diff.js.map +1 -1
- package/dist/esm/common/has.d.ts +2 -2
- package/dist/esm/common/has.d.ts.map +1 -1
- package/dist/esm/common/has.js +2 -2
- package/dist/esm/common/has.js.map +1 -1
- package/dist/esm/common/index.d.ts +11 -11
- package/dist/esm/common/index.js +11 -11
- package/dist/esm/common/match.d.ts +3 -10
- package/dist/esm/common/match.d.ts.map +1 -1
- package/dist/esm/common/match.js +29 -29
- package/dist/esm/common/match.js.map +1 -1
- package/dist/esm/common/patch.d.ts +1 -1
- package/dist/esm/common/patch.js +5 -5
- package/dist/esm/common/types.d.ts +3 -5
- package/dist/esm/common/types.d.ts.map +1 -1
- package/dist/esm/common/unique-array.js +1 -1
- package/dist/esm/common/unique-array.js.map +1 -1
- package/dist/esm/context.d.ts +2 -1
- package/dist/esm/context.d.ts.map +1 -1
- package/dist/esm/context.js +14 -0
- package/dist/esm/context.js.map +1 -1
- package/dist/esm/derived.d.ts.map +1 -1
- package/dist/esm/derived.js +1 -1
- package/dist/esm/effect.d.ts +2 -2
- package/dist/esm/effect.d.ts.map +1 -1
- package/dist/esm/index.d.ts +7 -7
- package/dist/esm/index.js +7 -7
- package/dist/esm/pubsub.d.ts +3 -2
- package/dist/esm/pubsub.d.ts.map +1 -1
- package/dist/esm/pubsub.js +9 -0
- package/dist/esm/pubsub.js.map +1 -1
- package/dist/esm/utils/findRefs.d.ts.map +1 -1
- package/dist/esm/utils/findRefs.js +3 -3
- package/dist/esm/utils/findRefs.js.map +1 -1
- package/dist/esm/utils/index.d.ts +2 -1
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +2 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/nextTick.d.ts +2 -0
- package/dist/esm/utils/nextTick.d.ts.map +1 -0
- package/dist/esm/utils/nextTick.js +9 -0
- package/dist/esm/utils/nextTick.js.map +1 -0
- package/package.json +13 -11
- package/package.json~ +2 -2
- package/src/.context.ts.~undo-tree~ +12 -0
- package/src/.effect.ts.~undo-tree~ +8 -0
- package/src/.pubsub.ts.~undo-tree~ +33 -0
- package/src/array-proxy.ts +18 -3
- package/src/common/.types.ts.~undo-tree~ +5 -0
- package/src/common/array.ts +6 -5
- package/src/common/diff.test.ts +40 -43
- package/src/common/diff.ts +13 -8
- package/src/common/has.ts +15 -5
- package/src/common/index.ts +11 -11
- package/src/common/match.ts +58 -47
- package/src/common/patch.test.ts +26 -24
- package/src/common/patch.ts +6 -6
- package/src/common/string.test.ts +13 -13
- package/src/common/types.ts +32 -34
- package/src/common/types.ts~ +61 -0
- package/src/common/unique-array.ts +1 -1
- package/src/context.ts +18 -2
- package/src/context.ts~ +36 -0
- package/src/derived.ts +2 -2
- package/src/effect.ts +2 -2
- package/src/effect.ts~ +9 -0
- package/src/index.ts +7 -7
- package/src/pubsub.ts +20 -5
- package/src/pubsub.ts~ +82 -0
- package/src/utils/.index.ts.~undo-tree~ +7 -0
- package/src/utils/.nextTick.ts.~undo-tree~ +6 -0
- package/src/utils/findRefs.ts +5 -7
- package/src/utils/index.ts +2 -1
- package/src/utils/index.ts~ +2 -0
- package/src/utils/nextTick.ts +7 -0
- package/src/utils/nextTick.ts~ +7 -0
package/src/common/diff.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { isPlainObject } from
|
|
1
|
+
import { isPlainObject } from "./is";
|
|
2
2
|
|
|
3
|
-
export type DiffType =
|
|
3
|
+
export type DiffType = "added" | "removed" | "changed";
|
|
4
4
|
|
|
5
5
|
export type Diff = {
|
|
6
6
|
type: DiffType;
|
|
@@ -15,7 +15,12 @@ export const diff = <T>(a: T, b: T): Diff[] => {
|
|
|
15
15
|
return diffs;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
const diffRecursive = (
|
|
18
|
+
const diffRecursive = (
|
|
19
|
+
a: unknown,
|
|
20
|
+
b: unknown,
|
|
21
|
+
path: PropertyKey[],
|
|
22
|
+
diffs: Diff[],
|
|
23
|
+
): void => {
|
|
19
24
|
if (a === b) return;
|
|
20
25
|
|
|
21
26
|
const aIsArray = Array.isArray(a);
|
|
@@ -34,7 +39,7 @@ const diffRecursive = (a: unknown, b: unknown, path: PropertyKey[], diffs: Diff[
|
|
|
34
39
|
return;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
diffs.push({ type:
|
|
42
|
+
diffs.push({ type: "changed", path, oldValue: a, newValue: b });
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
const diffObjects = (
|
|
@@ -50,7 +55,7 @@ const diffObjects = (
|
|
|
50
55
|
for (const key of aKeys) {
|
|
51
56
|
const childPath = [...path, key];
|
|
52
57
|
if (!bKeySet.has(key)) {
|
|
53
|
-
diffs.push({ type:
|
|
58
|
+
diffs.push({ type: "removed", path: childPath, oldValue: a[key] });
|
|
54
59
|
} else {
|
|
55
60
|
diffRecursive(a[key], b[key], childPath, diffs);
|
|
56
61
|
}
|
|
@@ -59,7 +64,7 @@ const diffObjects = (
|
|
|
59
64
|
const aKeySet = new Set(aKeys);
|
|
60
65
|
for (const key of bKeys) {
|
|
61
66
|
if (!aKeySet.has(key)) {
|
|
62
|
-
diffs.push({ type:
|
|
67
|
+
diffs.push({ type: "added", path: [...path, key], newValue: b[key] });
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
};
|
|
@@ -75,9 +80,9 @@ const diffArrays = (
|
|
|
75
80
|
for (let i = 0; i < maxLen; i++) {
|
|
76
81
|
const childPath = [...path, i];
|
|
77
82
|
if (i >= a.length) {
|
|
78
|
-
diffs.push({ type:
|
|
83
|
+
diffs.push({ type: "added", path: childPath, newValue: b[i] });
|
|
79
84
|
} else if (i >= b.length) {
|
|
80
|
-
diffs.push({ type:
|
|
85
|
+
diffs.push({ type: "removed", path: childPath, oldValue: a[i] });
|
|
81
86
|
} else {
|
|
82
87
|
diffRecursive(a[i], b[i], childPath, diffs);
|
|
83
88
|
}
|
package/src/common/has.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
export function has<Keys extends [...PropertyKey[]]>(
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export function has<Keys extends [...PropertyKey[]]>(
|
|
2
|
+
x: unknown,
|
|
3
|
+
...keys: Keys
|
|
4
|
+
): x is {
|
|
5
|
+
[Prop in keyof Keys as [Keys[Prop]] extends [string]
|
|
6
|
+
? Keys[Prop]
|
|
7
|
+
: never]: unknown;
|
|
8
|
+
} {
|
|
9
|
+
if (typeof x === "undefined" || x === null) return false;
|
|
10
|
+
if (
|
|
11
|
+
keys.every(
|
|
12
|
+
(key) => Object.hasOwn(x, key) || (typeof x === "object" && key in x),
|
|
13
|
+
)
|
|
14
|
+
)
|
|
15
|
+
return true;
|
|
6
16
|
return false;
|
|
7
17
|
}
|
package/src/common/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./is";
|
|
3
|
+
export * from "./stack";
|
|
4
|
+
export * from "./compare";
|
|
5
|
+
export * from "./unique-array";
|
|
6
|
+
export * from "./has";
|
|
7
|
+
export * from "./array";
|
|
8
|
+
export * from "./diff";
|
|
9
|
+
export * from "./patch";
|
|
10
|
+
export * from "./match";
|
|
11
|
+
export * from "./string";
|
package/src/common/match.ts
CHANGED
|
@@ -4,43 +4,51 @@ import { IsLiteral } from "./types";
|
|
|
4
4
|
export type ToPattern<
|
|
5
5
|
T,
|
|
6
6
|
U extends Exclude<T, undefined> = Exclude<T, undefined>,
|
|
7
|
-
> =
|
|
8
|
-
|
|
9
|
-
: [U] extends [string]
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
> = [IsLiteral<T>] extends [true]
|
|
8
|
+
? U
|
|
9
|
+
: [U] extends [string]
|
|
10
|
+
? string
|
|
11
|
+
: [U] extends [number]
|
|
12
|
+
? number
|
|
13
|
+
: [U] extends [boolean]
|
|
14
|
+
? boolean
|
|
15
|
+
: [U] extends [BigInt]
|
|
16
|
+
? BigInt
|
|
17
|
+
: [U] extends [null]
|
|
18
|
+
? null
|
|
19
|
+
: [U] extends [Array<infer A>]
|
|
20
|
+
? Array<ToPattern<A>>
|
|
21
|
+
: [U] extends [Record<PropertyKey, unknown>]
|
|
22
|
+
? {
|
|
23
|
+
[P in keyof U]?: ToPattern<U[P]>;
|
|
24
|
+
}
|
|
25
|
+
: T;
|
|
20
26
|
|
|
21
|
-
export type Unpattern<I, P> =
|
|
22
|
-
[
|
|
23
|
-
|
|
24
|
-
P
|
|
27
|
+
export type Unpattern<I, P> = [IsLiteral<I>, IsLiteral<P>] extends [true, true]
|
|
28
|
+
? [I] extends [P]
|
|
29
|
+
? P
|
|
25
30
|
: never
|
|
26
|
-
: [I, P] extends [string, string]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
: [I, P] extends [string, string]
|
|
32
|
+
? P
|
|
33
|
+
: [I, P] extends [number, number]
|
|
34
|
+
? P
|
|
35
|
+
: [I, P] extends [boolean, boolean]
|
|
36
|
+
? P
|
|
37
|
+
: [I, P] extends [BigInt, BigInt]
|
|
38
|
+
? P
|
|
39
|
+
: [I, P] extends [Array<infer AI>, Array<infer AP>]
|
|
40
|
+
? Array<Unpattern<AI, AP>>
|
|
41
|
+
: [[I], [P]] extends [
|
|
42
|
+
[infer AI extends Record<PropertyKey, unknown>],
|
|
43
|
+
[infer AP extends Record<PropertyKey, unknown>],
|
|
44
|
+
]
|
|
45
|
+
? Extract<
|
|
46
|
+
AI,
|
|
47
|
+
{
|
|
48
|
+
[KP in keyof AP]: AP[KP];
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
: "ERROR";
|
|
44
52
|
|
|
45
53
|
export function match<Input, const Pattern extends ToPattern<Input>>(
|
|
46
54
|
input: Input | Unpattern<Input, Pattern>,
|
|
@@ -54,14 +62,18 @@ export function match<Input, const Pattern extends ToPattern<Input>>(
|
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
switch (typeof pattern) {
|
|
57
|
-
case
|
|
58
|
-
case
|
|
59
|
-
case
|
|
60
|
-
case
|
|
61
|
-
case
|
|
62
|
-
case
|
|
63
|
-
|
|
64
|
-
case
|
|
65
|
+
case "string":
|
|
66
|
+
case "boolean":
|
|
67
|
+
case "undefined":
|
|
68
|
+
case "function":
|
|
69
|
+
case "symbol":
|
|
70
|
+
case "number":
|
|
71
|
+
return input === pattern;
|
|
72
|
+
case "bigint":
|
|
73
|
+
return (
|
|
74
|
+
typeof input === "bigint" && input.toString() === pattern.toString()
|
|
75
|
+
);
|
|
76
|
+
case "object": {
|
|
65
77
|
if (isPlainObject(input) && isPlainObject(pattern)) {
|
|
66
78
|
for (const [k, pv] of Object.entries(pattern)) {
|
|
67
79
|
const iv = input[k];
|
|
@@ -76,15 +88,14 @@ export function match<Input, const Pattern extends ToPattern<Input>>(
|
|
|
76
88
|
const pv = pattern[symbol];
|
|
77
89
|
if (!match(iv, pv)) return false;
|
|
78
90
|
}
|
|
79
|
-
|
|
91
|
+
|
|
80
92
|
return true;
|
|
81
93
|
} else {
|
|
82
94
|
console.error(`Tried to match`, input, pattern);
|
|
83
95
|
throw new Error(`Cannot match`);
|
|
84
96
|
}
|
|
85
|
-
}
|
|
97
|
+
}
|
|
86
98
|
}
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
|
|
89
100
|
return false;
|
|
90
101
|
}
|
package/src/common/patch.test.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { describe, it, assert } from
|
|
2
|
-
import { diff } from
|
|
3
|
-
import { patch } from
|
|
1
|
+
import { describe, it, assert } from "vitest";
|
|
2
|
+
import { diff } from "./diff";
|
|
3
|
+
import { patch } from "./patch";
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
it(
|
|
5
|
+
describe("patch", () => {
|
|
6
|
+
it("should patch a changed primitive at root", () => {
|
|
7
7
|
const diffs = diff(1, 2);
|
|
8
8
|
const result = patch(1, diffs);
|
|
9
9
|
assert.equal(result, 2);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
it(
|
|
12
|
+
it("should patch changed object properties in-place", () => {
|
|
13
13
|
const target = { a: 1, b: 2 };
|
|
14
14
|
const ref = target;
|
|
15
15
|
const diffs = diff(target, { a: 1, b: 99 });
|
|
@@ -18,7 +18,7 @@ describe('patch', () => {
|
|
|
18
18
|
assert.deepEqual(result, { a: 1, b: 99 });
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it(
|
|
21
|
+
it("should add new properties in-place", () => {
|
|
22
22
|
const target: Record<string, number> = { a: 1 };
|
|
23
23
|
const ref = target;
|
|
24
24
|
const diffs = diff(target, { a: 1, b: 2 });
|
|
@@ -27,17 +27,17 @@ describe('patch', () => {
|
|
|
27
27
|
assert.deepEqual(result, { a: 1, b: 2 });
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
it(
|
|
30
|
+
it("should remove properties in-place", () => {
|
|
31
31
|
const target: Record<string, number> = { a: 1, b: 2 };
|
|
32
32
|
const ref = target;
|
|
33
33
|
const diffs = diff(target, { a: 1 });
|
|
34
34
|
const result = patch(target, diffs);
|
|
35
35
|
assert.strictEqual(result, ref);
|
|
36
36
|
assert.deepEqual(result, { a: 1 });
|
|
37
|
-
assert.isFalse(
|
|
37
|
+
assert.isFalse("b" in result);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
it(
|
|
40
|
+
it("should patch nested objects in-place", () => {
|
|
41
41
|
const target = { x: { y: { z: 1 } } };
|
|
42
42
|
const innerRef = target.x.y;
|
|
43
43
|
const diffs = diff(target, { x: { y: { z: 42 } } });
|
|
@@ -46,7 +46,7 @@ describe('patch', () => {
|
|
|
46
46
|
assert.deepEqual(result, { x: { y: { z: 42 } } });
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it(
|
|
49
|
+
it("should patch array elements in-place", () => {
|
|
50
50
|
const target = [1, 2, 3];
|
|
51
51
|
const ref = target;
|
|
52
52
|
const diffs = diff(target, [1, 99, 3]);
|
|
@@ -55,7 +55,7 @@ describe('patch', () => {
|
|
|
55
55
|
assert.deepEqual(result, [1, 99, 3]);
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
it(
|
|
58
|
+
it("should add array elements in-place", () => {
|
|
59
59
|
const target = [1, 2];
|
|
60
60
|
const ref = target;
|
|
61
61
|
const diffs = diff(target, [1, 2, 3]);
|
|
@@ -64,7 +64,7 @@ describe('patch', () => {
|
|
|
64
64
|
assert.deepEqual(result, [1, 2, 3]);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it(
|
|
67
|
+
it("should remove array elements in-place", () => {
|
|
68
68
|
const target = [1, 2, 3];
|
|
69
69
|
const ref = target;
|
|
70
70
|
const diffs = diff(target, [1, 2]);
|
|
@@ -73,7 +73,7 @@ describe('patch', () => {
|
|
|
73
73
|
assert.deepEqual(result, [1, 2]);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
it(
|
|
76
|
+
it("should patch nested arrays inside objects in-place", () => {
|
|
77
77
|
const target = { items: [10, 20, 30] };
|
|
78
78
|
const arrRef = target.items;
|
|
79
79
|
const diffs = diff(target, { items: [10, 25, 30] });
|
|
@@ -82,24 +82,26 @@ describe('patch', () => {
|
|
|
82
82
|
assert.deepEqual(result, { items: [10, 25, 30] });
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
it(
|
|
86
|
-
const target = { users: [{ name:
|
|
85
|
+
it("should patch deeply nested mixed structures in-place", () => {
|
|
86
|
+
const target = { users: [{ name: "Alice", scores: [10, 20] }] };
|
|
87
87
|
const scoresRef = target.users[0]!.scores;
|
|
88
|
-
const diffs = diff(target, {
|
|
88
|
+
const diffs = diff(target, {
|
|
89
|
+
users: [{ name: "Alice", scores: [10, 30] }],
|
|
90
|
+
});
|
|
89
91
|
const result = patch(target, diffs);
|
|
90
92
|
assert.strictEqual(result.users[0]!.scores, scoresRef);
|
|
91
|
-
assert.deepEqual(result, { users: [{ name:
|
|
93
|
+
assert.deepEqual(result, { users: [{ name: "Alice", scores: [10, 30] }] });
|
|
92
94
|
});
|
|
93
95
|
|
|
94
|
-
it(
|
|
96
|
+
it("should handle type change from object to primitive", () => {
|
|
95
97
|
const target: any = { a: { b: 1 } };
|
|
96
98
|
const diffs = diff(target, { a: 42 } as any);
|
|
97
99
|
const result = patch(target, diffs);
|
|
98
100
|
assert.deepEqual(result, { a: 42 });
|
|
99
101
|
});
|
|
100
102
|
|
|
101
|
-
it(
|
|
102
|
-
const sym = Symbol(
|
|
103
|
+
it("should handle symbol keys", () => {
|
|
104
|
+
const sym = Symbol("test");
|
|
103
105
|
const target = { [sym]: 1 };
|
|
104
106
|
const ref = target;
|
|
105
107
|
const diffs = diff(target, { [sym]: 2 });
|
|
@@ -108,9 +110,9 @@ describe('patch', () => {
|
|
|
108
110
|
assert.equal(result[sym], 2);
|
|
109
111
|
});
|
|
110
112
|
|
|
111
|
-
it(
|
|
112
|
-
const a = { x: 1, y: [1, 2, 3], z: { nested: true, value:
|
|
113
|
-
const b = { x: 2, y: [1, 3], z: { nested: false, extra:
|
|
113
|
+
it("should produce the same result as b when diffing a->b and patching a", () => {
|
|
114
|
+
const a = { x: 1, y: [1, 2, 3], z: { nested: true, value: "hello" } };
|
|
115
|
+
const b = { x: 2, y: [1, 3], z: { nested: false, extra: "world" } };
|
|
114
116
|
const diffs = diff(a, b as any);
|
|
115
117
|
const result = patch(a, diffs);
|
|
116
118
|
assert.deepEqual(result, b as unknown);
|
package/src/common/patch.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Diff } from
|
|
1
|
+
import type { Diff } from "./diff";
|
|
2
2
|
|
|
3
3
|
export const patch = <T>(target: T, diffs: Diff[]): T => {
|
|
4
4
|
for (const d of diffs) {
|
|
@@ -9,7 +9,7 @@ export const patch = <T>(target: T, diffs: Diff[]): T => {
|
|
|
9
9
|
|
|
10
10
|
const applyDiff = <T>(target: T, d: Diff, depth: number): T => {
|
|
11
11
|
if (depth === d.path.length) {
|
|
12
|
-
if (d.type ===
|
|
12
|
+
if (d.type === "changed" || d.type === "added") {
|
|
13
13
|
return d.newValue as T;
|
|
14
14
|
}
|
|
15
15
|
return undefined as T;
|
|
@@ -20,9 +20,9 @@ const applyDiff = <T>(target: T, d: Diff, depth: number): T => {
|
|
|
20
20
|
if (Array.isArray(target)) {
|
|
21
21
|
const index = key as number;
|
|
22
22
|
|
|
23
|
-
if (d.type ===
|
|
23
|
+
if (d.type === "removed" && depth === d.path.length - 1) {
|
|
24
24
|
target.splice(index, 1);
|
|
25
|
-
} else if (d.type ===
|
|
25
|
+
} else if (d.type === "added" && depth === d.path.length - 1) {
|
|
26
26
|
target.splice(index, 0, d.newValue);
|
|
27
27
|
} else {
|
|
28
28
|
target[index] = applyDiff(target[index], d, depth + 1);
|
|
@@ -31,10 +31,10 @@ const applyDiff = <T>(target: T, d: Diff, depth: number): T => {
|
|
|
31
31
|
return target;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
if (typeof target ===
|
|
34
|
+
if (typeof target === "object" && target !== null) {
|
|
35
35
|
const obj = target as Record<PropertyKey, unknown>;
|
|
36
36
|
|
|
37
|
-
if (d.type ===
|
|
37
|
+
if (d.type === "removed" && depth === d.path.length - 1) {
|
|
38
38
|
delete obj[key];
|
|
39
39
|
} else {
|
|
40
40
|
obj[key] = applyDiff(obj[key], d, depth + 1);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { describe, assert } from
|
|
2
|
-
import { unjoin } from
|
|
1
|
+
import { describe, assert } from "vitest";
|
|
2
|
+
import { unjoin } from "./string";
|
|
3
3
|
|
|
4
4
|
describe("unjoin", (it) => {
|
|
5
5
|
it("unjoins a '/' joined string", () => {
|
|
6
|
-
const path: string =
|
|
7
|
-
const parts = unjoin(path,
|
|
6
|
+
const path: string = "foo/bar/baz";
|
|
7
|
+
const parts = unjoin(path, "/");
|
|
8
8
|
|
|
9
9
|
console.log(parts);
|
|
10
10
|
|
|
11
|
-
const expected: string[] = [
|
|
11
|
+
const expected: string[] = ["foo", "/", "bar", "/", "baz"];
|
|
12
12
|
assert.strictEqual(parts.length, expected.length);
|
|
13
13
|
|
|
14
14
|
for (let i = 0; i < expected.length; i++) {
|
|
@@ -17,12 +17,12 @@ describe("unjoin", (it) => {
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("unjoins a '/' joined string starting with '/'", () => {
|
|
20
|
-
const path: string =
|
|
21
|
-
const parts = unjoin(path,
|
|
20
|
+
const path: string = "/foo/bar/baz";
|
|
21
|
+
const parts = unjoin(path, "/");
|
|
22
22
|
|
|
23
23
|
console.log(parts);
|
|
24
24
|
|
|
25
|
-
const expected: string[] = [
|
|
25
|
+
const expected: string[] = ["/", "foo", "/", "bar", "/", "baz"];
|
|
26
26
|
assert.strictEqual(parts.length, expected.length);
|
|
27
27
|
|
|
28
28
|
for (let i = 0; i < expected.length; i++) {
|
|
@@ -31,16 +31,16 @@ describe("unjoin", (it) => {
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
it("unjoins a non-joined string", () => {
|
|
34
|
-
const path: string =
|
|
35
|
-
const parts = unjoin(path,
|
|
34
|
+
const path: string = "foobar";
|
|
35
|
+
const parts = unjoin(path, "/");
|
|
36
36
|
|
|
37
37
|
console.log(parts);
|
|
38
38
|
|
|
39
|
-
const expected: string[] = [
|
|
39
|
+
const expected: string[] = ["foobar"];
|
|
40
40
|
assert.strictEqual(parts.length, expected.length);
|
|
41
41
|
|
|
42
42
|
for (let i = 0; i < expected.length; i++) {
|
|
43
43
|
assert.strictEqual(parts[i], expected[i]);
|
|
44
44
|
}
|
|
45
|
-
})
|
|
46
|
-
})
|
|
45
|
+
});
|
|
46
|
+
});
|
package/src/common/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type Loose = any;
|
|
2
2
|
export type Boxed<T> = { value: T };
|
|
3
|
-
export type Subscription<T> = (value: T) => void
|
|
3
|
+
export type Subscription<T> = (value: T) => void | Promise<void>;
|
|
4
4
|
export type SubscriptionCaller<T> = (fn: Subscription<T>, value: T) => void;
|
|
5
5
|
export type ReturningFunction<T> = () => T;
|
|
6
6
|
export type VoidFunction = () => void;
|
|
@@ -15,15 +15,14 @@ export type LooseDict = Record<string, Loose>;
|
|
|
15
15
|
export type SubscribeOptions = {
|
|
16
16
|
cleanup?: VoidFunction;
|
|
17
17
|
immediate?: boolean;
|
|
18
|
-
}
|
|
18
|
+
};
|
|
19
19
|
|
|
20
20
|
export type Distribute<T> = T extends T ? T : never;
|
|
21
21
|
|
|
22
|
-
export type UnionToIntersection<union> =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
intersection
|
|
22
|
+
export type UnionToIntersection<union> = (
|
|
23
|
+
union extends Loose ? (k: union) => void : never
|
|
24
|
+
) extends (k: infer intersection) => void
|
|
25
|
+
? intersection
|
|
27
26
|
: never;
|
|
28
27
|
|
|
29
28
|
export type IsUnion<a> = [a] extends [UnionToIntersection<a>] ? false : true;
|
|
@@ -31,31 +30,30 @@ export type IsUnion<a> = [a] extends [UnionToIntersection<a>] ? false : true;
|
|
|
31
30
|
export type UnionToTuple<union, output extends Loose[] = []> =
|
|
32
31
|
UnionToIntersection<
|
|
33
32
|
union extends Loose ? (t: union) => union : never
|
|
34
|
-
> extends (_: Loose) => infer elem
|
|
35
|
-
UnionToTuple<Exclude<union, elem>, [elem, ...output]>
|
|
36
|
-
|
|
33
|
+
> extends (_: Loose) => infer elem
|
|
34
|
+
? UnionToTuple<Exclude<union, elem>, [elem, ...output]>
|
|
35
|
+
: output;
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
[a] extends [
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
: false;
|
|
37
|
+
export type IsLiteral<a> = [a] extends [null | undefined]
|
|
38
|
+
? true
|
|
39
|
+
: [a] extends [string]
|
|
40
|
+
? string extends a
|
|
41
|
+
? false
|
|
42
|
+
: true
|
|
43
|
+
: [a] extends [number]
|
|
44
|
+
? number extends a
|
|
45
|
+
? false
|
|
46
|
+
: true
|
|
47
|
+
: [a] extends [boolean]
|
|
48
|
+
? boolean extends a
|
|
49
|
+
? false
|
|
50
|
+
: true
|
|
51
|
+
: [a] extends [symbol]
|
|
52
|
+
? symbol extends a
|
|
53
|
+
? false
|
|
54
|
+
: true
|
|
55
|
+
: [a] extends [bigint]
|
|
56
|
+
? bigint extends a
|
|
57
|
+
? false
|
|
58
|
+
: true
|
|
59
|
+
: false;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type Loose = any;
|
|
2
|
+
export type Boxed<T> = { value: T };
|
|
3
|
+
export type Subscription<T> = (value: T) => void;
|
|
4
|
+
export type SubscriptionCaller<T> = (fn: Subscription<T>, value: T) => void;
|
|
5
|
+
export type ReturningFunction<T> = () => T;
|
|
6
|
+
export type VoidFunction = () => void;
|
|
7
|
+
export type VoidAsyncFunction = () => void;
|
|
8
|
+
export type VoidSyncOrAsyncFunction = () => void | Promise<void>;
|
|
9
|
+
export type LooseFunction = (...args: Loose[]) => Loose;
|
|
10
|
+
export type SetStateAction<S> = S | ((prevState: S) => S);
|
|
11
|
+
export type Dispatch<A> = (value: A) => void;
|
|
12
|
+
export type UnknownDict = Record<string, unknown>;
|
|
13
|
+
export type LooseDict = Record<string, Loose>;
|
|
14
|
+
|
|
15
|
+
export type SubscribeOptions = {
|
|
16
|
+
cleanup?: VoidFunction;
|
|
17
|
+
immediate?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type Distribute<T> = T extends T ? T : never;
|
|
21
|
+
|
|
22
|
+
export type UnionToIntersection<union> =
|
|
23
|
+
(union extends Loose ? (k: union) => void : never) extends (
|
|
24
|
+
(k: infer intersection) => void
|
|
25
|
+
) ?
|
|
26
|
+
intersection
|
|
27
|
+
: never;
|
|
28
|
+
|
|
29
|
+
export type IsUnion<a> = [a] extends [UnionToIntersection<a>] ? false : true;
|
|
30
|
+
|
|
31
|
+
export type UnionToTuple<union, output extends Loose[] = []> =
|
|
32
|
+
UnionToIntersection<
|
|
33
|
+
union extends Loose ? (t: union) => union : never
|
|
34
|
+
> extends (_: Loose) => infer elem ?
|
|
35
|
+
UnionToTuple<Exclude<union, elem>, [elem, ...output]>
|
|
36
|
+
: output;
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
export type IsLiteral<a> =
|
|
40
|
+
[a] extends [null | undefined] ? true
|
|
41
|
+
: [a] extends [string] ?
|
|
42
|
+
string extends a ?
|
|
43
|
+
false
|
|
44
|
+
: true
|
|
45
|
+
: [a] extends [number] ?
|
|
46
|
+
number extends a ?
|
|
47
|
+
false
|
|
48
|
+
: true
|
|
49
|
+
: [a] extends [boolean] ?
|
|
50
|
+
boolean extends a ?
|
|
51
|
+
false
|
|
52
|
+
: true
|
|
53
|
+
: [a] extends [symbol] ?
|
|
54
|
+
symbol extends a ?
|
|
55
|
+
false
|
|
56
|
+
: true
|
|
57
|
+
: [a] extends [bigint] ?
|
|
58
|
+
bigint extends a ?
|
|
59
|
+
false
|
|
60
|
+
: true
|
|
61
|
+
: false;
|
|
@@ -71,7 +71,7 @@ export class UniqueArray<T> {
|
|
|
71
71
|
if (offset < 0 || offset >= this.length) {
|
|
72
72
|
return this.push(...values);
|
|
73
73
|
}
|
|
74
|
-
const items = Array.from(new Set(values.filter(x => !this.includes(x))));
|
|
74
|
+
const items = Array.from(new Set(values.filter((x) => !this.includes(x))));
|
|
75
75
|
const left = this.items.slice(0, offset);
|
|
76
76
|
const right = this.items.slice(offset);
|
|
77
77
|
this.clear();
|
package/src/context.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFunction } from "./common/is";
|
|
2
2
|
import { Stack } from "./common/stack";
|
|
3
|
-
import { VoidFunction } from "./common/types";
|
|
3
|
+
import { VoidFunction, VoidSyncOrAsyncFunction } from "./common/types";
|
|
4
4
|
import { Effect } from "./effect";
|
|
5
5
|
|
|
6
6
|
export type ScopeData = {
|
|
@@ -20,7 +20,23 @@ export class Context {
|
|
|
20
20
|
return this.scopes.current;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
static
|
|
23
|
+
static async runEffectAsync(
|
|
24
|
+
effect: VoidSyncOrAsyncFunction | Effect,
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
if (isFunction(effect)) {
|
|
27
|
+
const prev = this.scope.current.effect;
|
|
28
|
+
this.scope.current.effect = { run: effect };
|
|
29
|
+
await effect();
|
|
30
|
+
this.scope.current.effect = prev;
|
|
31
|
+
} else {
|
|
32
|
+
const prev = this.scope.current.effect;
|
|
33
|
+
this.scope.current.effect = effect;
|
|
34
|
+
await effect.run();
|
|
35
|
+
this.scope.current.effect = prev;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static runEffect(effect: VoidFunction | Effect): void {
|
|
24
40
|
if (isFunction(effect)) {
|
|
25
41
|
const prev = this.scope.current.effect;
|
|
26
42
|
this.scope.current.effect = { run: effect };
|