@atproto/lex-schema 0.0.10 → 0.0.12
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.
- package/CHANGELOG.md +26 -0
- package/dist/core/$type.d.ts +149 -0
- package/dist/core/$type.d.ts.map +1 -1
- package/dist/core/$type.js +44 -0
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.d.ts +44 -0
- package/dist/core/record-key.d.ts.map +1 -1
- package/dist/core/record-key.js +30 -0
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.d.ts +85 -4
- package/dist/core/result.d.ts.map +1 -1
- package/dist/core/result.js +60 -4
- package/dist/core/result.js.map +1 -1
- package/dist/core/schema.d.ts +229 -2
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +197 -4
- package/dist/core/schema.js.map +1 -1
- package/dist/core/string-format.d.ts +244 -11
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +150 -0
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/types.d.ts +90 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/validation-error.d.ts +61 -1
- package/dist/core/validation-error.d.ts.map +1 -1
- package/dist/core/validation-error.js +60 -0
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.d.ts +61 -0
- package/dist/core/validation-issue.d.ts.map +1 -1
- package/dist/core/validation-issue.js +51 -0
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.d.ts +347 -10
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +184 -3
- package/dist/core/validator.js.map +1 -1
- package/dist/helpers.d.ts +13 -25
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +2 -2
- package/dist/helpers.js.map +1 -1
- package/dist/schema/array.d.ts +45 -0
- package/dist/schema/array.d.ts.map +1 -1
- package/dist/schema/array.js +14 -0
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.d.ts +46 -0
- package/dist/schema/blob.d.ts.map +1 -1
- package/dist/schema/blob.js +39 -0
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.d.ts +28 -0
- package/dist/schema/boolean.d.ts.map +1 -1
- package/dist/schema/boolean.js +28 -0
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.d.ts +38 -0
- package/dist/schema/bytes.d.ts.map +1 -1
- package/dist/schema/bytes.js +32 -0
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.d.ts +38 -0
- package/dist/schema/cid.d.ts.map +1 -1
- package/dist/schema/cid.js +33 -0
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.d.ts +66 -1
- package/dist/schema/custom.d.ts.map +1 -1
- package/dist/schema/custom.js +54 -0
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.d.ts +44 -0
- package/dist/schema/dict.d.ts.map +1 -1
- package/dist/schema/dict.js +44 -0
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.d.ts +58 -0
- package/dist/schema/discriminated-union.d.ts.map +1 -1
- package/dist/schema/discriminated-union.js +45 -0
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.d.ts +48 -0
- package/dist/schema/enum.d.ts.map +1 -1
- package/dist/schema/enum.js +48 -0
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.d.ts +42 -0
- package/dist/schema/integer.d.ts.map +1 -1
- package/dist/schema/integer.js +36 -0
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.d.ts +54 -0
- package/dist/schema/intersection.d.ts.map +1 -1
- package/dist/schema/intersection.js +49 -0
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/literal.d.ts +44 -0
- package/dist/schema/literal.d.ts.map +1 -1
- package/dist/schema/literal.js +44 -0
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.d.ts +42 -0
- package/dist/schema/never.d.ts.map +1 -1
- package/dist/schema/never.js +42 -0
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.d.ts +29 -0
- package/dist/schema/null.d.ts.map +1 -1
- package/dist/schema/null.js +29 -0
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.d.ts +41 -0
- package/dist/schema/nullable.d.ts.map +1 -1
- package/dist/schema/nullable.js +41 -0
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.d.ts +56 -0
- package/dist/schema/object.d.ts.map +1 -1
- package/dist/schema/object.js +51 -0
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.d.ts +42 -0
- package/dist/schema/optional.d.ts.map +1 -1
- package/dist/schema/optional.js +42 -0
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.d.ts +89 -7
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +84 -10
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.d.ts +111 -15
- package/dist/schema/payload.d.ts.map +1 -1
- package/dist/schema/payload.js +70 -0
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.d.ts +58 -0
- package/dist/schema/permission-set.d.ts.map +1 -1
- package/dist/schema/permission-set.js +50 -0
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.d.ts +42 -0
- package/dist/schema/permission.d.ts.map +1 -1
- package/dist/schema/permission.js +39 -0
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.d.ts +64 -0
- package/dist/schema/procedure.d.ts.map +1 -1
- package/dist/schema/procedure.js +64 -0
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.d.ts +55 -0
- package/dist/schema/query.d.ts.map +1 -1
- package/dist/schema/query.js +55 -0
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.d.ts +63 -8
- package/dist/schema/record.d.ts.map +1 -1
- package/dist/schema/record.js +20 -0
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.d.ts +50 -0
- package/dist/schema/ref.d.ts.map +1 -1
- package/dist/schema/ref.js +17 -0
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.d.ts +58 -9
- package/dist/schema/refine.d.ts.map +1 -1
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.d.ts +44 -0
- package/dist/schema/regexp.d.ts.map +1 -1
- package/dist/schema/regexp.js +44 -0
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.d.ts +50 -0
- package/dist/schema/string.d.ts.map +1 -1
- package/dist/schema/string.js +41 -0
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.d.ts +72 -2
- package/dist/schema/subscription.d.ts.map +1 -1
- package/dist/schema/subscription.js +59 -0
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.d.ts +47 -0
- package/dist/schema/token.d.ts.map +1 -1
- package/dist/schema/token.js +47 -0
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.d.ts +62 -8
- package/dist/schema/typed-object.d.ts.map +1 -1
- package/dist/schema/typed-object.js +18 -0
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.d.ts +53 -0
- package/dist/schema/typed-ref.d.ts.map +1 -1
- package/dist/schema/typed-ref.js +15 -0
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.d.ts +50 -1
- package/dist/schema/typed-union.d.ts.map +1 -1
- package/dist/schema/typed-union.js +50 -1
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.d.ts +45 -0
- package/dist/schema/union.d.ts.map +1 -1
- package/dist/schema/union.js +40 -0
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown-object.d.ts +34 -0
- package/dist/schema/unknown-object.d.ts.map +1 -1
- package/dist/schema/unknown-object.js +31 -0
- package/dist/schema/unknown-object.js.map +1 -1
- package/dist/schema/unknown.d.ts +33 -0
- package/dist/schema/unknown.d.ts.map +1 -1
- package/dist/schema/unknown.js +33 -0
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.d.ts +44 -0
- package/dist/schema/with-default.d.ts.map +1 -1
- package/dist/schema/with-default.js +44 -0
- package/dist/schema/with-default.js.map +1 -1
- package/package.json +4 -4
- package/src/core/$type.ts +150 -18
- package/src/core/record-key.ts +44 -0
- package/src/core/result.ts +86 -4
- package/src/core/schema.ts +236 -7
- package/src/core/string-format.ts +259 -13
- package/src/core/types.ts +91 -3
- package/src/core/validation-error.ts +60 -0
- package/src/core/validation-issue.ts +65 -0
- package/src/core/validator.ts +351 -10
- package/src/helpers.test.ts +110 -29
- package/src/helpers.ts +14 -14
- package/src/schema/array.test.ts +94 -79
- package/src/schema/array.ts +45 -0
- package/src/schema/blob.ts +46 -0
- package/src/schema/boolean.ts +28 -0
- package/src/schema/bytes.ts +38 -0
- package/src/schema/cid.ts +38 -0
- package/src/schema/custom.ts +66 -1
- package/src/schema/dict.ts +44 -0
- package/src/schema/discriminated-union.ts +58 -0
- package/src/schema/enum.ts +48 -0
- package/src/schema/integer.ts +42 -0
- package/src/schema/intersection.ts +54 -0
- package/src/schema/literal.ts +44 -0
- package/src/schema/never.ts +42 -0
- package/src/schema/null.ts +29 -0
- package/src/schema/nullable.ts +41 -0
- package/src/schema/object.ts +56 -0
- package/src/schema/optional.ts +42 -0
- package/src/schema/params.test.ts +58 -2
- package/src/schema/params.ts +124 -16
- package/src/schema/payload.test.ts +3 -3
- package/src/schema/payload.ts +142 -38
- package/src/schema/permission-set.ts +58 -0
- package/src/schema/permission.ts +42 -0
- package/src/schema/procedure.ts +64 -0
- package/src/schema/query.ts +55 -0
- package/src/schema/record.ts +63 -8
- package/src/schema/ref.ts +50 -0
- package/src/schema/refine.ts +58 -9
- package/src/schema/regexp.ts +44 -0
- package/src/schema/string.ts +50 -0
- package/src/schema/subscription.ts +72 -2
- package/src/schema/token.ts +47 -0
- package/src/schema/typed-object.ts +62 -8
- package/src/schema/typed-ref.ts +53 -0
- package/src/schema/typed-union.ts +55 -2
- package/src/schema/union.ts +45 -0
- package/src/schema/unknown-object.ts +34 -0
- package/src/schema/unknown.ts +33 -0
- package/src/schema/with-default.ts +44 -0
|
@@ -22,42 +22,193 @@ import {
|
|
|
22
22
|
} from '@atproto/syntax'
|
|
23
23
|
import { CheckFn } from '../util/assertion-util.js'
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// -----------------------------------------------------------------------------
|
|
26
|
+
// Individual string format types and type guards
|
|
27
|
+
// -----------------------------------------------------------------------------
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Type guard that checks if a value is a valid AT identifier (DID or handle).
|
|
31
|
+
*
|
|
32
|
+
* @param value - The value to check
|
|
33
|
+
* @returns `true` if the value is a valid AT identifier
|
|
34
|
+
*/
|
|
28
35
|
export const isAtIdentifierString: CheckFn<AtIdentifierString> = isValidAtId
|
|
36
|
+
export type {
|
|
37
|
+
/**
|
|
38
|
+
* An AT identifier string - either a DID or a handle.
|
|
39
|
+
*
|
|
40
|
+
* @example `"did:plc:1234..."` or `"alice.bsky.social"`
|
|
41
|
+
*/
|
|
42
|
+
AtIdentifierString,
|
|
43
|
+
}
|
|
29
44
|
|
|
30
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Type guard that checks if a value is a valid AT URI.
|
|
47
|
+
*
|
|
48
|
+
* @param value - The value to check
|
|
49
|
+
* @returns `true` if the value is a valid AT URI
|
|
50
|
+
*/
|
|
31
51
|
export const isAtUriString: CheckFn<AtUriString> = isValidAtUri
|
|
52
|
+
export type {
|
|
53
|
+
/**
|
|
54
|
+
* An AT URI string pointing to a resource in the AT Protocol network.
|
|
55
|
+
*
|
|
56
|
+
* @example `"at://did:plc:1234.../app.bsky.feed.post/3k2..."`
|
|
57
|
+
*/
|
|
58
|
+
AtUriString,
|
|
59
|
+
}
|
|
32
60
|
|
|
33
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Type guard that checks if a value is a valid CID string.
|
|
63
|
+
*
|
|
64
|
+
* @param value - The value to check
|
|
65
|
+
* @returns `true` if the value is a valid CID string
|
|
66
|
+
*/
|
|
34
67
|
export const isCidString = ((v) => validateCidString(v)) as CheckFn<CidString>
|
|
68
|
+
/**
|
|
69
|
+
* A Content Identifier (CID) string.
|
|
70
|
+
*
|
|
71
|
+
* CIDs are self-describing content addresses used to identify data by its hash.
|
|
72
|
+
*
|
|
73
|
+
* @example `"bafyreig..."`
|
|
74
|
+
*/
|
|
75
|
+
export type CidString = string
|
|
35
76
|
|
|
36
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Type guard that checks if a value is a valid datetime string.
|
|
79
|
+
*
|
|
80
|
+
* @param value - The value to check
|
|
81
|
+
* @returns `true` if the value is a valid datetime string
|
|
82
|
+
*/
|
|
37
83
|
export const isDatetimeString: CheckFn<DatetimeString> = isValidDatetime
|
|
84
|
+
export type {
|
|
85
|
+
/**
|
|
86
|
+
* An ISO 8601 datetime string.
|
|
87
|
+
*
|
|
88
|
+
* @example `"2024-01-15T12:30:00.000Z"`
|
|
89
|
+
*/
|
|
90
|
+
DatetimeString,
|
|
91
|
+
}
|
|
38
92
|
|
|
39
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Type guard that checks if a value is a valid DID string.
|
|
95
|
+
*
|
|
96
|
+
* @param value - The value to check
|
|
97
|
+
* @returns `true` if the value is a valid DID string
|
|
98
|
+
*/
|
|
40
99
|
export const isDidString: CheckFn<DidString> = isValidDid
|
|
100
|
+
export type {
|
|
101
|
+
/**
|
|
102
|
+
* A Decentralized Identifier (DID) string.
|
|
103
|
+
*
|
|
104
|
+
* DIDs are globally unique identifiers that don't require a central authority.
|
|
105
|
+
*
|
|
106
|
+
* @example `"did:plc:1234abcd..."` or `"did:web:example.com"`
|
|
107
|
+
*/
|
|
108
|
+
DidString,
|
|
109
|
+
}
|
|
41
110
|
|
|
42
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Type guard that checks if a value is a valid handle string.
|
|
113
|
+
*
|
|
114
|
+
* @param value - The value to check
|
|
115
|
+
* @returns `true` if the value is a valid handle string
|
|
116
|
+
*/
|
|
43
117
|
export const isHandleString: CheckFn<HandleString> = isValidHandle
|
|
118
|
+
export type {
|
|
119
|
+
/**
|
|
120
|
+
* A handle string - a human-readable identifier for users.
|
|
121
|
+
*
|
|
122
|
+
* @example `"alice.bsky.social"` or `"bob.example.com"`
|
|
123
|
+
*/
|
|
124
|
+
HandleString,
|
|
125
|
+
}
|
|
44
126
|
|
|
45
|
-
|
|
127
|
+
/**
|
|
128
|
+
* Type guard that checks if a value is a valid BCP-47 language tag.
|
|
129
|
+
*
|
|
130
|
+
* @param value - The value to check
|
|
131
|
+
* @returns `true` if the value is a valid language string
|
|
132
|
+
*/
|
|
46
133
|
export const isLanguageString = isValidLanguage as CheckFn<LanguageString>
|
|
134
|
+
/**
|
|
135
|
+
* A BCP-47 language tag string.
|
|
136
|
+
*
|
|
137
|
+
* @example `"en"`, `"en-US"`, `"zh-Hans"`
|
|
138
|
+
*/
|
|
139
|
+
export type LanguageString = string
|
|
47
140
|
|
|
48
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Type guard that checks if a value is a valid NSID string.
|
|
143
|
+
*
|
|
144
|
+
* @param value - The value to check
|
|
145
|
+
* @returns `true` if the value is a valid NSID string
|
|
146
|
+
*/
|
|
49
147
|
export const isNsidString: CheckFn<NsidString> = isValidNsid
|
|
148
|
+
export type {
|
|
149
|
+
/**
|
|
150
|
+
* A Namespaced Identifier (NSID) string identifying a lexicon.
|
|
151
|
+
*
|
|
152
|
+
* NSIDs use reverse-domain notation to identify schemas.
|
|
153
|
+
*
|
|
154
|
+
* @example `"app.bsky.feed.post"`, `"com.atproto.repo.createRecord"`
|
|
155
|
+
*/
|
|
156
|
+
NsidString,
|
|
157
|
+
}
|
|
50
158
|
|
|
51
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Type guard that checks if a value is a valid record key string.
|
|
161
|
+
*
|
|
162
|
+
* @param value - The value to check
|
|
163
|
+
* @returns `true` if the value is a valid record key string
|
|
164
|
+
*/
|
|
52
165
|
export const isRecordKeyString: CheckFn<RecordKeyString> = isValidRecordKey
|
|
166
|
+
export type {
|
|
167
|
+
/**
|
|
168
|
+
* A record key string identifying a record within a collection.
|
|
169
|
+
*
|
|
170
|
+
* @example `"3k2..."` (TID format) or `"self"` (literal key)
|
|
171
|
+
*/
|
|
172
|
+
RecordKeyString,
|
|
173
|
+
}
|
|
53
174
|
|
|
54
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Type guard that checks if a value is a valid TID string.
|
|
177
|
+
*
|
|
178
|
+
* @param value - The value to check
|
|
179
|
+
* @returns `true` if the value is a valid TID string
|
|
180
|
+
*/
|
|
55
181
|
export const isTidString: CheckFn<TidString> = isValidTid
|
|
182
|
+
export type {
|
|
183
|
+
/**
|
|
184
|
+
* A Timestamp Identifier (TID) string.
|
|
185
|
+
*
|
|
186
|
+
* TIDs are time-based identifiers used for record keys.
|
|
187
|
+
*
|
|
188
|
+
* @example `"3k2..."`
|
|
189
|
+
*/
|
|
190
|
+
TidString,
|
|
191
|
+
}
|
|
56
192
|
|
|
57
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Type guard that checks if a value is a valid URI string.
|
|
195
|
+
*
|
|
196
|
+
* @param value - The value to check
|
|
197
|
+
* @returns `true` if the value is a valid URI string
|
|
198
|
+
*/
|
|
58
199
|
export const isUriString: CheckFn<UriString> = isValidUri
|
|
200
|
+
export type {
|
|
201
|
+
/**
|
|
202
|
+
* A standard URI string.
|
|
203
|
+
*
|
|
204
|
+
* @example `"https://example.com/path"`
|
|
205
|
+
*/
|
|
206
|
+
UriString,
|
|
207
|
+
}
|
|
59
208
|
|
|
60
|
-
//
|
|
209
|
+
// -----------------------------------------------------------------------------
|
|
210
|
+
// String format registry
|
|
211
|
+
// -----------------------------------------------------------------------------
|
|
61
212
|
|
|
62
213
|
type StringFormats = {
|
|
63
214
|
'at-identifier': AtIdentifierString
|
|
@@ -73,6 +224,9 @@ type StringFormats = {
|
|
|
73
224
|
uri: UriString
|
|
74
225
|
}
|
|
75
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Union type of all valid string format names.
|
|
229
|
+
*/
|
|
76
230
|
export type StringFormat = Extract<keyof StringFormats, string>
|
|
77
231
|
|
|
78
232
|
const stringFormatVerifiers: {
|
|
@@ -93,10 +247,39 @@ const stringFormatVerifiers: {
|
|
|
93
247
|
uri: isUriString,
|
|
94
248
|
})
|
|
95
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Infers the string type for a given format name.
|
|
252
|
+
*
|
|
253
|
+
* @typeParam F - The format name
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* type Did = InferStringFormat<'did'>
|
|
258
|
+
* // Result: DidString
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
96
261
|
export type InferStringFormat<F extends StringFormat> = F extends StringFormat
|
|
97
262
|
? StringFormats[F]
|
|
98
263
|
: never
|
|
99
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Type guard that checks if a string matches a specific format.
|
|
267
|
+
*
|
|
268
|
+
* @typeParam I - The input string type
|
|
269
|
+
* @typeParam F - The format to check
|
|
270
|
+
* @param input - The string to validate
|
|
271
|
+
* @param format - The format name to validate against
|
|
272
|
+
* @returns `true` if the string matches the format
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* const value: string = 'did:plc:1234...'
|
|
277
|
+
* if (isStringFormat(value, 'did')) {
|
|
278
|
+
* // value is typed as DidString
|
|
279
|
+
* console.log('Valid DID:', value)
|
|
280
|
+
* }
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
100
283
|
/*@__NO_SIDE_EFFECTS__*/
|
|
101
284
|
export function isStringFormat<I extends string, F extends StringFormat>(
|
|
102
285
|
input: I,
|
|
@@ -109,6 +292,21 @@ export function isStringFormat<I extends string, F extends StringFormat>(
|
|
|
109
292
|
return formatVerifier(input)
|
|
110
293
|
}
|
|
111
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Asserts that a string matches a specific format, throwing if invalid.
|
|
297
|
+
*
|
|
298
|
+
* @typeParam I - The input string type
|
|
299
|
+
* @typeParam F - The format to check
|
|
300
|
+
* @param input - The string to validate
|
|
301
|
+
* @param format - The format name to validate against
|
|
302
|
+
* @throws {TypeError} If the string doesn't match the format
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* assertStringFormat(value, 'handle')
|
|
307
|
+
* // value is now typed as HandleString
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
112
310
|
/*@__NO_SIDE_EFFECTS__*/
|
|
113
311
|
export function assertStringFormat<I extends string, F extends StringFormat>(
|
|
114
312
|
input: I,
|
|
@@ -119,6 +317,24 @@ export function assertStringFormat<I extends string, F extends StringFormat>(
|
|
|
119
317
|
}
|
|
120
318
|
}
|
|
121
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Validates and returns a string as the specified format type, throwing if invalid.
|
|
322
|
+
*
|
|
323
|
+
* This is useful when you need to convert a string to a format type in an expression.
|
|
324
|
+
*
|
|
325
|
+
* @typeParam I - The input string type
|
|
326
|
+
* @typeParam F - The format to validate against
|
|
327
|
+
* @param input - The string to validate
|
|
328
|
+
* @param format - The format name to validate against
|
|
329
|
+
* @returns The input typed as the format type
|
|
330
|
+
* @throws {TypeError} If the string doesn't match the format
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* const did = asStringFormat(userInput, 'did')
|
|
335
|
+
* // did is typed as DidString
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
122
338
|
/*@__NO_SIDE_EFFECTS__*/
|
|
123
339
|
export function asStringFormat<I extends string, F extends StringFormat>(
|
|
124
340
|
input: I,
|
|
@@ -128,6 +344,26 @@ export function asStringFormat<I extends string, F extends StringFormat>(
|
|
|
128
344
|
return input
|
|
129
345
|
}
|
|
130
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Returns the string as the format type if valid, otherwise returns `undefined`.
|
|
349
|
+
*
|
|
350
|
+
* This is useful for optional validation where you want to handle invalid values
|
|
351
|
+
* without throwing.
|
|
352
|
+
*
|
|
353
|
+
* @typeParam I - The input string type
|
|
354
|
+
* @typeParam F - The format to validate against
|
|
355
|
+
* @param input - The string to validate
|
|
356
|
+
* @param format - The format name to validate against
|
|
357
|
+
* @returns The typed string if valid, otherwise `undefined`
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const did = ifStringFormat(maybeInvalid, 'did')
|
|
362
|
+
* if (did) {
|
|
363
|
+
* // did is typed as DidString
|
|
364
|
+
* }
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
131
367
|
/*@__NO_SIDE_EFFECTS__*/
|
|
132
368
|
export function ifStringFormat<I extends string, F extends StringFormat>(
|
|
133
369
|
input: I,
|
|
@@ -136,6 +372,16 @@ export function ifStringFormat<I extends string, F extends StringFormat>(
|
|
|
136
372
|
return isStringFormat(input, format) ? input : undefined
|
|
137
373
|
}
|
|
138
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Array of all valid string format names.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* for (const format of STRING_FORMATS) {
|
|
381
|
+
* console.log(format) // 'at-identifier', 'at-uri', 'cid', ...
|
|
382
|
+
* }
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
139
385
|
export const STRING_FORMATS = /*#__PURE__*/ Object.freeze(
|
|
140
386
|
/*#__PURE__*/ Object.keys(stringFormatVerifiers),
|
|
141
387
|
) as readonly StringFormat[]
|
package/src/core/types.ts
CHANGED
|
@@ -1,23 +1,95 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Same as
|
|
2
|
+
* Same as `string` but prevents TypeScript from allowing union types to
|
|
3
3
|
* be widened to `string` in IDEs.
|
|
4
|
+
*
|
|
5
|
+
* This is useful when you want autocompletion for known string values
|
|
6
|
+
* while still allowing arbitrary strings.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // With plain string, union is widened:
|
|
11
|
+
* type Status1 = 'active' | 'inactive' | string // just becomes "string"
|
|
12
|
+
*
|
|
13
|
+
* // With UnknownString, union is preserved:
|
|
14
|
+
* type Status2 = 'active' | 'inactive' | UnknownString
|
|
15
|
+
* // Autocomplete will suggest 'active' and 'inactive'
|
|
16
|
+
* ```
|
|
4
17
|
*/
|
|
5
18
|
export type UnknownString = string & NonNullable<unknown>
|
|
6
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Simplifies a type by expanding intersections and mapped types.
|
|
22
|
+
*
|
|
23
|
+
* This improves IDE tooltips by showing the actual shape of a type
|
|
24
|
+
* rather than complex intersections.
|
|
25
|
+
*
|
|
26
|
+
* @typeParam T - The type to simplify
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* type Complex = { a: string } & { b: number }
|
|
31
|
+
* type Simple = Simplify<Complex>
|
|
32
|
+
* // Hover shows: { a: string; b: number }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
7
35
|
export type Simplify<T> = { [K in keyof T]: T[K] } & NonNullable<unknown>
|
|
8
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Internal symbol for branding restricted types.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
9
41
|
declare const __restricted: unique symbol
|
|
42
|
+
|
|
10
43
|
/**
|
|
11
44
|
* A type that represents a value that cannot be used, with a custom
|
|
12
45
|
* message explaining the restriction.
|
|
46
|
+
*
|
|
47
|
+
* This is useful for creating "never use this" types that provide
|
|
48
|
+
* helpful error messages when someone tries to use them.
|
|
49
|
+
*
|
|
50
|
+
* @typeParam Message - A string literal type containing the error message
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* type DeprecatedField = Restricted<'This field has been deprecated. Use newField instead.'>
|
|
55
|
+
*
|
|
56
|
+
* interface MyType {
|
|
57
|
+
* oldField?: DeprecatedField
|
|
58
|
+
* newField: string
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* const obj: MyType = {
|
|
62
|
+
* oldField: 'value', // Error: Type 'string' is not assignable to type 'Restricted<...>'
|
|
63
|
+
* newField: 'value'
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
13
66
|
*/
|
|
14
67
|
export type Restricted<Message extends string> = typeof __restricted & {
|
|
15
68
|
[__restricted]: Message
|
|
16
69
|
}
|
|
17
70
|
|
|
18
71
|
/**
|
|
19
|
-
* Converts all properties of `P` that
|
|
20
|
-
*
|
|
72
|
+
* Converts all properties of `P` that may be `undefined` into actual
|
|
73
|
+
* optional properties on the resulting type.
|
|
74
|
+
*
|
|
75
|
+
* This is useful when working with types where some properties are typed as
|
|
76
|
+
* `T | undefined` but should really be optional (`T?`).
|
|
77
|
+
*
|
|
78
|
+
* @typeParam P - The object type to transform
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* type Input = {
|
|
83
|
+
* required: string
|
|
84
|
+
* optional: string | undefined
|
|
85
|
+
* }
|
|
86
|
+
*
|
|
87
|
+
* type Output = WithOptionalProperties<Input>
|
|
88
|
+
* // Result: {
|
|
89
|
+
* // required: string
|
|
90
|
+
* // optional?: string | undefined
|
|
91
|
+
* // }
|
|
92
|
+
* ```
|
|
21
93
|
*/
|
|
22
94
|
export type WithOptionalProperties<P> = Simplify<
|
|
23
95
|
{
|
|
@@ -27,6 +99,22 @@ export type WithOptionalProperties<P> = Simplify<
|
|
|
27
99
|
}
|
|
28
100
|
>
|
|
29
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Creates a type by omitting a specific key from an object type.
|
|
104
|
+
*
|
|
105
|
+
* Similar to TypeScript's built-in `Omit`, but preserves the type structure
|
|
106
|
+
* more accurately in some edge cases.
|
|
107
|
+
*
|
|
108
|
+
* @typeParam T - The object type to transform
|
|
109
|
+
* @typeParam K - The key to omit (must be a key of T)
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* type Person = { name: string; age: number; email: string }
|
|
114
|
+
* type PersonWithoutEmail = OmitKey<Person, 'email'>
|
|
115
|
+
* // Result: { name: string; age: number }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
30
118
|
export type OmitKey<T, K extends keyof T> = {
|
|
31
119
|
[K2 in keyof T as K2 extends K ? never : K2]: T[K2]
|
|
32
120
|
}
|
|
@@ -7,17 +7,60 @@ import {
|
|
|
7
7
|
IssueInvalidValue,
|
|
8
8
|
} from './validation-issue.js'
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Error thrown when validation fails.
|
|
12
|
+
*
|
|
13
|
+
* Contains detailed information about all validation issues encountered,
|
|
14
|
+
* including the path to each invalid value and descriptions of what was
|
|
15
|
+
* expected vs what was received.
|
|
16
|
+
*
|
|
17
|
+
* Extends {@link LexError} with the error name "InvalidRequest" for
|
|
18
|
+
* consistency with the AT Protocol error handling conventions.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const error = new ValidationError([
|
|
23
|
+
* new IssueInvalidType(['user', 'age'], 'hello', ['number'])
|
|
24
|
+
* ])
|
|
25
|
+
* console.log(error.message)
|
|
26
|
+
* // "Expected number value type at $.user.age (got string)"
|
|
27
|
+
*
|
|
28
|
+
* console.log(error.issues.length) // 1
|
|
29
|
+
* console.log(error.toJSON())
|
|
30
|
+
* // { error: 'InvalidRequest', message: '...', issues: [...] }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
10
33
|
export class ValidationError extends LexError {
|
|
11
34
|
name = 'ValidationError'
|
|
12
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The list of validation issues that caused this error.
|
|
38
|
+
*
|
|
39
|
+
* Issues are aggregated when possible (e.g., multiple invalid type issues
|
|
40
|
+
* at the same path are combined into a single issue listing all expected types).
|
|
41
|
+
*/
|
|
13
42
|
readonly issues: Issue[]
|
|
14
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Creates a new validation error from a list of issues.
|
|
46
|
+
*
|
|
47
|
+
* Issues are automatically aggregated to combine related issues at the same
|
|
48
|
+
* path (e.g., multiple type expectations from a union schema).
|
|
49
|
+
*
|
|
50
|
+
* @param issues - The validation issues that caused this error
|
|
51
|
+
* @param options - Standard Error options (e.g., `cause`)
|
|
52
|
+
*/
|
|
15
53
|
constructor(issues: Issue[], options?: ErrorOptions) {
|
|
16
54
|
const issuesAgg = aggregateIssues(issues)
|
|
17
55
|
super('InvalidRequest', issuesAgg.join(', '), options)
|
|
18
56
|
this.issues = issuesAgg
|
|
19
57
|
}
|
|
20
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Converts the error to a JSON-serializable object.
|
|
61
|
+
*
|
|
62
|
+
* @returns An object containing the error details and all issues in JSON format
|
|
63
|
+
*/
|
|
21
64
|
toJSON() {
|
|
22
65
|
return {
|
|
23
66
|
...super.toJSON(),
|
|
@@ -25,6 +68,23 @@ export class ValidationError extends LexError {
|
|
|
25
68
|
}
|
|
26
69
|
}
|
|
27
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Creates a validation error by combining multiple validation failures.
|
|
73
|
+
*
|
|
74
|
+
* This is useful when validating against multiple possible schemas (e.g., unions)
|
|
75
|
+
* and all branches fail. The resulting error contains issues from all failures.
|
|
76
|
+
*
|
|
77
|
+
* @param failures - The validation failures to combine
|
|
78
|
+
* @returns A single validation error containing all issues from the failures
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const failures = schemas.map(s => s.safeValidate(data)).filter(r => !r.success)
|
|
83
|
+
* if (failures.length === schemas.length) {
|
|
84
|
+
* throw ValidationError.fromFailures(failures)
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
28
88
|
static fromFailures(
|
|
29
89
|
failures: ResultFailure<ValidationError>[],
|
|
30
90
|
): ValidationError {
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { ifCid, isPlainObject } from '@atproto/lex-data'
|
|
2
2
|
import { PropertyKey } from './property-key.js'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for all validation issues.
|
|
6
|
+
*
|
|
7
|
+
* An issue represents a single validation failure, containing:
|
|
8
|
+
* - A code identifying the type of issue
|
|
9
|
+
* - The path to the invalid value in the data structure
|
|
10
|
+
* - The actual input value that failed validation
|
|
11
|
+
*
|
|
12
|
+
* Subclasses add specific properties relevant to each issue type and
|
|
13
|
+
* implement the {@link toString} method for human-readable error messages.
|
|
14
|
+
*/
|
|
4
15
|
export abstract class Issue {
|
|
5
16
|
constructor(
|
|
6
17
|
readonly code: string,
|
|
@@ -8,8 +19,16 @@ export abstract class Issue {
|
|
|
8
19
|
readonly input: unknown,
|
|
9
20
|
) {}
|
|
10
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Returns a human-readable description of the validation issue.
|
|
24
|
+
*/
|
|
11
25
|
abstract toString(): string
|
|
12
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Converts the issue to a JSON-serializable object.
|
|
29
|
+
*
|
|
30
|
+
* @returns An object containing the issue code, path, and message
|
|
31
|
+
*/
|
|
13
32
|
toJSON() {
|
|
14
33
|
return {
|
|
15
34
|
code: this.code,
|
|
@@ -19,6 +38,11 @@ export abstract class Issue {
|
|
|
19
38
|
}
|
|
20
39
|
}
|
|
21
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A custom validation issue with a user-defined message.
|
|
43
|
+
*
|
|
44
|
+
* Use this for validation rules that don't fit into the standard issue categories.
|
|
45
|
+
*/
|
|
22
46
|
export class IssueCustom extends Issue {
|
|
23
47
|
constructor(
|
|
24
48
|
readonly path: readonly PropertyKey[],
|
|
@@ -33,6 +57,11 @@ export class IssueCustom extends Issue {
|
|
|
33
57
|
}
|
|
34
58
|
}
|
|
35
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Issue for string values that don't match an expected format.
|
|
62
|
+
*
|
|
63
|
+
* Used for AT Protocol specific formats like DID, handle, NSID, AT-URI, etc.
|
|
64
|
+
*/
|
|
36
65
|
export class IssueInvalidFormat extends Issue {
|
|
37
66
|
constructor(
|
|
38
67
|
path: readonly PropertyKey[],
|
|
@@ -54,6 +83,7 @@ export class IssueInvalidFormat extends Issue {
|
|
|
54
83
|
}
|
|
55
84
|
}
|
|
56
85
|
|
|
86
|
+
/** Returns a human-readable description of the expected format. */
|
|
57
87
|
get formatDescription(): string {
|
|
58
88
|
switch (this.format) {
|
|
59
89
|
case 'at-identifier':
|
|
@@ -74,6 +104,12 @@ export class IssueInvalidFormat extends Issue {
|
|
|
74
104
|
}
|
|
75
105
|
}
|
|
76
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Issue for values that have an unexpected type.
|
|
109
|
+
*
|
|
110
|
+
* This is one of the most common validation issues, occurring when the
|
|
111
|
+
* runtime type of a value doesn't match the expected schema type.
|
|
112
|
+
*/
|
|
77
113
|
export class IssueInvalidType extends Issue {
|
|
78
114
|
constructor(
|
|
79
115
|
path: readonly PropertyKey[],
|
|
@@ -95,6 +131,12 @@ export class IssueInvalidType extends Issue {
|
|
|
95
131
|
}
|
|
96
132
|
}
|
|
97
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Issue for values that don't match any of the expected literal values.
|
|
136
|
+
*
|
|
137
|
+
* Used when a value must be one of a specific set of allowed values
|
|
138
|
+
* (e.g., enum-like constraints).
|
|
139
|
+
*/
|
|
98
140
|
export class IssueInvalidValue extends Issue {
|
|
99
141
|
constructor(
|
|
100
142
|
path: readonly PropertyKey[],
|
|
@@ -116,6 +158,9 @@ export class IssueInvalidValue extends Issue {
|
|
|
116
158
|
}
|
|
117
159
|
}
|
|
118
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Issue for missing required object properties.
|
|
163
|
+
*/
|
|
119
164
|
export class IssueRequiredKey extends Issue {
|
|
120
165
|
constructor(
|
|
121
166
|
path: readonly PropertyKey[],
|
|
@@ -137,6 +182,16 @@ export class IssueRequiredKey extends Issue {
|
|
|
137
182
|
}
|
|
138
183
|
}
|
|
139
184
|
|
|
185
|
+
/**
|
|
186
|
+
* The type of measurement for size constraint issues.
|
|
187
|
+
*
|
|
188
|
+
* - `'array'` - Array length
|
|
189
|
+
* - `'string'` - String length in characters
|
|
190
|
+
* - `'integer'` - Numeric value
|
|
191
|
+
* - `'grapheme'` - String length in grapheme clusters
|
|
192
|
+
* - `'bytes'` - Byte length
|
|
193
|
+
* - `'blob'` - Blob size
|
|
194
|
+
*/
|
|
140
195
|
export type MeasurableType =
|
|
141
196
|
| 'array'
|
|
142
197
|
| 'string'
|
|
@@ -145,6 +200,9 @@ export type MeasurableType =
|
|
|
145
200
|
| 'bytes'
|
|
146
201
|
| 'blob'
|
|
147
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Issue for values that exceed a maximum constraint.
|
|
205
|
+
*/
|
|
148
206
|
export class IssueTooBig extends Issue {
|
|
149
207
|
constructor(
|
|
150
208
|
path: readonly PropertyKey[],
|
|
@@ -169,6 +227,9 @@ export class IssueTooBig extends Issue {
|
|
|
169
227
|
}
|
|
170
228
|
}
|
|
171
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Issue for values that are below a minimum constraint.
|
|
232
|
+
*/
|
|
172
233
|
export class IssueTooSmall extends Issue {
|
|
173
234
|
constructor(
|
|
174
235
|
path: readonly PropertyKey[],
|
|
@@ -193,6 +254,10 @@ export class IssueTooSmall extends Issue {
|
|
|
193
254
|
}
|
|
194
255
|
}
|
|
195
256
|
|
|
257
|
+
// -----------------------------------------------------------------------------
|
|
258
|
+
// Helper functions for formatting error messages
|
|
259
|
+
// -----------------------------------------------------------------------------
|
|
260
|
+
|
|
196
261
|
function stringifyExpectedType(expected: string): string {
|
|
197
262
|
if (expected === '$typed') {
|
|
198
263
|
return 'an object or record which includes a "$type" property'
|