{"version":3,"file":"string.mjs","sources":["../../src/primitives/string.mts"],"sourcesContent":["import { Arr, expectType, isString, PositiveInt, Result } from 'ts-data-forge';\nimport {\n  type MaxLengthString,\n  type MinLengthString,\n  type NonEmptyString,\n  type RelaxedExclude,\n  type SupportedLength,\n} from 'ts-type-forge';\nimport { refine } from '../other-types/index.mjs';\nimport { type Type } from '../type.mjs';\nimport {\n  createPrimitiveType,\n  type StringConstraintViolation,\n} from '../utils/index.mjs';\n\nexport function string(defaultValue?: string): Type<string>;\n\nexport function string<S extends string, const C extends RawConstraints>(\n  defaultValue: S & DefaultValueType<S, C>,\n  constraints: C,\n): Type<ConstraintsResultType<C>>;\n\nexport function string<C extends RawConstraints>(\n  defaultValue: string = '',\n  constraints?: C,\n): Type<string> {\n  const baseType = createPrimitiveType({\n    typeName: 'string',\n    defaultValue,\n    is: isString,\n  });\n\n  if (\n    constraints === undefined ||\n    Arr.isFixedLengthTuple(Object.keys(constraints), 0)\n  ) {\n    return baseType;\n  }\n\n  const constraintsResult = validateConstraints(constraints);\n\n  if (Result.isErr(constraintsResult)) {\n    throw new TypeError(constraintsResult.value);\n  }\n\n  const constraintsPredicate = createConstraintsPredicate(\n    constraintsResult.value,\n  );\n\n  const defaultValueConstraintsCheck = constraintsPredicate(defaultValue);\n\n  if (Result.isErr(defaultValueConstraintsCheck)) {\n    throw new Error(\n      defaultValueErrorMessage(\n        defaultValue,\n        defaultValueConstraintsCheck.value,\n      ),\n    );\n  }\n\n  return refine({\n    baseType,\n    defaultValue,\n    is: (value): value is string => Result.isOk(constraintsPredicate(value)),\n    typeName: 'string',\n    getConstraintDetails: (value) => {\n      const result = constraintsPredicate(value);\n\n      return Result.isErr(result)\n        ? { kind: 'string-constraint', violation: result.value }\n        : undefined;\n    },\n  });\n}\n\ntype RawConstraints = Partial<\n  Readonly<{\n    nonempty: true;\n    minLength: number;\n    maxLength: number;\n    startsWith: string;\n    endsWith: string;\n    includes: string;\n    uppercase: true;\n    lowercase: true;\n    regex: RegExp;\n  }>\n>;\n\ntype Constraints = Partial<\n  Readonly<{\n    nonempty: true;\n    minLength: PositiveInt;\n    maxLength: PositiveInt;\n    startsWith: string;\n    endsWith: string;\n    includes: string;\n    uppercase: true;\n    lowercase: true;\n    regex: RegExp;\n  }>\n>;\n\n/**\n * The set of constraints accepted by {@link string}.\n */\nexport type StringTypeConstraints = RawConstraints;\n\n/**\n * The result type produced by {@link string} for a given set of constraints\n * `C` (e.g. `nonempty` yields {@link NonEmptyString}, `minLength` /\n * `maxLength` yield {@link MinLengthString} / {@link MaxLengthString},\n * `startsWith` narrows to a template literal type, etc.).\n */\nexport type StringConstraintsResultType<C extends StringTypeConstraints> =\n  ConstraintsResultType<C>;\n\ntype DefaultValueType<\n  S extends string,\n  C extends RawConstraints,\n> = DefaultValueWhenNonemptyIsOn<S, C> &\n  DefaultValueWhenMinLengthIsOn<S, C> &\n  DefaultValueWhenMaxLengthIsOn<S, C> &\n  DefaultValueWhenStartsWithIsOn<C> &\n  DefaultValueWhenEndsWithIsOn<C> &\n  DefaultValueWhenIncludesIsOn<C> &\n  DefaultValueWhenUppercaseIsOn<S, C> &\n  DefaultValueWhenLowercaseIsOn<S, C>;\n\ntype ConstraintsResultType<C extends RawConstraints> =\n  ConstraintsResultTypeWhenNonemptyIsOn<C> &\n    ConstraintsResultTypeWhenMinLengthIsOn<C> &\n    ConstraintsResultTypeWhenMaxLengthIsOn<C> &\n    DefaultValueWhenStartsWithIsOn<C> &\n    DefaultValueWhenEndsWithIsOn<C> &\n    DefaultValueWhenIncludesIsOn<C>;\n\ntype DefaultValueWhenStartsWithIsOn<C extends RawConstraints> =\n  C extends Readonly<{\n    startsWith: infer S extends string;\n  }>\n    ? `${S}${string}`\n    : string;\n\ntype DefaultValueWhenEndsWithIsOn<C extends RawConstraints> =\n  C extends Readonly<{\n    endsWith: infer E extends string;\n  }>\n    ? `${string}${E}`\n    : string;\n\ntype DefaultValueWhenIncludesIsOn<C extends RawConstraints> =\n  C extends Readonly<{\n    includes: infer M extends string;\n  }>\n    ? `${string}${M}${string}`\n    : string;\n\ntype DefaultValueWhenUppercaseIsOn<S extends string, C extends RawConstraints> =\n  C extends Readonly<{ uppercase: true }> ? CastUppercase<S> : string;\n\ntype DefaultValueWhenLowercaseIsOn<S extends string, C extends RawConstraints> =\n  C extends Readonly<{ lowercase: true }> ? CastLowercase<S> : string;\n\ntype CastLowercase<S extends string> = S extends Lowercase<S> ? S : never;\n\ntype CastUppercase<S extends string> = S extends Uppercase<S> ? S : never;\n\ntype DefaultValueWhenNonemptyIsOn<S extends string, C extends RawConstraints> =\n  C extends Readonly<{ nonempty: true }> ? RejectEmptyString<S> : string;\n\ntype RejectEmptyString<S extends string> = S extends '' ? never : S;\n\ntype ConstraintsResultTypeWhenNonemptyIsOn<C extends RawConstraints> =\n  C extends Readonly<{ nonempty: true }> ? NonEmptyString : string;\n\n/**\n * The length-bound literals supported at the type level:\n * `RelaxedExclude<SupportedLength, 0>` (i.e. `1 | 2 | ... | 2048`, the shared\n * cap of the branded length-constrained types in ts-type-forge). Only bounds\n * within this range are encoded in the result brand; larger literals and\n * non-literal `number`s collapse to plain `string` to keep the type-level\n * computation cheap (the runtime constraint applies regardless). Non-integer\n * bounds and bounds less than 1 throw a `TypeError` when the type is\n * constructed.\n */\ntype SupportedLengthLiteral = RelaxedExclude<SupportedLength, 0>;\n\ntype ConstraintsResultTypeWhenMinLengthIsOn<C extends RawConstraints> =\n  C extends Readonly<{ minLength: infer M extends SupportedLengthLiteral }>\n    ? MinLengthString<M>\n    : string; // minLength outside the supported literal range is not encoded in the brand\n\ntype ConstraintsResultTypeWhenMaxLengthIsOn<C extends RawConstraints> =\n  C extends Readonly<{ maxLength: infer M extends SupportedLengthLiteral }>\n    ? MaxLengthString<M>\n    : string; // maxLength outside the supported literal range is not encoded in the brand\n\ntype DefaultValueWhenMinLengthIsOn<S extends string, C extends RawConstraints> =\n  C extends Readonly<{ minLength: infer M extends SupportedLengthLiteral }>\n    ? StringWithMinLength<S, M>\n    : string; // the default's length is only checked at runtime for such bounds\n\ntype DefaultValueWhenMaxLengthIsOn<S extends string, C extends RawConstraints> =\n  C extends Readonly<{ maxLength: infer M extends SupportedLengthLiteral }>\n    ? StringWithMaxLength<S, M>\n    : string; // the default's length is only checked at runtime for such bounds\n\ntype StringWithMinLength<S extends string, N extends number> =\n  HasLengthAtLeast<S, N> extends true ? S : never;\n\ntype StringWithMaxLength<S extends string, N extends number> =\n  HasLengthAtMost<S, N> extends true ? S : never;\n\ntype HasLengthAtLeast<\n  S extends string,\n  N extends number,\n  Acc extends readonly unknown[] = readonly [],\n> = string extends S\n  ? true\n  : Acc['length'] extends N\n    ? true\n    : S extends ''\n      ? false\n      : S extends `${infer _}${infer Rest}`\n        ? HasLengthAtLeast<Rest, N, readonly [...Acc, unknown]>\n        : false;\n\ntype HasLengthAtMost<\n  S extends string,\n  N extends number,\n  Acc extends readonly unknown[] = readonly [],\n> = string extends S\n  ? true\n  : S extends ''\n    ? true\n    : Acc['length'] extends N\n      ? false\n      : S extends `${infer _}${infer Rest}`\n        ? HasLengthAtMost<Rest, N, readonly [...Acc, unknown]>\n        : false;\n\nconst validateConstraints = (\n  constraints: RawConstraints,\n): Result<Constraints, string> => {\n  const result1 = validateLengthConstraint('minLength', constraints.minLength);\n\n  if (Result.isErr(result1)) {\n    return Result.err(result1.value);\n  }\n\n  const result2 = validateLengthConstraint('maxLength', constraints.maxLength);\n\n  if (Result.isErr(result2)) {\n    return Result.err(result2.value);\n  }\n\n  // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n  return Result.ok(constraints as Constraints);\n};\n\nconst validateLengthConstraint = (\n  constraintName: 'maxLength' | 'minLength',\n  constraintValue: number | undefined,\n): Result<true, string> => {\n  if (constraintValue !== undefined && !PositiveInt.is(constraintValue)) {\n    return Result.err(\n      `${constraintName} constraint for string must be a positive integer, but ${constraintValue} was passed.`,\n    );\n  }\n\n  return Result.ok(true);\n};\n\nconst createConstraintsPredicate =\n  (constraints: Constraints) =>\n  (value: string): Result<true, StringConstraintViolation> => {\n    const {\n      nonempty,\n      minLength,\n      maxLength,\n      startsWith,\n      endsWith,\n      includes,\n      uppercase,\n      lowercase,\n      regex,\n      ..._rest\n    } = constraints;\n\n    expectType<keyof typeof _rest, never>('=');\n\n    if (nonempty === true && value.length === 0) {\n      return Result.err({ constraint: 'nonempty', value: true } as const);\n    }\n\n    if (minLength !== undefined && value.length < minLength) {\n      return Result.err({ constraint: 'minLength', value: minLength } as const);\n    }\n\n    if (maxLength !== undefined && value.length > maxLength) {\n      return Result.err({ constraint: 'maxLength', value: maxLength } as const);\n    }\n\n    if (startsWith !== undefined && !value.startsWith(startsWith)) {\n      return Result.err({\n        constraint: 'startsWith',\n        value: startsWith,\n      } as const);\n    }\n\n    if (endsWith !== undefined && !value.endsWith(endsWith)) {\n      return Result.err({ constraint: 'endsWith', value: endsWith } as const);\n    }\n\n    if (includes !== undefined && !value.includes(includes)) {\n      return Result.err({ constraint: 'includes', value: includes } as const);\n    }\n\n    if (uppercase === true && value !== value.toUpperCase()) {\n      return Result.err({ constraint: 'uppercase', value: true } as const);\n    }\n\n    if (lowercase === true && value !== value.toLowerCase()) {\n      return Result.err({ constraint: 'lowercase', value: true } as const);\n    }\n\n    if (regex !== undefined && !regex.test(value)) {\n      return Result.err({ constraint: 'regex', value: regex.source } as const);\n    }\n\n    return Result.ok(true);\n  };\n\nconst defaultValueErrorMessage = (\n  value: string,\n  violation: StringConstraintViolation,\n): string =>\n  `defaultValue \"${value}\" for string does not satisfy the constraint ${violation.constraint} = ${violation.value}` as const;\n"],"names":[],"mappings":"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"}