@pikku/core 0.12.4 → 0.12.5

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 (229) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/dist/dev/hot-reload.d.ts +10 -0
  3. package/dist/dev/hot-reload.js +158 -0
  4. package/dist/middleware-runner.d.ts +1 -0
  5. package/dist/middleware-runner.js +5 -0
  6. package/dist/permissions.d.ts +1 -0
  7. package/dist/permissions.js +5 -0
  8. package/dist/services/in-memory-workflow-service.js +7 -11
  9. package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
  10. package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
  11. package/dist/wirings/channel/channel-middleware-runner.js +5 -0
  12. package/dist/wirings/workflow/pikku-workflow-service.js +7 -8
  13. package/package.json +3 -2
  14. package/src/crypto-utils.test.ts +214 -0
  15. package/src/crypto-utils.ts +213 -0
  16. package/src/dev/hot-reload.test.ts +484 -0
  17. package/src/dev/hot-reload.ts +212 -0
  18. package/src/errors/error-handler.ts +62 -0
  19. package/src/errors/error.test.ts +195 -0
  20. package/src/errors/errors.ts +374 -0
  21. package/src/errors/index.ts +2 -0
  22. package/src/factory-functions.test.ts +109 -0
  23. package/src/function/function-runner.test.ts +536 -0
  24. package/src/function/function-runner.ts +365 -0
  25. package/src/function/functions.types.ts +304 -0
  26. package/src/function/index.ts +5 -0
  27. package/src/handle-error.test.ts +424 -0
  28. package/src/handle-error.ts +69 -0
  29. package/src/index.ts +118 -0
  30. package/src/internal.ts +6 -0
  31. package/src/middleware/auth-apikey.test.ts +363 -0
  32. package/src/middleware/auth-apikey.ts +47 -0
  33. package/src/middleware/auth-bearer.test.ts +450 -0
  34. package/src/middleware/auth-bearer.ts +72 -0
  35. package/src/middleware/auth-cookie.test.ts +528 -0
  36. package/src/middleware/auth-cookie.ts +80 -0
  37. package/src/middleware/cors.test.ts +424 -0
  38. package/src/middleware/cors.ts +104 -0
  39. package/src/middleware/index.ts +5 -0
  40. package/src/middleware/remote-auth.test.ts +488 -0
  41. package/src/middleware/remote-auth.ts +68 -0
  42. package/src/middleware/timeout.ts +15 -0
  43. package/src/middleware-runner.test.ts +418 -0
  44. package/src/middleware-runner.ts +240 -0
  45. package/src/permissions.test.ts +434 -0
  46. package/src/permissions.ts +327 -0
  47. package/src/pikku-request.ts +23 -0
  48. package/src/pikku-response.ts +5 -0
  49. package/src/pikku-state.test.ts +224 -0
  50. package/src/pikku-state.ts +216 -0
  51. package/src/run-tests-script.test.ts +49 -0
  52. package/src/schema.test.ts +249 -0
  53. package/src/schema.ts +151 -0
  54. package/src/services/ai-agent-runner-service.ts +41 -0
  55. package/src/services/ai-run-state-service.ts +20 -0
  56. package/src/services/ai-storage-service.ts +39 -0
  57. package/src/services/content-service.ts +76 -0
  58. package/src/services/deployment-service.ts +22 -0
  59. package/src/services/gateway-service.ts +20 -0
  60. package/src/services/gopass-secrets.ts +98 -0
  61. package/src/services/in-memory-ai-run-state-service.ts +73 -0
  62. package/src/services/in-memory-trigger-service.ts +64 -0
  63. package/src/services/in-memory-workflow-service.test.ts +351 -0
  64. package/src/services/in-memory-workflow-service.ts +502 -0
  65. package/src/services/index.ts +56 -0
  66. package/src/services/jwt-service.ts +30 -0
  67. package/src/services/local-content.ts +120 -0
  68. package/src/services/local-gateway-service.ts +62 -0
  69. package/src/services/local-secrets.test.ts +80 -0
  70. package/src/services/local-secrets.ts +94 -0
  71. package/src/services/local-variables.test.ts +61 -0
  72. package/src/services/local-variables.ts +39 -0
  73. package/src/services/logger-console.test.ts +118 -0
  74. package/src/services/logger-console.ts +98 -0
  75. package/src/services/logger.ts +57 -0
  76. package/src/services/scheduler-service.ts +86 -0
  77. package/src/services/schema-service.ts +33 -0
  78. package/src/services/scoped-secret-service.test.ts +74 -0
  79. package/src/services/scoped-secret-service.ts +41 -0
  80. package/src/services/secret-service.ts +38 -0
  81. package/src/services/trigger-service.ts +17 -0
  82. package/src/services/typed-secret-service.test.ts +93 -0
  83. package/src/services/typed-secret-service.ts +70 -0
  84. package/src/services/typed-variables-service.test.ts +73 -0
  85. package/src/services/typed-variables-service.ts +81 -0
  86. package/src/services/user-session-service.test.ts +113 -0
  87. package/src/services/user-session-service.ts +86 -0
  88. package/src/services/variables-service.ts +11 -0
  89. package/src/services/workflow-service.ts +95 -0
  90. package/src/testing/index.ts +2 -0
  91. package/src/testing/service-tests.ts +873 -0
  92. package/src/time-utils.test.ts +56 -0
  93. package/src/time-utils.ts +107 -0
  94. package/src/types/core.types.ts +478 -0
  95. package/src/types/state.types.ts +188 -0
  96. package/src/utils/hash.test.ts +68 -0
  97. package/src/utils/hash.ts +26 -0
  98. package/src/utils.test.ts +350 -0
  99. package/src/utils.ts +129 -0
  100. package/src/version.test.ts +80 -0
  101. package/src/version.ts +25 -0
  102. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
  103. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
  104. package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
  105. package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
  106. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
  107. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  108. package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
  109. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  110. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  111. package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
  112. package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
  113. package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
  114. package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
  115. package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
  116. package/src/wirings/ai-agent/index.ts +37 -0
  117. package/src/wirings/channel/channel-common.ts +90 -0
  118. package/src/wirings/channel/channel-handler.ts +250 -0
  119. package/src/wirings/channel/channel-middleware-runner.ts +126 -0
  120. package/src/wirings/channel/channel-runner.ts +166 -0
  121. package/src/wirings/channel/channel-store.ts +29 -0
  122. package/src/wirings/channel/channel.types.ts +172 -0
  123. package/src/wirings/channel/define-channel-routes.ts +25 -0
  124. package/src/wirings/channel/eventhub-service.ts +34 -0
  125. package/src/wirings/channel/eventhub-store.ts +13 -0
  126. package/src/wirings/channel/index.ts +24 -0
  127. package/src/wirings/channel/local/index.ts +3 -0
  128. package/src/wirings/channel/local/local-channel-handler.ts +81 -0
  129. package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
  130. package/src/wirings/channel/local/local-channel-runner.ts +210 -0
  131. package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
  132. package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
  133. package/src/wirings/channel/log-channels.ts +20 -0
  134. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
  135. package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
  136. package/src/wirings/channel/serverless/index.ts +5 -0
  137. package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
  138. package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
  139. package/src/wirings/cli/channel/index.ts +1 -0
  140. package/src/wirings/cli/cli-runner.test.ts +403 -0
  141. package/src/wirings/cli/cli-runner.ts +529 -0
  142. package/src/wirings/cli/cli.types.ts +358 -0
  143. package/src/wirings/cli/command-parser.test.ts +445 -0
  144. package/src/wirings/cli/command-parser.ts +504 -0
  145. package/src/wirings/cli/define-cli-commands.ts +24 -0
  146. package/src/wirings/cli/index.ts +19 -0
  147. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  148. package/src/wirings/gateway/gateway-runner.ts +411 -0
  149. package/src/wirings/gateway/gateway.types.ts +149 -0
  150. package/src/wirings/gateway/index.ts +13 -0
  151. package/src/wirings/http/http-routes.test.ts +322 -0
  152. package/src/wirings/http/http-routes.ts +197 -0
  153. package/src/wirings/http/http-runner.test.ts +144 -0
  154. package/src/wirings/http/http-runner.ts +588 -0
  155. package/src/wirings/http/http.types.ts +369 -0
  156. package/src/wirings/http/index.ts +28 -0
  157. package/src/wirings/http/log-http-routes.ts +22 -0
  158. package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
  159. package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
  160. package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
  161. package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
  162. package/src/wirings/http/routers/http-router.ts +13 -0
  163. package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
  164. package/src/wirings/http/routers/path-to-regex.ts +126 -0
  165. package/src/wirings/http/web-request.test.ts +236 -0
  166. package/src/wirings/http/web-request.ts +104 -0
  167. package/src/wirings/mcp/index.ts +27 -0
  168. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
  169. package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
  170. package/src/wirings/mcp/mcp-runner.ts +323 -0
  171. package/src/wirings/mcp/mcp.types.ts +216 -0
  172. package/src/wirings/node/index.ts +2 -0
  173. package/src/wirings/node/node.types.ts +23 -0
  174. package/src/wirings/oauth2/index.ts +3 -0
  175. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  176. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  177. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  178. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  179. package/src/wirings/queue/index.ts +31 -0
  180. package/src/wirings/queue/queue-runner.test.ts +710 -0
  181. package/src/wirings/queue/queue-runner.ts +192 -0
  182. package/src/wirings/queue/queue.types.ts +189 -0
  183. package/src/wirings/queue/register-queue-helper.ts +60 -0
  184. package/src/wirings/queue/validate-worker-config.test.ts +108 -0
  185. package/src/wirings/queue/validate-worker-config.ts +116 -0
  186. package/src/wirings/rpc/index.ts +4 -0
  187. package/src/wirings/rpc/rpc-runner.ts +460 -0
  188. package/src/wirings/rpc/rpc-types.ts +56 -0
  189. package/src/wirings/rpc/wire-addon.ts +21 -0
  190. package/src/wirings/scheduler/index.ts +11 -0
  191. package/src/wirings/scheduler/log-schedulers.ts +20 -0
  192. package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
  193. package/src/wirings/scheduler/scheduler-runner.ts +133 -0
  194. package/src/wirings/scheduler/scheduler.types.ts +53 -0
  195. package/src/wirings/secret/index.ts +9 -0
  196. package/src/wirings/secret/secret.types.ts +32 -0
  197. package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
  198. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  199. package/src/wirings/trigger/index.ts +10 -0
  200. package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
  201. package/src/wirings/trigger/trigger-runner.test.ts +79 -0
  202. package/src/wirings/trigger/trigger-runner.ts +135 -0
  203. package/src/wirings/trigger/trigger.types.ts +178 -0
  204. package/src/wirings/variable/index.ts +8 -0
  205. package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
  206. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  207. package/src/wirings/variable/variable.types.ts +22 -0
  208. package/src/wirings/workflow/dsl/index.ts +31 -0
  209. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
  210. package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
  211. package/src/wirings/workflow/graph/graph-node.ts +222 -0
  212. package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
  213. package/src/wirings/workflow/graph/graph-runner.ts +816 -0
  214. package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
  215. package/src/wirings/workflow/graph/graph-validation.ts +237 -0
  216. package/src/wirings/workflow/graph/index.ts +19 -0
  217. package/src/wirings/workflow/graph/template.test.ts +49 -0
  218. package/src/wirings/workflow/graph/template.ts +42 -0
  219. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  220. package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
  221. package/src/wirings/workflow/index.ts +82 -0
  222. package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
  223. package/src/wirings/workflow/pikku-workflow-service.ts +1179 -0
  224. package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
  225. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  226. package/src/wirings/workflow/workflow.types.ts +274 -0
  227. package/tsconfig.json +14 -0
  228. package/tsconfig.tsbuildinfo +1 -0
  229. package/lcov.info +0 -18891
@@ -0,0 +1,403 @@
1
+ import { test, describe, beforeEach, afterEach } from 'node:test'
2
+ import * as assert from 'assert'
3
+ import { NotFoundError } from '../../errors/errors.js'
4
+ import type { CorePikkuMiddleware } from '../../types/core.types.js'
5
+ import { wireCLI, runCLICommand, pikkuCLIRender } from './cli-runner.js'
6
+ import { pikkuState, resetPikkuState } from '../../pikku-state.js'
7
+ import { addFunction } from '../../function/function-runner.js'
8
+
9
+ describe('CLI Runner', () => {
10
+ let singletonServices: any
11
+ let createWireServices: any
12
+
13
+ beforeEach(() => {
14
+ resetPikkuState()
15
+
16
+ singletonServices = {
17
+ logger: {
18
+ info: () => {},
19
+ warn: () => {},
20
+ error: () => {},
21
+ },
22
+ }
23
+
24
+ createWireServices = async () => ({})
25
+ })
26
+
27
+ afterEach(() => {
28
+ resetPikkuState()
29
+ })
30
+
31
+ describe('runCLICommand', () => {
32
+ test('should throw NotFoundError when program not found', async () => {
33
+ pikkuState(null, 'cli', 'meta', { programs: {}, renderers: {} })
34
+
35
+ await assert.rejects(
36
+ async () =>
37
+ runCLICommand({
38
+ program: 'nonexistent',
39
+ commandPath: ['test'],
40
+ data: {},
41
+ singletonServices,
42
+ }),
43
+ NotFoundError
44
+ )
45
+ })
46
+
47
+ test('should throw NotFoundError when command not found', async () => {
48
+ pikkuState(null, 'cli', 'meta', {
49
+ programs: {
50
+ 'test-cli': {
51
+ program: 'test-cli',
52
+ commands: {},
53
+ options: {},
54
+ },
55
+ },
56
+ renderers: {},
57
+ })
58
+
59
+ pikkuState(null, 'cli', 'programs', {
60
+ 'test-cli': {
61
+ defaultRenderer: undefined,
62
+ middleware: [],
63
+ renderers: {},
64
+ },
65
+ })
66
+
67
+ await assert.rejects(
68
+ async () =>
69
+ runCLICommand({
70
+ program: 'test-cli',
71
+ commandPath: ['nonexistent'],
72
+ data: {},
73
+ singletonServices,
74
+ }),
75
+ NotFoundError
76
+ )
77
+ })
78
+
79
+ test('should execute command function with merged data', async () => {
80
+ let receivedData: any
81
+ const testFunc = async (_services: any, data: any, _wire: any) => {
82
+ receivedData = data
83
+ return { success: true }
84
+ }
85
+
86
+ // Setup metadata
87
+ pikkuState(null, 'cli', 'meta', {
88
+ programs: {
89
+ 'test-cli': {
90
+ program: 'test-cli',
91
+ commands: {
92
+ greet: {
93
+ command: 'greet <name>',
94
+ pikkuFuncId: 'greetFunc',
95
+ positionals: [{ name: 'name', required: true }],
96
+ options: {},
97
+ },
98
+ },
99
+ options: {},
100
+ },
101
+ },
102
+ renderers: {},
103
+ })
104
+
105
+ pikkuState(null, 'cli', 'programs', {
106
+ 'test-cli': {
107
+ defaultRenderer: undefined,
108
+ middleware: [],
109
+ renderers: {},
110
+ },
111
+ })
112
+
113
+ pikkuState(null, 'function', 'meta', {
114
+ greetFunc: {
115
+ pikkuFuncId: 'greetFunc',
116
+ inputSchemaName: null,
117
+ outputSchemaName: null,
118
+ },
119
+ })
120
+
121
+ addFunction('greetFunc', { func: testFunc, auth: false })
122
+
123
+ const result = await runCLICommand({
124
+ program: 'test-cli',
125
+ commandPath: ['greet'],
126
+ data: { name: 'Alice', loud: true },
127
+ singletonServices,
128
+ })
129
+
130
+ assert.strictEqual(result.success, true)
131
+ assert.deepStrictEqual(receivedData, { name: 'Alice', loud: true })
132
+ })
133
+
134
+ test('should run middleware before function execution', async () => {
135
+ const executionOrder: string[] = []
136
+
137
+ const testMiddleware: CorePikkuMiddleware = async (
138
+ _services,
139
+ _wire,
140
+ next
141
+ ) => {
142
+ executionOrder.push('middleware')
143
+ await next()
144
+ }
145
+
146
+ const testFunc = async () => {
147
+ executionOrder.push('function')
148
+ return { success: true }
149
+ }
150
+
151
+ pikkuState(null, 'cli', 'meta', {
152
+ programs: {
153
+ 'test-cli': {
154
+ program: 'test-cli',
155
+ commands: {
156
+ test: {
157
+ command: 'test',
158
+ pikkuFuncId: 'testFunc',
159
+ positionals: [],
160
+ options: {},
161
+ },
162
+ },
163
+ options: {},
164
+ },
165
+ },
166
+ renderers: {},
167
+ })
168
+
169
+ pikkuState(null, 'cli', 'programs', {
170
+ 'test-cli': {
171
+ defaultRenderer: undefined,
172
+ middleware: [testMiddleware],
173
+ renderers: {},
174
+ },
175
+ })
176
+
177
+ pikkuState(null, 'function', 'meta', {
178
+ testFunc: {
179
+ pikkuFuncId: 'testFunc',
180
+ inputSchemaName: null,
181
+ outputSchemaName: null,
182
+ },
183
+ })
184
+
185
+ addFunction('testFunc', { func: testFunc, auth: false })
186
+
187
+ await runCLICommand({
188
+ program: 'test-cli',
189
+ commandPath: ['test'],
190
+ data: {},
191
+ singletonServices,
192
+ })
193
+
194
+ assert.deepStrictEqual(executionOrder, ['middleware', 'function'])
195
+ })
196
+
197
+ test.skip('should provide CLI context to middleware', async () => {
198
+ let cliContext: any = null
199
+
200
+ const testMiddleware: CorePikkuMiddleware = async (
201
+ _services,
202
+ wire,
203
+ next
204
+ ) => {
205
+ cliContext = wire.cli
206
+ await next()
207
+ }
208
+
209
+ const testFunc = async () => ({ success: true })
210
+
211
+ pikkuState(null, 'cli', 'meta', {
212
+ programs: {
213
+ 'test-cli': {
214
+ program: 'test-cli',
215
+ commands: {
216
+ greet: {
217
+ command: 'greet <name>',
218
+ pikkuFuncId: 'greetFunc',
219
+ positionals: [{ name: 'name', required: true }],
220
+ options: {},
221
+ },
222
+ },
223
+ options: {},
224
+ },
225
+ },
226
+ renderers: {},
227
+ })
228
+
229
+ pikkuState(null, 'cli', 'programs', {
230
+ 'test-cli': {
231
+ defaultRenderer: undefined,
232
+ middleware: [testMiddleware],
233
+ renderers: {},
234
+ },
235
+ })
236
+
237
+ pikkuState(null, 'function', 'meta', {
238
+ greetFunc: {
239
+ pikkuFuncId: 'greetFunc',
240
+ inputSchemaName: null,
241
+ outputSchemaName: null,
242
+ },
243
+ })
244
+
245
+ addFunction('greetFunc', { func: testFunc, auth: false })
246
+
247
+ await runCLICommand({
248
+ program: 'test-cli',
249
+ commandPath: ['greet'],
250
+ data: { name: 'Alice' },
251
+ singletonServices,
252
+ })
253
+
254
+ assert.ok(cliContext, 'CLI context should be set by middleware')
255
+ assert.strictEqual(cliContext.program, 'test-cli')
256
+ assert.deepStrictEqual(cliContext.command, ['greet'])
257
+ assert.deepStrictEqual(cliContext.data, { name: 'Alice' })
258
+ })
259
+
260
+ test('should throw error when auth required but no session', async () => {
261
+ const testFunc = async () => ({ success: true })
262
+
263
+ pikkuState(null, 'cli', 'meta', {
264
+ programs: {
265
+ 'test-cli': {
266
+ program: 'test-cli',
267
+ commands: {
268
+ secure: {
269
+ command: 'secure',
270
+ pikkuFuncId: 'secureFunc',
271
+ positionals: [],
272
+ options: {},
273
+ },
274
+ },
275
+ options: {},
276
+ },
277
+ },
278
+ renderers: {},
279
+ })
280
+
281
+ pikkuState(null, 'function', 'meta', {
282
+ secureFunc: {
283
+ pikkuFuncId: 'secureFunc',
284
+ inputSchemaName: null,
285
+ outputSchemaName: null,
286
+ },
287
+ })
288
+
289
+ addFunction('secureFunc', { func: testFunc, auth: true })
290
+
291
+ await assert.rejects(
292
+ async () =>
293
+ runCLICommand({
294
+ program: 'test-cli',
295
+ commandPath: ['secure'],
296
+ data: {},
297
+ singletonServices,
298
+ createWireServices,
299
+ }),
300
+ /Authentication required/
301
+ )
302
+ })
303
+ })
304
+
305
+ describe('wireCLI', () => {
306
+ test('should register CLI program and commands', () => {
307
+ const greetFunc = async () => 'Hello'
308
+
309
+ pikkuState(null, 'cli', 'meta', {
310
+ programs: {
311
+ 'my-cli': {
312
+ program: 'my-cli',
313
+ commands: {
314
+ greet: {
315
+ command: 'greet <name>',
316
+ pikkuFuncId: 'greetFunc',
317
+ positionals: [{ name: 'name', required: true }],
318
+ options: {
319
+ loud: {
320
+ description: 'Use loud greeting',
321
+ short: 'l',
322
+ default: false,
323
+ },
324
+ },
325
+ },
326
+ },
327
+ options: {},
328
+ },
329
+ },
330
+ renderers: {},
331
+ })
332
+
333
+ wireCLI({
334
+ program: 'my-cli',
335
+ commands: {
336
+ greet: greetFunc,
337
+ },
338
+ })
339
+
340
+ const programs = pikkuState(null, 'cli', 'programs')
341
+ assert.ok(programs['my-cli'])
342
+ assert.strictEqual(programs['my-cli'].middleware.length, 0)
343
+ })
344
+
345
+ test('should register CLI with global middleware', () => {
346
+ const middleware: CorePikkuMiddleware = async (_s, _i, next) => {
347
+ await next()
348
+ }
349
+
350
+ pikkuState(null, 'cli', 'meta', {
351
+ programs: {
352
+ 'my-cli': {
353
+ program: 'my-cli',
354
+ commands: {},
355
+ options: {},
356
+ },
357
+ },
358
+ renderers: {},
359
+ })
360
+
361
+ wireCLI({
362
+ program: 'my-cli',
363
+ commands: {},
364
+ middleware: [middleware],
365
+ })
366
+
367
+ const programs = pikkuState(null, 'cli', 'programs')
368
+ assert.strictEqual(programs['my-cli'].middleware.length, 1)
369
+ assert.strictEqual(programs['my-cli'].middleware[0], middleware)
370
+ })
371
+
372
+ test('should throw error when CLI metadata not found', () => {
373
+ pikkuState(null, 'cli', 'meta', { programs: {}, renderers: {} })
374
+
375
+ assert.throws(() => {
376
+ wireCLI({
377
+ program: 'nonexistent',
378
+ commands: {},
379
+ })
380
+ }, /CLI metadata not found for program 'nonexistent'/)
381
+ })
382
+ })
383
+
384
+ describe('pikkuCLIRender', () => {
385
+ test('should create CLI renderer function', () => {
386
+ const renderer = pikkuCLIRender((services, data) => {
387
+ console.log(data)
388
+ })
389
+
390
+ assert.strictEqual(typeof renderer, 'function')
391
+ })
392
+
393
+ test('should preserve renderer function behavior', async () => {
394
+ let receivedData: any
395
+ const renderer = pikkuCLIRender((services, data) => {
396
+ receivedData = data
397
+ })
398
+
399
+ await renderer({} as any, { test: 'value' })
400
+ assert.deepStrictEqual(receivedData, { test: 'value' })
401
+ })
402
+ })
403
+ })