@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,365 @@
1
+ import { runMiddleware, combineMiddleware } from '../middleware-runner.js'
2
+ import {
3
+ combineChannelMiddleware,
4
+ wrapChannelWithMiddleware,
5
+ } from '../wirings/channel/channel-middleware-runner.js'
6
+ import { runPermissions } from '../permissions.js'
7
+ import { pikkuState } from '../pikku-state.js'
8
+ import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js'
9
+ import type {
10
+ CoreServices,
11
+ CoreUserSession,
12
+ CorePikkuMiddleware,
13
+ PikkuWiringTypes,
14
+ PikkuWire,
15
+ MiddlewareMetadata,
16
+ PermissionMetadata,
17
+ CoreSingletonServices,
18
+ CreateWireServices,
19
+ CoreConfig,
20
+ } from '../types/core.types.js'
21
+ import type { CorePikkuChannelMiddleware } from '../wirings/channel/channel.types.js'
22
+ import type {
23
+ CorePermissionGroup,
24
+ CorePikkuFunctionConfig,
25
+ CorePikkuPermission,
26
+ } from './functions.types.js'
27
+ import { parseVersionedId } from '../version.js'
28
+ import type { SessionService } from '../services/user-session-service.js'
29
+ import { createFunctionSessionWireProps } from '../services/user-session-service.js'
30
+ import { ForbiddenError, ReadonlySessionError } from '../errors/errors.js'
31
+ import { rpcService } from '../wirings/rpc/rpc-runner.js'
32
+ import { closeWireServices } from '../utils.js'
33
+
34
+ /**
35
+ * Get or create singleton services for an addon package.
36
+ * Services are cached in pikkuState to avoid recreation on each call.
37
+ *
38
+ * @param packageName - The addon package name
39
+ * @param parentServices - The parent/caller's singleton services (used as base)
40
+ * @returns The package's singleton services
41
+ */
42
+ const getOrCreatePackageSingletonServices = async (
43
+ packageName: string,
44
+ parentServices: CoreSingletonServices
45
+ ): Promise<CoreSingletonServices> => {
46
+ // Check if we already have cached singleton services for this package
47
+ const cachedServices = pikkuState(packageName, 'package', 'singletonServices')
48
+ if (cachedServices) {
49
+ return cachedServices
50
+ }
51
+
52
+ // Get the package's service factories
53
+ const factories = pikkuState(packageName, 'package', 'factories')
54
+ if (!factories || !factories.createSingletonServices) {
55
+ // No factories registered, use parent services
56
+ return parentServices
57
+ }
58
+
59
+ // Create config for the package (use parent config if no factory)
60
+ let config: CoreConfig = parentServices.config
61
+ if (factories.createConfig) {
62
+ config = await factories.createConfig(parentServices.variables)
63
+ }
64
+
65
+ // Create singleton services for the package, passing parent services as existing
66
+ const packageServices = await factories.createSingletonServices(
67
+ config,
68
+ parentServices
69
+ )
70
+
71
+ // Cache the services
72
+ pikkuState(packageName, 'package', 'singletonServices', packageServices)
73
+
74
+ return packageServices
75
+ }
76
+
77
+ export const addFunction = (
78
+ funcName: string,
79
+ funcConfig: CorePikkuFunctionConfig<any, any>,
80
+ packageName: string | null = null
81
+ ) => {
82
+ pikkuState(packageName, 'function', 'functions').set(funcName, funcConfig)
83
+ }
84
+
85
+ export const getFunctionNames = (
86
+ packageName: string | null = null
87
+ ): string[] => {
88
+ const functionsMeta = pikkuState(packageName, 'function', 'meta')
89
+ return Object.keys(functionsMeta)
90
+ }
91
+
92
+ export const getAllFunctionNames = (): string[] => {
93
+ const functions: string[] = []
94
+
95
+ const mainFunctionsMeta = pikkuState(null, 'function', 'meta')
96
+ functions.push(...Object.keys(mainFunctionsMeta))
97
+
98
+ const addons = pikkuState(null, 'addons', 'packages')
99
+ for (const [namespace, config] of addons) {
100
+ const packageFunctionsMeta = pikkuState(config.package, 'function', 'meta')
101
+ for (const funcName of Object.keys(packageFunctionsMeta)) {
102
+ functions.push(`${namespace}:${funcName}`)
103
+ }
104
+ }
105
+
106
+ return functions
107
+ }
108
+
109
+ export const runPikkuFuncDirectly = async <In, Out>(
110
+ funcName: string,
111
+ allServices: CoreServices,
112
+ wire: PikkuWire,
113
+ data: In,
114
+ userSession?: SessionService<CoreUserSession>,
115
+ packageName: string | null = null
116
+ ) => {
117
+ const funcConfig = pikkuState(packageName, 'function', 'functions').get(
118
+ funcName
119
+ )
120
+ if (!funcConfig) {
121
+ throw new Error(`Function not found: ${funcName}`)
122
+ }
123
+ const wireWithSession = {
124
+ ...wire,
125
+ ...(userSession && createFunctionSessionWireProps(userSession)),
126
+ }
127
+ return (await funcConfig.func(allServices, data, wireWithSession)) as Out
128
+ }
129
+
130
+ export const runPikkuFunc = async <In = any, Out = any>(
131
+ wireType: PikkuWiringTypes,
132
+ wireId: string,
133
+ funcName: string,
134
+ {
135
+ singletonServices,
136
+ createWireServices,
137
+ data,
138
+ auth: wiringAuth,
139
+ inheritedMiddleware,
140
+ wireMiddleware,
141
+ inheritedChannelMiddleware,
142
+ wireChannelMiddleware,
143
+ inheritedPermissions,
144
+ wirePermissions,
145
+ coerceDataFromSchema,
146
+ tags = [],
147
+ wire,
148
+ sessionService,
149
+ packageName = null,
150
+ }: {
151
+ singletonServices: CoreSingletonServices
152
+ createWireServices?: CreateWireServices
153
+ data: () => Promise<In> | In
154
+ auth?: boolean
155
+ inheritedMiddleware?: MiddlewareMetadata[]
156
+ wireMiddleware?: CorePikkuMiddleware[]
157
+ inheritedChannelMiddleware?: MiddlewareMetadata[]
158
+ wireChannelMiddleware?: CorePikkuChannelMiddleware[]
159
+ inheritedPermissions?: PermissionMetadata[]
160
+ wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
161
+ coerceDataFromSchema?: boolean
162
+ tags?: string[]
163
+ wire: PikkuWire
164
+ sessionService?: SessionService<CoreUserSession>
165
+ packageName?: string | null
166
+ }
167
+ ): Promise<Out> => {
168
+ wire.wireType ??= wireType
169
+ wire.wireId ??= wireId
170
+
171
+ const funcMap = pikkuState(packageName, 'function', 'functions')
172
+ let funcConfig = funcMap.get(funcName)
173
+ const allMeta = pikkuState(packageName, 'function', 'meta')
174
+ let funcMeta = allMeta[funcName]
175
+
176
+ if (!funcConfig || !funcMeta) {
177
+ const { baseName, version } = parseVersionedId(funcName)
178
+ if (version !== null) {
179
+ funcConfig = funcConfig || funcMap.get(baseName)
180
+ funcMeta = funcMeta || allMeta[baseName]
181
+ if (funcConfig && funcMeta) {
182
+ singletonServices.logger.warn(
183
+ `Version '${funcName}' not registered, resolved to '${baseName}'`
184
+ )
185
+ }
186
+ }
187
+ }
188
+
189
+ if (!funcConfig) {
190
+ throw new Error(`Function not found: ${funcName}`)
191
+ }
192
+ if (!funcMeta) {
193
+ throw new Error(`Function meta not found: ${funcName}`)
194
+ }
195
+
196
+ // For addon packages, get or create their singleton services
197
+ const resolvedSingletonServices = packageName
198
+ ? await getOrCreatePackageSingletonServices(packageName, singletonServices)
199
+ : singletonServices
200
+
201
+ // Get the package's createWireServices if available
202
+ let resolvedCreateWireServices = createWireServices
203
+ if (packageName) {
204
+ const factories = pikkuState(packageName, 'package', 'factories')
205
+ if (factories?.createWireServices) {
206
+ resolvedCreateWireServices = factories.createWireServices
207
+ }
208
+ }
209
+
210
+ const allChannelMiddleware = combineChannelMiddleware(wireType, wireId, {
211
+ wireInheritedChannelMiddleware: inheritedChannelMiddleware,
212
+ wireChannelMiddleware,
213
+ packageName,
214
+ })
215
+
216
+ const resolvedWire =
217
+ allChannelMiddleware.length > 0 && wire.channel
218
+ ? wrapChannelWithMiddleware(
219
+ wire,
220
+ resolvedSingletonServices,
221
+ allChannelMiddleware
222
+ )
223
+ : wire
224
+
225
+ // Convert tags to PermissionMetadata and merge with inheritedPermissions
226
+ let mergedInheritedPermissions: PermissionMetadata[]
227
+ if (tags && tags.length > 0) {
228
+ mergedInheritedPermissions = [
229
+ ...(inheritedPermissions || []),
230
+ ...tags.map((tag) => ({ type: 'tag' as const, tag })),
231
+ ]
232
+ } else {
233
+ mergedInheritedPermissions = inheritedPermissions || []
234
+ }
235
+
236
+ // Helper function to run permissions and execute the function
237
+ const executeFunction = async () => {
238
+ if (sessionService) {
239
+ resolvedWire.session = sessionService.freezeInitial()
240
+ resolvedWire.setSession = (s: any) => sessionService.set(s)
241
+ resolvedWire.clearSession = () => sessionService.clear()
242
+ resolvedWire.getSession = () => sessionService.get()
243
+ resolvedWire.hasSessionChanged = () => sessionService.sessionChanged
244
+ }
245
+
246
+ const session = resolvedWire.session
247
+
248
+ if (funcMeta.sessionless) {
249
+ if (wiringAuth === true || funcConfig.auth === true) {
250
+ if (!session) {
251
+ throw new ForbiddenError('Authentication required')
252
+ }
253
+ }
254
+ } else if (funcMeta.sessionless === false) {
255
+ if (wiringAuth === false || funcConfig.auth === false) {
256
+ resolvedSingletonServices.logger.warn(
257
+ `Function '${funcName}' requires a session but auth was explicitly disabled — use pikkuSessionlessFunc instead.`
258
+ )
259
+ }
260
+ if (!session) {
261
+ throw new ForbiddenError('Authentication required')
262
+ }
263
+ } else {
264
+ // TODO: Remove after a couple of releases — backward compat for
265
+ // generated metadata that doesn't include the `sessionless` field yet.
266
+ if (wiringAuth === true || funcConfig.auth === true) {
267
+ if (!session) {
268
+ throw new ForbiddenError('Authentication required')
269
+ }
270
+ }
271
+ }
272
+
273
+ if ((session as any)?.readonly && !funcMeta.readonly) {
274
+ throw new ReadonlySessionError()
275
+ }
276
+
277
+ // Evaluate the data from the lazy function
278
+ const actualData = await data()
279
+
280
+ // Validate and coerce data if schema is defined
281
+ const inputSchemaName = funcMeta.inputSchemaName
282
+ if (inputSchemaName) {
283
+ // Coerce (top level) data types before validation (e.g. string→array, string→date)
284
+ if (coerceDataFromSchema) {
285
+ coerceTopLevelDataFromSchema(inputSchemaName, actualData, packageName)
286
+ }
287
+ // Validate request data against the defined schema, if any
288
+ await validateSchema(
289
+ resolvedSingletonServices.logger,
290
+ resolvedSingletonServices.schema,
291
+ inputSchemaName,
292
+ actualData,
293
+ packageName
294
+ )
295
+ }
296
+
297
+ if (
298
+ mergedInheritedPermissions.length > 0 ||
299
+ wirePermissions ||
300
+ funcMeta.permissions ||
301
+ funcConfig.permissions
302
+ ) {
303
+ await runPermissions(wireType, wireId, {
304
+ wireInheritedPermissions: mergedInheritedPermissions,
305
+ wirePermissions: wirePermissions,
306
+ funcInheritedPermissions: funcMeta.permissions,
307
+ funcPermissions: funcConfig.permissions,
308
+ services: resolvedSingletonServices,
309
+ wire: resolvedWire as any,
310
+ data: actualData,
311
+ packageName,
312
+ })
313
+ }
314
+
315
+ const wireServices = await resolvedCreateWireServices?.(
316
+ resolvedSingletonServices,
317
+ resolvedWire
318
+ )
319
+ try {
320
+ const services =
321
+ wireServices && Object.keys(wireServices).length > 0
322
+ ? { ...resolvedSingletonServices, ...wireServices }
323
+ : resolvedSingletonServices
324
+ Object.defineProperty(resolvedWire, 'rpc', {
325
+ get() {
326
+ const rpc = rpcService.getContextRPCService(services, resolvedWire, {
327
+ sessionService,
328
+ })
329
+ Object.defineProperty(resolvedWire, 'rpc', {
330
+ value: rpc,
331
+ writable: true,
332
+ configurable: true,
333
+ })
334
+ return rpc
335
+ },
336
+ configurable: true,
337
+ enumerable: true,
338
+ })
339
+ return await funcConfig.func(services, actualData, resolvedWire)
340
+ } finally {
341
+ if (wireServices && Object.keys(wireServices).length > 0) {
342
+ await closeWireServices(resolvedSingletonServices.logger, wireServices)
343
+ }
344
+ }
345
+ }
346
+
347
+ const allMiddleware = combineMiddleware(wireType, wireId, {
348
+ wireInheritedMiddleware: inheritedMiddleware,
349
+ wireMiddleware,
350
+ funcInheritedMiddleware: funcMeta.middleware,
351
+ funcMiddleware: funcConfig.middleware,
352
+ packageName,
353
+ })
354
+
355
+ if (allMiddleware.length > 0) {
356
+ return (await runMiddleware<CorePikkuMiddleware>(
357
+ resolvedSingletonServices,
358
+ resolvedWire,
359
+ allMiddleware,
360
+ executeFunction
361
+ )) as Out
362
+ }
363
+
364
+ return (await executeFunction()) as Out
365
+ }
@@ -0,0 +1,304 @@
1
+ import type {
2
+ CoreServices,
3
+ CoreSingletonServices,
4
+ CoreUserSession,
5
+ CorePikkuMiddleware,
6
+ PikkuWire,
7
+ PickRequired,
8
+ } from '../types/core.types.js'
9
+ import type { StandardSchemaV1 } from '@standard-schema/spec'
10
+ import type { PikkuError } from '../errors/error-handler.js'
11
+ import type { CoreNodeConfig } from '../wirings/node/node.types.js'
12
+
13
+ /**
14
+ * @deprecated Use StandardSchemaV1 from @standard-schema/spec instead.
15
+ * This alias exists only for backward compatibility with generated code.
16
+ */
17
+ export type ZodLike<T = any> = StandardSchemaV1<T, T>
18
+
19
+ /**
20
+ * Represents a core API function that performs an operation using core services and a user session.
21
+ *
22
+ * @template In - The input type.
23
+ * @template Out - The output type.
24
+ * @template Services - The services type, defaults to `CoreServices`.
25
+ * @template Wire - The wire type, defaults to `PikkuWire<In, Out>`.
26
+ */
27
+ export type CorePikkuFunction<
28
+ In,
29
+ Out,
30
+ Services extends CoreSingletonServices = CoreServices,
31
+ Session extends CoreUserSession = CoreUserSession,
32
+ Wire extends PikkuWire<In, Out> = PikkuWire<In, Out, true, Session>,
33
+ > = (
34
+ services: Services,
35
+ data: In,
36
+ wire: Wire
37
+ ) => Wire['channel'] extends null ? Promise<Out> : Promise<Out> | Promise<void>
38
+
39
+ /**
40
+ * Represents a core API function that can be used without a session.
41
+ *
42
+ * @template In - The input type.
43
+ * @template Out - The output type.
44
+ * @template Services - The services type, defaults to `CoreServices`.
45
+ * @template Wire - The wire type, defaults to `PikkuWire<In, Out>`.
46
+ */
47
+ export type CorePikkuFunctionSessionless<
48
+ In,
49
+ Out,
50
+ Services extends CoreSingletonServices = CoreServices,
51
+ Session extends CoreUserSession = CoreUserSession,
52
+ Wire extends PikkuWire<In, Out, false, Session, any, any, any> = PikkuWire<
53
+ In,
54
+ Out,
55
+ false,
56
+ Session,
57
+ any,
58
+ any,
59
+ any
60
+ >,
61
+ > = (
62
+ services: Services,
63
+ data: In,
64
+ wire: Wire
65
+ ) => Wire['channel'] extends null ? Promise<Out> : Promise<Out> | Promise<void>
66
+
67
+ /**
68
+ * Represents a function that checks permissions for a given operation.
69
+ *
70
+ * @template In - The input type.
71
+ * @template Services - The services type, defaults to `CoreServices`.
72
+ * @template Session - The session type, defaults to `CoreUserSession`.
73
+ */
74
+ export type CorePikkuPermission<
75
+ In = any,
76
+ Services extends CoreSingletonServices = CoreServices,
77
+ Wire extends PikkuWire<
78
+ In,
79
+ never,
80
+ false,
81
+ CoreUserSession,
82
+ any,
83
+ never,
84
+ never
85
+ > = PikkuWire<In, never, false, CoreUserSession, never, never, never>,
86
+ > = (services: Services, data: In, wire: Wire) => Promise<boolean>
87
+
88
+ /**
89
+ * Configuration object for creating a permission with metadata
90
+ *
91
+ * @template In - The input type.
92
+ * @template Services - The services type, defaults to `CoreServices`.
93
+ * @template Session - The session type, defaults to `CoreUserSession`.
94
+ */
95
+ export type CorePikkuPermissionConfig<
96
+ In = any,
97
+ Services extends CoreSingletonServices = CoreServices,
98
+ Wire extends PikkuWire<In, never, false, CoreUserSession> = PikkuWire<
99
+ In,
100
+ never,
101
+ false,
102
+ CoreUserSession
103
+ >,
104
+ > = {
105
+ /** The permission function */
106
+ func: CorePikkuPermission<In, Services, Wire>
107
+ /** Optional human-readable name for the permission */
108
+ name?: string
109
+ /** Optional description of what the permission checks */
110
+ description?: string
111
+ }
112
+
113
+ /**
114
+ * Factory function for creating permissions with tree-shaking support
115
+ * Supports both direct function and configuration object syntax
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * // Direct function syntax
120
+ * export const adminPermission = pikkuPermission(
121
+ * async ({ logger }, _data, { getSession }) => {
122
+ * const currentSession = await getSession()
123
+ * return currentSession?.role === 'admin'
124
+ * }
125
+ * )
126
+ *
127
+ * // Configuration object syntax with metadata
128
+ * export const adminPermission = pikkuPermission({
129
+ * name: 'Admin Permission',
130
+ * description: 'Checks if user has admin role',
131
+ * func: async ({ logger }, _data, { getSession }) => {
132
+ * const currentSession = await getSession()
133
+ * return currentSession?.role === 'admin'
134
+ * }
135
+ * })
136
+ * ```
137
+ */
138
+ export const pikkuPermission = <
139
+ In = any,
140
+ Services extends CoreSingletonServices = CoreServices,
141
+ Wire extends PickRequired<
142
+ PikkuWire<In, never, false, CoreUserSession>,
143
+ 'session'
144
+ > = PickRequired<PikkuWire<In, never, false, CoreUserSession>, 'session'>,
145
+ >(
146
+ permission:
147
+ | CorePikkuPermission<In, Services, Wire>
148
+ | CorePikkuPermissionConfig<In, Services, Wire>
149
+ ): CorePikkuPermission<In, Services, Wire> => {
150
+ return typeof permission === 'function' ? permission : permission.func
151
+ }
152
+
153
+ /**
154
+ * A factory function that takes input and returns a permission
155
+ * Used when permissions need configuration/input parameters
156
+ *
157
+ * @template In - The input type for the factory.
158
+ * @template Services - The services type, defaults to `CoreServices`.
159
+ * @template Session - The session type, defaults to `CoreUserSession`.
160
+ */
161
+ export type CorePikkuPermissionFactory<
162
+ In = any,
163
+ Services extends CoreSingletonServices = CoreServices,
164
+ Wire extends PikkuWire<In, never, false, CoreUserSession> = PikkuWire<
165
+ In,
166
+ never,
167
+ false,
168
+ CoreUserSession
169
+ >,
170
+ > = (input: In) => CorePikkuPermission<any, Services, Wire>
171
+
172
+ /**
173
+ * Factory function for creating permission factories
174
+ * Use this when your permission needs configuration/input parameters
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * export const requireRole = pikkuPermissionFactory<{ role: string }>(({
179
+ * role
180
+ * }) => {
181
+ * return pikkuPermission(async ({ logger }, data, { getSession }) => {
182
+ * const currentSession = await getSession()
183
+ * if (!currentSession || currentSession.role !== role) {
184
+ * logger.warn(`Permission denied: required role '${role}'`)
185
+ * return false
186
+ * }
187
+ * return true
188
+ * })
189
+ * })
190
+ * ```
191
+ */
192
+ export const pikkuPermissionFactory = <In = any>(
193
+ factory: CorePikkuPermissionFactory<In>
194
+ ): CorePikkuPermissionFactory<In> => {
195
+ return factory
196
+ }
197
+
198
+ /**
199
+ * A function that generates a human-readable description of a pending approval action.
200
+ * Used by AI agents to show meaningful approval prompts instead of raw tool arguments.
201
+ *
202
+ * @template In - The input type (same as the function it describes).
203
+ * @template Services - The services type, defaults to `CoreSingletonServices`.
204
+ */
205
+ export type CorePikkuApprovalDescription<
206
+ In = any,
207
+ Services extends CoreSingletonServices = CoreSingletonServices,
208
+ > = (services: Services, data: In) => Promise<string>
209
+
210
+ /**
211
+ * Factory function for creating approval description functions with tree-shaking support.
212
+ *
213
+ * @example
214
+ * ```typescript
215
+ * export const deleteTodoApproval = pikkuApprovalDescription(
216
+ * async ({ todoStore }, { id }) => {
217
+ * const todo = await todoStore.get(id)
218
+ * return `Delete todo: "${todo.title}"`
219
+ * }
220
+ * )
221
+ * ```
222
+ */
223
+ export const pikkuApprovalDescription = <
224
+ In = any,
225
+ Services extends CoreSingletonServices = CoreSingletonServices,
226
+ >(
227
+ fn: CorePikkuApprovalDescription<In, Services>
228
+ ): CorePikkuApprovalDescription<In, Services> => {
229
+ return fn
230
+ }
231
+
232
+ export type CorePikkuAuth<
233
+ Services extends CoreSingletonServices = CoreServices,
234
+ Session extends CoreUserSession = CoreUserSession,
235
+ > = (services: Services, session: Session) => Promise<boolean> | boolean
236
+
237
+ export type CorePikkuAuthConfig<
238
+ Services extends CoreSingletonServices = CoreServices,
239
+ Session extends CoreUserSession = CoreUserSession,
240
+ > = {
241
+ func: CorePikkuAuth<Services, Session>
242
+ name?: string
243
+ description?: string
244
+ }
245
+
246
+ export const pikkuAuth = <
247
+ Services extends CoreSingletonServices = CoreServices,
248
+ Session extends CoreUserSession = CoreUserSession,
249
+ >(
250
+ auth:
251
+ | CorePikkuAuth<Services, Session>
252
+ | CorePikkuAuthConfig<Services, Session>
253
+ ): CorePikkuPermission<any, Services, any> => {
254
+ const fn = typeof auth === 'function' ? auth : auth.func
255
+ const wrapper = async (services: Services, _data: any, wire: any) => {
256
+ const session = wire.session
257
+ if (!session) return false
258
+ return fn(services, session as Session)
259
+ }
260
+ return wrapper as any
261
+ }
262
+
263
+ export type CorePermissionGroup<PikkuPermission = CorePikkuPermission<any>> =
264
+ | Record<string, PikkuPermission | PikkuPermission[]>
265
+ | undefined
266
+
267
+ export type CorePikkuFunctionConfig<
268
+ PikkuFunction extends
269
+ | CorePikkuFunction<any, any, any, any, any>
270
+ | CorePikkuFunctionSessionless<any, any, any, any, any>,
271
+ PikkuPermission extends CorePikkuPermission<
272
+ any,
273
+ any,
274
+ any
275
+ > = CorePikkuPermission<any>,
276
+ PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<
277
+ any,
278
+ any
279
+ >,
280
+ InputSchema extends StandardSchemaV1 | undefined = undefined,
281
+ OutputSchema extends StandardSchemaV1 | undefined = undefined,
282
+ > = {
283
+ /** Optional human-readable title for the function */
284
+ title?: string
285
+ /** Optional description of what the function does */
286
+ description?: string
287
+ override?: string
288
+ version?: number
289
+ tags?: string[]
290
+ expose?: boolean
291
+ remote?: boolean
292
+ mcp?: boolean
293
+ readonly?: boolean
294
+ approvalRequired?: boolean
295
+ approvalDescription?: any
296
+ func: PikkuFunction
297
+ auth?: boolean
298
+ permissions?: CorePermissionGroup<PikkuPermission>
299
+ middleware?: PikkuMiddleware[]
300
+ input?: InputSchema
301
+ output?: OutputSchema
302
+ node?: CoreNodeConfig
303
+ errors?: Array<typeof PikkuError>
304
+ }
@@ -0,0 +1,5 @@
1
+ export { addFunction, getAllFunctionNames } from './function-runner.js'
2
+ export type {
3
+ CorePikkuFunction,
4
+ CorePikkuFunctionSessionless,
5
+ } from './functions.types.js'