@atproto/lex-schema 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/core/$type.d.ts +149 -0
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js +44 -0
  5. package/dist/core/$type.js.map +1 -1
  6. package/dist/core/record-key.d.ts +44 -0
  7. package/dist/core/record-key.d.ts.map +1 -1
  8. package/dist/core/record-key.js +30 -0
  9. package/dist/core/record-key.js.map +1 -1
  10. package/dist/core/result.d.ts +85 -4
  11. package/dist/core/result.d.ts.map +1 -1
  12. package/dist/core/result.js +60 -4
  13. package/dist/core/result.js.map +1 -1
  14. package/dist/core/schema.d.ts +232 -5
  15. package/dist/core/schema.d.ts.map +1 -1
  16. package/dist/core/schema.js +197 -4
  17. package/dist/core/schema.js.map +1 -1
  18. package/dist/core/string-format.d.ts +244 -11
  19. package/dist/core/string-format.d.ts.map +1 -1
  20. package/dist/core/string-format.js +150 -0
  21. package/dist/core/string-format.js.map +1 -1
  22. package/dist/core/types.d.ts +90 -3
  23. package/dist/core/types.d.ts.map +1 -1
  24. package/dist/core/types.js.map +1 -1
  25. package/dist/core/validation-error.d.ts +60 -0
  26. package/dist/core/validation-error.d.ts.map +1 -1
  27. package/dist/core/validation-error.js +60 -0
  28. package/dist/core/validation-error.js.map +1 -1
  29. package/dist/core/validation-issue.d.ts +61 -0
  30. package/dist/core/validation-issue.d.ts.map +1 -1
  31. package/dist/core/validation-issue.js +54 -1
  32. package/dist/core/validation-issue.js.map +1 -1
  33. package/dist/core/validator.d.ts +356 -11
  34. package/dist/core/validator.d.ts.map +1 -1
  35. package/dist/core/validator.js +203 -4
  36. package/dist/core/validator.js.map +1 -1
  37. package/dist/helpers.d.ts +12 -28
  38. package/dist/helpers.d.ts.map +1 -1
  39. package/dist/helpers.js.map +1 -1
  40. package/dist/schema/array.d.ts +46 -0
  41. package/dist/schema/array.d.ts.map +1 -1
  42. package/dist/schema/array.js +16 -1
  43. package/dist/schema/array.js.map +1 -1
  44. package/dist/schema/blob.d.ts +50 -2
  45. package/dist/schema/blob.d.ts.map +1 -1
  46. package/dist/schema/blob.js +44 -2
  47. package/dist/schema/blob.js.map +1 -1
  48. package/dist/schema/boolean.d.ts +29 -0
  49. package/dist/schema/boolean.d.ts.map +1 -1
  50. package/dist/schema/boolean.js +30 -1
  51. package/dist/schema/boolean.js.map +1 -1
  52. package/dist/schema/bytes.d.ts +39 -0
  53. package/dist/schema/bytes.d.ts.map +1 -1
  54. package/dist/schema/bytes.js +34 -1
  55. package/dist/schema/bytes.js.map +1 -1
  56. package/dist/schema/cid.d.ts +39 -0
  57. package/dist/schema/cid.d.ts.map +1 -1
  58. package/dist/schema/cid.js +35 -1
  59. package/dist/schema/cid.js.map +1 -1
  60. package/dist/schema/custom.d.ts +67 -1
  61. package/dist/schema/custom.d.ts.map +1 -1
  62. package/dist/schema/custom.js +55 -0
  63. package/dist/schema/custom.js.map +1 -1
  64. package/dist/schema/dict.d.ts +45 -0
  65. package/dist/schema/dict.d.ts.map +1 -1
  66. package/dist/schema/dict.js +46 -1
  67. package/dist/schema/dict.js.map +1 -1
  68. package/dist/schema/discriminated-union.d.ts +59 -0
  69. package/dist/schema/discriminated-union.d.ts.map +1 -1
  70. package/dist/schema/discriminated-union.js +47 -1
  71. package/dist/schema/discriminated-union.js.map +1 -1
  72. package/dist/schema/enum.d.ts +49 -0
  73. package/dist/schema/enum.d.ts.map +1 -1
  74. package/dist/schema/enum.js +49 -0
  75. package/dist/schema/enum.js.map +1 -1
  76. package/dist/schema/integer.d.ts +43 -0
  77. package/dist/schema/integer.d.ts.map +1 -1
  78. package/dist/schema/integer.js +38 -1
  79. package/dist/schema/integer.js.map +1 -1
  80. package/dist/schema/intersection.d.ts +55 -0
  81. package/dist/schema/intersection.d.ts.map +1 -1
  82. package/dist/schema/intersection.js +50 -0
  83. package/dist/schema/intersection.js.map +1 -1
  84. package/dist/schema/lex-map.d.ts +37 -0
  85. package/dist/schema/lex-map.d.ts.map +1 -0
  86. package/dist/schema/lex-map.js +60 -0
  87. package/dist/schema/lex-map.js.map +1 -0
  88. package/dist/schema/lex-value.d.ts +35 -0
  89. package/dist/schema/lex-value.d.ts.map +1 -0
  90. package/dist/schema/lex-value.js +87 -0
  91. package/dist/schema/lex-value.js.map +1 -0
  92. package/dist/schema/literal.d.ts +45 -0
  93. package/dist/schema/literal.d.ts.map +1 -1
  94. package/dist/schema/literal.js +45 -0
  95. package/dist/schema/literal.js.map +1 -1
  96. package/dist/schema/never.d.ts +43 -0
  97. package/dist/schema/never.d.ts.map +1 -1
  98. package/dist/schema/never.js +44 -1
  99. package/dist/schema/never.js.map +1 -1
  100. package/dist/schema/null.d.ts +30 -0
  101. package/dist/schema/null.d.ts.map +1 -1
  102. package/dist/schema/null.js +31 -1
  103. package/dist/schema/null.js.map +1 -1
  104. package/dist/schema/nullable.d.ts +42 -0
  105. package/dist/schema/nullable.d.ts.map +1 -1
  106. package/dist/schema/nullable.js +42 -0
  107. package/dist/schema/nullable.js.map +1 -1
  108. package/dist/schema/object.d.ts +57 -0
  109. package/dist/schema/object.d.ts.map +1 -1
  110. package/dist/schema/object.js +53 -1
  111. package/dist/schema/object.js.map +1 -1
  112. package/dist/schema/optional.d.ts +43 -0
  113. package/dist/schema/optional.d.ts.map +1 -1
  114. package/dist/schema/optional.js +43 -0
  115. package/dist/schema/optional.js.map +1 -1
  116. package/dist/schema/params.d.ts +96 -12
  117. package/dist/schema/params.d.ts.map +1 -1
  118. package/dist/schema/params.js +155 -21
  119. package/dist/schema/params.js.map +1 -1
  120. package/dist/schema/payload.d.ts +111 -15
  121. package/dist/schema/payload.d.ts.map +1 -1
  122. package/dist/schema/payload.js +73 -3
  123. package/dist/schema/payload.js.map +1 -1
  124. package/dist/schema/permission-set.d.ts +58 -0
  125. package/dist/schema/permission-set.d.ts.map +1 -1
  126. package/dist/schema/permission-set.js +50 -0
  127. package/dist/schema/permission-set.js.map +1 -1
  128. package/dist/schema/permission.d.ts +42 -0
  129. package/dist/schema/permission.d.ts.map +1 -1
  130. package/dist/schema/permission.js +39 -0
  131. package/dist/schema/permission.js.map +1 -1
  132. package/dist/schema/procedure.d.ts +64 -0
  133. package/dist/schema/procedure.d.ts.map +1 -1
  134. package/dist/schema/procedure.js +64 -0
  135. package/dist/schema/procedure.js.map +1 -1
  136. package/dist/schema/query.d.ts +55 -0
  137. package/dist/schema/query.d.ts.map +1 -1
  138. package/dist/schema/query.js +55 -0
  139. package/dist/schema/query.js.map +1 -1
  140. package/dist/schema/record.d.ts +76 -25
  141. package/dist/schema/record.d.ts.map +1 -1
  142. package/dist/schema/record.js +21 -0
  143. package/dist/schema/record.js.map +1 -1
  144. package/dist/schema/ref.d.ts +51 -0
  145. package/dist/schema/ref.d.ts.map +1 -1
  146. package/dist/schema/ref.js +18 -0
  147. package/dist/schema/ref.js.map +1 -1
  148. package/dist/schema/refine.d.ts +58 -9
  149. package/dist/schema/refine.d.ts.map +1 -1
  150. package/dist/schema/refine.js.map +1 -1
  151. package/dist/schema/regexp.d.ts +45 -0
  152. package/dist/schema/regexp.d.ts.map +1 -1
  153. package/dist/schema/regexp.js +46 -1
  154. package/dist/schema/regexp.js.map +1 -1
  155. package/dist/schema/string.d.ts +72 -6
  156. package/dist/schema/string.d.ts.map +1 -1
  157. package/dist/schema/string.js +56 -8
  158. package/dist/schema/string.js.map +1 -1
  159. package/dist/schema/subscription.d.ts +72 -2
  160. package/dist/schema/subscription.d.ts.map +1 -1
  161. package/dist/schema/subscription.js +59 -0
  162. package/dist/schema/subscription.js.map +1 -1
  163. package/dist/schema/token.d.ts +48 -0
  164. package/dist/schema/token.d.ts.map +1 -1
  165. package/dist/schema/token.js +49 -1
  166. package/dist/schema/token.js.map +1 -1
  167. package/dist/schema/typed-object.d.ts +73 -23
  168. package/dist/schema/typed-object.d.ts.map +1 -1
  169. package/dist/schema/typed-object.js +20 -1
  170. package/dist/schema/typed-object.js.map +1 -1
  171. package/dist/schema/typed-ref.d.ts +54 -0
  172. package/dist/schema/typed-ref.d.ts.map +1 -1
  173. package/dist/schema/typed-ref.js +16 -0
  174. package/dist/schema/typed-ref.js.map +1 -1
  175. package/dist/schema/typed-union.d.ts +51 -1
  176. package/dist/schema/typed-union.d.ts.map +1 -1
  177. package/dist/schema/typed-union.js +52 -2
  178. package/dist/schema/typed-union.js.map +1 -1
  179. package/dist/schema/union.d.ts +46 -0
  180. package/dist/schema/union.d.ts.map +1 -1
  181. package/dist/schema/union.js +41 -0
  182. package/dist/schema/union.js.map +1 -1
  183. package/dist/schema/unknown.d.ts +34 -0
  184. package/dist/schema/unknown.d.ts.map +1 -1
  185. package/dist/schema/unknown.js +34 -0
  186. package/dist/schema/unknown.js.map +1 -1
  187. package/dist/schema/with-default.d.ts +45 -0
  188. package/dist/schema/with-default.d.ts.map +1 -1
  189. package/dist/schema/with-default.js +45 -0
  190. package/dist/schema/with-default.js.map +1 -1
  191. package/dist/schema.d.ts +2 -1
  192. package/dist/schema.d.ts.map +1 -1
  193. package/dist/schema.js +2 -1
  194. package/dist/schema.js.map +1 -1
  195. package/dist/util/if-any.d.ts +2 -0
  196. package/dist/util/if-any.d.ts.map +1 -0
  197. package/dist/util/if-any.js +3 -0
  198. package/dist/util/if-any.js.map +1 -0
  199. package/package.json +3 -3
  200. package/src/core/$type.ts +150 -18
  201. package/src/core/record-key.ts +44 -0
  202. package/src/core/result.ts +86 -4
  203. package/src/core/schema.ts +244 -9
  204. package/src/core/string-format.ts +259 -13
  205. package/src/core/types.ts +91 -3
  206. package/src/core/validation-error.ts +60 -0
  207. package/src/core/validation-issue.ts +68 -2
  208. package/src/core/validator.ts +373 -12
  209. package/src/helpers.test.ts +110 -29
  210. package/src/helpers.ts +54 -25
  211. package/src/schema/array.test.ts +94 -79
  212. package/src/schema/array.ts +48 -1
  213. package/src/schema/blob.ts +50 -1
  214. package/src/schema/boolean.ts +31 -1
  215. package/src/schema/bytes.ts +41 -1
  216. package/src/schema/cid.ts +41 -1
  217. package/src/schema/custom.ts +68 -1
  218. package/src/schema/dict.ts +47 -1
  219. package/src/schema/discriminated-union.ts +61 -1
  220. package/src/schema/enum.ts +50 -0
  221. package/src/schema/integer.ts +45 -1
  222. package/src/schema/intersection.ts +56 -0
  223. package/src/schema/{unknown-object.test.ts → lex-map.test.ts} +9 -9
  224. package/src/schema/lex-map.ts +63 -0
  225. package/src/schema/lex-value.test.ts +81 -0
  226. package/src/schema/lex-value.ts +86 -0
  227. package/src/schema/literal.ts +46 -0
  228. package/src/schema/never.ts +45 -1
  229. package/src/schema/null.ts +32 -1
  230. package/src/schema/nullable.ts +43 -0
  231. package/src/schema/object.ts +59 -1
  232. package/src/schema/optional.ts +44 -0
  233. package/src/schema/params.test.ts +133 -38
  234. package/src/schema/params.ts +237 -37
  235. package/src/schema/payload.test.ts +3 -3
  236. package/src/schema/payload.ts +145 -42
  237. package/src/schema/permission-set.ts +58 -0
  238. package/src/schema/permission.ts +42 -0
  239. package/src/schema/procedure.ts +64 -0
  240. package/src/schema/query.ts +55 -0
  241. package/src/schema/record.ts +82 -16
  242. package/src/schema/ref.ts +52 -0
  243. package/src/schema/refine.ts +58 -9
  244. package/src/schema/regexp.ts +47 -1
  245. package/src/schema/string.test.ts +99 -2
  246. package/src/schema/string.ts +108 -15
  247. package/src/schema/subscription.ts +72 -2
  248. package/src/schema/token.ts +50 -1
  249. package/src/schema/typed-object.ts +81 -16
  250. package/src/schema/typed-ref.ts +55 -0
  251. package/src/schema/typed-union.ts +58 -3
  252. package/src/schema/union.ts +47 -0
  253. package/src/schema/unknown.ts +35 -0
  254. package/src/schema/with-default.ts +46 -0
  255. package/src/schema.ts +2 -1
  256. package/src/util/if-any.ts +3 -0
  257. package/dist/schema/unknown-object.d.ts +0 -8
  258. package/dist/schema/unknown-object.d.ts.map +0 -1
  259. package/dist/schema/unknown-object.js +0 -19
  260. package/dist/schema/unknown-object.js.map +0 -1
  261. package/src/schema/unknown-object.ts +0 -19
@@ -1,50 +1,101 @@
1
1
  import { LexValue } from '@atproto/lex-data'
2
- import { Infer, Schema } from '../core.js'
3
- import { ObjectSchema, ObjectSchemaShape, object } from './object.js'
4
-
5
- export type InferPayload<
6
- TPayload extends Payload,
7
- TBody,
8
- > = TPayload['encoding'] extends infer E extends string
9
- ? {
10
- encoding: SchemaEncodingToDataEncoding<E>
11
- body: InferPayloadBody<TPayload, TBody>
12
- }
13
- : void | undefined
2
+ import { Infer, Schema, Validator } from '../core.js'
3
+ import { ObjectSchema, object } from './object.js'
4
+
5
+ export type { LexValue }
14
6
 
15
- export type SchemaEncodingToDataEncoding<E extends string> = E extends '*/*'
7
+ type ToBodyMime<TEncoding extends string> = TEncoding extends '*/*'
16
8
  ? `${string}/${string}`
17
- : E extends `${infer T extends string}/*`
9
+ : TEncoding extends `${infer T extends string}/*`
18
10
  ? `${T}/${string}`
19
- : E
11
+ : TEncoding
12
+
13
+ type ToBodyType<
14
+ TEncoding extends string,
15
+ TSchema,
16
+ TBinary,
17
+ > = TSchema extends Schema
18
+ ? Infer<TSchema>
19
+ : TEncoding extends `application/json`
20
+ ? LexValue
21
+ : TBinary
20
22
 
23
+ /**
24
+ * Infers the type of a Payload's encoding and body.
25
+ *
26
+ * @template TPayload - The Payload type
27
+ * @template TBody - Fallback body type for non-JSON encodings
28
+ */
29
+ export type InferPayload<TPayload extends Payload, TBinary> =
30
+ TPayload extends Payload<infer TEncoding, infer TSchema>
31
+ ? TEncoding extends string
32
+ ? {
33
+ encoding: ToBodyMime<TEncoding>
34
+ body: ToBodyType<TEncoding, TSchema, TBinary>
35
+ }
36
+ : undefined
37
+ : never
38
+
39
+ /**
40
+ * Converts schema encoding patterns to data encoding types.
41
+ *
42
+ * Handles wildcards like '*\/*' and 'image/*' in MIME types.
43
+ *
44
+ * @template TPayload - The Payload type
45
+ */
21
46
  export type InferPayloadEncoding<TPayload extends Payload> =
22
- TPayload['encoding'] extends string
23
- ? SchemaEncodingToDataEncoding<TPayload['encoding']>
24
- : undefined
25
-
26
- export type InferPayloadBody<
27
- TPayload extends Payload,
28
- TBody,
29
- > = TPayload['encoding'] extends undefined
30
- ? undefined // No encoding, no payload
31
- : TPayload['schema'] extends Schema
32
- ? Infer<TPayload['schema']>
33
- : TPayload['encoding'] extends `application/json`
34
- ? LexValue
35
- : TBody
36
-
37
- export type PayloadShape<E extends string | undefined> = E extends undefined
47
+ TPayload extends Payload<infer TEncoding, any>
48
+ ? TEncoding extends string
49
+ ? ToBodyMime<TEncoding>
50
+ : undefined
51
+ : never
52
+
53
+ /**
54
+ * Infers the body type from a Payload and fallback type.
55
+ *
56
+ * @template TPayload - The Payload type
57
+ * @template TBody - Fallback body type for non-JSON encodings without schema
58
+ */
59
+ export type InferPayloadBody<TPayload extends Payload, TBinary> =
60
+ TPayload extends Payload<infer TEncoding, infer TSchema>
61
+ ? TEncoding extends string
62
+ ? ToBodyType<TEncoding, TSchema, TBinary>
63
+ : undefined
64
+ : never
65
+
66
+ /**
67
+ * Determines valid schema type based on encoding presence.
68
+ *
69
+ * @template E - The encoding string type, or undefined
70
+ */
71
+ export type PayloadSchema<E extends string | undefined> = E extends undefined
38
72
  ? undefined
39
- : Schema | undefined
73
+ : Schema<LexValue> | undefined
40
74
 
75
+ /**
76
+ * Represents a payload definition for Lexicon endpoints.
77
+ *
78
+ * Payloads define the body format for HTTP requests and responses.
79
+ * They consist of an encoding (MIME type) and an optional schema
80
+ * for validating the body content.
81
+ *
82
+ * @template TEncoding - The MIME type string, or undefined for no body
83
+ * @template TPayload - The schema type for body validation
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const jsonPayload = new Payload('application/json', l.object({ data: l.string() }))
88
+ * const binaryPayload = new Payload('image/*', undefined)
89
+ * const noPayload = new Payload(undefined, undefined)
90
+ * ```
91
+ */
41
92
  export class Payload<
42
93
  const TEncoding extends string | undefined = string | undefined,
43
- const TPayload extends PayloadShape<TEncoding> = PayloadShape<TEncoding>,
94
+ const TSchema extends PayloadSchema<TEncoding> = PayloadSchema<TEncoding>,
44
95
  > {
45
96
  constructor(
46
97
  readonly encoding: TEncoding,
47
- readonly schema: TPayload,
98
+ readonly schema: TSchema,
48
99
  ) {
49
100
  if (encoding === undefined && schema !== undefined) {
50
101
  throw new TypeError('schema cannot be defined when encoding is undefined')
@@ -56,15 +107,13 @@ export class Payload<
56
107
  * encoding.
57
108
  */
58
109
  matchesEncoding(contentType: string | undefined): boolean {
59
- const mime = contentType?.split(';', 1)[0].trim()
60
-
61
110
  const { encoding } = this
62
111
 
63
112
  // Handle undefined cases
64
113
  if (encoding === undefined) {
65
114
  // Expecting no body
66
- return mime === undefined
67
- } else if (mime === undefined) {
115
+ return contentType == null
116
+ } else if (contentType == null) {
68
117
  // Expecting a body, but got no content-type
69
118
  return false
70
119
  }
@@ -73,6 +122,7 @@ export class Payload<
73
122
  return true
74
123
  }
75
124
 
125
+ const mime = contentType?.split(';', 1)[0].trim()
76
126
  if (encoding.endsWith('/*')) {
77
127
  return mime.startsWith(encoding.slice(0, -1))
78
128
  }
@@ -86,17 +136,70 @@ export class Payload<
86
136
  }
87
137
  }
88
138
 
139
+ /**
140
+ * Creates a payload definition for Lexicon endpoint bodies.
141
+ *
142
+ * Defines the expected MIME type and optional validation schema for
143
+ * request or response bodies.
144
+ *
145
+ * @param encoding - MIME type string (e.g., 'application/json', 'image/*'), or undefined for no body
146
+ * @param validator - Optional schema for validating the body content. Must be undefined if encoding is undefined.
147
+ * @returns A new {@link Payload} instance
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * // JSON payload with schema
152
+ * const output = l.payload('application/json', l.object({
153
+ * posts: l.array(postSchema),
154
+ * cursor: l.optional(l.string()),
155
+ * }))
156
+ *
157
+ * // Binary payload (no schema validation)
158
+ * const blobInput = l.payload('*\/*', undefined)
159
+ *
160
+ * // Image payload with wildcard
161
+ * const imageInput = l.payload('image/*', undefined)
162
+ *
163
+ * // No payload (for endpoints without body)
164
+ * const noBody = l.payload()
165
+ * ```
166
+ */
89
167
  /*@__NO_SIDE_EFFECTS__*/
90
168
  export function payload<
91
169
  const E extends string | undefined = undefined,
92
- const S extends PayloadShape<E> = undefined,
170
+ const S extends PayloadSchema<E> = undefined,
93
171
  >(encoding: E = undefined as E, validator: S = undefined as S) {
94
172
  return new Payload<E, S>(encoding, validator)
95
173
  }
96
174
 
175
+ /**
176
+ * Creates a JSON payload with an object schema.
177
+ *
178
+ * Convenience function for the common case of JSON request/response bodies.
179
+ * Equivalent to `l.payload('application/json', l.object(properties))`.
180
+ *
181
+ * @param properties - Object mapping property names to validators
182
+ * @returns A new {@link Payload} instance with 'application/json' encoding
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * // Query output
187
+ * const profileOutput = l.jsonPayload({
188
+ * did: l.string({ format: 'did' }),
189
+ * handle: l.string({ format: 'handle' }),
190
+ * displayName: l.optional(l.string()),
191
+ * })
192
+ *
193
+ * // Procedure input
194
+ * const createPostInput = l.jsonPayload({
195
+ * text: l.string({ maxGraphemes: 300 }),
196
+ * createdAt: l.string({ format: 'datetime' }),
197
+ * })
198
+ * ```
199
+ */
97
200
  /*@__NO_SIDE_EFFECTS__*/
98
- export function jsonPayload<const P extends ObjectSchemaShape>(
99
- properties: P,
100
- ): Payload<'application/json', ObjectSchema<P>> {
201
+ export function jsonPayload<
202
+ P extends Record<string, Validator<undefined | LexValue>>,
203
+ >(properties: P): Payload<'application/json', ObjectSchema<P>> {
101
204
  return payload('application/json', object(properties))
102
205
  }
@@ -1,6 +1,14 @@
1
1
  import { NsidString } from '../core.js'
2
2
  import { Permission } from './permission.js'
3
3
 
4
+ /**
5
+ * Configuration options for a permission set.
6
+ *
7
+ * @property title - Human-readable title for the permission set
8
+ * @property title:lang - Localized titles by language code
9
+ * @property detail - Detailed description of the permission set
10
+ * @property detail:lang - Localized descriptions by language code
11
+ */
4
12
  export type PermissionSetOptions = {
5
13
  title?: string
6
14
  'title:lang'?: Record<string, undefined | string>
@@ -8,6 +16,24 @@ export type PermissionSetOptions = {
8
16
  'detail:lang'?: Record<string, undefined | string>
9
17
  }
10
18
 
19
+ /**
20
+ * Represents a collection of related permissions in AT Protocol.
21
+ *
22
+ * Permission sets group permissions together with metadata for OAuth
23
+ * authorization flows. They are identified by an NSID.
24
+ *
25
+ * @template TNsid - The NSID identifying this permission set
26
+ * @template TPermissions - Tuple type of the included permissions
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const feedAccess = new PermissionSet(
31
+ * 'app.bsky.feed.access',
32
+ * [readPermission, writePermission],
33
+ * { title: 'Feed Access', detail: 'Read and write to your feed' }
34
+ * )
35
+ * ```
36
+ */
11
37
  export class PermissionSet<
12
38
  const TNsid extends NsidString = any,
13
39
  const TPermissions extends readonly Permission[] = any,
@@ -19,6 +45,38 @@ export class PermissionSet<
19
45
  ) {}
20
46
  }
21
47
 
48
+ /**
49
+ * Creates a permission set grouping related permissions.
50
+ *
51
+ * Permission sets define OAuth scopes that applications can request.
52
+ * They include human-readable metadata for authorization UIs.
53
+ *
54
+ * @param nsid - The NSID identifying this permission set
55
+ * @param permissions - Array of permissions included in this set
56
+ * @param options - Optional metadata (title, detail, localization)
57
+ * @returns A new {@link PermissionSet} instance
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * // Define individual permissions
62
+ * const readPosts = l.permission('read', { collection: 'app.bsky.feed.post' })
63
+ * const writePosts = l.permission('write', { collection: 'app.bsky.feed.post' })
64
+ *
65
+ * // Group into a permission set
66
+ * const postManagement = l.permissionSet(
67
+ * 'app.bsky.feed.postManagement',
68
+ * [readPosts, writePosts],
69
+ * {
70
+ * title: 'Post Management',
71
+ * detail: 'View and create posts on your behalf',
72
+ * 'title:lang': {
73
+ * 'es': 'Gestion de publicaciones',
74
+ * 'fr': 'Gestion des publications',
75
+ * },
76
+ * }
77
+ * )
78
+ * ```
79
+ */
22
80
  /*@__NO_SIDE_EFFECTS__*/
23
81
  export function permissionSet<
24
82
  const N extends NsidString,
@@ -1,7 +1,24 @@
1
1
  import { Params } from './params.js'
2
2
 
3
+ /**
4
+ * Type alias for permission options (same as Params).
5
+ */
3
6
  export type PermissionOptions = Params
4
7
 
8
+ /**
9
+ * Represents a single permission in an AT Protocol permission set.
10
+ *
11
+ * Permissions define access rights to specific resources with optional
12
+ * parameters for fine-grained control.
13
+ *
14
+ * @template TResource - The resource identifier string type
15
+ * @template TOptions - The options type (must be valid Params)
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const readPermission = new Permission('read', { collection: 'app.bsky.feed.post' })
20
+ * ```
21
+ */
5
22
  export class Permission<
6
23
  const TResource extends string = any,
7
24
  const TOptions extends PermissionOptions = any,
@@ -12,6 +29,31 @@ export class Permission<
12
29
  ) {}
13
30
  }
14
31
 
32
+ /**
33
+ * Creates a permission definition for AT Protocol authorization.
34
+ *
35
+ * Permissions specify what resources an application can access.
36
+ * Used in permission sets to define OAuth scopes.
37
+ *
38
+ * @param resource - The resource identifier (e.g., 'read', 'write', 'admin')
39
+ * @param options - Optional parameters for the permission
40
+ * @returns A new {@link Permission} instance
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * // Simple permission
45
+ * const readPermission = l.permission('read')
46
+ *
47
+ * // Permission with options
48
+ * const writePostsPermission = l.permission('write', {
49
+ * collection: 'app.bsky.feed.post',
50
+ * })
51
+ *
52
+ * // Multiple permissions with different scopes
53
+ * const readProfile = l.permission('read', { collection: 'app.bsky.actor.profile' })
54
+ * const readFeed = l.permission('read', { collection: 'app.bsky.feed.*' })
55
+ * ```
56
+ */
15
57
  /*@__NO_SIDE_EFFECTS__*/
16
58
  export function permission<
17
59
  const R extends string,
@@ -2,6 +2,30 @@ import { NsidString } from '../core.js'
2
2
  import { ParamsSchema } from './params.js'
3
3
  import { Payload } from './payload.js'
4
4
 
5
+ /**
6
+ * Represents a Lexicon procedure (HTTP POST) endpoint definition.
7
+ *
8
+ * Procedures are operations that may modify state on the server.
9
+ * They have parameters, an input payload (request body), an output
10
+ * payload (response body), and optional error types.
11
+ *
12
+ * @template TNsid - The NSID identifying this procedure
13
+ * @template TParameters - The parameters schema type
14
+ * @template TInputPayload - The request body payload type
15
+ * @template TOutputPayload - The response body payload type
16
+ * @template TErrors - Array of error type strings, or undefined
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const createPost = new Procedure(
21
+ * 'app.bsky.feed.post',
22
+ * l.params({}),
23
+ * l.jsonPayload({ text: l.string() }),
24
+ * l.jsonPayload({ uri: l.string(), cid: l.string() }),
25
+ * ['InvalidRecord']
26
+ * )
27
+ * ```
28
+ */
5
29
  export class Procedure<
6
30
  const TNsid extends NsidString = NsidString,
7
31
  const TParameters extends ParamsSchema = ParamsSchema,
@@ -22,6 +46,46 @@ export class Procedure<
22
46
  ) {}
23
47
  }
24
48
 
49
+ /**
50
+ * Creates a procedure definition for a Lexicon POST endpoint.
51
+ *
52
+ * Procedures can modify server state. They accept both URL parameters
53
+ * and a request body (input payload).
54
+ *
55
+ * @param nsid - The NSID identifying this procedure endpoint
56
+ * @param parameters - Schema for URL query parameters
57
+ * @param input - Schema for request body payload
58
+ * @param output - Schema for response body payload
59
+ * @param errors - Optional array of error type strings
60
+ * @returns A new {@link Procedure} instance
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * // Create record procedure
65
+ * const createRecord = l.procedure(
66
+ * 'com.atproto.repo.createRecord',
67
+ * l.params({}),
68
+ * l.jsonPayload({
69
+ * repo: l.string({ format: 'at-identifier' }),
70
+ * collection: l.string({ format: 'nsid' }),
71
+ * record: l.unknown(),
72
+ * }),
73
+ * l.jsonPayload({
74
+ * uri: l.string({ format: 'at-uri' }),
75
+ * cid: l.string({ format: 'cid' }),
76
+ * }),
77
+ * ['InvalidRecord', 'RepoNotFound'],
78
+ * )
79
+ *
80
+ * // Procedure with binary input
81
+ * const uploadBlob = l.procedure(
82
+ * 'com.atproto.repo.uploadBlob',
83
+ * l.params({}),
84
+ * l.payload('*\/*', undefined), // Accept any content type
85
+ * l.jsonPayload({ blob: l.blob() }),
86
+ * )
87
+ * ```
88
+ */
25
89
  /*@__NO_SIDE_EFFECTS__*/
26
90
  export function procedure<
27
91
  const N extends NsidString,
@@ -2,6 +2,28 @@ import { NsidString } from '../core.js'
2
2
  import { ParamsSchema } from './params.js'
3
3
  import { Payload } from './payload.js'
4
4
 
5
+ /**
6
+ * Represents a Lexicon query (HTTP GET) endpoint definition.
7
+ *
8
+ * Queries are read-only operations that retrieve data from a server.
9
+ * They have parameters (passed as URL query parameters), an output
10
+ * payload, and optional error types.
11
+ *
12
+ * @template TNsid - The NSID identifying this query
13
+ * @template TParameters - The parameters schema type
14
+ * @template TOutputPayload - The output payload type
15
+ * @template TErrors - Array of error type strings, or undefined
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const getPostQuery = new Query(
20
+ * 'app.bsky.feed.getPost',
21
+ * l.params({ uri: l.string({ format: 'at-uri' }) }),
22
+ * l.payload('application/json', postSchema),
23
+ * ['NotFound']
24
+ * )
25
+ * ```
26
+ */
5
27
  export class Query<
6
28
  const TNsid extends NsidString = NsidString,
7
29
  const TParameters extends ParamsSchema = ParamsSchema,
@@ -20,6 +42,39 @@ export class Query<
20
42
  ) {}
21
43
  }
22
44
 
45
+ /**
46
+ * Creates a query definition for a Lexicon GET endpoint.
47
+ *
48
+ * Queries retrieve data without side effects. Parameters are sent as
49
+ * URL query string parameters.
50
+ *
51
+ * @param nsid - The NSID identifying this query endpoint
52
+ * @param parameters - Schema for URL query parameters
53
+ * @param output - Expected response payload schema
54
+ * @param errors - Optional array of error type strings
55
+ * @returns A new {@link Query} instance
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * // Simple query with JSON output
60
+ * const getProfile = l.query(
61
+ * 'app.bsky.actor.getProfile',
62
+ * l.params({ actor: l.string({ format: 'at-identifier' }) }),
63
+ * l.jsonPayload({ displayName: l.string(), handle: l.string() }),
64
+ * )
65
+ *
66
+ * // Query with pagination and errors
67
+ * const getTimeline = l.query(
68
+ * 'app.bsky.feed.getTimeline',
69
+ * l.params({
70
+ * limit: l.optional(l.integer({ minimum: 1, maximum: 100 })),
71
+ * cursor: l.optional(l.string()),
72
+ * }),
73
+ * l.jsonPayload({ feed: l.array(feedItemSchema), cursor: l.optional(l.string()) }),
74
+ * ['BlockedActor', 'BlockedByActor'],
75
+ * )
76
+ * ```
77
+ */
23
78
  /*@__NO_SIDE_EFFECTS__*/
24
79
  export function query<
25
80
  const N extends NsidString,
@@ -14,9 +14,41 @@ import {
14
14
  import { literal } from './literal.js'
15
15
  import { string } from './string.js'
16
16
 
17
+ /**
18
+ * Infers the record key type from a RecordSchema.
19
+ *
20
+ * @template R - The RecordSchema type
21
+ */
17
22
  export type InferRecordKey<R extends RecordSchema> =
18
23
  R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never
19
24
 
25
+ export type TypedRecord<
26
+ TType extends NsidString,
27
+ TValue extends { $type?: unknown } = { $type?: unknown },
28
+ > = TValue extends { $type: TType }
29
+ ? TValue
30
+ : $Typed<Exclude<TValue, Unknown$TypedObject>, TType>
31
+
32
+ /**
33
+ * Schema for AT Protocol records with a type identifier and key constraints.
34
+ *
35
+ * Records are the primary data unit in AT Protocol. Each record has a `$type`
36
+ * field identifying its Lexicon schema, and is stored at a specific key
37
+ * (TID, NSID, or other format) in a repository.
38
+ *
39
+ * @template TKey - The record key type ('tid', 'nsid', 'any', or 'literal:...')
40
+ * @template TType - The NSID string identifying this record type
41
+ * @template TShape - The validator type for the record's data shape
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const postSchema = new RecordSchema(
46
+ * 'tid',
47
+ * 'app.bsky.feed.post',
48
+ * l.object({ text: l.string(), createdAt: l.string() })
49
+ * )
50
+ * ```
51
+ */
20
52
  export class RecordSchema<
21
53
  const TKey extends LexiconRecordKey = any,
22
54
  const TType extends NsidString = any,
@@ -25,6 +57,8 @@ export class RecordSchema<
25
57
  $Typed<InferInput<TShape>, TType>,
26
58
  $Typed<InferOutput<TShape>, TType>
27
59
  > {
60
+ readonly type = 'record' as const
61
+
28
62
  keySchema: RecordKeySchema<TKey>
29
63
 
30
64
  constructor(
@@ -36,11 +70,9 @@ export class RecordSchema<
36
70
  this.keySchema = recordKey(key)
37
71
  }
38
72
 
39
- isTypeOf<X extends { $type?: unknown }>(
40
- value: X,
41
- ): value is X extends { $type: TType }
42
- ? X
43
- : $Typed<Exclude<X, Unknown$TypedObject>, TType> {
73
+ isTypeOf<TValue extends { $type?: unknown }>(
74
+ value: TValue,
75
+ ): value is TypedRecord<TType, TValue> {
44
76
  return value.$type === this.$type
45
77
  }
46
78
 
@@ -50,11 +82,15 @@ export class RecordSchema<
50
82
  return this.parse($typed(input, this.$type))
51
83
  }
52
84
 
53
- $isTypeOf<X extends { $type?: unknown }>(value: X) {
54
- return this.isTypeOf<X>(value)
85
+ $isTypeOf<TValue extends { $type?: unknown }>(
86
+ value: TValue,
87
+ ): value is TypedRecord<TType, TValue> {
88
+ return this.isTypeOf<TValue>(value)
55
89
  }
56
90
 
57
- $build(input: Omit<InferInput<this>, '$type'>) {
91
+ $build(
92
+ input: Omit<InferInput<this>, '$type'>,
93
+ ): $Typed<InferOutput<this>, TType> {
58
94
  return this.build(input)
59
95
  }
60
96
 
@@ -115,15 +151,45 @@ function recordKey<Key extends LexiconRecordKey>(
115
151
  type AsNsid<T> = T extends `${string}#${string}` ? never : T
116
152
 
117
153
  /**
154
+ * Creates a record schema for AT Protocol records.
155
+ *
156
+ * Records are the primary data unit in AT Protocol repositories. They have
157
+ * a `$type` field identifying their Lexicon schema, and are stored at keys
158
+ * following a specific format (TID, NSID, etc.).
159
+ *
118
160
  * This function offers two overloads:
119
- * - One that allows creating a {@link RecordSchema}, and infer the output type
120
- * from the provided arguments, without requiring to specify any of the
121
- * generics. This is useful when you want to define a record without
122
- * explicitly defining its interface. This version does not support circular
123
- * references, as TypeScript cannot infer types in such cases.
124
- * - One allows creating a {@link RecordSchema} with an explicitly defined
125
- * interface. This will typically be used by codegen (`lex build`) to generate
126
- * schemas that work even if they contain circular references.
161
+ * - One that infers the output type from the provided arguments (does not
162
+ * support circular references)
163
+ * - One with an explicitly defined interface for use with codegen and
164
+ * circular references
165
+ *
166
+ * @param key - The record key type: 'tid', 'nsid', 'any', or 'literal:value'
167
+ * @param type - The NSID identifying this record type (e.g., 'app.bsky.feed.post')
168
+ * @param validator - Schema validator for the record's properties
169
+ * @returns A new {@link RecordSchema} instance
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * // Post record with TID key
174
+ * const postSchema = l.record('tid', 'app.bsky.feed.post', l.object({
175
+ * text: l.string({ maxGraphemes: 300 }),
176
+ * createdAt: l.string({ format: 'datetime' }),
177
+ * reply: l.optional(l.object({
178
+ * root: l.ref(() => strongRefSchema),
179
+ * parent: l.ref(() => strongRefSchema),
180
+ * })),
181
+ * }))
182
+ *
183
+ * // Profile record with literal 'self' key
184
+ * const profileSchema = l.record('literal:self', 'app.bsky.actor.profile', l.object({
185
+ * displayName: l.optional(l.string({ maxGraphemes: 64 })),
186
+ * description: l.optional(l.string({ maxGraphemes: 256 })),
187
+ * avatar: l.optional(l.blob({ accept: ['image/*'] })),
188
+ * }))
189
+ *
190
+ * // Build a record with automatic $type injection
191
+ * const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })
192
+ * ```
127
193
  */
128
194
  export function record<
129
195
  const K extends LexiconRecordKey,