@pikku/core 0.12.1 → 0.12.3

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 (165) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/crypto-utils.d.ts +10 -0
  3. package/dist/crypto-utils.js +62 -0
  4. package/dist/function/function-runner.js +4 -4
  5. package/dist/function/functions.types.d.ts +26 -1
  6. package/dist/function/functions.types.js +16 -0
  7. package/dist/handle-error.d.ts +1 -1
  8. package/dist/handle-error.js +7 -3
  9. package/dist/index.d.ts +14 -2
  10. package/dist/index.js +1 -1
  11. package/dist/middleware-runner.js +1 -0
  12. package/dist/permissions.js +1 -0
  13. package/dist/pikku-state.js +7 -1
  14. package/dist/schema.js +1 -1
  15. package/dist/services/gateway-service.d.ts +19 -0
  16. package/dist/services/gateway-service.js +1 -0
  17. package/dist/services/in-memory-workflow-service.d.ts +8 -2
  18. package/dist/services/in-memory-workflow-service.js +28 -1
  19. package/dist/services/index.d.ts +2 -0
  20. package/dist/services/index.js +1 -0
  21. package/dist/services/local-gateway-service.d.ts +22 -0
  22. package/dist/services/local-gateway-service.js +51 -0
  23. package/dist/services/workflow-service.d.ts +8 -2
  24. package/dist/testing/index.d.ts +2 -0
  25. package/dist/testing/index.js +1 -0
  26. package/dist/testing/service-tests.d.ts +31 -0
  27. package/dist/testing/service-tests.js +598 -0
  28. package/dist/types/core.types.d.ts +12 -2
  29. package/dist/types/state.types.d.ts +9 -2
  30. package/dist/utils/hash.d.ts +2 -0
  31. package/dist/utils/hash.js +23 -0
  32. package/dist/utils.js +1 -1
  33. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
  34. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
  35. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
  36. package/dist/wirings/ai-agent/ai-agent-memory.js +19 -7
  37. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
  38. package/dist/wirings/ai-agent/ai-agent-prepare.js +141 -48
  39. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
  40. package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
  41. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
  42. package/dist/wirings/ai-agent/ai-agent-stream.js +126 -48
  43. package/dist/wirings/ai-agent/ai-agent.types.d.ts +57 -3
  44. package/dist/wirings/ai-agent/index.d.ts +2 -3
  45. package/dist/wirings/ai-agent/index.js +1 -2
  46. package/dist/wirings/channel/channel-handler.d.ts +5 -2
  47. package/dist/wirings/channel/channel-handler.js +16 -2
  48. package/dist/wirings/channel/channel-middleware-runner.js +1 -0
  49. package/dist/wirings/channel/channel.types.d.ts +7 -7
  50. package/dist/wirings/channel/index.d.ts +1 -1
  51. package/dist/wirings/channel/local/local-channel-handler.d.ts +8 -1
  52. package/dist/wirings/channel/local/local-channel-handler.js +22 -3
  53. package/dist/wirings/channel/local/local-channel-runner.js +28 -5
  54. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -1
  55. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  56. package/dist/wirings/channel/serverless/serverless-channel-runner.js +5 -2
  57. package/dist/wirings/cli/cli-runner.js +13 -2
  58. package/dist/wirings/cli/cli.types.d.ts +1 -0
  59. package/dist/wirings/gateway/gateway-runner.d.ts +24 -0
  60. package/dist/wirings/gateway/gateway-runner.js +325 -0
  61. package/dist/wirings/gateway/gateway.types.d.ts +127 -0
  62. package/dist/wirings/gateway/gateway.types.js +1 -0
  63. package/dist/wirings/gateway/index.d.ts +2 -0
  64. package/dist/wirings/gateway/index.js +1 -0
  65. package/dist/wirings/http/http-routes.js +0 -1
  66. package/dist/wirings/http/http-runner.d.ts +1 -1
  67. package/dist/wirings/http/http-runner.js +28 -3
  68. package/dist/wirings/http/http.types.d.ts +3 -11
  69. package/dist/wirings/http/index.d.ts +2 -1
  70. package/dist/wirings/http/index.js +1 -0
  71. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
  72. package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
  73. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  74. package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
  75. package/dist/wirings/http/web-request.d.ts +12 -0
  76. package/dist/wirings/http/web-request.js +85 -0
  77. package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
  78. package/dist/wirings/mcp/mcp-runner.js +13 -1
  79. package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
  80. package/dist/wirings/rpc/rpc-runner.js +36 -14
  81. package/dist/wirings/rpc/rpc-types.d.ts +6 -1
  82. package/dist/wirings/rpc/wire-addon.d.ts +1 -0
  83. package/dist/wirings/rpc/wire-addon.js +1 -1
  84. package/dist/wirings/scheduler/scheduler-runner.js +1 -0
  85. package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
  86. package/dist/wirings/workflow/graph/graph-validation.js +175 -0
  87. package/dist/wirings/workflow/graph/index.d.ts +1 -0
  88. package/dist/wirings/workflow/graph/index.js +1 -0
  89. package/dist/wirings/workflow/index.d.ts +1 -1
  90. package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
  91. package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
  92. package/dist/wirings/workflow/workflow.types.d.ts +10 -1
  93. package/package.json +6 -3
  94. package/src/crypto-utils.test.ts +99 -1
  95. package/src/crypto-utils.ts +114 -0
  96. package/src/function/function-runner.ts +5 -5
  97. package/src/function/functions.types.ts +38 -1
  98. package/src/handle-error.test.ts +65 -2
  99. package/src/handle-error.ts +8 -4
  100. package/src/index.ts +14 -0
  101. package/src/middleware-runner.ts +1 -0
  102. package/src/permissions.ts +1 -0
  103. package/src/pikku-state.ts +8 -1
  104. package/src/schema.ts +1 -1
  105. package/src/services/gateway-service.ts +20 -0
  106. package/src/services/in-memory-workflow-service.ts +45 -2
  107. package/src/services/index.ts +2 -0
  108. package/src/services/local-gateway-service.ts +62 -0
  109. package/src/services/workflow-service.ts +12 -1
  110. package/src/testing/index.ts +2 -0
  111. package/src/testing/service-tests.ts +873 -0
  112. package/src/types/core.types.ts +12 -1
  113. package/src/types/state.types.ts +12 -2
  114. package/src/utils/hash.test.ts +68 -0
  115. package/src/utils/hash.ts +26 -0
  116. package/src/utils.ts +1 -1
  117. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
  118. package/src/wirings/ai-agent/ai-agent-memory.ts +17 -7
  119. package/src/wirings/ai-agent/ai-agent-prepare.ts +194 -62
  120. package/src/wirings/ai-agent/ai-agent-runner.test.ts +3 -1
  121. package/src/wirings/ai-agent/ai-agent-runner.ts +553 -31
  122. package/src/wirings/ai-agent/ai-agent-stream.test.ts +209 -2
  123. package/src/wirings/ai-agent/ai-agent-stream.ts +195 -91
  124. package/src/wirings/ai-agent/ai-agent.types.ts +57 -3
  125. package/src/wirings/ai-agent/index.ts +3 -5
  126. package/src/wirings/channel/channel-handler.ts +41 -4
  127. package/src/wirings/channel/channel-middleware-runner.ts +1 -0
  128. package/src/wirings/channel/channel.types.ts +12 -7
  129. package/src/wirings/channel/index.ts +1 -0
  130. package/src/wirings/channel/local/local-channel-handler.ts +31 -3
  131. package/src/wirings/channel/local/local-channel-runner.ts +32 -8
  132. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +2 -0
  133. package/src/wirings/channel/pikku-abstract-channel-handler.ts +8 -1
  134. package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
  135. package/src/wirings/cli/cli-runner.ts +15 -2
  136. package/src/wirings/cli/cli.types.ts +1 -0
  137. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  138. package/src/wirings/gateway/gateway-runner.ts +411 -0
  139. package/src/wirings/gateway/gateway.types.ts +149 -0
  140. package/src/wirings/gateway/index.ts +13 -0
  141. package/src/wirings/http/http-routes.ts +0 -1
  142. package/src/wirings/http/http-runner.ts +38 -10
  143. package/src/wirings/http/http.types.ts +3 -12
  144. package/src/wirings/http/index.ts +3 -0
  145. package/src/wirings/http/pikku-fetch-http-request.ts +4 -0
  146. package/src/wirings/http/pikku-fetch-http-response.ts +4 -4
  147. package/src/wirings/http/web-request.test.ts +236 -0
  148. package/src/wirings/http/web-request.ts +104 -0
  149. package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
  150. package/src/wirings/mcp/mcp-runner.ts +30 -12
  151. package/src/wirings/rpc/rpc-runner.ts +45 -24
  152. package/src/wirings/rpc/rpc-types.ts +6 -1
  153. package/src/wirings/rpc/wire-addon.ts +2 -1
  154. package/src/wirings/scheduler/scheduler-runner.ts +1 -0
  155. package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
  156. package/src/wirings/workflow/graph/graph-validation.ts +237 -0
  157. package/src/wirings/workflow/graph/index.ts +5 -0
  158. package/src/wirings/workflow/index.ts +1 -0
  159. package/src/wirings/workflow/pikku-workflow-service.ts +14 -3
  160. package/src/wirings/workflow/workflow.types.ts +9 -1
  161. package/tsconfig.tsbuildinfo +1 -1
  162. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
  163. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -146
  164. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
  165. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -214
@@ -0,0 +1,474 @@
1
+ import { test, describe, beforeEach } from 'node:test'
2
+ import * as assert from 'assert'
3
+ import { wireGateway, createListenerMessageHandler } from './gateway-runner.js'
4
+ import { pikkuState, resetPikkuState } from '../../pikku-state.js'
5
+ import { httpRouter } from '../http/routers/http-router.js'
6
+ import { fetch } from '../http/http-runner.js'
7
+ import type {
8
+ GatewayAdapter,
9
+ GatewayInboundMessage,
10
+ GatewayOutboundMessage,
11
+ } from './gateway.types.js'
12
+ import type { CorePikkuMiddleware } from '../../types/core.types.js'
13
+
14
+ // --- Mock adapter ----------------------------------------------------------
15
+
16
+ const createMockAdapter = (
17
+ opts: {
18
+ name?: string
19
+ parseResult?: GatewayInboundMessage | null
20
+ verifyResult?: { verified: true; response: unknown } | { verified: false }
21
+ } = {}
22
+ ): GatewayAdapter & {
23
+ sentMessages: Array<{ senderId: string; message: GatewayOutboundMessage }>
24
+ simulateMessage: (data: unknown) => Promise<void>
25
+ } => {
26
+ const sentMessages: Array<{
27
+ senderId: string
28
+ message: GatewayOutboundMessage
29
+ }> = []
30
+
31
+ let onMessage: ((data: unknown) => Promise<void>) | undefined
32
+
33
+ return {
34
+ name: opts.name ?? 'mock',
35
+ sentMessages,
36
+ parse(data: unknown): GatewayInboundMessage | null {
37
+ if (opts.parseResult !== undefined) return opts.parseResult
38
+ const d = data as Record<string, any>
39
+ if (!d?.text) return null
40
+ return {
41
+ senderId: d.senderId ?? 'sender-1',
42
+ text: d.text,
43
+ raw: data,
44
+ }
45
+ },
46
+ send: async (senderId, message) => {
47
+ sentMessages.push({ senderId, message })
48
+ },
49
+ init: async (cb) => {
50
+ onMessage = cb
51
+ },
52
+ close: async () => {
53
+ onMessage = undefined
54
+ },
55
+ async simulateMessage(data: unknown) {
56
+ if (onMessage) await onMessage(data)
57
+ },
58
+ ...(opts.verifyResult
59
+ ? {
60
+ verifyWebhook: () => opts.verifyResult!,
61
+ }
62
+ : {}),
63
+ }
64
+ }
65
+
66
+ // --- Test helpers -----------------------------------------------------------
67
+
68
+ const setupState = () => {
69
+ resetPikkuState()
70
+ httpRouter.reset()
71
+
72
+ const singletonServices = {
73
+ config: {},
74
+ logger: {
75
+ info: () => {},
76
+ warn: () => {},
77
+ error: () => {},
78
+ },
79
+ variables: {},
80
+ secrets: {},
81
+ }
82
+
83
+ pikkuState(null, 'package', 'singletonServices', singletonServices as any)
84
+ pikkuState(null, 'package', 'factories', {
85
+ createWireServices: async () => ({}),
86
+ } as any)
87
+ }
88
+
89
+ // --- Tests ------------------------------------------------------------------
90
+
91
+ describe('wireGateway', () => {
92
+ beforeEach(() => {
93
+ setupState()
94
+ })
95
+
96
+ describe('type: webhook', () => {
97
+ test('registers POST route and processes messages', async () => {
98
+ const adapter = createMockAdapter()
99
+ const funcCalls: any[] = []
100
+
101
+ wireGateway({
102
+ name: 'test-webhook',
103
+ type: 'webhook',
104
+ route: '/webhooks/test',
105
+ adapter,
106
+ func: {
107
+ func: async (_services: any, data: any, wire: any) => {
108
+ funcCalls.push({ data, gateway: wire.gateway })
109
+ return { text: 'reply' }
110
+ },
111
+ },
112
+ })
113
+
114
+ httpRouter.initialize()
115
+
116
+ const request = new Request('http://localhost/webhooks/test', {
117
+ method: 'POST',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify({ senderId: 'user-1', text: 'hello' }),
120
+ })
121
+
122
+ const response = await fetch(request)
123
+ assert.equal(response.status, 200)
124
+
125
+ // Verify func was called with parsed message
126
+ assert.equal(funcCalls.length, 1)
127
+ assert.equal(funcCalls[0].data.senderId, 'user-1')
128
+ assert.equal(funcCalls[0].data.text, 'hello')
129
+
130
+ // Verify wire.gateway was populated
131
+ assert.equal(funcCalls[0].gateway.gatewayName, 'test-webhook')
132
+ assert.equal(funcCalls[0].gateway.senderId, 'user-1')
133
+ assert.equal(funcCalls[0].gateway.platform, 'mock')
134
+ assert.equal(typeof funcCalls[0].gateway.send, 'function')
135
+
136
+ // Verify auto-send of response
137
+ assert.equal(adapter.sentMessages.length, 1)
138
+ assert.equal(adapter.sentMessages[0].senderId, 'user-1')
139
+ assert.equal(adapter.sentMessages[0].message.text, 'reply')
140
+ })
141
+
142
+ test('returns 200 OK for ignored events (adapter returns null)', async () => {
143
+ const adapter = createMockAdapter({ parseResult: null })
144
+ const funcCalls: any[] = []
145
+
146
+ wireGateway({
147
+ name: 'test-ignore',
148
+ type: 'webhook',
149
+ route: '/webhooks/ignore',
150
+ adapter,
151
+ func: {
152
+ func: async () => {
153
+ funcCalls.push(true)
154
+ },
155
+ },
156
+ })
157
+
158
+ httpRouter.initialize()
159
+
160
+ const request = new Request('http://localhost/webhooks/ignore', {
161
+ method: 'POST',
162
+ headers: { 'Content-Type': 'application/json' },
163
+ body: JSON.stringify({ type: 'delivery_receipt' }),
164
+ })
165
+
166
+ const response = await fetch(request)
167
+ assert.equal(response.status, 200)
168
+
169
+ // Func should NOT be called
170
+ assert.equal(funcCalls.length, 0)
171
+ })
172
+
173
+ test('runs middleware before func with wire.gateway available', async () => {
174
+ const adapter = createMockAdapter()
175
+ const middlewareCalls: any[] = []
176
+
177
+ const testMiddleware: CorePikkuMiddleware = async (
178
+ _services,
179
+ wire,
180
+ next
181
+ ) => {
182
+ middlewareCalls.push({
183
+ gateway: (wire as any).gateway,
184
+ hasSession: !!wire.session,
185
+ })
186
+ wire.setSession?.({ userId: 'resolved-user' } as any)
187
+ await next()
188
+ }
189
+
190
+ wireGateway({
191
+ name: 'test-middleware',
192
+ type: 'webhook',
193
+ route: '/webhooks/mw',
194
+ adapter,
195
+ middleware: [testMiddleware],
196
+ func: {
197
+ func: async (_services: any, _data: any, wire: any) => {
198
+ return { text: `hello ${wire.session?.userId ?? 'unknown'}` }
199
+ },
200
+ },
201
+ })
202
+
203
+ httpRouter.initialize()
204
+
205
+ const request = new Request('http://localhost/webhooks/mw', {
206
+ method: 'POST',
207
+ headers: { 'Content-Type': 'application/json' },
208
+ body: JSON.stringify({ senderId: 'user-2', text: 'hi' }),
209
+ })
210
+
211
+ await fetch(request)
212
+
213
+ // Middleware should have run
214
+ assert.equal(middlewareCalls.length, 1)
215
+ // Middleware should see wire.gateway
216
+ assert.equal(middlewareCalls[0].gateway.senderId, 'user-2')
217
+ assert.equal(middlewareCalls[0].gateway.platform, 'mock')
218
+ })
219
+
220
+ test('handles webhook verification via GET', async () => {
221
+ const adapter = createMockAdapter({
222
+ verifyResult: { verified: true, response: 'challenge-token-123' },
223
+ })
224
+
225
+ wireGateway({
226
+ name: 'test-verify',
227
+ type: 'webhook',
228
+ route: '/webhooks/verify',
229
+ adapter,
230
+ func: { func: async () => {} },
231
+ })
232
+
233
+ httpRouter.initialize()
234
+
235
+ const request = new Request(
236
+ 'http://localhost/webhooks/verify?hub.mode=subscribe&hub.verify_token=tok&hub.challenge=challenge-token-123',
237
+ { method: 'GET' }
238
+ )
239
+
240
+ const response = await fetch(request)
241
+ assert.equal(response.status, 200)
242
+
243
+ const body = await response.json()
244
+ assert.equal(body, 'challenge-token-123')
245
+ })
246
+
247
+ test('handles POST-based verification (Slack style)', async () => {
248
+ const adapter = createMockAdapter({
249
+ parseResult: null,
250
+ verifyResult: {
251
+ verified: true,
252
+ response: { challenge: 'abc123' },
253
+ },
254
+ })
255
+
256
+ wireGateway({
257
+ name: 'test-slack-verify',
258
+ type: 'webhook',
259
+ route: '/webhooks/slack',
260
+ adapter,
261
+ func: { func: async () => {} },
262
+ })
263
+
264
+ httpRouter.initialize()
265
+
266
+ const request = new Request('http://localhost/webhooks/slack', {
267
+ method: 'POST',
268
+ headers: { 'Content-Type': 'application/json' },
269
+ body: JSON.stringify({
270
+ type: 'url_verification',
271
+ challenge: 'abc123',
272
+ }),
273
+ })
274
+
275
+ const response = await fetch(request)
276
+ assert.equal(response.status, 200)
277
+
278
+ const body = await response.json()
279
+ assert.deepEqual(body, { challenge: 'abc123' })
280
+ })
281
+
282
+ test('wire.gateway.send() sends proactive messages', async () => {
283
+ const adapter = createMockAdapter()
284
+ let capturedGateway: any
285
+
286
+ wireGateway({
287
+ name: 'test-proactive',
288
+ type: 'webhook',
289
+ route: '/webhooks/proactive',
290
+ adapter,
291
+ func: {
292
+ func: async (_services: any, _data: any, wire: any) => {
293
+ capturedGateway = wire.gateway
294
+ // Send a proactive message via gateway
295
+ await wire.gateway.send({ text: 'proactive msg' })
296
+ // Don't return a response (no auto-send)
297
+ },
298
+ },
299
+ })
300
+
301
+ httpRouter.initialize()
302
+
303
+ const request = new Request('http://localhost/webhooks/proactive', {
304
+ method: 'POST',
305
+ headers: { 'Content-Type': 'application/json' },
306
+ body: JSON.stringify({ senderId: 'user-3', text: 'trigger' }),
307
+ })
308
+
309
+ await fetch(request)
310
+
311
+ // Proactive message was sent
312
+ assert.equal(adapter.sentMessages.length, 1)
313
+ assert.equal(adapter.sentMessages[0].message.text, 'proactive msg')
314
+ })
315
+ })
316
+
317
+ describe('type: webhook errors', () => {
318
+ test('throws if no route provided for webhook', () => {
319
+ const adapter = createMockAdapter()
320
+
321
+ assert.throws(
322
+ () =>
323
+ wireGateway({
324
+ name: 'no-route',
325
+ type: 'webhook',
326
+ adapter,
327
+ func: { func: async () => {} },
328
+ }),
329
+ /requires a route/
330
+ )
331
+ })
332
+ })
333
+
334
+ describe('type: listener', () => {
335
+ test('stores gateway config in pikkuState', () => {
336
+ const adapter = createMockAdapter()
337
+
338
+ wireGateway({
339
+ name: 'test-listener',
340
+ type: 'listener',
341
+ adapter,
342
+ func: { func: async () => {} },
343
+ })
344
+
345
+ const gateways = pikkuState(null, 'gateway', 'gateways')
346
+ assert.ok(gateways.has('test-listener'))
347
+ assert.equal(gateways.get('test-listener').type, 'listener')
348
+ })
349
+ })
350
+
351
+ describe('createListenerMessageHandler', () => {
352
+ test('creates handler that processes messages', async () => {
353
+ const adapter = createMockAdapter()
354
+ const funcCalls: any[] = []
355
+
356
+ const config = {
357
+ name: 'test-listener',
358
+ type: 'listener' as const,
359
+ adapter,
360
+ func: {
361
+ func: async (_services: any, data: any, wire: any) => {
362
+ funcCalls.push({ data, gateway: wire.gateway })
363
+ return { text: 'listener-reply' }
364
+ },
365
+ },
366
+ }
367
+
368
+ wireGateway(config)
369
+
370
+ const singletonServices = pikkuState(null, 'package', 'singletonServices')
371
+ const handleMessage = createListenerMessageHandler(
372
+ 'test-listener',
373
+ config,
374
+ singletonServices
375
+ )
376
+
377
+ // Simulate incoming message
378
+ await handleMessage({
379
+ senderId: 'listener-user',
380
+ text: 'hello from listener',
381
+ })
382
+
383
+ assert.equal(funcCalls.length, 1)
384
+ assert.equal(funcCalls[0].data.senderId, 'listener-user')
385
+ assert.equal(funcCalls[0].data.text, 'hello from listener')
386
+ assert.equal(funcCalls[0].gateway.platform, 'mock')
387
+
388
+ // Auto-send response
389
+ assert.equal(adapter.sentMessages.length, 1)
390
+ assert.equal(adapter.sentMessages[0].message.text, 'listener-reply')
391
+ })
392
+
393
+ test('handler ignores events when adapter returns null', async () => {
394
+ const adapter = createMockAdapter({ parseResult: null })
395
+ const funcCalls: any[] = []
396
+
397
+ const config = {
398
+ name: 'test-listener-ignore',
399
+ type: 'listener' as const,
400
+ adapter,
401
+ func: {
402
+ func: async () => {
403
+ funcCalls.push(true)
404
+ },
405
+ },
406
+ }
407
+
408
+ wireGateway(config)
409
+
410
+ const singletonServices = pikkuState(null, 'package', 'singletonServices')
411
+ const handleMessage = createListenerMessageHandler(
412
+ 'test-listener-ignore',
413
+ config,
414
+ singletonServices
415
+ )
416
+
417
+ await handleMessage({ type: 'delivery_receipt' })
418
+
419
+ assert.equal(funcCalls.length, 0)
420
+ })
421
+ })
422
+
423
+ describe('gateway: true metadata flag', () => {
424
+ test('webhook gateway adds gateway meta to pikkuState', () => {
425
+ const adapter = createMockAdapter()
426
+
427
+ wireGateway({
428
+ name: 'test-meta',
429
+ type: 'webhook',
430
+ route: '/webhooks/meta',
431
+ adapter,
432
+ func: { func: async () => {} },
433
+ })
434
+
435
+ const gateways = pikkuState(null, 'gateway', 'gateways')
436
+ const config = gateways.get('test-meta')
437
+ assert.ok(config)
438
+ assert.equal(config.type, 'webhook')
439
+ assert.equal(config.name, 'test-meta')
440
+ })
441
+ })
442
+
443
+ describe('type: websocket', () => {
444
+ test('registers channel config', () => {
445
+ const adapter = createMockAdapter()
446
+
447
+ wireGateway({
448
+ name: 'test-ws',
449
+ type: 'websocket',
450
+ route: '/gateway/ws',
451
+ adapter,
452
+ func: { func: async () => {} },
453
+ })
454
+
455
+ const channels = pikkuState(null, 'channel', 'channels')
456
+ assert.ok(channels.has('test-ws'))
457
+ })
458
+
459
+ test('throws if no route provided', () => {
460
+ const adapter = createMockAdapter()
461
+
462
+ assert.throws(
463
+ () =>
464
+ wireGateway({
465
+ name: 'no-route-ws',
466
+ type: 'websocket',
467
+ adapter,
468
+ func: { func: async () => {} },
469
+ }),
470
+ /requires a route/
471
+ )
472
+ })
473
+ })
474
+ })