@pikku/core 0.12.3 → 0.12.4

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 (218) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/services/in-memory-workflow-service.d.ts +15 -2
  3. package/dist/services/in-memory-workflow-service.js +49 -0
  4. package/lcov.info +18891 -0
  5. package/package.json +1 -1
  6. package/src/crypto-utils.test.ts +0 -214
  7. package/src/crypto-utils.ts +0 -213
  8. package/src/errors/error-handler.ts +0 -62
  9. package/src/errors/error.test.ts +0 -195
  10. package/src/errors/errors.ts +0 -374
  11. package/src/errors/index.ts +0 -2
  12. package/src/factory-functions.test.ts +0 -109
  13. package/src/function/function-runner.test.ts +0 -536
  14. package/src/function/function-runner.ts +0 -365
  15. package/src/function/functions.types.ts +0 -304
  16. package/src/function/index.ts +0 -5
  17. package/src/handle-error.test.ts +0 -424
  18. package/src/handle-error.ts +0 -69
  19. package/src/index.ts +0 -118
  20. package/src/internal.ts +0 -6
  21. package/src/middleware/auth-apikey.test.ts +0 -363
  22. package/src/middleware/auth-apikey.ts +0 -47
  23. package/src/middleware/auth-bearer.test.ts +0 -450
  24. package/src/middleware/auth-bearer.ts +0 -72
  25. package/src/middleware/auth-cookie.test.ts +0 -528
  26. package/src/middleware/auth-cookie.ts +0 -80
  27. package/src/middleware/cors.test.ts +0 -424
  28. package/src/middleware/cors.ts +0 -104
  29. package/src/middleware/index.ts +0 -5
  30. package/src/middleware/remote-auth.test.ts +0 -488
  31. package/src/middleware/remote-auth.ts +0 -68
  32. package/src/middleware/timeout.ts +0 -15
  33. package/src/middleware-runner.test.ts +0 -418
  34. package/src/middleware-runner.ts +0 -234
  35. package/src/permissions.test.ts +0 -434
  36. package/src/permissions.ts +0 -319
  37. package/src/pikku-request.ts +0 -23
  38. package/src/pikku-response.ts +0 -5
  39. package/src/pikku-state.test.ts +0 -224
  40. package/src/pikku-state.ts +0 -216
  41. package/src/run-tests-script.test.ts +0 -49
  42. package/src/schema.test.ts +0 -249
  43. package/src/schema.ts +0 -151
  44. package/src/services/ai-agent-runner-service.ts +0 -41
  45. package/src/services/ai-run-state-service.ts +0 -20
  46. package/src/services/ai-storage-service.ts +0 -39
  47. package/src/services/content-service.ts +0 -76
  48. package/src/services/deployment-service.ts +0 -22
  49. package/src/services/gateway-service.ts +0 -20
  50. package/src/services/gopass-secrets.ts +0 -98
  51. package/src/services/in-memory-ai-run-state-service.ts +0 -73
  52. package/src/services/in-memory-trigger-service.ts +0 -64
  53. package/src/services/in-memory-workflow-service.test.ts +0 -351
  54. package/src/services/in-memory-workflow-service.ts +0 -438
  55. package/src/services/index.ts +0 -56
  56. package/src/services/jwt-service.ts +0 -30
  57. package/src/services/local-content.ts +0 -120
  58. package/src/services/local-gateway-service.ts +0 -62
  59. package/src/services/local-secrets.test.ts +0 -80
  60. package/src/services/local-secrets.ts +0 -94
  61. package/src/services/local-variables.test.ts +0 -61
  62. package/src/services/local-variables.ts +0 -39
  63. package/src/services/logger-console.test.ts +0 -118
  64. package/src/services/logger-console.ts +0 -98
  65. package/src/services/logger.ts +0 -57
  66. package/src/services/scheduler-service.ts +0 -86
  67. package/src/services/schema-service.ts +0 -33
  68. package/src/services/scoped-secret-service.test.ts +0 -74
  69. package/src/services/scoped-secret-service.ts +0 -41
  70. package/src/services/secret-service.ts +0 -38
  71. package/src/services/trigger-service.ts +0 -17
  72. package/src/services/typed-secret-service.test.ts +0 -93
  73. package/src/services/typed-secret-service.ts +0 -70
  74. package/src/services/typed-variables-service.test.ts +0 -73
  75. package/src/services/typed-variables-service.ts +0 -81
  76. package/src/services/user-session-service.test.ts +0 -113
  77. package/src/services/user-session-service.ts +0 -86
  78. package/src/services/variables-service.ts +0 -11
  79. package/src/services/workflow-service.ts +0 -95
  80. package/src/testing/index.ts +0 -2
  81. package/src/testing/service-tests.ts +0 -873
  82. package/src/time-utils.test.ts +0 -56
  83. package/src/time-utils.ts +0 -107
  84. package/src/types/core.types.ts +0 -478
  85. package/src/types/state.types.ts +0 -188
  86. package/src/utils/hash.test.ts +0 -68
  87. package/src/utils/hash.ts +0 -26
  88. package/src/utils.test.ts +0 -350
  89. package/src/utils.ts +0 -129
  90. package/src/version.test.ts +0 -80
  91. package/src/version.ts +0 -25
  92. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +0 -469
  93. package/src/wirings/ai-agent/ai-agent-helpers.test.ts +0 -152
  94. package/src/wirings/ai-agent/ai-agent-helpers.ts +0 -76
  95. package/src/wirings/ai-agent/ai-agent-memory.ts +0 -310
  96. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +0 -115
  97. package/src/wirings/ai-agent/ai-agent-model-config.ts +0 -43
  98. package/src/wirings/ai-agent/ai-agent-prepare.ts +0 -636
  99. package/src/wirings/ai-agent/ai-agent-registry.test.ts +0 -37
  100. package/src/wirings/ai-agent/ai-agent-registry.ts +0 -82
  101. package/src/wirings/ai-agent/ai-agent-runner.test.ts +0 -319
  102. package/src/wirings/ai-agent/ai-agent-runner.ts +0 -773
  103. package/src/wirings/ai-agent/ai-agent-stream.test.ts +0 -859
  104. package/src/wirings/ai-agent/ai-agent-stream.ts +0 -1153
  105. package/src/wirings/ai-agent/ai-agent.types.ts +0 -382
  106. package/src/wirings/ai-agent/index.ts +0 -37
  107. package/src/wirings/channel/channel-common.ts +0 -90
  108. package/src/wirings/channel/channel-handler.ts +0 -250
  109. package/src/wirings/channel/channel-middleware-runner.ts +0 -120
  110. package/src/wirings/channel/channel-runner.ts +0 -166
  111. package/src/wirings/channel/channel-store.ts +0 -29
  112. package/src/wirings/channel/channel.types.ts +0 -172
  113. package/src/wirings/channel/define-channel-routes.ts +0 -25
  114. package/src/wirings/channel/eventhub-service.ts +0 -34
  115. package/src/wirings/channel/eventhub-store.ts +0 -13
  116. package/src/wirings/channel/index.ts +0 -24
  117. package/src/wirings/channel/local/index.ts +0 -3
  118. package/src/wirings/channel/local/local-channel-handler.ts +0 -81
  119. package/src/wirings/channel/local/local-channel-runner.test.ts +0 -215
  120. package/src/wirings/channel/local/local-channel-runner.ts +0 -210
  121. package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -95
  122. package/src/wirings/channel/local/local-eventhub-service.ts +0 -101
  123. package/src/wirings/channel/log-channels.ts +0 -20
  124. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -54
  125. package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -45
  126. package/src/wirings/channel/serverless/index.ts +0 -5
  127. package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -289
  128. package/src/wirings/cli/channel/cli-channel-runner.ts +0 -136
  129. package/src/wirings/cli/channel/index.ts +0 -1
  130. package/src/wirings/cli/cli-runner.test.ts +0 -403
  131. package/src/wirings/cli/cli-runner.ts +0 -529
  132. package/src/wirings/cli/cli.types.ts +0 -358
  133. package/src/wirings/cli/command-parser.test.ts +0 -445
  134. package/src/wirings/cli/command-parser.ts +0 -504
  135. package/src/wirings/cli/define-cli-commands.ts +0 -24
  136. package/src/wirings/cli/index.ts +0 -19
  137. package/src/wirings/gateway/gateway-runner.test.ts +0 -474
  138. package/src/wirings/gateway/gateway-runner.ts +0 -411
  139. package/src/wirings/gateway/gateway.types.ts +0 -149
  140. package/src/wirings/gateway/index.ts +0 -13
  141. package/src/wirings/http/http-routes.test.ts +0 -322
  142. package/src/wirings/http/http-routes.ts +0 -197
  143. package/src/wirings/http/http-runner.test.ts +0 -144
  144. package/src/wirings/http/http-runner.ts +0 -588
  145. package/src/wirings/http/http.types.ts +0 -369
  146. package/src/wirings/http/index.ts +0 -28
  147. package/src/wirings/http/log-http-routes.ts +0 -22
  148. package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -237
  149. package/src/wirings/http/pikku-fetch-http-request.ts +0 -170
  150. package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -82
  151. package/src/wirings/http/pikku-fetch-http-response.ts +0 -147
  152. package/src/wirings/http/routers/http-router.ts +0 -13
  153. package/src/wirings/http/routers/path-to-regex.test.ts +0 -319
  154. package/src/wirings/http/routers/path-to-regex.ts +0 -126
  155. package/src/wirings/http/web-request.test.ts +0 -236
  156. package/src/wirings/http/web-request.ts +0 -104
  157. package/src/wirings/mcp/index.ts +0 -27
  158. package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -389
  159. package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
  160. package/src/wirings/mcp/mcp-runner.ts +0 -323
  161. package/src/wirings/mcp/mcp.types.ts +0 -216
  162. package/src/wirings/node/index.ts +0 -2
  163. package/src/wirings/node/node.types.ts +0 -23
  164. package/src/wirings/oauth2/index.ts +0 -3
  165. package/src/wirings/oauth2/oauth2-client.test.ts +0 -929
  166. package/src/wirings/oauth2/oauth2-client.ts +0 -335
  167. package/src/wirings/oauth2/oauth2.types.ts +0 -69
  168. package/src/wirings/oauth2/wire-oauth2-credential.ts +0 -23
  169. package/src/wirings/queue/index.ts +0 -31
  170. package/src/wirings/queue/queue-runner.test.ts +0 -710
  171. package/src/wirings/queue/queue-runner.ts +0 -192
  172. package/src/wirings/queue/queue.types.ts +0 -189
  173. package/src/wirings/queue/register-queue-helper.ts +0 -60
  174. package/src/wirings/queue/validate-worker-config.test.ts +0 -108
  175. package/src/wirings/queue/validate-worker-config.ts +0 -116
  176. package/src/wirings/rpc/index.ts +0 -4
  177. package/src/wirings/rpc/rpc-runner.ts +0 -460
  178. package/src/wirings/rpc/rpc-types.ts +0 -56
  179. package/src/wirings/rpc/wire-addon.ts +0 -21
  180. package/src/wirings/scheduler/index.ts +0 -11
  181. package/src/wirings/scheduler/log-schedulers.ts +0 -20
  182. package/src/wirings/scheduler/scheduler-runner.test.ts +0 -660
  183. package/src/wirings/scheduler/scheduler-runner.ts +0 -133
  184. package/src/wirings/scheduler/scheduler.types.ts +0 -53
  185. package/src/wirings/secret/index.ts +0 -9
  186. package/src/wirings/secret/secret.types.ts +0 -32
  187. package/src/wirings/secret/validate-secret-definitions.test.ts +0 -140
  188. package/src/wirings/secret/validate-secret-definitions.ts +0 -82
  189. package/src/wirings/trigger/index.ts +0 -10
  190. package/src/wirings/trigger/pikku-trigger-service.ts +0 -112
  191. package/src/wirings/trigger/trigger-runner.test.ts +0 -79
  192. package/src/wirings/trigger/trigger-runner.ts +0 -135
  193. package/src/wirings/trigger/trigger.types.ts +0 -178
  194. package/src/wirings/variable/index.ts +0 -8
  195. package/src/wirings/variable/validate-variable-definitions.test.ts +0 -91
  196. package/src/wirings/variable/validate-variable-definitions.ts +0 -69
  197. package/src/wirings/variable/variable.types.ts +0 -22
  198. package/src/wirings/workflow/dsl/index.ts +0 -31
  199. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -320
  200. package/src/wirings/workflow/dsl/workflow-runner.ts +0 -28
  201. package/src/wirings/workflow/graph/graph-node.ts +0 -222
  202. package/src/wirings/workflow/graph/graph-runner.test.ts +0 -487
  203. package/src/wirings/workflow/graph/graph-runner.ts +0 -816
  204. package/src/wirings/workflow/graph/graph-validation.test.ts +0 -170
  205. package/src/wirings/workflow/graph/graph-validation.ts +0 -237
  206. package/src/wirings/workflow/graph/index.ts +0 -19
  207. package/src/wirings/workflow/graph/template.test.ts +0 -49
  208. package/src/wirings/workflow/graph/template.ts +0 -42
  209. package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -29
  210. package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -104
  211. package/src/wirings/workflow/index.ts +0 -82
  212. package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -200
  213. package/src/wirings/workflow/pikku-workflow-service.ts +0 -1176
  214. package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
  215. package/src/wirings/workflow/workflow-helpers.ts +0 -79
  216. package/src/wirings/workflow/workflow.types.ts +0 -274
  217. package/tsconfig.json +0 -14
  218. package/tsconfig.tsbuildinfo +0 -1
@@ -1,588 +0,0 @@
1
- import type {
2
- CoreHTTPFunctionWiring,
3
- RunHTTPWiringOptions,
4
- PikkuHTTP,
5
- PikkuHTTPRequest,
6
- PikkuHTTPResponse,
7
- HTTPWiringMeta,
8
- HTTPMethod,
9
- } from './http.types.js'
10
- import type {
11
- CorePikkuFunction,
12
- CorePikkuFunctionSessionless,
13
- CorePikkuPermission,
14
- CorePermissionGroup,
15
- } from '../../function/functions.types.js'
16
- import type {
17
- CoreUserSession,
18
- CorePikkuMiddleware,
19
- CorePikkuMiddlewareGroup,
20
- WireServices,
21
- PikkuWire,
22
- PikkuWiringTypes,
23
- } from '../../types/core.types.js'
24
- import { NotFoundError, PikkuMissingMetaError } from '../../errors/errors.js'
25
- import {
26
- closeWireServices,
27
- createWeakUID,
28
- isSerializable,
29
- } from '../../utils.js'
30
- import {
31
- getSingletonServices,
32
- getCreateWireServices,
33
- } from '../../pikku-state.js'
34
- import { PikkuSessionService } from '../../services/user-session-service.js'
35
- import { handleHTTPError } from '../../handle-error.js'
36
- import { pikkuState } from '../../pikku-state.js'
37
- import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
38
- import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
39
- import type { PikkuChannel } from '../channel/channel.types.js'
40
- import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
41
- import { applyWebResponse } from './web-request.js'
42
- import { httpRouter } from './routers/http-router.js'
43
- import { validateSchema } from '../../schema.js'
44
- import { runMiddleware } from '../../middleware-runner.js'
45
-
46
- /**
47
- * Extract headers from a PikkuHTTPRequest based on the schema keys
48
- */
49
- function extractHeadersFromRequest(
50
- request: PikkuHTTPRequest,
51
- headerKeys: string[]
52
- ): Record<string, string | string[] | undefined> {
53
- const headers: Record<string, string | string[] | undefined> = {}
54
- for (const headerName of headerKeys) {
55
- const value = request.header(headerName)
56
- if (value !== null) {
57
- headers[headerName] = value
58
- }
59
- }
60
- return headers
61
- }
62
-
63
- /**
64
- * Registers HTTP middleware for a specific route pattern.
65
- *
66
- * This function registers middleware at runtime that will be applied to
67
- * HTTP routes matching the specified pattern.
68
- *
69
- * For tree-shaking benefits, wrap in a factory function:
70
- * `export const x = () => addHTTPMiddleware('pattern', [...])`
71
- *
72
- * @template PikkuMiddleware The middleware type.
73
- * @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
74
- * @param {CorePikkuMiddlewareGroup} middleware - Array of middleware for this route pattern.
75
- *
76
- * @returns {CorePikkuMiddlewareGroup} The middleware array (for chaining/wrapping).
77
- *
78
- * @example
79
- * ```typescript
80
- * // Recommended: tree-shakeable
81
- * export const httpGlobal = () => addHTTPMiddleware('*', [
82
- * corsMiddleware,
83
- * loggingMiddleware
84
- * ])
85
- *
86
- * // Also works: no tree-shaking
87
- * export const apiMiddleware = addHTTPMiddleware('/api/*', [
88
- * authMiddleware
89
- * ])
90
- * ```
91
- */
92
- export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
93
- pattern: string,
94
- middleware: CorePikkuMiddlewareGroup,
95
- packageName: string | null = null
96
- ): CorePikkuMiddlewareGroup => {
97
- const httpGroups = pikkuState(packageName, 'middleware', 'httpGroup')
98
- httpGroups[pattern] = middleware
99
- return middleware
100
- }
101
-
102
- /**
103
- * Registers HTTP permissions for a specific route pattern.
104
- *
105
- * This function registers permissions at runtime that will be applied to
106
- * HTTP routes matching the specified pattern.
107
- *
108
- * For tree-shaking benefits, wrap in a factory function:
109
- * `export const x = () => addHTTPPermission('pattern', [...])`
110
- *
111
- * @template PikkuPermission The permission type.
112
- * @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
113
- * @param {CorePermissionGroup | CorePikkuPermission[]} permissions - Permissions for this route pattern.
114
- *
115
- * @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
116
- *
117
- * @example
118
- * ```typescript
119
- * // Recommended: tree-shakeable
120
- * export const httpGlobalPermissions = () => addHTTPPermission('*', [
121
- * authenticatedPermission,
122
- * rateLimitPermission
123
- * ])
124
- *
125
- * // Also works: no tree-shaking
126
- * export const apiPermissions = addHTTPPermission('/api/*', [
127
- * adminPermission
128
- * ])
129
- * ```
130
- */
131
- export const addHTTPPermission = <PikkuPermission extends CorePikkuPermission>(
132
- pattern: string,
133
- permissions: CorePermissionGroup | CorePikkuPermission[],
134
- packageName: string | null = null
135
- ): CorePermissionGroup | CorePikkuPermission[] => {
136
- const httpGroups = pikkuState(packageName, 'permissions', 'httpGroup')
137
- httpGroups[pattern] = permissions
138
- return permissions
139
- }
140
-
141
- /**
142
- * Adds a new route to the global HTTP route registry.
143
- *
144
- * The route configuration includes the HTTP method, route path, permissions,
145
- * middleware, and the handler function that implements the route's logic.
146
- *
147
- * @template In Expected input type.
148
- * @template Out Expected output type.
149
- * @template Route Route pattern as a string.
150
- * @template PikkuFunction Type for the route handler function.
151
- * @template PikkuFunctionSessionless Type for a sessionless handler.
152
- * @template PikkuPermissionGroup Type representing required permissions.
153
- * @template PikkuMiddleware Middleware type to be used with the route.
154
- * @param {CoreHTTPFunctionWiring<In, Out, Route, PikkuFunction, PikkuFunctionSessionless, PikkuPermission, PikkuMiddleware>} httpWiring - The HTTP wiring configuration object.
155
- */
156
- export const wireHTTP = <
157
- In,
158
- Out,
159
- Route extends string,
160
- PikkuFunction extends CorePikkuFunction<In, Out> = CorePikkuFunction<In, Out>,
161
- PikkuFunctionSessionless extends CorePikkuFunctionSessionless<
162
- In,
163
- Out
164
- > = CorePikkuFunctionSessionless<In, Out>,
165
- PikkuPermissionGroup extends
166
- CorePikkuPermission<In> = CorePikkuPermission<In>,
167
- PikkuMiddleware extends CorePikkuMiddleware = CorePikkuMiddleware,
168
- >(
169
- httpWiring: CoreHTTPFunctionWiring<
170
- In,
171
- Out,
172
- Route,
173
- PikkuFunction,
174
- PikkuFunctionSessionless,
175
- PikkuPermissionGroup,
176
- PikkuMiddleware
177
- >
178
- ) => {
179
- const httpMeta = pikkuState(null, 'http', 'meta')
180
- const routeMeta = httpMeta[httpWiring.method][httpWiring.route]
181
- if (!routeMeta) {
182
- throw new PikkuMissingMetaError(
183
- `Missing generated metadata for HTTP route '${httpWiring.method.toUpperCase()} ${httpWiring.route}'`
184
- )
185
- }
186
- if (httpWiring.func) {
187
- addFunction(routeMeta.pikkuFuncId, httpWiring.func)
188
- }
189
- const routes = pikkuState(null, 'http', 'routes')
190
- if (!routes.has(httpWiring.method)) {
191
- routes.set(httpWiring.method, new Map())
192
- }
193
- pikkuState(null, 'http', 'routes')
194
- .get(httpWiring.method)
195
- ?.set(httpWiring.route, httpWiring as any)
196
- }
197
-
198
- /**
199
- * Finds a matching route based on the HTTP method and URL path.
200
- *
201
- * Iterates over all registered routes, skipping those with a mismatched method.
202
- * When a route pattern matches the incoming request path, this function aggregates
203
- * any global middleware along with route-specific middleware and identifies any input schema.
204
- *
205
- * @param {string} requestType - The HTTP method (e.g., GET, POST).
206
- * @param {string} requestPath - The URL path of the incoming request.
207
- * @returns {Object | undefined} An object with matched route details or undefined if no match.
208
- */
209
- const getMatchingRoute = (requestType: string, requestPath: string) => {
210
- const matchedPath = httpRouter.match(
211
- requestType.toLowerCase() as HTTPMethod,
212
- requestPath
213
- )
214
-
215
- if (matchedPath) {
216
- const route = pikkuState(null, 'http', 'routes')
217
- .get(requestType.toLowerCase() as HTTPMethod)!
218
- .get(matchedPath.route)!
219
- const meta = pikkuState(null, 'http', 'meta')[
220
- requestType.toLowerCase() as PikkuWiringTypes
221
- ][route.route]
222
-
223
- return {
224
- matchedPath,
225
- params: matchedPath.params,
226
- route,
227
- permissions: route.permissions,
228
- meta: meta!,
229
- }
230
- }
231
- }
232
-
233
- /**
234
- * Combines the request and response objects into a single HTTP wire object.
235
- *
236
- * This utility function creates an object that holds both the HTTP request and response,
237
- * which simplifies passing these around through middleware and route execution.
238
- *
239
- * @param {PikkuHTTPRequest | undefined} request - The HTTP request object.
240
- * @param {PikkuHTTPResponse | undefined} response - The HTTP response object.
241
- * @returns {PikkuHTTP | undefined} The combined HTTP wire object or undefined if none provided.
242
- */
243
- export const createHTTPWire = (
244
- request: PikkuHTTPRequest | undefined,
245
- response: PikkuHTTPResponse | undefined
246
- ): PikkuHTTP | undefined => {
247
- let http: PikkuHTTP | undefined = undefined
248
-
249
- if (request || response) {
250
- http = {}
251
- if (request) {
252
- http.request = request
253
- }
254
- if (response) {
255
- http.response = response
256
- }
257
- }
258
-
259
- return http
260
- }
261
-
262
- /**
263
- * Validates the input data and executes the route handler
264
- *
265
- * This function performs these steps:
266
- * 1. Sets URL parameters on the request.
267
- * 2. Validates the user session if required.
268
- * 3. Creates session-specific services.
269
- * 4. Validates the incoming data against a schema.
270
- * 5. Optionally coerces query string values to arrays.
271
- * 6. Checks route-specific permissions.
272
- * 7. Executes the route handler.
273
- * 8. Sends the appropriate response.
274
- *
275
- * @param {Object} services - A collection of shared services and utilities.
276
- * @param {Object} matchedRoute - Contains route details, URL parameters, and optional schema.
277
- * @param {PikkuHTTP | undefined} http - The HTTP wire object.
278
- * @param {Object} options - Options for route execution (e.g., whether to coerce query strings to arrays).
279
- * @returns {Promise<any>} An object containing the route handler result and wire services (if any).
280
- * @throws Throws errors like MissingSessionError or ForbiddenError on validation failures.
281
- */
282
- const executeRoute = async (
283
- services: {
284
- singletonServices: any
285
- createWireServices?: any
286
- skipUserSession: boolean
287
- requestId: string
288
- },
289
- matchedRoute: {
290
- matchedPath: any
291
- params: any
292
- route: CoreHTTPFunctionWiring<any, any, any>
293
- meta: HTTPWiringMeta
294
- },
295
- http: PikkuHTTP,
296
- options: {
297
- coerceDataFromSchema: boolean
298
- }
299
- ) => {
300
- const userSession = new PikkuSessionService<CoreUserSession>()
301
- const { params, route, meta } = matchedRoute
302
- const { singletonServices, createWireServices, skipUserSession, requestId } =
303
- services
304
-
305
- // Attach URL parameters to the request object
306
- http?.request?.setParams(params)
307
-
308
- // Validate request headers if schema is defined
309
- if (meta.headersSchemaName && http.request && singletonServices.schema) {
310
- const headerKeys = singletonServices.schema.getSchemaKeys(
311
- meta.headersSchemaName
312
- )
313
- const rawHeaders = extractHeadersFromRequest(http.request, headerKeys)
314
- await validateSchema(
315
- singletonServices.logger,
316
- singletonServices.schema,
317
- meta.headersSchemaName,
318
- rawHeaders
319
- )
320
- }
321
-
322
- const requiresSession = route.auth !== false
323
- let wireServices: any
324
- let result: any
325
-
326
- singletonServices.logger.info(
327
- `Matched route: ${route.route} | method: ${route.method.toUpperCase()} | auth: ${requiresSession.toString()}`
328
- )
329
-
330
- // Ensure session is available when required
331
- if (skipUserSession && requiresSession) {
332
- throw new Error("Can't skip trying to get user session if auth is required")
333
- }
334
-
335
- let cachedData: unknown | undefined
336
- const data = async () => {
337
- if (cachedData === undefined) {
338
- cachedData = await http.request!.data()
339
- }
340
- return cachedData
341
- }
342
- let channel: PikkuChannel<unknown, unknown> | undefined
343
-
344
- if (matchedRoute.route.sse) {
345
- const response = http?.response
346
- if (!response) {
347
- throw new Error('SSE requires a valid HTTP response object')
348
- }
349
- if (!response.setMode) {
350
- throw new Error('Response object does not support SSE mode')
351
- }
352
- response.setMode('stream')
353
- response.header('Content-Type', 'text/event-stream')
354
- response.header('Cache-Control', 'no-cache')
355
- response.header('Connection', 'keep-alive')
356
- response.header('Transfer-Encoding', 'chunked')
357
- channel = {
358
- channelId: requestId,
359
- openingData: await data(),
360
- send: (data: any) => {
361
- response.arrayBuffer(isSerializable(data) ? JSON.stringify(data) : data)
362
- },
363
- sendBinary: (data) => {
364
- response.arrayBuffer(data)
365
- },
366
- close: () => {
367
- channel!.state = 'closed'
368
- response.close?.()
369
- },
370
- state: 'open',
371
- }
372
- }
373
-
374
- const wire: PikkuWire = {
375
- http,
376
- channel,
377
- session: userSession.get() as CoreUserSession | undefined,
378
- setSession: (s: any) => userSession.setInitial(s),
379
- getSession: () => userSession.get(),
380
- hasSessionChanged: () => userSession.sessionChanged,
381
- }
382
-
383
- result = await runPikkuFunc(
384
- 'http',
385
- `${meta.method}:${meta.route}`,
386
- meta.pikkuFuncId,
387
- {
388
- singletonServices,
389
- createWireServices,
390
- auth: route.auth !== false,
391
- data,
392
- inheritedMiddleware: meta.middleware,
393
- wireMiddleware: route.middleware,
394
- inheritedPermissions: meta.permissions,
395
- wirePermissions: route.permissions,
396
- coerceDataFromSchema: options.coerceDataFromSchema,
397
- tags: route.tags,
398
- wire,
399
- sessionService: userSession,
400
- packageName: meta.packageName,
401
- }
402
- )
403
- if (!matchedRoute.route.sse) {
404
- if (result instanceof Response) {
405
- await applyWebResponse(http!.response!, result)
406
- } else if (result === undefined || result === null) {
407
- http?.response?.status(204)
408
- } else if (route.returnsJSON === false) {
409
- http?.response?.arrayBuffer(result)
410
- } else {
411
- http?.response?.status(200)
412
- http?.response?.json(result)
413
- }
414
- }
415
-
416
- return wireServices ? { result, wireServices } : { result }
417
- }
418
-
419
- /**
420
- * Executes an HTTP route for a given Fetch API request.
421
- *
422
- * This function wraps the entire lifecycle of handling an HTTP request:
423
- * - Matching the request to a registered route.
424
- * - Validating input and session state.
425
- * - Running middleware and the route handler.
426
- * - Handling errors and forming the response.
427
- *
428
- * @template In Expected input data type.
429
- * @template Out Expected output data type.
430
- * @param {Request} request - The native Fetch API Request object.
431
- * @param {RunHTTPWiringOptions} params - Additional options including services and session management.
432
- * @returns {Promise<Response>} A promise that resolves to a Fetch API Response object.
433
- */
434
- export const fetch = async <In, Out>(
435
- request: Request,
436
- params: RunHTTPWiringOptions = {}
437
- ): Promise<Response> => {
438
- const pikkuResponse = new PikkuFetchHTTPResponse()
439
- await fetchData<In, Out>(request, pikkuResponse, params)
440
- return pikkuResponse.toResponse()
441
- }
442
-
443
- /**
444
- * Executes an HTTP route using a Pikku-specific request wrapper.
445
- *
446
- * This variant accepts either a native Request or a PikkuHTTPRequest object and returns
447
- * a PikkuFetchHTTPResponse for further manipulation if needed.
448
- *
449
- * @template In Expected input data type.
450
- * @template Out Expected output data type.
451
- * @param {Request | PikkuHTTPRequest} request - The request object.
452
- * @param {RunHTTPWiringOptions} params - Execution options including services and session configuration.
453
- * @returns {Promise<PikkuFetchHTTPResponse>} A promise that resolves to a PikkuFetchHTTPResponse object.
454
- */
455
- export const pikkuFetch = async <In, Out>(
456
- request: Request | PikkuHTTPRequest,
457
- params: RunHTTPWiringOptions = {}
458
- ): Promise<PikkuFetchHTTPResponse> => {
459
- const pikkuResponse = new PikkuFetchHTTPResponse()
460
- await fetchData<In, Out>(request, pikkuResponse, params)
461
- return pikkuResponse
462
- }
463
-
464
- /**
465
- * Core function to process an HTTP request through route matching, validation,
466
- * middleware execution, error handling, and session service cleanup.
467
- *
468
- * This function does the following:
469
- * - Wraps the incoming request and response into an HTTP wire object.
470
- * - Determines the correct route based on HTTP method and path.
471
- * - Executes middleware and the route handler.
472
- * - Catches and handles errors, optionally bubbling them if configured.
473
- * - Cleans up any wire services created during processing.
474
- *
475
- * @template In Expected input data type.
476
- * @template Out Expected output data type.
477
- * @param {Request | PikkuHTTPRequest} request - The incoming HTTP request.
478
- * @param {PikkuHTTPResponse} response - The response object to be populated.
479
- * @param {RunHTTPWiringOptions} options - Options such as singleton services, session handling, and error configuration.
480
- * @returns {Promise<Out | void>} The output from the route handler or void if an error occurred.
481
- */
482
- export const fetchData = async <In, Out>(
483
- request: Request | PikkuHTTPRequest,
484
- response: PikkuHTTPResponse,
485
- {
486
- skipUserSession = false,
487
- respondWith404 = true,
488
- logWarningsForStatusCodes = [],
489
- coerceDataFromSchema = true,
490
- bubbleErrors = false,
491
- exposeErrors = false,
492
- generateRequestId,
493
- }: RunHTTPWiringOptions = {}
494
- ): Promise<Out | void> => {
495
- const singletonServices = getSingletonServices()
496
- const createWireServices = getCreateWireServices()
497
- let wireServices: WireServices<typeof singletonServices> | undefined
498
- let result: Out
499
-
500
- // Combine the request and response into one wire object
501
- const pikkuRequest =
502
- request instanceof Request ? new PikkuFetchHTTPRequest(request) : request
503
- let requestId: string | null = null
504
- try {
505
- requestId = pikkuRequest.header('x-request-id')
506
- } catch {}
507
- requestId = requestId || generateRequestId?.() || createWeakUID()
508
- const http = createHTTPWire(pikkuRequest, response)
509
- const apiType = http!.request!.method()
510
- const apiRoute = http!.request!.path()
511
-
512
- // Locate the matching route based on the HTTP method and path
513
- const matchedRoute = getMatchingRoute(apiType, apiRoute)
514
- try {
515
- if (!matchedRoute) {
516
- if (apiType.toLowerCase() === 'options') {
517
- const httpGroups = pikkuState(null, 'middleware', 'httpGroup')
518
- const globalMiddleware = httpGroups['*']
519
- const wire: PikkuWire = { http: http! }
520
- if (globalMiddleware) {
521
- await runMiddleware(
522
- singletonServices,
523
- wire,
524
- globalMiddleware as CorePikkuMiddleware[]
525
- )
526
- }
527
- response.status(204).json(undefined as any)
528
- return
529
- }
530
- singletonServices.logger.info({
531
- message: 'Route not found',
532
- apiRoute,
533
- apiType,
534
- })
535
- throw new NotFoundError(`Route not found: ${apiRoute}`)
536
- }
537
-
538
- // Execute the matched route along with its middleware and session management
539
- ;({ result, wireServices } = await executeRoute(
540
- {
541
- singletonServices,
542
- createWireServices,
543
- skipUserSession,
544
- requestId,
545
- },
546
- matchedRoute,
547
- http!,
548
- { coerceDataFromSchema }
549
- ))
550
-
551
- return result
552
- } catch (e: any) {
553
- if (matchedRoute?.route.sse) {
554
- // For SSE routes, send error through the stream since the response is already in stream mode
555
- singletonServices.logger.error(e instanceof Error ? e.message : e)
556
- try {
557
- response.arrayBuffer(
558
- JSON.stringify({
559
- type: 'error',
560
- errorText: e instanceof Error ? e.message : String(e),
561
- })
562
- )
563
- response.arrayBuffer('[DONE]')
564
- } catch (streamErr: any) {
565
- singletonServices.logger.error(
566
- `SSE error while sending error payload: ${streamErr instanceof Error ? streamErr.message : String(streamErr)}`
567
- )
568
- }
569
- response.close?.()
570
- } else {
571
- handleHTTPError(
572
- e,
573
- http,
574
- requestId,
575
- singletonServices.logger,
576
- logWarningsForStatusCodes,
577
- respondWith404,
578
- bubbleErrors,
579
- exposeErrors
580
- )
581
- }
582
- } finally {
583
- // Clean up any session-specific services created during processing
584
- if (wireServices) {
585
- await closeWireServices(singletonServices.logger, wireServices)
586
- }
587
- }
588
- }