@atproto/lex-cli 0.10.2 → 0.10.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.
- package/CHANGELOG.md +16 -0
- package/bin.js +8 -4
- package/dist/codegen/client.d.ts +1 -1
- package/dist/codegen/client.d.ts.map +1 -1
- package/dist/codegen/client.js +1 -1
- package/dist/codegen/client.js.map +1 -1
- package/dist/codegen/common.d.ts +4 -4
- package/dist/codegen/common.d.ts.map +1 -1
- package/dist/codegen/common.js +1 -1
- package/dist/codegen/common.js.map +1 -1
- package/dist/codegen/lex-gen.js +2 -1
- package/dist/codegen/lex-gen.js.map +1 -1
- package/dist/codegen/server.d.ts +1 -1
- package/dist/codegen/server.d.ts.map +1 -1
- package/dist/codegen/server.js +1 -1
- package/dist/codegen/server.js.map +1 -1
- package/dist/codegen/util.js +1 -0
- package/dist/codegen/util.js.map +1 -1
- package/dist/mdgen/index.js +3 -2
- package/dist/mdgen/index.js.map +1 -1
- package/dist/util.d.ts +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js.map +1 -1
- package/package.json +17 -13
- package/src/codegen/client.ts +0 -566
- package/src/codegen/common.ts +0 -283
- package/src/codegen/lex-gen.ts +0 -720
- package/src/codegen/server.ts +0 -448
- package/src/codegen/util.ts +0 -84
- package/src/index.ts +0 -105
- package/src/mdgen/index.ts +0 -77
- package/src/types.ts +0 -14
- package/src/util.ts +0 -151
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -4
package/src/codegen/common.ts
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { Options as PrettierOptions, format } from 'prettier'
|
|
2
|
-
import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph'
|
|
3
|
-
import { type LexiconDoc } from '@atproto/lexicon'
|
|
4
|
-
import { type GeneratedFile } from '../types.js'
|
|
5
|
-
import { toTitleCase } from './util.js'
|
|
6
|
-
|
|
7
|
-
const PRETTIER_OPTS: PrettierOptions = {
|
|
8
|
-
parser: 'typescript',
|
|
9
|
-
tabWidth: 2,
|
|
10
|
-
semi: false,
|
|
11
|
-
singleQuote: true,
|
|
12
|
-
trailingComma: 'all',
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const utilTs = (project) =>
|
|
16
|
-
gen(project, '/util.ts', async (file) => {
|
|
17
|
-
file.replaceWithText(`
|
|
18
|
-
import { type ValidationResult } from '@atproto/lexicon'
|
|
19
|
-
|
|
20
|
-
export type OmitKey<T, K extends keyof T> = {
|
|
21
|
-
[K2 in keyof T as K2 extends K ? never : K2]: T[K2]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type $Typed<V, T extends string = string> = V & { $type: T }
|
|
25
|
-
export type Un$Typed<V extends { $type?: string }> = OmitKey<V, '$type'>
|
|
26
|
-
|
|
27
|
-
export type $Type<Id extends string, Hash extends string> = Hash extends 'main'
|
|
28
|
-
? Id
|
|
29
|
-
: \`\${Id}#\${Hash}\`
|
|
30
|
-
|
|
31
|
-
function isObject<V>(v: V): v is V & object {
|
|
32
|
-
return v != null && typeof v === 'object'
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function is$type<Id extends string, Hash extends string>(
|
|
36
|
-
$type: unknown,
|
|
37
|
-
id: Id,
|
|
38
|
-
hash: Hash,
|
|
39
|
-
): $type is $Type<Id, Hash> {
|
|
40
|
-
return hash === 'main'
|
|
41
|
-
? $type === id
|
|
42
|
-
: // $type === \`\${id}#\${hash}\`
|
|
43
|
-
typeof $type === 'string' &&
|
|
44
|
-
$type.length === id.length + 1 + hash.length &&
|
|
45
|
-
$type.charCodeAt(id.length) === 35 /* '#' */ &&
|
|
46
|
-
$type.startsWith(id) &&
|
|
47
|
-
$type.endsWith(hash)
|
|
48
|
-
}
|
|
49
|
-
${
|
|
50
|
-
/**
|
|
51
|
-
* The construct below allows to properly distinguish open unions. Consider
|
|
52
|
-
* the following example:
|
|
53
|
-
*
|
|
54
|
-
* ```ts
|
|
55
|
-
* type Foo = { $type?: $Type<'foo', 'main'>; foo: string }
|
|
56
|
-
* type Bar = { $type?: $Type<'bar', 'main'>; bar: string }
|
|
57
|
-
* type OpenFooBarUnion = $Typed<Foo> | $Typed<Bar> | { $type: string }
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* In the context of lexicons, when there is a open union as shown above, the
|
|
61
|
-
* if `$type` if either `foo` or `bar`, then the object IS of type `Foo` or
|
|
62
|
-
* `Bar`.
|
|
63
|
-
*
|
|
64
|
-
* ```ts
|
|
65
|
-
* declare const obj1: OpenFooBarUnion
|
|
66
|
-
* if (is$typed(obj1, 'foo', 'main')) {
|
|
67
|
-
* obj1.$type // $Type<'foo', 'main'>
|
|
68
|
-
* obj1.foo // string
|
|
69
|
-
* }
|
|
70
|
-
* ```
|
|
71
|
-
*
|
|
72
|
-
* Similarly, if an object is of type `unknown`, then the `is$typed` function
|
|
73
|
-
* should only return assurance about the `$type` property, which is what it
|
|
74
|
-
* actually checks:
|
|
75
|
-
*
|
|
76
|
-
* ```ts
|
|
77
|
-
* declare const obj2: unknown
|
|
78
|
-
* if (is$typed(obj2, 'foo', 'main')) {
|
|
79
|
-
* obj2.$type // $Type<'foo', 'main'>
|
|
80
|
-
* // @ts-expect-error
|
|
81
|
-
* obj2.foo
|
|
82
|
-
* }
|
|
83
|
-
* ```
|
|
84
|
-
*
|
|
85
|
-
* The construct bellow is what makes these two scenarios possible.
|
|
86
|
-
*/
|
|
87
|
-
''
|
|
88
|
-
}
|
|
89
|
-
export type $TypedObject<V, Id extends string, Hash extends string> = V extends {
|
|
90
|
-
$type: $Type<Id, Hash>
|
|
91
|
-
}
|
|
92
|
-
? V
|
|
93
|
-
: V extends { $type?: string }
|
|
94
|
-
? V extends { $type?: infer T extends $Type<Id, Hash> }
|
|
95
|
-
? V & { $type: T }
|
|
96
|
-
: never
|
|
97
|
-
: V & { $type: $Type<Id, Hash> }
|
|
98
|
-
|
|
99
|
-
export function is$typed<V, Id extends string, Hash extends string>(
|
|
100
|
-
v: V,
|
|
101
|
-
id: Id,
|
|
102
|
-
hash: Hash,
|
|
103
|
-
): v is $TypedObject<V, Id, Hash> {
|
|
104
|
-
return isObject(v) && '$type' in v && is$type(v.$type, id, hash)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function maybe$typed<V, Id extends string, Hash extends string>(
|
|
108
|
-
v: V,
|
|
109
|
-
id: Id,
|
|
110
|
-
hash: Hash,
|
|
111
|
-
): v is V & object & { $type?: $Type<Id, Hash> } {
|
|
112
|
-
return (
|
|
113
|
-
isObject(v) &&
|
|
114
|
-
('$type' in v
|
|
115
|
-
? v.$type === undefined || is$type(v.$type, id, hash)
|
|
116
|
-
: true)
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export type Validator<R = unknown> = (v: unknown) => ValidationResult<R>
|
|
121
|
-
export type ValidatorParam<V extends Validator> =
|
|
122
|
-
V extends Validator<infer R> ? R : never
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Utility function that allows to convert a "validate*" utility function into a
|
|
126
|
-
* type predicate.
|
|
127
|
-
*/
|
|
128
|
-
export function asPredicate<V extends Validator>(validate: V) {
|
|
129
|
-
return function <T>(v: T): v is T & ValidatorParam<V> {
|
|
130
|
-
return validate(v).success
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
`)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
export const lexiconsTs = (project, lexicons: LexiconDoc[]) =>
|
|
137
|
-
gen(project, '/lexicons.ts', async (file) => {
|
|
138
|
-
//= import { type LexiconDoc, Lexicons } from '@atproto/lexicon'
|
|
139
|
-
file
|
|
140
|
-
.addImportDeclaration({
|
|
141
|
-
moduleSpecifier: '@atproto/lexicon',
|
|
142
|
-
})
|
|
143
|
-
.addNamedImports([
|
|
144
|
-
{ name: 'LexiconDoc', isTypeOnly: true },
|
|
145
|
-
{ name: 'Lexicons' },
|
|
146
|
-
{ name: 'ValidationError' },
|
|
147
|
-
{ name: 'ValidationResult', isTypeOnly: true },
|
|
148
|
-
])
|
|
149
|
-
|
|
150
|
-
//= import { is$typed, maybe$typed, type $Typed } from './util.js'
|
|
151
|
-
file
|
|
152
|
-
.addImportDeclaration({
|
|
153
|
-
moduleSpecifier: './util.js',
|
|
154
|
-
})
|
|
155
|
-
.addNamedImports([
|
|
156
|
-
{ name: '$Typed', isTypeOnly: true },
|
|
157
|
-
{ name: 'is$typed' },
|
|
158
|
-
{ name: 'maybe$typed' },
|
|
159
|
-
])
|
|
160
|
-
|
|
161
|
-
//= export const schemaDict = {...} as const satisfies Record<string, LexiconDoc>
|
|
162
|
-
file.addVariableStatement({
|
|
163
|
-
isExported: true,
|
|
164
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
165
|
-
declarations: [
|
|
166
|
-
{
|
|
167
|
-
name: 'schemaDict',
|
|
168
|
-
initializer:
|
|
169
|
-
JSON.stringify(
|
|
170
|
-
lexicons.reduce(
|
|
171
|
-
(acc, cur) => ({
|
|
172
|
-
...acc,
|
|
173
|
-
[toTitleCase(cur.id)]: cur,
|
|
174
|
-
}),
|
|
175
|
-
{},
|
|
176
|
-
),
|
|
177
|
-
null,
|
|
178
|
-
2,
|
|
179
|
-
) + ' as const satisfies Record<string, LexiconDoc>',
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
//= export const schemas = Object.values(schemaDict) satisfies LexiconDoc[]
|
|
185
|
-
file.addVariableStatement({
|
|
186
|
-
isExported: true,
|
|
187
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
188
|
-
declarations: [
|
|
189
|
-
{
|
|
190
|
-
name: 'schemas',
|
|
191
|
-
initializer: 'Object.values(schemaDict) satisfies LexiconDoc[]',
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
//= export const lexicons: Lexicons = new Lexicons(schemas)
|
|
197
|
-
file.addVariableStatement({
|
|
198
|
-
isExported: true,
|
|
199
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
200
|
-
declarations: [
|
|
201
|
-
{
|
|
202
|
-
name: 'lexicons',
|
|
203
|
-
type: 'Lexicons',
|
|
204
|
-
initializer: 'new Lexicons(schemas)',
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
file.addFunction({
|
|
210
|
-
isExported: true,
|
|
211
|
-
name: 'validate',
|
|
212
|
-
overloads: [
|
|
213
|
-
{
|
|
214
|
-
typeParameters: ['T extends { $type: string }'],
|
|
215
|
-
parameters: [
|
|
216
|
-
{ name: 'v', type: 'unknown' },
|
|
217
|
-
{ name: 'id', type: 'string' },
|
|
218
|
-
{ name: 'hash', type: 'string' },
|
|
219
|
-
{ name: 'requiredType', type: 'true' },
|
|
220
|
-
],
|
|
221
|
-
returnType: 'ValidationResult<T>',
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
typeParameters: ['T extends { $type?: string }'],
|
|
225
|
-
parameters: [
|
|
226
|
-
{ name: 'v', type: 'unknown' },
|
|
227
|
-
{ name: 'id', type: 'string' },
|
|
228
|
-
{ name: 'hash', type: 'string' },
|
|
229
|
-
{ name: 'requiredType', type: 'false', hasQuestionToken: true },
|
|
230
|
-
],
|
|
231
|
-
returnType: 'ValidationResult<T>',
|
|
232
|
-
},
|
|
233
|
-
],
|
|
234
|
-
parameters: [
|
|
235
|
-
{ name: 'v', type: 'unknown' },
|
|
236
|
-
{ name: 'id', type: 'string' },
|
|
237
|
-
{ name: 'hash', type: 'string' },
|
|
238
|
-
{ name: 'requiredType', type: 'boolean', hasQuestionToken: true },
|
|
239
|
-
],
|
|
240
|
-
statements: [
|
|
241
|
-
// If $type is present, make sure it is valid before validating the rest of the object
|
|
242
|
-
'return (requiredType ? is$typed : maybe$typed)(v, id, hash) ? lexicons.validate(`${id}#${hash}`, v) : { success: false, error: new ValidationError(`Must be an object with "${hash === \'main\' ? id : `${id}#${hash}`}" $type property`) }',
|
|
243
|
-
],
|
|
244
|
-
returnType: 'ValidationResult',
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
//= export const ids = {...}
|
|
248
|
-
file.addVariableStatement({
|
|
249
|
-
isExported: true,
|
|
250
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
251
|
-
declarations: [
|
|
252
|
-
{
|
|
253
|
-
name: 'ids',
|
|
254
|
-
initializer: `{${lexicons
|
|
255
|
-
.map(
|
|
256
|
-
(lex) => `\n ${toTitleCase(lex.id)}: ${JSON.stringify(lex.id)},`,
|
|
257
|
-
)
|
|
258
|
-
.join('')}\n} as const`,
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
})
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
export async function gen(
|
|
265
|
-
project: Project,
|
|
266
|
-
path: string,
|
|
267
|
-
gen: (file: SourceFile) => Promise<void>,
|
|
268
|
-
): Promise<GeneratedFile> {
|
|
269
|
-
const file = project.createSourceFile(path)
|
|
270
|
-
await gen(file)
|
|
271
|
-
await file.save() // Save in the "in memory" file system
|
|
272
|
-
const src = `${banner()}${file.getFullText()}`
|
|
273
|
-
const content = await format(src, PRETTIER_OPTS)
|
|
274
|
-
|
|
275
|
-
return { path, content }
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function banner() {
|
|
279
|
-
return `/**
|
|
280
|
-
* GENERATED CODE - DO NOT MODIFY
|
|
281
|
-
*/
|
|
282
|
-
`
|
|
283
|
-
}
|