@evolu/common 6.0.1-preview.33 → 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 +3 -3
- package/dist/src/Array.js +3 -3
- 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 +10 -14
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +3 -16
- 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 +56 -14
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +57 -31
- package/package.json +2 -2
- package/src/Array.ts +3 -3
- 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 +5 -25
- package/src/Object.ts +13 -5
- package/src/Type.ts +57 -35
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,
|
|
@@ -192,29 +191,6 @@ export const createInitialTimestamp = (deps: RandomBytesDep): Timestamp => {
|
|
|
192
191
|
return createTimestamp({ nodeId });
|
|
193
192
|
};
|
|
194
193
|
|
|
195
|
-
/** Sortable string representation of {@link Timestamp}. */
|
|
196
|
-
export type TimestampString = string & Brand<"TimestampString">;
|
|
197
|
-
|
|
198
|
-
export const timestampToTimestampString = (t: Timestamp): TimestampString =>
|
|
199
|
-
[
|
|
200
|
-
new Date(t.millis).toISOString(),
|
|
201
|
-
t.counter.toString(16).toUpperCase().padStart(4, "0"),
|
|
202
|
-
t.nodeId,
|
|
203
|
-
].join("-") as TimestampString;
|
|
204
|
-
|
|
205
|
-
export const timestampStringToTimestamp = (
|
|
206
|
-
timestampString: TimestampString,
|
|
207
|
-
): Timestamp => {
|
|
208
|
-
const array = timestampString.split("-");
|
|
209
|
-
const timestamp = {
|
|
210
|
-
millis: Date.parse(array.slice(0, 3).join("-")).valueOf(),
|
|
211
|
-
counter: parseInt(array[3], 16),
|
|
212
|
-
nodeId: array[4],
|
|
213
|
-
};
|
|
214
|
-
assert(Timestamp.is(timestamp), "timestampString is malformed");
|
|
215
|
-
return timestamp;
|
|
216
|
-
};
|
|
217
|
-
|
|
218
194
|
const getNextMillis =
|
|
219
195
|
(deps: TimeDep & TimestampConfigDep) =>
|
|
220
196
|
(
|
|
@@ -361,3 +337,7 @@ export const timestampBytesToTimestamp = (
|
|
|
361
337
|
};
|
|
362
338
|
|
|
363
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,16 +12,19 @@
|
|
|
15
12
|
* messages.
|
|
16
13
|
* - **Consistent constraints via {@link Brand}** – every constraint becomes part
|
|
17
14
|
* of the type.
|
|
18
|
-
* - **
|
|
19
|
-
* by
|
|
20
|
-
* - **Simple, top-down implementation** – readable source code from top to
|
|
21
|
-
*
|
|
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.
|
|
22
25
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* currying. For optimal Hack pipe support, these should be refactored to
|
|
26
|
-
* `minLength(Type, min)` to enable: `Type |> minLength(%, 1) |> maxLength(%,
|
|
27
|
-
* 100)`
|
|
26
|
+
* Evolu Type supports [Standard Schema](https://standardschema.dev/) for
|
|
27
|
+
* interoperability with 40+ validation-compatible tools and frameworks.
|
|
28
28
|
*
|
|
29
29
|
* ### Base Types Quick Start
|
|
30
30
|
*
|
|
@@ -175,6 +175,29 @@
|
|
|
175
175
|
* reverse transforms would not buy much. We may revisit this if we can design a
|
|
176
176
|
* minimal, 100% safe API that preserves simplicity.
|
|
177
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
|
+
*
|
|
178
201
|
* @module
|
|
179
202
|
*/
|
|
180
203
|
|
|
@@ -1501,7 +1524,7 @@ export const base64UrlToUint8Array: (str: Base64Url) => Uint8Array =
|
|
|
1501
1524
|
* Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
|
|
1502
1525
|
* `_`). See `UrlSafeString` for details.
|
|
1503
1526
|
*
|
|
1504
|
-
* The string must be between 1 and
|
|
1527
|
+
* The string must be between 1 and 64 characters.
|
|
1505
1528
|
*
|
|
1506
1529
|
* ### Example
|
|
1507
1530
|
*
|
|
@@ -1517,7 +1540,7 @@ export const base64UrlToUint8Array: (str: Base64Url) => Uint8Array =
|
|
|
1517
1540
|
* @category String
|
|
1518
1541
|
*/
|
|
1519
1542
|
export const SimpleName = brand("SimpleName", UrlSafeString, (value) =>
|
|
1520
|
-
value.length >= 1 && value.length <=
|
|
1543
|
+
value.length >= 1 && value.length <= 64
|
|
1521
1544
|
? ok(value)
|
|
1522
1545
|
: err<SimpleNameError>({ type: "SimpleName", value }),
|
|
1523
1546
|
);
|
|
@@ -1527,6 +1550,25 @@ export interface SimpleNameError extends TypeError<"SimpleName"> {}
|
|
|
1527
1550
|
/**
|
|
1528
1551
|
* Trimmed string between 8 and 64 characters, branded as `SimplePassword`.
|
|
1529
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
|
+
*
|
|
1530
1572
|
* @category String
|
|
1531
1573
|
*/
|
|
1532
1574
|
export const SimplePassword = brand(
|
|
@@ -3432,26 +3474,6 @@ export const formatInt64Error = createTypeErrorFormatter<Int64Error>(
|
|
|
3432
3474
|
`The value ${error.value} is not a valid 64-bit signed integer (Int64).`,
|
|
3433
3475
|
);
|
|
3434
3476
|
|
|
3435
|
-
// // co s timhle? je to string, ze ktereho lze udelat bigint
|
|
3436
|
-
|
|
3437
|
-
// export const BigIntFromString = transform(
|
|
3438
|
-
// String,
|
|
3439
|
-
// BigInt,
|
|
3440
|
-
// (value) =>
|
|
3441
|
-
// trySync(
|
|
3442
|
-
// () => globalThis.BigInt(value),
|
|
3443
|
-
// (): BigIntFromStringError => ({ type: "BigIntFromString", value }),
|
|
3444
|
-
// ),
|
|
3445
|
-
// (value) => value.toString(),
|
|
3446
|
-
// );
|
|
3447
|
-
|
|
3448
|
-
// export interface BigIntFromStringError extends TypeError<"BigIntFromString"> {}
|
|
3449
|
-
|
|
3450
|
-
// export const formatBigIntFromStringError =
|
|
3451
|
-
// createTypeErrorFormatter<BigIntFromStringError>(
|
|
3452
|
-
// (error) => `The value ${error.value} could not be converted to a BigInt.`,
|
|
3453
|
-
// );
|
|
3454
|
-
|
|
3455
3477
|
/**
|
|
3456
3478
|
* Stringified {@link Int64}.
|
|
3457
3479
|
*
|