@pikku/cli 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/bin/pikku-all.ts +5 -2
  3. package/bin/pikku-channels-map.ts +2 -33
  4. package/bin/pikku-channels.ts +2 -30
  5. package/bin/pikku-fetch.ts +3 -3
  6. package/bin/pikku-function-types.ts +8 -42
  7. package/bin/pikku-functions.ts +3 -16
  8. package/bin/pikku-http-map.ts +3 -32
  9. package/bin/pikku-http-routes.ts +2 -30
  10. package/bin/pikku-nextjs.ts +1 -1
  11. package/bin/pikku-openapi.ts +4 -2
  12. package/bin/pikku-rpc-map.ts +25 -0
  13. package/bin/pikku-rpc.ts +62 -7
  14. package/bin/pikku-scheduler.ts +2 -30
  15. package/bin/pikku-schemas.ts +5 -4
  16. package/bin/pikku-websocket.ts +1 -1
  17. package/bin/pikku.ts +0 -10
  18. package/dist/bin/pikku-all.d.ts +1 -1
  19. package/dist/bin/pikku-all.js +5 -2
  20. package/dist/bin/pikku-channels-map.d.ts +0 -2
  21. package/dist/bin/pikku-channels-map.js +1 -16
  22. package/dist/bin/pikku-channels.d.ts +0 -2
  23. package/dist/bin/pikku-channels.js +1 -16
  24. package/dist/bin/pikku-fetch.d.ts +1 -1
  25. package/dist/bin/pikku-fetch.js +1 -1
  26. package/dist/bin/pikku-function-types.d.ts +2 -4
  27. package/dist/bin/pikku-function-types.js +3 -22
  28. package/dist/bin/pikku-functions.js +3 -4
  29. package/dist/bin/pikku-http-map.d.ts +0 -2
  30. package/dist/bin/pikku-http-map.js +2 -17
  31. package/dist/bin/pikku-http-routes.d.ts +0 -2
  32. package/dist/bin/pikku-http-routes.js +1 -16
  33. package/dist/bin/pikku-nextjs.d.ts +1 -1
  34. package/dist/bin/pikku-nextjs.js +1 -1
  35. package/dist/bin/pikku-openapi.js +3 -3
  36. package/dist/bin/pikku-rpc-map.d.ts +3 -0
  37. package/dist/bin/pikku-rpc-map.js +8 -0
  38. package/dist/bin/pikku-rpc.d.ts +5 -1
  39. package/dist/bin/pikku-rpc.js +33 -6
  40. package/dist/bin/pikku-scheduler.d.ts +0 -2
  41. package/dist/bin/pikku-scheduler.js +1 -16
  42. package/dist/bin/pikku-schemas.d.ts +1 -1
  43. package/dist/bin/pikku-schemas.js +4 -4
  44. package/dist/bin/pikku-websocket.d.ts +1 -1
  45. package/dist/bin/pikku-websocket.js +1 -1
  46. package/dist/bin/pikku.js +0 -10
  47. package/dist/src/inspector-glob.js +1 -1
  48. package/dist/src/openapi-spec-generator.d.ts +2 -2
  49. package/dist/src/openapi-spec-generator.js +14 -5
  50. package/dist/src/pikku-cli-config.d.ts +7 -5
  51. package/dist/src/pikku-cli-config.js +6 -0
  52. package/dist/src/schema-generator.d.ts +3 -3
  53. package/dist/src/schema-generator.js +16 -34
  54. package/dist/src/serialize-pikku-types.d.ts +1 -1
  55. package/dist/src/serialize-pikku-types.js +6 -3
  56. package/dist/src/serialize-typed-channel-map.js +2 -2
  57. package/dist/src/serialize-typed-http-map.d.ts +2 -1
  58. package/dist/src/serialize-typed-http-map.js +11 -5
  59. package/dist/src/serialize-typed-rpc-map.d.ts +4 -0
  60. package/dist/src/serialize-typed-rpc-map.js +66 -0
  61. package/dist/src/{utils.d.ts → utils/utils.d.ts} +2 -1
  62. package/dist/src/{utils.js → utils/utils.js} +15 -1
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +3 -3
  65. package/src/inspector-glob.ts +1 -1
  66. package/src/openapi-spec-generator.ts +23 -4
  67. package/src/pikku-cli-config.ts +31 -5
  68. package/src/schema-generator.ts +18 -36
  69. package/src/serialize-pikku-types.ts +6 -3
  70. package/src/serialize-typed-channel-map.ts +2 -2
  71. package/src/serialize-typed-http-map.ts +19 -3
  72. package/src/serialize-typed-rpc-map.ts +105 -0
  73. package/src/{utils.ts → utils/utils.ts} +20 -2
  74. package/dist/src/serialize-typed-function-map.d.ts +0 -4
  75. package/dist/src/serialize-typed-function-map.js +0 -107
  76. package/src/serialize-typed-function-map.ts +0 -151
  77. /package/dist/src/{serialize-import-map.d.ts → utils/serialize-import-map.d.ts} +0 -0
  78. /package/dist/src/{serialize-import-map.js → utils/serialize-import-map.js} +0 -0
  79. /package/src/{serialize-import-map.ts → utils/serialize-import-map.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  *
3
3
  */
4
- export const serializePikkuTypes = (userSessionTypeImport, userSessionTypeName, singletonServicesTypeImport, singletonServicesTypeName, sessionServicesTypeImport, servicesTypeName) => {
4
+ export const serializePikkuTypes = (userSessionTypeImport, userSessionTypeName, singletonServicesTypeImport, singletonServicesTypeName, sessionServicesTypeImport, rpcMapTypeImport) => {
5
5
  return `/**
6
6
  * This is used to provide the application types in the typescript project
7
7
  */
@@ -15,6 +15,7 @@ import { CoreAPIChannel, PikkuChannel, addChannel as addCoreChannel } from '@pik
15
15
  ${userSessionTypeImport}
16
16
  ${singletonServicesTypeImport}
17
17
  ${sessionServicesTypeImport}
18
+ ${rpcMapTypeImport}
18
19
 
19
20
  export type APIPermission<In = unknown, RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = CoreAPIPermission<In, RequiredServices, ${userSessionTypeName}>
20
21
  export type APIMiddleware<RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = PikkuMiddleware<RequiredServices, ${userSessionTypeName}>
@@ -23,7 +24,8 @@ type APIFunctionSessionless<
23
24
  In = unknown,
24
25
  Out = never,
25
26
  ChannelData = null, // null means optional channel
26
- RequiredServices extends Services = Services & (
27
+ RequiredServices extends Services = Services &
28
+ { rpc: TypedPikkuRPC } & (
27
29
  [ChannelData] extends [null]
28
30
  ? { channel?: PikkuChannel<unknown, Out> } // Optional channel
29
31
  : { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
@@ -34,7 +36,8 @@ type APIFunction<
34
36
  In = unknown,
35
37
  Out = never,
36
38
  ChannelData = null, // null means optional channel
37
- RequiredServices extends Services = Services & (
39
+ RequiredServices extends Services = Services &
40
+ { rpc: TypedPikkuRPC } & (
38
41
  [ChannelData] extends [null]
39
42
  ? { channel?: PikkuChannel<unknown, Out> } // Optional channel
40
43
  : { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
@@ -1,5 +1,5 @@
1
- import { serializeImportMap } from './serialize-import-map.js';
2
- import { generateCustomTypes } from './serialize-typed-http-map.js';
1
+ import { serializeImportMap } from './utils/serialize-import-map.js';
2
+ import { generateCustomTypes } from './utils/utils.js';
3
3
  export const serializeTypedChannelsMap = (relativeToPath, packageMappings, typesMap, channelsMeta) => {
4
4
  const { channels, requiredTypes } = generateChannels(channelsMeta);
5
5
  typesMap.customTypes.forEach(({ references }) => {
@@ -1,4 +1,5 @@
1
1
  import { HTTPRoutesMeta } from '@pikku/core/http';
2
2
  import { MetaInputTypes, TypesMap } from '@pikku/inspector';
3
- export declare const serializeTypedRoutesMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, routesMeta: HTTPRoutesMeta, metaTypes: MetaInputTypes) => string;
3
+ import { FunctionsMeta } from '@pikku/core';
4
+ export declare const serializeTypedRoutesMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, functionsMeta: FunctionsMeta, routesMeta: HTTPRoutesMeta, metaTypes: MetaInputTypes) => string;
4
5
  export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>): string;
@@ -1,9 +1,9 @@
1
- import { serializeImportMap } from './serialize-import-map.js';
2
- export const serializeTypedRoutesMap = (relativeToPath, packageMappings, typesMap, routesMeta, metaTypes) => {
1
+ import { serializeImportMap } from './utils/serialize-import-map.js';
2
+ export const serializeTypedRoutesMap = (relativeToPath, packageMappings, typesMap, functionsMeta, routesMeta, metaTypes) => {
3
3
  const requiredTypes = new Set();
4
4
  const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes);
5
5
  const serializedMetaTypes = generateMetaTypes(metaTypes, typesMap);
6
- const serializedRoutes = generateRoutes(routesMeta, typesMap, requiredTypes);
6
+ const serializedRoutes = generateRoutes(routesMeta, functionsMeta, typesMap, requiredTypes);
7
7
  const serializedImportMap = serializeImportMap(relativeToPath, packageMappings, typesMap, requiredTypes);
8
8
  return `/**
9
9
  * This provides the structure needed for typescript to be aware of routes and their return types
@@ -44,11 +44,17 @@ ${Array.from(typesMap.customTypes.entries())
44
44
  })
45
45
  .join('\n')}`;
46
46
  }
47
- function generateRoutes(routesMeta, typesMap, requiredTypes) {
47
+ function generateRoutes(routesMeta, functionsMeta, typesMap, requiredTypes) {
48
48
  // Initialize an object to collect routes
49
49
  const routesObj = {};
50
50
  for (const meta of routesMeta) {
51
- const { route, method, input, output } = meta;
51
+ const { route, method, pikkuFuncName } = meta;
52
+ const functionMeta = functionsMeta[pikkuFuncName];
53
+ if (!functionMeta) {
54
+ throw new Error(`Function ${pikkuFuncName} not found in functionsMeta. Please check your configuration.`);
55
+ }
56
+ const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined;
57
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined;
52
58
  // Initialize the route entry if it doesn't exist
53
59
  if (!routesObj[route]) {
54
60
  routesObj[route] = {};
@@ -0,0 +1,4 @@
1
+ import { TypesMap } from '@pikku/inspector';
2
+ import { FunctionsMeta } from '@pikku/core';
3
+ import { RPCMeta } from '../../core/src/rpc/rpc-types.js';
4
+ export declare const serializeTypedRPCMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, functionsMeta: FunctionsMeta, rpcMeta: Record<string, RPCMeta>) => string;
@@ -0,0 +1,66 @@
1
+ import { serializeImportMap } from './utils/serialize-import-map.js';
2
+ import { generateCustomTypes } from './utils/utils.js';
3
+ export const serializeTypedRPCMap = (relativeToPath, packageMappings, typesMap, functionsMeta, rpcMeta) => {
4
+ const requiredTypes = new Set();
5
+ const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes);
6
+ const serializedRPCs = generateRPCs(rpcMeta, functionsMeta, typesMap, requiredTypes);
7
+ const serializedImportMap = serializeImportMap(relativeToPath, packageMappings, typesMap, requiredTypes);
8
+ return `/**
9
+ * This provides the structure needed for typescript to be aware of RPCs and their return types
10
+ */
11
+
12
+ ${serializedImportMap}
13
+ ${serializedCustomTypes}
14
+
15
+ interface RPCHandler<I, O> {
16
+ input: I;
17
+ output: O;
18
+ }
19
+
20
+ ${serializedRPCs}
21
+
22
+ type RPCInvoke = <Name extends keyof RPCMap>(
23
+ name: Name,
24
+ data: RPCMap[Name]['input'],
25
+ options?: {
26
+ location?: 'local' | 'remote' | 'auto'
27
+ }
28
+ ) => Promise<RPCMap[Name]['output']>
29
+
30
+ export type TypedPikkuRPC = {
31
+ depth: number;
32
+ global: boolean;
33
+ invoke: RPCInvoke;
34
+ }
35
+ `;
36
+ };
37
+ function generateRPCs(rpcMeta, functionsMeta, typesMap, requiredTypes) {
38
+ // Initialize an object to collect RPCs
39
+ const rpcsObj = {};
40
+ // Iterate through RPC metadata
41
+ for (const [funcName, { pikkuFuncName }] of Object.entries(rpcMeta)) {
42
+ const functionMeta = functionsMeta[pikkuFuncName];
43
+ if (!functionMeta) {
44
+ throw new Error(`Function ${funcName} not found in functionsMeta. Please check your configuration.`);
45
+ }
46
+ const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined;
47
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined;
48
+ // Store the input and output types for RPCHandler
49
+ const inputType = input ? typesMap.getTypeMeta(input).uniqueName : 'null';
50
+ const outputType = output ? typesMap.getTypeMeta(output).uniqueName : 'null';
51
+ requiredTypes.add(inputType);
52
+ requiredTypes.add(outputType);
53
+ // Add RPC entry
54
+ rpcsObj[funcName] = {
55
+ inputType,
56
+ outputType,
57
+ };
58
+ }
59
+ // Build the RPCs object as a string
60
+ let rpcsStr = 'export type RPCMap = {\n';
61
+ for (const [funcName, handler] of Object.entries(rpcsObj)) {
62
+ rpcsStr += ` readonly '${funcName}': RPCHandler<${handler.inputType}, ${handler.outputType}>,\n`;
63
+ }
64
+ rpcsStr += '};\n';
65
+ return rpcsStr;
66
+ }
@@ -1,4 +1,4 @@
1
- import { InspectorState } from '@pikku/inspector';
1
+ import { InspectorState, TypesMap } from '@pikku/inspector';
2
2
  export declare const logPrimary: (message: string) => void;
3
3
  export declare const logSuccess: (message: string) => void;
4
4
  export declare const logInfo: (message: string) => void;
@@ -39,4 +39,5 @@ export declare const logCommandInfoAndTime: (commandStart: string, commandEnd: s
39
39
  export declare const logPikkuLogo: () => void;
40
40
  export declare const DO_NOT_MODIFY_COMMENT = "/**\n * This file was generated by the @pikku/cli\n */\n";
41
41
  export declare const serializeFileImports: (importType: string, outputPath: string, files: Set<string>, packageMappings?: Record<string, string>) => string;
42
+ export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>): string;
42
43
  export {};
@@ -123,7 +123,7 @@ const logo = `
123
123
  `;
124
124
  export const logPikkuLogo = () => {
125
125
  logPrimary(logo);
126
- const packageJson = JSON.parse(readFileSync(`${dirname(__filename)}/../../package.json`, 'utf-8'));
126
+ const packageJson = JSON.parse(readFileSync(`${dirname(__filename)}/../../../package.json`, 'utf-8'));
127
127
  logPrimary(`⚙️ Welcome to the Pikku CLI (v${packageJson.version})\n`);
128
128
  };
129
129
  // TODO: add version back in once the ESM dust settles
@@ -143,3 +143,17 @@ export const serializeFileImports = (importType, outputPath, files, packageMappi
143
143
  });
144
144
  return serializedOutput.join('\n');
145
145
  };
146
+ export function generateCustomTypes(typesMap, requiredTypes) {
147
+ return `
148
+ // Custom types are those that are defined directly within generics
149
+ // or are broken into simpler types
150
+ ${Array.from(typesMap.customTypes.entries())
151
+ .map(([name, { type, references }]) => {
152
+ references.forEach((name) => {
153
+ const originalName = typesMap.getTypeMeta(name).originalName;
154
+ requiredTypes.add(originalName);
155
+ });
156
+ return `export type ${name} = ${type}`;
157
+ })
158
+ .join('\n')}`;
159
+ }
@@ -1 +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"}
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-map.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-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-http-map.ts","../src/serialize-typed-rpc-map.ts","../src/serialize-websocket-wrapper.ts","../src/utils/serialize-import-map.ts","../src/utils/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.1",
3
+ "version": "0.7.3",
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.3",
26
- "@pikku/inspector": "^0.7.3",
25
+ "@pikku/core": "^0.7.5",
26
+ "@pikku/inspector": "^0.7.4",
27
27
  "@types/cookie": "^0.6.0",
28
28
  "@types/uuid": "^10.0.0",
29
29
  "chalk": "^5.4.1",
@@ -1,7 +1,7 @@
1
1
  import * as path from 'path'
2
2
  import { glob } from 'tinyglobby'
3
3
  import { InspectorFilters, InspectorState, inspect } from '@pikku/inspector'
4
- import { logCommandInfoAndTime } from './utils.js'
4
+ import { logCommandInfoAndTime } from './utils/utils.js'
5
5
 
6
6
  export const inspectorGlob = async (
7
7
  rootDir: string,
@@ -1,4 +1,4 @@
1
- import { HTTPRoutesMeta, pikkuState } from '@pikku/core'
1
+ import { FunctionsMeta, HTTPRoutesMeta, pikkuState } from '@pikku/core'
2
2
  import _convertSchema from '@openapi-contrib/json-schema-to-openapi-schema'
3
3
  const convertSchema =
4
4
  'default' in _convertSchema ? (_convertSchema.default as any) : _convertSchema
@@ -75,12 +75,16 @@ const getErrorResponseForConstructorName = (constructorName: string) => {
75
75
  }
76
76
 
77
77
  const convertSchemasToBodyPayloads = async (
78
+ functionsMeta: FunctionsMeta,
78
79
  routesMeta: HTTPRoutesMeta,
79
80
  schemas: Record<string, any>
80
81
  ) => {
81
82
  const requiredSchemas = new Set(
82
83
  routesMeta
83
- .map(({ inputTypes, output }) => [inputTypes?.body, output])
84
+ .map(({ inputTypes, pikkuFuncName }) => {
85
+ const output = functionsMeta[pikkuFuncName]?.outputs?.[0]
86
+ return [inputTypes?.body, output]
87
+ })
84
88
  .flat()
85
89
  .filter((schema) => !!schema)
86
90
  )
@@ -100,6 +104,7 @@ const convertSchemasToBodyPayloads = async (
100
104
  }
101
105
 
102
106
  export async function generateOpenAPISpec(
107
+ functionsMeta: FunctionsMeta,
103
108
  routeMeta: HTTPRoutesMeta,
104
109
  schemas: Record<string, any>,
105
110
  additionalInfo: OpenAPISpecInfo
@@ -107,7 +112,17 @@ export async function generateOpenAPISpec(
107
112
  const paths: Record<string, any> = {}
108
113
 
109
114
  routeMeta.forEach((meta) => {
110
- const { route, method, inputTypes, output, params, query, docs } = meta
115
+ const { route, method, inputTypes, pikkuFuncName, params, query, docs } =
116
+ meta
117
+ const functionMeta = functionsMeta[pikkuFuncName]
118
+ if (!functionMeta) {
119
+ console.error(
120
+ `• No function metadata found for '${pikkuFuncName}' in route '${route}'.`
121
+ )
122
+ return
123
+ }
124
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined
125
+
111
126
  const path = route.replace(/:(\w+)/g, '{$1}') // Convert ":param" to "{param}"
112
127
 
113
128
  if (!paths[path]) {
@@ -194,7 +209,11 @@ export async function generateOpenAPISpec(
194
209
  servers: additionalInfo.servers,
195
210
  paths,
196
211
  components: {
197
- schemas: await convertSchemasToBodyPayloads(routeMeta, schemas),
212
+ schemas: await convertSchemasToBodyPayloads(
213
+ functionsMeta,
214
+ routeMeta,
215
+ schemas
216
+ ),
198
217
  responses: {},
199
218
  parameters: {},
200
219
  examples: {},
@@ -4,20 +4,37 @@ import { OpenAPISpecInfo } from './openapi-spec-generator.js'
4
4
  import { InspectorFilters } from '@pikku/inspector'
5
5
 
6
6
  export interface PikkuCLICoreOutputFiles {
7
+ // Base directory
7
8
  outDir?: string
9
+
10
+ // Schema and types
11
+ schemaDirectory: string
12
+ typesDeclarationFile: string
13
+
14
+ // Function definitions
8
15
  functionsFile: string
9
16
  functionsMetaFile: string
17
+
18
+ // HTTP routes
10
19
  httpRoutesFile: string
11
20
  httpRoutesMetaFile: string
21
+ httpRoutesMapDeclarationFile: string
22
+
23
+ // Channels
12
24
  channelsFile: string
13
25
  channelsMetaFile: string
26
+ channelsMapDeclarationFile: string
27
+
28
+ // RPC
29
+ rpcFile: string
30
+ rpcMetaFile: string
31
+ rpcMapDeclarationFile: string
32
+
33
+ // Schedulers
14
34
  schedulersFile: string
15
35
  schedulersMetaFile: string
16
- rpcFile: string
17
- schemaDirectory: string
18
- typesDeclarationFile: string
19
- httpRoutesMapDeclarationFile: string
20
- channelsMapDeclarationFile: string
36
+
37
+ // Application bootstrap
21
38
  bootstrapFile: string
22
39
  }
23
40
 
@@ -139,6 +156,15 @@ const _getPikkuCLIConfig = async (
139
156
  if (!result.rpcFile) {
140
157
  result.rpcFile = join(result.outDir, 'pikku-rpc.gen.ts')
141
158
  }
159
+ if (!result.rpcMetaFile) {
160
+ result.rpcMetaFile = join(result.outDir, 'pikku-rpc-meta.gen.ts')
161
+ }
162
+ if (!result.rpcMapDeclarationFile) {
163
+ result.rpcMapDeclarationFile = join(
164
+ result.outDir,
165
+ 'pikku-rpc-map.gen.ts'
166
+ )
167
+ }
142
168
  if (!result.httpRoutesFile) {
143
169
  result.httpRoutesFile = join(result.outDir, 'pikku-http-routes.gen.ts')
144
170
  }
@@ -1,45 +1,27 @@
1
1
  import { createGenerator, RootlessError } from 'ts-json-schema-generator'
2
- import { logInfo, writeFileInDir } from './utils.js'
2
+ import { logInfo, writeFileInDir } from './utils/utils.js'
3
3
  import { mkdir, writeFile } from 'fs/promises'
4
- import { JSONValue } from '@pikku/core'
4
+ import { FunctionsMeta, JSONValue } from '@pikku/core'
5
5
  import { HTTPRoutesMeta } from '@pikku/core/http'
6
6
  import { TypesMap } from '@pikku/inspector'
7
7
 
8
8
  export async function generateSchemas(
9
9
  tsconfig: string,
10
- typesMaps: TypesMap[],
10
+ typesMap: TypesMap,
11
+ functionMeta: FunctionsMeta,
11
12
  httpRoutesMeta: HTTPRoutesMeta
12
13
  ): 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)
14
+ const schemasSet = new Set(typesMap.customTypes.keys())
15
+ for (const { inputs, outputs } of Object.values(functionMeta)) {
16
+ const types = [...(inputs || []), ...(outputs || [])]
17
+ for (const type of types) {
18
+ const uniqueName = typesMap.getUniqueName(type)
19
+ if (uniqueName) {
20
+ schemasSet.add(uniqueName)
31
21
  }
32
22
  }
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
- // }
23
+ }
24
+ for (const { inputTypes } of httpRoutesMeta) {
43
25
  if (inputTypes?.body) {
44
26
  schemasSet.add(inputTypes.body)
45
27
  }
@@ -78,7 +60,7 @@ export async function saveSchemas(
78
60
  schemaParentDir: string,
79
61
  schemas: Record<string, JSONValue>,
80
62
  typesMap: TypesMap,
81
- routesMeta: HTTPRoutesMeta,
63
+ functionsMeta: FunctionsMeta,
82
64
  supportsImportAttributes: boolean
83
65
  ) {
84
66
  await writeFileInDir(
@@ -87,10 +69,10 @@ export async function saveSchemas(
87
69
  )
88
70
 
89
71
  const desiredSchemas = new Set([
90
- ...routesMeta
91
- .map(({ input, output }) => [
92
- input ? typesMap.getUniqueName(input) : undefined,
93
- output ? typesMap.getUniqueName(output) : undefined,
72
+ ...Object.values(functionsMeta)
73
+ .map(({ inputs, outputs }) => [
74
+ inputs?.[0] ? typesMap.getUniqueName(inputs[0]) : undefined,
75
+ outputs?.[0] ? typesMap.getUniqueName(outputs[0]) : undefined,
94
76
  ])
95
77
  .flat()
96
78
  .filter(
@@ -7,7 +7,7 @@ export const serializePikkuTypes = (
7
7
  singletonServicesTypeImport: string,
8
8
  singletonServicesTypeName: string,
9
9
  sessionServicesTypeImport: string,
10
- servicesTypeName: string
10
+ rpcMapTypeImport: string
11
11
  ) => {
12
12
  return `/**
13
13
  * This is used to provide the application types in the typescript project
@@ -22,6 +22,7 @@ import { CoreAPIChannel, PikkuChannel, addChannel as addCoreChannel } from '@pik
22
22
  ${userSessionTypeImport}
23
23
  ${singletonServicesTypeImport}
24
24
  ${sessionServicesTypeImport}
25
+ ${rpcMapTypeImport}
25
26
 
26
27
  export type APIPermission<In = unknown, RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = CoreAPIPermission<In, RequiredServices, ${userSessionTypeName}>
27
28
  export type APIMiddleware<RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = PikkuMiddleware<RequiredServices, ${userSessionTypeName}>
@@ -30,7 +31,8 @@ type APIFunctionSessionless<
30
31
  In = unknown,
31
32
  Out = never,
32
33
  ChannelData = null, // null means optional channel
33
- RequiredServices extends Services = Services & (
34
+ RequiredServices extends Services = Services &
35
+ { rpc: TypedPikkuRPC } & (
34
36
  [ChannelData] extends [null]
35
37
  ? { channel?: PikkuChannel<unknown, Out> } // Optional channel
36
38
  : { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
@@ -41,7 +43,8 @@ type APIFunction<
41
43
  In = unknown,
42
44
  Out = never,
43
45
  ChannelData = null, // null means optional channel
44
- RequiredServices extends Services = Services & (
46
+ RequiredServices extends Services = Services &
47
+ { rpc: TypedPikkuRPC } & (
45
48
  [ChannelData] extends [null]
46
49
  ? { channel?: PikkuChannel<unknown, Out> } // Optional channel
47
50
  : { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
@@ -1,7 +1,7 @@
1
1
  import { ChannelsMeta } from '@pikku/core/channel'
2
- import { serializeImportMap } from './serialize-import-map.js'
2
+ import { serializeImportMap } from './utils/serialize-import-map.js'
3
3
  import { TypesMap } from '@pikku/inspector'
4
- import { generateCustomTypes } from './serialize-typed-http-map.js'
4
+ import { generateCustomTypes } from './utils/utils.js'
5
5
 
6
6
  export const serializeTypedChannelsMap = (
7
7
  relativeToPath: string,
@@ -1,18 +1,25 @@
1
1
  import { HTTPRoutesMeta } from '@pikku/core/http'
2
- import { serializeImportMap } from './serialize-import-map.js'
2
+ import { serializeImportMap } from './utils/serialize-import-map.js'
3
3
  import { MetaInputTypes, TypesMap } from '@pikku/inspector'
4
+ import { FunctionsMeta } from '@pikku/core'
4
5
 
5
6
  export const serializeTypedRoutesMap = (
6
7
  relativeToPath: string,
7
8
  packageMappings: Record<string, string>,
8
9
  typesMap: TypesMap,
10
+ functionsMeta: FunctionsMeta,
9
11
  routesMeta: HTTPRoutesMeta,
10
12
  metaTypes: MetaInputTypes
11
13
  ) => {
12
14
  const requiredTypes = new Set<string>()
13
15
  const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes)
14
16
  const serializedMetaTypes = generateMetaTypes(metaTypes, typesMap)
15
- const serializedRoutes = generateRoutes(routesMeta, typesMap, requiredTypes)
17
+ const serializedRoutes = generateRoutes(
18
+ routesMeta,
19
+ functionsMeta,
20
+ typesMap,
21
+ requiredTypes
22
+ )
16
23
 
17
24
  const serializedImportMap = serializeImportMap(
18
25
  relativeToPath,
@@ -67,6 +74,7 @@ ${Array.from(typesMap.customTypes.entries())
67
74
 
68
75
  function generateRoutes(
69
76
  routesMeta: HTTPRoutesMeta,
77
+ functionsMeta: FunctionsMeta,
70
78
  typesMap: TypesMap,
71
79
  requiredTypes: Set<string>
72
80
  ) {
@@ -77,7 +85,15 @@ function generateRoutes(
77
85
  > = {}
78
86
 
79
87
  for (const meta of routesMeta) {
80
- const { route, method, input, output } = meta
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
81
97
 
82
98
  // Initialize the route entry if it doesn't exist
83
99
  if (!routesObj[route]) {
@@ -0,0 +1,105 @@
1
+ import { serializeImportMap } from './utils/serialize-import-map.js'
2
+ import { TypesMap } from '@pikku/inspector'
3
+ import { FunctionsMeta } from '@pikku/core'
4
+ import { generateCustomTypes } from './utils/utils.js'
5
+ import { RPCMeta } from '../../core/src/rpc/rpc-types.js'
6
+
7
+ export const serializeTypedRPCMap = (
8
+ relativeToPath: string,
9
+ packageMappings: Record<string, string>,
10
+ typesMap: TypesMap,
11
+ functionsMeta: FunctionsMeta,
12
+ rpcMeta: Record<string, RPCMeta>
13
+ ) => {
14
+ const requiredTypes = new Set<string>()
15
+ const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes)
16
+ const serializedRPCs = generateRPCs(
17
+ rpcMeta,
18
+ functionsMeta,
19
+ typesMap,
20
+ requiredTypes
21
+ )
22
+
23
+ const serializedImportMap = serializeImportMap(
24
+ relativeToPath,
25
+ packageMappings,
26
+ typesMap,
27
+ requiredTypes
28
+ )
29
+
30
+ return `/**
31
+ * This provides the structure needed for typescript to be aware of RPCs and their return types
32
+ */
33
+
34
+ ${serializedImportMap}
35
+ ${serializedCustomTypes}
36
+
37
+ interface RPCHandler<I, O> {
38
+ input: I;
39
+ output: O;
40
+ }
41
+
42
+ ${serializedRPCs}
43
+
44
+ type RPCInvoke = <Name extends keyof RPCMap>(
45
+ name: Name,
46
+ data: RPCMap[Name]['input'],
47
+ options?: {
48
+ location?: 'local' | 'remote' | 'auto'
49
+ }
50
+ ) => Promise<RPCMap[Name]['output']>
51
+
52
+ export type TypedPikkuRPC = {
53
+ depth: number;
54
+ global: boolean;
55
+ invoke: RPCInvoke;
56
+ }
57
+ `
58
+ }
59
+
60
+ function generateRPCs(
61
+ rpcMeta: Record<string, RPCMeta>,
62
+ functionsMeta: FunctionsMeta,
63
+ typesMap: TypesMap,
64
+ requiredTypes: Set<string>
65
+ ) {
66
+ // Initialize an object to collect RPCs
67
+ const rpcsObj: Record<string, { inputType: string; outputType: string }> = {}
68
+
69
+ // Iterate through RPC metadata
70
+ for (const [funcName, { pikkuFuncName }] of Object.entries(rpcMeta)) {
71
+ const functionMeta = functionsMeta[pikkuFuncName]
72
+ if (!functionMeta) {
73
+ throw new Error(
74
+ `Function ${funcName} not found in functionsMeta. Please check your configuration.`
75
+ )
76
+ }
77
+
78
+ const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined
79
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined
80
+
81
+ // Store the input and output types for RPCHandler
82
+ const inputType = input ? typesMap.getTypeMeta(input).uniqueName : 'null'
83
+ const outputType = output ? typesMap.getTypeMeta(output).uniqueName : 'null'
84
+
85
+ requiredTypes.add(inputType)
86
+ requiredTypes.add(outputType)
87
+
88
+ // Add RPC entry
89
+ rpcsObj[funcName] = {
90
+ inputType,
91
+ outputType,
92
+ }
93
+ }
94
+
95
+ // Build the RPCs object as a string
96
+ let rpcsStr = 'export type RPCMap = {\n'
97
+
98
+ for (const [funcName, handler] of Object.entries(rpcsObj)) {
99
+ rpcsStr += ` readonly '${funcName}': RPCHandler<${handler.inputType}, ${handler.outputType}>,\n`
100
+ }
101
+
102
+ rpcsStr += '};\n'
103
+
104
+ return rpcsStr
105
+ }