@evolu/common 7.4.1 → 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 +851 -430
- 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 -824
- 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 +206 -277
- 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 -526
- package/src/Instances.ts +0 -92
- 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 -949
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid logical clock timestamps for CRDT ordering.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { bytesToHex } from "../Buffer.js";
|
|
2
|
-
import { RandomBytesDep } from "../Crypto.js";
|
|
8
|
+
import type { RandomBytesDep } from "../Crypto.js";
|
|
3
9
|
import { createEqObject, eqNumber, eqString } from "../Eq.js";
|
|
4
10
|
import { increment } from "../Number.js";
|
|
5
|
-
import { Order
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
11
|
+
import type { Order } from "../Order.js";
|
|
12
|
+
import { orderUint8Array } from "../Order.js";
|
|
13
|
+
import type { Result } from "../Result.js";
|
|
14
|
+
import { err, ok } from "../Result.js";
|
|
15
|
+
import type { TimeDep } from "../Time.js";
|
|
16
|
+
import { Millis, minMillis } from "../Time.js";
|
|
17
|
+
import type { DateIso, InferType, Typed } from "../Type.js";
|
|
8
18
|
import {
|
|
9
19
|
brand,
|
|
10
|
-
|
|
11
|
-
InferType,
|
|
20
|
+
length,
|
|
12
21
|
lessThanOrEqualTo,
|
|
13
22
|
NonNegativeInt,
|
|
14
23
|
object,
|
|
@@ -26,6 +35,9 @@ export interface TimestampConfig {
|
|
|
26
35
|
readonly maxDrift: number;
|
|
27
36
|
}
|
|
28
37
|
|
|
38
|
+
/** Default value for {@link TimestampConfig.maxDrift}. */
|
|
39
|
+
export const defaultTimestampMaxDrift = 5 * 60 * 1000;
|
|
40
|
+
|
|
29
41
|
export interface TimestampConfigDep {
|
|
30
42
|
readonly timestampConfig: TimestampConfig;
|
|
31
43
|
}
|
|
@@ -35,44 +47,18 @@ export type TimestampError =
|
|
|
35
47
|
| TimestampCounterOverflowError
|
|
36
48
|
| TimestampTimeOutOfRangeError;
|
|
37
49
|
|
|
38
|
-
export interface TimestampDriftError {
|
|
39
|
-
readonly type: "TimestampDriftError";
|
|
50
|
+
export interface TimestampDriftError extends Typed<"TimestampDriftError"> {
|
|
40
51
|
readonly next: Millis;
|
|
41
52
|
readonly now: Millis;
|
|
42
53
|
}
|
|
43
54
|
|
|
44
|
-
export interface TimestampCounterOverflowError {
|
|
45
|
-
readonly type: "TimestampCounterOverflowError";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface TimestampTimeOutOfRangeError {
|
|
49
|
-
readonly type: "TimestampTimeOutOfRangeError";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Millis is a timestamp in milliseconds, like `Date.now()`, but limited to the
|
|
54
|
-
* maximum value representable in 6 bytes (281474976710655) minus 1 (reserved
|
|
55
|
-
* for infinity). This enables more efficient binary serialization, saving 2
|
|
56
|
-
* bytes compared to the typical 8-byte (64-bit) timestamp representation.
|
|
57
|
-
*
|
|
58
|
-
* This limit is enforced to prevent data corruption. If a device's clock
|
|
59
|
-
* exceeds this range, Evolu will stop saving data until the clock is
|
|
60
|
-
* corrected.
|
|
61
|
-
*
|
|
62
|
-
* `new Date(281474976710654).toString()` = Tue Aug 02 10889 07:31:49
|
|
63
|
-
*/
|
|
64
|
-
export const Millis = brand(
|
|
65
|
-
"Millis",
|
|
66
|
-
lessThanOrEqualTo(281474976710655 - 1)(NonNegativeInt),
|
|
67
|
-
);
|
|
68
|
-
export type Millis = typeof Millis.Type;
|
|
55
|
+
export interface TimestampCounterOverflowError extends Typed<"TimestampCounterOverflowError"> {}
|
|
69
56
|
|
|
70
|
-
export
|
|
71
|
-
export const maxMillis = (281474976710655 - 1) as Millis;
|
|
57
|
+
export interface TimestampTimeOutOfRangeError extends Typed<"TimestampTimeOutOfRangeError"> {}
|
|
72
58
|
|
|
73
|
-
export const Counter = brand(
|
|
59
|
+
export const Counter = /*#__PURE__*/ brand(
|
|
74
60
|
"Counter",
|
|
75
|
-
lessThanOrEqualTo(65535)(NonNegativeInt),
|
|
61
|
+
/*#__PURE__*/ lessThanOrEqualTo(65535)(NonNegativeInt),
|
|
76
62
|
);
|
|
77
63
|
export type Counter = typeof Counter.Type;
|
|
78
64
|
|
|
@@ -104,7 +90,7 @@ export const maxCounter = 65535 as Counter;
|
|
|
104
90
|
* yet they will think they are synced. This is extremely rare and can be
|
|
105
91
|
* resolved by resetting one device to generate a new NodeId.
|
|
106
92
|
*/
|
|
107
|
-
export const NodeId = regex("NodeId", /^[a-f0-9]{16}$/)(String);
|
|
93
|
+
export const NodeId = /*#__PURE__*/ regex("NodeId", /^[a-f0-9]{16}$/)(String);
|
|
108
94
|
export type NodeId = typeof NodeId.Type;
|
|
109
95
|
|
|
110
96
|
export const minNodeId = "0000000000000000" as NodeId;
|
|
@@ -116,7 +102,7 @@ export const maxNodeId = "ffffffffffffffff" as NodeId;
|
|
|
116
102
|
* Timestamps serve as globally unique, causally ordered identifiers for CRDT
|
|
117
103
|
* messages in Evolu's sync protocol.
|
|
118
104
|
*
|
|
119
|
-
*
|
|
105
|
+
* ## Why Hybrid Logical Clocks
|
|
120
106
|
*
|
|
121
107
|
* Evolu uses Hybrid Logical Clocks (HLC), which combine physical time (millis)
|
|
122
108
|
* with a logical counter. This hybrid approach preserves causality like logical
|
|
@@ -141,14 +127,14 @@ export const maxNodeId = "ffffffffffffffff" as NodeId;
|
|
|
141
127
|
* configuration protects against buggy clocks and prevents problematic
|
|
142
128
|
* future-dated entries from propagating through the network.
|
|
143
129
|
*
|
|
144
|
-
*
|
|
130
|
+
* ## References
|
|
145
131
|
*
|
|
146
132
|
* - https://muratbuffalo.blogspot.com/2014/07/hybrid-logical-clocks.html
|
|
147
133
|
* - https://sergeiturukin.com/2017/06/26/hybrid-logical-clocks.html
|
|
148
134
|
* - https://jaredforsyth.com/posts/hybrid-logical-clocks/
|
|
149
135
|
* - https://willowprotocol.org/more/timestamps_really/index.html
|
|
150
136
|
*
|
|
151
|
-
*
|
|
137
|
+
* ## Privacy Considerations
|
|
152
138
|
*
|
|
153
139
|
* Timestamps are metadata visible to relays and collaborators. While it can be
|
|
154
140
|
* considered a privacy leak, let us explain why it's necessary, and how to
|
|
@@ -165,8 +151,12 @@ export const maxNodeId = "ffffffffffffffff" as NodeId;
|
|
|
165
151
|
* 2. Periodically and randomly flush messages to sync tables
|
|
166
152
|
*
|
|
167
153
|
* **Trade-off:** It breaks real-time collaboration.
|
|
154
|
+
*
|
|
155
|
+
* Another technique is generating fake random activity (dummy messages) to mask
|
|
156
|
+
* real usage patterns. This preserves real-time collaboration but increases
|
|
157
|
+
* storage and bandwidth usage.
|
|
168
158
|
*/
|
|
169
|
-
export const Timestamp = object({
|
|
159
|
+
export const Timestamp = /*#__PURE__*/ object({
|
|
170
160
|
millis: Millis,
|
|
171
161
|
counter: Counter,
|
|
172
162
|
nodeId: NodeId,
|
|
@@ -174,7 +164,7 @@ export const Timestamp = object({
|
|
|
174
164
|
export interface Timestamp extends InferType<typeof Timestamp> {}
|
|
175
165
|
|
|
176
166
|
/** Equality function for comparing {@link Timestamp}. */
|
|
177
|
-
export const eqTimestamp = createEqObject<Timestamp>({
|
|
167
|
+
export const eqTimestamp = /*#__PURE__*/ createEqObject<Timestamp>({
|
|
178
168
|
millis: eqNumber,
|
|
179
169
|
counter: eqNumber,
|
|
180
170
|
nodeId: eqString,
|
|
@@ -277,10 +267,13 @@ export const receiveTimestamp =
|
|
|
277
267
|
};
|
|
278
268
|
|
|
279
269
|
/** Sortable bytes representation of {@link Timestamp}. */
|
|
280
|
-
export const TimestampBytes = brand(
|
|
270
|
+
export const TimestampBytes = /*#__PURE__*/ brand(
|
|
271
|
+
"TimestampBytes",
|
|
272
|
+
/*#__PURE__*/ length(16)(Uint8Array),
|
|
273
|
+
);
|
|
281
274
|
export type TimestampBytes = typeof TimestampBytes.Type;
|
|
282
275
|
|
|
283
|
-
export const timestampBytesLength = NonNegativeInt.orThrow(16);
|
|
276
|
+
export const timestampBytesLength = /*#__PURE__*/ NonNegativeInt.orThrow(16);
|
|
284
277
|
|
|
285
278
|
export const timestampToTimestampBytes = (
|
|
286
279
|
timestamp: Timestamp,
|
package/src/local-first/index.ts
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal local-first modules re-exported from "@evolu/common/local-first"
|
|
3
|
-
*
|
|
4
|
-
* These exports expose the internal building blocks of Evolu's local-first
|
|
5
|
-
* implementation (Db, Relay, Sync, Query, Storage, Timestamp, etc.). They are
|
|
6
|
-
* primarily intended for use within Evolu packages and for advanced use-cases
|
|
7
|
-
* that require direct access to local-first internals.
|
|
8
|
-
*
|
|
9
|
-
* Public consumers should us top-level exports from `@evolu/common` unless you
|
|
10
|
-
* have a specific need to import these internals.
|
|
11
|
-
*
|
|
12
|
-
* @module
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
1
|
export * from "./Db.js";
|
|
2
|
+
export * from "./Error.js";
|
|
16
3
|
export * from "./Evolu.js";
|
|
17
4
|
export * from "./Owner.js";
|
|
18
|
-
export * from "./Platform.js";
|
|
19
5
|
export * from "./Protocol.js";
|
|
20
6
|
export * from "./Query.js";
|
|
21
7
|
export * from "./Relay.js";
|
|
22
8
|
export * from "./Schema.js";
|
|
9
|
+
export * from "./Shared.js";
|
|
23
10
|
export * from "./Storage.js";
|
|
24
|
-
export * from "./Sync.js";
|
|
25
11
|
export * from "./Timestamp.js";
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 Evolu
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/src/Instances.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Manages disposable instances by key, ensuring exactly one instance per key.
|
|
3
|
-
*
|
|
4
|
-
* Use cases:
|
|
5
|
-
*
|
|
6
|
-
* - One mutex per key to prevent concurrent writes
|
|
7
|
-
* - Preserving state during hot module reloading
|
|
8
|
-
*
|
|
9
|
-
* **Important:** Do not use this as global shared state. Use it locally or pass
|
|
10
|
-
* it as a dependency instead. The only exception is for hot reloading support,
|
|
11
|
-
* where Evolu uses it to ensure only one instance exists across module reloads
|
|
12
|
-
* (having two Evolu instances with the same name would mean two SQLite
|
|
13
|
-
* connections to the same file, which could corrupt data).
|
|
14
|
-
*/
|
|
15
|
-
export interface Instances<K extends string, T extends Disposable> extends Disposable {
|
|
16
|
-
/**
|
|
17
|
-
* Ensures an instance exists for the given key, creating it if necessary. If
|
|
18
|
-
* the instance already exists, the optional `onCacheHit` callback is invoked
|
|
19
|
-
* to update the existing instance.
|
|
20
|
-
*/
|
|
21
|
-
readonly ensure: (key: K, create: () => T, onCacheHit?: (instance: T) => void) => T;
|
|
22
|
-
/** Gets an instance by key, or returns `null` if it doesn't exist. */
|
|
23
|
-
readonly get: (key: K) => T | null;
|
|
24
|
-
/** Checks if an instance exists for the given key. */
|
|
25
|
-
readonly has: (key: K) => boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Deletes and disposes an instance by key. Returns `true` if the instance
|
|
28
|
-
* existed and was deleted, `false` otherwise.
|
|
29
|
-
*/
|
|
30
|
-
readonly delete: (key: K) => boolean;
|
|
31
|
-
}
|
|
32
|
-
/** Creates an {@link Instances}. */
|
|
33
|
-
export declare const createInstances: <K extends string, T extends Disposable>() => Instances<K, T>;
|
|
34
|
-
//# sourceMappingURL=Instances.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Instances.d.ts","sourceRoot":"","sources":["../../src/Instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,SAAS,CACxB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,UAAU,CACpB,SAAQ,UAAU;IAClB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CACf,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,MAAM,CAAC,EACf,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,KAC/B,CAAC,CAAC;IAEP,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAEnC,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;CACtC;AAED,oCAAoC;AACpC,eAAO,MAAM,eAAe,GAC1B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,UAAU,OACjB,SAAS,CAAC,CAAC,EAAE,CAAC,CA6ClB,CAAC"}
|
package/dist/src/Instances.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/** Creates an {@link Instances}. */
|
|
2
|
-
export const createInstances = () => {
|
|
3
|
-
const instances = new Map();
|
|
4
|
-
return {
|
|
5
|
-
ensure: (key, create, onCacheHit) => {
|
|
6
|
-
let instance = instances.get(key);
|
|
7
|
-
if (instance == null) {
|
|
8
|
-
instance = create();
|
|
9
|
-
instances.set(key, instance);
|
|
10
|
-
}
|
|
11
|
-
else if (onCacheHit) {
|
|
12
|
-
onCacheHit(instance);
|
|
13
|
-
}
|
|
14
|
-
return instance;
|
|
15
|
-
},
|
|
16
|
-
get: (key) => instances.get(key) ?? null,
|
|
17
|
-
has: (key) => instances.has(key),
|
|
18
|
-
delete: (key) => {
|
|
19
|
-
const instance = instances.get(key);
|
|
20
|
-
if (instance == null)
|
|
21
|
-
return false;
|
|
22
|
-
instances.delete(key);
|
|
23
|
-
instance[Symbol.dispose]();
|
|
24
|
-
return true;
|
|
25
|
-
},
|
|
26
|
-
[Symbol.dispose]: () => {
|
|
27
|
-
const errors = [];
|
|
28
|
-
for (const instance of instances.values()) {
|
|
29
|
-
try {
|
|
30
|
-
instance[Symbol.dispose]();
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
errors.push(error);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
instances.clear();
|
|
37
|
-
if (errors.length === 1)
|
|
38
|
-
throw errors[0];
|
|
39
|
-
if (errors.length > 1) {
|
|
40
|
-
throw new AggregateError(errors, "Multiple disposal errors occurred");
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
};
|
package/dist/src/Resources.d.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { Result } from "./Result.js";
|
|
2
|
-
/**
|
|
3
|
-
* A generic resource manager that handles reference counting and delayed
|
|
4
|
-
* disposal of shared resources. Useful for managing expensive resources like
|
|
5
|
-
* WebSocket connections that need to be shared among multiple consumers.
|
|
6
|
-
*/
|
|
7
|
-
export interface Resources<TResource extends Disposable, TResourceKey extends string, TResourceConfig, TConsumer, TConsumerId extends string> extends Disposable {
|
|
8
|
-
/**
|
|
9
|
-
* Adds a consumer to resources, creating them if necessary. Increments
|
|
10
|
-
* reference counts for existing consumer-resource pairs.
|
|
11
|
-
*/
|
|
12
|
-
readonly addConsumer: (consumer: TConsumer, resourceConfigs: ReadonlyArray<TResourceConfig>) => void;
|
|
13
|
-
/**
|
|
14
|
-
* Removes a consumer from resources. Decrements reference counts and
|
|
15
|
-
* schedules disposal when no consumers remain.
|
|
16
|
-
*
|
|
17
|
-
* Returns an error if the resource doesn't exist or if the consumer wasn't
|
|
18
|
-
* added to the resource.
|
|
19
|
-
*/
|
|
20
|
-
readonly removeConsumer: (consumer: TConsumer, resourceConfigs: ReadonlyArray<TResourceConfig>) => Result<void, ResourceNotFoundError<TResourceKey> | ConsumerNotFoundError<TConsumerId, TResourceKey>>;
|
|
21
|
-
/** Gets the resource for the specified key, or null if it doesn't exist. */
|
|
22
|
-
readonly getResource: (key: TResourceKey) => TResource | null;
|
|
23
|
-
/** Gets all consumer IDs currently using the specified resource key. */
|
|
24
|
-
readonly getConsumersForResource: (key: TResourceKey) => ReadonlyArray<TConsumerId>;
|
|
25
|
-
/** Checks if a consumer is currently using any resources. */
|
|
26
|
-
readonly hasConsumerAnyResource: (consumer: TConsumer) => boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Gets the consumer for the specified consumer ID, or null if not found or
|
|
29
|
-
* not using any resources.
|
|
30
|
-
*/
|
|
31
|
-
readonly getConsumer: (consumerId: TConsumerId) => TConsumer | null;
|
|
32
|
-
}
|
|
33
|
-
/** Error when trying to remove a consumer from a resource that doesn't exist. */
|
|
34
|
-
export interface ResourceNotFoundError<TResourceKey extends string = string> {
|
|
35
|
-
readonly type: "ResourceNotFoundError";
|
|
36
|
-
readonly resourceKey: TResourceKey;
|
|
37
|
-
}
|
|
38
|
-
/** Error when trying to remove a consumer that wasn't added to a resource. */
|
|
39
|
-
export interface ConsumerNotFoundError<TConsumerId extends string = string, TResourceKey extends string = string> {
|
|
40
|
-
readonly type: "ConsumerNotFoundError";
|
|
41
|
-
readonly consumerId: TConsumerId;
|
|
42
|
-
readonly resourceKey: TResourceKey;
|
|
43
|
-
}
|
|
44
|
-
export interface ResourcesConfig<TResource extends Disposable, TResourceKey extends string, TResourceConfig, TConsumer, TConsumerId extends string> {
|
|
45
|
-
/** Creates a new resource for the given config. */
|
|
46
|
-
readonly createResource: (config: TResourceConfig) => TResource;
|
|
47
|
-
/** Extracts a unique key from a resource config for deduplication. */
|
|
48
|
-
readonly getResourceKey: (config: TResourceConfig) => TResourceKey;
|
|
49
|
-
/** Extracts a unique identifier from a consumer for reference counting. */
|
|
50
|
-
readonly getConsumerId: (consumer: TConsumer) => TConsumerId;
|
|
51
|
-
/**
|
|
52
|
-
* Delay in milliseconds before disposing unused resources. Helps avoid
|
|
53
|
-
* resource churn during rapid add/remove cycles. Defaults to 100ms.
|
|
54
|
-
*/
|
|
55
|
-
readonly disposalDelay?: number;
|
|
56
|
-
/**
|
|
57
|
-
* Called when a consumer is added to a resource for the first time. This
|
|
58
|
-
* happens when the consumer's reference count goes from 0 to 1 for this
|
|
59
|
-
* resource.
|
|
60
|
-
*/
|
|
61
|
-
readonly onConsumerAdded?: (consumer: TConsumer, resource: TResource, resourceKey: TResourceKey) => void;
|
|
62
|
-
/**
|
|
63
|
-
* Called when a consumer is completely removed from a resource. This happens
|
|
64
|
-
* when the consumer's reference count goes from 1 to 0 for this resource.
|
|
65
|
-
*/
|
|
66
|
-
readonly onConsumerRemoved?: (consumer: TConsumer, resource: TResource, resourceKey: TResourceKey) => void;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Creates {@link Resources}.
|
|
70
|
-
*
|
|
71
|
-
* This tracks which consumers are using which resources and maintains reference
|
|
72
|
-
* counts to know when it's safe to dispose resources. Resources are created
|
|
73
|
-
* on-demand and disposed with a configurable delay to avoid churn.
|
|
74
|
-
*
|
|
75
|
-
* ### Example Usage
|
|
76
|
-
*
|
|
77
|
-
* ```ts
|
|
78
|
-
* // WebSocket connections
|
|
79
|
-
* interface WebSocketConfig {
|
|
80
|
-
* readonly url: WebSocketUrl;
|
|
81
|
-
* }
|
|
82
|
-
*
|
|
83
|
-
* type WebSocketUrl = string & Brand<"WebSocketUrl">;
|
|
84
|
-
* type UserId = string & Brand<"UserId">;
|
|
85
|
-
*
|
|
86
|
-
* const webSockets = createResources<
|
|
87
|
-
* WebSocket,
|
|
88
|
-
* WebSocketUrl,
|
|
89
|
-
* WebSocketConfig,
|
|
90
|
-
* User,
|
|
91
|
-
* UserId
|
|
92
|
-
* >({
|
|
93
|
-
* createResource: (config) => new WebSocket(config.url),
|
|
94
|
-
* getResourceKey: (config) => config.url,
|
|
95
|
-
* getConsumerId: (user) => user.id,
|
|
96
|
-
* disposalDelay: 1000,
|
|
97
|
-
* });
|
|
98
|
-
*
|
|
99
|
-
* // Add users to WebSocket connections
|
|
100
|
-
* webSockets.addConsumer(user1, [
|
|
101
|
-
* { url: "ws://server1.com" as WebSocketUrl },
|
|
102
|
-
* { url: "ws://server2.com" as WebSocketUrl },
|
|
103
|
-
* ]);
|
|
104
|
-
* webSockets.addConsumer(user2, [
|
|
105
|
-
* { url: "ws://server1.com" as WebSocketUrl },
|
|
106
|
-
* ]);
|
|
107
|
-
*
|
|
108
|
-
* // Remove users - server1 stays alive (user2 still using it)
|
|
109
|
-
* webSockets.removeConsumer(user1, [
|
|
110
|
-
* { url: "ws://server1.com" as WebSocketUrl },
|
|
111
|
-
* { url: "ws://server2.com" as WebSocketUrl },
|
|
112
|
-
* ]);
|
|
113
|
-
*
|
|
114
|
-
* // server2 gets disposed after delay, server1 stays alive
|
|
115
|
-
* ```
|
|
116
|
-
*/
|
|
117
|
-
export declare const createResources: <TResource extends Disposable, TResourceKey extends string, TResourceConfig, TConsumer, TConsumerId extends string>(config: ResourcesConfig<TResource, TResourceKey, TResourceConfig, TConsumer, TConsumerId>) => Resources<TResource, TResourceKey, TResourceConfig, TConsumer, TConsumerId>;
|
|
118
|
-
//# sourceMappingURL=Resources.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Resources.d.ts","sourceRoot":"","sources":["../../src/Resources.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Resources.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { err, ok } from "./Result.js";
|
|
2
|
-
import { PositiveInt } from "./Type.js";
|
|
3
|
-
/**
|
|
4
|
-
* Creates {@link Resources}.
|
|
5
|
-
*
|
|
6
|
-
* This tracks which consumers are using which resources and maintains reference
|
|
7
|
-
* counts to know when it's safe to dispose resources. Resources are created
|
|
8
|
-
* on-demand and disposed with a configurable delay to avoid churn.
|
|
9
|
-
*
|
|
10
|
-
* ### Example Usage
|
|
11
|
-
*
|
|
12
|
-
* ```ts
|
|
13
|
-
* // WebSocket connections
|
|
14
|
-
* interface WebSocketConfig {
|
|
15
|
-
* readonly url: WebSocketUrl;
|
|
16
|
-
* }
|
|
17
|
-
*
|
|
18
|
-
* type WebSocketUrl = string & Brand<"WebSocketUrl">;
|
|
19
|
-
* type UserId = string & Brand<"UserId">;
|
|
20
|
-
*
|
|
21
|
-
* const webSockets = createResources<
|
|
22
|
-
* WebSocket,
|
|
23
|
-
* WebSocketUrl,
|
|
24
|
-
* WebSocketConfig,
|
|
25
|
-
* User,
|
|
26
|
-
* UserId
|
|
27
|
-
* >({
|
|
28
|
-
* createResource: (config) => new WebSocket(config.url),
|
|
29
|
-
* getResourceKey: (config) => config.url,
|
|
30
|
-
* getConsumerId: (user) => user.id,
|
|
31
|
-
* disposalDelay: 1000,
|
|
32
|
-
* });
|
|
33
|
-
*
|
|
34
|
-
* // Add users to WebSocket connections
|
|
35
|
-
* webSockets.addConsumer(user1, [
|
|
36
|
-
* { url: "ws://server1.com" as WebSocketUrl },
|
|
37
|
-
* { url: "ws://server2.com" as WebSocketUrl },
|
|
38
|
-
* ]);
|
|
39
|
-
* webSockets.addConsumer(user2, [
|
|
40
|
-
* { url: "ws://server1.com" as WebSocketUrl },
|
|
41
|
-
* ]);
|
|
42
|
-
*
|
|
43
|
-
* // Remove users - server1 stays alive (user2 still using it)
|
|
44
|
-
* webSockets.removeConsumer(user1, [
|
|
45
|
-
* { url: "ws://server1.com" as WebSocketUrl },
|
|
46
|
-
* { url: "ws://server2.com" as WebSocketUrl },
|
|
47
|
-
* ]);
|
|
48
|
-
*
|
|
49
|
-
* // server2 gets disposed after delay, server1 stays alive
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export const createResources = (config) => {
|
|
53
|
-
let isDisposed = false;
|
|
54
|
-
const resourcesMap = new Map();
|
|
55
|
-
const consumerCounts = new Map();
|
|
56
|
-
const consumers = new Map();
|
|
57
|
-
const disposalTimeouts = new Map();
|
|
58
|
-
const disposalDelay = config.disposalDelay ?? 100;
|
|
59
|
-
const ensureResource = (resourceConfig) => {
|
|
60
|
-
const key = config.getResourceKey(resourceConfig);
|
|
61
|
-
const timeout = disposalTimeouts.get(key);
|
|
62
|
-
if (timeout) {
|
|
63
|
-
clearTimeout(timeout);
|
|
64
|
-
disposalTimeouts.delete(key);
|
|
65
|
-
}
|
|
66
|
-
if (!resourcesMap.has(key)) {
|
|
67
|
-
const resource = config.createResource(resourceConfig);
|
|
68
|
-
resourcesMap.set(key, resource);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const scheduleDisposal = (key) => {
|
|
72
|
-
const timeout = setTimeout(() => {
|
|
73
|
-
const resource = resourcesMap.get(key);
|
|
74
|
-
if (resource) {
|
|
75
|
-
resource[Symbol.dispose]();
|
|
76
|
-
resourcesMap.delete(key);
|
|
77
|
-
}
|
|
78
|
-
disposalTimeouts.delete(key);
|
|
79
|
-
}, disposalDelay);
|
|
80
|
-
disposalTimeouts.set(key, timeout);
|
|
81
|
-
};
|
|
82
|
-
const resources = {
|
|
83
|
-
addConsumer: (consumer, resourceConfigs) => {
|
|
84
|
-
if (isDisposed)
|
|
85
|
-
return;
|
|
86
|
-
const consumerId = config.getConsumerId(consumer);
|
|
87
|
-
// Store consumer (last added consumer for this ID)
|
|
88
|
-
consumers.set(consumerId, consumer);
|
|
89
|
-
for (const resourceConfig of resourceConfigs) {
|
|
90
|
-
ensureResource(resourceConfig);
|
|
91
|
-
const resourceKey = config.getResourceKey(resourceConfig);
|
|
92
|
-
let counts = consumerCounts.get(resourceKey);
|
|
93
|
-
if (!counts) {
|
|
94
|
-
counts = new Map();
|
|
95
|
-
consumerCounts.set(resourceKey, counts);
|
|
96
|
-
}
|
|
97
|
-
const currentCount = counts.get(consumerId) ?? 0;
|
|
98
|
-
const newCount = currentCount + 1;
|
|
99
|
-
counts.set(consumerId, PositiveInt.orThrow(newCount));
|
|
100
|
-
// Call onConsumerAdded callback only when consumer is added for the first time (0 -> 1)
|
|
101
|
-
if (currentCount === 0 && config.onConsumerAdded) {
|
|
102
|
-
const resource = resourcesMap.get(resourceKey);
|
|
103
|
-
if (resource) {
|
|
104
|
-
config.onConsumerAdded(consumer, resource, resourceKey);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
removeConsumer: (consumer, resourceConfigs) => {
|
|
110
|
-
if (isDisposed)
|
|
111
|
-
return ok();
|
|
112
|
-
const consumerId = config.getConsumerId(consumer);
|
|
113
|
-
for (const resourceConfig of resourceConfigs) {
|
|
114
|
-
const key = config.getResourceKey(resourceConfig);
|
|
115
|
-
const counts = consumerCounts.get(key);
|
|
116
|
-
if (!counts) {
|
|
117
|
-
return err({ type: "ResourceNotFoundError", resourceKey: key });
|
|
118
|
-
}
|
|
119
|
-
const currentCount = counts.get(consumerId);
|
|
120
|
-
if (currentCount == null) {
|
|
121
|
-
return err({
|
|
122
|
-
type: "ConsumerNotFoundError",
|
|
123
|
-
consumerId: consumerId,
|
|
124
|
-
resourceKey: key,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (currentCount === 1) {
|
|
128
|
-
counts.delete(consumerId);
|
|
129
|
-
// Call onConsumerRemoved callback only when consumer is completely removed (1 -> 0)
|
|
130
|
-
if (config.onConsumerRemoved) {
|
|
131
|
-
const resource = resourcesMap.get(key);
|
|
132
|
-
if (resource) {
|
|
133
|
-
config.onConsumerRemoved(consumer, resource, key);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (counts.size === 0) {
|
|
137
|
-
consumerCounts.delete(key);
|
|
138
|
-
scheduleDisposal(key);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
counts.set(consumerId, PositiveInt.orThrow(currentCount - 1));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (!resources.hasConsumerAnyResource(consumer)) {
|
|
146
|
-
consumers.delete(consumerId);
|
|
147
|
-
}
|
|
148
|
-
return ok();
|
|
149
|
-
},
|
|
150
|
-
getResource: (key) => {
|
|
151
|
-
if (isDisposed)
|
|
152
|
-
return null;
|
|
153
|
-
return resourcesMap.get(key) ?? null;
|
|
154
|
-
},
|
|
155
|
-
getConsumersForResource: (key) => {
|
|
156
|
-
if (isDisposed)
|
|
157
|
-
return [];
|
|
158
|
-
const counts = consumerCounts.get(key);
|
|
159
|
-
return counts ? Array.from(counts.keys()) : [];
|
|
160
|
-
},
|
|
161
|
-
hasConsumerAnyResource: (consumer) => {
|
|
162
|
-
if (isDisposed)
|
|
163
|
-
return false;
|
|
164
|
-
const consumerId = config.getConsumerId(consumer);
|
|
165
|
-
// If slow, can be optimized with reverse index
|
|
166
|
-
return Array.from(consumerCounts.values()).some((counts) => counts.has(consumerId));
|
|
167
|
-
},
|
|
168
|
-
getConsumer: (consumerId) => {
|
|
169
|
-
if (isDisposed)
|
|
170
|
-
return null;
|
|
171
|
-
const consumer = consumers.get(consumerId);
|
|
172
|
-
if (!consumer)
|
|
173
|
-
return null;
|
|
174
|
-
// Only return consumer if it's currently using any resources
|
|
175
|
-
if (!resources.hasConsumerAnyResource(consumer)) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
return consumer;
|
|
179
|
-
},
|
|
180
|
-
[Symbol.dispose]: () => {
|
|
181
|
-
if (isDisposed)
|
|
182
|
-
return;
|
|
183
|
-
isDisposed = true;
|
|
184
|
-
for (const timeout of disposalTimeouts.values()) {
|
|
185
|
-
clearTimeout(timeout);
|
|
186
|
-
}
|
|
187
|
-
disposalTimeouts.clear();
|
|
188
|
-
for (const resource of resourcesMap.values()) {
|
|
189
|
-
resource[Symbol.dispose]();
|
|
190
|
-
}
|
|
191
|
-
resourcesMap.clear();
|
|
192
|
-
consumerCounts.clear();
|
|
193
|
-
consumers.clear();
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
return resources;
|
|
197
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FlushSync is for libraries like React to flush updates synchronously inside
|
|
3
|
-
* the provided callback to ensure the DOM is updated immediately.
|
|
4
|
-
*
|
|
5
|
-
* For example, with React, when we want to focus on an element rendered as a
|
|
6
|
-
* result of a mutation, Evolu ensures all DOM changes are flushed synchronously
|
|
7
|
-
* if an onComplete callback is used.
|
|
8
|
-
*
|
|
9
|
-
* https://react.dev/reference/react-dom/flushSync
|
|
10
|
-
*/
|
|
11
|
-
export type FlushSync = (callback: () => void) => void;
|
|
12
|
-
export interface FlushSyncDep {
|
|
13
|
-
readonly flushSync: FlushSync;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Reload the app in a platform-specific way.
|
|
17
|
-
*
|
|
18
|
-
* - **Web**: Redirects to the specified URL
|
|
19
|
-
* - **React Native**: Restarts the app (URL parameter ignored)
|
|
20
|
-
*/
|
|
21
|
-
export type ReloadApp = (url: string) => void;
|
|
22
|
-
export interface ReloadAppDep {
|
|
23
|
-
readonly reloadApp: ReloadApp;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=Platform.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Platform.d.ts","sourceRoot":"","sources":["../../../src/local-first/Platform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B;AAED;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public local-first API exported from "@evolu/common"
|
|
3
|
-
*
|
|
4
|
-
* This module provides the supported consumer-facing API for Evolu's
|
|
5
|
-
* local-first system: prefer importing from `@evolu/common` (this module) when
|
|
6
|
-
* using Evolu in applications. If you require access to lower level internals,
|
|
7
|
-
* consider importing from `@evolu/common/local-first`.
|
|
8
|
-
*
|
|
9
|
-
* @module
|
|
10
|
-
*/
|
|
11
|
-
export { createEvolu } from "./Evolu.js";
|
|
12
|
-
export type { Evolu, EvoluConfig, EvoluDeps, EvoluError } from "./Evolu.js";
|
|
13
|
-
export type { UnuseOwner } from "./Evolu.js";
|
|
14
|
-
export * from "./LocalAuth.js";
|
|
15
|
-
export * from "./Owner.js";
|
|
16
|
-
export * as kysely from "./PublicKysely.js";
|
|
17
|
-
export type { InferRow, Query, QueryRows, Row } from "./Query.js";
|
|
18
|
-
export type { EvoluSchema } from "./Schema.js";
|
|
19
|
-
export type { NetworkError, PaymentRequiredError, ServerError, SyncOwner, SyncState, SyncStateInitial, SyncStateIsNotSynced, SyncStateIsSynced, SyncStateIsSyncing, } from "./Sync.js";
|
|
20
|
-
export { Timestamp, timestampBytesToTimestamp, timestampToTimestampBytes, } from "./Timestamp.js";
|
|
21
|
-
export type { TimestampBytes, TimestampCounterOverflowError, TimestampDriftError, TimestampError, TimestampTimeOutOfRangeError, } from "./Timestamp.js";
|
|
22
|
-
//# sourceMappingURL=Public.d.ts.map
|