@pikku/core 0.12.79 → 0.12.82
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 +473 -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/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/function/function-runner.js +22 -47
- package/dist/function/functions.types.d.ts +9 -5
- package/dist/function/index.d.ts +1 -1
- package/dist/index.d.ts +14 -19
- package/dist/index.js +6 -11
- 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 +16 -5
- package/dist/schema.js +35 -1
- package/dist/services/ai-agent-runner-service.d.ts +7 -0
- package/dist/services/ai-run-state-service.d.ts +17 -1
- package/dist/services/in-memory-ai-run-state-service.d.ts +6 -2
- package/dist/services/in-memory-ai-run-state-service.js +11 -1
- package/dist/services/in-memory-workflow-service.js +2 -0
- package/dist/services/index.d.ts +15 -15
- package/dist/services/index.js +5 -5
- 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/scoped-credential-service.d.ts +21 -0
- package/dist/services/scoped-credential-service.js +53 -0
- 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 +302 -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 -3
- package/dist/types/state.types.d.ts +14 -1
- package/dist/wirings/actor-flow/index.d.ts +1 -1
- 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-finalize.d.ts +58 -0
- package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
- package/dist/wirings/ai-agent/ai-agent-memory.js +37 -18
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +5 -9
- package/dist/wirings/ai-agent/ai-agent-runner.js +89 -147
- package/dist/wirings/ai-agent/ai-agent-stream.js +109 -97
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +57 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +82 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +47 -2
- package/dist/wirings/ai-agent/index.d.ts +8 -7
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.js +1 -6
- package/dist/wirings/ai-agent/voice-output.js +2 -12
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
- package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
- package/dist/wirings/ai-scorer/index.d.ts +6 -0
- package/dist/wirings/ai-scorer/index.js +5 -0
- 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/index.d.ts +5 -6
- package/dist/wirings/channel/index.js +3 -4
- package/dist/wirings/channel/local/local-channel-runner.js +8 -1
- 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/channel/cli-raw-channel-runner.js +9 -1
- package/dist/wirings/cli/channel/index.d.ts +1 -2
- package/dist/wirings/cli/channel/index.js +0 -1
- package/dist/wirings/cli/cli-runner.js +17 -3
- package/dist/wirings/cli/cli.types.d.ts +0 -8
- package/dist/wirings/cli/command-parser.js +13 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/gateway/gateway-runner.js +9 -2
- package/dist/wirings/gateway/index.d.ts +1 -1
- 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 +14 -15
- package/dist/wirings/http/http.types.d.ts +0 -10
- package/dist/wirings/http/index.d.ts +2 -3
- package/dist/wirings/http/index.js +1 -1
- package/dist/wirings/mcp/index.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +15 -7
- package/dist/wirings/mcp/mcp-runner.js +18 -11
- package/dist/wirings/persona/index.d.ts +3 -4
- package/dist/wirings/persona/index.js +2 -3
- package/dist/wirings/queue/index.d.ts +1 -3
- package/dist/wirings/queue/index.js +1 -3
- package/dist/wirings/rpc/addon-runner.d.ts +4 -0
- package/dist/wirings/rpc/addon-runner.js +19 -3
- package/dist/wirings/rpc/rpc-runner.d.ts +8 -51
- package/dist/wirings/rpc/rpc-runner.js +55 -86
- package/dist/wirings/rpc/rpc-types.d.ts +7 -0
- package/dist/wirings/rpc/wire-addon.d.ts +13 -0
- package/dist/wirings/rpc/wire-addon.js +4 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/secret/validate-secret-definitions.js +2 -2
- package/dist/wirings/trigger/index.d.ts +1 -1
- 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/index.d.ts +5 -6
- package/dist/wirings/virtual-user/index.js +2 -4
- 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 +100 -3
- 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 +12 -7
- package/dist/wirings/workflow/index.js +7 -3
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -3
- package/dist/wirings/workflow/pikku-scenario-service.js +64 -10
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +28 -147
- package/dist/wirings/workflow/pikku-workflow-service.js +85 -493
- package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
- package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
- package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
- package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
- package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
- package/dist/wirings/workflow/workflow-approval.d.ts +67 -0
- package/dist/wirings/workflow/workflow-approval.js +177 -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-ownership.d.ts +17 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +30 -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 +8 -1
- 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/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -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-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -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 +42 -3
- 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/validate-runs-checks-by-precondition.md +115 -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-function-never-receives-the-secret-service.md +37 -0
- 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 +50 -0
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +33 -0
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +24 -0
- package/knowledge/decisions/security/index.md +9 -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 +15 -3
- package/scripts/generate-api-report.d.mts +1 -0
- package/scripts/generate-api-report.mjs +89 -0
- package/scripts/generate-api-report.mts +343 -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/errors/index.ts +1 -1
- package/src/function/function-runner.test.ts +53 -1
- package/src/function/function-runner.ts +33 -48
- package/src/function/functions.types.ts +15 -9
- package/src/function/index.ts +0 -2
- package/src/handle-error.ts +1 -1
- package/src/index.ts +3 -57
- 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 +22 -6
- package/src/public-surface.json +554 -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-agent-runner-service.ts +12 -1
- package/src/services/ai-run-state-service.ts +18 -1
- package/src/services/audit-service.ts +2 -2
- package/src/services/in-memory-ai-run-state-service.ts +16 -2
- package/src/services/in-memory-workflow-service.ts +2 -0
- package/src/services/index.ts +4 -47
- 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/scoped-credential-service.test.ts +86 -0
- package/src/services/scoped-credential-service.ts +63 -0
- 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 +379 -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 +14 -3
- package/src/types/state.types.ts +17 -1
- package/src/wirings/actor-flow/index.ts +0 -3
- 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-finalize.test.ts +186 -0
- package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +2 -1
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +62 -39
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
- package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +12 -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 +120 -160
- package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +2 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +152 -117
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
- package/src/wirings/ai-agent/ai-agent-turn.ts +122 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +65 -5
- package/src/wirings/ai-agent/index.ts +2 -16
- package/src/wirings/ai-agent/voice-input.ts +1 -6
- package/src/wirings/ai-agent/voice-output.ts +2 -12
- package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
- package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
- package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
- package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
- package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
- package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
- package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
- package/src/wirings/ai-scorer/index.ts +24 -0
- 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/index.ts +1 -20
- package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
- package/src/wirings/channel/local/local-channel-runner.ts +9 -2
- 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-channel-runner.test.ts +23 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +4 -1
- package/src/wirings/cli/channel/index.ts +0 -7
- package/src/wirings/cli/cli-runner.test.ts +68 -0
- package/src/wirings/cli/cli-runner.ts +25 -5
- 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/credential/index.ts +0 -1
- package/src/wirings/gateway/gateway-channel-meta.test.ts +44 -0
- package/src/wirings/gateway/gateway-runner.ts +27 -19
- package/src/wirings/gateway/index.ts +0 -3
- package/src/wirings/http/http-routes.ts +4 -1
- package/src/wirings/http/http-runner.test.ts +66 -0
- package/src/wirings/http/http-runner.ts +21 -28
- package/src/wirings/http/http.types.ts +0 -15
- package/src/wirings/http/index.ts +2 -8
- package/src/wirings/http/pikku-fetch-http-request.ts +2 -2
- package/src/wirings/http/web-request.ts +1 -1
- package/src/wirings/mcp/index.ts +0 -1
- package/src/wirings/mcp/mcp-runner.test.ts +181 -0
- package/src/wirings/mcp/mcp-runner.ts +37 -21
- package/src/wirings/persona/index.ts +0 -8
- 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/index.ts +0 -14
- package/src/wirings/queue/queue-runner.ts +1 -1
- package/src/wirings/rpc/addon-auth-tags.test.ts +1 -5
- package/src/wirings/rpc/addon-runner.ts +34 -3
- package/src/wirings/rpc/addon-secrets.test.ts +261 -0
- package/src/wirings/rpc/rpc-runner.test.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +60 -136
- package/src/wirings/rpc/rpc-types.ts +11 -0
- package/src/wirings/rpc/wire-addon.ts +20 -1
- package/src/wirings/scheduler/index.ts +0 -1
- package/src/wirings/secret/validate-secret-definitions.test.ts +22 -0
- package/src/wirings/secret/validate-secret-definitions.ts +2 -2
- package/src/wirings/trigger/index.ts +0 -1
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -5
- package/src/wirings/trigger/trigger-runner.ts +7 -5
- package/src/wirings/virtual-user/index.ts +0 -16
- 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 +117 -4
- 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 +76 -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 +14 -24
- package/src/wirings/workflow/pikku-scenario-service.ts +101 -27
- package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -14
- package/src/wirings/workflow/pikku-workflow-service.ts +214 -743
- package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
- package/src/wirings/workflow/scenario-hooks.test.ts +52 -0
- package/src/wirings/workflow/scenario-step.types.ts +8 -0
- package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
- package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
- package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
- package/src/wirings/workflow/workflow-approval.ts +289 -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-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 +212 -0
- package/src/wirings/workflow/workflow-run-engine.types.ts +91 -0
- package/src/wirings/workflow/workflow-run-ownership.ts +37 -0
- package/src/wirings/workflow/workflow-suspend.ts +61 -0
- package/src/wirings/workflow/workflow.types.ts +7 -9
- package/src/wirings-stay-decoupled.test.ts +127 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/internal.d.ts +0 -3
- package/dist/internal.js +0 -2
- package/dist/middleware/timeout.d.ts +0 -9
- package/dist/middleware/timeout.js +0 -15
- package/dist/pikku-response.d.ts +0 -6
- package/dist/pikku-response.js +0 -6
- package/dist/services/gopass-secrets.d.ts +0 -15
- package/dist/services/gopass-secrets.js +0 -76
- package/dist/services/http-scenario-actors.d.ts +0 -75
- package/dist/services/http-scenario-actors.js +0 -195
- package/dist/services/http-user-flow-actors.d.ts +0 -67
- package/dist/services/http-user-flow-actors.js +0 -193
- package/dist/services/scenario-actors-service.d.ts +0 -127
- package/dist/services/scenario-actors-service.js +0 -40
- package/dist/services/user-flow-actors-service.d.ts +0 -39
- package/dist/wirings/credential/wire-credential.d.ts +0 -48
- package/dist/wirings/credential/wire-credential.js +0 -47
- package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
- package/dist/wirings/oauth2/oauth2-client.js +0 -263
- package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
- package/dist/wirings/oauth2/oauth2-routes.js +0 -146
- package/dist/wirings/scope/wire-scope.d.ts +0 -33
- package/dist/wirings/scope/wire-scope.js +0 -32
- package/dist/wirings/workflow/dsl/index.d.ts +0 -5
- package/dist/wirings/workflow/dsl/index.js +0 -4
- package/dist/wirings/workflow/graph/index.d.ts +0 -5
- package/dist/wirings/workflow/graph/index.js +0 -4
- package/src/internal.ts +0 -10
- /package/dist/{services/user-flow-actors-service.js → wirings/workflow/workflow-run-engine.types.js} +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emits `api-report.md`: every entry point's exported symbols with their full
|
|
3
|
+
* type signatures.
|
|
4
|
+
*
|
|
5
|
+
* `public-surface.json` pins the *names* a consumer can reach, by reading
|
|
6
|
+
* `Object.keys` off each entry point. That catches an export appearing or
|
|
7
|
+
* disappearing and nothing else — adding a method to `MetaService`, or making
|
|
8
|
+
* a field on `ChannelMeta` required, sails straight past it, and so does every
|
|
9
|
+
* type-only export, which erases before there is an object to enumerate.
|
|
10
|
+
*
|
|
11
|
+
* Those are the changes that break a consumer's build, and the report's own
|
|
12
|
+
* summary shows how many more of them there are than there are names.
|
|
13
|
+
*
|
|
14
|
+
* So this walks the type checker instead and writes what each symbol actually
|
|
15
|
+
* *is*. The report is committed, and `api-report.test.ts` fails when the two
|
|
16
|
+
* disagree — so a member-level change is a reviewable diff rather than a
|
|
17
|
+
* surprise in someone else's CI.
|
|
18
|
+
*
|
|
19
|
+
* A member per line, and no member bodies. Both are load-bearing rather than
|
|
20
|
+
* cosmetic: the report exists to be diffed, and a class emitted as one long
|
|
21
|
+
* line of its own source is neither reviewable nor mergeable.
|
|
22
|
+
*
|
|
23
|
+
* Run: yarn api-report
|
|
24
|
+
*/
|
|
25
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
26
|
+
import { resolve, dirname } from 'node:path'
|
|
27
|
+
import { fileURLToPath } from 'node:url'
|
|
28
|
+
import ts from 'typescript'
|
|
29
|
+
|
|
30
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
|
31
|
+
const pkg = JSON.parse(
|
|
32
|
+
readFileSync(resolve(packageRoot, 'package.json'), 'utf-8')
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const entryPoints = Object.entries(pkg.exports as Record<string, string>).map(
|
|
36
|
+
([subpath, dist]) => ({
|
|
37
|
+
subpath,
|
|
38
|
+
file: resolve(
|
|
39
|
+
packageRoot,
|
|
40
|
+
dist.replace('./dist/', './src/').replace(/\.js$/, '.ts')
|
|
41
|
+
),
|
|
42
|
+
})
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const program = ts.createProgram(
|
|
46
|
+
entryPoints.map((e) => e.file),
|
|
47
|
+
{
|
|
48
|
+
target: ts.ScriptTarget.ESNext,
|
|
49
|
+
module: ts.ModuleKind.Node16,
|
|
50
|
+
moduleResolution: ts.ModuleResolutionKind.Node16,
|
|
51
|
+
strict: true,
|
|
52
|
+
skipLibCheck: true,
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
const checker = program.getTypeChecker()
|
|
56
|
+
|
|
57
|
+
/** Comments are noise in a report the diff is read for. */
|
|
58
|
+
const stripComments = (text: string): string =>
|
|
59
|
+
text.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '')
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Punctuation that makes a line and its successor one thing.
|
|
63
|
+
*
|
|
64
|
+
* Read from both ends rather than one, because the closers are ambiguous from
|
|
65
|
+
* behind: `string[]`, `Promise<T>` and `= {}` all end in a closing bracket and
|
|
66
|
+
* none of them is a continuation. What the *next* line starts with is not.
|
|
67
|
+
*/
|
|
68
|
+
const CONTINUES = /(=>|[,;{([<|&=])$/
|
|
69
|
+
const CLOSES = /^(=>|extends\b|[}\])>,;|&])/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A member's own text on one line — long, but never long enough to matter.
|
|
73
|
+
*
|
|
74
|
+
* Inside an object type the author's newline *was* the separator, so collapsing
|
|
75
|
+
* without restoring it produces `{ a: string b: number }`, which is not the
|
|
76
|
+
* type it describes and does not parse in the ```ts fence it is printed into.
|
|
77
|
+
*/
|
|
78
|
+
const oneLine = (text: string): string => {
|
|
79
|
+
const lines = stripComments(text)
|
|
80
|
+
.split('\n')
|
|
81
|
+
.map((l) => l.trim())
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
let depth = 0
|
|
84
|
+
const joined = lines
|
|
85
|
+
.map((line, index) => {
|
|
86
|
+
const inObjectType = depth > 0
|
|
87
|
+
for (const character of line) {
|
|
88
|
+
if (character === '{') depth++
|
|
89
|
+
else if (character === '}') depth--
|
|
90
|
+
}
|
|
91
|
+
const next = lines[index + 1]
|
|
92
|
+
return inObjectType &&
|
|
93
|
+
next !== undefined &&
|
|
94
|
+
!CONTINUES.test(line) &&
|
|
95
|
+
!CLOSES.test(next)
|
|
96
|
+
? `${line};`
|
|
97
|
+
: line
|
|
98
|
+
})
|
|
99
|
+
.join(' ')
|
|
100
|
+
return joined.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A class member as a consumer sees it: the signature, and nothing after it.
|
|
105
|
+
*
|
|
106
|
+
* The body is where every character of this report used to come from — a class
|
|
107
|
+
* arrived as its own source, so the report promised implementation details and
|
|
108
|
+
* a one-word change to a method rewrote the whole declaration. Truncating at
|
|
109
|
+
* the body leaves exactly the part a compatibility promise covers.
|
|
110
|
+
*/
|
|
111
|
+
const memberSignature = (member: ts.ClassElement | ts.TypeElement): string => {
|
|
112
|
+
const source = member.getSourceFile().text
|
|
113
|
+
const start = member.getStart()
|
|
114
|
+
|
|
115
|
+
const body = (member as { body?: ts.Node }).body
|
|
116
|
+
if (body) {
|
|
117
|
+
const head = oneLine(source.slice(start, body.getStart())).replace(
|
|
118
|
+
/\s*$/,
|
|
119
|
+
''
|
|
120
|
+
)
|
|
121
|
+
// An inferred return type is still a return type a consumer depends on, and
|
|
122
|
+
// truncating at the body is what threw the author's away.
|
|
123
|
+
if (ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member)) {
|
|
124
|
+
const signature = member.type
|
|
125
|
+
? undefined
|
|
126
|
+
: checker.getSignatureFromDeclaration(member)
|
|
127
|
+
if (signature) {
|
|
128
|
+
const returns = checker.getReturnTypeOfSignature(signature)
|
|
129
|
+
return `${head}: ${checker.typeToString(returns, undefined, ts.TypeFormatFlags.NoTruncation)}`
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return head
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// A property's initializer is a body by another name. Its annotation stands in
|
|
136
|
+
// for it, and the checker supplies one when the author left it inferred.
|
|
137
|
+
if (ts.isPropertyDeclaration(member) && member.initializer) {
|
|
138
|
+
if (member.type) return oneLine(source.slice(start, member.type.end))
|
|
139
|
+
const type = checker.getTypeAtLocation(member)
|
|
140
|
+
return `${oneLine(source.slice(start, member.name.end))}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return oneLine(member.getText())
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Members a consumer can reach, so a private field is not a promise. */
|
|
147
|
+
const isPublic = (member: ts.ClassElement | ts.TypeElement): boolean =>
|
|
148
|
+
!(ts.getModifiers(member as ts.HasModifiers) ?? []).some(
|
|
149
|
+
(modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword
|
|
150
|
+
) && !(member.name && ts.isPrivateIdentifier(member.name))
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A declaration with members, one member per line.
|
|
154
|
+
*
|
|
155
|
+
* The line break is the point. Two branches that touch different methods of the
|
|
156
|
+
* same class are a textual conflict when the class is one line and an ordinary
|
|
157
|
+
* merge when it is not, which is a tax the whole repo was paying: every rebase
|
|
158
|
+
* over this file stopped to re-resolve a forty-thousand-character line by hand.
|
|
159
|
+
*/
|
|
160
|
+
const membered = (
|
|
161
|
+
declaration:
|
|
162
|
+
| ts.ClassDeclaration
|
|
163
|
+
| ts.InterfaceDeclaration
|
|
164
|
+
| ts.EnumDeclaration,
|
|
165
|
+
members: ts.NodeArray<ts.ClassElement | ts.TypeElement | ts.EnumMember>
|
|
166
|
+
): string => {
|
|
167
|
+
if (members.length === 0) return oneLine(declaration.getText())
|
|
168
|
+
const source = declaration.getSourceFile().text
|
|
169
|
+
// `members.pos` is the offset just past the `{`, so the header comes with its
|
|
170
|
+
// own brace and needs no reassembling.
|
|
171
|
+
const header = oneLine(source.slice(declaration.getStart(), members.pos))
|
|
172
|
+
const lines = ts.isEnumDeclaration(declaration)
|
|
173
|
+
? (members as readonly ts.EnumMember[]).map(
|
|
174
|
+
(member) => `${oneLine(member.getText())},`
|
|
175
|
+
)
|
|
176
|
+
: (members as readonly (ts.ClassElement | ts.TypeElement)[])
|
|
177
|
+
.filter(isPublic)
|
|
178
|
+
.map(memberSignature)
|
|
179
|
+
return [header, ...lines.map((line) => ` ${line}`), '}'].join('\n')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** How a symbol is declared, in as many lines as its members need. */
|
|
183
|
+
const signature = (exported: ts.Symbol): string => {
|
|
184
|
+
// A re-export is an alias; the declaration that matters is the target's, so
|
|
185
|
+
// an interface re-exported through a barrel still reports its members.
|
|
186
|
+
const symbol =
|
|
187
|
+
exported.flags & ts.SymbolFlags.Alias
|
|
188
|
+
? checker.getAliasedSymbol(exported)
|
|
189
|
+
: exported
|
|
190
|
+
const declaration = symbol.declarations?.[0]
|
|
191
|
+
if (!declaration) return exported.getName()
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
ts.isClassDeclaration(declaration) ||
|
|
195
|
+
ts.isInterfaceDeclaration(declaration) ||
|
|
196
|
+
ts.isEnumDeclaration(declaration)
|
|
197
|
+
) {
|
|
198
|
+
return membered(declaration, declaration.members)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
202
|
+
// No members to walk, so the author's own line breaks are the ones that
|
|
203
|
+
// keep a long union reviewable.
|
|
204
|
+
return stripComments(declaration.getText())
|
|
205
|
+
.split('\n')
|
|
206
|
+
.map((l) => l.trimEnd())
|
|
207
|
+
.filter((l) => l.trim())
|
|
208
|
+
.join('\n')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const type = checker.getTypeOfSymbolAtLocation(symbol, declaration)
|
|
212
|
+
return `${exported.getName()}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Entry points whose signatures move with the CLI's codegen. Everything else
|
|
217
|
+
* is what an application imports, and is what a compatibility promise covers.
|
|
218
|
+
*
|
|
219
|
+
* knowledge: decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
|
|
220
|
+
*/
|
|
221
|
+
const ECOSYSTEM_SUBPATHS = new Set(['./ecosystem', './internal'])
|
|
222
|
+
|
|
223
|
+
type Exported = { name: string; members: number; signature: string }
|
|
224
|
+
|
|
225
|
+
/** The members the report lists, which is what the tally is counting. */
|
|
226
|
+
const memberCount = (declaration: ts.Declaration): number => {
|
|
227
|
+
if (ts.isInterfaceDeclaration(declaration)) return declaration.members.length
|
|
228
|
+
if (ts.isEnumDeclaration(declaration)) return declaration.members.length
|
|
229
|
+
if (!ts.isClassDeclaration(declaration)) return 0
|
|
230
|
+
return declaration.members.filter(isPublic).length
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const modules = new Map<string, Exported[]>()
|
|
234
|
+
|
|
235
|
+
for (const { subpath, file } of entryPoints) {
|
|
236
|
+
const source = program.getSourceFile(file)
|
|
237
|
+
if (!source) continue
|
|
238
|
+
const moduleSymbol = checker.getSymbolAtLocation(source)
|
|
239
|
+
if (!moduleSymbol) continue
|
|
240
|
+
|
|
241
|
+
modules.set(
|
|
242
|
+
subpath,
|
|
243
|
+
checker
|
|
244
|
+
.getExportsOfModule(moduleSymbol)
|
|
245
|
+
.map((exported) => {
|
|
246
|
+
const symbol =
|
|
247
|
+
exported.flags & ts.SymbolFlags.Alias
|
|
248
|
+
? checker.getAliasedSymbol(exported)
|
|
249
|
+
: exported
|
|
250
|
+
const declaration = symbol.declarations?.[0]
|
|
251
|
+
return {
|
|
252
|
+
name: exported.getName(),
|
|
253
|
+
members: declaration ? memberCount(declaration) : 0,
|
|
254
|
+
signature: signature(exported),
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// A name reachable from more than one entry point is counted once, against the
|
|
262
|
+
// weaker promise — reaching it through a stable subpath is what a consumer will
|
|
263
|
+
// have done, whatever else also exports it.
|
|
264
|
+
const tierOf = new Map<string, string>()
|
|
265
|
+
const membersOf = new Map<string, number>()
|
|
266
|
+
for (const [subpath, exports] of modules) {
|
|
267
|
+
const tier = ECOSYSTEM_SUBPATHS.has(subpath) ? 'ecosystem' : 'stable'
|
|
268
|
+
for (const { name, members } of exports) {
|
|
269
|
+
if (tier === 'stable' || !tierOf.has(name)) tierOf.set(name, tier)
|
|
270
|
+
membersOf.set(name, Math.max(membersOf.get(name) ?? 0, members))
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const tally = (tier: string) => {
|
|
275
|
+
const names = [...tierOf].filter(([, t]) => t === tier).map(([n]) => n)
|
|
276
|
+
return {
|
|
277
|
+
entryPoints: [...modules.keys()].filter(
|
|
278
|
+
(s) => (ECOSYSTEM_SUBPATHS.has(s) ? 'ecosystem' : 'stable') === tier
|
|
279
|
+
).length,
|
|
280
|
+
names: names.length,
|
|
281
|
+
members: names.reduce((total, n) => total + (membersOf.get(n) ?? 0), 0),
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const stable = tally('stable')
|
|
285
|
+
const ecosystem = tally('ecosystem')
|
|
286
|
+
const observable =
|
|
287
|
+
stable.names + stable.members + ecosystem.names + ecosystem.members
|
|
288
|
+
|
|
289
|
+
const reachableFrom = new Map<string, number>()
|
|
290
|
+
for (const exports of modules.values())
|
|
291
|
+
for (const { name } of exports)
|
|
292
|
+
reachableFrom.set(name, (reachableFrom.get(name) ?? 0) + 1)
|
|
293
|
+
|
|
294
|
+
const perEntryPoint = [...modules]
|
|
295
|
+
.map(([subpath, exports]) => {
|
|
296
|
+
const exclusive = exports.filter(
|
|
297
|
+
({ name }) => reachableFrom.get(name) === 1
|
|
298
|
+
)
|
|
299
|
+
return {
|
|
300
|
+
subpath,
|
|
301
|
+
exports: exports.length,
|
|
302
|
+
exclusive: exclusive.length,
|
|
303
|
+
members: exclusive.reduce((total, e) => total + e.members, 0),
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
.sort((a, b) => b.exclusive + b.members - (a.exclusive + a.members))
|
|
307
|
+
|
|
308
|
+
const sections: string[] = [
|
|
309
|
+
'# @pikku/core API report',
|
|
310
|
+
'',
|
|
311
|
+
'Generated by `yarn api-report`. Every exported symbol with its full',
|
|
312
|
+
'signature, so a member-level change is a reviewable diff. Do not edit.',
|
|
313
|
+
'',
|
|
314
|
+
'## What a compatibility promise covers',
|
|
315
|
+
'',
|
|
316
|
+
`**${observable} observable things**: ${stable.names + ecosystem.names} exported names, plus`,
|
|
317
|
+
`${stable.members + ecosystem.members} members on the classes and interfaces among them.`,
|
|
318
|
+
'',
|
|
319
|
+
'| tier | entry points | names | members |',
|
|
320
|
+
'| --- | ---: | ---: | ---: |',
|
|
321
|
+
`| stable | ${stable.entryPoints} | ${stable.names} | ${stable.members} |`,
|
|
322
|
+
`| ecosystem | ${ecosystem.entryPoints} | ${ecosystem.names} | ${ecosystem.members} |`,
|
|
323
|
+
'',
|
|
324
|
+
'An entry point whose exports are mostly *exclusive* is a self-contained',
|
|
325
|
+
'subsystem rather than shared machinery — which tends to mean a newer one.',
|
|
326
|
+
'',
|
|
327
|
+
'| entry point | exports | exclusive | members on those |',
|
|
328
|
+
'| --- | ---: | ---: | ---: |',
|
|
329
|
+
...perEntryPoint.map(
|
|
330
|
+
(e) => `| \`${e.subpath}\` | ${e.exports} | ${e.exclusive} | ${e.members} |`
|
|
331
|
+
),
|
|
332
|
+
'',
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
for (const [subpath, exports] of modules) {
|
|
336
|
+
sections.push(`## ${subpath}`, '')
|
|
337
|
+
sections.push('```ts')
|
|
338
|
+
for (const { signature } of exports) sections.push(signature)
|
|
339
|
+
sections.push('```', '')
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
writeFileSync(resolve(packageRoot, 'api-report.md'), sections.join('\n'))
|
|
343
|
+
console.log(`api-report.md written for ${entryPoints.length} entry points`)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { execFileSync } from 'node:child_process'
|
|
4
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { join, dirname } from 'node:path'
|
|
7
|
+
|
|
8
|
+
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
9
|
+
const reportPath = join(packageRoot, 'api-report.md')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `public-surface.json` pins the *names* a consumer can reach. That catches an
|
|
13
|
+
* export appearing or vanishing and nothing else — adding a method to
|
|
14
|
+
* `MetaService`, or making a field on `ChannelMeta` required, sails past it,
|
|
15
|
+
* as does every type-only export. Those are the changes that break a
|
|
16
|
+
* consumer's build, and the report's summary counts how many more of them
|
|
17
|
+
* there are than there are names.
|
|
18
|
+
*
|
|
19
|
+
* So `api-report.md` records what each symbol *is*, and this fails when the
|
|
20
|
+
* committed report and the code disagree.
|
|
21
|
+
*
|
|
22
|
+
* knowledge: decisions/internals/the-api-report-pins-members-not-just-names.md
|
|
23
|
+
*/
|
|
24
|
+
describe('the API report matches the code', () => {
|
|
25
|
+
test('regenerating produces no diff', () => {
|
|
26
|
+
const committed = readFileSync(reportPath, 'utf-8')
|
|
27
|
+
|
|
28
|
+
execFileSync(
|
|
29
|
+
'npx',
|
|
30
|
+
['tsx', join(packageRoot, 'scripts', 'generate-api-report.mts')],
|
|
31
|
+
{ cwd: packageRoot, stdio: 'pipe' }
|
|
32
|
+
)
|
|
33
|
+
const regenerated = readFileSync(reportPath, 'utf-8')
|
|
34
|
+
|
|
35
|
+
if (committed !== regenerated) {
|
|
36
|
+
// Restore, so a failing run does not leave the tree dirty.
|
|
37
|
+
const committedLines = committed.split('\n')
|
|
38
|
+
const regeneratedLines = regenerated.split('\n')
|
|
39
|
+
const firstDiff = regeneratedLines.findIndex(
|
|
40
|
+
(line, i) => line !== committedLines[i]
|
|
41
|
+
)
|
|
42
|
+
writeFileSync(reportPath, committed)
|
|
43
|
+
|
|
44
|
+
assert.fail(
|
|
45
|
+
'the public API changed but api-report.md was not regenerated.\n' +
|
|
46
|
+
`First difference at line ${firstDiff + 1}:\n` +
|
|
47
|
+
` committed: ${committedLines[firstDiff] ?? '(end of file)'}\n` +
|
|
48
|
+
` regenerated: ${regeneratedLines[firstDiff] ?? '(end of file)'}\n` +
|
|
49
|
+
'Run `yarn api-report` and review the diff — a member added to an ' +
|
|
50
|
+
'exported interface or class is a change consumers compile against.'
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('the report actually contains member-level detail', () => {
|
|
56
|
+
// Guards the test above: a report of bare names would match itself forever
|
|
57
|
+
// while pinning nothing that public-surface.json does not already pin.
|
|
58
|
+
const report = readFileSync(reportPath, 'utf-8')
|
|
59
|
+
|
|
60
|
+
assert.ok(
|
|
61
|
+
/export interface MetaService \{[^}]*getHttpMeta/.test(report),
|
|
62
|
+
'expected MetaService to be reported with its members'
|
|
63
|
+
)
|
|
64
|
+
assert.ok(
|
|
65
|
+
!/^\w+: any$/m.test(report),
|
|
66
|
+
'symbols reported as `any` mean re-export aliases are not being followed'
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
})
|
package/src/column-form.ts
CHANGED
|
@@ -23,10 +23,7 @@ export const hashToken = async (raw: string): Promise<HashedValue> => {
|
|
|
23
23
|
if (!subtle) {
|
|
24
24
|
throw new Error('WebCrypto not available')
|
|
25
25
|
}
|
|
26
|
-
const digest = await subtle.digest(
|
|
27
|
-
'SHA-256',
|
|
28
|
-
new TextEncoder().encode(raw)
|
|
29
|
-
)
|
|
26
|
+
const digest = await subtle.digest('SHA-256', new TextEncoder().encode(raw))
|
|
30
27
|
let hex = ''
|
|
31
28
|
for (const byte of new Uint8Array(digest)) {
|
|
32
29
|
hex += byte.toString(16).padStart(2, '0')
|
package/src/crypto-utils.test.ts
CHANGED
|
@@ -627,8 +627,21 @@ describe('envelope encryption', () => {
|
|
|
627
627
|
)
|
|
628
628
|
})
|
|
629
629
|
|
|
630
|
+
/**
|
|
631
|
+
* Timed against a derivation measured here rather than a fixed millisecond
|
|
632
|
+
* budget. A constant threshold is a load test in disguise: this runs
|
|
633
|
+
* alongside two thousand other tests, and 50ms left barely 4x headroom over
|
|
634
|
+
* a 10ms window, so the suite went red roughly one run in five. Timing the
|
|
635
|
+
* derivation the test already needs costs nothing and makes both sides scale
|
|
636
|
+
* together, so the margin survives whatever else the machine is doing.
|
|
637
|
+
*
|
|
638
|
+
* knowledge: decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md
|
|
639
|
+
*/
|
|
630
640
|
test('N secrets cost one KEK derivation, not N', async () => {
|
|
641
|
+
const derivationStart = performance.now()
|
|
631
642
|
const kek = await deriveKEK(passphrase, salt)
|
|
643
|
+
const oneDerivation = performance.now() - derivationStart
|
|
644
|
+
|
|
632
645
|
const count = 50
|
|
633
646
|
const stored = []
|
|
634
647
|
for (let i = 0; i < count; i++) {
|
|
@@ -648,8 +661,8 @@ describe('envelope encryption', () => {
|
|
|
648
661
|
const elapsed = performance.now() - start
|
|
649
662
|
|
|
650
663
|
assert.ok(
|
|
651
|
-
elapsed <
|
|
652
|
-
`unwrapping ${count} DEKs took ${elapsed.toFixed(1)}ms
|
|
664
|
+
elapsed < oneDerivation,
|
|
665
|
+
`unwrapping ${count} DEKs took ${elapsed.toFixed(1)}ms, longer than the ${oneDerivation.toFixed(1)}ms a single KEK derivation costs — re-deriving per secret would cost about ${(oneDerivation * count).toFixed(0)}ms`
|
|
653
666
|
)
|
|
654
667
|
})
|
|
655
668
|
})
|
package/src/dev/hot-reload.ts
CHANGED
|
@@ -26,7 +26,7 @@ const isFunctionConfig = (
|
|
|
26
26
|
typeof value === 'object' &&
|
|
27
27
|
value !== null &&
|
|
28
28
|
'func' in value &&
|
|
29
|
-
typeof (value as
|
|
29
|
+
typeof (value as { func?: unknown }).func === 'function'
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -104,12 +104,7 @@ export async function pikkuDevReloader(
|
|
|
104
104
|
return
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
//
|
|
108
|
-
// adding new ones. Write into the map captured at startup, NOT pikkuState's
|
|
109
|
-
// current one: a dev-server watcher may have temporarily swapped in a
|
|
110
|
-
// codegen-scoped map, whose writes are discarded when it restores.
|
|
111
|
-
// Schemas are NOT touched here: `input`/`output` hold raw zod schemas while
|
|
112
|
-
// the schema map carries codegen JSON schemas; mixing them crashed reloads.
|
|
107
|
+
// knowledge: decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md
|
|
113
108
|
for (const [exportName, exportValue] of Object.entries(mod)) {
|
|
114
109
|
if (!isFunctionConfig(exportValue)) continue
|
|
115
110
|
const isNew = !functionsMap.has(exportName)
|
package/src/ecosystem.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The surface a package in the Pikku ecosystem reaches for — a runtime adapter,
|
|
3
|
+
* a service, the code generator's output, the CLI — and application code does
|
|
4
|
+
* not.
|
|
5
|
+
*
|
|
6
|
+
* Split out from the package root so 1.0's compatibility promise covers what
|
|
7
|
+
* applications actually import. These signatures move when the runtime needs
|
|
8
|
+
* them to — `runPikkuFunc` was reshaped over the last year while every field on
|
|
9
|
+
* `PikkuWire` survived — and promising stability on both would mean promising
|
|
10
|
+
* the weaker of the two.
|
|
11
|
+
*
|
|
12
|
+
* Not `/internal`: generated bootstrap files import from here, so the specifier
|
|
13
|
+
* lands in the user's own `.pikku` directory, and telling someone they are
|
|
14
|
+
* touching internals when the code generator put it there is both wrong and
|
|
15
|
+
* self-defeating — it could never be broken anyway. Not `/runtime` either: that
|
|
16
|
+
* reads as runtime-versus-compile-time, i.e. the real API, and `packages/runtimes/*`
|
|
17
|
+
* already claims the word while the CLI is the largest consumer here.
|
|
18
|
+
*
|
|
19
|
+
* `/internal` stays as an alias because the pinned bootstrap CLI still emits it.
|
|
20
|
+
*
|
|
21
|
+
* knowledge: decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md
|
|
22
|
+
*/
|
|
23
|
+
export {
|
|
24
|
+
pikkuState,
|
|
25
|
+
resetPikkuState,
|
|
26
|
+
getAllPackageStates,
|
|
27
|
+
getSingletonServices,
|
|
28
|
+
getCreateWireServices,
|
|
29
|
+
setSingletonServices,
|
|
30
|
+
} from './pikku-state.js'
|
|
31
|
+
export { runPikkuFunc, addFunction } from './function/function-runner.js'
|
|
32
|
+
export {
|
|
33
|
+
addGlobalMiddleware,
|
|
34
|
+
addTagMiddleware as addMiddleware,
|
|
35
|
+
} from './middleware-runner.js'
|
|
36
|
+
export { httpRouter } from './wirings/http/routers/http-router.js'
|
|
37
|
+
export type {
|
|
38
|
+
CreateSingletonServices,
|
|
39
|
+
CreateWireServices,
|
|
40
|
+
} from './types/core.types.js'
|
|
@@ -15,7 +15,8 @@ export class PikkuError extends Error {
|
|
|
15
15
|
* everything else.
|
|
16
16
|
*/
|
|
17
17
|
export const isExpectedError = (error: unknown): boolean =>
|
|
18
|
-
error instanceof PikkuError ||
|
|
18
|
+
error instanceof PikkuError ||
|
|
19
|
+
(error as { expected?: unknown } | null)?.expected === true
|
|
19
20
|
|
|
20
21
|
export interface ErrorDetails {
|
|
21
22
|
status: number
|
|
@@ -48,7 +49,7 @@ export const getErrorResponse = (
|
|
|
48
49
|
): { status: number; message: string; mcpCode?: number } | undefined => {
|
|
49
50
|
const errors = pikkuState(null, 'misc', 'errors')
|
|
50
51
|
|
|
51
|
-
let ctor: unknown = (error as
|
|
52
|
+
let ctor: unknown = (error as { constructor?: unknown } | null)?.constructor
|
|
52
53
|
while (typeof ctor === 'function' && ctor !== Object) {
|
|
53
54
|
const details = errors.get(ctor as PikkuErrorConstructor)
|
|
54
55
|
if (details) {
|
|
@@ -57,7 +58,8 @@ export const getErrorResponse = (
|
|
|
57
58
|
ctor = Object.getPrototypeOf(ctor)
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
const name = (error as
|
|
61
|
+
const name = (error as { constructor?: { name?: string } } | null)
|
|
62
|
+
?.constructor?.name
|
|
61
63
|
return name
|
|
62
64
|
? Array.from(errors.entries()).find(([e]) => e.name === name)?.[1]
|
|
63
65
|
: undefined
|
package/src/errors/error.test.ts
CHANGED
|
@@ -199,7 +199,10 @@ describe('getErrorResponse resolution order', () => {
|
|
|
199
199
|
test('a subclass of a registered error inherits its registered status', () => {
|
|
200
200
|
class TenantNotFoundError extends NotFoundError {}
|
|
201
201
|
const expected = getErrorResponse(new NotFoundError())
|
|
202
|
-
assert.deepStrictEqual(
|
|
202
|
+
assert.deepStrictEqual(
|
|
203
|
+
getErrorResponse(new TenantNotFoundError()),
|
|
204
|
+
expected
|
|
205
|
+
)
|
|
203
206
|
})
|
|
204
207
|
|
|
205
208
|
test('an unregistered, non-colliding error class resolves to undefined', () => {
|
package/src/errors/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './errors.js'
|
|
2
|
-
export { PikkuError, addError
|
|
2
|
+
export { PikkuError, addError } from './error-handler.js'
|
|
@@ -14,6 +14,7 @@ import type { CorePermissionGroup } from './functions.types.js'
|
|
|
14
14
|
import { PikkuSessionService } from '../services/user-session-service.js'
|
|
15
15
|
import { MissingScopeError, ReadonlySessionError } from '../errors/errors.js'
|
|
16
16
|
import { createInvocationAudit } from '../services/audit-service.js'
|
|
17
|
+
import { SecretAccessDeniedError } from '../services/secretless.js'
|
|
17
18
|
|
|
18
19
|
beforeEach(() => {
|
|
19
20
|
resetPikkuState()
|
|
@@ -637,7 +638,7 @@ describe('runPikkuFunc - Integration Tests', () => {
|
|
|
637
638
|
singletonServices: mockSingletonServices,
|
|
638
639
|
data: () => ({}),
|
|
639
640
|
auth: false,
|
|
640
|
-
wire: { session: { readonly: true }
|
|
641
|
+
wire: { session: { readonly: true } },
|
|
641
642
|
}),
|
|
642
643
|
ReadonlySessionError
|
|
643
644
|
)
|
|
@@ -1563,3 +1564,54 @@ describe('runPikkuFunc - scopes', () => {
|
|
|
1563
1564
|
})
|
|
1564
1565
|
})
|
|
1565
1566
|
})
|
|
1567
|
+
|
|
1568
|
+
describe('runPikkuFunc - secrets', () => {
|
|
1569
|
+
const singletonServicesWithSecrets = {
|
|
1570
|
+
...mockSingletonServices,
|
|
1571
|
+
secrets: { getSecret: async () => 'super-secret' },
|
|
1572
|
+
} as any
|
|
1573
|
+
|
|
1574
|
+
const addSecretReadingFunction = (
|
|
1575
|
+
funcName: string,
|
|
1576
|
+
seen: { error?: unknown }
|
|
1577
|
+
) => {
|
|
1578
|
+
addTestFunction(funcName, {
|
|
1579
|
+
func: async (services: any) => {
|
|
1580
|
+
try {
|
|
1581
|
+
return services.secrets
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
seen.error = error
|
|
1584
|
+
return 'denied'
|
|
1585
|
+
}
|
|
1586
|
+
},
|
|
1587
|
+
})
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
const run = (funcName: string) =>
|
|
1591
|
+
runPikkuFunc('rpc', Math.random().toString(), funcName, {
|
|
1592
|
+
singletonServices: singletonServicesWithSecrets,
|
|
1593
|
+
getAllServices: () => singletonServicesWithSecrets,
|
|
1594
|
+
data: () => ({}),
|
|
1595
|
+
auth: false,
|
|
1596
|
+
wire: {},
|
|
1597
|
+
})
|
|
1598
|
+
|
|
1599
|
+
test('a function never receives the secrets service', async () => {
|
|
1600
|
+
const seen: { error?: unknown } = {}
|
|
1601
|
+
addSecretReadingFunction('readsSecrets', seen)
|
|
1602
|
+
|
|
1603
|
+
assert.equal(await run('readsSecrets'), 'denied')
|
|
1604
|
+
assert.ok(seen.error instanceof SecretAccessDeniedError)
|
|
1605
|
+
})
|
|
1606
|
+
|
|
1607
|
+
test('no metadata flag can restore the secrets service', async () => {
|
|
1608
|
+
const seen: { error?: unknown } = {}
|
|
1609
|
+
addSecretReadingFunction('claimsAnExemption', seen)
|
|
1610
|
+
Object.assign(pikkuState(null, 'function', 'meta')['claimsAnExemption']!, {
|
|
1611
|
+
secretBroker: true,
|
|
1612
|
+
})
|
|
1613
|
+
|
|
1614
|
+
assert.equal(await run('claimsAnExemption'), 'denied')
|
|
1615
|
+
assert.ok(seen.error instanceof SecretAccessDeniedError)
|
|
1616
|
+
})
|
|
1617
|
+
})
|