@evolu/common 8.0.0 → 8.2.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/Number.d.ts +66 -6
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +11 -3
- package/dist/src/Schedule.d.ts +2 -2
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Task.d.ts +2 -1
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +3 -3
- package/dist/src/Time.d.ts +70 -34
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +77 -1
- package/dist/src/Type.d.ts +488 -72
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +634 -67
- package/dist/src/Types.d.ts +0 -24
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/intl/cs.d.ts +11 -1
- package/dist/src/intl/cs.d.ts.map +1 -1
- package/dist/src/intl/cs.js +11 -1
- package/package.json +1 -1
- package/src/Number.ts +85 -14
- package/src/Schedule.ts +1 -1
- package/src/Task.ts +3 -4
- package/src/Time.ts +108 -62
- package/src/Type.ts +1267 -130
- package/src/Types.ts +0 -49
- package/src/intl/cs.ts +36 -1
package/src/Type.ts
CHANGED
|
@@ -36,10 +36,7 @@ import {
|
|
|
36
36
|
instance,
|
|
37
37
|
isInstance,
|
|
38
38
|
type CompileTimeError,
|
|
39
|
-
type Digit,
|
|
40
|
-
type Digit1To9,
|
|
41
39
|
type Instance,
|
|
42
|
-
type Int1To100,
|
|
43
40
|
type IsUnion,
|
|
44
41
|
type Literal,
|
|
45
42
|
type Simplify,
|
|
@@ -144,8 +141,8 @@ import {
|
|
|
144
141
|
* normalized value, it uses a `null` prototype so every string key remains
|
|
145
142
|
* ordinary data.
|
|
146
143
|
*
|
|
147
|
-
* Evolu Type
|
|
148
|
-
* to be enabled.
|
|
144
|
+
* Evolu Type requires TypeScript 7 or newer and expects the
|
|
145
|
+
* `exactOptionalPropertyTypes` compiler option to be enabled.
|
|
149
146
|
*
|
|
150
147
|
* Predefined Types intentionally use the names of corresponding JavaScript
|
|
151
148
|
* built-ins because they represent those familiar value categories. If an
|
|
@@ -863,7 +860,7 @@ const assertTypeOutput = <Error extends TypeError>(
|
|
|
863
860
|
};
|
|
864
861
|
|
|
865
862
|
/**
|
|
866
|
-
*
|
|
863
|
+
* Localized copies of selected {@link Type} declarations for every locale.
|
|
867
864
|
*
|
|
868
865
|
* Each locale supplies one formatter for every Type that can own a formatted
|
|
869
866
|
* error. Structural Types use their own formatter for structural failures and
|
|
@@ -2128,10 +2125,13 @@ type RuntimeEncoder = RuntimeOperation<unknown>;
|
|
|
2128
2125
|
|
|
2129
2126
|
declare const encoderSymbolType: unique symbol;
|
|
2130
2127
|
declare const fromSymbolType: unique symbol;
|
|
2128
|
+
declare const templateLiteralSyntaxSymbolType: unique symbol;
|
|
2131
2129
|
const encoderSymbol: typeof encoderSymbolType =
|
|
2132
2130
|
/*#__PURE__*/ globalThis.Symbol() as typeof encoderSymbolType;
|
|
2133
2131
|
const fromSymbol: typeof fromSymbolType =
|
|
2134
2132
|
/*#__PURE__*/ globalThis.Symbol() as typeof fromSymbolType;
|
|
2133
|
+
const templateLiteralSyntaxSymbol: typeof templateLiteralSyntaxSymbolType =
|
|
2134
|
+
/*#__PURE__*/ globalThis.Symbol() as typeof templateLiteralSyntaxSymbolType;
|
|
2135
2135
|
|
|
2136
2136
|
/**
|
|
2137
2137
|
* Type-erased {@link Type} used to traverse and invoke heterogeneous Type nodes
|
|
@@ -2154,6 +2154,7 @@ type RuntimeTypeNode = Omit<TypeNode, typeof customFromSymbol> & {
|
|
|
2154
2154
|
// assertion, avoiding repeated validation and preserving identity fast paths.
|
|
2155
2155
|
readonly [encoderSymbolType]: RuntimeEncoder;
|
|
2156
2156
|
readonly [getRuntimeTypeIssuesSymbolType]: RuntimeGetTypeIssues;
|
|
2157
|
+
readonly [templateLiteralSyntaxSymbolType]?: true;
|
|
2157
2158
|
};
|
|
2158
2159
|
|
|
2159
2160
|
const mapRuntimeResult =
|
|
@@ -2612,7 +2613,7 @@ interface ObjectTagOutputByName {
|
|
|
2612
2613
|
}
|
|
2613
2614
|
|
|
2614
2615
|
/**
|
|
2615
|
-
*
|
|
2616
|
+
* Realm-neutral {@link Type} trusting an object's reported tag.
|
|
2616
2617
|
*
|
|
2617
2618
|
* Predefined built-in tags expose their native Output type under the assumption
|
|
2618
2619
|
* that trusted code does not forge their tags. They do not verify native
|
|
@@ -2720,7 +2721,7 @@ export const Uint8Array = /*#__PURE__*/ objectTag("Uint8Array");
|
|
|
2720
2721
|
export const ArrayBuffer = /*#__PURE__*/ objectTag("ArrayBuffer");
|
|
2721
2722
|
|
|
2722
2723
|
/**
|
|
2723
|
-
*
|
|
2724
|
+
* Instance {@link Type} for one constructor.
|
|
2724
2725
|
*
|
|
2725
2726
|
* Membership uses the intrinsic prototype chain, so subclasses are accepted,
|
|
2726
2727
|
* equivalent constructors from other realms are rejected, and custom
|
|
@@ -2831,6 +2832,26 @@ type InstanceConstructorCompileTimeError = CompileTimeError<
|
|
|
2831
2832
|
* primitive through `from.parent`. The expected value must have one exact
|
|
2832
2833
|
* literal type. Validation uses `===`, so `-0` matches `0`.
|
|
2833
2834
|
*
|
|
2835
|
+
* In {@link templateLiteralParser}, use a string Literal Type when the literal
|
|
2836
|
+
* should be decoded into the Output Tuple. Use a raw string when it should only
|
|
2837
|
+
* frame the canonical string.
|
|
2838
|
+
*
|
|
2839
|
+
* ### Example
|
|
2840
|
+
*
|
|
2841
|
+
* ```ts
|
|
2842
|
+
* import { literal } from "@evolu/common";
|
|
2843
|
+
*
|
|
2844
|
+
* const Ready = literal("ready");
|
|
2845
|
+
*
|
|
2846
|
+
* expectTypeOf<typeof Ready.Output>().toEqualTypeOf<"ready">();
|
|
2847
|
+
* expectOk(Ready.fromUnknown("ready"), "ready");
|
|
2848
|
+
* expectErr(Ready.fromUnknown("pending"), {
|
|
2849
|
+
* type: "Literal",
|
|
2850
|
+
* expected: "ready",
|
|
2851
|
+
* value: "pending",
|
|
2852
|
+
* });
|
|
2853
|
+
* ```
|
|
2854
|
+
*
|
|
2834
2855
|
* @group Unions
|
|
2835
2856
|
*/
|
|
2836
2857
|
export const literal = <const Expected extends Literal>(
|
|
@@ -2867,7 +2888,10 @@ export const literal = <const Expected extends Literal>(
|
|
|
2867
2888
|
formatError,
|
|
2868
2889
|
)
|
|
2869
2890
|
: createRootType("Literal", validate, formatError),
|
|
2870
|
-
{
|
|
2891
|
+
{
|
|
2892
|
+
expected: literalExpected,
|
|
2893
|
+
[templateLiteralSyntaxSymbol]: true,
|
|
2894
|
+
},
|
|
2871
2895
|
) as unknown as LiteralType<Expected>;
|
|
2872
2896
|
};
|
|
2873
2897
|
|
|
@@ -2886,6 +2910,7 @@ export interface LiteralType<Expected extends Literal> extends Type<
|
|
|
2886
2910
|
>,
|
|
2887
2911
|
IdentityEncodingForParent<LiteralParent<Expected>>
|
|
2888
2912
|
> {
|
|
2913
|
+
readonly [templateLiteralSyntaxSymbol]: true;
|
|
2889
2914
|
readonly expected: Expected;
|
|
2890
2915
|
}
|
|
2891
2916
|
|
|
@@ -3072,7 +3097,7 @@ export function union(
|
|
|
3072
3097
|
from,
|
|
3073
3098
|
to,
|
|
3074
3099
|
getTypeIssues,
|
|
3075
|
-
{ members },
|
|
3100
|
+
{ members, [templateLiteralSyntaxSymbol]: true },
|
|
3076
3101
|
);
|
|
3077
3102
|
}
|
|
3078
3103
|
|
|
@@ -3116,7 +3141,7 @@ const createUnionValidation =
|
|
|
3116
3141
|
};
|
|
3117
3142
|
|
|
3118
3143
|
/**
|
|
3119
|
-
*
|
|
3144
|
+
* Union {@link Type} containing the supplied Type and `undefined`.
|
|
3120
3145
|
*
|
|
3121
3146
|
* This does not make an object property optional. It changes only the values
|
|
3122
3147
|
* accepted when the property is present.
|
|
@@ -3128,7 +3153,7 @@ export const undefinedOr = <ValueType extends TypeNode>(
|
|
|
3128
3153
|
): UnionType<readonly [ValueType, typeof Undefined]> => union(type, Undefined);
|
|
3129
3154
|
|
|
3130
3155
|
/**
|
|
3131
|
-
*
|
|
3156
|
+
* Union {@link Type} containing the supplied Type and `null`.
|
|
3132
3157
|
*
|
|
3133
3158
|
* @group Unions
|
|
3134
3159
|
*/
|
|
@@ -3137,7 +3162,7 @@ export const nullOr = <ValueType extends TypeNode>(
|
|
|
3137
3162
|
): UnionType<readonly [ValueType, typeof Null]> => union(type, Null);
|
|
3138
3163
|
|
|
3139
3164
|
/**
|
|
3140
|
-
*
|
|
3165
|
+
* Union {@link Type} containing the supplied Type, `null`, and `undefined`.
|
|
3141
3166
|
*
|
|
3142
3167
|
* @group Unions
|
|
3143
3168
|
*/
|
|
@@ -3194,6 +3219,7 @@ export interface UnionType<
|
|
|
3194
3219
|
CanonicalInputOf<Members[number]>,
|
|
3195
3220
|
AllTypesUseIdentityEncoding<Members[number]>
|
|
3196
3221
|
> {
|
|
3222
|
+
readonly [templateLiteralSyntaxSymbol]: true;
|
|
3197
3223
|
readonly [reflectedTypesSymbol]?: Members[number];
|
|
3198
3224
|
readonly members: Members;
|
|
3199
3225
|
}
|
|
@@ -3326,6 +3352,849 @@ interface UnionErrorValue<
|
|
|
3326
3352
|
readonly errors: NonEmptyReadonlyArray<MemberError>;
|
|
3327
3353
|
}
|
|
3328
3354
|
|
|
3355
|
+
/**
|
|
3356
|
+
* Template literal {@link Type} for validation and parsing.
|
|
3357
|
+
*
|
|
3358
|
+
* Parses and creates structured strings.
|
|
3359
|
+
*
|
|
3360
|
+
* Accepts the same template parts as {@link templateLiteral}: fixed string
|
|
3361
|
+
* literals and Types canonically encoded as strings. Instead of keeping Output
|
|
3362
|
+
* as a string, fixed literals define the framing and Output is a readonly Tuple
|
|
3363
|
+
* of the decoded Type parts. `to` encodes that Tuple back into the canonical
|
|
3364
|
+
* string represented by the parent Type. At least one Type part is required.
|
|
3365
|
+
*
|
|
3366
|
+
* When every capture uses identity encoding, the parent Output is the exact
|
|
3367
|
+
* TypeScript template literal type. A transforming capture makes it nominal;
|
|
3368
|
+
* create such strings with `to` or validate them with the parent Type.
|
|
3369
|
+
*
|
|
3370
|
+
* Deterministic framing is a core correctness guarantee. It preserves
|
|
3371
|
+
* reversibility and keeps capture boundaries unambiguous. Different capture
|
|
3372
|
+
* Tuples must never encode to the same string. The parser provides predictable
|
|
3373
|
+
* parsing without pathological backtracking and decodes each capture once, so
|
|
3374
|
+
* adversarial input cannot trigger exponential parser work. Fixed-width captures
|
|
3375
|
+
* may be adjacent, but only one variable-width capture is allowed. Declarations
|
|
3376
|
+
* that could join UTF-16 surrogate halves across parts are rejected during
|
|
3377
|
+
* construction.
|
|
3378
|
+
*
|
|
3379
|
+
* Keep capture unions reasonably small to avoid excessive compiler work.
|
|
3380
|
+
*
|
|
3381
|
+
* TypeScript template literal types can describe a fixed number of digit
|
|
3382
|
+
* positions, but not an arbitrarily long sequence of digits. Such grammars use
|
|
3383
|
+
* branded Types such as {@link DecimalString}; `templateLiteralParser` preserves
|
|
3384
|
+
* that exactness by requiring a validated branded capture when encoding.
|
|
3385
|
+
*
|
|
3386
|
+
* ### Example
|
|
3387
|
+
*
|
|
3388
|
+
* A template literal Type defines both a canonical string representation and
|
|
3389
|
+
* the structured data decoded from it:
|
|
3390
|
+
*
|
|
3391
|
+
* ```ts
|
|
3392
|
+
* import { templateLiteralParser, union } from "@evolu/common";
|
|
3393
|
+
*
|
|
3394
|
+
* const Language = union("en", "cs");
|
|
3395
|
+
* const Region = union("US", "CZ");
|
|
3396
|
+
*
|
|
3397
|
+
* // Define a Type for "en-US" | "en-CZ" | "cs-US" | "cs-CZ".
|
|
3398
|
+
* const SupportedLocale = templateLiteralParser(Language, "-", Region);
|
|
3399
|
+
*
|
|
3400
|
+
* // Output is the decoded language and region.
|
|
3401
|
+
* type SupportedLocale = typeof SupportedLocale.Output;
|
|
3402
|
+
* expectTypeOf<SupportedLocale>().toEqualTypeOf<
|
|
3403
|
+
* readonly ["en" | "cs", "US" | "CZ"]
|
|
3404
|
+
* >();
|
|
3405
|
+
*
|
|
3406
|
+
* // The parent Output is the canonical locale string.
|
|
3407
|
+
* type SupportedLocaleLiteral = typeof SupportedLocale.parent.Output;
|
|
3408
|
+
* expectTypeOf<SupportedLocaleLiteral>().toEqualTypeOf<
|
|
3409
|
+
* "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
|
|
3410
|
+
* >();
|
|
3411
|
+
*
|
|
3412
|
+
* // Parse an unknown string into structured data.
|
|
3413
|
+
* const result = SupportedLocale.fromUnknown("cs-CZ");
|
|
3414
|
+
* assert(result.ok);
|
|
3415
|
+
* const locale = result.value;
|
|
3416
|
+
* expectTypeOf(locale).toEqualTypeOf<SupportedLocale>();
|
|
3417
|
+
* expect(locale).toEqual(["cs", "CZ"]);
|
|
3418
|
+
* expectErr(SupportedLocale.fromUnknown("cs/CZ"), {
|
|
3419
|
+
* type: "TemplateLiteral",
|
|
3420
|
+
* value: "cs/CZ",
|
|
3421
|
+
* });
|
|
3422
|
+
*
|
|
3423
|
+
* // Encode structured data into its canonical string.
|
|
3424
|
+
* const localeLiteral = SupportedLocale.to(locale);
|
|
3425
|
+
* expectTypeOf(localeLiteral).toEqualTypeOf<SupportedLocaleLiteral>();
|
|
3426
|
+
* expect(localeLiteral).toBe("cs-CZ");
|
|
3427
|
+
*
|
|
3428
|
+
* // Validate a string configuration value.
|
|
3429
|
+
* const configValue: unknown = "cs-CZ";
|
|
3430
|
+
* assert(SupportedLocale.parent.is(configValue));
|
|
3431
|
+
* expectTypeOf(configValue).toEqualTypeOf<SupportedLocaleLiteral>();
|
|
3432
|
+
* expect(SupportedLocale.parent.is("fr-CZ")).toBe(false);
|
|
3433
|
+
* ```
|
|
3434
|
+
*
|
|
3435
|
+
* `SupportedLocale` is structured data for application code.
|
|
3436
|
+
* `SupportedLocaleLiteral` is its canonical representation for configuration
|
|
3437
|
+
* and other APIs that require a string, such as URL parameters, environment
|
|
3438
|
+
* variables, and storage keys.
|
|
3439
|
+
*
|
|
3440
|
+
* Use branded captures for strings that TypeScript template literal types
|
|
3441
|
+
* cannot express exactly, such as arbitrary-length canonical decimals:
|
|
3442
|
+
*
|
|
3443
|
+
* ```ts
|
|
3444
|
+
* import {
|
|
3445
|
+
* NonNegativeDecimalString,
|
|
3446
|
+
* templateLiteralParser,
|
|
3447
|
+
* } from "@evolu/common";
|
|
3448
|
+
*
|
|
3449
|
+
* const DecimalText = templateLiteralParser(
|
|
3450
|
+
* "decimal:",
|
|
3451
|
+
* NonNegativeDecimalString,
|
|
3452
|
+
* );
|
|
3453
|
+
*
|
|
3454
|
+
* // DecimalText.to requires a validated NonNegativeDecimalString.
|
|
3455
|
+
* const zero = NonNegativeDecimalString.orThrow("0");
|
|
3456
|
+
*
|
|
3457
|
+
* expectOk(DecimalText.fromUnknown("decimal:0"), [zero]);
|
|
3458
|
+
* expect(DecimalText.to([zero])).toBe("decimal:0");
|
|
3459
|
+
* ```
|
|
3460
|
+
*
|
|
3461
|
+
* Capture Types (the Type arguments passed to `templateLiteralParser`) can use
|
|
3462
|
+
* transformations to decode substrings into non-string data:
|
|
3463
|
+
*
|
|
3464
|
+
* ```ts
|
|
3465
|
+
* import {
|
|
3466
|
+
* Int64FromInt64String,
|
|
3467
|
+
* templateLiteralParser,
|
|
3468
|
+
* } from "@evolu/common";
|
|
3469
|
+
*
|
|
3470
|
+
* const ItemId = templateLiteralParser("item-", Int64FromInt64String);
|
|
3471
|
+
* type ItemId = typeof ItemId.Output;
|
|
3472
|
+
* type ItemIdLiteral = typeof ItemId.parent.Output;
|
|
3473
|
+
*
|
|
3474
|
+
* // Decode the string into structured data.
|
|
3475
|
+
* const result = ItemId.fromUnknown("item-42");
|
|
3476
|
+
* assert(result.ok);
|
|
3477
|
+
* const itemId = result.value;
|
|
3478
|
+
* expectTypeOf(itemId).toEqualTypeOf<ItemId>();
|
|
3479
|
+
* expect(itemId).toEqual([42n]);
|
|
3480
|
+
*
|
|
3481
|
+
* // Encode the structured data into its canonical string.
|
|
3482
|
+
* const itemIdLiteral = ItemId.to(itemId);
|
|
3483
|
+
* expectTypeOf(itemIdLiteral).toEqualTypeOf<ItemIdLiteral>();
|
|
3484
|
+
* expect(itemIdLiteral).toBe("item-42");
|
|
3485
|
+
*
|
|
3486
|
+
* // TypeScript cannot prove from the literal alone that "42" is a valid Int64 encoding.
|
|
3487
|
+
* // @ts-expect-error Validate it with ItemId.parent or create it with ItemId.to.
|
|
3488
|
+
* const invalidItemIdLiteral: ItemIdLiteral = "item-42";
|
|
3489
|
+
* ```
|
|
3490
|
+
*
|
|
3491
|
+
* Fixed-width captures can be adjacent:
|
|
3492
|
+
*
|
|
3493
|
+
* ```ts
|
|
3494
|
+
* import { templateLiteralParser, union } from "@evolu/common";
|
|
3495
|
+
*
|
|
3496
|
+
* const Digit = union("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
|
|
3497
|
+
* const TwoDigits = templateLiteralParser(Digit, Digit);
|
|
3498
|
+
* type TwoDigits = typeof TwoDigits.Output;
|
|
3499
|
+
* type TwoDigitsLiteral = typeof TwoDigits.parent.Output;
|
|
3500
|
+
*
|
|
3501
|
+
* const twoDigits: TwoDigits = ["4", "2"];
|
|
3502
|
+
* const twoDigitsLiteral: TwoDigitsLiteral = "42";
|
|
3503
|
+
* // @ts-expect-error TwoDigitsLiteral requires exactly two digits.
|
|
3504
|
+
* const threeDigitsLiteral: TwoDigitsLiteral = "123";
|
|
3505
|
+
*
|
|
3506
|
+
* expectOk(TwoDigits.from.parent(twoDigitsLiteral), twoDigits);
|
|
3507
|
+
* expect(TwoDigits.to(twoDigits)).toBe(twoDigitsLiteral);
|
|
3508
|
+
* ```
|
|
3509
|
+
*
|
|
3510
|
+
* TypeScript rejects multiple variable-width captures because their encoded
|
|
3511
|
+
* boundaries would be ambiguous:
|
|
3512
|
+
*
|
|
3513
|
+
* ```ts
|
|
3514
|
+
* import { String, templateLiteralParser } from "@evolu/common";
|
|
3515
|
+
*
|
|
3516
|
+
* // @ts-expect-error At most one Type capture can have a variable-width string representation.
|
|
3517
|
+
* templateLiteralParser(String, ":", String);
|
|
3518
|
+
* ```
|
|
3519
|
+
*
|
|
3520
|
+
* This restriction keeps encoding reversible: different capture Tuples must
|
|
3521
|
+
* never produce the same string. A delimiter alone is not enough because it can
|
|
3522
|
+
* also occur inside a capture. Some formats could provide stronger guarantees,
|
|
3523
|
+
* such as captures that exclude a delimiter; support for those can be added
|
|
3524
|
+
* when concrete use cases justify the additional framing rules.
|
|
3525
|
+
*
|
|
3526
|
+
* @group Template literals
|
|
3527
|
+
*/
|
|
3528
|
+
export const templateLiteralParser = <const Parts extends TemplateLiteralParts>(
|
|
3529
|
+
...parts: {
|
|
3530
|
+
readonly [Index in keyof Parts]: ValidateTemplateLiteralPart<Parts[Index]>;
|
|
3531
|
+
} & TemplateLiteralValidation<Parts>
|
|
3532
|
+
): TemplateLiteralParserType<Parts> =>
|
|
3533
|
+
createTemplateLiteralParserType(parts as unknown as Parts);
|
|
3534
|
+
|
|
3535
|
+
const createTemplateLiteralParserType = <
|
|
3536
|
+
const Parts extends TemplateLiteralParts,
|
|
3537
|
+
>(
|
|
3538
|
+
templateParts: Parts,
|
|
3539
|
+
): TemplateLiteralParserType<Parts> => {
|
|
3540
|
+
const captureTypes = templateParts.filter(
|
|
3541
|
+
(part): part is TypeNode => typeof part !== "string",
|
|
3542
|
+
) as unknown as TemplateLiteralCaptureTypes<Parts>;
|
|
3543
|
+
const runtimeCaptureTypes =
|
|
3544
|
+
captureTypes as unknown as NonEmptyReadonlyArray<RuntimeTypeNode>;
|
|
3545
|
+
const output = tuple(...captureTypes);
|
|
3546
|
+
const runtimeOutput = output as TemplateLiteralCaptureTuple<Parts> &
|
|
3547
|
+
RuntimeTypeNode;
|
|
3548
|
+
const reflection = {
|
|
3549
|
+
output,
|
|
3550
|
+
parts: templateParts,
|
|
3551
|
+
[templateLiteralSyntaxSymbol]: true as const,
|
|
3552
|
+
};
|
|
3553
|
+
const parse = compileTemplateLiteralParser(templateParts);
|
|
3554
|
+
const decodeString = (
|
|
3555
|
+
value: string,
|
|
3556
|
+
options: ValidationOptions = firstValidationOptions,
|
|
3557
|
+
): Result<
|
|
3558
|
+
TemplateLiteralCaptureTuple<Parts>["Output"],
|
|
3559
|
+
TemplateLiteralRuntimeParseError<Parts>
|
|
3560
|
+
> => {
|
|
3561
|
+
const parseResult = parse(value);
|
|
3562
|
+
if (!parseResult.ok) return parseResult;
|
|
3563
|
+
|
|
3564
|
+
const outputResult = validateTupleItems(
|
|
3565
|
+
parseResult.value,
|
|
3566
|
+
runtimeCaptureTypes,
|
|
3567
|
+
(capture, value, captureOptions) =>
|
|
3568
|
+
capture.fromUnknown(value, captureOptions),
|
|
3569
|
+
options,
|
|
3570
|
+
false,
|
|
3571
|
+
);
|
|
3572
|
+
return (outputResult.ok
|
|
3573
|
+
? outputResult
|
|
3574
|
+
: err({
|
|
3575
|
+
type: "TemplateLiteral",
|
|
3576
|
+
outputError: outputResult.error,
|
|
3577
|
+
})) as Result<
|
|
3578
|
+
TemplateLiteralCaptureTuple<Parts>["Output"],
|
|
3579
|
+
TemplateLiteralRuntimeParseError<Parts>
|
|
3580
|
+
>;
|
|
3581
|
+
};
|
|
3582
|
+
const encodeCaptures = (
|
|
3583
|
+
captures: TemplateLiteralCaptureTuple<Parts>["Output"],
|
|
3584
|
+
): TemplateLiteralStringOutput<Parts> => {
|
|
3585
|
+
const encodedCaptures = runtimeOutput[encoderSymbol](
|
|
3586
|
+
captures as never,
|
|
3587
|
+
) as ReadonlyArray<string>;
|
|
3588
|
+
let value = "";
|
|
3589
|
+
let captureIndex = 0;
|
|
3590
|
+
|
|
3591
|
+
for (const part of templateParts) {
|
|
3592
|
+
value +=
|
|
3593
|
+
typeof part === "string" ? part : encodedCaptures[captureIndex++];
|
|
3594
|
+
}
|
|
3595
|
+
return value as TemplateLiteralStringOutput<Parts>;
|
|
3596
|
+
};
|
|
3597
|
+
const canonicalizeString = (
|
|
3598
|
+
value: string,
|
|
3599
|
+
options: ValidationOptions,
|
|
3600
|
+
): Result<
|
|
3601
|
+
TemplateLiteralStringOutput<Parts>,
|
|
3602
|
+
TemplateLiteralRuntimeParseError<Parts>
|
|
3603
|
+
> => {
|
|
3604
|
+
const result = decodeString(value, options);
|
|
3605
|
+
return result.ok ? ok(encodeCaptures(result.value)) : result;
|
|
3606
|
+
};
|
|
3607
|
+
const validateCanonicalString = (
|
|
3608
|
+
value: unknown,
|
|
3609
|
+
options: ValidationOptions = firstValidationOptions,
|
|
3610
|
+
): Result<
|
|
3611
|
+
TemplateLiteralStringOutput<Parts>,
|
|
3612
|
+
TypeOfError<"String"> | TemplateLiteralRuntimeParseError<Parts>
|
|
3613
|
+
> => {
|
|
3614
|
+
const stringResult = String.fromUnknown(value, options);
|
|
3615
|
+
if (!stringResult.ok) return stringResult;
|
|
3616
|
+
|
|
3617
|
+
const result = canonicalizeString(stringResult.value, options);
|
|
3618
|
+
if (!result.ok || result.value === stringResult.value) return result;
|
|
3619
|
+
|
|
3620
|
+
return err({ type: "TemplateLiteral", value: stringResult.value });
|
|
3621
|
+
};
|
|
3622
|
+
const getTypeIssues: RuntimeGetTypeIssues = (error, mode) => {
|
|
3623
|
+
if (error.type !== "TemplateLiteral") {
|
|
3624
|
+
return (String as unknown as RuntimeTypeNode)[getRuntimeTypeIssuesSymbol](
|
|
3625
|
+
error,
|
|
3626
|
+
mode,
|
|
3627
|
+
);
|
|
3628
|
+
}
|
|
3629
|
+
if ("outputError" in error) {
|
|
3630
|
+
return runtimeOutput[getRuntimeTypeIssuesSymbol](
|
|
3631
|
+
error.outputError as TypeError,
|
|
3632
|
+
mode,
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
return singleRuntimeTypeIssue(
|
|
3636
|
+
"TemplateLiteral",
|
|
3637
|
+
error,
|
|
3638
|
+
formatTemplateLiteralError as TypeErrorFormatter<TypeError>,
|
|
3639
|
+
);
|
|
3640
|
+
};
|
|
3641
|
+
const canonicalStringFromUnknown = (
|
|
3642
|
+
value: unknown,
|
|
3643
|
+
options: ValidationOptions = firstValidationOptions,
|
|
3644
|
+
): Result<
|
|
3645
|
+
TemplateLiteralStringOutput<Parts>,
|
|
3646
|
+
TypeOfError<"String"> | TemplateLiteralRuntimeParseError<Parts>
|
|
3647
|
+
> => {
|
|
3648
|
+
const stringResult = String.fromUnknown(value, options);
|
|
3649
|
+
return stringResult.ok
|
|
3650
|
+
? canonicalizeString(stringResult.value, options)
|
|
3651
|
+
: stringResult;
|
|
3652
|
+
};
|
|
3653
|
+
const canonicalStringFrom = createFromOperation(
|
|
3654
|
+
(value, options = firstValidationOptions) =>
|
|
3655
|
+
canonicalizeString(value, options),
|
|
3656
|
+
);
|
|
3657
|
+
const stringType = createTypeNode<TemplateLiteralType<Parts>>(
|
|
3658
|
+
"TemplateLiteral",
|
|
3659
|
+
String,
|
|
3660
|
+
canonicalStringFromUnknown,
|
|
3661
|
+
(value) => validateCanonicalString(value, firstValidationOptions).ok,
|
|
3662
|
+
validateCanonicalString,
|
|
3663
|
+
canonicalStringFrom,
|
|
3664
|
+
identity,
|
|
3665
|
+
getTypeIssues,
|
|
3666
|
+
reflection,
|
|
3667
|
+
) as TemplateLiteralType<Parts> & RuntimeTypeNode;
|
|
3668
|
+
const fromUnknown = (
|
|
3669
|
+
value: unknown,
|
|
3670
|
+
options: ValidationOptions = firstValidationOptions,
|
|
3671
|
+
): Result<
|
|
3672
|
+
TemplateLiteralCaptureTuple<Parts>["Output"],
|
|
3673
|
+
InferErrors<TemplateLiteralType<Parts>>
|
|
3674
|
+
> => {
|
|
3675
|
+
const stringResult = String.fromUnknown(value, options);
|
|
3676
|
+
if (!stringResult.ok) return stringResult;
|
|
3677
|
+
|
|
3678
|
+
// The internal parser has one broad signature, but a statically frameless
|
|
3679
|
+
// declaration cannot return its framing error.
|
|
3680
|
+
return decodeString(stringResult.value, options) as Result<
|
|
3681
|
+
TemplateLiteralCaptureTuple<Parts>["Output"],
|
|
3682
|
+
InferErrors<TemplateLiteralType<Parts>>
|
|
3683
|
+
>;
|
|
3684
|
+
};
|
|
3685
|
+
const fromCanonicalString: RuntimeOperation<Result<unknown, TypeError>> = (
|
|
3686
|
+
value: never,
|
|
3687
|
+
options = firstValidationOptions,
|
|
3688
|
+
) => {
|
|
3689
|
+
String.from(value);
|
|
3690
|
+
const result = decodeString(value, options);
|
|
3691
|
+
if (!result.ok || encodeCaptures(result.value) !== value) {
|
|
3692
|
+
throw new Error("Expected TemplateLiteral.", {
|
|
3693
|
+
cause: result.ok
|
|
3694
|
+
? ({
|
|
3695
|
+
type: "TemplateLiteral",
|
|
3696
|
+
value,
|
|
3697
|
+
} satisfies TemplateLiteralError)
|
|
3698
|
+
: result.error,
|
|
3699
|
+
});
|
|
3700
|
+
}
|
|
3701
|
+
return result;
|
|
3702
|
+
};
|
|
3703
|
+
const fromString: RuntimeOperation<Result<unknown, TypeError>> = (
|
|
3704
|
+
value: never,
|
|
3705
|
+
options = firstValidationOptions,
|
|
3706
|
+
) => {
|
|
3707
|
+
String.from(value);
|
|
3708
|
+
return decodeString(value, options);
|
|
3709
|
+
};
|
|
3710
|
+
fromCanonicalString.parent = fromString;
|
|
3711
|
+
const from = createFromOperation(fromCanonicalString);
|
|
3712
|
+
const type = createTypeNode<TemplateLiteralParserType<Parts>>(
|
|
3713
|
+
"TemplateLiteral",
|
|
3714
|
+
stringType,
|
|
3715
|
+
fromUnknown,
|
|
3716
|
+
runtimeOutput.is,
|
|
3717
|
+
runtimeOutput[outputValidationSymbol],
|
|
3718
|
+
from,
|
|
3719
|
+
encodeCaptures,
|
|
3720
|
+
getTypeIssues,
|
|
3721
|
+
reflection,
|
|
3722
|
+
);
|
|
3723
|
+
(type.from as RuntimeOperation<Result<unknown, TypeError>>).parent =
|
|
3724
|
+
fromCanonicalString;
|
|
3725
|
+
|
|
3726
|
+
return type;
|
|
3727
|
+
};
|
|
3728
|
+
|
|
3729
|
+
/** @group Template literals */
|
|
3730
|
+
export interface TemplateLiteralParserType<
|
|
3731
|
+
Parts extends TemplateLiteralParts,
|
|
3732
|
+
> extends Type<
|
|
3733
|
+
"TemplateLiteral",
|
|
3734
|
+
string,
|
|
3735
|
+
TemplateLiteralCaptureTuple<Parts>["Output"],
|
|
3736
|
+
never,
|
|
3737
|
+
TemplateLiteralType<Parts>,
|
|
3738
|
+
InferErrors<TemplateLiteralType<Parts>>,
|
|
3739
|
+
never,
|
|
3740
|
+
TemplateLiteralStringOutput<Parts>,
|
|
3741
|
+
false
|
|
3742
|
+
> {
|
|
3743
|
+
readonly [templateLiteralSyntaxSymbol]: true;
|
|
3744
|
+
readonly [reflectedTypesSymbol]?: TemplateLiteralCaptureTuple<Parts>;
|
|
3745
|
+
readonly output: TemplateLiteralCaptureTuple<Parts>;
|
|
3746
|
+
readonly parts: Parts;
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
/** @group Template literals */
|
|
3750
|
+
export interface TemplateLiteralType<
|
|
3751
|
+
Parts extends TemplateLiteralParts,
|
|
3752
|
+
> extends Type<
|
|
3753
|
+
"TemplateLiteral",
|
|
3754
|
+
string,
|
|
3755
|
+
TemplateLiteralStringOutput<Parts>,
|
|
3756
|
+
TemplateLiteralParseError<Parts>,
|
|
3757
|
+
typeof String,
|
|
3758
|
+
TypeOfError<"String"> | TemplateLiteralParseError<Parts>,
|
|
3759
|
+
never,
|
|
3760
|
+
TemplateLiteralStringOutput<Parts>,
|
|
3761
|
+
true
|
|
3762
|
+
> {
|
|
3763
|
+
readonly [templateLiteralSyntaxSymbol]: true;
|
|
3764
|
+
readonly [reflectedTypesSymbol]?: TemplateLiteralCaptureTuple<Parts>;
|
|
3765
|
+
readonly output: TemplateLiteralCaptureTuple<Parts>;
|
|
3766
|
+
readonly parts: Parts;
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
/**
|
|
3770
|
+
* Template literal {@link Type} for validation.
|
|
3771
|
+
*
|
|
3772
|
+
* Creates a canonical string Type from fixed strings and string-encoded Types.
|
|
3773
|
+
*
|
|
3774
|
+
* Use this factory when Output should remain a string. Switch to
|
|
3775
|
+
* {@link templateLiteralParser} when the individual Type parts should be
|
|
3776
|
+
* decoded into a Tuple.
|
|
3777
|
+
*
|
|
3778
|
+
* ### Example
|
|
3779
|
+
*
|
|
3780
|
+
* ```ts
|
|
3781
|
+
* import { templateLiteral, union } from "@evolu/common";
|
|
3782
|
+
*
|
|
3783
|
+
* const Language = union("en", "cs");
|
|
3784
|
+
* const Region = union("US", "CZ");
|
|
3785
|
+
* const Locale = templateLiteral(Language, "-", Region);
|
|
3786
|
+
*
|
|
3787
|
+
* expectTypeOf<typeof Locale.Output>().toEqualTypeOf<
|
|
3788
|
+
* "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
|
|
3789
|
+
* >();
|
|
3790
|
+
* expectOk(Locale.fromUnknown("cs-CZ"), "cs-CZ");
|
|
3791
|
+
* expect(Locale.is("fr-CZ")).toBe(false);
|
|
3792
|
+
* ```
|
|
3793
|
+
*
|
|
3794
|
+
* @group Template literals
|
|
3795
|
+
*/
|
|
3796
|
+
export const templateLiteral = <const Parts extends TemplateLiteralParts>(
|
|
3797
|
+
...parts: {
|
|
3798
|
+
readonly [Index in keyof Parts]: ValidateTemplateLiteralPart<Parts[Index]>;
|
|
3799
|
+
} & TemplateLiteralValidation<Parts>
|
|
3800
|
+
): TemplateLiteralType<Parts> =>
|
|
3801
|
+
createTemplateLiteralParserType(parts as unknown as Parts).parent;
|
|
3802
|
+
|
|
3803
|
+
type TemplateLiteralParseError<Parts extends TemplateLiteralParts> =
|
|
3804
|
+
TransformError<
|
|
3805
|
+
"TemplateLiteral",
|
|
3806
|
+
TemplateLiteralIsFrameless<Parts> extends true
|
|
3807
|
+
? never
|
|
3808
|
+
: TemplateLiteralError,
|
|
3809
|
+
TemplateLiteralCaptureTupleError<Parts>
|
|
3810
|
+
>;
|
|
3811
|
+
|
|
3812
|
+
type TemplateLiteralRuntimeParseError<Parts extends TemplateLiteralParts> =
|
|
3813
|
+
TransformError<
|
|
3814
|
+
"TemplateLiteral",
|
|
3815
|
+
TemplateLiteralError,
|
|
3816
|
+
TemplateLiteralCaptureTupleError<Parts>
|
|
3817
|
+
>;
|
|
3818
|
+
|
|
3819
|
+
type TemplateLiteralCaptureTupleError<Parts extends TemplateLiteralParts> =
|
|
3820
|
+
TupleElementsError<
|
|
3821
|
+
TemplateLiteralCaptureFromStringError<
|
|
3822
|
+
TemplateLiteralCaptureTypes<Parts>[number]
|
|
3823
|
+
>
|
|
3824
|
+
>;
|
|
3825
|
+
|
|
3826
|
+
type TemplateLiteralCaptureFromStringError<T extends TypeNode> =
|
|
3827
|
+
T extends TypeNode
|
|
3828
|
+
? string extends RootType<T>["Output"]
|
|
3829
|
+
? TypeFromError<T>
|
|
3830
|
+
: InferErrors<T>
|
|
3831
|
+
: never;
|
|
3832
|
+
|
|
3833
|
+
/** @group Template literals */
|
|
3834
|
+
export interface TemplateLiteralError extends TypeError<"TemplateLiteral"> {
|
|
3835
|
+
readonly value: string;
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3838
|
+
const formatTemplateLiteralError: TypeErrorFormatter<TemplateLiteralError> =
|
|
3839
|
+
(error) =>
|
|
3840
|
+
`The value ${safelyStringifyUnknownValue(error.value)} does not match the template literal.`;
|
|
3841
|
+
|
|
3842
|
+
declare const templateLiteralStringBrandSymbol: unique symbol;
|
|
3843
|
+
|
|
3844
|
+
interface TemplateLiteralStringBrand<Parts extends TemplateLiteralParts> {
|
|
3845
|
+
readonly [templateLiteralStringBrandSymbol]: Parts;
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
type TemplateLiteralPart = string | TypeNode;
|
|
3849
|
+
|
|
3850
|
+
type TemplateLiteralParts = NonEmptyReadonlyArray<TemplateLiteralPart>;
|
|
3851
|
+
|
|
3852
|
+
type TemplateLiteralValidation<Parts extends TemplateLiteralParts> =
|
|
3853
|
+
number extends Parts["length"]
|
|
3854
|
+
? readonly [ValidationFailure<TemplateLiteralPartsTupleError>]
|
|
3855
|
+
: IsUnion<Parts["length"]> extends true
|
|
3856
|
+
? readonly [ValidationFailure<TemplateLiteralPartsTupleError>]
|
|
3857
|
+
: [Extract<Parts[number], TypeNode>] extends [never]
|
|
3858
|
+
? readonly [ValidationFailure<TemplateLiteralCaptureRequiredError>]
|
|
3859
|
+
: TemplateLiteralHasAmbiguousCaptures<Parts> extends true
|
|
3860
|
+
? readonly [ValidationFailure<TemplateLiteralAmbiguousCapturesError>]
|
|
3861
|
+
: unknown;
|
|
3862
|
+
|
|
3863
|
+
type TemplateLiteralPartsTupleError = CompileTimeError<
|
|
3864
|
+
"TemplateLiteral",
|
|
3865
|
+
"Parts must use one concrete finite non-empty tuple."
|
|
3866
|
+
>;
|
|
3867
|
+
|
|
3868
|
+
type TemplateLiteralCaptureRequiredError = CompileTimeError<
|
|
3869
|
+
"TemplateLiteral",
|
|
3870
|
+
"At least one part must be a Type capture."
|
|
3871
|
+
>;
|
|
3872
|
+
|
|
3873
|
+
type TemplateLiteralAmbiguousCapturesError = CompileTimeError<
|
|
3874
|
+
"TemplateLiteral",
|
|
3875
|
+
"At most one Type capture can have a variable-width string representation."
|
|
3876
|
+
>;
|
|
3877
|
+
|
|
3878
|
+
type TemplateLiteralCaptureTypes<
|
|
3879
|
+
Parts extends ReadonlyArray<TemplateLiteralPart>,
|
|
3880
|
+
Captures extends ReadonlyArray<TypeNode> = readonly [],
|
|
3881
|
+
> = Parts extends readonly [infer Head, ...infer Tail]
|
|
3882
|
+
? TemplateLiteralCaptureTypes<
|
|
3883
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
3884
|
+
Head extends TypeNode ? readonly [...Captures, Head] : Captures
|
|
3885
|
+
>
|
|
3886
|
+
: Extract<Captures, NonEmptyReadonlyArray<TypeNode>>;
|
|
3887
|
+
|
|
3888
|
+
type TemplateLiteralCaptureTuple<Parts extends TemplateLiteralParts> =
|
|
3889
|
+
TupleType<TemplateLiteralCaptureTypes<Parts>>;
|
|
3890
|
+
|
|
3891
|
+
type TemplateLiteralCanonicalInput<
|
|
3892
|
+
Parts extends ReadonlyArray<TemplateLiteralPart>,
|
|
3893
|
+
Input extends string = "",
|
|
3894
|
+
> = Parts extends readonly [infer Head, ...infer Tail]
|
|
3895
|
+
? TemplateLiteralCanonicalInput<
|
|
3896
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
3897
|
+
`${Input}${TemplateLiteralPartCanonicalInput<Extract<Head, TemplateLiteralPart>>}`
|
|
3898
|
+
>
|
|
3899
|
+
: Input;
|
|
3900
|
+
|
|
3901
|
+
type TemplateLiteralStringOutput<Parts extends TemplateLiteralParts> =
|
|
3902
|
+
AllTypesUseIdentityEncoding<Extract<Parts[number], TypeNode>> extends true
|
|
3903
|
+
? TemplateLiteralCanonicalInput<Parts>
|
|
3904
|
+
: TemplateLiteralCanonicalInput<Parts> & TemplateLiteralStringBrand<Parts>;
|
|
3905
|
+
|
|
3906
|
+
type TemplateLiteralPartCanonicalInput<Part extends TemplateLiteralPart> =
|
|
3907
|
+
Part extends string
|
|
3908
|
+
? Part
|
|
3909
|
+
: Part extends TypeNode
|
|
3910
|
+
? Extract<CanonicalInputOf<Part>, string>
|
|
3911
|
+
: never;
|
|
3912
|
+
|
|
3913
|
+
type ValidateTemplateLiteralPart<Part extends TemplateLiteralPart> =
|
|
3914
|
+
IsUnion<Part> extends false
|
|
3915
|
+
? Part extends string
|
|
3916
|
+
? ValidateLiteral<Part>
|
|
3917
|
+
: Part extends ConcreteTypeNode
|
|
3918
|
+
? IsTemplateLiteralPartType<Part> extends true
|
|
3919
|
+
? Part
|
|
3920
|
+
: TemplateLiteralPartCompileTimeError
|
|
3921
|
+
: TemplateLiteralPartCompileTimeError
|
|
3922
|
+
: TemplateLiteralPartCompileTimeError;
|
|
3923
|
+
|
|
3924
|
+
type IsTemplateLiteralPartType<T extends TypeNode> = [
|
|
3925
|
+
CanonicalInputOf<T>,
|
|
3926
|
+
] extends [never]
|
|
3927
|
+
? false
|
|
3928
|
+
: [CanonicalInputOf<T>] extends [string]
|
|
3929
|
+
? true
|
|
3930
|
+
: false;
|
|
3931
|
+
|
|
3932
|
+
type TemplateLiteralPartCompileTimeError = CompileTimeError<
|
|
3933
|
+
"TemplateLiteral",
|
|
3934
|
+
"Part must be a raw string literal or a Type canonically encoded as a string."
|
|
3935
|
+
>;
|
|
3936
|
+
|
|
3937
|
+
type TemplateLiteralHasAmbiguousCaptures<
|
|
3938
|
+
Parts extends ReadonlyArray<TemplateLiteralPart>,
|
|
3939
|
+
VariableCaptures extends ReadonlyArray<unknown> = readonly [],
|
|
3940
|
+
> = Parts extends readonly [infer Head, ...infer Tail]
|
|
3941
|
+
? Head extends TypeNode
|
|
3942
|
+
? [TemplateLiteralTypeWidth<Head>] extends [null]
|
|
3943
|
+
? VariableCaptures extends readonly [unknown]
|
|
3944
|
+
? true
|
|
3945
|
+
: TemplateLiteralHasAmbiguousCaptures<
|
|
3946
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
3947
|
+
readonly [unknown]
|
|
3948
|
+
>
|
|
3949
|
+
: TemplateLiteralHasAmbiguousCaptures<
|
|
3950
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
3951
|
+
VariableCaptures
|
|
3952
|
+
>
|
|
3953
|
+
: TemplateLiteralHasAmbiguousCaptures<
|
|
3954
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
3955
|
+
VariableCaptures
|
|
3956
|
+
>
|
|
3957
|
+
: false;
|
|
3958
|
+
|
|
3959
|
+
type TemplateLiteralTypeWidth<T extends TypeNode> =
|
|
3960
|
+
T extends LiteralType<infer Expected extends string>
|
|
3961
|
+
? TemplateLiteralStringWidth<Expected>
|
|
3962
|
+
: T extends UnionType<infer Members>
|
|
3963
|
+
? NormalizeTemplateLiteralWidth<TemplateLiteralTypeWidth<Members[number]>>
|
|
3964
|
+
: T extends TemplateLiteralParserType<infer Parts>
|
|
3965
|
+
? TemplateLiteralPartsWidth<Parts>
|
|
3966
|
+
: T extends TemplateLiteralType<infer Parts>
|
|
3967
|
+
? TemplateLiteralPartsWidth<Parts>
|
|
3968
|
+
: T["parent"] extends infer Parent extends TypeNode
|
|
3969
|
+
? TemplateLiteralTypeWidth<Parent>
|
|
3970
|
+
: null;
|
|
3971
|
+
|
|
3972
|
+
type NormalizeTemplateLiteralWidth<Width> =
|
|
3973
|
+
IsUnion<Width> extends true
|
|
3974
|
+
? null
|
|
3975
|
+
: Width extends ReadonlyArray<unknown>
|
|
3976
|
+
? Width
|
|
3977
|
+
: null;
|
|
3978
|
+
|
|
3979
|
+
type TemplateLiteralPartsWidth<
|
|
3980
|
+
Parts extends ReadonlyArray<TemplateLiteralPart>,
|
|
3981
|
+
Width extends ReadonlyArray<unknown> = readonly [],
|
|
3982
|
+
> = Parts extends readonly [infer Head, ...infer Tail]
|
|
3983
|
+
? TemplateLiteralPartWidth<
|
|
3984
|
+
Extract<Head, TemplateLiteralPart>
|
|
3985
|
+
> extends infer PartWidth
|
|
3986
|
+
? [PartWidth] extends [ReadonlyArray<unknown>]
|
|
3987
|
+
? TemplateLiteralPartsWidth<
|
|
3988
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
3989
|
+
readonly [...Width, ...PartWidth]
|
|
3990
|
+
>
|
|
3991
|
+
: null
|
|
3992
|
+
: never
|
|
3993
|
+
: Width;
|
|
3994
|
+
|
|
3995
|
+
type TemplateLiteralPartWidth<Part extends TemplateLiteralPart> =
|
|
3996
|
+
Part extends string
|
|
3997
|
+
? TemplateLiteralStringWidth<Part>
|
|
3998
|
+
: Part extends TypeNode
|
|
3999
|
+
? TemplateLiteralTypeWidth<Part>
|
|
4000
|
+
: never;
|
|
4001
|
+
|
|
4002
|
+
type TemplateLiteralIsFrameless<
|
|
4003
|
+
Parts extends ReadonlyArray<TemplateLiteralPart>,
|
|
4004
|
+
HasVariableCapture extends boolean = false,
|
|
4005
|
+
> = Parts extends readonly [infer Head, ...infer Tail]
|
|
4006
|
+
? TemplateLiteralPartWidth<
|
|
4007
|
+
Extract<Head, TemplateLiteralPart>
|
|
4008
|
+
> extends infer Width
|
|
4009
|
+
? [Width] extends [null]
|
|
4010
|
+
? HasVariableCapture extends true
|
|
4011
|
+
? false
|
|
4012
|
+
: TemplateLiteralIsFrameless<
|
|
4013
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
4014
|
+
true
|
|
4015
|
+
>
|
|
4016
|
+
: [Width] extends [readonly []]
|
|
4017
|
+
? TemplateLiteralIsFrameless<
|
|
4018
|
+
Extract<Tail, ReadonlyArray<TemplateLiteralPart>>,
|
|
4019
|
+
HasVariableCapture
|
|
4020
|
+
>
|
|
4021
|
+
: false
|
|
4022
|
+
: false
|
|
4023
|
+
: HasVariableCapture;
|
|
4024
|
+
|
|
4025
|
+
type TemplateLiteralStringWidth<
|
|
4026
|
+
Value extends string,
|
|
4027
|
+
Width extends ReadonlyArray<unknown> = readonly [],
|
|
4028
|
+
> = string extends Value
|
|
4029
|
+
? null
|
|
4030
|
+
: Value extends ""
|
|
4031
|
+
? Width
|
|
4032
|
+
: Value extends `${infer _CodePoint}${infer Tail}`
|
|
4033
|
+
? TemplateLiteralStringWidth<Tail, readonly [...Width, unknown]>
|
|
4034
|
+
: null;
|
|
4035
|
+
|
|
4036
|
+
interface TemplateLiteralFraming {
|
|
4037
|
+
readonly width: number | null;
|
|
4038
|
+
readonly canBeEmpty: boolean;
|
|
4039
|
+
readonly canStartWithLowSurrogate: boolean;
|
|
4040
|
+
readonly canEndWithHighSurrogate: boolean;
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
const compileTemplateLiteralParser = <Parts extends TemplateLiteralParts>(
|
|
4044
|
+
parts: Parts,
|
|
4045
|
+
): ((
|
|
4046
|
+
input: string,
|
|
4047
|
+
) => Result<
|
|
4048
|
+
TemplateLiteralCaptureTuple<Parts>["Input"],
|
|
4049
|
+
TemplateLiteralError
|
|
4050
|
+
>) => {
|
|
4051
|
+
let framing = emptyTemplateLiteralFraming;
|
|
4052
|
+
let fixedPartsWidth = 0;
|
|
4053
|
+
const compiledParts = parts.map((part) => {
|
|
4054
|
+
const partFraming = getTemplateLiteralPartFraming(part);
|
|
4055
|
+
framing = concatenateTemplateLiteralFraming(framing, partFraming);
|
|
4056
|
+
fixedPartsWidth += partFraming.width ?? 0;
|
|
4057
|
+
return [part, partFraming.width] as const;
|
|
4058
|
+
});
|
|
4059
|
+
|
|
4060
|
+
return (input) => {
|
|
4061
|
+
const inputCodePoints = globalThis.Array.from(input);
|
|
4062
|
+
const variableWidth = inputCodePoints.length - fixedPartsWidth;
|
|
4063
|
+
if (variableWidth < 0) {
|
|
4064
|
+
return err({ type: "TemplateLiteral", value: input });
|
|
4065
|
+
}
|
|
4066
|
+
|
|
4067
|
+
const captures: Array<string> = [];
|
|
4068
|
+
let position = 0;
|
|
4069
|
+
|
|
4070
|
+
for (const [part, width] of compiledParts) {
|
|
4071
|
+
const partWidth = width ?? variableWidth;
|
|
4072
|
+
const value = inputCodePoints
|
|
4073
|
+
.slice(position, position + partWidth)
|
|
4074
|
+
.join("");
|
|
4075
|
+
|
|
4076
|
+
if (typeof part === "string") {
|
|
4077
|
+
if (value !== part) {
|
|
4078
|
+
return err({ type: "TemplateLiteral", value: input });
|
|
4079
|
+
}
|
|
4080
|
+
} else {
|
|
4081
|
+
captures.push(value);
|
|
4082
|
+
}
|
|
4083
|
+
position += partWidth;
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
return position === inputCodePoints.length
|
|
4087
|
+
? ok(
|
|
4088
|
+
captures as unknown as TemplateLiteralCaptureTuple<Parts>["Input"],
|
|
4089
|
+
)
|
|
4090
|
+
: err({ type: "TemplateLiteral", value: input });
|
|
4091
|
+
};
|
|
4092
|
+
};
|
|
4093
|
+
|
|
4094
|
+
const emptyTemplateLiteralFraming: TemplateLiteralFraming = {
|
|
4095
|
+
width: 0,
|
|
4096
|
+
canBeEmpty: true,
|
|
4097
|
+
canStartWithLowSurrogate: false,
|
|
4098
|
+
canEndWithHighSurrogate: false,
|
|
4099
|
+
};
|
|
4100
|
+
|
|
4101
|
+
const unknownTemplateLiteralFraming: TemplateLiteralFraming = {
|
|
4102
|
+
width: null,
|
|
4103
|
+
canBeEmpty: true,
|
|
4104
|
+
canStartWithLowSurrogate: true,
|
|
4105
|
+
canEndWithHighSurrogate: true,
|
|
4106
|
+
};
|
|
4107
|
+
|
|
4108
|
+
const concatenateTemplateLiteralFraming = (
|
|
4109
|
+
left: TemplateLiteralFraming,
|
|
4110
|
+
right: TemplateLiteralFraming,
|
|
4111
|
+
): TemplateLiteralFraming => {
|
|
4112
|
+
assert(
|
|
4113
|
+
!(left.canEndWithHighSurrogate && right.canStartWithLowSurrogate),
|
|
4114
|
+
"A TemplateLiteral cannot form a Unicode surrogate pair across part boundaries.",
|
|
4115
|
+
);
|
|
4116
|
+
|
|
4117
|
+
return {
|
|
4118
|
+
width:
|
|
4119
|
+
left.width !== null && right.width !== null
|
|
4120
|
+
? left.width + right.width
|
|
4121
|
+
: null,
|
|
4122
|
+
canBeEmpty: left.canBeEmpty && right.canBeEmpty,
|
|
4123
|
+
canStartWithLowSurrogate:
|
|
4124
|
+
left.canStartWithLowSurrogate ||
|
|
4125
|
+
(left.canBeEmpty && right.canStartWithLowSurrogate),
|
|
4126
|
+
canEndWithHighSurrogate:
|
|
4127
|
+
right.canEndWithHighSurrogate ||
|
|
4128
|
+
(right.canBeEmpty && left.canEndWithHighSurrogate),
|
|
4129
|
+
};
|
|
4130
|
+
};
|
|
4131
|
+
|
|
4132
|
+
const getStringTemplateLiteralFraming = (
|
|
4133
|
+
value: string,
|
|
4134
|
+
): TemplateLiteralFraming => {
|
|
4135
|
+
const firstCodeUnit = value.charCodeAt(0);
|
|
4136
|
+
const lastCodeUnit = value.charCodeAt(value.length - 1);
|
|
4137
|
+
|
|
4138
|
+
return {
|
|
4139
|
+
width: globalThis.Array.from(value).length,
|
|
4140
|
+
canBeEmpty: value.length === 0,
|
|
4141
|
+
canStartWithLowSurrogate:
|
|
4142
|
+
firstCodeUnit >= 0xdc00 && firstCodeUnit <= 0xdfff,
|
|
4143
|
+
canEndWithHighSurrogate: lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff,
|
|
4144
|
+
};
|
|
4145
|
+
};
|
|
4146
|
+
|
|
4147
|
+
const getTemplateLiteralPartFraming = (
|
|
4148
|
+
part: TemplateLiteralPart,
|
|
4149
|
+
): TemplateLiteralFraming => {
|
|
4150
|
+
if (typeof part === "string") return getStringTemplateLiteralFraming(part);
|
|
4151
|
+
|
|
4152
|
+
const type = part as RuntimeTypeNode;
|
|
4153
|
+
if (type[templateLiteralSyntaxSymbol] === true) {
|
|
4154
|
+
if (type.name === "Literal") {
|
|
4155
|
+
return getStringTemplateLiteralFraming(
|
|
4156
|
+
(type as unknown as LiteralType<string>).expected,
|
|
4157
|
+
);
|
|
4158
|
+
}
|
|
4159
|
+
if (type.name === "Union") {
|
|
4160
|
+
const memberFramings = (
|
|
4161
|
+
type as unknown as RuntimeUnionTypeNode
|
|
4162
|
+
).members.map(getTemplateLiteralPartFraming);
|
|
4163
|
+
const width = memberFramings[0].width;
|
|
4164
|
+
|
|
4165
|
+
return {
|
|
4166
|
+
width:
|
|
4167
|
+
width !== null &&
|
|
4168
|
+
memberFramings.every((framing) => framing.width === width)
|
|
4169
|
+
? width
|
|
4170
|
+
: null,
|
|
4171
|
+
canBeEmpty: memberFramings.some((framing) => framing.canBeEmpty),
|
|
4172
|
+
canStartWithLowSurrogate: memberFramings.some(
|
|
4173
|
+
(framing) => framing.canStartWithLowSurrogate,
|
|
4174
|
+
),
|
|
4175
|
+
canEndWithHighSurrogate: memberFramings.some(
|
|
4176
|
+
(framing) => framing.canEndWithHighSurrogate,
|
|
4177
|
+
),
|
|
4178
|
+
};
|
|
4179
|
+
}
|
|
4180
|
+
if (type.name === "TemplateLiteral") {
|
|
4181
|
+
return (
|
|
4182
|
+
type as unknown as { readonly parts: TemplateLiteralParts }
|
|
4183
|
+
).parts.reduce(
|
|
4184
|
+
(framing, part) =>
|
|
4185
|
+
concatenateTemplateLiteralFraming(
|
|
4186
|
+
framing,
|
|
4187
|
+
getTemplateLiteralPartFraming(part),
|
|
4188
|
+
),
|
|
4189
|
+
emptyTemplateLiteralFraming,
|
|
4190
|
+
);
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
if (type.parent === null) return unknownTemplateLiteralFraming;
|
|
4194
|
+
|
|
4195
|
+
return getTemplateLiteralPartFraming(type.parent);
|
|
4196
|
+
};
|
|
4197
|
+
|
|
3329
4198
|
/**
|
|
3330
4199
|
* Branded {@link Type}.
|
|
3331
4200
|
*
|
|
@@ -3809,7 +4678,7 @@ export const CapitalizedString = /*#__PURE__*/ capitalized(String);
|
|
|
3809
4678
|
export type CapitalizedString = typeof CapitalizedString.Output;
|
|
3810
4679
|
|
|
3811
4680
|
/**
|
|
3812
|
-
*
|
|
4681
|
+
* String {@link Brand} without surrounding whitespace.
|
|
3813
4682
|
*
|
|
3814
4683
|
* @group String
|
|
3815
4684
|
*/
|
|
@@ -3855,7 +4724,7 @@ export const trim = (value: string): TrimmedString =>
|
|
|
3855
4724
|
value.trim() as TrimmedString;
|
|
3856
4725
|
|
|
3857
4726
|
/**
|
|
3858
|
-
*
|
|
4727
|
+
* Minimum-length {@link Brand} requiring a value to have at least `min` items.
|
|
3859
4728
|
*
|
|
3860
4729
|
* @group String
|
|
3861
4730
|
* @group Collection
|
|
@@ -3905,7 +4774,7 @@ export const NonEmptyTrimmedString = /*#__PURE__*/ minLength(1)(TrimmedString);
|
|
|
3905
4774
|
export type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Output;
|
|
3906
4775
|
|
|
3907
4776
|
/**
|
|
3908
|
-
*
|
|
4777
|
+
* Maximum-length {@link Brand} requiring a value to have at most `max` items.
|
|
3909
4778
|
*
|
|
3910
4779
|
* @group String
|
|
3911
4780
|
* @group Collection
|
|
@@ -3958,7 +4827,7 @@ export const NonEmptyTrimmedString1000 = /*#__PURE__*/ maxLength(1000)(
|
|
|
3958
4827
|
export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Output;
|
|
3959
4828
|
|
|
3960
4829
|
/**
|
|
3961
|
-
*
|
|
4830
|
+
* Exact-length {@link Brand} requiring a value to have exactly `exact` items.
|
|
3962
4831
|
*
|
|
3963
4832
|
* @group String
|
|
3964
4833
|
* @group Collection
|
|
@@ -3991,7 +4860,7 @@ export interface LengthError<
|
|
|
3991
4860
|
}
|
|
3992
4861
|
|
|
3993
4862
|
/**
|
|
3994
|
-
*
|
|
4863
|
+
* String {@link Brand} constrained by a regular expression.
|
|
3995
4864
|
*
|
|
3996
4865
|
* ### Example
|
|
3997
4866
|
*
|
|
@@ -4318,7 +5187,7 @@ export const createIdAsUuidv7 = <B extends string = never>(
|
|
|
4318
5187
|
};
|
|
4319
5188
|
|
|
4320
5189
|
/**
|
|
4321
|
-
*
|
|
5190
|
+
* Table-specific {@link Id} Type.
|
|
4322
5191
|
*
|
|
4323
5192
|
* @group String
|
|
4324
5193
|
*/
|
|
@@ -4475,7 +5344,7 @@ export const Int64FromInt64String = /*#__PURE__*/ transform(
|
|
|
4475
5344
|
);
|
|
4476
5345
|
|
|
4477
5346
|
/**
|
|
4478
|
-
*
|
|
5347
|
+
* Number {@link Brand} requiring a value greater than or equal to zero.
|
|
4479
5348
|
*
|
|
4480
5349
|
* @group Number
|
|
4481
5350
|
*/
|
|
@@ -4507,7 +5376,7 @@ export const NonNegativeNumber = /*#__PURE__*/ nonNegative(Number);
|
|
|
4507
5376
|
export type NonNegativeNumber = typeof NonNegativeNumber.Output;
|
|
4508
5377
|
|
|
4509
5378
|
/**
|
|
4510
|
-
*
|
|
5379
|
+
* Number {@link Brand} requiring a value greater than zero.
|
|
4511
5380
|
*
|
|
4512
5381
|
* @group Number
|
|
4513
5382
|
*/
|
|
@@ -4540,7 +5409,7 @@ export const PositiveNumber = /*#__PURE__*/ positive(NonNegativeNumber);
|
|
|
4540
5409
|
export type PositiveNumber = typeof PositiveNumber.Output;
|
|
4541
5410
|
|
|
4542
5411
|
/**
|
|
4543
|
-
*
|
|
5412
|
+
* Number {@link Brand} requiring a value less than or equal to zero.
|
|
4544
5413
|
*
|
|
4545
5414
|
* @group Number
|
|
4546
5415
|
*/
|
|
@@ -4572,7 +5441,7 @@ export const NonPositiveNumber = /*#__PURE__*/ nonPositive(Number);
|
|
|
4572
5441
|
export type NonPositiveNumber = typeof NonPositiveNumber.Output;
|
|
4573
5442
|
|
|
4574
5443
|
/**
|
|
4575
|
-
*
|
|
5444
|
+
* Number {@link Brand} requiring a value less than zero.
|
|
4576
5445
|
*
|
|
4577
5446
|
* @group Number
|
|
4578
5447
|
*/
|
|
@@ -4605,7 +5474,7 @@ export const NegativeNumber = /*#__PURE__*/ negative(NonPositiveNumber);
|
|
|
4605
5474
|
export type NegativeNumber = typeof NegativeNumber.Output;
|
|
4606
5475
|
|
|
4607
5476
|
/**
|
|
4608
|
-
*
|
|
5477
|
+
* Number {@link Brand} requiring a value other than `NaN`.
|
|
4609
5478
|
*
|
|
4610
5479
|
* @group Number
|
|
4611
5480
|
*/
|
|
@@ -4639,7 +5508,7 @@ export const NonNaNNumber = /*#__PURE__*/ nonNaN(Number);
|
|
|
4639
5508
|
export type NonNaNNumber = typeof NonNaNNumber.Output;
|
|
4640
5509
|
|
|
4641
5510
|
/**
|
|
4642
|
-
*
|
|
5511
|
+
* Number {@link Brand} requiring a finite value.
|
|
4643
5512
|
*
|
|
4644
5513
|
* @group Number
|
|
4645
5514
|
*/
|
|
@@ -4676,6 +5545,16 @@ export type FiniteNumber = typeof FiniteNumber.Output;
|
|
|
4676
5545
|
export const NonNegativeFiniteNumber = /*#__PURE__*/ nonNegative(FiniteNumber);
|
|
4677
5546
|
export type NonNegativeFiniteNumber = typeof NonNegativeFiniteNumber.Output;
|
|
4678
5547
|
|
|
5548
|
+
/**
|
|
5549
|
+
* Positive {@link FiniteNumber}.
|
|
5550
|
+
*
|
|
5551
|
+
* @group Number
|
|
5552
|
+
*/
|
|
5553
|
+
export const PositiveFiniteNumber = /*#__PURE__*/ positive(
|
|
5554
|
+
NonNegativeFiniteNumber,
|
|
5555
|
+
);
|
|
5556
|
+
export type PositiveFiniteNumber = typeof PositiveFiniteNumber.Output;
|
|
5557
|
+
|
|
4679
5558
|
/**
|
|
4680
5559
|
* Safe integer {@link Brand}.
|
|
4681
5560
|
*
|
|
@@ -4731,13 +5610,6 @@ export type Int = typeof Int.Output;
|
|
|
4731
5610
|
export const NonNegativeInt = /*#__PURE__*/ nonNegative(Int);
|
|
4732
5611
|
export type NonNegativeInt = typeof NonNegativeInt.Output;
|
|
4733
5612
|
|
|
4734
|
-
/**
|
|
4735
|
-
* 0-100 as a literal, or any already-validated {@link NonNegativeInt}.
|
|
4736
|
-
*
|
|
4737
|
-
* @group Number
|
|
4738
|
-
*/
|
|
4739
|
-
export type Int0To100OrNonNegativeInt = 0 | Int1To100 | NonNegativeInt;
|
|
4740
|
-
|
|
4741
5613
|
/**
|
|
4742
5614
|
* Minimum {@link NonNegativeInt} value.
|
|
4743
5615
|
*
|
|
@@ -4756,13 +5628,6 @@ export const zeroNonNegativeInt = /*#__PURE__*/ NonNegativeInt.orThrow(0);
|
|
|
4756
5628
|
export const PositiveInt = /*#__PURE__*/ positive(NonNegativeInt);
|
|
4757
5629
|
export type PositiveInt = typeof PositiveInt.Output;
|
|
4758
5630
|
|
|
4759
|
-
/**
|
|
4760
|
-
* 1-100 as a literal, or any already-validated {@link PositiveInt}.
|
|
4761
|
-
*
|
|
4762
|
-
* @group Number
|
|
4763
|
-
*/
|
|
4764
|
-
export type Int1To100OrPositiveInt = Int1To100 | PositiveInt;
|
|
4765
|
-
|
|
4766
5631
|
/**
|
|
4767
5632
|
* Minimum {@link PositiveInt} value.
|
|
4768
5633
|
*
|
|
@@ -4798,7 +5663,7 @@ export const NegativeInt = /*#__PURE__*/ negative(NonPositiveInt);
|
|
|
4798
5663
|
export type NegativeInt = typeof NegativeInt.Output;
|
|
4799
5664
|
|
|
4800
5665
|
/**
|
|
4801
|
-
*
|
|
5666
|
+
* Number {@link Brand} requiring a value greater than `min`.
|
|
4802
5667
|
*
|
|
4803
5668
|
* @group Number
|
|
4804
5669
|
*/
|
|
@@ -4830,7 +5695,7 @@ export interface GreaterThanError<
|
|
|
4830
5695
|
}
|
|
4831
5696
|
|
|
4832
5697
|
/**
|
|
4833
|
-
*
|
|
5698
|
+
* Number {@link Brand} requiring a value greater than or equal to `min`.
|
|
4834
5699
|
*
|
|
4835
5700
|
* @group Number
|
|
4836
5701
|
*/
|
|
@@ -4870,7 +5735,7 @@ export interface GreaterThanOrEqualToError<
|
|
|
4870
5735
|
}
|
|
4871
5736
|
|
|
4872
5737
|
/**
|
|
4873
|
-
*
|
|
5738
|
+
* Number {@link Brand} requiring a value less than `max`.
|
|
4874
5739
|
*
|
|
4875
5740
|
* @group Number
|
|
4876
5741
|
*/
|
|
@@ -4913,7 +5778,7 @@ export const Age = /*#__PURE__*/ brand(
|
|
|
4913
5778
|
export type Age = typeof Age.Output;
|
|
4914
5779
|
|
|
4915
5780
|
/**
|
|
4916
|
-
*
|
|
5781
|
+
* Number {@link Brand} requiring a value less than or equal to `max`.
|
|
4917
5782
|
*
|
|
4918
5783
|
* @group Number
|
|
4919
5784
|
*/
|
|
@@ -4962,104 +5827,245 @@ export const Ratio = /*#__PURE__*/ brand(
|
|
|
4962
5827
|
export type Ratio = typeof Ratio.Output;
|
|
4963
5828
|
|
|
4964
5829
|
/**
|
|
4965
|
-
* Canonical string representation of a
|
|
5830
|
+
* Canonical string representation of a signed base-10 decimal value.
|
|
5831
|
+
*
|
|
5832
|
+
* Use this Type when a decimal value must remain exact instead of being
|
|
5833
|
+
* converted to an IEEE-754 number. Equivalent values have one accepted
|
|
5834
|
+
* representation, so leading zeroes, trailing fractional zeroes, `-0`, plus
|
|
5835
|
+
* signs, and exponent notation are rejected.
|
|
5836
|
+
*
|
|
5837
|
+
* The decoded value remains a string. Arithmetic requires an explicit decimal
|
|
5838
|
+
* or fixed-point representation.
|
|
5839
|
+
*
|
|
5840
|
+
* TypeScript template literal types can describe a fixed number of digit
|
|
5841
|
+
* positions, but not the arbitrarily long integer and fractional parts accepted
|
|
5842
|
+
* here. `DecimalString` therefore uses a {@link Brand} so its TypeScript type
|
|
5843
|
+
* does not accept strings that have not been validated.
|
|
4966
5844
|
*
|
|
4967
|
-
*
|
|
4968
|
-
*
|
|
4969
|
-
*
|
|
5845
|
+
* Use these predefined Types or their corresponding factories to add sign
|
|
5846
|
+
* constraints to compatible decimal string Types:
|
|
5847
|
+
*
|
|
5848
|
+
* - {@link NonNegativeDecimalString} / {@link nonNegativeDecimalString}
|
|
5849
|
+
* - {@link PositiveDecimalString} / {@link positiveDecimalString}
|
|
5850
|
+
* - {@link NonPositiveDecimalString} / {@link nonPositiveDecimalString}
|
|
5851
|
+
* - {@link NegativeDecimalString} / {@link negativeDecimalString}
|
|
4970
5852
|
*
|
|
4971
5853
|
* ### Example
|
|
4972
5854
|
*
|
|
4973
5855
|
* ```ts
|
|
4974
|
-
* import {
|
|
5856
|
+
* import { DecimalString } from "@evolu/common";
|
|
4975
5857
|
*
|
|
4976
|
-
* expectOk(
|
|
4977
|
-
* expectOk(
|
|
5858
|
+
* expectOk(DecimalString.fromUnknown("-10.25"), "-10.25");
|
|
5859
|
+
* expectOk(DecimalString.fromUnknown("0"), "0");
|
|
5860
|
+
* expectOk(DecimalString.fromUnknown("10.25"), "10.25");
|
|
4978
5861
|
*
|
|
4979
|
-
* expectErr(
|
|
4980
|
-
* type: "
|
|
4981
|
-
* value: "
|
|
5862
|
+
* expectErr(DecimalString.fromUnknown("10.250"), {
|
|
5863
|
+
* type: "DecimalString",
|
|
5864
|
+
* value: "10.250",
|
|
4982
5865
|
* });
|
|
4983
5866
|
* ```
|
|
4984
5867
|
*
|
|
4985
5868
|
* @group Number
|
|
4986
5869
|
*/
|
|
4987
|
-
export const
|
|
4988
|
-
"
|
|
5870
|
+
export const DecimalString = /*#__PURE__*/ brand(
|
|
5871
|
+
"DecimalString",
|
|
4989
5872
|
String,
|
|
4990
5873
|
(value) =>
|
|
4991
|
-
/^(?:[1-9]\d*|(?:0|[1-9]\d*)\.\d*[1-9])$/.test(value)
|
|
5874
|
+
/^(?:0|-?(?:[1-9]\d*|(?:0|[1-9]\d*)\.\d*[1-9]))$/.test(value)
|
|
4992
5875
|
? ok()
|
|
4993
|
-
: err<
|
|
4994
|
-
type: "PositiveDecimalString",
|
|
4995
|
-
value,
|
|
4996
|
-
}),
|
|
5876
|
+
: err<DecimalStringError>({ type: "DecimalString", value }),
|
|
4997
5877
|
(error) =>
|
|
4998
|
-
`The value ${safelyStringifyUnknownValue(error.value)} must be a canonical
|
|
5878
|
+
`The value ${safelyStringifyUnknownValue(error.value)} must be a canonical decimal string.`,
|
|
4999
5879
|
);
|
|
5000
|
-
export type
|
|
5880
|
+
export type DecimalString = typeof DecimalString.Output;
|
|
5001
5881
|
|
|
5002
5882
|
/** @group Number */
|
|
5003
|
-
export interface
|
|
5883
|
+
export interface DecimalStringError extends TypeError<"DecimalString"> {
|
|
5004
5884
|
readonly value: string;
|
|
5005
5885
|
}
|
|
5006
5886
|
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5887
|
+
/**
|
|
5888
|
+
* {@link DecimalString} Brand requiring a value greater than or equal to zero.
|
|
5889
|
+
*
|
|
5890
|
+
* @group Number
|
|
5891
|
+
*/
|
|
5892
|
+
export const nonNegativeDecimalString: BrandFactory<
|
|
5893
|
+
"NonNegativeDecimalString",
|
|
5894
|
+
DecimalString,
|
|
5895
|
+
NonNegativeDecimalStringError
|
|
5896
|
+
> = (parent) =>
|
|
5897
|
+
brand(
|
|
5898
|
+
"NonNegativeDecimalString",
|
|
5899
|
+
parent,
|
|
5900
|
+
(value) =>
|
|
5901
|
+
value[0] !== "-"
|
|
5902
|
+
? ok()
|
|
5903
|
+
: err<NonNegativeDecimalStringError>({
|
|
5904
|
+
type: "NonNegativeDecimalString",
|
|
5905
|
+
value,
|
|
5906
|
+
}),
|
|
5907
|
+
(error) =>
|
|
5908
|
+
`The value ${safelyStringifyUnknownValue(error.value)} must be a non-negative decimal string.`,
|
|
5909
|
+
);
|
|
5015
5910
|
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
: false
|
|
5021
|
-
: IsCanonicalPositiveIntegerString<Value>;
|
|
5911
|
+
/** @group Number */
|
|
5912
|
+
export interface NonNegativeDecimalStringError extends TypeError<"NonNegativeDecimalString"> {
|
|
5913
|
+
readonly value: string;
|
|
5914
|
+
}
|
|
5022
5915
|
|
|
5023
|
-
|
|
5024
|
-
|
|
5916
|
+
/**
|
|
5917
|
+
* Non-negative {@link DecimalString}.
|
|
5918
|
+
*
|
|
5919
|
+
* @group Number
|
|
5920
|
+
*/
|
|
5921
|
+
export const NonNegativeDecimalString =
|
|
5922
|
+
/*#__PURE__*/ nonNegativeDecimalString(DecimalString);
|
|
5923
|
+
export type NonNegativeDecimalString = typeof NonNegativeDecimalString.Output;
|
|
5025
5924
|
|
|
5026
|
-
|
|
5027
|
-
|
|
5925
|
+
/**
|
|
5926
|
+
* {@link DecimalString} Brand requiring a value greater than zero.
|
|
5927
|
+
*
|
|
5928
|
+
* @group Number
|
|
5929
|
+
*/
|
|
5930
|
+
export const positiveDecimalString: BrandFactory<
|
|
5931
|
+
"PositiveDecimalString",
|
|
5932
|
+
DecimalString,
|
|
5933
|
+
PositiveDecimalStringError
|
|
5934
|
+
> = (parent) =>
|
|
5935
|
+
brand(
|
|
5936
|
+
"PositiveDecimalString",
|
|
5937
|
+
parent,
|
|
5938
|
+
(value) =>
|
|
5939
|
+
value !== "0" && value[0] !== "-"
|
|
5940
|
+
? ok()
|
|
5941
|
+
: err<PositiveDecimalStringError>({
|
|
5942
|
+
type: "PositiveDecimalString",
|
|
5943
|
+
value,
|
|
5944
|
+
}),
|
|
5945
|
+
(error) =>
|
|
5946
|
+
`The value ${safelyStringifyUnknownValue(error.value)} must be a positive decimal string.`,
|
|
5947
|
+
);
|
|
5028
5948
|
|
|
5029
|
-
|
|
5030
|
-
|
|
5949
|
+
/** @group Number */
|
|
5950
|
+
export interface PositiveDecimalStringError extends TypeError<"PositiveDecimalString"> {
|
|
5951
|
+
readonly value: string;
|
|
5952
|
+
}
|
|
5031
5953
|
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5954
|
+
/**
|
|
5955
|
+
* Positive {@link DecimalString}.
|
|
5956
|
+
*
|
|
5957
|
+
* Also satisfies {@link NonNegativeDecimalString}, so it can be used wherever a
|
|
5958
|
+
* non-negative decimal string is required.
|
|
5959
|
+
*
|
|
5960
|
+
* @group Number
|
|
5961
|
+
*/
|
|
5962
|
+
export const PositiveDecimalString = /*#__PURE__*/ positiveDecimalString(
|
|
5963
|
+
NonNegativeDecimalString,
|
|
5964
|
+
);
|
|
5965
|
+
export type PositiveDecimalString = typeof PositiveDecimalString.Output;
|
|
5037
5966
|
|
|
5038
|
-
|
|
5039
|
-
|
|
5967
|
+
/**
|
|
5968
|
+
* {@link DecimalString} Brand requiring a value less than or equal to zero.
|
|
5969
|
+
*
|
|
5970
|
+
* @group Number
|
|
5971
|
+
*/
|
|
5972
|
+
export const nonPositiveDecimalString: BrandFactory<
|
|
5973
|
+
"NonPositiveDecimalString",
|
|
5974
|
+
DecimalString,
|
|
5975
|
+
NonPositiveDecimalStringError
|
|
5976
|
+
> = (parent) =>
|
|
5977
|
+
brand(
|
|
5978
|
+
"NonPositiveDecimalString",
|
|
5979
|
+
parent,
|
|
5980
|
+
(value) =>
|
|
5981
|
+
value === "0" || value[0] === "-"
|
|
5982
|
+
? ok()
|
|
5983
|
+
: err<NonPositiveDecimalStringError>({
|
|
5984
|
+
type: "NonPositiveDecimalString",
|
|
5985
|
+
value,
|
|
5986
|
+
}),
|
|
5987
|
+
(error) =>
|
|
5988
|
+
`The value ${safelyStringifyUnknownValue(error.value)} must be a non-positive decimal string.`,
|
|
5989
|
+
);
|
|
5040
5990
|
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5991
|
+
/** @group Number */
|
|
5992
|
+
export interface NonPositiveDecimalStringError extends TypeError<"NonPositiveDecimalString"> {
|
|
5993
|
+
readonly value: string;
|
|
5994
|
+
}
|
|
5995
|
+
|
|
5996
|
+
/**
|
|
5997
|
+
* Non-positive {@link DecimalString}.
|
|
5998
|
+
*
|
|
5999
|
+
* @group Number
|
|
6000
|
+
*/
|
|
6001
|
+
export const NonPositiveDecimalString =
|
|
6002
|
+
/*#__PURE__*/ nonPositiveDecimalString(DecimalString);
|
|
6003
|
+
export type NonPositiveDecimalString = typeof NonPositiveDecimalString.Output;
|
|
6004
|
+
|
|
6005
|
+
/**
|
|
6006
|
+
* {@link DecimalString} Brand requiring a value less than zero.
|
|
6007
|
+
*
|
|
6008
|
+
* @group Number
|
|
6009
|
+
*/
|
|
6010
|
+
export const negativeDecimalString: BrandFactory<
|
|
6011
|
+
"NegativeDecimalString",
|
|
6012
|
+
DecimalString,
|
|
6013
|
+
NegativeDecimalStringError
|
|
6014
|
+
> = (parent) =>
|
|
6015
|
+
brand(
|
|
6016
|
+
"NegativeDecimalString",
|
|
6017
|
+
parent,
|
|
6018
|
+
(value) =>
|
|
6019
|
+
value[0] === "-"
|
|
6020
|
+
? ok()
|
|
6021
|
+
: err<NegativeDecimalStringError>({
|
|
6022
|
+
type: "NegativeDecimalString",
|
|
6023
|
+
value,
|
|
6024
|
+
}),
|
|
6025
|
+
(error) =>
|
|
6026
|
+
`The value ${safelyStringifyUnknownValue(error.value)} must be a negative decimal string.`,
|
|
6027
|
+
);
|
|
6028
|
+
|
|
6029
|
+
/** @group Number */
|
|
6030
|
+
export interface NegativeDecimalStringError extends TypeError<"NegativeDecimalString"> {
|
|
6031
|
+
readonly value: string;
|
|
6032
|
+
}
|
|
5045
6033
|
|
|
5046
6034
|
/**
|
|
5047
|
-
*
|
|
5048
|
-
* `divisor`.
|
|
6035
|
+
* Negative {@link DecimalString}.
|
|
5049
6036
|
*
|
|
5050
|
-
*
|
|
5051
|
-
*
|
|
5052
|
-
*
|
|
5053
|
-
*
|
|
5054
|
-
|
|
6037
|
+
* Also satisfies {@link NonPositiveDecimalString}, so it can be used wherever a
|
|
6038
|
+
* non-positive decimal string is required.
|
|
6039
|
+
*
|
|
6040
|
+
* @group Number
|
|
6041
|
+
*/
|
|
6042
|
+
export const NegativeDecimalString = /*#__PURE__*/ negativeDecimalString(
|
|
6043
|
+
NonPositiveDecimalString,
|
|
6044
|
+
);
|
|
6045
|
+
export type NegativeDecimalString = typeof NegativeDecimalString.Output;
|
|
6046
|
+
|
|
6047
|
+
/**
|
|
6048
|
+
* Number {@link Brand} requiring an exact decimal multiple of `divisor`.
|
|
6049
|
+
*
|
|
6050
|
+
* The divisor must be one canonical positive decimal string literal because its
|
|
6051
|
+
* exact value is encoded in the resulting Brand name. The declaration is
|
|
6052
|
+
* validated at compile time and is never converted to an IEEE-754 number.
|
|
6053
|
+
*
|
|
6054
|
+
* Validation treats each finite Number as its canonical decimal representation
|
|
6055
|
+
* and checks exact base-10 divisibility. It does not use floating-point
|
|
6056
|
+
* remainder, apply a tolerance, or round to the divisor's precision.
|
|
5055
6057
|
*
|
|
5056
6058
|
* ### Example
|
|
5057
6059
|
*
|
|
5058
6060
|
* ```ts
|
|
5059
|
-
* import { FiniteNumber, multipleOf } from "@evolu/common";
|
|
6061
|
+
* import { FiniteNumber, multipleOf, type Brand } from "@evolu/common";
|
|
5060
6062
|
*
|
|
5061
6063
|
* const Tenths = multipleOf("0.1")(FiniteNumber);
|
|
5062
6064
|
*
|
|
6065
|
+
* expectTypeOf<typeof Tenths.Output>().toEqualTypeOf<
|
|
6066
|
+
* FiniteNumber & Brand<"MultipleOf0.1">
|
|
6067
|
+
* >();
|
|
6068
|
+
*
|
|
5063
6069
|
* expectOk(Tenths.fromUnknown(0.3), 0.3);
|
|
5064
6070
|
* expectErr(Tenths.fromUnknown(0.31), {
|
|
5065
6071
|
* type: "MultipleOf0.1",
|
|
@@ -5073,11 +6079,6 @@ type MultipleOfDivisorError = CompileTimeError<
|
|
|
5073
6079
|
export const multipleOf = <const Divisor extends string>(
|
|
5074
6080
|
divisor: ValidateMultipleOfDivisor<Divisor>,
|
|
5075
6081
|
): BrandFactory<`MultipleOf${Divisor}`, number, MultipleOfError<Divisor>> => {
|
|
5076
|
-
// TODO: Define the positive-decimal literal domain with templateLiteral so
|
|
5077
|
-
// its TypeScript type is inferred from runtime Type code, then require that
|
|
5078
|
-
// literal Type here instead of accepting a string guarded by assertType.
|
|
5079
|
-
assertType(PositiveDecimalString, divisor);
|
|
5080
|
-
|
|
5081
6082
|
const name = `MultipleOf${divisor}` as `MultipleOf${Divisor}`;
|
|
5082
6083
|
const decimalDivisor = decimalStringToParts(divisor);
|
|
5083
6084
|
|
|
@@ -5090,22 +6091,18 @@ export const multipleOf = <const Divisor extends string>(
|
|
|
5090
6091
|
|
|
5091
6092
|
if (globalThis.Number.isFinite(value)) {
|
|
5092
6093
|
const decimalValue = decimalStringToParts(value.toString());
|
|
5093
|
-
const
|
|
5094
|
-
decimalValue.exponent
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
(decimalDivisor.coefficient *
|
|
5106
|
-
10n ** globalThis.BigInt(-exponentDifference)) ===
|
|
5107
|
-
0n;
|
|
5108
|
-
}
|
|
6094
|
+
const exponent = Math.min(
|
|
6095
|
+
decimalValue.exponent,
|
|
6096
|
+
decimalDivisor.exponent,
|
|
6097
|
+
);
|
|
6098
|
+
const valueCoefficient =
|
|
6099
|
+
decimalValue.coefficient *
|
|
6100
|
+
10n ** globalThis.BigInt(decimalValue.exponent - exponent);
|
|
6101
|
+
const divisorCoefficient =
|
|
6102
|
+
decimalDivisor.coefficient *
|
|
6103
|
+
10n ** globalThis.BigInt(decimalDivisor.exponent - exponent);
|
|
6104
|
+
|
|
6105
|
+
isMultiple = valueCoefficient % divisorCoefficient === 0n;
|
|
5109
6106
|
}
|
|
5110
6107
|
|
|
5111
6108
|
return isMultiple
|
|
@@ -5129,6 +6126,45 @@ export interface MultipleOfError<
|
|
|
5129
6126
|
readonly divisor: Divisor;
|
|
5130
6127
|
}
|
|
5131
6128
|
|
|
6129
|
+
type ValidateMultipleOfDivisor<Divisor extends string> =
|
|
6130
|
+
IsUnion<Divisor> extends false
|
|
6131
|
+
? string extends Divisor
|
|
6132
|
+
? InvalidMultipleOfDivisor<Divisor>
|
|
6133
|
+
: IsCanonicalPositiveDecimalString<Divisor> extends true
|
|
6134
|
+
? Divisor
|
|
6135
|
+
: InvalidMultipleOfDivisor<Divisor>
|
|
6136
|
+
: InvalidMultipleOfDivisor<Divisor>;
|
|
6137
|
+
|
|
6138
|
+
type IsCanonicalPositiveDecimalString<Value extends string> =
|
|
6139
|
+
Value extends `${infer Integer}.${infer Fraction}`
|
|
6140
|
+
? IsCanonicalNonNegativeIntegerString<Integer> extends true
|
|
6141
|
+
? IsCanonicalPositiveFractionString<Fraction>
|
|
6142
|
+
: false
|
|
6143
|
+
: IsCanonicalPositiveIntegerString<Value>;
|
|
6144
|
+
|
|
6145
|
+
type IsCanonicalNonNegativeIntegerString<Value extends string> =
|
|
6146
|
+
Value extends "0" ? true : IsCanonicalPositiveIntegerString<Value>;
|
|
6147
|
+
|
|
6148
|
+
type IsCanonicalPositiveIntegerString<Value extends string> =
|
|
6149
|
+
Value extends `${Digit1To9}${infer Rest}` ? IsDecimalDigits<Rest> : false;
|
|
6150
|
+
|
|
6151
|
+
type IsCanonicalPositiveFractionString<Value extends string> =
|
|
6152
|
+
Value extends `${infer Rest}${Digit1To9}` ? IsDecimalDigits<Rest> : false;
|
|
6153
|
+
|
|
6154
|
+
type IsDecimalDigits<Value extends string> = Value extends ""
|
|
6155
|
+
? true
|
|
6156
|
+
: Value extends `${Digit}${infer Rest}`
|
|
6157
|
+
? IsDecimalDigits<Rest>
|
|
6158
|
+
: false;
|
|
6159
|
+
|
|
6160
|
+
type InvalidMultipleOfDivisor<Divisor extends string> = Divisor &
|
|
6161
|
+
Readonly<Record<MultipleOfDivisorError, never>>;
|
|
6162
|
+
|
|
6163
|
+
type MultipleOfDivisorError = CompileTimeError<
|
|
6164
|
+
"MultipleOf",
|
|
6165
|
+
'Divisor must be one canonical positive decimal string literal such as "0.1".'
|
|
6166
|
+
>;
|
|
6167
|
+
|
|
5132
6168
|
interface DecimalParts {
|
|
5133
6169
|
readonly coefficient: bigint;
|
|
5134
6170
|
readonly exponent: number;
|
|
@@ -5159,7 +6195,7 @@ const decimalStringToParts = (value: string): DecimalParts => {
|
|
|
5159
6195
|
};
|
|
5160
6196
|
|
|
5161
6197
|
/**
|
|
5162
|
-
*
|
|
6198
|
+
* Number {@link Brand} requiring a value within an inclusive range.
|
|
5163
6199
|
*
|
|
5164
6200
|
* @group Number
|
|
5165
6201
|
*/
|
|
@@ -6449,6 +7485,106 @@ const validateTupleItems = (
|
|
|
6449
7485
|
checkStructure,
|
|
6450
7486
|
);
|
|
6451
7487
|
|
|
7488
|
+
/**
|
|
7489
|
+
* Decimal digit from `"0"` to `"9"`.
|
|
7490
|
+
*
|
|
7491
|
+
* @group String
|
|
7492
|
+
*/
|
|
7493
|
+
export const Digit = /*#__PURE__*/ union(
|
|
7494
|
+
"0",
|
|
7495
|
+
"1",
|
|
7496
|
+
"2",
|
|
7497
|
+
"3",
|
|
7498
|
+
"4",
|
|
7499
|
+
"5",
|
|
7500
|
+
"6",
|
|
7501
|
+
"7",
|
|
7502
|
+
"8",
|
|
7503
|
+
"9",
|
|
7504
|
+
);
|
|
7505
|
+
export type Digit = typeof Digit.Output;
|
|
7506
|
+
|
|
7507
|
+
/**
|
|
7508
|
+
* Decimal digit from `"1"` to `"9"`.
|
|
7509
|
+
*
|
|
7510
|
+
* @group String
|
|
7511
|
+
*/
|
|
7512
|
+
export const Digit1To9 = /*#__PURE__*/ union(
|
|
7513
|
+
"1",
|
|
7514
|
+
"2",
|
|
7515
|
+
"3",
|
|
7516
|
+
"4",
|
|
7517
|
+
"5",
|
|
7518
|
+
"6",
|
|
7519
|
+
"7",
|
|
7520
|
+
"8",
|
|
7521
|
+
"9",
|
|
7522
|
+
);
|
|
7523
|
+
export type Digit1To9 = typeof Digit1To9.Output;
|
|
7524
|
+
|
|
7525
|
+
/**
|
|
7526
|
+
* Decimal string from `"1"` to `"6"`.
|
|
7527
|
+
*
|
|
7528
|
+
* @group String
|
|
7529
|
+
*/
|
|
7530
|
+
export const Digit1To6 = /*#__PURE__*/ union("1", "2", "3", "4", "5", "6");
|
|
7531
|
+
export type Digit1To6 = typeof Digit1To6.Output;
|
|
7532
|
+
|
|
7533
|
+
/**
|
|
7534
|
+
* Decimal string from `"1"` to `"23"`.
|
|
7535
|
+
*
|
|
7536
|
+
* @group String
|
|
7537
|
+
*/
|
|
7538
|
+
export const Digit1To23 = /*#__PURE__*/ union(
|
|
7539
|
+
Digit1To9,
|
|
7540
|
+
/*#__PURE__*/ templateLiteral("1", Digit),
|
|
7541
|
+
/*#__PURE__*/ templateLiteral(
|
|
7542
|
+
"2",
|
|
7543
|
+
/*#__PURE__*/ union("0", "1", "2", "3"),
|
|
7544
|
+
),
|
|
7545
|
+
);
|
|
7546
|
+
export type Digit1To23 = typeof Digit1To23.Output;
|
|
7547
|
+
|
|
7548
|
+
/**
|
|
7549
|
+
* Decimal string from `"1"` to `"51"`.
|
|
7550
|
+
*
|
|
7551
|
+
* @group String
|
|
7552
|
+
*/
|
|
7553
|
+
export const Digit1To51 = /*#__PURE__*/ union(
|
|
7554
|
+
Digit1To9,
|
|
7555
|
+
/*#__PURE__*/ templateLiteral(
|
|
7556
|
+
/*#__PURE__*/ union("1", "2", "3", "4"),
|
|
7557
|
+
Digit,
|
|
7558
|
+
),
|
|
7559
|
+
/*#__PURE__*/ templateLiteral("5", /*#__PURE__*/ union("0", "1")),
|
|
7560
|
+
);
|
|
7561
|
+
export type Digit1To51 = typeof Digit1To51.Output;
|
|
7562
|
+
|
|
7563
|
+
/**
|
|
7564
|
+
* Decimal string from `"1"` to `"99"`.
|
|
7565
|
+
*
|
|
7566
|
+
* @group String
|
|
7567
|
+
*/
|
|
7568
|
+
export const Digit1To99 = /*#__PURE__*/ union(
|
|
7569
|
+
Digit1To9,
|
|
7570
|
+
/*#__PURE__*/ templateLiteral(Digit1To9, Digit),
|
|
7571
|
+
);
|
|
7572
|
+
export type Digit1To99 = typeof Digit1To99.Output;
|
|
7573
|
+
|
|
7574
|
+
/**
|
|
7575
|
+
* Decimal string from `"1"` to `"59"`.
|
|
7576
|
+
*
|
|
7577
|
+
* @group String
|
|
7578
|
+
*/
|
|
7579
|
+
export const Digit1To59 = /*#__PURE__*/ union(
|
|
7580
|
+
Digit1To9,
|
|
7581
|
+
/*#__PURE__*/ templateLiteral(
|
|
7582
|
+
/*#__PURE__*/ union("1", "2", "3", "4", "5"),
|
|
7583
|
+
Digit,
|
|
7584
|
+
),
|
|
7585
|
+
);
|
|
7586
|
+
export type Digit1To59 = typeof Digit1To59.Output;
|
|
7587
|
+
|
|
6452
7588
|
const createObjectRuntimeTypeIssues =
|
|
6453
7589
|
(
|
|
6454
7590
|
defaultFormatter: TypeErrorFormatter<TypeError>,
|
|
@@ -8473,7 +9609,7 @@ const createRecordPropertyError = <Error extends TypeError>(
|
|
|
8473
9609
|
});
|
|
8474
9610
|
|
|
8475
9611
|
/**
|
|
8476
|
-
*
|
|
9612
|
+
* Object {@link Type} with every property optional.
|
|
8477
9613
|
*
|
|
8478
9614
|
* No property is required, but every present property must still satisfy its
|
|
8479
9615
|
* Type.
|
|
@@ -8523,7 +9659,8 @@ export type PartialObjectProps<Props extends ObjectProps> = {
|
|
|
8523
9659
|
};
|
|
8524
9660
|
|
|
8525
9661
|
/**
|
|
8526
|
-
*
|
|
9662
|
+
* Object {@link Type} making every property whose Union Type includes
|
|
9663
|
+
* {@link Null} optional.
|
|
8527
9664
|
*
|
|
8528
9665
|
* The property retains its original Union Type, so consumers may omit it, set
|
|
8529
9666
|
* it to `null`, or provide any other member of that Union. Properties without
|
|
@@ -8594,7 +9731,7 @@ export type NullableToOptionalProps<Props extends ObjectProps> = {
|
|
|
8594
9731
|
};
|
|
8595
9732
|
|
|
8596
9733
|
/**
|
|
8597
|
-
*
|
|
9734
|
+
* Object {@link Type} without the selected declared properties.
|
|
8598
9735
|
*
|
|
8599
9736
|
* @group Objects
|
|
8600
9737
|
*/
|
|
@@ -8648,7 +9785,7 @@ type OmitKeyConcreteTypeError = CompileTimeError<
|
|
|
8648
9785
|
>;
|
|
8649
9786
|
|
|
8650
9787
|
/**
|
|
8651
|
-
*
|
|
9788
|
+
* {@link Result} {@link Type} for typed success and error values.
|
|
8652
9789
|
*
|
|
8653
9790
|
* Use this to validate Results crossing a storage, worker, API, or other
|
|
8654
9791
|
* serialization boundary. The operation returns an outer validation Result. Its
|
|
@@ -8942,7 +10079,7 @@ type TypedTypePropertyError = CompileTimeError<
|
|
|
8942
10079
|
>;
|
|
8943
10080
|
|
|
8944
10081
|
/**
|
|
8945
|
-
*
|
|
10082
|
+
* Producer-result {@link Type} for value, error, or done outcomes.
|
|
8946
10083
|
*
|
|
8947
10084
|
* The three outcomes are `Ok<Value>`, `Err<Error>`, and `Err<Typed<"Done"> & {
|
|
8948
10085
|
* done: Done }>`. This keeps normal completion distinct from failure while
|
|
@@ -10577,7 +11714,7 @@ export const JsonValueFromJson = /*#__PURE__*/ transform(
|
|
|
10577
11714
|
);
|
|
10578
11715
|
|
|
10579
11716
|
/**
|
|
10580
|
-
*
|
|
11717
|
+
* Branded {@link Json} Type and total conversions for another Type.
|
|
10581
11718
|
*
|
|
10582
11719
|
* Use this factory when a domain value must be stored as JSON text while its
|
|
10583
11720
|
* exact Type remains visible to TypeScript, such as a JSON column in an Evolu
|