@pikku/core 0.12.4 → 0.12.6

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 (235) hide show
  1. package/CHANGELOG.md +21 -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/content-service.d.ts +2 -0
  9. package/dist/services/in-memory-workflow-service.d.ts +1 -0
  10. package/dist/services/in-memory-workflow-service.js +16 -11
  11. package/dist/services/workflow-service.d.ts +1 -0
  12. package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
  13. package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
  14. package/dist/wirings/channel/channel-middleware-runner.js +5 -0
  15. package/dist/wirings/http/http-runner.js +1 -1
  16. package/dist/wirings/workflow/pikku-workflow-service.d.ts +6 -0
  17. package/dist/wirings/workflow/pikku-workflow-service.js +51 -16
  18. package/dist/wirings/workflow/workflow.types.d.ts +2 -0
  19. package/package.json +5 -3
  20. package/src/crypto-utils.test.ts +214 -0
  21. package/src/crypto-utils.ts +213 -0
  22. package/src/dev/hot-reload.test.ts +484 -0
  23. package/src/dev/hot-reload.ts +212 -0
  24. package/src/errors/error-handler.ts +62 -0
  25. package/src/errors/error.test.ts +195 -0
  26. package/src/errors/errors.ts +374 -0
  27. package/src/errors/index.ts +2 -0
  28. package/src/factory-functions.test.ts +109 -0
  29. package/src/function/function-runner.test.ts +536 -0
  30. package/src/function/function-runner.ts +365 -0
  31. package/src/function/functions.types.ts +304 -0
  32. package/src/function/index.ts +5 -0
  33. package/src/handle-error.test.ts +424 -0
  34. package/src/handle-error.ts +69 -0
  35. package/src/index.ts +118 -0
  36. package/src/internal.ts +6 -0
  37. package/src/middleware/auth-apikey.test.ts +363 -0
  38. package/src/middleware/auth-apikey.ts +47 -0
  39. package/src/middleware/auth-bearer.test.ts +450 -0
  40. package/src/middleware/auth-bearer.ts +72 -0
  41. package/src/middleware/auth-cookie.test.ts +528 -0
  42. package/src/middleware/auth-cookie.ts +80 -0
  43. package/src/middleware/cors.test.ts +424 -0
  44. package/src/middleware/cors.ts +104 -0
  45. package/src/middleware/index.ts +5 -0
  46. package/src/middleware/remote-auth.test.ts +488 -0
  47. package/src/middleware/remote-auth.ts +68 -0
  48. package/src/middleware/timeout.ts +15 -0
  49. package/src/middleware-runner.test.ts +418 -0
  50. package/src/middleware-runner.ts +240 -0
  51. package/src/permissions.test.ts +434 -0
  52. package/src/permissions.ts +327 -0
  53. package/src/pikku-request.ts +23 -0
  54. package/src/pikku-response.ts +5 -0
  55. package/src/pikku-state.test.ts +224 -0
  56. package/src/pikku-state.ts +216 -0
  57. package/src/run-tests-script.test.ts +49 -0
  58. package/src/schema.test.ts +249 -0
  59. package/src/schema.ts +151 -0
  60. package/src/services/ai-agent-runner-service.ts +41 -0
  61. package/src/services/ai-run-state-service.ts +20 -0
  62. package/src/services/ai-storage-service.ts +39 -0
  63. package/src/services/content-service.ts +81 -0
  64. package/src/services/deployment-service.ts +22 -0
  65. package/src/services/gateway-service.ts +20 -0
  66. package/src/services/gopass-secrets.ts +98 -0
  67. package/src/services/in-memory-ai-run-state-service.ts +73 -0
  68. package/src/services/in-memory-trigger-service.ts +64 -0
  69. package/src/services/in-memory-workflow-service.test.ts +351 -0
  70. package/src/services/in-memory-workflow-service.ts +512 -0
  71. package/src/services/index.ts +56 -0
  72. package/src/services/jwt-service.ts +30 -0
  73. package/src/services/local-content.ts +120 -0
  74. package/src/services/local-gateway-service.ts +62 -0
  75. package/src/services/local-secrets.test.ts +80 -0
  76. package/src/services/local-secrets.ts +94 -0
  77. package/src/services/local-variables.test.ts +61 -0
  78. package/src/services/local-variables.ts +39 -0
  79. package/src/services/logger-console.test.ts +118 -0
  80. package/src/services/logger-console.ts +98 -0
  81. package/src/services/logger.ts +57 -0
  82. package/src/services/scheduler-service.ts +86 -0
  83. package/src/services/schema-service.ts +33 -0
  84. package/src/services/scoped-secret-service.test.ts +74 -0
  85. package/src/services/scoped-secret-service.ts +41 -0
  86. package/src/services/secret-service.ts +38 -0
  87. package/src/services/trigger-service.ts +17 -0
  88. package/src/services/typed-secret-service.test.ts +93 -0
  89. package/src/services/typed-secret-service.ts +70 -0
  90. package/src/services/typed-variables-service.test.ts +73 -0
  91. package/src/services/typed-variables-service.ts +81 -0
  92. package/src/services/user-session-service.test.ts +113 -0
  93. package/src/services/user-session-service.ts +86 -0
  94. package/src/services/variables-service.ts +11 -0
  95. package/src/services/workflow-service.ts +96 -0
  96. package/src/testing/index.ts +2 -0
  97. package/src/testing/service-tests.ts +873 -0
  98. package/src/time-utils.test.ts +56 -0
  99. package/src/time-utils.ts +107 -0
  100. package/src/types/core.types.ts +478 -0
  101. package/src/types/state.types.ts +188 -0
  102. package/src/utils/hash.test.ts +68 -0
  103. package/src/utils/hash.ts +26 -0
  104. package/src/utils.test.ts +350 -0
  105. package/src/utils.ts +129 -0
  106. package/src/version.test.ts +80 -0
  107. package/src/version.ts +25 -0
  108. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
  109. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
  110. package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
  111. package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
  112. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
  113. package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
  114. package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
  115. package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
  116. package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
  117. package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
  118. package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
  119. package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
  120. package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
  121. package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
  122. package/src/wirings/ai-agent/index.ts +37 -0
  123. package/src/wirings/channel/channel-common.ts +90 -0
  124. package/src/wirings/channel/channel-handler.ts +250 -0
  125. package/src/wirings/channel/channel-middleware-runner.ts +126 -0
  126. package/src/wirings/channel/channel-runner.ts +166 -0
  127. package/src/wirings/channel/channel-store.ts +29 -0
  128. package/src/wirings/channel/channel.types.ts +172 -0
  129. package/src/wirings/channel/define-channel-routes.ts +25 -0
  130. package/src/wirings/channel/eventhub-service.ts +34 -0
  131. package/src/wirings/channel/eventhub-store.ts +13 -0
  132. package/src/wirings/channel/index.ts +24 -0
  133. package/src/wirings/channel/local/index.ts +3 -0
  134. package/src/wirings/channel/local/local-channel-handler.ts +81 -0
  135. package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
  136. package/src/wirings/channel/local/local-channel-runner.ts +210 -0
  137. package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
  138. package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
  139. package/src/wirings/channel/log-channels.ts +20 -0
  140. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
  141. package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
  142. package/src/wirings/channel/serverless/index.ts +5 -0
  143. package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
  144. package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
  145. package/src/wirings/cli/channel/index.ts +1 -0
  146. package/src/wirings/cli/cli-runner.test.ts +403 -0
  147. package/src/wirings/cli/cli-runner.ts +529 -0
  148. package/src/wirings/cli/cli.types.ts +358 -0
  149. package/src/wirings/cli/command-parser.test.ts +445 -0
  150. package/src/wirings/cli/command-parser.ts +504 -0
  151. package/src/wirings/cli/define-cli-commands.ts +24 -0
  152. package/src/wirings/cli/index.ts +19 -0
  153. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  154. package/src/wirings/gateway/gateway-runner.ts +411 -0
  155. package/src/wirings/gateway/gateway.types.ts +149 -0
  156. package/src/wirings/gateway/index.ts +13 -0
  157. package/src/wirings/http/http-routes.test.ts +322 -0
  158. package/src/wirings/http/http-routes.ts +197 -0
  159. package/src/wirings/http/http-runner.test.ts +144 -0
  160. package/src/wirings/http/http-runner.ts +588 -0
  161. package/src/wirings/http/http.types.ts +369 -0
  162. package/src/wirings/http/index.ts +28 -0
  163. package/src/wirings/http/log-http-routes.ts +22 -0
  164. package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
  165. package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
  166. package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
  167. package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
  168. package/src/wirings/http/routers/http-router.ts +13 -0
  169. package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
  170. package/src/wirings/http/routers/path-to-regex.ts +126 -0
  171. package/src/wirings/http/web-request.test.ts +236 -0
  172. package/src/wirings/http/web-request.ts +104 -0
  173. package/src/wirings/mcp/index.ts +27 -0
  174. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
  175. package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
  176. package/src/wirings/mcp/mcp-runner.ts +323 -0
  177. package/src/wirings/mcp/mcp.types.ts +216 -0
  178. package/src/wirings/node/index.ts +2 -0
  179. package/src/wirings/node/node.types.ts +23 -0
  180. package/src/wirings/oauth2/index.ts +3 -0
  181. package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
  182. package/src/wirings/oauth2/oauth2-client.ts +335 -0
  183. package/src/wirings/oauth2/oauth2.types.ts +69 -0
  184. package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
  185. package/src/wirings/queue/index.ts +31 -0
  186. package/src/wirings/queue/queue-runner.test.ts +710 -0
  187. package/src/wirings/queue/queue-runner.ts +192 -0
  188. package/src/wirings/queue/queue.types.ts +189 -0
  189. package/src/wirings/queue/register-queue-helper.ts +60 -0
  190. package/src/wirings/queue/validate-worker-config.test.ts +108 -0
  191. package/src/wirings/queue/validate-worker-config.ts +116 -0
  192. package/src/wirings/rpc/index.ts +4 -0
  193. package/src/wirings/rpc/rpc-runner.ts +460 -0
  194. package/src/wirings/rpc/rpc-types.ts +56 -0
  195. package/src/wirings/rpc/wire-addon.ts +21 -0
  196. package/src/wirings/scheduler/index.ts +11 -0
  197. package/src/wirings/scheduler/log-schedulers.ts +20 -0
  198. package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
  199. package/src/wirings/scheduler/scheduler-runner.ts +133 -0
  200. package/src/wirings/scheduler/scheduler.types.ts +53 -0
  201. package/src/wirings/secret/index.ts +9 -0
  202. package/src/wirings/secret/secret.types.ts +32 -0
  203. package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
  204. package/src/wirings/secret/validate-secret-definitions.ts +82 -0
  205. package/src/wirings/trigger/index.ts +10 -0
  206. package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
  207. package/src/wirings/trigger/trigger-runner.test.ts +79 -0
  208. package/src/wirings/trigger/trigger-runner.ts +135 -0
  209. package/src/wirings/trigger/trigger.types.ts +178 -0
  210. package/src/wirings/variable/index.ts +8 -0
  211. package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
  212. package/src/wirings/variable/validate-variable-definitions.ts +69 -0
  213. package/src/wirings/variable/variable.types.ts +22 -0
  214. package/src/wirings/workflow/dsl/index.ts +31 -0
  215. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
  216. package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
  217. package/src/wirings/workflow/graph/graph-node.ts +222 -0
  218. package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
  219. package/src/wirings/workflow/graph/graph-runner.ts +816 -0
  220. package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
  221. package/src/wirings/workflow/graph/graph-validation.ts +237 -0
  222. package/src/wirings/workflow/graph/index.ts +19 -0
  223. package/src/wirings/workflow/graph/template.test.ts +49 -0
  224. package/src/wirings/workflow/graph/template.ts +42 -0
  225. package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
  226. package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
  227. package/src/wirings/workflow/index.ts +82 -0
  228. package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
  229. package/src/wirings/workflow/pikku-workflow-service.ts +1229 -0
  230. package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
  231. package/src/wirings/workflow/workflow-helpers.ts +79 -0
  232. package/src/wirings/workflow/workflow.types.ts +276 -0
  233. package/tsconfig.json +14 -0
  234. package/tsconfig.tsbuildinfo +1 -0
  235. package/lcov.info +0 -18891
@@ -0,0 +1,369 @@
1
+ import type { SerializeOptions } from 'cookie'
2
+ import type { StandardSchemaV1 } from '@standard-schema/spec'
3
+ import type {
4
+ CorePikkuMiddleware,
5
+ CommonWireMeta,
6
+ } from '../../types/core.types.js'
7
+ import type {
8
+ CorePikkuFunction,
9
+ CorePikkuFunctionSessionless,
10
+ CorePikkuPermission,
11
+ CorePermissionGroup,
12
+ CorePikkuFunctionConfig,
13
+ } from '../../function/functions.types.js'
14
+
15
+ type ExtractHTTPWiringParams<S extends string> =
16
+ S extends `${string}:${infer Param}/${infer Rest}`
17
+ ? Param | ExtractHTTPWiringParams<`/${Rest}`>
18
+ : S extends `${string}:${infer Param}`
19
+ ? Param
20
+ : never
21
+
22
+ export type AssertHTTPWiringParams<In, HTTPWiring extends string> =
23
+ ExtractHTTPWiringParams<HTTPWiring> extends keyof In
24
+ ? unknown
25
+ : [
26
+ 'Error: HTTPWiring parameters',
27
+ ExtractHTTPWiringParams<HTTPWiring>,
28
+ 'not in',
29
+ keyof In,
30
+ ]
31
+
32
+ export type RunHTTPWiringOptions = Partial<{
33
+ skipUserSession: boolean
34
+ respondWith404: boolean
35
+ logWarningsForStatusCodes: number[]
36
+ coerceDataFromSchema: boolean
37
+ bubbleErrors: boolean
38
+ exposeErrors: boolean
39
+ generateRequestId: () => string
40
+ }>
41
+
42
+ /**
43
+ * Represents the HTTP methods supported for API HTTP wirings.
44
+ */
45
+ export type HTTPMethod =
46
+ | 'post'
47
+ | 'get'
48
+ | 'delete'
49
+ | 'patch'
50
+ | 'head'
51
+ | 'put'
52
+ | 'options'
53
+
54
+ /**
55
+ * Header schema type for request header validation.
56
+ * Uses Standard Schema interface - works with Zod, Valibot, ArkType, Effect Schema, etc.
57
+ */
58
+ export type HTTPHeadersSchema = StandardSchemaV1<
59
+ Record<string, string | string[] | undefined>
60
+ >
61
+
62
+ /**
63
+ * Common HTTP route configuration shared between wireHTTP and wireHTTPRoutes
64
+ */
65
+ export type HTTPRouteBaseConfig = {
66
+ contentType?: 'xml' | 'json'
67
+ timeout?: number
68
+ tags?: string[]
69
+ headers?: HTTPHeadersSchema
70
+ }
71
+
72
+ /**
73
+ * Represents an API HTTP wiring without a function, including metadata such as content type, route, and timeout settings.
74
+ */
75
+ export type CoreHTTPFunction = HTTPRouteBaseConfig & {
76
+ route: string
77
+ eventChannel?: false
78
+ returnsJSON?: false
79
+ }
80
+ /**
81
+ * Represents a http wire within Pikku, including a request and response.
82
+ */
83
+ export interface PikkuHTTP<In = unknown> {
84
+ request?: PikkuHTTPRequest<In>
85
+ response?: PikkuHTTPResponse
86
+ }
87
+
88
+ /**
89
+ * Represents request headers as either a record or a function to get headers by name.
90
+ */
91
+ export type RequestHeaders =
92
+ | Record<string, string | string[] | undefined>
93
+ | ((headerName: string) => string | string[] | undefined)
94
+
95
+ /**
96
+ * Represents a query object for Pikku, where each key can be a string, a value, or an array of values.
97
+ */
98
+ export type PikkuQuery<T = Record<string, string | undefined>> = Record<
99
+ string,
100
+ string | T | null | Array<T | null>
101
+ >
102
+
103
+ /**
104
+ * Represents a core API HTTP wiring, which can have different configurations depending on whether it requires authentication and permissions.
105
+ *
106
+ * @template In - The input type.
107
+ * @template Out - The output type.
108
+ * @template R - The route string type.
109
+ * @template PikkuFunction - The API function type, defaults to `CorePikkuFunction`.
110
+ * @template PikkuFunctionSessionless - The sessionless API function type, defaults to `CorePikkuFunctionSessionless`.
111
+ * @template PikkuPermission - The permission function type, defaults to `CorePikkuPermission`.
112
+ */
113
+ export type CoreHTTPFunctionWiring<
114
+ In,
115
+ Out,
116
+ R extends string,
117
+ PikkuFunction extends CorePikkuFunction<
118
+ In,
119
+ Out,
120
+ any,
121
+ any,
122
+ any
123
+ > = CorePikkuFunction<In, Out>,
124
+ PikkuFunctionSessionless extends CorePikkuFunctionSessionless<
125
+ In,
126
+ Out,
127
+ any,
128
+ any,
129
+ any
130
+ > = CorePikkuFunctionSessionless<In, Out>,
131
+ PikkuPermission extends CorePikkuPermission<
132
+ In,
133
+ any,
134
+ any
135
+ > = CorePikkuPermission<In, any, any>,
136
+ PikkuMiddleware extends CorePikkuMiddleware<
137
+ any,
138
+ any
139
+ > = CorePikkuMiddleware<any>,
140
+ > =
141
+ | (CoreHTTPFunction & {
142
+ route: R
143
+ method: HTTPMethod
144
+ func: CorePikkuFunctionConfig<
145
+ PikkuFunction,
146
+ PikkuPermission,
147
+ PikkuMiddleware
148
+ >
149
+ permissions?: CorePermissionGroup<PikkuPermission>
150
+ auth?: true
151
+ middleware?: PikkuMiddleware[]
152
+ sse?: undefined
153
+ })
154
+ | (CoreHTTPFunction & {
155
+ route: R
156
+ method: HTTPMethod
157
+ func: CorePikkuFunctionConfig<
158
+ PikkuFunctionSessionless,
159
+ PikkuPermission,
160
+ PikkuMiddleware
161
+ >
162
+ permissions?: undefined
163
+ auth?: false
164
+ middleware?: PikkuMiddleware[]
165
+ sse?: undefined
166
+ })
167
+ | (CoreHTTPFunction & {
168
+ route: R
169
+ method: 'get'
170
+ func: CorePikkuFunctionConfig<
171
+ PikkuFunction,
172
+ PikkuPermission,
173
+ PikkuMiddleware
174
+ >
175
+ permissions?: CorePermissionGroup<PikkuPermission>
176
+ auth?: true
177
+ middleware?: PikkuMiddleware[]
178
+ sse?: boolean
179
+ })
180
+ | (CoreHTTPFunction & {
181
+ route: R
182
+ method: 'get'
183
+ func: CorePikkuFunctionConfig<
184
+ PikkuFunctionSessionless,
185
+ PikkuPermission,
186
+ PikkuMiddleware
187
+ >
188
+ permissions?: undefined
189
+ auth?: false
190
+ middleware?: PikkuMiddleware[]
191
+ sse?: boolean
192
+ })
193
+ | (CoreHTTPFunction & {
194
+ route: R
195
+ method: 'post'
196
+ func: CorePikkuFunctionConfig<
197
+ PikkuFunction,
198
+ PikkuPermission,
199
+ PikkuMiddleware
200
+ >
201
+ permissions?: CorePermissionGroup<PikkuPermission>
202
+ auth?: true
203
+ middleware?: PikkuMiddleware[]
204
+ query?: Array<keyof In>
205
+ sse?: undefined
206
+ })
207
+ | (CoreHTTPFunction & {
208
+ route: R
209
+ method: 'post'
210
+ func: CorePikkuFunctionConfig<
211
+ PikkuFunctionSessionless,
212
+ PikkuPermission,
213
+ PikkuMiddleware
214
+ >
215
+ permissions?: undefined
216
+ auth?: false
217
+ middleware?: PikkuMiddleware[]
218
+ query?: Array<keyof In>
219
+ sse?: undefined
220
+ })
221
+
222
+ /**
223
+ * Represents the input types for HTTP wiring metadata, including parameters, query, and body types.
224
+ */
225
+ export type HTTPFunctionMetaInputTypes = {
226
+ params?: string
227
+ query?: string
228
+ body?: string
229
+ }
230
+
231
+ /**
232
+ * Represents metadata for a set of HTTP wirings, including HTTP wiring details, methods, input/output types, and documentation.
233
+ */
234
+ export type HTTPWiringMeta = CommonWireMeta & {
235
+ route: string
236
+ method: HTTPMethod
237
+ params?: string[]
238
+ query?: string[]
239
+ inputTypes?: HTTPFunctionMetaInputTypes
240
+ headersSchemaName?: string
241
+ sse?: true
242
+ groupBasePath?: string
243
+ }
244
+ export type HTTPWiringsMeta = Record<HTTPMethod, Record<string, HTTPWiringMeta>>
245
+
246
+ export type HTTPFunctionsMeta = Array<{
247
+ name: string
248
+ inputs: string[] | null
249
+ outputs: string[] | null
250
+ }>
251
+
252
+ export type HTTPWiringMiddleware = {
253
+ route: string
254
+ middleware: CorePikkuMiddleware[]
255
+ }
256
+
257
+ export interface PikkuHTTPRequest<In = unknown> {
258
+ method(): HTTPMethod
259
+ path(): string
260
+ data(): Promise<In>
261
+ json(): Promise<unknown>
262
+ arrayBuffer(): Promise<ArrayBuffer>
263
+ headers(): Record<string, string>
264
+ header(headerName: string): string | null
265
+ cookie(name?: string): string | null
266
+ params(): Partial<Record<string, string | string[]>>
267
+ setParams(params: Record<string, string | string[] | undefined>): void
268
+ query(): PikkuQuery
269
+ }
270
+
271
+ export interface PikkuHTTPResponse<Out = unknown> {
272
+ status(code: number): this
273
+ cookie(name: string, value: string | null, options: SerializeOptions): this
274
+ header(name: string, value: string | string[]): this
275
+ arrayBuffer(
276
+ data:
277
+ | ArrayBuffer
278
+ | ArrayBufferView
279
+ | Blob
280
+ | string
281
+ | FormData
282
+ | URLSearchParams
283
+ | ReadableStream
284
+ ): this
285
+ json(data: Out): this
286
+ send?(data: string | ArrayBuffer | Buffer): this
287
+ redirect(location: string, status?: number): this
288
+ close?: () => void
289
+ setMode?: (mode: 'stream') => void
290
+ }
291
+
292
+ /**
293
+ * Single route configuration - supports all wireHTTP options
294
+ */
295
+ export type HTTPRouteConfig<
296
+ PikkuFunction extends
297
+ | CorePikkuFunction<any, any, any, any, any>
298
+ | CorePikkuFunctionSessionless<any, any, any, any, any> =
299
+ | CorePikkuFunction<any, any, any, any, any>
300
+ | CorePikkuFunctionSessionless<any, any, any, any, any>,
301
+ PikkuPermission extends CorePikkuPermission<
302
+ any,
303
+ any,
304
+ any
305
+ > = CorePikkuPermission<any>,
306
+ PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<
307
+ any,
308
+ any
309
+ >,
310
+ > = HTTPRouteBaseConfig & {
311
+ // Required
312
+ method: HTTPMethod
313
+ route: string
314
+ func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>
315
+
316
+ // Auth & permissions
317
+ auth?: boolean
318
+ permissions?: CorePermissionGroup<PikkuPermission>
319
+
320
+ // Middleware
321
+ middleware?: PikkuMiddleware[]
322
+
323
+ // SSE support
324
+ sse?: boolean
325
+ }
326
+
327
+ /**
328
+ * Group-level configuration applied to all routes
329
+ */
330
+ export type HTTPRoutesGroupConfig<
331
+ PikkuPermission extends CorePikkuPermission<
332
+ any,
333
+ any,
334
+ any
335
+ > = CorePikkuPermission<any>,
336
+ PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<
337
+ any,
338
+ any
339
+ >,
340
+ > = {
341
+ basePath?: string
342
+ tags?: string[]
343
+ auth?: boolean
344
+ middleware?: PikkuMiddleware[]
345
+ permissions?: CorePermissionGroup<PikkuPermission>
346
+ }
347
+
348
+ /**
349
+ * Nested route map - allows hierarchical organization
350
+ * Can contain individual routes, nested maps, or contracts with config
351
+ */
352
+ export type HTTPRouteMap = {
353
+ [key: string]: HTTPRouteConfig | HTTPRouteMap | HTTPRouteContract
354
+ }
355
+
356
+ /**
357
+ * A route contract with optional group config (returned by defineRoutes)
358
+ */
359
+ export type HTTPRouteContract<T extends HTTPRouteMap = HTTPRouteMap> =
360
+ HTTPRoutesGroupConfig & {
361
+ routes: T
362
+ }
363
+
364
+ /**
365
+ * Full configuration for wireHTTPRoutes
366
+ */
367
+ export type WireHTTPRoutesConfig = HTTPRoutesGroupConfig & {
368
+ routes: HTTPRouteMap | HTTPRouteConfig[]
369
+ }
@@ -0,0 +1,28 @@
1
+ export { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
2
+ export { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
3
+ export { logRoutes } from './log-http-routes.js'
4
+
5
+ export {
6
+ fetch,
7
+ fetchData,
8
+ wireHTTP,
9
+ addHTTPMiddleware,
10
+ addHTTPPermission,
11
+ } from './http-runner.js'
12
+
13
+ export { wireHTTPRoutes, defineHTTPRoutes } from './http-routes.js'
14
+ export { toWebRequest, applyWebResponse } from './web-request.js'
15
+
16
+ export type {
17
+ AssertHTTPWiringParams,
18
+ CoreHTTPFunctionWiring,
19
+ HTTPMethod,
20
+ HTTPRouteBaseConfig,
21
+ HTTPRouteContract,
22
+ HTTPRouteMap,
23
+ HTTPWiringsMeta,
24
+ PikkuHTTPRequest,
25
+ PikkuHTTPResponse,
26
+ PikkuQuery,
27
+ RunHTTPWiringOptions,
28
+ } from './http.types.js'
@@ -0,0 +1,22 @@
1
+ import { pikkuState } from '../../pikku-state.js'
2
+ import type { Logger } from '../../services/index.js'
3
+
4
+ /**
5
+ * Logs all the loaded routes.
6
+ * @param logger - A logger for logging information.
7
+ */
8
+ export const logRoutes = (logger: Logger) => {
9
+ const routesByType = pikkuState(null, 'http', 'routes')
10
+ if (routesByType.size === 0) {
11
+ logger.info('No routes added')
12
+ return
13
+ }
14
+
15
+ let routesMessage = 'Routes loaded:'
16
+ routesByType.forEach((routes) => {
17
+ routes.forEach((route) => {
18
+ routesMessage += `\n\t- ${route.method.toUpperCase()} -> ${route.route}`
19
+ })
20
+ })
21
+ logger.info(routesMessage)
22
+ }
@@ -0,0 +1,237 @@
1
+ import { test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request'
4
+
5
+ const createRequest = (method, url, body, headers = {}) => {
6
+ return new Request(url, {
7
+ method,
8
+ headers,
9
+ body:
10
+ method === 'POST'
11
+ ? typeof body === 'string'
12
+ ? body
13
+ : JSON.stringify(body)
14
+ : undefined,
15
+ })
16
+ }
17
+
18
+ test('method() returns lowercase HTTP method', () => {
19
+ const req = createRequest('POST', 'http://localhost/test', null)
20
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
21
+ assert.equal(pikkuReq.method(), 'post')
22
+ })
23
+
24
+ test('path() returns pathname only', () => {
25
+ const req = createRequest('GET', 'http://localhost/foo/bar?x=1', null)
26
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
27
+ assert.equal(pikkuReq.path(), '/foo/bar')
28
+ })
29
+
30
+ test('header() retrieves headers case-insensitively', () => {
31
+ const req = createRequest('GET', 'http://localhost', null, {
32
+ 'Content-Type': 'application/json',
33
+ })
34
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
35
+ assert.equal(pikkuReq.header('content-type'), 'application/json')
36
+ })
37
+
38
+ test('cookie() parses cookies correctly', () => {
39
+ const req = createRequest('GET', 'http://localhost', null, {
40
+ Cookie: 'session=abc; user=test',
41
+ })
42
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
43
+ assert.equal(pikkuReq.cookie('session'), 'abc')
44
+ assert.equal(pikkuReq.cookie('user'), 'test')
45
+ assert.equal(pikkuReq.cookie('missing'), null)
46
+ })
47
+
48
+ test('params() and setParams()', () => {
49
+ const req = createRequest('GET', 'http://localhost', null)
50
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
51
+ assert.deepEqual(pikkuReq.params(), {})
52
+ pikkuReq.setParams({ id: '123' })
53
+ assert.deepEqual(pikkuReq.params(), { id: '123' })
54
+ })
55
+
56
+ test('query() parses URL search params', () => {
57
+ const req = createRequest('GET', 'http://localhost?x=1&y=2', null)
58
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
59
+ assert.equal(pikkuReq.query().x, '1')
60
+ assert.equal(pikkuReq.query().y, '2')
61
+ })
62
+
63
+ test('data() merges json body, query, and params', async () => {
64
+ const req = createRequest(
65
+ 'POST',
66
+ 'http://localhost/test?a=5',
67
+ { foo: 'bar' },
68
+ { 'Content-Type': 'application/json' }
69
+ )
70
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
71
+ pikkuReq.setParams({ id: '22' })
72
+ const result = await pikkuReq.data()
73
+ assert.deepEqual(result, { id: '22', a: '5', foo: 'bar' })
74
+ })
75
+
76
+ test('data() wraps string JSON body under data key', async () => {
77
+ const req = createRequest('POST', 'http://localhost', '"hello"', {
78
+ 'Content-Type': 'application/json',
79
+ })
80
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
81
+ const result = await pikkuReq.data()
82
+ assert.deepEqual(result, { data: 'hello' })
83
+ })
84
+
85
+ test('data() handles text/plain correctly', async () => {
86
+ const req = new Request('http://localhost', {
87
+ method: 'POST',
88
+ headers: { 'Content-Type': 'text/plain' },
89
+ body: 'hello world',
90
+ })
91
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
92
+ const result = await pikkuReq.data()
93
+ assert.deepEqual(result, { data: 'hello world' })
94
+ })
95
+
96
+ test('data() returns arrayBuffer for unknown content-type', async () => {
97
+ const buffer = Buffer.from('raw')
98
+ const req = new Request('http://localhost', {
99
+ method: 'POST',
100
+ headers: { 'Content-Type': 'application/octet-stream' },
101
+ body: buffer,
102
+ })
103
+ const pikkuReq = new PikkuFetchHTTPRequest<any>(req)
104
+ const result = await pikkuReq.data()
105
+ assert(result.data instanceof ArrayBuffer)
106
+ const str = Buffer.from(result.data).toString()
107
+ assert.equal(str, 'raw')
108
+ })
109
+
110
+ test('data() handles invalid JSON safely', async () => {
111
+ const req = new Request('http://localhost', {
112
+ method: 'POST',
113
+ headers: { 'Content-Type': 'application/json' },
114
+ body: 'not-json',
115
+ })
116
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
117
+ try {
118
+ await pikkuReq.data()
119
+ assert(false, 'Should have thrown')
120
+ } catch (e) {
121
+ assert(e.message.includes('Error parsing body'))
122
+ }
123
+ })
124
+
125
+ test('data() parses application/x-www-form-urlencoded correctly', async () => {
126
+ const formBody = 'name=Yasser&age=35&active=true'
127
+
128
+ const req = new Request('http://localhost/form?ref=abc', {
129
+ method: 'POST',
130
+ headers: {
131
+ 'Content-Type': 'application/x-www-form-urlencoded',
132
+ },
133
+ body: formBody,
134
+ })
135
+
136
+ const pikkuReq = new PikkuFetchHTTPRequest(req)
137
+ pikkuReq.setParams({ userId: '999' })
138
+
139
+ const result = await pikkuReq.data()
140
+
141
+ assert.deepEqual(result, {
142
+ userId: '999',
143
+ ref: 'abc',
144
+ name: 'Yasser',
145
+ age: '35',
146
+ active: 'true',
147
+ })
148
+ })
149
+
150
+ // --- Compatible types
151
+
152
+ test('data() treats "123" and 123 as equivalent', async () => {
153
+ const req = createRequest(
154
+ 'POST',
155
+ 'http://localhost?id=123',
156
+ { id: 123 },
157
+ {
158
+ 'Content-Type': 'application/json',
159
+ }
160
+ )
161
+
162
+ const r = new PikkuFetchHTTPRequest(req)
163
+ r.setParams({ id: '123' }) // All match
164
+ const result = await r.data()
165
+ assert.deepEqual(result, { id: '123' })
166
+ })
167
+
168
+ test('data() treats "true" and true as equivalent', async () => {
169
+ const req = createRequest(
170
+ 'POST',
171
+ 'http://localhost?flag=true',
172
+ { flag: true },
173
+ {
174
+ 'Content-Type': 'application/json',
175
+ }
176
+ )
177
+
178
+ const r = new PikkuFetchHTTPRequest(req)
179
+ r.setParams({ flag: 'true' }) // All match
180
+ const result = await r.data()
181
+ assert.deepEqual(result, { flag: 'true' })
182
+ })
183
+
184
+ // --- Conflicts
185
+
186
+ test('data() throws on conflicting values', async () => {
187
+ const req = createRequest(
188
+ 'POST',
189
+ 'http://localhost?foo=123',
190
+ { foo: 456 },
191
+ {
192
+ 'Content-Type': 'application/json',
193
+ }
194
+ )
195
+
196
+ const r = new PikkuFetchHTTPRequest(req)
197
+ r.setParams({ foo: '123' })
198
+
199
+ await assert.rejects(async () => await r.data(), {
200
+ message: 'Conflicting values for key "foo": "123" vs "456"',
201
+ })
202
+ })
203
+
204
+ test('data() throws on boolean conflict', async () => {
205
+ const req = createRequest(
206
+ 'POST',
207
+ 'http://localhost?debug=false',
208
+ { debug: true },
209
+ {
210
+ 'Content-Type': 'application/json',
211
+ }
212
+ )
213
+
214
+ const r = new PikkuFetchHTTPRequest(req)
215
+ r.setParams({})
216
+
217
+ await assert.rejects(async () => await r.data(), {
218
+ message: 'Conflicting values for key "debug": "false" vs "true"',
219
+ })
220
+ })
221
+
222
+ // --- Safe fallback: only one source
223
+
224
+ test('data() works when only body has values', async () => {
225
+ const req = createRequest(
226
+ 'POST',
227
+ 'http://localhost',
228
+ { test: 'ok' },
229
+ {
230
+ 'Content-Type': 'application/json',
231
+ }
232
+ )
233
+
234
+ const r = new PikkuFetchHTTPRequest(req)
235
+ const result = await r.data()
236
+ assert.deepEqual(result, { test: 'ok' })
237
+ })