@atproto/lex-schema 0.0.1 → 0.0.3

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 (289) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/core/$type.d.ts +6 -3
  3. package/dist/core/$type.d.ts.map +1 -1
  4. package/dist/core/$type.js +1 -0
  5. package/dist/core/$type.js.map +1 -1
  6. package/dist/core/record-key.d.ts +3 -3
  7. package/dist/core/record-key.d.ts.map +1 -1
  8. package/dist/core/record-key.js +12 -6
  9. package/dist/core/record-key.js.map +1 -1
  10. package/dist/core/result.d.ts.map +1 -1
  11. package/dist/core/result.js +6 -0
  12. package/dist/core/result.js.map +1 -1
  13. package/dist/core/string-format.d.ts +30 -27
  14. package/dist/core/string-format.d.ts.map +1 -1
  15. package/dist/core/string-format.js +56 -42
  16. package/dist/core/string-format.js.map +1 -1
  17. package/dist/core/types.d.ts +9 -1
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js.map +1 -1
  20. package/dist/external.d.ts +31 -28
  21. package/dist/external.d.ts.map +1 -1
  22. package/dist/external.js +33 -17
  23. package/dist/external.js.map +1 -1
  24. package/dist/schema/_parameters.d.ts +2 -2
  25. package/dist/schema/_parameters.d.ts.map +1 -1
  26. package/dist/schema/array.d.ts +5 -6
  27. package/dist/schema/array.d.ts.map +1 -1
  28. package/dist/schema/array.js +5 -6
  29. package/dist/schema/array.js.map +1 -1
  30. package/dist/schema/blob.d.ts +2 -3
  31. package/dist/schema/blob.d.ts.map +1 -1
  32. package/dist/schema/blob.js +1 -2
  33. package/dist/schema/blob.js.map +1 -1
  34. package/dist/schema/boolean.d.ts +4 -5
  35. package/dist/schema/boolean.d.ts.map +1 -1
  36. package/dist/schema/boolean.js +2 -3
  37. package/dist/schema/boolean.js.map +1 -1
  38. package/dist/schema/bytes.d.ts +3 -4
  39. package/dist/schema/bytes.d.ts.map +1 -1
  40. package/dist/schema/bytes.js +2 -3
  41. package/dist/schema/bytes.js.map +1 -1
  42. package/dist/schema/cid.d.ts +13 -6
  43. package/dist/schema/cid.d.ts.map +1 -1
  44. package/dist/schema/cid.js +2 -4
  45. package/dist/schema/cid.js.map +1 -1
  46. package/dist/schema/custom.d.ts +3 -4
  47. package/dist/schema/custom.d.ts.map +1 -1
  48. package/dist/schema/custom.js +4 -3
  49. package/dist/schema/custom.js.map +1 -1
  50. package/dist/schema/dict.d.ts +3 -3
  51. package/dist/schema/dict.d.ts.map +1 -1
  52. package/dist/schema/dict.js +1 -1
  53. package/dist/schema/dict.js.map +1 -1
  54. package/dist/schema/discriminated-union.d.ts +15 -24
  55. package/dist/schema/discriminated-union.d.ts.map +1 -1
  56. package/dist/schema/discriminated-union.js +40 -64
  57. package/dist/schema/discriminated-union.js.map +1 -1
  58. package/dist/schema/enum.d.ts +8 -4
  59. package/dist/schema/enum.d.ts.map +1 -1
  60. package/dist/schema/enum.js +5 -3
  61. package/dist/schema/enum.js.map +1 -1
  62. package/dist/schema/integer.d.ts +3 -4
  63. package/dist/schema/integer.d.ts.map +1 -1
  64. package/dist/schema/integer.js +3 -4
  65. package/dist/schema/integer.js.map +1 -1
  66. package/dist/schema/intersection.d.ts +22 -14
  67. package/dist/schema/intersection.d.ts.map +1 -1
  68. package/dist/schema/intersection.js +12 -22
  69. package/dist/schema/intersection.js.map +1 -1
  70. package/dist/schema/literal.d.ts +8 -4
  71. package/dist/schema/literal.d.ts.map +1 -1
  72. package/dist/schema/literal.js +5 -3
  73. package/dist/schema/literal.js.map +1 -1
  74. package/dist/schema/never.d.ts +2 -2
  75. package/dist/schema/never.d.ts.map +1 -1
  76. package/dist/schema/never.js +1 -1
  77. package/dist/schema/never.js.map +1 -1
  78. package/dist/schema/null.d.ts +2 -3
  79. package/dist/schema/null.d.ts.map +1 -1
  80. package/dist/schema/null.js +1 -2
  81. package/dist/schema/null.js.map +1 -1
  82. package/dist/schema/nullable.d.ts +7 -0
  83. package/dist/schema/nullable.d.ts.map +1 -0
  84. package/dist/schema/nullable.js +19 -0
  85. package/dist/schema/nullable.js.map +1 -0
  86. package/dist/schema/object.d.ts +10 -44
  87. package/dist/schema/object.d.ts.map +1 -1
  88. package/dist/schema/object.js +13 -56
  89. package/dist/schema/object.js.map +1 -1
  90. package/dist/schema/optional.d.ts +7 -0
  91. package/dist/schema/optional.d.ts.map +1 -0
  92. package/dist/schema/optional.js +25 -0
  93. package/dist/schema/optional.js.map +1 -0
  94. package/dist/schema/params.d.ts +14 -19
  95. package/dist/schema/params.d.ts.map +1 -1
  96. package/dist/schema/params.js +10 -24
  97. package/dist/schema/params.js.map +1 -1
  98. package/dist/schema/payload.d.ts +4 -4
  99. package/dist/schema/payload.d.ts.map +1 -1
  100. package/dist/schema/payload.js.map +1 -1
  101. package/dist/schema/permission-set.d.ts +6 -6
  102. package/dist/schema/permission-set.d.ts.map +1 -1
  103. package/dist/schema/permission-set.js +1 -2
  104. package/dist/schema/permission-set.js.map +1 -1
  105. package/dist/schema/permission.d.ts +0 -1
  106. package/dist/schema/permission.d.ts.map +1 -1
  107. package/dist/schema/permission.js +0 -1
  108. package/dist/schema/permission.js.map +1 -1
  109. package/dist/schema/procedure.d.ts +8 -9
  110. package/dist/schema/procedure.d.ts.map +1 -1
  111. package/dist/schema/procedure.js +0 -1
  112. package/dist/schema/procedure.js.map +1 -1
  113. package/dist/schema/query.d.ts +7 -8
  114. package/dist/schema/query.d.ts.map +1 -1
  115. package/dist/schema/query.js +0 -1
  116. package/dist/schema/query.js.map +1 -1
  117. package/dist/schema/record.d.ts +34 -28
  118. package/dist/schema/record.d.ts.map +1 -1
  119. package/dist/schema/record.js +1 -2
  120. package/dist/schema/record.js.map +1 -1
  121. package/dist/schema/ref.d.ts +2 -3
  122. package/dist/schema/ref.d.ts.map +1 -1
  123. package/dist/schema/ref.js +1 -2
  124. package/dist/schema/ref.js.map +1 -1
  125. package/dist/schema/refine.d.ts +18 -0
  126. package/dist/schema/refine.d.ts.map +1 -0
  127. package/dist/schema/refine.js +33 -0
  128. package/dist/schema/refine.js.map +1 -0
  129. package/dist/schema/regexp.d.ts +7 -0
  130. package/dist/schema/regexp.d.ts.map +1 -0
  131. package/dist/schema/regexp.js +22 -0
  132. package/dist/schema/regexp.js.map +1 -0
  133. package/dist/schema/string.d.ts +4 -8
  134. package/dist/schema/string.d.ts.map +1 -1
  135. package/dist/schema/string.js +6 -3
  136. package/dist/schema/string.js.map +1 -1
  137. package/dist/schema/subscription.d.ts +7 -6
  138. package/dist/schema/subscription.d.ts.map +1 -1
  139. package/dist/schema/subscription.js.map +1 -1
  140. package/dist/schema/token.d.ts +2 -3
  141. package/dist/schema/token.d.ts.map +1 -1
  142. package/dist/schema/token.js +1 -2
  143. package/dist/schema/token.js.map +1 -1
  144. package/dist/schema/typed-object.d.ts +29 -27
  145. package/dist/schema/typed-object.d.ts.map +1 -1
  146. package/dist/schema/typed-object.js +1 -2
  147. package/dist/schema/typed-object.js.map +1 -1
  148. package/dist/schema/typed-ref.d.ts +2 -2
  149. package/dist/schema/typed-ref.d.ts.map +1 -1
  150. package/dist/schema/typed-ref.js +1 -1
  151. package/dist/schema/typed-ref.js.map +1 -1
  152. package/dist/schema/typed-union.d.ts +3 -4
  153. package/dist/schema/typed-union.d.ts.map +1 -1
  154. package/dist/schema/typed-union.js +3 -10
  155. package/dist/schema/typed-union.js.map +1 -1
  156. package/dist/schema/union.d.ts +2 -2
  157. package/dist/schema/union.d.ts.map +1 -1
  158. package/dist/schema/union.js +1 -1
  159. package/dist/schema/union.js.map +1 -1
  160. package/dist/schema/unknown-object.d.ts +2 -3
  161. package/dist/schema/unknown-object.d.ts.map +1 -1
  162. package/dist/schema/unknown-object.js +1 -2
  163. package/dist/schema/unknown-object.js.map +1 -1
  164. package/dist/schema/unknown.d.ts +2 -2
  165. package/dist/schema/unknown.d.ts.map +1 -1
  166. package/dist/schema/unknown.js +1 -1
  167. package/dist/schema/unknown.js.map +1 -1
  168. package/dist/schema.d.ts +4 -0
  169. package/dist/schema.d.ts.map +1 -1
  170. package/dist/schema.js +6 -1
  171. package/dist/schema.js.map +1 -1
  172. package/dist/util/array-agg.d.ts.map +1 -1
  173. package/dist/util/array-agg.js +1 -0
  174. package/dist/util/array-agg.js.map +1 -1
  175. package/dist/util/lazy-property.d.ts +2 -0
  176. package/dist/util/lazy-property.d.ts.map +1 -0
  177. package/dist/util/lazy-property.js +14 -0
  178. package/dist/util/lazy-property.js.map +1 -0
  179. package/dist/validation/schema.d.ts +24 -0
  180. package/dist/validation/schema.d.ts.map +1 -0
  181. package/dist/validation/schema.js +57 -0
  182. package/dist/validation/schema.js.map +1 -0
  183. package/dist/validation/validation-error.d.ts +3 -3
  184. package/dist/validation/validation-error.d.ts.map +1 -1
  185. package/dist/validation/validation-error.js +32 -4
  186. package/dist/validation/validation-error.js.map +1 -1
  187. package/dist/validation/validation-issue.d.ts +32 -24
  188. package/dist/validation/validation-issue.d.ts.map +1 -1
  189. package/dist/validation/validation-issue.js +136 -92
  190. package/dist/validation/validation-issue.js.map +1 -1
  191. package/dist/validation/validator.d.ts +20 -50
  192. package/dist/validation/validator.d.ts.map +1 -1
  193. package/dist/validation/validator.js +40 -134
  194. package/dist/validation/validator.js.map +1 -1
  195. package/dist/validation.d.ts +1 -0
  196. package/dist/validation.d.ts.map +1 -1
  197. package/dist/validation.js +1 -0
  198. package/dist/validation.js.map +1 -1
  199. package/package.json +8 -4
  200. package/src/core/$type.ts +7 -4
  201. package/src/core/record-key.ts +12 -5
  202. package/src/core/result.ts +6 -0
  203. package/src/core/string-format.ts +97 -61
  204. package/src/core/types.ts +12 -6
  205. package/src/external.ts +92 -70
  206. package/src/schema/_parameters.test.ts +416 -0
  207. package/src/schema/array.test.ts +237 -0
  208. package/src/schema/array.ts +17 -11
  209. package/src/schema/blob.test.ts +506 -0
  210. package/src/schema/blob.ts +3 -5
  211. package/src/schema/boolean.test.ts +116 -0
  212. package/src/schema/boolean.ts +5 -7
  213. package/src/schema/bytes.test.ts +226 -0
  214. package/src/schema/bytes.ts +4 -6
  215. package/src/schema/cid.test.ts +155 -0
  216. package/src/schema/cid.ts +14 -8
  217. package/src/schema/custom.test.ts +413 -0
  218. package/src/schema/custom.ts +10 -8
  219. package/src/schema/dict.test.ts +198 -0
  220. package/src/schema/dict.ts +6 -8
  221. package/src/schema/discriminated-union.test.ts +675 -0
  222. package/src/schema/discriminated-union.ts +68 -95
  223. package/src/schema/enum.test.ts +396 -0
  224. package/src/schema/enum.ts +12 -5
  225. package/src/schema/integer.test.ts +312 -0
  226. package/src/schema/integer.ts +5 -7
  227. package/src/schema/intersection.test.ts +32 -0
  228. package/src/schema/intersection.ts +37 -40
  229. package/src/schema/literal.test.ts +531 -0
  230. package/src/schema/literal.ts +12 -5
  231. package/src/schema/never.test.ts +174 -0
  232. package/src/schema/never.ts +3 -10
  233. package/src/schema/null.test.ts +79 -0
  234. package/src/schema/null.ts +3 -5
  235. package/src/schema/nullable.test.ts +480 -0
  236. package/src/schema/nullable.ts +23 -0
  237. package/src/schema/object.test.ts +47 -115
  238. package/src/schema/object.ts +23 -134
  239. package/src/schema/optional.test.ts +485 -0
  240. package/src/schema/optional.ts +31 -0
  241. package/src/schema/params.test.ts +582 -0
  242. package/src/schema/params.ts +37 -55
  243. package/src/schema/payload.test.ts +345 -0
  244. package/src/schema/payload.ts +5 -5
  245. package/src/schema/permission-set.test.ts +679 -0
  246. package/src/schema/permission-set.ts +6 -8
  247. package/src/schema/permission.test.ts +536 -0
  248. package/src/schema/permission.ts +0 -2
  249. package/src/schema/procedure.test.ts +443 -0
  250. package/src/schema/procedure.ts +11 -13
  251. package/src/schema/query.test.ts +408 -0
  252. package/src/schema/query.ts +9 -11
  253. package/src/schema/record.test.ts +694 -0
  254. package/src/schema/record.ts +38 -36
  255. package/src/schema/ref.test.ts +365 -0
  256. package/src/schema/ref.ts +8 -5
  257. package/src/schema/refine.test.ts +578 -0
  258. package/src/schema/refine.ts +85 -0
  259. package/src/schema/regexp.test.ts +580 -0
  260. package/src/schema/regexp.ts +22 -0
  261. package/src/schema/string.test.ts +612 -0
  262. package/src/schema/string.ts +11 -17
  263. package/src/schema/subscription.test.ts +689 -0
  264. package/src/schema/subscription.ts +13 -8
  265. package/src/schema/token.test.ts +428 -0
  266. package/src/schema/token.ts +3 -5
  267. package/src/schema/typed-object.test.ts +612 -0
  268. package/src/schema/typed-object.ts +23 -20
  269. package/src/schema/typed-ref.test.ts +823 -0
  270. package/src/schema/typed-ref.ts +10 -5
  271. package/src/schema/typed-union.test.ts +378 -0
  272. package/src/schema/typed-union.ts +6 -15
  273. package/src/schema/union.test.ts +200 -0
  274. package/src/schema/union.ts +5 -4
  275. package/src/schema/unknown-object.test.ts +592 -0
  276. package/src/schema/unknown-object.ts +3 -5
  277. package/src/schema/unknown.test.ts +312 -0
  278. package/src/schema/unknown.ts +3 -3
  279. package/src/schema.ts +7 -1
  280. package/src/util/array-agg.ts +1 -0
  281. package/src/util/lazy-property.ts +14 -0
  282. package/src/validation/schema.ts +92 -0
  283. package/src/validation/validation-error.ts +60 -9
  284. package/src/validation/validation-issue.ts +141 -144
  285. package/src/validation/validator.ts +67 -206
  286. package/src/validation.ts +1 -0
  287. package/tsconfig.build.json +12 -0
  288. package/tsconfig.json +7 -0
  289. package/tsconfig.tests.json +9 -0
@@ -1,72 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ValidatorContext = exports.Validator = void 0;
3
+ exports.ValidatorContext = void 0;
4
4
  const core_js_1 = require("../core.js");
5
5
  const validation_error_js_1 = require("./validation-error.js");
6
- class Validator {
7
- /**
8
- * This property is used for type inference purposes and does not actually
9
- * exist at runtime.
10
- *
11
- * @deprecated For internal use only (not actually deprecated)
12
- */
13
- _lex;
14
- lexiconType;
15
- assert(input) {
16
- const result = this.validate(input, { allowTransform: false });
17
- if (!result.success)
18
- throw result.error;
19
- }
20
- check(input) {
21
- const result = this.validate(input, { allowTransform: false });
22
- return result.success;
23
- }
24
- maybe(input) {
25
- return this.check(input) ? input : undefined;
26
- }
27
- parse(input, options) {
28
- const result = ValidatorContext.validate(input, this, options);
29
- if (!result.success)
30
- throw result.error;
31
- return result.value;
32
- }
33
- validate(input, options) {
34
- return ValidatorContext.validate(input, this, options);
35
- }
36
- // @NOTE The built lexicons namespaces will export utility functions that
37
- // allow accessing the schema's methods without the need to specify ".main."
38
- // as part of the namespace. This way, a utility for a particular record type
39
- // can be called like "app.bsky.feed.post.<utility>()" instead of
40
- // "app.bsky.feed.post.main.<utility>()". Because those utilities could
41
- // conflict with other schemas (e.g. if there is a lexicon definition at
42
- // "#<utility>"), those exported utilities will be prefixed with "$". In order
43
- // to be able to consistently call the utilities, when using the "main" and
44
- // non "main" definitions, we also expose the same methods with a "$" prefix.
45
- // Thanks to this, both of the following call will be possible:
46
- //
47
- // - "app.bsky.feed.post.$parse(...)" // calls a utility function created by "lex build"
48
- // - "app.bsky.feed.defs.postView.$parse(...)" // uses the alias defined below on the schema instance
49
- $assert(input) {
50
- return this.assert(input);
51
- }
52
- $check(input) {
53
- return this.check(input);
54
- }
55
- $maybe(input) {
56
- return this.maybe(input);
57
- }
58
- $parse(input, options) {
59
- return this.parse(input, options);
60
- }
61
- $validate(input, options) {
62
- return this.validate(input, options);
63
- }
64
- }
65
- exports.Validator = Validator;
66
- const asIssue = ({ path, ...issue }, currentPath) => ({
67
- ...issue,
68
- path: path != null ? currentPath.concat(path) : [...currentPath],
69
- });
6
+ const validation_issue_js_1 = require("./validation-issue.js");
70
7
  class ValidatorContext {
71
8
  options;
72
9
  /**
@@ -82,14 +19,15 @@ class ValidatorContext {
82
19
  constructor(options) {
83
20
  this.options = options;
84
21
  // Create a copy because we will be mutating the array during validation.
85
- this.currentPath = options?.path != null ? [...options.path] : [];
22
+ this.currentPath = options?.path != null ? Array.from(options.path) : [];
86
23
  }
87
24
  get path() {
88
- return [...this.currentPath];
25
+ return Array.from(this.currentPath);
89
26
  }
90
- get allowTransform() {
91
- // Default to true
92
- return this.options?.allowTransform !== false;
27
+ concatPath(path) {
28
+ if (path == null)
29
+ return this.path;
30
+ return this.currentPath.concat(path);
93
31
  }
94
32
  /**
95
33
  * This is basically the entry point for validation within a context. Use this
@@ -97,9 +35,13 @@ class ValidatorContext {
97
35
  * this method enforces the {@link ValidationOptions.allowTransform} option.
98
36
  */
99
37
  validate(input, validator) {
38
+ // This is the only place where validateInContext should be called.
100
39
  const result = validator.validateInContext(input, this);
101
40
  if (result.success) {
102
- if (!this.allowTransform && !Object.is(result.value, input)) {
41
+ if (
42
+ // Defaults to true
43
+ this.options?.allowTransform === false &&
44
+ !Object.is(result.value, input)) {
103
45
  // If the value changed, it means that a default (or some other
104
46
  // transformation) was applied, meaning that the original value did
105
47
  // *not* match the (output) schema. When "allowTransform" is false, we
@@ -116,7 +58,7 @@ class ValidatorContext {
116
58
  if (this.issues.length > 0) {
117
59
  // Validator returned a success but issues were added via the context.
118
60
  // This means the overall validation failed.
119
- return { success: false, error: new validation_error_js_1.ValidationError(this.issues) };
61
+ return (0, core_js_1.failure)(new validation_error_js_1.ValidationError(Array.from(this.issues)));
120
62
  }
121
63
  }
122
64
  return result;
@@ -132,77 +74,41 @@ class ValidatorContext {
132
74
  }
133
75
  }
134
76
  addIssue(issue) {
135
- this.issues.push(asIssue(issue, this.currentPath));
77
+ this.issues.push(issue);
136
78
  }
137
79
  success(value) {
138
80
  return (0, core_js_1.success)(value);
139
81
  }
140
82
  failure(issue) {
141
- return (0, core_js_1.failure)(new validation_error_js_1.ValidationError([...this.issues, asIssue(issue, this.currentPath)]));
142
- }
143
- issueInvalidValue(input, values, path) {
144
- return this.failure({
145
- code: 'invalid_value',
146
- input,
147
- values,
148
- path,
149
- });
150
- }
151
- issueInvalidType(input, expected, path) {
152
- return this.failure({
153
- code: 'invalid_type',
154
- input,
155
- expected: Array.isArray(expected) ? expected : [expected],
156
- path,
157
- });
83
+ return (0, core_js_1.failure)(new validation_error_js_1.ValidationError([...this.issues, issue]));
158
84
  }
159
- issueInvalidPropertyValue(input, property, values) {
160
- return this.issueInvalidValue(input[property], values, property);
85
+ issueInvalidValue(input, values) {
86
+ return this.failure(new validation_issue_js_1.IssueInvalidValue(this.path, input, values));
161
87
  }
162
- issueInvalidPropertyType(input, property, expected) {
163
- return this.issueInvalidType(input[property], expected, property);
88
+ issueInvalidType(input, expected) {
89
+ return this.failure(new validation_issue_js_1.IssueInvalidType(this.path, input, [expected]));
164
90
  }
165
91
  issueRequiredKey(input, key) {
166
- return this.failure({
167
- code: 'required_key',
168
- key,
169
- input,
170
- path: key,
171
- });
172
- }
173
- issueInvalidFormat(input, format, message) {
174
- return this.failure({
175
- code: 'invalid_format',
176
- message,
177
- format,
178
- input,
179
- });
180
- }
181
- issueTooBig(input, type, maximum, actual) {
182
- return this.failure({
183
- code: 'too_big',
184
- type,
185
- maximum,
186
- actual,
187
- input,
188
- });
189
- }
190
- issueTooSmall(input, type, minimum, actual) {
191
- return this.failure({
192
- code: 'too_small',
193
- type,
194
- minimum,
195
- actual,
196
- input,
197
- });
198
- }
199
- custom(input, message, path) {
200
- return this.failure({
201
- code: 'custom',
202
- input,
203
- message,
204
- path,
205
- });
92
+ return this.failure(new validation_issue_js_1.IssueRequiredKey(this.path, input, key));
93
+ }
94
+ issueInvalidFormat(input, format, msg) {
95
+ return this.failure(new validation_issue_js_1.IssueInvalidFormat(this.path, input, format, msg));
96
+ }
97
+ issueTooBig(input, type, max, actual) {
98
+ return this.failure(new validation_issue_js_1.IssueTooBig(this.path, input, max, type, actual));
99
+ }
100
+ issueTooSmall(input, type, min, actual) {
101
+ return this.failure(new validation_issue_js_1.IssueTooSmall(this.path, input, min, type, actual));
102
+ }
103
+ issueInvalidPropertyValue(input, property, values) {
104
+ const value = input[property];
105
+ const path = this.concatPath(property);
106
+ return this.failure(new validation_issue_js_1.IssueInvalidValue(path, value, values));
107
+ }
108
+ issueInvalidPropertyType(input, property, expected) {
109
+ const value = input[property];
110
+ const path = this.concatPath(property);
111
+ return this.failure(new validation_issue_js_1.IssueInvalidType(path, value, [expected]));
206
112
  }
207
113
  }
208
114
  exports.ValidatorContext = ValidatorContext;
@@ -1 +1 @@
1
- {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/validation/validator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ResultFailure, ResultSuccess, failure, success } from '../core.js'\nimport { PropertyKey } from './property-key.js'\nimport { ValidationError } from './validation-error.js'\nimport {\n IssueTooBig,\n IssueTooSmall,\n ValidationIssue,\n} from './validation-issue.js'\n\nexport type ValidationSuccess<Value = any> = ResultSuccess<Value>\nexport type ValidationFailure = ResultFailure<ValidationError>\nexport type ValidationResult<Value = any> =\n | ValidationSuccess<Value>\n | ValidationFailure\n\ntype ValidationOptions = {\n path?: PropertyKey[]\n\n /** @default true */\n allowTransform?: boolean\n}\n\nexport type Infer<T extends Validator> = T['_lex']['output']\n\nexport abstract class Validator<Output = any> {\n /**\n * This property is used for type inference purposes and does not actually\n * exist at runtime.\n *\n * @deprecated For internal use only (not actually deprecated)\n */\n _lex!: { output: Output }\n\n readonly lexiconType?: string\n\n /**\n * @internal **INTERNAL API, DO NOT USE**.\n *\n * Use {@link Validator.assert assert}, {@link Validator.check check},\n * {@link Validator.parse parse} or {@link Validator.validate validate}\n * instead.\n *\n * This method is implemented by subclasses to perform transformation and\n * validation of the input value. Do not call this method directly; as the\n * {@link ValidatorContext.options.allowTransform} option will **not** be\n * enforced. See {@link ValidatorContext.validate} for details. When\n * delegating validation from one validator sub-class implementation to\n * another schema, {@link ValidatorContext.validate} should be used instead\n * of calling {@link Validator.validateInContext}. This will allow to stop the\n * validation process if the value was transformed (by the other schema) but\n * transformations are not allowed.\n *\n * By convention, the {@link ValidationResult} must return the original input\n * value if validation was successful and no transformation was applied (i.e.\n * the input already conformed to the schema). If a default value, or any\n * other transformation was applied, the returned value c&an be different from\n * the input.\n *\n * This convention allows the {@link Validator.check check} and\n * {@link Validator.assert assert} methods to check whether the input value\n * exactly matches the schema (without defaults or transformations), by\n * checking if the returned value is strictly equal to the input.\n *\n * @see {@link ValidatorContext.validate}\n */\n abstract validateInContext(\n input: unknown,\n ctx: ValidatorContext,\n ): ValidationResult<Output>\n\n assert(input: unknown): asserts input is Output {\n const result = this.validate(input, { allowTransform: false })\n if (!result.success) throw result.error\n }\n\n check(input: unknown): input is Output {\n const result = this.validate(input, { allowTransform: false })\n return result.success\n }\n\n maybe<I>(input: I): (I & Output) | undefined {\n return this.check(input) ? input : undefined\n }\n\n parse<I>(\n input: I,\n options: ValidationOptions & { allowTransform: false },\n ): I & Output\n parse(input: unknown, options?: ValidationOptions): Output\n parse(input: unknown, options?: ValidationOptions): Output {\n const result = ValidatorContext.validate(input, this, options)\n if (!result.success) throw result.error\n return result.value\n }\n\n validate<I>(\n input: I,\n options: ValidationOptions & { allowTransform: false },\n ): ValidationResult<I & Output>\n validate(\n input: unknown,\n options?: ValidationOptions,\n ): ValidationResult<Output>\n validate(\n input: unknown,\n options?: ValidationOptions,\n ): ValidationResult<Output> {\n return ValidatorContext.validate(input, this, options)\n }\n\n // @NOTE The built lexicons namespaces will export utility functions that\n // allow accessing the schema's methods without the need to specify \".main.\"\n // as part of the namespace. This way, a utility for a particular record type\n // can be called like \"app.bsky.feed.post.<utility>()\" instead of\n // \"app.bsky.feed.post.main.<utility>()\". Because those utilities could\n // conflict with other schemas (e.g. if there is a lexicon definition at\n // \"#<utility>\"), those exported utilities will be prefixed with \"$\". In order\n // to be able to consistently call the utilities, when using the \"main\" and\n // non \"main\" definitions, we also expose the same methods with a \"$\" prefix.\n // Thanks to this, both of the following call will be possible:\n //\n // - \"app.bsky.feed.post.$parse(...)\" // calls a utility function created by \"lex build\"\n // - \"app.bsky.feed.defs.postView.$parse(...)\" // uses the alias defined below on the schema instance\n\n $assert(input: unknown): asserts input is Output {\n return this.assert(input)\n }\n\n $check(input: unknown): input is Output {\n return this.check(input)\n }\n\n $maybe<I>(input: I): (I & Output) | undefined {\n return this.maybe(input)\n }\n\n $parse(input: unknown, options?: ValidationOptions): Output {\n return this.parse(input, options)\n }\n\n $validate(\n input: unknown,\n options?: ValidationOptions,\n ): ValidationResult<Output> {\n return this.validate(input, options)\n }\n}\n\nexport type ContextualIssue = {\n [Code in ValidationIssue['code']]: Omit<\n Extract<ValidationIssue, { code: Code }>,\n 'path'\n > & { path?: PropertyKey | readonly PropertyKey[] }\n}[ValidationIssue['code']]\n\nconst asIssue = (\n { path, ...issue }: ContextualIssue,\n currentPath: readonly PropertyKey[],\n): ValidationIssue & { path: PropertyKey[] } => ({\n ...issue,\n path: path != null ? currentPath.concat(path) : [...currentPath],\n})\n\nexport class ValidatorContext {\n /**\n * Creates a new validation context and validates the input using the\n * provided validator.\n */\n static validate<V>(\n input: unknown,\n validator: Validator<V>,\n options: ValidationOptions = {},\n ): ValidationResult<V> {\n const context = new ValidatorContext(options)\n return context.validate(input, validator)\n }\n\n private readonly currentPath: PropertyKey[]\n private readonly issues: ValidationIssue[] = []\n\n protected constructor(readonly options: ValidationOptions) {\n // Create a copy because we will be mutating the array during validation.\n this.currentPath = options?.path != null ? [...options.path] : []\n }\n\n get path() {\n return [...this.currentPath]\n }\n\n get allowTransform() {\n // Default to true\n return this.options?.allowTransform !== false\n }\n\n /**\n * This is basically the entry point for validation within a context. Use this\n * method instead of {@link Validator.validateInContext} directly, because\n * this method enforces the {@link ValidationOptions.allowTransform} option.\n */\n validate<V>(input: unknown, validator: Validator<V>): ValidationResult<V> {\n const result = validator.validateInContext(input, this)\n\n if (result.success) {\n if (!this.allowTransform && !Object.is(result.value, input)) {\n // If the value changed, it means that a default (or some other\n // transformation) was applied, meaning that the original value did\n // *not* match the (output) schema. When \"allowTransform\" is false, we\n // consider this a failure.\n\n // This check is the reason why Validator.validateInContext should not\n // be used directly, and ValidatorContext.validate should be used\n // instead, even when delegating validation from one validator to\n // another.\n\n // This if block comes before the next one because 'this.issues' will\n // end-up being appended to the returned ValidationError (see the\n // \"failure\" method below), resulting in a more complete error report.\n return this.issueInvalidValue(input, [result.value])\n }\n\n if (this.issues.length > 0) {\n // Validator returned a success but issues were added via the context.\n // This means the overall validation failed.\n return { success: false, error: new ValidationError(this.issues) }\n }\n }\n\n return result\n }\n\n validateChild<I extends object, K extends PropertyKey & keyof I, V>(\n input: I,\n key: K,\n validator: Validator<V>,\n ): ValidationResult<V> {\n // Instead of creating a new context, we just push/pop the path segment.\n this.currentPath.push(key)\n try {\n return this.validate(input[key], validator)\n } finally {\n this.currentPath.length--\n }\n }\n\n addIssue(issue: ContextualIssue): void {\n this.issues.push(asIssue(issue, this.currentPath))\n }\n\n success<V>(value: V): ValidationResult<V> {\n return success(value)\n }\n\n failure(issue: ContextualIssue): ValidationFailure {\n return failure(\n new ValidationError([...this.issues, asIssue(issue, this.currentPath)]),\n )\n }\n\n issueInvalidValue(\n input: unknown,\n values: readonly unknown[],\n path?: PropertyKey | readonly PropertyKey[],\n ) {\n return this.failure({\n code: 'invalid_value',\n input,\n values,\n path,\n })\n }\n\n issueInvalidType(\n input: unknown,\n expected: string | readonly string[],\n path?: PropertyKey | readonly PropertyKey[],\n ) {\n return this.failure({\n code: 'invalid_type',\n input,\n expected: Array.isArray(expected) ? expected : [expected],\n path,\n })\n }\n\n issueInvalidPropertyValue<I>(\n input: I,\n property: keyof I & PropertyKey,\n values: readonly unknown[],\n ) {\n return this.issueInvalidValue(input[property], values, property)\n }\n\n issueInvalidPropertyType<I>(\n input: I,\n property: keyof I & PropertyKey,\n expected: string | readonly string[],\n ) {\n return this.issueInvalidType(input[property], expected, property)\n }\n\n issueRequiredKey(input: object, key: PropertyKey) {\n return this.failure({\n code: 'required_key',\n key,\n input,\n path: key,\n })\n }\n\n issueInvalidFormat(input: unknown, format: string, message?: string) {\n return this.failure({\n code: 'invalid_format',\n message,\n format,\n input,\n })\n }\n\n issueTooBig(\n input: unknown,\n type: IssueTooBig['type'],\n maximum: number,\n actual: number,\n ) {\n return this.failure({\n code: 'too_big',\n type,\n maximum,\n actual,\n input,\n })\n }\n\n issueTooSmall(\n input: unknown,\n type: IssueTooSmall['type'],\n minimum: number,\n actual: number,\n ) {\n return this.failure({\n code: 'too_small',\n type,\n minimum,\n actual,\n input,\n })\n }\n\n custom(\n input: unknown,\n message: string,\n path?: PropertyKey | readonly PropertyKey[],\n ) {\n return this.failure({\n code: 'custom',\n input,\n message,\n path,\n })\n }\n}\n"]}
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/validation/validator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ResultFailure, ResultSuccess, failure, success } from '../core.js'\nimport { PropertyKey } from './property-key.js'\nimport { ValidationError } from './validation-error.js'\nimport {\n Issue,\n IssueInvalidFormat,\n IssueInvalidType,\n IssueInvalidValue,\n IssueRequiredKey,\n IssueTooBig,\n IssueTooSmall,\n MeasurableType,\n} from './validation-issue.js'\n\nexport type ValidationSuccess<Value = any> = ResultSuccess<Value>\nexport type ValidationFailure = ResultFailure<ValidationError>\nexport type ValidationResult<Value = any> =\n | ValidationSuccess<Value>\n | ValidationFailure\n\nexport type ValidationOptions = {\n path?: PropertyKey[]\n\n /** @default true */\n allowTransform?: boolean\n}\n\nexport type Infer<T extends Validator> = T['_lex']['output']\n\nexport interface Validator<Output = any> {\n /**\n * This property is used for type inference purposes and does not actually\n * exist at runtime.\n *\n * @deprecated **INTERNAL API, DO NOT USE**.\n */\n readonly ['_lex']: { output: Output }\n\n /**\n * @internal **INTERNAL API**: use {@link ValidatorContext.validate} instead\n *\n * This method is implemented by subclasses to perform transformation and\n * validation of the input value. Do not call this method directly; as the\n * {@link ValidatorContext.options.allowTransform} option will **not** be\n * enforced. See {@link ValidatorContext.validate} for details. When\n * delegating validation from one validator sub-class implementation to\n * another schema, {@link ValidatorContext.validate} must be used instead of\n * calling {@link Validator.validateInContext}. This will allow to stop the\n * validation process if the value was transformed (by the other schema) but\n * transformations are not allowed.\n *\n * By convention, the {@link ValidationResult} must return the original input\n * value if validation was successful and no transformation was applied (i.e.\n * the input already conformed to the schema). If a default value, or any\n * other transformation was applied, the returned value c&an be different from\n * the input.\n *\n * This convention allows the {@link Validator.check check} and\n * {@link Validator.assert assert} methods to check whether the input value\n * exactly matches the schema (without defaults or transformations), by\n * checking if the returned value is strictly equal to the input.\n *\n * @see {@link ValidatorContext.validate}\n */\n validateInContext(\n input: unknown,\n ctx: ValidatorContext,\n ): ValidationResult<Output>\n}\n\nexport class ValidatorContext {\n /**\n * Creates a new validation context and validates the input using the\n * provided validator.\n */\n static validate<V>(\n input: unknown,\n validator: Validator<V>,\n options: ValidationOptions = {},\n ): ValidationResult<V> {\n const context = new ValidatorContext(options)\n return context.validate(input, validator)\n }\n\n private readonly currentPath: PropertyKey[]\n private readonly issues: Issue[] = []\n\n protected constructor(readonly options: ValidationOptions) {\n // Create a copy because we will be mutating the array during validation.\n this.currentPath = options?.path != null ? Array.from(options.path) : []\n }\n\n get path() {\n return Array.from(this.currentPath)\n }\n\n concatPath(path?: PropertyKey | readonly PropertyKey[]) {\n if (path == null) return this.path\n return this.currentPath.concat(path)\n }\n\n /**\n * This is basically the entry point for validation within a context. Use this\n * method instead of {@link Validator.validateInContext} directly, because\n * this method enforces the {@link ValidationOptions.allowTransform} option.\n */\n validate<V>(input: unknown, validator: Validator<V>): ValidationResult<V> {\n // This is the only place where validateInContext should be called.\n const result = validator.validateInContext(input, this)\n\n if (result.success) {\n if (\n // Defaults to true\n this.options?.allowTransform === false &&\n !Object.is(result.value, input)\n ) {\n // If the value changed, it means that a default (or some other\n // transformation) was applied, meaning that the original value did\n // *not* match the (output) schema. When \"allowTransform\" is false, we\n // consider this a failure.\n\n // This check is the reason why Validator.validateInContext should not\n // be used directly, and ValidatorContext.validate should be used\n // instead, even when delegating validation from one validator to\n // another.\n\n // This if block comes before the next one because 'this.issues' will\n // end-up being appended to the returned ValidationError (see the\n // \"failure\" method below), resulting in a more complete error report.\n return this.issueInvalidValue(input, [result.value])\n }\n\n if (this.issues.length > 0) {\n // Validator returned a success but issues were added via the context.\n // This means the overall validation failed.\n return failure(new ValidationError(Array.from(this.issues)))\n }\n }\n\n return result as ValidationResult<V>\n }\n\n validateChild<\n I extends object,\n K extends PropertyKey & keyof I,\n V extends Validator,\n >(input: I, key: K, validator: V): ValidationResult<Infer<V>> {\n // Instead of creating a new context, we just push/pop the path segment.\n this.currentPath.push(key)\n try {\n return this.validate(input[key], validator)\n } finally {\n this.currentPath.length--\n }\n }\n\n addIssue(issue: Issue): void {\n this.issues.push(issue)\n }\n\n success<V>(value: V): ValidationResult<V> {\n return success(value)\n }\n\n failure(issue: Issue): ValidationFailure {\n return failure(new ValidationError([...this.issues, issue]))\n }\n\n issueInvalidValue(input: unknown, values: readonly unknown[]) {\n return this.failure(new IssueInvalidValue(this.path, input, values))\n }\n\n issueInvalidType(input: unknown, expected: string) {\n return this.failure(new IssueInvalidType(this.path, input, [expected]))\n }\n\n issueRequiredKey(input: object, key: PropertyKey) {\n return this.failure(new IssueRequiredKey(this.path, input, key))\n }\n\n issueInvalidFormat(input: unknown, format: string, msg?: string) {\n return this.failure(new IssueInvalidFormat(this.path, input, format, msg))\n }\n\n issueTooBig(\n input: unknown,\n type: MeasurableType,\n max: number,\n actual: number,\n ) {\n return this.failure(new IssueTooBig(this.path, input, max, type, actual))\n }\n\n issueTooSmall(\n input: unknown,\n type: MeasurableType,\n min: number,\n actual: number,\n ) {\n return this.failure(new IssueTooSmall(this.path, input, min, type, actual))\n }\n\n issueInvalidPropertyValue<I>(\n input: I,\n property: keyof I & PropertyKey,\n values: readonly unknown[],\n ) {\n const value = input[property]\n const path = this.concatPath(property)\n return this.failure(new IssueInvalidValue(path, value, values))\n }\n\n issueInvalidPropertyType<I>(\n input: I,\n property: keyof I & PropertyKey,\n expected: string,\n ) {\n const value = input[property]\n const path = this.concatPath(property)\n return this.failure(new IssueInvalidType(path, value, [expected]))\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from './validation/property-key.js';
2
+ export * from './validation/schema.js';
2
3
  export * from './validation/validation-error.js';
3
4
  export * from './validation/validation-issue.js';
4
5
  export * from './validation/validator.js';
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA;AAC5C,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,2BAA2B,CAAA"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,2BAA2B,CAAA"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./validation/property-key.js"), exports);
5
+ tslib_1.__exportStar(require("./validation/schema.js"), exports);
5
6
  tslib_1.__exportStar(require("./validation/validation-error.js"), exports);
6
7
  tslib_1.__exportStar(require("./validation/validation-issue.js"), exports);
7
8
  tslib_1.__exportStar(require("./validation/validator.js"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":";;;AAAA,uEAA4C;AAC5C,2EAAgD;AAChD,2EAAgD;AAChD,oEAAyC","sourcesContent":["export * from './validation/property-key.js'\nexport * from './validation/validation-error.js'\nexport * from './validation/validation-issue.js'\nexport * from './validation/validator.js'\n"]}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":";;;AAAA,uEAA4C;AAC5C,iEAAsC;AACtC,2EAAgD;AAChD,2EAAgD;AAChD,oEAAyC","sourcesContent":["export * from './validation/property-key.js'\nexport * from './validation/schema.js'\nexport * from './validation/validation-error.js'\nexport * from './validation/validation-issue.js'\nexport * from './validation/validator.js'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-schema",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "license": "MIT",
5
5
  "description": "Lexicon schema system for AT Lexicons",
6
6
  "keywords": [
@@ -16,7 +16,11 @@
16
16
  },
17
17
  "files": [
18
18
  "./src",
19
- "./dist"
19
+ "./tsconfig.build.json",
20
+ "./tsconfig.tests.json",
21
+ "./tsconfig.json",
22
+ "./dist",
23
+ "./CHANGELOG.md"
20
24
  ],
21
25
  "sideEffects": false,
22
26
  "type": "commonjs",
@@ -32,8 +36,8 @@
32
36
  },
33
37
  "dependencies": {
34
38
  "tslib": "^2.8.1",
35
- "@atproto/syntax": "0.4.1",
36
- "@atproto/lex-data": "0.0.1"
39
+ "@atproto/syntax": "0.4.2",
40
+ "@atproto/lex-data": "0.0.2"
37
41
  },
38
42
  "devDependencies": {
39
43
  "jest": "^28.1.2"
package/src/core/$type.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { Nsid } from './string-format.js'
1
+ import { NsidString } from './string-format.js'
2
2
 
3
3
  export type $Type<
4
- N extends Nsid = Nsid,
4
+ N extends NsidString = NsidString,
5
5
  H extends string = string,
6
- > = N extends Nsid
6
+ > = N extends NsidString
7
7
  ? string extends H
8
8
  ? N | `${N}#${string}`
9
9
  : H extends 'main'
@@ -11,7 +11,10 @@ export type $Type<
11
11
  : `${N}#${H}`
12
12
  : never
13
13
 
14
- export function $type<N extends Nsid, H extends string>(
14
+ export type $TypeOf<O extends { $type?: string }> = NonNullable<O['$type']>
15
+
16
+ /*@__NO_SIDE_EFFECTS__*/
17
+ export function $type<N extends NsidString, H extends string>(
15
18
  nsid: N,
16
19
  hash: H,
17
20
  ): $Type<N, H> {
@@ -1,15 +1,22 @@
1
- export type RecordKey = 'any' | 'nsid' | 'tid' | `literal:${string}`
1
+ import { isValidRecordKey } from '@atproto/syntax'
2
2
 
3
- export function isRecordKey<T>(key: T): key is T & RecordKey {
3
+ export type LexiconRecordKey = 'any' | 'nsid' | 'tid' | `literal:${string}`
4
+
5
+ /*@__NO_SIDE_EFFECTS__*/
6
+ export function isLexiconRecordKey<T>(key: T): key is T & LexiconRecordKey {
4
7
  return (
5
8
  key === 'any' ||
6
9
  key === 'nsid' ||
7
10
  key === 'tid' ||
8
- (typeof key === 'string' && key.startsWith('literal:'))
11
+ (typeof key === 'string' &&
12
+ key.startsWith('literal:') &&
13
+ key.length > 8 &&
14
+ isValidRecordKey(key.slice(8)))
9
15
  )
10
16
  }
11
17
 
12
- export function asRecordKey(key: unknown): RecordKey {
13
- if (isRecordKey(key)) return key
18
+ /*@__NO_SIDE_EFFECTS__*/
19
+ export function asLexiconRecordKey(key: unknown): LexiconRecordKey {
20
+ if (isLexiconRecordKey(key)) return key
14
21
  throw new Error(`Invalid record key: ${String(key)}`)
15
22
  }
@@ -3,18 +3,22 @@ export type ResultFailure<E = Error> = { success: false; error: E }
3
3
 
4
4
  export type Result<V = any, E = Error> = ResultSuccess<V> | ResultFailure<E>
5
5
 
6
+ /*@__NO_SIDE_EFFECTS__*/
6
7
  export function success<V>(value: V): ResultSuccess<V> {
7
8
  return { success: true, value }
8
9
  }
9
10
 
11
+ /*@__NO_SIDE_EFFECTS__*/
10
12
  export function failure<E>(error: E): ResultFailure<E> {
11
13
  return { success: false, error }
12
14
  }
13
15
 
16
+ /*@__NO_SIDE_EFFECTS__*/
14
17
  export function failureError<T>(result: ResultFailure<T>): T {
15
18
  return result.error
16
19
  }
17
20
 
21
+ /*@__NO_SIDE_EFFECTS__*/
18
22
  export function successValue<T>(result: ResultSuccess<T>): T {
19
23
  return result.value
20
24
  }
@@ -38,6 +42,7 @@ export function successValue<T>(result: ResultSuccess<T>): T {
38
42
  * }
39
43
  * ```
40
44
  */
45
+ /*@__NO_SIDE_EFFECTS__*/
41
46
  export function catchall(err: unknown): ResultFailure<Error> {
42
47
  if (err instanceof Error) return failure(err)
43
48
  return failure(new Error('Unknown error', { cause: err }))
@@ -65,6 +70,7 @@ export function catchall(err: unknown): ResultFailure<Error> {
65
70
  * console.error(result.error) // FooError | BarError
66
71
  * }
67
72
  */
73
+ /*@__NO_SIDE_EFFECTS__*/
68
74
  export function createCatcher<T>(Ctor: new (...args: any[]) => T) {
69
75
  return (err: unknown): ResultFailure<T> => {
70
76
  if (err instanceof Ctor) return failure(err)
@@ -1,5 +1,14 @@
1
1
  import { ensureValidCidString, isLanguage } from '@atproto/lex-data'
2
2
  import {
3
+ AtIdentifierString,
4
+ AtUriString,
5
+ DatetimeString,
6
+ DidString,
7
+ HandleString,
8
+ NsidString,
9
+ RecordKeyString,
10
+ TidString,
11
+ ensureValidAtIdentifier,
3
12
  ensureValidAtUri,
4
13
  ensureValidDatetime,
5
14
  ensureValidDid,
@@ -9,13 +18,87 @@ import {
9
18
  ensureValidTid,
10
19
  } from '@atproto/syntax'
11
20
 
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`
21
+ type AssertFn<T> = <I extends string>(input: I) => asserts input is I & T
22
+ type CastFn<T> = <I extends string>(input: I) => I & T
23
+
24
+ /*@__NO_SIDE_EFFECTS__*/
25
+ function createCastFunction<T>(assertFn: AssertFn<T>): CastFn<T> {
26
+ return <I extends string>(input: I) => {
27
+ assertFn(input)
28
+ return input as I & T
16
29
  }
17
30
  }
18
31
 
32
+ // Re-export utility typed as assertion functions so that TypeScript can
33
+ // infer the narrowed type after calling them.
34
+
35
+ export type {
36
+ AtIdentifierString,
37
+ AtUriString,
38
+ DatetimeString,
39
+ DidString,
40
+ HandleString,
41
+ NsidString,
42
+ RecordKeyString,
43
+ TidString,
44
+ }
45
+
46
+ export const assertDid: AssertFn<DidString> = ensureValidDid
47
+ export const assertAtUri: AssertFn<AtUriString> = ensureValidAtUri
48
+ export const assertAtIdentifier: AssertFn<AtIdentifierString> =
49
+ ensureValidAtIdentifier
50
+ export const assertNsid: AssertFn<NsidString> = ensureValidNsid
51
+ export const assertTid: AssertFn<TidString> = ensureValidTid
52
+ export const assertRecordKey: AssertFn<RecordKeyString> = ensureValidRecordKey
53
+ export const assertDatetime: AssertFn<DatetimeString> = ensureValidDatetime
54
+ export const assertCidString: AssertFn<string> = ensureValidCidString
55
+ export const assertHandle: AssertFn<HandleString> = ensureValidHandle
56
+
57
+ // Export utilities for formats missing from @atproto/syntax
58
+
59
+ export type CidString = string
60
+ export type UriString = `${string}:${string}`
61
+ export type LanguageString = string
62
+
63
+ /*@__NO_SIDE_EFFECTS__*/
64
+ export function assertUri(input: string): asserts input is UriString {
65
+ if (!/^\w+:(?:\/\/)?[^\s/][^\s]*$/.test(input)) {
66
+ throw new Error('Invalid URI')
67
+ }
68
+ }
69
+
70
+ /*@__NO_SIDE_EFFECTS__*/
71
+ export function assertLanguage(input: string): asserts input is LanguageString {
72
+ if (!isLanguage(input)) {
73
+ throw new Error('Invalid BCP 47 string')
74
+ }
75
+ }
76
+
77
+ export const asDid: CastFn<DidString> =
78
+ /*#__PURE__*/ createCastFunction(ensureValidDid)
79
+ export const asAtUri: CastFn<AtUriString> =
80
+ /*#__PURE__*/ createCastFunction(ensureValidAtUri)
81
+ export const asNsid: CastFn<NsidString> =
82
+ /*#__PURE__*/ createCastFunction(ensureValidNsid)
83
+ export const asTid: CastFn<TidString> =
84
+ /*#__PURE__*/ createCastFunction(ensureValidTid)
85
+ export const asRecordKey: CastFn<RecordKeyString> =
86
+ /*#__PURE__*/ createCastFunction(ensureValidRecordKey)
87
+ export const asDatetime: CastFn<DatetimeString> =
88
+ /*#__PURE__*/ createCastFunction(ensureValidDatetime)
89
+ export const asCidString: CastFn<string> =
90
+ /*#__PURE__*/ createCastFunction(ensureValidCidString)
91
+ export const asHandle: CastFn<HandleString> =
92
+ /*#__PURE__*/ createCastFunction(ensureValidHandle)
93
+ export const asUri: CastFn<UriString> =
94
+ /*#__PURE__*/ createCastFunction(assertUri)
95
+ export const asLanguage: CastFn<LanguageString> =
96
+ /*#__PURE__*/ createCastFunction(assertLanguage)
97
+ export const asAtIdentifier: CastFn<AtIdentifierString> =
98
+ /*#__PURE__*/ createCastFunction(assertAtIdentifier)
99
+
100
+ // String formatting types and utilities
101
+
19
102
  export const STRING_FORMATS = Object.freeze([
20
103
  'datetime',
21
104
  'uri',
@@ -32,72 +115,24 @@ export const STRING_FORMATS = Object.freeze([
32
115
 
33
116
  export type StringFormat = (typeof STRING_FORMATS)[number]
34
117
 
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
118
  export type InferStringFormat<F> =
45
119
  //
46
120
  F extends 'datetime'
47
- ? Datetime
121
+ ? DatetimeString
48
122
  : F extends 'uri'
49
- ? Uri
123
+ ? UriString
50
124
  : F extends 'at-uri'
51
- ? AtUri
125
+ ? AtUriString
52
126
  : F extends 'did'
53
- ? Did
127
+ ? DidString
54
128
  : F extends 'handle'
55
- ? Handle
129
+ ? HandleString
56
130
  : F extends 'at-identifier'
57
- ? AtIdentifier
131
+ ? AtIdentifierString
58
132
  : 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
- }
133
+ ? NsidString
134
+ : // LanguageString | CidString | TidString | RecordKeyString
135
+ string
101
136
 
102
137
  const formatters = /*#__PURE__*/ new Map<StringFormat, (str: string) => void>([
103
138
  ['datetime', assertDatetime],
@@ -113,6 +148,7 @@ const formatters = /*#__PURE__*/ new Map<StringFormat, (str: string) => void>([
113
148
  ['record-key', assertRecordKey],
114
149
  ] as const)
115
150
 
151
+ /*@__NO_SIDE_EFFECTS__*/
116
152
  export function assertStringFormat<F extends StringFormat>(
117
153
  input: string,
118
154
  format: F,