@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.
@@ -10,7 +10,7 @@ import type { RandomBytesDep } from "./Crypto.ts";
10
10
  import { type Thunk } from "./Function.ts";
11
11
  import { type Result } from "./Result.ts";
12
12
  import type { TimeDep } from "./Time.ts";
13
- import { type CompileTimeError, type Digit, type Digit1To9, type Int1To100, type IsUnion, type Literal, type Simplify, type ValueWithLength, type WidenLiteral } from "./Types.ts";
13
+ import { type CompileTimeError, type IsUnion, type Literal, type Simplify, type ValueWithLength, type WidenLiteral } from "./Types.ts";
14
14
  /**
15
15
  * A runtime representation of a TypeScript type with typed structured errors.
16
16
  *
@@ -108,8 +108,8 @@ import { type CompileTimeError, type Digit, type Digit1To9, type Int1To100, type
108
108
  * normalized value, it uses a `null` prototype so every string key remains
109
109
  * ordinary data.
110
110
  *
111
- * Evolu Type expects TypeScript's `exactOptionalPropertyTypes` compiler option
112
- * to be enabled.
111
+ * Evolu Type requires TypeScript 7 or newer and expects the
112
+ * `exactOptionalPropertyTypes` compiler option to be enabled.
113
113
  *
114
114
  * Predefined Types intentionally use the names of corresponding JavaScript
115
115
  * built-ins because they represent those familiar value categories. If an
@@ -625,7 +625,7 @@ export interface TypeNode {
625
625
  */
626
626
  export declare const assertType: <T extends TypeNode>(type: T, value: unknown) => asserts value is T["Output"];
627
627
  /**
628
- * Creates localized copies of selected {@link Type | Types} for every locale.
628
+ * Localized copies of selected {@link Type} declarations for every locale.
629
629
  *
630
630
  * Each locale supplies one formatter for every Type that can own a formatted
631
631
  * error. Structural Types use their own formatter for structural failures and
@@ -988,6 +988,8 @@ type DeepestFromError<Operation> = DeepestFromOperation<Operation> extends (...a
988
988
  readonly error: infer Error extends TypeError;
989
989
  } ? Error : never : never;
990
990
  type TypeFromError<T extends TypeNode> = unknown extends T[typeof customFromSymbol] ? DeepestFromError<TypeFromOperation<T>> : [T[typeof customFromSymbol]] extends [never] ? NonRootErrors<T> : DeepestFromError<T[typeof customFromSymbol]>;
991
+ declare const templateLiteralSyntaxSymbolType: unique symbol;
992
+ declare const templateLiteralSyntaxSymbol: typeof templateLiteralSyntaxSymbolType;
991
993
  /** @group Base Types */
992
994
  export declare const Unknown: Type<"Unknown", unknown, unknown, never, null, never, never, unknown, true>;
993
995
  /** @group Base Types */
@@ -1178,7 +1180,7 @@ interface ObjectTagOutputByName {
1178
1180
  readonly ArrayBuffer: globalThis.ArrayBuffer;
1179
1181
  }
1180
1182
  /**
1181
- * Creates a realm-neutral {@link Type} that trusts an object's reported tag.
1183
+ * Realm-neutral {@link Type} trusting an object's reported tag.
1182
1184
  *
1183
1185
  * Predefined built-in tags expose their native Output type under the assumption
1184
1186
  * that trusted code does not forge their tags. They do not verify native
@@ -1227,7 +1229,7 @@ export declare const Uint8Array: Type<"Uint8Array", Uint8Array<ArrayBufferLike>,
1227
1229
  */
1228
1230
  export declare const ArrayBuffer: Type<"ArrayBuffer", ArrayBuffer, ArrayBuffer, ObjectTagError<"ArrayBuffer">, null, ObjectTagError<"ArrayBuffer">, never, ArrayBuffer, true>;
1229
1231
  /**
1230
- * Creates a {@link Type} for instances of one constructor.
1232
+ * Instance {@link Type} for one constructor.
1231
1233
  *
1232
1234
  * Membership uses the intrinsic prototype chain, so subclasses are accepted,
1233
1235
  * equivalent constructors from other realms are rejected, and custom
@@ -1286,11 +1288,32 @@ type InstanceConstructorCompileTimeError = CompileTimeError<"Type", "Constructor
1286
1288
  * primitive through `from.parent`. The expected value must have one exact
1287
1289
  * literal type. Validation uses `===`, so `-0` matches `0`.
1288
1290
  *
1291
+ * In {@link templateLiteralParser}, use a string Literal Type when the literal
1292
+ * should be decoded into the Output Tuple. Use a raw string when it should only
1293
+ * frame the canonical string.
1294
+ *
1295
+ * ### Example
1296
+ *
1297
+ * ```ts
1298
+ * import { literal } from "@evolu/common";
1299
+ *
1300
+ * const Ready = literal("ready");
1301
+ *
1302
+ * expectTypeOf<typeof Ready.Output>().toEqualTypeOf<"ready">();
1303
+ * expectOk(Ready.fromUnknown("ready"), "ready");
1304
+ * expectErr(Ready.fromUnknown("pending"), {
1305
+ * type: "Literal",
1306
+ * expected: "ready",
1307
+ * value: "pending",
1308
+ * });
1309
+ * ```
1310
+ *
1289
1311
  * @group Unions
1290
1312
  */
1291
1313
  export declare const literal: <const Expected extends Literal>(expected: ValidateLiteral<Expected>) => LiteralType<Expected>;
1292
1314
  /** @group Unions */
1293
1315
  export interface LiteralType<Expected extends Literal> extends Type<"Literal", WidenLiteral<Expected>, Expected, LiteralError<Expected>, LiteralParent<Expected>, LiteralError<Expected> | LiteralParentErrors<Expected>, never, CanonicalInputSubset<WidenLiteral<Expected>, CanonicalInputForParent<LiteralParent<Expected>, Expected>>, IdentityEncodingForParent<LiteralParent<Expected>>> {
1316
+ readonly [templateLiteralSyntaxSymbol]: true;
1294
1317
  readonly expected: Expected;
1295
1318
  }
1296
1319
  type LiteralParent<Expected extends Literal> = Expected extends string ? typeof String : Expected extends number ? typeof Number : Expected extends bigint ? typeof BigInt : Expected extends boolean ? typeof Boolean : null;
@@ -1373,7 +1396,7 @@ export declare function union<const Members extends AtLeastTwoReadonlyArray<Type
1373
1396
  readonly [Index in keyof Members]: ValidateUnionMember<Members[Index]>;
1374
1397
  }): UnionType<NormalizeUnionMembers<Members>>;
1375
1398
  /**
1376
- * Shorthand for passing a {@link Type} and `undefined` to {@link union}.
1399
+ * Union {@link Type} containing the supplied Type and `undefined`.
1377
1400
  *
1378
1401
  * This does not make an object property optional. It changes only the values
1379
1402
  * accepted when the property is present.
@@ -1382,13 +1405,13 @@ export declare function union<const Members extends AtLeastTwoReadonlyArray<Type
1382
1405
  */
1383
1406
  export declare const undefinedOr: <ValueType extends TypeNode>(type: ValidateUnionTypeMember<ValueType>) => UnionType<readonly [ValueType, typeof Undefined]>;
1384
1407
  /**
1385
- * Shorthand for passing a {@link Type} and `null` to {@link union}.
1408
+ * Union {@link Type} containing the supplied Type and `null`.
1386
1409
  *
1387
1410
  * @group Unions
1388
1411
  */
1389
1412
  export declare const nullOr: <ValueType extends TypeNode>(type: ValidateUnionTypeMember<ValueType>) => UnionType<readonly [ValueType, typeof Null]>;
1390
1413
  /**
1391
- * Shorthand for passing a {@link Type}, `null`, and `undefined` to {@link union}.
1414
+ * Union {@link Type} containing the supplied Type, `null`, and `undefined`.
1392
1415
  *
1393
1416
  * @group Unions
1394
1417
  */
@@ -1403,6 +1426,7 @@ type NormalizeUnionMembers<Members extends AtLeastTwoReadonlyArray<TypeNode | Li
1403
1426
  } extends infer Normalized extends AtLeastTwoReadonlyArray<TypeNode> ? Normalized : never;
1404
1427
  /** @group Unions */
1405
1428
  export interface UnionType<Members extends AtLeastTwoReadonlyArray<TypeNode>> extends Type<"Union", Members[number]["Input"], Members[number]["Output"], UnionTypeError<Members>, UnionInputParent<Members>, UnionTypeError<Members>, never, CanonicalInputOf<Members[number]>, AllTypesUseIdentityEncoding<Members[number]>> {
1429
+ readonly [templateLiteralSyntaxSymbol]: true;
1406
1430
  readonly [reflectedTypesSymbol]?: Members[number];
1407
1431
  readonly members: Members;
1408
1432
  }
@@ -1447,6 +1471,256 @@ type InfallibleTypeNode = TypeNode & {
1447
1471
  interface UnionErrorValue<Error extends TypeError, MemberError extends UnionMemberError<Error> = UnionMemberError<Error>> extends TypeError<"Union"> {
1448
1472
  readonly errors: NonEmptyReadonlyArray<MemberError>;
1449
1473
  }
1474
+ /**
1475
+ * Template literal {@link Type} for validation and parsing.
1476
+ *
1477
+ * Parses and creates structured strings.
1478
+ *
1479
+ * Accepts the same template parts as {@link templateLiteral}: fixed string
1480
+ * literals and Types canonically encoded as strings. Instead of keeping Output
1481
+ * as a string, fixed literals define the framing and Output is a readonly Tuple
1482
+ * of the decoded Type parts. `to` encodes that Tuple back into the canonical
1483
+ * string represented by the parent Type. At least one Type part is required.
1484
+ *
1485
+ * When every capture uses identity encoding, the parent Output is the exact
1486
+ * TypeScript template literal type. A transforming capture makes it nominal;
1487
+ * create such strings with `to` or validate them with the parent Type.
1488
+ *
1489
+ * Deterministic framing is a core correctness guarantee. It preserves
1490
+ * reversibility and keeps capture boundaries unambiguous. Different capture
1491
+ * Tuples must never encode to the same string. The parser provides predictable
1492
+ * parsing without pathological backtracking and decodes each capture once, so
1493
+ * adversarial input cannot trigger exponential parser work. Fixed-width captures
1494
+ * may be adjacent, but only one variable-width capture is allowed. Declarations
1495
+ * that could join UTF-16 surrogate halves across parts are rejected during
1496
+ * construction.
1497
+ *
1498
+ * Keep capture unions reasonably small to avoid excessive compiler work.
1499
+ *
1500
+ * TypeScript template literal types can describe a fixed number of digit
1501
+ * positions, but not an arbitrarily long sequence of digits. Such grammars use
1502
+ * branded Types such as {@link DecimalString}; `templateLiteralParser` preserves
1503
+ * that exactness by requiring a validated branded capture when encoding.
1504
+ *
1505
+ * ### Example
1506
+ *
1507
+ * A template literal Type defines both a canonical string representation and
1508
+ * the structured data decoded from it:
1509
+ *
1510
+ * ```ts
1511
+ * import { templateLiteralParser, union } from "@evolu/common";
1512
+ *
1513
+ * const Language = union("en", "cs");
1514
+ * const Region = union("US", "CZ");
1515
+ *
1516
+ * // Define a Type for "en-US" | "en-CZ" | "cs-US" | "cs-CZ".
1517
+ * const SupportedLocale = templateLiteralParser(Language, "-", Region);
1518
+ *
1519
+ * // Output is the decoded language and region.
1520
+ * type SupportedLocale = typeof SupportedLocale.Output;
1521
+ * expectTypeOf<SupportedLocale>().toEqualTypeOf<
1522
+ * readonly ["en" | "cs", "US" | "CZ"]
1523
+ * >();
1524
+ *
1525
+ * // The parent Output is the canonical locale string.
1526
+ * type SupportedLocaleLiteral = typeof SupportedLocale.parent.Output;
1527
+ * expectTypeOf<SupportedLocaleLiteral>().toEqualTypeOf<
1528
+ * "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
1529
+ * >();
1530
+ *
1531
+ * // Parse an unknown string into structured data.
1532
+ * const result = SupportedLocale.fromUnknown("cs-CZ");
1533
+ * assert(result.ok);
1534
+ * const locale = result.value;
1535
+ * expectTypeOf(locale).toEqualTypeOf<SupportedLocale>();
1536
+ * expect(locale).toEqual(["cs", "CZ"]);
1537
+ * expectErr(SupportedLocale.fromUnknown("cs/CZ"), {
1538
+ * type: "TemplateLiteral",
1539
+ * value: "cs/CZ",
1540
+ * });
1541
+ *
1542
+ * // Encode structured data into its canonical string.
1543
+ * const localeLiteral = SupportedLocale.to(locale);
1544
+ * expectTypeOf(localeLiteral).toEqualTypeOf<SupportedLocaleLiteral>();
1545
+ * expect(localeLiteral).toBe("cs-CZ");
1546
+ *
1547
+ * // Validate a string configuration value.
1548
+ * const configValue: unknown = "cs-CZ";
1549
+ * assert(SupportedLocale.parent.is(configValue));
1550
+ * expectTypeOf(configValue).toEqualTypeOf<SupportedLocaleLiteral>();
1551
+ * expect(SupportedLocale.parent.is("fr-CZ")).toBe(false);
1552
+ * ```
1553
+ *
1554
+ * `SupportedLocale` is structured data for application code.
1555
+ * `SupportedLocaleLiteral` is its canonical representation for configuration
1556
+ * and other APIs that require a string, such as URL parameters, environment
1557
+ * variables, and storage keys.
1558
+ *
1559
+ * Use branded captures for strings that TypeScript template literal types
1560
+ * cannot express exactly, such as arbitrary-length canonical decimals:
1561
+ *
1562
+ * ```ts
1563
+ * import {
1564
+ * NonNegativeDecimalString,
1565
+ * templateLiteralParser,
1566
+ * } from "@evolu/common";
1567
+ *
1568
+ * const DecimalText = templateLiteralParser(
1569
+ * "decimal:",
1570
+ * NonNegativeDecimalString,
1571
+ * );
1572
+ *
1573
+ * // DecimalText.to requires a validated NonNegativeDecimalString.
1574
+ * const zero = NonNegativeDecimalString.orThrow("0");
1575
+ *
1576
+ * expectOk(DecimalText.fromUnknown("decimal:0"), [zero]);
1577
+ * expect(DecimalText.to([zero])).toBe("decimal:0");
1578
+ * ```
1579
+ *
1580
+ * Capture Types (the Type arguments passed to `templateLiteralParser`) can use
1581
+ * transformations to decode substrings into non-string data:
1582
+ *
1583
+ * ```ts
1584
+ * import {
1585
+ * Int64FromInt64String,
1586
+ * templateLiteralParser,
1587
+ * } from "@evolu/common";
1588
+ *
1589
+ * const ItemId = templateLiteralParser("item-", Int64FromInt64String);
1590
+ * type ItemId = typeof ItemId.Output;
1591
+ * type ItemIdLiteral = typeof ItemId.parent.Output;
1592
+ *
1593
+ * // Decode the string into structured data.
1594
+ * const result = ItemId.fromUnknown("item-42");
1595
+ * assert(result.ok);
1596
+ * const itemId = result.value;
1597
+ * expectTypeOf(itemId).toEqualTypeOf<ItemId>();
1598
+ * expect(itemId).toEqual([42n]);
1599
+ *
1600
+ * // Encode the structured data into its canonical string.
1601
+ * const itemIdLiteral = ItemId.to(itemId);
1602
+ * expectTypeOf(itemIdLiteral).toEqualTypeOf<ItemIdLiteral>();
1603
+ * expect(itemIdLiteral).toBe("item-42");
1604
+ *
1605
+ * // TypeScript cannot prove from the literal alone that "42" is a valid Int64 encoding.
1606
+ * // @ts-expect-error Validate it with ItemId.parent or create it with ItemId.to.
1607
+ * const invalidItemIdLiteral: ItemIdLiteral = "item-42";
1608
+ * ```
1609
+ *
1610
+ * Fixed-width captures can be adjacent:
1611
+ *
1612
+ * ```ts
1613
+ * import { templateLiteralParser, union } from "@evolu/common";
1614
+ *
1615
+ * const Digit = union("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
1616
+ * const TwoDigits = templateLiteralParser(Digit, Digit);
1617
+ * type TwoDigits = typeof TwoDigits.Output;
1618
+ * type TwoDigitsLiteral = typeof TwoDigits.parent.Output;
1619
+ *
1620
+ * const twoDigits: TwoDigits = ["4", "2"];
1621
+ * const twoDigitsLiteral: TwoDigitsLiteral = "42";
1622
+ * // @ts-expect-error TwoDigitsLiteral requires exactly two digits.
1623
+ * const threeDigitsLiteral: TwoDigitsLiteral = "123";
1624
+ *
1625
+ * expectOk(TwoDigits.from.parent(twoDigitsLiteral), twoDigits);
1626
+ * expect(TwoDigits.to(twoDigits)).toBe(twoDigitsLiteral);
1627
+ * ```
1628
+ *
1629
+ * TypeScript rejects multiple variable-width captures because their encoded
1630
+ * boundaries would be ambiguous:
1631
+ *
1632
+ * ```ts
1633
+ * import { String, templateLiteralParser } from "@evolu/common";
1634
+ *
1635
+ * // @ts-expect-error At most one Type capture can have a variable-width string representation.
1636
+ * templateLiteralParser(String, ":", String);
1637
+ * ```
1638
+ *
1639
+ * This restriction keeps encoding reversible: different capture Tuples must
1640
+ * never produce the same string. A delimiter alone is not enough because it can
1641
+ * also occur inside a capture. Some formats could provide stronger guarantees,
1642
+ * such as captures that exclude a delimiter; support for those can be added
1643
+ * when concrete use cases justify the additional framing rules.
1644
+ *
1645
+ * @group Template literals
1646
+ */
1647
+ export declare const templateLiteralParser: <const Parts extends TemplateLiteralParts>(...parts: { readonly [Index in keyof Parts]: ValidateTemplateLiteralPart<Parts[Index]>; } & TemplateLiteralValidation<Parts>) => TemplateLiteralParserType<Parts>;
1648
+ /** @group Template literals */
1649
+ export interface TemplateLiteralParserType<Parts extends TemplateLiteralParts> extends Type<"TemplateLiteral", string, TemplateLiteralCaptureTuple<Parts>["Output"], never, TemplateLiteralType<Parts>, InferErrors<TemplateLiteralType<Parts>>, never, TemplateLiteralStringOutput<Parts>, false> {
1650
+ readonly [templateLiteralSyntaxSymbol]: true;
1651
+ readonly [reflectedTypesSymbol]?: TemplateLiteralCaptureTuple<Parts>;
1652
+ readonly output: TemplateLiteralCaptureTuple<Parts>;
1653
+ readonly parts: Parts;
1654
+ }
1655
+ /** @group Template literals */
1656
+ export interface TemplateLiteralType<Parts extends TemplateLiteralParts> extends Type<"TemplateLiteral", string, TemplateLiteralStringOutput<Parts>, TemplateLiteralParseError<Parts>, typeof String, TypeOfError<"String"> | TemplateLiteralParseError<Parts>, never, TemplateLiteralStringOutput<Parts>, true> {
1657
+ readonly [templateLiteralSyntaxSymbol]: true;
1658
+ readonly [reflectedTypesSymbol]?: TemplateLiteralCaptureTuple<Parts>;
1659
+ readonly output: TemplateLiteralCaptureTuple<Parts>;
1660
+ readonly parts: Parts;
1661
+ }
1662
+ /**
1663
+ * Template literal {@link Type} for validation.
1664
+ *
1665
+ * Creates a canonical string Type from fixed strings and string-encoded Types.
1666
+ *
1667
+ * Use this factory when Output should remain a string. Switch to
1668
+ * {@link templateLiteralParser} when the individual Type parts should be
1669
+ * decoded into a Tuple.
1670
+ *
1671
+ * ### Example
1672
+ *
1673
+ * ```ts
1674
+ * import { templateLiteral, union } from "@evolu/common";
1675
+ *
1676
+ * const Language = union("en", "cs");
1677
+ * const Region = union("US", "CZ");
1678
+ * const Locale = templateLiteral(Language, "-", Region);
1679
+ *
1680
+ * expectTypeOf<typeof Locale.Output>().toEqualTypeOf<
1681
+ * "en-US" | "en-CZ" | "cs-US" | "cs-CZ"
1682
+ * >();
1683
+ * expectOk(Locale.fromUnknown("cs-CZ"), "cs-CZ");
1684
+ * expect(Locale.is("fr-CZ")).toBe(false);
1685
+ * ```
1686
+ *
1687
+ * @group Template literals
1688
+ */
1689
+ export declare const templateLiteral: <const Parts extends TemplateLiteralParts>(...parts: { readonly [Index in keyof Parts]: ValidateTemplateLiteralPart<Parts[Index]>; } & TemplateLiteralValidation<Parts>) => TemplateLiteralType<Parts>;
1690
+ type TemplateLiteralParseError<Parts extends TemplateLiteralParts> = TransformError<"TemplateLiteral", TemplateLiteralIsFrameless<Parts> extends true ? never : TemplateLiteralError, TemplateLiteralCaptureTupleError<Parts>>;
1691
+ type TemplateLiteralCaptureTupleError<Parts extends TemplateLiteralParts> = TupleElementsError<TemplateLiteralCaptureFromStringError<TemplateLiteralCaptureTypes<Parts>[number]>>;
1692
+ type TemplateLiteralCaptureFromStringError<T extends TypeNode> = T extends TypeNode ? string extends RootType<T>["Output"] ? TypeFromError<T> : InferErrors<T> : never;
1693
+ /** @group Template literals */
1694
+ export interface TemplateLiteralError extends TypeError<"TemplateLiteral"> {
1695
+ readonly value: string;
1696
+ }
1697
+ declare const templateLiteralStringBrandSymbol: unique symbol;
1698
+ interface TemplateLiteralStringBrand<Parts extends TemplateLiteralParts> {
1699
+ readonly [templateLiteralStringBrandSymbol]: Parts;
1700
+ }
1701
+ type TemplateLiteralPart = string | TypeNode;
1702
+ type TemplateLiteralParts = NonEmptyReadonlyArray<TemplateLiteralPart>;
1703
+ type TemplateLiteralValidation<Parts extends TemplateLiteralParts> = number extends Parts["length"] ? readonly [ValidationFailure<TemplateLiteralPartsTupleError>] : IsUnion<Parts["length"]> extends true ? readonly [ValidationFailure<TemplateLiteralPartsTupleError>] : [Extract<Parts[number], TypeNode>] extends [never] ? readonly [ValidationFailure<TemplateLiteralCaptureRequiredError>] : TemplateLiteralHasAmbiguousCaptures<Parts> extends true ? readonly [ValidationFailure<TemplateLiteralAmbiguousCapturesError>] : unknown;
1704
+ type TemplateLiteralPartsTupleError = CompileTimeError<"TemplateLiteral", "Parts must use one concrete finite non-empty tuple.">;
1705
+ type TemplateLiteralCaptureRequiredError = CompileTimeError<"TemplateLiteral", "At least one part must be a Type capture.">;
1706
+ type TemplateLiteralAmbiguousCapturesError = CompileTimeError<"TemplateLiteral", "At most one Type capture can have a variable-width string representation.">;
1707
+ type TemplateLiteralCaptureTypes<Parts extends ReadonlyArray<TemplateLiteralPart>, Captures extends ReadonlyArray<TypeNode> = readonly []> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralCaptureTypes<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, Head extends TypeNode ? readonly [...Captures, Head] : Captures> : Extract<Captures, NonEmptyReadonlyArray<TypeNode>>;
1708
+ type TemplateLiteralCaptureTuple<Parts extends TemplateLiteralParts> = TupleType<TemplateLiteralCaptureTypes<Parts>>;
1709
+ type TemplateLiteralCanonicalInput<Parts extends ReadonlyArray<TemplateLiteralPart>, Input extends string = ""> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralCanonicalInput<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, `${Input}${TemplateLiteralPartCanonicalInput<Extract<Head, TemplateLiteralPart>>}`> : Input;
1710
+ type TemplateLiteralStringOutput<Parts extends TemplateLiteralParts> = AllTypesUseIdentityEncoding<Extract<Parts[number], TypeNode>> extends true ? TemplateLiteralCanonicalInput<Parts> : TemplateLiteralCanonicalInput<Parts> & TemplateLiteralStringBrand<Parts>;
1711
+ type TemplateLiteralPartCanonicalInput<Part extends TemplateLiteralPart> = Part extends string ? Part : Part extends TypeNode ? Extract<CanonicalInputOf<Part>, string> : never;
1712
+ type ValidateTemplateLiteralPart<Part extends TemplateLiteralPart> = IsUnion<Part> extends false ? Part extends string ? ValidateLiteral<Part> : Part extends ConcreteTypeNode ? IsTemplateLiteralPartType<Part> extends true ? Part : TemplateLiteralPartCompileTimeError : TemplateLiteralPartCompileTimeError : TemplateLiteralPartCompileTimeError;
1713
+ type IsTemplateLiteralPartType<T extends TypeNode> = [
1714
+ CanonicalInputOf<T>
1715
+ ] extends [never] ? false : [CanonicalInputOf<T>] extends [string] ? true : false;
1716
+ type TemplateLiteralPartCompileTimeError = CompileTimeError<"TemplateLiteral", "Part must be a raw string literal or a Type canonically encoded as a string.">;
1717
+ type TemplateLiteralHasAmbiguousCaptures<Parts extends ReadonlyArray<TemplateLiteralPart>, VariableCaptures extends ReadonlyArray<unknown> = readonly []> = Parts extends readonly [infer Head, ...infer Tail] ? Head extends TypeNode ? [TemplateLiteralTypeWidth<Head>] extends [null] ? VariableCaptures extends readonly [unknown] ? true : TemplateLiteralHasAmbiguousCaptures<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, readonly [unknown]> : TemplateLiteralHasAmbiguousCaptures<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, VariableCaptures> : TemplateLiteralHasAmbiguousCaptures<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, VariableCaptures> : false;
1718
+ type TemplateLiteralTypeWidth<T extends TypeNode> = T extends LiteralType<infer Expected extends string> ? TemplateLiteralStringWidth<Expected> : T extends UnionType<infer Members> ? NormalizeTemplateLiteralWidth<TemplateLiteralTypeWidth<Members[number]>> : T extends TemplateLiteralParserType<infer Parts> ? TemplateLiteralPartsWidth<Parts> : T extends TemplateLiteralType<infer Parts> ? TemplateLiteralPartsWidth<Parts> : T["parent"] extends infer Parent extends TypeNode ? TemplateLiteralTypeWidth<Parent> : null;
1719
+ type NormalizeTemplateLiteralWidth<Width> = IsUnion<Width> extends true ? null : Width extends ReadonlyArray<unknown> ? Width : null;
1720
+ type TemplateLiteralPartsWidth<Parts extends ReadonlyArray<TemplateLiteralPart>, Width extends ReadonlyArray<unknown> = readonly []> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralPartWidth<Extract<Head, TemplateLiteralPart>> extends infer PartWidth ? [PartWidth] extends [ReadonlyArray<unknown>] ? TemplateLiteralPartsWidth<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, readonly [...Width, ...PartWidth]> : null : never : Width;
1721
+ type TemplateLiteralPartWidth<Part extends TemplateLiteralPart> = Part extends string ? TemplateLiteralStringWidth<Part> : Part extends TypeNode ? TemplateLiteralTypeWidth<Part> : never;
1722
+ type TemplateLiteralIsFrameless<Parts extends ReadonlyArray<TemplateLiteralPart>, HasVariableCapture extends boolean = false> = Parts extends readonly [infer Head, ...infer Tail] ? TemplateLiteralPartWidth<Extract<Head, TemplateLiteralPart>> extends infer Width ? [Width] extends [null] ? HasVariableCapture extends true ? false : TemplateLiteralIsFrameless<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, true> : [Width] extends [readonly []] ? TemplateLiteralIsFrameless<Extract<Tail, ReadonlyArray<TemplateLiteralPart>>, HasVariableCapture> : false : false : HasVariableCapture;
1723
+ type TemplateLiteralStringWidth<Value extends string, Width extends ReadonlyArray<unknown> = readonly []> = string extends Value ? null : Value extends "" ? Width : Value extends `${infer _CodePoint}${infer Tail}` ? TemplateLiteralStringWidth<Tail, readonly [...Width, unknown]> : null;
1450
1724
  /**
1451
1725
  * Branded {@link Type}.
1452
1726
  *
@@ -1800,7 +2074,7 @@ export interface CapitalizedError extends TypeError<"Capitalized"> {
1800
2074
  export declare const CapitalizedString: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Capitalized", CapitalizedError>;
1801
2075
  export type CapitalizedString = typeof CapitalizedString.Output;
1802
2076
  /**
1803
- * Adds a {@link Brand} requiring a string without surrounding whitespace.
2077
+ * String {@link Brand} without surrounding whitespace.
1804
2078
  *
1805
2079
  * @group String
1806
2080
  */
@@ -1829,7 +2103,7 @@ export type TrimmedString = typeof TrimmedString.Output;
1829
2103
  */
1830
2104
  export declare const trim: (value: string) => TrimmedString;
1831
2105
  /**
1832
- * Adds a {@link Brand} requiring a value to have at least `min` items.
2106
+ * Minimum-length {@link Brand} requiring a value to have at least `min` items.
1833
2107
  *
1834
2108
  * @group String
1835
2109
  * @group Collection
@@ -1857,7 +2131,7 @@ export interface MinLengthError<Min extends number = number> extends TypeError<`
1857
2131
  export declare const NonEmptyTrimmedString: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>;
1858
2132
  export type NonEmptyTrimmedString = typeof NonEmptyTrimmedString.Output;
1859
2133
  /**
1860
- * Adds a {@link Brand} requiring a value to have at most `max` items.
2134
+ * Maximum-length {@link Brand} requiring a value to have at most `max` items.
1861
2135
  *
1862
2136
  * @group String
1863
2137
  * @group Collection
@@ -1883,7 +2157,7 @@ export type NonEmptyTrimmedString100 = typeof NonEmptyTrimmedString100.Output;
1883
2157
  export declare const NonEmptyTrimmedString1000: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>, "MaxLength1000", MaxLengthError<1000>>;
1884
2158
  export type NonEmptyTrimmedString1000 = typeof NonEmptyTrimmedString1000.Output;
1885
2159
  /**
1886
- * Adds a {@link Brand} requiring a value to have exactly `exact` items.
2160
+ * Exact-length {@link Brand} requiring a value to have exactly `exact` items.
1887
2161
  *
1888
2162
  * @group String
1889
2163
  * @group Collection
@@ -1895,7 +2169,7 @@ export interface LengthError<Exact extends number = number> extends TypeError<`L
1895
2169
  readonly exact: Exact;
1896
2170
  }
1897
2171
  /**
1898
- * Creates a string {@link Brand} that must match a regular expression.
2172
+ * String {@link Brand} constrained by a regular expression.
1899
2173
  *
1900
2174
  * ### Example
1901
2175
  *
@@ -2061,7 +2335,7 @@ export declare const createIdFromString: <B extends string = never>(value: strin
2061
2335
  */
2062
2336
  export declare const createIdAsUuidv7: <B extends string = never>(deps: RandomBytesDep & TimeDep, ..._validation: IdBrandValidation<B>) => CreatedId<B>;
2063
2337
  /**
2064
- * A table-specific {@link Id} Type.
2338
+ * Table-specific {@link Id} Type.
2065
2339
  *
2066
2340
  * @group String
2067
2341
  */
@@ -2118,7 +2392,7 @@ export interface Int64StringError extends TypeError<"Int64String"> {
2118
2392
  */
2119
2393
  export declare const Int64FromInt64String: TransformType<BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Trimmed", TrimmedError>, "MinLength1", MinLengthError<1>>, "Int64String", Int64StringError>, BrandType<Type<"BigInt", bigint, bigint, TypeOfError<"BigInt">, null, TypeOfError<"BigInt">, never, bigint, true>, "Int64", Int64Error>, "Int64FromInt64String", never, string & Brand<"Trimmed"> & Brand<"MinLength1"> & Brand<"Int64String">>;
2120
2394
  /**
2121
- * Adds a {@link Brand} requiring a number greater than or equal to zero.
2395
+ * Number {@link Brand} requiring a value greater than or equal to zero.
2122
2396
  *
2123
2397
  * @group Number
2124
2398
  */
@@ -2135,7 +2409,7 @@ export interface NonNegativeError extends TypeError<"NonNegative"> {
2135
2409
  export declare const NonNegativeNumber: BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNegative", NonNegativeError>;
2136
2410
  export type NonNegativeNumber = typeof NonNegativeNumber.Output;
2137
2411
  /**
2138
- * Adds a {@link Brand} requiring a number greater than zero.
2412
+ * Number {@link Brand} requiring a value greater than zero.
2139
2413
  *
2140
2414
  * @group Number
2141
2415
  */
@@ -2155,7 +2429,7 @@ export interface PositiveError extends TypeError<"Positive"> {
2155
2429
  export declare const PositiveNumber: BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNegative", NonNegativeError>, "Positive", PositiveError>;
2156
2430
  export type PositiveNumber = typeof PositiveNumber.Output;
2157
2431
  /**
2158
- * Adds a {@link Brand} requiring a number less than or equal to zero.
2432
+ * Number {@link Brand} requiring a value less than or equal to zero.
2159
2433
  *
2160
2434
  * @group Number
2161
2435
  */
@@ -2172,7 +2446,7 @@ export interface NonPositiveError extends TypeError<"NonPositive"> {
2172
2446
  export declare const NonPositiveNumber: BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonPositive", NonPositiveError>;
2173
2447
  export type NonPositiveNumber = typeof NonPositiveNumber.Output;
2174
2448
  /**
2175
- * Adds a {@link Brand} requiring a number less than zero.
2449
+ * Number {@link Brand} requiring a value less than zero.
2176
2450
  *
2177
2451
  * @group Number
2178
2452
  */
@@ -2192,7 +2466,7 @@ export interface NegativeError extends TypeError<"Negative"> {
2192
2466
  export declare const NegativeNumber: BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonPositive", NonPositiveError>, "Negative", NegativeError>;
2193
2467
  export type NegativeNumber = typeof NegativeNumber.Output;
2194
2468
  /**
2195
- * Adds a {@link Brand} requiring a number other than `NaN`.
2469
+ * Number {@link Brand} requiring a value other than `NaN`.
2196
2470
  *
2197
2471
  * @group Number
2198
2472
  */
@@ -2214,7 +2488,7 @@ export interface NonNaNError extends TypeError<"NonNaN"> {
2214
2488
  export declare const NonNaNNumber: BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>;
2215
2489
  export type NonNaNNumber = typeof NonNaNNumber.Output;
2216
2490
  /**
2217
- * Adds a {@link Brand} requiring a finite number.
2491
+ * Number {@link Brand} requiring a finite value.
2218
2492
  *
2219
2493
  * @group Number
2220
2494
  */
@@ -2237,6 +2511,13 @@ export type FiniteNumber = typeof FiniteNumber.Output;
2237
2511
  */
2238
2512
  export declare const NonNegativeFiniteNumber: BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "NonNegative", NonNegativeError>;
2239
2513
  export type NonNegativeFiniteNumber = typeof NonNegativeFiniteNumber.Output;
2514
+ /**
2515
+ * Positive {@link FiniteNumber}.
2516
+ *
2517
+ * @group Number
2518
+ */
2519
+ export declare const PositiveFiniteNumber: BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "NonNegative", NonNegativeError>, "Positive", PositiveError>;
2520
+ export type PositiveFiniteNumber = typeof PositiveFiniteNumber.Output;
2240
2521
  /**
2241
2522
  * Safe integer {@link Brand}.
2242
2523
  *
@@ -2278,12 +2559,6 @@ export type Int = typeof Int.Output;
2278
2559
  */
2279
2560
  export declare const NonNegativeInt: BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonNegative", NonNegativeError>;
2280
2561
  export type NonNegativeInt = typeof NonNegativeInt.Output;
2281
- /**
2282
- * 0-100 as a literal, or any already-validated {@link NonNegativeInt}.
2283
- *
2284
- * @group Number
2285
- */
2286
- export type Int0To100OrNonNegativeInt = 0 | Int1To100 | NonNegativeInt;
2287
2562
  /**
2288
2563
  * Minimum {@link NonNegativeInt} value.
2289
2564
  *
@@ -2300,12 +2575,6 @@ export declare const zeroNonNegativeInt: number & Brand<"NonNaN"> & Brand<"Finit
2300
2575
  */
2301
2576
  export declare const PositiveInt: BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonNegative", NonNegativeError>, "Positive", PositiveError>;
2302
2577
  export type PositiveInt = typeof PositiveInt.Output;
2303
- /**
2304
- * 1-100 as a literal, or any already-validated {@link PositiveInt}.
2305
- *
2306
- * @group Number
2307
- */
2308
- export type Int1To100OrPositiveInt = Int1To100 | PositiveInt;
2309
2578
  /**
2310
2579
  * Minimum {@link PositiveInt} value.
2311
2580
  *
@@ -2336,7 +2605,7 @@ export type NonPositiveInt = typeof NonPositiveInt.Output;
2336
2605
  export declare const NegativeInt: BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonPositive", NonPositiveError>, "Negative", NegativeError>;
2337
2606
  export type NegativeInt = typeof NegativeInt.Output;
2338
2607
  /**
2339
- * Adds a {@link Brand} requiring a number greater than `min`.
2608
+ * Number {@link Brand} requiring a value greater than `min`.
2340
2609
  *
2341
2610
  * @group Number
2342
2611
  */
@@ -2347,7 +2616,7 @@ export interface GreaterThanError<Min extends number = number> extends TypeError
2347
2616
  readonly min: Min;
2348
2617
  }
2349
2618
  /**
2350
- * Adds a {@link Brand} requiring a number greater than or equal to `min`.
2619
+ * Number {@link Brand} requiring a value greater than or equal to `min`.
2351
2620
  *
2352
2621
  * @group Number
2353
2622
  */
@@ -2358,7 +2627,7 @@ export interface GreaterThanOrEqualToError<Min extends number = number> extends
2358
2627
  readonly min: Min;
2359
2628
  }
2360
2629
  /**
2361
- * Adds a {@link Brand} requiring a number less than `max`.
2630
+ * Number {@link Brand} requiring a value less than `max`.
2362
2631
  *
2363
2632
  * @group Number
2364
2633
  */
@@ -2376,7 +2645,7 @@ export interface LessThanError<Max extends number = number> extends TypeError<`L
2376
2645
  export declare const Age: BrandType<BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "Int", IntError>, "NonNegative", NonNegativeError>, "LessThan200", LessThanError<200>>, "Age", never>;
2377
2646
  export type Age = typeof Age.Output;
2378
2647
  /**
2379
- * Adds a {@link Brand} requiring a number less than or equal to `max`.
2648
+ * Number {@link Brand} requiring a value less than or equal to `max`.
2380
2649
  *
2381
2650
  * @group Number
2382
2651
  */
@@ -2396,59 +2665,148 @@ export interface LessThanOrEqualToError<Max extends number = number> extends Typ
2396
2665
  export declare const Ratio: BrandType<BrandType<BrandType<BrandType<BrandType<Type<"Number", number, number, TypeOfError<"Number">, null, TypeOfError<"Number">, never, number, true>, "NonNaN", NonNaNError>, "Finite", FiniteError>, "NonNegative", NonNegativeError>, "LessThanOrEqualTo1", LessThanOrEqualToError<1>>, "Ratio", never>;
2397
2666
  export type Ratio = typeof Ratio.Output;
2398
2667
  /**
2399
- * Canonical string representation of a positive base-10 decimal.
2668
+ * Canonical string representation of a signed base-10 decimal value.
2669
+ *
2670
+ * Use this Type when a decimal value must remain exact instead of being
2671
+ * converted to an IEEE-754 number. Equivalent values have one accepted
2672
+ * representation, so leading zeroes, trailing fractional zeroes, `-0`, plus
2673
+ * signs, and exponent notation are rejected.
2674
+ *
2675
+ * The decoded value remains a string. Arithmetic requires an explicit decimal
2676
+ * or fixed-point representation.
2677
+ *
2678
+ * TypeScript template literal types can describe a fixed number of digit
2679
+ * positions, but not the arbitrarily long integer and fractional parts accepted
2680
+ * here. `DecimalString` therefore uses a {@link Brand} so its TypeScript type
2681
+ * does not accept strings that have not been validated.
2400
2682
  *
2401
- * Each value has one spelling: leading zeroes, trailing fractional zeroes,
2402
- * signs, and exponent notation are rejected. This preserves exact decimal
2403
- * meaning without representing the value as an IEEE-754 number.
2683
+ * Use these predefined Types or their corresponding factories to add sign
2684
+ * constraints to compatible decimal string Types:
2685
+ *
2686
+ * - {@link NonNegativeDecimalString} / {@link nonNegativeDecimalString}
2687
+ * - {@link PositiveDecimalString} / {@link positiveDecimalString}
2688
+ * - {@link NonPositiveDecimalString} / {@link nonPositiveDecimalString}
2689
+ * - {@link NegativeDecimalString} / {@link negativeDecimalString}
2404
2690
  *
2405
2691
  * ### Example
2406
2692
  *
2407
2693
  * ```ts
2408
- * import { PositiveDecimalString } from "@evolu/common";
2694
+ * import { DecimalString } from "@evolu/common";
2409
2695
  *
2410
- * expectOk(PositiveDecimalString.fromUnknown("0.3"), "0.3");
2411
- * expectOk(PositiveDecimalString.fromUnknown("25"), "25");
2696
+ * expectOk(DecimalString.fromUnknown("-10.25"), "-10.25");
2697
+ * expectOk(DecimalString.fromUnknown("0"), "0");
2698
+ * expectOk(DecimalString.fromUnknown("10.25"), "10.25");
2412
2699
  *
2413
- * expectErr(PositiveDecimalString.fromUnknown("0.30"), {
2414
- * type: "PositiveDecimalString",
2415
- * value: "0.30",
2700
+ * expectErr(DecimalString.fromUnknown("10.250"), {
2701
+ * type: "DecimalString",
2702
+ * value: "10.250",
2416
2703
  * });
2417
2704
  * ```
2418
2705
  *
2419
2706
  * @group Number
2420
2707
  */
2421
- export declare const PositiveDecimalString: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "PositiveDecimalString", PositiveDecimalStringError>;
2422
- export type PositiveDecimalString = typeof PositiveDecimalString.Output;
2708
+ export declare const DecimalString: BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>;
2709
+ export type DecimalString = typeof DecimalString.Output;
2710
+ /** @group Number */
2711
+ export interface DecimalStringError extends TypeError<"DecimalString"> {
2712
+ readonly value: string;
2713
+ }
2714
+ /**
2715
+ * {@link DecimalString} Brand requiring a value greater than or equal to zero.
2716
+ *
2717
+ * @group Number
2718
+ */
2719
+ export declare const nonNegativeDecimalString: BrandFactory<"NonNegativeDecimalString", DecimalString, NonNegativeDecimalStringError>;
2720
+ /** @group Number */
2721
+ export interface NonNegativeDecimalStringError extends TypeError<"NonNegativeDecimalString"> {
2722
+ readonly value: string;
2723
+ }
2724
+ /**
2725
+ * Non-negative {@link DecimalString}.
2726
+ *
2727
+ * @group Number
2728
+ */
2729
+ export declare const NonNegativeDecimalString: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonNegativeDecimalString", NonNegativeDecimalStringError>;
2730
+ export type NonNegativeDecimalString = typeof NonNegativeDecimalString.Output;
2731
+ /**
2732
+ * {@link DecimalString} Brand requiring a value greater than zero.
2733
+ *
2734
+ * @group Number
2735
+ */
2736
+ export declare const positiveDecimalString: BrandFactory<"PositiveDecimalString", DecimalString, PositiveDecimalStringError>;
2423
2737
  /** @group Number */
2424
2738
  export interface PositiveDecimalStringError extends TypeError<"PositiveDecimalString"> {
2425
2739
  readonly value: string;
2426
2740
  }
2427
- type ValidateMultipleOfDivisor<Divisor extends string> = IsUnion<Divisor> extends false ? string extends Divisor ? InvalidMultipleOfDivisor<Divisor> : IsCanonicalPositiveDecimalString<Divisor> extends true ? Divisor : InvalidMultipleOfDivisor<Divisor> : InvalidMultipleOfDivisor<Divisor>;
2428
- type IsCanonicalPositiveDecimalString<Value extends string> = Value extends `${infer Integer}.${infer Fraction}` ? IsCanonicalNonNegativeIntegerString<Integer> extends true ? IsCanonicalPositiveFractionString<Fraction> : false : IsCanonicalPositiveIntegerString<Value>;
2429
- type IsCanonicalNonNegativeIntegerString<Value extends string> = Value extends "0" ? true : IsCanonicalPositiveIntegerString<Value>;
2430
- type IsCanonicalPositiveIntegerString<Value extends string> = Value extends `${Digit1To9}${infer Rest}` ? IsDecimalDigits<Rest> : false;
2431
- type IsCanonicalPositiveFractionString<Value extends string> = Value extends `${infer Rest}${Digit1To9}` ? IsDecimalDigits<Rest> : false;
2432
- type IsDecimalDigits<Value extends string> = Value extends "" ? true : Value extends `${Digit}${infer Rest}` ? IsDecimalDigits<Rest> : false;
2433
- type InvalidMultipleOfDivisor<Divisor extends string> = Divisor & Readonly<Record<MultipleOfDivisorError, never>>;
2434
- type MultipleOfDivisorError = CompileTimeError<"MultipleOf", 'Divisor must be one canonical positive decimal string literal such as "0.1".'>;
2435
2741
  /**
2436
- * Adds a {@link Brand} requiring a number to be a multiple of an exact decimal
2437
- * `divisor`.
2742
+ * Positive {@link DecimalString}.
2743
+ *
2744
+ * Also satisfies {@link NonNegativeDecimalString}, so it can be used wherever a
2745
+ * non-negative decimal string is required.
2746
+ *
2747
+ * @group Number
2748
+ */
2749
+ export declare const PositiveDecimalString: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonNegativeDecimalString", NonNegativeDecimalStringError>, "PositiveDecimalString", PositiveDecimalStringError>;
2750
+ export type PositiveDecimalString = typeof PositiveDecimalString.Output;
2751
+ /**
2752
+ * {@link DecimalString} Brand requiring a value less than or equal to zero.
2753
+ *
2754
+ * @group Number
2755
+ */
2756
+ export declare const nonPositiveDecimalString: BrandFactory<"NonPositiveDecimalString", DecimalString, NonPositiveDecimalStringError>;
2757
+ /** @group Number */
2758
+ export interface NonPositiveDecimalStringError extends TypeError<"NonPositiveDecimalString"> {
2759
+ readonly value: string;
2760
+ }
2761
+ /**
2762
+ * Non-positive {@link DecimalString}.
2763
+ *
2764
+ * @group Number
2765
+ */
2766
+ export declare const NonPositiveDecimalString: BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonPositiveDecimalString", NonPositiveDecimalStringError>;
2767
+ export type NonPositiveDecimalString = typeof NonPositiveDecimalString.Output;
2768
+ /**
2769
+ * {@link DecimalString} Brand requiring a value less than zero.
2770
+ *
2771
+ * @group Number
2772
+ */
2773
+ export declare const negativeDecimalString: BrandFactory<"NegativeDecimalString", DecimalString, NegativeDecimalStringError>;
2774
+ /** @group Number */
2775
+ export interface NegativeDecimalStringError extends TypeError<"NegativeDecimalString"> {
2776
+ readonly value: string;
2777
+ }
2778
+ /**
2779
+ * Negative {@link DecimalString}.
2780
+ *
2781
+ * Also satisfies {@link NonPositiveDecimalString}, so it can be used wherever a
2782
+ * non-positive decimal string is required.
2783
+ *
2784
+ * @group Number
2785
+ */
2786
+ export declare const NegativeDecimalString: BrandType<BrandType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "DecimalString", DecimalStringError>, "NonPositiveDecimalString", NonPositiveDecimalStringError>, "NegativeDecimalString", NegativeDecimalStringError>;
2787
+ export type NegativeDecimalString = typeof NegativeDecimalString.Output;
2788
+ /**
2789
+ * Number {@link Brand} requiring an exact decimal multiple of `divisor`.
2438
2790
  *
2439
- * The literal divisor is validated against {@link PositiveDecimalString} and
2440
- * encoded in the resulting Brand name. A runtime string cannot define this Type
2441
- * because its exact value is not available to TypeScript for that name.
2442
- * Validation does not round: `"0.1"` accepts `0.3`, but rejects `0.1 + 0.2`
2443
- * because that expression evaluates to `0.30000000000000004`.
2791
+ * The divisor must be one canonical positive decimal string literal because its
2792
+ * exact value is encoded in the resulting Brand name. The declaration is
2793
+ * validated at compile time and is never converted to an IEEE-754 number.
2794
+ *
2795
+ * Validation treats each finite Number as its canonical decimal representation
2796
+ * and checks exact base-10 divisibility. It does not use floating-point
2797
+ * remainder, apply a tolerance, or round to the divisor's precision.
2444
2798
  *
2445
2799
  * ### Example
2446
2800
  *
2447
2801
  * ```ts
2448
- * import { FiniteNumber, multipleOf } from "@evolu/common";
2802
+ * import { FiniteNumber, multipleOf, type Brand } from "@evolu/common";
2449
2803
  *
2450
2804
  * const Tenths = multipleOf("0.1")(FiniteNumber);
2451
2805
  *
2806
+ * expectTypeOf<typeof Tenths.Output>().toEqualTypeOf<
2807
+ * FiniteNumber & Brand<"MultipleOf0.1">
2808
+ * >();
2809
+ *
2452
2810
  * expectOk(Tenths.fromUnknown(0.3), 0.3);
2453
2811
  * expectErr(Tenths.fromUnknown(0.31), {
2454
2812
  * type: "MultipleOf0.1",
@@ -2465,8 +2823,16 @@ export interface MultipleOfError<Divisor extends string = string> extends TypeEr
2465
2823
  readonly value: number;
2466
2824
  readonly divisor: Divisor;
2467
2825
  }
2826
+ type ValidateMultipleOfDivisor<Divisor extends string> = IsUnion<Divisor> extends false ? string extends Divisor ? InvalidMultipleOfDivisor<Divisor> : IsCanonicalPositiveDecimalString<Divisor> extends true ? Divisor : InvalidMultipleOfDivisor<Divisor> : InvalidMultipleOfDivisor<Divisor>;
2827
+ type IsCanonicalPositiveDecimalString<Value extends string> = Value extends `${infer Integer}.${infer Fraction}` ? IsCanonicalNonNegativeIntegerString<Integer> extends true ? IsCanonicalPositiveFractionString<Fraction> : false : IsCanonicalPositiveIntegerString<Value>;
2828
+ type IsCanonicalNonNegativeIntegerString<Value extends string> = Value extends "0" ? true : IsCanonicalPositiveIntegerString<Value>;
2829
+ type IsCanonicalPositiveIntegerString<Value extends string> = Value extends `${Digit1To9}${infer Rest}` ? IsDecimalDigits<Rest> : false;
2830
+ type IsCanonicalPositiveFractionString<Value extends string> = Value extends `${infer Rest}${Digit1To9}` ? IsDecimalDigits<Rest> : false;
2831
+ type IsDecimalDigits<Value extends string> = Value extends "" ? true : Value extends `${Digit}${infer Rest}` ? IsDecimalDigits<Rest> : false;
2832
+ type InvalidMultipleOfDivisor<Divisor extends string> = Divisor & Readonly<Record<MultipleOfDivisorError, never>>;
2833
+ type MultipleOfDivisorError = CompileTimeError<"MultipleOf", 'Divisor must be one canonical positive decimal string literal such as "0.1".'>;
2468
2834
  /**
2469
- * Adds a {@link Brand} requiring a number to be within an inclusive range.
2835
+ * Number {@link Brand} requiring a value within an inclusive range.
2470
2836
  *
2471
2837
  * @group Number
2472
2838
  */
@@ -2837,6 +3203,55 @@ interface TupleItemsErrorValue<Error extends TypeError, IncludeStructuralIssues
2837
3203
  readonly issues: NonEmptyReadonlyArray<(true extends IncludeStructuralIssues ? TupleStructuralIssue : never) | TupleElementIssue<Error>>;
2838
3204
  };
2839
3205
  }
3206
+ /**
3207
+ * Decimal digit from `"0"` to `"9"`.
3208
+ *
3209
+ * @group String
3210
+ */
3211
+ export declare const Digit: UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>;
3212
+ export type Digit = typeof Digit.Output;
3213
+ /**
3214
+ * Decimal digit from `"1"` to `"9"`.
3215
+ *
3216
+ * @group String
3217
+ */
3218
+ export declare const Digit1To9: UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>;
3219
+ export type Digit1To9 = typeof Digit1To9.Output;
3220
+ /**
3221
+ * Decimal string from `"1"` to `"6"`.
3222
+ *
3223
+ * @group String
3224
+ */
3225
+ export declare const Digit1To6: UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">]>;
3226
+ export type Digit1To6 = typeof Digit1To6.Output;
3227
+ /**
3228
+ * Decimal string from `"1"` to `"23"`.
3229
+ *
3230
+ * @group String
3231
+ */
3232
+ export declare const Digit1To23: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly ["1", UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>, TemplateLiteralType<readonly ["2", UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">]>]>]>;
3233
+ export type Digit1To23 = typeof Digit1To23.Output;
3234
+ /**
3235
+ * Decimal string from `"1"` to `"51"`.
3236
+ *
3237
+ * @group String
3238
+ */
3239
+ export declare const Digit1To51: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">]>, UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>, TemplateLiteralType<readonly ["5", UnionType<readonly [LiteralType<"0">, LiteralType<"1">]>]>]>;
3240
+ export type Digit1To51 = typeof Digit1To51.Output;
3241
+ /**
3242
+ * Decimal string from `"1"` to `"99"`.
3243
+ *
3244
+ * @group String
3245
+ */
3246
+ export declare const Digit1To99: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>]>;
3247
+ export type Digit1To99 = typeof Digit1To99.Output;
3248
+ /**
3249
+ * Decimal string from `"1"` to `"59"`.
3250
+ *
3251
+ * @group String
3252
+ */
3253
+ export declare const Digit1To59: UnionType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>, TemplateLiteralType<readonly [UnionType<readonly [LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">]>, UnionType<readonly [LiteralType<"0">, LiteralType<"1">, LiteralType<"2">, LiteralType<"3">, LiteralType<"4">, LiteralType<"5">, LiteralType<"6">, LiteralType<"7">, LiteralType<"8">, LiteralType<"9">]>]>]>;
3254
+ export type Digit1To59 = typeof Digit1To59.Output;
2840
3255
  type PlainObjectError = ObjectError<Readonly<Record<never, never>>, ObjectPropertyAccessError | ObjectExcessPropertyError>;
2841
3256
  /**
2842
3257
  * A {@link Type} for readonly plain objects with unknown property values.
@@ -3448,7 +3863,7 @@ type ObjectUnknownPropertyErrors<Error extends TypeError, RestError extends Type
3448
3863
  type ObjectRestFromUnknownError<Rest extends ObjectRecordTypeNode | undefined> = Rest extends ObjectRecordTypeNode ? ObjectPropertyAccessError | RecordEntriesErrorValue<TypeOfError<"String">, InferErrors<Rest["value"]>, never> : ObjectExcessPropertyError;
3449
3864
  type ObjectRestFromParentError<Rest extends ObjectRecordTypeNode | undefined> = Rest extends ObjectRecordTypeNode ? RecordEntriesError<never, TypeFromError<Rest["value"]>, never> : never;
3450
3865
  /**
3451
- * Creates an {@link object} Type with every property optional.
3866
+ * Object {@link Type} with every property optional.
3452
3867
  *
3453
3868
  * No property is required, but every present property must still satisfy its
3454
3869
  * Type.
@@ -3473,7 +3888,8 @@ export type PartialObjectProps<Props extends ObjectProps> = {
3473
3888
  readonly [Key in keyof Props]: Props[Key] extends OptionalProperty<TypeNode> ? Props[Key] : Props[Key] extends TypeNode ? OptionalProperty<Props[Key]> : never;
3474
3889
  };
3475
3890
  /**
3476
- * Makes every property whose Union Type includes {@link Null} optional.
3891
+ * Object {@link Type} making every property whose Union Type includes
3892
+ * {@link Null} optional.
3477
3893
  *
3478
3894
  * The property retains its original Union Type, so consumers may omit it, set
3479
3895
  * it to `null`, or provide any other member of that Union. Properties without
@@ -3504,7 +3920,7 @@ export type NullableToOptionalProps<Props extends ObjectProps> = {
3504
3920
  readonly [Key in keyof Props]: Props[Key] extends OptionalProperty<TypeNode> ? Props[Key] : Props[Key] extends UnionType<infer Members> ? typeof Null extends Members[number] ? OptionalProperty<Props[Key]> : Props[Key] : Props[Key];
3505
3921
  };
3506
3922
  /**
3507
- * Creates an {@link object} Type without the selected declared properties.
3923
+ * Object {@link Type} without the selected declared properties.
3508
3924
  *
3509
3925
  * @group Objects
3510
3926
  */
@@ -3515,7 +3931,7 @@ type ValidateOmitKeys<Keys extends ReadonlyArray<PropertyKey>> = number extends
3515
3931
  type OmitKeysTupleError = CompileTimeError<"Type", "Omitted keys must use one concrete finite tuple.">;
3516
3932
  type OmitKeyConcreteTypeError = CompileTimeError<"Type", "Each omitted key must be one concrete property key.">;
3517
3933
  /**
3518
- * Creates a {@link Type} for {@link Result} values.
3934
+ * {@link Result} {@link Type} for typed success and error values.
3519
3935
  *
3520
3936
  * Use this to validate Results crossing a storage, worker, API, or other
3521
3937
  * serialization boundary. The operation returns an outer validation Result. Its
@@ -3715,7 +4131,7 @@ type ConcreteTypedTagError = CompileTimeError<"Type", "Tag must be one concrete
3715
4131
  type TypedValidationError<Props extends ObjectProps> = "type" extends keyof Props ? TypedTypePropertyError : ObjectValidationError<Props>;
3716
4132
  type TypedTypePropertyError = CompileTimeError<"Type", 'Additional properties must not declare the reserved "type" property.'>;
3717
4133
  /**
3718
- * Creates a {@link Type} for a producer's value, error, or done {@link Result}.
4134
+ * Producer-result {@link Type} for value, error, or done outcomes.
3719
4135
  *
3720
4136
  * The three outcomes are `Ok<Value>`, `Err<Error>`, and `Err<Typed<"Done"> & {
3721
4137
  * done: Done }>`. This keeps normal completion distinct from failure while
@@ -4212,7 +4628,7 @@ export declare const jsonValueToJson: (value: JsonValue) => Json;
4212
4628
  */
4213
4629
  export declare const JsonValueFromJson: TransformType<BrandType<Type<"String", string, string, TypeOfError<"String">, null, TypeOfError<"String">, never, string, true>, "Json", JsonError>, JsonValueType, "JsonValueFromJson", never, string & Brand<"Json">>;
4214
4630
  /**
4215
- * Creates a branded {@link Json} Type and total conversions for another Type.
4631
+ * Branded {@link Json} Type and total conversions for another Type.
4216
4632
  *
4217
4633
  * Use this factory when a domain value must be stored as JSON text while its
4218
4634
  * exact Type remains visible to TypeScript, such as a JSON column in an Evolu