@pikku/core 0.12.78 → 0.12.80
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 +234 -0
- package/dist/dev/hot-reload.js +1 -6
- package/dist/ecosystem.d.ts +27 -0
- package/dist/ecosystem.js +26 -0
- package/dist/errors/error-handler.js +4 -2
- package/dist/function/function-runner.js +20 -42
- package/dist/function/functions.types.d.ts +9 -5
- package/dist/index.d.ts +4 -9
- package/dist/index.js +3 -8
- package/dist/middleware/auth-cookie.d.ts +0 -4
- package/dist/middleware/auth-cookie.js +38 -2
- package/dist/middleware/cors.js +1 -0
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/remote-auth.js +14 -3
- package/dist/permissions.d.ts +8 -10
- package/dist/permissions.js +0 -11
- package/dist/pikku-state.js +12 -5
- package/dist/schema.js +35 -1
- package/dist/services/ai-run-state-service.d.ts +7 -1
- package/dist/services/in-memory-ai-run-state-service.d.ts +1 -1
- package/dist/services/in-memory-ai-run-state-service.js +2 -1
- package/dist/services/in-memory-workflow-service.d.ts +10 -0
- package/dist/services/in-memory-workflow-service.js +25 -0
- package/dist/services/local-content-request-handler.d.ts +21 -0
- package/dist/services/local-content-request-handler.js +72 -53
- package/dist/services/local-content.d.ts +6 -0
- package/dist/services/local-content.js +14 -1
- package/dist/services/workflow-service.d.ts +4 -2
- package/dist/testing/service-tests/agent-run-service-tests.d.ts +10 -0
- package/dist/testing/service-tests/agent-run-service-tests.js +72 -0
- package/dist/testing/service-tests/ai-storage-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/ai-storage-service-tests.js +226 -0
- package/dist/testing/service-tests/channel-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/channel-store-tests.js +72 -0
- package/dist/testing/service-tests/credential-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/credential-service-tests.js +109 -0
- package/dist/testing/service-tests/deployment-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/deployment-service-tests.js +21 -0
- package/dist/testing/service-tests/event-hub-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/event-hub-store-tests.js +34 -0
- package/dist/testing/service-tests/secret-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/secret-service-tests.js +80 -0
- package/dist/testing/service-tests/session-store-tests.d.ts +3 -0
- package/dist/testing/service-tests/session-store-tests.js +43 -0
- package/dist/testing/service-tests/workflow-run-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/workflow-run-service-tests.js +42 -0
- package/dist/testing/service-tests/workflow-service-tests.d.ts +3 -0
- package/dist/testing/service-tests/workflow-service-tests.js +150 -0
- package/dist/testing/service-tests.d.ts +6 -0
- package/dist/testing/service-tests.js +26 -791
- package/dist/types/core.types.d.ts +11 -1
- package/dist/types/state.types.d.ts +1 -1
- package/dist/utils/node-host-resolver.d.ts +12 -0
- package/dist/utils/node-host-resolver.js +16 -0
- package/dist/utils/safe-fetch.d.ts +18 -0
- package/dist/utils/safe-fetch.js +167 -29
- package/dist/wirings/actor-flow/run-conversation.js +1 -6
- package/dist/wirings/ai-agent/agent-rpc.d.ts +15 -0
- package/dist/wirings/ai-agent/agent-rpc.js +53 -0
- package/dist/wirings/ai-agent/ai-agent-agui.js +1 -5
- package/dist/wirings/ai-agent/ai-agent-memory.js +3 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +3 -9
- package/dist/wirings/ai-agent/ai-agent-runner.js +28 -107
- package/dist/wirings/ai-agent/ai-agent-stream.js +20 -61
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +56 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +81 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +1 -1
- package/dist/wirings/ai-agent/voice-input.js +1 -6
- package/dist/wirings/ai-agent/voice-output.js +2 -12
- package/dist/wirings/channel/channel-common.js +1 -0
- package/dist/wirings/channel/channel-handler.js +3 -5
- package/dist/wirings/channel/channel-rpc-service.d.ts +0 -6
- package/dist/wirings/channel/channel-rpc-service.js +0 -8
- package/dist/wirings/channel/channel-rpc.types.d.ts +6 -0
- package/dist/wirings/channel/channel-rpc.types.js +8 -0
- package/dist/wirings/channel/channel-runner.d.ts +1 -3
- package/dist/wirings/channel/channel-runner.js +16 -8
- package/dist/wirings/channel/channel.types.d.ts +2 -0
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +3 -0
- package/dist/wirings/cli/channel/cli-channel-runner.js +2 -0
- package/dist/wirings/cli/cli-runner.js +4 -2
- package/dist/wirings/cli/cli.types.d.ts +0 -8
- package/dist/wirings/cli/command-parser.js +13 -0
- package/dist/wirings/gateway/gateway-runner.js +41 -5
- package/dist/wirings/http/http-routes.js +2 -0
- package/dist/wirings/http/http-runner.d.ts +0 -10
- package/dist/wirings/http/http-runner.js +6 -13
- package/dist/wirings/http/http.types.d.ts +0 -10
- package/dist/wirings/http/index.d.ts +1 -1
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +0 -7
- package/dist/wirings/mcp/mcp-runner.js +0 -6
- package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
- package/dist/wirings/rpc/rpc-runner.js +53 -86
- package/dist/wirings/rpc/rpc-types.d.ts +3 -0
- package/dist/wirings/secret/validate-secret-definitions.js +2 -0
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +0 -4
- package/dist/wirings/trigger/trigger-runner.js +1 -0
- package/dist/wirings/virtual-user/run-virtual-user.js +11 -11
- package/dist/wirings/virtual-user/virtual-user-derive.js +4 -25
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +1 -4
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +29 -2
- package/dist/wirings/workflow/feature.js +7 -4
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -2
- package/dist/wirings/workflow/graph/graph-runner.js +8 -7
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -4
- package/dist/wirings/workflow/index.d.ts +7 -2
- package/dist/wirings/workflow/index.js +5 -1
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
- package/dist/wirings/workflow/pikku-scenario-service.js +38 -10
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +44 -111
- package/dist/wirings/workflow/pikku-workflow-service.js +86 -404
- package/dist/wirings/workflow/workflow-approval.d.ts +39 -0
- package/dist/wirings/workflow/workflow-approval.js +114 -0
- package/dist/wirings/workflow/workflow-constants.d.ts +26 -0
- package/dist/wirings/workflow/workflow-constants.js +35 -0
- package/dist/wirings/workflow/workflow-errors.d.ts +58 -0
- package/dist/wirings/workflow/workflow-errors.js +112 -0
- package/dist/wirings/workflow/workflow-meta-resolver.d.ts +11 -0
- package/dist/wirings/workflow/workflow-meta-resolver.js +31 -0
- package/dist/wirings/workflow/workflow-queue-routing.d.ts +8 -0
- package/dist/wirings/workflow/workflow-queue-routing.js +38 -0
- package/dist/wirings/workflow/workflow-queue-wiring.d.ts +20 -0
- package/dist/wirings/workflow/workflow-queue-wiring.js +79 -0
- package/dist/wirings/workflow/workflow-recovery.d.ts +68 -0
- package/dist/wirings/workflow/workflow-recovery.js +101 -0
- package/dist/wirings/workflow/workflow-run-engine.types.d.ts +54 -0
- package/dist/wirings/workflow/workflow-run-engine.types.js +1 -0
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +16 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +29 -0
- package/dist/wirings/workflow/workflow-suspend.d.ts +12 -0
- package/dist/wirings/workflow/workflow-suspend.js +33 -0
- package/dist/wirings/workflow/workflow.types.d.ts +7 -0
- package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-airunstate-too.md +22 -0
- package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +20 -0
- package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +21 -0
- package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +21 -0
- package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +46 -0
- package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +45 -0
- package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +21 -0
- package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +22 -0
- package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +23 -0
- package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +25 -0
- package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +22 -0
- package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +20 -0
- package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +22 -0
- package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +66 -0
- package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +33 -0
- package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +43 -0
- package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +26 -0
- package/knowledge/decisions/internals/index.md +38 -26
- package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +21 -0
- package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +24 -0
- package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +34 -0
- package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +24 -0
- package/knowledge/decisions/internals/the-actor-prompt-says-json-because-of-json-object-mode.md +22 -0
- package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +26 -0
- package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +43 -0
- package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +58 -0
- package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +40 -0
- package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +40 -0
- package/knowledge/decisions/internals/the-persona-runtime-is-exported-from-the-persona-entry-point.md +28 -0
- package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +25 -0
- package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +21 -0
- package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +22 -0
- package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +25 -0
- package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +21 -0
- package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +24 -0
- package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +35 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +4 -2
- package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +33 -0
- package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +33 -0
- package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +37 -0
- package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +28 -0
- package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +34 -0
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +13 -3
- package/knowledge/decisions/security/index.md +7 -0
- package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +35 -0
- package/knowledge/questions/index.md +2 -1
- package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +44 -0
- package/package.json +14 -3
- package/scripts/generate-api-report.d.mts +1 -0
- package/scripts/generate-api-report.mjs +89 -0
- package/scripts/generate-api-report.mts +218 -0
- package/src/api-report.test.ts +69 -0
- package/src/column-form.ts +1 -4
- package/src/crypto-utils.test.ts +15 -2
- package/src/dev/hot-reload.ts +2 -7
- package/src/ecosystem.ts +40 -0
- package/src/errors/error-handler.ts +5 -3
- package/src/errors/error.test.ts +4 -1
- package/src/function/function-runner.test.ts +1 -1
- package/src/function/function-runner.ts +28 -39
- package/src/function/functions.types.ts +15 -9
- package/src/handle-error.ts +1 -1
- package/src/index.ts +3 -22
- package/src/middleware/auth-cookie.test.ts +50 -0
- package/src/middleware/auth-cookie.ts +38 -2
- package/src/middleware/cors.ts +2 -1
- package/src/middleware/index.ts +1 -1
- package/src/middleware/remote-auth.test.ts +43 -0
- package/src/middleware/remote-auth.ts +17 -3
- package/src/middleware-runner.ts +4 -2
- package/src/no-any-casts.test.ts +57 -0
- package/src/permissions.test.ts +3 -1
- package/src/permissions.ts +22 -24
- package/src/pikku-state.ts +17 -6
- package/src/public-surface.json +578 -0
- package/src/public-surface.json.README +25 -0
- package/src/public-surface.test.ts +105 -0
- package/src/removed-legacy-exports.test.ts +62 -0
- package/src/schema.test.ts +78 -0
- package/src/schema.ts +36 -1
- package/src/services/ai-run-state-service.ts +7 -1
- package/src/services/audit-service.ts +2 -2
- package/src/services/in-memory-ai-run-state-service.ts +3 -2
- package/src/services/in-memory-workflow-service.ts +25 -0
- package/src/services/index.ts +1 -4
- package/src/services/local-content-request-handler.test.ts +43 -5
- package/src/services/local-content-request-handler.ts +103 -74
- package/src/services/local-content.ts +15 -1
- package/src/services/local-email-service.ts +5 -1
- package/src/services/system-role-guard.test.ts +4 -1
- package/src/services/workflow-service.ts +9 -2
- package/src/side-effects-are-declared.test.ts +84 -0
- package/src/source-files-stay-composable.test.ts +41 -0
- package/src/testing/service-tests/agent-run-service-tests.ts +98 -0
- package/src/testing/service-tests/ai-storage-service-tests.ts +286 -0
- package/src/testing/service-tests/channel-store-tests.ts +98 -0
- package/src/testing/service-tests/credential-service-tests.ts +143 -0
- package/src/testing/service-tests/deployment-service-tests.ts +33 -0
- package/src/testing/service-tests/event-hub-store-tests.ts +48 -0
- package/src/testing/service-tests/secret-service-tests.ts +105 -0
- package/src/testing/service-tests/session-store-tests.ts +62 -0
- package/src/testing/service-tests/workflow-run-service-tests.ts +59 -0
- package/src/testing/service-tests/workflow-service-tests.ts +308 -0
- package/src/testing/service-tests.ts +31 -1111
- package/src/types/core.types.ts +15 -1
- package/src/types/state.types.ts +1 -1
- package/src/utils/node-host-resolver.ts +20 -0
- package/src/utils/safe-fetch.test.ts +143 -1
- package/src/utils/safe-fetch.ts +200 -25
- package/src/wirings/actor-flow/run-conversation.ts +1 -6
- package/src/wirings/ai-agent/agent-rpc.ts +120 -0
- package/src/wirings/ai-agent/ai-agent-agui.ts +1 -5
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-memory.ts +8 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +10 -12
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +82 -2
- package/src/wirings/ai-agent/ai-agent-runner.ts +49 -120
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +36 -63
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-turn.ts +121 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +1 -1
- package/src/wirings/ai-agent/voice-input.ts +1 -6
- package/src/wirings/ai-agent/voice-output.ts +2 -12
- package/src/wirings/channel/channel-common.ts +5 -2
- package/src/wirings/channel/channel-handler-shapes.test.ts +72 -0
- package/src/wirings/channel/channel-handler.ts +7 -7
- package/src/wirings/channel/channel-rpc-service.ts +0 -14
- package/src/wirings/channel/channel-rpc.test.ts +25 -4
- package/src/wirings/channel/channel-rpc.types.ts +14 -0
- package/src/wirings/channel/channel-runner.ts +33 -20
- package/src/wirings/channel/channel.types.ts +2 -0
- package/src/wirings/channel/local/local-channel-runner.ts +1 -1
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +2 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +6 -3
- package/src/wirings/cli/channel/cli-channel-runner.ts +3 -1
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
- package/src/wirings/cli/cli-runner.ts +7 -4
- package/src/wirings/cli/cli.types.ts +0 -39
- package/src/wirings/cli/command-parser.test.ts +19 -0
- package/src/wirings/cli/command-parser.ts +17 -1
- package/src/wirings/gateway/gateway-authorization.test.ts +131 -0
- package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +62 -22
- package/src/wirings/http/http-routes.ts +4 -1
- package/src/wirings/http/http-runner.ts +11 -26
- package/src/wirings/http/http.types.ts +0 -15
- package/src/wirings/http/index.ts +1 -7
- package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
- package/src/wirings/http/web-request.ts +1 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -16
- package/src/wirings/persona/persona-environments.test.ts +14 -3
- package/src/wirings/persona/persona.test.ts +13 -3
- package/src/wirings/persona/validate-personas.ts +5 -1
- package/src/wirings/queue/queue-runner.ts +1 -1
- package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
- package/src/wirings/rpc/rpc-runner.ts +58 -136
- package/src/wirings/rpc/rpc-types.ts +7 -0
- package/src/wirings/rpc/wire-addon.ts +3 -1
- package/src/wirings/secret/validate-secret-definitions.test.ts +69 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
- package/src/wirings/trigger/trigger-runner.ts +7 -5
- package/src/wirings/virtual-user/run-virtual-user.test.ts +18 -9
- package/src/wirings/virtual-user/run-virtual-user.ts +28 -15
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +4 -1
- package/src/wirings/virtual-user/virtual-user-derive.ts +4 -25
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +7 -2
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +1 -4
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +13 -3
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +35 -2
- package/src/wirings/workflow/feature.ts +7 -4
- package/src/wirings/workflow/graph/graph-node.ts +1 -1
- package/src/wirings/workflow/graph/graph-runner.test.ts +4 -2
- package/src/wirings/workflow/graph/graph-runner.ts +13 -16
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -5
- package/src/wirings/workflow/index.ts +12 -4
- package/src/wirings/workflow/pikku-scenario-service.ts +54 -25
- package/src/wirings/workflow/pikku-workflow-service.test.ts +2 -2
- package/src/wirings/workflow/pikku-workflow-service.ts +208 -630
- package/src/wirings/workflow/scenario-hooks.test.ts +51 -0
- package/src/wirings/workflow/workflow-approval.ts +185 -0
- package/src/wirings/workflow/workflow-child-run-session.test.ts +89 -0
- package/src/wirings/workflow/workflow-constants.ts +47 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +1 -1
- package/src/wirings/workflow/workflow-dispatch-relay.test.ts +128 -0
- package/src/wirings/workflow/workflow-errors.ts +128 -0
- package/src/wirings/workflow/workflow-inline-authority.test.ts +12 -6
- package/src/wirings/workflow/workflow-meta-resolver.ts +45 -0
- package/src/wirings/workflow/workflow-missing-meta.test.ts +92 -0
- package/src/wirings/workflow/workflow-queue-routing.ts +85 -0
- package/src/wirings/workflow/workflow-queue-wiring.ts +149 -0
- package/src/wirings/workflow/workflow-recovery.ts +162 -0
- package/src/wirings/workflow/workflow-retry-policy.test.ts +1 -1
- package/src/wirings/workflow/workflow-run-authority.test.ts +215 -0
- package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
- package/src/wirings/workflow/workflow-run-ownership.ts +36 -0
- package/src/wirings/workflow/workflow-suspend.ts +61 -0
- package/src/wirings/workflow/workflow.types.ts +7 -0
- package/src/wirings-stay-decoupled.test.ts +123 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/src/internal.ts +0 -10
|
@@ -197,18 +197,6 @@ export type ValidateParameters<Params extends string, Input> =
|
|
|
197
197
|
? Params
|
|
198
198
|
: never
|
|
199
199
|
|
|
200
|
-
/**
|
|
201
|
-
* Extract output type from a Pikku function config type
|
|
202
|
-
*/
|
|
203
|
-
export type ExtractFunctionOutput<Func> =
|
|
204
|
-
Func extends CorePikkuFunctionConfig<infer FuncType, any, any>
|
|
205
|
-
? FuncType extends
|
|
206
|
-
| CorePikkuFunction<any, infer Output, any, any, any>
|
|
207
|
-
| CorePikkuFunctionSessionless<any, infer Output, any, any, any>
|
|
208
|
-
? Output
|
|
209
|
-
: never
|
|
210
|
-
: never
|
|
211
|
-
|
|
212
200
|
/**
|
|
213
201
|
* CLI command configuration that infers options from function input type.
|
|
214
202
|
* This is a helper type for creating type-safe CLI commands.
|
|
@@ -290,33 +278,6 @@ export type CLICommandShorthand<
|
|
|
290
278
|
>,
|
|
291
279
|
> = PikkuFunctionConfig
|
|
292
280
|
|
|
293
|
-
/**
|
|
294
|
-
* Command definition (either full or shorthand)
|
|
295
|
-
*/
|
|
296
|
-
export type CLICommandDefinition<
|
|
297
|
-
In,
|
|
298
|
-
Out,
|
|
299
|
-
PikkuFunctionConfig extends CorePikkuFunctionConfig<
|
|
300
|
-
| CorePikkuFunction<In, Out, any, any, any>
|
|
301
|
-
| CorePikkuFunctionSessionless<In, Out, any, any, any>
|
|
302
|
-
>,
|
|
303
|
-
PikkuPermission extends CorePikkuPermission<any, any, any>,
|
|
304
|
-
PikkuMiddleware extends CorePikkuMiddleware,
|
|
305
|
-
Options = any,
|
|
306
|
-
Subcommands extends Record<string, CoreCLICommandConfig<any, any, any>> =
|
|
307
|
-
Record<string, CoreCLICommandConfig<any, any, any>>,
|
|
308
|
-
> =
|
|
309
|
-
| CoreCLICommand<
|
|
310
|
-
In,
|
|
311
|
-
Out,
|
|
312
|
-
PikkuFunctionConfig,
|
|
313
|
-
PikkuPermission,
|
|
314
|
-
PikkuMiddleware,
|
|
315
|
-
Options,
|
|
316
|
-
Subcommands
|
|
317
|
-
>
|
|
318
|
-
| CLICommandShorthand<In, Out, PikkuFunctionConfig>
|
|
319
|
-
|
|
320
281
|
/**
|
|
321
282
|
* CLI wiring configuration
|
|
322
283
|
*/
|
|
@@ -710,5 +710,24 @@ describe('Command Parser', () => {
|
|
|
710
710
|
assert.ok(result.errors.some((e) => e.includes('Unknown option: -x')))
|
|
711
711
|
assert.deepStrictEqual(result.warnings, [])
|
|
712
712
|
})
|
|
713
|
+
describe('short-flag cluster is bounded (DoS)', () => {
|
|
714
|
+
test('a huge short-flag cluster yields a single error, not one per char', () => {
|
|
715
|
+
const huge = '-' + 'a'.repeat(200_000)
|
|
716
|
+
const result = parseCLIArguments(
|
|
717
|
+
['greet', 'Alice', huge],
|
|
718
|
+
'test-cli',
|
|
719
|
+
testMeta
|
|
720
|
+
)
|
|
721
|
+
const clusterErrors = result.errors.filter((e) =>
|
|
722
|
+
e.includes('Unknown option')
|
|
723
|
+
)
|
|
724
|
+
// Without the cap this would be ~200k errors (one per character).
|
|
725
|
+
assert.ok(
|
|
726
|
+
clusterErrors.length <= 2,
|
|
727
|
+
`expected the oversized cluster to collapse to a single error, got ${clusterErrors.length}`
|
|
728
|
+
)
|
|
729
|
+
assert.ok(result.errors.some((e) => e.includes('too long')))
|
|
730
|
+
})
|
|
731
|
+
})
|
|
713
732
|
})
|
|
714
733
|
})
|
|
@@ -86,6 +86,15 @@ export interface ParsedCommand {
|
|
|
86
86
|
warnings: string[]
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* A real short-flag cluster is a handful of characters (`-abc`). Anything past
|
|
91
|
+
* this is treated as a single malformed token rather than scanned per
|
|
92
|
+
* character — the parser runs over untrusted CLI-channel frames, and a
|
|
93
|
+
* multi-megabyte `-aaaa…` would otherwise be O(length) work that also pushed
|
|
94
|
+
* one error per character.
|
|
95
|
+
*/
|
|
96
|
+
const MAX_SHORT_FLAG_CLUSTER = 64
|
|
97
|
+
|
|
89
98
|
export function parseCLIArguments(
|
|
90
99
|
args: string[],
|
|
91
100
|
programName: string,
|
|
@@ -238,6 +247,13 @@ export function parseCLIArguments(
|
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
249
|
} else if (arg.startsWith('-') && arg.length > 1) {
|
|
250
|
+
if (arg.length > MAX_SHORT_FLAG_CLUSTER) {
|
|
251
|
+
result.errors.push(
|
|
252
|
+
`Unknown option: ${arg.slice(0, MAX_SHORT_FLAG_CLUSTER)}… (too long)`
|
|
253
|
+
)
|
|
254
|
+
currentIndex++
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
241
257
|
for (let i = 1; i < arg.length; i++) {
|
|
242
258
|
const shortFlag = arg[i]
|
|
243
259
|
|
|
@@ -354,7 +370,7 @@ function parseOptionValue(value: string, optionDef?: CLIOption): any {
|
|
|
354
370
|
return parseFloat(value)
|
|
355
371
|
}
|
|
356
372
|
|
|
357
|
-
if (optionDef.choices && !optionDef.choices.includes(value as
|
|
373
|
+
if (optionDef.choices && !optionDef.choices.includes(value as never)) {
|
|
358
374
|
// Deliberately not an error here — applyOptionDefaults reports bad choices.
|
|
359
375
|
return value
|
|
360
376
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
addGlobalPermission,
|
|
9
9
|
clearPermissionsCache,
|
|
10
10
|
} from '../../permissions.js'
|
|
11
|
+
import { addTagMiddleware } from '../../middleware-runner.js'
|
|
11
12
|
import type {
|
|
12
13
|
GatewayAdapter,
|
|
13
14
|
GatewayInboundMessage,
|
|
@@ -84,6 +85,31 @@ const seedCompiledMeta = () => {
|
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* What the inspector records for a gateway and the function it was wired with.
|
|
90
|
+
* These tests wire gateways by hand, so nothing populates it otherwise — and it
|
|
91
|
+
* has to be in place before `wireGateway`, exactly as the generated bootstrap
|
|
92
|
+
* loads every meta file before any wiring file.
|
|
93
|
+
*/
|
|
94
|
+
const seedDeclaredHandler = (
|
|
95
|
+
gatewayName: string,
|
|
96
|
+
funcId: string,
|
|
97
|
+
funcMeta: Record<string, any>,
|
|
98
|
+
gatewayMeta: Record<string, any> = {}
|
|
99
|
+
) => {
|
|
100
|
+
;(pikkuState(null, 'function', 'meta') as any)[funcId] = {
|
|
101
|
+
pikkuFuncId: funcId,
|
|
102
|
+
inputSchemaName: null,
|
|
103
|
+
outputSchemaName: null,
|
|
104
|
+
...funcMeta,
|
|
105
|
+
}
|
|
106
|
+
;(pikkuState(null, 'gateway', 'meta') as any)[gatewayName] = {
|
|
107
|
+
pikkuFuncId: funcId,
|
|
108
|
+
name: gatewayName,
|
|
109
|
+
...gatewayMeta,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
87
113
|
const postMessage = async (route: string) => {
|
|
88
114
|
const request = new Request(`http://localhost${route}`, {
|
|
89
115
|
method: 'POST',
|
|
@@ -229,6 +255,68 @@ describe('gateway handler authorization', () => {
|
|
|
229
255
|
assert.deepEqual(calls, ['ran'])
|
|
230
256
|
})
|
|
231
257
|
|
|
258
|
+
test('a handler declared with pikkuFunc keeps its session requirement', async () => {
|
|
259
|
+
const calls: string[] = []
|
|
260
|
+
|
|
261
|
+
seedDeclaredHandler('declared-session', 'myHandler', {
|
|
262
|
+
sessionless: false,
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
wireGateway({
|
|
266
|
+
name: 'declared-session',
|
|
267
|
+
type: 'webhook',
|
|
268
|
+
route: '/webhooks/declared-session',
|
|
269
|
+
adapter: createMockAdapter(),
|
|
270
|
+
func: {
|
|
271
|
+
func: async () => {
|
|
272
|
+
calls.push('ran')
|
|
273
|
+
return { text: 'reply' }
|
|
274
|
+
},
|
|
275
|
+
} as any,
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
seedCompiledMeta()
|
|
279
|
+
httpRouter.initialize()
|
|
280
|
+
|
|
281
|
+
const response = await postMessage('/webhooks/declared-session')
|
|
282
|
+
|
|
283
|
+
assert.equal(response.status, 403)
|
|
284
|
+
assert.deepEqual(
|
|
285
|
+
calls,
|
|
286
|
+
[],
|
|
287
|
+
'a session-required pikkuFunc must not be silently made sessionless'
|
|
288
|
+
)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
test('a handler declared sessionless keeps running without a session', async () => {
|
|
292
|
+
const calls: string[] = []
|
|
293
|
+
|
|
294
|
+
seedDeclaredHandler('declared-sessionless', 'mySessionlessHandler', {
|
|
295
|
+
sessionless: true,
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
wireGateway({
|
|
299
|
+
name: 'declared-sessionless',
|
|
300
|
+
type: 'webhook',
|
|
301
|
+
route: '/webhooks/declared-sessionless',
|
|
302
|
+
adapter: createMockAdapter(),
|
|
303
|
+
func: {
|
|
304
|
+
func: async () => {
|
|
305
|
+
calls.push('ran')
|
|
306
|
+
return { text: 'reply' }
|
|
307
|
+
},
|
|
308
|
+
} as any,
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
seedCompiledMeta()
|
|
312
|
+
httpRouter.initialize()
|
|
313
|
+
|
|
314
|
+
const response = await postMessage('/webhooks/declared-sessionless')
|
|
315
|
+
|
|
316
|
+
assert.equal(response.status, 200)
|
|
317
|
+
assert.deepEqual(calls, ['ran'])
|
|
318
|
+
})
|
|
319
|
+
|
|
232
320
|
test('gateway-level auth: true requires a session', async () => {
|
|
233
321
|
const calls: string[] = []
|
|
234
322
|
|
|
@@ -281,6 +369,49 @@ describe('gateway handler authorization', () => {
|
|
|
281
369
|
assert.deepEqual(calls, [], 'auth: true must require a session')
|
|
282
370
|
})
|
|
283
371
|
|
|
372
|
+
test('tag middleware declared for the gateway actually runs', async () => {
|
|
373
|
+
const order: string[] = []
|
|
374
|
+
|
|
375
|
+
addTagMiddleware('audited', [
|
|
376
|
+
async (_s: any, _wire: any, next: any) => {
|
|
377
|
+
order.push('middleware')
|
|
378
|
+
await next()
|
|
379
|
+
},
|
|
380
|
+
] as any)
|
|
381
|
+
|
|
382
|
+
seedDeclaredHandler(
|
|
383
|
+
'tagged',
|
|
384
|
+
'myTaggedHandler',
|
|
385
|
+
{ sessionless: true },
|
|
386
|
+
{ tags: ['audited'], middleware: [{ type: 'tag', tag: 'audited' }] }
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
wireGateway({
|
|
390
|
+
name: 'tagged',
|
|
391
|
+
type: 'webhook',
|
|
392
|
+
route: '/webhooks/tagged',
|
|
393
|
+
adapter: createMockAdapter(),
|
|
394
|
+
func: {
|
|
395
|
+
func: async () => {
|
|
396
|
+
order.push('handler')
|
|
397
|
+
return { text: 'reply' }
|
|
398
|
+
},
|
|
399
|
+
} as any,
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
seedCompiledMeta()
|
|
403
|
+
httpRouter.initialize()
|
|
404
|
+
|
|
405
|
+
const response = await postMessage('/webhooks/tagged')
|
|
406
|
+
|
|
407
|
+
assert.equal(response.status, 200)
|
|
408
|
+
assert.deepEqual(
|
|
409
|
+
order,
|
|
410
|
+
['middleware', 'handler'],
|
|
411
|
+
'addTagMiddleware must gate a gateway carrying the tag'
|
|
412
|
+
)
|
|
413
|
+
})
|
|
414
|
+
|
|
284
415
|
test('the adapter still auto-sends the handler reply', async () => {
|
|
285
416
|
const adapter = createMockAdapter()
|
|
286
417
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { pikkuState, resetPikkuState } from '../../pikku-state.js'
|
|
5
|
+
import { wireGateway } from './gateway-runner.js'
|
|
6
|
+
|
|
7
|
+
const wireWebsocketGateway = (name: string) => {
|
|
8
|
+
resetPikkuState()
|
|
9
|
+
wireGateway({
|
|
10
|
+
name,
|
|
11
|
+
type: 'websocket',
|
|
12
|
+
route: `/${name}`,
|
|
13
|
+
adapter: { name: 'test', parse: () => null, send: async () => {} },
|
|
14
|
+
func: { func: async () => undefined },
|
|
15
|
+
} as any)
|
|
16
|
+
return pikkuState(null, 'channel', 'meta')[name]!
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe('a gateway-wired websocket channel writes complete channel meta', () => {
|
|
20
|
+
test('the fields every channel reader indexes are present', () => {
|
|
21
|
+
const meta = wireWebsocketGateway('chatGateway')
|
|
22
|
+
|
|
23
|
+
// channel-handler.ts indexes messageWirings without a guard, and the
|
|
24
|
+
// serverless and local runners read disconnect directly.
|
|
25
|
+
assert.deepEqual(
|
|
26
|
+
meta.messageWirings,
|
|
27
|
+
{},
|
|
28
|
+
'messageWirings must be an object — channel-handler indexes it unguarded'
|
|
29
|
+
)
|
|
30
|
+
assert.equal(meta.disconnect, null)
|
|
31
|
+
assert.equal(meta.input, null)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('it is marked as gateway-wired', () => {
|
|
35
|
+
assert.equal(wireWebsocketGateway('flagged').gateway, true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('connect and message point at the generated handlers', () => {
|
|
39
|
+
const meta = wireWebsocketGateway('handlers')
|
|
40
|
+
|
|
41
|
+
assert.equal(meta.connect?.pikkuFuncId, 'gateway__handlers__connect')
|
|
42
|
+
assert.equal(meta.message?.pikkuFuncId, 'gateway__handlers__message')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -3,6 +3,7 @@ import { NotFoundError, UnauthorizedError } from '../../errors/errors.js'
|
|
|
3
3
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
4
4
|
import { runMiddleware } from '../../middleware-runner.js'
|
|
5
5
|
import { httpRouter } from '../http/routers/http-router.js'
|
|
6
|
+
import type { CoreHTTPFunctionWiring } from '../http/http.types.js'
|
|
6
7
|
import type {
|
|
7
8
|
CoreGateway,
|
|
8
9
|
GatewayAdapter,
|
|
@@ -23,20 +24,46 @@ const bridgeMiddlewareSession = async (wire: PikkuRawWire): Promise<void> => {
|
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Metadata the inspector recorded for the function the gateway was wired with.
|
|
29
|
+
* The bootstrap loads every meta file before any wiring file, so this is
|
|
30
|
+
* already populated by the time a gateway wires itself. A gateway wired by
|
|
31
|
+
* hand rather than through codegen has no entry, and falls back to the
|
|
32
|
+
* sessionless default below.
|
|
33
|
+
*/
|
|
34
|
+
const declaredHandlerMeta = (config: CoreGateway) => {
|
|
35
|
+
const declaredFuncId = pikkuState(null, 'gateway', 'meta')[config.name]
|
|
36
|
+
?.pikkuFuncId
|
|
37
|
+
return declaredFuncId
|
|
38
|
+
? pikkuState(null, 'function', 'meta')[declaredFuncId]
|
|
39
|
+
: undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
26
42
|
// knowledge: decisions/security/gateway-handlers-run-through-the-function-runner-gate.md
|
|
27
43
|
const registerGatewayHandler = (config: CoreGateway): string => {
|
|
28
44
|
const funcId = gatewayHandlerFuncId(config.name)
|
|
29
45
|
const funcMeta = pikkuState(null, 'function', 'meta')
|
|
46
|
+
const declared = declaredHandlerMeta(config)
|
|
30
47
|
funcMeta[funcId] = {
|
|
48
|
+
...declared,
|
|
31
49
|
pikkuFuncId: funcId,
|
|
32
|
-
inputSchemaName: null,
|
|
33
|
-
outputSchemaName: null,
|
|
34
|
-
sessionless: true,
|
|
50
|
+
inputSchemaName: declared?.inputSchemaName ?? null,
|
|
51
|
+
outputSchemaName: declared?.outputSchemaName ?? null,
|
|
52
|
+
sessionless: declared?.sessionless ?? true,
|
|
35
53
|
}
|
|
36
|
-
addFunction(funcId, config.func
|
|
54
|
+
addFunction(funcId, config.func)
|
|
37
55
|
return funcId
|
|
38
56
|
}
|
|
39
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Tag middleware the inspector resolved for this gateway. It is keyed by
|
|
60
|
+
* gateway name rather than reachable from `config`, because `tags` is a
|
|
61
|
+
* compile-time input everywhere — nothing at runtime maps a tag to its
|
|
62
|
+
* middleware group.
|
|
63
|
+
*/
|
|
64
|
+
const gatewayInheritedMiddleware = (config: CoreGateway) =>
|
|
65
|
+
pikkuState(null, 'gateway', 'meta')[config.name]?.middleware
|
|
66
|
+
|
|
40
67
|
export const resolveGatewayAdapter = (
|
|
41
68
|
config: CoreGateway,
|
|
42
69
|
services: CoreSingletonServices
|
|
@@ -73,7 +100,7 @@ export const wireGateway = (config: CoreGateway): void => {
|
|
|
73
100
|
break
|
|
74
101
|
default:
|
|
75
102
|
throw new Error(
|
|
76
|
-
`Unknown gateway type '${
|
|
103
|
+
`Unknown gateway type '${config.type}' for gateway '${config.name}'`
|
|
77
104
|
)
|
|
78
105
|
}
|
|
79
106
|
}
|
|
@@ -92,7 +119,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
|
|
|
92
119
|
func: createWebhookPostHandler(config),
|
|
93
120
|
}
|
|
94
121
|
|
|
95
|
-
addFunction(postFuncId, postHandler
|
|
122
|
+
addFunction(postFuncId, postHandler)
|
|
96
123
|
|
|
97
124
|
if (!routes.has('post')) {
|
|
98
125
|
routes.set('post', new Map())
|
|
@@ -102,7 +129,10 @@ const wireWebhookGateway = (config: CoreGateway): void => {
|
|
|
102
129
|
route,
|
|
103
130
|
func: postHandler,
|
|
104
131
|
auth: false,
|
|
105
|
-
|
|
132
|
+
// A gateway handler takes (services, data, wire) rather than a pikku
|
|
133
|
+
// function's shape, so it never matches CorePikkuFunctionConfig. It is only
|
|
134
|
+
// ever invoked through runPikkuFunc, which is what makes that safe.
|
|
135
|
+
} as unknown as CoreHTTPFunctionWiring<unknown, unknown, string>)
|
|
106
136
|
|
|
107
137
|
// knowledge: decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md
|
|
108
138
|
if (typeof adapter === 'function' || adapter.verifyWebhook) {
|
|
@@ -113,7 +143,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
|
|
|
113
143
|
func: createWebhookVerifyHandler(config),
|
|
114
144
|
}
|
|
115
145
|
|
|
116
|
-
addFunction(verifyFuncId, verifyHandler
|
|
146
|
+
addFunction(verifyFuncId, verifyHandler)
|
|
117
147
|
|
|
118
148
|
if (!routes.has('get')) {
|
|
119
149
|
routes.set('get', new Map())
|
|
@@ -125,7 +155,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
|
|
|
125
155
|
auth: false,
|
|
126
156
|
// knowledge: decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md
|
|
127
157
|
returnsJSON: false,
|
|
128
|
-
} as
|
|
158
|
+
} as unknown as CoreHTTPFunctionWiring<unknown, unknown, string>)
|
|
129
159
|
}
|
|
130
160
|
|
|
131
161
|
httpRouter.reset()
|
|
@@ -134,6 +164,7 @@ const wireWebhookGateway = (config: CoreGateway): void => {
|
|
|
134
164
|
const createWebhookPostHandler = (config: CoreGateway) => {
|
|
135
165
|
const { name, middleware: userMiddleware } = config
|
|
136
166
|
const handlerFuncId = registerGatewayHandler(config)
|
|
167
|
+
const inheritedMiddleware = gatewayInheritedMiddleware(config)
|
|
137
168
|
|
|
138
169
|
return async (
|
|
139
170
|
services: CoreSingletonServices,
|
|
@@ -160,16 +191,17 @@ const createWebhookPostHandler = (config: CoreGateway) => {
|
|
|
160
191
|
platform: adapter.name,
|
|
161
192
|
send: (msg: GatewayOutboundMessage) => adapter.send(parsed.senderId, msg),
|
|
162
193
|
}
|
|
163
|
-
|
|
194
|
+
wire.gateway = gateway
|
|
164
195
|
|
|
165
196
|
// Gateway middleware runs outside the gate so it can establish the session the gate checks.
|
|
166
197
|
const invoke = async () => {
|
|
167
|
-
await bridgeMiddlewareSession(wire
|
|
198
|
+
await bridgeMiddlewareSession(wire)
|
|
168
199
|
return await runPikkuFunc('gateway', name, handlerFuncId, {
|
|
169
200
|
singletonServices: services,
|
|
170
201
|
data: () => parsed,
|
|
171
202
|
auth: config.auth,
|
|
172
|
-
|
|
203
|
+
inheritedMiddleware,
|
|
204
|
+
wire,
|
|
173
205
|
})
|
|
174
206
|
}
|
|
175
207
|
|
|
@@ -247,18 +279,22 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
|
|
|
247
279
|
name,
|
|
248
280
|
route,
|
|
249
281
|
gateway: true,
|
|
282
|
+
input: null,
|
|
250
283
|
connect: { pikkuFuncId: connectFuncId },
|
|
284
|
+
disconnect: null,
|
|
251
285
|
message: { pikkuFuncId: messageFuncId },
|
|
252
|
-
|
|
286
|
+
messageWirings: {},
|
|
287
|
+
}
|
|
253
288
|
|
|
254
289
|
const userMiddleware = config.middleware as CorePikkuMiddleware[] | undefined
|
|
255
290
|
const handlerFuncId = registerGatewayHandler(config)
|
|
291
|
+
const inheritedMiddleware = gatewayInheritedMiddleware(config)
|
|
256
292
|
|
|
257
293
|
addFunction(connectFuncId, {
|
|
258
294
|
auth: false,
|
|
259
295
|
func: async (services: any, _data: unknown, wire: PikkuWire) => {
|
|
260
296
|
const adapter = await resolveGatewayAdapter(config, services)
|
|
261
|
-
|
|
297
|
+
wire.gateway = {
|
|
262
298
|
gatewayName: name,
|
|
263
299
|
senderId: '',
|
|
264
300
|
platform: adapter.name,
|
|
@@ -267,7 +303,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
|
|
|
267
303
|
},
|
|
268
304
|
} satisfies PikkuGateway
|
|
269
305
|
},
|
|
270
|
-
}
|
|
306
|
+
})
|
|
271
307
|
|
|
272
308
|
addFunction(messageFuncId, {
|
|
273
309
|
auth: false,
|
|
@@ -284,15 +320,16 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
|
|
|
284
320
|
wire.channel?.send(msg)
|
|
285
321
|
},
|
|
286
322
|
}
|
|
287
|
-
|
|
323
|
+
wire.gateway = gateway
|
|
288
324
|
|
|
289
325
|
const invoke = async () => {
|
|
290
|
-
await bridgeMiddlewareSession(wire
|
|
326
|
+
await bridgeMiddlewareSession(wire)
|
|
291
327
|
return await runPikkuFunc('gateway', name, handlerFuncId, {
|
|
292
328
|
singletonServices: services,
|
|
293
329
|
data: () => parsed,
|
|
294
330
|
auth: config.auth,
|
|
295
|
-
|
|
331
|
+
inheritedMiddleware,
|
|
332
|
+
wire,
|
|
296
333
|
})
|
|
297
334
|
}
|
|
298
335
|
|
|
@@ -307,7 +344,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
|
|
|
307
344
|
wire.channel?.send(result)
|
|
308
345
|
}
|
|
309
346
|
},
|
|
310
|
-
}
|
|
347
|
+
})
|
|
311
348
|
|
|
312
349
|
// knowledge: decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md
|
|
313
350
|
channels.set(name, {
|
|
@@ -316,7 +353,7 @@ const wireWebsocketGateway = (config: CoreGateway): void => {
|
|
|
316
353
|
auth: false,
|
|
317
354
|
onConnect: { func: async () => {} },
|
|
318
355
|
onMessage: { func: async () => {} },
|
|
319
|
-
}
|
|
356
|
+
})
|
|
320
357
|
|
|
321
358
|
httpRouter.reset()
|
|
322
359
|
}
|
|
@@ -333,6 +370,7 @@ export const createListenerMessageHandler = (
|
|
|
333
370
|
): ((rawData: unknown) => Promise<void>) => {
|
|
334
371
|
const userMiddleware = config.middleware as CorePikkuMiddleware[] | undefined
|
|
335
372
|
const handlerFuncId = registerGatewayHandler(config)
|
|
373
|
+
const inheritedMiddleware = gatewayInheritedMiddleware(config)
|
|
336
374
|
|
|
337
375
|
return async (rawData: unknown): Promise<void> => {
|
|
338
376
|
const adapter = await resolveGatewayAdapter(config, singletonServices)
|
|
@@ -346,7 +384,7 @@ export const createListenerMessageHandler = (
|
|
|
346
384
|
platform: adapter.name,
|
|
347
385
|
send: (msg: GatewayOutboundMessage) => adapter.send(parsed.senderId, msg),
|
|
348
386
|
}
|
|
349
|
-
|
|
387
|
+
wire.gateway = gateway
|
|
350
388
|
|
|
351
389
|
const invoke = async () => {
|
|
352
390
|
await bridgeMiddlewareSession(wire)
|
|
@@ -354,14 +392,16 @@ export const createListenerMessageHandler = (
|
|
|
354
392
|
singletonServices,
|
|
355
393
|
data: () => parsed,
|
|
356
394
|
auth: config.auth,
|
|
395
|
+
inheritedMiddleware,
|
|
357
396
|
wire,
|
|
358
397
|
})
|
|
359
398
|
}
|
|
360
399
|
|
|
400
|
+
// knowledge: decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md
|
|
361
401
|
const result: any = userMiddleware?.length
|
|
362
402
|
? await runMiddleware(
|
|
363
403
|
singletonServices,
|
|
364
|
-
wire as
|
|
404
|
+
wire as PikkuWire,
|
|
365
405
|
userMiddleware,
|
|
366
406
|
invoke
|
|
367
407
|
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
CoreHTTPFunctionWiring,
|
|
2
3
|
HTTPRouteConfig,
|
|
3
4
|
HTTPRouteMap,
|
|
4
5
|
HTTPRouteContract,
|
|
@@ -84,7 +85,9 @@ function registerRoute(
|
|
|
84
85
|
timeout: route.timeout,
|
|
85
86
|
headers: route.headers,
|
|
86
87
|
sse: route.sse,
|
|
87
|
-
|
|
88
|
+
// `CoreHTTPFunctionWiring` is discriminated by `method`, and a group builds
|
|
89
|
+
// its routes from a method chosen at runtime — no arm can be narrowed to.
|
|
90
|
+
} as CoreHTTPFunctionWiring<unknown, unknown, string>)
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
function isRouteConfig(value: unknown): value is HTTPRouteConfig {
|
|
@@ -33,8 +33,10 @@ import { isProduction } from '../../env.js'
|
|
|
33
33
|
import { pikkuState } from '../../pikku-state.js'
|
|
34
34
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
|
|
35
35
|
import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
|
|
36
|
-
import type { PikkuChannel } from '../channel/channel.types.js'
|
|
37
|
-
|
|
36
|
+
import type { BinaryData, PikkuChannel } from '../channel/channel.types.js'
|
|
37
|
+
// The leaf module, not the channel-rpc barrel: http needs one refusal
|
|
38
|
+
// function, and the barrel drags in 4,932 lines of channel RPC runtime.
|
|
39
|
+
import { unsupportedChannelRemote } from '../channel/channel-rpc.types.js'
|
|
38
40
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
39
41
|
import { applyWebResponse } from './web-request.js'
|
|
40
42
|
import { httpRouter } from './routers/http-router.js'
|
|
@@ -68,26 +70,6 @@ export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
|
|
|
68
70
|
return middleware
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
/**
|
|
72
|
-
* @deprecated HTTP-route-level permissions were removed in #972 — permissions
|
|
73
|
-
* are now function-scoped only (declare them on the function via
|
|
74
|
-
* `pikkuFunc({ permissions })`). This throwing stub exists solely so the pinned
|
|
75
|
-
* bootstrap CLI (which still generates an `addHTTPPermission` wrapper) can
|
|
76
|
-
* resolve the import at build time; it is never called. Delete once
|
|
77
|
-
* `PIKKU_CLI_VERSION` in the CLI build is bumped past the release that removes
|
|
78
|
-
* HTTP-route permissions.
|
|
79
|
-
*/
|
|
80
|
-
export const addHTTPPermission = (
|
|
81
|
-
_pattern: string,
|
|
82
|
-
_permissions:
|
|
83
|
-
| Record<string, CorePikkuPermission | CorePikkuPermission[]>
|
|
84
|
-
| CorePikkuPermission[]
|
|
85
|
-
): never => {
|
|
86
|
-
throw new Error(
|
|
87
|
-
'addHTTPPermission was removed in #972 — HTTP-route-level permissions no longer exist. Declare permissions on the function definition instead: pikkuFunc({ permissions }).'
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
73
|
export const wireHTTP = <
|
|
92
74
|
In,
|
|
93
75
|
Out,
|
|
@@ -126,7 +108,8 @@ export const wireHTTP = <
|
|
|
126
108
|
}
|
|
127
109
|
pikkuState(null, 'http', 'routes')
|
|
128
110
|
.get(httpWiring.method)
|
|
129
|
-
|
|
111
|
+
// knowledge: decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md
|
|
112
|
+
?.set(httpWiring.route, httpWiring as CoreHTTPFunctionWiring<any, any, any>)
|
|
130
113
|
}
|
|
131
114
|
|
|
132
115
|
const getMatchingRoute = (requestType: string, requestPath: string) => {
|
|
@@ -261,7 +244,8 @@ const executeRoute = async (
|
|
|
261
244
|
setState: (s) => {
|
|
262
245
|
sseState = s
|
|
263
246
|
},
|
|
264
|
-
|
|
247
|
+
// knowledge: decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md
|
|
248
|
+
getState: () => sseState as never,
|
|
265
249
|
clearState: () => {
|
|
266
250
|
sseState = undefined
|
|
267
251
|
},
|
|
@@ -275,7 +259,7 @@ const executeRoute = async (
|
|
|
275
259
|
const channelHandler = {
|
|
276
260
|
getChannel: () => channelRef,
|
|
277
261
|
send: (data: unknown, isBinary?: boolean) => {
|
|
278
|
-
if (isBinary) channelRef.sendBinary(data as
|
|
262
|
+
if (isBinary) channelRef.sendBinary(data as BinaryData)
|
|
279
263
|
else channelRef.send(data)
|
|
280
264
|
},
|
|
281
265
|
sendBinary: (data: any) => channelRef.sendBinary(data),
|
|
@@ -424,7 +408,8 @@ export const fetchData = async <In, Out>(
|
|
|
424
408
|
globalMiddleware as CorePikkuMiddleware[]
|
|
425
409
|
)
|
|
426
410
|
}
|
|
427
|
-
|
|
411
|
+
// 204 carries no body, and `json` has no no-content overload.
|
|
412
|
+
response.status(204).json(undefined as never)
|
|
428
413
|
return
|
|
429
414
|
}
|
|
430
415
|
scopedLogger.info({
|
|
@@ -73,10 +73,6 @@ export interface PikkuHTTP<In = unknown> {
|
|
|
73
73
|
response?: PikkuHTTPResponse
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export type RequestHeaders =
|
|
77
|
-
| Record<string, string | string[] | undefined>
|
|
78
|
-
| ((headerName: string) => string | string[] | undefined)
|
|
79
|
-
|
|
80
76
|
export type PikkuQuery<T = Record<string, string | undefined>> = Record<
|
|
81
77
|
string,
|
|
82
78
|
string | T | null | Array<T | null>
|
|
@@ -207,17 +203,6 @@ export type HTTPWiringMeta = CommonWireMeta & {
|
|
|
207
203
|
}
|
|
208
204
|
export type HTTPWiringsMeta = Record<HTTPMethod, Record<string, HTTPWiringMeta>>
|
|
209
205
|
|
|
210
|
-
export type HTTPFunctionsMeta = Array<{
|
|
211
|
-
name: string
|
|
212
|
-
inputs: string[] | null
|
|
213
|
-
outputs: string[] | null
|
|
214
|
-
}>
|
|
215
|
-
|
|
216
|
-
export type HTTPWiringMiddleware = {
|
|
217
|
-
route: string
|
|
218
|
-
middleware: CorePikkuMiddleware[]
|
|
219
|
-
}
|
|
220
|
-
|
|
221
206
|
export interface PikkuHTTPRequest<In = unknown> {
|
|
222
207
|
method(): HTTPMethod
|
|
223
208
|
path(): string
|