galbe 0.8.0 → 0.9.1
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/bin/commands/build.ts +42 -25
- package/bin/commands/dev.ts +8 -6
- package/bin/commands/generate/client.ts +26 -15
- package/bin/commands/generate/code/openapi.parser.ts +92 -57
- package/bin/commands/generate/code.ts +14 -13
- package/bin/commands/generate/spec.ts +2 -2
- package/bin/res/cli.template.js +13 -13
- package/bin/res/client.template.ts +26 -16
- package/bin/util.ts +11 -8
- package/bun.lock +904 -0
- package/docs/error-handler.md +24 -13
- package/docs/getting-started.md +72 -37
- package/docs/routes.md +3 -0
- package/package.json +2 -1
- package/src/extras/spec/openapi.serializer.ts +27 -21
- package/src/index.ts +44 -4
- package/src/parser.ts +18 -13
- package/src/routes.ts +96 -91
- package/src/server.ts +2 -2
- package/src/types.ts +32 -15
- package/src/util.ts +21 -7
- package/src/validator.ts +17 -16
- package/test/parser.test.ts +38 -38
- package/test/plugins.test.ts +1 -1
- package/test/requests.test.ts +8 -5
- package/test/responses.test.ts +3 -2
- package/test/routeFiles.test.ts +6 -6
- package/test/router.test.ts +2 -2
- package/tsconfig.json +1 -1
- package/bun.lockb +0 -0
package/bin/res/cli.template.js
CHANGED
|
@@ -31,17 +31,17 @@ const fmtRes = (r, p = false) => {
|
|
|
31
31
|
if (typeof r === 'object') {
|
|
32
32
|
try {
|
|
33
33
|
let resp = fmtObject(r, p, 2)
|
|
34
|
-
return
|
|
34
|
+
return Bun.write(Bun.stdout, `${resp}\n`)
|
|
35
35
|
} catch (err) {
|
|
36
36
|
console.error(err)
|
|
37
|
-
return
|
|
37
|
+
return Bun.write(Bun.stdout, `${r}\n`)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
if (!p) return
|
|
41
|
-
if (typeof r === 'boolean') return
|
|
42
|
-
if (typeof r === 'number') return
|
|
43
|
-
if (typeof r === 'string') return
|
|
44
|
-
return
|
|
40
|
+
if (!p) return Bun.write(Bun.stdout, `${r}\n`)
|
|
41
|
+
if (typeof r === 'boolean') return Bun.write(Bun.stdout, `${ansi(p, '38;2;255;128;0', r)}\n`)
|
|
42
|
+
if (typeof r === 'number') return Bun.write(Bun.stdout, `${ansi(p, '38;2;10;180;220', r)}\n`)
|
|
43
|
+
if (typeof r === 'string') return Bun.write(Bun.stdout, `${ansi(p, '38;2;125;170;0', r)}\n`)
|
|
44
|
+
return Bun.write(Bun.stdout, `${r}\n`)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const fetchApi = async (method, path, props) => {
|
|
@@ -84,9 +84,9 @@ const fetchApi = async (method, path, props) => {
|
|
|
84
84
|
let isJson = res.headers.get('content-type') === 'application/json'
|
|
85
85
|
if (isJson) fmtRes(await res.json(), format.has('p'))
|
|
86
86
|
else if (res.headers.get('content-type')?.match(/^text\//)) fmtRes(await res.text(), format.has('p'))
|
|
87
|
-
else
|
|
87
|
+
else Bun.write(Bun.stdout, await res.arrayBuffer())
|
|
88
88
|
}
|
|
89
|
-
if (format.has('t'))
|
|
89
|
+
if (format.has('t')) Bun.write(Bun.stdout, `${(endTime / 1_000_000).toFixed(2)}ms\n`)
|
|
90
90
|
process.exit(res.ok ? 0 : 1)
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -100,7 +100,7 @@ const formatDefault = def =>
|
|
|
100
100
|
: def ?? 'undefined';
|
|
101
101
|
|
|
102
102
|
result = commands.map(c=>{
|
|
103
|
-
let args = c.arguments.map(a=>`.argument("${a.name}", "${a.description || a.name+' argument' || ''}")`)
|
|
103
|
+
let args = c.arguments.map(a=>`.argument("${a.name}", "${JSON.stringify(a.description).slice(1,-1) || a.name+' argument' || ''}")`)
|
|
104
104
|
let optionsBase = [
|
|
105
105
|
{name: '%format', short:'%f', type: '[string]', description: 'response format [\'s\',\'h\',\'b\',\'t\',\'p\']', default:["s","b","p"]},
|
|
106
106
|
{name: '%header', short:'%h', type: '<string...>', description: 'request header formated as headerName=headerValue', default:[]},
|
|
@@ -108,12 +108,12 @@ result = commands.map(c=>{
|
|
|
108
108
|
{name: '%body', short:'%b', type: '<string>', description: 'request body', default:''},
|
|
109
109
|
{name: '%bodyFile', short:'%bf', type: '<path>', description: 'request body file', default:''}
|
|
110
110
|
]
|
|
111
|
-
let options = [...optionsBase,...(c.options||[])].map(o=>`.addOption(new Option("-${o.short}, --${o.name} ${o.type}", "${o.description}").default(${formatDefault(o.default)}))`)
|
|
112
|
-
let action = `.action(async (${c.arguments.map(a=>`${a.name},`)} props) => {
|
|
111
|
+
let options = [...optionsBase,...(c.options||[])].map(o=>`.addOption(new Option("-${o.short}, --${o.name} ${o.type}", "${JSON.stringify(o.description).slice(1,-1)}").default(${formatDefault(o.default)}))`)
|
|
112
|
+
let action = `.action(async (${c.arguments.map(a=>`${a.name},`).join('')} props) => {
|
|
113
113
|
${c.action ? ';('+c.action.toString()+')(props)' : ''}
|
|
114
114
|
return await fetchApi("${c.route.method.toUpperCase()}",\`${c.route.pathT}\`, props)
|
|
115
115
|
})`
|
|
116
|
-
return `program.command("${c.name}").description("${c.description}")${args.join('')}${options.join('')}${action}`
|
|
116
|
+
return `program.command("${c.name}").description("${JSON.stringify(c.description).slice(1,-1)}")${args.join('')}${options.join('')}${action}`
|
|
117
117
|
})
|
|
118
118
|
%*/
|
|
119
119
|
|
|
@@ -6,8 +6,8 @@ export type GalbeClientConfig = {
|
|
|
6
6
|
export const Kind = Symbol.for('json.string')
|
|
7
7
|
type Json<T> = { T: T }
|
|
8
8
|
|
|
9
|
-
interface GR<S extends number =
|
|
10
|
-
status: S
|
|
9
|
+
interface GR<S extends number | 'default' = 'default', B = any, OKS extends number = OKStatusCode> {
|
|
10
|
+
status: Exclude<S, "default">
|
|
11
11
|
ok: S extends OKS ? true : false
|
|
12
12
|
redirected: boolean
|
|
13
13
|
statusText: string
|
|
@@ -18,16 +18,16 @@ interface GR<S extends number = number, B = any, OKS extends number = OKStatusCo
|
|
|
18
18
|
stream?: ST
|
|
19
19
|
) => B extends Uint8Array
|
|
20
20
|
? ST extends true
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
? Promise<AsyncGenerator<Uint8Array, void, unknown>>
|
|
22
|
+
: B extends Json<infer T>
|
|
23
|
+
? Promise<T>
|
|
24
|
+
: Promise<B>
|
|
25
25
|
: B extends string
|
|
26
26
|
? ST extends true
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
? Promise<AsyncGenerator<string, void, unknown>>
|
|
28
|
+
: B extends Json<infer T>
|
|
29
|
+
? Promise<T>
|
|
30
|
+
: Promise<B>
|
|
31
31
|
: B extends Json<infer T>
|
|
32
32
|
? Promise<T>
|
|
33
33
|
: Promise<B>
|
|
@@ -35,9 +35,9 @@ interface GR<S extends number = number, B = any, OKS extends number = OKStatusCo
|
|
|
35
35
|
|
|
36
36
|
export type OKStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226
|
|
37
37
|
// prettier-ignore
|
|
38
|
-
export type HttpStatusCode = 100|101|102|103|OKStatusCode|300|301|302|303|304|305|307|308|400|401|402|403|404|405|406|407|408|409|410|411|412|413|414|415|416|417|418|421|422|423|424|426|428|429|431|451|500|501|502|503|504|505|506|507|508|510|511
|
|
38
|
+
export type HttpStatusCode = 100 | 101 | 102 | 103 | OKStatusCode | 300 | 301 | 302 | 303 | 304 | 305 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511
|
|
39
39
|
type PGR<
|
|
40
|
-
S extends number =
|
|
40
|
+
S extends number | 'default' = 'default',
|
|
41
41
|
B = any,
|
|
42
42
|
O extends number = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226
|
|
43
43
|
> = Promise<GR<S, B, O>>
|
|
@@ -54,9 +54,15 @@ const DEFAULT_HEADERS = {
|
|
|
54
54
|
'user-agent': 'Galbe//*%(()=>version)()%*/'
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Typescript types
|
|
58
|
+
/*%
|
|
59
|
+
Object.entries(types).map(([tk, t])=>{
|
|
60
|
+
return `export type ${tk} = ${t}`
|
|
61
|
+
})
|
|
62
|
+
%*/
|
|
63
|
+
|
|
57
64
|
export default class GalbeClient {
|
|
58
65
|
config?: GalbeClientConfig
|
|
59
|
-
|
|
60
66
|
/*%
|
|
61
67
|
Object.entries(routes).map(([method, list])=>{
|
|
62
68
|
return`${method} = {\n${list.map( r => {
|
|
@@ -66,7 +72,7 @@ export default class GalbeClient {
|
|
|
66
72
|
''
|
|
67
73
|
let oks = Object.keys(r.schemas?.response||{}).filter(s=>s>=200&&s<300)
|
|
68
74
|
let responses = Object.keys(r.schemas?.response||{}).length ?
|
|
69
|
-
`${Object.entries(r.schemas.response).map(([k,v])=>`PGR<${k},${v}${oks?.length?`,${oks.join('|')}`:''}>`).join('|')}|PGR<Exclude<HttpStatusCode,${Object.keys(r.schemas.response).join('|')}
|
|
75
|
+
`${Object.entries(r.schemas.response).filter(([s,_])=>s!=='"default"').map(([k,v])=>`PGR<${k},${v}${oks?.length?`,${oks.join('|')}`:''}>`).join('|')}|PGR<Exclude<HttpStatusCode,${Object.keys(r.schemas.response).filter(s=>s!=='"default"').join('|')}>,${'"default"' in r.schemas.response ? r.schemas.response['"default"'] : 'any'}${oks?.length?`,${oks.join('|')}`:''}>`:
|
|
70
76
|
`PGR<HttpStatusCode,any${oks?.length?`,${oks.join('|')}`:',any'}>`
|
|
71
77
|
return ` '${r.path}':(${p.length?p.map(([k,v])=>`${k}:${v.type}`).join(',')+', ':''}options:RequestOptions${schemas}={})=>this.fetch(\`${r.pathT}\`,{...options,method:'${r.method.toUpperCase()}'}) as ${responses}`
|
|
72
78
|
}).join(',\n')}\n}`
|
|
@@ -155,9 +161,13 @@ export default class GalbeClient {
|
|
|
155
161
|
''
|
|
156
162
|
let oks = Object.keys(r.schemas?.response||{}).filter(s=>s>=200&&s<300)
|
|
157
163
|
let responses = Object.keys(r.schemas?.response||{}).length ?
|
|
158
|
-
`${Object.entries(r.schemas.response).map(([k,v])=>`PGR<${k},${v}${oks?.length?`,${oks.join('|')}`:''}>`).join('|')}|PGR<Exclude<HttpStatusCode,${Object.keys(r.schemas.response).join('|')}
|
|
164
|
+
`${Object.entries(r.schemas.response).filter(([s,_])=>s!=='"default"').map(([k,v])=>`PGR<${k},${v}${oks?.length?`,${oks.join('|')}`:''}>`).join('|')}|PGR<Exclude<HttpStatusCode,${Object.keys(r.schemas.response).filter(s=>s!=='"default"').join('|')}>,${'"default"' in r.schemas.response ? r.schemas.response['"default"'] : 'any'}${oks?.length?`,${oks.join('|')}`:''}>`:
|
|
159
165
|
`PGR<HttpStatusCode,any${oks?.length?`,${oks.join('|')}`:',any'}>`
|
|
160
|
-
|
|
166
|
+
let summary = r.summary ? ` * ${r.summary || ''}\n *\n` : ''
|
|
167
|
+
let description = r.description ? ` * ${r.description.replace(/\n/g,'\n * ')}` : ''
|
|
168
|
+
let params = Object.entries(r.schema.params || {}).map( ([k,v])=>`\n * @param ${k} - ${v.description?.replace(/\n/g,'\n ')}` ).join('')
|
|
169
|
+
let query = Object.entries(r.schema.query || {}).map( ([k,v])=>`\n * @param options.query.${k} - ${v.description?.replace(/\n/g,'\n ')}` ).join('')
|
|
170
|
+
return `/**\n${summary}${description}\n *${params}${query}\n *\/\n ${r.alias}(${p.length ? p.map(([k,v])=>`${k}: ${v.type}`).join(', ')+', ':''}options: RequestOptions${schemas} = {}){return this.fetch(\`${r.pathT}\`, {...options, method: '${r.method.toUpperCase()}'}) as ${responses}}\n`
|
|
161
171
|
}).join(' ')
|
|
162
172
|
})
|
|
163
173
|
%*/
|
package/bin/util.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { relative } from 'path'
|
|
|
2
2
|
import { watch } from 'chokidar'
|
|
3
3
|
import { Galbe, Route } from '../src'
|
|
4
4
|
import { logRoute, walkRoutes } from '../src/util'
|
|
5
|
-
import { RouteMeta, defineRoutes } from '../src/routes'
|
|
5
|
+
import { GalbeProxy, RouteMeta, defineRoutes } from '../src/routes'
|
|
6
6
|
|
|
7
7
|
export { default as pckg } from '../package.json'
|
|
8
8
|
|
|
@@ -22,7 +22,7 @@ export const silentExec = async (fn: () => any) => {
|
|
|
22
22
|
let consoleMock = Object.fromEntries(
|
|
23
23
|
Object.entries(console)
|
|
24
24
|
.filter(([_, v]) => typeof v === 'function')
|
|
25
|
-
.map(([k, _]) => [k, () => {}])
|
|
25
|
+
.map(([k, _]) => [k, () => { }])
|
|
26
26
|
)
|
|
27
27
|
const _console = console
|
|
28
28
|
const _processStdoutWrite = process.stdout.write
|
|
@@ -30,9 +30,9 @@ export const silentExec = async (fn: () => any) => {
|
|
|
30
30
|
//@ts-ignore
|
|
31
31
|
global.console = consoleMock
|
|
32
32
|
//@ts-ignore
|
|
33
|
-
process.stdout.write = function () {}
|
|
33
|
+
process.stdout.write = function () { }
|
|
34
34
|
//@ts-ignore
|
|
35
|
-
process.stderr.write = function () {}
|
|
35
|
+
process.stderr.write = function () { }
|
|
36
36
|
const r = await fn()
|
|
37
37
|
global.console = _console
|
|
38
38
|
process.stdout.write = _processStdoutWrite
|
|
@@ -65,29 +65,32 @@ export const instanciateRoutes = async (g: Galbe) => {
|
|
|
65
65
|
hasMainRoutes = true
|
|
66
66
|
logRoute(r)
|
|
67
67
|
})
|
|
68
|
-
if (hasMainRoutes)
|
|
68
|
+
if (hasMainRoutes) Bun.write(Bun.stdout, '\n')
|
|
69
69
|
// Route Files Analysis
|
|
70
70
|
let routes: Record<string, { route?: Route; meta?: RouteMeta; error?: any }[]> = {}
|
|
71
71
|
let errors: Record<string, any> = {}
|
|
72
|
-
|
|
72
|
+
|
|
73
|
+
const proxy = new GalbeProxy(g, ({ type, route, error, filepath, meta }) => {
|
|
74
|
+
if (meta?.ignore || meta?.hide) return
|
|
73
75
|
if (!filepath) return
|
|
74
76
|
if (!(filepath in routes)) routes[filepath] = []
|
|
75
77
|
if (type === 'add' && route && filepath) routes[filepath].push({ route, meta })
|
|
76
78
|
if (type === 'error') errors[filepath] = error
|
|
77
79
|
})
|
|
80
|
+
await defineRoutes({ routes: g?.config?.routes }, proxy)
|
|
78
81
|
for (let [fp, e] of Object.entries(routes)) {
|
|
79
82
|
console.log(`\x1b\[0;36m ${relative(CWD, fp)}\x1b[0m`)
|
|
80
83
|
let maxPathLength = e.reduce((p, c) => {
|
|
81
84
|
return Math.max(p, c.route?.path.length || 0)
|
|
82
85
|
}, 0)
|
|
83
86
|
for (let r of e) {
|
|
84
|
-
if (r.route) logRoute(r.route, r.meta, { maxPathLength })
|
|
87
|
+
if (r.route && !r.meta?.ignore && !r.meta?.hide) logRoute(r.route, r.meta, { maxPathLength })
|
|
85
88
|
}
|
|
86
89
|
if (errors?.[fp]) {
|
|
87
90
|
console.log(`\x1b\[0;31m Error:\x1b[0m`)
|
|
88
91
|
console.log(errors?.[fp])
|
|
89
92
|
}
|
|
90
|
-
|
|
93
|
+
console.log("")
|
|
91
94
|
}
|
|
92
95
|
console.log('\x1b[1;30m\x1b[32mdone\x1b[0m\n')
|
|
93
96
|
}
|