@evolu/common 7.4.0 → 8.0.0-next.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 +4 -61
- package/dist/src/Array.d.ts +343 -102
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +181 -85
- package/dist/src/Assert.d.ts +57 -11
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +47 -11
- package/dist/src/BigInt.d.ts +6 -1
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -0
- package/dist/src/Brand.d.ts +40 -12
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Brand.js +5 -0
- package/dist/src/Buffer.d.ts +6 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +7 -2
- package/dist/src/Cache.d.ts +10 -2
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +6 -0
- package/dist/src/Callbacks.d.ts +10 -7
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +23 -13
- package/dist/src/Console.d.ts +361 -69
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +217 -117
- package/dist/src/Crypto.d.ts +70 -21
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +72 -27
- package/dist/src/Eq.d.ts +22 -2
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +21 -1
- package/dist/src/Error.d.ts +23 -12
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +27 -11
- package/dist/src/Function.d.ts +128 -56
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +118 -14
- package/dist/src/Identicon.d.ts +5 -0
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +6 -1
- package/dist/src/Lookup.d.ts +160 -0
- package/dist/src/Lookup.d.ts.map +1 -0
- package/dist/src/Lookup.js +192 -0
- package/dist/src/Microtask.d.ts +21 -0
- package/dist/src/Microtask.d.ts.map +1 -0
- package/dist/src/Microtask.js +37 -0
- package/dist/src/Number.d.ts +18 -4
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +23 -1
- package/dist/src/Object.d.ts +110 -5
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +108 -5
- package/dist/src/Option.d.ts +58 -0
- package/dist/src/Option.d.ts.map +1 -0
- package/dist/src/Option.js +43 -0
- package/dist/src/Order.d.ts +5 -0
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +8 -3
- package/dist/src/Platform.d.ts +36 -2
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +10 -6
- package/dist/src/Polyfills.d.ts +27 -0
- package/dist/src/Polyfills.d.ts.map +1 -0
- package/dist/src/Polyfills.js +299 -0
- package/dist/src/Random.d.ts +18 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +9 -7
- package/dist/src/Redacted.d.ts +7 -4
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +5 -0
- package/dist/src/Ref.d.ts +31 -16
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +35 -7
- package/dist/src/RefCount.d.ts +62 -0
- package/dist/src/RefCount.d.ts.map +1 -0
- package/dist/src/RefCount.js +83 -0
- package/dist/src/Relation.d.ts +84 -29
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +83 -105
- package/dist/src/Resource.d.ts +263 -0
- package/dist/src/Resource.d.ts.map +1 -0
- package/dist/src/Resource.js +389 -0
- package/dist/src/Result.d.ts +390 -374
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +142 -70
- package/dist/src/Schedule.d.ts +953 -0
- package/dist/src/Schedule.d.ts.map +1 -0
- package/dist/src/Schedule.js +1199 -0
- package/dist/src/Set.d.ts +181 -0
- package/dist/src/Set.d.ts.map +1 -0
- package/dist/src/Set.js +137 -0
- package/dist/src/Skiplist.d.ts +6 -1
- package/dist/src/Skiplist.d.ts.map +1 -1
- package/dist/src/Skiplist.js +5 -0
- package/dist/src/Sqlite.d.ts +160 -60
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +290 -190
- package/dist/src/Store.d.ts +36 -28
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +55 -15
- package/dist/src/String.d.ts +5 -0
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +5 -0
- package/dist/src/Task.d.ts +2263 -423
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1488 -348
- package/dist/src/Test.d.ts +122 -0
- package/dist/src/Test.d.ts.map +1 -0
- package/dist/src/Test.js +66 -0
- package/dist/src/Time.d.ts +201 -48
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +190 -86
- package/dist/src/Tracer.d.ts +48 -0
- package/dist/src/Tracer.d.ts.map +1 -0
- package/dist/src/Tracer.js +6 -0
- package/dist/src/Type.d.ts +582 -209
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +362 -240
- package/dist/src/Types.d.ts +160 -6
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +22 -2
- package/dist/src/WebSocket.d.ts +113 -58
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +210 -118
- package/dist/src/Worker.d.ts +245 -60
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +220 -64
- package/dist/src/index.d.ts +25 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -3
- package/dist/src/local-first/Db.d.ts +24 -213
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +622 -293
- package/dist/src/local-first/Error.d.ts +12 -0
- package/dist/src/local-first/Error.d.ts.map +1 -0
- package/dist/src/local-first/Error.js +6 -0
- package/dist/src/local-first/Evolu.d.ts +330 -285
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +472 -402
- package/dist/src/local-first/LocalAuth.d.ts +10 -10
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/LocalAuth.js +6 -1
- package/dist/src/local-first/Owner.d.ts +70 -27
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +27 -14
- package/dist/src/local-first/Protocol.d.ts +58 -50
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +253 -155
- package/dist/src/local-first/Query.d.ts +133 -43
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +139 -90
- package/dist/src/local-first/Relay.d.ts +18 -38
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +59 -171
- package/dist/src/local-first/Schema.d.ts +139 -144
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +78 -163
- package/dist/src/local-first/Shared.d.ts +125 -0
- package/dist/src/local-first/Shared.d.ts.map +1 -0
- package/dist/src/local-first/Shared.js +569 -0
- package/dist/src/local-first/Storage.d.ts +50 -53
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +121 -173
- package/dist/src/local-first/Timestamp.d.ts +28 -35
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +24 -25
- package/dist/src/local-first/index.d.ts +2 -15
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +2 -15
- package/package.json +36 -26
- package/src/Array.ts +467 -106
- package/src/Assert.ts +87 -12
- package/src/BigInt.ts +7 -1
- package/src/Brand.ts +41 -12
- package/src/Buffer.ts +10 -4
- package/src/Cache.ts +11 -2
- package/src/Callbacks.ts +33 -23
- package/src/Console.ts +567 -178
- package/src/Crypto.ts +111 -62
- package/src/Eq.ts +30 -3
- package/src/Error.ts +50 -34
- package/src/Function.ts +140 -68
- package/src/Identicon.ts +7 -1
- package/src/Lookup.ts +415 -0
- package/src/Microtask.ts +58 -0
- package/src/Number.ts +40 -7
- package/src/Object.ts +155 -16
- package/src/Option.ts +74 -0
- package/src/Order.ts +11 -3
- package/src/Platform.ts +43 -7
- package/src/Polyfills.ts +465 -0
- package/src/Random.ts +25 -11
- package/src/Redacted.ts +8 -4
- package/src/Ref.ts +72 -21
- package/src/RefCount.ts +170 -0
- package/src/Relation.ts +188 -136
- package/src/Resource.ts +864 -0
- package/src/Result.ts +548 -381
- package/src/Schedule.ts +1471 -0
- package/src/Set.ts +247 -0
- package/src/Skiplist.ts +7 -1
- package/src/Sqlite.ts +409 -304
- package/src/Store.ts +94 -48
- package/src/String.ts +6 -0
- package/src/Task.ts +3685 -740
- package/src/Test.ts +162 -0
- package/src/Time.ts +348 -145
- package/src/Tracer.ts +54 -0
- package/src/Type.ts +1064 -630
- package/src/Types.ts +202 -7
- package/src/WebSocket.ts +311 -178
- package/src/Worker.ts +527 -147
- package/src/index.ts +74 -3
- package/src/local-first/Db.ts +871 -597
- package/src/local-first/Error.ts +17 -0
- package/src/local-first/Evolu.ts +884 -823
- package/src/local-first/LocalAuth.ts +10 -10
- package/src/local-first/Owner.ts +94 -42
- package/src/local-first/Protocol.ts +440 -303
- package/src/local-first/Query.ts +243 -194
- package/src/local-first/Relay.ts +91 -252
- package/src/local-first/Schema.ts +284 -394
- package/src/local-first/Shared.ts +932 -0
- package/src/local-first/Storage.ts +209 -282
- package/src/local-first/Timestamp.ts +38 -45
- package/src/local-first/index.ts +2 -16
- package/LICENSE +0 -21
- package/dist/src/Instances.d.ts +0 -34
- package/dist/src/Instances.d.ts.map +0 -1
- package/dist/src/Instances.js +0 -44
- package/dist/src/Resources.d.ts +0 -118
- package/dist/src/Resources.d.ts.map +0 -1
- package/dist/src/Resources.js +0 -197
- package/dist/src/local-first/Platform.d.ts +0 -25
- package/dist/src/local-first/Platform.d.ts.map +0 -1
- package/dist/src/local-first/Platform.js +0 -1
- package/dist/src/local-first/Public.d.ts +0 -22
- package/dist/src/local-first/Public.d.ts.map +0 -1
- package/dist/src/local-first/Public.js +0 -15
- package/dist/src/local-first/PublicKysely.d.ts +0 -148
- package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
- package/dist/src/local-first/PublicKysely.js +0 -184
- package/dist/src/local-first/Sync.d.ts +0 -112
- package/dist/src/local-first/Sync.d.ts.map +0 -1
- package/dist/src/local-first/Sync.js +0 -529
- package/src/Instances.ts +0 -90
- package/src/Resources.ts +0 -367
- package/src/local-first/Platform.ts +0 -27
- package/src/local-first/Public.ts +0 -42
- package/src/local-first/PublicKysely.ts +0 -239
- package/src/local-first/Sync.ts +0 -960
package/src/Identicon.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visual identicon generation from IDs.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { md5 } from "@noble/hashes/legacy.js";
|
|
1
8
|
import type { Brand } from "./Brand.js";
|
|
2
9
|
import { Id, idToIdBytes } from "./Type.js";
|
|
3
|
-
import { md5 } from "@noble/hashes/legacy.js";
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* SVG string representing a visual identicon for an {@link Id}, created with
|
package/src/Lookup.ts
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lookup-key collections and helpers.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { assert } from "./Assert.js";
|
|
8
|
+
import type { Brand } from "./Brand.js";
|
|
9
|
+
import { isPlainObject } from "./Object.js";
|
|
10
|
+
import { Uint8Array, uint8ArrayToBase64Url } from "./Type.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Derives the native lookup key used by lookup-based collections.
|
|
14
|
+
*
|
|
15
|
+
* Native `Map` and `Set` are suitable for primitive keys and keys where
|
|
16
|
+
* reference identity is the intended key, but they are not useful when
|
|
17
|
+
* different references should represent the same logical key.
|
|
18
|
+
*
|
|
19
|
+
* Lookup-based collections solve that by deriving a stable lookup key from each
|
|
20
|
+
* input key and using that lookup key for the internal native `Map` lookup.
|
|
21
|
+
* Keys that derive the same lookup key are treated as the same logical key.
|
|
22
|
+
*
|
|
23
|
+
* Use this when callers provide keys in one form but logical identity should be
|
|
24
|
+
* based on another stable key, such as a natural id or {@link StructuralKey}.
|
|
25
|
+
*
|
|
26
|
+
* @see {@link createLookupMap}
|
|
27
|
+
* @see {@link createLookupSet}
|
|
28
|
+
*/
|
|
29
|
+
export type Lookup<K, L> = (key: K) => L;
|
|
30
|
+
|
|
31
|
+
/** Optional {@link Lookup}. */
|
|
32
|
+
export interface LookupOption<K, L = K> {
|
|
33
|
+
/** Derives the lookup key used for logical equality. */
|
|
34
|
+
readonly lookup?: Lookup<K, L>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `Map`-like collection keyed by a {@link Lookup derived lookup key}.
|
|
39
|
+
*
|
|
40
|
+
* `set` preserves the first inserted representative for a logical key. `getKey`
|
|
41
|
+
* returns that representative for callers that need stable identity.
|
|
42
|
+
*
|
|
43
|
+
* @see {@link createLookupMap}
|
|
44
|
+
*/
|
|
45
|
+
export interface LookupMap<K, V> extends Iterable<readonly [K, V]> {
|
|
46
|
+
readonly size: number;
|
|
47
|
+
readonly has: (key: K) => boolean;
|
|
48
|
+
readonly get: (key: K) => V | undefined;
|
|
49
|
+
readonly getKey: (key: K) => K | undefined;
|
|
50
|
+
readonly set: (key: K, value: V) => LookupMap<K, V>;
|
|
51
|
+
readonly delete: (key: K) => boolean;
|
|
52
|
+
readonly clear: () => void;
|
|
53
|
+
readonly keys: () => IterableIterator<K>;
|
|
54
|
+
readonly values: () => IterableIterator<V>;
|
|
55
|
+
readonly entries: () => IterableIterator<readonly [K, V]>;
|
|
56
|
+
readonly forEach: (
|
|
57
|
+
callback: (value: V, key: K, map: LookupMap<K, V>) => void,
|
|
58
|
+
) => void;
|
|
59
|
+
readonly [Symbol.iterator]: () => IterableIterator<readonly [K, V]>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Options for {@link createLookupMap}. */
|
|
63
|
+
export interface CreateLookupMapOptions<K, V, L> extends Required<
|
|
64
|
+
LookupOption<K, L>
|
|
65
|
+
> {
|
|
66
|
+
/** Initial entries for the map. */
|
|
67
|
+
readonly entries?: Iterable<readonly [K, V]>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Creates {@link LookupMap}. */
|
|
71
|
+
export const createLookupMap = <K, V, L>({
|
|
72
|
+
lookup,
|
|
73
|
+
entries,
|
|
74
|
+
}: CreateLookupMapOptions<K, V, L>): LookupMap<K, V> => {
|
|
75
|
+
interface LookupEntry<K, V> {
|
|
76
|
+
readonly key: K;
|
|
77
|
+
readonly value: V;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const entriesByLookupKey = new Map<L, LookupEntry<K, V>>();
|
|
81
|
+
|
|
82
|
+
const map: LookupMap<K, V> = {
|
|
83
|
+
get size() {
|
|
84
|
+
return entriesByLookupKey.size;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
has: (key) => entriesByLookupKey.has(lookup(key)),
|
|
88
|
+
|
|
89
|
+
get: (key) => entriesByLookupKey.get(lookup(key))?.value,
|
|
90
|
+
|
|
91
|
+
getKey: (key) => entriesByLookupKey.get(lookup(key))?.key,
|
|
92
|
+
|
|
93
|
+
set: (key, value) => {
|
|
94
|
+
const lookupKey = lookup(key);
|
|
95
|
+
const existingEntry = entriesByLookupKey.get(lookupKey);
|
|
96
|
+
entriesByLookupKey.set(lookupKey, {
|
|
97
|
+
key: existingEntry?.key ?? key,
|
|
98
|
+
value,
|
|
99
|
+
});
|
|
100
|
+
return map;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
delete: (key) => entriesByLookupKey.delete(lookup(key)),
|
|
104
|
+
|
|
105
|
+
clear: () => {
|
|
106
|
+
entriesByLookupKey.clear();
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
keys: function* () {
|
|
110
|
+
for (const entry of entriesByLookupKey.values()) {
|
|
111
|
+
yield entry.key;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
values: function* () {
|
|
116
|
+
for (const entry of entriesByLookupKey.values()) {
|
|
117
|
+
yield entry.value;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
entries: function* () {
|
|
122
|
+
for (const entry of entriesByLookupKey.values()) {
|
|
123
|
+
yield [entry.key, entry.value] as const;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
forEach: (callback) => {
|
|
128
|
+
for (const entry of entriesByLookupKey.values()) {
|
|
129
|
+
callback(entry.value, entry.key, map);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
[Symbol.iterator]: function () {
|
|
134
|
+
return map.entries();
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
if (entries) {
|
|
139
|
+
for (const [key, value] of entries) {
|
|
140
|
+
map.set(key, value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return map;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* `Set`-like collection keyed by a {@link Lookup derived lookup key}.
|
|
149
|
+
*
|
|
150
|
+
* `add` preserves the first inserted representative for a logical key. `get`
|
|
151
|
+
* returns that representative for callers that need stable identity.
|
|
152
|
+
*
|
|
153
|
+
* @see {@link createLookupSet}
|
|
154
|
+
*/
|
|
155
|
+
export interface LookupSet<K> extends Iterable<K> {
|
|
156
|
+
readonly size: number;
|
|
157
|
+
readonly has: (key: K) => boolean;
|
|
158
|
+
readonly get: (key: K) => K | undefined;
|
|
159
|
+
readonly add: (key: K) => LookupSet<K>;
|
|
160
|
+
readonly delete: (key: K) => boolean;
|
|
161
|
+
readonly clear: () => void;
|
|
162
|
+
readonly keys: () => IterableIterator<K>;
|
|
163
|
+
readonly values: () => IterableIterator<K>;
|
|
164
|
+
readonly entries: () => IterableIterator<readonly [K, K]>;
|
|
165
|
+
readonly forEach: (
|
|
166
|
+
callback: (value: K, key: K, set: LookupSet<K>) => void,
|
|
167
|
+
) => void;
|
|
168
|
+
readonly [Symbol.iterator]: () => IterableIterator<K>;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Options for {@link createLookupSet}. */
|
|
172
|
+
export interface CreateLookupSetOptions<K, L> extends Required<
|
|
173
|
+
LookupOption<K, L>
|
|
174
|
+
> {
|
|
175
|
+
/** Initial values for the set. */
|
|
176
|
+
readonly values?: Iterable<K>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Creates {@link LookupSet}. */
|
|
180
|
+
export const createLookupSet = <K, L>({
|
|
181
|
+
lookup,
|
|
182
|
+
values,
|
|
183
|
+
}: CreateLookupSetOptions<K, L>): LookupSet<K> => {
|
|
184
|
+
const map = createLookupMap<K, true, L>({ lookup });
|
|
185
|
+
|
|
186
|
+
const set: LookupSet<K> = {
|
|
187
|
+
get size() {
|
|
188
|
+
return map.size;
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
has: (key) => map.has(key),
|
|
192
|
+
|
|
193
|
+
get: (key) => map.getKey(key),
|
|
194
|
+
|
|
195
|
+
add: (key) => {
|
|
196
|
+
map.set(key, true);
|
|
197
|
+
return set;
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
delete: (key) => map.delete(key),
|
|
201
|
+
|
|
202
|
+
clear: () => {
|
|
203
|
+
map.clear();
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
keys: () => map.keys(),
|
|
207
|
+
|
|
208
|
+
values: () => map.keys(),
|
|
209
|
+
|
|
210
|
+
entries: function* () {
|
|
211
|
+
for (const key of map.keys()) {
|
|
212
|
+
yield [key, key] as const;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
forEach: (callback) => {
|
|
217
|
+
for (const key of map.keys()) {
|
|
218
|
+
callback(key, key, set);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
[Symbol.iterator]: function () {
|
|
223
|
+
return set.keys();
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
if (values) {
|
|
228
|
+
for (const value of values) {
|
|
229
|
+
set.add(value);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return set;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/** Scalar branch shared by {@link StructuralKey} and {@link Structural}. */
|
|
237
|
+
export type StructuralScalar = string | number | boolean | null | Uint8Array;
|
|
238
|
+
|
|
239
|
+
/** Branded native key produced by {@link structuralLookup}. */
|
|
240
|
+
export type StructuralLookupKey = string & Brand<"StructuralLookupKey">;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Immutable structural key.
|
|
244
|
+
*
|
|
245
|
+
* Structural keys are JSON-like values with `NaN` and positive/negative
|
|
246
|
+
* infinity also supported, or `Uint8Array`.
|
|
247
|
+
*
|
|
248
|
+
* @see {@link Structural}
|
|
249
|
+
* @see {@link structuralLookup}
|
|
250
|
+
*/
|
|
251
|
+
export type StructuralKey =
|
|
252
|
+
| StructuralScalar
|
|
253
|
+
| ReadonlyArray<StructuralKey>
|
|
254
|
+
| { readonly [key: string]: StructuralKey };
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Compile-time structural form of `T` for structural lookup APIs.
|
|
258
|
+
*
|
|
259
|
+
* This exists because {@link StructuralKey} is the runtime serialization model,
|
|
260
|
+
* not a good public generic constraint for interface-shaped objects.
|
|
261
|
+
* `StructuralKey` models object values with a string index signature, which is
|
|
262
|
+
* stricter than ordinary interfaces like `{ readonly id: string }` even though
|
|
263
|
+
* the runtime serializer accepts such plain objects.
|
|
264
|
+
*
|
|
265
|
+
* `Structural<T>` checks a concrete type recursively at compile time instead:
|
|
266
|
+
* scalars pass through, arrays recurse, object properties recurse, and
|
|
267
|
+
* function-valued properties are rejected. This keeps public structural APIs
|
|
268
|
+
* ergonomic for interface-based inputs while preserving the same runtime
|
|
269
|
+
* constraints as structural lookup serialization.
|
|
270
|
+
*
|
|
271
|
+
* @see {@link StructuralKey}
|
|
272
|
+
* @see {@link structuralLookup}
|
|
273
|
+
*/
|
|
274
|
+
export type Structural<T> = T extends StructuralScalar
|
|
275
|
+
? T
|
|
276
|
+
: T extends ReadonlyArray<infer Item>
|
|
277
|
+
? ReadonlyArray<Structural<Item>>
|
|
278
|
+
: T extends StructuralFunction
|
|
279
|
+
? never
|
|
280
|
+
: T extends object
|
|
281
|
+
? {
|
|
282
|
+
readonly [K in keyof T as Extract<
|
|
283
|
+
T[K],
|
|
284
|
+
StructuralFunction
|
|
285
|
+
> extends never
|
|
286
|
+
? K
|
|
287
|
+
: never]: Structural<Exclude<T[K], StructuralFunction>>;
|
|
288
|
+
} & {
|
|
289
|
+
readonly [K in keyof T as Extract<
|
|
290
|
+
T[K],
|
|
291
|
+
StructuralFunction
|
|
292
|
+
> extends never
|
|
293
|
+
? never
|
|
294
|
+
: K]?: never;
|
|
295
|
+
}
|
|
296
|
+
: never;
|
|
297
|
+
|
|
298
|
+
/** Function branch excluded by {@link Structural}. */
|
|
299
|
+
export type StructuralFunction = (...args: ReadonlyArray<unknown>) => unknown;
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Returns the structural lookup key for `key`.
|
|
303
|
+
*
|
|
304
|
+
* Structural lookup keys are derived from JSON-like values plus `Uint8Array`.
|
|
305
|
+
* Equal structures produce the same lookup key even when they are different
|
|
306
|
+
* JavaScript instances.
|
|
307
|
+
*
|
|
308
|
+
* The derived key is memoized by non-null object identity in a module-scoped
|
|
309
|
+
* `WeakMap` shared by all callers, so keys must be immutable.
|
|
310
|
+
*
|
|
311
|
+
* Use this as a {@link Lookup} when logical equality should be based on
|
|
312
|
+
* structural value instead of reference identity.
|
|
313
|
+
*
|
|
314
|
+
* ### Example
|
|
315
|
+
*
|
|
316
|
+
* ```ts
|
|
317
|
+
* const byFilter = createLookupMap<
|
|
318
|
+
* { readonly table: string; readonly where: readonly [string, string] },
|
|
319
|
+
* string,
|
|
320
|
+
* StructuralLookupKey
|
|
321
|
+
* >({
|
|
322
|
+
* lookup: structuralLookup,
|
|
323
|
+
* });
|
|
324
|
+
*
|
|
325
|
+
* byFilter.set({ table: "todo", where: ["owner", "ada"] }, "cached");
|
|
326
|
+
* byFilter.get({ table: "todo", where: ["owner", "ada"] }); // "cached"
|
|
327
|
+
* ```
|
|
328
|
+
*
|
|
329
|
+
* @see {@link StructuralKey}
|
|
330
|
+
* @see {@link Structural}
|
|
331
|
+
*/
|
|
332
|
+
export const structuralLookup = <K>(key: Structural<K>): StructuralLookupKey =>
|
|
333
|
+
structuralLookupInternal(key) as StructuralLookupKey;
|
|
334
|
+
|
|
335
|
+
const structuralLookupKeyByValue = new WeakMap<object, string>();
|
|
336
|
+
|
|
337
|
+
const structuralLookupInternal = (
|
|
338
|
+
value: unknown,
|
|
339
|
+
path: Set<object> = new Set(),
|
|
340
|
+
): string => {
|
|
341
|
+
switch (typeof value) {
|
|
342
|
+
case "string":
|
|
343
|
+
return `s:${JSON.stringify(value)}`;
|
|
344
|
+
case "number":
|
|
345
|
+
if (Number.isNaN(value)) return "n:NaN";
|
|
346
|
+
if (value === Number.POSITIVE_INFINITY) return "n:Infinity";
|
|
347
|
+
if (value === Number.NEGATIVE_INFINITY) return "n:-Infinity";
|
|
348
|
+
return Object.is(value, -0) ? "n:0" : `n:${value}`;
|
|
349
|
+
case "boolean":
|
|
350
|
+
return value ? "b:true" : "b:false";
|
|
351
|
+
case "object": {
|
|
352
|
+
if (value === null) return "l:null";
|
|
353
|
+
|
|
354
|
+
const cachedLookupKey = structuralLookupKeyByValue.get(value);
|
|
355
|
+
if (cachedLookupKey) return cachedLookupKey;
|
|
356
|
+
|
|
357
|
+
let lookupKey: string;
|
|
358
|
+
|
|
359
|
+
if (Array.isArray(value)) {
|
|
360
|
+
assert(
|
|
361
|
+
!path.has(value),
|
|
362
|
+
"Structural lookup keys must not contain cycles.",
|
|
363
|
+
);
|
|
364
|
+
path.add(value);
|
|
365
|
+
lookupKey = getStructuralArrayLookupKey(value, path);
|
|
366
|
+
path.delete(value);
|
|
367
|
+
} else if (isPlainObject(value)) {
|
|
368
|
+
assert(
|
|
369
|
+
!path.has(value),
|
|
370
|
+
"Structural lookup keys must not contain cycles.",
|
|
371
|
+
);
|
|
372
|
+
path.add(value);
|
|
373
|
+
lookupKey = getStructuralObjectLookupKey(value, path);
|
|
374
|
+
path.delete(value);
|
|
375
|
+
} else if (Uint8Array.is(value)) {
|
|
376
|
+
lookupKey = `u:${uint8ArrayToBase64Url(value)}`;
|
|
377
|
+
} else {
|
|
378
|
+
assert(
|
|
379
|
+
false,
|
|
380
|
+
"Structural lookup keys must be JSON-like values or Uint8Array.",
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
structuralLookupKeyByValue.set(value, lookupKey);
|
|
385
|
+
return lookupKey;
|
|
386
|
+
}
|
|
387
|
+
default:
|
|
388
|
+
assert(
|
|
389
|
+
false,
|
|
390
|
+
"Structural lookup keys must be JSON-like values or Uint8Array.",
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const getStructuralArrayLookupKey = (
|
|
396
|
+
value: ReadonlyArray<unknown>,
|
|
397
|
+
path: Set<object>,
|
|
398
|
+
): string =>
|
|
399
|
+
`a:[${Array.from(value, (item) => structuralLookupInternal(item, path)).join(",")}]`;
|
|
400
|
+
|
|
401
|
+
const getStructuralObjectLookupKey = (
|
|
402
|
+
value: object,
|
|
403
|
+
path: Set<object>,
|
|
404
|
+
): string => {
|
|
405
|
+
const valueRecord = value as Record<string, unknown>;
|
|
406
|
+
|
|
407
|
+
const entries = Object.keys(valueRecord)
|
|
408
|
+
.toSorted()
|
|
409
|
+
.map((key) => {
|
|
410
|
+
const item = valueRecord[key];
|
|
411
|
+
return `${JSON.stringify(key)}:${structuralLookupInternal(item, path)}`;
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
return `o:{${entries.join(",")}}`;
|
|
415
|
+
};
|
package/src/Microtask.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microtask queue utilities.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { appendToArray, type NonEmptyReadonlyArray } from "./Array.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Batches values and flushes them in a single microtask.
|
|
11
|
+
*
|
|
12
|
+
* Calls to {@link MicrotaskBatch.push} within the same tick are coalesced into
|
|
13
|
+
* one flush.
|
|
14
|
+
*/
|
|
15
|
+
export interface MicrotaskBatch<T> extends Disposable {
|
|
16
|
+
/** Enqueues one item and schedules a microtask flush if needed. */
|
|
17
|
+
readonly push: (item: T) => void;
|
|
18
|
+
|
|
19
|
+
/** Flushes queued items immediately. */
|
|
20
|
+
readonly flushNow: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Creates {@link MicrotaskBatch}. */
|
|
24
|
+
export const createMicrotaskBatch = <T>(
|
|
25
|
+
onFlush: (items: NonEmptyReadonlyArray<T>) => void,
|
|
26
|
+
): MicrotaskBatch<T> => {
|
|
27
|
+
let queue: NonEmptyReadonlyArray<T> | null = null;
|
|
28
|
+
let disposed = false;
|
|
29
|
+
|
|
30
|
+
const flushNow = () => {
|
|
31
|
+
if (disposed || queue == null) return;
|
|
32
|
+
const queuedItems = queue;
|
|
33
|
+
queue = null;
|
|
34
|
+
onFlush(queuedItems);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
push: (item) => {
|
|
39
|
+
if (disposed) return;
|
|
40
|
+
|
|
41
|
+
if (queue == null) {
|
|
42
|
+
queue = [item];
|
|
43
|
+
queueMicrotask(flushNow);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
queue = appendToArray(queue, item);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
flushNow,
|
|
51
|
+
|
|
52
|
+
[Symbol.dispose]: () => {
|
|
53
|
+
if (disposed) return;
|
|
54
|
+
disposed = true;
|
|
55
|
+
queue = null;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
package/src/Number.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Number arithmetic, comparison, and branded numeric types.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { NonEmptyReadonlyArray } from "./Array.js";
|
|
2
8
|
import { assertNonEmptyReadonlyArray } from "./Assert.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
9
|
+
import type { IsBranded } from "./Brand.js";
|
|
10
|
+
import type { Result } from "./Result.js";
|
|
11
|
+
import { err, ok } from "./Result.js";
|
|
12
|
+
import {
|
|
13
|
+
brand,
|
|
14
|
+
lessThanOrEqualTo,
|
|
15
|
+
onePositiveInt,
|
|
16
|
+
NonNegativeInt,
|
|
17
|
+
PositiveInt,
|
|
18
|
+
} from "./Type.js";
|
|
19
|
+
import type { Predicate, WidenLiteral } from "./Types.js";
|
|
7
20
|
|
|
8
21
|
export const increment = (n: number): number => n + 1;
|
|
9
22
|
|
|
@@ -35,13 +48,13 @@ export const isBetween =
|
|
|
35
48
|
export const min = <T extends number>(
|
|
36
49
|
...values: [T, ...ReadonlyArray<T>]
|
|
37
50
|
): IsBranded<T> extends true ? T : WidenLiteral<T> =>
|
|
38
|
-
values.reduce((a, b) => (a < b ? a : b)) as
|
|
51
|
+
values.reduce((a, b) => (a < b ? a : b)) as never;
|
|
39
52
|
|
|
40
53
|
/** Returns the maximum value, preserving branded type if applicable. */
|
|
41
54
|
export const max = <T extends number>(
|
|
42
55
|
...values: [T, ...ReadonlyArray<T>]
|
|
43
56
|
): IsBranded<T> extends true ? T : WidenLiteral<T> =>
|
|
44
|
-
values.reduce((a, b) => (a > b ? a : b)) as
|
|
57
|
+
values.reduce((a, b) => (a > b ? a : b)) as never;
|
|
45
58
|
|
|
46
59
|
/**
|
|
47
60
|
* Divides items into buckets as evenly as possible, ensuring each bucket has at
|
|
@@ -84,3 +97,23 @@ export const computeBalancedBuckets = (
|
|
|
84
97
|
assertNonEmptyReadonlyArray(indexes);
|
|
85
98
|
return ok(indexes);
|
|
86
99
|
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Valid index for {@link fibonacciAt}, constrained to 1-78.
|
|
103
|
+
*
|
|
104
|
+
* Limited to 78 because F(79) exceeds JavaScript's `MAX_SAFE_INTEGER`.
|
|
105
|
+
*/
|
|
106
|
+
export const FibonacciIndex = /*#__PURE__*/ brand(
|
|
107
|
+
"FibonacciIndex",
|
|
108
|
+
/*#__PURE__*/ lessThanOrEqualTo(78)(PositiveInt),
|
|
109
|
+
);
|
|
110
|
+
export type FibonacciIndex = typeof FibonacciIndex.Type;
|
|
111
|
+
|
|
112
|
+
/** Returns the Fibonacci number at the given index (1-indexed: 1,1,2,3,5,8,...). */
|
|
113
|
+
export const fibonacciAt = (index: FibonacciIndex): PositiveInt => {
|
|
114
|
+
if (index <= 2) return onePositiveInt;
|
|
115
|
+
let a = 1;
|
|
116
|
+
let b = 1;
|
|
117
|
+
for (let i = 3; i <= index; i++) [a, b] = [b, a + b];
|
|
118
|
+
return PositiveInt.orThrow(b);
|
|
119
|
+
};
|