@evolu/common 6.0.1-preview.2 → 6.0.1-preview.21
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/src/Assert.d.ts +6 -3
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +7 -4
- package/dist/src/Brand.d.ts +75 -0
- package/dist/src/Brand.d.ts.map +1 -0
- package/dist/src/Brand.js +1 -0
- package/dist/src/Buffer.d.ts +1 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +1 -1
- package/dist/src/CallbackRegistry.d.ts +53 -0
- package/dist/src/CallbackRegistry.d.ts.map +1 -0
- package/dist/src/CallbackRegistry.js +25 -0
- package/dist/src/Console.d.ts +31 -6
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +72 -9
- package/dist/src/Crypto.d.ts +61 -34
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +29 -42
- package/dist/src/Evolu/Db.d.ts +159 -66
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +300 -705
- package/dist/src/Evolu/Diff.d.ts +3 -3
- package/dist/src/Evolu/Diff.d.ts.map +1 -1
- package/dist/src/Evolu/Diff.js +7 -5
- package/dist/src/Evolu/Evolu.d.ts +200 -115
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +287 -157
- package/dist/src/Evolu/Internal.d.ts +0 -2
- package/dist/src/Evolu/Internal.d.ts.map +1 -1
- package/dist/src/Evolu/Internal.js +0 -2
- package/dist/src/Evolu/LocalAuth.d.ts +144 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +171 -0
- package/dist/src/Evolu/Owner.d.ts +209 -124
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +118 -104
- package/dist/src/Evolu/Platform.d.ts +9 -7
- package/dist/src/Evolu/Platform.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.d.ts +256 -211
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +558 -359
- package/dist/src/Evolu/Public.d.ts +6 -8
- package/dist/src/Evolu/Public.d.ts.map +1 -1
- package/dist/src/Evolu/Public.js +2 -3
- package/dist/src/Evolu/PublicKysely.js +3 -3
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.d.ts +72 -4
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +124 -13
- package/dist/src/Evolu/Schema.d.ts +129 -73
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +169 -89
- package/dist/src/Evolu/Storage.d.ts +159 -14
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +42 -32
- package/dist/src/Evolu/Sync.d.ts +68 -13
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +414 -20
- package/dist/src/Evolu/Timestamp.d.ts +35 -27
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +27 -18
- package/dist/src/ManyToManyMap.d.ts +74 -10
- package/dist/src/ManyToManyMap.d.ts.map +1 -1
- package/dist/src/ManyToManyMap.js +41 -6
- package/dist/src/Number.d.ts +2 -1
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/RefCountedResourceManager.d.ts +119 -0
- package/dist/src/RefCountedResourceManager.d.ts.map +1 -0
- package/dist/src/RefCountedResourceManager.js +197 -0
- package/dist/src/Result.d.ts +176 -51
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Sqlite.d.ts +21 -4
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +50 -8
- package/dist/src/Task.d.ts +511 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +410 -0
- package/dist/src/Time.d.ts +59 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +87 -4
- package/dist/src/Type.d.ts +474 -327
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +502 -436
- package/dist/src/Types.d.ts +0 -74
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts +5 -2
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +12 -13
- package/dist/src/Worker.d.ts +39 -11
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +22 -4
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -3
- package/package.json +14 -13
- package/src/Assert.ts +8 -7
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +1 -1
- package/src/CallbackRegistry.ts +84 -0
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +94 -79
- package/src/Evolu/Db.ts +528 -1027
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +682 -329
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +422 -0
- package/src/Evolu/Owner.ts +282 -227
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +788 -640
- package/src/Evolu/Public.ts +7 -14
- package/src/Evolu/PublicKysely.ts +3 -3
- package/src/Evolu/Query.ts +2 -1
- package/src/Evolu/Relay.ts +238 -18
- package/src/Evolu/Schema.ts +391 -191
- package/src/Evolu/Storage.ts +275 -55
- package/src/Evolu/Sync.ts +708 -37
- package/src/Evolu/Timestamp.ts +38 -35
- package/src/ManyToManyMap.ts +127 -24
- package/src/Number.ts +2 -6
- package/src/Random.ts +3 -2
- package/src/RefCountedResourceManager.ts +368 -0
- package/src/Result.ts +183 -53
- package/src/Sqlite.ts +59 -23
- package/src/Task.ts +779 -0
- package/src/Time.ts +168 -4
- package/src/Type.ts +708 -660
- package/src/Types.ts +0 -76
- package/src/WebSocket.ts +23 -17
- package/src/Worker.ts +72 -23
- package/src/index.ts +3 -3
- package/dist/src/Callbacks.d.ts +0 -20
- package/dist/src/Callbacks.d.ts.map +0 -1
- package/dist/src/Callbacks.js +0 -18
- package/dist/src/Evolu/Config.d.ts +0 -69
- package/dist/src/Evolu/Config.d.ts.map +0 -1
- package/dist/src/Evolu/Config.js +0 -9
- package/dist/src/Evolu/Kysely.d.ts +0 -6
- package/dist/src/Evolu/Kysely.d.ts.map +0 -1
- package/dist/src/Evolu/Kysely.js +0 -21
- package/dist/src/NanoId.d.ts +0 -27
- package/dist/src/NanoId.d.ts.map +0 -1
- package/dist/src/NanoId.js +0 -6
- package/dist/src/Promise.d.ts +0 -180
- package/dist/src/Promise.d.ts.map +0 -1
- package/dist/src/Promise.js +0 -176
- package/src/Callbacks.ts +0 -43
- package/src/Evolu/Config.ts +0 -83
- package/src/Evolu/Kysely.ts +0 -38
- package/src/NanoId.ts +0 -39
- package/src/Promise.ts +0 -295
package/src/Evolu/Timestamp.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { assert } from "../Assert.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Brand } from "../Brand.js";
|
|
3
|
+
import { bytesToHex } from "../Buffer.js";
|
|
4
|
+
import { RandomBytesDep } from "../Crypto.js";
|
|
5
|
+
import { createEqObject, eqNumber, eqString } from "../Eq.js";
|
|
3
6
|
import { increment } from "../Number.js";
|
|
4
7
|
import { Order, orderUint8Array } from "../Order.js";
|
|
5
|
-
import { err,
|
|
8
|
+
import { err, ok, Result } from "../Result.js";
|
|
6
9
|
import { TimeDep } from "../Time.js";
|
|
7
10
|
import {
|
|
8
11
|
brand,
|
|
12
|
+
InferType,
|
|
9
13
|
lessThanOrEqualTo,
|
|
10
14
|
NonNegativeInt,
|
|
11
15
|
object,
|
|
12
16
|
regex,
|
|
13
17
|
String,
|
|
18
|
+
Uint8Array,
|
|
14
19
|
} from "../Type.js";
|
|
15
|
-
import { Brand } from "../Types.js";
|
|
16
20
|
|
|
17
21
|
export interface TimestampConfig {
|
|
18
22
|
/**
|
|
@@ -30,7 +34,6 @@ export interface TimestampConfigDep {
|
|
|
30
34
|
export type TimestampError =
|
|
31
35
|
| TimestampDriftError
|
|
32
36
|
| TimestampCounterOverflowError
|
|
33
|
-
| TimestampDuplicateNodeError
|
|
34
37
|
| TimestampTimeOutOfRangeError;
|
|
35
38
|
|
|
36
39
|
export interface TimestampDriftError {
|
|
@@ -43,11 +46,6 @@ export interface TimestampCounterOverflowError {
|
|
|
43
46
|
readonly type: "TimestampCounterOverflowError";
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
export interface TimestampDuplicateNodeError {
|
|
47
|
-
readonly type: "TimestampDuplicateNodeError";
|
|
48
|
-
readonly nodeId: NodeId;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
49
|
export interface TimestampTimeOutOfRangeError {
|
|
52
50
|
readonly type: "TimestampTimeOutOfRangeError";
|
|
53
51
|
}
|
|
@@ -95,14 +93,17 @@ export const maxCounter = 65535 as Counter;
|
|
|
95
93
|
*
|
|
96
94
|
* https://lemire.me/blog/2019/12/12/are-64-bit-random-identifiers-free-from-collision
|
|
97
95
|
*
|
|
98
|
-
* What
|
|
96
|
+
* What happens if different devices generate the same NodeId?
|
|
99
97
|
*
|
|
100
|
-
* If the
|
|
101
|
-
* different owner have different owner IDs. Timestamps are partitioned by
|
|
102
|
-
* OwnerId.
|
|
98
|
+
* If devices with the same NodeId use different owners, no issues occur.
|
|
103
99
|
*
|
|
104
|
-
* If the
|
|
105
|
-
*
|
|
100
|
+
* If devices with the same NodeId use the same owner, problems only arise when
|
|
101
|
+
* they generate CRDT messages with identical timestamps (same millis, counter,
|
|
102
|
+
* and NodeId). In this case, the protocol sync algorithm treats them as the
|
|
103
|
+
* same message: the first will be synced with the relay, while the affected
|
|
104
|
+
* message will not be delivered. The affected devices will see different data
|
|
105
|
+
* yet they will think they are synced. This is extremely rare and can be
|
|
106
|
+
* resolved by resetting one device to generate a new NodeId.
|
|
106
107
|
*/
|
|
107
108
|
export const NodeId = regex("NodeId", /^[a-f0-9]{16}$/)(String);
|
|
108
109
|
export type NodeId = typeof NodeId.Type;
|
|
@@ -122,7 +123,14 @@ export const Timestamp = object({
|
|
|
122
123
|
counter: Counter,
|
|
123
124
|
nodeId: NodeId,
|
|
124
125
|
});
|
|
125
|
-
export
|
|
126
|
+
export interface Timestamp extends InferType<typeof Timestamp> {}
|
|
127
|
+
|
|
128
|
+
/** Equality function for comparing {@link Timestamp}. */
|
|
129
|
+
export const eqTimestamp = createEqObject<Timestamp>({
|
|
130
|
+
millis: eqNumber,
|
|
131
|
+
counter: eqNumber,
|
|
132
|
+
nodeId: eqString,
|
|
133
|
+
});
|
|
126
134
|
|
|
127
135
|
export const createTimestamp = ({
|
|
128
136
|
millis = minMillis,
|
|
@@ -130,14 +138,12 @@ export const createTimestamp = ({
|
|
|
130
138
|
nodeId = minNodeId,
|
|
131
139
|
}: Partial<Timestamp> = {}): Timestamp => ({ millis, counter, nodeId });
|
|
132
140
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
export const createInitialTimestamp = (deps: NanoIdLibDep): Timestamp => {
|
|
136
|
-
const nodeId = deps.nanoIdLib.customAlphabet(hexAlphabet, 16)() as NodeId;
|
|
141
|
+
export const createInitialTimestamp = (deps: RandomBytesDep): Timestamp => {
|
|
142
|
+
const nodeId = bytesToHex(deps.randomBytes.create(8)) as NodeId;
|
|
137
143
|
return createTimestamp({ nodeId });
|
|
138
144
|
};
|
|
139
145
|
|
|
140
|
-
/**
|
|
146
|
+
/** Sortable string representation of {@link Timestamp}. */
|
|
141
147
|
export type TimestampString = string & Brand<"TimestampString">;
|
|
142
148
|
|
|
143
149
|
export const timestampToTimestampString = (t: Timestamp): TimestampString =>
|
|
@@ -222,12 +228,8 @@ export const receiveTimestamp =
|
|
|
222
228
|
Timestamp,
|
|
223
229
|
| TimestampDriftError
|
|
224
230
|
| TimestampCounterOverflowError
|
|
225
|
-
| TimestampDuplicateNodeError
|
|
226
231
|
| TimestampTimeOutOfRangeError
|
|
227
232
|
> => {
|
|
228
|
-
if (local.nodeId === remote.nodeId) {
|
|
229
|
-
return err({ type: "TimestampDuplicateNodeError", nodeId: local.nodeId });
|
|
230
|
-
}
|
|
231
233
|
const millis = getNextMillis(deps)([local.millis, remote.millis]);
|
|
232
234
|
if (!millis.ok) return millis;
|
|
233
235
|
|
|
@@ -249,18 +251,19 @@ export const receiveTimestamp =
|
|
|
249
251
|
});
|
|
250
252
|
};
|
|
251
253
|
|
|
252
|
-
/**
|
|
253
|
-
export
|
|
254
|
+
/** Sortable bytes representation of {@link Timestamp}. */
|
|
255
|
+
export const TimestampBytes = brand("TimestampBytes", Uint8Array);
|
|
256
|
+
export type TimestampBytes = typeof TimestampBytes.Type;
|
|
254
257
|
|
|
255
|
-
export const
|
|
258
|
+
export const timestampBytesLength = NonNegativeInt.orThrow(16);
|
|
256
259
|
|
|
257
|
-
export const
|
|
260
|
+
export const timestampToTimestampBytes = (
|
|
258
261
|
timestamp: Timestamp,
|
|
259
|
-
):
|
|
262
|
+
): TimestampBytes => {
|
|
260
263
|
const { millis, counter, nodeId } = timestamp;
|
|
261
264
|
|
|
262
265
|
// 6 bytes for millis, 2 bytes for counter, 8 bytes for nodeId.
|
|
263
|
-
const value = new Uint8Array(16);
|
|
266
|
+
const value = new globalThis.Uint8Array(16);
|
|
264
267
|
|
|
265
268
|
// Encode `millis` into the first 6 bytes.
|
|
266
269
|
const millisBigInt = BigInt(millis);
|
|
@@ -281,11 +284,11 @@ export const timestampToBinaryTimestamp = (
|
|
|
281
284
|
value[8 + i] = byte;
|
|
282
285
|
}
|
|
283
286
|
|
|
284
|
-
return value as
|
|
287
|
+
return value as TimestampBytes;
|
|
285
288
|
};
|
|
286
289
|
|
|
287
|
-
export const
|
|
288
|
-
timestamp:
|
|
290
|
+
export const timestampBytesToTimestamp = (
|
|
291
|
+
timestamp: TimestampBytes,
|
|
289
292
|
): Timestamp => {
|
|
290
293
|
// Decode `millis` from the first 6 bytes.
|
|
291
294
|
const millis =
|
|
@@ -308,4 +311,4 @@ export const binaryTimestampToTimestamp = (
|
|
|
308
311
|
return { millis: Number(millis), counter, nodeId } as Timestamp;
|
|
309
312
|
};
|
|
310
313
|
|
|
311
|
-
export const
|
|
314
|
+
export const orderTimestampBytes: Order<TimestampBytes> = orderUint8Array;
|
package/src/ManyToManyMap.ts
CHANGED
|
@@ -1,22 +1,84 @@
|
|
|
1
|
-
|
|
1
|
+
import { assert } from "./Assert.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bidirectional many-to-many map between keys and values.
|
|
5
|
+
*
|
|
6
|
+
* Why useful:
|
|
7
|
+
*
|
|
8
|
+
* - Provides O(1) (amortized) forward lookup (key -> values) and reverse lookup
|
|
9
|
+
* (value -> keys) without maintaining two maps manually and risking them
|
|
10
|
+
* diverging.
|
|
11
|
+
* - Natural fit for pub/sub style relations such as ownerId ↔ WebSocket, tag ↔
|
|
12
|
+
* item, user ↔ role, entity ↔ subscription where both directions are
|
|
13
|
+
* frequently queried.
|
|
14
|
+
* - Supports fast membership tests via `hasPair`, `hasKey`, and `hasValue`.
|
|
15
|
+
* - Iteration helpers (`forEach`, iterator) allow treating the structure as an
|
|
16
|
+
* edge list when needed.
|
|
17
|
+
*
|
|
18
|
+
* Complexity:
|
|
19
|
+
*
|
|
20
|
+
* - `add` / `remove` / `has*` / `get*` each perform a constant number of Map/Set
|
|
21
|
+
* operations (O(1) expected).
|
|
22
|
+
* - `deleteKey` and `deleteValue` are O(d) where d is the number of associated
|
|
23
|
+
* values / keys (the degree). This is optimal because every associated pair
|
|
24
|
+
* must be touched once.
|
|
25
|
+
* - In the Relay use case a socket (value) typically has only dozens of owners
|
|
26
|
+
* (degree small), and connection closes (triggering deleteValue) are
|
|
27
|
+
* relatively infrequent, so O(d) is acceptable.
|
|
28
|
+
*
|
|
29
|
+
* Object identity:
|
|
30
|
+
*
|
|
31
|
+
* - Keys and values are compared by reference (standard Map / Set semantics).
|
|
32
|
+
* Structural hashing of objects in JavaScript is non-trivial, can be
|
|
33
|
+
* expensive, and collision-prone if done naively. Prefer using stable
|
|
34
|
+
* primitive identifiers (ids, strings) instead of attempting to hash full
|
|
35
|
+
* object structures.
|
|
36
|
+
* - If structural equivalence is truly required, wrap objects in an adapter that
|
|
37
|
+
* supplies a canonical hash/id and stores/retrieves the original objects
|
|
38
|
+
* separately. This is a rare need; avoid unless you have clear requirements.
|
|
39
|
+
*/
|
|
2
40
|
export interface ManyToManyMap<K, V> {
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Adds a key-value association to the map. Returns true if the pair was newly
|
|
43
|
+
* added, false if it already existed.
|
|
44
|
+
*/
|
|
45
|
+
add: (key: K, value: V) => boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Removes a specific key-value association. Returns true if the pair existed
|
|
49
|
+
* and was removed, false if it was not present.
|
|
50
|
+
*/
|
|
7
51
|
remove: (key: K, value: V) => boolean;
|
|
8
52
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Gets all values associated with a key. Returned set is the internal Set
|
|
55
|
+
* instance typed as ReadonlySet. Do not mutate.
|
|
56
|
+
*/
|
|
57
|
+
getValues: (key: K) => ReadonlySet<V> | undefined;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets all keys associated with a value. Returned set is the internal Set
|
|
61
|
+
* instance typed as ReadonlySet. Do not mutate.
|
|
62
|
+
*/
|
|
63
|
+
getKeys: (value: V) => ReadonlySet<K> | undefined;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Iterates over each key-value pair (in insertion order of keys, then values
|
|
67
|
+
* per key).
|
|
68
|
+
*/
|
|
69
|
+
forEach: (callback: (key: K, value: V) => void) => void;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Iterator over all key-value pairs enabling for..of and spread. Yields
|
|
73
|
+
* readonly [key, value] tuples.
|
|
74
|
+
*/
|
|
75
|
+
readonly [Symbol.iterator]: () => IterableIterator<readonly [K, V]>;
|
|
14
76
|
|
|
15
77
|
/** Checks if a specific key-value pair exists. */
|
|
16
78
|
hasPair: (key: K, value: V) => boolean;
|
|
17
79
|
|
|
18
80
|
/** Checks if a key exists in the map. */
|
|
19
|
-
hasKey(key: K)
|
|
81
|
+
hasKey: (key: K) => boolean;
|
|
20
82
|
|
|
21
83
|
/** Checks if a value exists in the map. */
|
|
22
84
|
hasValue: (value: V) => boolean;
|
|
@@ -28,17 +90,26 @@ export interface ManyToManyMap<K, V> {
|
|
|
28
90
|
deleteValue: (value: V) => boolean;
|
|
29
91
|
|
|
30
92
|
/** Clears all associations in the map. */
|
|
31
|
-
clear()
|
|
93
|
+
clear: () => void;
|
|
94
|
+
|
|
95
|
+
/** Number of distinct keys currently present. */
|
|
96
|
+
keyCount: () => number;
|
|
97
|
+
/** Number of distinct values currently present. */
|
|
98
|
+
valueCount: () => number;
|
|
99
|
+
/** Number of key-value pairs (associations) currently stored. */
|
|
100
|
+
pairCount: () => number;
|
|
32
101
|
}
|
|
33
102
|
|
|
34
103
|
/** Creates a {@link ManyToManyMap}. */
|
|
35
104
|
export const createManyToManyMap = <K, V>(): ManyToManyMap<K, V> => {
|
|
36
105
|
const forwardMap = new Map<K, Set<V>>();
|
|
37
106
|
const reverseMap = new Map<V, Set<K>>();
|
|
107
|
+
let pairCountInternal = 0;
|
|
38
108
|
|
|
39
109
|
const map: ManyToManyMap<K, V> = {
|
|
40
110
|
add(key: K, value: V) {
|
|
41
111
|
let values = forwardMap.get(key);
|
|
112
|
+
if (values?.has(value)) return false;
|
|
42
113
|
if (!values) {
|
|
43
114
|
values = new Set<V>();
|
|
44
115
|
forwardMap.set(key, values);
|
|
@@ -51,8 +122,8 @@ export const createManyToManyMap = <K, V>(): ManyToManyMap<K, V> => {
|
|
|
51
122
|
reverseMap.set(value, keys);
|
|
52
123
|
}
|
|
53
124
|
keys.add(key);
|
|
54
|
-
|
|
55
|
-
return
|
|
125
|
+
pairCountInternal++;
|
|
126
|
+
return true;
|
|
56
127
|
},
|
|
57
128
|
|
|
58
129
|
remove(key: K, value: V) {
|
|
@@ -65,24 +136,41 @@ export const createManyToManyMap = <K, V>(): ManyToManyMap<K, V> => {
|
|
|
65
136
|
}
|
|
66
137
|
|
|
67
138
|
const keys = reverseMap.get(value);
|
|
68
|
-
|
|
69
|
-
keys.delete(key);
|
|
70
|
-
if (keys.size === 0) {
|
|
71
|
-
reverseMap.delete(value);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
139
|
+
assert(keys, "Key-value mapping inconsistency");
|
|
74
140
|
|
|
141
|
+
keys.delete(key);
|
|
142
|
+
if (keys.size === 0) {
|
|
143
|
+
reverseMap.delete(value);
|
|
144
|
+
}
|
|
145
|
+
pairCountInternal--;
|
|
75
146
|
return true;
|
|
76
147
|
},
|
|
77
148
|
|
|
78
|
-
getValues(key: K) {
|
|
149
|
+
getValues(key: K): ReadonlySet<V> | undefined {
|
|
79
150
|
return forwardMap.get(key);
|
|
80
151
|
},
|
|
81
152
|
|
|
82
|
-
getKeys(value: V) {
|
|
153
|
+
getKeys(value: V): ReadonlySet<K> | undefined {
|
|
83
154
|
return reverseMap.get(value);
|
|
84
155
|
},
|
|
85
156
|
|
|
157
|
+
forEach(callback: (key: K, value: V) => void) {
|
|
158
|
+
for (const [key, values] of forwardMap) {
|
|
159
|
+
for (const value of values) callback(key, value);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
[Symbol.iterator](): IterableIterator<readonly [K, V]> {
|
|
164
|
+
const iterator = function* () {
|
|
165
|
+
for (const [key, values] of forwardMap) {
|
|
166
|
+
for (const value of values) {
|
|
167
|
+
yield [key, value] as const;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
return iterator();
|
|
172
|
+
},
|
|
173
|
+
|
|
86
174
|
hasPair(key: K, value: V) {
|
|
87
175
|
const values = forwardMap.get(key);
|
|
88
176
|
return values?.has(value) ?? false;
|
|
@@ -99,7 +187,7 @@ export const createManyToManyMap = <K, V>(): ManyToManyMap<K, V> => {
|
|
|
99
187
|
deleteKey(key: K) {
|
|
100
188
|
const values = forwardMap.get(key);
|
|
101
189
|
if (!values) return false;
|
|
102
|
-
|
|
190
|
+
const removed = values.size;
|
|
103
191
|
for (const value of values) {
|
|
104
192
|
const keys = reverseMap.get(value);
|
|
105
193
|
if (keys) {
|
|
@@ -110,13 +198,14 @@ export const createManyToManyMap = <K, V>(): ManyToManyMap<K, V> => {
|
|
|
110
198
|
}
|
|
111
199
|
}
|
|
112
200
|
forwardMap.delete(key);
|
|
201
|
+
pairCountInternal -= removed;
|
|
113
202
|
return true;
|
|
114
203
|
},
|
|
115
204
|
|
|
116
205
|
deleteValue(value: V) {
|
|
117
206
|
const keys = reverseMap.get(value);
|
|
118
207
|
if (!keys) return false;
|
|
119
|
-
|
|
208
|
+
const removed = keys.size;
|
|
120
209
|
for (const key of keys) {
|
|
121
210
|
const values = forwardMap.get(key);
|
|
122
211
|
if (values) {
|
|
@@ -127,12 +216,26 @@ export const createManyToManyMap = <K, V>(): ManyToManyMap<K, V> => {
|
|
|
127
216
|
}
|
|
128
217
|
}
|
|
129
218
|
reverseMap.delete(value);
|
|
219
|
+
pairCountInternal -= removed;
|
|
130
220
|
return true;
|
|
131
221
|
},
|
|
132
222
|
|
|
133
223
|
clear() {
|
|
134
224
|
forwardMap.clear();
|
|
135
225
|
reverseMap.clear();
|
|
226
|
+
pairCountInternal = 0;
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
keyCount() {
|
|
230
|
+
return forwardMap.size;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
valueCount() {
|
|
234
|
+
return reverseMap.size;
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
pairCount() {
|
|
238
|
+
return pairCountInternal;
|
|
136
239
|
},
|
|
137
240
|
};
|
|
138
241
|
|
package/src/Number.ts
CHANGED
|
@@ -2,12 +2,8 @@ import { NonEmptyReadonlyArray } from "./Array.js";
|
|
|
2
2
|
import { assertNonEmptyReadonlyArray } from "./Assert.js";
|
|
3
3
|
import { err, ok, Result } from "./Result.js";
|
|
4
4
|
import { NonNegativeInt, PositiveInt } from "./Type.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
IsBranded,
|
|
8
|
-
Predicate,
|
|
9
|
-
WidenLiteral,
|
|
10
|
-
} from "./Types.js";
|
|
5
|
+
import { IntentionalNever, Predicate, WidenLiteral } from "./Types.js";
|
|
6
|
+
import { IsBranded } from "./Brand.js";
|
|
11
7
|
|
|
12
8
|
export const increment = (n: number): number => n + 1;
|
|
13
9
|
|
package/src/Random.ts
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
import { Random as RandomLib } from "random";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* A simple wrapper around Math.random().
|
|
11
|
-
*
|
|
10
|
+
* A simple wrapper around Math.random().
|
|
11
|
+
*
|
|
12
|
+
* For more complex needs check {@link RandomLibDep}.
|
|
12
13
|
*
|
|
13
14
|
* ### Example
|
|
14
15
|
*
|