@atproto/lex-schema 0.0.11 → 0.0.13

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.
Files changed (261) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/core/$type.d.ts +149 -0
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js +44 -0
  5. package/dist/core/$type.js.map +1 -1
  6. package/dist/core/record-key.d.ts +44 -0
  7. package/dist/core/record-key.d.ts.map +1 -1
  8. package/dist/core/record-key.js +30 -0
  9. package/dist/core/record-key.js.map +1 -1
  10. package/dist/core/result.d.ts +85 -4
  11. package/dist/core/result.d.ts.map +1 -1
  12. package/dist/core/result.js +60 -4
  13. package/dist/core/result.js.map +1 -1
  14. package/dist/core/schema.d.ts +232 -5
  15. package/dist/core/schema.d.ts.map +1 -1
  16. package/dist/core/schema.js +197 -4
  17. package/dist/core/schema.js.map +1 -1
  18. package/dist/core/string-format.d.ts +244 -11
  19. package/dist/core/string-format.d.ts.map +1 -1
  20. package/dist/core/string-format.js +150 -0
  21. package/dist/core/string-format.js.map +1 -1
  22. package/dist/core/types.d.ts +90 -3
  23. package/dist/core/types.d.ts.map +1 -1
  24. package/dist/core/types.js.map +1 -1
  25. package/dist/core/validation-error.d.ts +60 -0
  26. package/dist/core/validation-error.d.ts.map +1 -1
  27. package/dist/core/validation-error.js +60 -0
  28. package/dist/core/validation-error.js.map +1 -1
  29. package/dist/core/validation-issue.d.ts +61 -0
  30. package/dist/core/validation-issue.d.ts.map +1 -1
  31. package/dist/core/validation-issue.js +54 -1
  32. package/dist/core/validation-issue.js.map +1 -1
  33. package/dist/core/validator.d.ts +356 -11
  34. package/dist/core/validator.d.ts.map +1 -1
  35. package/dist/core/validator.js +203 -4
  36. package/dist/core/validator.js.map +1 -1
  37. package/dist/helpers.d.ts +12 -28
  38. package/dist/helpers.d.ts.map +1 -1
  39. package/dist/helpers.js.map +1 -1
  40. package/dist/schema/array.d.ts +46 -0
  41. package/dist/schema/array.d.ts.map +1 -1
  42. package/dist/schema/array.js +16 -1
  43. package/dist/schema/array.js.map +1 -1
  44. package/dist/schema/blob.d.ts +50 -2
  45. package/dist/schema/blob.d.ts.map +1 -1
  46. package/dist/schema/blob.js +44 -2
  47. package/dist/schema/blob.js.map +1 -1
  48. package/dist/schema/boolean.d.ts +29 -0
  49. package/dist/schema/boolean.d.ts.map +1 -1
  50. package/dist/schema/boolean.js +30 -1
  51. package/dist/schema/boolean.js.map +1 -1
  52. package/dist/schema/bytes.d.ts +39 -0
  53. package/dist/schema/bytes.d.ts.map +1 -1
  54. package/dist/schema/bytes.js +34 -1
  55. package/dist/schema/bytes.js.map +1 -1
  56. package/dist/schema/cid.d.ts +39 -0
  57. package/dist/schema/cid.d.ts.map +1 -1
  58. package/dist/schema/cid.js +35 -1
  59. package/dist/schema/cid.js.map +1 -1
  60. package/dist/schema/custom.d.ts +67 -1
  61. package/dist/schema/custom.d.ts.map +1 -1
  62. package/dist/schema/custom.js +55 -0
  63. package/dist/schema/custom.js.map +1 -1
  64. package/dist/schema/dict.d.ts +45 -0
  65. package/dist/schema/dict.d.ts.map +1 -1
  66. package/dist/schema/dict.js +46 -1
  67. package/dist/schema/dict.js.map +1 -1
  68. package/dist/schema/discriminated-union.d.ts +59 -0
  69. package/dist/schema/discriminated-union.d.ts.map +1 -1
  70. package/dist/schema/discriminated-union.js +47 -1
  71. package/dist/schema/discriminated-union.js.map +1 -1
  72. package/dist/schema/enum.d.ts +49 -0
  73. package/dist/schema/enum.d.ts.map +1 -1
  74. package/dist/schema/enum.js +49 -0
  75. package/dist/schema/enum.js.map +1 -1
  76. package/dist/schema/integer.d.ts +43 -0
  77. package/dist/schema/integer.d.ts.map +1 -1
  78. package/dist/schema/integer.js +38 -1
  79. package/dist/schema/integer.js.map +1 -1
  80. package/dist/schema/intersection.d.ts +55 -0
  81. package/dist/schema/intersection.d.ts.map +1 -1
  82. package/dist/schema/intersection.js +50 -0
  83. package/dist/schema/intersection.js.map +1 -1
  84. package/dist/schema/lex-map.d.ts +37 -0
  85. package/dist/schema/lex-map.d.ts.map +1 -0
  86. package/dist/schema/lex-map.js +60 -0
  87. package/dist/schema/lex-map.js.map +1 -0
  88. package/dist/schema/lex-value.d.ts +35 -0
  89. package/dist/schema/lex-value.d.ts.map +1 -0
  90. package/dist/schema/lex-value.js +87 -0
  91. package/dist/schema/lex-value.js.map +1 -0
  92. package/dist/schema/literal.d.ts +45 -0
  93. package/dist/schema/literal.d.ts.map +1 -1
  94. package/dist/schema/literal.js +45 -0
  95. package/dist/schema/literal.js.map +1 -1
  96. package/dist/schema/never.d.ts +43 -0
  97. package/dist/schema/never.d.ts.map +1 -1
  98. package/dist/schema/never.js +44 -1
  99. package/dist/schema/never.js.map +1 -1
  100. package/dist/schema/null.d.ts +30 -0
  101. package/dist/schema/null.d.ts.map +1 -1
  102. package/dist/schema/null.js +31 -1
  103. package/dist/schema/null.js.map +1 -1
  104. package/dist/schema/nullable.d.ts +42 -0
  105. package/dist/schema/nullable.d.ts.map +1 -1
  106. package/dist/schema/nullable.js +42 -0
  107. package/dist/schema/nullable.js.map +1 -1
  108. package/dist/schema/object.d.ts +57 -0
  109. package/dist/schema/object.d.ts.map +1 -1
  110. package/dist/schema/object.js +53 -1
  111. package/dist/schema/object.js.map +1 -1
  112. package/dist/schema/optional.d.ts +43 -0
  113. package/dist/schema/optional.d.ts.map +1 -1
  114. package/dist/schema/optional.js +43 -0
  115. package/dist/schema/optional.js.map +1 -1
  116. package/dist/schema/params.d.ts +96 -12
  117. package/dist/schema/params.d.ts.map +1 -1
  118. package/dist/schema/params.js +155 -21
  119. package/dist/schema/params.js.map +1 -1
  120. package/dist/schema/payload.d.ts +111 -15
  121. package/dist/schema/payload.d.ts.map +1 -1
  122. package/dist/schema/payload.js +73 -3
  123. package/dist/schema/payload.js.map +1 -1
  124. package/dist/schema/permission-set.d.ts +58 -0
  125. package/dist/schema/permission-set.d.ts.map +1 -1
  126. package/dist/schema/permission-set.js +50 -0
  127. package/dist/schema/permission-set.js.map +1 -1
  128. package/dist/schema/permission.d.ts +42 -0
  129. package/dist/schema/permission.d.ts.map +1 -1
  130. package/dist/schema/permission.js +39 -0
  131. package/dist/schema/permission.js.map +1 -1
  132. package/dist/schema/procedure.d.ts +64 -0
  133. package/dist/schema/procedure.d.ts.map +1 -1
  134. package/dist/schema/procedure.js +64 -0
  135. package/dist/schema/procedure.js.map +1 -1
  136. package/dist/schema/query.d.ts +55 -0
  137. package/dist/schema/query.d.ts.map +1 -1
  138. package/dist/schema/query.js +55 -0
  139. package/dist/schema/query.js.map +1 -1
  140. package/dist/schema/record.d.ts +76 -25
  141. package/dist/schema/record.d.ts.map +1 -1
  142. package/dist/schema/record.js +21 -0
  143. package/dist/schema/record.js.map +1 -1
  144. package/dist/schema/ref.d.ts +51 -0
  145. package/dist/schema/ref.d.ts.map +1 -1
  146. package/dist/schema/ref.js +18 -0
  147. package/dist/schema/ref.js.map +1 -1
  148. package/dist/schema/refine.d.ts +58 -9
  149. package/dist/schema/refine.d.ts.map +1 -1
  150. package/dist/schema/refine.js.map +1 -1
  151. package/dist/schema/regexp.d.ts +45 -0
  152. package/dist/schema/regexp.d.ts.map +1 -1
  153. package/dist/schema/regexp.js +46 -1
  154. package/dist/schema/regexp.js.map +1 -1
  155. package/dist/schema/string.d.ts +72 -6
  156. package/dist/schema/string.d.ts.map +1 -1
  157. package/dist/schema/string.js +56 -8
  158. package/dist/schema/string.js.map +1 -1
  159. package/dist/schema/subscription.d.ts +72 -2
  160. package/dist/schema/subscription.d.ts.map +1 -1
  161. package/dist/schema/subscription.js +59 -0
  162. package/dist/schema/subscription.js.map +1 -1
  163. package/dist/schema/token.d.ts +48 -0
  164. package/dist/schema/token.d.ts.map +1 -1
  165. package/dist/schema/token.js +49 -1
  166. package/dist/schema/token.js.map +1 -1
  167. package/dist/schema/typed-object.d.ts +73 -23
  168. package/dist/schema/typed-object.d.ts.map +1 -1
  169. package/dist/schema/typed-object.js +20 -1
  170. package/dist/schema/typed-object.js.map +1 -1
  171. package/dist/schema/typed-ref.d.ts +54 -0
  172. package/dist/schema/typed-ref.d.ts.map +1 -1
  173. package/dist/schema/typed-ref.js +16 -0
  174. package/dist/schema/typed-ref.js.map +1 -1
  175. package/dist/schema/typed-union.d.ts +51 -1
  176. package/dist/schema/typed-union.d.ts.map +1 -1
  177. package/dist/schema/typed-union.js +52 -2
  178. package/dist/schema/typed-union.js.map +1 -1
  179. package/dist/schema/union.d.ts +46 -0
  180. package/dist/schema/union.d.ts.map +1 -1
  181. package/dist/schema/union.js +41 -0
  182. package/dist/schema/union.js.map +1 -1
  183. package/dist/schema/unknown.d.ts +34 -0
  184. package/dist/schema/unknown.d.ts.map +1 -1
  185. package/dist/schema/unknown.js +34 -0
  186. package/dist/schema/unknown.js.map +1 -1
  187. package/dist/schema/with-default.d.ts +45 -0
  188. package/dist/schema/with-default.d.ts.map +1 -1
  189. package/dist/schema/with-default.js +45 -0
  190. package/dist/schema/with-default.js.map +1 -1
  191. package/dist/schema.d.ts +2 -1
  192. package/dist/schema.d.ts.map +1 -1
  193. package/dist/schema.js +2 -1
  194. package/dist/schema.js.map +1 -1
  195. package/dist/util/if-any.d.ts +2 -0
  196. package/dist/util/if-any.d.ts.map +1 -0
  197. package/dist/util/if-any.js +3 -0
  198. package/dist/util/if-any.js.map +1 -0
  199. package/package.json +3 -3
  200. package/src/core/$type.ts +150 -18
  201. package/src/core/record-key.ts +44 -0
  202. package/src/core/result.ts +86 -4
  203. package/src/core/schema.ts +244 -9
  204. package/src/core/string-format.ts +259 -13
  205. package/src/core/types.ts +91 -3
  206. package/src/core/validation-error.ts +60 -0
  207. package/src/core/validation-issue.ts +68 -2
  208. package/src/core/validator.ts +373 -12
  209. package/src/helpers.test.ts +110 -29
  210. package/src/helpers.ts +54 -25
  211. package/src/schema/array.test.ts +94 -79
  212. package/src/schema/array.ts +48 -1
  213. package/src/schema/blob.ts +50 -1
  214. package/src/schema/boolean.ts +31 -1
  215. package/src/schema/bytes.ts +41 -1
  216. package/src/schema/cid.ts +41 -1
  217. package/src/schema/custom.ts +68 -1
  218. package/src/schema/dict.ts +47 -1
  219. package/src/schema/discriminated-union.ts +61 -1
  220. package/src/schema/enum.ts +50 -0
  221. package/src/schema/integer.ts +45 -1
  222. package/src/schema/intersection.ts +56 -0
  223. package/src/schema/{unknown-object.test.ts → lex-map.test.ts} +9 -9
  224. package/src/schema/lex-map.ts +63 -0
  225. package/src/schema/lex-value.test.ts +81 -0
  226. package/src/schema/lex-value.ts +86 -0
  227. package/src/schema/literal.ts +46 -0
  228. package/src/schema/never.ts +45 -1
  229. package/src/schema/null.ts +32 -1
  230. package/src/schema/nullable.ts +43 -0
  231. package/src/schema/object.ts +59 -1
  232. package/src/schema/optional.ts +44 -0
  233. package/src/schema/params.test.ts +133 -38
  234. package/src/schema/params.ts +237 -37
  235. package/src/schema/payload.test.ts +3 -3
  236. package/src/schema/payload.ts +145 -42
  237. package/src/schema/permission-set.ts +58 -0
  238. package/src/schema/permission.ts +42 -0
  239. package/src/schema/procedure.ts +64 -0
  240. package/src/schema/query.ts +55 -0
  241. package/src/schema/record.ts +82 -16
  242. package/src/schema/ref.ts +52 -0
  243. package/src/schema/refine.ts +58 -9
  244. package/src/schema/regexp.ts +47 -1
  245. package/src/schema/string.test.ts +99 -2
  246. package/src/schema/string.ts +108 -15
  247. package/src/schema/subscription.ts +72 -2
  248. package/src/schema/token.ts +50 -1
  249. package/src/schema/typed-object.ts +81 -16
  250. package/src/schema/typed-ref.ts +55 -0
  251. package/src/schema/typed-union.ts +58 -3
  252. package/src/schema/union.ts +47 -0
  253. package/src/schema/unknown.ts +35 -0
  254. package/src/schema/with-default.ts +46 -0
  255. package/src/schema.ts +2 -1
  256. package/src/util/if-any.ts +3 -0
  257. package/dist/schema/unknown-object.d.ts +0 -8
  258. package/dist/schema/unknown-object.d.ts.map +0 -1
  259. package/dist/schema/unknown-object.js +0 -19
  260. package/dist/schema/unknown-object.js.map +0 -1
  261. package/src/schema/unknown-object.ts +0 -19
@@ -3,7 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RefSchema = void 0;
4
4
  exports.ref = ref;
5
5
  const core_js_1 = require("../core.js");
6
+ /**
7
+ * Schema for creating references to other schemas with lazy resolution.
8
+ *
9
+ * Useful for handling circular references or breaking module dependency cycles.
10
+ * The referenced schema is resolved lazily when first needed for validation.
11
+ *
12
+ * @template TValidator - The referenced validator type
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // Self-referential schema for tree structure
17
+ * const nodeSchema = l.object({
18
+ * value: l.string(),
19
+ * children: l.array(l.ref(() => nodeSchema)),
20
+ * })
21
+ * ```
22
+ */
6
23
  class RefSchema extends core_js_1.Schema {
24
+ type = 'ref';
7
25
  #getter;
8
26
  constructor(getter) {
9
27
  // @NOTE In order to avoid circular dependency issues, we don't resolve
@@ -1 +1 @@
1
- {"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":";;;AAkDA,kBAIC;AAtDD,wCAOmB;AAInB,MAAa,SACX,SAAQ,gBAIP;IAGD,OAAO,CAA6B;IAEpC,YAAY,MAAmC;QAC7C,uEAAuE;QACvE,sEAAsE;QAEtE,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACvB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IAC5C,CAAC;CACF;AA9BD,8BA8BC;AASD,SAAgB,GAAG,CACjB,GAAgC;IAEhC,OAAO,IAAI,SAAS,CAAa,GAAG,CAAC,CAAA;AACvC,CAAC","sourcesContent":["import {\n InferInput,\n InferOutput,\n Schema,\n ValidationContext,\n Validator,\n WrappedValidator,\n} from '../core.js'\n\nexport type RefSchemaGetter<out TValidator extends Validator> = () => TValidator\n\nexport class RefSchema<const TValidator extends Validator>\n extends Schema<\n InferInput<TValidator>,\n InferOutput<TValidator>,\n TValidator['__lex']\n >\n implements WrappedValidator<TValidator>\n{\n #getter: RefSchemaGetter<TValidator>\n\n constructor(getter: RefSchemaGetter<TValidator>) {\n // @NOTE In order to avoid circular dependency issues, we don't resolve\n // the schema here. Instead, we resolve it lazily when first accessed.\n\n super()\n\n this.#getter = getter\n }\n\n get validator(): TValidator {\n return this.#getter.call(null)\n }\n\n unwrap(): TValidator {\n return this.validator\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n return ctx.validate(input, this.validator)\n }\n}\n\n/*@__NO_SIDE_EFFECTS__*/\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n): RefSchema<TValidator>\nexport function ref<TInput, TOutput extends TInput = TInput>(\n get: RefSchemaGetter<Validator<TInput, TOutput>>,\n): RefSchema<Validator<TInput, TOutput>>\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n) {\n return new RefSchema<TValidator>(get)\n}\n"]}
1
+ {"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":";;;AAsGA,kBAIC;AA1GD,wCAOmB;AASnB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,SACX,SAAQ,gBAIP;IAGQ,IAAI,GAAG,KAAc,CAAA;IAE9B,OAAO,CAA6B;IAEpC,YAAY,MAAmC;QAC7C,uEAAuE;QACvE,sEAAsE;QAEtE,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACvB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IAC5C,CAAC;CACF;AAhCD,8BAgCC;AAqCD,SAAgB,GAAG,CACjB,GAAgC;IAEhC,OAAO,IAAI,SAAS,CAAa,GAAG,CAAC,CAAA;AACvC,CAAC","sourcesContent":["import {\n InferInput,\n InferOutput,\n Schema,\n ValidationContext,\n Validator,\n WrappedValidator,\n} from '../core.js'\n\n/**\n * Function type that returns a validator, used for lazy schema resolution.\n *\n * @template TValidator - The validator type that will be returned\n */\nexport type RefSchemaGetter<out TValidator extends Validator> = () => TValidator\n\n/**\n * Schema for creating references to other schemas with lazy resolution.\n *\n * Useful for handling circular references or breaking module dependency cycles.\n * The referenced schema is resolved lazily when first needed for validation.\n *\n * @template TValidator - The referenced validator type\n *\n * @example\n * ```ts\n * // Self-referential schema for tree structure\n * const nodeSchema = l.object({\n * value: l.string(),\n * children: l.array(l.ref(() => nodeSchema)),\n * })\n * ```\n */\nexport class RefSchema<const TValidator extends Validator>\n extends Schema<\n InferInput<TValidator>,\n InferOutput<TValidator>,\n TValidator['__lex']\n >\n implements WrappedValidator<TValidator>\n{\n readonly type = 'ref' as const\n\n #getter: RefSchemaGetter<TValidator>\n\n constructor(getter: RefSchemaGetter<TValidator>) {\n // @NOTE In order to avoid circular dependency issues, we don't resolve\n // the schema here. Instead, we resolve it lazily when first accessed.\n\n super()\n\n this.#getter = getter\n }\n\n get validator(): TValidator {\n return this.#getter.call(null)\n }\n\n unwrap(): TValidator {\n return this.validator\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n return ctx.validate(input, this.validator)\n }\n}\n\n/**\n * Creates a reference schema with lazy resolution.\n *\n * Allows referencing schemas that may not be defined yet, enabling\n * circular references and breaking dependency cycles. The getter function\n * is called lazily when validation is first performed.\n *\n * @param get - Function that returns the referenced validator\n * @returns A new {@link RefSchema} instance\n *\n * @example\n * ```ts\n * // Circular reference - tree node that contains children of the same type\n * const treeNodeSchema = l.object({\n * name: l.string(),\n * children: l.optional(l.array(l.ref(() => treeNodeSchema))),\n * })\n *\n * // Cross-module reference\n * const commentSchema = l.object({\n * text: l.string(),\n * author: l.ref(() => userSchema), // userSchema defined elsewhere\n * })\n *\n * // Explicitly typed reference\n * const itemSchema = l.ref<Item>(() => complexItemSchema)\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n): RefSchema<TValidator>\nexport function ref<TInput, TOutput extends TInput = TInput>(\n get: RefSchemaGetter<Validator<TInput, TOutput>>,\n): RefSchema<Validator<TInput, TOutput>>\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n) {\n return new RefSchema<TValidator>(get)\n}\n"]}
@@ -1,32 +1,81 @@
1
1
  import { InferInput, PropertyKey, Validator } from '../core.js';
2
2
  import { CustomAssertionContext } from './custom.js';
3
+ /**
4
+ * Configuration for a refinement check that validates a condition.
5
+ *
6
+ * @template T - The type being validated
7
+ * @property check - Function that returns true if the value passes the check
8
+ * @property message - Error message when the check fails
9
+ * @property path - Optional path to associate with the error
10
+ */
3
11
  export type RefinementCheck<T> = {
4
12
  check: (value: T, ctx: CustomAssertionContext) => boolean;
5
13
  message: string;
6
14
  path?: PropertyKey | readonly PropertyKey[];
7
15
  };
16
+ /**
17
+ * Configuration for a refinement assertion that narrows the type.
18
+ *
19
+ * @template T - The input type being validated
20
+ * @template Out - The narrowed output type
21
+ * @property check - Type guard function that narrows the type
22
+ * @property message - Error message when the assertion fails
23
+ * @property path - Optional path to associate with the error
24
+ */
8
25
  export type RefinementAssertion<T, Out extends T> = {
9
26
  check: (this: null, value: T, ctx: CustomAssertionContext) => value is Out;
10
27
  message: string;
11
28
  path?: PropertyKey | readonly PropertyKey[];
12
29
  };
30
+ /**
31
+ * Infers the input type from a refinement configuration.
32
+ *
33
+ * @template R - The refinement type
34
+ */
13
35
  export type InferRefinement<R> = R extends RefinementCheck<infer T> ? T : R extends RefinementAssertion<infer T, any> ? T : never;
36
+ /**
37
+ * Union type of refinement check or assertion.
38
+ *
39
+ * @template T - The input type being validated
40
+ * @template Out - The output type (same as T for checks, narrowed for assertions)
41
+ */
14
42
  export type Refinement<T = any, Out extends T = T> = RefinementCheck<T> | RefinementAssertion<T, Out>;
15
43
  /**
16
- * Create a refined schema based on an existing schema and a refinement check.
44
+ * Creates a refined schema by adding additional validation constraints.
17
45
  *
18
- * @param schema - The base schema to refine.
19
- * @param refinement - The refinement check to apply.
20
- * @returns A new schema that includes the refinement.
21
- * @example
46
+ * Wraps an existing schema with an additional check function. The base schema
47
+ * is validated first, then the refinement check is applied to the result.
22
48
  *
49
+ * @param schema - The base schema to refine
50
+ * @param refinement - The refinement check or assertion to apply
51
+ * @returns A new schema that includes the refinement
52
+ *
53
+ * @example
23
54
  * ```ts
24
- * const PositiveInt = refine(l.integer(), {
55
+ * // Simple check refinement
56
+ * const positiveInt = l.refine(l.integer(), {
25
57
  * check: (value) => value > 0,
26
- * message: 'Value must be a positive integer',
58
+ * message: 'Value must be positive',
27
59
  * })
28
- * const result = PositiveInt.validate(-5)
29
- * // result.success === false
60
+ *
61
+ * positiveInt.parse(5) // 5
62
+ * positiveInt.parse(-1) // throws
63
+ *
64
+ * // Type-narrowing assertion
65
+ * const nonEmptyString = l.refine(l.string(), {
66
+ * check: (value): value is string & { length: number } => value.length > 0,
67
+ * message: 'String must not be empty',
68
+ * })
69
+ *
70
+ * // With custom path for nested errors
71
+ * const validDateRange = l.refine(
72
+ * l.object({ start: l.string(), end: l.string() }),
73
+ * {
74
+ * check: (v) => new Date(v.start) < new Date(v.end),
75
+ * message: 'Start date must be before end date',
76
+ * path: ['end'],
77
+ * }
78
+ * )
30
79
  * ```
31
80
  */
32
81
  export declare function refine<const TValidator extends Validator, TInput extends InferInput<TValidator>>(schema: TValidator, refinement: RefinementAssertion<InferInput<TValidator>, TInput>): TValidator & Validator<TInput>;
@@ -1 +1 @@
1
- {"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,WAAW,EAGX,SAAS,EACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEpD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAA;IACzD,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI;IAClD,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,sBAAsB,KAAK,KAAK,IAAI,GAAG,CAAA;IAC1E,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,IAC3B,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,CAAC,GAC9B,CAAC,GACD,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GACzC,CAAC,GACD,KAAK,CAAA;AAEb,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,IAC7C,eAAe,CAAC,CAAC,CAAC,GAClB,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAE/B;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CACpB,KAAK,CAAC,UAAU,SAAS,SAAS,EAClC,MAAM,SAAS,UAAU,CAAC,UAAU,CAAC,EAErC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,mBAAmB,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,GAC9D,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;AACjC,wBAAgB,MAAM,CAAC,KAAK,CAAC,UAAU,SAAS,SAAS,EACvD,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,GAClD,UAAU,CAAA;AACb,wBAAgB,MAAM,CACpB,WAAW,SAAS,UAAU,EAC9B,KAAK,CAAC,UAAU,SAAS,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,EAChE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,GAAG,UAAU,CAAA"}
1
+ {"version":3,"file":"refine.d.ts","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,WAAW,EAGX,SAAS,EACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEpD;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAA;IACzD,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;CAC5C,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,IAAI;IAClD,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,sBAAsB,KAAK,KAAK,IAAI,GAAG,CAAA;IAC1E,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;CAC5C,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAC3B,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,CAAC,GAC9B,CAAC,GACD,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GACzC,CAAC,GACD,KAAK,CAAA;AAEb;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,IAC7C,eAAe,CAAC,CAAC,CAAC,GAClB,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,MAAM,CACpB,KAAK,CAAC,UAAU,SAAS,SAAS,EAClC,MAAM,SAAS,UAAU,CAAC,UAAU,CAAC,EAErC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,mBAAmB,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,GAC9D,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;AACjC,wBAAgB,MAAM,CAAC,KAAK,CAAC,UAAU,SAAS,SAAS,EACvD,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,GAClD,UAAU,CAAA;AACb,wBAAgB,MAAM,CACpB,WAAW,SAAS,UAAU,EAC9B,KAAK,CAAC,UAAU,SAAS,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,EAChE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,GAAG,UAAU,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":";;AAkEA,wBAiBC;AAnFD,wCAOmB;AA0DnB,wBAAwB;AACxB,SAAgB,MAAM,CACpB,MAAkB,EAClB,UAA+B;IAE/B,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,6BAA6B;IAC7B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;QAC3B,iBAAiB,EAAE;YACjB,8DAA8D;YAC9D,KAAK,EAAE,wBAAwB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC5D,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;SACnB;KACF,CAAC,CAAA;AACJ,CAAC;AAED,wBAAwB;AACxB,SAAS,wBAAwB,CAK/B,KAAc,EACd,GAAsB;IAEtB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAA;IAElC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACvE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACjD,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,qBAAW,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import {\n InferInput,\n IssueCustom,\n PropertyKey,\n ValidationContext,\n ValidationResult,\n Validator,\n} from '../core.js'\nimport { CustomAssertionContext } from './custom.js'\n\nexport type RefinementCheck<T> = {\n check: (value: T, ctx: CustomAssertionContext) => boolean\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\nexport type RefinementAssertion<T, Out extends T> = {\n check: (this: null, value: T, ctx: CustomAssertionContext) => value is Out\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\nexport type InferRefinement<R> =\n R extends RefinementCheck<infer T>\n ? T\n : R extends RefinementAssertion<infer T, any>\n ? T\n : never\n\nexport type Refinement<T = any, Out extends T = T> =\n | RefinementCheck<T>\n | RefinementAssertion<T, Out>\n\n/**\n * Create a refined schema based on an existing schema and a refinement check.\n *\n * @param schema - The base schema to refine.\n * @param refinement - The refinement check to apply.\n * @returns A new schema that includes the refinement.\n * @example\n *\n * ```ts\n * const PositiveInt = refine(l.integer(), {\n * check: (value) => value > 0,\n * message: 'Value must be a positive integer',\n * })\n * const result = PositiveInt.validate(-5)\n * // result.success === false\n * ```\n */\nexport function refine<\n const TValidator extends Validator,\n TInput extends InferInput<TValidator>,\n>(\n schema: TValidator,\n refinement: RefinementAssertion<InferInput<TValidator>, TInput>,\n): TValidator & Validator<TInput>\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: RefinementCheck<InferInput<TValidator>>,\n): TValidator\nexport function refine<\n TRefinement extends Refinement,\n const TValidator extends Validator<InferRefinement<TRefinement>>,\n>(schema: TValidator, refinement: TRefinement): TValidator\n/*@__NO_SIDE_EFFECTS__*/\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: Refinement<unknown>,\n): TValidator {\n // This is basically the same as monkey patching the \"validateInContext\"\n // method to the schema, but done in a way that does not mutate the original\n // schema. This is safe to do because Validators don't update their internal\n // state over their lifetime.\n return Object.create(schema, {\n validateInContext: {\n // We do not use an arrow function to avoid creating a closure\n value: validateInContextUnbound.bind({ schema, refinement }),\n enumerable: false,\n writable: false,\n configurable: true,\n },\n })\n}\n\n/*@__NO_SIDE_EFFECTS__*/\nfunction validateInContextUnbound<S extends Validator>(\n this: {\n schema: S\n refinement: Refinement<InferInput<S>>\n },\n input: unknown,\n ctx: ValidationContext,\n): ValidationResult<InferInput<S>> {\n const result = ctx.validate(input, this.schema)\n if (!result.success) return result\n\n const checkResult = this.refinement.check.call(null, result.value, ctx)\n if (!checkResult) {\n const path = ctx.concatPath(this.refinement.path)\n return ctx.issue(new IssueCustom(path, input, this.refinement.message))\n }\n\n return result\n}\n"]}
1
+ {"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":";;AAmHA,wBAiBC;AApID,wCAOmB;AA2GnB,wBAAwB;AACxB,SAAgB,MAAM,CACpB,MAAkB,EAClB,UAA+B;IAE/B,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,6BAA6B;IAC7B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;QAC3B,iBAAiB,EAAE;YACjB,8DAA8D;YAC9D,KAAK,EAAE,wBAAwB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC5D,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;SACnB;KACF,CAAC,CAAA;AACJ,CAAC;AAED,wBAAwB;AACxB,SAAS,wBAAwB,CAK/B,KAAc,EACd,GAAsB;IAEtB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAA;IAElC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACvE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACjD,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,qBAAW,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import {\n InferInput,\n IssueCustom,\n PropertyKey,\n ValidationContext,\n ValidationResult,\n Validator,\n} from '../core.js'\nimport { CustomAssertionContext } from './custom.js'\n\n/**\n * Configuration for a refinement check that validates a condition.\n *\n * @template T - The type being validated\n * @property check - Function that returns true if the value passes the check\n * @property message - Error message when the check fails\n * @property path - Optional path to associate with the error\n */\nexport type RefinementCheck<T> = {\n check: (value: T, ctx: CustomAssertionContext) => boolean\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\n/**\n * Configuration for a refinement assertion that narrows the type.\n *\n * @template T - The input type being validated\n * @template Out - The narrowed output type\n * @property check - Type guard function that narrows the type\n * @property message - Error message when the assertion fails\n * @property path - Optional path to associate with the error\n */\nexport type RefinementAssertion<T, Out extends T> = {\n check: (this: null, value: T, ctx: CustomAssertionContext) => value is Out\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\n/**\n * Infers the input type from a refinement configuration.\n *\n * @template R - The refinement type\n */\nexport type InferRefinement<R> =\n R extends RefinementCheck<infer T>\n ? T\n : R extends RefinementAssertion<infer T, any>\n ? T\n : never\n\n/**\n * Union type of refinement check or assertion.\n *\n * @template T - The input type being validated\n * @template Out - The output type (same as T for checks, narrowed for assertions)\n */\nexport type Refinement<T = any, Out extends T = T> =\n | RefinementCheck<T>\n | RefinementAssertion<T, Out>\n\n/**\n * Creates a refined schema by adding additional validation constraints.\n *\n * Wraps an existing schema with an additional check function. The base schema\n * is validated first, then the refinement check is applied to the result.\n *\n * @param schema - The base schema to refine\n * @param refinement - The refinement check or assertion to apply\n * @returns A new schema that includes the refinement\n *\n * @example\n * ```ts\n * // Simple check refinement\n * const positiveInt = l.refine(l.integer(), {\n * check: (value) => value > 0,\n * message: 'Value must be positive',\n * })\n *\n * positiveInt.parse(5) // 5\n * positiveInt.parse(-1) // throws\n *\n * // Type-narrowing assertion\n * const nonEmptyString = l.refine(l.string(), {\n * check: (value): value is string & { length: number } => value.length > 0,\n * message: 'String must not be empty',\n * })\n *\n * // With custom path for nested errors\n * const validDateRange = l.refine(\n * l.object({ start: l.string(), end: l.string() }),\n * {\n * check: (v) => new Date(v.start) < new Date(v.end),\n * message: 'Start date must be before end date',\n * path: ['end'],\n * }\n * )\n * ```\n */\nexport function refine<\n const TValidator extends Validator,\n TInput extends InferInput<TValidator>,\n>(\n schema: TValidator,\n refinement: RefinementAssertion<InferInput<TValidator>, TInput>,\n): TValidator & Validator<TInput>\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: RefinementCheck<InferInput<TValidator>>,\n): TValidator\nexport function refine<\n TRefinement extends Refinement,\n const TValidator extends Validator<InferRefinement<TRefinement>>,\n>(schema: TValidator, refinement: TRefinement): TValidator\n/*@__NO_SIDE_EFFECTS__*/\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: Refinement<unknown>,\n): TValidator {\n // This is basically the same as monkey patching the \"validateInContext\"\n // method to the schema, but done in a way that does not mutate the original\n // schema. This is safe to do because Validators don't update their internal\n // state over their lifetime.\n return Object.create(schema, {\n validateInContext: {\n // We do not use an arrow function to avoid creating a closure\n value: validateInContextUnbound.bind({ schema, refinement }),\n enumerable: false,\n writable: false,\n configurable: true,\n },\n })\n}\n\n/*@__NO_SIDE_EFFECTS__*/\nfunction validateInContextUnbound<S extends Validator>(\n this: {\n schema: S\n refinement: Refinement<InferInput<S>>\n },\n input: unknown,\n ctx: ValidationContext,\n): ValidationResult<InferInput<S>> {\n const result = ctx.validate(input, this.schema)\n if (!result.success) return result\n\n const checkResult = this.refinement.check.call(null, result.value, ctx)\n if (!checkResult) {\n const path = ctx.concatPath(this.refinement.path)\n return ctx.issue(new IssueCustom(path, input, this.refinement.message))\n }\n\n return result\n}\n"]}
@@ -1,8 +1,53 @@
1
1
  import { Schema, ValidationContext } from '../core.js';
2
+ /**
3
+ * Schema for validating strings against a regular expression pattern.
4
+ *
5
+ * Validates that the input is a string and matches the provided pattern.
6
+ * The pattern is tested using RegExp.test().
7
+ *
8
+ * @template TValue - The string type (can be narrowed with branded types)
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const schema = new RegexpSchema(/^[a-z]+$/)
13
+ * schema.validate('hello') // success
14
+ * schema.validate('Hello') // fails - uppercase not allowed
15
+ * ```
16
+ */
2
17
  export declare class RegexpSchema<TValue extends string = string> extends Schema<TValue> {
3
18
  readonly pattern: RegExp;
19
+ readonly type: "regexp";
4
20
  constructor(pattern: RegExp);
5
21
  validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<TValue>;
6
22
  }
23
+ /**
24
+ * Creates a regexp schema that validates strings against a pattern.
25
+ *
26
+ * Useful for custom string formats not covered by the built-in format
27
+ * validators.
28
+ *
29
+ * @param pattern - Regular expression pattern to match against
30
+ * @returns A new {@link RegexpSchema} instance
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * // Simple pattern
35
+ * const slugSchema = l.regexp(/^[a-z0-9-]+$/)
36
+ *
37
+ * // With anchors for exact match
38
+ * const uuidSchema = l.regexp(
39
+ * /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
40
+ * )
41
+ *
42
+ * // Semantic versioning
43
+ * const semverSchema = l.regexp(/^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/)
44
+ *
45
+ * // Use in object
46
+ * const configSchema = l.object({
47
+ * name: l.regexp(/^[a-z][a-z0-9-]*$/), // kebab-case identifier
48
+ * version: semverSchema,
49
+ * })
50
+ * ```
51
+ */
7
52
  export declare function regexp<TInput extends string = string>(pattern: RegExp): RegexpSchema<TInput>;
8
53
  //# sourceMappingURL=regexp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"regexp.d.ts","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEtD,qBAAa,YAAY,CACvB,MAAM,SAAS,MAAM,GAAG,MAAM,CAC9B,SAAQ,MAAM,CAAC,MAAM,CAAC;aACM,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;IAI3C,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAWzD;AAGD,wBAAgB,MAAM,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,wBAErE"}
1
+ {"version":3,"file":"regexp.d.ts","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEtD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY,CACvB,MAAM,SAAS,MAAM,GAAG,MAAM,CAC9B,SAAQ,MAAM,CAAC,MAAM,CAAC;aAGM,OAAO,EAAE,MAAM;IAF3C,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;gBAEL,OAAO,EAAE,MAAM;IAI3C,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAWzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,wBAAgB,MAAM,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,wBAErE"}
@@ -3,15 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RegexpSchema = void 0;
4
4
  exports.regexp = regexp;
5
5
  const core_js_1 = require("../core.js");
6
+ /**
7
+ * Schema for validating strings against a regular expression pattern.
8
+ *
9
+ * Validates that the input is a string and matches the provided pattern.
10
+ * The pattern is tested using RegExp.test().
11
+ *
12
+ * @template TValue - The string type (can be narrowed with branded types)
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const schema = new RegexpSchema(/^[a-z]+$/)
17
+ * schema.validate('hello') // success
18
+ * schema.validate('Hello') // fails - uppercase not allowed
19
+ * ```
20
+ */
6
21
  class RegexpSchema extends core_js_1.Schema {
7
22
  pattern;
23
+ type = 'regexp';
8
24
  constructor(pattern) {
9
25
  super();
10
26
  this.pattern = pattern;
11
27
  }
12
28
  validateInContext(input, ctx) {
13
29
  if (typeof input !== 'string') {
14
- return ctx.issueInvalidType(input, 'string');
30
+ return ctx.issueUnexpectedType(input, 'string');
15
31
  }
16
32
  if (!this.pattern.test(input)) {
17
33
  return ctx.issueInvalidFormat(input, this.pattern.toString());
@@ -20,6 +36,35 @@ class RegexpSchema extends core_js_1.Schema {
20
36
  }
21
37
  }
22
38
  exports.RegexpSchema = RegexpSchema;
39
+ /**
40
+ * Creates a regexp schema that validates strings against a pattern.
41
+ *
42
+ * Useful for custom string formats not covered by the built-in format
43
+ * validators.
44
+ *
45
+ * @param pattern - Regular expression pattern to match against
46
+ * @returns A new {@link RegexpSchema} instance
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * // Simple pattern
51
+ * const slugSchema = l.regexp(/^[a-z0-9-]+$/)
52
+ *
53
+ * // With anchors for exact match
54
+ * const uuidSchema = l.regexp(
55
+ * /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
56
+ * )
57
+ *
58
+ * // Semantic versioning
59
+ * const semverSchema = l.regexp(/^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/)
60
+ *
61
+ * // Use in object
62
+ * const configSchema = l.object({
63
+ * name: l.regexp(/^[a-z][a-z0-9-]*$/), // kebab-case identifier
64
+ * version: semverSchema,
65
+ * })
66
+ * ```
67
+ */
23
68
  /*@__NO_SIDE_EFFECTS__*/
24
69
  function regexp(pattern) {
25
70
  return new RegexpSchema(pattern);
@@ -1 +1 @@
1
- {"version":3,"file":"regexp.js","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":";;;AAuBA,wBAEC;AAzBD,wCAAsD;AAEtD,MAAa,YAEX,SAAQ,gBAAc;IACM;IAA5B,YAA4B,OAAe;QACzC,KAAK,EAAE,CAAA;QADmB,YAAO,GAAP,OAAO,CAAQ;IAE3C,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QAC9C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC,KAAe,CAAC,CAAA;IACrC,CAAC;CACF;AAlBD,oCAkBC;AAED,wBAAwB;AACxB,SAAgB,MAAM,CAAiC,OAAe;IACpE,OAAO,IAAI,YAAY,CAAS,OAAO,CAAC,CAAA;AAC1C,CAAC","sourcesContent":["import { Schema, ValidationContext } from '../core.js'\n\nexport class RegexpSchema<\n TValue extends string = string,\n> extends Schema<TValue> {\n constructor(public readonly pattern: RegExp) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (typeof input !== 'string') {\n return ctx.issueInvalidType(input, 'string')\n }\n\n if (!this.pattern.test(input)) {\n return ctx.issueInvalidFormat(input, this.pattern.toString())\n }\n\n return ctx.success(input as TValue)\n }\n}\n\n/*@__NO_SIDE_EFFECTS__*/\nexport function regexp<TInput extends string = string>(pattern: RegExp) {\n return new RegexpSchema<TInput>(pattern)\n}\n"]}
1
+ {"version":3,"file":"regexp.js","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":";;;AAqEA,wBAEC;AAvED,wCAAsD;AAEtD;;;;;;;;;;;;;;GAcG;AACH,MAAa,YAEX,SAAQ,gBAAc;IAGM;IAFnB,IAAI,GAAG,QAAiB,CAAA;IAEjC,YAA4B,OAAe;QACzC,KAAK,EAAE,CAAA;QADmB,YAAO,GAAP,OAAO,CAAQ;IAE3C,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjD,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC,KAAe,CAAC,CAAA;IACrC,CAAC;CACF;AApBD,oCAoBC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAwB;AACxB,SAAgB,MAAM,CAAiC,OAAe;IACpE,OAAO,IAAI,YAAY,CAAS,OAAO,CAAC,CAAA;AAC1C,CAAC","sourcesContent":["import { Schema, ValidationContext } from '../core.js'\n\n/**\n * Schema for validating strings against a regular expression pattern.\n *\n * Validates that the input is a string and matches the provided pattern.\n * The pattern is tested using RegExp.test().\n *\n * @template TValue - The string type (can be narrowed with branded types)\n *\n * @example\n * ```ts\n * const schema = new RegexpSchema(/^[a-z]+$/)\n * schema.validate('hello') // success\n * schema.validate('Hello') // fails - uppercase not allowed\n * ```\n */\nexport class RegexpSchema<\n TValue extends string = string,\n> extends Schema<TValue> {\n readonly type = 'regexp' as const\n\n constructor(public readonly pattern: RegExp) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (typeof input !== 'string') {\n return ctx.issueUnexpectedType(input, 'string')\n }\n\n if (!this.pattern.test(input)) {\n return ctx.issueInvalidFormat(input, this.pattern.toString())\n }\n\n return ctx.success(input as TValue)\n }\n}\n\n/**\n * Creates a regexp schema that validates strings against a pattern.\n *\n * Useful for custom string formats not covered by the built-in format\n * validators.\n *\n * @param pattern - Regular expression pattern to match against\n * @returns A new {@link RegexpSchema} instance\n *\n * @example\n * ```ts\n * // Simple pattern\n * const slugSchema = l.regexp(/^[a-z0-9-]+$/)\n *\n * // With anchors for exact match\n * const uuidSchema = l.regexp(\n * /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n * )\n *\n * // Semantic versioning\n * const semverSchema = l.regexp(/^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$/)\n *\n * // Use in object\n * const configSchema = l.object({\n * name: l.regexp(/^[a-z][a-z0-9-]*$/), // kebab-case identifier\n * version: semverSchema,\n * })\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function regexp<TInput extends string = string>(pattern: RegExp) {\n return new RegexpSchema<TInput>(pattern)\n}\n"]}
@@ -1,18 +1,84 @@
1
- import { InferStringFormat, Schema, StringFormat, ValidationContext } from '../core.js';
1
+ import { InferStringFormat, Restricted, Schema, StringFormat, UnknownString, ValidationContext } from '../core.js';
2
+ import { IfAny } from '../util/if-any.js';
3
+ /**
4
+ * Configuration options for string schema validation.
5
+ *
6
+ * @property format - Expected string format (e.g., 'datetime', 'uri', 'at-uri', 'did', 'handle', 'nsid', 'cid', 'tid', 'record-key', 'at-identifier', 'language')
7
+ * @property knownValues - Known string literal values for type narrowing
8
+ * @property minLength - Minimum length in UTF-8 bytes
9
+ * @property maxLength - Maximum length in UTF-8 bytes
10
+ * @property minGraphemes - Minimum number of grapheme clusters
11
+ * @property maxGraphemes - Maximum number of grapheme clusters
12
+ */
2
13
  export type StringSchemaOptions = {
3
14
  format?: StringFormat;
15
+ knownValues?: readonly string[];
4
16
  minLength?: number;
5
17
  maxLength?: number;
6
18
  minGraphemes?: number;
7
19
  maxGraphemes?: number;
8
20
  };
9
- export declare class StringSchema<const TOptions extends StringSchemaOptions = StringSchemaOptions> extends Schema<TOptions extends {
21
+ /**
22
+ * Schema for validating string values with optional format and length constraints.
23
+ *
24
+ * Supports various string formats defined in the Lexicon specification, as well as
25
+ * length constraints measured in UTF-8 bytes or grapheme clusters.
26
+ *
27
+ * @template TOptions - The configuration options type
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const schema = new StringSchema({ format: 'datetime', maxLength: 64 })
32
+ * const result = schema.validate('2024-01-15T10:30:00Z')
33
+ * ```
34
+ */
35
+ export declare class StringSchema<const TOptions extends StringSchemaOptions = StringSchemaOptions> extends Schema<IfAny<TOptions, string, TOptions extends {
10
36
  format: infer F extends StringFormat;
11
- } ? InferStringFormat<F> : string> {
12
- readonly options?: TOptions | undefined;
13
- constructor(options?: TOptions | undefined);
37
+ } ? InferStringFormat<F> : TOptions extends {
38
+ knownValues: readonly (infer V extends string)[];
39
+ } ? V | UnknownString : string>> {
40
+ readonly type: "string";
41
+ readonly options: StringSchemaOptions;
42
+ constructor(options: TOptions);
14
43
  validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<string>;
15
44
  }
16
45
  export declare function coerceToString(input: unknown): string | null;
17
- export declare const string: <const O extends StringSchemaOptions = {}>(options?: StringSchemaOptions & O) => StringSchema<O>;
46
+ declare function _string(): StringSchema<NonNullable<unknown>>;
47
+ declare function _string<const TOptions extends {
48
+ knownValues: StringSchemaOptions['knownValues'];
49
+ } & {
50
+ [K in Exclude<keyof StringSchemaOptions, 'knownValues'>]?: Restricted<`An options argument is required when using the "${K}" option`>;
51
+ }>(): StringSchema<IfAny<TOptions, any, {
52
+ knownValues: TOptions['knownValues'];
53
+ }>>;
54
+ declare function _string<const TOptions extends StringSchemaOptions>(options: TOptions | Omit<TOptions, 'knownValues'>): StringSchema<TOptions>;
55
+ /**
56
+ * Creates a string schema with optional format and length constraints.
57
+ *
58
+ * Strings can be validated against various formats (datetime, uri, did, handle, etc.)
59
+ * and constrained by length in UTF-8 bytes or grapheme clusters.
60
+ *
61
+ * @param options - Optional configuration for format and length constraints
62
+ * @returns A new {@link StringSchema} instance
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // Basic string
67
+ * const nameSchema = l.string()
68
+ *
69
+ * // With format validation
70
+ * const dateSchema = l.string({ format: 'datetime' })
71
+ *
72
+ * // With length constraints (UTF-8 bytes)
73
+ * const bioSchema = l.string({ maxLength: 256 })
74
+ *
75
+ * // With grapheme constraints (user-perceived characters)
76
+ * const displayNameSchema = l.string({ maxGraphemes: 64 })
77
+ *
78
+ * // Combining constraints
79
+ * const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })
80
+ * ```
81
+ */
82
+ export declare const string: typeof _string;
83
+ export {};
18
84
  //# sourceMappingURL=string.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/schema/string.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,YAAY,EACZ,iBAAiB,EAElB,MAAM,YAAY,CAAA;AAInB,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,YAAY,CACvB,KAAK,CAAC,QAAQ,SAAS,mBAAmB,GAAG,mBAAmB,CAChE,SAAQ,MAAM,CACd,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,SAAS,YAAY,CAAA;CAAE,GACrD,iBAAiB,CAAC,CAAC,CAAC,GACpB,MAAM,CACX;IACa,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ;gBAAlB,OAAO,CAAC,EAAE,QAAQ,YAAA;IAIvC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAqDzD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAsC5D;AAED,eAAO,MAAM,MAAM,SACX,CAAC,SAAS,mBAAmB,iBACzB,mBAAmB,GAAG,CAAC,oBAEjC,CAAA"}
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/schema/string.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,aAAa,EACb,iBAAiB,EAElB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAIzC;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY,CACvB,KAAK,CAAC,QAAQ,SAAS,mBAAmB,GAAG,mBAAmB,CAChE,SAAQ,MAAM,CACd,KAAK,CACH,QAAQ,EACR,MAAM,EACN,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,SAAS,YAAY,CAAA;CAAE,GACrD,iBAAiB,CAAC,CAAC,CAAC,GACpB,QAAQ,SAAS;IAAE,WAAW,EAAE,SAAS,CAAC,MAAM,CAAC,SAAS,MAAM,CAAC,EAAE,CAAA;CAAE,GACnE,CAAC,GAAG,aAAa,GACjB,MAAM,CACb,CACF;IACC,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;IAOjC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAA;gBAEzB,OAAO,EAAE,QAAQ;IAK7B,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAqDzD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAsC5D;AAED,iBAAS,OAAO,IAAI,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;AACtD,iBAAS,OAAO,CAId,KAAK,CAAC,QAAQ,SAAS;IACrB,WAAW,EAAE,mBAAmB,CAAC,aAAa,CAAC,CAAA;CAChD,GAAG;KACD,CAAC,IAAI,OAAO,CACX,MAAM,mBAAmB,EACzB,aAAa,CACd,CAAC,CAAC,EAAE,UAAU,CAAC,mDAAmD,CAAC,UAAU,CAAC;CAChF,KACE,YAAY,CACf,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE;IAAE,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAA;CAAE,CAAC,CAC/D,CAAA;AACD,iBAAS,OAAO,CAAC,KAAK,CAAC,QAAQ,SAAS,mBAAmB,EAIzD,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,GAChD,YAAY,CAAC,QAAQ,CAAC,CAAA;AAKzB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,MAAM,gBAAyC,CAAA"}
@@ -6,7 +6,27 @@ const lex_data_1 = require("@atproto/lex-data");
6
6
  const core_js_1 = require("../core.js");
7
7
  const memoize_js_1 = require("../util/memoize.js");
8
8
  const token_js_1 = require("./token.js");
9
+ /**
10
+ * Schema for validating string values with optional format and length constraints.
11
+ *
12
+ * Supports various string formats defined in the Lexicon specification, as well as
13
+ * length constraints measured in UTF-8 bytes or grapheme clusters.
14
+ *
15
+ * @template TOptions - The configuration options type
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const schema = new StringSchema({ format: 'datetime', maxLength: 64 })
20
+ * const result = schema.validate('2024-01-15T10:30:00Z')
21
+ * ```
22
+ */
9
23
  class StringSchema extends core_js_1.Schema {
24
+ type = 'string';
25
+ // @NOTE since the _string utility allows omitting knownValues when TOptions
26
+ // *does* include it (since it's only used for typing), we cannot type options
27
+ // as TOptions directly since it may not actually include knownValues at
28
+ // runtime, making schema.options.knownValues potentially undefined even when
29
+ // TOptions includes it.
10
30
  options;
11
31
  constructor(options) {
12
32
  super();
@@ -15,16 +35,16 @@ class StringSchema extends core_js_1.Schema {
15
35
  validateInContext(input, ctx) {
16
36
  const str = coerceToString(input);
17
37
  if (str == null) {
18
- return ctx.issueInvalidType(input, 'string');
38
+ return ctx.issueUnexpectedType(input, 'string');
19
39
  }
20
40
  let lazyUtf8Len;
21
- const minLength = this.options?.minLength;
41
+ const minLength = this.options.minLength;
22
42
  if (minLength != null) {
23
43
  if ((lazyUtf8Len ??= (0, lex_data_1.utf8Len)(str)) < minLength) {
24
44
  return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len);
25
45
  }
26
46
  }
27
- const maxLength = this.options?.maxLength;
47
+ const maxLength = this.options.maxLength;
28
48
  if (maxLength != null) {
29
49
  // Optimization: we can avoid computing the UTF-8 length if the maximum
30
50
  // possible length, in bytes, of the input JS string is smaller than the
@@ -37,7 +57,7 @@ class StringSchema extends core_js_1.Schema {
37
57
  }
38
58
  }
39
59
  let lazyGraphLen;
40
- const minGraphemes = this.options?.minGraphemes;
60
+ const minGraphemes = this.options.minGraphemes;
41
61
  if (minGraphemes != null) {
42
62
  // Optimization: avoid counting graphemes if the length check already fails
43
63
  if (str.length < minGraphemes) {
@@ -47,13 +67,13 @@ class StringSchema extends core_js_1.Schema {
47
67
  return ctx.issueTooSmall(str, 'grapheme', minGraphemes, lazyGraphLen);
48
68
  }
49
69
  }
50
- const maxGraphemes = this.options?.maxGraphemes;
70
+ const maxGraphemes = this.options.maxGraphemes;
51
71
  if (maxGraphemes != null) {
52
72
  if ((lazyGraphLen ??= (0, lex_data_1.graphemeLen)(str)) > maxGraphemes) {
53
73
  return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen);
54
74
  }
55
75
  }
56
- const format = this.options?.format;
76
+ const format = this.options.format;
57
77
  if (format != null && !(0, core_js_1.isStringFormat)(str, format)) {
58
78
  return ctx.issueInvalidFormat(str, format);
59
79
  }
@@ -97,7 +117,35 @@ function coerceToString(input) {
97
117
  return null;
98
118
  }
99
119
  }
100
- exports.string = (0, memoize_js_1.memoizedOptions)(function (options) {
120
+ function _string(options = {}) {
101
121
  return new StringSchema(options);
102
- });
122
+ }
123
+ /**
124
+ * Creates a string schema with optional format and length constraints.
125
+ *
126
+ * Strings can be validated against various formats (datetime, uri, did, handle, etc.)
127
+ * and constrained by length in UTF-8 bytes or grapheme clusters.
128
+ *
129
+ * @param options - Optional configuration for format and length constraints
130
+ * @returns A new {@link StringSchema} instance
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * // Basic string
135
+ * const nameSchema = l.string()
136
+ *
137
+ * // With format validation
138
+ * const dateSchema = l.string({ format: 'datetime' })
139
+ *
140
+ * // With length constraints (UTF-8 bytes)
141
+ * const bioSchema = l.string({ maxLength: 256 })
142
+ *
143
+ * // With grapheme constraints (user-perceived characters)
144
+ * const displayNameSchema = l.string({ maxGraphemes: 64 })
145
+ *
146
+ * // Combining constraints
147
+ * const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })
148
+ * ```
149
+ */
150
+ exports.string = (0, memoize_js_1.memoizedOptions)(_string);
103
151
  //# sourceMappingURL=string.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/schema/string.ts"],"names":[],"mappings":"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","sourcesContent":["import { graphemeLen, ifCid, utf8Len } from '@atproto/lex-data'\nimport {\n InferStringFormat,\n Schema,\n StringFormat,\n ValidationContext,\n isStringFormat,\n} from '../core.js'\nimport { memoizedOptions } from '../util/memoize.js'\nimport { TokenSchema } from './token.js'\n\nexport type StringSchemaOptions = {\n format?: StringFormat\n minLength?: number\n maxLength?: number\n minGraphemes?: number\n maxGraphemes?: number\n}\n\nexport class StringSchema<\n const TOptions extends StringSchemaOptions = StringSchemaOptions,\n> extends Schema<\n TOptions extends { format: infer F extends StringFormat }\n ? InferStringFormat<F>\n : string\n> {\n constructor(readonly options?: TOptions) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const str = coerceToString(input)\n if (str == null) {\n return ctx.issueInvalidType(input, 'string')\n }\n\n let lazyUtf8Len: number\n\n const minLength = this.options?.minLength\n if (minLength != null) {\n if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {\n return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len)\n }\n }\n\n const maxLength = this.options?.maxLength\n if (maxLength != null) {\n // Optimization: we can avoid computing the UTF-8 length if the maximum\n // possible length, in bytes, of the input JS string is smaller than the\n // maxLength (in UTF-8 string bytes).\n if (str.length * 3 <= maxLength) {\n // Input string so small it can't possibly exceed maxLength\n } else if ((lazyUtf8Len ??= utf8Len(str)) > maxLength) {\n return ctx.issueTooBig(str, 'string', maxLength, lazyUtf8Len)\n }\n }\n\n let lazyGraphLen: number\n\n const minGraphemes = this.options?.minGraphemes\n if (minGraphemes != null) {\n // Optimization: avoid counting graphemes if the length check already fails\n if (str.length < minGraphemes) {\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, str.length)\n } else if ((lazyGraphLen ??= graphemeLen(str)) < minGraphemes) {\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, lazyGraphLen)\n }\n }\n\n const maxGraphemes = this.options?.maxGraphemes\n if (maxGraphemes != null) {\n if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {\n return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen)\n }\n }\n\n const format = this.options?.format\n if (format != null && !isStringFormat(str, format)) {\n return ctx.issueInvalidFormat(str, format)\n }\n\n return ctx.success(str)\n }\n}\n\nexport function coerceToString(input: unknown): string | null {\n switch (typeof input) {\n // @NOTE We do *not* coerce numbers/booleans to strings because that can\n // lead to them being accepted as string instead of being coerced to\n // number/boolean when the input is a string and the expected result is\n // number/boolean (e.g. in params).\n case 'string':\n return input\n case 'object': {\n if (input == null) return null\n\n // @NOTE Allow using TokenSchema instances in places expecting strings,\n // converting them to their string value.\n if (input instanceof TokenSchema) {\n return input.toString()\n }\n\n if (input instanceof Date) {\n if (Number.isNaN(input.getTime())) return null\n return input.toISOString()\n }\n\n if (input instanceof URL) {\n return input.toString()\n }\n\n const cid = ifCid(input)\n if (cid) return cid.toString()\n\n if (input instanceof String) {\n return input.valueOf()\n }\n }\n\n // falls through\n default:\n return null\n }\n}\n\nexport const string = /*#__PURE__*/ memoizedOptions(function <\n const O extends StringSchemaOptions = NonNullable<unknown>,\n>(options?: StringSchemaOptions & O) {\n return new StringSchema<O>(options)\n})\n"]}
1
+ {"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/schema/string.ts"],"names":[],"mappings":"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","sourcesContent":["import { graphemeLen, ifCid, utf8Len } from '@atproto/lex-data'\nimport {\n InferStringFormat,\n Restricted,\n Schema,\n StringFormat,\n UnknownString,\n ValidationContext,\n isStringFormat,\n} from '../core.js'\nimport { IfAny } from '../util/if-any.js'\nimport { memoizedOptions } from '../util/memoize.js'\nimport { TokenSchema } from './token.js'\n\n/**\n * Configuration options for string schema validation.\n *\n * @property format - Expected string format (e.g., 'datetime', 'uri', 'at-uri', 'did', 'handle', 'nsid', 'cid', 'tid', 'record-key', 'at-identifier', 'language')\n * @property knownValues - Known string literal values for type narrowing\n * @property minLength - Minimum length in UTF-8 bytes\n * @property maxLength - Maximum length in UTF-8 bytes\n * @property minGraphemes - Minimum number of grapheme clusters\n * @property maxGraphemes - Maximum number of grapheme clusters\n */\nexport type StringSchemaOptions = {\n format?: StringFormat\n knownValues?: readonly string[]\n minLength?: number\n maxLength?: number\n minGraphemes?: number\n maxGraphemes?: number\n}\n\n/**\n * Schema for validating string values with optional format and length constraints.\n *\n * Supports various string formats defined in the Lexicon specification, as well as\n * length constraints measured in UTF-8 bytes or grapheme clusters.\n *\n * @template TOptions - The configuration options type\n *\n * @example\n * ```ts\n * const schema = new StringSchema({ format: 'datetime', maxLength: 64 })\n * const result = schema.validate('2024-01-15T10:30:00Z')\n * ```\n */\nexport class StringSchema<\n const TOptions extends StringSchemaOptions = StringSchemaOptions,\n> extends Schema<\n IfAny<\n TOptions,\n string,\n TOptions extends { format: infer F extends StringFormat }\n ? InferStringFormat<F>\n : TOptions extends { knownValues: readonly (infer V extends string)[] }\n ? V | UnknownString\n : string\n >\n> {\n readonly type = 'string' as const\n\n // @NOTE since the _string utility allows omitting knownValues when TOptions\n // *does* include it (since it's only used for typing), we cannot type options\n // as TOptions directly since it may not actually include knownValues at\n // runtime, making schema.options.knownValues potentially undefined even when\n // TOptions includes it.\n readonly options: StringSchemaOptions\n\n constructor(options: TOptions) {\n super()\n this.options = options\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const str = coerceToString(input)\n if (str == null) {\n return ctx.issueUnexpectedType(input, 'string')\n }\n\n let lazyUtf8Len: number\n\n const minLength = this.options.minLength\n if (minLength != null) {\n if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {\n return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len)\n }\n }\n\n const maxLength = this.options.maxLength\n if (maxLength != null) {\n // Optimization: we can avoid computing the UTF-8 length if the maximum\n // possible length, in bytes, of the input JS string is smaller than the\n // maxLength (in UTF-8 string bytes).\n if (str.length * 3 <= maxLength) {\n // Input string so small it can't possibly exceed maxLength\n } else if ((lazyUtf8Len ??= utf8Len(str)) > maxLength) {\n return ctx.issueTooBig(str, 'string', maxLength, lazyUtf8Len)\n }\n }\n\n let lazyGraphLen: number\n\n const minGraphemes = this.options.minGraphemes\n if (minGraphemes != null) {\n // Optimization: avoid counting graphemes if the length check already fails\n if (str.length < minGraphemes) {\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, str.length)\n } else if ((lazyGraphLen ??= graphemeLen(str)) < minGraphemes) {\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, lazyGraphLen)\n }\n }\n\n const maxGraphemes = this.options.maxGraphemes\n if (maxGraphemes != null) {\n if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {\n return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen)\n }\n }\n\n const format = this.options.format\n if (format != null && !isStringFormat(str, format)) {\n return ctx.issueInvalidFormat(str, format)\n }\n\n return ctx.success(str)\n }\n}\n\nexport function coerceToString(input: unknown): string | null {\n switch (typeof input) {\n // @NOTE We do *not* coerce numbers/booleans to strings because that can\n // lead to them being accepted as string instead of being coerced to\n // number/boolean when the input is a string and the expected result is\n // number/boolean (e.g. in params).\n case 'string':\n return input\n case 'object': {\n if (input == null) return null\n\n // @NOTE Allow using TokenSchema instances in places expecting strings,\n // converting them to their string value.\n if (input instanceof TokenSchema) {\n return input.toString()\n }\n\n if (input instanceof Date) {\n if (Number.isNaN(input.getTime())) return null\n return input.toISOString()\n }\n\n if (input instanceof URL) {\n return input.toString()\n }\n\n const cid = ifCid(input)\n if (cid) return cid.toString()\n\n if (input instanceof String) {\n return input.valueOf()\n }\n }\n\n // falls through\n default:\n return null\n }\n}\n\nfunction _string(): StringSchema<NonNullable<unknown>>\nfunction _string<\n // Allow calling `string<{ knownValues: [...] }>()` without passing an options\n // object, since knownValues is only used for typing and has no runtime\n // effect, so it can be safely omitted at runtime.\n const TOptions extends {\n knownValues: StringSchemaOptions['knownValues']\n } & {\n [K in Exclude<\n keyof StringSchemaOptions,\n 'knownValues'\n >]?: Restricted<`An options argument is required when using the \"${K}\" option`>\n },\n>(): StringSchema<\n IfAny<TOptions, any, { knownValues: TOptions['knownValues'] }>\n>\nfunction _string<const TOptions extends StringSchemaOptions>(\n // If TOptions is explicitly provided (e.g. `string<{ ... }>({ ... })`), we\n // allow the actual options argument to omit the \"knownValues\" property since\n // it's only used for inferring the type and has no runtime effect.\n options: TOptions | Omit<TOptions, 'knownValues'>,\n): StringSchema<TOptions>\nfunction _string(options: StringSchemaOptions = {}) {\n return new StringSchema(options)\n}\n\n/**\n * Creates a string schema with optional format and length constraints.\n *\n * Strings can be validated against various formats (datetime, uri, did, handle, etc.)\n * and constrained by length in UTF-8 bytes or grapheme clusters.\n *\n * @param options - Optional configuration for format and length constraints\n * @returns A new {@link StringSchema} instance\n *\n * @example\n * ```ts\n * // Basic string\n * const nameSchema = l.string()\n *\n * // With format validation\n * const dateSchema = l.string({ format: 'datetime' })\n *\n * // With length constraints (UTF-8 bytes)\n * const bioSchema = l.string({ maxLength: 256 })\n *\n * // With grapheme constraints (user-perceived characters)\n * const displayNameSchema = l.string({ maxGraphemes: 64 })\n *\n * // Combining constraints\n * const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })\n * ```\n */\nexport const string = /*#__PURE__*/ memoizedOptions(_string)\n"]}