@evolu/common 8.3.3 → 8.5.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/dist/src/Array.d.ts +24 -0
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +27 -1
- package/dist/src/Assert.d.ts +62 -19
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +64 -17
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +4 -1
- package/dist/src/Lookup.js +1 -0
- package/dist/src/Platform.js +1 -0
- package/dist/src/Relation.js +3 -3
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +9 -3
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +3 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +9 -7
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +3 -1
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +1 -1
- package/dist/src/Task.d.ts +122 -60
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +40 -53
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +1 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +9 -2
- package/dist/src/Type.d.ts +146 -9
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +10 -7
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +8 -0
- package/dist/src/Worker.js +1 -1
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +7 -6
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +1 -0
- package/dist/src/local-first/Owner.d.ts +8 -7
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +4 -4
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +31 -25
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +2 -1
- package/dist/src/local-first/Relay.js +1 -1
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +4 -2
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +1 -1
- package/package.json +1 -1
- package/src/Array.ts +28 -1
- package/src/Assert.ts +81 -19
- package/src/LeakDetector.ts +4 -3
- package/src/Lookup.ts +1 -0
- package/src/Platform.ts +1 -0
- package/src/Relation.ts +3 -3
- package/src/Resource.ts +9 -3
- package/src/Result.ts +5 -5
- package/src/Sqlite.ts +9 -7
- package/src/Store.ts +3 -1
- package/src/String.ts +1 -1
- package/src/Task.ts +133 -82
- package/src/Test.ts +1 -0
- package/src/Time.ts +9 -2
- package/src/Type.ts +173 -29
- package/src/WebSocket.ts +8 -0
- package/src/Worker.ts +1 -1
- package/src/local-first/Db.ts +11 -7
- package/src/local-first/Evolu.ts +1 -0
- package/src/local-first/Owner.ts +8 -7
- package/src/local-first/Protocol.ts +41 -31
- package/src/local-first/Query.ts +6 -3
- package/src/local-first/Relay.ts +1 -1
- package/src/local-first/Shared.ts +4 -2
- package/src/local-first/Timestamp.ts +1 -1
package/src/Assert.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts";
|
|
7
8
|
import type { Type } from "./Type.ts";
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -13,9 +14,9 @@ import type { Type } from "./Type.ts";
|
|
|
13
14
|
* Prevents invalid states from propagating through the system by halting
|
|
14
15
|
* execution when a condition fails, improving reliability and debuggability.
|
|
15
16
|
*
|
|
16
|
-
* Do not use this instead of {@link Type}. Assertions are intended
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Do not use this instead of {@link Type}. Assertions are intended when a
|
|
18
|
+
* condition is logically guaranteed to be true but TypeScript cannot prove it,
|
|
19
|
+
* or for catching and signaling developer mistakes eagerly.
|
|
19
20
|
*
|
|
20
21
|
* ### Example
|
|
21
22
|
*
|
|
@@ -32,6 +33,7 @@ export const assert: (
|
|
|
32
33
|
condition: unknown,
|
|
33
34
|
message: string,
|
|
34
35
|
) => asserts condition = (condition, message) => {
|
|
36
|
+
// oxlint-disable-next-line typescript/strict-boolean-expressions -- JavaScript truthiness is the contract of assert.
|
|
35
37
|
if (!condition) {
|
|
36
38
|
throw new Error(message);
|
|
37
39
|
}
|
|
@@ -40,8 +42,9 @@ export const assert: (
|
|
|
40
42
|
/**
|
|
41
43
|
* Asserts that a value is non-nullable.
|
|
42
44
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
+
* Following TypeScript's {@link NonNullable}, non-nullable here means neither
|
|
46
|
+
* null nor undefined. Use this when a value is logically guaranteed to be
|
|
47
|
+
* non-nullable but TypeScript cannot prove it.
|
|
45
48
|
*/
|
|
46
49
|
export const assertNonNullable: <T>(
|
|
47
50
|
value: T,
|
|
@@ -53,21 +56,80 @@ export const assertNonNullable: <T>(
|
|
|
53
56
|
assert(value != null, message);
|
|
54
57
|
};
|
|
55
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Asserts that a value is not null while preserving undefined.
|
|
61
|
+
*
|
|
62
|
+
* Use this when a value is logically guaranteed not to be null but TypeScript
|
|
63
|
+
* cannot prove it.
|
|
64
|
+
*
|
|
65
|
+
* ### Example
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { assertNotNull } from "@evolu/common";
|
|
69
|
+
*
|
|
70
|
+
* const value = undefined as string | null | undefined;
|
|
71
|
+
* assertNotNull(value);
|
|
72
|
+
* expectTypeOf(value).toEqualTypeOf<string | undefined>();
|
|
73
|
+
* expect(value).toBeUndefined();
|
|
74
|
+
* expect(() => assertNotNull(null)).toThrow(
|
|
75
|
+
* "Expected value not to be null.",
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export const assertNotNull: <T>(
|
|
80
|
+
value: T,
|
|
81
|
+
message?: string,
|
|
82
|
+
) => asserts value is T & ({} | undefined) = (
|
|
83
|
+
value,
|
|
84
|
+
message = "Expected value not to be null.",
|
|
85
|
+
) => {
|
|
86
|
+
assert(value !== null, message);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Asserts that a value is not undefined while preserving null.
|
|
91
|
+
*
|
|
92
|
+
* Use this when a value is logically guaranteed not to be undefined but
|
|
93
|
+
* TypeScript cannot prove it.
|
|
94
|
+
*
|
|
95
|
+
* ### Example
|
|
96
|
+
*
|
|
97
|
+
* ```ts
|
|
98
|
+
* import { assertNotUndefined } from "@evolu/common";
|
|
99
|
+
*
|
|
100
|
+
* const value = null as string | null | undefined;
|
|
101
|
+
* assertNotUndefined(value);
|
|
102
|
+
* expectTypeOf(value).toEqualTypeOf<string | null>();
|
|
103
|
+
* expect(value).toBeNull();
|
|
104
|
+
* expect(() => assertNotUndefined(undefined)).toThrow(
|
|
105
|
+
* "Expected value not to be undefined.",
|
|
106
|
+
* );
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export const assertNotUndefined: <T>(
|
|
110
|
+
value: T,
|
|
111
|
+
message?: string,
|
|
112
|
+
) => asserts value is T & ({} | null) = (
|
|
113
|
+
value,
|
|
114
|
+
message = "Expected value not to be undefined.",
|
|
115
|
+
) => {
|
|
116
|
+
assert(value !== undefined, message);
|
|
117
|
+
};
|
|
118
|
+
|
|
56
119
|
/**
|
|
57
120
|
* Asserts that an array is non-empty.
|
|
58
121
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* known.
|
|
122
|
+
* Use this when an array is logically guaranteed to be non-empty but TypeScript
|
|
123
|
+
* cannot prove it.
|
|
62
124
|
*
|
|
63
125
|
* ### Example
|
|
64
126
|
*
|
|
65
127
|
* ```ts
|
|
66
|
-
* import { assertNonEmptyArray } from "@evolu/common";
|
|
128
|
+
* import { assertNonEmptyArray, type NonEmptyArray } from "@evolu/common";
|
|
67
129
|
*
|
|
68
130
|
* const values = [1, 2, 3];
|
|
69
131
|
* assertNonEmptyArray(values);
|
|
70
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
132
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyArray<number>>();
|
|
71
133
|
* expect(values[0]).toBe(1);
|
|
72
134
|
* expect(() => assertNonEmptyArray([])).toThrow();
|
|
73
135
|
* ```
|
|
@@ -75,7 +137,7 @@ export const assertNonNullable: <T>(
|
|
|
75
137
|
export const assertNonEmptyArray: <T>(
|
|
76
138
|
arr: Array<T>,
|
|
77
139
|
message?: string,
|
|
78
|
-
) => asserts arr is
|
|
140
|
+
) => asserts arr is NonEmptyArray<T> = (
|
|
79
141
|
arr,
|
|
80
142
|
message = "Expected a non-empty array.",
|
|
81
143
|
) => {
|
|
@@ -85,20 +147,20 @@ export const assertNonEmptyArray: <T>(
|
|
|
85
147
|
/**
|
|
86
148
|
* Asserts that a readonly array is non-empty.
|
|
87
149
|
*
|
|
88
|
-
*
|
|
89
|
-
* TypeScript
|
|
90
|
-
* statically known.
|
|
150
|
+
* Use this when a readonly array is logically guaranteed to be non-empty but
|
|
151
|
+
* TypeScript cannot prove it.
|
|
91
152
|
*
|
|
92
153
|
* ### Example
|
|
93
154
|
*
|
|
94
155
|
* ```ts
|
|
95
|
-
* import {
|
|
156
|
+
* import {
|
|
157
|
+
* assertNonEmptyReadonlyArray,
|
|
158
|
+
* type NonEmptyReadonlyArray,
|
|
159
|
+
* } from "@evolu/common";
|
|
96
160
|
*
|
|
97
161
|
* const values: ReadonlyArray<number> = [1, 2, 3];
|
|
98
162
|
* assertNonEmptyReadonlyArray(values);
|
|
99
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
100
|
-
* readonly [number, ...Array<number>]
|
|
101
|
-
* >();
|
|
163
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyReadonlyArray<number>>();
|
|
102
164
|
* expect(values[0]).toBe(1);
|
|
103
165
|
* expect(() => assertNonEmptyReadonlyArray([])).toThrow();
|
|
104
166
|
* ```
|
|
@@ -106,7 +168,7 @@ export const assertNonEmptyArray: <T>(
|
|
|
106
168
|
export const assertNonEmptyReadonlyArray: <T>(
|
|
107
169
|
arr: ReadonlyArray<T>,
|
|
108
170
|
message?: string,
|
|
109
|
-
) => asserts arr is
|
|
171
|
+
) => asserts arr is NonEmptyReadonlyArray<T> = (
|
|
110
172
|
arr,
|
|
111
173
|
message = "Expected a non-empty readonly array.",
|
|
112
174
|
) => {
|
package/src/LeakDetector.ts
CHANGED
|
@@ -66,9 +66,10 @@ export const createLeakDetector = (deps: ConsoleDep): LeakDetector => {
|
|
|
66
66
|
if (typeof globalThis.FinalizationRegistry !== "function")
|
|
67
67
|
return noopLeakDetector;
|
|
68
68
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
const report = reportLeak(deps);
|
|
70
|
+
const registry = new globalThis.FinalizationRegistry<TrackedLeak>((leak) => {
|
|
71
|
+
report(leak);
|
|
72
|
+
});
|
|
72
73
|
|
|
73
74
|
return {
|
|
74
75
|
track: (target, leak, unregisterToken) => {
|
package/src/Lookup.ts
CHANGED
|
@@ -384,6 +384,7 @@ const structuralLookupInternal = (
|
|
|
384
384
|
value: unknown,
|
|
385
385
|
path: Set<object> = new Set(),
|
|
386
386
|
): string => {
|
|
387
|
+
// oxlint-disable-next-line typescript/switch-exhaustiveness-check -- The default rejects every unsupported unknown runtime type.
|
|
387
388
|
switch (typeof value) {
|
|
388
389
|
case "string":
|
|
389
390
|
return `s:${JSON.stringify(value)}`;
|
package/src/Platform.ts
CHANGED
|
@@ -176,6 +176,7 @@ const createTestGlobalErrors = (
|
|
|
176
176
|
globalThis.removeEventListener(webEvent, listener);
|
|
177
177
|
});
|
|
178
178
|
} else {
|
|
179
|
+
// oxlint-disable-next-line unicorn/prefer-type-error -- An unsupported runtime environment is not an invalid argument type.
|
|
179
180
|
throw new Error(`Unsupported platform global ${errorType} reporting.`);
|
|
180
181
|
}
|
|
181
182
|
|
package/src/Relation.ts
CHANGED
|
@@ -250,14 +250,14 @@ export function createRelation<A, B, LA = A, LB = B>({
|
|
|
250
250
|
removeByA: (a) => {
|
|
251
251
|
const relatedB = bByA.get(a);
|
|
252
252
|
if (!relatedB) return false;
|
|
253
|
-
for (const b of
|
|
253
|
+
for (const b of relatedB.keys()) removePair(a, b);
|
|
254
254
|
return true;
|
|
255
255
|
},
|
|
256
256
|
|
|
257
257
|
removeByB: (b) => {
|
|
258
258
|
const relatedA = aByB.get(b);
|
|
259
259
|
if (!relatedA) return false;
|
|
260
|
-
for (const a of
|
|
260
|
+
for (const a of relatedA.keys()) removePair(a, b);
|
|
261
261
|
return true;
|
|
262
262
|
},
|
|
263
263
|
|
|
@@ -306,7 +306,7 @@ export function createRelation<A, B, LA = A, LB = B>({
|
|
|
306
306
|
const assertRelationMappingConsistency: (
|
|
307
307
|
condition: unknown,
|
|
308
308
|
) => asserts condition = (condition) => {
|
|
309
|
-
assert(condition, "Relation mapping inconsistency");
|
|
309
|
+
assert(Boolean(condition), "Relation mapping inconsistency");
|
|
310
310
|
};
|
|
311
311
|
|
|
312
312
|
/**
|
package/src/Resource.ts
CHANGED
|
@@ -392,7 +392,7 @@ export const createSharedResource =
|
|
|
392
392
|
// only.
|
|
393
393
|
if (sharedResourceRun.getState().type !== "Running") return true;
|
|
394
394
|
|
|
395
|
-
if (idleDisposeAfter) {
|
|
395
|
+
if (idleDisposeAfter !== undefined) {
|
|
396
396
|
idleDisposeFiber = sharedResourceRun.abortable<void, never>(
|
|
397
397
|
async (run) => {
|
|
398
398
|
await run.ok(sleep(idleDisposeAfter));
|
|
@@ -414,7 +414,9 @@ export const createSharedResource =
|
|
|
414
414
|
resource: current as unknown as BorrowedResource<T>,
|
|
415
415
|
created,
|
|
416
416
|
release,
|
|
417
|
-
[Symbol.dispose]:
|
|
417
|
+
[Symbol.dispose]: () => {
|
|
418
|
+
release();
|
|
419
|
+
},
|
|
418
420
|
};
|
|
419
421
|
|
|
420
422
|
leakDetector.track(
|
|
@@ -832,6 +834,7 @@ export function createSharedResourceByKey<
|
|
|
832
834
|
using leaseDisposer = new DisposableStack();
|
|
833
835
|
const leasesByKey: Array<readonly [Lease<T>, K]> = [];
|
|
834
836
|
|
|
837
|
+
// oxlint-disable-next-line unicorn/no-useless-spread -- Snapshot keys because each acquisition awaits and can mutate the registry.
|
|
835
838
|
for (const [key] of [...sharedResourcesByKey]) {
|
|
836
839
|
const lease = await run.ok(sharedResourceByKey.acquireCurrent(key));
|
|
837
840
|
if (lease) leasesByKey.push([leaseDisposer.use(lease), key]);
|
|
@@ -1346,7 +1349,9 @@ export const createSharedResourceByKeyWithClaims =
|
|
|
1346
1349
|
const release = (): boolean => releaseClaimLease(heldClaimLease);
|
|
1347
1350
|
const claimLease: ClaimLease = {
|
|
1348
1351
|
release,
|
|
1349
|
-
[Symbol.dispose]:
|
|
1352
|
+
[Symbol.dispose]: () => {
|
|
1353
|
+
release();
|
|
1354
|
+
},
|
|
1350
1355
|
};
|
|
1351
1356
|
|
|
1352
1357
|
leakDetector.track(
|
|
@@ -1367,6 +1372,7 @@ export const createSharedResourceByKeyWithClaims =
|
|
|
1367
1372
|
resourceKey,
|
|
1368
1373
|
] of firstClaimTransitions) {
|
|
1369
1374
|
onFirstClaimAdded?.(claim, resource, resourceKey);
|
|
1375
|
+
// oxlint-disable-next-line eslint/no-loop-func -- Captures this iteration's block-scoped transition values for rollback.
|
|
1370
1376
|
compensations.defer(() => {
|
|
1371
1377
|
if (!succeeded) {
|
|
1372
1378
|
notifyLastClaimRemoved(claim, resource, resourceKey);
|
package/src/Result.ts
CHANGED
|
@@ -189,6 +189,7 @@
|
|
|
189
189
|
* @module
|
|
190
190
|
*/
|
|
191
191
|
import {
|
|
192
|
+
createMutableArray,
|
|
192
193
|
emptyArray,
|
|
193
194
|
isNonEmptyArray,
|
|
194
195
|
type NonEmptyReadonlyArray,
|
|
@@ -442,9 +443,8 @@ export const isErr = <T, E>(result: Result<T, E>): result is Err<E> =>
|
|
|
442
443
|
export const getOrThrow = <T, E>(result: Result<T, E>): T => {
|
|
443
444
|
if (result.ok) {
|
|
444
445
|
return result.value;
|
|
445
|
-
} else {
|
|
446
|
-
throw new Error("getOrThrow", { cause: result.error });
|
|
447
446
|
}
|
|
447
|
+
throw new Error("getOrThrow", { cause: result.error });
|
|
448
448
|
};
|
|
449
449
|
|
|
450
450
|
/**
|
|
@@ -572,7 +572,7 @@ export function trySync<T, E>(
|
|
|
572
572
|
export function trySync<T, E>(
|
|
573
573
|
fn: () => T,
|
|
574
574
|
mapError?: (error: unknown) => E,
|
|
575
|
-
): Result<T,
|
|
575
|
+
): Result<T, unknown> {
|
|
576
576
|
try {
|
|
577
577
|
return ok(fn());
|
|
578
578
|
} catch (error) {
|
|
@@ -644,7 +644,7 @@ export function tryAsync<T, E>(
|
|
|
644
644
|
export async function tryAsync<T, E>(
|
|
645
645
|
promiseThunk: Thunk<Awaitable<T>>,
|
|
646
646
|
mapError?: (error: unknown) => E,
|
|
647
|
-
): Promise<Result<T,
|
|
647
|
+
): Promise<Result<T, unknown>> {
|
|
648
648
|
try {
|
|
649
649
|
return ok(await promiseThunk());
|
|
650
650
|
} catch (error) {
|
|
@@ -1181,7 +1181,7 @@ export function allResult(
|
|
|
1181
1181
|
const length = input.length;
|
|
1182
1182
|
if (length === 0) return ok(emptyArray);
|
|
1183
1183
|
|
|
1184
|
-
const values =
|
|
1184
|
+
const values = createMutableArray<unknown>(length);
|
|
1185
1185
|
for (let i = 0; i < length; i++) {
|
|
1186
1186
|
const result = fn ? fn(input[i]) : (input[i] as AnyResult);
|
|
1187
1187
|
if (!result.ok) return result;
|
package/src/Sqlite.ts
CHANGED
|
@@ -256,12 +256,13 @@ export const createSqlite =
|
|
|
256
256
|
console.debug({ query });
|
|
257
257
|
|
|
258
258
|
const label =
|
|
259
|
-
query.options?.logQueryExecutionTime
|
|
260
|
-
|
|
259
|
+
query.options?.logQueryExecutionTime === true
|
|
260
|
+
? `SqliteQueryExecutionTime ${query.sql}`
|
|
261
|
+
: null;
|
|
261
262
|
|
|
262
|
-
if (label) console.time(label);
|
|
263
|
+
if (label !== null) console.time(label);
|
|
263
264
|
const result = driver.exec(query);
|
|
264
|
-
if (label) console.timeEnd(label);
|
|
265
|
+
if (label !== null) console.timeEnd(label);
|
|
265
266
|
|
|
266
267
|
if (query.options?.logExplainQueryPlan) {
|
|
267
268
|
const result = driver.exec({
|
|
@@ -343,11 +344,12 @@ const drawSqliteQueryPlan = (rows: Array<SqliteQueryPlanRow>): string =>
|
|
|
343
344
|
let indent = 0;
|
|
344
345
|
|
|
345
346
|
do {
|
|
347
|
+
// oxlint-disable-next-line eslint/no-loop-func -- find invokes the callback synchronously before parentId changes.
|
|
346
348
|
const parent = rows.find((r) => r.id === parentId);
|
|
347
349
|
if (!parent) break;
|
|
348
350
|
parentId = parent.parent;
|
|
349
351
|
indent++;
|
|
350
|
-
//
|
|
352
|
+
// oxlint-disable-next-line eslint/no-constant-condition -- A missing parent terminates the traversal above.
|
|
351
353
|
} while (true);
|
|
352
354
|
|
|
353
355
|
return `${" ".repeat(indent)}${row.detail}`;
|
|
@@ -395,7 +397,7 @@ export const createPreparedStatementsCache = <P>(
|
|
|
395
397
|
if (alwaysPrepare !== true && !query.options?.prepare)
|
|
396
398
|
return null as never;
|
|
397
399
|
let statement = statementsBySql.get(query.sql);
|
|
398
|
-
if (
|
|
400
|
+
if (statement === undefined) {
|
|
399
401
|
statement = factory(query.sql);
|
|
400
402
|
statementsBySql.set(query.sql, statement);
|
|
401
403
|
}
|
|
@@ -492,7 +494,7 @@ export const sql = (
|
|
|
492
494
|
sql.identifier = (identifier: string): SqlIdentifier => ({
|
|
493
495
|
type: "SqlIdentifier",
|
|
494
496
|
// From Kysely
|
|
495
|
-
sql: `"${identifier.
|
|
497
|
+
sql: `"${identifier.replaceAll('"', '""')}"` as SafeSql,
|
|
496
498
|
});
|
|
497
499
|
|
|
498
500
|
/**
|
package/src/Store.ts
CHANGED
|
@@ -70,7 +70,9 @@ export const createStore = <T>(initialState: T, eq?: Eq<T>): Store<T> => {
|
|
|
70
70
|
get: ref.get,
|
|
71
71
|
subscribe: (listener) => {
|
|
72
72
|
listeners.add(listener);
|
|
73
|
-
return () =>
|
|
73
|
+
return () => {
|
|
74
|
+
listeners.delete(listener);
|
|
75
|
+
};
|
|
74
76
|
},
|
|
75
77
|
|
|
76
78
|
set: (state) => {
|
package/src/String.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* pattern.
|
|
10
10
|
*/
|
|
11
11
|
export const escapeRegExp = (value: string): string =>
|
|
12
|
-
value.
|
|
12
|
+
value.replaceAll(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
13
13
|
|
|
14
14
|
/** Converts an unknown value to a compact, human-readable diagnostic string. */
|
|
15
15
|
export const safelyStringifyUnknownValue = (value: unknown): string => {
|