@pikku/core 0.11.2 → 0.12.1
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 +57 -2
- package/dist/crypto-utils.d.ts +2 -0
- package/dist/crypto-utils.js +78 -0
- package/dist/errors/errors.d.ts +14 -0
- package/dist/errors/errors.js +26 -0
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/function/function-runner.d.ts +10 -4
- package/dist/function/function-runner.js +126 -45
- package/dist/function/functions.types.d.ts +32 -87
- package/dist/function/functions.types.js +16 -46
- package/dist/function/index.d.ts +2 -2
- package/dist/function/index.js +1 -2
- package/dist/handle-error.d.ts +2 -2
- package/dist/handle-error.js +3 -12
- package/dist/index.d.ts +30 -26
- package/dist/index.js +19 -29
- package/dist/internal.d.ts +3 -0
- package/dist/internal.js +2 -0
- package/dist/middleware/auth-apikey.d.ts +2 -2
- package/dist/middleware/auth-apikey.js +3 -3
- package/dist/middleware/auth-bearer.d.ts +3 -3
- package/dist/middleware/auth-bearer.js +4 -7
- package/dist/middleware/auth-cookie.d.ts +4 -4
- package/dist/middleware/auth-cookie.js +7 -9
- package/dist/middleware/cors.d.ts +46 -0
- package/dist/middleware/cors.js +81 -0
- package/dist/middleware/index.d.ts +5 -3
- package/dist/middleware/index.js +5 -3
- package/dist/middleware/remote-auth.d.ts +5 -0
- package/dist/middleware/remote-auth.js +55 -0
- package/dist/middleware/timeout.d.ts +2 -2
- package/dist/middleware-runner.d.ts +1 -1
- package/dist/middleware-runner.js +8 -10
- package/dist/permissions.d.ts +2 -2
- package/dist/permissions.js +11 -15
- package/dist/pikku-state.d.ts +10 -8
- package/dist/pikku-state.js +48 -25
- package/dist/schema.d.ts +4 -4
- package/dist/schema.js +20 -15
- package/dist/services/ai-agent-runner-service.d.ts +43 -0
- package/dist/services/ai-agent-runner-service.js +1 -0
- package/dist/services/ai-run-state-service.d.ts +13 -0
- package/dist/services/ai-run-state-service.js +1 -0
- package/dist/services/ai-storage-service.d.ts +18 -0
- package/dist/services/ai-storage-service.js +1 -0
- package/dist/services/content-service.d.ts +6 -0
- package/dist/services/deployment-service.d.ts +19 -0
- package/dist/services/deployment-service.js +1 -0
- package/dist/services/gopass-secrets.d.ts +47 -0
- package/dist/services/gopass-secrets.js +97 -0
- package/dist/services/in-memory-ai-run-state-service.d.ts +15 -0
- package/dist/services/in-memory-ai-run-state-service.js +44 -0
- package/dist/services/in-memory-trigger-service.d.ts +22 -0
- package/dist/services/in-memory-trigger-service.js +48 -0
- package/dist/services/in-memory-workflow-service.d.ts +59 -0
- package/dist/services/in-memory-workflow-service.js +287 -0
- package/dist/services/index.d.ts +26 -11
- package/dist/services/index.js +11 -13
- package/dist/services/local-content.d.ts +2 -1
- package/dist/services/local-content.js +6 -1
- package/dist/services/local-secrets.d.ts +25 -2
- package/dist/services/local-secrets.js +45 -0
- package/dist/services/local-variables.d.ts +6 -1
- package/dist/services/local-variables.js +18 -0
- package/dist/services/logger-console.d.ts +2 -1
- package/dist/services/scheduler-service.d.ts +9 -1
- package/dist/services/scheduler-service.js +8 -0
- package/dist/services/schema-service.d.ts +6 -0
- package/dist/services/scoped-secret-service.d.ts +16 -0
- package/dist/services/scoped-secret-service.js +35 -0
- package/dist/services/secret-service.d.ts +25 -4
- package/dist/services/trigger-service.d.ts +16 -0
- package/dist/services/trigger-service.js +1 -0
- package/dist/services/typed-secret-service.d.ts +30 -0
- package/dist/services/typed-secret-service.js +40 -0
- package/dist/services/typed-variables-service.d.ts +27 -0
- package/dist/services/typed-variables-service.js +46 -0
- package/dist/services/user-session-service.d.ts +15 -2
- package/dist/services/user-session-service.js +17 -0
- package/dist/services/variables-service.d.ts +5 -0
- package/dist/services/workflow-service.d.ts +9 -5
- package/dist/types/core.types.d.ts +67 -25
- package/dist/types/core.types.js +8 -1
- package/dist/types/state.types.d.ts +53 -59
- package/dist/utils.d.ts +7 -6
- package/dist/utils.js +27 -12
- package/dist/version.d.ts +6 -0
- package/dist/version.js +19 -0
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +5 -0
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +146 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +28 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +40 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +59 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +348 -0
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
- package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +195 -0
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +12 -0
- package/dist/wirings/ai-agent/ai-agent-stream.js +662 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +313 -0
- package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
- package/dist/wirings/ai-agent/index.d.ts +7 -0
- package/dist/wirings/ai-agent/index.js +6 -0
- package/dist/wirings/channel/channel-common.d.ts +8 -5
- package/dist/wirings/channel/channel-common.js +15 -8
- package/dist/wirings/channel/channel-handler.d.ts +3 -3
- package/dist/wirings/channel/channel-handler.js +15 -6
- package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
- package/dist/wirings/channel/channel-middleware-runner.js +73 -0
- package/dist/wirings/channel/channel-runner.d.ts +5 -5
- package/dist/wirings/channel/channel-runner.js +10 -9
- package/dist/wirings/channel/channel-store.d.ts +1 -1
- package/dist/wirings/channel/channel.types.d.ts +9 -7
- package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
- package/dist/wirings/channel/define-channel-routes.js +23 -0
- package/dist/wirings/channel/index.d.ts +10 -8
- package/dist/wirings/channel/index.js +7 -8
- package/dist/wirings/channel/local/index.d.ts +3 -3
- package/dist/wirings/channel/local/index.js +3 -3
- package/dist/wirings/channel/local/local-channel-runner.d.ts +7 -2
- package/dist/wirings/channel/local/local-channel-runner.js +22 -9
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +2 -2
- package/dist/wirings/channel/log-channels.d.ts +1 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +1 -1
- package/dist/wirings/channel/serverless/index.d.ts +1 -1
- package/dist/wirings/channel/serverless/index.js +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +4 -4
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +40 -23
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +1 -1
- package/dist/wirings/cli/channel/index.d.ts +1 -1
- package/dist/wirings/cli/channel/index.js +1 -1
- package/dist/wirings/cli/cli-runner.d.ts +3 -3
- package/dist/wirings/cli/cli-runner.js +12 -8
- package/dist/wirings/cli/cli.types.d.ts +9 -9
- package/dist/wirings/cli/command-parser.d.ts +1 -1
- package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
- package/dist/wirings/cli/define-cli-commands.js +20 -0
- package/dist/wirings/cli/index.d.ts +2 -3
- package/dist/wirings/cli/index.js +1 -3
- package/dist/wirings/http/http-routes.d.ts +69 -0
- package/dist/wirings/http/http-routes.js +139 -0
- package/dist/wirings/http/http-runner.d.ts +9 -9
- package/dist/wirings/http/http-runner.js +79 -34
- package/dist/wirings/http/http.types.d.ts +77 -41
- package/dist/wirings/http/index.d.ts +5 -4
- package/dist/wirings/http/index.js +4 -3
- package/dist/wirings/http/log-http-routes.d.ts +1 -1
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -1
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +2 -2
- package/dist/wirings/http/pikku-fetch-http-response.js +6 -1
- package/dist/wirings/http/routers/http-router.d.ts +1 -1
- package/dist/wirings/http/routers/path-to-regex.d.ts +2 -2
- package/dist/wirings/mcp/index.d.ts +4 -3
- package/dist/wirings/mcp/index.js +3 -3
- package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -6
- package/dist/wirings/mcp/mcp-runner.js +28 -40
- package/dist/wirings/mcp/mcp.types.d.ts +5 -5
- package/dist/wirings/node/index.d.ts +2 -0
- package/dist/wirings/node/index.js +1 -0
- package/dist/wirings/node/node.types.d.ts +20 -0
- package/dist/wirings/node/node.types.js +1 -0
- package/dist/wirings/oauth2/index.d.ts +3 -0
- package/dist/wirings/oauth2/index.js +2 -0
- package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
- package/dist/wirings/oauth2/oauth2-client.js +267 -0
- package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
- package/dist/wirings/oauth2/oauth2.types.js +4 -0
- package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
- package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
- package/dist/wirings/queue/index.d.ts +1 -1
- package/dist/wirings/queue/queue-runner.d.ts +1 -5
- package/dist/wirings/queue/queue-runner.js +11 -8
- package/dist/wirings/queue/queue.types.d.ts +5 -5
- package/dist/wirings/queue/register-queue-helper.d.ts +2 -2
- package/dist/wirings/queue/register-queue-helper.js +1 -1
- package/dist/wirings/queue/validate-worker-config.d.ts +1 -1
- package/dist/wirings/rpc/index.d.ts +3 -1
- package/dist/wirings/rpc/index.js +2 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +58 -8
- package/dist/wirings/rpc/rpc-runner.js +177 -31
- package/dist/wirings/rpc/rpc-types.d.ts +20 -4
- package/dist/wirings/rpc/wire-addon.d.ts +10 -0
- package/dist/wirings/rpc/wire-addon.js +9 -0
- package/dist/wirings/scheduler/index.d.ts +2 -2
- package/dist/wirings/scheduler/index.js +1 -2
- package/dist/wirings/scheduler/log-schedulers.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -5
- package/dist/wirings/scheduler/scheduler-runner.js +11 -14
- package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
- package/dist/wirings/secret/index.d.ts +3 -0
- package/dist/wirings/secret/index.js +2 -0
- package/dist/wirings/secret/secret.types.d.ts +27 -0
- package/dist/wirings/secret/secret.types.js +1 -0
- package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
- package/dist/wirings/secret/validate-secret-definitions.js +55 -0
- package/dist/wirings/trigger/index.d.ts +3 -2
- package/dist/wirings/trigger/index.js +2 -2
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +22 -0
- package/dist/wirings/trigger/pikku-trigger-service.js +70 -0
- package/dist/wirings/trigger/trigger-runner.d.ts +18 -9
- package/dist/wirings/trigger/trigger-runner.js +48 -19
- package/dist/wirings/trigger/trigger.types.d.ts +99 -11
- package/dist/wirings/trigger/trigger.types.js +40 -1
- package/dist/wirings/variable/index.d.ts +3 -0
- package/dist/wirings/variable/index.js +2 -0
- package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
- package/dist/wirings/variable/validate-variable-definitions.js +45 -0
- package/dist/wirings/variable/variable.types.d.ts +18 -0
- package/dist/wirings/variable/variable.types.js +1 -0
- package/dist/wirings/workflow/dsl/index.d.ts +1 -1
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +7 -1
- package/dist/wirings/workflow/dsl/workflow-runner.js +3 -2
- package/dist/wirings/workflow/graph/graph-node.d.ts +1 -6
- package/dist/wirings/workflow/graph/graph-runner.d.ts +5 -31
- package/dist/wirings/workflow/graph/graph-runner.js +384 -296
- package/dist/wirings/workflow/graph/index.d.ts +4 -3
- package/dist/wirings/workflow/graph/index.js +3 -3
- package/dist/wirings/workflow/graph/template.d.ts +15 -0
- package/dist/wirings/workflow/graph/template.js +27 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -13
- package/dist/wirings/workflow/index.d.ts +6 -6
- package/dist/wirings/workflow/index.js +6 -4
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +29 -19
- package/dist/wirings/workflow/pikku-workflow-service.js +217 -82
- package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
- package/dist/wirings/workflow/workflow-helpers.js +38 -0
- package/dist/wirings/workflow/workflow.types.d.ts +42 -95
- package/package.json +14 -15
- package/run-tests.sh +4 -1
- package/src/crypto-utils.test.ts +116 -0
- package/src/crypto-utils.ts +99 -0
- package/src/errors/error.test.ts +143 -2
- package/src/errors/errors.ts +27 -0
- package/src/errors/index.ts +1 -1
- package/src/factory-functions.test.ts +42 -2
- package/src/function/function-runner.test.ts +6 -13
- package/src/function/function-runner.ts +161 -53
- package/src/function/functions.types.ts +63 -132
- package/src/function/index.ts +5 -2
- package/src/handle-error.test.ts +361 -0
- package/src/handle-error.ts +6 -14
- package/src/index.ts +97 -30
- package/src/internal.ts +6 -0
- package/src/middleware/auth-apikey.test.ts +16 -13
- package/src/middleware/auth-apikey.ts +3 -3
- package/src/middleware/auth-bearer.test.ts +18 -15
- package/src/middleware/auth-bearer.ts +27 -32
- package/src/middleware/auth-cookie.test.ts +17 -14
- package/src/middleware/auth-cookie.ts +9 -13
- package/src/middleware/cors.test.ts +424 -0
- package/src/middleware/cors.ts +104 -0
- package/src/middleware/index.ts +5 -3
- package/src/middleware/remote-auth.test.ts +488 -0
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware-runner.test.ts +123 -1
- package/src/middleware-runner.ts +13 -13
- package/src/permissions.test.ts +83 -9
- package/src/permissions.ts +17 -19
- package/src/pikku-state.test.ts +224 -0
- package/src/pikku-state.ts +59 -35
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.test.ts +198 -6
- package/src/schema.ts +28 -21
- 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 +7 -0
- package/src/services/deployment-service.ts +22 -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 +395 -0
- package/src/services/index.ts +50 -14
- package/src/services/local-content.ts +9 -3
- package/src/services/local-secrets.test.ts +80 -0
- package/src/services/local-secrets.ts +53 -2
- package/src/services/local-variables.test.ts +61 -0
- package/src/services/local-variables.ts +23 -1
- package/src/services/logger-console.test.ts +118 -0
- package/src/services/logger-console.ts +2 -1
- package/src/services/scheduler-service.ts +11 -1
- package/src/services/schema-service.ts +7 -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 +25 -4
- 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 +25 -2
- package/src/services/variables-service.ts +5 -0
- package/src/services/workflow-service.ts +23 -13
- package/src/time-utils.test.ts +1 -1
- package/src/types/core.types.ts +95 -24
- package/src/types/state.types.ts +56 -73
- package/src/utils.test.ts +350 -0
- package/src/utils.ts +33 -18
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +363 -0
- package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +214 -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 +300 -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 +504 -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 +317 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +251 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +652 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +1049 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +328 -0
- package/src/wirings/ai-agent/index.ts +39 -0
- package/src/wirings/channel/channel-common.ts +33 -20
- package/src/wirings/channel/channel-handler.ts +26 -9
- package/src/wirings/channel/channel-middleware-runner.ts +119 -0
- package/src/wirings/channel/channel-runner.ts +15 -16
- package/src/wirings/channel/channel-store.ts +1 -1
- package/src/wirings/channel/channel.types.ts +21 -9
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/index.ts +23 -8
- package/src/wirings/channel/local/index.ts +3 -3
- package/src/wirings/channel/local/local-channel-runner.test.ts +57 -7
- package/src/wirings/channel/local/local-channel-runner.ts +40 -19
- package/src/wirings/channel/local/local-eventhub-service.test.ts +2 -2
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -2
- package/src/wirings/channel/log-channels.ts +1 -1
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +1 -1
- package/src/wirings/channel/serverless/index.ts +5 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +57 -36
- package/src/wirings/cli/channel/cli-channel-runner.ts +1 -1
- package/src/wirings/cli/channel/index.ts +1 -1
- package/src/wirings/cli/cli-runner.test.ts +10 -10
- package/src/wirings/cli/cli-runner.ts +28 -14
- package/src/wirings/cli/cli.types.ts +14 -14
- package/src/wirings/cli/command-parser.test.ts +9 -9
- package/src/wirings/cli/command-parser.ts +1 -1
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +10 -4
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +198 -0
- package/src/wirings/http/http-runner.test.ts +12 -20
- package/src/wirings/http/http-runner.ts +101 -51
- package/src/wirings/http/http.types.ts +119 -48
- package/src/wirings/http/index.ts +16 -4
- package/src/wirings/http/log-http-routes.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +12 -5
- package/src/wirings/http/routers/http-router.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.test.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.ts +4 -3
- package/src/wirings/mcp/index.ts +27 -3
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
- package/src/wirings/mcp/mcp-endpoint-registry.ts +5 -1
- package/src/wirings/mcp/mcp-runner.ts +48 -65
- package/src/wirings/mcp/mcp.types.ts +5 -5
- 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 +13 -2
- package/src/wirings/queue/queue-runner.test.ts +142 -73
- package/src/wirings/queue/queue-runner.ts +18 -14
- package/src/wirings/queue/queue.types.ts +5 -5
- package/src/wirings/queue/register-queue-helper.ts +3 -5
- package/src/wirings/queue/validate-worker-config.test.ts +108 -0
- package/src/wirings/queue/validate-worker-config.ts +4 -1
- package/src/wirings/rpc/index.ts +3 -1
- package/src/wirings/rpc/rpc-runner.ts +254 -41
- package/src/wirings/rpc/rpc-types.ts +28 -4
- package/src/wirings/rpc/wire-addon.ts +20 -0
- package/src/wirings/scheduler/index.ts +6 -4
- package/src/wirings/scheduler/log-schedulers.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.test.ts +78 -45
- package/src/wirings/scheduler/scheduler-runner.ts +20 -27
- package/src/wirings/scheduler/scheduler.types.ts +2 -2
- 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 -2
- 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 +80 -41
- package/src/wirings/trigger/trigger.types.ts +144 -22
- 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 +1 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +9 -1
- package/src/wirings/workflow/dsl/workflow-runner.ts +4 -3
- package/src/wirings/workflow/graph/graph-node.ts +1 -6
- package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
- package/src/wirings/workflow/graph/graph-runner.ts +496 -374
- package/src/wirings/workflow/graph/index.ts +14 -3
- 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 +0 -22
- package/src/wirings/workflow/index.ts +21 -10
- package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +308 -105
- 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 +47 -92
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/forge-node/forge-node.types.d.ts +0 -120
- package/dist/wirings/forge-node/forge-node.types.js +0 -38
- package/dist/wirings/forge-node/index.d.ts +0 -1
- package/dist/wirings/forge-node/index.js +0 -1
- package/dist/wirings/workflow/wire-workflow.d.ts +0 -42
- package/dist/wirings/workflow/wire-workflow.js +0 -53
- package/dist/wirings/workflow/workflow-utils.d.ts +0 -23
- package/dist/wirings/workflow/workflow-utils.js +0 -66
- package/src/wirings/forge-node/forge-node.types.ts +0 -135
- package/src/wirings/forge-node/index.ts +0 -1
- package/src/wirings/workflow/wire-workflow.ts +0 -94
- package/src/wirings/workflow/workflow-utils.ts +0 -120
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { SerializedError, CommonWireMeta } from '../../types/core.types.js';
|
|
2
|
-
import { CorePikkuFunctionConfig } from '../../function/functions.types.js';
|
|
1
|
+
import type { SerializedError, CommonWireMeta } from '../../types/core.types.js';
|
|
2
|
+
import type { CorePikkuFunctionConfig } from '../../function/functions.types.js';
|
|
3
3
|
export type { WorkflowService } from '../../services/workflow-service.js';
|
|
4
|
-
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './dsl/workflow-dsl.types.js';
|
|
4
|
+
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './dsl/workflow-dsl.types.js';
|
|
5
5
|
import type { WorkflowStepMeta } from './dsl/workflow-dsl.types.js';
|
|
6
|
+
export interface WorkflowRunWire {
|
|
7
|
+
type: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
parentRunId?: string;
|
|
10
|
+
}
|
|
6
11
|
export interface WorkflowServiceConfig {
|
|
7
12
|
retries: number;
|
|
8
13
|
retryDelay: number;
|
|
@@ -10,99 +15,14 @@ export interface WorkflowServiceConfig {
|
|
|
10
15
|
stepWorkerQueueName: string;
|
|
11
16
|
sleeperRPCName: string;
|
|
12
17
|
}
|
|
13
|
-
/**
|
|
14
|
-
* HTTP wire configuration for workflows
|
|
15
|
-
*/
|
|
16
|
-
export interface WorkflowHTTPWire {
|
|
17
|
-
route: string;
|
|
18
|
-
method: 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
19
|
-
startNode: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Channel wire configuration for workflows
|
|
23
|
-
*/
|
|
24
|
-
export interface WorkflowChannelWire {
|
|
25
|
-
name: string;
|
|
26
|
-
onConnect?: string;
|
|
27
|
-
onDisconnect?: string;
|
|
28
|
-
onMessage?: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Queue wire configuration for workflows
|
|
32
|
-
*/
|
|
33
|
-
export interface WorkflowQueueWire {
|
|
34
|
-
name: string;
|
|
35
|
-
startNode: string;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* CLI wire configuration for workflows
|
|
39
|
-
*/
|
|
40
|
-
export interface WorkflowCliWire {
|
|
41
|
-
command: string;
|
|
42
|
-
startNode: string;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* MCP wire configurations for workflows
|
|
46
|
-
*/
|
|
47
|
-
export interface WorkflowMcpWires {
|
|
48
|
-
tool?: Array<{
|
|
49
|
-
name: string;
|
|
50
|
-
startNode: string;
|
|
51
|
-
}>;
|
|
52
|
-
prompt?: Array<{
|
|
53
|
-
name: string;
|
|
54
|
-
startNode: string;
|
|
55
|
-
}>;
|
|
56
|
-
resource?: Array<{
|
|
57
|
-
uri: string;
|
|
58
|
-
startNode: string;
|
|
59
|
-
}>;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Schedule wire configuration for workflows
|
|
63
|
-
*/
|
|
64
|
-
export interface WorkflowScheduleWire {
|
|
65
|
-
cron?: string;
|
|
66
|
-
interval?: string;
|
|
67
|
-
startNode: string;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Trigger wire configuration for workflows
|
|
71
|
-
*/
|
|
72
|
-
export interface WorkflowTriggerWire {
|
|
73
|
-
name: string;
|
|
74
|
-
startNode: string;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Wire configuration for workflows
|
|
78
|
-
* Defines how a workflow can be triggered
|
|
79
|
-
*/
|
|
80
|
-
export interface WorkflowWires {
|
|
81
|
-
/** API entry point - node ID for startWorkflow() calls */
|
|
82
|
-
api?: string;
|
|
83
|
-
/** HTTP triggers */
|
|
84
|
-
http?: WorkflowHTTPWire[];
|
|
85
|
-
/** Channel triggers */
|
|
86
|
-
channel?: WorkflowChannelWire[];
|
|
87
|
-
/** Queue triggers */
|
|
88
|
-
queue?: WorkflowQueueWire[];
|
|
89
|
-
/** CLI triggers */
|
|
90
|
-
cli?: WorkflowCliWire[];
|
|
91
|
-
/** MCP triggers (tool, prompt, resource) */
|
|
92
|
-
mcp?: WorkflowMcpWires;
|
|
93
|
-
/** Schedule triggers */
|
|
94
|
-
schedule?: WorkflowScheduleWire[];
|
|
95
|
-
/** Named trigger wires */
|
|
96
|
-
trigger?: WorkflowTriggerWire[];
|
|
97
|
-
}
|
|
98
18
|
/**
|
|
99
19
|
* Workflow run status
|
|
100
20
|
*/
|
|
101
|
-
export type WorkflowStatus = 'running' | 'completed' | 'failed' | 'cancelled';
|
|
21
|
+
export type WorkflowStatus = 'running' | 'suspended' | 'completed' | 'failed' | 'cancelled';
|
|
102
22
|
/**
|
|
103
23
|
* Workflow step status
|
|
104
24
|
*/
|
|
105
|
-
export type StepStatus = 'pending' | 'running' | 'scheduled' | 'succeeded' | 'failed';
|
|
25
|
+
export type StepStatus = 'pending' | 'running' | 'scheduled' | 'succeeded' | 'failed' | 'suspended';
|
|
106
26
|
/**
|
|
107
27
|
* Workflow run representation
|
|
108
28
|
*/
|
|
@@ -121,6 +41,10 @@ export interface WorkflowRun {
|
|
|
121
41
|
error?: SerializedError;
|
|
122
42
|
/** If true, workflow executes inline without queues */
|
|
123
43
|
inline?: boolean;
|
|
44
|
+
/** Graph hash of the workflow definition at run creation time */
|
|
45
|
+
graphHash?: string;
|
|
46
|
+
/** Wire origin info (how this run was started) */
|
|
47
|
+
wire: WorkflowRunWire;
|
|
124
48
|
/** Creation timestamp */
|
|
125
49
|
createdAt: Date;
|
|
126
50
|
/** Last update timestamp */
|
|
@@ -157,6 +81,29 @@ export interface StepState {
|
|
|
157
81
|
/** Timestamp when step failed */
|
|
158
82
|
failedAt?: Date;
|
|
159
83
|
}
|
|
84
|
+
export interface WorkflowRunService {
|
|
85
|
+
listRuns(options?: {
|
|
86
|
+
workflowName?: string;
|
|
87
|
+
status?: string;
|
|
88
|
+
limit?: number;
|
|
89
|
+
offset?: number;
|
|
90
|
+
}): Promise<WorkflowRun[]>;
|
|
91
|
+
getRun(id: string): Promise<WorkflowRun | null>;
|
|
92
|
+
getRunSteps(runId: string): Promise<Array<StepState & {
|
|
93
|
+
stepName: string;
|
|
94
|
+
rpcName?: string;
|
|
95
|
+
data?: any;
|
|
96
|
+
}>>;
|
|
97
|
+
getRunHistory(runId: string): Promise<Array<StepState & {
|
|
98
|
+
stepName: string;
|
|
99
|
+
}>>;
|
|
100
|
+
getDistinctWorkflowNames(): Promise<string[]>;
|
|
101
|
+
getWorkflowVersion(name: string, graphHash: string): Promise<{
|
|
102
|
+
graph: any;
|
|
103
|
+
source: string;
|
|
104
|
+
} | null>;
|
|
105
|
+
deleteRun(id: string): Promise<boolean>;
|
|
106
|
+
}
|
|
160
107
|
/**
|
|
161
108
|
* Core workflow definition
|
|
162
109
|
*/
|
|
@@ -204,7 +151,7 @@ export type WorkflowContext = Record<string, ContextVariable>;
|
|
|
204
151
|
* Workflows metadata for inspector/CLI (DSL step-based format)
|
|
205
152
|
*/
|
|
206
153
|
export type WorkflowsMeta = Record<string, CommonWireMeta & {
|
|
207
|
-
|
|
154
|
+
name: string;
|
|
208
155
|
steps: WorkflowStepMeta[];
|
|
209
156
|
context?: WorkflowContext;
|
|
210
157
|
dsl?: boolean;
|
|
@@ -218,19 +165,19 @@ export interface WorkflowRuntimeMeta {
|
|
|
218
165
|
/** Workflow name (used as key in registrations) */
|
|
219
166
|
name: string;
|
|
220
167
|
/** Pikku function name (for execution) */
|
|
221
|
-
|
|
168
|
+
pikkuFuncId: string;
|
|
222
169
|
/** Source type: 'dsl' (serializable), 'complex' (has inline steps), 'graph' */
|
|
223
|
-
source
|
|
170
|
+
source: 'dsl' | 'complex' | 'graph';
|
|
224
171
|
/** Optional description */
|
|
225
172
|
description?: string;
|
|
226
173
|
/** Tags for organization */
|
|
227
174
|
tags?: string[];
|
|
228
|
-
/** Wires - how the workflow is triggered */
|
|
229
|
-
wires?: WorkflowWires;
|
|
230
175
|
/** Serialized nodes */
|
|
231
176
|
nodes?: Record<string, any>;
|
|
232
177
|
/** Entry node IDs for graph workflows (computed at build time) */
|
|
233
178
|
entryNodeIds?: string[];
|
|
179
|
+
/** Hash of graph topology (nodes, edges, input mappings) */
|
|
180
|
+
graphHash?: string;
|
|
234
181
|
}
|
|
235
182
|
/**
|
|
236
183
|
* Unified workflow runtime metadata map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -26,35 +26,34 @@
|
|
|
26
26
|
"./http": "./dist/wirings/http/index.js",
|
|
27
27
|
"./queue": "./dist/wirings/queue/index.js",
|
|
28
28
|
"./scheduler": "./dist/wirings/scheduler/index.js",
|
|
29
|
+
"./trigger": "./dist/wirings/trigger/index.js",
|
|
29
30
|
"./rpc": "./dist/wirings/rpc/index.js",
|
|
30
31
|
"./mcp": "./dist/wirings/mcp/index.js",
|
|
32
|
+
"./ai-agent": "./dist/wirings/ai-agent/index.js",
|
|
31
33
|
"./cli": "./dist/wirings/cli/index.js",
|
|
32
34
|
"./cli/channel": "./dist/wirings/cli/channel/index.js",
|
|
33
|
-
"./
|
|
35
|
+
"./node": "./dist/wirings/node/index.js",
|
|
36
|
+
"./secret": "./dist/wirings/secret/index.js",
|
|
37
|
+
"./variable": "./dist/wirings/variable/index.js",
|
|
38
|
+
"./oauth2": "./dist/wirings/oauth2/index.js",
|
|
34
39
|
"./errors": "./dist/errors/index.js",
|
|
35
40
|
"./services": "./dist/services/index.js",
|
|
36
41
|
"./services/local-content": "./dist/services/local-content.js",
|
|
37
|
-
"./
|
|
38
|
-
"./
|
|
42
|
+
"./services/gopass-secrets": "./dist/services/gopass-secrets.js",
|
|
43
|
+
"./internal": "./dist/internal.js",
|
|
44
|
+
"./schema": "./dist/schema.js"
|
|
39
45
|
},
|
|
40
46
|
"dependencies": {
|
|
41
|
-
"
|
|
47
|
+
"@standard-schema/spec": "^1.1.0",
|
|
48
|
+
"cookie": "^1.1.1",
|
|
42
49
|
"path-to-regexp": "^8.3.0",
|
|
43
50
|
"picoquery": "^2.5.0"
|
|
44
51
|
},
|
|
45
52
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^24.10.
|
|
47
|
-
"tsx": "^4.
|
|
53
|
+
"@types/node": "^24.10.12",
|
|
54
|
+
"tsx": "^4.21.0",
|
|
48
55
|
"typescript": "^5.9"
|
|
49
56
|
},
|
|
50
|
-
"peerDependencies": {
|
|
51
|
-
"zod": "^4.0.0"
|
|
52
|
-
},
|
|
53
|
-
"peerDependenciesMeta": {
|
|
54
|
-
"zod": {
|
|
55
|
-
"optional": true
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
57
|
"engines": {
|
|
59
58
|
"node": ">=18"
|
|
60
59
|
}
|
package/run-tests.sh
CHANGED
|
@@ -34,6 +34,9 @@ files=($(find src -type f -name "*.test.ts"))
|
|
|
34
34
|
# Check if any files matched the pattern
|
|
35
35
|
if [ ${#files[@]} -eq 0 ]; then
|
|
36
36
|
echo "No test files found matching pattern: $pattern"
|
|
37
|
+
if [ "${STRICT_TEST_DISCOVERY}" = "1" ] || [ "${CI}" = "true" ]; then
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
37
40
|
exit 0
|
|
38
41
|
fi
|
|
39
42
|
|
|
@@ -50,4 +53,4 @@ if [ "$coverage_mode" = true ]; then
|
|
|
50
53
|
fi
|
|
51
54
|
|
|
52
55
|
# Execute the node command with the expanded list of files
|
|
53
|
-
$node_cmd "${files[@]}"
|
|
56
|
+
$node_cmd "${files[@]}"
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { encryptJSON, decryptJSON } from './crypto-utils.js'
|
|
4
|
+
|
|
5
|
+
describe('encryptJSON / decryptJSON', () => {
|
|
6
|
+
const secret = 'test-secret-key-for-encryption'
|
|
7
|
+
|
|
8
|
+
test('should round-trip a simple object', async () => {
|
|
9
|
+
const original = { hello: 'world', num: 42 }
|
|
10
|
+
const encrypted = await encryptJSON(secret, original)
|
|
11
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
12
|
+
assert.deepStrictEqual(decrypted, original)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('should round-trip a string value', async () => {
|
|
16
|
+
const original = 'just a string'
|
|
17
|
+
const encrypted = await encryptJSON(secret, original)
|
|
18
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
19
|
+
assert.strictEqual(decrypted, original)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('should round-trip a number', async () => {
|
|
23
|
+
const original = 12345
|
|
24
|
+
const encrypted = await encryptJSON(secret, original)
|
|
25
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
26
|
+
assert.strictEqual(decrypted, original)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('should round-trip null', async () => {
|
|
30
|
+
const encrypted = await encryptJSON(secret, null)
|
|
31
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
32
|
+
assert.strictEqual(decrypted, null)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('should round-trip a boolean', async () => {
|
|
36
|
+
const encrypted = await encryptJSON(secret, true)
|
|
37
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
38
|
+
assert.strictEqual(decrypted, true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('should round-trip a nested object', async () => {
|
|
42
|
+
const original = { user: { id: 1, roles: ['admin', 'user'] }, active: true }
|
|
43
|
+
const encrypted = await encryptJSON(secret, original)
|
|
44
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
45
|
+
assert.deepStrictEqual(decrypted, original)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('should round-trip an array', async () => {
|
|
49
|
+
const original = [1, 'two', { three: 3 }]
|
|
50
|
+
const encrypted = await encryptJSON(secret, original)
|
|
51
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
52
|
+
assert.deepStrictEqual(decrypted, original)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('should produce different ciphertexts for same input (random IV)', async () => {
|
|
56
|
+
const value = { same: 'data' }
|
|
57
|
+
const enc1 = await encryptJSON(secret, value)
|
|
58
|
+
const enc2 = await encryptJSON(secret, value)
|
|
59
|
+
assert.notStrictEqual(enc1, enc2)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('should produce a base64url string (no +, /, or = characters)', async () => {
|
|
63
|
+
const encrypted = await encryptJSON(secret, { test: 'base64url' })
|
|
64
|
+
assert.ok(!/[+/=]/.test(encrypted), `Expected base64url, got: ${encrypted}`)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('should fail to decrypt with wrong secret', async () => {
|
|
68
|
+
const encrypted = await encryptJSON(secret, { data: 'sensitive' })
|
|
69
|
+
await assert.rejects(
|
|
70
|
+
() => decryptJSON('wrong-secret', encrypted),
|
|
71
|
+
(err: any) => err instanceof Error
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('should reject an invalid encrypted payload (too short)', async () => {
|
|
76
|
+
await assert.rejects(() => decryptJSON(secret, 'dG9vc2hvcnQ'), {
|
|
77
|
+
message: 'Invalid encrypted payload',
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('should reject empty string', async () => {
|
|
82
|
+
await assert.rejects(() => decryptJSON(secret, ''), {
|
|
83
|
+
message: 'Invalid encrypted payload',
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('should reject corrupted ciphertext', async () => {
|
|
88
|
+
const encrypted = await encryptJSON(secret, { data: 'test' })
|
|
89
|
+
const corrupted = encrypted.slice(0, -4) + 'XXXX'
|
|
90
|
+
await assert.rejects(
|
|
91
|
+
() => decryptJSON(secret, corrupted),
|
|
92
|
+
(err: any) => err instanceof Error
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('should handle unicode content', async () => {
|
|
97
|
+
const original = { emoji: '🎉', japanese: 'こんにちは', arabic: 'مرحبا' }
|
|
98
|
+
const encrypted = await encryptJSON(secret, original)
|
|
99
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
100
|
+
assert.deepStrictEqual(decrypted, original)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('should handle empty object', async () => {
|
|
104
|
+
const original = {}
|
|
105
|
+
const encrypted = await encryptJSON(secret, original)
|
|
106
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
107
|
+
assert.deepStrictEqual(decrypted, original)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('should handle large payload', async () => {
|
|
111
|
+
const original = { data: 'x'.repeat(10000) }
|
|
112
|
+
const encrypted = await encryptJSON(secret, original)
|
|
113
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
114
|
+
assert.deepStrictEqual(decrypted, original)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const encoder = new TextEncoder()
|
|
2
|
+
const decoder = new TextDecoder()
|
|
3
|
+
|
|
4
|
+
const getSubtle = () => {
|
|
5
|
+
const crypto = globalThis.crypto
|
|
6
|
+
if (!crypto?.subtle) {
|
|
7
|
+
throw new Error('WebCrypto not available')
|
|
8
|
+
}
|
|
9
|
+
return crypto.subtle
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const base64Encode = (bytes: Uint8Array): string => {
|
|
13
|
+
if (typeof Buffer !== 'undefined') {
|
|
14
|
+
return Buffer.from(bytes).toString('base64')
|
|
15
|
+
}
|
|
16
|
+
let binary = ''
|
|
17
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
18
|
+
binary += String.fromCharCode(bytes[i]!)
|
|
19
|
+
}
|
|
20
|
+
return btoa(binary)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const base64Decode = (input: string): Uint8Array => {
|
|
24
|
+
if (typeof Buffer !== 'undefined') {
|
|
25
|
+
return new Uint8Array(Buffer.from(input, 'base64'))
|
|
26
|
+
}
|
|
27
|
+
const binary = atob(input)
|
|
28
|
+
const bytes = new Uint8Array(binary.length)
|
|
29
|
+
for (let i = 0; i < binary.length; i++) {
|
|
30
|
+
bytes[i] = binary.charCodeAt(i)
|
|
31
|
+
}
|
|
32
|
+
return bytes
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const toBase64Url = (bytes: Uint8Array): string => {
|
|
36
|
+
return base64Encode(bytes)
|
|
37
|
+
.replace(/\+/g, '-')
|
|
38
|
+
.replace(/\//g, '_')
|
|
39
|
+
.replace(/=+$/g, '')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const fromBase64Url = (input: string): Uint8Array => {
|
|
43
|
+
let base64 = input.replace(/-/g, '+').replace(/_/g, '/')
|
|
44
|
+
while (base64.length % 4 !== 0) base64 += '='
|
|
45
|
+
return base64Decode(base64)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const deriveKey = async (secret: string): Promise<CryptoKey> => {
|
|
49
|
+
const subtle = getSubtle()
|
|
50
|
+
const hash = await subtle.digest('SHA-256', encoder.encode(secret))
|
|
51
|
+
return subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, [
|
|
52
|
+
'encrypt',
|
|
53
|
+
'decrypt',
|
|
54
|
+
])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const encryptJSON = async (
|
|
58
|
+
secret: string,
|
|
59
|
+
value: unknown
|
|
60
|
+
): Promise<string> => {
|
|
61
|
+
const crypto = globalThis.crypto
|
|
62
|
+
if (!crypto?.getRandomValues) {
|
|
63
|
+
throw new Error('WebCrypto not available')
|
|
64
|
+
}
|
|
65
|
+
const subtle = getSubtle()
|
|
66
|
+
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
67
|
+
const key = await deriveKey(secret)
|
|
68
|
+
const plaintext = encoder.encode(JSON.stringify(value))
|
|
69
|
+
const encrypted = await subtle.encrypt(
|
|
70
|
+
{ name: 'AES-GCM', iv },
|
|
71
|
+
key,
|
|
72
|
+
plaintext
|
|
73
|
+
)
|
|
74
|
+
const cipherBytes = new Uint8Array(encrypted)
|
|
75
|
+
const out = new Uint8Array(iv.length + cipherBytes.length)
|
|
76
|
+
out.set(iv, 0)
|
|
77
|
+
out.set(cipherBytes, iv.length)
|
|
78
|
+
return toBase64Url(out)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const decryptJSON = async <T>(
|
|
82
|
+
secret: string,
|
|
83
|
+
token: string
|
|
84
|
+
): Promise<T> => {
|
|
85
|
+
const subtle = getSubtle()
|
|
86
|
+
const data = fromBase64Url(token)
|
|
87
|
+
if (data.length < 13) {
|
|
88
|
+
throw new Error('Invalid encrypted payload')
|
|
89
|
+
}
|
|
90
|
+
const iv = data.slice(0, 12)
|
|
91
|
+
const ciphertext = data.slice(12)
|
|
92
|
+
const key = await deriveKey(secret)
|
|
93
|
+
const decrypted = await subtle.decrypt(
|
|
94
|
+
{ name: 'AES-GCM', iv },
|
|
95
|
+
key,
|
|
96
|
+
ciphertext
|
|
97
|
+
)
|
|
98
|
+
return JSON.parse(decoder.decode(new Uint8Array(decrypted))) as T
|
|
99
|
+
}
|
package/src/errors/error.test.ts
CHANGED
|
@@ -1,7 +1,75 @@
|
|
|
1
1
|
import { test, describe } from 'node:test'
|
|
2
2
|
import assert from 'assert'
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
getErrorResponse,
|
|
5
|
+
addError,
|
|
6
|
+
addErrors,
|
|
7
|
+
PikkuError,
|
|
8
|
+
} from './error-handler.js'
|
|
9
|
+
import {
|
|
10
|
+
BadRequestError,
|
|
11
|
+
UnauthorizedError,
|
|
12
|
+
MissingSessionError,
|
|
13
|
+
InvalidSessionError,
|
|
14
|
+
PaymentRequiredError,
|
|
15
|
+
ForbiddenError,
|
|
16
|
+
InvalidOriginError,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
MethodNotAllowedError,
|
|
19
|
+
NotAcceptableError,
|
|
20
|
+
ProxyAuthenticationRequiredError,
|
|
21
|
+
RequestTimeoutError,
|
|
22
|
+
ConflictError,
|
|
23
|
+
GoneError,
|
|
24
|
+
LengthRequiredError,
|
|
25
|
+
PreconditionFailedError,
|
|
26
|
+
PayloadTooLargeError,
|
|
27
|
+
URITooLongError,
|
|
28
|
+
UnsupportedMediaTypeError,
|
|
29
|
+
RangeNotSatisfiableError,
|
|
30
|
+
ExpectationFailedError,
|
|
31
|
+
UnprocessableContentError,
|
|
32
|
+
LockedError,
|
|
33
|
+
TooManyRequestsError,
|
|
34
|
+
InternalServerError,
|
|
35
|
+
NotImplementedError,
|
|
36
|
+
BadGatewayError,
|
|
37
|
+
ServiceUnavailableError,
|
|
38
|
+
GatewayTimeoutError,
|
|
39
|
+
HTTPVersionNotSupportedError,
|
|
40
|
+
MaxComputeTimeReachedError,
|
|
41
|
+
MissingSchemaError,
|
|
42
|
+
InvalidMiddlewareWireError,
|
|
43
|
+
PikkuMissingMetaError,
|
|
44
|
+
} from './errors.js'
|
|
45
|
+
|
|
46
|
+
describe('PikkuError', () => {
|
|
47
|
+
test('should create error with default message', () => {
|
|
48
|
+
const error = new PikkuError()
|
|
49
|
+
assert.strictEqual(error.message, 'An error occurred')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('should create error with custom message', () => {
|
|
53
|
+
const error = new PikkuError('custom message')
|
|
54
|
+
assert.strictEqual(error.message, 'custom message')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('should be instance of Error', () => {
|
|
58
|
+
const error = new PikkuError()
|
|
59
|
+
assert.ok(error instanceof Error)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('should be instance of PikkuError', () => {
|
|
63
|
+
const error = new PikkuError()
|
|
64
|
+
assert.ok(error instanceof PikkuError)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('subclasses should be instanceof PikkuError', () => {
|
|
68
|
+
assert.ok(new BadRequestError() instanceof PikkuError)
|
|
69
|
+
assert.ok(new NotFoundError() instanceof PikkuError)
|
|
70
|
+
assert.ok(new ForbiddenError() instanceof PikkuError)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
5
73
|
|
|
6
74
|
describe('getErrorResponse', () => {
|
|
7
75
|
test('should return the correct error response for BadRequestError', () => {
|
|
@@ -52,3 +120,76 @@ describe('getErrorResponse', () => {
|
|
|
52
120
|
assert.deepStrictEqual(response, customErrorDetails)
|
|
53
121
|
})
|
|
54
122
|
})
|
|
123
|
+
|
|
124
|
+
describe('addErrors', () => {
|
|
125
|
+
test('should register multiple errors at once', () => {
|
|
126
|
+
class ErrA extends Error {}
|
|
127
|
+
class ErrB extends Error {}
|
|
128
|
+
addErrors([
|
|
129
|
+
[ErrA, { status: 450, message: 'Error A' }],
|
|
130
|
+
[ErrB, { status: 451, message: 'Error B' }],
|
|
131
|
+
])
|
|
132
|
+
assert.deepStrictEqual(getErrorResponse(new ErrA()), {
|
|
133
|
+
status: 450,
|
|
134
|
+
message: 'Error A',
|
|
135
|
+
})
|
|
136
|
+
assert.deepStrictEqual(getErrorResponse(new ErrB()), {
|
|
137
|
+
status: 451,
|
|
138
|
+
message: 'Error B',
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe('all built-in error classes', () => {
|
|
144
|
+
const errorCases: [string, any, number][] = [
|
|
145
|
+
['InvalidMiddlewareWireError', InvalidMiddlewareWireError, 500],
|
|
146
|
+
['PikkuMissingMetaError', PikkuMissingMetaError, 500],
|
|
147
|
+
['BadRequestError', BadRequestError, 400],
|
|
148
|
+
['UnauthorizedError', UnauthorizedError, 401],
|
|
149
|
+
['MissingSessionError', MissingSessionError, 401],
|
|
150
|
+
['InvalidSessionError', InvalidSessionError, 401],
|
|
151
|
+
['PaymentRequiredError', PaymentRequiredError, 402],
|
|
152
|
+
['ForbiddenError', ForbiddenError, 403],
|
|
153
|
+
['InvalidOriginError', InvalidOriginError, 403],
|
|
154
|
+
['NotFoundError', NotFoundError, 404],
|
|
155
|
+
['MethodNotAllowedError', MethodNotAllowedError, 405],
|
|
156
|
+
['NotAcceptableError', NotAcceptableError, 406],
|
|
157
|
+
['ProxyAuthenticationRequiredError', ProxyAuthenticationRequiredError, 407],
|
|
158
|
+
['RequestTimeoutError', RequestTimeoutError, 408],
|
|
159
|
+
['ConflictError', ConflictError, 409],
|
|
160
|
+
['GoneError', GoneError, 410],
|
|
161
|
+
['LengthRequiredError', LengthRequiredError, 411],
|
|
162
|
+
['PreconditionFailedError', PreconditionFailedError, 412],
|
|
163
|
+
['PayloadTooLargeError', PayloadTooLargeError, 413],
|
|
164
|
+
['URITooLongError', URITooLongError, 414],
|
|
165
|
+
['UnsupportedMediaTypeError', UnsupportedMediaTypeError, 415],
|
|
166
|
+
['RangeNotSatisfiableError', RangeNotSatisfiableError, 416],
|
|
167
|
+
['ExpectationFailedError', ExpectationFailedError, 417],
|
|
168
|
+
['UnprocessableContentError', UnprocessableContentError, 422],
|
|
169
|
+
['LockedError', LockedError, 423],
|
|
170
|
+
['TooManyRequestsError', TooManyRequestsError, 429],
|
|
171
|
+
['InternalServerError', InternalServerError, 500],
|
|
172
|
+
['NotImplementedError', NotImplementedError, 501],
|
|
173
|
+
['BadGatewayError', BadGatewayError, 502],
|
|
174
|
+
['ServiceUnavailableError', ServiceUnavailableError, 503],
|
|
175
|
+
['GatewayTimeoutError', GatewayTimeoutError, 504],
|
|
176
|
+
['HTTPVersionNotSupportedError', HTTPVersionNotSupportedError, 505],
|
|
177
|
+
['MaxComputeTimeReachedError', MaxComputeTimeReachedError, 524],
|
|
178
|
+
['MissingSchemaError', MissingSchemaError, 500],
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
for (const [name, ErrorClass, expectedStatus] of errorCases) {
|
|
182
|
+
test(`${name} should map to status ${expectedStatus}`, () => {
|
|
183
|
+
const error = new ErrorClass()
|
|
184
|
+
const response = getErrorResponse(error)
|
|
185
|
+
assert.ok(response, `Expected error response for ${name}`)
|
|
186
|
+
assert.strictEqual(response!.status, expectedStatus)
|
|
187
|
+
assert.ok(response!.message.length > 0)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test(`${name} should be instance of PikkuError`, () => {
|
|
191
|
+
const error = new ErrorClass()
|
|
192
|
+
assert.ok(error instanceof PikkuError)
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
})
|
package/src/errors/errors.ts
CHANGED
|
@@ -9,6 +9,12 @@ addError(InvalidMiddlewareWireError, {
|
|
|
9
9
|
message: 'The middleware wire is invalid for the current wiring type.',
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
+
export class PikkuMissingMetaError extends PikkuError {}
|
|
13
|
+
addError(PikkuMissingMetaError, {
|
|
14
|
+
status: 500,
|
|
15
|
+
message: 'Required metadata is missing',
|
|
16
|
+
})
|
|
17
|
+
|
|
12
18
|
/**
|
|
13
19
|
* The server cannot or will not process the request due to client error (e.g., malformed request syntax).
|
|
14
20
|
* @group Error
|
|
@@ -70,6 +76,16 @@ addError(ForbiddenError, {
|
|
|
70
76
|
'The client does not have permission to access the requested resource.',
|
|
71
77
|
})
|
|
72
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The session is readonly and cannot access a non-readonly function.
|
|
81
|
+
* @group Error
|
|
82
|
+
*/
|
|
83
|
+
export class ReadonlySessionError extends PikkuError {}
|
|
84
|
+
addError(ReadonlySessionError, {
|
|
85
|
+
status: 403,
|
|
86
|
+
message: 'The session is readonly and cannot access this function.',
|
|
87
|
+
})
|
|
88
|
+
|
|
73
89
|
/**
|
|
74
90
|
* The request was made from an origin that is not permitted to access this resource.
|
|
75
91
|
* @group Error
|
|
@@ -345,3 +361,14 @@ addError(MaxComputeTimeReachedError, {
|
|
|
345
361
|
message:
|
|
346
362
|
'The server took too long to complete the request, reaching the maximum compute time allowed.',
|
|
347
363
|
})
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* A required schema was not found during validation.
|
|
367
|
+
* @group Error
|
|
368
|
+
*/
|
|
369
|
+
export class MissingSchemaError extends PikkuError {}
|
|
370
|
+
addError(MissingSchemaError, {
|
|
371
|
+
status: 500,
|
|
372
|
+
message:
|
|
373
|
+
'A required schema was not found. Ensure schema generation has been run.',
|
|
374
|
+
})
|
package/src/errors/index.ts
CHANGED