@atproto/lex-schema 0.0.0

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 (243) hide show
  1. package/dist/core/$type.d.ts +4 -0
  2. package/dist/core/$type.d.ts.map +1 -0
  3. package/dist/core/$type.js +7 -0
  4. package/dist/core/$type.js.map +1 -0
  5. package/dist/core/record-key.d.ts +4 -0
  6. package/dist/core/record-key.d.ts.map +1 -0
  7. package/dist/core/record-key.js +16 -0
  8. package/dist/core/record-key.js.map +1 -0
  9. package/dist/core/result.d.ts +57 -0
  10. package/dist/core/result.d.ts.map +1 -0
  11. package/dist/core/result.js +74 -0
  12. package/dist/core/result.js.map +1 -0
  13. package/dist/core/string-format.d.ts +31 -0
  14. package/dist/core/string-format.d.ts.map +1 -0
  15. package/dist/core/string-format.js +81 -0
  16. package/dist/core/string-format.js.map +1 -0
  17. package/dist/core/types.d.ts +19 -0
  18. package/dist/core/types.d.ts.map +1 -0
  19. package/dist/core/types.js +3 -0
  20. package/dist/core/types.js.map +1 -0
  21. package/dist/core.d.ts +6 -0
  22. package/dist/core.d.ts.map +1 -0
  23. package/dist/core.js +9 -0
  24. package/dist/core.js.map +1 -0
  25. package/dist/external.d.ts +86 -0
  26. package/dist/external.d.ts.map +1 -0
  27. package/dist/external.js +171 -0
  28. package/dist/external.js.map +1 -0
  29. package/dist/index.d.ts +4 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +8 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/schema/_parameters.d.ts +17 -0
  34. package/dist/schema/_parameters.d.ts.map +1 -0
  35. package/dist/schema/_parameters.js +20 -0
  36. package/dist/schema/_parameters.js.map +1 -0
  37. package/dist/schema/array.d.ts +13 -0
  38. package/dist/schema/array.d.ts.map +1 -0
  39. package/dist/schema/array.js +40 -0
  40. package/dist/schema/array.js.map +1 -0
  41. package/dist/schema/blob.d.ts +32 -0
  42. package/dist/schema/blob.d.ts.map +1 -0
  43. package/dist/schema/blob.js +40 -0
  44. package/dist/schema/blob.js.map +1 -0
  45. package/dist/schema/boolean.d.ts +11 -0
  46. package/dist/schema/boolean.d.ts.map +1 -0
  47. package/dist/schema/boolean.js +20 -0
  48. package/dist/schema/boolean.js.map +1 -0
  49. package/dist/schema/bytes.d.ts +12 -0
  50. package/dist/schema/bytes.d.ts.map +1 -0
  51. package/dist/schema/bytes.js +31 -0
  52. package/dist/schema/bytes.js.map +1 -0
  53. package/dist/schema/cid.d.ts +13 -0
  54. package/dist/schema/cid.d.ts.map +1 -0
  55. package/dist/schema/cid.js +22 -0
  56. package/dist/schema/cid.js.map +1 -0
  57. package/dist/schema/custom.d.ts +15 -0
  58. package/dist/schema/custom.d.ts.map +1 -0
  59. package/dist/schema/custom.js +22 -0
  60. package/dist/schema/custom.js.map +1 -0
  61. package/dist/schema/dict.d.ts +18 -0
  62. package/dist/schema/dict.d.ts.map +1 -0
  63. package/dist/schema/dict.js +48 -0
  64. package/dist/schema/dict.js.map +1 -0
  65. package/dist/schema/discriminated-union.d.ts +34 -0
  66. package/dist/schema/discriminated-union.d.ts.map +1 -0
  67. package/dist/schema/discriminated-union.js +93 -0
  68. package/dist/schema/discriminated-union.js.map +1 -0
  69. package/dist/schema/enum.d.ts +7 -0
  70. package/dist/schema/enum.d.ts.map +1 -0
  71. package/dist/schema/enum.js +19 -0
  72. package/dist/schema/enum.js.map +1 -0
  73. package/dist/schema/integer.d.ts +13 -0
  74. package/dist/schema/integer.d.ts.map +1 -0
  75. package/dist/schema/integer.js +32 -0
  76. package/dist/schema/integer.js.map +1 -0
  77. package/dist/schema/intersection.d.ts +16 -0
  78. package/dist/schema/intersection.d.ts.map +1 -0
  79. package/dist/schema/intersection.js +33 -0
  80. package/dist/schema/intersection.js.map +1 -0
  81. package/dist/schema/literal.d.ts +7 -0
  82. package/dist/schema/literal.d.ts.map +1 -0
  83. package/dist/schema/literal.js +19 -0
  84. package/dist/schema/literal.js.map +1 -0
  85. package/dist/schema/never.d.ts +5 -0
  86. package/dist/schema/never.d.ts.map +1 -0
  87. package/dist/schema/never.js +11 -0
  88. package/dist/schema/never.js.map +1 -0
  89. package/dist/schema/null.d.ts +7 -0
  90. package/dist/schema/null.d.ts.map +1 -0
  91. package/dist/schema/null.js +18 -0
  92. package/dist/schema/null.js.map +1 -0
  93. package/dist/schema/object.d.ts +47 -0
  94. package/dist/schema/object.d.ts.map +1 -0
  95. package/dist/schema/object.js +89 -0
  96. package/dist/schema/object.js.map +1 -0
  97. package/dist/schema/params.d.ts +22 -0
  98. package/dist/schema/params.d.ts.map +1 -0
  99. package/dist/schema/params.js +115 -0
  100. package/dist/schema/params.js.map +1 -0
  101. package/dist/schema/payload.d.ts +19 -0
  102. package/dist/schema/payload.d.ts.map +1 -0
  103. package/dist/schema/payload.js +16 -0
  104. package/dist/schema/payload.js.map +1 -0
  105. package/dist/schema/permission-set.d.ts +15 -0
  106. package/dist/schema/permission-set.d.ts.map +1 -0
  107. package/dist/schema/permission-set.js +16 -0
  108. package/dist/schema/permission-set.js.map +1 -0
  109. package/dist/schema/permission.d.ts +9 -0
  110. package/dist/schema/permission.d.ts.map +1 -0
  111. package/dist/schema/permission.js +14 -0
  112. package/dist/schema/permission.js.map +1 -0
  113. package/dist/schema/procedure.d.ts +17 -0
  114. package/dist/schema/procedure.d.ts.map +1 -0
  115. package/dist/schema/procedure.js +20 -0
  116. package/dist/schema/procedure.js.map +1 -0
  117. package/dist/schema/query.d.ts +15 -0
  118. package/dist/schema/query.d.ts.map +1 -0
  119. package/dist/schema/query.js +18 -0
  120. package/dist/schema/query.js.map +1 -0
  121. package/dist/schema/record.d.ts +37 -0
  122. package/dist/schema/record.d.ts.map +1 -0
  123. package/dist/schema/record.js +64 -0
  124. package/dist/schema/record.js.map +1 -0
  125. package/dist/schema/ref.d.ts +10 -0
  126. package/dist/schema/ref.d.ts.map +1 -0
  127. package/dist/schema/ref.js +36 -0
  128. package/dist/schema/ref.js.map +1 -0
  129. package/dist/schema/string.d.ts +24 -0
  130. package/dist/schema/string.d.ts.map +1 -0
  131. package/dist/schema/string.js +107 -0
  132. package/dist/schema/string.js.map +1 -0
  133. package/dist/schema/subscription.d.ts +16 -0
  134. package/dist/schema/subscription.d.ts.map +1 -0
  135. package/dist/schema/subscription.js +18 -0
  136. package/dist/schema/subscription.js.map +1 -0
  137. package/dist/schema/token.d.ts +10 -0
  138. package/dist/schema/token.d.ts.map +1 -0
  139. package/dist/schema/token.js +36 -0
  140. package/dist/schema/token.js.map +1 -0
  141. package/dist/schema/typed-object.d.ts +32 -0
  142. package/dist/schema/typed-object.d.ts.map +1 -0
  143. package/dist/schema/typed-object.js +40 -0
  144. package/dist/schema/typed-object.js.map +1 -0
  145. package/dist/schema/typed-ref.d.ts +30 -0
  146. package/dist/schema/typed-ref.d.ts.map +1 -0
  147. package/dist/schema/typed-ref.js +44 -0
  148. package/dist/schema/typed-ref.js.map +1 -0
  149. package/dist/schema/typed-union.d.ts +26 -0
  150. package/dist/schema/typed-union.d.ts.map +1 -0
  151. package/dist/schema/typed-union.js +54 -0
  152. package/dist/schema/typed-union.js.map +1 -0
  153. package/dist/schema/union.d.ts +9 -0
  154. package/dist/schema/union.d.ts.map +1 -0
  155. package/dist/schema/union.js +29 -0
  156. package/dist/schema/union.js.map +1 -0
  157. package/dist/schema/unknown-object.d.ts +9 -0
  158. package/dist/schema/unknown-object.d.ts.map +1 -0
  159. package/dist/schema/unknown-object.js +16 -0
  160. package/dist/schema/unknown-object.js.map +1 -0
  161. package/dist/schema/unknown.d.ts +5 -0
  162. package/dist/schema/unknown.d.ts.map +1 -0
  163. package/dist/schema/unknown.js +11 -0
  164. package/dist/schema/unknown.js.map +1 -0
  165. package/dist/schema.d.ts +34 -0
  166. package/dist/schema.d.ts.map +1 -0
  167. package/dist/schema.js +41 -0
  168. package/dist/schema.js.map +1 -0
  169. package/dist/util/array-agg.d.ts +20 -0
  170. package/dist/util/array-agg.d.ts.map +1 -0
  171. package/dist/util/array-agg.js +42 -0
  172. package/dist/util/array-agg.js.map +1 -0
  173. package/dist/validation/property-key.d.ts +2 -0
  174. package/dist/validation/property-key.d.ts.map +1 -0
  175. package/dist/validation/property-key.js +3 -0
  176. package/dist/validation/property-key.js.map +1 -0
  177. package/dist/validation/validation-error.d.ts +9 -0
  178. package/dist/validation/validation-error.d.ts.map +1 -0
  179. package/dist/validation/validation-error.js +27 -0
  180. package/dist/validation/validation-error.js.map +1 -0
  181. package/dist/validation/validation-issue.d.ts +45 -0
  182. package/dist/validation/validation-issue.d.ts.map +1 -0
  183. package/dist/validation/validation-issue.js +167 -0
  184. package/dist/validation/validation-issue.js.map +1 -0
  185. package/dist/validation/validator.d.ts +113 -0
  186. package/dist/validation/validator.d.ts.map +1 -0
  187. package/dist/validation/validator.js +209 -0
  188. package/dist/validation/validator.js.map +1 -0
  189. package/dist/validation.d.ts +5 -0
  190. package/dist/validation.d.ts.map +1 -0
  191. package/dist/validation.js +8 -0
  192. package/dist/validation.js.map +1 -0
  193. package/package.json +45 -0
  194. package/src/core/$type.ts +19 -0
  195. package/src/core/record-key.ts +15 -0
  196. package/src/core/result.ts +73 -0
  197. package/src/core/string-format.ts +124 -0
  198. package/src/core/types.ts +22 -0
  199. package/src/core.ts +5 -0
  200. package/src/external.ts +365 -0
  201. package/src/index.ts +3 -0
  202. package/src/schema/_parameters.ts +26 -0
  203. package/src/schema/array.ts +51 -0
  204. package/src/schema/blob.ts +82 -0
  205. package/src/schema/boolean.ts +24 -0
  206. package/src/schema/bytes.ts +38 -0
  207. package/src/schema/cid.ts +27 -0
  208. package/src/schema/custom.ts +36 -0
  209. package/src/schema/dict.ts +69 -0
  210. package/src/schema/discriminated-union.ts +144 -0
  211. package/src/schema/enum.ts +20 -0
  212. package/src/schema/integer.ts +41 -0
  213. package/src/schema/intersection.ts +57 -0
  214. package/src/schema/literal.ts +20 -0
  215. package/src/schema/never.ts +14 -0
  216. package/src/schema/null.ts +20 -0
  217. package/src/schema/object.test.ts +138 -0
  218. package/src/schema/object.ts +180 -0
  219. package/src/schema/params.ts +157 -0
  220. package/src/schema/payload.ts +53 -0
  221. package/src/schema/permission-set.ts +22 -0
  222. package/src/schema/permission.ts +15 -0
  223. package/src/schema/procedure.ts +35 -0
  224. package/src/schema/query.ts +28 -0
  225. package/src/schema/record.ts +106 -0
  226. package/src/schema/ref.ts +47 -0
  227. package/src/schema/string.ts +139 -0
  228. package/src/schema/subscription.ts +35 -0
  229. package/src/schema/token.ts +41 -0
  230. package/src/schema/typed-object.ts +64 -0
  231. package/src/schema/typed-ref.ts +68 -0
  232. package/src/schema/typed-union.ts +106 -0
  233. package/src/schema/union.ts +40 -0
  234. package/src/schema/unknown-object.ts +20 -0
  235. package/src/schema/unknown.ts +10 -0
  236. package/src/schema.ts +40 -0
  237. package/src/util/array-agg.test.ts +41 -0
  238. package/src/util/array-agg.ts +43 -0
  239. package/src/validation/property-key.ts +1 -0
  240. package/src/validation/validation-error.ts +32 -0
  241. package/src/validation/validation-issue.ts +231 -0
  242. package/src/validation/validator.ts +361 -0
  243. package/src/validation.ts +4 -0
@@ -0,0 +1,124 @@
1
+ import { ensureValidCidString, isLanguage } from '@atproto/lex-data'
2
+ import {
3
+ ensureValidAtUri,
4
+ ensureValidDatetime,
5
+ ensureValidDid,
6
+ ensureValidHandle,
7
+ ensureValidNsid,
8
+ ensureValidRecordKey,
9
+ ensureValidTid,
10
+ } from '@atproto/syntax'
11
+
12
+ // Allow (date as Date).toISOString() to be used where datetime format is expected
13
+ declare global {
14
+ interface Date {
15
+ toISOString(): `${string}T${string}Z`
16
+ }
17
+ }
18
+
19
+ export const STRING_FORMATS = Object.freeze([
20
+ 'datetime',
21
+ 'uri',
22
+ 'at-uri',
23
+ 'did',
24
+ 'handle',
25
+ 'at-identifier',
26
+ 'nsid',
27
+ 'cid',
28
+ 'language',
29
+ 'tid',
30
+ 'record-key',
31
+ ] as const)
32
+
33
+ export type StringFormat = (typeof STRING_FORMATS)[number]
34
+
35
+ export type Did<M extends string = string> = `did:${M}:${string}`
36
+ export type Uri = `${string}:${string}`
37
+ export type Nsid = `${string}.${string}.${string}`
38
+ /** An ISO 8601 formatted datetime string (YYYY-MM-DDTHH:mm:ss.sssZ) */
39
+ export type Datetime = `${string}T${string}`
40
+ export type Handle = `${string}.${string}`
41
+ export type AtIdentifier = Did | Handle
42
+ export type AtUri = `at://${AtIdentifier}/${Nsid}/${string}`
43
+
44
+ export type InferStringFormat<F> =
45
+ //
46
+ F extends 'datetime'
47
+ ? Datetime
48
+ : F extends 'uri'
49
+ ? Uri
50
+ : F extends 'at-uri'
51
+ ? AtUri
52
+ : F extends 'did'
53
+ ? Did
54
+ : F extends 'handle'
55
+ ? Handle
56
+ : F extends 'at-identifier'
57
+ ? AtIdentifier
58
+ : F extends 'nsid'
59
+ ? Nsid
60
+ : string
61
+
62
+ type AssertFn<T> = <I extends string>(input: I) => asserts input is I & T
63
+
64
+ // Re-export utility typed as assertion functions so that TypeScript can
65
+ // infer the narrowed type after calling them.
66
+
67
+ export const assertDid: AssertFn<Did> = ensureValidDid
68
+ export const assertAtUri: AssertFn<AtUri> = ensureValidAtUri
69
+ export const assertNsid: AssertFn<Nsid> = ensureValidNsid
70
+ export const assertTid: AssertFn<string> = ensureValidTid
71
+ export const assertRecordKey: AssertFn<string> = ensureValidRecordKey
72
+ export const assertDatetime: AssertFn<Datetime> = ensureValidDatetime
73
+ export const assertCidString: AssertFn<string> = ensureValidCidString
74
+ export const assertHandle: AssertFn<Handle> = ensureValidHandle
75
+
76
+ // Export utilities for formats missing from @atproto/syntax
77
+
78
+ export const assertUri: AssertFn<Uri> = (input) => {
79
+ if (!/^\w+:(?:\/\/)?[^\s/][^\s]*$/.test(input)) {
80
+ throw new Error('Invalid URI')
81
+ }
82
+ }
83
+ export const assertLanguage: AssertFn<string> = (input) => {
84
+ if (!isLanguage(input)) {
85
+ throw new Error('Invalid BCP 47 string')
86
+ }
87
+ }
88
+ export const assertAtIdentifier: AssertFn<AtIdentifier> = (input) => {
89
+ if (input.startsWith('did:web:') || input.startsWith('did:plc:')) {
90
+ assertDid(input)
91
+ } else if (input.startsWith('did:')) {
92
+ throw new Error('Invalid DID method')
93
+ } else {
94
+ try {
95
+ assertHandle(input)
96
+ } catch (cause) {
97
+ throw new Error('Invalid DID or handle', { cause })
98
+ }
99
+ }
100
+ }
101
+
102
+ const formatters = /*#__PURE__*/ new Map<StringFormat, (str: string) => void>([
103
+ ['datetime', assertDatetime],
104
+ ['uri', assertUri],
105
+ ['at-uri', assertAtUri],
106
+ ['did', assertDid],
107
+ ['handle', assertHandle],
108
+ ['at-identifier', assertAtIdentifier],
109
+ ['nsid', assertNsid],
110
+ ['cid', assertCidString],
111
+ ['language', assertLanguage],
112
+ ['tid', assertTid],
113
+ ['record-key', assertRecordKey],
114
+ ] as const)
115
+
116
+ export function assertStringFormat<F extends StringFormat>(
117
+ input: string,
118
+ format: F,
119
+ ): asserts input is InferStringFormat<F> {
120
+ const assertFn = formatters.get(format)
121
+ if (assertFn) assertFn(input)
122
+ // Fool-proof
123
+ else throw new Error(`Unknown string format: ${format}`)
124
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Same as {@link string} but prevents TypeScript allowing union types to
3
+ * be widened to `string` in IDEs.
4
+ */
5
+ export type UnknownString = string & NonNullable<unknown>
6
+
7
+ export type Simplify<T> = { [K in keyof T]: T[K] } & NonNullable<unknown>
8
+
9
+ // @NOTE there is no way to express "array containing at least one P", so we use
10
+ // "array that contains P at first or last position" as a workaround.
11
+ export type ArrayContaining<T, Items = unknown> =
12
+ | readonly [T, ...Items[]]
13
+ | readonly [...Items[], T]
14
+
15
+ declare const __restricted: unique symbol
16
+ /**
17
+ * A type that represents a value that cannot be used, with a custom
18
+ * message explaining the restriction.
19
+ */
20
+ export type Restricted<Message extends string> = typeof __restricted & {
21
+ [__restricted]: Message
22
+ }
package/src/core.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from './core/$type.js'
2
+ export * from './core/record-key.js'
3
+ export * from './core/result.js'
4
+ export * from './core/string-format.js'
5
+ export * from './core/types.js'
@@ -0,0 +1,365 @@
1
+ import { $Type, $type, Nsid, RecordKey } from './core.js'
2
+ import {
3
+ ArraySchema,
4
+ ArraySchemaOptions,
5
+ BlobSchema,
6
+ BlobSchemaOptions,
7
+ BooleanSchema,
8
+ BooleanSchemaOptions,
9
+ BytesSchema,
10
+ BytesSchemaOptions,
11
+ CidSchema,
12
+ CustomAssertion,
13
+ CustomSchema,
14
+ DictSchema,
15
+ DiscriminatedUnionSchema,
16
+ DiscriminatedUnionSchemaVariants,
17
+ EnumSchema,
18
+ IntegerSchema,
19
+ IntegerSchemaOptions,
20
+ IntersectionSchema,
21
+ IntersectionSchemaValidators,
22
+ LiteralSchema,
23
+ NeverSchema,
24
+ NullSchema,
25
+ ObjectSchema,
26
+ ObjectSchemaOptions,
27
+ ObjectSchemaProperties,
28
+ ParamsSchema,
29
+ ParamsSchemaOptions,
30
+ ParamsSchemaProperties,
31
+ Payload,
32
+ PayloadBody,
33
+ Permission,
34
+ PermissionOptions,
35
+ PermissionSet,
36
+ PermissionSetOptions,
37
+ Procedure,
38
+ Query,
39
+ RecordSchema,
40
+ RefSchema,
41
+ RefSchemaGetter,
42
+ StringSchema,
43
+ StringSchemaOptions,
44
+ Subscription,
45
+ TokenSchema,
46
+ TypedObjectSchema,
47
+ TypedRefGetter,
48
+ TypedRefSchema,
49
+ TypedUnionSchema,
50
+ UnionSchema,
51
+ UnionSchemaValidators,
52
+ UnknownObjectOutput,
53
+ UnknownObjectSchema,
54
+ UnknownSchema,
55
+ } from './schema.js'
56
+ import { Infer, PropertyKey, Validator } from './validation.js'
57
+
58
+ export * from './core.js'
59
+ export * from './schema.js'
60
+ export * from './validation.js'
61
+
62
+ /*@__NO_SIDE_EFFECTS__*/
63
+ export function never() {
64
+ return new NeverSchema()
65
+ }
66
+
67
+ /*@__NO_SIDE_EFFECTS__*/
68
+ export function unknown() {
69
+ return new UnknownSchema()
70
+ }
71
+
72
+ /*@__NO_SIDE_EFFECTS__*/
73
+ export function _null() {
74
+ return new NullSchema()
75
+ }
76
+
77
+ export { _null as null }
78
+
79
+ /*@__NO_SIDE_EFFECTS__*/
80
+ export function literal<const V extends null | string | number | boolean>(
81
+ value: V,
82
+ ) {
83
+ return new LiteralSchema<V>(value)
84
+ }
85
+
86
+ /*@__NO_SIDE_EFFECTS__*/
87
+ export function _enum<const V extends null | string | number | boolean>(
88
+ value: readonly V[],
89
+ ) {
90
+ return new EnumSchema<V>(value)
91
+ }
92
+
93
+ // @NOTE "enum" is a reserved keyword in JS/TS
94
+ export { _enum as enum }
95
+
96
+ /*@__NO_SIDE_EFFECTS__*/
97
+ export function boolean(options: BooleanSchemaOptions = {}) {
98
+ return new BooleanSchema(options)
99
+ }
100
+
101
+ /*@__NO_SIDE_EFFECTS__*/
102
+ export function integer(options: IntegerSchemaOptions = {}) {
103
+ return new IntegerSchema(options)
104
+ }
105
+
106
+ /*@__NO_SIDE_EFFECTS__*/
107
+ export function cidLink() {
108
+ return new CidSchema()
109
+ }
110
+
111
+ /*@__NO_SIDE_EFFECTS__*/
112
+ export function bytes(options: BytesSchemaOptions = {}) {
113
+ return new BytesSchema(options)
114
+ }
115
+
116
+ /*@__NO_SIDE_EFFECTS__*/
117
+ export function blob(options: BlobSchemaOptions = {}) {
118
+ return new BlobSchema(options)
119
+ }
120
+
121
+ /*@__NO_SIDE_EFFECTS__*/
122
+ export function string<
123
+ const O extends StringSchemaOptions = NonNullable<unknown>,
124
+ >(options: StringSchemaOptions & O = {} as O) {
125
+ return new StringSchema<O>(options)
126
+ }
127
+
128
+ /*@__NO_SIDE_EFFECTS__*/
129
+ export function array<const T>(
130
+ items: Validator<T>,
131
+ options: ArraySchemaOptions = {},
132
+ ) {
133
+ return new ArraySchema(items, options)
134
+ }
135
+
136
+ /*@__NO_SIDE_EFFECTS__*/
137
+ export function object<
138
+ const P extends ObjectSchemaProperties,
139
+ const O extends ObjectSchemaOptions = NonNullable<unknown>,
140
+ >(
141
+ properties: ObjectSchemaProperties & P,
142
+ options: ObjectSchemaOptions & O = {} as O,
143
+ ) {
144
+ return new ObjectSchema<P, O>(properties, options)
145
+ }
146
+
147
+ /*@__NO_SIDE_EFFECTS__*/
148
+ export function dict<const K extends Validator, const V extends Validator>(
149
+ key: K,
150
+ value: V,
151
+ ) {
152
+ return new DictSchema<K, V>(key, value)
153
+ }
154
+
155
+ // Utility
156
+ export type { UnknownObjectOutput as UnknownObject }
157
+
158
+ /*@__NO_SIDE_EFFECTS__*/
159
+ export function unknownObject() {
160
+ return new UnknownObjectSchema()
161
+ }
162
+
163
+ /*@__NO_SIDE_EFFECTS__*/
164
+ export function ref<T>(get: RefSchemaGetter<T>) {
165
+ return new RefSchema<T>(get)
166
+ }
167
+
168
+ /*@__NO_SIDE_EFFECTS__*/
169
+ export function custom<T>(
170
+ assertion: CustomAssertion<T>,
171
+ message: string,
172
+ path?: PropertyKey | readonly PropertyKey[],
173
+ ) {
174
+ return new CustomSchema<T>(assertion, message, path)
175
+ }
176
+
177
+ /*@__NO_SIDE_EFFECTS__*/
178
+ export function union<const V extends UnionSchemaValidators>(validators: V) {
179
+ return new UnionSchema<V>(validators)
180
+ }
181
+
182
+ /*@__NO_SIDE_EFFECTS__*/
183
+ export function intersection<const V extends IntersectionSchemaValidators>(
184
+ validators: V,
185
+ ) {
186
+ return new IntersectionSchema<V>(validators)
187
+ }
188
+
189
+ /*@__NO_SIDE_EFFECTS__*/
190
+ export function discriminatedUnion<
191
+ const Discriminator extends string,
192
+ const Options extends DiscriminatedUnionSchemaVariants<Discriminator>,
193
+ >(discriminator: Discriminator, variants: Options) {
194
+ return new DiscriminatedUnionSchema<Discriminator, Options>(
195
+ discriminator,
196
+ variants,
197
+ )
198
+ }
199
+
200
+ /*@__NO_SIDE_EFFECTS__*/
201
+ export function token<const N extends Nsid, const H extends string>(
202
+ nsid: N,
203
+ hash: H,
204
+ ) {
205
+ return new TokenSchema($type(nsid, hash))
206
+ }
207
+
208
+ /*@__NO_SIDE_EFFECTS__*/
209
+ export function typedRef<const V extends { $type?: string }>(
210
+ get: TypedRefGetter<V>,
211
+ ) {
212
+ return new TypedRefSchema<V>(get)
213
+ }
214
+
215
+ /*@__NO_SIDE_EFFECTS__*/
216
+ export function typedUnion<
217
+ const R extends readonly TypedRefSchema[],
218
+ const C extends boolean,
219
+ >(refs: R, closed: C) {
220
+ return new TypedUnionSchema<R, C>(refs, closed)
221
+ }
222
+
223
+ /**
224
+ * This function offers two overloads:
225
+ * - One that allows creating a {@link TypedObjectSchema}, and infer the output
226
+ * type from the provided arguments, without requiring to specify any of the
227
+ * generics. This is useful when you want to define a record without
228
+ * explicitly defining its interface. This version does not support circular
229
+ * references, as TypeScript cannot infer types in such cases.
230
+ * - One allows creating a {@link TypedObjectSchema} with an explicitly defined
231
+ * interface. This will typically be used by codegen (`lex build`) to generate
232
+ * schemas that work even if they contain circular references.
233
+ */
234
+ export function typedObject<
235
+ const N extends Nsid,
236
+ const H extends string,
237
+ const Schema extends Validator<{ [_ in string]?: unknown }>,
238
+ >(nsid: N, hash: H, schema: Schema): TypedObjectSchema<$Type<N, H>, Schema>
239
+ export function typedObject<const V extends { $type?: $Type }>(
240
+ nsid: V extends { $type?: infer T extends string }
241
+ ? T extends `${infer N}#${string}`
242
+ ? N
243
+ : T // (T is a "main" type, so already an NSID)
244
+ : never,
245
+ hash: V extends { $type?: infer T extends string }
246
+ ? T extends `${string}#${infer H}`
247
+ ? H
248
+ : 'main'
249
+ : never,
250
+ schema: Validator<Omit<V, '$type'>>,
251
+ ): TypedObjectSchema<NonNullable<V['$type']>, typeof schema, V>
252
+ /*@__NO_SIDE_EFFECTS__*/
253
+ export function typedObject<
254
+ const N extends Nsid,
255
+ const H extends string,
256
+ const Schema extends Validator<{ [_ in string]?: unknown }>,
257
+ >(nsid: N, hash: H, schema: Schema) {
258
+ return new TypedObjectSchema<$Type<N, H>, Schema>($type(nsid, hash), schema)
259
+ }
260
+
261
+ /**
262
+ * Ensures that a `$type` used in a record is a valid NSID (i.e. no fragment).
263
+ */
264
+ type AsNsid<T> = T extends `${string}#${string}` ? never : T
265
+
266
+ /**
267
+ * This function offers two overloads:
268
+ * - One that allows creating a {@link RecordSchema}, and infer the output type
269
+ * from the provided arguments, without requiring to specify any of the
270
+ * generics. This is useful when you want to define a record without
271
+ * explicitly defining its interface. This version does not support circular
272
+ * references, as TypeScript cannot infer types in such cases.
273
+ * - One allows creating a {@link RecordSchema} with an explicitly defined
274
+ * interface. This will typically be used by codegen (`lex build`) to generate
275
+ * schemas that work even if they contain circular references.
276
+ */
277
+ export function record<
278
+ const K extends RecordKey,
279
+ const T extends Nsid,
280
+ const S extends Validator<{ [_ in string]?: unknown }>,
281
+ >(
282
+ key: K,
283
+ type: AsNsid<T>,
284
+ schema: S,
285
+ ): RecordSchema<K, T, S, Infer<S> & { $type: T }>
286
+ export function record<
287
+ const K extends RecordKey,
288
+ const V extends { $type: Nsid },
289
+ >(
290
+ key: K,
291
+ type: AsNsid<V['$type']>,
292
+ schema: Validator<Omit<V, '$type'>>,
293
+ ): RecordSchema<K, V['$type'], typeof schema, V>
294
+ /*@__NO_SIDE_EFFECTS__*/
295
+ export function record<
296
+ const K extends RecordKey,
297
+ const T extends Nsid,
298
+ const S extends Validator<{ [_ in string]?: unknown }>,
299
+ >(key: K, type: T, schema: S) {
300
+ return new RecordSchema<K, T, S, Infer<S> & { $type: T }>(key, type, schema)
301
+ }
302
+
303
+ /*@__NO_SIDE_EFFECTS__*/
304
+ export function params<
305
+ const P extends ParamsSchemaProperties = NonNullable<unknown>,
306
+ const O extends ParamsSchemaOptions = ParamsSchemaOptions,
307
+ >(properties: P = {} as P, options: ParamsSchemaOptions & O = {} as O) {
308
+ return new ParamsSchema<P, O>(properties, options)
309
+ }
310
+
311
+ /*@__NO_SIDE_EFFECTS__*/
312
+ export function payload<
313
+ const E extends string | undefined = undefined,
314
+ const S extends PayloadBody<E> = undefined,
315
+ >(encoding: E = undefined as E, schema: S = undefined as S) {
316
+ return new Payload<E, S>(encoding, schema)
317
+ }
318
+
319
+ /*@__NO_SIDE_EFFECTS__*/
320
+ export function query<
321
+ const N extends Nsid,
322
+ const P extends ParamsSchema,
323
+ const O extends Payload,
324
+ const E extends undefined | readonly string[] = undefined,
325
+ >(nsid: N, parameters: P, output: O, errors: E = undefined as E) {
326
+ return new Query<N, P, O, E>(nsid, parameters, output, errors)
327
+ }
328
+
329
+ /*@__NO_SIDE_EFFECTS__*/
330
+ export function procedure<
331
+ const N extends Nsid,
332
+ const P extends ParamsSchema,
333
+ const I extends Payload,
334
+ const O extends Payload,
335
+ const E extends undefined | readonly string[] = undefined,
336
+ >(nsid: N, parameters: P, input: I, output: O, errors: E = undefined as E) {
337
+ return new Procedure<N, P, I, O, E>(nsid, parameters, input, output, errors)
338
+ }
339
+
340
+ /*@__NO_SIDE_EFFECTS__*/
341
+ export function subscription<
342
+ const N extends string,
343
+ const P extends ParamsSchema,
344
+ const M extends undefined | RefSchema | TypedUnionSchema | ObjectSchema,
345
+ const E extends undefined | readonly string[] = undefined,
346
+ >(nsid: N, parameters: P, message: M, errors: E = undefined as E) {
347
+ return new Subscription<N, P, M, E>(nsid, parameters, message, errors)
348
+ }
349
+
350
+ /*@__NO_SIDE_EFFECTS__*/
351
+ export function permission<
352
+ const R extends string,
353
+ const O extends PermissionOptions,
354
+ >(resource: R, options: PermissionOptions & O = {} as O) {
355
+ return new Permission<R, O>(resource, options)
356
+ }
357
+
358
+ /*@__NO_SIDE_EFFECTS__*/
359
+ export function permissionSet<
360
+ const N extends string,
361
+ const P extends readonly Permission[],
362
+ const O extends PermissionSetOptions,
363
+ >(nsid: N, permissions: P, options: PermissionSetOptions & O = {} as O) {
364
+ return new PermissionSet<N, P, O>(nsid, permissions, options)
365
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import * as l from './external.js'
2
+ export * from './external.js'
3
+ export { l }
@@ -0,0 +1,26 @@
1
+ import { Infer, Validator } from '../validation.js'
2
+ import { ArraySchema } from './array.js'
3
+ import { BooleanSchema } from './boolean.js'
4
+ import { DictSchema } from './dict.js'
5
+ import { IntegerSchema } from './integer.js'
6
+ import { StringSchema } from './string.js'
7
+ import { UnionSchema } from './union.js'
8
+
9
+ export type ParamScalar = Infer<typeof paramScalarSchema>
10
+ const paramScalarSchema = new UnionSchema([
11
+ new BooleanSchema({}),
12
+ new IntegerSchema({}),
13
+ new StringSchema({}),
14
+ ])
15
+
16
+ export type Param = Infer<typeof paramSchema>
17
+ export const paramSchema = new UnionSchema([
18
+ paramScalarSchema,
19
+ new ArraySchema(paramScalarSchema, {}),
20
+ ])
21
+
22
+ export type Params = { [_: string]: undefined | Param }
23
+ export const paramsSchema = new DictSchema(
24
+ new StringSchema({}),
25
+ paramSchema,
26
+ ) satisfies Validator<Params>
@@ -0,0 +1,51 @@
1
+ import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
2
+
3
+ export type ArraySchemaOptions = {
4
+ minLength?: number
5
+ maxLength?: number
6
+ }
7
+
8
+ export class ArraySchema<Item = any> extends Validator<Array<Item>> {
9
+ readonly lexiconType = 'array' as const
10
+
11
+ constructor(
12
+ readonly items: Validator<Item>,
13
+ readonly options: ArraySchemaOptions,
14
+ ) {
15
+ super()
16
+ }
17
+
18
+ override validateInContext(
19
+ input: unknown,
20
+ ctx: ValidatorContext,
21
+ ): ValidationResult<Array<Item>> {
22
+ if (!Array.isArray(input)) {
23
+ return ctx.issueInvalidType(input, 'array')
24
+ }
25
+
26
+ const { minLength, maxLength } = this.options
27
+
28
+ if (minLength != null && input.length < minLength) {
29
+ return ctx.issueTooSmall(input, 'array', minLength, input.length)
30
+ }
31
+
32
+ if (maxLength != null && input.length > maxLength) {
33
+ return ctx.issueTooBig(input, 'array', maxLength, input.length)
34
+ }
35
+
36
+ let copy: undefined | Array<Item>
37
+
38
+ for (let i = 0; i < input.length; i++) {
39
+ const result = ctx.validateChild(input, i, this.items)
40
+ if (!result.success) return result
41
+
42
+ if (result.value !== input[i]) {
43
+ // Copy on write (but only if we did not already make a copy)
44
+ copy ??= Array.from(input)
45
+ copy[i] = result.value
46
+ }
47
+ }
48
+
49
+ return ctx.success(copy ?? input) as ValidationResult<Array<Item>>
50
+ }
51
+ }
@@ -0,0 +1,82 @@
1
+ import {
2
+ BlobRef,
3
+ LegacyBlobRef,
4
+ isBlobRef,
5
+ isLegacyBlobRef,
6
+ } from '@atproto/lex-data'
7
+ import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
8
+
9
+ export type BlobSchemaOptions = {
10
+ /**
11
+ * Whether to allow legacy blob references format
12
+ * @see {@link LegacyBlobRef}
13
+ */
14
+ allowLegacy?: boolean
15
+ /**
16
+ * Whether to enforce strict validation on the blob reference (CID version, codec, hash function)
17
+ */
18
+ strict?: boolean
19
+ /**
20
+ * List of accepted mime types
21
+ */
22
+ accept?: string[]
23
+ /**
24
+ * Maximum size in bytes
25
+ */
26
+ maxSize?: number
27
+ }
28
+
29
+ export type { BlobRef, LegacyBlobRef }
30
+
31
+ export type BlobSchemaOutput<Options> = Options extends { allowLegacy: true }
32
+ ? BlobRef | LegacyBlobRef
33
+ : BlobRef
34
+
35
+ export class BlobSchema<O extends BlobSchemaOptions> extends Validator<
36
+ BlobSchemaOutput<O>
37
+ > {
38
+ readonly lexiconType = 'blob' as const
39
+
40
+ constructor(readonly options: O) {
41
+ super()
42
+ }
43
+
44
+ override validateInContext(
45
+ input: unknown,
46
+ ctx: ValidatorContext,
47
+ ): ValidationResult<BlobSchemaOutput<O>> {
48
+ if (!isBlob(input, this.options)) {
49
+ return ctx.issueInvalidType(input, 'blob')
50
+ }
51
+
52
+ // @NOTE Historically, we did not enforce constraints on blob references
53
+ // https://github.com/bluesky-social/atproto/blob/4c15fb47cec26060bff2e710e95869a90c9d7fdd/packages/lexicon/src/validators/blob.ts#L5-L19
54
+
55
+ // const { accept } = this.options
56
+ // if (accept && !accept.includes(input.mimeType)) {
57
+ // return ctx.issueInvalidValue(input, accept)
58
+ // }
59
+
60
+ // const { maxSize } = this.options
61
+ // if (maxSize != null && input.size != -1 && input.size > maxSize) {
62
+ // return ctx.issueTooBig(input, 'blob', maxSize, input.size)
63
+ // }
64
+
65
+ return ctx.success(input)
66
+ }
67
+ }
68
+
69
+ function isBlob<O extends BlobSchemaOptions>(
70
+ input: unknown,
71
+ options: O,
72
+ ): input is BlobSchemaOutput<O> {
73
+ if ((input as any)?.$type !== undefined) {
74
+ return isBlobRef(input, options)
75
+ }
76
+
77
+ if (options.allowLegacy === true) {
78
+ return isLegacyBlobRef(input)
79
+ }
80
+
81
+ return false
82
+ }
@@ -0,0 +1,24 @@
1
+ import { ValidationResult, Validator, ValidatorContext } from '../validation.js'
2
+
3
+ export type BooleanSchemaOptions = {
4
+ default?: boolean
5
+ }
6
+
7
+ export class BooleanSchema extends Validator<boolean> {
8
+ readonly lexiconType = 'boolean' as const
9
+
10
+ constructor(readonly options: BooleanSchemaOptions) {
11
+ super()
12
+ }
13
+
14
+ override validateInContext(
15
+ input: unknown = this.options.default,
16
+ ctx: ValidatorContext,
17
+ ): ValidationResult<boolean> {
18
+ if (typeof input === 'boolean') {
19
+ return ctx.success(input)
20
+ }
21
+
22
+ return ctx.issueInvalidType(input, 'boolean')
23
+ }
24
+ }