@atproto/lex-schema 0.0.16 → 0.0.17

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 (49) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/core/schema.d.ts +5 -11
  3. package/dist/core/schema.d.ts.map +1 -1
  4. package/dist/core/schema.js +10 -16
  5. package/dist/core/schema.js.map +1 -1
  6. package/dist/core/validation-error.d.ts +2 -2
  7. package/dist/core/validation-error.d.ts.map +1 -1
  8. package/dist/core/validation-error.js +1 -1
  9. package/dist/core/validation-error.js.map +1 -1
  10. package/dist/core/validation-issue.d.ts.map +1 -1
  11. package/dist/core/validation-issue.js +19 -38
  12. package/dist/core/validation-issue.js.map +1 -1
  13. package/dist/helpers.d.ts +4 -3
  14. package/dist/helpers.d.ts.map +1 -1
  15. package/dist/helpers.js +6 -1
  16. package/dist/helpers.js.map +1 -1
  17. package/dist/schema/payload.d.ts.map +1 -1
  18. package/dist/schema/payload.js +2 -3
  19. package/dist/schema/payload.js.map +1 -1
  20. package/dist/schema/record.d.ts +6 -8
  21. package/dist/schema/record.d.ts.map +1 -1
  22. package/dist/schema/record.js +1 -1
  23. package/dist/schema/record.js.map +1 -1
  24. package/dist/schema/regexp.d.ts +3 -2
  25. package/dist/schema/regexp.d.ts.map +1 -1
  26. package/dist/schema/regexp.js +6 -4
  27. package/dist/schema/regexp.js.map +1 -1
  28. package/dist/schema/string.d.ts.map +1 -1
  29. package/dist/schema/string.js +9 -2
  30. package/dist/schema/string.js.map +1 -1
  31. package/dist/schema/typed-object.d.ts +5 -7
  32. package/dist/schema/typed-object.d.ts.map +1 -1
  33. package/dist/schema/typed-object.js +1 -1
  34. package/dist/schema/typed-object.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/core/$type.test.ts +9 -5
  37. package/src/core/schema.ts +19 -16
  38. package/src/core/validation-error.ts +2 -2
  39. package/src/core/validation-issue.ts +20 -36
  40. package/src/helpers.ts +7 -1
  41. package/src/schema/array.test.ts +1 -1
  42. package/src/schema/params.test.ts +2 -2
  43. package/src/schema/payload.ts +2 -3
  44. package/src/schema/record.test.ts +135 -17
  45. package/src/schema/record.ts +14 -9
  46. package/src/schema/regexp.ts +14 -4
  47. package/src/schema/string.ts +8 -2
  48. package/src/schema/typed-object.test.ts +77 -0
  49. package/src/schema/typed-object.ts +11 -10
@@ -1 +1 @@
1
- {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/schema/payload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/schema/payload.ts"],"names":[],"mappings":"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"}
@@ -37,10 +37,9 @@ class Payload {
37
37
  */
38
38
  matchesEncoding(contentType) {
39
39
  const { encoding } = this;
40
- // Handle undefined cases
41
40
  if (encoding === undefined) {
42
- // Expecting no body
43
- return contentType == null;
41
+ // When the output is not defined, we don't enforce any rule on the payload.
42
+ return true;
44
43
  }
45
44
  else if (contentType == null) {
46
45
  // Expecting a body, but got no content-type
@@ -1 +1 @@
1
- {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/schema/payload.ts"],"names":[],"mappings":";;;AAuKA,0BAKC;AA4BD,kCAIC;AA1MD,2CAAkD;AAwElD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,OAAO;IAKP;IACA;IAFX,YACW,QAAmB,EACnB,MAAe;QADf,aAAQ,GAAR,QAAQ,CAAW;QACnB,WAAM,GAAN,MAAM,CAAS;QAExB,IAAI,QAAQ,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACnD,MAAM,IAAI,SAAS,CAAC,qDAAqD,CAAC,CAAA;QAC5E,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,WAA+B;QAC7C,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;QAEzB,yBAAyB;QACzB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,oBAAoB;YACpB,OAAO,WAAW,IAAI,IAAI,CAAA;QAC5B,CAAC;aAAM,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YAC/B,4CAA4C;YAC5C,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,MAAM,IAAI,GAAG,WAAW,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACjD,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/C,CAAC;QAED,gEAAgE;QAChE,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,QAAQ,KAAK,IAAI,CAAA;IAC1B,CAAC;CACF;AA7CD,0BA6CC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAwB;AACxB,SAAgB,OAAO,CAGrB,WAAc,SAAc,EAAE,YAAe,SAAc;IAC3D,OAAO,IAAI,OAAO,CAAO,QAAQ,EAAE,SAAS,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAwB;AACxB,SAAgB,WAAW,CAEzB,UAAa;IACb,OAAO,OAAO,CAAC,kBAAkB,EAAE,IAAA,kBAAM,EAAC,UAAU,CAAC,CAAC,CAAA;AACxD,CAAC","sourcesContent":["import { LexValue } from '@atproto/lex-data'\nimport { InferInput, Schema, Validator } from '../core.js'\nimport { ObjectSchema, object } from './object.js'\n\nexport type { LexValue }\n\ntype ToBodyMime<TEncoding extends string> = TEncoding extends '*/*'\n ? `${string}/${string}`\n : TEncoding extends `${infer T extends string}/*`\n ? `${T}/${string}`\n : TEncoding\n\ntype ToBodyType<\n TEncoding extends string,\n TSchema,\n TBinary,\n> = TSchema extends Schema\n ? InferInput<TSchema>\n : TEncoding extends `application/json`\n ? LexValue\n : TBinary\n\n/**\n * Infers the type of a Payload's encoding and body.\n *\n * @template TPayload - The Payload type\n * @template TBody - Fallback body type for non-JSON encodings\n */\nexport type InferPayload<TPayload extends Payload, TBinary> =\n TPayload extends Payload<infer TEncoding, infer TSchema>\n ? TEncoding extends string\n ? {\n encoding: ToBodyMime<TEncoding>\n body: ToBodyType<TEncoding, TSchema, TBinary>\n }\n : undefined\n : never\n\n/**\n * Converts schema encoding patterns to data encoding types.\n *\n * Handles wildcards like '*\\/*' and 'image/*' in MIME types.\n *\n * @template TPayload - The Payload type\n */\nexport type InferPayloadEncoding<TPayload extends Payload> =\n TPayload extends Payload<infer TEncoding, any>\n ? TEncoding extends string\n ? ToBodyMime<TEncoding>\n : undefined\n : never\n\n/**\n * Infers the body type from a Payload and fallback type.\n *\n * @template TPayload - The Payload type\n * @template TBody - Fallback body type for non-JSON encodings without schema\n */\nexport type InferPayloadBody<TPayload extends Payload, TBinary> =\n TPayload extends Payload<infer TEncoding, infer TSchema>\n ? TEncoding extends string\n ? ToBodyType<TEncoding, TSchema, TBinary>\n : undefined\n : never\n\n/**\n * Determines valid schema type based on encoding presence.\n *\n * @template E - The encoding string type, or undefined\n */\nexport type PayloadSchema<E extends string | undefined> = E extends undefined\n ? undefined\n : Schema<LexValue> | undefined\n\n/**\n * Represents a payload definition for Lexicon endpoints.\n *\n * Payloads define the body format for HTTP requests and responses.\n * They consist of an encoding (MIME type) and an optional schema\n * for validating the body content.\n *\n * @template TEncoding - The MIME type string, or undefined for no body\n * @template TPayload - The schema type for body validation\n *\n * @example\n * ```ts\n * const jsonPayload = new Payload('application/json', l.object({ data: l.string() }))\n * const binaryPayload = new Payload('image/*', undefined)\n * const noPayload = new Payload(undefined, undefined)\n * ```\n */\nexport class Payload<\n const TEncoding extends string | undefined = string | undefined,\n const TSchema extends PayloadSchema<TEncoding> = PayloadSchema<TEncoding>,\n> {\n constructor(\n readonly encoding: TEncoding,\n readonly schema: TSchema,\n ) {\n if (encoding === undefined && schema !== undefined) {\n throw new TypeError('schema cannot be defined when encoding is undefined')\n }\n }\n\n /**\n * Checks whether the given content-type matches the expected payload schema's\n * encoding.\n */\n matchesEncoding(contentType: string | undefined): boolean {\n const { encoding } = this\n\n // Handle undefined cases\n if (encoding === undefined) {\n // Expecting no body\n return contentType == null\n } else if (contentType == null) {\n // Expecting a body, but got no content-type\n return false\n }\n\n if (encoding === '*/*') {\n return true\n }\n\n const mime = contentType?.split(';', 1)[0].trim()\n if (encoding.endsWith('/*')) {\n return mime.startsWith(encoding.slice(0, -1))\n }\n\n // Invalid: Lexicon can only specify \"*/*\" or \"type/*\" wildcards\n if (encoding.includes('*')) {\n return false\n }\n\n return encoding === mime\n }\n}\n\n/**\n * Creates a payload definition for Lexicon endpoint bodies.\n *\n * Defines the expected MIME type and optional validation schema for\n * request or response bodies.\n *\n * @param encoding - MIME type string (e.g., 'application/json', 'image/*'), or undefined for no body\n * @param validator - Optional schema for validating the body content. Must be undefined if encoding is undefined.\n * @returns A new {@link Payload} instance\n *\n * @example\n * ```ts\n * // JSON payload with schema\n * const output = l.payload('application/json', l.object({\n * posts: l.array(postSchema),\n * cursor: l.optional(l.string()),\n * }))\n *\n * // Binary payload (no schema validation)\n * const blobInput = l.payload('*\\/*', undefined)\n *\n * // Image payload with wildcard\n * const imageInput = l.payload('image/*', undefined)\n *\n * // No payload (for endpoints without body)\n * const noBody = l.payload()\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function payload<\n const E extends string | undefined = undefined,\n const S extends PayloadSchema<E> = undefined,\n>(encoding: E = undefined as E, validator: S = undefined as S) {\n return new Payload<E, S>(encoding, validator)\n}\n\n/**\n * Creates a JSON payload with an object schema.\n *\n * Convenience function for the common case of JSON request/response bodies.\n * Equivalent to `l.payload('application/json', l.object(properties))`.\n *\n * @param properties - Object mapping property names to validators\n * @returns A new {@link Payload} instance with 'application/json' encoding\n *\n * @example\n * ```ts\n * // Query output\n * const profileOutput = l.jsonPayload({\n * did: l.string({ format: 'did' }),\n * handle: l.string({ format: 'handle' }),\n * displayName: l.optional(l.string()),\n * })\n *\n * // Procedure input\n * const createPostInput = l.jsonPayload({\n * text: l.string({ maxGraphemes: 300 }),\n * createdAt: l.string({ format: 'datetime' }),\n * })\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function jsonPayload<\n P extends Record<string, Validator<undefined | LexValue>>,\n>(properties: P): Payload<'application/json', ObjectSchema<P>> {\n return payload('application/json', object(properties))\n}\n"]}
1
+ {"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/schema/payload.ts"],"names":[],"mappings":";;;AAsKA,0BAKC;AA4BD,kCAIC;AAzMD,2CAAkD;AAwElD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,OAAO;IAKP;IACA;IAFX,YACW,QAAmB,EACnB,MAAe;QADf,aAAQ,GAAR,QAAQ,CAAW;QACnB,WAAM,GAAN,MAAM,CAAS;QAExB,IAAI,QAAQ,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACnD,MAAM,IAAI,SAAS,CAAC,qDAAqD,CAAC,CAAA;QAC5E,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,WAA+B;QAC7C,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;QAEzB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,4EAA4E;YAC5E,OAAO,IAAI,CAAA;QACb,CAAC;aAAM,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YAC/B,4CAA4C;YAC5C,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,MAAM,IAAI,GAAG,WAAW,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACjD,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/C,CAAC;QAED,gEAAgE;QAChE,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,QAAQ,KAAK,IAAI,CAAA;IAC1B,CAAC;CACF;AA5CD,0BA4CC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAwB;AACxB,SAAgB,OAAO,CAGrB,WAAc,SAAc,EAAE,YAAe,SAAc;IAC3D,OAAO,IAAI,OAAO,CAAO,QAAQ,EAAE,SAAS,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAwB;AACxB,SAAgB,WAAW,CAEzB,UAAa;IACb,OAAO,OAAO,CAAC,kBAAkB,EAAE,IAAA,kBAAM,EAAC,UAAU,CAAC,CAAC,CAAA;AACxD,CAAC","sourcesContent":["import { LexValue } from '@atproto/lex-data'\nimport { InferInput, Schema, Validator } from '../core.js'\nimport { ObjectSchema, object } from './object.js'\n\nexport type { LexValue }\n\ntype ToBodyMime<TEncoding extends string> = TEncoding extends '*/*'\n ? `${string}/${string}`\n : TEncoding extends `${infer T extends string}/*`\n ? `${T}/${string}`\n : TEncoding\n\ntype ToBodyType<\n TEncoding extends string,\n TSchema,\n TBinary,\n> = TSchema extends Schema\n ? InferInput<TSchema>\n : TEncoding extends `application/json`\n ? LexValue\n : TBinary\n\n/**\n * Infers the type of a Payload's encoding and body.\n *\n * @template TPayload - The Payload type\n * @template TBody - Fallback body type for non-JSON encodings\n */\nexport type InferPayload<TPayload extends Payload, TBinary> =\n TPayload extends Payload<infer TEncoding, infer TSchema>\n ? TEncoding extends string\n ? {\n encoding: ToBodyMime<TEncoding>\n body: ToBodyType<TEncoding, TSchema, TBinary>\n }\n : undefined\n : never\n\n/**\n * Converts schema encoding patterns to data encoding types.\n *\n * Handles wildcards like '*\\/*' and 'image/*' in MIME types.\n *\n * @template TPayload - The Payload type\n */\nexport type InferPayloadEncoding<TPayload extends Payload> =\n TPayload extends Payload<infer TEncoding, any>\n ? TEncoding extends string\n ? ToBodyMime<TEncoding>\n : undefined\n : never\n\n/**\n * Infers the body type from a Payload and fallback type.\n *\n * @template TPayload - The Payload type\n * @template TBody - Fallback body type for non-JSON encodings without schema\n */\nexport type InferPayloadBody<TPayload extends Payload, TBinary> =\n TPayload extends Payload<infer TEncoding, infer TSchema>\n ? TEncoding extends string\n ? ToBodyType<TEncoding, TSchema, TBinary>\n : undefined\n : never\n\n/**\n * Determines valid schema type based on encoding presence.\n *\n * @template E - The encoding string type, or undefined\n */\nexport type PayloadSchema<E extends string | undefined> = E extends undefined\n ? undefined\n : Schema<LexValue> | undefined\n\n/**\n * Represents a payload definition for Lexicon endpoints.\n *\n * Payloads define the body format for HTTP requests and responses.\n * They consist of an encoding (MIME type) and an optional schema\n * for validating the body content.\n *\n * @template TEncoding - The MIME type string, or undefined for no body\n * @template TPayload - The schema type for body validation\n *\n * @example\n * ```ts\n * const jsonPayload = new Payload('application/json', l.object({ data: l.string() }))\n * const binaryPayload = new Payload('image/*', undefined)\n * const noPayload = new Payload(undefined, undefined)\n * ```\n */\nexport class Payload<\n const TEncoding extends string | undefined = string | undefined,\n const TSchema extends PayloadSchema<TEncoding> = PayloadSchema<TEncoding>,\n> {\n constructor(\n readonly encoding: TEncoding,\n readonly schema: TSchema,\n ) {\n if (encoding === undefined && schema !== undefined) {\n throw new TypeError('schema cannot be defined when encoding is undefined')\n }\n }\n\n /**\n * Checks whether the given content-type matches the expected payload schema's\n * encoding.\n */\n matchesEncoding(contentType: string | undefined): boolean {\n const { encoding } = this\n\n if (encoding === undefined) {\n // When the output is not defined, we don't enforce any rule on the payload.\n return true\n } else if (contentType == null) {\n // Expecting a body, but got no content-type\n return false\n }\n\n if (encoding === '*/*') {\n return true\n }\n\n const mime = contentType?.split(';', 1)[0].trim()\n if (encoding.endsWith('/*')) {\n return mime.startsWith(encoding.slice(0, -1))\n }\n\n // Invalid: Lexicon can only specify \"*/*\" or \"type/*\" wildcards\n if (encoding.includes('*')) {\n return false\n }\n\n return encoding === mime\n }\n}\n\n/**\n * Creates a payload definition for Lexicon endpoint bodies.\n *\n * Defines the expected MIME type and optional validation schema for\n * request or response bodies.\n *\n * @param encoding - MIME type string (e.g., 'application/json', 'image/*'), or undefined for no body\n * @param validator - Optional schema for validating the body content. Must be undefined if encoding is undefined.\n * @returns A new {@link Payload} instance\n *\n * @example\n * ```ts\n * // JSON payload with schema\n * const output = l.payload('application/json', l.object({\n * posts: l.array(postSchema),\n * cursor: l.optional(l.string()),\n * }))\n *\n * // Binary payload (no schema validation)\n * const blobInput = l.payload('*\\/*', undefined)\n *\n * // Image payload with wildcard\n * const imageInput = l.payload('image/*', undefined)\n *\n * // No payload (for endpoints without body)\n * const noBody = l.payload()\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function payload<\n const E extends string | undefined = undefined,\n const S extends PayloadSchema<E> = undefined,\n>(encoding: E = undefined as E, validator: S = undefined as S) {\n return new Payload<E, S>(encoding, validator)\n}\n\n/**\n * Creates a JSON payload with an object schema.\n *\n * Convenience function for the common case of JSON request/response bodies.\n * Equivalent to `l.payload('application/json', l.object(properties))`.\n *\n * @param properties - Object mapping property names to validators\n * @returns A new {@link Payload} instance with 'application/json' encoding\n *\n * @example\n * ```ts\n * // Query output\n * const profileOutput = l.jsonPayload({\n * did: l.string({ format: 'did' }),\n * handle: l.string({ format: 'handle' }),\n * displayName: l.optional(l.string()),\n * })\n *\n * // Procedure input\n * const createPostInput = l.jsonPayload({\n * text: l.string({ maxGraphemes: 300 }),\n * createdAt: l.string({ format: 'datetime' }),\n * })\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function jsonPayload<\n P extends Record<string, Validator<undefined | LexValue>>,\n>(properties: P): Payload<'application/json', ObjectSchema<P>> {\n return payload('application/json', object(properties))\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { LexMap } from '@atproto/lex-data';
1
2
  import { $Typed, InferInput, InferOutput, LexiconRecordKey, NsidString, Schema, TidString, Unknown$TypedObject, ValidationContext, Validator } from '../core.js';
2
3
  /**
3
4
  * Infers the record key type from a RecordSchema.
@@ -32,9 +33,7 @@ export type TypedRecord<TType extends NsidString, TValue extends {
32
33
  * )
33
34
  * ```
34
35
  */
35
- export declare class RecordSchema<const TKey extends LexiconRecordKey = any, const TType extends NsidString = any, const TShape extends Validator<{
36
- [k: string]: unknown;
37
- }> = any> extends Schema<$Typed<InferInput<TShape>, TType>, $Typed<InferOutput<TShape>, TType>> {
36
+ export declare class RecordSchema<const TKey extends LexiconRecordKey = LexiconRecordKey, const TType extends NsidString = NsidString, const TShape extends Validator<LexMap> = Validator<LexMap>> extends Schema<$Typed<InferInput<TShape>, TType>, $Typed<InferOutput<TShape>, TType>> {
38
37
  readonly key: TKey;
39
38
  readonly $type: TType;
40
39
  readonly schema: TShape;
@@ -42,7 +41,8 @@ export declare class RecordSchema<const TKey extends LexiconRecordKey = any, con
42
41
  keySchema: RecordKeySchema<TKey>;
43
42
  constructor(key: TKey, $type: TType, schema: TShape);
44
43
  validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").LexValidationError | import("../core.js").ValidationSuccess<InferInput<TShape>>;
45
- build(input: Omit<InferInput<this>, '$type'>): $Typed<InferOutput<this>, TType>;
44
+ build(input: Omit<InferOutput<TShape>, '$type'>): $Typed<InferOutput<TShape>, TType>;
45
+ build(input: Omit<InferInput<TShape>, '$type'>): $Typed<InferInput<TShape>, TType>;
46
46
  isTypeOf<TValue extends {
47
47
  $type?: unknown;
48
48
  }>(value: TValue): value is TypedRecord<TType, TValue>;
@@ -104,10 +104,8 @@ type AsNsid<T> = T extends `${string}#${string}` ? never : T;
104
104
  * const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })
105
105
  * ```
106
106
  */
107
- export declare function record<const K extends LexiconRecordKey, const T extends NsidString, const S extends Validator<{
108
- [k: string]: unknown;
109
- }>>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>;
110
- export declare function record<const K extends LexiconRecordKey, const V extends {
107
+ export declare function record<const K extends LexiconRecordKey, const T extends NsidString, const S extends Validator<LexMap>>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>;
108
+ export declare function record<const K extends LexiconRecordKey, const V extends LexMap & {
111
109
  $type: NsidString;
112
110
  }>(key: K, type: AsNsid<V['$type']>, validator: Validator<Omit<V, '$type'>>): RecordSchema<K, V['$type'], Validator<Omit<V, '$type'>>>;
113
111
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"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"}
@@ -50,7 +50,7 @@ class RecordSchema extends core_js_1.Schema {
50
50
  return result;
51
51
  }
52
52
  build(input) {
53
- return this.parse((0, core_js_1.$typed)(input, this.$type));
53
+ return (0, core_js_1.$typed)(input, this.$type);
54
54
  }
55
55
  isTypeOf(value) {
56
56
  return value.$type === this.$type;
@@ -1 +1 @@
1
- {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n $Typed,\n $typed,\n InferInput,\n InferOutput,\n LexiconRecordKey,\n NsidString,\n Schema,\n TidString,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\nimport { literal } from './literal.js'\nimport { string } from './string.js'\n\n/**\n * Infers the record key type from a RecordSchema.\n *\n * @template R - The RecordSchema type\n */\nexport type InferRecordKey<R extends RecordSchema> =\n R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never\n\nexport type TypedRecord<\n TType extends NsidString,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type: TType }\n ? TValue\n : $Typed<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for AT Protocol records with a type identifier and key constraints.\n *\n * Records are the primary data unit in AT Protocol. Each record has a `$type`\n * field identifying its Lexicon schema, and is stored at a specific key\n * (TID, NSID, or other format) in a repository.\n *\n * @template TKey - The record key type ('tid', 'nsid', 'any', or 'literal:...')\n * @template TType - The NSID string identifying this record type\n * @template TShape - The validator type for the record's data shape\n *\n * @example\n * ```ts\n * const postSchema = new RecordSchema(\n * 'tid',\n * 'app.bsky.feed.post',\n * l.object({ text: l.string(), createdAt: l.string() })\n * )\n * ```\n */\nexport class RecordSchema<\n const TKey extends LexiconRecordKey = any,\n const TType extends NsidString = any,\n const TShape extends Validator<{ [k: string]: unknown }> = any,\n> extends Schema<\n $Typed<InferInput<TShape>, TType>,\n $Typed<InferOutput<TShape>, TType>\n> {\n readonly type = 'record' as const\n\n keySchema: RecordKeySchema<TKey>\n\n constructor(\n readonly key: TKey,\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n this.keySchema = recordKey(key)\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const result = ctx.validate(input, this.schema)\n\n if (!result.success) {\n return result\n }\n\n if (result.value.$type !== this.$type) {\n return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])\n }\n\n return result\n }\n\n build(\n input: Omit<InferInput<this>, '$type'>,\n ): $Typed<InferOutput<this>, TType> {\n return this.parse($typed(input, this.$type))\n }\n\n isTypeOf<TValue extends { $type?: unknown }>(\n value: TValue,\n ): value is TypedRecord<TType, TValue> {\n return value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\nexport type RecordKeySchemaOutput<Key extends LexiconRecordKey> =\n Key extends 'any'\n ? string\n : Key extends 'tid'\n ? TidString\n : Key extends 'nsid'\n ? NsidString\n : Key extends `literal:${infer L extends string}`\n ? L\n : never\n\nexport type RecordKeySchema<Key extends LexiconRecordKey> = Schema<\n RecordKeySchemaOutput<Key>\n>\n\nconst keySchema = string({ minLength: 1 })\nconst tidSchema = string({ format: 'tid' })\nconst nsidSchema = string({ format: 'nsid' })\nconst selfLiteralSchema = literal('self')\n\nfunction recordKey<Key extends LexiconRecordKey>(\n key: Key,\n): RecordKeySchema<Key> {\n // @NOTE Use cached instances for common schemas\n if (key === 'any') return keySchema as any\n if (key === 'tid') return tidSchema as any\n if (key === 'nsid') return nsidSchema as any\n if (key.startsWith('literal:')) {\n const value = key.slice(8) as RecordKeySchemaOutput<Key>\n if (value === 'self') return selfLiteralSchema as any\n return literal(value)\n }\n\n throw new Error(`Unsupported record key type: ${key}`)\n}\n\n/**\n * Ensures that a `$type` used in a record is a valid NSID (i.e. no fragment).\n */\ntype AsNsid<T> = T extends `${string}#${string}` ? never : T\n\n/**\n * Creates a record schema for AT Protocol records.\n *\n * Records are the primary data unit in AT Protocol repositories. They have\n * a `$type` field identifying their Lexicon schema, and are stored at keys\n * following a specific format (TID, NSID, etc.).\n *\n * This function offers two overloads:\n * - One that infers the output type from the provided arguments (does not\n * support circular references)\n * - One with an explicitly defined interface for use with codegen and\n * circular references\n *\n * @param key - The record key type: 'tid', 'nsid', 'any', or 'literal:value'\n * @param type - The NSID identifying this record type (e.g., 'app.bsky.feed.post')\n * @param validator - Schema validator for the record's properties\n * @returns A new {@link RecordSchema} instance\n *\n * @example\n * ```ts\n * // Post record with TID key\n * const postSchema = l.record('tid', 'app.bsky.feed.post', l.object({\n * text: l.string({ maxGraphemes: 300 }),\n * createdAt: l.string({ format: 'datetime' }),\n * reply: l.optional(l.object({\n * root: l.ref(() => strongRefSchema),\n * parent: l.ref(() => strongRefSchema),\n * })),\n * }))\n *\n * // Profile record with literal 'self' key\n * const profileSchema = l.record('literal:self', 'app.bsky.actor.profile', l.object({\n * displayName: l.optional(l.string({ maxGraphemes: 64 })),\n * description: l.optional(l.string({ maxGraphemes: 256 })),\n * avatar: l.optional(l.blob({ accept: ['image/*'] })),\n * }))\n *\n * // Build a record with automatic $type injection\n * const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })\n * ```\n */\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<{ [k: string]: unknown }>,\n>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>\nexport function record<\n const K extends LexiconRecordKey,\n const V extends { $type: NsidString },\n>(\n key: K,\n type: AsNsid<V['$type']>,\n validator: Validator<Omit<V, '$type'>>,\n): RecordSchema<K, V['$type'], Validator<Omit<V, '$type'>>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<{ [k: string]: unknown }>,\n>(key: K, type: T, validator: S) {\n return new RecordSchema<K, T, S>(key, type, validator)\n}\n"]}
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":";;;AAyNA,wBAMC;AA9ND,wCAYmB;AACnB,+DAAuD;AACvD,6CAAsC;AACtC,2CAAoC;AAiBpC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAa,YAIX,SAAQ,gBAGT;IAMY;IACA;IACA;IAPF,IAAI,GAAG,QAAiB,CAAA;IAEjC,SAAS,CAAuB;IAEhC,YACW,GAAS,EACT,KAAY,EACZ,MAAc;QAEvB,KAAK,EAAE,CAAA;QAJE,QAAG,GAAH,GAAG,CAAM;QACT,UAAK,GAAL,KAAK,CAAO;QACZ,WAAM,GAAN,MAAM,CAAQ;QAGvB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;IACjC,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAE/C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,MAAM,CAAA;QACf,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,OAAO,GAAG,CAAC,yBAAyB,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAC3E,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAQD,KAAK,CAAC,KAA8B;QAClC,OAAO,IAAA,gBAAM,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,QAAQ,CACN,KAAa;QAEb,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAA;IACnC,CAAC;IAED;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5D,CAAC;IAED;;;OAGG;IACH,IAAI,SAAS;QACX,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAClE,CAAC;CACF;AAlED,oCAkEC;AAiBD,MAAM,SAAS,GAAG,IAAA,kBAAM,EAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAA;AAC1C,MAAM,SAAS,GAAG,IAAA,kBAAM,EAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;AAC3C,MAAM,UAAU,GAAG,IAAA,kBAAM,EAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;AAC7C,MAAM,iBAAiB,GAAG,IAAA,oBAAO,EAAC,MAAM,CAAC,CAAA;AAEzC,SAAS,SAAS,CAChB,GAAQ;IAER,gDAAgD;IAChD,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,SAAgB,CAAA;IAC1C,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,SAAgB,CAAA;IAC1C,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,UAAiB,CAAA;IAC5C,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAA+B,CAAA;QACxD,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,iBAAwB,CAAA;QACrD,OAAO,IAAA,oBAAO,EAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAA;AACxD,CAAC;AA6DD,wBAAwB;AACxB,SAAgB,MAAM,CAIpB,GAAM,EAAE,IAAO,EAAE,SAAY;IAC7B,OAAO,IAAI,YAAY,CAAU,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;AACxD,CAAC","sourcesContent":["import { LexMap } from '@atproto/lex-data'\nimport {\n $Typed,\n $typed,\n InferInput,\n InferOutput,\n LexiconRecordKey,\n NsidString,\n Schema,\n TidString,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\nimport { literal } from './literal.js'\nimport { string } from './string.js'\n\n/**\n * Infers the record key type from a RecordSchema.\n *\n * @template R - The RecordSchema type\n */\nexport type InferRecordKey<R extends RecordSchema> =\n R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never\n\nexport type TypedRecord<\n TType extends NsidString,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type: TType }\n ? TValue\n : $Typed<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for AT Protocol records with a type identifier and key constraints.\n *\n * Records are the primary data unit in AT Protocol. Each record has a `$type`\n * field identifying its Lexicon schema, and is stored at a specific key\n * (TID, NSID, or other format) in a repository.\n *\n * @template TKey - The record key type ('tid', 'nsid', 'any', or 'literal:...')\n * @template TType - The NSID string identifying this record type\n * @template TShape - The validator type for the record's data shape\n *\n * @example\n * ```ts\n * const postSchema = new RecordSchema(\n * 'tid',\n * 'app.bsky.feed.post',\n * l.object({ text: l.string(), createdAt: l.string() })\n * )\n * ```\n */\nexport class RecordSchema<\n const TKey extends LexiconRecordKey = LexiconRecordKey,\n const TType extends NsidString = NsidString,\n const TShape extends Validator<LexMap> = Validator<LexMap>,\n> extends Schema<\n $Typed<InferInput<TShape>, TType>,\n $Typed<InferOutput<TShape>, TType>\n> {\n readonly type = 'record' as const\n\n keySchema: RecordKeySchema<TKey>\n\n constructor(\n readonly key: TKey,\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n this.keySchema = recordKey(key)\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const result = ctx.validate(input, this.schema)\n\n if (!result.success) {\n return result\n }\n\n if (result.value.$type !== this.$type) {\n return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])\n }\n\n return result\n }\n\n build(\n input: Omit<InferOutput<TShape>, '$type'>,\n ): $Typed<InferOutput<TShape>, TType>\n build(\n input: Omit<InferInput<TShape>, '$type'>,\n ): $Typed<InferInput<TShape>, TType>\n build(input: Record<string, unknown>) {\n return $typed(input, this.$type)\n }\n\n isTypeOf<TValue extends { $type?: unknown }>(\n value: TValue,\n ): value is TypedRecord<TType, TValue> {\n return value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\nexport type RecordKeySchemaOutput<Key extends LexiconRecordKey> =\n Key extends 'any'\n ? string\n : Key extends 'tid'\n ? TidString\n : Key extends 'nsid'\n ? NsidString\n : Key extends `literal:${infer L extends string}`\n ? L\n : never\n\nexport type RecordKeySchema<Key extends LexiconRecordKey> = Schema<\n RecordKeySchemaOutput<Key>\n>\n\nconst keySchema = string({ minLength: 1 })\nconst tidSchema = string({ format: 'tid' })\nconst nsidSchema = string({ format: 'nsid' })\nconst selfLiteralSchema = literal('self')\n\nfunction recordKey<Key extends LexiconRecordKey>(\n key: Key,\n): RecordKeySchema<Key> {\n // @NOTE Use cached instances for common schemas\n if (key === 'any') return keySchema as any\n if (key === 'tid') return tidSchema as any\n if (key === 'nsid') return nsidSchema as any\n if (key.startsWith('literal:')) {\n const value = key.slice(8) as RecordKeySchemaOutput<Key>\n if (value === 'self') return selfLiteralSchema as any\n return literal(value)\n }\n\n throw new Error(`Unsupported record key type: ${key}`)\n}\n\n/**\n * Ensures that a `$type` used in a record is a valid NSID (i.e. no fragment).\n */\ntype AsNsid<T> = T extends `${string}#${string}` ? never : T\n\n/**\n * Creates a record schema for AT Protocol records.\n *\n * Records are the primary data unit in AT Protocol repositories. They have\n * a `$type` field identifying their Lexicon schema, and are stored at keys\n * following a specific format (TID, NSID, etc.).\n *\n * This function offers two overloads:\n * - One that infers the output type from the provided arguments (does not\n * support circular references)\n * - One with an explicitly defined interface for use with codegen and\n * circular references\n *\n * @param key - The record key type: 'tid', 'nsid', 'any', or 'literal:value'\n * @param type - The NSID identifying this record type (e.g., 'app.bsky.feed.post')\n * @param validator - Schema validator for the record's properties\n * @returns A new {@link RecordSchema} instance\n *\n * @example\n * ```ts\n * // Post record with TID key\n * const postSchema = l.record('tid', 'app.bsky.feed.post', l.object({\n * text: l.string({ maxGraphemes: 300 }),\n * createdAt: l.string({ format: 'datetime' }),\n * reply: l.optional(l.object({\n * root: l.ref(() => strongRefSchema),\n * parent: l.ref(() => strongRefSchema),\n * })),\n * }))\n *\n * // Profile record with literal 'self' key\n * const profileSchema = l.record('literal:self', 'app.bsky.actor.profile', l.object({\n * displayName: l.optional(l.string({ maxGraphemes: 64 })),\n * description: l.optional(l.string({ maxGraphemes: 256 })),\n * avatar: l.optional(l.blob({ accept: ['image/*'] })),\n * }))\n *\n * // Build a record with automatic $type injection\n * const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })\n * ```\n */\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<LexMap>,\n>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>\nexport function record<\n const K extends LexiconRecordKey,\n const V extends LexMap & { $type: NsidString },\n>(\n key: K,\n type: AsNsid<V['$type']>,\n validator: Validator<Omit<V, '$type'>>,\n): RecordSchema<K, V['$type'], Validator<Omit<V, '$type'>>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<LexMap>,\n>(key: K, type: T, validator: S) {\n return new RecordSchema<K, T, S>(key, type, validator)\n}\n"]}
@@ -16,8 +16,9 @@ import { Schema, ValidationContext } from '../core.js';
16
16
  */
17
17
  export declare class RegexpSchema<TValue extends string = string> extends Schema<TValue> {
18
18
  readonly pattern: RegExp;
19
+ readonly message?: string | undefined;
19
20
  readonly type: "regexp";
20
- constructor(pattern: RegExp);
21
+ constructor(pattern: RegExp, message?: string | undefined);
21
22
  validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<TValue>;
22
23
  }
23
24
  /**
@@ -49,5 +50,5 @@ export declare class RegexpSchema<TValue extends string = string> extends Schema
49
50
  * })
50
51
  * ```
51
52
  */
52
- export declare function regexp<TInput extends string = string>(pattern: RegExp): RegexpSchema<TInput>;
53
+ export declare function regexp<TInput extends string = string>(pattern: RegExp, message?: string): RegexpSchema<TInput>;
53
54
  //# 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;;;;;;;;;;;;;;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"}
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;aAIJ,OAAO,EAAE,MAAM;aACf,OAAO,CAAC,EAAE,MAAM;IAJlC,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;gBAGf,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,YAAA;IAKlC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;CAezD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,wBAAgB,MAAM,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,EACnD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,YAAY,CAAC,MAAM,CAAC,CAEtB"}
@@ -20,17 +20,19 @@ const core_js_1 = require("../core.js");
20
20
  */
21
21
  class RegexpSchema extends core_js_1.Schema {
22
22
  pattern;
23
+ message;
23
24
  type = 'regexp';
24
- constructor(pattern) {
25
+ constructor(pattern, message) {
25
26
  super();
26
27
  this.pattern = pattern;
28
+ this.message = message;
27
29
  }
28
30
  validateInContext(input, ctx) {
29
31
  if (typeof input !== 'string') {
30
32
  return ctx.issueUnexpectedType(input, 'string');
31
33
  }
32
34
  if (!this.pattern.test(input)) {
33
- return ctx.issueInvalidFormat(input, this.pattern.toString());
35
+ return ctx.issueInvalidFormat(input, this.pattern.toString(), this.message);
34
36
  }
35
37
  return ctx.success(input);
36
38
  }
@@ -66,7 +68,7 @@ exports.RegexpSchema = RegexpSchema;
66
68
  * ```
67
69
  */
68
70
  /*@__NO_SIDE_EFFECTS__*/
69
- function regexp(pattern) {
70
- return new RegexpSchema(pattern);
71
+ function regexp(pattern, message) {
72
+ return new RegexpSchema(pattern, message);
71
73
  }
72
74
  //# sourceMappingURL=regexp.js.map
@@ -1 +1 @@
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
+ {"version":3,"file":"regexp.js","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":";;;AA4EA,wBAKC;AAjFD,wCAAsD;AAEtD;;;;;;;;;;;;;;GAcG;AACH,MAAa,YAEX,SAAQ,gBAAc;IAIJ;IACA;IAJT,IAAI,GAAG,QAAiB,CAAA;IAEjC,YACkB,OAAe,EACf,OAAgB;QAEhC,KAAK,EAAE,CAAA;QAHS,YAAO,GAAP,OAAO,CAAQ;QACf,YAAO,GAAP,OAAO,CAAS;IAGlC,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,CAC3B,KAAK,EACL,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EACvB,IAAI,CAAC,OAAO,CACb,CAAA;QACH,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC,KAAe,CAAC,CAAA;IACrC,CAAC;CACF;AA3BD,oCA2BC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAwB;AACxB,SAAgB,MAAM,CACpB,OAAe,EACf,OAAgB;IAEhB,OAAO,IAAI,YAAY,CAAS,OAAO,EAAE,OAAO,CAAC,CAAA;AACnD,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(\n public readonly pattern: RegExp,\n public readonly message?: string,\n ) {\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(\n input,\n this.pattern.toString(),\n this.message,\n )\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>(\n pattern: RegExp,\n message?: string,\n): RegexpSchema<TInput> {\n return new RegexpSchema<TInput>(pattern, message)\n}\n"]}
@@ -1 +1 @@
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"}
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;CA2DzD;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"}
@@ -56,11 +56,14 @@ class StringSchema extends core_js_1.Schema {
56
56
  return ctx.issueTooBig(str, 'string', maxLength, lazyUtf8Len);
57
57
  }
58
58
  }
59
+ // Optimization: count graphemes once
59
60
  let lazyGraphLen;
60
61
  const minGraphemes = this.options.minGraphemes;
61
62
  if (minGraphemes != null) {
62
- // Optimization: avoid counting graphemes if the length check already fails
63
63
  if (str.length < minGraphemes) {
64
+ // If the JavaScript string length (UTF-16) is below the minimal limit,
65
+ // its grapheme length (which <= .length) will also be below.
66
+ // Fail early.
64
67
  return ctx.issueTooSmall(str, 'grapheme', minGraphemes, str.length);
65
68
  }
66
69
  else if ((lazyGraphLen ??= (0, lex_data_1.graphemeLen)(str)) < minGraphemes) {
@@ -69,7 +72,11 @@ class StringSchema extends core_js_1.Schema {
69
72
  }
70
73
  const maxGraphemes = this.options.maxGraphemes;
71
74
  if (maxGraphemes != null) {
72
- if ((lazyGraphLen ??= (0, lex_data_1.graphemeLen)(str)) > maxGraphemes) {
75
+ if (str.length <= maxGraphemes) {
76
+ // If the JavaScript string length (UTF-16) is within the maximum limit,
77
+ // its grapheme length (which <= .length) will also be within.
78
+ }
79
+ else if ((lazyGraphLen ??= (0, lex_data_1.graphemeLen)(str)) > maxGraphemes) {
73
80
  return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen);
74
81
  }
75
82
  }
@@ -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 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, ctx.options)) {\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"]}
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 // Optimization: count graphemes once\n let lazyGraphLen: number\n\n const minGraphemes = this.options.minGraphemes\n if (minGraphemes != null) {\n if (str.length < minGraphemes) {\n // If the JavaScript string length (UTF-16) is below the minimal limit,\n // its grapheme length (which <= .length) will also be below.\n // Fail early.\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 (str.length <= maxGraphemes) {\n // If the JavaScript string length (UTF-16) is within the maximum limit,\n // its grapheme length (which <= .length) will also be within.\n } else 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, ctx.options)) {\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"]}
@@ -1,3 +1,4 @@
1
+ import { LexMap } from '@atproto/lex-data';
1
2
  import { $Type, $TypeOf, $Typed, $TypedMaybe, InferInput, InferOutput, NsidString, Schema, Unknown$TypedObject, ValidationContext, Validator } from '../core.js';
2
3
  export type MaybeTypedObject<TType extends $Type, TValue extends {
3
4
  $type?: unknown;
@@ -24,15 +25,14 @@ export type MaybeTypedObject<TType extends $Type, TValue extends {
24
25
  * )
25
26
  * ```
26
27
  */
27
- export declare class TypedObjectSchema<const TType extends $Type = $Type, const TShape extends Validator<{
28
- [k: string]: unknown;
29
- }> = any> extends Schema<$TypedMaybe<InferInput<TShape>, TType>, $TypedMaybe<InferOutput<TShape>, TType>> {
28
+ export declare class TypedObjectSchema<const TType extends $Type = $Type, const TShape extends Validator<LexMap> = Validator<LexMap>> extends Schema<$TypedMaybe<InferInput<TShape>, TType>, $TypedMaybe<InferOutput<TShape>, TType>> {
30
29
  readonly $type: TType;
31
30
  readonly schema: TShape;
32
31
  readonly type: "typedObject";
33
32
  constructor($type: TType, schema: TShape);
34
33
  validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<InferInput<TShape>>;
35
- build(input: Omit<InferInput<this>, '$type'>): $Typed<InferOutput<this>, TType>;
34
+ build(input: Omit<InferOutput<TShape>, '$type'>): $Typed<InferOutput<TShape>, TType>;
35
+ build(input: Omit<InferInput<TShape>, '$type'>): $Typed<InferInput<TShape>, TType>;
36
36
  isTypeOf<TValue extends Record<string, unknown>>(value: TValue): value is MaybeTypedObject<TType, TValue>;
37
37
  /**
38
38
  * Bound alias for {@link build} for compatibility with generated utilities.
@@ -92,9 +92,7 @@ export declare class TypedObjectSchema<const TType extends $Type = $Type, const
92
92
  * // view.$type === 'app.bsky.embed.images#view'
93
93
  * ```
94
94
  */
95
- export declare function typedObject<const N extends NsidString, const H extends string, const S extends Validator<{
96
- [k: string]: unknown;
97
- }>>(nsid: N, hash: H, validator: S): TypedObjectSchema<$Type<N, H>, S>;
95
+ export declare function typedObject<const N extends NsidString, const H extends string, const S extends Validator<LexMap>>(nsid: N, hash: H, validator: S): TypedObjectSchema<$Type<N, H>, S>;
98
96
  export declare function typedObject<V extends {
99
97
  $type?: $Type;
100
98
  }>(nsid: V extends {
@@ -1 +1 @@
1
- {"version":3,"file":"typed-object.d.ts","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,EACL,OAAO,EACP,MAAM,EACN,WAAW,EAGX,UAAU,EACV,WAAW,EACX,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACV,MAAM,YAAY,CAAA;AAGnB,MAAM,MAAM,gBAAgB,CAC1B,KAAK,SAAS,KAAK,EACnB,MAAM,SAAS;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,IACtD,MAAM,SAAS;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAChC,MAAM,GACN,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,KAAK,CAAC,CAAA;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAiB,CAC5B,KAAK,CAAC,KAAK,SAAS,KAAK,GAAG,KAAK,EACjC,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,GAAG,GAAG,CAC9D,SAAQ,MAAM,CACd,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EACtC,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CACxC;IAIG,QAAQ,CAAC,KAAK,EAAE,KAAK;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,QAAQ,CAAC,IAAI,EAAG,aAAa,CAAS;gBAG3B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM;IAKzB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;IAgBxD,KAAK,CACH,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,GACrC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IAOnC,QAAQ,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,KAAK,EAAE,MAAM,GACZ,KAAK,IAAI,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC;IAI3C;;;OAGG;IACH,IAAI,MAAM,IAAI,OAAO,IAAI,CAAC,KAAK,CAE9B;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,OAAO,IAAI,CAAC,QAAQ,CAEpC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,WAAW,CACzB,KAAK,CAAC,CAAC,SAAS,UAAU,EAC1B,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,KAAK,CAAC,CAAC,SAAS,SAAS,CAAC;IAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC,EACnD,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACpE,wBAAgB,WAAW,CAAC,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,EACrD,IAAI,EAAE,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAC9C,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,MAAM,EAAE,GAC9B,CAAC,GACD,CAAC,GACH,KAAK,EACT,IAAI,EAAE,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAC9C,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,CAAC,EAAE,GAC9B,CAAC,GACD,MAAM,GACR,KAAK,EACT,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,GACrC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"typed-object.d.ts","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":"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"}
@@ -44,7 +44,7 @@ class TypedObjectSchema extends core_js_1.Schema {
44
44
  return ctx.validate(input, this.schema);
45
45
  }
46
46
  build(input) {
47
- return this.parse((0, core_js_1.$typed)(input, this.$type));
47
+ return (0, core_js_1.$typed)(input, this.$type);
48
48
  }
49
49
  isTypeOf(value) {
50
50
  return value.$type === undefined || value.$type === this.$type;
@@ -1 +1 @@
1
- {"version":3,"file":"typed-object.js","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":";;;AA6KA,kCAMC;AAnLD,gDAAiD;AACjD,wCAcmB;AACnB,+DAAuD;AASvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,iBAGX,SAAQ,gBAGT;IAIY;IACA;IAJF,IAAI,GAAG,aAAsB,CAAA;IAEtC,YACW,KAAY,EACZ,MAAc;QAEvB,KAAK,EAAE,CAAA;QAHE,UAAK,GAAL,KAAK,CAAO;QACZ,WAAM,GAAN,MAAM,CAAQ;IAGzB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,CAAC,IAAA,wBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjD,CAAC;QAED,IACE,OAAO,IAAI,KAAK;YAChB,KAAK,CAAC,KAAK,KAAK,SAAS;YACzB,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAC1B,CAAC;YACD,OAAO,GAAG,CAAC,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACpE,CAAC;QAED,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IAED,KAAK,CACH,KAAsC;QAEtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,gBAAM,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAG1C,CAAA;IACH,CAAC;IAED,QAAQ,CACN,KAAa;QAEb,OAAO,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAA;IAChE,CAAC;IAED;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5D,CAAC;IAED;;;OAGG;IACH,IAAI,SAAS;QACX,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAClE,CAAC;CACF;AA9DD,8CA8DC;AAmED,wBAAwB;AACxB,SAAgB,WAAW,CAIzB,IAAO,EAAE,IAAO,EAAE,SAAY;IAC9B,OAAO,IAAI,iBAAiB,CAAiB,IAAA,eAAK,EAAC,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,CAAA;AAC5E,CAAC","sourcesContent":["import { isPlainObject } from '@atproto/lex-data'\nimport {\n $Type,\n $TypeOf,\n $Typed,\n $TypedMaybe,\n $type,\n $typed,\n InferInput,\n InferOutput,\n NsidString,\n Schema,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\n\nexport type MaybeTypedObject<\n TType extends $Type,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type?: TType }\n ? TValue\n : $TypedMaybe<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for typed objects in Lexicon unions.\n *\n * Typed objects have a `$type` field that identifies which variant they are\n * in a union. The `$type` can be omitted in input (it's implicit), but if\n * present, it must match the expected value.\n *\n * @template TType - The $type string literal type\n * @template TShape - The validator type for the object's shape\n *\n * @example\n * ```ts\n * const schema = new TypedObjectSchema(\n * 'app.bsky.embed.images#view',\n * l.object({ images: l.array(imageSchema) })\n * )\n * ```\n */\nexport class TypedObjectSchema<\n const TType extends $Type = $Type,\n const TShape extends Validator<{ [k: string]: unknown }> = any,\n> extends Schema<\n $TypedMaybe<InferInput<TShape>, TType>,\n $TypedMaybe<InferOutput<TShape>, TType>\n> {\n readonly type = 'typedObject' as const\n\n constructor(\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (!isPlainObject(input)) {\n return ctx.issueUnexpectedType(input, 'object')\n }\n\n if (\n '$type' in input &&\n input.$type !== undefined &&\n input.$type !== this.$type\n ) {\n return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])\n }\n\n return ctx.validate(input, this.schema)\n }\n\n build(\n input: Omit<InferInput<this>, '$type'>,\n ): $Typed<InferOutput<this>, TType> {\n return this.parse($typed(input, this.$type)) as $Typed<\n InferOutput<this>,\n TType\n >\n }\n\n isTypeOf<TValue extends Record<string, unknown>>(\n value: TValue,\n ): value is MaybeTypedObject<TType, TValue> {\n return value.$type === undefined || value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\n/**\n * Creates a typed object schema for use in Lexicon unions.\n *\n * Typed objects are identified by their `$type` field, which combines an NSID\n * and a hash (e.g., 'app.bsky.embed.images#view'). Used for union variants.\n *\n * This function offers two overloads:\n * - One that infers the type from arguments (no circular reference support)\n * - One with explicit interface for codegen with circular references\n *\n * @param nsid - The NSID part of the type (e.g., 'app.bsky.embed.images')\n * @param hash - The hash part of the type (e.g., 'view'), defaults to 'main'\n * @param validator - Schema for validating the object properties\n * @returns A new {@link TypedObjectSchema} instance\n *\n * @example\n * ```ts\n * // Image embed view\n * const imageViewSchema = l.typedObject(\n * 'app.bsky.embed.images',\n * 'view',\n * l.object({\n * images: l.array(l.object({\n * thumb: l.string(),\n * fullsize: l.string(),\n * alt: l.string(),\n * })),\n * })\n * )\n *\n * // Main type (hash defaults to 'main')\n * const postViewSchema = l.typedObject(\n * 'app.bsky.feed.defs',\n * 'postView',\n * l.object({ uri: l.string(), cid: l.string(), author: authorSchema })\n * )\n *\n * // Use $isTypeOf to narrow union types\n * if (imageViewSchema.$isTypeOf(embed)) {\n * // embed is narrowed to image view type\n * }\n *\n * // Use $build to construct typed objects\n * const view = imageViewSchema.$build({ images: [...] })\n * // view.$type === 'app.bsky.embed.images#view'\n * ```\n */\nexport function typedObject<\n const N extends NsidString,\n const H extends string,\n const S extends Validator<{ [k: string]: unknown }>,\n>(nsid: N, hash: H, validator: S): TypedObjectSchema<$Type<N, H>, S>\nexport function typedObject<V extends { $type?: $Type }>(\n nsid: V extends { $type?: infer T extends string }\n ? T extends `${infer N}#${string}`\n ? N\n : T // (T is a \"main\" type, so already an NSID)\n : never,\n hash: V extends { $type?: infer T extends string }\n ? T extends `${string}#${infer H}`\n ? H\n : 'main'\n : never,\n validator: Validator<Omit<V, '$type'>>,\n): TypedObjectSchema<$TypeOf<V>, Validator<V>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function typedObject<\n const N extends NsidString,\n const H extends string,\n const S extends Validator<{ [k: string]: unknown }>,\n>(nsid: N, hash: H, validator: S) {\n return new TypedObjectSchema<$Type<N, H>, S>($type(nsid, hash), validator)\n}\n"]}
1
+ {"version":3,"file":"typed-object.js","sourceRoot":"","sources":["../../src/schema/typed-object.ts"],"names":[],"mappings":";;;AA8KA,kCAMC;AApLD,gDAAyD;AACzD,wCAcmB;AACnB,+DAAuD;AASvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,iBAGX,SAAQ,gBAGT;IAIY;IACA;IAJF,IAAI,GAAG,aAAsB,CAAA;IAEtC,YACW,KAAY,EACZ,MAAc;QAEvB,KAAK,EAAE,CAAA;QAHE,UAAK,GAAL,KAAK,CAAO;QACZ,WAAM,GAAN,MAAM,CAAQ;IAGzB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,CAAC,IAAA,wBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjD,CAAC;QAED,IACE,OAAO,IAAI,KAAK;YAChB,KAAK,CAAC,KAAK,KAAK,SAAS;YACzB,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAC1B,CAAC;YACD,OAAO,GAAG,CAAC,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACpE,CAAC;QAED,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IAQD,KAAK,CAAC,KAA8B;QAClC,OAAO,IAAA,gBAAM,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,QAAQ,CACN,KAAa;QAEb,OAAO,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAA;IAChE,CAAC;IAED;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5D,CAAC;IAED;;;OAGG;IACH,IAAI,SAAS;QACX,OAAO,IAAA,+BAAY,EAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAClE,CAAC;CACF;AA/DD,8CA+DC;AAmED,wBAAwB;AACxB,SAAgB,WAAW,CAIzB,IAAO,EAAE,IAAO,EAAE,SAAY;IAC9B,OAAO,IAAI,iBAAiB,CAAiB,IAAA,eAAK,EAAC,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC,CAAA;AAC5E,CAAC","sourcesContent":["import { LexMap, isPlainObject } from '@atproto/lex-data'\nimport {\n $Type,\n $TypeOf,\n $Typed,\n $TypedMaybe,\n $type,\n $typed,\n InferInput,\n InferOutput,\n NsidString,\n Schema,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\n\nexport type MaybeTypedObject<\n TType extends $Type,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type?: TType }\n ? TValue\n : $TypedMaybe<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for typed objects in Lexicon unions.\n *\n * Typed objects have a `$type` field that identifies which variant they are\n * in a union. The `$type` can be omitted in input (it's implicit), but if\n * present, it must match the expected value.\n *\n * @template TType - The $type string literal type\n * @template TShape - The validator type for the object's shape\n *\n * @example\n * ```ts\n * const schema = new TypedObjectSchema(\n * 'app.bsky.embed.images#view',\n * l.object({ images: l.array(imageSchema) })\n * )\n * ```\n */\nexport class TypedObjectSchema<\n const TType extends $Type = $Type,\n const TShape extends Validator<LexMap> = Validator<LexMap>,\n> extends Schema<\n $TypedMaybe<InferInput<TShape>, TType>,\n $TypedMaybe<InferOutput<TShape>, TType>\n> {\n readonly type = 'typedObject' as const\n\n constructor(\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (!isPlainObject(input)) {\n return ctx.issueUnexpectedType(input, 'object')\n }\n\n if (\n '$type' in input &&\n input.$type !== undefined &&\n input.$type !== this.$type\n ) {\n return ctx.issueInvalidPropertyValue(input, '$type', [this.$type])\n }\n\n return ctx.validate(input, this.schema)\n }\n\n build(\n input: Omit<InferOutput<TShape>, '$type'>,\n ): $Typed<InferOutput<TShape>, TType>\n build(\n input: Omit<InferInput<TShape>, '$type'>,\n ): $Typed<InferInput<TShape>, TType>\n build(input: Record<string, unknown>) {\n return $typed(input, this.$type)\n }\n\n isTypeOf<TValue extends Record<string, unknown>>(\n value: TValue,\n ): value is MaybeTypedObject<TType, TValue> {\n return value.$type === undefined || value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\n/**\n * Creates a typed object schema for use in Lexicon unions.\n *\n * Typed objects are identified by their `$type` field, which combines an NSID\n * and a hash (e.g., 'app.bsky.embed.images#view'). Used for union variants.\n *\n * This function offers two overloads:\n * - One that infers the type from arguments (no circular reference support)\n * - One with explicit interface for codegen with circular references\n *\n * @param nsid - The NSID part of the type (e.g., 'app.bsky.embed.images')\n * @param hash - The hash part of the type (e.g., 'view'), defaults to 'main'\n * @param validator - Schema for validating the object properties\n * @returns A new {@link TypedObjectSchema} instance\n *\n * @example\n * ```ts\n * // Image embed view\n * const imageViewSchema = l.typedObject(\n * 'app.bsky.embed.images',\n * 'view',\n * l.object({\n * images: l.array(l.object({\n * thumb: l.string(),\n * fullsize: l.string(),\n * alt: l.string(),\n * })),\n * })\n * )\n *\n * // Main type (hash defaults to 'main')\n * const postViewSchema = l.typedObject(\n * 'app.bsky.feed.defs',\n * 'postView',\n * l.object({ uri: l.string(), cid: l.string(), author: authorSchema })\n * )\n *\n * // Use $isTypeOf to narrow union types\n * if (imageViewSchema.$isTypeOf(embed)) {\n * // embed is narrowed to image view type\n * }\n *\n * // Use $build to construct typed objects\n * const view = imageViewSchema.$build({ images: [...] })\n * // view.$type === 'app.bsky.embed.images#view'\n * ```\n */\nexport function typedObject<\n const N extends NsidString,\n const H extends string,\n const S extends Validator<LexMap>,\n>(nsid: N, hash: H, validator: S): TypedObjectSchema<$Type<N, H>, S>\nexport function typedObject<V extends { $type?: $Type }>(\n nsid: V extends { $type?: infer T extends string }\n ? T extends `${infer N}#${string}`\n ? N\n : T // (T is a \"main\" type, so already an NSID)\n : never,\n hash: V extends { $type?: infer T extends string }\n ? T extends `${string}#${infer H}`\n ? H\n : 'main'\n : never,\n validator: Validator<Omit<V, '$type'>>,\n): TypedObjectSchema<$TypeOf<V>, Validator<V>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function typedObject<\n const N extends NsidString,\n const H extends string,\n const S extends Validator<LexMap>,\n>(nsid: N, hash: H, validator: S) {\n return new TypedObjectSchema<$Type<N, H>, S>($type(nsid, hash), validator)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-schema",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "license": "MIT",
5
5
  "description": "Lexicon schema system for AT Lexicons",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  "@standard-schema/spec": "^1.1.0",
39
39
  "iso-datestring-validator": "^2.2.2",
40
40
  "tslib": "^2.8.1",
41
- "@atproto/syntax": "^0.5.1",
41
+ "@atproto/syntax": "^0.5.2",
42
42
  "@atproto/lex-data": "^0.0.14"
43
43
  },
44
44
  "devDependencies": {
@@ -1,14 +1,18 @@
1
- import { describe, it } from 'vitest'
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
2
  import { LexMap } from '@atproto/lex-data'
3
- import { Unknown$TypedObject } from './$type.js'
3
+ import { Unknown$Type, Unknown$TypedObject } from './$type.js'
4
4
 
5
5
  describe('Unknown$TypedObject', () => {
6
6
  it('allows assigning Unknown$TypedObject to LexMap', () => {
7
7
  function expectLexMap(_value: LexMap) {}
8
8
 
9
- const someObject = {
10
- $type: 'some-type',
11
- } as Unknown$TypedObject
9
+ const someObject: Unknown$TypedObject = {
10
+ $type: 'some-type' as Unknown$Type,
11
+ // @ts-expect-error should not allow arbitrary properties
12
+ foo: 'bar',
13
+ }
14
+
15
+ expectTypeOf(someObject).toEqualTypeOf<Unknown$TypedObject>()
12
16
 
13
17
  expectLexMap(someObject)
14
18
 
@@ -120,8 +120,11 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
120
120
  * will typically arise in generic contexts, where the narrowed type is not
121
121
  * needed.
122
122
  */
123
- assert(input: unknown): asserts input is InferInput<this> {
124
- const result = ValidationContext.validate(input, this)
123
+ assert(
124
+ input: unknown,
125
+ options?: ValidateOptions,
126
+ ): asserts input is InferInput<this> {
127
+ const result = this.safeValidate(input, options)
125
128
  if (!result.success) throw result.reason
126
129
  }
127
130
 
@@ -131,8 +134,8 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
131
134
  * every name in the call target to be declared with an explicit type
132
135
  * annotation. ts(2775)_" errors.
133
136
  */
134
- check(input: unknown): void {
135
- this.assert(input)
137
+ check(input: unknown, options?: ValidateOptions): void {
138
+ this.assert(input, options)
136
139
  }
137
140
 
138
141
  /**
@@ -140,8 +143,8 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
140
143
  * schema, otherwise throws. This is the same as calling {@link parse}() with
141
144
  * `mode: "validate"`.
142
145
  */
143
- cast<I>(input: I): I & InferInput<this> {
144
- const result = ValidationContext.validate(input, this)
146
+ cast<I>(input: I, options?: ValidateOptions): I & InferInput<this> {
147
+ const result = this.safeValidate(input, options)
145
148
  if (result.success) return result.value
146
149
  throw result.reason
147
150
  }
@@ -149,9 +152,6 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
149
152
  /**
150
153
  * Type guard that checks if the input matches this schema.
151
154
  *
152
- * @param input - The value to check
153
- * @returns `true` if the input is valid according to this schema
154
- *
155
155
  * @example
156
156
  * ```typescript
157
157
  * if (schema.matches(data)) {
@@ -160,8 +160,11 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
160
160
  * }
161
161
  * ```
162
162
  */
163
- matches<I>(input: I): input is I & InferInput<this> {
164
- const result = ValidationContext.validate(input, this)
163
+ matches<I>(
164
+ input: I,
165
+ options?: ValidateOptions,
166
+ ): input is I & InferInput<this> {
167
+ const result = this.safeValidate(input, options)
165
168
  return result.success
166
169
  }
167
170
 
@@ -171,9 +174,6 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
171
174
  * This is useful for optional filtering operations where you want to
172
175
  * conditionally extract values that match a schema.
173
176
  *
174
- * @param input - The value to check
175
- * @returns The input value with narrowed type if valid, otherwise `undefined`
176
- *
177
177
  * @example
178
178
  * ```typescript
179
179
  * const validData = schema.ifMatches(data)
@@ -183,8 +183,11 @@ export abstract class Schema<out TInput = unknown, out TOutput = TInput>
183
183
  * }
184
184
  * ```
185
185
  */
186
- ifMatches<I>(input: I): (I & InferInput<this>) | undefined {
187
- return this.matches(input) ? input : undefined
186
+ ifMatches<I>(
187
+ input: I,
188
+ options?: ValidateOptions,
189
+ ): (I & InferInput<this>) | undefined {
190
+ return this.matches(input, options) ? input : undefined
188
191
  }
189
192
 
190
193
  /**
@@ -23,7 +23,7 @@ import {
23
23
  * new IssueInvalidType(['user', 'age'], 'hello', ['number'])
24
24
  * ])
25
25
  * console.log(error.message)
26
- * // "Expected number value type at $.user.age (got string)"
26
+ * // "Expected integer value type (got "some-string") at $.user.age"
27
27
  *
28
28
  * console.log(error.issues.length) // 1
29
29
  * console.log(error.toJSON())
@@ -46,7 +46,7 @@ export class LexValidationError
46
46
  * Issues are aggregated when possible (e.g., multiple invalid type issues
47
47
  * at the same path are combined into a single issue listing all expected types).
48
48
  */
49
- readonly issues: Issue[]
49
+ readonly issues: readonly Issue[]
50
50
 
51
51
  /**
52
52
  * Creates a new validation error from a list of issues.