@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/lex-gen.ts
DELETED
|
@@ -1,720 +0,0 @@
|
|
|
1
|
-
import { relative as getRelativePath } from 'node:path/posix'
|
|
2
|
-
import { JSDoc, SourceFile, VariableDeclarationKind } from 'ts-morph'
|
|
3
|
-
import {
|
|
4
|
-
type LexArray,
|
|
5
|
-
type LexBlob,
|
|
6
|
-
type LexBytes,
|
|
7
|
-
type LexCidLink,
|
|
8
|
-
type LexIpldType,
|
|
9
|
-
type LexObject,
|
|
10
|
-
type LexPrimitive,
|
|
11
|
-
type LexToken,
|
|
12
|
-
Lexicons,
|
|
13
|
-
} from '@atproto/lexicon'
|
|
14
|
-
import { toCamelCase, toScreamingSnakeCase, toTitleCase } from './util.js'
|
|
15
|
-
|
|
16
|
-
interface Commentable {
|
|
17
|
-
addJsDoc: ({ description }: { description: string }) => JSDoc
|
|
18
|
-
}
|
|
19
|
-
export function genComment<T extends Commentable>(
|
|
20
|
-
commentable: T,
|
|
21
|
-
def: { description?: string },
|
|
22
|
-
): T {
|
|
23
|
-
if (def.description) {
|
|
24
|
-
commentable.addJsDoc({ description: def.description })
|
|
25
|
-
}
|
|
26
|
-
return commentable
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function genCommonImports(file: SourceFile, baseNsid: string) {
|
|
30
|
-
//= import {ValidationResult, BlobRef} from '@atproto/lexicon'
|
|
31
|
-
file
|
|
32
|
-
.addImportDeclaration({
|
|
33
|
-
moduleSpecifier: '@atproto/lexicon',
|
|
34
|
-
})
|
|
35
|
-
.addNamedImports([
|
|
36
|
-
{ name: 'ValidationResult', isTypeOnly: true },
|
|
37
|
-
{ name: 'BlobRef' },
|
|
38
|
-
])
|
|
39
|
-
|
|
40
|
-
//= import {CID} from 'multiformats/cid'
|
|
41
|
-
file
|
|
42
|
-
.addImportDeclaration({
|
|
43
|
-
moduleSpecifier: 'multiformats/cid',
|
|
44
|
-
})
|
|
45
|
-
.addNamedImports([{ name: 'CID' }])
|
|
46
|
-
|
|
47
|
-
//= import { validate as _validate } from '../../lexicons.ts'
|
|
48
|
-
file
|
|
49
|
-
.addImportDeclaration({
|
|
50
|
-
moduleSpecifier: `${baseNsid
|
|
51
|
-
.split('.')
|
|
52
|
-
.map((_str) => '..')
|
|
53
|
-
.join('/')}/lexicons.js`,
|
|
54
|
-
})
|
|
55
|
-
.addNamedImports([{ name: 'validate', alias: '_validate' }])
|
|
56
|
-
|
|
57
|
-
//= import { type $Typed, is$typed as _is$typed, type OmitKey } from '../[...]/util.ts'
|
|
58
|
-
file
|
|
59
|
-
.addImportDeclaration({
|
|
60
|
-
moduleSpecifier: `${baseNsid
|
|
61
|
-
.split('.')
|
|
62
|
-
.map((_str) => '..')
|
|
63
|
-
.join('/')}/util.js`,
|
|
64
|
-
})
|
|
65
|
-
.addNamedImports([
|
|
66
|
-
{ name: '$Typed', isTypeOnly: true },
|
|
67
|
-
{ name: 'is$typed', alias: '_is$typed' },
|
|
68
|
-
{ name: 'OmitKey', isTypeOnly: true },
|
|
69
|
-
])
|
|
70
|
-
|
|
71
|
-
// TypeScript adds protection against circular imports, which hurts bundle
|
|
72
|
-
// size. Since we know that lexicon.ts and util.ts do not depend on the file
|
|
73
|
-
// being generated, we can safely bypass this protection. Note that we are not
|
|
74
|
-
// using `import * as util from '../../util.js'` because typescript will emit
|
|
75
|
-
// is own helpers for the import, which we want to avoid.
|
|
76
|
-
file.addVariableStatement({
|
|
77
|
-
isExported: false,
|
|
78
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
79
|
-
declarations: [
|
|
80
|
-
{ name: 'is$typed', initializer: '_is$typed' },
|
|
81
|
-
{ name: 'validate', initializer: '_validate' },
|
|
82
|
-
],
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
//= const id = "{baseNsid}"
|
|
86
|
-
file.addVariableStatement({
|
|
87
|
-
isExported: false, // Do not export to allow tree-shaking
|
|
88
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
89
|
-
declarations: [{ name: 'id', initializer: JSON.stringify(baseNsid) }],
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function genImports(
|
|
94
|
-
file: SourceFile,
|
|
95
|
-
imports: Set<string>,
|
|
96
|
-
baseNsid: string,
|
|
97
|
-
) {
|
|
98
|
-
const startPath = '/' + baseNsid.split('.').slice(0, -1).join('/')
|
|
99
|
-
|
|
100
|
-
for (const nsid of imports) {
|
|
101
|
-
const targetPath = '/' + nsid.split('.').join('/') + '.js'
|
|
102
|
-
let resolvedPath = getRelativePath(startPath, targetPath)
|
|
103
|
-
if (!resolvedPath.startsWith('.')) {
|
|
104
|
-
resolvedPath = `./${resolvedPath}`
|
|
105
|
-
}
|
|
106
|
-
file.addImportDeclaration({
|
|
107
|
-
isTypeOnly: true,
|
|
108
|
-
moduleSpecifier: resolvedPath,
|
|
109
|
-
namespaceImport: toTitleCase(nsid),
|
|
110
|
-
})
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function genUserType(
|
|
115
|
-
file: SourceFile,
|
|
116
|
-
imports: Set<string>,
|
|
117
|
-
lexicons: Lexicons,
|
|
118
|
-
lexUri: string,
|
|
119
|
-
) {
|
|
120
|
-
const def = lexicons.getDefOrThrow(lexUri)
|
|
121
|
-
switch (def.type) {
|
|
122
|
-
case 'array':
|
|
123
|
-
genArray(file, imports, lexUri, def)
|
|
124
|
-
break
|
|
125
|
-
case 'token':
|
|
126
|
-
genToken(file, lexUri, def)
|
|
127
|
-
break
|
|
128
|
-
case 'object': {
|
|
129
|
-
const ifaceName: string = toTitleCase(getHash(lexUri))
|
|
130
|
-
genObject(file, imports, lexUri, def, ifaceName, {
|
|
131
|
-
typeProperty: true,
|
|
132
|
-
})
|
|
133
|
-
genObjHelpers(file, lexUri, ifaceName, {
|
|
134
|
-
requireTypeProperty: false,
|
|
135
|
-
})
|
|
136
|
-
break
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
case 'blob':
|
|
140
|
-
case 'bytes':
|
|
141
|
-
case 'cid-link':
|
|
142
|
-
case 'boolean':
|
|
143
|
-
case 'integer':
|
|
144
|
-
case 'string':
|
|
145
|
-
case 'unknown':
|
|
146
|
-
genPrimitiveOrBlob(file, lexUri, def)
|
|
147
|
-
break
|
|
148
|
-
|
|
149
|
-
default:
|
|
150
|
-
throw new Error(
|
|
151
|
-
`genLexUserType() called with wrong definition type (${def.type}) in ${lexUri}`,
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function genObject(
|
|
157
|
-
file: SourceFile,
|
|
158
|
-
imports: Set<string>,
|
|
159
|
-
lexUri: string,
|
|
160
|
-
def: LexObject,
|
|
161
|
-
ifaceName: string,
|
|
162
|
-
{
|
|
163
|
-
defaultsArePresent = true,
|
|
164
|
-
allowUnknownProperties = false,
|
|
165
|
-
typeProperty = false,
|
|
166
|
-
}: {
|
|
167
|
-
defaultsArePresent?: boolean
|
|
168
|
-
allowUnknownProperties?: boolean
|
|
169
|
-
typeProperty?: boolean | 'required'
|
|
170
|
-
} = {},
|
|
171
|
-
) {
|
|
172
|
-
const iface = file.addInterface({
|
|
173
|
-
name: ifaceName,
|
|
174
|
-
isExported: true,
|
|
175
|
-
})
|
|
176
|
-
genComment(iface, def)
|
|
177
|
-
|
|
178
|
-
if (typeProperty) {
|
|
179
|
-
const hash = getHash(lexUri)
|
|
180
|
-
const baseNsid = stripScheme(stripHash(lexUri))
|
|
181
|
-
|
|
182
|
-
//= $type?: <uri>
|
|
183
|
-
iface.addProperty({
|
|
184
|
-
name: typeProperty === 'required' ? `$type` : `$type?`,
|
|
185
|
-
type:
|
|
186
|
-
// Not using $Type here because it is less readable than a plain string
|
|
187
|
-
// `$Type<${JSON.stringify(baseNsid)}, ${JSON.stringify(hash)}>`
|
|
188
|
-
hash === 'main'
|
|
189
|
-
? JSON.stringify(`${baseNsid}`)
|
|
190
|
-
: JSON.stringify(`${baseNsid}#${hash}`),
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const nullableProps = new Set(def.nullable)
|
|
195
|
-
if (def.properties) {
|
|
196
|
-
for (const propKey in def.properties) {
|
|
197
|
-
const propDef = def.properties[propKey]
|
|
198
|
-
const propNullable = nullableProps.has(propKey)
|
|
199
|
-
const req =
|
|
200
|
-
def.required?.includes(propKey) ||
|
|
201
|
-
(defaultsArePresent &&
|
|
202
|
-
'default' in propDef &&
|
|
203
|
-
propDef.default !== undefined)
|
|
204
|
-
if (propDef.type === 'ref' || propDef.type === 'union') {
|
|
205
|
-
//= propName: External|External
|
|
206
|
-
const types =
|
|
207
|
-
propDef.type === 'union'
|
|
208
|
-
? propDef.refs.map((ref) => refToUnionType(ref, lexUri, imports))
|
|
209
|
-
: [refToType(propDef.ref, stripScheme(stripHash(lexUri)), imports)]
|
|
210
|
-
if (propDef.type === 'union' && !propDef.closed) {
|
|
211
|
-
types.push('{ $type: string }')
|
|
212
|
-
}
|
|
213
|
-
iface.addProperty({
|
|
214
|
-
name: `${propKey}${req ? '' : '?'}`,
|
|
215
|
-
type: makeType(types, { nullable: propNullable }),
|
|
216
|
-
})
|
|
217
|
-
continue
|
|
218
|
-
} else {
|
|
219
|
-
if (propDef.type === 'array') {
|
|
220
|
-
//= propName: type[]
|
|
221
|
-
let propAst
|
|
222
|
-
if (propDef.items.type === 'ref') {
|
|
223
|
-
propAst = iface.addProperty({
|
|
224
|
-
name: `${propKey}${req ? '' : '?'}`,
|
|
225
|
-
type: makeType(
|
|
226
|
-
refToType(
|
|
227
|
-
propDef.items.ref,
|
|
228
|
-
stripScheme(stripHash(lexUri)),
|
|
229
|
-
imports,
|
|
230
|
-
),
|
|
231
|
-
{
|
|
232
|
-
nullable: propNullable,
|
|
233
|
-
array: true,
|
|
234
|
-
},
|
|
235
|
-
),
|
|
236
|
-
})
|
|
237
|
-
} else if (propDef.items.type === 'union') {
|
|
238
|
-
const types = propDef.items.refs.map((ref) =>
|
|
239
|
-
refToUnionType(ref, lexUri, imports),
|
|
240
|
-
)
|
|
241
|
-
if (!propDef.items.closed) {
|
|
242
|
-
types.push('{ $type: string }')
|
|
243
|
-
}
|
|
244
|
-
propAst = iface.addProperty({
|
|
245
|
-
name: `${propKey}${req ? '' : '?'}`,
|
|
246
|
-
type: makeType(types, {
|
|
247
|
-
nullable: propNullable,
|
|
248
|
-
array: true,
|
|
249
|
-
}),
|
|
250
|
-
})
|
|
251
|
-
} else {
|
|
252
|
-
propAst = iface.addProperty({
|
|
253
|
-
name: `${propKey}${req ? '' : '?'}`,
|
|
254
|
-
type: makeType(primitiveOrBlobToType(propDef.items), {
|
|
255
|
-
nullable: propNullable,
|
|
256
|
-
array: true,
|
|
257
|
-
}),
|
|
258
|
-
})
|
|
259
|
-
}
|
|
260
|
-
genComment(propAst, propDef)
|
|
261
|
-
} else {
|
|
262
|
-
//= propName: type
|
|
263
|
-
genComment(
|
|
264
|
-
iface.addProperty({
|
|
265
|
-
name: `${propKey}${req ? '' : '?'}`,
|
|
266
|
-
type: makeType(primitiveOrBlobToType(propDef), {
|
|
267
|
-
nullable: propNullable,
|
|
268
|
-
}),
|
|
269
|
-
}),
|
|
270
|
-
propDef,
|
|
271
|
-
)
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (allowUnknownProperties) {
|
|
277
|
-
//= [k: string]: unknown
|
|
278
|
-
iface.addIndexSignature({
|
|
279
|
-
keyName: 'k',
|
|
280
|
-
keyType: 'string',
|
|
281
|
-
returnType: 'unknown',
|
|
282
|
-
})
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export function genToken(file: SourceFile, lexUri: string, def: LexToken) {
|
|
288
|
-
//= /** <comment> */
|
|
289
|
-
//= export const <TOKEN> = `${id}#<token>`
|
|
290
|
-
genComment(
|
|
291
|
-
file.addVariableStatement({
|
|
292
|
-
isExported: true,
|
|
293
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
294
|
-
declarations: [
|
|
295
|
-
{
|
|
296
|
-
name: toScreamingSnakeCase(getHash(lexUri)),
|
|
297
|
-
initializer: `\`\${id}#${getHash(lexUri)}\``,
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
}),
|
|
301
|
-
def,
|
|
302
|
-
)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export function genArray(
|
|
306
|
-
file: SourceFile,
|
|
307
|
-
imports: Set<string>,
|
|
308
|
-
lexUri: string,
|
|
309
|
-
def: LexArray,
|
|
310
|
-
) {
|
|
311
|
-
if (def.items.type === 'ref') {
|
|
312
|
-
file.addTypeAlias({
|
|
313
|
-
name: toTitleCase(getHash(lexUri)),
|
|
314
|
-
type: `${refToType(
|
|
315
|
-
def.items.ref,
|
|
316
|
-
stripScheme(stripHash(lexUri)),
|
|
317
|
-
imports,
|
|
318
|
-
)}[]`,
|
|
319
|
-
isExported: true,
|
|
320
|
-
})
|
|
321
|
-
} else if (def.items.type === 'union') {
|
|
322
|
-
const types = def.items.refs.map((ref) =>
|
|
323
|
-
refToUnionType(ref, lexUri, imports),
|
|
324
|
-
)
|
|
325
|
-
if (!def.items.closed) {
|
|
326
|
-
types.push('{ $type: string }')
|
|
327
|
-
}
|
|
328
|
-
file.addTypeAlias({
|
|
329
|
-
name: toTitleCase(getHash(lexUri)),
|
|
330
|
-
type: `(${types.join('|')})[]`,
|
|
331
|
-
isExported: true,
|
|
332
|
-
})
|
|
333
|
-
} else {
|
|
334
|
-
genComment(
|
|
335
|
-
file.addTypeAlias({
|
|
336
|
-
name: toTitleCase(getHash(lexUri)),
|
|
337
|
-
type: `${primitiveOrBlobToType(def.items)}[]`,
|
|
338
|
-
isExported: true,
|
|
339
|
-
}),
|
|
340
|
-
def,
|
|
341
|
-
)
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export function genPrimitiveOrBlob(
|
|
346
|
-
file: SourceFile,
|
|
347
|
-
lexUri: string,
|
|
348
|
-
def: LexPrimitive | LexBlob | LexIpldType,
|
|
349
|
-
) {
|
|
350
|
-
genComment(
|
|
351
|
-
file.addTypeAlias({
|
|
352
|
-
name: toTitleCase(getHash(lexUri)),
|
|
353
|
-
type: primitiveOrBlobToType(def),
|
|
354
|
-
isExported: true,
|
|
355
|
-
}),
|
|
356
|
-
def,
|
|
357
|
-
)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
export function genXrpcParams(
|
|
361
|
-
file: SourceFile,
|
|
362
|
-
lexicons: Lexicons,
|
|
363
|
-
lexUri: string,
|
|
364
|
-
defaultsArePresent = true,
|
|
365
|
-
) {
|
|
366
|
-
const def = lexicons.getDefOrThrow(lexUri, [
|
|
367
|
-
'query',
|
|
368
|
-
'subscription',
|
|
369
|
-
'procedure',
|
|
370
|
-
])
|
|
371
|
-
|
|
372
|
-
// @NOTE We need to use a `type` here instead of an `interface` because we
|
|
373
|
-
// need the generated type to be used as generic type parameter like this:
|
|
374
|
-
//
|
|
375
|
-
// type QueryParams = {} // Generated by this function
|
|
376
|
-
//
|
|
377
|
-
// type MyUtil<P extends xrpcServer.QueryParam> = (...)
|
|
378
|
-
// type NsType = MyUtil<NS.QueryParams> // ERROR if `NS.QueryParams` is an `interface`
|
|
379
|
-
//
|
|
380
|
-
// Second line will fail if `NS.QueryParams` is an `interface` that does
|
|
381
|
-
// not explicitly extend `xrpcServer.QueryParam`, or have a string index
|
|
382
|
-
// signature that encompasses `xrpcServer.QueryParam`.
|
|
383
|
-
|
|
384
|
-
//= export type QueryParams = {...}
|
|
385
|
-
if (def.parameters) {
|
|
386
|
-
genComment(
|
|
387
|
-
file.addTypeAlias({
|
|
388
|
-
name: 'QueryParams',
|
|
389
|
-
isExported: true,
|
|
390
|
-
type: `{
|
|
391
|
-
${Object.entries(def.parameters.properties)
|
|
392
|
-
.map(([paramKey, paramDef]) => {
|
|
393
|
-
const req =
|
|
394
|
-
def.parameters!.required?.includes(paramKey) ||
|
|
395
|
-
(defaultsArePresent &&
|
|
396
|
-
'default' in paramDef &&
|
|
397
|
-
paramDef.default !== undefined)
|
|
398
|
-
const jsDoc = paramDef.description
|
|
399
|
-
? `/** ${paramDef.description} */\n`
|
|
400
|
-
: ''
|
|
401
|
-
return `${jsDoc}${paramKey}${req ? '' : '?'}: ${
|
|
402
|
-
paramDef.type === 'array'
|
|
403
|
-
? primitiveToType(paramDef.items) + '[]'
|
|
404
|
-
: primitiveToType(paramDef)
|
|
405
|
-
}`
|
|
406
|
-
})
|
|
407
|
-
.join('\n')}
|
|
408
|
-
}`,
|
|
409
|
-
}),
|
|
410
|
-
def.parameters,
|
|
411
|
-
)
|
|
412
|
-
} else {
|
|
413
|
-
file.addTypeAlias({
|
|
414
|
-
name: 'QueryParams',
|
|
415
|
-
isExported: true,
|
|
416
|
-
type: '{}',
|
|
417
|
-
})
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export function genXrpcInput(
|
|
422
|
-
file: SourceFile,
|
|
423
|
-
imports: Set<string>,
|
|
424
|
-
lexicons: Lexicons,
|
|
425
|
-
lexUri: string,
|
|
426
|
-
defaultsArePresent = true,
|
|
427
|
-
) {
|
|
428
|
-
const def = lexicons.getDefOrThrow(lexUri, ['query', 'procedure'])
|
|
429
|
-
|
|
430
|
-
if (def.type === 'procedure' && def.input?.schema) {
|
|
431
|
-
if (def.input.schema.type === 'ref' || def.input.schema.type === 'union') {
|
|
432
|
-
//= export type InputSchema = ...
|
|
433
|
-
|
|
434
|
-
const types =
|
|
435
|
-
def.input.schema.type === 'union'
|
|
436
|
-
? def.input.schema.refs.map((ref) =>
|
|
437
|
-
refToUnionType(ref, lexUri, imports),
|
|
438
|
-
)
|
|
439
|
-
: [
|
|
440
|
-
refToType(
|
|
441
|
-
def.input.schema.ref,
|
|
442
|
-
stripScheme(stripHash(lexUri)),
|
|
443
|
-
imports,
|
|
444
|
-
),
|
|
445
|
-
]
|
|
446
|
-
|
|
447
|
-
if (def.input.schema.type === 'union' && !def.input.schema.closed) {
|
|
448
|
-
types.push('{ $type: string }')
|
|
449
|
-
}
|
|
450
|
-
file.addTypeAlias({
|
|
451
|
-
name: 'InputSchema',
|
|
452
|
-
type: types.join('|'),
|
|
453
|
-
isExported: true,
|
|
454
|
-
})
|
|
455
|
-
} else {
|
|
456
|
-
//= export interface InputSchema {...}
|
|
457
|
-
genObject(file, imports, lexUri, def.input.schema, `InputSchema`, {
|
|
458
|
-
defaultsArePresent,
|
|
459
|
-
})
|
|
460
|
-
}
|
|
461
|
-
} else if (def.type === 'procedure' && def.input?.encoding) {
|
|
462
|
-
//= export type InputSchema = string | Uint8Array | Blob
|
|
463
|
-
file.addTypeAlias({
|
|
464
|
-
isExported: true,
|
|
465
|
-
name: 'InputSchema',
|
|
466
|
-
type: 'string | Uint8Array | Blob',
|
|
467
|
-
})
|
|
468
|
-
} else {
|
|
469
|
-
//= export type InputSchema = undefined
|
|
470
|
-
file.addTypeAlias({
|
|
471
|
-
isExported: true,
|
|
472
|
-
name: 'InputSchema',
|
|
473
|
-
type: 'undefined',
|
|
474
|
-
})
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
export function genXrpcOutput(
|
|
479
|
-
file: SourceFile,
|
|
480
|
-
imports: Set<string>,
|
|
481
|
-
lexicons: Lexicons,
|
|
482
|
-
lexUri: string,
|
|
483
|
-
defaultsArePresent = true,
|
|
484
|
-
) {
|
|
485
|
-
const def = lexicons.getDefOrThrow(lexUri, [
|
|
486
|
-
'query',
|
|
487
|
-
'subscription',
|
|
488
|
-
'procedure',
|
|
489
|
-
])
|
|
490
|
-
|
|
491
|
-
const schema =
|
|
492
|
-
def.type === 'subscription' ? def.message?.schema : def.output?.schema
|
|
493
|
-
if (schema) {
|
|
494
|
-
if (schema.type === 'ref' || schema.type === 'union') {
|
|
495
|
-
//= export type OutputSchema = ...
|
|
496
|
-
const types =
|
|
497
|
-
schema.type === 'union'
|
|
498
|
-
? schema.refs.map((ref) => refToUnionType(ref, lexUri, imports))
|
|
499
|
-
: [refToType(schema.ref, stripScheme(stripHash(lexUri)), imports)]
|
|
500
|
-
if (schema.type === 'union' && !schema.closed) {
|
|
501
|
-
types.push('{ $type: string }')
|
|
502
|
-
}
|
|
503
|
-
file.addTypeAlias({
|
|
504
|
-
name: 'OutputSchema',
|
|
505
|
-
type: types.join('|'),
|
|
506
|
-
isExported: true,
|
|
507
|
-
})
|
|
508
|
-
} else {
|
|
509
|
-
//= export interface OutputSchema {...}
|
|
510
|
-
genObject(file, imports, lexUri, schema, `OutputSchema`, {
|
|
511
|
-
defaultsArePresent,
|
|
512
|
-
})
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
export function genRecord(
|
|
518
|
-
file: SourceFile,
|
|
519
|
-
imports: Set<string>,
|
|
520
|
-
lexicons: Lexicons,
|
|
521
|
-
lexUri: string,
|
|
522
|
-
) {
|
|
523
|
-
const hash = getHash(lexUri)
|
|
524
|
-
const ifaceName: string = toTitleCase(hash)
|
|
525
|
-
const def = lexicons.getDefOrThrow(lexUri, ['record'])
|
|
526
|
-
|
|
527
|
-
//= export interface {X} {...}
|
|
528
|
-
genObject(file, imports, lexUri, def.record, ifaceName, {
|
|
529
|
-
defaultsArePresent: true,
|
|
530
|
-
allowUnknownProperties: true,
|
|
531
|
-
typeProperty: 'required',
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
//= export function is{X}(v: unknown): v is {X} {...}
|
|
535
|
-
genObjHelpers(file, lexUri, ifaceName, {
|
|
536
|
-
requireTypeProperty: true,
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
// For convenience, we re-export the type and the type guard under the generic
|
|
540
|
-
// names "Record", "isRecord" and "validateRecord".
|
|
541
|
-
// @NOTE This does not account for potential name clashes with a potential
|
|
542
|
-
// "#record" def.
|
|
543
|
-
|
|
544
|
-
//= export { {X} as Record, is{X} as isRecord }
|
|
545
|
-
file.addExportDeclaration({
|
|
546
|
-
namedExports: [
|
|
547
|
-
{
|
|
548
|
-
isTypeOnly: true,
|
|
549
|
-
name: ifaceName,
|
|
550
|
-
alias: 'Record',
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
name: `is${ifaceName}`,
|
|
554
|
-
alias: 'isRecord',
|
|
555
|
-
},
|
|
556
|
-
{
|
|
557
|
-
name: `validate${ifaceName}`,
|
|
558
|
-
alias: 'validateRecord',
|
|
559
|
-
},
|
|
560
|
-
],
|
|
561
|
-
})
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
function genObjHelpers(
|
|
565
|
-
file: SourceFile,
|
|
566
|
-
lexUri: string,
|
|
567
|
-
ifaceName: string,
|
|
568
|
-
{
|
|
569
|
-
requireTypeProperty,
|
|
570
|
-
}: {
|
|
571
|
-
requireTypeProperty: boolean
|
|
572
|
-
},
|
|
573
|
-
) {
|
|
574
|
-
const hash = getHash(lexUri)
|
|
575
|
-
|
|
576
|
-
const hashVar = `hash${ifaceName}`
|
|
577
|
-
|
|
578
|
-
file.addVariableStatement({
|
|
579
|
-
isExported: false,
|
|
580
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
581
|
-
declarations: [{ name: hashVar, initializer: JSON.stringify(hash) }],
|
|
582
|
-
})
|
|
583
|
-
|
|
584
|
-
const isX = toCamelCase(`is-${ifaceName}`)
|
|
585
|
-
|
|
586
|
-
//= export function is{X}<V>(v: V) {...}
|
|
587
|
-
file
|
|
588
|
-
.addFunction({
|
|
589
|
-
name: isX,
|
|
590
|
-
typeParameters: [{ name: `V` }],
|
|
591
|
-
parameters: [{ name: `v`, type: `V` }],
|
|
592
|
-
isExported: true,
|
|
593
|
-
})
|
|
594
|
-
.setBodyText(`return is$typed(v, id, ${hashVar})`)
|
|
595
|
-
|
|
596
|
-
const validateX = toCamelCase(`validate-${ifaceName}`)
|
|
597
|
-
|
|
598
|
-
//= export function validate{X}(v: unknown) {...}
|
|
599
|
-
file
|
|
600
|
-
.addFunction({
|
|
601
|
-
name: validateX,
|
|
602
|
-
typeParameters: [{ name: `V` }],
|
|
603
|
-
parameters: [{ name: `v`, type: `V` }],
|
|
604
|
-
isExported: true,
|
|
605
|
-
})
|
|
606
|
-
.setBodyText(
|
|
607
|
-
`return validate<${ifaceName} & V>(v, id, ${hashVar}${requireTypeProperty ? ', true' : ''})`,
|
|
608
|
-
)
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
export function stripScheme(uri: string): string {
|
|
612
|
-
if (uri.startsWith('lex:')) return uri.slice(4)
|
|
613
|
-
return uri
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
export function stripHash(uri: string): string {
|
|
617
|
-
return uri.split('#')[0] || ''
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
export function getHash(uri: string): string {
|
|
621
|
-
return uri.split('#').pop() || ''
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
export function ipldToType(def: LexCidLink | LexBytes) {
|
|
625
|
-
if (def.type === 'bytes') {
|
|
626
|
-
return 'Uint8Array'
|
|
627
|
-
}
|
|
628
|
-
return 'CID'
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
function refToUnionType(
|
|
632
|
-
ref: string,
|
|
633
|
-
lexUri: string,
|
|
634
|
-
imports: Set<string>,
|
|
635
|
-
): string {
|
|
636
|
-
const baseNsid = stripScheme(stripHash(lexUri))
|
|
637
|
-
return `$Typed<${refToType(ref, baseNsid, imports)}>`
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function refToType(
|
|
641
|
-
ref: string,
|
|
642
|
-
baseNsid: string,
|
|
643
|
-
imports: Set<string>,
|
|
644
|
-
): string {
|
|
645
|
-
// TODO: import external types!
|
|
646
|
-
let [refBase, refHash] = ref.split('#')
|
|
647
|
-
refBase = stripScheme(refBase)
|
|
648
|
-
if (!refHash) refHash = 'main'
|
|
649
|
-
|
|
650
|
-
// internal
|
|
651
|
-
if (!refBase || baseNsid === refBase) {
|
|
652
|
-
return toTitleCase(refHash)
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// external
|
|
656
|
-
imports.add(refBase)
|
|
657
|
-
return `${toTitleCase(refBase)}.${toTitleCase(refHash)}`
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
export function primitiveOrBlobToType(
|
|
661
|
-
def: LexBlob | LexPrimitive | LexIpldType,
|
|
662
|
-
): string {
|
|
663
|
-
switch (def.type) {
|
|
664
|
-
case 'blob':
|
|
665
|
-
return 'BlobRef'
|
|
666
|
-
case 'bytes':
|
|
667
|
-
return 'Uint8Array'
|
|
668
|
-
case 'cid-link':
|
|
669
|
-
return 'CID'
|
|
670
|
-
default:
|
|
671
|
-
return primitiveToType(def)
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
export function primitiveToType(def: LexPrimitive): string {
|
|
676
|
-
switch (def.type) {
|
|
677
|
-
case 'string':
|
|
678
|
-
if (def.knownValues?.length) {
|
|
679
|
-
return `${def.knownValues
|
|
680
|
-
.map((v) => JSON.stringify(v))
|
|
681
|
-
.join(' | ')} | (string & {})`
|
|
682
|
-
} else if (def.enum) {
|
|
683
|
-
return def.enum.map((v) => JSON.stringify(v)).join(' | ')
|
|
684
|
-
} else if (def.const) {
|
|
685
|
-
return JSON.stringify(def.const)
|
|
686
|
-
}
|
|
687
|
-
return 'string'
|
|
688
|
-
case 'integer':
|
|
689
|
-
if (def.enum) {
|
|
690
|
-
return def.enum.map((v) => JSON.stringify(v)).join(' | ')
|
|
691
|
-
} else if (def.const) {
|
|
692
|
-
return JSON.stringify(def.const)
|
|
693
|
-
}
|
|
694
|
-
return 'number'
|
|
695
|
-
case 'boolean':
|
|
696
|
-
if (def.const) {
|
|
697
|
-
return JSON.stringify(def.const)
|
|
698
|
-
}
|
|
699
|
-
return 'boolean'
|
|
700
|
-
case 'unknown':
|
|
701
|
-
// @TODO Should we use "object" here ?
|
|
702
|
-
// the "Record" identifier from typescript get overwritten by the Record
|
|
703
|
-
// interface created by lex-cli.
|
|
704
|
-
return '{ [_ in string]: unknown }' // Record<string, unknown>
|
|
705
|
-
default:
|
|
706
|
-
throw new Error(`Unexpected primitive type: ${JSON.stringify(def)}`)
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
function makeType(
|
|
711
|
-
_types: string | string[],
|
|
712
|
-
opts?: { array?: boolean; nullable?: boolean },
|
|
713
|
-
) {
|
|
714
|
-
const types = ([] as string[]).concat(_types)
|
|
715
|
-
if (opts?.nullable) types.push('null')
|
|
716
|
-
const arr = opts?.array ? '[]' : ''
|
|
717
|
-
if (types.length === 1) return `(${types[0]})${arr}`
|
|
718
|
-
if (arr) return `(${types.join(' | ')})${arr}`
|
|
719
|
-
return types.join(' | ')
|
|
720
|
-
}
|