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