effectweb 0.2.1 → 0.2.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/dist/cache.js +3 -3
- package/dist/collection.d.ts +4 -0
- package/dist/collection.js +44 -1
- package/dist/query.d.ts +4 -0
- package/dist/share.d.ts +5 -1
- package/dist/share.js +9 -3
- package/package.json +1 -1
package/dist/cache.js
CHANGED
|
@@ -13,7 +13,7 @@ function createQueryCache(runtime) {
|
|
|
13
13
|
const registry = AtomRegistry.make({ defaultIdleTTL: 30_000 });
|
|
14
14
|
const generation = Atom.keepAlive(Atom.make(0));
|
|
15
15
|
const resources = new Map();
|
|
16
|
-
const acquire = (key, load) => {
|
|
16
|
+
const acquire = (key, load, share = shareValue) => {
|
|
17
17
|
let entry = resources.get(key);
|
|
18
18
|
if (entry)
|
|
19
19
|
entry.load = load;
|
|
@@ -24,7 +24,7 @@ function createQueryCache(runtime) {
|
|
|
24
24
|
const previous = Option.flatMap(get.self(), AsyncResult.value);
|
|
25
25
|
return loadEffect(() => next.load()).pipe(Effect.map((value) => {
|
|
26
26
|
next.loadedAt = Date.now();
|
|
27
|
-
return Option.isSome(previous) ?
|
|
27
|
+
return Option.isSome(previous) ? share(previous.value, value) : value;
|
|
28
28
|
}));
|
|
29
29
|
}),
|
|
30
30
|
};
|
|
@@ -47,7 +47,7 @@ function createQueryCache(runtime) {
|
|
|
47
47
|
const { entry, atom } = acquire(queryKey(definition, args), () => {
|
|
48
48
|
const effect = Effect.suspend(() => definition.load(args));
|
|
49
49
|
return runtime ? runtime.provide(effect) : effect;
|
|
50
|
-
});
|
|
50
|
+
}, definition.share);
|
|
51
51
|
entry.queryId = definition.id;
|
|
52
52
|
if (registry.getNodes().has(atom) &&
|
|
53
53
|
entry.loadedAt !== undefined &&
|
package/dist/collection.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface Rows<A> {
|
|
|
10
10
|
}
|
|
11
11
|
export declare function collection<A>(identity: (item: A, index: number) => Identity): {
|
|
12
12
|
from: (items: readonly A[]) => Rows<A>;
|
|
13
|
+
share: {
|
|
14
|
+
<B extends A>(previous: readonly B[], next: B[]): B[];
|
|
15
|
+
<B extends A>(previous: readonly B[], next: readonly B[]): readonly B[];
|
|
16
|
+
};
|
|
13
17
|
};
|
|
14
18
|
/** Use positional identity for ordered values without stable entity IDs. */
|
|
15
19
|
export declare function sequence<A>(items: readonly A[]): Rows<A>;
|
package/dist/collection.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { shareValue } from './share.js';
|
|
1
2
|
export function collection(identity) {
|
|
2
3
|
const cache = new WeakMap();
|
|
4
|
+
const comparisons = new WeakMap();
|
|
3
5
|
const from = (items) => {
|
|
4
6
|
const cached = cache.get(items);
|
|
5
7
|
if (cached)
|
|
@@ -15,7 +17,48 @@ export function collection(identity) {
|
|
|
15
17
|
cache.set(items, rows);
|
|
16
18
|
return rows;
|
|
17
19
|
};
|
|
18
|
-
|
|
20
|
+
function share(previous, next) {
|
|
21
|
+
if (previous === next)
|
|
22
|
+
return previous;
|
|
23
|
+
let pairs = comparisons.get(previous);
|
|
24
|
+
const cached = pairs?.get(next);
|
|
25
|
+
if (cached)
|
|
26
|
+
return cached;
|
|
27
|
+
let byIdentity;
|
|
28
|
+
let result;
|
|
29
|
+
let equal = previous.length === next.length;
|
|
30
|
+
for (let index = 0; index < next.length; index++) {
|
|
31
|
+
const item = next[index];
|
|
32
|
+
if (index < previous.length && Object.is(previous[index], item))
|
|
33
|
+
continue;
|
|
34
|
+
const key = identity(item, index);
|
|
35
|
+
let old = previous[index];
|
|
36
|
+
if (index >= previous.length || !Object.is(identity(old, index), key)) {
|
|
37
|
+
if (!byIdentity) {
|
|
38
|
+
byIdentity = new Map(previous.map((value, i) => [identity(value, i), value]));
|
|
39
|
+
const keys = next.map(identity);
|
|
40
|
+
if (byIdentity.size !== previous.length || new Set(keys).size !== keys.length)
|
|
41
|
+
throw new Error('Duplicate collection identity. Identity must be unique within the collection.');
|
|
42
|
+
}
|
|
43
|
+
old = byIdentity.get(key);
|
|
44
|
+
}
|
|
45
|
+
const value = old === undefined ? item : shareValue(old, item);
|
|
46
|
+
if (!Object.is(value, previous[index]))
|
|
47
|
+
equal = false;
|
|
48
|
+
if (!Object.is(value, item) && !result)
|
|
49
|
+
result = next.slice();
|
|
50
|
+
if (result)
|
|
51
|
+
result[index] = value;
|
|
52
|
+
}
|
|
53
|
+
const shared = equal ? previous : (result ?? next);
|
|
54
|
+
if (!pairs) {
|
|
55
|
+
pairs = new WeakMap();
|
|
56
|
+
comparisons.set(previous, pairs);
|
|
57
|
+
}
|
|
58
|
+
pairs.set(next, shared);
|
|
59
|
+
return shared;
|
|
60
|
+
}
|
|
61
|
+
return { from, share };
|
|
19
62
|
}
|
|
20
63
|
const positions = collection((_item, index) => index);
|
|
21
64
|
/** Use positional identity for ordered values without stable entity IDs. */
|
package/dist/query.d.ts
CHANGED
|
@@ -7,16 +7,20 @@ export interface Query<Args, A, E = never, R = never> {
|
|
|
7
7
|
readonly load: (args: Args) => Effect.Effect<A, E, R>;
|
|
8
8
|
/** Revalidate on activation/prefetch after this many milliseconds. No polling timer. */
|
|
9
9
|
readonly staleTime: number;
|
|
10
|
+
/** Reuse unchanged domain entities before publishing a refreshed result. */
|
|
11
|
+
readonly share?: (previous: A, next: A) => A;
|
|
10
12
|
}
|
|
11
13
|
/** Identity belongs to arguments, never callback identity. Separate caches isolate independent data scopes. */
|
|
12
14
|
export declare function query<A, E = never, R = never>(definition: {
|
|
13
15
|
readonly name: string;
|
|
14
16
|
readonly load: () => Effect.Effect<A, E, R>;
|
|
15
17
|
readonly staleTime?: number;
|
|
18
|
+
readonly share?: (previous: A, next: A) => A;
|
|
16
19
|
}): Query<true, A, E, R>;
|
|
17
20
|
export declare function query<Args, A, E = never, R = never>(definition: {
|
|
18
21
|
readonly name: string;
|
|
19
22
|
readonly key: (args: Args) => string;
|
|
20
23
|
readonly load: (args: Args) => Effect.Effect<A, E, R>;
|
|
21
24
|
readonly staleTime?: number;
|
|
25
|
+
readonly share?: (previous: A, next: A) => A;
|
|
22
26
|
}): Query<Args, A, E, R>;
|
package/dist/share.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
+
/** Override sharing for selected fields, for example a collection keyed by domain identity. */
|
|
2
|
+
export type ShareFields<T> = {
|
|
3
|
+
readonly [K in keyof T]?: (previous: T[K], next: T[K]) => T[K];
|
|
4
|
+
};
|
|
1
5
|
/** Share JSON-shaped data; Blob, typed arrays and class instances retain their own identity. */
|
|
2
|
-
export declare function shareValue<T>(previous: T, next: T): T;
|
|
6
|
+
export declare function shareValue<T>(previous: T, next: T, fields?: ShareFields<T>): T;
|
package/dist/share.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// already compared object pair without retaining either snapshot after its owners release it.
|
|
3
3
|
const sharedPairs = new WeakMap();
|
|
4
4
|
/** Share JSON-shaped data; Blob, typed arrays and class instances retain their own identity. */
|
|
5
|
-
export function shareValue(previous, next) {
|
|
5
|
+
export function shareValue(previous, next, fields) {
|
|
6
6
|
if (Object.is(previous, next))
|
|
7
7
|
return previous;
|
|
8
8
|
if (!previous || !next || typeof previous !== 'object' || typeof next !== 'object')
|
|
@@ -15,7 +15,7 @@ export function shareValue(previous, next) {
|
|
|
15
15
|
Object.getPrototypeOf(previous) !== Object.prototype))
|
|
16
16
|
return next;
|
|
17
17
|
let pairs = sharedPairs.get(previous);
|
|
18
|
-
if (pairs?.has(next))
|
|
18
|
+
if (!fields && pairs?.has(next))
|
|
19
19
|
return pairs.get(next);
|
|
20
20
|
const old = previous;
|
|
21
21
|
const value = next;
|
|
@@ -24,13 +24,19 @@ export function shareValue(previous, next) {
|
|
|
24
24
|
let unchangedNext = true;
|
|
25
25
|
const shared = array ? [] : {};
|
|
26
26
|
for (const key of keys) {
|
|
27
|
-
|
|
27
|
+
const custom = fields && Object.hasOwn(fields, key) ? fields[key] : undefined;
|
|
28
|
+
shared[key] =
|
|
29
|
+
custom && Object.hasOwn(old, key)
|
|
30
|
+
? custom(old[key], value[key])
|
|
31
|
+
: shareValue(old[key], value[key]);
|
|
28
32
|
if (!Object.hasOwn(old, key) || shared[key] !== old[key])
|
|
29
33
|
equal = false;
|
|
30
34
|
if (shared[key] !== value[key])
|
|
31
35
|
unchangedNext = false;
|
|
32
36
|
}
|
|
33
37
|
const result = equal ? previous : unchangedNext ? next : shared;
|
|
38
|
+
if (fields)
|
|
39
|
+
return result;
|
|
34
40
|
if (!pairs) {
|
|
35
41
|
pairs = new WeakMap();
|
|
36
42
|
sharedPairs.set(previous, pairs);
|