@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.
Files changed (89) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/bin/pikku-all.ts +203 -0
  3. package/bin/pikku-channels-map.ts +55 -0
  4. package/bin/pikku-channels.ts +63 -0
  5. package/bin/pikku-fetch.ts +55 -0
  6. package/bin/pikku-function-types.ts +84 -0
  7. package/bin/pikku-functions.ts +35 -0
  8. package/bin/pikku-http-map.ts +55 -0
  9. package/bin/pikku-http-routes.ts +63 -0
  10. package/bin/pikku-nextjs.test.ts +279 -0
  11. package/bin/pikku-nextjs.ts +152 -0
  12. package/bin/pikku-openapi.ts +74 -0
  13. package/bin/pikku-rpc.ts +22 -0
  14. package/bin/pikku-scheduler.ts +64 -0
  15. package/bin/pikku-schemas.ts +56 -0
  16. package/bin/pikku-websocket.ts +58 -0
  17. package/bin/pikku.ts +26 -0
  18. package/dist/bin/pikku-all.js +3 -0
  19. package/dist/bin/pikku-functions.d.ts +0 -2
  20. package/dist/bin/pikku-functions.js +2 -17
  21. package/dist/bin/pikku-rpc.d.ts +3 -0
  22. package/dist/bin/pikku-rpc.js +8 -0
  23. package/dist/bin/pikku.js +0 -0
  24. package/dist/src/pikku-cli-config.d.ts +1 -0
  25. package/dist/src/pikku-cli-config.js +3 -0
  26. package/dist/src/schema-generator.js +1 -2
  27. package/dist/tsconfig.tsbuildinfo +1 -0
  28. package/package.json +3 -3
  29. package/src/inspector-glob.ts +28 -0
  30. package/src/openapi-spec-generator.ts +227 -0
  31. package/src/pikku-cli-config.ts +240 -0
  32. package/src/schema-generator.ts +136 -0
  33. package/{dist/src/events/http/serialize-fetch-wrapper.js → src/serialize-fetch-wrapper.ts} +4 -4
  34. package/src/serialize-import-map.ts +34 -0
  35. package/{dist/src/nextjs/serialize-nextjs-backend-wrapper.js → src/serialize-nextjs-backend-wrapper.ts} +11 -4
  36. package/{dist/src/nextjs/serialize-nextjs-http-wrapper.js → src/serialize-nextjs-http-wrapper.ts} +7 -4
  37. package/{dist/src/core/serialize-pikku-types.js → src/serialize-pikku-types.ts} +46 -35
  38. package/src/serialize-scheduler-meta.ts +18 -0
  39. package/src/serialize-typed-channel-map.ts +138 -0
  40. package/src/serialize-typed-function-map.ts +151 -0
  41. package/src/serialize-typed-http-map.ts +151 -0
  42. package/{dist/src/channels/serialize-websocket-wrapper.js → src/serialize-websocket-wrapper.ts} +4 -4
  43. package/src/utils.ts +284 -0
  44. package/tsconfig.json +21 -0
  45. package/dist/bin/pikku-http.d.ts +0 -5
  46. package/dist/bin/pikku-http.js +0 -27
  47. package/dist/bin/pikku-routes-map.d.ts +0 -5
  48. package/dist/bin/pikku-routes-map.js +0 -23
  49. package/dist/src/channels/serialize-channels.d.ts +0 -3
  50. package/dist/src/channels/serialize-channels.js +0 -19
  51. package/dist/src/channels/serialize-typed-channel-map.d.ts +0 -3
  52. package/dist/src/channels/serialize-typed-channel-map.js +0 -93
  53. package/dist/src/channels/serialize-websocket-wrapper.d.ts +0 -1
  54. package/dist/src/core/serialize-import-map.d.ts +0 -2
  55. package/dist/src/core/serialize-import-map.js +0 -24
  56. package/dist/src/core/serialize-pikku-types.d.ts +0 -4
  57. package/dist/src/events/channels/serialize-channels.d.ts +0 -3
  58. package/dist/src/events/channels/serialize-channels.js +0 -19
  59. package/dist/src/events/channels/serialize-typed-channel-map.d.ts +0 -3
  60. package/dist/src/events/channels/serialize-typed-channel-map.js +0 -90
  61. package/dist/src/events/channels/serialize-websocket-wrapper.d.ts +0 -1
  62. package/dist/src/events/channels/serialize-websocket-wrapper.js +0 -61
  63. package/dist/src/events/http/serialize-fetch-wrapper.d.ts +0 -1
  64. package/dist/src/events/http/serialize-route-imports.d.ts +0 -1
  65. package/dist/src/events/http/serialize-route-imports.js +0 -13
  66. package/dist/src/events/http/serialize-route-meta.d.ts +0 -2
  67. package/dist/src/events/http/serialize-route-meta.js +0 -6
  68. package/dist/src/events/http/serialize-typed-route-map.d.ts +0 -4
  69. package/dist/src/events/http/serialize-typed-route-map.js +0 -107
  70. package/dist/src/events/scheduler/serialize-schedulers.d.ts +0 -3
  71. package/dist/src/events/scheduler/serialize-schedulers.js +0 -23
  72. package/dist/src/http/serialize-fetch-wrapper.d.ts +0 -1
  73. package/dist/src/http/serialize-fetch-wrapper.js +0 -67
  74. package/dist/src/http/serialize-route-imports.d.ts +0 -1
  75. package/dist/src/http/serialize-route-imports.js +0 -13
  76. package/dist/src/http/serialize-route-meta.d.ts +0 -2
  77. package/dist/src/http/serialize-route-meta.js +0 -6
  78. package/dist/src/http/serialize-typed-route-map.d.ts +0 -4
  79. package/dist/src/http/serialize-typed-route-map.js +0 -107
  80. package/dist/src/nextjs/serialize-nextjs-backend-wrapper.d.ts +0 -1
  81. package/dist/src/nextjs/serialize-nextjs-http-wrapper.d.ts +0 -1
  82. package/dist/src/openapi/openapi-spec-generator.d.ts +0 -79
  83. package/dist/src/openapi/openapi-spec-generator.js +0 -136
  84. package/dist/src/scheduler/serialize-schedulers.d.ts +0 -3
  85. package/dist/src/scheduler/serialize-schedulers.js +0 -23
  86. package/dist/src/schema/schema-generator.d.ts +0 -5
  87. package/dist/src/schema/schema-generator.js +0 -89
  88. package/dist/src/serialize-typed-route-map.d.ts +0 -4
  89. package/dist/src/serialize-typed-route-map.js +0 -107
@@ -0,0 +1,240 @@
1
+ import { join, dirname, resolve, isAbsolute } from 'path'
2
+ import { readdir, readFile } from 'fs/promises'
3
+ import { OpenAPISpecInfo } from './openapi-spec-generator.js'
4
+ import { InspectorFilters } from '@pikku/inspector'
5
+
6
+ export interface PikkuCLICoreOutputFiles {
7
+ outDir?: string
8
+ functionsFile: string
9
+ functionsMetaFile: string
10
+ httpRoutesFile: string
11
+ httpRoutesMetaFile: string
12
+ channelsFile: string
13
+ channelsMetaFile: string
14
+ schedulersFile: string
15
+ schedulersMetaFile: string
16
+ rpcFile: string
17
+ schemaDirectory: string
18
+ typesDeclarationFile: string
19
+ httpRoutesMapDeclarationFile: string
20
+ channelsMapDeclarationFile: string
21
+ bootstrapFile: string
22
+ }
23
+
24
+ export type PikkuCLIConfig = {
25
+ $schema?: string
26
+
27
+ extends?: string
28
+
29
+ rootDir: string
30
+ srcDirectories: string[]
31
+ packageMappings: Record<string, string>
32
+ supportsImportAttributes: boolean
33
+
34
+ configDir: string
35
+ tsconfig: string
36
+
37
+ nextBackendFile?: string
38
+ nextHTTPFile?: string
39
+ fetchFile?: string
40
+ websocketFile?: string
41
+
42
+ openAPI?: {
43
+ outputFile: string
44
+ additionalInfo: OpenAPISpecInfo
45
+ }
46
+
47
+ filters: InspectorFilters
48
+ } & PikkuCLICoreOutputFiles
49
+
50
+ const CONFIG_DIR_FILES = [
51
+ 'nextBackendFile',
52
+ 'nextHTTPFile',
53
+ 'fetchFile',
54
+ 'websocketFile',
55
+ ]
56
+
57
+ export const getPikkuCLIConfig = async (
58
+ configFile: string | undefined = undefined,
59
+ requiredFields: Array<keyof PikkuCLIConfig>,
60
+ tags: string[] = [],
61
+ exitProcess: boolean = false
62
+ ): Promise<PikkuCLIConfig> => {
63
+ const config = await _getPikkuCLIConfig(
64
+ configFile,
65
+ requiredFields,
66
+ tags,
67
+ exitProcess
68
+ )
69
+ return config
70
+ }
71
+
72
+ const _getPikkuCLIConfig = async (
73
+ configFile: string | undefined = undefined,
74
+ requiredFields: Array<keyof PikkuCLIConfig>,
75
+ tags: string[] = [],
76
+ exitProcess: boolean = false
77
+ ): Promise<PikkuCLIConfig> => {
78
+ if (!configFile) {
79
+ let execDirectory = process.cwd()
80
+ const files = await readdir(execDirectory)
81
+ const file = files.find((file) => /pikku\.config\.(ts|js|json)$/.test(file))
82
+ if (!file) {
83
+ const errorMessage =
84
+ '\nConfig file pikku.config.json not found\nExiting...'
85
+ if (exitProcess) {
86
+ console.error(errorMessage)
87
+ process.exit(1)
88
+ }
89
+ throw new Error(errorMessage)
90
+ }
91
+ configFile = join(execDirectory, file)
92
+ }
93
+
94
+ try {
95
+ let result: PikkuCLIConfig
96
+ const file = await readFile(configFile, 'utf-8')
97
+ const configDir = dirname(configFile)
98
+ const config: PikkuCLIConfig = JSON.parse(file)
99
+ if (config.extends) {
100
+ const extendedConfig = await getPikkuCLIConfig(
101
+ resolve(configDir, config.extends),
102
+ [],
103
+ tags,
104
+ exitProcess
105
+ )
106
+ result = {
107
+ ...extendedConfig,
108
+ ...config,
109
+ configDir,
110
+ packageMappings: {
111
+ ...extendedConfig.packageMappings,
112
+ ...config.packageMappings,
113
+ },
114
+ }
115
+ } else {
116
+ result = {
117
+ ...config,
118
+ configDir,
119
+ packageMappings: config.packageMappings || {},
120
+ rootDir: config.rootDir
121
+ ? resolve(configDir, config.rootDir)
122
+ : configDir,
123
+ }
124
+ }
125
+
126
+ if (result.outDir) {
127
+ if (!result.schemaDirectory) {
128
+ result.schemaDirectory = join(result.outDir, 'pikku-schemas')
129
+ }
130
+ if (!result.functionsFile) {
131
+ result.functionsFile = join(result.outDir, 'pikku-functions.gen.ts')
132
+ }
133
+ if (!result.functionsMetaFile) {
134
+ result.functionsMetaFile = join(
135
+ result.outDir,
136
+ 'pikku-functions-meta.gen.ts'
137
+ )
138
+ }
139
+ if (!result.rpcFile) {
140
+ result.rpcFile = join(result.outDir, 'pikku-rpc.gen.ts')
141
+ }
142
+ if (!result.httpRoutesFile) {
143
+ result.httpRoutesFile = join(result.outDir, 'pikku-http-routes.gen.ts')
144
+ }
145
+ if (!result.httpRoutesMetaFile) {
146
+ result.httpRoutesMetaFile = join(
147
+ result.outDir,
148
+ 'pikku-http-routes-meta.gen.ts'
149
+ )
150
+ }
151
+ if (!result.schedulersFile) {
152
+ result.schedulersFile = join(result.outDir, 'pikku-schedules.gen.ts')
153
+ }
154
+ if (!result.schedulersMetaFile) {
155
+ result.schedulersMetaFile = join(
156
+ result.outDir,
157
+ 'pikku-schedules-meta.gen.ts'
158
+ )
159
+ }
160
+ if (!result.channelsFile) {
161
+ result.channelsFile = join(result.outDir, 'pikku-channels.gen.ts')
162
+ }
163
+ if (!result.channelsMetaFile) {
164
+ result.channelsMetaFile = join(
165
+ result.outDir,
166
+ 'pikku-channels-meta.gen.ts'
167
+ )
168
+ }
169
+ if (!result.typesDeclarationFile) {
170
+ result.typesDeclarationFile = join(result.outDir, 'pikku-types.gen.ts')
171
+ }
172
+ if (!result.httpRoutesMapDeclarationFile) {
173
+ result.httpRoutesMapDeclarationFile = join(
174
+ result.outDir,
175
+ 'pikku-routes-map.gen.d.ts'
176
+ )
177
+ }
178
+ if (!result.channelsMapDeclarationFile) {
179
+ result.channelsMapDeclarationFile = join(
180
+ result.outDir,
181
+ 'pikku-channels-map.gen.d.ts'
182
+ )
183
+ }
184
+ if (!result.bootstrapFile) {
185
+ result.bootstrapFile = join(result.outDir, 'pikku-bootstrap.gen.ts')
186
+ }
187
+ }
188
+
189
+ if (requiredFields.length > 0) {
190
+ validateCLIConfig(result, requiredFields)
191
+ }
192
+
193
+ for (const objectKey of Object.keys(result)) {
194
+ if (objectKey.endsWith('File') || objectKey.endsWith('Directory')) {
195
+ const relativeTo = CONFIG_DIR_FILES.includes(objectKey)
196
+ ? result.configDir
197
+ : result.rootDir
198
+ if (result[objectKey]) {
199
+ if (!isAbsolute(result[objectKey])) {
200
+ result[objectKey] = join(relativeTo, result[objectKey])
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ result.filters = result.filters || {}
207
+ if (tags.length > 0) {
208
+ result.filters.tags = tags
209
+ }
210
+
211
+ if (!isAbsolute(result.tsconfig)) {
212
+ result.tsconfig = join(result.rootDir, result.tsconfig)
213
+ }
214
+
215
+ return result
216
+ } catch (e: any) {
217
+ console.error(e)
218
+ console.error(`Config file not found: ${configFile}`)
219
+ process.exit(1)
220
+ }
221
+ }
222
+
223
+ export const validateCLIConfig = (
224
+ cliConfig: PikkuCLIConfig,
225
+ required: Array<keyof PikkuCLIConfig>
226
+ ) => {
227
+ let errors: string[] = []
228
+ for (const key of required) {
229
+ if (!cliConfig[key]) {
230
+ errors.push(key)
231
+ }
232
+ }
233
+
234
+ if (errors.length > 0) {
235
+ console.error(
236
+ `${errors.join(', ')} ${errors.length === 1 ? 'is' : 'are'} required in pikku.config.json`
237
+ )
238
+ process.exit(1)
239
+ }
240
+ }
@@ -0,0 +1,136 @@
1
+ import { createGenerator, RootlessError } from 'ts-json-schema-generator'
2
+ import { logInfo, writeFileInDir } from './utils.js'
3
+ import { mkdir, writeFile } from 'fs/promises'
4
+ import { JSONValue } from '@pikku/core'
5
+ import { HTTPRoutesMeta } from '@pikku/core/http'
6
+ import { TypesMap } from '@pikku/inspector'
7
+
8
+ export async function generateSchemas(
9
+ tsconfig: string,
10
+ typesMaps: TypesMap[],
11
+ httpRoutesMeta: HTTPRoutesMeta
12
+ ): Promise<Record<string, JSONValue>> {
13
+ const schemasSet = new Set(
14
+ typesMaps.flatMap((tm) => [...tm.customTypes.keys()])
15
+ )
16
+ for (const { input, inputTypes } of httpRoutesMeta) {
17
+ if (input) {
18
+ let found = false
19
+ for (const typesMap of typesMaps) {
20
+ try {
21
+ const uniqueName = typesMap.getUniqueName(input)
22
+ if (uniqueName) {
23
+ found = true
24
+ schemasSet.add(uniqueName)
25
+ break
26
+ }
27
+ } catch (e) {}
28
+ }
29
+ if (!found) {
30
+ console.error('Input type not found in any types map:', input)
31
+ }
32
+ }
33
+ // if (output) {
34
+ // for (const typesMap of typesMaps) {
35
+ // const uniqueName = typesMap.getUniqueName(output)
36
+ // if (uniqueName) {
37
+ // console.log('Adding output schema:', uniqueName)
38
+ // schemasSet.add(uniqueName)
39
+ // break
40
+ // }
41
+ // }
42
+ // }
43
+ if (inputTypes?.body) {
44
+ schemasSet.add(inputTypes.body)
45
+ }
46
+ if (inputTypes?.query) {
47
+ schemasSet.add(inputTypes.query)
48
+ }
49
+ if (inputTypes?.params) {
50
+ schemasSet.add(inputTypes.params)
51
+ }
52
+ }
53
+
54
+ const generator = createGenerator({
55
+ tsconfig,
56
+ skipTypeCheck: true,
57
+ topRef: false,
58
+ discriminatorType: 'open-api',
59
+ })
60
+ const schemas: Record<string, JSONValue> = {}
61
+ schemasSet.forEach((schema) => {
62
+ try {
63
+ schemas[schema] = generator.createSchema(schema) as JSONValue
64
+ } catch (e) {
65
+ // Ignore rootless errors
66
+ if (e instanceof RootlessError) {
67
+ console.error('Error generating schema since it has no root:', schema)
68
+ return
69
+ }
70
+ throw e
71
+ }
72
+ })
73
+
74
+ return schemas
75
+ }
76
+
77
+ export async function saveSchemas(
78
+ schemaParentDir: string,
79
+ schemas: Record<string, JSONValue>,
80
+ typesMap: TypesMap,
81
+ routesMeta: HTTPRoutesMeta,
82
+ supportsImportAttributes: boolean
83
+ ) {
84
+ await writeFileInDir(
85
+ `${schemaParentDir}/register.gen.ts`,
86
+ 'export const empty = null;'
87
+ )
88
+
89
+ const desiredSchemas = new Set([
90
+ ...routesMeta
91
+ .map(({ input, output }) => [
92
+ input ? typesMap.getUniqueName(input) : undefined,
93
+ output ? typesMap.getUniqueName(output) : undefined,
94
+ ])
95
+ .flat()
96
+ .filter(
97
+ (s) =>
98
+ !!s &&
99
+ !['boolean', 'string', 'number', 'null', 'undefined'].includes(s)
100
+ ),
101
+ ...typesMap.customTypes.keys(),
102
+ ])
103
+
104
+ if (desiredSchemas.size === 0) {
105
+ logInfo(`• Skipping schemas since none found.\x1b[0m`)
106
+ return
107
+ }
108
+
109
+ await mkdir(`${schemaParentDir}/schemas`, { recursive: true })
110
+ await Promise.all(
111
+ Object.entries(schemas).map(async ([schemaName, schema]) => {
112
+ if (desiredSchemas.has(schemaName)) {
113
+ await writeFile(
114
+ `${schemaParentDir}/schemas/${schemaName}.schema.json`,
115
+ JSON.stringify(schema),
116
+ 'utf-8'
117
+ )
118
+ }
119
+ })
120
+ )
121
+
122
+ const schemaImports = Array.from(desiredSchemas)
123
+ .map(
124
+ (schema) => `
125
+ import * as ${schema} from './schemas/${schema}.schema.json' ${supportsImportAttributes ? `with { type: 'json' }` : ''}
126
+ addSchema('${schema}', ${schema})
127
+ `
128
+ )
129
+ .join('\n')
130
+
131
+ await writeFileInDir(
132
+ `${schemaParentDir}/register.gen.ts`,
133
+ `import { addSchema } from '@pikku/core/schema'
134
+ ${schemaImports}`
135
+ )
136
+ }
@@ -1,5 +1,5 @@
1
- export const serializeFetchWrapper = (routesMapPath) => {
2
- return `
1
+ export const serializeFetchWrapper = (routesMapPath: string) => {
2
+ return `
3
3
  import { CorePikkuFetch, HTTPMethod } from '@pikku/fetch'
4
4
  import type { RoutesMap, RouteHandlerOf, RoutesWithMethod } from '${routesMapPath}'
5
5
 
@@ -63,5 +63,5 @@ export class PikkuFetch extends CorePikkuFetch {
63
63
  }
64
64
 
65
65
  export const pikkuFetch = new PikkuFetch();
66
- `;
67
- };
66
+ `
67
+ }
@@ -0,0 +1,34 @@
1
+ import { TypesMap } from '@pikku/inspector'
2
+ import { getFileImportRelativePath } from './utils.js'
3
+
4
+ export const serializeImportMap = (
5
+ relativeToPath: string,
6
+ packageMappings: Record<string, string>,
7
+ typesMap: TypesMap,
8
+ requiredTypes: Set<string>
9
+ ) => {
10
+ const paths = new Map<string, string[]>()
11
+ Array.from(requiredTypes).forEach((requiredType) => {
12
+ const { originalName, uniqueName, path } =
13
+ typesMap.getTypeMeta(requiredType)
14
+ if (!path) {
15
+ // This is a custom type that exists in file, so we don't need to import it
16
+ return
17
+ }
18
+ const variables = paths.get(path) || []
19
+ if (originalName === uniqueName) {
20
+ variables.push(originalName)
21
+ } else {
22
+ variables.push(`${originalName} as ${uniqueName}`)
23
+ }
24
+ paths.set(path, variables)
25
+ })
26
+
27
+ const imports: string[] = []
28
+ for (const [path, variables] of paths.entries()) {
29
+ imports.push(
30
+ `import type { ${variables.join(', ')} } from '${getFileImportRelativePath(relativeToPath, path, packageMappings)}'`
31
+ )
32
+ }
33
+ return imports.join('\n')
34
+ }
@@ -1,5 +1,12 @@
1
- export const serializeNextJsBackendWrapper = (routesPath, routesMapPath, schemasPath, configImport, singleServicesFactoryImport, sessionServicesImport) => {
2
- return `'server-only'
1
+ export const serializeNextJsBackendWrapper = (
2
+ routesPath: string,
3
+ routesMapPath: string,
4
+ schemasPath: string,
5
+ configImport: string,
6
+ singleServicesFactoryImport: string,
7
+ sessionServicesImport: string
8
+ ) => {
9
+ return `'server-only'
3
10
 
4
11
  /**
5
12
  * This file provides a wrapper around the PikkuNextJS class to allow for methods to be type checked against your routes.
@@ -176,5 +183,5 @@ export const pikku = (_options?: any) => {
176
183
  staticPost
177
184
  }
178
185
  }
179
- `;
180
- };
186
+ `
187
+ }
@@ -1,5 +1,8 @@
1
- export const serializeNextJsHTTPWrapper = (routesMapPath, pikkuFetchImport) => {
2
- return `'server-only'
1
+ export const serializeNextJsHTTPWrapper = (
2
+ routesMapPath: string,
3
+ pikkuFetchImport: string
4
+ ) => {
5
+ return `'server-only'
3
6
 
4
7
  /**
5
8
  * This file provides a wrapper around the PikkuNextJS class to allow for methods to be type checked against your routes.
@@ -156,5 +159,5 @@ export const pikku = (options?: CorePikkuFetchOptions) => {
156
159
  staticPost
157
160
  }
158
161
  }
159
- `;
160
- };
162
+ `
163
+ }
@@ -1,14 +1,21 @@
1
1
  /**
2
2
  *
3
3
  */
4
- export const serializePikkuTypes = (userSessionTypeImport, userSessionTypeName, singletonServicesTypeImport, singletonServicesTypeName, sessionServicesTypeImport, servicesTypeName) => {
5
- return `/**
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 { CorePermissionGroup, CoreAPIPermission, PikkuMiddleware, MakeRequired } from '@pikku/core'
16
+ import { CoreAPIPermission, PikkuMiddleware } from '@pikku/core'
10
17
  import { CoreAPIFunction, CoreAPIFunctionSessionless } from '@pikku/core/function'
11
- import { CoreHTTPFunctionRoute, AssertRouteParams, addRoute as addCoreHTTP } from '@pikku/core/http'
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<In = unknown, Out = never, Channel extends boolean = false, RequiredServices extends Services = Services & (Channel extends true ? { channel: PikkuChannel<unknown, Out> } : { channel?: PikkuChannel<unknown, Out> })> = CoreAPIFunctionSessionless<In, Out, Channel, RequiredServices, ${userSessionTypeName}>
23
- type APIFunction<In = unknown, Out = never, Channel extends boolean = false, RequiredServices extends Services = Services & (Channel extends true ? { channel: PikkuChannel<unknown, Out> } : { channel?: PikkuChannel<unknown, Out> })> = CoreAPIFunction<In, Out, Channel, RequiredServices, ${userSessionTypeName}>
24
- type APIRoute<In, Out, Route extends string> = CoreHTTPFunctionRoute<In, Out, Route, APIFunction<In, Out>, APIFunctionSessionless<In, Out>, CorePermissionGroup<APIPermission>, APIMiddleware>
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
- export type ChannelConnection<Out = unknown, ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<RequiredServices, 'userSession'>, channel: PikkuChannel<ChannelData, Out>) => Promise<void>
27
- export type ChannelDisconnection<ChannelData = unknown, RequiredServices extends ${servicesTypeName} = ${servicesTypeName}> = (services: MakeRequired<RequiredServices, 'userSession'>, channel: PikkuChannel<ChannelData, never>) => Promise<void>
28
- type APIChannel<ChannelData, Channel extends string> = CoreAPIChannel<ChannelData, Channel, APIFunction<any, any, false> | APIFunction<any, any, true>, APIPermission>
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 pikkuChannelConnection = <In, Out = unknown>(
83
+ export const pikkuChannelConnectionFunc = <Out = unknown, ChannelData = unknown>(
61
84
  func:
62
- | ChannelConnection<In, Out>
63
- | {
64
- func: ChannelConnection<In, Out>
65
- auth?: true
66
- name?: string
67
- }
85
+ | APIFunctionSessionless<void, Out, ChannelData>
68
86
  | {
69
- func: ChannelConnection<In, Out>
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 pikkuChannelDisconnection = <In>(
94
+ export const pikkuChannelDisconnectionFunc = <ChannelData = unknown>(
78
95
  func:
79
- | ChannelDisconnection<In>
96
+ | APIFunctionSessionless<void, void, ChannelData>
80
97
  | {
81
- func: ChannelDisconnection<In>
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, true>
107
+ | APIFunctionSessionless<In, Out, ChannelData>
97
108
  | {
98
- func: APIFunctionSessionless<In, Out, true>
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 addRoute = <In, Out, Route extends string>(
133
+ export const addHTTPRoute = <In, Out, Route extends string>(
123
134
  route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
124
135
  ) => {
125
- addCoreHTTP(route)
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
+ }