@atproto/lex-schema 0.0.16 → 0.0.18

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 (55) hide show
  1. package/CHANGELOG.md +30 -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/blob.d.ts +6 -20
  18. package/dist/schema/blob.d.ts.map +1 -1
  19. package/dist/schema/blob.js +23 -28
  20. package/dist/schema/blob.js.map +1 -1
  21. package/dist/schema/payload.d.ts.map +1 -1
  22. package/dist/schema/payload.js +2 -3
  23. package/dist/schema/payload.js.map +1 -1
  24. package/dist/schema/record.d.ts +6 -8
  25. package/dist/schema/record.d.ts.map +1 -1
  26. package/dist/schema/record.js +1 -1
  27. package/dist/schema/record.js.map +1 -1
  28. package/dist/schema/regexp.d.ts +3 -2
  29. package/dist/schema/regexp.d.ts.map +1 -1
  30. package/dist/schema/regexp.js +6 -4
  31. package/dist/schema/regexp.js.map +1 -1
  32. package/dist/schema/string.d.ts.map +1 -1
  33. package/dist/schema/string.js +9 -2
  34. package/dist/schema/string.js.map +1 -1
  35. package/dist/schema/typed-object.d.ts +5 -7
  36. package/dist/schema/typed-object.d.ts.map +1 -1
  37. package/dist/schema/typed-object.js +1 -1
  38. package/dist/schema/typed-object.js.map +1 -1
  39. package/package.json +3 -3
  40. package/src/core/$type.test.ts +9 -5
  41. package/src/core/schema.ts +19 -16
  42. package/src/core/validation-error.ts +2 -2
  43. package/src/core/validation-issue.ts +20 -36
  44. package/src/helpers.ts +7 -1
  45. package/src/schema/array.test.ts +1 -1
  46. package/src/schema/blob.test.ts +223 -263
  47. package/src/schema/blob.ts +27 -46
  48. package/src/schema/params.test.ts +2 -2
  49. package/src/schema/payload.ts +2 -3
  50. package/src/schema/record.test.ts +135 -17
  51. package/src/schema/record.ts +14 -9
  52. package/src/schema/regexp.ts +14 -4
  53. package/src/schema/string.ts +8 -2
  54. package/src/schema/typed-object.test.ts +77 -0
  55. package/src/schema/typed-object.ts +11 -10
@@ -1,16 +1,9 @@
1
- import { BlobRef, LegacyBlobRef, isBlobRef, isLegacyBlobRef } from '@atproto/lex-data';
1
+ import { BlobRef, LegacyBlobRef, TypedBlobRef, isBlobRef, isLegacyBlobRef, isTypedBlobRef } from '@atproto/lex-data';
2
2
  import { Schema, ValidationContext } from '../core.js';
3
3
  /**
4
4
  * Configuration options for blob schema validation.
5
5
  */
6
6
  export type BlobSchemaOptions = {
7
- /**
8
- * Whether to allow legacy blob references format
9
- *
10
- * @default false
11
- * @see {@link LegacyBlobRef}
12
- */
13
- allowLegacy?: boolean;
14
7
  /**
15
8
  * List of accepted MIME types (supports wildcards like 'image/*' or '*\/*')
16
9
  *
@@ -24,8 +17,8 @@ export type BlobSchemaOptions = {
24
17
  */
25
18
  maxSize?: number;
26
19
  };
27
- export type { BlobRef, LegacyBlobRef };
28
- export { isBlobRef, isLegacyBlobRef };
20
+ export type { BlobRef, LegacyBlobRef, TypedBlobRef };
21
+ export { isBlobRef, isLegacyBlobRef, isTypedBlobRef };
29
22
  /**
30
23
  * Schema for validating blob references in AT Protocol.
31
24
  *
@@ -41,13 +34,11 @@ export { isBlobRef, isLegacyBlobRef };
41
34
  * const result = schema.validate(blobRef)
42
35
  * ```
43
36
  */
44
- export declare class BlobSchema<const TOptions extends BlobSchemaOptions = NonNullable<unknown>> extends Schema<TOptions extends {
45
- allowLegacy: true;
46
- } ? BlobRef | LegacyBlobRef : BlobRef> {
37
+ export declare class BlobSchema<const TOptions extends BlobSchemaOptions = NonNullable<unknown>> extends Schema<BlobRef> {
47
38
  readonly options?: TOptions | undefined;
48
39
  readonly type: "blob";
49
40
  constructor(options?: TOptions | undefined);
50
- validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<LegacyBlobRef | BlobRef>;
41
+ validateInContext(input: unknown, ctx: ValidationContext): import("../core.js").ValidationResult<BlobRef>;
51
42
  matchesMime(mime: string): boolean;
52
43
  }
53
44
  /**
@@ -69,12 +60,7 @@ export declare class BlobSchema<const TOptions extends BlobSchemaOptions = NonNu
69
60
  *
70
61
  * // Any image type with size limit
71
62
  * const avatarSchema = l.blob({ accept: ['image/*'], maxSize: 1000000 })
72
- *
73
- * // Allow legacy format
74
- * const legacySchema = l.blob({ allowLegacy: true })
75
63
  * ```
76
64
  */
77
- export declare const blob: <O extends BlobSchemaOptions = {
78
- allowLegacy?: false;
79
- }>(options?: O) => BlobSchema<O>;
65
+ export declare const blob: <O extends BlobSchemaOptions = {}>(options?: O) => BlobSchema<O>;
80
66
  //# sourceMappingURL=blob.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../../src/schema/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,eAAe,EAEhB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IAErB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IAEjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAAA;AAErC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,UAAU,CACrB,KAAK,CAAC,QAAQ,SAAS,iBAAiB,GAAG,WAAW,CAAC,OAAO,CAAC,CAC/D,SAAQ,MAAM,CACd,QAAQ,SAAS;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,GAAG,aAAa,GAAG,OAAO,CAC3E;IAGa,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ;IAFvC,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAS;gBAEV,OAAO,CAAC,EAAE,QAAQ,YAAA;IAIvC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;IAwBxD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAKnC;AA6CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,GACf,CAAC,SAAS,iBAAiB;kBAAmB,KAAK;aACzC,CAAC,kBAEX,CAAA"}
1
+ {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../../src/schema/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,aAAa,EACb,YAAY,EAEZ,SAAS,EACT,eAAe,EACf,cAAc,EACf,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IAEjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,CAAA;AAErD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,UAAU,CACrB,KAAK,CAAC,QAAQ,SAAS,iBAAiB,GAAG,WAAW,CAAC,OAAO,CAAC,CAC/D,SAAQ,MAAM,CAAC,OAAO,CAAC;IAGX,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ;IAFvC,QAAQ,CAAC,IAAI,EAAG,MAAM,CAAS;gBAEV,OAAO,CAAC,EAAE,QAAQ,YAAA;IAIvC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB;IA8BxD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAKnC;AA+BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,IAAI,GACf,CAAC,SAAS,iBAAiB,iBACjB,CAAC,kBAEX,CAAA"}
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.blob = exports.BlobSchema = exports.isLegacyBlobRef = exports.isBlobRef = void 0;
3
+ exports.blob = exports.BlobSchema = exports.isTypedBlobRef = exports.isLegacyBlobRef = exports.isBlobRef = void 0;
4
4
  const lex_data_1 = require("@atproto/lex-data");
5
5
  Object.defineProperty(exports, "isBlobRef", { enumerable: true, get: function () { return lex_data_1.isBlobRef; } });
6
6
  Object.defineProperty(exports, "isLegacyBlobRef", { enumerable: true, get: function () { return lex_data_1.isLegacyBlobRef; } });
7
+ Object.defineProperty(exports, "isTypedBlobRef", { enumerable: true, get: function () { return lex_data_1.isTypedBlobRef; } });
7
8
  const core_js_1 = require("../core.js");
8
9
  const memoize_js_1 = require("../util/memoize.js");
9
10
  /**
@@ -29,20 +30,28 @@ class BlobSchema extends core_js_1.Schema {
29
30
  this.options = options;
30
31
  }
31
32
  validateInContext(input, ctx) {
32
- const blob = parseValue.call(ctx, input, this.options);
33
+ const blob = parseValue.call(ctx, input);
33
34
  if (!blob) {
34
35
  return ctx.issueUnexpectedType(input, 'blob');
35
36
  }
36
37
  // In non-strict mode, we allow blob refs to pass through without MIME
37
38
  // type or size checks.
38
- if (ctx.options.strict) {
39
- const accept = this.options?.accept;
39
+ if (ctx.options.strict && this.options != null) {
40
+ const { accept } = this.options;
40
41
  if (accept && !matchesMime(blob.mimeType, accept)) {
41
42
  return ctx.issueInvalidPropertyValue(blob, 'mimeType', accept);
42
43
  }
43
- const maxSize = this.options?.maxSize;
44
- if (maxSize != null && 'size' in blob && blob.size > maxSize) {
45
- return ctx.issueTooBig(blob, 'blob', maxSize, blob.size);
44
+ const { maxSize } = this.options;
45
+ if (maxSize != null) {
46
+ const size = (0, lex_data_1.getBlobSize)(blob);
47
+ if (size === undefined) {
48
+ // Unable to enforce size constraint if size is not available (legacy
49
+ // blob ref), so we treat it as a validation failure in strict mode.
50
+ return ctx.issueInvalidPropertyType(blob, 'size', 'integer');
51
+ }
52
+ else if (size > maxSize) {
53
+ return ctx.issueTooBig(blob, 'blob', maxSize, size);
54
+ }
46
55
  }
47
56
  }
48
57
  return ctx.success(blob);
@@ -55,30 +64,19 @@ class BlobSchema extends core_js_1.Schema {
55
64
  }
56
65
  }
57
66
  exports.BlobSchema = BlobSchema;
58
- function parseValue(input, options) {
59
- // If there is a $type property, we treat if as a potential BlobRef and
67
+ function parseValue(input) {
68
+ // If there is a $type property, we treat if as a potential TypedBlobRef and
60
69
  // validate accordingly.
61
70
  if (input?.$type !== undefined) {
62
71
  // Use the context's option for the "strict" check
63
- return (0, lex_data_1.isBlobRef)(input, this.options) ? input : null;
72
+ return (0, lex_data_1.isTypedBlobRef)(input, this.options) ? input : null;
64
73
  }
65
74
  // If there is no $type property, we may be dealing with a legacy blob ref. If
66
- // legacy refs are allowed, validate against the legacy format. If not
67
- // allowed, but we are in non-strict "parse" mode, coerce legacy refs into
68
- // standard BlobRef format for backward compatibility. Otherwise, reject the
69
- // value.
70
- if (options?.allowLegacy) {
71
- if ((0, lex_data_1.isLegacyBlobRef)(input)) {
75
+ // legacy refs are allowed (non-strict mode), we check if the input matches
76
+ // the legacy format.
77
+ if (!this.options.strict) {
78
+ if ((0, lex_data_1.isLegacyBlobRef)(input, this.options))
72
79
  return input;
73
- }
74
- }
75
- else if (!this.options.strict && this.options.mode === 'parse') {
76
- if ((0, lex_data_1.isLegacyBlobRef)(input)) {
77
- const { cid, mimeType } = input;
78
- const ref = (0, lex_data_1.parseCidSafe)(cid);
79
- if (ref)
80
- return { $type: 'blob', ref, mimeType, size: -1 };
81
- }
82
80
  }
83
81
  return null;
84
82
  }
@@ -113,9 +111,6 @@ function matchesMime(mime, accepted) {
113
111
  *
114
112
  * // Any image type with size limit
115
113
  * const avatarSchema = l.blob({ accept: ['image/*'], maxSize: 1000000 })
116
- *
117
- * // Allow legacy format
118
- * const legacySchema = l.blob({ allowLegacy: true })
119
114
  * ```
120
115
  */
121
116
  exports.blob = (0, memoize_js_1.memoizedOptions)(function (options) {
@@ -1 +1 @@
1
- {"version":3,"file":"blob.js","sourceRoot":"","sources":["../../src/schema/blob.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BlobRef,\n LegacyBlobRef,\n isBlobRef,\n isLegacyBlobRef,\n parseCidSafe,\n} from '@atproto/lex-data'\nimport { Schema, ValidationContext } from '../core.js'\nimport { memoizedOptions } from '../util/memoize.js'\n\n/**\n * Configuration options for blob schema validation.\n */\nexport type BlobSchemaOptions = {\n /**\n * Whether to allow legacy blob references format\n *\n * @default false\n * @see {@link LegacyBlobRef}\n */\n allowLegacy?: boolean\n\n /**\n * List of accepted MIME types (supports wildcards like 'image/*' or '*\\/*')\n *\n * @default undefined // accepts all MIME types\n */\n accept?: string[]\n\n /**\n * Maximum blob size in bytes\n *\n * @default undefined // no size limit\n */\n maxSize?: number\n}\n\nexport type { BlobRef, LegacyBlobRef }\nexport { isBlobRef, isLegacyBlobRef }\n\n/**\n * Schema for validating blob references in AT Protocol.\n *\n * Validates BlobRef objects which contain a CID reference to binary data,\n * along with metadata like MIME type and size. Can optionally accept\n * legacy blob reference format.\n *\n * @template TOptions - The configuration options type\n *\n * @example\n * ```ts\n * const schema = new BlobSchema({ accept: ['image/*'], maxSize: 1000000 })\n * const result = schema.validate(blobRef)\n * ```\n */\nexport class BlobSchema<\n const TOptions extends BlobSchemaOptions = NonNullable<unknown>,\n> extends Schema<\n TOptions extends { allowLegacy: true } ? BlobRef | LegacyBlobRef : BlobRef\n> {\n readonly type = 'blob' as const\n\n constructor(readonly options?: TOptions) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const blob = parseValue.call(ctx, input, this.options)\n\n if (!blob) {\n return ctx.issueUnexpectedType(input, 'blob')\n }\n\n // In non-strict mode, we allow blob refs to pass through without MIME\n // type or size checks.\n if (ctx.options.strict) {\n const accept = this.options?.accept\n if (accept && !matchesMime(blob.mimeType, accept)) {\n return ctx.issueInvalidPropertyValue(blob, 'mimeType', accept)\n }\n\n const maxSize = this.options?.maxSize\n if (maxSize != null && 'size' in blob && blob.size > maxSize) {\n return ctx.issueTooBig(blob, 'blob', maxSize, blob.size)\n }\n }\n\n return ctx.success(blob)\n }\n\n matchesMime(mime: string): boolean {\n const accept = this.options?.accept\n if (!accept) return true\n return matchesMime(mime, accept)\n }\n}\n\nfunction parseValue(\n this: ValidationContext,\n input: unknown,\n options?: BlobSchemaOptions,\n): BlobRef | LegacyBlobRef | null {\n // If there is a $type property, we treat if as a potential BlobRef and\n // validate accordingly.\n if ((input as any)?.$type !== undefined) {\n // Use the context's option for the \"strict\" check\n return isBlobRef(input, this.options) ? input : null\n }\n\n // If there is no $type property, we may be dealing with a legacy blob ref. If\n // legacy refs are allowed, validate against the legacy format. If not\n // allowed, but we are in non-strict \"parse\" mode, coerce legacy refs into\n // standard BlobRef format for backward compatibility. Otherwise, reject the\n // value.\n if (options?.allowLegacy) {\n if (isLegacyBlobRef(input)) {\n return input\n }\n } else if (!this.options.strict && this.options.mode === 'parse') {\n if (isLegacyBlobRef(input)) {\n const { cid, mimeType } = input\n const ref = parseCidSafe(cid)\n if (ref) return { $type: 'blob', ref, mimeType, size: -1 }\n }\n }\n\n return null\n}\n\nfunction matchesMime(mime: string, accepted: string[]): boolean {\n if (accepted.includes('*/*')) return true\n if (accepted.includes(mime)) return true\n for (const value of accepted) {\n if (value.endsWith('/*') && mime.startsWith(value.slice(0, -1))) {\n return true\n }\n }\n return false\n}\n\n/**\n * Creates a blob schema for validating blob references with optional constraints.\n *\n * Blob references are used in AT Protocol to reference binary data stored\n * separately from records. They contain a CID, MIME type, and size information.\n *\n * @param options - Optional configuration for MIME type filtering and size limits\n * @returns A new {@link BlobSchema} instance\n *\n * @example\n * ```ts\n * // Basic blob reference\n * const fileSchema = l.blob()\n *\n * // Image files only\n * const imageSchema = l.blob({ accept: ['image/png', 'image/jpeg', 'image/gif'] })\n *\n * // Any image type with size limit\n * const avatarSchema = l.blob({ accept: ['image/*'], maxSize: 1000000 })\n *\n * // Allow legacy format\n * const legacySchema = l.blob({ allowLegacy: true })\n * ```\n */\nexport const blob = /*#__PURE__*/ memoizedOptions(function <\n O extends BlobSchemaOptions = { allowLegacy?: false },\n>(options?: O) {\n return new BlobSchema(options)\n})\n"]}
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../../src/schema/blob.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BlobRef,\n LegacyBlobRef,\n TypedBlobRef,\n getBlobSize,\n isBlobRef,\n isLegacyBlobRef,\n isTypedBlobRef,\n} from '@atproto/lex-data'\nimport { Schema, ValidationContext } from '../core.js'\nimport { memoizedOptions } from '../util/memoize.js'\n\n/**\n * Configuration options for blob schema validation.\n */\nexport type BlobSchemaOptions = {\n /**\n * List of accepted MIME types (supports wildcards like 'image/*' or '*\\/*')\n *\n * @default undefined // accepts all MIME types\n */\n accept?: string[]\n\n /**\n * Maximum blob size in bytes\n *\n * @default undefined // no size limit\n */\n maxSize?: number\n}\n\nexport type { BlobRef, LegacyBlobRef, TypedBlobRef }\nexport { isBlobRef, isLegacyBlobRef, isTypedBlobRef }\n\n/**\n * Schema for validating blob references in AT Protocol.\n *\n * Validates BlobRef objects which contain a CID reference to binary data,\n * along with metadata like MIME type and size. Can optionally accept\n * legacy blob reference format.\n *\n * @template TOptions - The configuration options type\n *\n * @example\n * ```ts\n * const schema = new BlobSchema({ accept: ['image/*'], maxSize: 1000000 })\n * const result = schema.validate(blobRef)\n * ```\n */\nexport class BlobSchema<\n const TOptions extends BlobSchemaOptions = NonNullable<unknown>,\n> extends Schema<BlobRef> {\n readonly type = 'blob' as const\n\n constructor(readonly options?: TOptions) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const blob = parseValue.call(ctx, input)\n if (!blob) {\n return ctx.issueUnexpectedType(input, 'blob')\n }\n\n // In non-strict mode, we allow blob refs to pass through without MIME\n // type or size checks.\n if (ctx.options.strict && this.options != null) {\n const { accept } = this.options\n if (accept && !matchesMime(blob.mimeType, accept)) {\n return ctx.issueInvalidPropertyValue(blob, 'mimeType', accept)\n }\n\n const { maxSize } = this.options\n if (maxSize != null) {\n const size = getBlobSize(blob)\n if (size === undefined) {\n // Unable to enforce size constraint if size is not available (legacy\n // blob ref), so we treat it as a validation failure in strict mode.\n return ctx.issueInvalidPropertyType(blob, 'size' as any, 'integer')\n } else if (size > maxSize) {\n return ctx.issueTooBig(blob, 'blob', maxSize, size)\n }\n }\n }\n\n return ctx.success(blob)\n }\n\n matchesMime(mime: string): boolean {\n const accept = this.options?.accept\n if (!accept) return true\n return matchesMime(mime, accept)\n }\n}\n\nfunction parseValue(this: ValidationContext, input: unknown): BlobRef | null {\n // If there is a $type property, we treat if as a potential TypedBlobRef and\n // validate accordingly.\n if ((input as any)?.$type !== undefined) {\n // Use the context's option for the \"strict\" check\n return isTypedBlobRef(input, this.options) ? input : null\n }\n\n // If there is no $type property, we may be dealing with a legacy blob ref. If\n // legacy refs are allowed (non-strict mode), we check if the input matches\n // the legacy format.\n if (!this.options.strict) {\n if (isLegacyBlobRef(input, this.options)) return input\n }\n\n return null\n}\n\nfunction matchesMime(mime: string, accepted: string[]): boolean {\n if (accepted.includes('*/*')) return true\n if (accepted.includes(mime)) return true\n for (const value of accepted) {\n if (value.endsWith('/*') && mime.startsWith(value.slice(0, -1))) {\n return true\n }\n }\n return false\n}\n\n/**\n * Creates a blob schema for validating blob references with optional constraints.\n *\n * Blob references are used in AT Protocol to reference binary data stored\n * separately from records. They contain a CID, MIME type, and size information.\n *\n * @param options - Optional configuration for MIME type filtering and size limits\n * @returns A new {@link BlobSchema} instance\n *\n * @example\n * ```ts\n * // Basic blob reference\n * const fileSchema = l.blob()\n *\n * // Image files only\n * const imageSchema = l.blob({ accept: ['image/png', 'image/jpeg', 'image/gif'] })\n *\n * // Any image type with size limit\n * const avatarSchema = l.blob({ accept: ['image/*'], maxSize: 1000000 })\n * ```\n */\nexport const blob = /*#__PURE__*/ memoizedOptions(function <\n O extends BlobSchemaOptions = NonNullable<unknown>,\n>(options?: O) {\n return new BlobSchema(options)\n})\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/schema/payload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAU,MAAM,aAAa,CAAA;AAElD,YAAY,EAAE,QAAQ,EAAE,CAAA;AAExB,KAAK,UAAU,CAAC,SAAS,SAAS,MAAM,IAAI,SAAS,SAAS,KAAK,GAC/D,GAAG,MAAM,IAAI,MAAM,EAAE,GACrB,SAAS,SAAS,GAAG,MAAM,CAAC,SAAS,MAAM,IAAI,GAC7C,GAAG,CAAC,IAAI,MAAM,EAAE,GAChB,SAAS,CAAA;AAEf,KAAK,UAAU,CACb,SAAS,SAAS,MAAM,EACxB,OAAO,EACP,OAAO,IACL,OAAO,SAAS,MAAM,GACtB,UAAU,CAAC,OAAO,CAAC,GACnB,SAAS,SAAS,kBAAkB,GAClC,QAAQ,GACR,OAAO,CAAA;AAEb;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,QAAQ,SAAS,OAAO,EAAE,OAAO,IACxD,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,EAAE,MAAM,OAAO,CAAC,GACpD,SAAS,SAAS,MAAM,GACtB;IACE,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAC/B,IAAI,EAAE,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;CAC9C,GACD,SAAS,GACX,KAAK,CAAA;AAEX;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,OAAO,IACvD,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,EAAE,GAAG,CAAC,GAC1C,SAAS,SAAS,MAAM,GACtB,UAAU,CAAC,SAAS,CAAC,GACrB,SAAS,GACX,KAAK,CAAA;AAEX;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,CAAC,QAAQ,SAAS,OAAO,EAAE,OAAO,IAC5D,QAAQ,SAAS,OAAO,CAAC,MAAM,SAAS,EAAE,MAAM,OAAO,CAAC,GACpD,SAAS,SAAS,MAAM,GACtB,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GACvC,SAAS,GACX,KAAK,CAAA;AAEX;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,IAAI,CAAC,SAAS,SAAS,GACzE,SAAS,GACT,MAAM,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAA;AAEhC;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,OAAO,CAClB,KAAK,CAAC,SAAS,SAAS,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,EAC/D,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC;IAGvE,QAAQ,CAAC,QAAQ,EAAE,SAAS;IAC5B,QAAQ,CAAC,MAAM,EAAE,OAAO;gBADf,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,OAAO;IAO1B;;;OAGG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO;CA4B1D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,wBAAgB,OAAO,CACrB,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EAC9C,KAAK,CAAC,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,EAC5C,QAAQ,GAAE,CAAkB,EAAE,SAAS,GAAE,CAAkB,iBAE5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,wBAAgB,WAAW,CACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,EACzD,UAAU,EAAE,CAAC,GAAG,OAAO,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAE7D"}
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":"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","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 {