@kb-labs/rest-api-app 1.3.0
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/.kb/agents/sessions/session-1771531145393-ws3uh6q/turns.json +42 -0
- package/.kb/agents/sessions/session-1771548930486-ui6rtvm/turns.json +33 -0
- package/.kb/agents/sessions/session-1771549691485-nibqroq/turns.json +133 -0
- package/.kb/agents/sessions/session-1771550943182-dkwqdjc/turns.json +130 -0
- package/.kb/agents/sessions/session-1771551564182-cr4bwzn/turns.json +349 -0
- package/.kb/agents/sessions/session-1771552401370-g4ztwd1/turns.json +206 -0
- package/.kb/commit/current/plan.json +248 -0
- package/.kb/commit/current/status.json +472 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js +215 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/health.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js +271 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/list.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js +230 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/logs.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js +228 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/metrics.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js +281 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/run.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js +266 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/status.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js +284 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/commands/workflow-run.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/flags-WLdpSZl4.d.ts +77 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.d.ts +2 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js +441 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/index.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.d.ts +196 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js +441 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/manifest.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.d.ts +19 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js +48 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/cron-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.d.ts +6 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js +170 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/index.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.d.ts +12 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js +52 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-cancel-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.d.ts +22 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js +52 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-detail-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.d.ts +13 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js +52 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-logs-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.d.ts +9 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js +43 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/job-steps-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.d.ts +26 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js +63 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/jobs-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.d.ts +9 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js +46 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/pending-approvals-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.d.ts +16 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js +58 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/resolve-approval-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.d.ts +9 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js +46 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/run-detail-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js +50 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/runs-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.d.ts +7 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js +39 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/stats-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js +46 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-detail-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.d.ts +13 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js +47 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-cancel-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.d.ts +14 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js +53 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-run-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.d.ts +13 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js +54 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflow-runs-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.d.ts +12 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js +51 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/rest/workflows-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.d.ts +17 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js +133 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/logs-channel.js.map +1 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js +46 -0
- package/.kb/runtime/workspaces/ws_94c2deab-4cf9-49cb-94ac-3958062754a1/ws/progress-channel.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js +215 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/health.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js +271 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/list.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js +230 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/logs.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js +228 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/metrics.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js +281 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/run.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js +266 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/status.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js +284 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/commands/workflow-run.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/flags-WLdpSZl4.d.ts +77 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.d.ts +2 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js +441 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/index.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.d.ts +196 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js +441 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/manifest.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.d.ts +19 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js +48 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/cron-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.d.ts +6 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js +170 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/index.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.d.ts +12 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js +52 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-cancel-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.d.ts +22 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js +52 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-detail-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.d.ts +13 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js +52 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-logs-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.d.ts +9 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js +43 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/job-steps-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.d.ts +26 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js +63 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/jobs-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.d.ts +9 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js +46 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/pending-approvals-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.d.ts +16 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js +58 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/resolve-approval-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.d.ts +9 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js +46 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/run-detail-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js +50 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/runs-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.d.ts +7 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js +39 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/stats-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.d.ts +10 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js +46 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-detail-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.d.ts +13 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js +47 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-cancel-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.d.ts +14 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js +53 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-run-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.d.ts +13 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js +54 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflow-runs-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.d.ts +12 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js +51 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/rest/workflows-list-handler.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.d.ts +17 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js +133 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/logs-channel.js.map +1 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.d.ts +11 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js +46 -0
- package/.kb/runtime/workspaces/ws_f0f301b8-388d-4dc4-b301-475f2bc95f02/ws/progress-channel.js.map +1 -0
- package/README.md +54 -0
- package/eslint.config.js +23 -0
- package/package.json +72 -0
- package/src/.kb/agents/sessions/session-1773470583796-ximqnfk/kpi-baseline.json +17 -0
- package/src/.kb/memory/sessions/session-1773470583796-ximqnfk/fact-sheet.json +1 -0
- package/src/bootstrap.ts +233 -0
- package/src/diagnostics/header-debug.ts +37 -0
- package/src/events/hub.ts +60 -0
- package/src/events/index.ts +1 -0
- package/src/index.ts +13 -0
- package/src/manifest.ts +21 -0
- package/src/middleware/__tests__/cache.spec.ts +184 -0
- package/src/middleware/__tests__/envelope.spec.ts +268 -0
- package/src/middleware/__tests__/metrics.redis.spec.ts +64 -0
- package/src/middleware/__tests__/rate-limit.spec.ts +283 -0
- package/src/middleware/__tests__/request-id.spec.ts +160 -0
- package/src/middleware/cache.ts +67 -0
- package/src/middleware/envelope.ts +136 -0
- package/src/middleware/error-guard.ts +69 -0
- package/src/middleware/index.ts +37 -0
- package/src/middleware/metrics.ts +810 -0
- package/src/middleware/mock-mode.ts +44 -0
- package/src/middleware/prom-metrics.ts +284 -0
- package/src/middleware/rate-limit-routes.ts +73 -0
- package/src/middleware/rate-limit.ts +72 -0
- package/src/middleware/request-id.ts +41 -0
- package/src/middleware/request-timeout.ts +69 -0
- package/src/middleware/security-headers.ts +24 -0
- package/src/middleware/startup-guard.ts +107 -0
- package/src/observability/service-contract.ts +192 -0
- package/src/plugins/index.ts +118 -0
- package/src/routes/__tests__/adapters-date-helpers.spec.ts +332 -0
- package/src/routes/__tests__/adapters.spec.ts +680 -0
- package/src/routes/__tests__/cache.spec.ts +264 -0
- package/src/routes/__tests__/events.spec.ts +434 -0
- package/src/routes/__tests__/health.spec.ts +414 -0
- package/src/routes/__tests__/logs.spec.ts +494 -0
- package/src/routes/__tests__/observability.spec.ts +392 -0
- package/src/routes/__tests__/plugins.spec.ts +931 -0
- package/src/routes/__tests__/workflow-management.spec.ts +698 -0
- package/src/routes/__tests__/workflows.spec.ts +204 -0
- package/src/routes/adapter-call.ts +220 -0
- package/src/routes/adapters.ts +1206 -0
- package/src/routes/analytics.ts +328 -0
- package/src/routes/cache.ts +104 -0
- package/src/routes/debug-routes.ts +95 -0
- package/src/routes/events.ts +115 -0
- package/src/routes/health.ts +348 -0
- package/src/routes/hello.ts +47 -0
- package/src/routes/index.ts +395 -0
- package/src/routes/jobs.ts +253 -0
- package/src/routes/logs.ts +718 -0
- package/src/routes/metrics.ts +120 -0
- package/src/routes/observability.ts +1388 -0
- package/src/routes/openapi.ts +101 -0
- package/src/routes/platform.ts +143 -0
- package/src/routes/plugins.ts +998 -0
- package/src/routes/readiness.ts +83 -0
- package/src/routes/workflow-management.ts +469 -0
- package/src/routes/workflows.ts +256 -0
- package/src/server.ts +203 -0
- package/src/services/historical-metrics.ts +496 -0
- package/src/services/incident-analyzer.ts +289 -0
- package/src/services/incident-detector.ts +691 -0
- package/src/services/incident-schema.sql +76 -0
- package/src/services/incident-storage.ts +593 -0
- package/src/services/system-metrics-collector.ts +244 -0
- package/src/types/fastify.d.ts +58 -0
- package/src/utils/__tests__/sse-auth.spec.ts +113 -0
- package/src/utils/path-helpers.ts +15 -0
- package/src/utils/repo.ts +42 -0
- package/src/utils/schema.ts +20 -0
- package/src/utils/sse-auth.ts +81 -0
- package/test-sandbox.sh +105 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +18 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/rest-api-app/middleware/__tests__/rate-limit
|
|
3
|
+
*
|
|
4
|
+
* Tests for tenant-aware rate limiting middleware.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import Fastify from 'fastify';
|
|
9
|
+
import type { FastifyRequest } from 'fastify';
|
|
10
|
+
|
|
11
|
+
import { extractTenantId, createRateLimitMiddleware } from '../rate-limit';
|
|
12
|
+
|
|
13
|
+
interface TenantRateLimiter {
|
|
14
|
+
checkLimit(tenantId: string, resource: string): Promise<RateLimitResult>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RateLimitResult {
|
|
18
|
+
allowed: boolean;
|
|
19
|
+
limit: number;
|
|
20
|
+
remaining: number;
|
|
21
|
+
resetAt: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('extractTenantId', () => {
|
|
25
|
+
const originalEnv = process.env;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
process.env = { ...originalEnv };
|
|
29
|
+
delete process.env.KB_TENANT_ID;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
process.env = originalEnv;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should extract tenant ID from X-Tenant-ID header', () => {
|
|
37
|
+
const request = {
|
|
38
|
+
headers: { 'x-tenant-id': 'acme-corp' },
|
|
39
|
+
} as any;
|
|
40
|
+
|
|
41
|
+
expect(extractTenantId(request)).toBe('acme-corp');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should fall back to KB_TENANT_ID env var when header is missing', () => {
|
|
45
|
+
process.env.KB_TENANT_ID = 'env-tenant';
|
|
46
|
+
|
|
47
|
+
const request = {
|
|
48
|
+
headers: {},
|
|
49
|
+
} as any;
|
|
50
|
+
|
|
51
|
+
expect(extractTenantId(request)).toBe('env-tenant');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should use default when no header and no env var', () => {
|
|
55
|
+
const request = {
|
|
56
|
+
headers: {},
|
|
57
|
+
} as any;
|
|
58
|
+
|
|
59
|
+
expect(extractTenantId(request)).toBe('default');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should prefer header over env var', () => {
|
|
63
|
+
process.env.KB_TENANT_ID = 'env-tenant';
|
|
64
|
+
|
|
65
|
+
const request = {
|
|
66
|
+
headers: { 'x-tenant-id': 'header-tenant' },
|
|
67
|
+
} as any;
|
|
68
|
+
|
|
69
|
+
expect(extractTenantId(request)).toBe('header-tenant');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('createRateLimitMiddleware', () => {
|
|
74
|
+
let app: ReturnType<typeof Fastify>;
|
|
75
|
+
let mockRateLimiter: TenantRateLimiter;
|
|
76
|
+
|
|
77
|
+
const createMockRateLimiter = (overrides: Partial<RateLimitResult> = {}): TenantRateLimiter => {
|
|
78
|
+
const defaultResult: RateLimitResult = {
|
|
79
|
+
allowed: true,
|
|
80
|
+
limit: 100,
|
|
81
|
+
remaining: 99,
|
|
82
|
+
resetAt: Date.now() + 60000,
|
|
83
|
+
...overrides,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
checkLimit: vi.fn().mockResolvedValue(defaultResult),
|
|
88
|
+
} as any;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
beforeEach(async () => {
|
|
92
|
+
mockRateLimiter = createMockRateLimiter();
|
|
93
|
+
app = Fastify({ logger: false });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
afterEach(async () => {
|
|
97
|
+
await app.close();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('allowed requests', () => {
|
|
101
|
+
it('should allow request and add rate limit headers', async () => {
|
|
102
|
+
mockRateLimiter = createMockRateLimiter({
|
|
103
|
+
allowed: true,
|
|
104
|
+
limit: 100,
|
|
105
|
+
remaining: 95,
|
|
106
|
+
resetAt: Date.now() + 60000,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
110
|
+
app.addHook('preHandler', middleware);
|
|
111
|
+
|
|
112
|
+
app.get('/test', async () => ({ ok: true }));
|
|
113
|
+
await app.ready();
|
|
114
|
+
|
|
115
|
+
const response = await app.inject({
|
|
116
|
+
method: 'GET',
|
|
117
|
+
url: '/test',
|
|
118
|
+
headers: { 'x-tenant-id': 'test-tenant' },
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(response.statusCode).toBe(200);
|
|
122
|
+
expect(response.headers['x-ratelimit-limit']).toBe('100');
|
|
123
|
+
expect(response.headers['x-ratelimit-remaining']).toBe('95');
|
|
124
|
+
expect(response.headers['x-ratelimit-reset']).toBeDefined();
|
|
125
|
+
|
|
126
|
+
expect(mockRateLimiter.checkLimit).toHaveBeenCalledWith('test-tenant', 'requests');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should store tenantId in request context', async () => {
|
|
130
|
+
let capturedTenantId: string | undefined;
|
|
131
|
+
|
|
132
|
+
mockRateLimiter = createMockRateLimiter({ allowed: true });
|
|
133
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
134
|
+
app.addHook('preHandler', middleware);
|
|
135
|
+
|
|
136
|
+
app.get('/test', async (request: FastifyRequest) => {
|
|
137
|
+
capturedTenantId = (request as any).tenantId;
|
|
138
|
+
return { tenantId: capturedTenantId };
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await app.ready();
|
|
142
|
+
|
|
143
|
+
await app.inject({
|
|
144
|
+
method: 'GET',
|
|
145
|
+
url: '/test',
|
|
146
|
+
headers: { 'x-tenant-id': 'captured-tenant' },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(capturedTenantId).toBe('captured-tenant');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('rate limited requests', () => {
|
|
154
|
+
it('should return 429 when rate limit exceeded', async () => {
|
|
155
|
+
const resetAt = Date.now() + 30000; // 30 seconds from now
|
|
156
|
+
|
|
157
|
+
mockRateLimiter = createMockRateLimiter({
|
|
158
|
+
allowed: false,
|
|
159
|
+
limit: 100,
|
|
160
|
+
remaining: 0,
|
|
161
|
+
resetAt,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
165
|
+
app.addHook('preHandler', middleware);
|
|
166
|
+
|
|
167
|
+
app.get('/test', async () => ({ ok: true }));
|
|
168
|
+
await app.ready();
|
|
169
|
+
|
|
170
|
+
const response = await app.inject({
|
|
171
|
+
method: 'GET',
|
|
172
|
+
url: '/test',
|
|
173
|
+
headers: { 'x-tenant-id': 'limited-tenant' },
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(response.statusCode).toBe(429);
|
|
177
|
+
|
|
178
|
+
const body = response.json();
|
|
179
|
+
expect(body.error).toBe('RATE_LIMIT_EXCEEDED');
|
|
180
|
+
expect(body.message).toBe('Too many requests. Please try again later.');
|
|
181
|
+
expect(body.retryAfter).toBeDefined();
|
|
182
|
+
expect(body.limit).toBe(100);
|
|
183
|
+
expect(body.resetAt).toBeDefined();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should include Retry-After header when rate limited', async () => {
|
|
187
|
+
const resetAt = Date.now() + 45000; // 45 seconds from now
|
|
188
|
+
|
|
189
|
+
mockRateLimiter = createMockRateLimiter({
|
|
190
|
+
allowed: false,
|
|
191
|
+
resetAt,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
195
|
+
app.addHook('preHandler', middleware);
|
|
196
|
+
|
|
197
|
+
app.get('/test', async () => ({ ok: true }));
|
|
198
|
+
await app.ready();
|
|
199
|
+
|
|
200
|
+
const response = await app.inject({
|
|
201
|
+
method: 'GET',
|
|
202
|
+
url: '/test',
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(response.statusCode).toBe(429);
|
|
206
|
+
expect(response.headers['retry-after']).toBeDefined();
|
|
207
|
+
|
|
208
|
+
const retryAfter = parseInt(response.headers['retry-after'] as string, 10);
|
|
209
|
+
expect(retryAfter).toBeGreaterThan(0);
|
|
210
|
+
expect(retryAfter).toBeLessThanOrEqual(45);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should still include rate limit headers when limited', async () => {
|
|
214
|
+
mockRateLimiter = createMockRateLimiter({
|
|
215
|
+
allowed: false,
|
|
216
|
+
limit: 50,
|
|
217
|
+
remaining: 0,
|
|
218
|
+
resetAt: Date.now() + 60000,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
222
|
+
app.addHook('preHandler', middleware);
|
|
223
|
+
|
|
224
|
+
app.get('/test', async () => ({ ok: true }));
|
|
225
|
+
await app.ready();
|
|
226
|
+
|
|
227
|
+
const response = await app.inject({
|
|
228
|
+
method: 'GET',
|
|
229
|
+
url: '/test',
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(response.headers['x-ratelimit-limit']).toBe('50');
|
|
233
|
+
expect(response.headers['x-ratelimit-remaining']).toBe('0');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('tenant isolation', () => {
|
|
238
|
+
it('should check rate limit for each tenant separately', async () => {
|
|
239
|
+
mockRateLimiter = createMockRateLimiter({ allowed: true });
|
|
240
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
241
|
+
app.addHook('preHandler', middleware);
|
|
242
|
+
|
|
243
|
+
app.get('/test', async () => ({ ok: true }));
|
|
244
|
+
await app.ready();
|
|
245
|
+
|
|
246
|
+
// First tenant
|
|
247
|
+
await app.inject({
|
|
248
|
+
method: 'GET',
|
|
249
|
+
url: '/test',
|
|
250
|
+
headers: { 'x-tenant-id': 'tenant-a' },
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Second tenant
|
|
254
|
+
await app.inject({
|
|
255
|
+
method: 'GET',
|
|
256
|
+
url: '/test',
|
|
257
|
+
headers: { 'x-tenant-id': 'tenant-b' },
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(mockRateLimiter.checkLimit).toHaveBeenCalledTimes(2);
|
|
261
|
+
expect(mockRateLimiter.checkLimit).toHaveBeenCalledWith('tenant-a', 'requests');
|
|
262
|
+
expect(mockRateLimiter.checkLimit).toHaveBeenCalledWith('tenant-b', 'requests');
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe('default tenant', () => {
|
|
267
|
+
it('should use default tenant when no header provided', async () => {
|
|
268
|
+
mockRateLimiter = createMockRateLimiter({ allowed: true });
|
|
269
|
+
const middleware = createRateLimitMiddleware(mockRateLimiter);
|
|
270
|
+
app.addHook('preHandler', middleware);
|
|
271
|
+
|
|
272
|
+
app.get('/test', async () => ({ ok: true }));
|
|
273
|
+
await app.ready();
|
|
274
|
+
|
|
275
|
+
await app.inject({
|
|
276
|
+
method: 'GET',
|
|
277
|
+
url: '/test',
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(mockRateLimiter.checkLimit).toHaveBeenCalledWith('default', 'requests');
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/rest-api-app/middleware/__tests__/request-id
|
|
3
|
+
*
|
|
4
|
+
* Tests for request ID middleware.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import Fastify from 'fastify';
|
|
9
|
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
10
|
+
|
|
11
|
+
import { registerRequestIdMiddleware } from '../request-id';
|
|
12
|
+
|
|
13
|
+
describe('registerRequestIdMiddleware', () => {
|
|
14
|
+
let app: ReturnType<typeof Fastify>;
|
|
15
|
+
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
app = Fastify({ logger: false });
|
|
18
|
+
registerRequestIdMiddleware(app);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
await app.close();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('request ID generation', () => {
|
|
26
|
+
it('should generate request ID if not provided', async () => {
|
|
27
|
+
app.get('/test', async () => {
|
|
28
|
+
return { message: 'ok' };
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await app.ready();
|
|
32
|
+
|
|
33
|
+
const response = await app.inject({
|
|
34
|
+
method: 'GET',
|
|
35
|
+
url: '/test',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(response.statusCode).toBe(200);
|
|
39
|
+
expect(response.headers['x-request-id']).toBeDefined();
|
|
40
|
+
expect(response.headers['x-trace-id']).toBeDefined();
|
|
41
|
+
expect(typeof response.headers['x-request-id']).toBe('string');
|
|
42
|
+
expect(typeof response.headers['x-trace-id']).toBe('string');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should use provided request ID from header', async () => {
|
|
46
|
+
app.get('/test', async () => {
|
|
47
|
+
return { message: 'ok' };
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await app.ready();
|
|
51
|
+
|
|
52
|
+
const customRequestId = 'custom-request-123';
|
|
53
|
+
const response = await app.inject({
|
|
54
|
+
method: 'GET',
|
|
55
|
+
url: '/test',
|
|
56
|
+
headers: {
|
|
57
|
+
'x-request-id': customRequestId,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(response.statusCode).toBe(200);
|
|
62
|
+
expect(response.headers['x-request-id']).toBe(customRequestId);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should use provided trace ID from header', async () => {
|
|
66
|
+
app.get('/test', async () => {
|
|
67
|
+
return { message: 'ok' };
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await app.ready();
|
|
71
|
+
|
|
72
|
+
const customTraceId = 'custom-trace-456';
|
|
73
|
+
const response = await app.inject({
|
|
74
|
+
method: 'GET',
|
|
75
|
+
url: '/test',
|
|
76
|
+
headers: {
|
|
77
|
+
'x-trace-id': customTraceId,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(response.statusCode).toBe(200);
|
|
82
|
+
expect(response.headers['x-trace-id']).toBe(customTraceId);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('logger integration', () => {
|
|
87
|
+
it('should attach logger to request object', async () => {
|
|
88
|
+
let requestLogger: any;
|
|
89
|
+
|
|
90
|
+
app.get('/test', async (request: FastifyRequest) => {
|
|
91
|
+
requestLogger = (request as any).log;
|
|
92
|
+
return { message: 'ok' };
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await app.ready();
|
|
96
|
+
|
|
97
|
+
await app.inject({
|
|
98
|
+
method: 'GET',
|
|
99
|
+
url: '/test',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Verify request.log is defined and has expected methods
|
|
103
|
+
expect(requestLogger).toBeDefined();
|
|
104
|
+
expect(typeof requestLogger.debug).toBe('function');
|
|
105
|
+
expect(typeof requestLogger.info).toBe('function');
|
|
106
|
+
expect(typeof requestLogger.warn).toBe('function');
|
|
107
|
+
expect(typeof requestLogger.error).toBe('function');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should attach logger to reply object', async () => {
|
|
111
|
+
let replyLogger: any;
|
|
112
|
+
|
|
113
|
+
app.get('/test', async (request: FastifyRequest, reply: FastifyReply) => {
|
|
114
|
+
replyLogger = (reply as any).log;
|
|
115
|
+
return { message: 'ok' };
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await app.ready();
|
|
119
|
+
|
|
120
|
+
await app.inject({
|
|
121
|
+
method: 'GET',
|
|
122
|
+
url: '/test',
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Verify reply.log is defined and has expected methods
|
|
126
|
+
expect(replyLogger).toBeDefined();
|
|
127
|
+
expect(typeof replyLogger.debug).toBe('function');
|
|
128
|
+
expect(typeof replyLogger.info).toBe('function');
|
|
129
|
+
expect(typeof replyLogger.warn).toBe('function');
|
|
130
|
+
expect(typeof replyLogger.error).toBe('function');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should allow calling logger methods without errors', async () => {
|
|
134
|
+
let requestLogger: any;
|
|
135
|
+
|
|
136
|
+
app.get('/test', async (request: FastifyRequest) => {
|
|
137
|
+
requestLogger = (request as any).log;
|
|
138
|
+
|
|
139
|
+
// Call all logger methods to ensure they work
|
|
140
|
+
requestLogger.debug('Debug message', { foo: 'bar' });
|
|
141
|
+
requestLogger.info('Info message', { baz: 'qux' });
|
|
142
|
+
requestLogger.warn('Warning message');
|
|
143
|
+
requestLogger.error('Error message');
|
|
144
|
+
|
|
145
|
+
return { message: 'ok' };
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
await app.ready();
|
|
149
|
+
|
|
150
|
+
const response = await app.inject({
|
|
151
|
+
method: 'GET',
|
|
152
|
+
url: '/test',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(response.statusCode).toBe(200);
|
|
156
|
+
// If we got here without errors, logger methods work correctly
|
|
157
|
+
expect(requestLogger).toBeDefined();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/rest-api-app/middleware/cache
|
|
3
|
+
* Cache middleware for ETag/Last-Modified support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FastifyInstance } from 'fastify';
|
|
7
|
+
import { createHash } from 'crypto';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generate ETag from content
|
|
11
|
+
*/
|
|
12
|
+
export function generateETag(content: string | Buffer | object): string {
|
|
13
|
+
const contentStr = typeof content === 'object' ? JSON.stringify(content) : content.toString();
|
|
14
|
+
const hash = createHash('sha256').update(contentStr).digest('hex');
|
|
15
|
+
return `"${hash.substring(0, 16)}"`; // Short hash for ETag
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Register cache middleware
|
|
20
|
+
*/
|
|
21
|
+
export function registerCacheMiddleware(server: FastifyInstance): void {
|
|
22
|
+
server.addHook('onSend', async (request, reply, payload) => {
|
|
23
|
+
// Only cache successful GET requests
|
|
24
|
+
if (request.method !== 'GET' || reply.statusCode >= 300) {
|
|
25
|
+
return payload;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Skip cache for streaming responses
|
|
29
|
+
if (reply.getHeader('content-type') === 'text/event-stream') {
|
|
30
|
+
return payload;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check If-None-Match header (ETag)
|
|
34
|
+
const ifNoneMatch = request.headers['if-none-match'];
|
|
35
|
+
if (ifNoneMatch && payload) {
|
|
36
|
+
const payloadStr = typeof payload === 'object' ? JSON.stringify(payload) : String(payload);
|
|
37
|
+
const etag = generateETag(payloadStr);
|
|
38
|
+
|
|
39
|
+
if (ifNoneMatch === etag || ifNoneMatch === `W/${etag}`) {
|
|
40
|
+
reply.code(304).header('ETag', etag);
|
|
41
|
+
return undefined; // Not Modified
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
reply.header('ETag', etag);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check If-Modified-Since header (Last-Modified)
|
|
48
|
+
const ifModifiedSince = request.headers['if-modified-since'];
|
|
49
|
+
if (ifModifiedSince && payload) {
|
|
50
|
+
// For now, we'll use finishedAt from job metadata if available
|
|
51
|
+
// This is a simplified implementation - in production, use actual modification time
|
|
52
|
+
const lastModified = new Date().toUTCString();
|
|
53
|
+
reply.header('Last-Modified', lastModified);
|
|
54
|
+
|
|
55
|
+
const ifModifiedSinceDate = new Date(ifModifiedSince);
|
|
56
|
+
const lastModifiedDate = new Date(lastModified);
|
|
57
|
+
|
|
58
|
+
if (lastModifiedDate <= ifModifiedSinceDate) {
|
|
59
|
+
reply.code(304);
|
|
60
|
+
return undefined; // Not Modified
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return payload;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/rest-api-app/middleware/envelope
|
|
3
|
+
* Response envelope wrapper middleware
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FastifyInstance } from 'fastify';
|
|
7
|
+
import type { RestApiConfig } from '@kb-labs/rest-api-core';
|
|
8
|
+
import { errorEnvelopeSchema } from '@kb-labs/rest-api-contracts';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Register envelope middleware
|
|
12
|
+
*/
|
|
13
|
+
export function registerEnvelopeMiddleware(
|
|
14
|
+
server: FastifyInstance,
|
|
15
|
+
config: RestApiConfig
|
|
16
|
+
): void {
|
|
17
|
+
// Add response schema version header and wrap in envelope
|
|
18
|
+
// Use 'onSend' hook to modify payload after Fastify serializes it but before sending
|
|
19
|
+
// This ensures we get the serialized JSON string and can wrap it properly
|
|
20
|
+
server.addHook('onSend', async (request, reply, payload) => {
|
|
21
|
+
// Set header with try-catch to prevent ERR_HTTP_HEADERS_SENT
|
|
22
|
+
try {
|
|
23
|
+
if (!reply.raw.headersSent) {
|
|
24
|
+
reply.header('x-schema-version', config.apiVersion);
|
|
25
|
+
}
|
|
26
|
+
} catch (err) {
|
|
27
|
+
// Headers already sent, ignore
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Skip envelope wrapping for streaming responses
|
|
31
|
+
if (reply.getHeader('content-type') === 'text/event-stream') {
|
|
32
|
+
return payload;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Skip if payload is not a string (Buffer or Stream)
|
|
36
|
+
if (typeof payload !== 'string') {
|
|
37
|
+
return payload;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Parse the serialized JSON payload
|
|
41
|
+
let parsedPayload: unknown;
|
|
42
|
+
try {
|
|
43
|
+
parsedPayload = JSON.parse(payload);
|
|
44
|
+
} catch {
|
|
45
|
+
// If not JSON, return as is
|
|
46
|
+
return payload;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If payload is already an envelope (has 'ok' field), return as is
|
|
50
|
+
if (parsedPayload && typeof parsedPayload === 'object' && 'ok' in parsedPayload) {
|
|
51
|
+
return payload;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Wrap all non-streaming responses in envelope
|
|
55
|
+
// Note: Even 4xx/5xx responses with data should be wrapped with ok: true
|
|
56
|
+
// (e.g., 503 for health/ready still contains useful data)
|
|
57
|
+
|
|
58
|
+
// Handle empty payloads
|
|
59
|
+
let dataToWrap = parsedPayload;
|
|
60
|
+
|
|
61
|
+
// If payload is undefined or null, wrap as null
|
|
62
|
+
if (parsedPayload === null || parsedPayload === undefined) {
|
|
63
|
+
dataToWrap = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// For responses with data (including 4xx/5xx with payload), wrap with ok: true
|
|
67
|
+
// The HTTP status code indicates the result, but envelope.ok indicates successful request processing
|
|
68
|
+
// Only responses without payload (handled by error handler) should have ok: false
|
|
69
|
+
const envelope = {
|
|
70
|
+
ok: true as const,
|
|
71
|
+
data: dataToWrap,
|
|
72
|
+
meta: {
|
|
73
|
+
requestId: (request as any).id || '',
|
|
74
|
+
durationMs: reply.elapsedTime || 0,
|
|
75
|
+
apiVersion: config.apiVersion,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Set Content-Type header if not set
|
|
80
|
+
if (!reply.getHeader('content-type')) {
|
|
81
|
+
try {
|
|
82
|
+
if (!reply.raw.headersSent) {
|
|
83
|
+
reply.header('content-type', 'application/json');
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// Headers already sent, ignore
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Serialize and return envelope as string
|
|
91
|
+
return JSON.stringify(envelope);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Error handler
|
|
95
|
+
server.setErrorHandler(async (error: any, request, reply) => {
|
|
96
|
+
const statusCode = error.statusCode || 500;
|
|
97
|
+
|
|
98
|
+
// Extract error details
|
|
99
|
+
const errorCode = (error as any).code || 'E_INTERNAL';
|
|
100
|
+
const message = error.message || 'Internal server error';
|
|
101
|
+
const details = (error as any).details || {};
|
|
102
|
+
const cause = (error as any).cause;
|
|
103
|
+
const traceId = (error as any).traceId;
|
|
104
|
+
|
|
105
|
+
// Store error code for metrics
|
|
106
|
+
(reply as any).errorCode = errorCode;
|
|
107
|
+
|
|
108
|
+
// Log error with correlation ID
|
|
109
|
+
if ((request as any).kbLogger) {
|
|
110
|
+
(request as any).kbLogger.error('Request error', error, {
|
|
111
|
+
errorCode,
|
|
112
|
+
statusCode,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const errorEnvelope = errorEnvelopeSchema.parse({
|
|
117
|
+
ok: false,
|
|
118
|
+
error: {
|
|
119
|
+
code: errorCode,
|
|
120
|
+
message,
|
|
121
|
+
details,
|
|
122
|
+
cause,
|
|
123
|
+
traceId,
|
|
124
|
+
},
|
|
125
|
+
meta: {
|
|
126
|
+
requestId: request.id,
|
|
127
|
+
durationMs: reply.elapsedTime || 0,
|
|
128
|
+
apiVersion: config.apiVersion,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
reply.status(statusCode);
|
|
133
|
+
return errorEnvelope;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|