@pikku/cli 0.7.7 → 0.8.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 (190) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/pikku-all.ts +87 -48
  3. package/bin/pikku-fetch.ts +5 -37
  4. package/bin/pikku-nextjs.ts +7 -112
  5. package/bin/pikku-openapi.ts +6 -53
  6. package/bin/pikku-queue-service.ts +24 -0
  7. package/bin/pikku-schemas.ts +6 -32
  8. package/bin/pikku-websocket.ts +6 -40
  9. package/bin/pikku.ts +2 -0
  10. package/dist/bin/pikku-all.d.ts +1 -1
  11. package/dist/bin/pikku-all.js +68 -48
  12. package/dist/bin/pikku-fetch.d.ts +1 -3
  13. package/dist/bin/pikku-fetch.js +4 -14
  14. package/dist/bin/pikku-nextjs.d.ts +1 -4
  15. package/dist/bin/pikku-nextjs.js +5 -39
  16. package/dist/bin/pikku-openapi.d.ts +0 -3
  17. package/dist/bin/pikku-openapi.js +5 -23
  18. package/dist/bin/pikku-queue-service.d.ts +4 -0
  19. package/dist/bin/pikku-queue-service.js +15 -0
  20. package/dist/bin/pikku-schemas.d.ts +0 -3
  21. package/dist/bin/pikku-schemas.js +5 -11
  22. package/dist/bin/pikku-websocket.d.ts +1 -3
  23. package/dist/bin/pikku-websocket.js +4 -17
  24. package/dist/bin/pikku.js +2 -0
  25. package/dist/src/events/channels/pikku-channels.d.ts +2 -0
  26. package/dist/src/events/channels/pikku-channels.js +9 -0
  27. package/dist/src/events/channels/pikku-command-channels-map.d.ts +2 -0
  28. package/dist/src/events/channels/pikku-command-channels-map.js +8 -0
  29. package/dist/src/events/channels/pikku-command-channels.d.ts +2 -0
  30. package/dist/src/events/channels/pikku-command-channels.js +9 -0
  31. package/dist/src/events/channels/pikku-command-websocket-typed.d.ts +2 -0
  32. package/dist/src/events/channels/pikku-command-websocket-typed.js +15 -0
  33. package/dist/src/{serialize-typed-channel-map.js → events/channels/serialize-typed-channel-map.js} +2 -2
  34. package/dist/src/events/fetch/index.d.ts +2 -0
  35. package/dist/src/events/fetch/index.js +12 -0
  36. package/dist/src/events/functions/pikku-command-function-types.d.ts +2 -0
  37. package/dist/src/events/functions/pikku-command-function-types.js +13 -0
  38. package/dist/src/events/functions/pikku-command-functions.d.ts +6 -0
  39. package/dist/src/events/functions/pikku-command-functions.js +35 -0
  40. package/dist/src/events/functions/pikku-function-types.d.ts +2 -0
  41. package/dist/{bin → src/events/functions}/pikku-function-types.js +6 -6
  42. package/dist/src/events/functions/pikku-functions.d.ts +6 -0
  43. package/dist/{bin → src/events/functions}/pikku-functions.js +5 -5
  44. package/dist/src/events/http/pikku-command-http-map.d.ts +2 -0
  45. package/dist/src/events/http/pikku-command-http-map.js +8 -0
  46. package/dist/src/events/http/pikku-command-http-routes.d.ts +2 -0
  47. package/dist/src/events/http/pikku-command-http-routes.js +9 -0
  48. package/dist/src/events/http/pikku-command-nextjs.d.ts +2 -0
  49. package/dist/src/events/http/pikku-command-nextjs.js +36 -0
  50. package/dist/src/events/http/pikku-command-openapi.d.ts +2 -0
  51. package/dist/src/events/http/pikku-command-openapi.js +20 -0
  52. package/dist/src/events/http/pikku-http-routes.d.ts +2 -0
  53. package/dist/src/events/http/pikku-http-routes.js +9 -0
  54. package/dist/src/{serialize-typed-http-map.js → events/http/serialize-typed-http-map.js} +1 -1
  55. package/dist/src/events/mcp/pikku-command-mcp-json.d.ts +2 -0
  56. package/dist/src/events/mcp/pikku-command-mcp-json.js +13 -0
  57. package/dist/src/events/mcp/pikku-command-mcp.d.ts +2 -0
  58. package/dist/src/events/mcp/pikku-command-mcp.js +54 -0
  59. package/dist/src/events/mcp/serialize-mcp-json.d.ts +5 -0
  60. package/dist/src/events/mcp/serialize-mcp-json.js +101 -0
  61. package/dist/src/events/queue/pikku-command-queue-map.d.ts +2 -0
  62. package/dist/src/events/queue/pikku-command-queue-map.js +8 -0
  63. package/dist/src/events/queue/pikku-command-queue-service.d.ts +2 -0
  64. package/dist/src/events/queue/pikku-command-queue-service.js +12 -0
  65. package/dist/src/events/queue/pikku-command-queue.d.ts +2 -0
  66. package/dist/src/events/queue/pikku-command-queue.js +10 -0
  67. package/dist/src/events/queue/pikku-queue-map.d.ts +2 -0
  68. package/dist/src/events/queue/pikku-queue-map.js +8 -0
  69. package/dist/src/events/queue/pikku-queue.d.ts +2 -0
  70. package/dist/src/events/queue/pikku-queue.js +10 -0
  71. package/dist/src/events/queue/serialize-queue-map.d.ts +4 -0
  72. package/dist/src/events/queue/serialize-queue-map.js +77 -0
  73. package/dist/src/events/queue/serialize-queue-meta.d.ts +2 -0
  74. package/dist/src/events/queue/serialize-queue-meta.js +6 -0
  75. package/dist/src/events/queue/serialize-queue-wrapper.d.ts +1 -0
  76. package/dist/src/events/queue/serialize-queue-wrapper.js +35 -0
  77. package/dist/src/events/rpc/pikku-command-rpc-map.d.ts +2 -0
  78. package/dist/src/events/rpc/pikku-command-rpc-map.js +8 -0
  79. package/dist/src/events/rpc/pikku-command-rpc.d.ts +2 -0
  80. package/dist/src/events/rpc/pikku-command-rpc.js +6 -0
  81. package/dist/src/events/rpc/pikku-rpc.d.ts +2 -0
  82. package/dist/src/events/rpc/pikku-rpc.js +6 -0
  83. package/dist/src/{serialize-typed-rpc-map.d.ts → events/rpc/serialize-typed-rpc-map.d.ts} +1 -1
  84. package/dist/src/{serialize-typed-rpc-map.js → events/rpc/serialize-typed-rpc-map.js} +2 -2
  85. package/dist/src/events/scheduler/pikku-command-scheduler.d.ts +2 -0
  86. package/dist/src/events/scheduler/pikku-command-scheduler.js +10 -0
  87. package/dist/src/inspector-glob.d.ts +1 -1
  88. package/dist/src/inspector-glob.js +3 -3
  89. package/dist/src/pikku-cli-config.d.ts +8 -1
  90. package/dist/src/pikku-cli-config.js +59 -22
  91. package/dist/src/pikku-command-schemas.d.ts +2 -0
  92. package/dist/src/pikku-command-schemas.js +8 -0
  93. package/dist/src/runtimes/nextjs/pikku-command-nextjs.d.ts +2 -0
  94. package/dist/src/runtimes/nextjs/pikku-command-nextjs.js +36 -0
  95. package/dist/src/schema-generator.d.ts +3 -2
  96. package/dist/src/schema-generator.js +7 -7
  97. package/dist/src/schemas.d.ts +2 -0
  98. package/dist/src/schemas.js +8 -0
  99. package/dist/src/{utils/serialize-import-map.js → serialize-import-map.js} +3 -0
  100. package/dist/src/serialize-pikku-types.js +378 -1
  101. package/dist/src/types.d.ts +5 -0
  102. package/dist/src/types.js +1 -0
  103. package/dist/src/{utils/utils.d.ts → utils.d.ts} +17 -7
  104. package/dist/src/{utils/utils.js → utils.js} +47 -29
  105. package/dist/tsconfig.tsbuildinfo +1 -1
  106. package/package.json +3 -3
  107. package/{bin → src/events/channels}/pikku-channels.ts +8 -3
  108. package/src/events/channels/pikku-command-channels-map.ts +26 -0
  109. package/src/events/channels/pikku-command-channels.ts +38 -0
  110. package/src/events/channels/pikku-command-websocket-typed.ts +36 -0
  111. package/src/{serialize-typed-channel-map.ts → events/channels/serialize-typed-channel-map.ts} +2 -2
  112. package/src/events/fetch/index.ts +33 -0
  113. package/src/events/functions/pikku-command-function-types.ts +48 -0
  114. package/src/events/functions/pikku-command-functions.ts +84 -0
  115. package/{bin → src/events/functions}/pikku-function-types.ts +12 -15
  116. package/{bin → src/events/functions}/pikku-functions.ts +9 -6
  117. package/src/events/http/pikku-command-http-map.ts +27 -0
  118. package/src/events/http/pikku-command-http-routes.ts +40 -0
  119. package/src/events/http/pikku-command-nextjs.ts +111 -0
  120. package/src/events/http/pikku-command-openapi.ts +54 -0
  121. package/{bin → src/events/http}/pikku-http-routes.ts +8 -3
  122. package/src/{serialize-typed-http-map.ts → events/http/serialize-typed-http-map.ts} +1 -1
  123. package/src/events/mcp/pikku-command-mcp-json.ts +33 -0
  124. package/src/events/mcp/pikku-command-mcp.ts +110 -0
  125. package/src/events/mcp/serialize-mcp-json.ts +159 -0
  126. package/src/events/queue/pikku-command-queue-map.ts +26 -0
  127. package/src/events/queue/pikku-command-queue-service.ts +33 -0
  128. package/src/events/queue/pikku-command-queue.ts +42 -0
  129. package/src/events/queue/pikku-queue-map.ts +26 -0
  130. package/src/events/queue/pikku-queue.ts +40 -0
  131. package/src/events/queue/serialize-queue-map.ts +119 -0
  132. package/src/events/queue/serialize-queue-meta.ts +10 -0
  133. package/src/events/queue/serialize-queue-wrapper.ts +35 -0
  134. package/src/events/rpc/pikku-command-rpc-map.ts +26 -0
  135. package/src/events/rpc/pikku-command-rpc.ts +22 -0
  136. package/{bin → src/events/rpc}/pikku-rpc.ts +8 -6
  137. package/src/{serialize-typed-rpc-map.ts → events/rpc/serialize-typed-rpc-map.ts} +4 -3
  138. package/{bin/pikku-scheduler.ts → src/events/scheduler/pikku-command-scheduler.ts} +11 -8
  139. package/src/inspector-glob.ts +3 -1
  140. package/src/pikku-cli-config.ts +93 -32
  141. package/src/pikku-command-schemas.ts +33 -0
  142. package/src/runtimes/nextjs/pikku-command-nextjs.ts +110 -0
  143. package/src/schema-generator.ts +8 -3
  144. package/src/schemas.ts +33 -0
  145. package/src/{utils/serialize-import-map.ts → serialize-import-map.ts} +5 -0
  146. package/src/serialize-pikku-types.ts +378 -1
  147. package/src/types.ts +16 -0
  148. package/src/{utils/utils.ts → utils.ts} +55 -31
  149. package/tsconfig.json +1 -1
  150. package/bin/pikku-channels-map.ts +0 -25
  151. package/bin/pikku-http-map.ts +0 -26
  152. package/bin/pikku-nextjs.test.ts +0 -279
  153. package/bin/pikku-rpc-map.ts +0 -25
  154. package/dist/bin/pikku-channels-map.d.ts +0 -3
  155. package/dist/bin/pikku-channels-map.js +0 -8
  156. package/dist/bin/pikku-channels.d.ts +0 -3
  157. package/dist/bin/pikku-channels.js +0 -9
  158. package/dist/bin/pikku-function-types.d.ts +0 -4
  159. package/dist/bin/pikku-functions.d.ts +0 -7
  160. package/dist/bin/pikku-http-map.d.ts +0 -3
  161. package/dist/bin/pikku-http-map.js +0 -8
  162. package/dist/bin/pikku-http-routes.d.ts +0 -3
  163. package/dist/bin/pikku-http-routes.js +0 -9
  164. package/dist/bin/pikku-rpc-map.d.ts +0 -3
  165. package/dist/bin/pikku-rpc-map.js +0 -8
  166. package/dist/bin/pikku-rpc.d.ts +0 -3
  167. package/dist/bin/pikku-rpc.js +0 -6
  168. package/dist/bin/pikku-scheduler.d.ts +0 -3
  169. package/dist/bin/pikku-scheduler.js +0 -10
  170. /package/dist/src/{serialize-typed-channel-map.d.ts → events/channels/serialize-typed-channel-map.d.ts} +0 -0
  171. /package/dist/src/{serialize-websocket-wrapper.d.ts → events/channels/serialize-websocket-wrapper.d.ts} +0 -0
  172. /package/dist/src/{serialize-websocket-wrapper.js → events/channels/serialize-websocket-wrapper.js} +0 -0
  173. /package/dist/src/{openapi-spec-generator.d.ts → events/http/openapi-spec-generator.d.ts} +0 -0
  174. /package/dist/src/{openapi-spec-generator.js → events/http/openapi-spec-generator.js} +0 -0
  175. /package/dist/src/{serialize-fetch-wrapper.d.ts → events/http/serialize-fetch-wrapper.d.ts} +0 -0
  176. /package/dist/src/{serialize-fetch-wrapper.js → events/http/serialize-fetch-wrapper.js} +0 -0
  177. /package/dist/src/{serialize-typed-http-map.d.ts → events/http/serialize-typed-http-map.d.ts} +0 -0
  178. /package/dist/src/{serialize-scheduler-meta.d.ts → events/scheduler/serialize-scheduler-meta.d.ts} +0 -0
  179. /package/dist/src/{serialize-scheduler-meta.js → events/scheduler/serialize-scheduler-meta.js} +0 -0
  180. /package/dist/src/{serialize-nextjs-backend-wrapper.d.ts → runtimes/nextjs/serialize-nextjs-backend-wrapper.d.ts} +0 -0
  181. /package/dist/src/{serialize-nextjs-backend-wrapper.js → runtimes/nextjs/serialize-nextjs-backend-wrapper.js} +0 -0
  182. /package/dist/src/{serialize-nextjs-http-wrapper.d.ts → runtimes/nextjs/serialize-nextjs-http-wrapper.d.ts} +0 -0
  183. /package/dist/src/{serialize-nextjs-http-wrapper.js → runtimes/nextjs/serialize-nextjs-http-wrapper.js} +0 -0
  184. /package/dist/src/{utils/serialize-import-map.d.ts → serialize-import-map.d.ts} +0 -0
  185. /package/src/{serialize-websocket-wrapper.ts → events/channels/serialize-websocket-wrapper.ts} +0 -0
  186. /package/src/{openapi-spec-generator.ts → events/http/openapi-spec-generator.ts} +0 -0
  187. /package/src/{serialize-fetch-wrapper.ts → events/http/serialize-fetch-wrapper.ts} +0 -0
  188. /package/src/{serialize-scheduler-meta.ts → events/scheduler/serialize-scheduler-meta.ts} +0 -0
  189. /package/src/{serialize-nextjs-backend-wrapper.ts → runtimes/nextjs/serialize-nextjs-backend-wrapper.ts} +0 -0
  190. /package/src/{serialize-nextjs-http-wrapper.ts → runtimes/nextjs/serialize-nextjs-http-wrapper.ts} +0 -0
@@ -18,43 +18,158 @@ import { CoreAPIFunction, CoreAPIFunctionSessionless } from '@pikku/core/functio
18
18
  import { CoreHTTPFunctionRoute, AssertRouteParams, addHTTPRoute as addCoreHTTPRoute } from '@pikku/core/http'
19
19
  import { CoreScheduledTask, addScheduledTask as addCoreScheduledTask } from '@pikku/core/scheduler'
20
20
  import { CoreAPIChannel, PikkuChannel, addChannel as addCoreChannel } from '@pikku/core/channel'
21
+ import { CoreQueueWorker, addQueueWorker as addCoreQueueWorker } from '@pikku/core/queue'
22
+ import { CoreMCPResource, CoreMCPTool, CoreMCPPrompt, addMCPResource as addCoreMCPResource, addMCPTool as addCoreMCPTool, addMCPPrompt as addCoreMCPPrompt, MCPResourceResponse, MCPToolResponse, MCPPromptResponse, PikkuMCP } from '@pikku/core'
21
23
 
22
24
  ${userSessionTypeImport}
23
25
  ${singletonServicesTypeImport}
24
26
  ${sessionServicesTypeImport}
25
27
  ${rpcMapTypeImport}
26
28
 
29
+ /**
30
+ * Type-safe API permission definition that integrates with your application's session type.
31
+ * Use this to define authorization logic for your API endpoints.
32
+ *
33
+ * @template In - The input type that the permission check will receive
34
+ * @template RequiredServices - The services required for this permission check
35
+ */
27
36
  export type APIPermission<In = unknown, RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = CoreAPIPermission<In, RequiredServices, ${userSessionTypeName}>
37
+
38
+ /**
39
+ * Type-safe middleware definition that can access your application's services and session.
40
+ * Use this to define reusable middleware that can be applied to multiple routes.
41
+ *
42
+ * @template RequiredServices - The services required for this middleware
43
+ */
28
44
  export type APIMiddleware<RequiredServices extends ${singletonServicesTypeName} = ${singletonServicesTypeName}> = PikkuMiddleware<RequiredServices, ${userSessionTypeName}>
29
45
 
46
+ /**
47
+ * A sessionless API function that doesn't require user authentication.
48
+ * Use this for public endpoints, health checks, or operations that don't need user context.
49
+ *
50
+ * @template In - The input type
51
+ * @template Out - The output type that the function returns
52
+ * @template ChannelData - Channel data type (null = optional channel)
53
+ * @template MCPData - MCP data type (null = optional MCP)
54
+ * @template RequiredServices - Services required by this function
55
+ */
30
56
  type APIFunctionSessionless<
31
57
  In = unknown,
32
58
  Out = never,
33
59
  ChannelData = null, // null means optional channel
60
+ MCPData = null, // null means optional MCP
34
61
  RequiredServices extends Services = Services &
35
62
  { rpc: TypedPikkuRPC } & (
36
63
  [ChannelData] extends [null]
37
64
  ? { channel?: PikkuChannel<unknown, Out> } // Optional channel
38
65
  : { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
66
+ ) & ([MCPData] extends [null]
67
+ ? { mcp?: PikkuMCP } // Optional MCP
68
+ : { mcp: PikkuMCP } // Required MCP
39
69
  )
40
70
  > = CoreAPIFunctionSessionless<In, Out, ChannelData, RequiredServices, ${userSessionTypeName}>
41
71
 
72
+ /**
73
+ * A session-aware API function that requires user authentication.
74
+ * Use this for protected endpoints that need access to user session data.
75
+ *
76
+ * @template In - The input type
77
+ * @template Out - The output type that the function returns
78
+ * @template ChannelData - Channel data type (null = optional channel)
79
+ * @template MCPData - MCP data type (null = optional MCP)
80
+ * @template RequiredServices - Services required by this function
81
+ */
42
82
  type APIFunction<
43
83
  In = unknown,
44
84
  Out = never,
45
85
  ChannelData = null, // null means optional channel
86
+ MCPData = null, // null means optional MCP
46
87
  RequiredServices extends Services = Services &
47
88
  { rpc: TypedPikkuRPC } & (
48
89
  [ChannelData] extends [null]
49
90
  ? { channel?: PikkuChannel<unknown, Out> } // Optional channel
50
91
  : { channel: PikkuChannel<ChannelData, Out> } // Required channel with any data type
92
+ ) & ([MCPData] extends [null]
93
+ ? { mcp?: PikkuMCP } // Optional MCP
94
+ : { mcp: PikkuMCP } // Required MCP
51
95
  )
52
96
  > = CoreAPIFunction<In, Out, ChannelData, RequiredServices, ${userSessionTypeName}>
53
97
 
98
+ /**
99
+ * Type definition for HTTP API routes with type-safe path parameters.
100
+ * Supports both authenticated and unauthenticated functions.
101
+ *
102
+ * @template In - Input type for the route
103
+ * @template Out - Output type for the route
104
+ * @template Route - String literal type for the route path (e.g., "/users/:id")
105
+ */
54
106
  type APIRoute<In, Out, Route extends string> = CoreHTTPFunctionRoute<In, Out, Route, APIFunction<In, Out>, APIFunctionSessionless<In, Out>, APIPermission<In>, APIMiddleware>
107
+
108
+ /**
109
+ * Type definition for WebSocket channels with typed data exchange.
110
+ * Supports connection, disconnection, and message handling.
111
+ *
112
+ * @template ChannelData - Type of data exchanged through the channel
113
+ * @template Channel - String literal type for the channel name
114
+ */
55
115
  type APIChannel<ChannelData, Channel extends string> = CoreAPIChannel<ChannelData, Channel, APIFunction<void, unknown> | APIFunction<void, unknown, ChannelData>, APIFunction<void, void> | APIFunction<void, void, ChannelData>, APIFunction<any, any> | APIFunction<any, any, ChannelData>, APIPermission>
56
- type ScheduledTask = CoreScheduledTask<APIFunctionSessionless<void, void>, ${userSessionTypeName}>
57
116
 
117
+ /**
118
+ * Type definition for scheduled tasks that run at specified intervals.
119
+ * These are sessionless functions that execute based on cron expressions.
120
+ */
121
+ type ScheduledTask = CoreScheduledTask<APIFunctionSessionless<void, void>>
122
+
123
+ /**
124
+ * Type definition for queue workers that process background jobs.
125
+ *
126
+ * @template In - Input type for the queue job
127
+ * @template Out - Output type for the queue job
128
+ */
129
+ type QueueWorker<In, Out> = CoreQueueWorker<APIFunctionSessionless<In, Out>>
130
+
131
+ /**
132
+ * Type definition for MCP resources that provide data to AI models.
133
+ *
134
+ * @template In - Input type for the resource request
135
+ */
136
+ type MCPResource<In> = CoreMCPResource<APIFunctionSessionless<In, MCPResourceResponse, null, true>>
137
+
138
+ /**
139
+ * Type definition for MCP tools that AI models can invoke.
140
+ *
141
+ * @template In - Input type for the tool invocation
142
+ */
143
+ type MCPTool<In> = CoreMCPTool<APIFunctionSessionless<In, MCPToolResponse, null, true>>
144
+
145
+ /**
146
+ * Type definition for MCP prompts that provide templates to AI models.
147
+ *
148
+ * @template In - Input type for the prompt parameters
149
+ */
150
+ type MCPPrompt<In> = CoreMCPPrompt<APIFunctionSessionless<In, MCPPromptResponse, null, true>>
151
+
152
+ /**
153
+ * Creates a Pikku function that can be either session-aware or sessionless.
154
+ * This is the main function wrapper for creating API endpoints.
155
+ *
156
+ * @template In - Input type for the function
157
+ * @template Out - Output type for the function
158
+ * @param func - Function definition, either direct function or configuration object
159
+ * @returns The unwrapped function for internal use
160
+ *
161
+ * @example
162
+ * \\\`\\\`\\\`typescript
163
+ * const createUser = pikkuFunc<{name: string, email: string}, {id: number, message: string}>({
164
+ * func: async ({db, logger}, input) => {
165
+ * logger.info('Creating user', input.name)
166
+ * const user = await db.users.create(input)
167
+ * return {id: user.id, message: \\\`User \\\${input.name} created successfully\\\`}
168
+ * },
169
+ * auth: true
170
+ * })
171
+ * \\\`\\\`\\\`
172
+ */
58
173
  export const pikkuFunc = <In, Out = unknown>(
59
174
  func:
60
175
  | APIFunction<In, Out>
@@ -72,6 +187,26 @@ export const pikkuFunc = <In, Out = unknown>(
72
187
  return typeof func === 'function' ? func : func.func
73
188
  }
74
189
 
190
+ /**
191
+ * Creates a sessionless Pikku function that doesn't require user authentication.
192
+ * Use this for public endpoints, webhooks, or background tasks.
193
+ *
194
+ * @template In - Input type for the function
195
+ * @template Out - Output type for the function
196
+ * @param func - Function definition, either direct function or configuration object
197
+ * @returns The unwrapped function for internal use
198
+ *
199
+ * @example
200
+ * \\\`\\\`\\\`typescript
201
+ * const healthCheck = pikkuSessionlessFunc<void, {status: string, timestamp: string}>({
202
+ * func: async ({logger}) => {
203
+ * logger.info('Health check requested')
204
+ * return {status: 'healthy', timestamp: new Date().toISOString()}
205
+ * },
206
+ * name: 'healthCheck'
207
+ * })
208
+ * \\\`\\\`\\\`
209
+ */
75
210
  export const pikkuSessionlessFunc = <In, Out = unknown>(
76
211
  func:
77
212
  | APIFunctionSessionless<In, Out>
@@ -83,6 +218,26 @@ export const pikkuSessionlessFunc = <In, Out = unknown>(
83
218
  return typeof func === 'function' ? func : func.func
84
219
  }
85
220
 
221
+ /**
222
+ * Creates a function that handles WebSocket channel connections.
223
+ * Called when a client connects to a channel.
224
+ *
225
+ * @template Out - Output type for connection response
226
+ * @template ChannelData - Type of data associated with the channel
227
+ * @param func - Function definition, either direct function or configuration object
228
+ * @returns The unwrapped function for internal use
229
+ *
230
+ * @example
231
+ * \\\`\\\`\\\`typescript
232
+ * const onChatConnect = pikkuChannelConnectionFunc<string>({
233
+ * func: async ({logger, channel, eventHub}) => {
234
+ * logger.info('User connected to chat')
235
+ * await eventHub.publish('chat:join', channel.channelId, {channelId: channel.channelId})
236
+ * return 'Welcome to the chat!'
237
+ * }
238
+ * })
239
+ * \\\`\\\`\\\`
240
+ */
86
241
  export const pikkuChannelConnectionFunc = <Out = unknown, ChannelData = unknown>(
87
242
  func:
88
243
  | APIFunctionSessionless<void, Out, ChannelData>
@@ -94,6 +249,24 @@ export const pikkuChannelConnectionFunc = <Out = unknown, ChannelData = unknown>
94
249
  return typeof func === 'function' ? func : func.func
95
250
  }
96
251
 
252
+ /**
253
+ * Creates a function that handles WebSocket channel disconnections.
254
+ * Called when a client disconnects from a channel.
255
+ *
256
+ * @template ChannelData - Type of data associated with the channel
257
+ * @param func - Function definition, either direct function or configuration object
258
+ * @returns The unwrapped function for internal use
259
+ *
260
+ * @example
261
+ * \\\`\\\`\\\`typescript
262
+ * const onChatDisconnect = pikkuChannelDisconnectionFunc({
263
+ * func: async ({logger, channel, eventHub}) => {
264
+ * logger.info('User disconnected from chat')
265
+ * await eventHub.publish('chat:join', channel.channelId, {channelId: channel.channelId})
266
+ * }
267
+ * })
268
+ * \\\`\\\`\\\`
269
+ */
97
270
  export const pikkuChannelDisconnectionFunc = <ChannelData = unknown>(
98
271
  func:
99
272
  | APIFunctionSessionless<void, void, ChannelData>
@@ -105,6 +278,25 @@ export const pikkuChannelDisconnectionFunc = <ChannelData = unknown>(
105
278
  return typeof func === 'function' ? func : func.func
106
279
  }
107
280
 
281
+ /**
282
+ * Creates a function that handles WebSocket channel messages.
283
+ * Called when a message is received on a channel.
284
+ *
285
+ * @template In - Input type for channel messages
286
+ * @template Out - Output type for channel responses
287
+ * @template ChannelData - Type of data associated with the channel
288
+ * @param func - Function definition, either direct function or configuration object
289
+ * @returns The unwrapped function for internal use
290
+ *
291
+ * @example
292
+ * \\\`\\\`\\\`typescript
293
+ * const handleChatMessage = pikkuChannelFunc<{message: string}, void>({
294
+ * func: async ({logger, channel}, input) => {
295
+ * logger.info('Chat message received:', input.message)
296
+ * }
297
+ * })
298
+ * \\\`\\\`\\\`
299
+ */
108
300
  export const pikkuChannelFunc = <In = unknown, Out = unknown, ChannelData = unknown>(
109
301
  func:
110
302
  | APIFunctionSessionless<In, Out, ChannelData>
@@ -116,6 +308,22 @@ export const pikkuChannelFunc = <In = unknown, Out = unknown, ChannelData = unkn
116
308
  return typeof func === 'function' ? func : func.func
117
309
  }
118
310
 
311
+ /**
312
+ * Creates a function that takes no input and returns no output.
313
+ * Useful for health checks, triggers, or cleanup operations.
314
+ *
315
+ * @param func - Function definition, either direct function or configuration object
316
+ * @returns The unwrapped function for internal use
317
+ *
318
+ * @example
319
+ * \\\`\\\`\\\`typescript
320
+ * const cleanupTempFiles = pikkuVoidFunc(async ({fileSystem, logger}) => {
321
+ * logger.info('Starting cleanup of temporary files')
322
+ * await fileSystem.deleteDirectory('/tmp/uploads')
323
+ * logger.info('Cleanup completed')
324
+ * })
325
+ * \\\`\\\`\\\`
326
+ */
119
327
  export const pikkuVoidFunc = (
120
328
  func:
121
329
  | APIFunctionSessionless<void, void>
@@ -127,20 +335,189 @@ export const pikkuVoidFunc = (
127
335
  return typeof func === 'function' ? func : func.func
128
336
  }
129
337
 
338
+ /**
339
+ * Registers a WebSocket channel with the Pikku framework.
340
+ *
341
+ * @template ChannelData - Type of data associated with the channel
342
+ * @template Channel - String literal type for the channel name
343
+ * @param channel - Channel definition with connection, disconnection, and message handlers
344
+ */
130
345
  export const addChannel = <ChannelData, Channel extends string>(
131
346
  channel: APIChannel<ChannelData, Channel> & AssertRouteParams<ChannelData, Channel>
132
347
  ) => {
133
348
  addCoreChannel(channel as any) // TODO
134
349
  }
135
350
 
351
+ /**
352
+ * Registers an HTTP route with the Pikku framework.
353
+ *
354
+ * @template In - Input type for the route
355
+ * @template Out - Output type for the route
356
+ * @template Route - String literal type for the route path (e.g., "/users/:id")
357
+ * @param route - Route definition with handler, method, and optional middleware
358
+ */
136
359
  export const addHTTPRoute = <In, Out, Route extends string>(
137
360
  route: APIRoute<In, Out, Route> & AssertRouteParams<In, Route>
138
361
  ) => {
139
362
  addCoreHTTPRoute(route)
140
363
  }
141
364
 
365
+ /**
366
+ * Registers a scheduled task with the Pikku framework.
367
+ * Tasks run based on cron expressions and are sessionless.
368
+ *
369
+ * @param task - Scheduled task definition with cron expression and handler
370
+ */
142
371
  export const addScheduledTask = (task: ScheduledTask) => {
143
372
  addCoreScheduledTask(task as any) // TODO
144
373
  }
374
+
375
+ /**
376
+ * Registers a queue worker with the Pikku framework.
377
+ * Workers process background jobs from queues.
378
+ *
379
+ * @param queueWorker - Queue worker definition with job handler
380
+ */
381
+ export const addQueueWorker = (queueWorker: QueueWorker<any, any>) => {
382
+ addCoreQueueWorker(queueWorker as any) // TODO
383
+ }
384
+
385
+ /**
386
+ * Registers an MCP resource with the Pikku framework.
387
+ * Resources provide data that AI models can access.
388
+ *
389
+ * @template In - Input type for the resource request
390
+ * @param mcpResource - MCP resource definition with data provider function
391
+ */
392
+ export const addMCPResource = <In>(
393
+ mcpResource: MCPResource<In>
394
+ ) => {
395
+ addCoreMCPResource(mcpResource as any)
396
+ }
397
+
398
+ /**
399
+ * Registers an MCP tool with the Pikku framework.
400
+ * Tools are functions that AI models can invoke.
401
+ *
402
+ * @template In - Input type for the tool invocation
403
+ * @param mcpTool - MCP tool definition with action function
404
+ */
405
+ export const addMCPTool = <In>(
406
+ mcpTool: MCPTool<In>
407
+ ) => {
408
+ addCoreMCPTool(mcpTool as any)
409
+ }
410
+
411
+ /**
412
+ * Registers an MCP prompt with the Pikku framework.
413
+ * Prompts provide templates that AI models can use.
414
+ *
415
+ * @template In - Input type for the prompt parameters
416
+ * @param mcpPrompt - MCP prompt definition with template function
417
+ */
418
+ export const addMCPPrompt = <In>(
419
+ mcpPrompt: MCPPrompt<In>
420
+ ) => {
421
+ addCoreMCPPrompt(mcpPrompt as any)
422
+ }
423
+
424
+ /**
425
+ * Creates a function for handling MCP prompt requests.
426
+ * These functions generate prompt templates for AI models.
427
+ *
428
+ * @template In - Input type for the prompt parameters
429
+ * @param func - Function definition, either direct function or configuration object
430
+ * @returns The unwrapped function for internal use
431
+ *
432
+ * @example
433
+ * \`\`\`typescript
434
+ * const codeReviewPrompt = pikkuMCPPromptFunc<{language: string, code: string}>({
435
+ * func: async ({}, input) => ({
436
+ * messages: [{
437
+ * role: 'user',
438
+ * content: {
439
+ * type: 'text',
440
+ * text: \`Please review this \${input.language} code: \${input.code}\`
441
+ * }
442
+ * }]
443
+ * })
444
+ * })
445
+ * \`\`\`
446
+ */
447
+ export const pikkuMCPPromptFunc = <In>(
448
+ func:
449
+ | APIFunctionSessionless<In, MCPPromptResponse>
450
+ | {
451
+ func: APIFunctionSessionless<In, MCPPromptResponse>
452
+ name?: string
453
+ }
454
+ ) => {
455
+ return typeof func === 'function' ? func : func.func
456
+ }
457
+
458
+ /**
459
+ * Creates a function for handling MCP tool invocations.
460
+ * These functions perform actions that AI models can request.
461
+ *
462
+ * @template In - Input type for the tool invocation
463
+ * @param func - Function definition, either direct function or configuration object
464
+ * @returns The unwrapped function for internal use
465
+ *
466
+ * @example
467
+ * \`\`\`typescript
468
+ * const searchFiles = pikkuMCPToolFunc<{query: string, directory: string}>({
469
+ * func: async ({fileSystem}, input) => {
470
+ * const results = await fileSystem.search(input.query, input.directory)
471
+ * return [{
472
+ * type: 'text',
473
+ * text: \`Found \${results.length} files matching \"\${input.query}\"\`
474
+ * }]
475
+ * }
476
+ * })
477
+ * \`\`\`
478
+ */
479
+ export const pikkuMCPToolFunc = <In>(
480
+ func:
481
+ | APIFunctionSessionless<In, MCPToolResponse, null, true>
482
+ | {
483
+ func: APIFunctionSessionless<In, MCPToolResponse, null, true>
484
+ name?: string
485
+ }
486
+ ) => {
487
+ return typeof func === 'function' ? func : func.func
488
+ }
489
+
490
+ /**
491
+ * Creates a function for handling MCP resource requests.
492
+ * These functions provide data that AI models can access.
493
+ *
494
+ * @template In - Input type for the resource request
495
+ * @param func - Function definition, either direct function or configuration object
496
+ * @returns The unwrapped function for internal use
497
+ *
498
+ * @example
499
+ * \`\`\`typescript
500
+ * const getProjectFiles = pikkuMCPResourceFunc<{path: string}>({
501
+ * func: async ({ fileSystem }, input) => {
502
+ * const fileContent = await fileSystem.readFile(input.path)
503
+ * return [{
504
+ * uri: \`file://\${input.path}\`,
505
+ * mimeType: 'text/plain',
506
+ * text: fileContent
507
+ * }]
508
+ * }
509
+ * })
510
+ * \`\`\`
511
+ */
512
+ export const pikkuMCPResourceFunc = <In>(
513
+ func:
514
+ | APIFunctionSessionless<In, MCPResourceResponse, null, true>
515
+ | {
516
+ func: APIFunctionSessionless<In, MCPResourceResponse, null, true>
517
+ name?: string
518
+ }
519
+ ) => {
520
+ return typeof func === 'function' ? func : func.func
521
+ }
145
522
  `
146
523
  }
package/src/types.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { InspectorState } from '@pikku/inspector'
2
+ import { PikkuCLIConfig } from './pikku-cli-config.js'
3
+ import { CLILogger, PikkuCLIOptions } from './utils.js'
4
+
5
+ export type PikkuCommand = (
6
+ logger: CLILogger,
7
+ cliConfig: PikkuCLIConfig,
8
+ visitState: InspectorState,
9
+ options?: PikkuCLIOptions
10
+ ) => Promise<boolean>
11
+
12
+ export type PikkuCommandWithoutState = (
13
+ logger: CLILogger,
14
+ cliConfig: PikkuCLIConfig,
15
+ options?: PikkuCLIOptions
16
+ ) => Promise<boolean>
@@ -7,16 +7,45 @@ import { readFileSync } from 'fs'
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url)
9
9
 
10
- export const logPrimary = (message: string) => {
11
- console.log(chalk.green(message))
12
- }
10
+ const logo = `
11
+ ______ _ _ _
12
+ (_____ (_) | | |
13
+ _____) )| | _| | _ _ _
14
+ | ____/ | |_/ ) |_/ ) | | |
15
+ | | | | _ (| _ (| |_| |
16
+ |_| |_|_| \_)_| \_)____/
17
+ `
13
18
 
14
- export const logSuccess = (message: string) => {
15
- console.log(chalk.green(message))
16
- }
19
+ export class CLILogger {
20
+ constructor({ logLogo }: { logLogo: boolean }) {
21
+ if (logLogo) {
22
+ this.logPikkuLogo()
23
+ }
24
+ }
17
25
 
18
- export const logInfo = (message: string) => {
19
- console.log(chalk.blue(message))
26
+ primary(message: string) {
27
+ console.log(chalk.green(message))
28
+ }
29
+ success(message: string) {
30
+ console.log(chalk.green(message))
31
+ }
32
+ info(message: string) {
33
+ console.log(chalk.blue(message))
34
+ }
35
+ error(message: string) {
36
+ console.error(chalk.red(message))
37
+ }
38
+ warn(message: string) {
39
+ console.error(chalk.yellow(message))
40
+ }
41
+
42
+ private logPikkuLogo() {
43
+ this.primary(logo)
44
+ const packageJson = JSON.parse(
45
+ readFileSync(`${dirname(__filename)}/../../package.json`, 'utf-8')
46
+ )
47
+ this.primary(`⚙️ Welcome to the Pikku CLI (v${packageJson.version})\n`)
48
+ }
20
49
  }
21
50
 
22
51
  export const getFileImportRelativePath = (
@@ -104,6 +133,7 @@ const getMetaTypes = (
104
133
  }
105
134
 
106
135
  export const getPikkuFilesAndMethods = async (
136
+ logger: CLILogger,
107
137
  {
108
138
  singletonServicesTypeImportMap,
109
139
  sessionServicesTypeImportMap,
@@ -188,7 +218,7 @@ export const getPikkuFilesAndMethods = async (
188
218
  }
189
219
  })
190
220
 
191
- console.error(result.join('\n'))
221
+ logger.error(result.join('\n'))
192
222
  process.exit(1)
193
223
  }
194
224
 
@@ -196,10 +226,18 @@ export const getPikkuFilesAndMethods = async (
196
226
  }
197
227
 
198
228
  export const writeFileInDir = async (
229
+ logger: CLILogger,
199
230
  path: string,
200
231
  content: string,
201
- ignoreModifyComment: boolean = false
232
+ {
233
+ ignoreModifyComment = false,
234
+ logWrite = true,
235
+ }: { ignoreModifyComment?: boolean; logWrite?: boolean } = {}
202
236
  ) => {
237
+ if (path.includes('.json')) {
238
+ ignoreModifyComment = true
239
+ }
240
+
203
241
  if (content.includes('server-only')) {
204
242
  content = content.replace(
205
243
  "'server-only'",
@@ -211,17 +249,21 @@ export const writeFileInDir = async (
211
249
 
212
250
  await mkdir(dirname(path), { recursive: true })
213
251
  await writeFile(path, content, 'utf-8')
214
- logSuccess(`✓ File written to ${path}`)
252
+
253
+ if (logWrite) {
254
+ logger.success(`✓ File written to ${path}`)
255
+ }
215
256
  }
216
257
 
217
258
  export const logCommandInfoAndTime = async (
259
+ logger: CLILogger,
218
260
  commandStart: string,
219
261
  commandEnd: string,
220
262
  [skipCondition, skipMessage = 'none found']: [boolean] | [boolean, string],
221
263
  callback: (...args: any[]) => Promise<unknown>
222
264
  ): Promise<boolean> => {
223
265
  if (skipCondition === true) {
224
- logInfo(
266
+ logger.info(
225
267
  `• Skipping ${commandStart.charAt(0).toLocaleLowerCase()}${commandStart.slice(1)} since ${skipMessage}.`
226
268
  )
227
269
  return false
@@ -231,28 +273,10 @@ export const logCommandInfoAndTime = async (
231
273
  chalk.blue(`• ${commandStart}...`)
232
274
  await callback()
233
275
 
234
- logSuccess(`✓ ${commandEnd} in ${Date.now() - start}ms.`)
276
+ logger.success(`✓ ${commandEnd} in ${Date.now() - start}ms.`)
235
277
  return true
236
278
  }
237
279
 
238
- const logo = `
239
- ______ _ _ _
240
- (_____ (_) | | |
241
- _____) )| | _| | _ _ _
242
- | ____/ | |_/ ) |_/ ) | | |
243
- | | | | _ (| _ (| |_| |
244
- |_| |_|_| \_)_| \_)____/
245
- `
246
-
247
- export const logPikkuLogo = () => {
248
- logPrimary(logo)
249
-
250
- const packageJson = JSON.parse(
251
- readFileSync(`${dirname(__filename)}/../../../package.json`, 'utf-8')
252
- )
253
- logPrimary(`⚙️ Welcome to the Pikku CLI (v${packageJson.version})\n`)
254
- }
255
-
256
280
  // TODO: add version back in once the ESM dust settles
257
281
  export const DO_NOT_MODIFY_COMMENT = `/**
258
282
  * This file was generated by the @pikku/cli
package/tsconfig.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "extends": "../tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "rootDir": ".",
5
- "module": "Node16",
5
+ "module": "Node18",
6
6
  "outDir": "dist",
7
7
  "target": "esnext",
8
8
  "declaration": true,
@@ -1,25 +0,0 @@
1
- import { PikkuCLIConfig } from '../src/pikku-cli-config.js'
2
- import { InspectorState } from '@pikku/inspector'
3
- import { logCommandInfoAndTime, writeFileInDir } from '../src/utils/utils.js'
4
- import { serializeTypedChannelsMap } from '../src/serialize-typed-channel-map.js'
5
-
6
- export const pikkuChannelsMap = async (
7
- { channelsMapDeclarationFile, packageMappings }: PikkuCLIConfig,
8
- state: InspectorState
9
- ) => {
10
- return await logCommandInfoAndTime(
11
- 'Creating channels map',
12
- 'Created channels map',
13
- [state.channels.files.size === 0],
14
- async () => {
15
- const content = serializeTypedChannelsMap(
16
- channelsMapDeclarationFile,
17
- packageMappings,
18
- state.functions.typesMap,
19
- state.functions.meta,
20
- state.channels.meta
21
- )
22
- await writeFileInDir(channelsMapDeclarationFile, content)
23
- }
24
- )
25
- }
@@ -1,26 +0,0 @@
1
- import { PikkuCLIConfig } from '../src/pikku-cli-config.js'
2
- import { InspectorState } from '@pikku/inspector'
3
- import { logCommandInfoAndTime, writeFileInDir } from '../src/utils/utils.js'
4
- import { serializeTypedRoutesMap } from '../src/serialize-typed-http-map.js'
5
-
6
- export const pikkuHTTPMap = async (
7
- { httpRoutesMapDeclarationFile, packageMappings }: PikkuCLIConfig,
8
- { http, functions }: InspectorState
9
- ) => {
10
- return await logCommandInfoAndTime(
11
- 'Creating HTTP map',
12
- 'Created HTTP map',
13
- [http.files.size === 0],
14
- async () => {
15
- const content = serializeTypedRoutesMap(
16
- httpRoutesMapDeclarationFile,
17
- packageMappings,
18
- functions.typesMap,
19
- functions.meta,
20
- http.meta,
21
- http.metaInputTypes
22
- )
23
- await writeFileInDir(httpRoutesMapDeclarationFile, content)
24
- }
25
- )
26
- }