@evolu/common 6.0.1-preview.32 → 6.0.1-preview.34
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 +2 -2
- package/dist/src/Array.d.ts +193 -41
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +155 -48
- package/dist/src/Crypto.d.ts +6 -8
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +9 -9
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +4 -4
- package/dist/src/Evolu/Evolu.d.ts +9 -4
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +11 -11
- package/dist/src/Evolu/Protocol.d.ts +24 -1
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +59 -30
- package/dist/src/Evolu/Query.d.ts +1 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Query.js +1 -1
- package/dist/src/Evolu/Schema.d.ts +24 -20
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +36 -27
- package/dist/src/Evolu/Storage.d.ts +10 -2
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +18 -4
- package/dist/src/Evolu/Sync.d.ts +2 -1
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +45 -33
- package/dist/src/Evolu/Timestamp.d.ts +12 -17
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +5 -19
- package/dist/src/Object.d.ts +10 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +9 -3
- package/dist/src/Type.d.ts +57 -11
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +58 -28
- package/package.json +2 -2
- package/src/Array.ts +213 -55
- package/src/Crypto.ts +11 -9
- package/src/Evolu/Db.ts +7 -7
- package/src/Evolu/Evolu.ts +21 -20
- package/src/Evolu/Protocol.ts +70 -35
- package/src/Evolu/Query.ts +2 -2
- package/src/Evolu/Schema.ts +44 -32
- package/src/Evolu/Storage.ts +40 -2
- package/src/Evolu/Sync.ts +54 -32
- package/src/Evolu/Timestamp.ts +7 -28
- package/src/Object.ts +13 -5
- package/src/Type.ts +58 -32
package/src/Evolu/Sync.ts
CHANGED
|
@@ -22,7 +22,13 @@ import { err, ok, Result } from "../Result.js";
|
|
|
22
22
|
import { sql, SqliteDep, SqliteError, SqliteValue } from "../Sqlite.js";
|
|
23
23
|
import { AbortError, createMutex } from "../Task.js";
|
|
24
24
|
import { TimeDep } from "../Time.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
DateIso,
|
|
27
|
+
IdBytes,
|
|
28
|
+
idBytesToId,
|
|
29
|
+
idToIdBytes,
|
|
30
|
+
PositiveInt,
|
|
31
|
+
} from "../Type.js";
|
|
26
32
|
import { CreateWebSocketDep, WebSocket } from "../WebSocket.js";
|
|
27
33
|
import type { PostMessageDep } from "./Db.js";
|
|
28
34
|
import {
|
|
@@ -74,8 +80,8 @@ import {
|
|
|
74
80
|
TimestampCounterOverflowError,
|
|
75
81
|
TimestampDriftError,
|
|
76
82
|
TimestampTimeOutOfRangeError,
|
|
83
|
+
timestampToDateIso,
|
|
77
84
|
timestampToTimestampBytes,
|
|
78
|
-
timestampToTimestampString,
|
|
79
85
|
} from "./Timestamp.js";
|
|
80
86
|
|
|
81
87
|
export interface Sync extends Disposable {
|
|
@@ -341,7 +347,10 @@ export const createSync =
|
|
|
341
347
|
clockTimestamp = nextTimestamp.value;
|
|
342
348
|
|
|
343
349
|
const { ownerId = config.appOwner.id, ...dbChange } = change;
|
|
344
|
-
const message = {
|
|
350
|
+
const message: CrdtMessage = {
|
|
351
|
+
timestamp: clockTimestamp,
|
|
352
|
+
change: dbChange,
|
|
353
|
+
};
|
|
345
354
|
|
|
346
355
|
const messages = ownerMessages.get(ownerId);
|
|
347
356
|
if (messages) messages.push(message);
|
|
@@ -397,7 +406,6 @@ export interface ClockDep {
|
|
|
397
406
|
readonly clock: Clock;
|
|
398
407
|
}
|
|
399
408
|
|
|
400
|
-
// HLC
|
|
401
409
|
export interface Clock {
|
|
402
410
|
readonly get: () => Timestamp;
|
|
403
411
|
readonly save: (timestamp: Timestamp) => Result<void, SqliteError>;
|
|
@@ -413,9 +421,9 @@ export const createClock =
|
|
|
413
421
|
save: (timestamp) => {
|
|
414
422
|
currentTimestamp = timestamp;
|
|
415
423
|
|
|
416
|
-
const timestampString = timestampToTimestampString(timestamp);
|
|
417
424
|
const result = deps.sqlite.exec(sql.prepared`
|
|
418
|
-
update evolu_config
|
|
425
|
+
update evolu_config
|
|
426
|
+
set "clock" = ${timestampToTimestampBytes(timestamp)};
|
|
419
427
|
`);
|
|
420
428
|
if (!result.ok) return result;
|
|
421
429
|
|
|
@@ -587,20 +595,26 @@ const createClientStorage =
|
|
|
587
595
|
|
|
588
596
|
const { table, id } = rows[0];
|
|
589
597
|
const values: Record<string, SqliteValue> = {};
|
|
598
|
+
let isInsert = false;
|
|
590
599
|
|
|
591
600
|
for (const r of rows) {
|
|
592
601
|
assert(r.table === table, "All rows must have the same table");
|
|
593
602
|
assert(eqArrayNumber(r.id, id), "All rows must have the same Id");
|
|
594
|
-
|
|
603
|
+
if (r.column === "createdAt") {
|
|
604
|
+
isInsert = true;
|
|
605
|
+
} else {
|
|
606
|
+
values[r.column] = r.value;
|
|
607
|
+
}
|
|
595
608
|
}
|
|
596
609
|
|
|
597
610
|
const message: CrdtMessage = {
|
|
598
611
|
timestamp: timestampBytesToTimestamp(timestamp),
|
|
599
|
-
change: {
|
|
612
|
+
change: DbChange.orThrow({
|
|
600
613
|
table: rows[0].table,
|
|
601
614
|
id: idBytesToId(rows[0].id),
|
|
602
615
|
values,
|
|
603
|
-
|
|
616
|
+
isInsert,
|
|
617
|
+
}),
|
|
604
618
|
};
|
|
605
619
|
|
|
606
620
|
return encodeAndEncryptDbChange(deps)(message, owner.encryptionKey);
|
|
@@ -620,33 +634,27 @@ const createTransportKey = (transportConfig: OwnerTransport): TransportKey => {
|
|
|
620
634
|
export const applyLocalOnlyChange =
|
|
621
635
|
(deps: SqliteDep & TimeDep) =>
|
|
622
636
|
(change: MutationChange): Result<void, SqliteError> => {
|
|
623
|
-
const dbChange: DbChange = {
|
|
624
|
-
table: change.table,
|
|
625
|
-
id: change.id,
|
|
626
|
-
values: change.values,
|
|
627
|
-
};
|
|
628
|
-
|
|
629
637
|
const isDeletion =
|
|
630
|
-
"isDeleted" in
|
|
638
|
+
"isDeleted" in change.values && change.values.isDeleted === 1;
|
|
631
639
|
|
|
632
640
|
if (isDeletion) {
|
|
633
641
|
const result = deps.sqlite.exec(sql`
|
|
634
|
-
delete from ${sql.identifier(
|
|
635
|
-
where id = ${
|
|
642
|
+
delete from ${sql.identifier(change.table)}
|
|
643
|
+
where id = ${change.id};
|
|
636
644
|
`);
|
|
637
645
|
if (!result.ok) return result;
|
|
638
646
|
} else {
|
|
639
|
-
const
|
|
647
|
+
const now = deps.time.nowIso();
|
|
640
648
|
|
|
641
|
-
for (const [column, value] of objectToEntries(
|
|
649
|
+
for (const [column, value] of objectToEntries(change.values)) {
|
|
642
650
|
const result = deps.sqlite.exec(sql.prepared`
|
|
643
|
-
insert into ${sql.identifier(
|
|
651
|
+
insert into ${sql.identifier(change.table)}
|
|
644
652
|
("id", ${sql.identifier(column)}, createdAt, updatedAt)
|
|
645
|
-
values (${
|
|
653
|
+
values (${change.id}, ${value}, ${now}, ${now})
|
|
646
654
|
on conflict ("id") do update
|
|
647
655
|
set
|
|
648
656
|
${sql.identifier(column)} = ${value},
|
|
649
|
-
updatedAt = ${
|
|
657
|
+
updatedAt = ${now};
|
|
650
658
|
`);
|
|
651
659
|
if (!result.ok) return result;
|
|
652
660
|
}
|
|
@@ -672,7 +680,8 @@ const applyMessages =
|
|
|
672
680
|
let { firstTimestamp, lastTimestamp } = usageResult.value;
|
|
673
681
|
|
|
674
682
|
for (const message of messages) {
|
|
675
|
-
const
|
|
683
|
+
const date = timestampToDateIso(message.timestamp);
|
|
684
|
+
const result1 = applyMessageToAppTable(deps)(ownerIdBytes, message, date);
|
|
676
685
|
if (!result1.ok) return result1;
|
|
677
686
|
|
|
678
687
|
const timestamp = timestampToTimestampBytes(message.timestamp);
|
|
@@ -688,6 +697,7 @@ const applyMessages =
|
|
|
688
697
|
ownerIdBytes,
|
|
689
698
|
message,
|
|
690
699
|
strategy,
|
|
700
|
+
date,
|
|
691
701
|
);
|
|
692
702
|
if (!result2.ok) return result2;
|
|
693
703
|
}
|
|
@@ -710,11 +720,17 @@ const applyMessages =
|
|
|
710
720
|
|
|
711
721
|
const applyMessageToAppTable =
|
|
712
722
|
(deps: SqliteDep) =>
|
|
713
|
-
(
|
|
714
|
-
|
|
715
|
-
|
|
723
|
+
(
|
|
724
|
+
ownerId: OwnerIdBytes,
|
|
725
|
+
message: CrdtMessage,
|
|
726
|
+
date: DateIso,
|
|
727
|
+
): Result<void, SqliteError> => {
|
|
728
|
+
let entries = objectToEntries(message.change.values);
|
|
729
|
+
if (message.change.isInsert) {
|
|
730
|
+
entries = [...entries, ["createdAt", date]];
|
|
731
|
+
}
|
|
716
732
|
|
|
717
|
-
for (const [column, value] of
|
|
733
|
+
for (const [column, value] of entries) {
|
|
718
734
|
const result = deps.sqlite.exec(sql.prepared`
|
|
719
735
|
with
|
|
720
736
|
existingTimestamp as (
|
|
@@ -725,17 +741,17 @@ const applyMessageToAppTable =
|
|
|
725
741
|
and "table" = ${message.change.table}
|
|
726
742
|
and "id" = ${idToIdBytes(message.change.id)}
|
|
727
743
|
and "column" = ${column}
|
|
728
|
-
and "timestamp" >= ${timestamp}
|
|
744
|
+
and "timestamp" >= ${timestampToTimestampBytes(message.timestamp)}
|
|
729
745
|
limit 1
|
|
730
746
|
)
|
|
731
747
|
insert into ${sql.identifier(message.change.table)}
|
|
732
748
|
("id", ${sql.identifier(column)}, updatedAt)
|
|
733
|
-
select ${message.change.id}, ${value}, ${
|
|
749
|
+
select ${message.change.id}, ${value}, ${date}
|
|
734
750
|
where not exists (select 1 from existingTimestamp)
|
|
735
751
|
on conflict ("id") do update
|
|
736
752
|
set
|
|
737
753
|
${sql.identifier(column)} = ${value},
|
|
738
|
-
updatedAt = ${
|
|
754
|
+
updatedAt = ${date}
|
|
739
755
|
where not exists (select 1 from existingTimestamp);
|
|
740
756
|
`);
|
|
741
757
|
|
|
@@ -751,6 +767,7 @@ export const applyMessageToTimestampAndHistoryTables =
|
|
|
751
767
|
ownerId: OwnerIdBytes,
|
|
752
768
|
message: CrdtMessage,
|
|
753
769
|
strategy: StorageInsertTimestampStrategy,
|
|
770
|
+
date: DateIso,
|
|
754
771
|
): Result<void, SqliteError> => {
|
|
755
772
|
const timestamp = timestampToTimestampBytes(message.timestamp);
|
|
756
773
|
const id = idToIdBytes(message.change.id);
|
|
@@ -758,7 +775,12 @@ export const applyMessageToTimestampAndHistoryTables =
|
|
|
758
775
|
const result = deps.storage.insertTimestamp(ownerId, timestamp, strategy);
|
|
759
776
|
if (!result.ok) return result;
|
|
760
777
|
|
|
761
|
-
|
|
778
|
+
let entries = objectToEntries(message.change.values);
|
|
779
|
+
if (message.change.isInsert) {
|
|
780
|
+
entries = [...entries, ["createdAt", date]];
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
for (const [column, value] of entries) {
|
|
762
784
|
const result = deps.sqlite.exec(sql.prepared`
|
|
763
785
|
insert into evolu_history
|
|
764
786
|
("ownerId", "table", "id", "column", "value", "timestamp")
|
package/src/Evolu/Timestamp.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { assert } from "../Assert.js";
|
|
2
|
-
import { Brand } from "../Brand.js";
|
|
3
1
|
import { bytesToHex } from "../Buffer.js";
|
|
4
2
|
import { RandomBytesDep } from "../Crypto.js";
|
|
5
3
|
import { createEqObject, eqNumber, eqString } from "../Eq.js";
|
|
@@ -9,6 +7,7 @@ import { err, ok, Result } from "../Result.js";
|
|
|
9
7
|
import { TimeDep } from "../Time.js";
|
|
10
8
|
import {
|
|
11
9
|
brand,
|
|
10
|
+
DateIso,
|
|
12
11
|
InferType,
|
|
13
12
|
lessThanOrEqualTo,
|
|
14
13
|
NonNegativeInt,
|
|
@@ -163,10 +162,9 @@ export const maxNodeId = "ffffffffffffffff" as NodeId;
|
|
|
163
162
|
* queue:
|
|
164
163
|
*
|
|
165
164
|
* 1. Write changes immediately to a local-only table
|
|
166
|
-
* 2. Periodically
|
|
167
|
-
* 3. This decouples user activity from sync timing
|
|
165
|
+
* 2. Periodically and randomly flush messages to sync tables
|
|
168
166
|
*
|
|
169
|
-
*
|
|
167
|
+
* **Trade-off:** It breaks real-time collaboration.
|
|
170
168
|
*/
|
|
171
169
|
export const Timestamp = object({
|
|
172
170
|
millis: Millis,
|
|
@@ -193,29 +191,6 @@ export const createInitialTimestamp = (deps: RandomBytesDep): Timestamp => {
|
|
|
193
191
|
return createTimestamp({ nodeId });
|
|
194
192
|
};
|
|
195
193
|
|
|
196
|
-
/** Sortable string representation of {@link Timestamp}. */
|
|
197
|
-
export type TimestampString = string & Brand<"TimestampString">;
|
|
198
|
-
|
|
199
|
-
export const timestampToTimestampString = (t: Timestamp): TimestampString =>
|
|
200
|
-
[
|
|
201
|
-
new Date(t.millis).toISOString(),
|
|
202
|
-
t.counter.toString(16).toUpperCase().padStart(4, "0"),
|
|
203
|
-
t.nodeId,
|
|
204
|
-
].join("-") as TimestampString;
|
|
205
|
-
|
|
206
|
-
export const timestampStringToTimestamp = (
|
|
207
|
-
timestampString: TimestampString,
|
|
208
|
-
): Timestamp => {
|
|
209
|
-
const array = timestampString.split("-");
|
|
210
|
-
const timestamp = {
|
|
211
|
-
millis: Date.parse(array.slice(0, 3).join("-")).valueOf(),
|
|
212
|
-
counter: parseInt(array[3], 16),
|
|
213
|
-
nodeId: array[4],
|
|
214
|
-
};
|
|
215
|
-
assert(Timestamp.is(timestamp), "timestampString is malformed");
|
|
216
|
-
return timestamp;
|
|
217
|
-
};
|
|
218
|
-
|
|
219
194
|
const getNextMillis =
|
|
220
195
|
(deps: TimeDep & TimestampConfigDep) =>
|
|
221
196
|
(
|
|
@@ -362,3 +337,7 @@ export const timestampBytesToTimestamp = (
|
|
|
362
337
|
};
|
|
363
338
|
|
|
364
339
|
export const orderTimestampBytes: Order<TimestampBytes> = orderUint8Array;
|
|
340
|
+
|
|
341
|
+
export const timestampToDateIso = (timestamp: Timestamp): DateIso =>
|
|
342
|
+
// `as DateIso` is safe because the timestamp is always valid
|
|
343
|
+
new Date(timestamp.millis).toISOString() as DateIso;
|
package/src/Object.ts
CHANGED
|
@@ -25,14 +25,22 @@ export type ReadonlyRecord<K extends keyof any, V> = Readonly<Record<K, V>>;
|
|
|
25
25
|
type StringKeyOf<T> = Extract<keyof T, string>;
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* Like `Object.entries` but preserves branded keys.
|
|
29
|
+
*
|
|
30
|
+
* ### Example
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* type UserId = string & { readonly __brand: "UserId" };
|
|
34
|
+
* const users: Record<UserId, string> = {};
|
|
35
|
+
* const entries = objectToEntries(users); // [UserId, string][]
|
|
36
|
+
* ```
|
|
31
37
|
*/
|
|
32
38
|
export const objectToEntries = <T extends Record<string, any>>(
|
|
33
39
|
record: T,
|
|
34
|
-
):
|
|
35
|
-
Object.entries(record) as Array<
|
|
40
|
+
): ReadonlyArray<[StringKeyOf<T>, T[StringKeyOf<T>]]> =>
|
|
41
|
+
Object.entries(record) as Array<
|
|
42
|
+
[StringKeyOf<T>, T[StringKeyOf<T>]]
|
|
43
|
+
> as ReadonlyArray<[StringKeyOf<T>, T[StringKeyOf<T>]]>;
|
|
36
44
|
|
|
37
45
|
/**
|
|
38
46
|
* Maps a `ReadonlyRecord<K, V>` to a new `ReadonlyRecord<K, U>`, preserving
|
package/src/Type.ts
CHANGED
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Evolu {@link Type} is like a type guard that returns typed errors (via
|
|
5
5
|
* {@link Result}) instead of throwing. We either get a safely typed value or a
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Evolu Type supports [Standard Schema](https://standardschema.dev/) for
|
|
9
|
-
* interoperability with 40+ validation-compatible tools and frameworks.
|
|
6
|
+
* composable typed error telling us exactly why validation failed.
|
|
10
7
|
*
|
|
11
8
|
* Why another validation library?
|
|
12
9
|
*
|
|
@@ -15,12 +12,19 @@
|
|
|
15
12
|
* messages.
|
|
16
13
|
* - **Consistent constraints via {@link Brand}** – every constraint becomes part
|
|
17
14
|
* of the type.
|
|
18
|
-
* - **
|
|
19
|
-
*
|
|
20
|
-
* - **
|
|
21
|
-
*
|
|
22
|
-
* - **
|
|
23
|
-
*
|
|
15
|
+
* - **Skippable validation** – parent validations can be skipped when already
|
|
16
|
+
* proved by types.
|
|
17
|
+
* - **Simple, top-down implementation** – readable source code from top to
|
|
18
|
+
* bottom.
|
|
19
|
+
* - **No user-land chaining DSL** – prepared for TC39 Hack pipes.
|
|
20
|
+
*
|
|
21
|
+
* A distinctive feature of Evolu Type compared to other validation libraries is
|
|
22
|
+
* that it returns typed errors rather than string messages. This allows
|
|
23
|
+
* TypeScript to enforce that all validation errors are handled by type
|
|
24
|
+
* checking, significantly improving the developer experience.
|
|
25
|
+
*
|
|
26
|
+
* Evolu Type supports [Standard Schema](https://standardschema.dev/) for
|
|
27
|
+
* interoperability with 40+ validation-compatible tools and frameworks.
|
|
24
28
|
*
|
|
25
29
|
* ### Base Types Quick Start
|
|
26
30
|
*
|
|
@@ -171,6 +175,29 @@
|
|
|
171
175
|
* reverse transforms would not buy much. We may revisit this if we can design a
|
|
172
176
|
* minimal, 100% safe API that preserves simplicity.
|
|
173
177
|
*
|
|
178
|
+
* ### Prepared for TC39 Hack Pipes
|
|
179
|
+
*
|
|
180
|
+
* Take a look how `SimplePassword` is defined:
|
|
181
|
+
*
|
|
182
|
+
* ```ts
|
|
183
|
+
* export const SimplePassword = brand(
|
|
184
|
+
* "SimplePassword",
|
|
185
|
+
* minLength(8)(maxLength(64)(TrimmedString)),
|
|
186
|
+
* );
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
189
|
+
* Nested functions are often OK (if not, make a helper) and read well, but with
|
|
190
|
+
* TC39 Hack pipes it would be clearer:
|
|
191
|
+
*
|
|
192
|
+
* ```ts
|
|
193
|
+
* // TrimmedString
|
|
194
|
+
* // |> minLength(8)(%)
|
|
195
|
+
* // |> maxLength(64)(%)
|
|
196
|
+
* // |> brand("SimplePassword", %)
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* Note `minLength` and `maxLength` are curried because they are factories.
|
|
200
|
+
*
|
|
174
201
|
* @module
|
|
175
202
|
*/
|
|
176
203
|
|
|
@@ -1497,7 +1524,7 @@ export const base64UrlToUint8Array: (str: Base64Url) => Uint8Array =
|
|
|
1497
1524
|
* Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
|
|
1498
1525
|
* `_`). See `UrlSafeString` for details.
|
|
1499
1526
|
*
|
|
1500
|
-
* The string must be between 1 and
|
|
1527
|
+
* The string must be between 1 and 64 characters.
|
|
1501
1528
|
*
|
|
1502
1529
|
* ### Example
|
|
1503
1530
|
*
|
|
@@ -1513,7 +1540,7 @@ export const base64UrlToUint8Array: (str: Base64Url) => Uint8Array =
|
|
|
1513
1540
|
* @category String
|
|
1514
1541
|
*/
|
|
1515
1542
|
export const SimpleName = brand("SimpleName", UrlSafeString, (value) =>
|
|
1516
|
-
value.length >= 1 && value.length <=
|
|
1543
|
+
value.length >= 1 && value.length <= 64
|
|
1517
1544
|
? ok(value)
|
|
1518
1545
|
: err<SimpleNameError>({ type: "SimpleName", value }),
|
|
1519
1546
|
);
|
|
@@ -1523,6 +1550,25 @@ export interface SimpleNameError extends TypeError<"SimpleName"> {}
|
|
|
1523
1550
|
/**
|
|
1524
1551
|
* Trimmed string between 8 and 64 characters, branded as `SimplePassword`.
|
|
1525
1552
|
*
|
|
1553
|
+
* Take a look how `SimplePassword` is defined:
|
|
1554
|
+
*
|
|
1555
|
+
* ```ts
|
|
1556
|
+
* export const SimplePassword = brand(
|
|
1557
|
+
* "SimplePassword",
|
|
1558
|
+
* minLength(8)(maxLength(64)(TrimmedString)),
|
|
1559
|
+
* );
|
|
1560
|
+
* ```
|
|
1561
|
+
*
|
|
1562
|
+
* Nested functions are often OK (if not, make a helper), but with TC39 Hack
|
|
1563
|
+
* pipes it would be clearer:
|
|
1564
|
+
*
|
|
1565
|
+
* ```ts
|
|
1566
|
+
* // TrimmedString
|
|
1567
|
+
* // |> minLength(8)(%)
|
|
1568
|
+
* // |> maxLength(64)(%)
|
|
1569
|
+
* // |> brand("SimplePassword", %)
|
|
1570
|
+
* ```
|
|
1571
|
+
*
|
|
1526
1572
|
* @category String
|
|
1527
1573
|
*/
|
|
1528
1574
|
export const SimplePassword = brand(
|
|
@@ -3428,26 +3474,6 @@ export const formatInt64Error = createTypeErrorFormatter<Int64Error>(
|
|
|
3428
3474
|
`The value ${error.value} is not a valid 64-bit signed integer (Int64).`,
|
|
3429
3475
|
);
|
|
3430
3476
|
|
|
3431
|
-
// // co s timhle? je to string, ze ktereho lze udelat bigint
|
|
3432
|
-
|
|
3433
|
-
// export const BigIntFromString = transform(
|
|
3434
|
-
// String,
|
|
3435
|
-
// BigInt,
|
|
3436
|
-
// (value) =>
|
|
3437
|
-
// trySync(
|
|
3438
|
-
// () => globalThis.BigInt(value),
|
|
3439
|
-
// (): BigIntFromStringError => ({ type: "BigIntFromString", value }),
|
|
3440
|
-
// ),
|
|
3441
|
-
// (value) => value.toString(),
|
|
3442
|
-
// );
|
|
3443
|
-
|
|
3444
|
-
// export interface BigIntFromStringError extends TypeError<"BigIntFromString"> {}
|
|
3445
|
-
|
|
3446
|
-
// export const formatBigIntFromStringError =
|
|
3447
|
-
// createTypeErrorFormatter<BigIntFromStringError>(
|
|
3448
|
-
// (error) => `The value ${error.value} could not be converted to a BigInt.`,
|
|
3449
|
-
// );
|
|
3450
|
-
|
|
3451
3477
|
/**
|
|
3452
3478
|
* Stringified {@link Int64}.
|
|
3453
3479
|
*
|