@pikku/cli 0.7.0 → 0.7.2
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 +18 -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 +56 -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 +76 -0
- package/bin/pikku-rpc.ts +22 -0
- package/bin/pikku-scheduler.ts +64 -0
- package/bin/pikku-schemas.ts +57 -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-http-map.js +1 -1
- package/dist/bin/pikku-openapi.js +2 -2
- package/dist/bin/pikku-rpc.d.ts +3 -0
- package/dist/bin/pikku-rpc.js +8 -0
- package/dist/bin/pikku-schemas.js +2 -2
- package/dist/bin/pikku.js +0 -0
- package/dist/src/openapi-spec-generator.d.ts +2 -2
- package/dist/src/openapi-spec-generator.js +14 -5
- package/dist/src/pikku-cli-config.d.ts +1 -0
- package/dist/src/pikku-cli-config.js +3 -0
- package/dist/src/schema-generator.d.ts +3 -3
- package/dist/src/schema-generator.js +15 -34
- package/dist/src/serialize-typed-function-map.d.ts +2 -1
- package/dist/src/serialize-typed-function-map.js +6 -4
- package/dist/src/serialize-typed-http-map.d.ts +2 -1
- package/dist/src/serialize-typed-http-map.js +10 -4
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +3 -3
- package/src/inspector-glob.ts +28 -0
- package/src/openapi-spec-generator.ts +246 -0
- package/src/pikku-cli-config.ts +240 -0
- package/src/schema-generator.ts +118 -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 +161 -0
- package/src/serialize-typed-http-map.ts +167 -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
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
*
|
|
3
3
|
*/
|
|
4
|
-
export const serializePikkuTypes = (
|
|
5
|
-
|
|
4
|
+
export const serializePikkuTypes = (
|
|
5
|
+
userSessionTypeImport: string,
|
|
6
|
+
userSessionTypeName: string,
|
|
7
|
+
singletonServicesTypeImport: string,
|
|
8
|
+
singletonServicesTypeName: string,
|
|
9
|
+
sessionServicesTypeImport: string,
|
|
10
|
+
servicesTypeName: string
|
|
11
|
+
) => {
|
|
12
|
+
return `/**
|
|
6
13
|
* This is used to provide the application types in the typescript project
|
|
7
14
|
*/
|
|
8
15
|
|
|
9
|
-
import {
|
|
16
|
+
import { CoreAPIPermission, PikkuMiddleware } from '@pikku/core'
|
|
10
17
|
import { CoreAPIFunction, CoreAPIFunctionSessionless } from '@pikku/core/function'
|
|
11
|
-
import { CoreHTTPFunctionRoute, AssertRouteParams,
|
|
18
|
+
import { CoreHTTPFunctionRoute, AssertRouteParams, addHTTPRoute as addCoreHTTPRoute } from '@pikku/core/http'
|
|
12
19
|
import { CoreScheduledTask, addScheduledTask as addCoreScheduledTask } from '@pikku/core/scheduler'
|
|
13
20
|
import { CoreAPIChannel, PikkuChannel, addChannel as addCoreChannel } from '@pikku/core/channel'
|
|
14
21
|
|
|
@@ -19,14 +26,30 @@ ${sessionServicesTypeImport}
|
|
|
19
26
|
export type APIPermission<In = unknown, RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = CoreAPIPermission<In, RequiredServices, ${userSessionTypeName}>
|
|
20
27
|
export type APIMiddleware<RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = PikkuMiddleware<RequiredServices, ${userSessionTypeName}>
|
|
21
28
|
|
|
22
|
-
type APIFunctionSessionless<
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
type APIFunctionSessionless<
|
|
30
|
+
In = unknown,
|
|
31
|
+
Out = never,
|
|
32
|
+
ChannelData = null, // null means optional channel
|
|
33
|
+
RequiredServices extends Services = Services & (
|
|
34
|
+
[ChannelData] extends [null]
|
|
35
|
+
? { channel?: PikkuChannel<unknown, Out> } // Optional channel
|
|
36
|
+
: { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
|
|
37
|
+
)
|
|
38
|
+
> = CoreAPIFunctionSessionless<In, Out, ChannelData, RequiredServices, ${userSessionTypeName}>
|
|
25
39
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
type APIFunction<
|
|
41
|
+
In = unknown,
|
|
42
|
+
Out = never,
|
|
43
|
+
ChannelData = null, // null means optional channel
|
|
44
|
+
RequiredServices extends Services = Services & (
|
|
45
|
+
[ChannelData] extends [null]
|
|
46
|
+
? { channel?: PikkuChannel<unknown, Out> } // Optional channel
|
|
47
|
+
: { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
|
|
48
|
+
)
|
|
49
|
+
> = CoreAPIFunction<In, Out, ChannelData, RequiredServices, ${userSessionTypeName}>
|
|
29
50
|
|
|
51
|
+
type APIRoute<In, Out, Route extends string> = CoreHTTPFunctionRoute<In, Out, Route, APIFunction<In, Out>, APIFunctionSessionless<In, Out>, APIPermission<In>, APIMiddleware>
|
|
52
|
+
type APIChannel<ChannelData, Channel extends string> = CoreAPIChannel<ChannelData, Channel, APIFunction<void, unknown> | APIFunction<void, unknown, ChannelData>, APIFunction<void, void> | APIFunction<void, void, ChannelData>, APIFunction<any, any> | APIFunction<any, any, ChannelData>, APIPermission>
|
|
30
53
|
type ScheduledTask = CoreScheduledTask<APIFunctionSessionless<void, void>, ${userSessionTypeName}>
|
|
31
54
|
|
|
32
55
|
export const pikkuFunc = <In, Out = unknown>(
|
|
@@ -57,45 +80,33 @@ export const pikkuSessionlessFunc = <In, Out = unknown>(
|
|
|
57
80
|
return typeof func === 'function' ? func : func.func
|
|
58
81
|
}
|
|
59
82
|
|
|
60
|
-
export const
|
|
83
|
+
export const pikkuChannelConnectionFunc = <Out = unknown, ChannelData = unknown>(
|
|
61
84
|
func:
|
|
62
|
-
|
|
|
63
|
-
| {
|
|
64
|
-
func: ChannelConnection<In, Out>
|
|
65
|
-
auth?: true
|
|
66
|
-
name?: string
|
|
67
|
-
}
|
|
85
|
+
| APIFunctionSessionless<void, Out, ChannelData>
|
|
68
86
|
| {
|
|
69
|
-
func:
|
|
70
|
-
auth: false
|
|
87
|
+
func: APIFunctionSessionless<void, Out, ChannelData>
|
|
71
88
|
name?: string
|
|
72
89
|
}
|
|
73
90
|
) => {
|
|
74
91
|
return typeof func === 'function' ? func : func.func
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
export const
|
|
94
|
+
export const pikkuChannelDisconnectionFunc = <ChannelData = unknown>(
|
|
78
95
|
func:
|
|
79
|
-
|
|
|
96
|
+
| APIFunctionSessionless<void, void, ChannelData>
|
|
80
97
|
| {
|
|
81
|
-
func:
|
|
82
|
-
auth?: true
|
|
83
|
-
name?: string
|
|
84
|
-
}
|
|
85
|
-
| {
|
|
86
|
-
func: ChannelDisconnection<In>
|
|
87
|
-
auth: false
|
|
98
|
+
func: APIFunction<void, void, ChannelData>
|
|
88
99
|
name?: string
|
|
89
100
|
}
|
|
90
101
|
) => {
|
|
91
102
|
return typeof func === 'function' ? func : func.func
|
|
92
103
|
}
|
|
93
104
|
|
|
94
|
-
export const pikkuChannelFunc = <In, Out = unknown>(
|
|
105
|
+
export const pikkuChannelFunc = <In = unknown, Out = unknown, ChannelData = unknown>(
|
|
95
106
|
func:
|
|
96
|
-
| APIFunctionSessionless<In, Out,
|
|
107
|
+
| APIFunctionSessionless<In, Out, ChannelData>
|
|
97
108
|
| {
|
|
98
|
-
func: APIFunctionSessionless<In, Out,
|
|
109
|
+
func: APIFunctionSessionless<In, Out, ChannelData>
|
|
99
110
|
name?: string
|
|
100
111
|
}
|
|
101
112
|
) => {
|
|
@@ -119,14 +130,14 @@ export const addChannel = <ChannelData, Channel extends string>(
|
|
|
119
130
|
addCoreChannel(channel as any) // TODO
|
|
120
131
|
}
|
|
121
132
|
|
|
122
|
-
export const
|
|
133
|
+
export const addHTTPRoute = <In, Out, Route extends string>(
|
|
123
134
|
route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
|
|
124
135
|
) => {
|
|
125
|
-
|
|
136
|
+
addCoreHTTPRoute(route)
|
|
126
137
|
}
|
|
127
138
|
|
|
128
139
|
export const addScheduledTask = (task: ScheduledTask) => {
|
|
129
140
|
addCoreScheduledTask(task as any) // TODO
|
|
130
141
|
}
|
|
131
|
-
|
|
132
|
-
}
|
|
142
|
+
`
|
|
143
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ScheduledTasksMeta } from '@pikku/core/scheduler'
|
|
2
|
+
|
|
3
|
+
export const serializeSchedulerMeta = (
|
|
4
|
+
scheduledTasksMeta: ScheduledTasksMeta
|
|
5
|
+
) => {
|
|
6
|
+
const serializedOutput: string[] = []
|
|
7
|
+
serializedOutput.push("import { pikkuState } from '@pikku/core'")
|
|
8
|
+
serializedOutput.push(
|
|
9
|
+
`pikkuState('scheduler', 'meta', ${JSON.stringify(scheduledTasksMeta, null, 2)})`
|
|
10
|
+
)
|
|
11
|
+
const scheduledTasksMetaValues = Object.values(scheduledTasksMeta)
|
|
12
|
+
if (scheduledTasksMetaValues.length > 0) {
|
|
13
|
+
serializedOutput.push(
|
|
14
|
+
`export type ScheduledTaskNames = '${scheduledTasksMetaValues.map((s) => s.name).join("' | '")}'`
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
return serializedOutput.join('\n')
|
|
18
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ChannelsMeta } from '@pikku/core/channel'
|
|
2
|
+
import { serializeImportMap } from './serialize-import-map.js'
|
|
3
|
+
import { TypesMap } from '@pikku/inspector'
|
|
4
|
+
import { generateCustomTypes } from './serialize-typed-http-map.js'
|
|
5
|
+
|
|
6
|
+
export const serializeTypedChannelsMap = (
|
|
7
|
+
relativeToPath: string,
|
|
8
|
+
packageMappings: Record<string, string>,
|
|
9
|
+
typesMap: TypesMap,
|
|
10
|
+
channelsMeta: ChannelsMeta
|
|
11
|
+
): string => {
|
|
12
|
+
const { channels, requiredTypes } = generateChannels(channelsMeta)
|
|
13
|
+
typesMap.customTypes.forEach(({ references }) => {
|
|
14
|
+
for (const reference of references) {
|
|
15
|
+
requiredTypes.add(reference)
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
const imports = serializeImportMap(
|
|
19
|
+
relativeToPath,
|
|
20
|
+
packageMappings,
|
|
21
|
+
typesMap,
|
|
22
|
+
requiredTypes
|
|
23
|
+
)
|
|
24
|
+
const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes)
|
|
25
|
+
return `/**
|
|
26
|
+
* This provides the structure needed for TypeScript to be aware of channels
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
${imports}
|
|
30
|
+
${serializedCustomTypes}
|
|
31
|
+
|
|
32
|
+
interface ChannelHandler<I, O> {
|
|
33
|
+
input: I;
|
|
34
|
+
output: O;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
${channels}
|
|
38
|
+
|
|
39
|
+
export type ChannelDefaultHandlerOf<Channel extends keyof ChannelsMap> =
|
|
40
|
+
ChannelsMap[Channel]['defaultMessage'] extends { input: infer I; output: infer O }
|
|
41
|
+
? ChannelHandler<I, O>
|
|
42
|
+
: never;
|
|
43
|
+
|
|
44
|
+
export type ChannelRouteHandlerOf<
|
|
45
|
+
Channel extends keyof ChannelsMap,
|
|
46
|
+
Route extends keyof ChannelsMap[Channel]['routes'],
|
|
47
|
+
Method extends keyof ChannelsMap[Channel]['routes'][Route],
|
|
48
|
+
> =
|
|
49
|
+
ChannelsMap[Channel]['routes'][Route][Method] extends { input: infer I; output: infer O }
|
|
50
|
+
? ChannelHandler<I, O>
|
|
51
|
+
: never;
|
|
52
|
+
`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function generateChannels(channelsMeta: ChannelsMeta) {
|
|
56
|
+
const requiredTypes = new Set<string>()
|
|
57
|
+
const channelsObject: Record<
|
|
58
|
+
string,
|
|
59
|
+
{
|
|
60
|
+
message: { inputs: string[] | null; outputs: string[] | null } | null
|
|
61
|
+
routes: Record<
|
|
62
|
+
string,
|
|
63
|
+
Record<
|
|
64
|
+
string,
|
|
65
|
+
{
|
|
66
|
+
inputTypes: string[] | null
|
|
67
|
+
outputTypes: string[] | null
|
|
68
|
+
}
|
|
69
|
+
>
|
|
70
|
+
>
|
|
71
|
+
}
|
|
72
|
+
> = {}
|
|
73
|
+
|
|
74
|
+
for (const meta of Object.values(channelsMeta)) {
|
|
75
|
+
const { name, messageRoutes, message } = meta
|
|
76
|
+
|
|
77
|
+
if (!channelsObject[name]) {
|
|
78
|
+
channelsObject[name] = { message, routes: {} }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const [key, route] of Object.entries(messageRoutes)) {
|
|
82
|
+
if (!channelsObject[name].routes[key]) {
|
|
83
|
+
channelsObject[name].routes[key] = {}
|
|
84
|
+
}
|
|
85
|
+
for (const [method, { inputs, outputs }] of Object.entries(route)) {
|
|
86
|
+
const inputTypes = inputs || null
|
|
87
|
+
const outputTypes = outputs || null
|
|
88
|
+
channelsObject[name].routes[key][method] = {
|
|
89
|
+
inputTypes,
|
|
90
|
+
outputTypes,
|
|
91
|
+
}
|
|
92
|
+
inputTypes?.forEach((type) => requiredTypes.add(type))
|
|
93
|
+
outputTypes?.forEach((type) => requiredTypes.add(type))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let routesStr = 'export type ChannelsMap = {\n'
|
|
99
|
+
|
|
100
|
+
for (const [channelPath, { routes, message }] of Object.entries(
|
|
101
|
+
channelsObject
|
|
102
|
+
)) {
|
|
103
|
+
routesStr += ` readonly '${channelPath}': {\n`
|
|
104
|
+
|
|
105
|
+
// Add `routes` object
|
|
106
|
+
routesStr += ` readonly routes: {\n`
|
|
107
|
+
for (const [key, methods] of Object.entries(routes)) {
|
|
108
|
+
routesStr += ` readonly ${key}: {\n`
|
|
109
|
+
for (const [method, handler] of Object.entries(methods)) {
|
|
110
|
+
routesStr += ` readonly ${method}: ChannelHandler<${
|
|
111
|
+
formatTypeArray(handler.inputTypes) || 'void'
|
|
112
|
+
}, ${formatTypeArray(handler.outputTypes) || 'never'}>,\n`
|
|
113
|
+
}
|
|
114
|
+
routesStr += ' },\n'
|
|
115
|
+
}
|
|
116
|
+
routesStr += ' },\n'
|
|
117
|
+
|
|
118
|
+
// Add `defaultMessage` outside `routes`
|
|
119
|
+
if (message) {
|
|
120
|
+
routesStr += ` readonly defaultMessage: ChannelHandler<${formatTypeArray(
|
|
121
|
+
message.inputs
|
|
122
|
+
)}, ${formatTypeArray(message.outputs)}>,\n`
|
|
123
|
+
} else {
|
|
124
|
+
routesStr += ` readonly defaultMessage: never,\n`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
routesStr += ' },\n'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
routesStr += '};'
|
|
131
|
+
|
|
132
|
+
return { channels: routesStr, requiredTypes }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Utility to format type arrays
|
|
136
|
+
function formatTypeArray(types: string[] | null): string {
|
|
137
|
+
return types ? types.join(' | ') : 'null'
|
|
138
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { HTTPRoutesMeta } from '@pikku/core/http'
|
|
2
|
+
import { serializeImportMap } from './serialize-import-map.js'
|
|
3
|
+
import { MetaInputTypes, TypesMap } from '@pikku/inspector'
|
|
4
|
+
import { FunctionsMeta } from '@pikku/core'
|
|
5
|
+
|
|
6
|
+
export const serializeTypedRoutesMap = (
|
|
7
|
+
relativeToPath: string,
|
|
8
|
+
packageMappings: Record<string, string>,
|
|
9
|
+
typesMap: TypesMap,
|
|
10
|
+
functionsMeta: FunctionsMeta,
|
|
11
|
+
routesMeta: HTTPRoutesMeta,
|
|
12
|
+
metaTypes: MetaInputTypes
|
|
13
|
+
) => {
|
|
14
|
+
const requiredTypes = new Set<string>()
|
|
15
|
+
const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes)
|
|
16
|
+
const serializedMetaTypes = generateMetaTypes(metaTypes, typesMap)
|
|
17
|
+
const serializedRoutes = generateRoutes(
|
|
18
|
+
functionsMeta,
|
|
19
|
+
routesMeta,
|
|
20
|
+
typesMap,
|
|
21
|
+
requiredTypes
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const serializedImportMap = serializeImportMap(
|
|
25
|
+
relativeToPath,
|
|
26
|
+
packageMappings,
|
|
27
|
+
typesMap,
|
|
28
|
+
requiredTypes
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
return `/**
|
|
32
|
+
* This provides the structure needed for typescript to be aware of routes and their return types
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
${serializedImportMap}
|
|
36
|
+
${serializedCustomTypes}
|
|
37
|
+
${serializedMetaTypes}
|
|
38
|
+
|
|
39
|
+
interface RouteHandler<I, O> {
|
|
40
|
+
input: I;
|
|
41
|
+
output: O;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
${serializedRoutes}
|
|
45
|
+
|
|
46
|
+
export type RouteHandlerOf<Route extends keyof RoutesMap, Method extends keyof RoutesMap[Route]> =
|
|
47
|
+
RoutesMap[Route][Method] extends { input: infer I; output: infer O }
|
|
48
|
+
? RouteHandler<I, O>
|
|
49
|
+
: never;
|
|
50
|
+
|
|
51
|
+
export type RoutesWithMethod<Method extends string> = {
|
|
52
|
+
[Route in keyof RoutesMap]: Method extends keyof RoutesMap[Route] ? Route : never;
|
|
53
|
+
}[keyof RoutesMap];
|
|
54
|
+
`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function generateCustomTypes(
|
|
58
|
+
typesMap: TypesMap,
|
|
59
|
+
requiredTypes: Set<string>
|
|
60
|
+
) {
|
|
61
|
+
return `
|
|
62
|
+
// Custom types are those that are defined directly within generics
|
|
63
|
+
// or are broken into simpler types
|
|
64
|
+
${Array.from(typesMap.customTypes.entries())
|
|
65
|
+
.map(([name, { type, references }]) => {
|
|
66
|
+
references.forEach((name) => {
|
|
67
|
+
const originalName = typesMap.getTypeMeta(name).originalName
|
|
68
|
+
requiredTypes.add(originalName)
|
|
69
|
+
})
|
|
70
|
+
return `export type ${name} = ${type}`
|
|
71
|
+
})
|
|
72
|
+
.join('\n')}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function generateRoutes(
|
|
76
|
+
functionsMeta: FunctionsMeta,
|
|
77
|
+
routesMeta: HTTPRoutesMeta,
|
|
78
|
+
typesMap: TypesMap,
|
|
79
|
+
requiredTypes: Set<string>
|
|
80
|
+
) {
|
|
81
|
+
// Initialize an object to collect routes
|
|
82
|
+
const routesObj: Record<
|
|
83
|
+
string,
|
|
84
|
+
Record<string, { inputType: string; outputType: string }>
|
|
85
|
+
> = {}
|
|
86
|
+
|
|
87
|
+
for (const meta of routesMeta) {
|
|
88
|
+
const { route, method, pikkuFuncName } = meta
|
|
89
|
+
const input = functionsMeta[pikkuFuncName]?.inputs?.[0]
|
|
90
|
+
const output = functionsMeta[pikkuFuncName]?.outputs?.[0]
|
|
91
|
+
|
|
92
|
+
// Initialize the route entry if it doesn't exist
|
|
93
|
+
if (!routesObj[route]) {
|
|
94
|
+
routesObj[route] = {}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Store the input and output types separately for RouteHandler
|
|
98
|
+
const inputType = input ? typesMap.getTypeMeta(input).uniqueName : 'null'
|
|
99
|
+
const outputType = output ? typesMap.getTypeMeta(output).uniqueName : 'null'
|
|
100
|
+
|
|
101
|
+
requiredTypes.add(inputType)
|
|
102
|
+
requiredTypes.add(outputType)
|
|
103
|
+
|
|
104
|
+
// Add method entry
|
|
105
|
+
routesObj[route][method] = {
|
|
106
|
+
inputType,
|
|
107
|
+
outputType,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Build the routes object as a string
|
|
112
|
+
let routesStr = 'export type RoutesMap = {\n'
|
|
113
|
+
|
|
114
|
+
for (const [routePath, methods] of Object.entries(routesObj)) {
|
|
115
|
+
routesStr += ` readonly '${routePath}': {\n`
|
|
116
|
+
for (const [method, handler] of Object.entries(methods)) {
|
|
117
|
+
routesStr += ` readonly ${method.toUpperCase()}: RouteHandler<${handler.inputType}, ${handler.outputType}>,\n`
|
|
118
|
+
}
|
|
119
|
+
routesStr += ' },\n'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
routesStr += '};'
|
|
123
|
+
|
|
124
|
+
return routesStr
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const generateMetaTypes = (metaTypes: MetaInputTypes, typesMap: TypesMap) => {
|
|
128
|
+
const nameToTypeMap = Array.from(metaTypes.entries()).reduce<
|
|
129
|
+
Map<string, string>
|
|
130
|
+
>((result, [_name, { query, body, params }]) => {
|
|
131
|
+
const { uniqueName } = typesMap.getTypeMeta(_name)
|
|
132
|
+
const queryType =
|
|
133
|
+
query && query.length > 0
|
|
134
|
+
? `Pick<${uniqueName}, '${query?.join("' | '")}'>`
|
|
135
|
+
: undefined
|
|
136
|
+
if (queryType) {
|
|
137
|
+
result.set(`${uniqueName}Query`, queryType)
|
|
138
|
+
}
|
|
139
|
+
const paramsType =
|
|
140
|
+
params && params.length > 0
|
|
141
|
+
? `Pick<${uniqueName}, '${params.join("' | '")}'>`
|
|
142
|
+
: undefined
|
|
143
|
+
if (paramsType) {
|
|
144
|
+
result.set(`${uniqueName}Params`, paramsType)
|
|
145
|
+
}
|
|
146
|
+
const bodyType =
|
|
147
|
+
(body && body.length > 0) || (params && params.length > 0)
|
|
148
|
+
? `Omit<${uniqueName}, '${[...new Set([...(query || []), ...(params || [])])].join("' | '")}'>`
|
|
149
|
+
: uniqueName!
|
|
150
|
+
if (bodyType) {
|
|
151
|
+
result.set(`${uniqueName}Body`, bodyType)
|
|
152
|
+
}
|
|
153
|
+
return result
|
|
154
|
+
}, new Map())
|
|
155
|
+
|
|
156
|
+
return `
|
|
157
|
+
// The '& {}' is a workaround for not directly refering to a type since it confuses typescript
|
|
158
|
+
${Array.from(nameToTypeMap.entries())
|
|
159
|
+
.map(([name, type]) => `export type ${name} = ${type} & {}`)
|
|
160
|
+
.join('\n')}`
|
|
161
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { HTTPRoutesMeta } from '@pikku/core/http'
|
|
2
|
+
import { serializeImportMap } from './serialize-import-map.js'
|
|
3
|
+
import { MetaInputTypes, TypesMap } from '@pikku/inspector'
|
|
4
|
+
import { FunctionsMeta } from '@pikku/core'
|
|
5
|
+
|
|
6
|
+
export const serializeTypedRoutesMap = (
|
|
7
|
+
relativeToPath: string,
|
|
8
|
+
packageMappings: Record<string, string>,
|
|
9
|
+
typesMap: TypesMap,
|
|
10
|
+
functionsMeta: FunctionsMeta,
|
|
11
|
+
routesMeta: HTTPRoutesMeta,
|
|
12
|
+
metaTypes: MetaInputTypes
|
|
13
|
+
) => {
|
|
14
|
+
const requiredTypes = new Set<string>()
|
|
15
|
+
const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes)
|
|
16
|
+
const serializedMetaTypes = generateMetaTypes(metaTypes, typesMap)
|
|
17
|
+
const serializedRoutes = generateRoutes(
|
|
18
|
+
routesMeta,
|
|
19
|
+
functionsMeta,
|
|
20
|
+
typesMap,
|
|
21
|
+
requiredTypes
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const serializedImportMap = serializeImportMap(
|
|
25
|
+
relativeToPath,
|
|
26
|
+
packageMappings,
|
|
27
|
+
typesMap,
|
|
28
|
+
requiredTypes
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
return `/**
|
|
32
|
+
* This provides the structure needed for typescript to be aware of routes and their return types
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
${serializedImportMap}
|
|
36
|
+
${serializedCustomTypes}
|
|
37
|
+
${serializedMetaTypes}
|
|
38
|
+
|
|
39
|
+
interface RouteHandler<I, O> {
|
|
40
|
+
input: I;
|
|
41
|
+
output: O;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
${serializedRoutes}
|
|
45
|
+
|
|
46
|
+
export type RouteHandlerOf<Route extends keyof RoutesMap, Method extends keyof RoutesMap[Route]> =
|
|
47
|
+
RoutesMap[Route][Method] extends { input: infer I; output: infer O }
|
|
48
|
+
? RouteHandler<I, O>
|
|
49
|
+
: never;
|
|
50
|
+
|
|
51
|
+
export type RoutesWithMethod<Method extends string> = {
|
|
52
|
+
[Route in keyof RoutesMap]: Method extends keyof RoutesMap[Route] ? Route : never;
|
|
53
|
+
}[keyof RoutesMap];
|
|
54
|
+
`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function generateCustomTypes(
|
|
58
|
+
typesMap: TypesMap,
|
|
59
|
+
requiredTypes: Set<string>
|
|
60
|
+
) {
|
|
61
|
+
return `
|
|
62
|
+
// Custom types are those that are defined directly within generics
|
|
63
|
+
// or are broken into simpler types
|
|
64
|
+
${Array.from(typesMap.customTypes.entries())
|
|
65
|
+
.map(([name, { type, references }]) => {
|
|
66
|
+
references.forEach((name) => {
|
|
67
|
+
const originalName = typesMap.getTypeMeta(name).originalName
|
|
68
|
+
requiredTypes.add(originalName)
|
|
69
|
+
})
|
|
70
|
+
return `export type ${name} = ${type}`
|
|
71
|
+
})
|
|
72
|
+
.join('\n')}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function generateRoutes(
|
|
76
|
+
routesMeta: HTTPRoutesMeta,
|
|
77
|
+
functionsMeta: FunctionsMeta,
|
|
78
|
+
typesMap: TypesMap,
|
|
79
|
+
requiredTypes: Set<string>
|
|
80
|
+
) {
|
|
81
|
+
// Initialize an object to collect routes
|
|
82
|
+
const routesObj: Record<
|
|
83
|
+
string,
|
|
84
|
+
Record<string, { inputType: string; outputType: string }>
|
|
85
|
+
> = {}
|
|
86
|
+
|
|
87
|
+
for (const meta of routesMeta) {
|
|
88
|
+
const { route, method, pikkuFuncName } = meta
|
|
89
|
+
const functionMeta = functionsMeta[pikkuFuncName]
|
|
90
|
+
if (!functionMeta) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Function ${pikkuFuncName} not found in functionsMeta. Please check your configuration.`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined
|
|
96
|
+
const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined
|
|
97
|
+
|
|
98
|
+
// Initialize the route entry if it doesn't exist
|
|
99
|
+
if (!routesObj[route]) {
|
|
100
|
+
routesObj[route] = {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Store the input and output types separately for RouteHandler
|
|
104
|
+
const inputType = input ? typesMap.getTypeMeta(input).uniqueName : 'null'
|
|
105
|
+
const outputType = output ? typesMap.getTypeMeta(output).uniqueName : 'null'
|
|
106
|
+
|
|
107
|
+
requiredTypes.add(inputType)
|
|
108
|
+
requiredTypes.add(outputType)
|
|
109
|
+
|
|
110
|
+
// Add method entry
|
|
111
|
+
routesObj[route][method] = {
|
|
112
|
+
inputType,
|
|
113
|
+
outputType,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Build the routes object as a string
|
|
118
|
+
let routesStr = 'export type RoutesMap = {\n'
|
|
119
|
+
|
|
120
|
+
for (const [routePath, methods] of Object.entries(routesObj)) {
|
|
121
|
+
routesStr += ` readonly '${routePath}': {\n`
|
|
122
|
+
for (const [method, handler] of Object.entries(methods)) {
|
|
123
|
+
routesStr += ` readonly ${method.toUpperCase()}: RouteHandler<${handler.inputType}, ${handler.outputType}>,\n`
|
|
124
|
+
}
|
|
125
|
+
routesStr += ' },\n'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
routesStr += '};'
|
|
129
|
+
|
|
130
|
+
return routesStr
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const generateMetaTypes = (metaTypes: MetaInputTypes, typesMap: TypesMap) => {
|
|
134
|
+
const nameToTypeMap = Array.from(metaTypes.entries()).reduce<
|
|
135
|
+
Map<string, string>
|
|
136
|
+
>((result, [_name, { query, body, params }]) => {
|
|
137
|
+
const { uniqueName } = typesMap.getTypeMeta(_name)
|
|
138
|
+
const queryType =
|
|
139
|
+
query && query.length > 0
|
|
140
|
+
? `Pick<${uniqueName}, '${query?.join("' | '")}'>`
|
|
141
|
+
: undefined
|
|
142
|
+
if (queryType) {
|
|
143
|
+
result.set(`${uniqueName}Query`, queryType)
|
|
144
|
+
}
|
|
145
|
+
const paramsType =
|
|
146
|
+
params && params.length > 0
|
|
147
|
+
? `Pick<${uniqueName}, '${params.join("' | '")}'>`
|
|
148
|
+
: undefined
|
|
149
|
+
if (paramsType) {
|
|
150
|
+
result.set(`${uniqueName}Params`, paramsType)
|
|
151
|
+
}
|
|
152
|
+
const bodyType =
|
|
153
|
+
(body && body.length > 0) || (params && params.length > 0)
|
|
154
|
+
? `Omit<${uniqueName}, '${[...new Set([...(query || []), ...(params || [])])].join("' | '")}'>`
|
|
155
|
+
: uniqueName!
|
|
156
|
+
if (bodyType) {
|
|
157
|
+
result.set(`${uniqueName}Body`, bodyType)
|
|
158
|
+
}
|
|
159
|
+
return result
|
|
160
|
+
}, new Map())
|
|
161
|
+
|
|
162
|
+
return `
|
|
163
|
+
// The '& {}' is a workaround for not directly refering to a type since it confuses typescript
|
|
164
|
+
${Array.from(nameToTypeMap.entries())
|
|
165
|
+
.map(([name, type]) => `export type ${name} = ${type} & {}`)
|
|
166
|
+
.join('\n')}`
|
|
167
|
+
}
|
package/{dist/src/channels/serialize-websocket-wrapper.js → src/serialize-websocket-wrapper.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export const serializeWebsocketWrapper = (channelsMapPath) => {
|
|
2
|
-
|
|
1
|
+
export const serializeWebsocketWrapper = (channelsMapPath: string) => {
|
|
2
|
+
return `import { CorePikkuWebsocket, CorePikkuRouteHandler } from '@pikku/websocket'
|
|
3
3
|
import { ChannelDefaultHandlerOf, ChannelRouteHandlerOf, ChannelsMap } from '${channelsMapPath}';
|
|
4
4
|
|
|
5
5
|
class PikkuWebSocketRoute<Channel extends keyof ChannelsMap, Route extends keyof ChannelsMap[Channel]['routes']> extends CorePikkuRouteHandler {
|
|
@@ -57,5 +57,5 @@ export class PikkuWebSocket<Channel extends keyof ChannelsMap> extends CorePikku
|
|
|
57
57
|
super.send(data)
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
}
|
|
60
|
+
`
|
|
61
|
+
}
|