@pikku/core 0.12.4 → 0.12.5
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.
- package/CHANGELOG.md +11 -1
- package/dist/dev/hot-reload.d.ts +10 -0
- package/dist/dev/hot-reload.js +158 -0
- package/dist/middleware-runner.d.ts +1 -0
- package/dist/middleware-runner.js +5 -0
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.js +5 -0
- package/dist/services/in-memory-workflow-service.js +7 -11
- package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
- package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
- package/dist/wirings/channel/channel-middleware-runner.js +5 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +7 -8
- package/package.json +3 -2
- package/src/crypto-utils.test.ts +214 -0
- package/src/crypto-utils.ts +213 -0
- package/src/dev/hot-reload.test.ts +484 -0
- package/src/dev/hot-reload.ts +212 -0
- package/src/errors/error-handler.ts +62 -0
- package/src/errors/error.test.ts +195 -0
- package/src/errors/errors.ts +374 -0
- package/src/errors/index.ts +2 -0
- package/src/factory-functions.test.ts +109 -0
- package/src/function/function-runner.test.ts +536 -0
- package/src/function/function-runner.ts +365 -0
- package/src/function/functions.types.ts +304 -0
- package/src/function/index.ts +5 -0
- package/src/handle-error.test.ts +424 -0
- package/src/handle-error.ts +69 -0
- package/src/index.ts +118 -0
- package/src/internal.ts +6 -0
- package/src/middleware/auth-apikey.test.ts +363 -0
- package/src/middleware/auth-apikey.ts +47 -0
- package/src/middleware/auth-bearer.test.ts +450 -0
- package/src/middleware/auth-bearer.ts +72 -0
- package/src/middleware/auth-cookie.test.ts +528 -0
- package/src/middleware/auth-cookie.ts +80 -0
- package/src/middleware/cors.test.ts +424 -0
- package/src/middleware/cors.ts +104 -0
- package/src/middleware/index.ts +5 -0
- package/src/middleware/remote-auth.test.ts +488 -0
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware/timeout.ts +15 -0
- package/src/middleware-runner.test.ts +418 -0
- package/src/middleware-runner.ts +240 -0
- package/src/permissions.test.ts +434 -0
- package/src/permissions.ts +327 -0
- package/src/pikku-request.ts +23 -0
- package/src/pikku-response.ts +5 -0
- package/src/pikku-state.test.ts +224 -0
- package/src/pikku-state.ts +216 -0
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.test.ts +249 -0
- package/src/schema.ts +151 -0
- package/src/services/ai-agent-runner-service.ts +41 -0
- package/src/services/ai-run-state-service.ts +20 -0
- package/src/services/ai-storage-service.ts +39 -0
- package/src/services/content-service.ts +76 -0
- package/src/services/deployment-service.ts +22 -0
- package/src/services/gateway-service.ts +20 -0
- package/src/services/gopass-secrets.ts +98 -0
- package/src/services/in-memory-ai-run-state-service.ts +73 -0
- package/src/services/in-memory-trigger-service.ts +64 -0
- package/src/services/in-memory-workflow-service.test.ts +351 -0
- package/src/services/in-memory-workflow-service.ts +502 -0
- package/src/services/index.ts +56 -0
- package/src/services/jwt-service.ts +30 -0
- package/src/services/local-content.ts +120 -0
- package/src/services/local-gateway-service.ts +62 -0
- package/src/services/local-secrets.test.ts +80 -0
- package/src/services/local-secrets.ts +94 -0
- package/src/services/local-variables.test.ts +61 -0
- package/src/services/local-variables.ts +39 -0
- package/src/services/logger-console.test.ts +118 -0
- package/src/services/logger-console.ts +98 -0
- package/src/services/logger.ts +57 -0
- package/src/services/scheduler-service.ts +86 -0
- package/src/services/schema-service.ts +33 -0
- package/src/services/scoped-secret-service.test.ts +74 -0
- package/src/services/scoped-secret-service.ts +41 -0
- package/src/services/secret-service.ts +38 -0
- package/src/services/trigger-service.ts +17 -0
- package/src/services/typed-secret-service.test.ts +93 -0
- package/src/services/typed-secret-service.ts +70 -0
- package/src/services/typed-variables-service.test.ts +73 -0
- package/src/services/typed-variables-service.ts +81 -0
- package/src/services/user-session-service.test.ts +113 -0
- package/src/services/user-session-service.ts +86 -0
- package/src/services/variables-service.ts +11 -0
- package/src/services/workflow-service.ts +95 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/service-tests.ts +873 -0
- package/src/time-utils.test.ts +56 -0
- package/src/time-utils.ts +107 -0
- package/src/types/core.types.ts +478 -0
- package/src/types/state.types.ts +188 -0
- package/src/utils/hash.test.ts +68 -0
- package/src/utils/hash.ts +26 -0
- package/src/utils.test.ts +350 -0
- package/src/utils.ts +129 -0
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
- package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
- package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
- package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
- package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
- package/src/wirings/ai-agent/index.ts +37 -0
- package/src/wirings/channel/channel-common.ts +90 -0
- package/src/wirings/channel/channel-handler.ts +250 -0
- package/src/wirings/channel/channel-middleware-runner.ts +126 -0
- package/src/wirings/channel/channel-runner.ts +166 -0
- package/src/wirings/channel/channel-store.ts +29 -0
- package/src/wirings/channel/channel.types.ts +172 -0
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/eventhub-service.ts +34 -0
- package/src/wirings/channel/eventhub-store.ts +13 -0
- package/src/wirings/channel/index.ts +24 -0
- package/src/wirings/channel/local/index.ts +3 -0
- package/src/wirings/channel/local/local-channel-handler.ts +81 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
- package/src/wirings/channel/local/local-channel-runner.ts +210 -0
- package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
- package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
- package/src/wirings/channel/log-channels.ts +20 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
- package/src/wirings/channel/serverless/index.ts +5 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/cli/cli-runner.test.ts +403 -0
- package/src/wirings/cli/cli-runner.ts +529 -0
- package/src/wirings/cli/cli.types.ts +358 -0
- package/src/wirings/cli/command-parser.test.ts +445 -0
- package/src/wirings/cli/command-parser.ts +504 -0
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +19 -0
- package/src/wirings/gateway/gateway-runner.test.ts +474 -0
- package/src/wirings/gateway/gateway-runner.ts +411 -0
- package/src/wirings/gateway/gateway.types.ts +149 -0
- package/src/wirings/gateway/index.ts +13 -0
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +197 -0
- package/src/wirings/http/http-runner.test.ts +144 -0
- package/src/wirings/http/http-runner.ts +588 -0
- package/src/wirings/http/http.types.ts +369 -0
- package/src/wirings/http/index.ts +28 -0
- package/src/wirings/http/log-http-routes.ts +22 -0
- package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
- package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
- package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
- package/src/wirings/http/routers/http-router.ts +13 -0
- package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
- package/src/wirings/http/routers/path-to-regex.ts +126 -0
- package/src/wirings/http/web-request.test.ts +236 -0
- package/src/wirings/http/web-request.ts +104 -0
- package/src/wirings/mcp/index.ts +27 -0
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
- package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
- package/src/wirings/mcp/mcp-runner.ts +323 -0
- package/src/wirings/mcp/mcp.types.ts +216 -0
- package/src/wirings/node/index.ts +2 -0
- package/src/wirings/node/node.types.ts +23 -0
- package/src/wirings/oauth2/index.ts +3 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
- package/src/wirings/oauth2/oauth2-client.ts +335 -0
- package/src/wirings/oauth2/oauth2.types.ts +69 -0
- package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
- package/src/wirings/queue/index.ts +31 -0
- package/src/wirings/queue/queue-runner.test.ts +710 -0
- package/src/wirings/queue/queue-runner.ts +192 -0
- package/src/wirings/queue/queue.types.ts +189 -0
- package/src/wirings/queue/register-queue-helper.ts +60 -0
- package/src/wirings/queue/validate-worker-config.test.ts +108 -0
- package/src/wirings/queue/validate-worker-config.ts +116 -0
- package/src/wirings/rpc/index.ts +4 -0
- package/src/wirings/rpc/rpc-runner.ts +460 -0
- package/src/wirings/rpc/rpc-types.ts +56 -0
- package/src/wirings/rpc/wire-addon.ts +21 -0
- package/src/wirings/scheduler/index.ts +11 -0
- package/src/wirings/scheduler/log-schedulers.ts +20 -0
- package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
- package/src/wirings/scheduler/scheduler-runner.ts +133 -0
- package/src/wirings/scheduler/scheduler.types.ts +53 -0
- package/src/wirings/secret/index.ts +9 -0
- package/src/wirings/secret/secret.types.ts +32 -0
- package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
- package/src/wirings/secret/validate-secret-definitions.ts +82 -0
- package/src/wirings/trigger/index.ts +10 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
- package/src/wirings/trigger/trigger-runner.test.ts +79 -0
- package/src/wirings/trigger/trigger-runner.ts +135 -0
- package/src/wirings/trigger/trigger.types.ts +178 -0
- package/src/wirings/variable/index.ts +8 -0
- package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
- package/src/wirings/variable/validate-variable-definitions.ts +69 -0
- package/src/wirings/variable/variable.types.ts +22 -0
- package/src/wirings/workflow/dsl/index.ts +31 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
- package/src/wirings/workflow/graph/graph-node.ts +222 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
- package/src/wirings/workflow/graph/graph-runner.ts +816 -0
- package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
- package/src/wirings/workflow/graph/graph-validation.ts +237 -0
- package/src/wirings/workflow/graph/index.ts +19 -0
- package/src/wirings/workflow/graph/template.test.ts +49 -0
- package/src/wirings/workflow/graph/template.ts +42 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
- package/src/wirings/workflow/index.ts +82 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +1179 -0
- package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
- package/src/wirings/workflow/workflow-helpers.ts +79 -0
- package/src/wirings/workflow/workflow.types.ts +274 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
- 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
|
+
}
|