@pikku/core 0.12.72 → 0.12.77
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 +1171 -0
- package/dist/column-form.d.ts +32 -0
- package/dist/column-form.js +42 -0
- package/dist/crypto-utils.d.ts +43 -7
- package/dist/crypto-utils.js +163 -42
- package/dist/data-classification.d.ts +44 -0
- package/dist/dev/hot-reload.js +11 -30
- package/dist/dev/module-runner.d.ts +3 -7
- package/dist/dev/module-runner.js +4 -10
- package/dist/dev/reload-meta.d.ts +8 -20
- package/dist/dev/reload-meta.js +9 -29
- package/dist/errors/error-handler.d.ts +5 -30
- package/dist/errors/error-handler.js +16 -32
- package/dist/errors/errors.d.ts +32 -151
- package/dist/errors/errors.js +55 -157
- package/dist/function/abort-scope.d.ts +47 -0
- package/dist/function/abort-scope.js +63 -0
- package/dist/function/function-runner.js +37 -32
- package/dist/function/functions.types.d.ts +57 -136
- package/dist/function/functions.types.js +0 -58
- package/dist/function/index.d.ts +1 -1
- package/dist/function/list.types.d.ts +12 -62
- package/dist/function/list.types.js +4 -25
- package/dist/handle-error.d.ts +0 -13
- package/dist/handle-error.js +0 -18
- package/dist/index.d.ts +11 -5
- package/dist/index.js +7 -2
- package/dist/middleware/auth-apikey.d.ts +3 -18
- package/dist/middleware/auth-apikey.js +0 -17
- package/dist/middleware/auth-bearer.d.ts +6 -41
- package/dist/middleware/auth-bearer.js +5 -41
- package/dist/middleware/auth-cookie.d.ts +5 -27
- package/dist/middleware/auth-cookie.js +2 -26
- package/dist/middleware/cors.d.ts +7 -34
- package/dist/middleware/cors.js +7 -34
- package/dist/middleware/remote-auth.d.ts +3 -1
- package/dist/middleware/remote-auth.js +4 -3
- package/dist/middleware/telemetry.d.ts +8 -33
- package/dist/middleware/telemetry.js +2 -31
- package/dist/middleware-runner.d.ts +4 -55
- package/dist/middleware-runner.js +5 -74
- package/dist/permissions.d.ts +3 -44
- package/dist/permissions.js +19 -71
- package/dist/pikku-request.d.ts +0 -6
- package/dist/pikku-request.js +0 -6
- package/dist/pikku-state.d.ts +0 -26
- package/dist/pikku-state.js +2 -30
- package/dist/remote.d.ts +3 -5
- package/dist/remote.js +9 -8
- package/dist/schema.d.ts +5 -39
- package/dist/schema.js +5 -39
- package/dist/scopes.d.ts +4 -23
- package/dist/scopes.js +7 -48
- package/dist/secret-value.d.ts +56 -0
- package/dist/secret-value.js +46 -0
- package/dist/services/ai-agent-runner-service.d.ts +20 -0
- package/dist/services/ai-embedding-service.d.ts +2 -25
- package/dist/services/audit-service.d.ts +74 -4
- package/dist/services/audit-service.js +8 -7
- package/dist/services/content-service.d.ts +1 -46
- package/dist/services/credential-service.d.ts +3 -40
- package/dist/services/credential-wire-service.d.ts +5 -0
- package/dist/services/credential-wire-service.js +9 -1
- package/dist/services/deployment-service.d.ts +3 -9
- package/dist/services/email-service.d.ts +2 -1
- package/dist/services/gateway-service.d.ts +0 -15
- package/dist/services/http-personas.d.ts +80 -0
- package/dist/services/http-personas.js +233 -0
- package/dist/services/in-memory-queue-service.d.ts +0 -14
- package/dist/services/in-memory-queue-service.js +1 -15
- package/dist/services/in-memory-trigger-service.d.ts +0 -18
- package/dist/services/in-memory-trigger-service.js +1 -18
- package/dist/services/in-memory-workflow-service.d.ts +0 -16
- package/dist/services/in-memory-workflow-service.js +4 -33
- package/dist/services/index.d.ts +8 -9
- package/dist/services/index.js +3 -6
- package/dist/services/istanbul-coverage-service.d.ts +1 -5
- package/dist/services/istanbul-coverage-service.js +2 -8
- package/dist/services/jwt-service.d.ts +1 -16
- package/dist/services/local-content-request-handler.d.ts +29 -0
- package/dist/services/local-content-request-handler.js +176 -0
- package/dist/services/local-content.d.ts +13 -2
- package/dist/services/local-content.js +40 -13
- package/dist/services/local-gateway-service.d.ts +0 -16
- package/dist/services/local-gateway-service.js +2 -17
- package/dist/services/local-secrets.d.ts +4 -7
- package/dist/services/local-secrets.js +7 -7
- package/dist/services/logger-console.d.ts +3 -7
- package/dist/services/logger-console.js +3 -7
- package/dist/services/logger.d.ts +22 -40
- package/dist/services/meta-service.d.ts +23 -26
- package/dist/services/meta-service.js +22 -36
- package/dist/services/personas-service.d.ts +134 -0
- package/dist/services/personas-service.js +40 -0
- package/dist/services/pikku-user-id.js +0 -4
- package/dist/services/queue-webhook-service.d.ts +2 -36
- package/dist/services/queue-webhook-service.js +10 -42
- package/dist/services/scheduler-service.d.ts +1 -50
- package/dist/services/scheduler-service.js +0 -10
- package/dist/services/schema-service.d.ts +1 -24
- package/dist/services/scope-service.d.ts +49 -34
- package/dist/services/scoped-secret-service.d.ts +4 -7
- package/dist/services/scoped-secret-service.js +0 -4
- package/dist/services/secret-host-binding.d.ts +8 -0
- package/dist/services/secret-host-binding.js +36 -0
- package/dist/services/secret-service.d.ts +12 -35
- package/dist/services/secretless.d.ts +6 -0
- package/dist/services/secretless.js +21 -0
- package/dist/services/stub-tracker.d.ts +7 -18
- package/dist/services/stub-tracker.js +8 -18
- package/dist/services/system-role-guard.d.ts +33 -0
- package/dist/services/system-role-guard.js +38 -0
- package/dist/services/trigger-service.d.ts +0 -12
- package/dist/services/typed-secret-service.d.ts +5 -11
- package/dist/services/typed-secret-service.js +1 -7
- package/dist/services/v8-coverage-service.d.ts +2 -3
- package/dist/services/v8-coverage-service.js +1 -2
- package/dist/services/variables-service.d.ts +1 -8
- package/dist/services/webhook-service.d.ts +21 -64
- package/dist/services/webhook-service.js +6 -20
- package/dist/services/workflow-service.d.ts +3 -15
- package/dist/testing/service-tests.js +6 -23
- package/dist/time-utils.d.ts +0 -16
- package/dist/time-utils.js +1 -19
- package/dist/types/core.types.d.ts +120 -219
- package/dist/types/core.types.js +0 -42
- package/dist/types/state.types.d.ts +4 -9
- package/dist/utils/hmac.d.ts +4 -10
- package/dist/utils/hmac.js +4 -10
- package/dist/utils/safe-fetch.d.ts +7 -35
- package/dist/utils/safe-fetch.js +13 -53
- package/dist/utils.d.ts +1 -6
- package/dist/utils.js +6 -15
- package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
- package/dist/wirings/actor-flow/index.d.ts +0 -9
- package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
- package/dist/wirings/actor-flow/run-conversation.js +14 -7
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-agui.js +46 -12
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
- package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
- package/dist/wirings/ai-agent/ai-agent-prepare.js +65 -132
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +63 -22
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +180 -64
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +124 -4
- package/dist/wirings/ai-agent/index.d.ts +6 -4
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.d.ts +59 -1
- package/dist/wirings/ai-agent/voice-input.js +90 -12
- package/dist/wirings/ai-agent/voice-output.d.ts +69 -1
- package/dist/wirings/ai-agent/voice-output.js +162 -50
- package/dist/wirings/channel/channel-common.d.ts +7 -20
- package/dist/wirings/channel/channel-common.js +7 -21
- package/dist/wirings/channel/channel-handler.js +25 -6
- package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
- package/dist/wirings/channel/channel-host-rpc.js +38 -0
- package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
- package/dist/wirings/channel/channel-middleware-runner.js +0 -12
- package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
- package/dist/wirings/channel/channel-rpc-registry.js +89 -0
- package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
- package/dist/wirings/channel/channel-rpc-responder.js +71 -0
- package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
- package/dist/wirings/channel/channel-rpc-service.js +106 -0
- package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
- package/dist/wirings/channel/channel-rpc-validators.js +30 -0
- package/dist/wirings/channel/channel-rpc.d.ts +5 -0
- package/dist/wirings/channel/channel-rpc.js +5 -0
- package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
- package/dist/wirings/channel/channel-rpc.types.js +50 -0
- package/dist/wirings/channel/channel-runner.d.ts +0 -4
- package/dist/wirings/channel/channel-runner.js +0 -14
- package/dist/wirings/channel/channel-store.d.ts +0 -10
- package/dist/wirings/channel/channel.types.d.ts +12 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
- package/dist/wirings/channel/define-channel-routes.js +0 -20
- package/dist/wirings/channel/eventhub-service.d.ts +0 -18
- package/dist/wirings/channel/index.d.ts +4 -1
- package/dist/wirings/channel/index.js +2 -0
- package/dist/wirings/channel/local/local-channel-runner.js +3 -1
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
- package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
- package/dist/wirings/channel/log-channels.d.ts +0 -4
- package/dist/wirings/channel/log-channels.js +0 -4
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
- package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
- package/dist/wirings/cli/channel/cli-approval.js +81 -0
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
- package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +38 -0
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +129 -0
- package/dist/wirings/cli/channel/index.d.ts +5 -0
- package/dist/wirings/cli/channel/index.js +2 -0
- package/dist/wirings/cli/cli-runner.d.ts +20 -20
- package/dist/wirings/cli/cli-runner.js +28 -89
- package/dist/wirings/cli/cli.types.d.ts +14 -3
- package/dist/wirings/cli/command-parser.d.ts +1 -10
- package/dist/wirings/cli/command-parser.js +10 -87
- package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
- package/dist/wirings/cli/define-cli-commands.js +1 -17
- package/dist/wirings/credential/credential.types.d.ts +0 -12
- package/dist/wirings/credential/define-credential.d.ts +48 -0
- package/dist/wirings/credential/define-credential.js +47 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/credential/index.js +1 -1
- package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
- package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
- package/dist/wirings/gateway/gateway-runner.js +8 -105
- package/dist/wirings/gateway/gateway.types.d.ts +7 -80
- package/dist/wirings/http/http-routes.d.ts +0 -63
- package/dist/wirings/http/http-routes.js +0 -63
- package/dist/wirings/http/http-runner.d.ts +0 -99
- package/dist/wirings/http/http-runner.js +9 -165
- package/dist/wirings/http/http.types.d.ts +14 -55
- package/dist/wirings/http/log-http-routes.d.ts +0 -4
- package/dist/wirings/http/log-http-routes.js +0 -4
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +0 -40
- package/dist/wirings/http/pikku-fetch-http-request.js +0 -58
- package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
- package/dist/wirings/http/routers/path-to-regex.js +2 -13
- package/dist/wirings/http/web-request.d.ts +0 -8
- package/dist/wirings/http/web-request.js +25 -17
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -4
- package/dist/wirings/mcp/mcp-runner.js +1 -14
- package/dist/wirings/mcp/mcp.types.d.ts +2 -35
- package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
- package/dist/wirings/oauth2/oauth2.types.js +0 -3
- package/dist/wirings/persona/define-personas.d.ts +32 -0
- package/dist/wirings/persona/define-personas.js +31 -0
- package/dist/wirings/persona/index.d.ts +21 -0
- package/dist/wirings/persona/index.js +17 -0
- package/dist/wirings/persona/persona-email.d.ts +37 -0
- package/dist/wirings/persona/persona-email.js +69 -0
- package/dist/wirings/persona/persona-environments.d.ts +45 -0
- package/dist/wirings/persona/persona-environments.js +81 -0
- package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
- package/dist/wirings/persona/persona-mailbox.js +53 -0
- package/dist/wirings/persona/persona.types.d.ts +136 -0
- package/dist/wirings/persona/persona.types.js +1 -0
- package/dist/wirings/persona/validate-personas.d.ts +53 -0
- package/dist/wirings/persona/validate-personas.js +94 -0
- package/dist/wirings/queue/index.d.ts +3 -0
- package/dist/wirings/queue/index.js +2 -3
- package/dist/wirings/queue/queue-identity.d.ts +28 -0
- package/dist/wirings/queue/queue-identity.js +103 -0
- package/dist/wirings/queue/queue-runner.d.ts +0 -19
- package/dist/wirings/queue/queue-runner.js +9 -30
- package/dist/wirings/queue/queue.types.d.ts +20 -90
- package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
- package/dist/wirings/queue/register-queue-helper.js +0 -11
- package/dist/wirings/queue/signed-queue-service.d.ts +17 -0
- package/dist/wirings/queue/signed-queue-service.js +42 -0
- package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
- package/dist/wirings/queue/validate-worker-config.js +0 -14
- package/dist/wirings/role/define-system-role.d.ts +32 -0
- package/dist/wirings/role/define-system-role.js +31 -0
- package/dist/wirings/role/index.d.ts +3 -0
- package/dist/wirings/role/index.js +2 -0
- package/dist/wirings/role/role.types.d.ts +43 -0
- package/dist/wirings/role/role.types.js +1 -0
- package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
- package/dist/wirings/role/validate-role-definitions.js +71 -0
- package/dist/wirings/rpc/addon-runner.d.ts +0 -19
- package/dist/wirings/rpc/addon-runner.js +0 -51
- package/dist/wirings/rpc/remote-addon-auth.d.ts +3 -13
- package/dist/wirings/rpc/remote-addon-auth.js +7 -11
- package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
- package/dist/wirings/rpc/rpc-runner.js +88 -105
- package/dist/wirings/rpc/rpc-types.d.ts +7 -6
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +62 -0
- package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
- package/dist/wirings/rpc/wire-remote-addon.js +0 -8
- package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
- package/dist/wirings/scheduler/log-schedulers.js +0 -4
- package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
- package/dist/wirings/scheduler/scheduler-runner.js +0 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
- package/dist/wirings/scope/define-scope.d.ts +32 -0
- package/dist/wirings/scope/define-scope.js +31 -0
- package/dist/wirings/scope/index.d.ts +1 -1
- package/dist/wirings/scope/index.js +1 -1
- package/dist/wirings/scope/scope.types.d.ts +7 -9
- package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
- package/dist/wirings/scope/validate-scope-definitions.js +3 -21
- package/dist/wirings/secret/index.d.ts +1 -1
- package/dist/wirings/secret/index.js +1 -1
- package/dist/wirings/secret/secret.types.d.ts +19 -15
- package/dist/wirings/secret/secret.types.js +1 -1
- package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
- package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
- package/dist/wirings/trigger/trigger-runner.js +1 -24
- package/dist/wirings/trigger/trigger.types.d.ts +1 -82
- package/dist/wirings/trigger/trigger.types.js +0 -34
- package/dist/wirings/variable/index.d.ts +1 -1
- package/dist/wirings/variable/index.js +1 -1
- package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
- package/dist/wirings/variable/variable.types.d.ts +1 -13
- package/dist/wirings/variable/variable.types.js +1 -1
- package/dist/wirings/virtual-user/index.d.ts +30 -0
- package/dist/wirings/virtual-user/index.js +10 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +54 -0
- package/dist/wirings/virtual-user/prepare-virtual-user-run.js +49 -0
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
- package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
- package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
- package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
- package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
- package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
- package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
- package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +90 -0
- package/dist/wirings/virtual-user/virtual-user-run-store.js +1 -0
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
- package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
- package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +19 -15
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
- package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
- package/dist/wirings/workflow/feature.d.ts +0 -19
- package/dist/wirings/workflow/feature.js +0 -19
- package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
- package/dist/wirings/workflow/graph/graph-node.js +0 -34
- package/dist/wirings/workflow/graph/graph-runner.js +6 -41
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
- package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
- package/dist/wirings/workflow/index.d.ts +5 -7
- package/dist/wirings/workflow/index.js +3 -17
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
- package/dist/wirings/workflow/pikku-scenario-service.js +204 -43
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
- package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
- package/dist/wirings/workflow/run-timeline.d.ts +0 -47
- package/dist/wirings/workflow/run-timeline.js +0 -22
- package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
- package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
- package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
- package/dist/wirings/workflow/scenario-poll.js +0 -12
- package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
- package/dist/wirings/workflow/scenario-prose.js +1 -19
- package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
- package/dist/wirings/workflow/scenario-step-guards.js +1 -14
- package/dist/wirings/workflow/scenario-step.types.d.ts +84 -12
- package/dist/wirings/workflow/scenario-step.types.js +5 -1
- package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
- package/dist/wirings/workflow/scenario-surface.js +56 -0
- package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
- package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
- package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
- package/dist/wirings/workflow/workflow.types.d.ts +5 -195
- package/knowledge/decisions/index.md +19 -0
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-and-not-a-queued-job.md +48 -0
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
- package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
- package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
- package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
- package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
- package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
- package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
- package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
- package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
- package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
- package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
- package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
- package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
- package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
- package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
- package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
- package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +84 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +41 -0
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
- package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
- package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
- package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
- package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
- package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
- package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
- package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
- package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
- package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
- package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
- package/knowledge/decisions/internals/index.md +117 -0
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
- package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
- package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +53 -0
- package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
- package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +48 -0
- package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
- package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
- package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
- package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
- package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
- package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
- package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
- package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
- package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +43 -0
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
- package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
- package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
- package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
- package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
- package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
- package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
- package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
- package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
- package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
- package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
- package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
- package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
- package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
- package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
- package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
- package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
- package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
- package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
- package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
- package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
- package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
- package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
- package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
- package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
- package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
- package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
- package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
- package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
- package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
- package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
- package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
- package/knowledge/decisions/security/index.md +55 -0
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
- package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
- package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
- package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
- package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
- package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
- package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
- package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
- package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
- package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
- package/knowledge/index.md +24 -0
- package/knowledge/questions/index.md +15 -0
- package/package.json +6 -2
- package/run-tests.sh +0 -0
- package/src/column-form.test.ts +97 -0
- package/src/column-form.ts +58 -0
- package/src/crypto-utils.test.ts +460 -19
- package/src/crypto-utils.ts +306 -59
- package/src/data-classification.ts +45 -7
- package/src/dev/hot-reload.test.ts +0 -4
- package/src/dev/hot-reload.ts +11 -30
- package/src/dev/module-runner.ts +7 -32
- package/src/dev/reload-meta.ts +9 -29
- package/src/errors/error-handler.ts +20 -35
- package/src/errors/error.test.ts +30 -1
- package/src/errors/errors.ts +73 -157
- package/src/function/abort-scope.test.ts +97 -0
- package/src/function/abort-scope.ts +80 -0
- package/src/function/function-runner.test.ts +0 -7
- package/src/function/function-runner.ts +62 -32
- package/src/function/functions.types.ts +95 -138
- package/src/function/index.ts +1 -0
- package/src/function/list.types.test.ts +3 -25
- package/src/function/list.types.ts +12 -62
- package/src/handle-error.ts +0 -18
- package/src/index.ts +84 -3
- package/src/middleware/auth-apikey.test.ts +0 -1
- package/src/middleware/auth-apikey.ts +0 -17
- package/src/middleware/auth-bearer.test.ts +3 -5
- package/src/middleware/auth-bearer.ts +5 -41
- package/src/middleware/auth-cookie.test.ts +0 -6
- package/src/middleware/auth-cookie.ts +2 -26
- package/src/middleware/cors.test.ts +34 -0
- package/src/middleware/cors.ts +12 -33
- package/src/middleware/remote-auth.test.ts +26 -10
- package/src/middleware/remote-auth.ts +11 -3
- package/src/middleware/telemetry.ts +2 -31
- package/src/middleware-runner.test.ts +0 -2
- package/src/middleware-runner.ts +5 -74
- package/src/permissions.test.ts +30 -0
- package/src/permissions.ts +24 -74
- package/src/pikku-request.ts +0 -6
- package/src/pikku-state.ts +2 -30
- package/src/production-barrels-stay-lean.test.ts +110 -0
- package/src/remote.test.ts +173 -0
- package/src/remote.ts +18 -8
- package/src/schema.ts +5 -39
- package/src/scopes.ts +7 -48
- package/src/secret-value.test.ts +204 -0
- package/src/secret-value.ts +111 -0
- package/src/services/ai-agent-runner-service.ts +20 -0
- package/src/services/ai-embedding-service.ts +3 -25
- package/src/services/audit-service.ts +88 -11
- package/src/services/content-service.ts +1 -46
- package/src/services/credential-service.ts +3 -40
- package/src/services/credential-wire-service.test.ts +0 -2
- package/src/services/credential-wire-service.ts +9 -1
- package/src/services/deployment-service.ts +3 -9
- package/src/services/email-service.ts +3 -1
- package/src/services/gateway-service.ts +0 -15
- package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
- package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
- package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
- package/src/services/in-memory-queue-service.ts +1 -15
- package/src/services/in-memory-trigger-service.ts +1 -18
- package/src/services/in-memory-workflow-service.test.ts +0 -13
- package/src/services/in-memory-workflow-service.ts +4 -38
- package/src/services/index.ts +23 -18
- package/src/services/istanbul-coverage-service.ts +2 -8
- package/src/services/jwt-service.ts +1 -16
- package/src/services/local-content-request-handler.test.ts +202 -0
- package/src/services/local-content-request-handler.ts +267 -0
- package/src/services/local-content.test.ts +159 -27
- package/src/services/local-content.ts +55 -23
- package/src/services/local-gateway-service.ts +2 -17
- package/src/services/local-secrets.test.ts +20 -5
- package/src/services/local-secrets.ts +15 -11
- package/src/services/logger-console.test.ts +0 -1
- package/src/services/logger-console.ts +3 -7
- package/src/services/logger.ts +31 -46
- package/src/services/meta-service.test.ts +1 -5
- package/src/services/meta-service.ts +41 -61
- package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
- package/src/services/pikku-user-id.ts +0 -4
- package/src/services/queue-webhook-service.test.ts +2 -1
- package/src/services/queue-webhook-service.ts +10 -42
- package/src/services/scheduler-service.ts +1 -50
- package/src/services/schema-service.ts +1 -24
- package/src/services/scope-service.ts +50 -34
- package/src/services/scoped-secret-service.ts +4 -7
- package/src/services/secret-host-binding.test.ts +138 -0
- package/src/services/secret-host-binding.ts +51 -0
- package/src/services/secret-service.ts +12 -35
- package/src/services/secretless.test.ts +54 -0
- package/src/services/secretless.ts +29 -0
- package/src/services/stub-tracker.ts +8 -18
- package/src/services/system-role-guard.test.ts +93 -0
- package/src/services/system-role-guard.ts +71 -0
- package/src/services/trigger-service.ts +0 -12
- package/src/services/typed-secret-service.ts +12 -14
- package/src/services/v8-coverage-service.ts +3 -6
- package/src/services/variables-service.ts +1 -8
- package/src/services/webhook-service.ts +23 -64
- package/src/services/workflow-service.ts +3 -20
- package/src/testing/service-tests.ts +6 -32
- package/src/time-utils.ts +1 -19
- package/src/types/core.types.ts +137 -229
- package/src/types/state.types.ts +7 -9
- package/src/utils/hmac.ts +4 -10
- package/src/utils/safe-fetch.ts +13 -54
- package/src/utils.test.ts +11 -2
- package/src/utils.ts +6 -15
- package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
- package/src/wirings/actor-flow/index.ts +0 -9
- package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
- package/src/wirings/actor-flow/run-conversation.ts +19 -12
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +91 -10
- package/src/wirings/ai-agent/ai-agent-agui.ts +49 -17
- package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
- package/src/wirings/ai-agent/ai-agent-prepare.ts +89 -139
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
- package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
- package/src/wirings/ai-agent/ai-agent-runner.ts +85 -35
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
- package/src/wirings/ai-agent/ai-agent-stream.ts +224 -76
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +130 -5
- package/src/wirings/ai-agent/index.ts +37 -3
- package/src/wirings/ai-agent/voice-input.test.ts +137 -7
- package/src/wirings/ai-agent/voice-input.ts +96 -12
- package/src/wirings/ai-agent/voice-output.test.ts +512 -0
- package/src/wirings/ai-agent/voice-output.ts +243 -56
- package/src/wirings/channel/channel-common.ts +15 -20
- package/src/wirings/channel/channel-handler.test.ts +50 -0
- package/src/wirings/channel/channel-handler.ts +32 -11
- package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
- package/src/wirings/channel/channel-host-rpc.ts +69 -0
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
- package/src/wirings/channel/channel-middleware-runner.ts +0 -12
- package/src/wirings/channel/channel-rpc-registry.ts +116 -0
- package/src/wirings/channel/channel-rpc-responder.ts +117 -0
- package/src/wirings/channel/channel-rpc-service.ts +146 -0
- package/src/wirings/channel/channel-rpc-validators.ts +65 -0
- package/src/wirings/channel/channel-rpc.test.ts +820 -0
- package/src/wirings/channel/channel-rpc.ts +5 -0
- package/src/wirings/channel/channel-rpc.types.ts +150 -0
- package/src/wirings/channel/channel-runner.ts +0 -14
- package/src/wirings/channel/channel-store.ts +0 -10
- package/src/wirings/channel/channel.types.ts +19 -8
- package/src/wirings/channel/define-channel-routes.ts +0 -20
- package/src/wirings/channel/eventhub-service.ts +0 -18
- package/src/wirings/channel/index.ts +35 -0
- package/src/wirings/channel/local/local-channel-handler.ts +3 -1
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
- package/src/wirings/channel/local/local-channel-runner.ts +3 -1
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
- package/src/wirings/channel/log-channels.ts +0 -4
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
- package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
- package/src/wirings/cli/channel/cli-approval.ts +135 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +185 -0
- package/src/wirings/cli/channel/index.ts +13 -0
- package/src/wirings/cli/cli-runner.test.ts +0 -1
- package/src/wirings/cli/cli-runner.ts +46 -88
- package/src/wirings/cli/cli.types.ts +14 -3
- package/src/wirings/cli/command-parser.test.ts +0 -4
- package/src/wirings/cli/command-parser.ts +11 -91
- package/src/wirings/cli/define-cli-commands.ts +1 -17
- package/src/wirings/credential/credential.types.ts +0 -12
- package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
- package/src/wirings/credential/index.ts +1 -1
- package/src/wirings/credential/validate-credential-definitions.ts +2 -4
- package/src/wirings/gateway/gateway-runner.test.ts +1 -21
- package/src/wirings/gateway/gateway-runner.ts +8 -110
- package/src/wirings/gateway/gateway.types.ts +7 -80
- package/src/wirings/http/http-routes.test.ts +0 -3
- package/src/wirings/http/http-routes.ts +0 -86
- package/src/wirings/http/http-runner.test.ts +0 -1
- package/src/wirings/http/http-runner.ts +8 -167
- package/src/wirings/http/http.types.ts +15 -62
- package/src/wirings/http/log-http-routes.ts +0 -4
- package/src/wirings/http/pikku-fetch-http-request.test.ts +2 -10
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -58
- package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
- package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
- package/src/wirings/http/routers/path-to-regex.ts +2 -13
- package/src/wirings/http/web-request.test.ts +33 -2
- package/src/wirings/http/web-request.ts +30 -17
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -17
- package/src/wirings/mcp/mcp.types.ts +7 -42
- package/src/wirings/oauth2/oauth2.types.ts +0 -30
- package/src/wirings/persona/define-personas.ts +33 -0
- package/src/wirings/persona/index.ts +62 -0
- package/src/wirings/persona/persona-email.ts +87 -0
- package/src/wirings/persona/persona-environments.test.ts +183 -0
- package/src/wirings/persona/persona-environments.ts +138 -0
- package/src/wirings/persona/persona-mailbox.ts +156 -0
- package/src/wirings/persona/persona.test.ts +220 -0
- package/src/wirings/persona/persona.types.ts +142 -0
- package/src/wirings/persona/validate-personas.ts +133 -0
- package/src/wirings/queue/index.ts +13 -3
- package/src/wirings/queue/queue-identity.test.ts +454 -0
- package/src/wirings/queue/queue-identity.ts +176 -0
- package/src/wirings/queue/queue-runner.ts +12 -31
- package/src/wirings/queue/queue.types.ts +25 -90
- package/src/wirings/queue/register-queue-helper.ts +0 -14
- package/src/wirings/queue/signed-queue-service.ts +60 -0
- package/src/wirings/queue/validate-worker-config.ts +2 -28
- package/src/wirings/role/define-system-role.ts +33 -0
- package/src/wirings/role/index.ts +13 -0
- package/src/wirings/role/role.test.ts +104 -0
- package/src/wirings/role/role.types.ts +47 -0
- package/src/wirings/role/validate-role-definitions.ts +93 -0
- package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
- package/src/wirings/rpc/addon-runner.ts +0 -56
- package/src/wirings/rpc/addon-scopes.test.ts +225 -0
- package/src/wirings/rpc/remote-addon-auth.ts +9 -16
- package/src/wirings/rpc/rpc-runner.test.ts +192 -6
- package/src/wirings/rpc/rpc-runner.ts +145 -127
- package/src/wirings/rpc/rpc-types.ts +11 -6
- package/src/wirings/rpc/wire-addon.test.ts +43 -1
- package/src/wirings/rpc/wire-addon.ts +99 -0
- package/src/wirings/rpc/wire-remote-addon.ts +9 -29
- package/src/wirings/scheduler/log-schedulers.ts +0 -4
- package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
- package/src/wirings/scheduler/scheduler-runner.ts +0 -2
- package/src/wirings/scheduler/scheduler.types.ts +1 -14
- package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
- package/src/wirings/scope/index.ts +1 -1
- package/src/wirings/scope/scope.test.ts +1 -2
- package/src/wirings/scope/scope.types.ts +7 -9
- package/src/wirings/scope/validate-scope-definitions.ts +3 -21
- package/src/wirings/secret/index.ts +1 -1
- package/src/wirings/secret/secret.types.ts +19 -15
- package/src/wirings/secret/validate-secret-definitions.ts +2 -4
- package/src/wirings/trigger/trigger-runner.ts +1 -27
- package/src/wirings/trigger/trigger.types.ts +1 -82
- package/src/wirings/variable/index.ts +1 -1
- package/src/wirings/variable/validate-variable-definitions.ts +2 -4
- package/src/wirings/variable/variable.types.ts +1 -13
- package/src/wirings/virtual-user/index.ts +88 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +115 -0
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +95 -0
- package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
- package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
- package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
- package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
- package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
- package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
- package/src/wirings/virtual-user/virtual-user-run-store.ts +98 -0
- package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
- package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +19 -20
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
- package/src/wirings/workflow/feature.ts +0 -19
- package/src/wirings/workflow/graph/graph-node.ts +0 -136
- package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
- package/src/wirings/workflow/graph/graph-runner.ts +6 -41
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
- package/src/wirings/workflow/index.ts +10 -44
- package/src/wirings/workflow/pikku-scenario-service.ts +235 -61
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
- package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
- package/src/wirings/workflow/run-timeline.test.ts +7 -19
- package/src/wirings/workflow/run-timeline.ts +0 -56
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
- package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
- package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
- package/src/wirings/workflow/scenario-poll.test.ts +0 -2
- package/src/wirings/workflow/scenario-poll.ts +0 -15
- package/src/wirings/workflow/scenario-prose.test.ts +5 -7
- package/src/wirings/workflow/scenario-prose.ts +1 -29
- package/src/wirings/workflow/scenario-service.test.ts +2 -10
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +163 -19
- package/src/wirings/workflow/scenario-step.types.ts +94 -12
- package/src/wirings/workflow/scenario-surface.test.ts +146 -0
- package/src/wirings/workflow/scenario-surface.ts +71 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
- package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
- package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
- package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
- package/src/wirings/workflow/workflow.types.ts +5 -201
- package/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.type-tests.json +12 -0
- package/src/middleware/timeout.ts +0 -22
- package/src/pikku-response.ts +0 -5
- package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
- package/src/wirings/workflow/dsl/index.ts +0 -36
- package/src/wirings/workflow/graph/index.ts +0 -15
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { join, dirname, resolve, relative } from 'node:path'
|
|
6
|
+
|
|
7
|
+
const srcRoot = dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Statements that only move types. TypeScript erases them, so they cost a
|
|
11
|
+
* production bundle nothing and are deliberately not followed.
|
|
12
|
+
*/
|
|
13
|
+
const TYPE_ONLY = /^\s*(?:import|export)\s+type\b/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The modules a production server must never load by importing a barrel.
|
|
17
|
+
*
|
|
18
|
+
* Each is scenario or virtual-user runtime: `http-personas` signs personas in
|
|
19
|
+
* and reaches the actor-flow conversation runner, which reaches the agent
|
|
20
|
+
* runner. None of it runs in production, and an unbundled Node or Lambda deploy
|
|
21
|
+
* does no tree-shaking — it loads whatever the graph names.
|
|
22
|
+
*/
|
|
23
|
+
const FORBIDDEN = [
|
|
24
|
+
'services/http-personas.ts',
|
|
25
|
+
'wirings/actor-flow/run-conversation.ts',
|
|
26
|
+
'wirings/workflow/pikku-scenario-service.ts',
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const resolveSpecifier = (fromFile: string, specifier: string) => {
|
|
30
|
+
const base = resolve(dirname(fromFile), specifier.replace(/\.js$/, ''))
|
|
31
|
+
for (const candidate of [`${base}.ts`, join(base, 'index.ts')]) {
|
|
32
|
+
if (existsSync(candidate)) return candidate
|
|
33
|
+
}
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Every module loaded as a side effect of importing `entry`, following value
|
|
39
|
+
* imports only.
|
|
40
|
+
*
|
|
41
|
+
* A dynamic `await import(...)` is not followed: it does not run at module
|
|
42
|
+
* init, which is the whole reason `pikku-scenario-service` reaches
|
|
43
|
+
* `http-personas` that way.
|
|
44
|
+
*/
|
|
45
|
+
const runtimeGraph = (entry: string): Set<string> => {
|
|
46
|
+
const seen = new Set<string>()
|
|
47
|
+
const queue = [entry]
|
|
48
|
+
|
|
49
|
+
while (queue.length) {
|
|
50
|
+
const file = queue.pop()!
|
|
51
|
+
if (seen.has(file)) continue
|
|
52
|
+
seen.add(file)
|
|
53
|
+
|
|
54
|
+
for (const line of readFileSync(file, 'utf-8').split('\n')) {
|
|
55
|
+
if (TYPE_ONLY.test(line)) continue
|
|
56
|
+
const specifier = line.match(/\bfrom\s+'(\.[^']+)'/)?.[1]
|
|
57
|
+
if (!specifier) continue
|
|
58
|
+
const target = resolveSpecifier(file, specifier)
|
|
59
|
+
if (target) queue.push(target)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return seen
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe('production barrels stay lean', () => {
|
|
67
|
+
for (const barrel of ['services/index.ts', 'wirings/workflow/index.ts']) {
|
|
68
|
+
test(`@pikku/core ${barrel} does not load scenario runtime`, () => {
|
|
69
|
+
const reached = runtimeGraph(join(srcRoot, barrel))
|
|
70
|
+
const offenders = FORBIDDEN.filter((module) =>
|
|
71
|
+
reached.has(join(srcRoot, module))
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
assert.deepEqual(
|
|
75
|
+
offenders,
|
|
76
|
+
[],
|
|
77
|
+
`${barrel} pulls scenario runtime into every app that imports it:\n` +
|
|
78
|
+
`${offenders.join('\n')}\n` +
|
|
79
|
+
`Export the value from '@pikku/core/persona' instead — types may stay here.`
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
test('the persona entry point still exports the runtime that moved', async () => {
|
|
85
|
+
const persona = await import('./wirings/persona/index.js')
|
|
86
|
+
|
|
87
|
+
for (const name of [
|
|
88
|
+
'createHttpPersonas',
|
|
89
|
+
'HttpPersona',
|
|
90
|
+
'postScenarioJson',
|
|
91
|
+
'readScenarioHttpResponse',
|
|
92
|
+
]) {
|
|
93
|
+
assert.ok(
|
|
94
|
+
name in persona,
|
|
95
|
+
`@pikku/core/persona no longer exports ${name}; it has to live somewhere`
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('the graph walker actually reaches things', () => {
|
|
101
|
+
// Guards the test above: a walker that silently resolved nothing would pass
|
|
102
|
+
// no matter what the barrels imported.
|
|
103
|
+
const reached = runtimeGraph(join(srcRoot, 'wirings/persona/index.ts'))
|
|
104
|
+
assert.ok(
|
|
105
|
+
reached.has(join(srcRoot, 'services/http-personas.ts')),
|
|
106
|
+
`walker failed to reach a known value import; reached ${reached.size} files:\n` +
|
|
107
|
+
[...reached].map((f) => relative(srcRoot, f)).join('\n')
|
|
108
|
+
)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, test, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { buildRemoteHeaders } from './remote.js'
|
|
4
|
+
import { pikkuRemoteAuthMiddleware } from './middleware/remote-auth.js'
|
|
5
|
+
import { resetPikkuState } from './pikku-state.js'
|
|
6
|
+
import { WeakKeyMaterialError } from './errors/errors.js'
|
|
7
|
+
import { createSecretValue } from './secret-value.js'
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
resetPikkuState()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const STRONG_SECRET = 'a'.repeat(43)
|
|
14
|
+
const WEAK_SECRET = 'dev-remote-secret'
|
|
15
|
+
|
|
16
|
+
const createSecrets = (secret: string) => ({
|
|
17
|
+
getSecret: async (key: string) => {
|
|
18
|
+
if (key === 'PIKKU_REMOTE_SECRET') return createSecretValue(secret)
|
|
19
|
+
throw new Error(`Secret ${key} not found`)
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Signing is deliberately free here so the measurement isolates key derivation.
|
|
25
|
+
*/
|
|
26
|
+
const createJWT = () => ({
|
|
27
|
+
encode: async (_expiresIn: unknown, payload: unknown) =>
|
|
28
|
+
Buffer.from(JSON.stringify(payload)).toString('base64url'),
|
|
29
|
+
decode: async (token: string) =>
|
|
30
|
+
JSON.parse(Buffer.from(token, 'base64url').toString('utf-8')),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const createRequest = (
|
|
34
|
+
headers: Record<string, string>,
|
|
35
|
+
path = '/remote/rpc/myFunc'
|
|
36
|
+
) => ({
|
|
37
|
+
header: (name: string) => headers[name.toLowerCase()] || headers[name],
|
|
38
|
+
path: () => path,
|
|
39
|
+
method: () => 'POST',
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const roundTrip = async (secret: string, session: unknown) => {
|
|
43
|
+
const jwt = createJWT()
|
|
44
|
+
const secrets = createSecrets(secret)
|
|
45
|
+
|
|
46
|
+
const headers = await buildRemoteHeaders(
|
|
47
|
+
jwt as any,
|
|
48
|
+
secrets as any,
|
|
49
|
+
'myFunc',
|
|
50
|
+
session
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
let received: unknown
|
|
54
|
+
await pikkuRemoteAuthMiddleware(
|
|
55
|
+
{ secrets, jwt } as any,
|
|
56
|
+
{
|
|
57
|
+
http: {
|
|
58
|
+
request: createRequest({ authorization: headers.authorization! }),
|
|
59
|
+
},
|
|
60
|
+
setSession: async (s: unknown) => {
|
|
61
|
+
received = s
|
|
62
|
+
},
|
|
63
|
+
} as any,
|
|
64
|
+
async () => {}
|
|
65
|
+
)
|
|
66
|
+
return received
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe('remote RPC session round trip', () => {
|
|
70
|
+
test('round-trips the session through header build and middleware verify', async () => {
|
|
71
|
+
const session = { userId: 'user-1', role: 'admin' }
|
|
72
|
+
assert.deepStrictEqual(await roundTrip(STRONG_SECRET, session), session)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('a header built with one secret is rejected by a peer holding another', async () => {
|
|
76
|
+
const jwt = createJWT()
|
|
77
|
+
const headers = await buildRemoteHeaders(
|
|
78
|
+
jwt as any,
|
|
79
|
+
createSecrets(STRONG_SECRET) as any,
|
|
80
|
+
'myFunc',
|
|
81
|
+
{ userId: 'user-1' }
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
await assert.rejects(() =>
|
|
85
|
+
pikkuRemoteAuthMiddleware(
|
|
86
|
+
{ secrets: createSecrets('b'.repeat(43)), jwt } as any,
|
|
87
|
+
{
|
|
88
|
+
http: {
|
|
89
|
+
request: createRequest({ authorization: headers.authorization! }),
|
|
90
|
+
},
|
|
91
|
+
setSession: async () => {},
|
|
92
|
+
} as any,
|
|
93
|
+
async () => {}
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('a full round trip stays under the per-hop latency budget', async () => {
|
|
99
|
+
const session = { userId: 'user-1', role: 'admin' }
|
|
100
|
+
|
|
101
|
+
// Warm up so the measurement excludes module/JIT startup.
|
|
102
|
+
await roundTrip(STRONG_SECRET, session)
|
|
103
|
+
|
|
104
|
+
const iterations = 5
|
|
105
|
+
const start = performance.now()
|
|
106
|
+
for (let i = 0; i < iterations; i++) {
|
|
107
|
+
await roundTrip(STRONG_SECRET, session)
|
|
108
|
+
}
|
|
109
|
+
const perRoundTrip = (performance.now() - start) / iterations
|
|
110
|
+
|
|
111
|
+
assert.ok(
|
|
112
|
+
perRoundTrip < 20,
|
|
113
|
+
`remote RPC round trip took ${perRoundTrip.toFixed(1)}ms, budget is 20ms`
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('PIKKU_REMOTE_SECRET strength', () => {
|
|
119
|
+
test('buildRemoteHeaders rejects a secret shorter than 32 characters', async () => {
|
|
120
|
+
await assert.rejects(
|
|
121
|
+
() =>
|
|
122
|
+
buildRemoteHeaders(
|
|
123
|
+
createJWT() as any,
|
|
124
|
+
createSecrets(WEAK_SECRET) as any,
|
|
125
|
+
'myFunc',
|
|
126
|
+
{ userId: 'user-1' }
|
|
127
|
+
),
|
|
128
|
+
(err: unknown) => err instanceof WeakKeyMaterialError
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('buildRemoteHeaders rejects a weak secret even with no session to encrypt', async () => {
|
|
133
|
+
await assert.rejects(
|
|
134
|
+
() =>
|
|
135
|
+
buildRemoteHeaders(
|
|
136
|
+
createJWT() as any,
|
|
137
|
+
createSecrets(WEAK_SECRET) as any,
|
|
138
|
+
'myFunc'
|
|
139
|
+
),
|
|
140
|
+
(err: unknown) => err instanceof WeakKeyMaterialError
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test('the middleware rejects a weak secret rather than authenticating with it', async () => {
|
|
145
|
+
const jwt = createJWT()
|
|
146
|
+
const headers = await buildRemoteHeaders(
|
|
147
|
+
jwt as any,
|
|
148
|
+
createSecrets(STRONG_SECRET) as any,
|
|
149
|
+
'myFunc',
|
|
150
|
+
{ userId: 'user-1' }
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
await assert.rejects(
|
|
154
|
+
() =>
|
|
155
|
+
pikkuRemoteAuthMiddleware(
|
|
156
|
+
{ secrets: createSecrets(WEAK_SECRET), jwt } as any,
|
|
157
|
+
{
|
|
158
|
+
http: {
|
|
159
|
+
request: createRequest({ authorization: headers.authorization! }),
|
|
160
|
+
},
|
|
161
|
+
setSession: async () => {},
|
|
162
|
+
} as any,
|
|
163
|
+
async () => {}
|
|
164
|
+
),
|
|
165
|
+
(err: unknown) => err instanceof WeakKeyMaterialError
|
|
166
|
+
)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('a secret of exactly 32 characters is accepted', async () => {
|
|
170
|
+
const session = { userId: 'user-1' }
|
|
171
|
+
assert.deepStrictEqual(await roundTrip('x'.repeat(32), session), session)
|
|
172
|
+
})
|
|
173
|
+
})
|
package/src/remote.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { JWTService } from './services/jwt-service.js'
|
|
2
2
|
import type { SecretService } from './services/secret-service.js'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
assertStrongKeyMaterial,
|
|
5
|
+
encryptWithKeyMaterial,
|
|
6
|
+
REMOTE_SESSION_INFO,
|
|
7
|
+
} from './crypto-utils.js'
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
7
|
-
* pikkuRemoteAuthMiddleware on the receiving end.
|
|
8
|
-
*
|
|
9
|
-
* Used by all deployment services (Kysely, Redis, MongoDB, Lambda, CF, Azure)
|
|
10
|
-
* regardless of transport (HTTP, Lambda Invoke, service bindings).
|
|
10
|
+
* Authorization headers carrying a JWT-signed session and traceId for
|
|
11
|
+
* `pikkuRemoteAuthMiddleware` on the receiving end. Used by every deployment
|
|
12
|
+
* service regardless of transport (HTTP, Lambda Invoke, service bindings).
|
|
11
13
|
*/
|
|
12
14
|
export async function buildRemoteHeaders(
|
|
13
15
|
jwt: JWTService | undefined,
|
|
@@ -23,12 +25,20 @@ export async function buildRemoteHeaders(
|
|
|
23
25
|
|
|
24
26
|
let secret: string | undefined
|
|
25
27
|
try {
|
|
26
|
-
secret = await secrets?.getSecret('PIKKU_REMOTE_SECRET')
|
|
28
|
+
secret = (await secrets?.getSecret('PIKKU_REMOTE_SECRET'))?.reveal()
|
|
27
29
|
} catch {}
|
|
28
30
|
|
|
29
31
|
if (secret && jwt) {
|
|
32
|
+
assertStrongKeyMaterial('PIKKU_REMOTE_SECRET', secret)
|
|
30
33
|
const sessionEnc = session
|
|
31
|
-
? await
|
|
34
|
+
? await encryptWithKeyMaterial(
|
|
35
|
+
'PIKKU_REMOTE_SECRET',
|
|
36
|
+
secret,
|
|
37
|
+
REMOTE_SESSION_INFO,
|
|
38
|
+
{
|
|
39
|
+
session,
|
|
40
|
+
}
|
|
41
|
+
)
|
|
32
42
|
: undefined
|
|
33
43
|
const token = await jwt.encode(
|
|
34
44
|
{ value: 5, unit: 'minute' },
|
package/src/schema.ts
CHANGED
|
@@ -9,13 +9,6 @@ import { pikkuState, getAllPackageStates } from './pikku-state.js'
|
|
|
9
9
|
const schemaKey = (name: string, packageName: string | null): string =>
|
|
10
10
|
packageName ? `${packageName}:${name}` : name
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* Adds a schema to the schemas map for a specific package.
|
|
14
|
-
* @param name - The name of the schema.
|
|
15
|
-
* @param value - The schema value.
|
|
16
|
-
* @param packageName - The package name (null for main package, '@scope/package' for addon packages).
|
|
17
|
-
* @ignore
|
|
18
|
-
*/
|
|
19
12
|
export const addSchema = (
|
|
20
13
|
name: string,
|
|
21
14
|
value: any,
|
|
@@ -25,13 +18,6 @@ export const addSchema = (
|
|
|
25
18
|
pikkuState(packageName, 'misc', 'schemas').set(name, schema)
|
|
26
19
|
}
|
|
27
20
|
|
|
28
|
-
/**
|
|
29
|
-
* Retrieves a schema from the schemas map for a specific package.
|
|
30
|
-
* @param name - The name of the schema.
|
|
31
|
-
* @param packageName - The package name (null for main package, '@scope/package' for addon packages).
|
|
32
|
-
* @returns The schema value or undefined if not found.
|
|
33
|
-
* @ignore
|
|
34
|
-
*/
|
|
35
21
|
export const getSchema = (
|
|
36
22
|
name: string,
|
|
37
23
|
packageName: string | null = null
|
|
@@ -39,10 +25,6 @@ export const getSchema = (
|
|
|
39
25
|
return pikkuState(packageName, 'misc', 'schemas').get(name)
|
|
40
26
|
}
|
|
41
27
|
|
|
42
|
-
/**
|
|
43
|
-
* Loads a schema and compiles it into a validator.
|
|
44
|
-
* @param logger - A logger for logging information.
|
|
45
|
-
*/
|
|
46
28
|
export const compileAllSchemas = (
|
|
47
29
|
logger: Logger,
|
|
48
30
|
schemaService?: SchemaService
|
|
@@ -95,27 +77,11 @@ const validateAllSchemasLoaded = (
|
|
|
95
77
|
}
|
|
96
78
|
|
|
97
79
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* ships with (`@cfworker/json-schema`, and Ajv unless `useDefaults` is set)
|
|
104
|
-
* annotate the instance, so the function received `undefined` for a property
|
|
105
|
-
* its generated type declares as present. That is the worst shape a mismatch
|
|
106
|
-
* can take: validation permits the omission, the type says the value is there,
|
|
107
|
-
* and the body reads `undefined`.
|
|
108
|
-
*
|
|
109
|
-
* Applied unconditionally rather than alongside `coerceTopLevelDataFromSchema`,
|
|
110
|
-
* whose `coerceDataFromSchema` flag is about decoding transport-encoded values
|
|
111
|
-
* (a query string's `"1,2"` into an array). Defaults are a property of the
|
|
112
|
-
* schema, not of how the call arrived, so gating them on that flag would apply
|
|
113
|
-
* them over HTTP and skip them on a direct RPC invocation.
|
|
114
|
-
*
|
|
115
|
-
* Returns the data to use, which is a new object only when defaults had to be
|
|
116
|
-
* added to a nullish input — a call made with no arguments at all still gets
|
|
117
|
-
* them. Values are cloned so an object or array default (`[]`, `{}`) is never
|
|
118
|
-
* shared as one mutable instance across every request.
|
|
80
|
+
* Fills in absent top-level properties from their schema `default`. Applied
|
|
81
|
+
* unconditionally: a default belongs to the schema, not to the transport the
|
|
82
|
+
* call arrived on, and runs before coercion so a defaulted and a supplied
|
|
83
|
+
* value are treated identically from here on. Values are cloned so an object
|
|
84
|
+
* or array default is never shared as one mutable instance across requests.
|
|
119
85
|
*/
|
|
120
86
|
export const applyDefaultsFromSchema = (
|
|
121
87
|
schemaName: string,
|
package/src/scopes.ts
CHANGED
|
@@ -5,13 +5,9 @@ const SEPARATOR = ':'
|
|
|
5
5
|
const WILDCARD = '*'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Every grant that would satisfy `scope`: the scope itself, a wildcard
|
|
9
9
|
* directly beneath it, a wildcard at each ancestor level, and each plain
|
|
10
|
-
* ancestor id
|
|
11
|
-
*
|
|
12
|
-
* For `admin:invoices:create` that is:
|
|
13
|
-
* admin:invoices:create, admin:invoices:create:*, *, admin:*, admin:invoices:*,
|
|
14
|
-
* admin, admin:invoices
|
|
10
|
+
* ancestor id.
|
|
15
11
|
*/
|
|
16
12
|
const satisfyingGrants = (scope: string): string[] => {
|
|
17
13
|
const segments = scope.split(SEPARATOR)
|
|
@@ -25,28 +21,10 @@ const satisfyingGrants = (scope: string): string[] => {
|
|
|
25
21
|
return grants
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
/**
|
|
29
|
-
* Checks whether `held` satisfies a single required scope.
|
|
30
|
-
*
|
|
31
|
-
* A grant satisfies a requirement when it is the scope itself, a plain ancestor
|
|
32
|
-
* (`admin` covers `admin:invoices:create`), a wildcard at or above it (`admin:*`
|
|
33
|
-
* covers `admin` and `admin:invoices:create`), or the bare `*`. A narrower grant
|
|
34
|
-
* never satisfies a broader requirement — `admin:invoices` does not grant
|
|
35
|
-
* `admin`.
|
|
36
|
-
*/
|
|
37
24
|
const holds = (held: ReadonlySet<string>, scope: string): boolean =>
|
|
38
25
|
satisfyingGrants(scope).some((grant) => held.has(grant))
|
|
39
26
|
|
|
40
|
-
/**
|
|
41
|
-
* The first required scope a set of held grants does not satisfy, or `null`
|
|
42
|
-
* when every one is satisfied.
|
|
43
|
-
*
|
|
44
|
-
* Scopes are an AND gate: every entry in `required` must be satisfied. This is
|
|
45
|
-
* deliberately distinct from `permissions`, which OR together — a scope can
|
|
46
|
-
* only ever narrow access, so adding one to a function can never widen it.
|
|
47
|
-
*
|
|
48
|
-
* Fails closed: an absent `held` satisfies nothing.
|
|
49
|
-
*/
|
|
27
|
+
/** The first required scope `held` does not satisfy, or `null`. Fails closed. */
|
|
50
28
|
const firstUnsatisfied = (
|
|
51
29
|
required: readonly string[] | undefined,
|
|
52
30
|
held: Iterable<string> | undefined
|
|
@@ -65,17 +43,8 @@ const firstUnsatisfied = (
|
|
|
65
43
|
}
|
|
66
44
|
|
|
67
45
|
/**
|
|
68
|
-
* Whether a set of held grants satisfies every required scope.
|
|
69
|
-
*
|
|
70
46
|
* The non-throwing counterpart to {@link verifyScopes}, for deciding rather
|
|
71
|
-
* than enforcing
|
|
72
|
-
* it is not satisfied, rather than rejecting the request outright.
|
|
73
|
-
*
|
|
74
|
-
* Fails closed: an absent or empty `held` satisfies nothing. An empty
|
|
75
|
-
* `required` is satisfied by anything.
|
|
76
|
-
*
|
|
77
|
-
* @param required - Scopes to check for. Empty means no gate.
|
|
78
|
-
* @param held - The grants held, e.g. `session.scopes`. May be undefined.
|
|
47
|
+
* than enforcing. Fails closed; an empty `required` is satisfied by anything.
|
|
79
48
|
*/
|
|
80
49
|
export const hasScopes = (
|
|
81
50
|
required: readonly string[] | undefined,
|
|
@@ -83,19 +52,9 @@ export const hasScopes = (
|
|
|
83
52
|
): boolean => firstUnsatisfied(required, held) === null
|
|
84
53
|
|
|
85
54
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* Scopes are an AND gate: every entry in `required` must be satisfied. This is
|
|
90
|
-
* deliberately distinct from `permissions`, which OR together — a scope can
|
|
91
|
-
* only ever narrow access, so adding one to a function can never widen it.
|
|
92
|
-
*
|
|
93
|
-
* Fails closed: a session without a `scopes` field, or no session at all,
|
|
94
|
-
* satisfies nothing.
|
|
95
|
-
*
|
|
96
|
-
* @param required - Scopes the function declares. Empty means no gate.
|
|
97
|
-
* @param session - The session to check. May be undefined.
|
|
98
|
-
* @throws {MissingScopeError} Naming the first unsatisfied scope.
|
|
55
|
+
* Throws {@link MissingScopeError} naming the first scope the session does not
|
|
56
|
+
* hold. Fails closed: no session, or a session without `scopes`, satisfies
|
|
57
|
+
* nothing.
|
|
99
58
|
*/
|
|
100
59
|
export const verifyScopes = (
|
|
101
60
|
required: readonly string[] | undefined,
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import {
|
|
4
|
+
createSecretValue,
|
|
5
|
+
isSecretValue,
|
|
6
|
+
REDACTED,
|
|
7
|
+
SecretCoercionError,
|
|
8
|
+
type Safe,
|
|
9
|
+
type SecretValue,
|
|
10
|
+
} from './secret-value.js'
|
|
11
|
+
import type { Logger } from './services/logger.js'
|
|
12
|
+
import type { AuditLog } from './services/audit-service.js'
|
|
13
|
+
|
|
14
|
+
const VALUE = 'sk-live-DEADBEEF'
|
|
15
|
+
|
|
16
|
+
describe('SecretValue', () => {
|
|
17
|
+
test('reveals the value', () => {
|
|
18
|
+
assert.equal(createSecretValue(VALUE).reveal(), VALUE)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('unwraps object secrets whole', () => {
|
|
22
|
+
const creds = createSecretValue({ apiKey: 'k', apiSecret: 's' })
|
|
23
|
+
assert.deepEqual(creds.reveal(), { apiKey: 'k', apiSecret: 's' })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('is recognisable at runtime', () => {
|
|
27
|
+
assert.equal(isSecretValue(createSecretValue(VALUE)), true)
|
|
28
|
+
assert.equal(
|
|
29
|
+
isSecretValue(() => VALUE),
|
|
30
|
+
false
|
|
31
|
+
)
|
|
32
|
+
assert.equal(isSecretValue(VALUE), false)
|
|
33
|
+
assert.equal(isSecretValue(undefined), false)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('structured serialization redacts', () => {
|
|
38
|
+
test('JSON.stringify renders [secret] rather than dropping the key', () => {
|
|
39
|
+
const event = {
|
|
40
|
+
type: 'fn.call',
|
|
41
|
+
input: { user: 'ada', token: createSecretValue(VALUE) },
|
|
42
|
+
}
|
|
43
|
+
assert.equal(
|
|
44
|
+
JSON.stringify(event),
|
|
45
|
+
'{"type":"fn.call","input":{"user":"ada","token":"[secret]"}}'
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('a bare secret serializes to [secret]', () => {
|
|
50
|
+
assert.equal(JSON.stringify(createSecretValue(VALUE)), `"${REDACTED}"`)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('node inspect renders [secret]', () => {
|
|
54
|
+
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
|
55
|
+
const secret = createSecretValue(VALUE) as unknown as Record<
|
|
56
|
+
symbol,
|
|
57
|
+
() => string
|
|
58
|
+
>
|
|
59
|
+
assert.equal(secret[inspect]!(), REDACTED)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('structuredClone carries no secret material across the boundary', () => {
|
|
63
|
+
// The value lives in a private field, so a clone is an empty object rather
|
|
64
|
+
// than a copy — it crosses a worker or postMessage boundary carrying nothing.
|
|
65
|
+
const clone = structuredClone({ token: createSecretValue(VALUE) })
|
|
66
|
+
assert.equal(JSON.stringify(clone), '{"token":{}}')
|
|
67
|
+
assert.equal(Object.keys(clone.token).length, 0)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('string coercion throws', () => {
|
|
72
|
+
const secret = createSecretValue(VALUE)
|
|
73
|
+
|
|
74
|
+
test('template literal', () => {
|
|
75
|
+
assert.throws(() => `Bearer ${secret}`, SecretCoercionError)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('concatenation', () => {
|
|
79
|
+
assert.throws(() => ('Bearer ' + secret) as string, SecretCoercionError)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('String()', () => {
|
|
83
|
+
assert.throws(() => String(secret), SecretCoercionError)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('explicit toString()', () => {
|
|
87
|
+
assert.throws(
|
|
88
|
+
() => (secret as unknown as { toString(): string }).toString(),
|
|
89
|
+
SecretCoercionError
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('the thrown error never contains the secret', () => {
|
|
94
|
+
try {
|
|
95
|
+
String(secret)
|
|
96
|
+
assert.fail('expected a throw')
|
|
97
|
+
} catch (error) {
|
|
98
|
+
assert.equal((error as Error).message.includes(VALUE), false)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// ── Type-level assertions ────────────────────────────────────────────────────
|
|
104
|
+
// The point of the exercise: these fail `yarn tsc`, not `yarn test`. Held in a
|
|
105
|
+
// function that is never called so the runtime never evaluates them.
|
|
106
|
+
//
|
|
107
|
+
// `tsconfig.json` excludes `**/*.test.ts`, so this file is only reached through
|
|
108
|
+
// `tsconfig.type-tests.json`, which lists it under `files` — `exclude` filters
|
|
109
|
+
// `include` but not `files`. Both run from this package's `tsc` script. Without
|
|
110
|
+
// that second pass the assertions below type-check nothing at all.
|
|
111
|
+
|
|
112
|
+
const _typeAssertions = (
|
|
113
|
+
secret: SecretValue<string>,
|
|
114
|
+
creds: SecretValue<{ token: string }>,
|
|
115
|
+
anything: any,
|
|
116
|
+
sink: <T>(value: Safe<T>) => void,
|
|
117
|
+
secretPromise: Promise<SecretValue<string>>,
|
|
118
|
+
secretMap: Map<string, SecretValue<string>>,
|
|
119
|
+
secretSet: Set<SecretValue<string>>
|
|
120
|
+
) => {
|
|
121
|
+
// Nominal: not assignable to what it wraps.
|
|
122
|
+
// @ts-expect-error a secret is not a string
|
|
123
|
+
const _notAString: string = secret
|
|
124
|
+
void _notAString
|
|
125
|
+
|
|
126
|
+
// Unwrapping composes with plain types, so call sites need no cast.
|
|
127
|
+
const _unwrapped: string = secret.reveal()
|
|
128
|
+
const _unwrappedField: string = creds.reveal().token
|
|
129
|
+
void _unwrapped
|
|
130
|
+
void _unwrappedField
|
|
131
|
+
|
|
132
|
+
// @ts-expect-error a secret cannot reach a guarded sink
|
|
133
|
+
sink(secret)
|
|
134
|
+
// @ts-expect-error nor nested inside an object
|
|
135
|
+
sink({ config: { token: secret } })
|
|
136
|
+
// @ts-expect-error nor inside an array
|
|
137
|
+
sink([secret])
|
|
138
|
+
// @ts-expect-error nor as one branch of a union
|
|
139
|
+
sink(true ? secret : 'plain')
|
|
140
|
+
|
|
141
|
+
// Unwrapped values pass — disclosure is deliberate and visible.
|
|
142
|
+
sink(secret.reveal())
|
|
143
|
+
sink({ config: { token: secret.reveal() } })
|
|
144
|
+
|
|
145
|
+
// Ordinary values must not trip the guard.
|
|
146
|
+
sink('plain')
|
|
147
|
+
sink(42)
|
|
148
|
+
sink(null)
|
|
149
|
+
sink(undefined)
|
|
150
|
+
sink({ nested: { deep: [1, 2, 3] } })
|
|
151
|
+
sink(() => 'an ordinary function')
|
|
152
|
+
sink(new Date())
|
|
153
|
+
sink(new Error('boom'))
|
|
154
|
+
sink(Buffer.from('bytes'))
|
|
155
|
+
sink(new Map([['k', 'v']]))
|
|
156
|
+
sink([{ a: 1 }, { a: 2 }])
|
|
157
|
+
|
|
158
|
+
// A container is not a hiding place: a mapped type cannot reach what a
|
|
159
|
+
// `Promise`, `Map` or `Set` holds, so `Safe` recurses into them explicitly.
|
|
160
|
+
// @ts-expect-error nor awaiting later — `getSecret()` returns exactly this
|
|
161
|
+
sink(secretPromise)
|
|
162
|
+
// @ts-expect-error nor as a map value
|
|
163
|
+
sink(secretMap)
|
|
164
|
+
// @ts-expect-error nor as a set member
|
|
165
|
+
sink(secretSet)
|
|
166
|
+
|
|
167
|
+
// The same containers holding ordinary values must still pass.
|
|
168
|
+
sink(Promise.resolve('plain'))
|
|
169
|
+
sink(new Map([['k', 'v']]))
|
|
170
|
+
sink(new Set(['plain']))
|
|
171
|
+
|
|
172
|
+
// `any` cannot be guarded and must stay usable rather than collapsing to never.
|
|
173
|
+
sink(anything)
|
|
174
|
+
}
|
|
175
|
+
void _typeAssertions
|
|
176
|
+
|
|
177
|
+
// The assertions above prove `Safe<>` works against a stand-in sink. These prove
|
|
178
|
+
// the real interfaces are actually wired to it — the two can drift apart, and a
|
|
179
|
+
// sink that forgot the guard is exactly the leak this design exists to stop.
|
|
180
|
+
|
|
181
|
+
const _sinkAssertions = (
|
|
182
|
+
secret: SecretValue<string>,
|
|
183
|
+
logger: Logger,
|
|
184
|
+
auditLog: AuditLog
|
|
185
|
+
) => {
|
|
186
|
+
// @ts-expect-error a secret cannot be logged
|
|
187
|
+
logger.info(secret)
|
|
188
|
+
// @ts-expect-error nor nested in a log message
|
|
189
|
+
logger.info({ token: secret })
|
|
190
|
+
// @ts-expect-error nor in the log metadata
|
|
191
|
+
logger.info('using', { token: secret })
|
|
192
|
+
|
|
193
|
+
// @ts-expect-error nor written into an audit input
|
|
194
|
+
auditLog.write({ type: 'used', source: 'explicit', input: { token: secret } })
|
|
195
|
+
const metadata = { token: secret }
|
|
196
|
+
// @ts-expect-error nor into audit metadata
|
|
197
|
+
auditLog.write({ type: 'used', source: 'explicit', metadata })
|
|
198
|
+
|
|
199
|
+
// Ordinary logging and auditing must still compile.
|
|
200
|
+
logger.info({ msg: 'fine' })
|
|
201
|
+
logger.info('fine', { count: 1 })
|
|
202
|
+
auditLog.write({ type: 'used', source: 'explicit', input: { userId: 'u1' } })
|
|
203
|
+
}
|
|
204
|
+
void _sinkAssertions
|