@pikku/cli 0.7.0 → 0.7.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/CHANGELOG.md +9 -0
- package/bin/pikku-all.ts +203 -0
- package/bin/pikku-channels-map.ts +55 -0
- package/bin/pikku-channels.ts +63 -0
- package/bin/pikku-fetch.ts +55 -0
- package/bin/pikku-function-types.ts +84 -0
- package/bin/pikku-functions.ts +35 -0
- package/bin/pikku-http-map.ts +55 -0
- package/bin/pikku-http-routes.ts +63 -0
- package/bin/pikku-nextjs.test.ts +279 -0
- package/bin/pikku-nextjs.ts +152 -0
- package/bin/pikku-openapi.ts +74 -0
- package/bin/pikku-rpc.ts +22 -0
- package/bin/pikku-scheduler.ts +64 -0
- package/bin/pikku-schemas.ts +56 -0
- package/bin/pikku-websocket.ts +58 -0
- package/bin/pikku.ts +26 -0
- package/dist/bin/pikku-all.js +3 -0
- package/dist/bin/pikku-functions.d.ts +0 -2
- package/dist/bin/pikku-functions.js +2 -17
- package/dist/bin/pikku-rpc.d.ts +3 -0
- package/dist/bin/pikku-rpc.js +8 -0
- package/dist/bin/pikku.js +0 -0
- package/dist/src/pikku-cli-config.d.ts +1 -0
- package/dist/src/pikku-cli-config.js +3 -0
- package/dist/src/schema-generator.js +1 -2
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +3 -3
- package/src/inspector-glob.ts +28 -0
- package/src/openapi-spec-generator.ts +227 -0
- package/src/pikku-cli-config.ts +240 -0
- package/src/schema-generator.ts +136 -0
- package/{dist/src/events/http/serialize-fetch-wrapper.js → src/serialize-fetch-wrapper.ts} +4 -4
- package/src/serialize-import-map.ts +34 -0
- package/{dist/src/nextjs/serialize-nextjs-backend-wrapper.js → src/serialize-nextjs-backend-wrapper.ts} +11 -4
- package/{dist/src/nextjs/serialize-nextjs-http-wrapper.js → src/serialize-nextjs-http-wrapper.ts} +7 -4
- package/{dist/src/core/serialize-pikku-types.js → src/serialize-pikku-types.ts} +46 -35
- package/src/serialize-scheduler-meta.ts +18 -0
- package/src/serialize-typed-channel-map.ts +138 -0
- package/src/serialize-typed-function-map.ts +151 -0
- package/src/serialize-typed-http-map.ts +151 -0
- package/{dist/src/channels/serialize-websocket-wrapper.js → src/serialize-websocket-wrapper.ts} +4 -4
- package/src/utils.ts +284 -0
- package/tsconfig.json +21 -0
- package/dist/bin/pikku-http.d.ts +0 -5
- package/dist/bin/pikku-http.js +0 -27
- package/dist/bin/pikku-routes-map.d.ts +0 -5
- package/dist/bin/pikku-routes-map.js +0 -23
- package/dist/src/channels/serialize-channels.d.ts +0 -3
- package/dist/src/channels/serialize-channels.js +0 -19
- package/dist/src/channels/serialize-typed-channel-map.d.ts +0 -3
- package/dist/src/channels/serialize-typed-channel-map.js +0 -93
- package/dist/src/channels/serialize-websocket-wrapper.d.ts +0 -1
- package/dist/src/core/serialize-import-map.d.ts +0 -2
- package/dist/src/core/serialize-import-map.js +0 -24
- package/dist/src/core/serialize-pikku-types.d.ts +0 -4
- package/dist/src/events/channels/serialize-channels.d.ts +0 -3
- package/dist/src/events/channels/serialize-channels.js +0 -19
- package/dist/src/events/channels/serialize-typed-channel-map.d.ts +0 -3
- package/dist/src/events/channels/serialize-typed-channel-map.js +0 -90
- package/dist/src/events/channels/serialize-websocket-wrapper.d.ts +0 -1
- package/dist/src/events/channels/serialize-websocket-wrapper.js +0 -61
- package/dist/src/events/http/serialize-fetch-wrapper.d.ts +0 -1
- package/dist/src/events/http/serialize-route-imports.d.ts +0 -1
- package/dist/src/events/http/serialize-route-imports.js +0 -13
- package/dist/src/events/http/serialize-route-meta.d.ts +0 -2
- package/dist/src/events/http/serialize-route-meta.js +0 -6
- package/dist/src/events/http/serialize-typed-route-map.d.ts +0 -4
- package/dist/src/events/http/serialize-typed-route-map.js +0 -107
- package/dist/src/events/scheduler/serialize-schedulers.d.ts +0 -3
- package/dist/src/events/scheduler/serialize-schedulers.js +0 -23
- package/dist/src/http/serialize-fetch-wrapper.d.ts +0 -1
- package/dist/src/http/serialize-fetch-wrapper.js +0 -67
- package/dist/src/http/serialize-route-imports.d.ts +0 -1
- package/dist/src/http/serialize-route-imports.js +0 -13
- package/dist/src/http/serialize-route-meta.d.ts +0 -2
- package/dist/src/http/serialize-route-meta.js +0 -6
- package/dist/src/http/serialize-typed-route-map.d.ts +0 -4
- package/dist/src/http/serialize-typed-route-map.js +0 -107
- package/dist/src/nextjs/serialize-nextjs-backend-wrapper.d.ts +0 -1
- package/dist/src/nextjs/serialize-nextjs-http-wrapper.d.ts +0 -1
- package/dist/src/openapi/openapi-spec-generator.d.ts +0 -79
- package/dist/src/openapi/openapi-spec-generator.js +0 -136
- package/dist/src/scheduler/serialize-schedulers.d.ts +0 -3
- package/dist/src/scheduler/serialize-schedulers.js +0 -23
- package/dist/src/schema/schema-generator.d.ts +0 -5
- package/dist/src/schema/schema-generator.js +0 -89
- package/dist/src/serialize-typed-route-map.d.ts +0 -4
- package/dist/src/serialize-typed-route-map.js +0 -107
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import {
|
|
3
|
+
getFileImportRelativePath,
|
|
4
|
+
logCommandInfoAndTime,
|
|
5
|
+
logPikkuLogo,
|
|
6
|
+
PikkuCLIOptions,
|
|
7
|
+
writeFileInDir,
|
|
8
|
+
} from '../src/utils.js'
|
|
9
|
+
import { getPikkuCLIConfig, PikkuCLIConfig } from '../src/pikku-cli-config.js'
|
|
10
|
+
import { serializeWebsocketWrapper } from '../src/serialize-websocket-wrapper.js'
|
|
11
|
+
|
|
12
|
+
export const pikkuWebSocket = async ({
|
|
13
|
+
websocketFile,
|
|
14
|
+
channelsMapDeclarationFile,
|
|
15
|
+
packageMappings,
|
|
16
|
+
}: PikkuCLIConfig) => {
|
|
17
|
+
await logCommandInfoAndTime(
|
|
18
|
+
'Generating websocket wrapper',
|
|
19
|
+
'Generated websocket wrapper',
|
|
20
|
+
[
|
|
21
|
+
websocketFile === undefined,
|
|
22
|
+
"websocketFile isn't set in the pikku config",
|
|
23
|
+
],
|
|
24
|
+
async () => {
|
|
25
|
+
if (!websocketFile) {
|
|
26
|
+
throw new Error("fetchFile is isn't set in the pikku config")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const channelsMapDeclarationPath = getFileImportRelativePath(
|
|
30
|
+
websocketFile,
|
|
31
|
+
channelsMapDeclarationFile,
|
|
32
|
+
packageMappings
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const content = [serializeWebsocketWrapper(channelsMapDeclarationPath)]
|
|
36
|
+
await writeFileInDir(websocketFile, content.join('\n'))
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const action = async (options: PikkuCLIOptions): Promise<void> => {
|
|
42
|
+
logPikkuLogo()
|
|
43
|
+
const cliConfig = await getPikkuCLIConfig(
|
|
44
|
+
options.config,
|
|
45
|
+
['rootDir', 'schemaDirectory', 'configDir', 'fetchFile'],
|
|
46
|
+
options.tags,
|
|
47
|
+
true
|
|
48
|
+
)
|
|
49
|
+
await pikkuWebSocket(cliConfig)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const websocket = (program: Command): void => {
|
|
53
|
+
program
|
|
54
|
+
.command('websocket')
|
|
55
|
+
.description('generate websocket wrapper')
|
|
56
|
+
.option('-c | --config <string>', 'The path to pikku cli config file')
|
|
57
|
+
.action(action)
|
|
58
|
+
}
|
package/bin/pikku.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { schemas } from './pikku-schemas.js'
|
|
4
|
+
import { routes } from './pikku-http-routes.js'
|
|
5
|
+
import { nextjs } from './pikku-nextjs.js'
|
|
6
|
+
import { all } from './pikku-all.js'
|
|
7
|
+
import { functionTypes } from './pikku-function-types.js'
|
|
8
|
+
import { routesMap } from './pikku-http-map.js'
|
|
9
|
+
import { fetch } from './pikku-fetch.js'
|
|
10
|
+
import { channels } from './pikku-channels.js'
|
|
11
|
+
import { schedules } from './pikku-scheduler.js'
|
|
12
|
+
|
|
13
|
+
const program = new Command('pikku')
|
|
14
|
+
program.usage('[command]')
|
|
15
|
+
|
|
16
|
+
all(program)
|
|
17
|
+
routes(program)
|
|
18
|
+
routesMap(program)
|
|
19
|
+
functionTypes(program)
|
|
20
|
+
schemas(program)
|
|
21
|
+
nextjs(program)
|
|
22
|
+
fetch(program)
|
|
23
|
+
channels(program)
|
|
24
|
+
schedules(program)
|
|
25
|
+
|
|
26
|
+
program.parse(process.argv)
|
package/dist/bin/pikku-all.js
CHANGED
|
@@ -15,6 +15,7 @@ import { pikkuWebSocket } from './pikku-websocket.js';
|
|
|
15
15
|
import { inspectorGlob } from '../src/inspector-glob.js';
|
|
16
16
|
import chokidar from 'chokidar';
|
|
17
17
|
import { pikkuFunctions } from './pikku-functions.js';
|
|
18
|
+
import { pikkuRPC } from './pikku-rpc.js';
|
|
18
19
|
const runAll = async (cliConfig, options) => {
|
|
19
20
|
const metaImports = [];
|
|
20
21
|
const imports = [];
|
|
@@ -45,6 +46,8 @@ const runAll = async (cliConfig, options) => {
|
|
|
45
46
|
}
|
|
46
47
|
addImport(cliConfig.functionsMetaFile, 'meta');
|
|
47
48
|
addImport(cliConfig.functionsFile, 'events');
|
|
49
|
+
await pikkuRPC(cliConfig, visitState);
|
|
50
|
+
addImport(cliConfig.rpcFile, 'meta');
|
|
48
51
|
const routes = await pikkuHTTP(cliConfig, visitState);
|
|
49
52
|
if (routes) {
|
|
50
53
|
await pikkuHTTPMap(cliConfig, visitState);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
1
|
import { PikkuCLIConfig } from '../src/pikku-cli-config.js';
|
|
3
2
|
import { InspectorState } from '@pikku/inspector';
|
|
4
3
|
export declare const pikkuFunctions: (cliConfig: PikkuCLIConfig, visitState: InspectorState) => Promise<boolean>;
|
|
5
|
-
export declare const routes: (program: Command) => void;
|
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { logCommandInfoAndTime, logPikkuLogo, serializeFileImports, writeFileInDir, } from '../src/utils.js';
|
|
3
|
-
import { inspectorGlob } from '../src/inspector-glob.js';
|
|
1
|
+
import { logCommandInfoAndTime, serializeFileImports, writeFileInDir, } from '../src/utils.js';
|
|
4
2
|
export const pikkuFunctions = async (cliConfig, visitState) => {
|
|
5
3
|
return await logCommandInfoAndTime('Finding Pikku functions', 'Found Pikku functions', [visitState.functions.files.size === 0], async () => {
|
|
6
4
|
const { functionsFile, functionsMetaFile, packageMappings } = cliConfig;
|
|
7
5
|
const { functions } = visitState;
|
|
8
6
|
await writeFileInDir(functionsFile, serializeFileImports('addFunction', functionsFile, functions.files, packageMappings));
|
|
9
|
-
await writeFileInDir(functionsMetaFile, `import { pikkuState } from '@pikku/core'\npikkuState('
|
|
7
|
+
await writeFileInDir(functionsMetaFile, `import { pikkuState } from '@pikku/core'\npikkuState('function', 'meta', ${JSON.stringify(functions.meta, null, 2)})`);
|
|
10
8
|
});
|
|
11
9
|
};
|
|
12
|
-
async function action(cliOptions) {
|
|
13
|
-
logPikkuLogo();
|
|
14
|
-
const cliConfig = await getPikkuCLIConfig(cliOptions.config, ['rootDir', 'srcDirectories', 'functionsFile'], cliOptions.tags);
|
|
15
|
-
const visitState = await inspectorGlob(cliConfig.rootDir, cliConfig.srcDirectories, cliConfig.filters);
|
|
16
|
-
await pikkuFunctions(cliConfig, visitState);
|
|
17
|
-
}
|
|
18
|
-
export const routes = (program) => {
|
|
19
|
-
program
|
|
20
|
-
.command('functions')
|
|
21
|
-
.description('Find all functions to import')
|
|
22
|
-
.option('-c | --config <string>', 'The path to pikku cli config file')
|
|
23
|
-
.action(action);
|
|
24
|
-
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { logCommandInfoAndTime, writeFileInDir } from '../src/utils.js';
|
|
2
|
+
export const pikkuRPC = async (cliConfig, visitState) => {
|
|
3
|
+
return await logCommandInfoAndTime('Finding RPCs tasks', 'Found rpcs', [visitState.functions.files.size === 0], async () => {
|
|
4
|
+
const { rpcFile } = cliConfig;
|
|
5
|
+
const { rpc } = visitState;
|
|
6
|
+
await writeFileInDir(rpcFile, `import { pikkuState } from '@pikku/core'\npikkuState('rpc', 'meta', ${JSON.stringify(rpc.meta, null, 2)})`);
|
|
7
|
+
});
|
|
8
|
+
};
|
package/dist/bin/pikku.js
CHANGED
|
File without changes
|
|
@@ -62,6 +62,9 @@ const _getPikkuCLIConfig = async (configFile = undefined, requiredFields, tags =
|
|
|
62
62
|
if (!result.functionsMetaFile) {
|
|
63
63
|
result.functionsMetaFile = join(result.outDir, 'pikku-functions-meta.gen.ts');
|
|
64
64
|
}
|
|
65
|
+
if (!result.rpcFile) {
|
|
66
|
+
result.rpcFile = join(result.outDir, 'pikku-rpc.gen.ts');
|
|
67
|
+
}
|
|
65
68
|
if (!result.httpRoutesFile) {
|
|
66
69
|
result.httpRoutesFile = join(result.outDir, 'pikku-http-routes.gen.ts');
|
|
67
70
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../bin/pikku-all.ts","../bin/pikku-channels-map.ts","../bin/pikku-channels.ts","../bin/pikku-fetch.ts","../bin/pikku-function-types.ts","../bin/pikku-functions.ts","../bin/pikku-http-map.ts","../bin/pikku-http-routes.ts","../bin/pikku-nextjs.ts","../bin/pikku-openapi.ts","../bin/pikku-rpc.ts","../bin/pikku-scheduler.ts","../bin/pikku-schemas.ts","../bin/pikku-websocket.ts","../bin/pikku.ts","../src/inspector-glob.ts","../src/openapi-spec-generator.ts","../src/pikku-cli-config.ts","../src/schema-generator.ts","../src/serialize-fetch-wrapper.ts","../src/serialize-import-map.ts","../src/serialize-nextjs-backend-wrapper.ts","../src/serialize-nextjs-http-wrapper.ts","../src/serialize-pikku-types.ts","../src/serialize-scheduler-meta.ts","../src/serialize-typed-channel-map.ts","../src/serialize-typed-function-map.ts","../src/serialize-typed-http-map.ts","../src/serialize-websocket-wrapper.ts","../src/utils.ts"],"version":"5.7.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@openapi-contrib/json-schema-to-openapi-schema": "^3.0.2",
|
|
25
|
-
"@pikku/core": "^0.7.
|
|
26
|
-
"@pikku/inspector": "^0.7.
|
|
25
|
+
"@pikku/core": "^0.7.3",
|
|
26
|
+
"@pikku/inspector": "^0.7.3",
|
|
27
27
|
"@types/cookie": "^0.6.0",
|
|
28
28
|
"@types/uuid": "^10.0.0",
|
|
29
29
|
"chalk": "^5.4.1",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
import { glob } from 'tinyglobby'
|
|
3
|
+
import { InspectorFilters, InspectorState, inspect } from '@pikku/inspector'
|
|
4
|
+
import { logCommandInfoAndTime } from './utils.js'
|
|
5
|
+
|
|
6
|
+
export const inspectorGlob = async (
|
|
7
|
+
rootDir: string,
|
|
8
|
+
srcDirectories: string[],
|
|
9
|
+
filters: InspectorFilters
|
|
10
|
+
) => {
|
|
11
|
+
let result: InspectorState
|
|
12
|
+
await logCommandInfoAndTime(
|
|
13
|
+
'Inspecting codebase',
|
|
14
|
+
'Inspected codebase',
|
|
15
|
+
[false],
|
|
16
|
+
async () => {
|
|
17
|
+
const routeFiles = (
|
|
18
|
+
await Promise.all(
|
|
19
|
+
srcDirectories.map((dir) =>
|
|
20
|
+
glob(`${path.join(rootDir, dir)}/**/*.ts`)
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
).flat()
|
|
24
|
+
result = await inspect(routeFiles, filters)
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
return result!
|
|
28
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { HTTPRoutesMeta, pikkuState } from '@pikku/core'
|
|
2
|
+
import _convertSchema from '@openapi-contrib/json-schema-to-openapi-schema'
|
|
3
|
+
const convertSchema =
|
|
4
|
+
'default' in _convertSchema ? (_convertSchema.default as any) : _convertSchema
|
|
5
|
+
|
|
6
|
+
interface OpenAPISpec {
|
|
7
|
+
openapi: string
|
|
8
|
+
info: {
|
|
9
|
+
title: string
|
|
10
|
+
version: string
|
|
11
|
+
description?: string
|
|
12
|
+
termsOfService?: string
|
|
13
|
+
contact?: {
|
|
14
|
+
name?: string
|
|
15
|
+
url?: string
|
|
16
|
+
email?: string
|
|
17
|
+
}
|
|
18
|
+
license?: {
|
|
19
|
+
name: string
|
|
20
|
+
url?: string
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
servers: { url: string; description?: string }[]
|
|
24
|
+
paths: Record<string, any>
|
|
25
|
+
components: {
|
|
26
|
+
schemas: Record<string, any>
|
|
27
|
+
responses?: Record<string, any>
|
|
28
|
+
parameters?: Record<string, any>
|
|
29
|
+
examples?: Record<string, any>
|
|
30
|
+
requestBodies?: Record<string, any>
|
|
31
|
+
headers?: Record<string, any>
|
|
32
|
+
securitySchemes?: Record<string, any>
|
|
33
|
+
}
|
|
34
|
+
security?: { [key: string]: any[] }[]
|
|
35
|
+
tags?: { name: string; description?: string }[]
|
|
36
|
+
externalDocs?: {
|
|
37
|
+
description?: string
|
|
38
|
+
url: string
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface OpenAPISpecInfo {
|
|
43
|
+
info: {
|
|
44
|
+
title: string
|
|
45
|
+
version: string
|
|
46
|
+
description: string
|
|
47
|
+
termsOfService?: string
|
|
48
|
+
contact?: {
|
|
49
|
+
name?: string
|
|
50
|
+
url?: string
|
|
51
|
+
email?: string
|
|
52
|
+
}
|
|
53
|
+
license?: {
|
|
54
|
+
name: string
|
|
55
|
+
url?: string
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
servers: { url: string; description?: string }[]
|
|
59
|
+
tags?: { name: string; description?: string }[]
|
|
60
|
+
externalDocs?: {
|
|
61
|
+
description?: string
|
|
62
|
+
url: string
|
|
63
|
+
}
|
|
64
|
+
securitySchemes?: Record<string, any>
|
|
65
|
+
security?: { [key: string]: any[] }[]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const getErrorResponseForConstructorName = (constructorName: string) => {
|
|
69
|
+
const errors = Array.from(pikkuState('misc', 'errors').entries())
|
|
70
|
+
const foundError = errors.find(([e]) => e.name === constructorName)
|
|
71
|
+
if (foundError) {
|
|
72
|
+
return foundError[1]
|
|
73
|
+
}
|
|
74
|
+
return undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const convertSchemasToBodyPayloads = async (
|
|
78
|
+
routesMeta: HTTPRoutesMeta,
|
|
79
|
+
schemas: Record<string, any>
|
|
80
|
+
) => {
|
|
81
|
+
const requiredSchemas = new Set(
|
|
82
|
+
routesMeta
|
|
83
|
+
.map(({ inputTypes, output }) => [inputTypes?.body, output])
|
|
84
|
+
.flat()
|
|
85
|
+
.filter((schema) => !!schema)
|
|
86
|
+
)
|
|
87
|
+
const convertedEntries = await Promise.all(
|
|
88
|
+
Object.entries(schemas).map(async ([key, schema]) => {
|
|
89
|
+
if (requiredSchemas.has(key)) {
|
|
90
|
+
const convertedSchema = await convertSchema(schema, {
|
|
91
|
+
convertUnreferencedDefinitions: false,
|
|
92
|
+
dereference: { circular: 'ignore' },
|
|
93
|
+
})
|
|
94
|
+
return [key, convertedSchema]
|
|
95
|
+
}
|
|
96
|
+
return
|
|
97
|
+
})
|
|
98
|
+
)
|
|
99
|
+
return Object.fromEntries(convertedEntries.filter((s) => !!s))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function generateOpenAPISpec(
|
|
103
|
+
routeMeta: HTTPRoutesMeta,
|
|
104
|
+
schemas: Record<string, any>,
|
|
105
|
+
additionalInfo: OpenAPISpecInfo
|
|
106
|
+
): Promise<OpenAPISpec> {
|
|
107
|
+
const paths: Record<string, any> = {}
|
|
108
|
+
|
|
109
|
+
routeMeta.forEach((meta) => {
|
|
110
|
+
const { route, method, inputTypes, output, params, query, docs } = meta
|
|
111
|
+
const path = route.replace(/:(\w+)/g, '{$1}') // Convert ":param" to "{param}"
|
|
112
|
+
|
|
113
|
+
if (!paths[path]) {
|
|
114
|
+
paths[path] = {}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const responses = {}
|
|
118
|
+
docs?.errors?.forEach((error) => {
|
|
119
|
+
const errorResponse = getErrorResponseForConstructorName(error)
|
|
120
|
+
if (errorResponse) {
|
|
121
|
+
responses[errorResponse.status] = {
|
|
122
|
+
description: errorResponse.message,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const operation: any = {
|
|
128
|
+
description:
|
|
129
|
+
docs?.description ||
|
|
130
|
+
`This endpoint handles the ${method.toUpperCase()} request for the route ${route}.`,
|
|
131
|
+
tags: docs?.tags || [route.split('/')[1] || 'default'],
|
|
132
|
+
parameters: [],
|
|
133
|
+
responses: {
|
|
134
|
+
...responses,
|
|
135
|
+
'200': {
|
|
136
|
+
description: 'Successful response',
|
|
137
|
+
content: output
|
|
138
|
+
? {
|
|
139
|
+
'application/json': {
|
|
140
|
+
schema:
|
|
141
|
+
typeof output === 'string' &&
|
|
142
|
+
['boolean', 'string', 'number'].includes(output)
|
|
143
|
+
? { type: output }
|
|
144
|
+
: { $ref: `#/components/schemas/${output}` },
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
: undefined,
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const bodyType = inputTypes?.body
|
|
153
|
+
if (bodyType) {
|
|
154
|
+
operation.requestBody = {
|
|
155
|
+
required: true,
|
|
156
|
+
content: {
|
|
157
|
+
'application/json': {
|
|
158
|
+
schema:
|
|
159
|
+
typeof bodyType === 'string' &&
|
|
160
|
+
['boolean', 'string', 'number'].includes(bodyType)
|
|
161
|
+
? { type: bodyType }
|
|
162
|
+
: { $ref: `#/components/schemas/${bodyType}` },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (params) {
|
|
169
|
+
operation.parameters = params.map((param) => ({
|
|
170
|
+
name: param,
|
|
171
|
+
in: 'path',
|
|
172
|
+
required: true,
|
|
173
|
+
schema: { type: 'string' },
|
|
174
|
+
}))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (query) {
|
|
178
|
+
operation.parameters.push(
|
|
179
|
+
...query.map((query) => ({
|
|
180
|
+
name: query,
|
|
181
|
+
in: 'query',
|
|
182
|
+
required: false,
|
|
183
|
+
schema: { type: 'string' },
|
|
184
|
+
}))
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
paths[path][method] = operation
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
openapi: '3.1.0',
|
|
193
|
+
info: additionalInfo.info,
|
|
194
|
+
servers: additionalInfo.servers,
|
|
195
|
+
paths,
|
|
196
|
+
components: {
|
|
197
|
+
schemas: await convertSchemasToBodyPayloads(routeMeta, schemas),
|
|
198
|
+
responses: {},
|
|
199
|
+
parameters: {},
|
|
200
|
+
examples: {},
|
|
201
|
+
requestBodies: {},
|
|
202
|
+
headers: {},
|
|
203
|
+
securitySchemes: additionalInfo.securitySchemes || {
|
|
204
|
+
ApiKeyAuth: {
|
|
205
|
+
type: 'apiKey',
|
|
206
|
+
in: 'header',
|
|
207
|
+
name: 'x-api-key',
|
|
208
|
+
},
|
|
209
|
+
BearerAuth: {
|
|
210
|
+
type: 'http',
|
|
211
|
+
scheme: 'bearer',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
security: additionalInfo.security || [
|
|
216
|
+
{
|
|
217
|
+
ApiKeyAuth: [],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
BearerAuth: [],
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
tags: additionalInfo.tags,
|
|
224
|
+
externalDocs: additionalInfo.externalDocs,
|
|
225
|
+
// definitions
|
|
226
|
+
}
|
|
227
|
+
}
|