UNPKG

typia

Version:

Superfast runtime validators with only one line

1 lines 5.71 kB
{"version":3,"file":"_randomStringLength.mjs","names":[],"sources":["../../src/internal/_randomStringLength.ts"],"sourcesContent":["import { _randomInteger } from \"./_randomInteger\";\nimport { _randomString } from \"./_randomString\";\n\nexport const _RANDOM_LENGTH_ERROR =\n \"unable to generate a random string satisfying both the format and the length constraints.\";\n\n/**\n * Length bounds forwarded to a string format or pattern generator.\n *\n * `typia.random<T>()` passes this object to the `_randomFormat*` and\n * `_randomPattern` helpers whenever the string leaf also carries a `MinLength`\n * or `MaxLength` tag, so a constrained format or pattern is generated at a\n * length its own validator accepts (issue #2189).\n */\nexport interface _ILengthProps {\n minLength?: number;\n maxLength?: number;\n}\n\n/**\n * Builds the variable segment of a fixed-shape format string so that the total\n * length `fixed + segment.length` lands inside the requested window.\n *\n * `fixed` is the number of characters the format always contributes around the\n * segment, `fallback` is the segment length used when the leaf is unconstrained\n * on that side, and `minimum` is the smallest segment the format still\n * validates with. Throws when the window cannot be satisfied by this format's\n * shape (e.g. `Format<\"email\"> & MaxLength<3>`).\n */\nexport const _randomSegmentLength = (\n props: _ILengthProps | undefined,\n fixed: number,\n fallback: number,\n minimum: number,\n): string => {\n let low: number =\n props?.minLength === undefined ? minimum : props.minLength - fixed;\n if (low < minimum) low = minimum;\n // With only a lower bound, spread the segment across `fallback` extra\n // characters so the generated length varies, mirroring `_randomString`.\n const high: number =\n props?.maxLength === undefined ? low + fallback : props.maxLength - fixed;\n if (high < low || high < minimum) throw new Error(_RANDOM_LENGTH_ERROR);\n return _randomString({ type: \"string\", minLength: low, maxLength: high });\n};\n\n/**\n * Intersects the requested length window with the lengths a format's grammar\n * can express, and draws one of them.\n *\n * `minimum` and `maximum` are the shortest and longest values the format admits\n * — omit `maximum` when the grammar is open above — and `spread` is how far\n * past the floor an unbounded request may reach, so an open side still varies\n * the way `_randomString` does. The caller draws from the returned window and\n * applies whatever step its grammar has (base64 lengths are multiples of four,\n * a fractional second needs at least one digit).\n *\n * Throws only when the window and the grammar do not overlap at all. That is\n * the distinction the retry driver below cannot make: it redraws one shape, so\n * it gives up on every length that shape never emits even when the format\n * itself accepts one (issue #2284).\n */\nexport const _randomLengthWindow = (\n props: _ILengthProps | undefined,\n format: { minimum: number; maximum?: number; spread?: number },\n): { low: number; high: number } => {\n const ceiling: number = format.maximum ?? Number.MAX_SAFE_INTEGER;\n const low: number = Math.max(\n props?.minLength ?? format.minimum,\n format.minimum,\n );\n const high: number = Math.min(\n props?.maxLength ?? low + (format.spread ?? 0),\n ceiling,\n );\n if (low > high) throw new Error(_RANDOM_LENGTH_ERROR);\n return { low, high };\n};\n\n/** Draws a length inside a window produced by {@link _randomLengthWindow}. */\nexport const _randomLengthPick = (window: {\n low: number;\n high: number;\n}): number =>\n _randomInteger({\n type: \"integer\",\n minimum: window.low,\n maximum: window.high,\n });\n\n/**\n * Wraps a fixed-length format generator (uuid, date) with the requested length\n * window.\n *\n * Use it only where the grammar admits exactly one length, so a window that\n * excludes it is genuinely unsatisfiable. A format that can express more than\n * one length must build its value at a length drawn from\n * {@link _randomLengthWindow} instead; redrawing one shape cannot reach a length\n * that shape never emits.\n */\nexport const _randomFormatLength = (\n props: _ILengthProps | undefined,\n generate: () => string,\n): string => {\n if (props?.minLength === undefined && props?.maxLength === undefined)\n return generate();\n for (let i: number = 0; i < 256; ++i) {\n const value: string = generate();\n if (\n (props.minLength === undefined || value.length >= props.minLength) &&\n (props.maxLength === undefined || value.length <= props.maxLength)\n )\n return value;\n }\n throw new Error(_RANDOM_LENGTH_ERROR);\n};\n"],"mappings":";;;AAGA,MAAa,uBACX;;;;;;;;;;;AAyBF,MAAa,wBACX,OACA,OACA,UACA,YACW;CACX,IAAI,MACF,OAAO,cAAc,KAAA,IAAY,UAAU,MAAM,YAAY;CAC/D,IAAI,MAAM,SAAS,MAAM;CAGzB,MAAM,OACJ,OAAO,cAAc,KAAA,IAAY,MAAM,WAAW,MAAM,YAAY;CACtE,IAAI,OAAO,OAAO,OAAO,SAAS,MAAM,IAAI,MAAM,oBAAoB;CACtE,OAAO,cAAc;EAAE,MAAM;EAAU,WAAW;EAAK,WAAW;CAAK,CAAC;AAC1E;;;;;;;;;;;;;;;;;AAkBA,MAAa,uBACX,OACA,WACkC;CAClC,MAAM,UAAkB,OAAO,WAAW,OAAO;CACjD,MAAM,MAAc,KAAK,IACvB,OAAO,aAAa,OAAO,SAC3B,OAAO,OACT;CACA,MAAM,OAAe,KAAK,IACxB,OAAO,aAAa,OAAO,OAAO,UAAU,IAC5C,OACF;CACA,IAAI,MAAM,MAAM,MAAM,IAAI,MAAM,oBAAoB;CACpD,OAAO;EAAE;EAAK;CAAK;AACrB;;AAGA,MAAa,qBAAqB,WAIhC,eAAe;CACb,MAAM;CACN,SAAS,OAAO;CAChB,SAAS,OAAO;AAClB,CAAC;;;;;;;;;;;AAYH,MAAa,uBACX,OACA,aACW;CACX,IAAI,OAAO,cAAc,KAAA,KAAa,OAAO,cAAc,KAAA,GACzD,OAAO,SAAS;CAClB,KAAK,IAAI,IAAY,GAAG,IAAI,KAAK,EAAE,GAAG;EACpC,MAAM,QAAgB,SAAS;EAC/B,KACG,MAAM,cAAc,KAAA,KAAa,MAAM,UAAU,MAAM,eACvD,MAAM,cAAc,KAAA,KAAa,MAAM,UAAU,MAAM,YAExD,OAAO;CACX;CACA,MAAM,IAAI,MAAM,oBAAoB;AACtC"}