@evolu/common 6.0.1-preview.3 → 6.0.1-preview.31
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/Array.d.ts +69 -5
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +64 -5
- package/dist/src/Assert.d.ts +6 -16
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +6 -18
- 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 +8 -7
- package/dist/src/Cache.d.ts +44 -0
- package/dist/src/Cache.d.ts.map +1 -0
- package/dist/src/Cache.js +52 -0
- package/dist/src/Callbacks.d.ts +45 -12
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +14 -7
- 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 +32 -45
- package/dist/src/Evolu/Db.d.ts +161 -65
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +286 -694
- 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 +208 -133
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +188 -183
- 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 +150 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +174 -0
- package/dist/src/Evolu/Owner.d.ts +273 -120
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +130 -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 +277 -232
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +603 -378
- 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 +92 -7
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +238 -76
- 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 +240 -26
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +189 -91
- package/dist/src/Evolu/Sync.d.ts +67 -13
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +441 -20
- package/dist/src/Evolu/Timestamp.d.ts +85 -27
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +77 -18
- package/dist/src/Identicon.d.ts +35 -0
- package/dist/src/Identicon.d.ts.map +1 -0
- package/dist/src/Identicon.js +143 -0
- package/dist/src/Instances.d.ts +34 -0
- package/dist/src/Instances.d.ts.map +1 -0
- package/dist/src/Instances.js +44 -0
- package/dist/src/ManyToManyMap.d.ts +71 -10
- package/dist/src/ManyToManyMap.d.ts.map +1 -1
- package/dist/src/ManyToManyMap.js +41 -6
- package/dist/src/Number.d.ts +4 -3
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +5 -4
- package/dist/src/Platform.d.ts +20 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +22 -0
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Resources.d.ts +118 -0
- package/dist/src/Resources.d.ts.map +1 -0
- package/dist/src/Resources.js +197 -0
- package/dist/src/Result.d.ts +184 -52
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Skiplist.js +2 -1
- package/dist/src/Sqlite.d.ts +63 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +110 -9
- package/dist/src/Task.d.ts +586 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +469 -0
- package/dist/src/Time.d.ts +66 -1
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +99 -5
- package/dist/src/Type.d.ts +622 -340
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +666 -464
- package/dist/src/Types.d.ts +1 -75
- 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 -18
- 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 +7 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -2
- package/package.json +14 -13
- package/src/Array.ts +90 -11
- package/src/Assert.ts +6 -24
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +7 -7
- package/src/Cache.ts +85 -0
- package/src/Callbacks.ts +62 -22
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +97 -82
- package/src/Evolu/Db.ts +517 -1020
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +464 -355
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +463 -0
- package/src/Evolu/Owner.ts +355 -228
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +859 -676
- 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 +437 -93
- package/src/Evolu/Schema.ts +391 -191
- package/src/Evolu/Storage.ts +532 -135
- package/src/Evolu/Sync.ts +766 -37
- package/src/Evolu/Timestamp.ts +88 -35
- package/src/Identicon.ts +197 -0
- package/src/Instances.ts +90 -0
- package/src/ManyToManyMap.ts +124 -24
- package/src/Number.ts +6 -10
- package/src/Platform.ts +26 -0
- package/src/Random.ts +3 -2
- package/src/Resources.ts +367 -0
- package/src/Result.ts +191 -54
- package/src/Skiplist.ts +1 -1
- package/src/Sqlite.ts +122 -17
- package/src/Task.ts +901 -0
- package/src/Time.ts +180 -5
- package/src/Type.ts +1084 -727
- package/src/Types.ts +1 -77
- package/src/WebSocket.ts +27 -25
- package/src/Worker.ts +72 -23
- package/src/index.ts +7 -2
- 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/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/Brand.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility interface for creating branded types.
|
|
3
|
+
*
|
|
4
|
+
* Branded types enhance type safety by differentiating otherwise identical base
|
|
5
|
+
* types, such as `number` or `string`, to enforce stricter type checks.
|
|
6
|
+
*
|
|
7
|
+
* Supports multiple brands, allowing types to act like flags.
|
|
8
|
+
*
|
|
9
|
+
* ### Example 1: Single Brand
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // A branded type definition
|
|
13
|
+
* type UserId = number & Brand<"UserId">;
|
|
14
|
+
*
|
|
15
|
+
* // A function that creates `UserId` values.
|
|
16
|
+
* // Casting with `as UserId` is unsafe, so `createUserId` must be unit-tested.
|
|
17
|
+
* const createUserId = (): UserId => {
|
|
18
|
+
* return 123 as UserId; // Unsafe casting
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* const userId = createUserId();
|
|
22
|
+
*
|
|
23
|
+
* // A function that accepts only `UserId`.
|
|
24
|
+
* const getUser = (id: UserId) => {
|
|
25
|
+
* // Implementation
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* getUser(userId); // ✅ Valid
|
|
29
|
+
* getUser(123); // ❌ TypeScript error
|
|
30
|
+
* getUser("123"); // ❌ TypeScript error
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ### Example 2: Multiple Brands
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* // Define branded types
|
|
37
|
+
* type Min1 = string & Brand<"Min1">;
|
|
38
|
+
* type Max100 = string & Brand<"Max100">;
|
|
39
|
+
* type Min1Max100 = string & Brand<"Min1" | "Max100">;
|
|
40
|
+
*
|
|
41
|
+
* // Functions requiring specific brands
|
|
42
|
+
* const requiresMin1 = (value: Min1): void => {};
|
|
43
|
+
* const requiresMax100 = (value: Max100): void => {};
|
|
44
|
+
*
|
|
45
|
+
* // Values with single brands
|
|
46
|
+
* const min1Value: Min1 = "hello" as Min1;
|
|
47
|
+
* const max100Value: Max100 = "world" as Max100;
|
|
48
|
+
*
|
|
49
|
+
* // Value with multiple brands
|
|
50
|
+
* const min1Max100Value: Min1Max100 = "typescript" as Min1Max100;
|
|
51
|
+
*
|
|
52
|
+
* // Valid cases
|
|
53
|
+
* requiresMin1(min1Value); // ✅ Valid
|
|
54
|
+
* requiresMax100(max100Value); // ✅ Valid
|
|
55
|
+
* requiresMin1(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Min1
|
|
56
|
+
* requiresMax100(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Max100
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export interface Brand<B extends string> {
|
|
60
|
+
readonly [__brand]: Readonly<Record<B, true>>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare const __brand: unique symbol;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Determines whether a type `T` is a branded type.
|
|
67
|
+
*
|
|
68
|
+
* Works with any base type intersected with a `Brand`.
|
|
69
|
+
*
|
|
70
|
+
* ### Examples
|
|
71
|
+
*
|
|
72
|
+
* - `IsBranded<string>` -> false
|
|
73
|
+
* - `IsBranded<string & Brand<"X">>` -> true
|
|
74
|
+
*/
|
|
75
|
+
export type IsBranded<T> = T extends Brand<string> ? true : false;
|
package/src/Buffer.ts
CHANGED
|
@@ -6,7 +6,7 @@ export {
|
|
|
6
6
|
concatBytes,
|
|
7
7
|
hexToBytes,
|
|
8
8
|
utf8ToBytes,
|
|
9
|
-
} from "@noble/ciphers/utils";
|
|
9
|
+
} from "@noble/ciphers/utils.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Custom error for {@link Buffer}-related failures like premature end of data.
|
|
@@ -116,10 +116,10 @@ export const createBuffer = (
|
|
|
116
116
|
let value = arrayLike
|
|
117
117
|
? new globalThis.Uint8Array(arrayLike)
|
|
118
118
|
: new globalThis.Uint8Array(512);
|
|
119
|
-
let length = (arrayLike ? arrayLike.length : 0)
|
|
119
|
+
let length = NonNegativeInt.orThrow(arrayLike ? arrayLike.length : 0);
|
|
120
120
|
|
|
121
121
|
const buffer: Buffer = {
|
|
122
|
-
getCapacity: () => value.length
|
|
122
|
+
getCapacity: () => NonNegativeInt.orThrow(value.length),
|
|
123
123
|
|
|
124
124
|
getLength: () => length,
|
|
125
125
|
|
|
@@ -132,7 +132,7 @@ export const createBuffer = (
|
|
|
132
132
|
value.set(oldValue);
|
|
133
133
|
}
|
|
134
134
|
value.set(arg, length);
|
|
135
|
-
length = (length + arg.length)
|
|
135
|
+
length = NonNegativeInt.orThrow(length + arg.length);
|
|
136
136
|
},
|
|
137
137
|
|
|
138
138
|
shift: () => {
|
|
@@ -142,7 +142,7 @@ export const createBuffer = (
|
|
|
142
142
|
const first = value[0];
|
|
143
143
|
value = value.subarray(1);
|
|
144
144
|
length--;
|
|
145
|
-
return first
|
|
145
|
+
return NonNegativeInt.orThrow(first);
|
|
146
146
|
},
|
|
147
147
|
|
|
148
148
|
shiftN: (n) => {
|
|
@@ -151,7 +151,7 @@ export const createBuffer = (
|
|
|
151
151
|
}
|
|
152
152
|
const subarray = value.subarray(0, n);
|
|
153
153
|
value = value.subarray(n);
|
|
154
|
-
length = (length - n)
|
|
154
|
+
length = NonNegativeInt.orThrow(length - n);
|
|
155
155
|
return subarray;
|
|
156
156
|
},
|
|
157
157
|
|
|
@@ -165,7 +165,7 @@ export const createBuffer = (
|
|
|
165
165
|
},
|
|
166
166
|
|
|
167
167
|
reset: () => {
|
|
168
|
-
length = 0
|
|
168
|
+
length = NonNegativeInt.orThrow(0);
|
|
169
169
|
},
|
|
170
170
|
|
|
171
171
|
unwrap: () => value.subarray(0, length),
|
package/src/Cache.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🗄️ Generic cache interface and LRU cache implementation.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { PositiveInt } from "./Type.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generic cache interface providing basic key-value storage operations.
|
|
11
|
+
*
|
|
12
|
+
* Keys are compared by reference (standard Map semantics).
|
|
13
|
+
*
|
|
14
|
+
* Note: Cache does not extend Map because eviction policies (like in LRU)
|
|
15
|
+
* violate the Liskov Substitution Principle.
|
|
16
|
+
*/
|
|
17
|
+
export interface Cache<K, V> {
|
|
18
|
+
/** Checks if a key exists in the cache. */
|
|
19
|
+
has: (key: K) => boolean;
|
|
20
|
+
|
|
21
|
+
/** Retrieves the value for a key, or undefined if not present. */
|
|
22
|
+
get: (key: K) => V | undefined;
|
|
23
|
+
|
|
24
|
+
/** Stores a key-value pair in the cache. */
|
|
25
|
+
set: (key: K, val: V) => void;
|
|
26
|
+
|
|
27
|
+
/** Removes a key from the cache. */
|
|
28
|
+
delete: (key: K) => void;
|
|
29
|
+
|
|
30
|
+
/** Returns a readonly view of the internal Map. */
|
|
31
|
+
readonly map: ReadonlyMap<K, V>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Creates a Least Recently Used (LRU) cache with a maximum capacity.
|
|
36
|
+
*
|
|
37
|
+
* When the cache reaches capacity, the least recently used entry is evicted.
|
|
38
|
+
* Both `get` and `set` operations update the access order.
|
|
39
|
+
*
|
|
40
|
+
* ### Example
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* const cache = createLruCache<string, number>(2);
|
|
44
|
+
* cache.set("a", 1);
|
|
45
|
+
* cache.set("b", 2);
|
|
46
|
+
* cache.set("c", 3); // Evicts "a"
|
|
47
|
+
* cache.has("a"); // false
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export const createLruCache = <K, V>(capacity: PositiveInt): Cache<K, V> => {
|
|
51
|
+
const internalMap = new Map<K, V>();
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
has: (key) => internalMap.has(key),
|
|
55
|
+
|
|
56
|
+
get: (key) => {
|
|
57
|
+
const value = internalMap.get(key);
|
|
58
|
+
if (value === undefined) return undefined;
|
|
59
|
+
|
|
60
|
+
// Move to end (most recently used)
|
|
61
|
+
internalMap.delete(key);
|
|
62
|
+
internalMap.set(key, value);
|
|
63
|
+
return value;
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
set: (key, val) => {
|
|
67
|
+
// If key exists, delete it first to update order
|
|
68
|
+
if (internalMap.has(key)) {
|
|
69
|
+
internalMap.delete(key);
|
|
70
|
+
} else if (internalMap.size === capacity) {
|
|
71
|
+
// Evict least recently used (first entry)
|
|
72
|
+
const firstKey = internalMap.keys().next().value as K;
|
|
73
|
+
internalMap.delete(firstKey);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
internalMap.set(key, val);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
delete: (key) => {
|
|
80
|
+
internalMap.delete(key);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
map: internalMap,
|
|
84
|
+
};
|
|
85
|
+
};
|
package/src/Callbacks.ts
CHANGED
|
@@ -1,43 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Brand } from "./Brand.js";
|
|
2
|
+
import { RandomBytesDep } from "./Crypto.js";
|
|
3
|
+
import { Result } from "./Result.js";
|
|
4
|
+
import { createId, Id } from "./Type.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
|
-
*
|
|
7
|
+
* Request-response correlation for callbacks across boundaries.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
+
* Stores callbacks with unique IDs and executes them once with an optional
|
|
10
|
+
* argument. Executed callbacks are automatically removed.
|
|
9
11
|
*
|
|
10
|
-
* This is useful for
|
|
11
|
-
*
|
|
12
|
+
* This is useful for correlating asynchronous request-response operations
|
|
13
|
+
* across boundaries where callback functions cannot be passed directly (e.g.,
|
|
14
|
+
* web workers, message queues).
|
|
15
|
+
*
|
|
16
|
+
* The `execute` method intentionally does not use try-catch or {@link Result}
|
|
17
|
+
* because it's the callback's responsibility to handle its own errors.
|
|
18
|
+
*
|
|
19
|
+
* ### Example
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* // No-argument callbacks
|
|
23
|
+
* const callbacks = createCallbacks(deps);
|
|
24
|
+
* const id = callbacks.register(() => console.log("called"));
|
|
25
|
+
* callbacks.execute(id);
|
|
26
|
+
*
|
|
27
|
+
* // With argument callbacks
|
|
28
|
+
* const stringCallbacks = createCallbacks<string>(deps);
|
|
29
|
+
* const id = stringCallbacks.register((value) => {
|
|
30
|
+
* console.log(value);
|
|
31
|
+
* });
|
|
32
|
+
* stringCallbacks.execute(id, "hello");
|
|
33
|
+
*
|
|
34
|
+
* // Promise.withResolvers pattern
|
|
35
|
+
* const promiseCallbacks = createCallbacks<string>(deps);
|
|
36
|
+
* const { promise, resolve } = Promise.withResolvers<string>();
|
|
37
|
+
* const id = promiseCallbacks.register(resolve);
|
|
38
|
+
* promiseCallbacks.execute(id, "resolved value");
|
|
39
|
+
* await promise; // "resolved value"
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @template T - The type of argument passed to callbacks (defaults to undefined
|
|
43
|
+
* for no-argument callbacks)
|
|
12
44
|
*/
|
|
13
|
-
export interface Callbacks {
|
|
45
|
+
export interface Callbacks<T = undefined> {
|
|
14
46
|
/** Registers a callback function and returns a unique ID. */
|
|
15
|
-
readonly register: (callback: (arg
|
|
47
|
+
readonly register: (callback: (arg: T) => void) => CallbackId;
|
|
16
48
|
|
|
17
49
|
/** Executes and removes a callback associated with the given ID. */
|
|
18
|
-
readonly execute:
|
|
50
|
+
readonly execute: T extends undefined
|
|
51
|
+
? (id: CallbackId) => undefined
|
|
52
|
+
: (id: CallbackId, arg: T) => undefined;
|
|
19
53
|
}
|
|
20
54
|
|
|
21
|
-
|
|
55
|
+
/** Unique identifier for a callback in {@link Callbacks}. */
|
|
56
|
+
export type CallbackId = Id & Brand<"Callback">;
|
|
22
57
|
|
|
23
|
-
|
|
24
|
-
|
|
58
|
+
/** Creates a new {@link Callbacks}. */
|
|
59
|
+
export const createCallbacks = <T = undefined>(
|
|
60
|
+
deps: RandomBytesDep,
|
|
61
|
+
): Callbacks<T> => {
|
|
62
|
+
const callbackMap = new Map<CallbackId, (arg: T) => void>();
|
|
25
63
|
|
|
26
|
-
|
|
64
|
+
return {
|
|
27
65
|
register: (callback) => {
|
|
28
|
-
const id = deps
|
|
66
|
+
const id = createId<"Callback">(deps);
|
|
29
67
|
callbackMap.set(id, callback);
|
|
30
68
|
return id;
|
|
31
69
|
},
|
|
32
70
|
|
|
33
|
-
execute: (id,
|
|
71
|
+
execute: (id: CallbackId, ...args: T extends undefined ? [] : [T]) => {
|
|
34
72
|
const callback = callbackMap.get(id);
|
|
35
|
-
if (callback)
|
|
36
|
-
|
|
37
|
-
|
|
73
|
+
if (!callback) return;
|
|
74
|
+
callbackMap.delete(id);
|
|
75
|
+
if (args.length === 0) {
|
|
76
|
+
// Called without argument (undefined case)
|
|
77
|
+
(callback as () => void)();
|
|
78
|
+
} else {
|
|
79
|
+
callback(args[0]);
|
|
38
80
|
}
|
|
39
81
|
},
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return callbacks;
|
|
82
|
+
} as Callbacks<T>;
|
|
43
83
|
};
|
package/src/Console.ts
CHANGED
|
@@ -13,14 +13,28 @@
|
|
|
13
13
|
* ### Example
|
|
14
14
|
*
|
|
15
15
|
* ```ts
|
|
16
|
-
* deps.console.log("[
|
|
16
|
+
* deps.console.log("[evolu]", "createEvoluInstance", { name });
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
19
|
* **Tip**: In browser dev tools, you can filter logs by tag (e.g., `[db]`) to
|
|
20
|
-
* quickly find relevant messages.
|
|
20
|
+
* quickly find relevant messages. In Node.js, use `grep` to filter output:
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* ```bash
|
|
23
|
+
* node app.js | grep "\[relay\]" # Show only relay logs
|
|
24
|
+
* node app.js | grep -E "\[db\]|\[sql\]" # Show db and sql logs
|
|
25
|
+
* node app.js | grep -v "\[debug\]" # Hide debug logs
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Or add to package.json scripts:
|
|
29
|
+
*
|
|
30
|
+
* ```json
|
|
31
|
+
* {
|
|
32
|
+
* "scripts": {
|
|
33
|
+
* "dev:relay": "node app.js | grep \"\\[relay\\]\"",
|
|
34
|
+
* "dev:db": "node app.js | grep -E \"\\[db\\]|\\[sql\\]\""
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
24
38
|
*
|
|
25
39
|
* @module
|
|
26
40
|
*/
|
|
@@ -51,6 +65,9 @@ export interface Console {
|
|
|
51
65
|
/** Starts a timer with an optional label */
|
|
52
66
|
time: (label?: string) => void;
|
|
53
67
|
|
|
68
|
+
/** Logs the elapsed time for a timer without ending it */
|
|
69
|
+
timeLog: (label?: string, ...data: Array<any>) => void;
|
|
70
|
+
|
|
54
71
|
/** Ends a timer and logs the elapsed time */
|
|
55
72
|
timeEnd: (label?: string) => void;
|
|
56
73
|
|
|
@@ -69,9 +86,6 @@ export interface Console {
|
|
|
69
86
|
/** Writes a message if the value is falsy, otherwise does nothing */
|
|
70
87
|
assert: (value: any, message?: string, ...optionalParams: Array<any>) => void;
|
|
71
88
|
|
|
72
|
-
/** Logs the elapsed time for a timer without ending it */
|
|
73
|
-
timeLog: (label?: string, ...data: Array<any>) => void;
|
|
74
|
-
|
|
75
89
|
/** Prints a stack trace with an optional message */
|
|
76
90
|
trace: (message?: any, ...optionalParams: Array<any>) => void;
|
|
77
91
|
}
|
|
@@ -121,6 +135,10 @@ export const createConsole = (config: ConsoleConfig = {}): Console => {
|
|
|
121
135
|
// eslint-disable-next-line no-console
|
|
122
136
|
if (instance.enabled) console.time(label);
|
|
123
137
|
},
|
|
138
|
+
timeLog: (label, ...data) => {
|
|
139
|
+
// eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
|
|
140
|
+
if (instance.enabled) console.timeLog(label, ...data);
|
|
141
|
+
},
|
|
124
142
|
timeEnd: (label) => {
|
|
125
143
|
// eslint-disable-next-line no-console
|
|
126
144
|
if (instance.enabled) console.timeEnd(label);
|
|
@@ -145,10 +163,6 @@ export const createConsole = (config: ConsoleConfig = {}): Console => {
|
|
|
145
163
|
// eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
|
|
146
164
|
if (instance.enabled) console.assert(value, message, ...optionalParams);
|
|
147
165
|
},
|
|
148
|
-
timeLog: (label, ...data) => {
|
|
149
|
-
// eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
|
|
150
|
-
if (instance.enabled) console.timeLog(label, ...data);
|
|
151
|
-
},
|
|
152
166
|
trace: (message, ...optionalParams) => {
|
|
153
167
|
// eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
|
|
154
168
|
if (instance.enabled) console.trace(message, ...optionalParams);
|
|
@@ -157,3 +171,69 @@ export const createConsole = (config: ConsoleConfig = {}): Console => {
|
|
|
157
171
|
|
|
158
172
|
return instance;
|
|
159
173
|
};
|
|
174
|
+
|
|
175
|
+
export interface ConsoleWithTimeConfig extends ConsoleConfig {
|
|
176
|
+
/**
|
|
177
|
+
* Type of timestamp to prepend to log messages.
|
|
178
|
+
*
|
|
179
|
+
* - 'absolute': Shows actual time (e.g., "14:32:15.234")
|
|
180
|
+
* - 'relative': Shows time since console creation (e.g., "+1.234s")
|
|
181
|
+
*/
|
|
182
|
+
readonly timestampType: "absolute" | "relative";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Creates a console instance with timestamp prefixes. */
|
|
186
|
+
export const createConsoleWithTime = (
|
|
187
|
+
config: ConsoleWithTimeConfig = { timestampType: "relative" },
|
|
188
|
+
): Console => {
|
|
189
|
+
const console = createConsole(config);
|
|
190
|
+
const startTime = performance.now();
|
|
191
|
+
|
|
192
|
+
const getTimestamp = (): string => {
|
|
193
|
+
if (config.timestampType === "relative") {
|
|
194
|
+
const elapsed = (performance.now() - startTime) / 1000;
|
|
195
|
+
|
|
196
|
+
// Format for better readability at different time scales
|
|
197
|
+
if (elapsed < 60) {
|
|
198
|
+
// Under 1 minute: show seconds with millisecond precision
|
|
199
|
+
return `+${elapsed.toFixed(3)}s`;
|
|
200
|
+
} else if (elapsed < 3600) {
|
|
201
|
+
// 1 minute to 1 hour: show minutes and seconds with millisecond precision
|
|
202
|
+
const minutes = Math.floor(elapsed / 60);
|
|
203
|
+
const seconds = (elapsed % 60).toFixed(3);
|
|
204
|
+
return `+${minutes}m${seconds}s`;
|
|
205
|
+
} else {
|
|
206
|
+
// Over 1 hour: show hours, minutes, and seconds with millisecond precision
|
|
207
|
+
const hours = Math.floor(elapsed / 3600);
|
|
208
|
+
const minutes = Math.floor((elapsed % 3600) / 60);
|
|
209
|
+
const seconds = ((elapsed % 3600) % 60).toFixed(3);
|
|
210
|
+
return `+${hours}h${minutes}m${seconds}s`;
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
// Absolute time - format as HH:MM:SS.mmm
|
|
214
|
+
const now = new Date();
|
|
215
|
+
const hours = now.getHours().toString().padStart(2, "0");
|
|
216
|
+
const minutes = now.getMinutes().toString().padStart(2, "0");
|
|
217
|
+
const seconds = now.getSeconds().toString().padStart(2, "0");
|
|
218
|
+
const milliseconds = now.getMilliseconds().toString().padStart(3, "0");
|
|
219
|
+
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const withTimestamp =
|
|
224
|
+
(fn: (...args: Array<any>) => void) =>
|
|
225
|
+
(...args: Array<any>) => {
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
227
|
+
fn(`[${getTimestamp()}]`, ...args);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// Override methods that should have timestamps
|
|
231
|
+
console.log = withTimestamp(console.log);
|
|
232
|
+
console.info = withTimestamp(console.info);
|
|
233
|
+
console.warn = withTimestamp(console.warn);
|
|
234
|
+
console.error = withTimestamp(console.error);
|
|
235
|
+
console.debug = withTimestamp(console.debug);
|
|
236
|
+
console.trace = withTimestamp(console.trace);
|
|
237
|
+
|
|
238
|
+
return console;
|
|
239
|
+
};
|