@pikku/cli 0.8.3 → 0.9.0

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 (172) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/bin/pikku-all.ts +41 -32
  3. package/bin/pikku-fetch.ts +1 -1
  4. package/bin/pikku-openapi.ts +2 -2
  5. package/bin/pikku-queue-service.ts +2 -2
  6. package/bin/pikku-websocket.ts +1 -1
  7. package/cli.schema.json +30 -30
  8. package/dist/bin/pikku-all.js +32 -32
  9. package/dist/bin/pikku-fetch.js +1 -1
  10. package/dist/bin/pikku-openapi.js +2 -2
  11. package/dist/bin/pikku-queue-service.js +2 -2
  12. package/dist/bin/pikku-websocket.js +1 -1
  13. package/dist/bin/pikku.js +0 -0
  14. package/dist/src/events/channels/pikku-channels.js +3 -3
  15. package/dist/src/events/channels/pikku-command-channels.js +3 -3
  16. package/dist/src/events/channels/serialize-typed-channel-map.js +2 -2
  17. package/dist/src/events/channels/serialize-websocket-wrapper.js +15 -1
  18. package/dist/src/events/fetch/index.js +2 -2
  19. package/dist/src/events/http/openapi-spec-generator.d.ts +2 -2
  20. package/dist/src/events/http/pikku-command-http-map.js +4 -4
  21. package/dist/src/events/http/pikku-command-http-routes.js +3 -3
  22. package/dist/src/events/http/pikku-command-nextjs.d.ts +2 -0
  23. package/dist/src/events/http/pikku-command-nextjs.js +36 -0
  24. package/dist/src/events/http/pikku-http-routes.js +3 -3
  25. package/dist/src/events/http/serialize-typed-http-map.d.ts +2 -2
  26. package/dist/src/events/http/serialize-typed-http-map.js +11 -11
  27. package/dist/src/events/mcp/pikku-command-mcp.js +3 -3
  28. package/dist/src/events/queue/pikku-command-queue-service.js +6 -6
  29. package/dist/src/events/queue/pikku-command-queue.js +3 -3
  30. package/dist/src/events/queue/pikku-queue.js +3 -3
  31. package/dist/src/events/rpc/index.d.ts +2 -0
  32. package/dist/src/events/rpc/index.js +12 -0
  33. package/dist/src/events/rpc/pikku-command-rpc-client.js +6 -6
  34. package/dist/src/events/rpc/pikku-command-rpc.js +2 -2
  35. package/dist/src/events/rpc/pikku-rpc.js +2 -2
  36. package/dist/src/events/rpc/serialize-rpc-wrapper.js +40 -1
  37. package/dist/src/events/scheduler/pikku-command-scheduler.js +3 -3
  38. package/dist/src/inspector-glob.js +2 -2
  39. package/dist/src/pikku-cli-config.d.ts +17 -17
  40. package/dist/src/pikku-cli-config.js +31 -31
  41. package/dist/src/runtimes/nextjs/pikku-command-nextjs.js +3 -3
  42. package/dist/src/runtimes/nextjs/serialize-nextjs-backend-wrapper.js +29 -29
  43. package/dist/src/runtimes/nextjs/serialize-nextjs-http-wrapper.js +28 -28
  44. package/dist/src/schema-generator.d.ts +2 -2
  45. package/dist/src/schema-generator.js +2 -2
  46. package/dist/src/serialize-pikku-types.js +67 -67
  47. package/dist/src/wirings/channels/pikku-channels.d.ts +2 -0
  48. package/dist/src/wirings/channels/pikku-channels.js +9 -0
  49. package/dist/src/wirings/channels/pikku-command-channels-map.d.ts +2 -0
  50. package/dist/src/wirings/channels/pikku-command-channels-map.js +8 -0
  51. package/dist/src/wirings/channels/pikku-command-channels.d.ts +2 -0
  52. package/dist/src/wirings/channels/pikku-command-channels.js +9 -0
  53. package/dist/src/wirings/channels/pikku-command-websocket-typed.d.ts +2 -0
  54. package/dist/src/wirings/channels/pikku-command-websocket-typed.js +15 -0
  55. package/dist/src/wirings/channels/serialize-typed-channel-map.d.ts +4 -0
  56. package/dist/src/wirings/channels/serialize-typed-channel-map.js +111 -0
  57. package/dist/src/wirings/channels/serialize-websocket-wrapper.d.ts +1 -0
  58. package/dist/src/wirings/channels/serialize-websocket-wrapper.js +75 -0
  59. package/dist/src/wirings/fetch/index.d.ts +2 -0
  60. package/dist/src/wirings/fetch/index.js +12 -0
  61. package/dist/src/wirings/functions/pikku-command-function-types.d.ts +2 -0
  62. package/dist/src/wirings/functions/pikku-command-function-types.js +13 -0
  63. package/dist/src/wirings/functions/pikku-command-functions.d.ts +6 -0
  64. package/dist/src/wirings/functions/pikku-command-functions.js +35 -0
  65. package/dist/src/wirings/functions/pikku-command-services.d.ts +3 -0
  66. package/dist/src/wirings/functions/pikku-command-services.js +73 -0
  67. package/dist/src/wirings/functions/pikku-function-types.d.ts +2 -0
  68. package/dist/src/wirings/functions/pikku-function-types.js +13 -0
  69. package/dist/src/wirings/functions/pikku-functions.d.ts +6 -0
  70. package/dist/src/wirings/functions/pikku-functions.js +35 -0
  71. package/dist/src/wirings/http/openapi-spec-generator.d.ts +79 -0
  72. package/dist/src/wirings/http/openapi-spec-generator.js +145 -0
  73. package/dist/src/wirings/http/pikku-command-http-map.d.ts +2 -0
  74. package/dist/src/wirings/http/pikku-command-http-map.js +8 -0
  75. package/dist/src/wirings/http/pikku-command-http-routes.d.ts +2 -0
  76. package/dist/src/wirings/http/pikku-command-http-routes.js +9 -0
  77. package/dist/src/wirings/http/pikku-command-openapi.d.ts +2 -0
  78. package/dist/src/wirings/http/pikku-command-openapi.js +20 -0
  79. package/dist/src/wirings/http/pikku-http-routes.d.ts +2 -0
  80. package/dist/src/wirings/http/pikku-http-routes.js +9 -0
  81. package/dist/src/wirings/http/serialize-fetch-wrapper.d.ts +1 -0
  82. package/dist/src/wirings/http/serialize-fetch-wrapper.js +67 -0
  83. package/dist/src/wirings/http/serialize-typed-http-map.d.ts +4 -0
  84. package/dist/src/wirings/http/serialize-typed-http-map.js +100 -0
  85. package/dist/src/wirings/mcp/pikku-command-mcp-json.d.ts +2 -0
  86. package/dist/src/wirings/mcp/pikku-command-mcp-json.js +13 -0
  87. package/dist/src/wirings/mcp/pikku-command-mcp.d.ts +2 -0
  88. package/dist/src/wirings/mcp/pikku-command-mcp.js +54 -0
  89. package/dist/src/wirings/mcp/serialize-mcp-json.d.ts +5 -0
  90. package/dist/src/wirings/mcp/serialize-mcp-json.js +101 -0
  91. package/dist/src/wirings/queue/pikku-command-queue-map.d.ts +2 -0
  92. package/dist/src/wirings/queue/pikku-command-queue-map.js +8 -0
  93. package/dist/src/wirings/queue/pikku-command-queue-service.d.ts +2 -0
  94. package/dist/src/wirings/queue/pikku-command-queue-service.js +15 -0
  95. package/dist/src/wirings/queue/pikku-command-queue.d.ts +2 -0
  96. package/dist/src/wirings/queue/pikku-command-queue.js +10 -0
  97. package/dist/src/wirings/queue/pikku-queue-map.d.ts +2 -0
  98. package/dist/src/wirings/queue/pikku-queue-map.js +8 -0
  99. package/dist/src/wirings/queue/pikku-queue.d.ts +2 -0
  100. package/dist/src/wirings/queue/pikku-queue.js +10 -0
  101. package/dist/src/wirings/queue/serialize-queue-map.d.ts +4 -0
  102. package/dist/src/wirings/queue/serialize-queue-map.js +77 -0
  103. package/dist/src/wirings/queue/serialize-queue-meta.d.ts +2 -0
  104. package/dist/src/wirings/queue/serialize-queue-meta.js +6 -0
  105. package/dist/src/wirings/queue/serialize-queue-wrapper.d.ts +1 -0
  106. package/dist/src/wirings/queue/serialize-queue-wrapper.js +35 -0
  107. package/dist/src/wirings/rpc/pikku-command-rpc-client.d.ts +2 -0
  108. package/dist/src/wirings/rpc/pikku-command-rpc-client.js +15 -0
  109. package/dist/src/wirings/rpc/pikku-command-rpc-map.d.ts +2 -0
  110. package/dist/src/wirings/rpc/pikku-command-rpc-map.js +8 -0
  111. package/dist/src/wirings/rpc/pikku-command-rpc.d.ts +2 -0
  112. package/dist/src/wirings/rpc/pikku-command-rpc.js +6 -0
  113. package/dist/src/wirings/rpc/pikku-rpc.d.ts +2 -0
  114. package/dist/src/wirings/rpc/pikku-rpc.js +6 -0
  115. package/dist/src/wirings/rpc/serialize-rpc-wrapper.d.ts +1 -0
  116. package/dist/src/wirings/rpc/serialize-rpc-wrapper.js +68 -0
  117. package/dist/src/wirings/rpc/serialize-typed-rpc-map.d.ts +4 -0
  118. package/dist/src/wirings/rpc/serialize-typed-rpc-map.js +66 -0
  119. package/dist/src/wirings/scheduler/pikku-command-scheduler.d.ts +2 -0
  120. package/dist/src/wirings/scheduler/pikku-command-scheduler.js +10 -0
  121. package/dist/src/wirings/scheduler/serialize-scheduler-meta.d.ts +2 -0
  122. package/dist/src/wirings/scheduler/serialize-scheduler-meta.js +10 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -1
  124. package/lcov.info +582 -0
  125. package/package.json +3 -3
  126. package/src/inspector-glob.ts +2 -2
  127. package/src/pikku-cli-config.ts +67 -52
  128. package/src/runtimes/nextjs/pikku-command-nextjs.ts +3 -3
  129. package/src/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts +29 -29
  130. package/src/runtimes/nextjs/serialize-nextjs-http-wrapper.ts +28 -28
  131. package/src/schema-generator.ts +3 -3
  132. package/src/serialize-pikku-types.ts +67 -67
  133. package/src/{events → wirings}/channels/pikku-channels.ts +5 -4
  134. package/src/{events → wirings}/channels/pikku-command-channels.ts +5 -4
  135. package/src/{events → wirings}/channels/serialize-typed-channel-map.ts +3 -3
  136. package/src/{events → wirings}/channels/serialize-websocket-wrapper.ts +19 -5
  137. package/src/{events → wirings}/fetch/index.ts +2 -2
  138. package/src/{events → wirings}/http/openapi-spec-generator.ts +3 -3
  139. package/src/{events → wirings}/http/pikku-command-http-map.ts +5 -5
  140. package/src/{events → wirings}/http/pikku-command-http-routes.ts +5 -5
  141. package/src/{events → wirings}/http/pikku-http-routes.ts +5 -5
  142. package/src/wirings/http/serialize-fetch-wrapper.ts +67 -0
  143. package/src/{events → wirings}/http/serialize-typed-http-map.ts +17 -17
  144. package/src/{events → wirings}/mcp/pikku-command-mcp.ts +5 -5
  145. package/src/{events → wirings}/queue/pikku-command-queue-service.ts +9 -6
  146. package/src/{events → wirings}/queue/pikku-command-queue.ts +8 -5
  147. package/src/{events → wirings}/queue/pikku-queue.ts +8 -5
  148. package/src/{events → wirings}/rpc/pikku-command-rpc-client.ts +9 -6
  149. package/src/{events → wirings}/rpc/pikku-command-rpc.ts +2 -2
  150. package/src/{events → wirings}/rpc/pikku-rpc.ts +2 -2
  151. package/src/wirings/rpc/serialize-rpc-wrapper.ts +68 -0
  152. package/src/{events → wirings}/scheduler/pikku-command-scheduler.ts +8 -4
  153. package/src/events/http/serialize-fetch-wrapper.ts +0 -67
  154. package/src/events/rpc/serialize-rpc-wrapper.ts +0 -29
  155. /package/src/{events → wirings}/channels/pikku-command-channels-map.ts +0 -0
  156. /package/src/{events → wirings}/channels/pikku-command-websocket-typed.ts +0 -0
  157. /package/src/{events → wirings}/functions/pikku-command-function-types.ts +0 -0
  158. /package/src/{events → wirings}/functions/pikku-command-functions.ts +0 -0
  159. /package/src/{events → wirings}/functions/pikku-command-services.ts +0 -0
  160. /package/src/{events → wirings}/functions/pikku-function-types.ts +0 -0
  161. /package/src/{events → wirings}/functions/pikku-functions.ts +0 -0
  162. /package/src/{events → wirings}/http/pikku-command-openapi.ts +0 -0
  163. /package/src/{events → wirings}/mcp/pikku-command-mcp-json.ts +0 -0
  164. /package/src/{events → wirings}/mcp/serialize-mcp-json.ts +0 -0
  165. /package/src/{events → wirings}/queue/pikku-command-queue-map.ts +0 -0
  166. /package/src/{events → wirings}/queue/pikku-queue-map.ts +0 -0
  167. /package/src/{events → wirings}/queue/serialize-queue-map.ts +0 -0
  168. /package/src/{events → wirings}/queue/serialize-queue-meta.ts +0 -0
  169. /package/src/{events → wirings}/queue/serialize-queue-wrapper.ts +0 -0
  170. /package/src/{events → wirings}/rpc/pikku-command-rpc-map.ts +0 -0
  171. /package/src/{events → wirings}/rpc/serialize-typed-rpc-map.ts +0 -0
  172. /package/src/{events → wirings}/scheduler/serialize-scheduler-meta.ts +0 -0
@@ -0,0 +1,145 @@
1
+ import { pikkuState } from '@pikku/core';
2
+ import _convertSchema from '@openapi-contrib/json-schema-to-openapi-schema';
3
+ const convertSchema = 'default' in _convertSchema ? _convertSchema.default : _convertSchema;
4
+ const getErrorResponseForConstructorName = (constructorName) => {
5
+ const errors = Array.from(pikkuState('misc', 'errors').entries());
6
+ const foundError = errors.find(([e]) => e.name === constructorName);
7
+ if (foundError) {
8
+ return foundError[1];
9
+ }
10
+ return undefined;
11
+ };
12
+ const convertSchemasToBodyPayloads = async (functionsMeta, routesMeta, schemas) => {
13
+ const requiredSchemas = new Set(routesMeta
14
+ .map(({ inputTypes, pikkuFuncName }) => {
15
+ const output = functionsMeta[pikkuFuncName]?.outputs?.[0];
16
+ return [inputTypes?.body, output];
17
+ })
18
+ .flat()
19
+ .filter((schema) => !!schema));
20
+ const convertedEntries = await Promise.all(Object.entries(schemas).map(async ([key, schema]) => {
21
+ if (requiredSchemas.has(key)) {
22
+ const convertedSchema = await convertSchema(schema, {
23
+ convertUnreferencedDefinitions: false,
24
+ dereference: { circular: 'ignore' },
25
+ });
26
+ return [key, convertedSchema];
27
+ }
28
+ return;
29
+ }));
30
+ return Object.fromEntries(convertedEntries.filter((s) => !!s));
31
+ };
32
+ export async function generateOpenAPISpec(functionsMeta, routeMeta, schemas, additionalInfo) {
33
+ const paths = {};
34
+ routeMeta.forEach((meta) => {
35
+ const { route, method, inputTypes, pikkuFuncName, params, query, docs } = meta;
36
+ const functionMeta = functionsMeta[pikkuFuncName];
37
+ if (!functionMeta) {
38
+ console.error(`• No function metadata found for '${pikkuFuncName}' in route '${route}'.`);
39
+ return;
40
+ }
41
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined;
42
+ const path = route.replace(/:(\w+)/g, '{$1}'); // Convert ":param" to "{param}"
43
+ if (!paths[path]) {
44
+ paths[path] = {};
45
+ }
46
+ const responses = {};
47
+ docs?.errors?.forEach((error) => {
48
+ const errorResponse = getErrorResponseForConstructorName(error);
49
+ if (errorResponse) {
50
+ responses[errorResponse.status] = {
51
+ description: errorResponse.message,
52
+ };
53
+ }
54
+ });
55
+ const operation = {
56
+ description: docs?.description ||
57
+ `This endpoint handles the ${method.toUpperCase()} request for the route ${route}.`,
58
+ tags: docs?.tags || [route.split('/')[1] || 'default'],
59
+ parameters: [],
60
+ responses: {
61
+ ...responses,
62
+ '200': {
63
+ description: 'Successful response',
64
+ content: output
65
+ ? {
66
+ 'application/json': {
67
+ schema: typeof output === 'string' &&
68
+ ['boolean', 'string', 'number'].includes(output)
69
+ ? { type: output }
70
+ : { $ref: `#/components/schemas/${output}` },
71
+ },
72
+ }
73
+ : undefined,
74
+ },
75
+ },
76
+ };
77
+ const bodyType = inputTypes?.body;
78
+ if (bodyType) {
79
+ operation.requestBody = {
80
+ required: true,
81
+ content: {
82
+ 'application/json': {
83
+ schema: typeof bodyType === 'string' &&
84
+ ['boolean', 'string', 'number'].includes(bodyType)
85
+ ? { type: bodyType }
86
+ : { $ref: `#/components/schemas/${bodyType}` },
87
+ },
88
+ },
89
+ };
90
+ }
91
+ if (params) {
92
+ operation.parameters = params.map((param) => ({
93
+ name: param,
94
+ in: 'path',
95
+ required: true,
96
+ schema: { type: 'string' },
97
+ }));
98
+ }
99
+ if (query) {
100
+ operation.parameters.push(...query.map((query) => ({
101
+ name: query,
102
+ in: 'query',
103
+ required: false,
104
+ schema: { type: 'string' },
105
+ })));
106
+ }
107
+ paths[path][method] = operation;
108
+ });
109
+ return {
110
+ openapi: '3.1.0',
111
+ info: additionalInfo.info,
112
+ servers: additionalInfo.servers,
113
+ paths,
114
+ components: {
115
+ schemas: await convertSchemasToBodyPayloads(functionsMeta, routeMeta, schemas),
116
+ responses: {},
117
+ parameters: {},
118
+ examples: {},
119
+ requestBodies: {},
120
+ headers: {},
121
+ securitySchemes: additionalInfo.securitySchemes || {
122
+ ApiKeyAuth: {
123
+ type: 'apiKey',
124
+ in: 'header',
125
+ name: 'x-api-key',
126
+ },
127
+ BearerAuth: {
128
+ type: 'http',
129
+ scheme: 'bearer',
130
+ },
131
+ },
132
+ },
133
+ security: additionalInfo.security || [
134
+ {
135
+ ApiKeyAuth: [],
136
+ },
137
+ {
138
+ BearerAuth: [],
139
+ },
140
+ ],
141
+ tags: additionalInfo.tags,
142
+ externalDocs: additionalInfo.externalDocs,
143
+ // definitions
144
+ };
145
+ }
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuHTTPMap: PikkuCommand;
@@ -0,0 +1,8 @@
1
+ import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
2
+ import { serializeTypedHTTPWiringsMap } from './serialize-typed-http-map.js';
3
+ export const pikkuHTTPMap = async (logger, { httpMapDeclarationFile, packageMappings }, { http, functions }) => {
4
+ return await logCommandInfoAndTime(logger, 'Creating HTTP map', 'Created HTTP map', [http.files.size === 0], async () => {
5
+ const content = serializeTypedHTTPWiringsMap(httpMapDeclarationFile, packageMappings, functions.typesMap, functions.meta, http.meta, http.metaInputTypes);
6
+ await writeFileInDir(logger, httpMapDeclarationFile, content);
7
+ });
8
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuHTTP: PikkuCommand;
@@ -0,0 +1,9 @@
1
+ import { logCommandInfoAndTime, serializeFileImports, writeFileInDir, } from '../../utils.js';
2
+ export const pikkuHTTP = async (logger, cliConfig, visitState) => {
3
+ return await logCommandInfoAndTime(logger, 'Finding HTTP routes', 'Found HTTP routes', [visitState.http.files.size === 0], async () => {
4
+ const { httpWiringsFile, httpWiringMetaFile, packageMappings } = cliConfig;
5
+ const { http } = visitState;
6
+ await writeFileInDir(logger, httpWiringsFile, serializeFileImports('wireHTTP', httpWiringsFile, http.files, packageMappings));
7
+ await writeFileInDir(logger, httpWiringMetaFile, `import { pikkuState } from '@pikku/core'\npikkuState('http', 'meta', ${JSON.stringify(http.meta, null, 2)})`);
8
+ });
9
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuOpenAPI: PikkuCommand;
@@ -0,0 +1,20 @@
1
+ import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
2
+ import { generateSchemas } from '../../schema-generator.js';
3
+ import { generateOpenAPISpec } from './openapi-spec-generator.js';
4
+ import { stringify } from 'yaml';
5
+ export const pikkuOpenAPI = async (logger, { tsconfig, openAPI }, { http, functions }) => {
6
+ return await logCommandInfoAndTime(logger, 'Creating OpenAPI spec', 'Created OpenAPI spec', [openAPI?.outputFile === undefined, 'openAPI outfile is not defined'], async () => {
7
+ if (!openAPI?.outputFile) {
8
+ throw new Error('openAPI is required');
9
+ }
10
+ const schemas = await generateSchemas(logger, tsconfig, functions.typesMap, functions.meta, http.meta);
11
+ const openAPISpec = await generateOpenAPISpec(functions.meta, http.meta, schemas, openAPI.additionalInfo);
12
+ if (openAPI.outputFile.endsWith('.json')) {
13
+ await writeFileInDir(logger, openAPI.outputFile, JSON.stringify(openAPISpec, null, 2), { ignoreModifyComment: true });
14
+ }
15
+ else if (openAPI.outputFile.endsWith('.yaml') ||
16
+ openAPI.outputFile.endsWith('.yml')) {
17
+ await writeFileInDir(logger, openAPI.outputFile, stringify(openAPISpec), { ignoreModifyComment: true });
18
+ }
19
+ });
20
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuHTTP: PikkuCommand;
@@ -0,0 +1,9 @@
1
+ import { logCommandInfoAndTime, serializeFileImports, writeFileInDir, } from '../../utils.js';
2
+ export const pikkuHTTP = async (logger, cliConfig, visitState) => {
3
+ return await logCommandInfoAndTime(logger, 'Finding HTTP routes', 'Found HTTP routes', [visitState.http.files.size === 0], async () => {
4
+ const { httpWiringsFile, httpWiringMetaFile, packageMappings } = cliConfig;
5
+ const { http } = visitState;
6
+ await writeFileInDir(logger, httpWiringsFile, serializeFileImports('wireHTTP', httpWiringsFile, http.files, packageMappings));
7
+ await writeFileInDir(logger, httpWiringMetaFile, `import { pikkuState } from '@pikku/core'\npikkuState('http', 'meta', ${JSON.stringify(http.meta, null, 2)})`);
8
+ });
9
+ };
@@ -0,0 +1 @@
1
+ export declare const serializeFetchWrapper: (routesMapPath: string) => string;
@@ -0,0 +1,67 @@
1
+ export const serializeFetchWrapper = (routesMapPath) => {
2
+ return `
3
+ import { CorePikkuFetch, HTTPMethod } from '@pikku/fetch'
4
+ import type { HTTPWiringsMap, HTTPWiringHandlerOf, HTTPWiringsWithMethod } from '${routesMapPath}'
5
+
6
+ export class PikkuFetch extends CorePikkuFetch {
7
+ public async post<Route extends HTTPWiringsWithMethod<'POST'>>(
8
+ route: Route,
9
+ ...args: null extends HTTPWiringHandlerOf<Route, 'POST'>['input']
10
+ ? [data?: Exclude<HTTPWiringHandlerOf<Route, 'POST'>['input'], null>, options?: Omit<RequestInit, 'body'>]
11
+ : [data: HTTPWiringHandlerOf<Route, 'POST'>['input'], options?: Omit<RequestInit, 'body'>]
12
+ ): Promise<HTTPWiringHandlerOf<Route, 'POST'>['output']> {
13
+ const [data, options] = args;
14
+ return super.api(route, 'POST', data, options);
15
+ }
16
+
17
+ public async get<Route extends HTTPWiringsWithMethod<'GET'>>(
18
+ route: Route,
19
+ ...args: null extends HTTPWiringHandlerOf<Route, 'GET'>['input']
20
+ ? [data?: Exclude<HTTPWiringHandlerOf<Route, 'GET'>['input'], null>, options?: Omit<RequestInit, 'body'>]
21
+ : [data: HTTPWiringHandlerOf<Route, 'GET'>['input'], options?: Omit<RequestInit, 'body'>]
22
+ ): Promise<HTTPWiringHandlerOf<Route, 'GET'>['output']> {
23
+ const [data, options] = args;
24
+ return super.api(route, 'GET', data, options);
25
+ }
26
+
27
+ public async patch<Route extends HTTPWiringsWithMethod<'PATCH'>>(
28
+ route: Route,
29
+ ...args: null extends HTTPWiringHandlerOf<Route, 'PATCH'>['input']
30
+ ? [data?: Exclude<HTTPWiringHandlerOf<Route, 'PATCH'>['input'], null>, options?: Omit<RequestInit, 'body'>]
31
+ : [data: HTTPWiringHandlerOf<Route, 'PATCH'>['input'], options?: Omit<RequestInit, 'body'>]
32
+ ): Promise<HTTPWiringHandlerOf<Route, 'PATCH'>['output']> {
33
+ const [data, options] = args;
34
+ return super.api(route, 'PATCH', data, options);
35
+ }
36
+
37
+ public async head<Route extends HTTPWiringsWithMethod<'HEAD'>>(
38
+ route: Route,
39
+ ...args: null extends HTTPWiringHandlerOf<Route, 'HEAD'>['input']
40
+ ? [data?: Exclude<HTTPWiringHandlerOf<Route, 'HEAD'>['input'], null>, options?: Omit<RequestInit, 'body'>]
41
+ : [data: HTTPWiringHandlerOf<Route, 'HEAD'>['input'], options?: Omit<RequestInit, 'body'>]
42
+ ): Promise<HTTPWiringHandlerOf<Route, 'HEAD'>['output']> {
43
+ const [data, options] = args;
44
+ return super.api(route, 'HEAD', data, options);
45
+ }
46
+
47
+ public async delete<Route extends HTTPWiringsWithMethod<'DELETE'>>(
48
+ route: Route,
49
+ ...args: null extends HTTPWiringHandlerOf<Route, 'DELETE'>['input']
50
+ ? [data?: Exclude<HTTPWiringHandlerOf<Route, 'DELETE'>['input'], null>, options?: Omit<RequestInit, 'body'>]
51
+ : [data: HTTPWiringHandlerOf<Route, 'DELETE'>['input'], options?: Omit<RequestInit, 'body'>]
52
+ ): Promise<HTTPWiringHandlerOf<Route, 'DELETE'>['output']> {
53
+ const [data, options] = args;
54
+ return super.api(route, 'DELETE', data, options);
55
+ }
56
+
57
+ public async fetch<
58
+ Route extends keyof HTTPWiringsMap,
59
+ Method extends keyof HTTPWiringsMap[Route]
60
+ >(route: Route, method: Method, data: HTTPWiringHandlerOf<Route, Method>['input'], options?: Omit<RequestInit, 'body'>): Promise<Response> {
61
+ return await super.fetch(route, method as HTTPMethod, data, options);
62
+ }
63
+ }
64
+
65
+ export const pikkuFetch = new PikkuFetch();
66
+ `;
67
+ };
@@ -0,0 +1,4 @@
1
+ import { HTTPWiringsMeta } from '@pikku/core/http';
2
+ import { MetaInputTypes, TypesMap } from '@pikku/inspector';
3
+ import { FunctionsMeta } from '@pikku/core';
4
+ export declare const serializeTypedHTTPWiringsMap: (relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, functionsMeta: FunctionsMeta, wiringsMeta: HTTPWiringsMeta, metaTypes: MetaInputTypes) => string;
@@ -0,0 +1,100 @@
1
+ import { serializeImportMap } from '../../serialize-import-map.js';
2
+ import { generateCustomTypes } from '../../utils.js';
3
+ export const serializeTypedHTTPWiringsMap = (relativeToPath, packageMappings, typesMap, functionsMeta, wiringsMeta, metaTypes) => {
4
+ const requiredTypes = new Set();
5
+ const serializedCustomTypes = generateCustomTypes(typesMap, requiredTypes);
6
+ const serializedMetaTypes = generateMetaTypes(metaTypes, typesMap);
7
+ const serializedHTTPWirings = generateHTTPWirings(wiringsMeta, functionsMeta, typesMap, requiredTypes);
8
+ const serializedImportMap = serializeImportMap(relativeToPath, packageMappings, typesMap, requiredTypes);
9
+ return `/**
10
+ * This provides the structure needed for typescript to be aware of routes and their return types
11
+ */
12
+
13
+ ${serializedImportMap}
14
+ ${serializedCustomTypes}
15
+ ${serializedMetaTypes}
16
+
17
+ interface HTTPWiringHandler<I, O> {
18
+ input: I;
19
+ output: O;
20
+ }
21
+
22
+ ${serializedHTTPWirings}
23
+
24
+ export type HTTPWiringHandlerOf<HTTPWiring extends keyof HTTPWiringsMap, Method extends keyof HTTPWiringsMap[HTTPWiring]> =
25
+ HTTPWiringsMap[HTTPWiring][Method] extends { input: infer I; output: infer O }
26
+ ? HTTPWiringHandler<I, O>
27
+ : never;
28
+
29
+ export type HTTPWiringsWithMethod<Method extends string> = {
30
+ [HTTPWiring in keyof HTTPWiringsMap]: Method extends keyof HTTPWiringsMap[HTTPWiring] ? HTTPWiring : never;
31
+ }[keyof HTTPWiringsMap];
32
+ `;
33
+ };
34
+ function generateHTTPWirings(routesMeta, functionsMeta, typesMap, requiredTypes) {
35
+ // Initialize an object to collect routes
36
+ const routesObj = {};
37
+ for (const meta of routesMeta) {
38
+ const { route, method, pikkuFuncName } = meta;
39
+ const functionMeta = functionsMeta[pikkuFuncName];
40
+ if (!functionMeta) {
41
+ throw new Error(`Function ${pikkuFuncName} not found in functionsMeta. Please check your configuration.`);
42
+ }
43
+ const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined;
44
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined;
45
+ // Initialize the route entry if it doesn't exist
46
+ if (!routesObj[route]) {
47
+ routesObj[route] = {};
48
+ }
49
+ // Store the input and output types separately for RouteHandler
50
+ const inputType = input ? typesMap.getTypeMeta(input).uniqueName : 'null';
51
+ const outputType = output ? typesMap.getTypeMeta(output).uniqueName : 'null';
52
+ requiredTypes.add(inputType);
53
+ requiredTypes.add(outputType);
54
+ // Add method entry
55
+ routesObj[route][method] = {
56
+ inputType,
57
+ outputType,
58
+ };
59
+ }
60
+ // Build the routes object as a string
61
+ let routesStr = 'export type HTTPWiringsMap = {\n';
62
+ for (const [routePath, methods] of Object.entries(routesObj)) {
63
+ routesStr += ` readonly '${routePath}': {\n`;
64
+ for (const [method, handler] of Object.entries(methods)) {
65
+ routesStr += ` readonly ${method.toUpperCase()}: HTTPWiringHandler<${handler.inputType}, ${handler.outputType}>,\n`;
66
+ }
67
+ routesStr += ' },\n';
68
+ }
69
+ routesStr += '};';
70
+ return routesStr;
71
+ }
72
+ const generateMetaTypes = (metaTypes, typesMap) => {
73
+ const nameToTypeMap = Array.from(metaTypes.entries()).reduce((result, [_name, { query, body, params }]) => {
74
+ const { uniqueName } = typesMap.getTypeMeta(_name);
75
+ const queryType = query && query.length > 0
76
+ ? `Pick<${uniqueName}, '${query?.join("' | '")}'>`
77
+ : undefined;
78
+ if (queryType) {
79
+ result.set(`${uniqueName}Query`, queryType);
80
+ }
81
+ const paramsType = params && params.length > 0
82
+ ? `Pick<${uniqueName}, '${params.join("' | '")}'>`
83
+ : undefined;
84
+ if (paramsType) {
85
+ result.set(`${uniqueName}Params`, paramsType);
86
+ }
87
+ const bodyType = (body && body.length > 0) || (params && params.length > 0)
88
+ ? `Omit<${uniqueName}, '${[...new Set([...(query || []), ...(params || [])])].join("' | '")}'>`
89
+ : uniqueName;
90
+ if (bodyType) {
91
+ result.set(`${uniqueName}Body`, bodyType);
92
+ }
93
+ return result;
94
+ }, new Map());
95
+ return `
96
+ // The '& {}' is a workaround for not directly refering to a type since it confuses typescript
97
+ ${Array.from(nameToTypeMap.entries())
98
+ .map(([name, type]) => `export type ${name} = ${type} & {}`)
99
+ .join('\n')}`;
100
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuMCPJSON: PikkuCommand;
@@ -0,0 +1,13 @@
1
+ import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
2
+ import { serializeMCPJson } from './serialize-mcp-json.js';
3
+ export const pikkuMCPJSON = async (logger, { mcpJsonFile, schemaDirectory }, { mcpEndpoints, functions }) => {
4
+ return await logCommandInfoAndTime(logger, 'Generating MCP JSON', 'Generated MCP JSON', [mcpEndpoints.files.size === 0 || !mcpJsonFile], async () => {
5
+ // Generate MCP JSON file
6
+ if (mcpJsonFile) {
7
+ const mcpJson = await serializeMCPJson(logger, schemaDirectory, functions.meta, functions.typesMap, mcpEndpoints.resourcesMeta, mcpEndpoints.toolsMeta, mcpEndpoints.promptsMeta);
8
+ await writeFileInDir(logger, mcpJsonFile, mcpJson, {
9
+ ignoreModifyComment: true,
10
+ });
11
+ }
12
+ });
13
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuMCP: PikkuCommand;
@@ -0,0 +1,54 @@
1
+ import { logCommandInfoAndTime, serializeFileImports, writeFileInDir, } from '../../utils.js';
2
+ import { readFile } from 'fs/promises';
3
+ import { join } from 'path';
4
+ // Helper function to generate arguments from schema
5
+ const generateArgumentsFromSchema = async (inputSchema, schemaDirectory, typesMap, logger) => {
6
+ if (!inputSchema)
7
+ return [];
8
+ if (['boolean', 'string', 'number', 'null', 'undefined', 'void'].includes(inputSchema)) {
9
+ return [];
10
+ }
11
+ const uniqueName = typesMap.getUniqueName(inputSchema);
12
+ if (!uniqueName)
13
+ return [];
14
+ try {
15
+ const schemaPath = join(schemaDirectory, 'schemas', `${uniqueName}.schema.json`);
16
+ const schemaContent = await readFile(schemaPath, 'utf-8');
17
+ const schema = JSON.parse(schemaContent);
18
+ const argumentsArray = [];
19
+ if (schema && typeof schema === 'object' && schema.properties) {
20
+ const properties = schema.properties;
21
+ const required = schema.required || [];
22
+ for (const [propName, propSchema] of Object.entries(properties)) {
23
+ argumentsArray.push({
24
+ name: propName,
25
+ description: propSchema.description || `${propName} parameter`,
26
+ required: required.includes(propName),
27
+ });
28
+ }
29
+ }
30
+ return argumentsArray;
31
+ }
32
+ catch (e) {
33
+ logger.warn(`Could not load schema for type: ${uniqueName}`);
34
+ return [];
35
+ }
36
+ };
37
+ export const pikkuMCP = async (logger, { mcpWiringsFile, mcpWiringsMetaFile, packageMappings, schemaDirectory }, { mcpEndpoints, functions }) => {
38
+ return await logCommandInfoAndTime(logger, 'Finding MCP endpoints', 'Found MCP endpoints', [mcpEndpoints.files.size === 0], async () => {
39
+ await writeFileInDir(logger, mcpWiringsFile, serializeFileImports('wireMCPResource or wireMCPTool', mcpWiringsFile, mcpEndpoints.files, packageMappings));
40
+ // Populate arguments for prompts meta before serializing
41
+ const promptsMetaWithArguments = { ...mcpEndpoints.promptsMeta };
42
+ for (const promptMeta of Object.values(promptsMetaWithArguments)) {
43
+ const functionMeta = functions.meta[promptMeta.pikkuFuncName];
44
+ if (functionMeta) {
45
+ const inputType = functionMeta.inputs?.[0];
46
+ promptMeta.arguments = await generateArgumentsFromSchema(inputType || null, schemaDirectory || '', functions.typesMap, logger);
47
+ }
48
+ }
49
+ await writeFileInDir(logger, mcpWiringsMetaFile, `import { pikkuState } from '@pikku/core'
50
+ pikkuState('mcp', 'resourcesMeta', ${JSON.stringify(mcpEndpoints.resourcesMeta, null, 2)})
51
+ pikkuState('mcp', 'toolsMeta', ${JSON.stringify(mcpEndpoints.toolsMeta, null, 2)})
52
+ pikkuState('mcp', 'promptsMeta', ${JSON.stringify(promptsMetaWithArguments, null, 2)})`);
53
+ });
54
+ };
@@ -0,0 +1,5 @@
1
+ import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core';
2
+ import { FunctionsMeta } from '@pikku/core';
3
+ import { TypesMap } from '@pikku/inspector';
4
+ import { CLILogger } from '../../utils.js';
5
+ export declare const serializeMCPJson: (logger: CLILogger, schemaDirectory: string, functionsMeta: FunctionsMeta, typesMap: TypesMap, mcpResourceMeta: MCPResourceMeta, mcpToolMeta: MCPToolMeta, mcpPromptMeta: MCPPromptMeta) => Promise<string>;
@@ -0,0 +1,101 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ export const serializeMCPJson = async (logger, schemaDirectory, functionsMeta, typesMap, mcpResourceMeta, mcpToolMeta, mcpPromptMeta) => {
4
+ const tools = [];
5
+ const resources = [];
6
+ const prompts = [];
7
+ // Helper function to load schema from file
8
+ const loadSchema = async (typeName) => {
9
+ if (!typeName ||
10
+ ['boolean', 'string', 'number', 'null', 'undefined', 'void'].includes(typeName)) {
11
+ return undefined;
12
+ }
13
+ const uniqueName = typesMap.getUniqueName(typeName);
14
+ if (!uniqueName) {
15
+ return undefined;
16
+ }
17
+ try {
18
+ const schemaPath = join(schemaDirectory, 'schemas', `${uniqueName}.schema.json`);
19
+ const schemaContent = await readFile(schemaPath, 'utf-8');
20
+ return JSON.parse(schemaContent);
21
+ }
22
+ catch (e) {
23
+ logger.warn(`Could not load schema for type: ${uniqueName}`);
24
+ return undefined;
25
+ }
26
+ };
27
+ // Process MCP resources
28
+ for (const [name, endpointMeta] of Object.entries(mcpResourceMeta)) {
29
+ const functionMeta = functionsMeta[endpointMeta.pikkuFuncName];
30
+ if (!functionMeta) {
31
+ logger.warn(`Function ${endpointMeta.pikkuFuncName} not found in functionsMeta. Skipping resource ${name}.`);
32
+ continue;
33
+ }
34
+ const inputType = functionMeta.inputs?.[0];
35
+ const outputType = functionMeta.outputs?.[0];
36
+ const parameters = await loadSchema(inputType);
37
+ const returns = await loadSchema(outputType);
38
+ const endpoint = {
39
+ uri: name,
40
+ name,
41
+ description: endpointMeta.description,
42
+ ...(parameters && { parameters }),
43
+ ...(returns && { returns }),
44
+ ...(endpointMeta.streaming && { streaming: true }),
45
+ };
46
+ resources.push(endpoint);
47
+ }
48
+ // Process MCP tools
49
+ for (const [name, endpointMeta] of Object.entries(mcpToolMeta)) {
50
+ const functionMeta = functionsMeta[endpointMeta.pikkuFuncName];
51
+ if (!functionMeta) {
52
+ logger.warn(`Function ${endpointMeta.pikkuFuncName} not found in functionsMeta. Skipping tool ${name}.`);
53
+ continue;
54
+ }
55
+ const inputType = functionMeta.inputs?.[0];
56
+ const outputType = functionMeta.outputs?.[0];
57
+ const parameters = await loadSchema(inputType);
58
+ const returns = await loadSchema(outputType);
59
+ const endpoint = {
60
+ name,
61
+ description: endpointMeta.description,
62
+ ...(parameters && { parameters }),
63
+ ...(returns && { returns }),
64
+ ...(endpointMeta.streaming && { streaming: true }),
65
+ };
66
+ tools.push(endpoint);
67
+ }
68
+ // Process MCP prompts
69
+ for (const [name, endpointMeta] of Object.entries(mcpPromptMeta)) {
70
+ const functionMeta = functionsMeta[endpointMeta.pikkuFuncName];
71
+ if (!functionMeta) {
72
+ logger.warn(`Function ${endpointMeta.pikkuFuncName} not found in functionsMeta. Skipping prompt ${name}.`);
73
+ continue;
74
+ }
75
+ const inputType = functionMeta.inputs?.[0];
76
+ // TODO: this needs to be a json schema type, not any
77
+ const inputSchema = await loadSchema(inputType);
78
+ // Generate arguments from input schema
79
+ const argumentsArray = [];
80
+ if (inputSchema &&
81
+ typeof inputSchema === 'object' &&
82
+ inputSchema.properties) {
83
+ const properties = inputSchema.properties;
84
+ const required = inputSchema.required || [];
85
+ for (const [propName, propSchema] of Object.entries(properties)) {
86
+ argumentsArray.push({
87
+ name: propName,
88
+ description: propSchema.description || `${propName} parameter`,
89
+ required: required.includes(propName),
90
+ });
91
+ }
92
+ }
93
+ const prompt = {
94
+ name,
95
+ description: endpointMeta.description,
96
+ arguments: argumentsArray,
97
+ };
98
+ prompts.push(prompt);
99
+ }
100
+ return JSON.stringify({ tools, resources, prompts }, null, 2);
101
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuQueueMap: PikkuCommand;
@@ -0,0 +1,8 @@
1
+ import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
2
+ import { serializeQueueMap } from './serialize-queue-map.js';
3
+ export const pikkuQueueMap = async (logger, { queueMapDeclarationFile, packageMappings }, { queueWorkers, functions }) => {
4
+ return await logCommandInfoAndTime(logger, 'Creating Queue map', 'Created Queue map', [queueWorkers.files.size === 0], async () => {
5
+ const content = serializeQueueMap(queueMapDeclarationFile, packageMappings, functions.typesMap, functions.meta, queueWorkers.meta);
6
+ await writeFileInDir(logger, queueMapDeclarationFile, content);
7
+ });
8
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommandWithoutState } from '../../types.js';
2
+ export declare const pikkuQueueService: PikkuCommandWithoutState;
@@ -0,0 +1,15 @@
1
+ import { serializeQueueWrapper } from './serialize-queue-wrapper.js';
2
+ import { getFileImportRelativePath, logCommandInfoAndTime, writeFileInDir, } from '../../utils.js';
3
+ export const pikkuQueueService = async (logger, { queueWiringsFile, queueMapDeclarationFile, packageMappings }) => {
4
+ return await logCommandInfoAndTime(logger, 'Generating queue service wrapper', 'Generated queue service wrapper', [
5
+ queueWiringsFile === undefined,
6
+ "queueWiringsFile isn't set in the pikku config",
7
+ ], async () => {
8
+ if (!queueWiringsFile) {
9
+ throw new Error("queueWiringsFile is isn't set in the pikku config");
10
+ }
11
+ const queueMapDeclarationPath = getFileImportRelativePath(queueWiringsFile, queueMapDeclarationFile, packageMappings);
12
+ const content = [serializeQueueWrapper(queueMapDeclarationPath)];
13
+ await writeFileInDir(logger, queueWiringsFile, content.join('\n'));
14
+ });
15
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuQueue: PikkuCommand;
@@ -0,0 +1,10 @@
1
+ import { logCommandInfoAndTime, serializeFileImports, writeFileInDir, } from '../../utils.js';
2
+ import { serializeQueueMeta } from './serialize-queue-meta.js';
3
+ export const pikkuQueue = async (logger, cliConfig, visitState) => {
4
+ return await logCommandInfoAndTime(logger, 'Finding queues', 'Found queue', [visitState.queueWorkers.files.size === 0], async () => {
5
+ const { queueWorkersWiringFile, queueWorkersWiringMetaFile, packageMappings, } = cliConfig;
6
+ const { queueWorkers } = visitState;
7
+ await writeFileInDir(logger, queueWorkersWiringMetaFile, serializeQueueMeta(queueWorkers.meta));
8
+ await writeFileInDir(logger, queueWorkersWiringFile, serializeFileImports('addQueueWorkers', queueWorkersWiringFile, queueWorkers.files, packageMappings));
9
+ });
10
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuQueueMap: PikkuCommand;
@@ -0,0 +1,8 @@
1
+ import { logCommandInfoAndTime, writeFileInDir } from '../../utils.js';
2
+ import { serializeQueueMap } from './serialize-queue-map.js';
3
+ export const pikkuQueueMap = async (logger, { queueMapDeclarationFile, packageMappings }, { queueWorkers, functions }) => {
4
+ return await logCommandInfoAndTime(logger, 'Creating Queue map', 'Created Queue map', [queueWorkers.files.size === 0], async () => {
5
+ const content = serializeQueueMap(queueMapDeclarationFile, packageMappings, functions.typesMap, functions.meta, queueWorkers.meta);
6
+ await writeFileInDir(logger, queueMapDeclarationFile, content);
7
+ });
8
+ };
@@ -0,0 +1,2 @@
1
+ import { PikkuCommand } from '../../types.js';
2
+ export declare const pikkuQueue: PikkuCommand;