@pikku/cli 0.10.1 → 0.11.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 (214) hide show
  1. package/.pikku/channel/pikku-channel-types.gen.ts +4 -3
  2. package/.pikku/channel/pikku-channels-map.gen.d.ts +5 -2
  3. package/.pikku/channel/pikku-channels-meta.gen.ts +1 -1
  4. package/.pikku/channel/pikku-channels.gen.ts +1 -1
  5. package/.pikku/cli/pikku-cli-types.gen.ts +23 -1
  6. package/.pikku/cli/pikku-cli-wirings-meta.gen.ts +8 -116
  7. package/.pikku/cli/pikku-cli-wirings.gen.ts +2 -3
  8. package/.pikku/function/pikku-function-types.gen.ts +1 -1
  9. package/.pikku/function/pikku-functions-meta.gen.ts +265 -138
  10. package/.pikku/function/pikku-functions-meta.min.gen.ts +62 -32
  11. package/.pikku/function/pikku-functions.gen.ts +9 -1
  12. package/.pikku/http/pikku-http-types.gen.ts +1 -1
  13. package/.pikku/http/pikku-http-wirings-map.gen.d.ts +5 -2
  14. package/.pikku/http/pikku-http-wirings-meta.gen.ts +1 -1
  15. package/.pikku/http/pikku-http-wirings.gen.ts +1 -1
  16. package/.pikku/mcp/pikku-mcp-types.gen.ts +1 -1
  17. package/.pikku/mcp/pikku-mcp-wirings-meta.gen.ts +1 -1
  18. package/.pikku/mcp/pikku-mcp-wirings.gen.ts +1 -1
  19. package/.pikku/pikku-bootstrap.gen.ts +4 -1
  20. package/.pikku/pikku-services.gen.ts +21 -12
  21. package/.pikku/pikku-types.gen.ts +1 -1
  22. package/.pikku/pikku-websocket.gen.ts +15 -1
  23. package/.pikku/queue/pikku-queue-types.gen.ts +1 -1
  24. package/.pikku/queue/pikku-queue-workers-wirings-map.gen.d.ts +5 -2
  25. package/.pikku/queue/pikku-queue-workers-wirings-meta.gen.ts +7 -2
  26. package/.pikku/queue/pikku-queue-workers-wirings.gen.ts +1 -1
  27. package/.pikku/rpc/pikku-remote-rpc-workers.gen.ts +27 -0
  28. package/.pikku/rpc/pikku-rpc-wirings-map.gen.d.ts +13 -3
  29. package/.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts +26 -10
  30. package/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.ts +15 -9
  31. package/.pikku/scheduler/pikku-scheduler-types.gen.ts +1 -1
  32. package/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.ts +1 -1
  33. package/.pikku/scheduler/pikku-schedulers-wirings.gen.ts +1 -1
  34. package/.pikku/schemas/register.gen.ts +17 -5
  35. package/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  36. package/.pikku/schemas/schemas/PikkuChannelsOutput.schema.json +1 -1
  37. package/.pikku/schemas/schemas/PikkuPublicRPCOutput.schema.json +1 -0
  38. package/.pikku/schemas/schemas/PikkuRemoteRPCOutput.schema.json +1 -0
  39. package/.pikku/schemas/schemas/PikkuSchemasOutput.schema.json +1 -1
  40. package/.pikku/schemas/schemas/PikkuWorkflowOutput.schema.json +1 -0
  41. package/.pikku/workflow/pikku-workflow-map.gen.d.ts +62 -0
  42. package/.pikku/workflow/pikku-workflow-types.gen.ts +92 -0
  43. package/.pikku/workflow/pikku-workflow-wirings-meta.gen.ts +5 -0
  44. package/.pikku/workflow/pikku-workflow-wirings.gen.ts +4 -0
  45. package/CHANGELOG.md +57 -0
  46. package/bin/pikku.ts +30 -21
  47. package/cli.schema.json +1 -1
  48. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +4 -3
  49. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  50. package/dist/.pikku/channel/pikku-channels-meta.gen.js +1 -1
  51. package/dist/.pikku/channel/pikku-channels.gen.d.ts +1 -1
  52. package/dist/.pikku/channel/pikku-channels.gen.js +1 -1
  53. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +18 -1
  54. package/dist/.pikku/cli/pikku-cli-types.gen.js +20 -1
  55. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +8 -116
  56. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -2
  57. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -2
  58. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  59. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  60. package/dist/.pikku/function/pikku-functions-meta.gen.js +265 -138
  61. package/dist/.pikku/function/pikku-functions-meta.min.gen.js +62 -32
  62. package/dist/.pikku/function/pikku-functions.gen.js +9 -1
  63. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  64. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  65. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  66. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  67. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  68. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  69. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  70. package/dist/.pikku/mcp/pikku-mcp-wirings-meta.gen.js +1 -1
  71. package/dist/.pikku/mcp/pikku-mcp-wirings.gen.d.ts +1 -1
  72. package/dist/.pikku/mcp/pikku-mcp-wirings.gen.js +1 -1
  73. package/dist/.pikku/pikku-bootstrap.gen.d.ts +4 -1
  74. package/dist/.pikku/pikku-bootstrap.gen.js +4 -1
  75. package/dist/.pikku/pikku-services.gen.d.ts +15 -6
  76. package/dist/.pikku/pikku-services.gen.js +14 -2
  77. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  78. package/dist/.pikku/pikku-types.gen.js +1 -1
  79. package/dist/.pikku/pikku-websocket.gen.d.ts +15 -1
  80. package/dist/.pikku/pikku-websocket.gen.js +15 -1
  81. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  82. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  83. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +7 -2
  84. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  85. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  86. package/dist/.pikku/rpc/pikku-remote-rpc-workers.gen.d.ts +17 -0
  87. package/dist/.pikku/rpc/pikku-remote-rpc-workers.gen.js +22 -0
  88. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +15 -9
  89. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  90. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  91. package/dist/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.js +1 -1
  92. package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.d.ts +1 -1
  93. package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.js +1 -1
  94. package/dist/.pikku/schemas/register.gen.js +9 -3
  95. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  96. package/dist/.pikku/schemas/schemas/PikkuChannelsOutput.schema.json +1 -1
  97. package/dist/.pikku/schemas/schemas/PikkuPublicRPCOutput.schema.json +1 -0
  98. package/dist/.pikku/schemas/schemas/PikkuRemoteInternalRPCInput.schema.json +1 -0
  99. package/dist/.pikku/schemas/schemas/PikkuRemoteRPCOutput.schema.json +1 -0
  100. package/dist/.pikku/schemas/schemas/PikkuSchemasOutput.schema.json +1 -1
  101. package/dist/.pikku/schemas/schemas/PikkuWorkflowOutput.schema.json +1 -0
  102. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +58 -0
  103. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +28 -0
  104. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +5 -0
  105. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.d.ts +4 -0
  106. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +5 -0
  107. package/dist/bin/pikku.js +24 -19
  108. package/dist/src/cli.wiring.js +107 -99
  109. package/dist/src/functions/commands/all.js +51 -2
  110. package/dist/src/functions/commands/bootstrap.d.ts +1 -0
  111. package/dist/src/functions/commands/bootstrap.js +24 -0
  112. package/dist/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.js +46 -2
  113. package/dist/src/functions/wirings/channels/serialize-channel-types.js +3 -2
  114. package/dist/src/functions/wirings/channels/serialize-websocket-wrapper.js +14 -0
  115. package/dist/src/functions/wirings/cli/pikku-command-cli-entry.js +4 -4
  116. package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +20 -7
  117. package/dist/src/functions/wirings/cli/serialize-channel-cli.js +32 -7
  118. package/dist/src/functions/wirings/cli/serialize-cli-types.js +22 -0
  119. package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
  120. package/dist/src/functions/wirings/functions/pikku-command-services.js +54 -26
  121. package/dist/src/functions/wirings/functions/schemas.js +2 -2
  122. package/dist/src/functions/wirings/http/pikku-command-openapi.js +1 -1
  123. package/dist/src/functions/wirings/middleware/pikku-command-middleware.js +3 -10
  124. package/dist/src/functions/wirings/queue/pikku-queue.js +9 -1
  125. package/dist/src/functions/wirings/queue/serialize-queue-map.d.ts +2 -2
  126. package/dist/src/functions/wirings/queue/serialize-queue-meta.d.ts +2 -2
  127. package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.d.ts +1 -0
  128. package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +23 -0
  129. package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.d.ts +1 -0
  130. package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.js +23 -0
  131. package/dist/src/functions/wirings/rpc/serialize-public-rpc.d.ts +4 -0
  132. package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +30 -0
  133. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.d.ts +4 -0
  134. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +30 -0
  135. package/dist/src/functions/wirings/rpc/serialize-rpc-wrapper.js +2 -2
  136. package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.js +27 -3
  137. package/dist/src/functions/wirings/workflow/pikku-command-workflow-map.d.ts +1 -0
  138. package/dist/src/functions/wirings/workflow/pikku-command-workflow-map.js +12 -0
  139. package/dist/src/functions/wirings/workflow/pikku-command-workflow-types.d.ts +1 -0
  140. package/dist/src/functions/wirings/workflow/pikku-command-workflow-types.js +11 -0
  141. package/dist/src/functions/wirings/workflow/pikku-command-workflow.d.ts +1 -0
  142. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +55 -0
  143. package/dist/src/functions/wirings/workflow/serialize-workflow-map.d.ts +4 -0
  144. package/dist/src/functions/wirings/workflow/serialize-workflow-map.js +79 -0
  145. package/dist/src/functions/wirings/workflow/serialize-workflow-meta.d.ts +2 -0
  146. package/dist/src/functions/wirings/workflow/serialize-workflow-meta.js +10 -0
  147. package/dist/src/functions/wirings/workflow/serialize-workflow-types.d.ts +4 -0
  148. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +95 -0
  149. package/dist/src/functions/wirings/workflow/serialize-workflow-workers.d.ts +4 -0
  150. package/dist/src/functions/wirings/workflow/serialize-workflow-workers.js +60 -0
  151. package/dist/src/middleware/log-command-info-and-time.d.ts +1 -1
  152. package/dist/src/middleware/log-command-info-and-time.js +8 -5
  153. package/dist/src/services/cli-logger-forwarder.service.js +1 -1
  154. package/dist/src/services/cli-logger.service.d.ts +7 -2
  155. package/dist/src/services/cli-logger.service.js +17 -5
  156. package/dist/src/services.js +77 -12
  157. package/dist/src/utils/command-summary.d.ts +44 -0
  158. package/dist/src/utils/command-summary.js +74 -0
  159. package/dist/src/utils/file-writer.js +2 -2
  160. package/dist/src/utils/pikku-cli-config.js +48 -0
  161. package/dist/src/utils/schema-generator.d.ts +2 -2
  162. package/dist/src/utils/schema-generator.js +31 -10
  163. package/dist/tsconfig.tsbuildinfo +1 -1
  164. package/package.json +3 -4
  165. package/pikku.config.json +5 -2
  166. package/src/cli.wiring.ts +106 -101
  167. package/src/functions/commands/all.ts +66 -2
  168. package/src/functions/commands/bootstrap.ts +28 -0
  169. package/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.ts +46 -2
  170. package/src/functions/wirings/channels/serialize-channel-types.ts +3 -2
  171. package/src/functions/wirings/channels/serialize-websocket-wrapper.ts +14 -0
  172. package/src/functions/wirings/cli/pikku-command-cli-entry.ts +4 -4
  173. package/src/functions/wirings/cli/serialize-channel-cli-client.ts +20 -7
  174. package/src/functions/wirings/cli/serialize-channel-cli.ts +40 -8
  175. package/src/functions/wirings/cli/serialize-cli-types.ts +22 -0
  176. package/src/functions/wirings/functions/pikku-command-services.ts +57 -28
  177. package/src/functions/wirings/functions/schemas.ts +4 -3
  178. package/src/functions/wirings/http/pikku-command-openapi.ts +2 -1
  179. package/src/functions/wirings/middleware/pikku-command-middleware.ts +11 -22
  180. package/src/functions/wirings/queue/pikku-queue.ts +10 -1
  181. package/src/functions/wirings/queue/serialize-queue-map.ts +3 -3
  182. package/src/functions/wirings/queue/serialize-queue-meta.ts +2 -2
  183. package/src/functions/wirings/rpc/pikku-command-public-rpc.ts +32 -0
  184. package/src/functions/wirings/rpc/pikku-command-remote-rpc.ts +35 -0
  185. package/src/functions/wirings/rpc/serialize-public-rpc.ts +30 -0
  186. package/src/functions/wirings/rpc/serialize-remote-rpc.ts +30 -0
  187. package/src/functions/wirings/rpc/serialize-rpc-wrapper.ts +2 -2
  188. package/src/functions/wirings/rpc/serialize-typed-rpc-map.ts +26 -3
  189. package/src/functions/wirings/workflow/pikku-command-workflow-map.ts +24 -0
  190. package/src/functions/wirings/workflow/pikku-command-workflow-types.ts +22 -0
  191. package/src/functions/wirings/workflow/pikku-command-workflow.ts +123 -0
  192. package/src/functions/wirings/workflow/serialize-workflow-map.ts +123 -0
  193. package/src/functions/wirings/workflow/serialize-workflow-meta.ts +16 -0
  194. package/src/functions/wirings/workflow/serialize-workflow-types.ts +95 -0
  195. package/src/functions/wirings/workflow/serialize-workflow-workers.ts +60 -0
  196. package/src/middleware/log-command-info-and-time.ts +8 -5
  197. package/src/services/cli-logger-forwarder.service.ts +1 -1
  198. package/src/services/cli-logger.service.ts +21 -6
  199. package/src/services.ts +86 -11
  200. package/src/utils/command-summary.ts +103 -0
  201. package/src/utils/file-writer.ts +2 -2
  202. package/src/utils/pikku-cli-config.ts +68 -0
  203. package/src/utils/schema-generator.ts +30 -11
  204. package/types/application-types.d.ts +5 -1
  205. package/types/config.d.ts +61 -6
  206. package/.pikku/cli/pikku-cli-channel.gen.ts +0 -34
  207. package/.pikku/cli/pikku-cli-client.gen.ts +0 -43
  208. package/.pikku/cli/pikku-cli.gen.ts +0 -41
  209. package/dist/.pikku/cli/pikku-cli-channel.gen.js +0 -33
  210. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +0 -10
  211. package/dist/.pikku/cli/pikku-cli-client.gen.js +0 -34
  212. package/dist/.pikku/cli/pikku-cli.gen.d.ts +0 -10
  213. package/dist/.pikku/cli/pikku-cli.gen.js +0 -38
  214. /package/dist/.pikku/{cli/pikku-cli-channel.gen.d.ts → workflow/pikku-workflow-wirings-meta.gen.d.ts} +0 -0
@@ -2,9 +2,11 @@ import { existsSync } from 'fs'
2
2
  import { pikkuVoidFunc } from '../../../.pikku/pikku-types.gen.js'
3
3
  import { getFileImportRelativePath } from '../../utils/file-import-path.js'
4
4
  import { writeFileInDir } from '../../utils/file-writer.js'
5
+ import { CommandSummary } from '../../utils/command-summary.js'
5
6
 
6
7
  export const all: any = pikkuVoidFunc({
7
8
  func: async ({ logger, config, rpc, getInspectorState }) => {
9
+ const summary = new CommandSummary('all')
8
10
  const allImports: string[] = []
9
11
  let typesDeclarationFileExists = true
10
12
 
@@ -16,7 +18,7 @@ export const all: any = pikkuVoidFunc({
16
18
 
17
19
  // This is needed since the wireHTTP function will add the routes to the visitState
18
20
  if (!typesDeclarationFileExists) {
19
- logger.info(`• Type file first created, inspecting again...\x1b[0m`)
21
+ logger.debug(`• Type file first created, inspecting again...`)
20
22
  await getInspectorState(true)
21
23
  }
22
24
 
@@ -26,6 +28,7 @@ export const all: any = pikkuVoidFunc({
26
28
  await rpc.invoke('pikkuChannelTypes', null)
27
29
  await rpc.invoke('pikkuSchedulerTypes', null)
28
30
  await rpc.invoke('pikkuQueueTypes', null)
31
+ await rpc.invoke('pikkuWorkflowTypes', null)
29
32
  await rpc.invoke('pikkuMCPTypes', null)
30
33
  await rpc.invoke('pikkuCLITypes', null)
31
34
 
@@ -87,6 +90,30 @@ export const all: any = pikkuVoidFunc({
87
90
  )
88
91
  }
89
92
 
93
+ // Generate Workflows
94
+ const workflows = await rpc.invoke('pikkuWorkflow', null)
95
+
96
+ // Generate Remote RPC Workers (must be before queue discovery so wireQueueWorker calls are picked up)
97
+ const remoteRPC = await rpc.invoke('pikkuRemoteRPC', null)
98
+
99
+ // Reinspect to pick up generated workflow workers and remote RPC workers BEFORE generating maps
100
+ if (workflows || remoteRPC) {
101
+ await getInspectorState(true)
102
+ }
103
+
104
+ if (workflows) {
105
+ await rpc.invoke('pikkuWorkflowMap', null)
106
+ allImports.push(
107
+ config.workflowsWiringMetaFile,
108
+ config.workflowsWiringFile
109
+ )
110
+ }
111
+
112
+ if (remoteRPC && config.rpc?.remoteRpcWorkersPath) {
113
+ // Only add to imports if we actually generated the file
114
+ allImports.push(config.rpc.remoteRpcWorkersPath)
115
+ }
116
+
90
117
  // Generate Queues
91
118
  const queues = await rpc.invoke('pikkuQueue', null)
92
119
  if (queues) {
@@ -125,7 +152,7 @@ export const all: any = pikkuVoidFunc({
125
152
  }
126
153
 
127
154
  if (config.openAPI) {
128
- logger.info(
155
+ logger.debug(
129
156
  `• OpenAPI requires a reinspection to pickup new generated types..`
130
157
  )
131
158
  await getInspectorState(true)
@@ -145,6 +172,43 @@ export const all: any = pikkuVoidFunc({
145
172
  .join('\n')
146
173
  )
147
174
 
175
+ // Get final inspector state and collect stats for summary
176
+ const state = await getInspectorState()
177
+ if (state.http?.meta)
178
+ summary.set('httpRoutes', Object.keys(state.http.meta).length)
179
+ if (state.channels?.meta)
180
+ summary.set('channels', Object.keys(state.channels.meta).length)
181
+ if (state.scheduledTasks?.meta)
182
+ summary.set(
183
+ 'scheduledTasks',
184
+ Object.keys(state.scheduledTasks.meta).length
185
+ )
186
+ if (state.queueWorkers?.meta)
187
+ summary.set('queueWorkers', Object.keys(state.queueWorkers.meta).length)
188
+ if (state.mcpEndpoints) {
189
+ const mcpTotal =
190
+ Object.keys(state.mcpEndpoints.toolsMeta || {}).length +
191
+ Object.keys(state.mcpEndpoints.resourcesMeta || {}).length +
192
+ Object.keys(state.mcpEndpoints.promptsMeta || {}).length
193
+ if (mcpTotal > 0) summary.set('mcpEndpoints', mcpTotal)
194
+ }
195
+ if (state.cli?.meta) {
196
+ // Count total CLI commands across all programs
197
+ const totalCommands = Object.values(state.cli.meta).reduce(
198
+ (sum, program) => sum + (program.commands?.length || 0),
199
+ 0
200
+ )
201
+ if (totalCommands > 0) summary.set('cliCommands', totalCommands)
202
+ }
203
+ if (state.workflows?.meta) {
204
+ summary.set('workflows', Object.keys(state.workflows.meta).length)
205
+ }
206
+
207
+ // Display summary (unless in silent mode)
208
+ if (!logger.isSilent()) {
209
+ console.log(summary.format())
210
+ }
211
+
148
212
  // Check for critical errors and exit if any were logged
149
213
  if (logger.hasCriticalErrors()) {
150
214
  process.exit(1)
@@ -0,0 +1,28 @@
1
+ import { pikkuVoidFunc } from '../../../.pikku/pikku-types.gen.js'
2
+
3
+ export const bootstrap: any = pikkuVoidFunc({
4
+ func: async ({ logger, config, rpc, getInspectorState }) => {
5
+ // Initialize inspector state in bootstrap mode with core types only
6
+ // This allows bootstrap to run immediately without inspecting the codebase
7
+ // All subsequent RPC commands will use this cached state
8
+ await getInspectorState(false, false, true)
9
+
10
+ await rpc.invoke('pikkuFunctionTypes', null)
11
+
12
+ // Generate wiring-specific type files for tree-shaking
13
+ // These use the bootstrap mode state with core types
14
+ await rpc.invoke('pikkuFunctionTypesSplit', null)
15
+ await rpc.invoke('pikkuHTTPTypes', null)
16
+ await rpc.invoke('pikkuChannelTypes', null)
17
+ await rpc.invoke('pikkuSchedulerTypes', null)
18
+ await rpc.invoke('pikkuQueueTypes', null)
19
+ await rpc.invoke('pikkuWorkflowTypes', null)
20
+ await rpc.invoke('pikkuMCPTypes', null)
21
+ await rpc.invoke('pikkuCLITypes', null)
22
+
23
+ // Check for critical errors and exit if any were logged
24
+ if (logger.hasCriticalErrors()) {
25
+ process.exit(1)
26
+ }
27
+ },
28
+ })
@@ -6,14 +6,17 @@ export const serializeNextJsBackendWrapper = (
6
6
  sessionServicesImport: string
7
7
  ) => {
8
8
  return `'server-only'
9
-
9
+
10
10
  /**
11
11
  * This file provides a wrapper around the PikkuNextJS class to allow for methods to be type checked against your routes.
12
12
  * It ensures type safety for route handling methods when integrating with the @pikku/core framework.
13
13
  */
14
14
  import { PikkuNextJS } from '@pikku/next'
15
+ import { NextRequest } from 'next/server.js'
15
16
  import type { HTTPWiringsMap, HTTPWiringHandlerOf, HTTPWiringsWithMethod } from '${routesMapPath}'
16
17
 
18
+ type RouteContext = { params: Promise<Record<string, string | string[]>> }
19
+
17
20
  ${configImport}
18
21
  ${singleServicesFactoryImport}
19
22
  ${sessionServicesImport}
@@ -21,6 +24,11 @@ ${sessionServicesImport}
21
24
  import '${bootstrapPath}'
22
25
 
23
26
  let _pikku: PikkuNextJS | undefined
27
+ let _removeAPIPrefix = true
28
+
29
+ export const removeAPIPrefix = (enable: boolean) => {
30
+ _removeAPIPrefix = enable
31
+ }
24
32
 
25
33
  /**
26
34
  * Initializes and returns an instance of PikkuNextJS with helper methods for handling route requests.
@@ -178,8 +186,44 @@ export const pikku = (_options?: any) => {
178
186
  patch: dynamicPatch,
179
187
  del: dynamicDel,
180
188
  staticGet,
181
- staticPost
189
+ staticPost,
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Pre-bound API request handler for Next.js App Router route handlers.
195
+ * Use this to directly export route handlers without losing context.
196
+ *
197
+ * @param req - The Next.js request object.
198
+ * @param context - Next.js route context (unused by Pikku, but required by Next.js signature).
199
+ * @returns A promise that resolves to a Next.js Response object.
200
+ *
201
+ * @example
202
+ * export const GET = pikkuAPIRequest
203
+ * export const POST = pikkuAPIRequest
204
+ */
205
+ export const pikkuAPIRequest = (
206
+ req: NextRequest,
207
+ context: RouteContext
208
+ ): Promise<Response> => {
209
+ if (!_pikku) {
210
+ _pikku = new PikkuNextJS(
211
+ createConfig as any,
212
+ createSingletonServices as any,
213
+ createSessionServices
214
+ )
215
+ }
216
+ if (_removeAPIPrefix) {
217
+ const url = new URL(req.url)
218
+ url.pathname = url.pathname.replace(/^\\/api/, '') || '/'
219
+ req = new NextRequest(url.toString(), {
220
+ method: req.method,
221
+ headers: req.headers,
222
+ body: req.body,
223
+ duplex: 'half',
224
+ } as any)
182
225
  }
226
+ return _pikku.apiRequest(req)
183
227
  }
184
228
  `
185
229
  }
@@ -10,11 +10,12 @@ export const serializeChannelTypes = (functionTypesImportPath: string) => {
10
10
  import { CoreChannel, wireChannel as wireChannelCore } from '@pikku/core/channel'
11
11
  import { CorePikkuFunctionConfig } from '@pikku/core'
12
12
  import { AssertHTTPWiringParams } from '@pikku/core/http'
13
- import type { PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware } from '${functionTypesImportPath}'
13
+ import type { PikkuFunction, PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware } from '${functionTypesImportPath}'
14
14
 
15
15
  /**
16
16
  * Type definition for WebSocket channels with typed data exchange.
17
17
  * Supports connection, disconnection, and message handling.
18
+ * Accepts both session-based (PikkuFunction) and sessionless (PikkuFunctionSessionless) functions.
18
19
  *
19
20
  * @template ChannelData - Type of data exchanged through the channel
20
21
  * @template Channel - String literal type for the channel name
@@ -24,7 +25,7 @@ type ChannelWiring<ChannelData, Channel extends string> = CoreChannel<
24
25
  Channel,
25
26
  CorePikkuFunctionConfig<PikkuFunctionSessionless<void, any, ChannelData>, PikkuPermission<void>, PikkuMiddleware>,
26
27
  CorePikkuFunctionConfig<PikkuFunctionSessionless<void, void, ChannelData>, PikkuPermission<void>, PikkuMiddleware>,
27
- CorePikkuFunctionConfig<PikkuFunctionSessionless<any, any, ChannelData>, PikkuPermission<any>, PikkuMiddleware>,
28
+ CorePikkuFunctionConfig<PikkuFunctionSessionless<any, any, ChannelData> | PikkuFunction<any, any, ChannelData>, PikkuPermission<any>, PikkuMiddleware>,
28
29
  PikkuPermission,
29
30
  PikkuMiddleware
30
31
  >
@@ -26,6 +26,20 @@ class PikkuWebSocketRoute<Channel extends keyof ChannelsMap, Route extends keyof
26
26
  }
27
27
  }
28
28
 
29
+ /**
30
+ * Type-safe WebSocket wrapper for Pikku channels.
31
+ *
32
+ * @example
33
+ * // Browser usage
34
+ * const ws = new WebSocket('ws://localhost:3000')
35
+ * const pikkuWS = new PikkuWebSocket<'events'>(ws)
36
+ *
37
+ * @example
38
+ * // Node.js usage
39
+ * import WebSocket from 'ws'
40
+ * const ws = new WebSocket('ws://localhost:3000')
41
+ * const pikkuWS = new PikkuWebSocket<'events'>(ws)
42
+ */
29
43
  export class PikkuWebSocket<Channel extends keyof ChannelsMap, EventHubTopics extends Record<string, any> = {}> extends CorePikkuWebsocket {
30
44
  /**
31
45
  * Send a message to a specific route and method.
@@ -52,8 +52,8 @@ export const pikkuCLIEntry: any = pikkuSessionlessFunc<void, void>({
52
52
  // Normalize entrypoint config to get type
53
53
  const entrypointType =
54
54
  typeof entrypointConfig === 'string'
55
- ? 'cli'
56
- : entrypointConfig.type || 'cli'
55
+ ? 'local'
56
+ : entrypointConfig.type || 'local'
57
57
 
58
58
  // Handle channel type entrypoint
59
59
  if (entrypointType === 'channel') {
@@ -152,11 +152,11 @@ export const pikkuCLIEntry: any = pikkuSessionlessFunc<void, void>({
152
152
  continue
153
153
  }
154
154
 
155
- // Handle CLI type entrypoint (default)
155
+ // Handle local CLI type entrypoint (default)
156
156
  const entrypointPath =
157
157
  typeof entrypointConfig === 'string'
158
158
  ? entrypointConfig
159
- : entrypointConfig.type === 'cli'
159
+ : entrypointConfig.type === 'local'
160
160
  ? entrypointConfig.path
161
161
  : undefined
162
162
 
@@ -139,11 +139,11 @@ ${rendererImports}
139
139
  * Executes CLI commands over a WebSocket connection
140
140
  */
141
141
  export async function ${capitalizedName}CLIClient(
142
- url: string,
142
+ ws: WebSocket,
143
143
  args?: string[]
144
144
  ): Promise<void> {
145
- // Create WebSocket connection
146
- const pikkuWS = new CorePikkuWebsocket(url)
145
+ // Create Pikku WebSocket wrapper
146
+ const pikkuWS = new CorePikkuWebsocket(ws)
147
147
 
148
148
  // Register renderers for CLI commands
149
149
  const renderers = ${renderersMap}
@@ -161,10 +161,23 @@ export default ${capitalizedName}CLIClient
161
161
 
162
162
  // For direct execution (if this file is run directly)
163
163
  if (import.meta.url === \`file://\${process.argv[1]}\`) {
164
- const url = process.env.PIKKU_WS_URL || 'ws://localhost:3000${finalChannelRoute}'
165
- ${capitalizedName}CLIClient(url, process.argv.slice(2)).catch(error => {
166
- console.error('Fatal error:', error.message)
167
- process.exit(1)
164
+ const url = process.env.PIKKU_WS_URL || 'ws://localhost:4002${finalChannelRoute}'
165
+
166
+ // Create WebSocket instance
167
+ let WebSocketImpl: any
168
+ if (typeof WebSocket !== 'undefined') {
169
+ WebSocketImpl = WebSocket
170
+ } else {
171
+ // Node.js environment - dynamically import 'ws'
172
+ const wsModule = await import('ws')
173
+ WebSocketImpl = wsModule.default
174
+ }
175
+
176
+ const ws = new WebSocketImpl(url) as WebSocket
177
+ ${capitalizedName}CLIClient(ws, process.argv.slice(2)).catch(error => {
178
+ console.error('Fatal channel CLI error:', error)
179
+ // TODO: We get an error code even when it exists cleanly, investigate
180
+ // process.exit(1)
168
181
  })
169
182
  }
170
183
  `
@@ -47,13 +47,6 @@ export function serializeChannelCLI(
47
47
 
48
48
  collectCommands(programMeta.commands)
49
49
 
50
- // Generate the wireChannel call
51
- const commandEntries = Object.entries(commandMap)
52
- .map(([commandKey, { pikkuFuncName }]) => {
53
- return ` '${commandKey}': ${pikkuFuncName}`
54
- })
55
- .join(',\n')
56
-
57
50
  // Generate imports from function file locations
58
51
  const funcNames = [
59
52
  ...new Set(Object.values(commandMap).map((v) => v.pikkuFuncName)),
@@ -81,19 +74,58 @@ export function serializeChannelCLI(
81
74
  packageMappings
82
75
  )
83
76
 
77
+ // Get relative path to function types file
78
+ const functionTypesPath = getFileImportRelativePath(
79
+ channelFile,
80
+ functionTypesFile,
81
+ packageMappings
82
+ )
83
+
84
84
  return `/**
85
85
  * WebSocket channel backend for '${programName}' CLI commands
86
86
  */
87
87
  import { wireChannel } from '${channelTypesPath}'
88
+ import { pikkuMiddleware } from '${functionTypesPath}'
88
89
  ${imports}
89
90
 
91
+ // Middleware to close the channel after CLI command completes
92
+ const cliCloseOnComplete = pikkuMiddleware(async (services, { channel }, next) => {
93
+ const closeChannel = () => {
94
+ setTimeout(async () => {
95
+ try {
96
+ // This gives time for the response to be sent before closing
97
+ await channel?.close()
98
+ } catch (err) {
99
+ // Ignore errors on close
100
+ }
101
+ }, 200)
102
+ }
103
+
104
+ try {
105
+ const result = await next()
106
+ closeChannel()
107
+ return result
108
+ } catch (error) {
109
+ closeChannel()
110
+ throw error
111
+ }
112
+ })
113
+
90
114
  wireChannel({
91
115
  name: '${finalChannelName}',
92
116
  route: '${finalChannelRoute}',
93
117
  auth: false,
94
118
  onMessageWiring: {
95
119
  command: {
96
- ${commandEntries}
120
+ ${Object.entries(commandMap)
121
+ .map(
122
+ ([commandKey, { pikkuFuncName }]) =>
123
+ ` '${commandKey}': {
124
+ func: ${pikkuFuncName},
125
+ middleware: [cliCloseOnComplete],
126
+ }`
127
+ )
128
+ .join(',\n')}
97
129
  }
98
130
  },
99
131
  tags: ['cli', '${programName}']
@@ -30,6 +30,28 @@ ${userSessionTypeName !== 'Session' ? `type Session = ${userSessionTypeName}` :
30
30
  */
31
31
  type PikkuCLIRender<Data, RequiredServices extends SingletonServices = SingletonServices> = CorePikkuCLIRender<Data, RequiredServices, Session>
32
32
 
33
+ /**
34
+ * Creates a type-safe CLI renderer with access to your application's singleton services.
35
+ * The renderer receives the full singleton services and output data to format and display results.
36
+ *
37
+ * @template Data - The output data type from the CLI command
38
+ * @template RequiredServices - The minimum services required for type checking (defaults to SingletonServices)
39
+ * @param render - Function that receives singleton services and data to render output
40
+ * @returns A CLI renderer configuration
41
+ *
42
+ * @example
43
+ * \`\`\`typescript
44
+ * const myRenderer = pikkuCLIRender<MyData>(({ logger }, data) => {
45
+ * logger.info(data.message)
46
+ * })
47
+ * \`\`\`
48
+ */
49
+ export const pikkuCLIRender = <Data, RequiredServices extends SingletonServices = SingletonServices>(
50
+ render: (services: SingletonServices, data: Data) => void | Promise<void>
51
+ ): PikkuCLIRender<Data, RequiredServices> => {
52
+ return render as any
53
+ }
54
+
33
55
  /**
34
56
  * CLI command configuration with project-specific types.
35
57
  * Uses CoreCLICommandConfig from @pikku/core with local middleware and render types.
@@ -5,6 +5,8 @@ import { writeFileInDir } from '../../../utils/file-writer.js'
5
5
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js'
6
6
 
7
7
  export const serializeServicesMap = (
8
+ allSingletonServices: string[],
9
+ allSessionServices: string[],
8
10
  requiredServices: Set<string>,
9
11
  forceRequiredServices: string[] = [],
10
12
  servicesImport: string,
@@ -18,8 +20,18 @@ export const serializeServicesMap = (
18
20
  // - Session factories
19
21
  const usedServices = new Set(requiredServices)
20
22
 
21
- // Internal services that are created internally and not via the create service script
22
- const internalServices = new Set(['rpc', 'mcp', 'channel', 'userSession'])
23
+ // Internal services that are created internally by the framework (PikkuInteraction)
24
+ // These should not appear in the services maps
25
+ const internalServices = new Set([
26
+ 'rpc',
27
+ 'mcp',
28
+ 'channel',
29
+ 'userSession',
30
+ 'cli',
31
+ 'http',
32
+ 'queue',
33
+ 'scheduledTask',
34
+ ])
23
35
 
24
36
  // Add force-required services that might not be detected from function inspection
25
37
  forceRequiredServices.forEach((service) => {
@@ -28,44 +40,59 @@ export const serializeServicesMap = (
28
40
  }
29
41
  })
30
42
 
31
- // Create a map of services with true for all needed services
32
- const servicesMap = Object.fromEntries(
33
- Array.from(usedServices)
34
- .sort()
35
- .map((service) => [service, true])
36
- )
37
-
38
- // Generate the TypeScript code
39
- const serviceKeys = Object.keys(servicesMap).sort()
40
-
41
43
  // Services that are always required internally by the framework
42
44
  const defaultServices = ['config', 'logger', 'variables', 'schema']
45
+ defaultServices.forEach((service) => usedServices.add(service))
43
46
 
44
- // Combine default services with detected services
45
- const allRequiredServices = [
46
- ...new Set([...defaultServices, ...serviceKeys]),
47
- ].sort()
47
+ // Create singleton services map: all singleton services with true/false based on usage
48
+ const singletonServicesMap: Record<string, boolean> = {}
49
+ allSingletonServices.forEach((service) => {
50
+ singletonServicesMap[service] = usedServices.has(service)
51
+ })
52
+
53
+ // Create session services map: all session services with true/false based on usage
54
+ // Exclude internal framework services (PikkuInteraction)
55
+ const sessionServicesMap: Record<string, boolean> = {}
56
+ allSessionServices.forEach((service) => {
57
+ if (!internalServices.has(service)) {
58
+ sessionServicesMap[service] = usedServices.has(service)
59
+ }
60
+ })
48
61
 
49
- // For RequiredSingletonServices, we need to pick from the actual SingletonServices interface
50
- // This will be resolved at compile time based on what's actually in the SingletonServices interface
51
- // We don't need to hardcode which services are singletons beyond the core framework ones
62
+ // Get all required service names (those marked as true)
63
+ const requiredSingletonServiceNames = Object.keys(singletonServicesMap)
64
+ .filter((key) => singletonServicesMap[key])
65
+ .sort()
66
+ const requiredSessionServiceNames = Object.keys(sessionServicesMap)
67
+ .filter((key) => sessionServicesMap[key])
68
+ .sort()
52
69
 
53
70
  const code = [
54
71
  servicesImport,
55
72
  sessionServicesImport,
56
- "import type { PikkuInteraction } from '@pikku/core'",
57
73
  '',
58
- 'export const singletonServices = {',
59
- ...Object.keys(servicesMap).map((service) => ` '${service}': true,`),
74
+ '// Singleton services map: true if required, false if available but unused',
75
+ 'export const requiredSingletonServices = {',
76
+ ...Object.keys(singletonServicesMap)
77
+ .sort()
78
+ .map((service) => ` '${service}': ${singletonServicesMap[service]},`),
79
+ '} as const',
80
+ '',
81
+ '// Session services map: true if required, false if available but unused',
82
+ 'export const requiredSessionServices = {',
83
+ ...Object.keys(sessionServicesMap)
84
+ .sort()
85
+ .map((service) => ` '${service}': ${sessionServicesMap[service]},`),
60
86
  '} as const',
61
87
  '',
62
- '// Singleton services (created once at startup)',
63
- '// Only includes services that are both required and available in SingletonServices',
64
- `export type RequiredSingletonServices = Pick<SingletonServices, Extract<keyof SingletonServices, ${allRequiredServices.map((key) => `'${key}'`).join(' | ')}>> & Partial<Omit<SingletonServices, ${allRequiredServices.map((key) => `'${key}'`).join(' | ')}>>`,
88
+ '// Type exports',
89
+ requiredSingletonServiceNames.length > 0
90
+ ? `export type RequiredSingletonServices = Pick<SingletonServices, ${requiredSingletonServiceNames.map((key) => `'${key}'`).join(' | ')}> & Partial<Omit<SingletonServices, ${requiredSingletonServiceNames.map((key) => `'${key}'`).join(' | ')}>>`
91
+ : 'export type RequiredSingletonServices = Partial<SingletonServices>',
65
92
  '',
66
- '// Session services (created per request, can access singleton services)',
67
- '// Omits singleton services and PikkuInteraction (mcp, rpc, http, channel)',
68
- `export type RequiredSessionServices = Omit<Services, keyof SingletonServices | keyof PikkuInteraction>`,
93
+ requiredSessionServiceNames.length > 0
94
+ ? `export type RequiredSessionServices = Pick<Services, ${requiredSessionServiceNames.map((key) => `'${key}'`).join(' | ')}> & Partial<Omit<Services, ${requiredSessionServiceNames.map((key) => `'${key}'`).join(' | ')}>>`
95
+ : 'export type RequiredSessionServices = Partial<Services>',
69
96
  '',
70
97
  ].join('\n')
71
98
 
@@ -95,6 +122,8 @@ export const pikkuServices: any = pikkuSessionlessFunc<void, void>({
95
122
  const sessionServicesImport = `import type { ${sessionServicesType.type} } from '${getFileImportRelativePath(config.typesDeclarationFile, sessionServicesType.typePath, config.packageMappings)}'`
96
123
 
97
124
  const servicesCode = serializeServicesMap(
125
+ visitState.serviceAggregation.allSingletonServices,
126
+ visitState.serviceAggregation.allSessionServices,
98
127
  visitState.serviceAggregation.requiredServices,
99
128
  config.forceRequiredServices,
100
129
  servicesImport,
@@ -21,7 +21,8 @@ export const pikkuSchemas: any = pikkuSessionlessFunc<
21
21
  visitState.functions.typesMap,
22
22
  visitState.functions.meta,
23
23
  visitState.http.meta,
24
- config.schemasFromTypes
24
+ config.schemasFromTypes,
25
+ config.schema?.additionalProperties
25
26
  )
26
27
 
27
28
  await saveSchemas(
@@ -30,8 +31,8 @@ export const pikkuSchemas: any = pikkuSessionlessFunc<
30
31
  schemas,
31
32
  visitState.functions.typesMap,
32
33
  visitState.functions.meta,
33
- config.supportsImportAttributes,
34
- config.schemasFromTypes
34
+ config.schemasFromTypes,
35
+ config.schema?.supportsImportAttributes
35
36
  )
36
37
 
37
38
  return true
@@ -27,7 +27,8 @@ export const pikkuOpenAPI: any = pikkuSessionlessFunc<void, void>({
27
27
  functions.typesMap,
28
28
  functions.meta,
29
29
  http.meta,
30
- schemasFromTypes
30
+ schemasFromTypes,
31
+ config.schema?.additionalProperties
31
32
  )
32
33
  const openAPISpec = await generateOpenAPISpec(
33
34
  logger,
@@ -15,17 +15,18 @@ export const pikkuMiddleware: any = pikkuSessionlessFunc<
15
15
 
16
16
  let filesGenerated = false
17
17
 
18
- // Check if there are any middleware group factories
19
- const hasHTTPFactories = Array.from(
20
- state.http.routeMiddleware.values()
21
- ).some((meta) => meta.exportName && meta.isFactory)
22
- const hasTagFactories = Array.from(
23
- state.middleware.tagMiddleware.values()
24
- ).some((meta) => meta.exportName && meta.isFactory)
25
- const hasFactories = hasHTTPFactories || hasTagFactories
18
+ // Check if there are any middleware groups
19
+ const hasHTTPGroups = state.http.routeMiddleware.size > 0
20
+ const hasTagGroups = state.middleware.tagMiddleware.size > 0
21
+
22
+ if (hasHTTPGroups || hasTagGroups) {
23
+ await writeFileInDir(
24
+ logger,
25
+ config.middlewareGroupsMetaFile,
26
+ serializeMiddlewareGroupsMeta(state)
27
+ )
26
28
 
27
- // Generate middleware imports file if there are factories
28
- if (hasFactories) {
29
+ // Always generate middleware imports file when groups exist (even if empty)
29
30
  await writeFileInDir(
30
31
  logger,
31
32
  middlewareFile,
@@ -36,19 +37,7 @@ export const pikkuMiddleware: any = pikkuSessionlessFunc<
36
37
  packageMappings
37
38
  )
38
39
  )
39
- filesGenerated = true
40
- }
41
40
 
42
- // Generate middleware groups metadata file
43
- const hasHTTPGroups = state.http.routeMiddleware.size > 0
44
- const hasTagGroups = state.middleware.tagMiddleware.size > 0
45
-
46
- if (hasHTTPGroups || hasTagGroups) {
47
- await writeFileInDir(
48
- logger,
49
- config.middlewareGroupsMetaFile,
50
- serializeMiddlewareGroupsMeta(state)
51
- )
52
41
  filesGenerated = true
53
42
  }
54
43
 
@@ -14,10 +14,19 @@ export const pikkuQueue: any = pikkuSessionlessFunc<void, boolean>({
14
14
  } = config
15
15
  const { queueWorkers } = visitState
16
16
 
17
+ // Add remote RPC worker to queue metadata if it exists
18
+ const queueMeta = { ...queueWorkers.meta }
19
+ if (config.rpc?.remoteRpcWorkersPath) {
20
+ queueMeta['pikku-remote-internal-rpc'] = {
21
+ pikkuFuncName: 'pikkuRemoteInternalRPC',
22
+ queueName: 'pikku-remote-internal-rpc',
23
+ }
24
+ }
25
+
17
26
  await writeFileInDir(
18
27
  logger,
19
28
  queueWorkersWiringMetaFile,
20
- serializeQueueMeta(queueWorkers.meta)
29
+ serializeQueueMeta(queueMeta)
21
30
  )
22
31
  await writeFileInDir(
23
32
  logger,