@pikku/core 0.11.1 → 0.12.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/CHANGELOG.md +55 -0
- package/dist/crypto-utils.d.ts +2 -0
- package/dist/crypto-utils.js +78 -0
- package/dist/errors/error-handler.js +3 -3
- package/dist/errors/errors.d.ts +8 -0
- package/dist/errors/errors.js +16 -0
- package/dist/errors/index.d.ts +0 -1
- package/dist/errors/index.js +0 -1
- package/dist/function/function-runner.d.ts +10 -3
- package/dist/function/function-runner.js +136 -31
- package/dist/function/functions.types.d.ts +35 -12
- package/dist/function/functions.types.js +16 -6
- package/dist/function/index.d.ts +2 -2
- package/dist/function/index.js +1 -2
- package/dist/handle-error.js +1 -10
- package/dist/index.d.ts +30 -24
- package/dist/index.js +19 -27
- package/dist/middleware/auth-apikey.d.ts +1 -1
- package/dist/middleware/auth-apikey.js +3 -3
- package/dist/middleware/auth-bearer.d.ts +1 -1
- package/dist/middleware/auth-bearer.js +3 -6
- package/dist/middleware/auth-cookie.d.ts +1 -1
- package/dist/middleware/auth-cookie.js +6 -8
- package/dist/middleware/cors.d.ts +46 -0
- package/dist/middleware/cors.js +73 -0
- package/dist/middleware/index.d.ts +5 -3
- package/dist/middleware/index.js +5 -3
- package/dist/middleware/remote-auth.d.ts +5 -0
- package/dist/middleware/remote-auth.js +55 -0
- package/dist/middleware/timeout.d.ts +1 -1
- package/dist/middleware-runner.d.ts +3 -14
- package/dist/middleware-runner.js +14 -16
- package/dist/permissions.d.ts +3 -86
- package/dist/permissions.js +20 -80
- package/dist/pikku-state.d.ts +33 -104
- package/dist/pikku-state.js +150 -66
- package/dist/schema.d.ts +8 -6
- package/dist/schema.js +31 -18
- package/dist/services/ai-agent-runner-service.d.ts +24 -0
- package/dist/services/ai-agent-runner-service.js +1 -0
- package/dist/services/ai-run-state-service.d.ts +13 -0
- package/dist/services/ai-run-state-service.js +1 -0
- package/dist/services/ai-storage-service.d.ts +18 -0
- package/dist/services/ai-storage-service.js +1 -0
- package/dist/services/deployment-service.d.ts +19 -0
- package/dist/services/deployment-service.js +1 -0
- package/dist/services/gopass-secrets.d.ts +47 -0
- package/dist/services/gopass-secrets.js +94 -0
- package/dist/services/in-memory-trigger-service.d.ts +23 -0
- package/dist/services/in-memory-trigger-service.js +50 -0
- package/dist/services/in-memory-workflow-service.d.ts +59 -0
- package/dist/services/in-memory-workflow-service.js +287 -0
- package/dist/services/index.d.ts +25 -11
- package/dist/services/index.js +10 -13
- package/dist/services/local-secrets.d.ts +23 -0
- package/dist/services/local-secrets.js +45 -0
- package/dist/services/local-variables.d.ts +5 -0
- package/dist/services/local-variables.js +18 -0
- package/dist/services/scheduler-service.d.ts +21 -1
- package/dist/services/scheduler-service.js +14 -0
- package/dist/services/schema-service.d.ts +6 -0
- package/dist/services/scoped-secret-service.d.ts +16 -0
- package/dist/services/scoped-secret-service.js +35 -0
- package/dist/services/secret-service.d.ts +25 -4
- package/dist/services/trigger-service.d.ts +23 -0
- package/dist/services/trigger-service.js +1 -0
- package/dist/services/typed-secret-service.d.ts +30 -0
- package/dist/services/typed-secret-service.js +40 -0
- package/dist/services/typed-variables-service.d.ts +27 -0
- package/dist/services/typed-variables-service.js +46 -0
- package/dist/services/user-session-service.d.ts +13 -0
- package/dist/services/user-session-service.js +17 -0
- package/dist/services/variables-service.d.ts +5 -0
- package/dist/services/workflow-service.d.ts +43 -0
- package/dist/services/workflow-service.js +1 -0
- package/dist/types/core.types.d.ts +54 -10
- package/dist/types/core.types.js +7 -0
- package/dist/types/state.types.d.ts +127 -0
- package/dist/types/state.types.js +1 -0
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +60 -3
- package/dist/version.d.ts +6 -0
- package/dist/version.js +19 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +29 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +199 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +9 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +36 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +60 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +312 -0
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +15 -0
- package/dist/wirings/ai-agent/ai-agent-registry.js +47 -0
- package/dist/wirings/ai-agent/ai-agent-runner.d.ts +3 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +96 -0
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +11 -0
- package/dist/wirings/ai-agent/ai-agent-stream.js +503 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +254 -0
- package/dist/wirings/ai-agent/ai-agent.types.js +1 -0
- package/dist/wirings/ai-agent/index.d.ts +5 -0
- package/dist/wirings/ai-agent/index.js +4 -0
- package/dist/wirings/channel/channel-common.d.ts +7 -4
- package/dist/wirings/channel/channel-common.js +15 -8
- package/dist/wirings/channel/channel-handler.js +13 -4
- package/dist/wirings/channel/channel-middleware-runner.d.ts +9 -0
- package/dist/wirings/channel/channel-middleware-runner.js +73 -0
- package/dist/wirings/channel/channel-runner.js +12 -12
- package/dist/wirings/channel/channel.types.d.ts +5 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +21 -0
- package/dist/wirings/channel/define-channel-routes.js +23 -0
- package/dist/wirings/channel/index.d.ts +10 -8
- package/dist/wirings/channel/index.js +7 -8
- package/dist/wirings/channel/local/index.d.ts +3 -3
- package/dist/wirings/channel/local/index.js +3 -3
- package/dist/wirings/channel/local/local-channel-runner.js +18 -6
- package/dist/wirings/channel/log-channels.js +1 -1
- package/dist/wirings/channel/serverless/index.d.ts +1 -1
- package/dist/wirings/channel/serverless/index.js +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +17 -6
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
- package/dist/wirings/cli/channel/index.d.ts +1 -1
- package/dist/wirings/cli/channel/index.js +1 -1
- package/dist/wirings/cli/cli-runner.d.ts +1 -1
- package/dist/wirings/cli/cli-runner.js +22 -18
- package/dist/wirings/cli/cli.types.d.ts +7 -6
- package/dist/wirings/cli/define-cli-commands.d.ts +19 -0
- package/dist/wirings/cli/define-cli-commands.js +20 -0
- package/dist/wirings/cli/index.d.ts +2 -3
- package/dist/wirings/cli/index.js +1 -3
- package/dist/wirings/http/http-routes.d.ts +69 -0
- package/dist/wirings/http/http-routes.js +139 -0
- package/dist/wirings/http/http-runner.d.ts +2 -2
- package/dist/wirings/http/http-runner.js +62 -27
- package/dist/wirings/http/http.types.d.ts +77 -24
- package/dist/wirings/http/index.d.ts +5 -4
- package/dist/wirings/http/index.js +4 -3
- package/dist/wirings/http/log-http-routes.js +1 -1
- package/dist/wirings/http/routers/path-to-regex.js +2 -2
- package/dist/wirings/mcp/index.d.ts +4 -3
- package/dist/wirings/mcp/index.js +3 -3
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -3
- package/dist/wirings/mcp/mcp-runner.js +42 -56
- package/dist/wirings/mcp/mcp.types.d.ts +3 -3
- package/dist/wirings/node/index.d.ts +2 -0
- package/dist/wirings/node/index.js +1 -0
- package/dist/wirings/node/node.types.d.ts +20 -0
- package/dist/wirings/node/node.types.js +1 -0
- package/dist/wirings/oauth2/index.d.ts +3 -0
- package/dist/wirings/oauth2/index.js +2 -0
- package/dist/wirings/oauth2/oauth2-client.d.ts +47 -0
- package/dist/wirings/oauth2/oauth2-client.js +267 -0
- package/dist/wirings/oauth2/oauth2.types.d.ts +65 -0
- package/dist/wirings/oauth2/oauth2.types.js +4 -0
- package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +20 -0
- package/dist/wirings/oauth2/wire-oauth2-credential.js +21 -0
- package/dist/wirings/queue/index.d.ts +2 -2
- package/dist/wirings/queue/index.js +1 -1
- package/dist/wirings/queue/queue-runner.d.ts +7 -0
- package/dist/wirings/queue/queue-runner.js +23 -12
- package/dist/wirings/queue/queue.types.d.ts +8 -2
- package/dist/wirings/rpc/index.d.ts +1 -1
- package/dist/wirings/rpc/index.js +1 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +40 -13
- package/dist/wirings/rpc/rpc-runner.js +191 -25
- package/dist/wirings/rpc/rpc-types.d.ts +15 -4
- package/dist/wirings/scheduler/index.d.ts +3 -3
- package/dist/wirings/scheduler/index.js +2 -3
- package/dist/wirings/scheduler/log-schedulers.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +5 -0
- package/dist/wirings/scheduler/scheduler-runner.js +28 -10
- package/dist/wirings/secret/index.d.ts +3 -0
- package/dist/wirings/secret/index.js +2 -0
- package/dist/wirings/secret/secret.types.d.ts +27 -0
- package/dist/wirings/secret/secret.types.js +1 -0
- package/dist/wirings/secret/validate-secret-definitions.d.ts +6 -0
- package/dist/wirings/secret/validate-secret-definitions.js +55 -0
- package/dist/wirings/trigger/index.d.ts +3 -0
- package/dist/wirings/trigger/index.js +2 -0
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +26 -0
- package/dist/wirings/trigger/pikku-trigger-service.js +75 -0
- package/dist/wirings/trigger/trigger-runner.d.ts +42 -0
- package/dist/wirings/trigger/trigger-runner.js +85 -0
- package/dist/wirings/trigger/trigger.types.d.ts +130 -0
- package/dist/wirings/trigger/trigger.types.js +40 -0
- package/dist/wirings/variable/index.d.ts +3 -0
- package/dist/wirings/variable/index.js +2 -0
- package/dist/wirings/variable/validate-variable-definitions.d.ts +6 -0
- package/dist/wirings/variable/validate-variable-definitions.js +45 -0
- package/dist/wirings/variable/variable.types.d.ts +18 -0
- package/dist/wirings/variable/variable.types.js +1 -0
- package/dist/wirings/workflow/dsl/index.d.ts +5 -0
- package/dist/wirings/workflow/dsl/index.js +4 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +292 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
- package/dist/wirings/workflow/dsl/workflow-runner.js +23 -0
- package/dist/wirings/workflow/graph/graph-node.d.ts +117 -0
- package/dist/wirings/workflow/graph/graph-node.js +58 -0
- package/dist/wirings/workflow/graph/graph-runner.d.ts +9 -0
- package/dist/wirings/workflow/graph/graph-runner.js +540 -0
- package/dist/wirings/workflow/graph/index.d.ts +4 -0
- package/dist/wirings/workflow/graph/index.js +3 -0
- package/dist/wirings/workflow/graph/template.d.ts +15 -0
- package/dist/wirings/workflow/graph/template.js +27 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +15 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.js +8 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +82 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
- package/dist/wirings/workflow/index.d.ts +7 -12
- package/dist/wirings/workflow/index.js +8 -11
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +114 -10
- package/dist/wirings/workflow/pikku-workflow-service.js +386 -115
- package/dist/wirings/workflow/workflow-helpers.d.ts +36 -0
- package/dist/wirings/workflow/workflow-helpers.js +38 -0
- package/dist/wirings/workflow/workflow-utils.d.ts +1 -0
- package/dist/wirings/workflow/workflow-utils.js +1 -0
- package/dist/wirings/workflow/workflow.types.d.ts +83 -214
- package/package.json +13 -6
- package/run-tests.sh +4 -1
- package/src/crypto-utils.ts +99 -0
- package/src/errors/error-handler.ts +3 -3
- package/src/errors/errors.ts +17 -0
- package/src/errors/index.ts +0 -1
- package/src/factory-functions.test.ts +42 -2
- package/src/function/function-runner.test.ts +5 -12
- package/src/function/function-runner.ts +195 -40
- package/src/function/functions.types.ts +66 -14
- package/src/function/index.ts +5 -2
- package/src/handle-error.ts +2 -10
- package/src/index.ts +106 -27
- package/src/middleware/auth-apikey.test.ts +15 -12
- package/src/middleware/auth-apikey.ts +3 -3
- package/src/middleware/auth-bearer.test.ts +17 -14
- package/src/middleware/auth-bearer.ts +25 -27
- package/src/middleware/auth-cookie.test.ts +16 -13
- package/src/middleware/auth-cookie.ts +6 -8
- package/src/middleware/cors.ts +96 -0
- package/src/middleware/index.ts +5 -3
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware-runner.test.ts +122 -0
- package/src/middleware-runner.ts +24 -17
- package/src/permissions.test.ts +102 -86
- package/src/permissions.ts +32 -93
- package/src/pikku-state.ts +168 -201
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.ts +50 -18
- package/src/services/ai-agent-runner-service.ts +29 -0
- package/src/services/ai-run-state-service.ts +20 -0
- package/src/services/ai-storage-service.ts +39 -0
- package/src/services/deployment-service.ts +22 -0
- package/src/services/gopass-secrets.ts +95 -0
- package/src/services/in-memory-trigger-service.ts +68 -0
- package/src/services/in-memory-workflow-service.ts +395 -0
- package/src/services/index.ts +49 -14
- package/src/services/local-secrets.ts +51 -0
- package/src/services/local-variables.ts +22 -0
- package/src/services/scheduler-service.ts +29 -1
- package/src/services/schema-service.ts +7 -0
- package/src/services/scoped-secret-service.ts +41 -0
- package/src/services/secret-service.ts +25 -4
- package/src/services/trigger-service.ts +38 -0
- package/src/services/typed-secret-service.ts +70 -0
- package/src/services/typed-variables-service.ts +81 -0
- package/src/services/user-session-service.ts +23 -0
- package/src/services/variables-service.ts +5 -0
- package/src/services/workflow-service.ts +94 -0
- package/src/types/core.types.ts +82 -8
- package/src/types/state.types.ts +169 -0
- package/src/utils.ts +74 -5
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +300 -0
- package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +470 -0
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
- package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +75 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +137 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +246 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +774 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +273 -0
- package/src/wirings/ai-agent/index.ts +28 -0
- package/src/wirings/channel/channel-common.ts +32 -19
- package/src/wirings/channel/channel-handler.ts +18 -5
- package/src/wirings/channel/channel-middleware-runner.ts +119 -0
- package/src/wirings/channel/channel-runner.ts +14 -15
- package/src/wirings/channel/channel.types.ts +17 -1
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/index.ts +23 -8
- package/src/wirings/channel/local/index.ts +3 -3
- package/src/wirings/channel/local/local-channel-runner.test.ts +47 -2
- package/src/wirings/channel/local/local-channel-runner.ts +22 -6
- package/src/wirings/channel/log-channels.ts +1 -1
- package/src/wirings/channel/serverless/index.ts +5 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +22 -6
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
- package/src/wirings/cli/channel/index.ts +1 -1
- package/src/wirings/cli/cli-runner.test.ts +28 -28
- package/src/wirings/cli/cli-runner.ts +27 -19
- package/src/wirings/cli/cli.types.ts +12 -11
- package/src/wirings/cli/command-parser.test.ts +8 -8
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +10 -4
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +198 -0
- package/src/wirings/http/http-runner.test.ts +5 -5
- package/src/wirings/http/http-runner.ts +80 -29
- package/src/wirings/http/http.types.ts +121 -23
- package/src/wirings/http/index.ts +16 -4
- package/src/wirings/http/log-http-routes.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
- package/src/wirings/http/routers/path-to-regex.ts +2 -2
- package/src/wirings/mcp/index.ts +27 -3
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +3 -3
- package/src/wirings/mcp/mcp-runner.ts +56 -67
- package/src/wirings/mcp/mcp.types.ts +3 -3
- package/src/wirings/node/index.ts +2 -0
- package/src/wirings/node/node.types.ts +23 -0
- package/src/wirings/oauth2/index.ts +3 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
- package/src/wirings/oauth2/oauth2-client.ts +335 -0
- package/src/wirings/oauth2/oauth2.types.ts +69 -0
- package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
- package/src/wirings/queue/index.ts +14 -2
- package/src/wirings/queue/queue-runner.test.ts +115 -73
- package/src/wirings/queue/queue-runner.ts +39 -13
- package/src/wirings/queue/queue.types.ts +12 -2
- package/src/wirings/rpc/index.ts +1 -1
- package/src/wirings/rpc/rpc-runner.ts +270 -34
- package/src/wirings/rpc/rpc-types.ts +21 -4
- package/src/wirings/scheduler/index.ts +7 -4
- package/src/wirings/scheduler/log-schedulers.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.test.ts +97 -53
- package/src/wirings/scheduler/scheduler-runner.ts +50 -10
- package/src/wirings/secret/index.ts +9 -0
- package/src/wirings/secret/secret.types.ts +32 -0
- package/src/wirings/secret/validate-secret-definitions.ts +82 -0
- package/src/wirings/trigger/index.ts +10 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +134 -0
- package/src/wirings/trigger/trigger-runner.ts +138 -0
- package/src/wirings/trigger/trigger.types.ts +178 -0
- package/src/wirings/variable/index.ts +8 -0
- package/src/wirings/variable/validate-variable-definitions.ts +69 -0
- package/src/wirings/variable/variable.types.ts +22 -0
- package/src/wirings/workflow/dsl/index.ts +31 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
- package/src/wirings/workflow/graph/graph-node.ts +222 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +495 -0
- package/src/wirings/workflow/graph/graph-runner.ts +816 -0
- package/src/wirings/workflow/graph/index.ts +14 -0
- package/src/wirings/workflow/graph/template.ts +42 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
- package/src/wirings/workflow/index.ts +64 -26
- package/src/wirings/workflow/pikku-workflow-service.test.ts +212 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +571 -136
- package/src/wirings/workflow/workflow-helpers.ts +79 -0
- package/src/wirings/workflow/workflow-utils.ts +0 -0
- package/src/wirings/workflow/workflow.types.ts +123 -279
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/workflow/workflow-runner.d.ts +0 -34
- package/dist/wirings/workflow/workflow-runner.js +0 -65
- package/src/function/function-runner.ts.bak +0 -188
- package/src/wirings/workflow/workflow-runner.ts +0 -72
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { pikkuMiddleware, pikkuMiddlewareFactory } from '../types/core.types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CORS middleware that handles cross-origin requests including OPTIONS preflight.
|
|
5
|
+
*
|
|
6
|
+
* Sets appropriate CORS headers on all responses and short-circuits OPTIONS
|
|
7
|
+
* preflight requests with a 204 No Content response.
|
|
8
|
+
*
|
|
9
|
+
* @param options.origin - Allowed origin(s). Use `'*'` for any origin, a string for a single origin, or an array for multiple origins. Defaults to `'*'`.
|
|
10
|
+
* @param options.methods - Allowed HTTP methods. Defaults to common methods.
|
|
11
|
+
* @param options.headers - Allowed request headers. Defaults to common headers.
|
|
12
|
+
* @param options.credentials - Whether to allow credentials. Defaults to `false`.
|
|
13
|
+
* @param options.maxAge - Preflight cache duration in seconds. Defaults to `86400` (24 hours).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { cors } from '@pikku/core/middleware'
|
|
18
|
+
*
|
|
19
|
+
* // Allow all origins
|
|
20
|
+
* addHTTPMiddleware('*', [cors()])
|
|
21
|
+
*
|
|
22
|
+
* // Specific origin with credentials
|
|
23
|
+
* addHTTPMiddleware('*', [
|
|
24
|
+
* cors({
|
|
25
|
+
* origin: 'https://app.example.com',
|
|
26
|
+
* credentials: true,
|
|
27
|
+
* })
|
|
28
|
+
* ])
|
|
29
|
+
*
|
|
30
|
+
* // Multiple origins
|
|
31
|
+
* addHTTPMiddleware('*', [
|
|
32
|
+
* cors({
|
|
33
|
+
* origin: ['https://app.example.com', 'https://admin.example.com'],
|
|
34
|
+
* })
|
|
35
|
+
* ])
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const cors = pikkuMiddlewareFactory<{
|
|
39
|
+
origin?: string | string[]
|
|
40
|
+
methods?: string[]
|
|
41
|
+
headers?: string[]
|
|
42
|
+
credentials?: boolean
|
|
43
|
+
maxAge?: number
|
|
44
|
+
}>(
|
|
45
|
+
({
|
|
46
|
+
origin = '*',
|
|
47
|
+
methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
|
48
|
+
headers = ['Content-Type', 'Authorization', 'x-api-key'],
|
|
49
|
+
credentials = false,
|
|
50
|
+
maxAge = 86400,
|
|
51
|
+
} = {}) =>
|
|
52
|
+
pikkuMiddleware({
|
|
53
|
+
name: 'CORS',
|
|
54
|
+
description: 'Handles cross-origin requests including OPTIONS preflight',
|
|
55
|
+
func: async (_services, wires, next) => {
|
|
56
|
+
const request = wires.http?.request
|
|
57
|
+
const response = wires.http?.response
|
|
58
|
+
|
|
59
|
+
if (!request || !response) {
|
|
60
|
+
return next()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const requestOrigin = request.header('origin')
|
|
64
|
+
|
|
65
|
+
let allowedOrigin: string
|
|
66
|
+
if (Array.isArray(origin)) {
|
|
67
|
+
allowedOrigin =
|
|
68
|
+
requestOrigin && origin.includes(requestOrigin)
|
|
69
|
+
? requestOrigin
|
|
70
|
+
: origin[0]
|
|
71
|
+
} else {
|
|
72
|
+
allowedOrigin = origin
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
response.header('Access-Control-Allow-Origin', allowedOrigin)
|
|
76
|
+
response.header('Access-Control-Allow-Methods', methods.join(', '))
|
|
77
|
+
response.header('Access-Control-Allow-Headers', headers.join(', '))
|
|
78
|
+
|
|
79
|
+
if (credentials) {
|
|
80
|
+
response.header('Access-Control-Allow-Credentials', 'true')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (Array.isArray(origin)) {
|
|
84
|
+
response.header('Vary', 'Origin')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (request.method() === 'options') {
|
|
88
|
+
response.header('Access-Control-Max-Age', String(maxAge))
|
|
89
|
+
response.status(204).json(undefined as any)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return next()
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
)
|
package/src/middleware/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
1
|
+
export { authAPIKey } from './auth-apikey.js'
|
|
2
|
+
export { authCookie } from './auth-cookie.js'
|
|
3
|
+
export { authBearer } from './auth-bearer.js'
|
|
4
|
+
export { pikkuRemoteAuthMiddleware } from './remote-auth.js'
|
|
5
|
+
export { cors } from './cors.js'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { UnauthorizedError } from '../errors/errors.js'
|
|
2
|
+
import { pikkuMiddleware } from '../types/core.types.js'
|
|
3
|
+
import { decryptJSON } from '../crypto-utils.js'
|
|
4
|
+
|
|
5
|
+
export const pikkuRemoteAuthMiddleware = pikkuMiddleware(
|
|
6
|
+
async ({ secrets, jwt }, { http, setSession }, next) => {
|
|
7
|
+
if (!http?.request || !secrets) {
|
|
8
|
+
return next()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let secret: string
|
|
12
|
+
try {
|
|
13
|
+
secret = await secrets.getSecret('PIKKU_REMOTE_SECRET')
|
|
14
|
+
} catch {
|
|
15
|
+
return next()
|
|
16
|
+
}
|
|
17
|
+
if (!jwt) {
|
|
18
|
+
throw new Error('PIKKU_REMOTE_SECRET set but JWT service missing')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const authHeader =
|
|
22
|
+
http.request.header('authorization') ||
|
|
23
|
+
http.request.header('Authorization')
|
|
24
|
+
|
|
25
|
+
if (!authHeader) {
|
|
26
|
+
throw new UnauthorizedError()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const [scheme, token] = authHeader.split(' ')
|
|
30
|
+
if (scheme !== 'Bearer' || !token) {
|
|
31
|
+
throw new UnauthorizedError()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let payload: any
|
|
35
|
+
try {
|
|
36
|
+
payload = await jwt.decode(token)
|
|
37
|
+
} catch {
|
|
38
|
+
throw new UnauthorizedError()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (payload?.aud !== 'pikku-remote') {
|
|
42
|
+
throw new UnauthorizedError()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (payload?.fn && http.request.path().startsWith('/rpc/')) {
|
|
46
|
+
const fn = decodeURIComponent(http.request.path().slice('/rpc/'.length))
|
|
47
|
+
if (fn && payload.fn !== fn) {
|
|
48
|
+
throw new UnauthorizedError()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (payload?.session) {
|
|
53
|
+
try {
|
|
54
|
+
const decrypted = await decryptJSON<{ session?: unknown }>(
|
|
55
|
+
secret,
|
|
56
|
+
payload.session
|
|
57
|
+
)
|
|
58
|
+
if (decrypted?.session && setSession) {
|
|
59
|
+
await setSession(decrypted.session)
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
throw new UnauthorizedError()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return next()
|
|
67
|
+
}
|
|
68
|
+
)
|
|
@@ -189,6 +189,128 @@ describe('combineMiddleware', () => {
|
|
|
189
189
|
assert.equal(result[1], middleware2)
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
+
test('should apply parent tag middleware to namespaced tag', () => {
|
|
193
|
+
const billingMiddleware: CorePikkuMiddleware = async (
|
|
194
|
+
services,
|
|
195
|
+
wire,
|
|
196
|
+
next
|
|
197
|
+
) => {
|
|
198
|
+
await next()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
addMiddleware('billing', [billingMiddleware])
|
|
202
|
+
|
|
203
|
+
const result = combineMiddleware('http', Math.random().toString(), {
|
|
204
|
+
wireInheritedMiddleware: [{ type: 'tag', tag: 'billing:read' }],
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
assert.equal(result.length, 1)
|
|
208
|
+
assert.equal(result[0], billingMiddleware)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('should apply exact tag middleware for non-namespaced tag', () => {
|
|
212
|
+
const billingMiddleware: CorePikkuMiddleware = async (
|
|
213
|
+
services,
|
|
214
|
+
wire,
|
|
215
|
+
next
|
|
216
|
+
) => {
|
|
217
|
+
await next()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
addMiddleware('billing', [billingMiddleware])
|
|
221
|
+
|
|
222
|
+
const result = combineMiddleware('http', Math.random().toString(), {
|
|
223
|
+
wireInheritedMiddleware: [{ type: 'tag', tag: 'billing' }],
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
assert.equal(result.length, 1)
|
|
227
|
+
assert.equal(result[0], billingMiddleware)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
test('should apply both exact and parent tag middleware (specific first)', () => {
|
|
231
|
+
const billingMiddleware: CorePikkuMiddleware = async (
|
|
232
|
+
services,
|
|
233
|
+
wire,
|
|
234
|
+
next
|
|
235
|
+
) => {
|
|
236
|
+
await next()
|
|
237
|
+
}
|
|
238
|
+
const billingReadMiddleware: CorePikkuMiddleware = async (
|
|
239
|
+
services,
|
|
240
|
+
wire,
|
|
241
|
+
next
|
|
242
|
+
) => {
|
|
243
|
+
await next()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
addMiddleware('billing', [billingMiddleware])
|
|
247
|
+
addMiddleware('billing:read', [billingReadMiddleware])
|
|
248
|
+
|
|
249
|
+
const result = combineMiddleware('http', Math.random().toString(), {
|
|
250
|
+
wireInheritedMiddleware: [{ type: 'tag', tag: 'billing:read' }],
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
assert.equal(result.length, 2)
|
|
254
|
+
assert.equal(result[0], billingReadMiddleware)
|
|
255
|
+
assert.equal(result[1], billingMiddleware)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test('should resolve multi-level namespace tags', () => {
|
|
259
|
+
const billingMiddleware: CorePikkuMiddleware = async (
|
|
260
|
+
services,
|
|
261
|
+
wire,
|
|
262
|
+
next
|
|
263
|
+
) => {
|
|
264
|
+
await next()
|
|
265
|
+
}
|
|
266
|
+
const billingReadMiddleware: CorePikkuMiddleware = async (
|
|
267
|
+
services,
|
|
268
|
+
wire,
|
|
269
|
+
next
|
|
270
|
+
) => {
|
|
271
|
+
await next()
|
|
272
|
+
}
|
|
273
|
+
const billingReadAdminMiddleware: CorePikkuMiddleware = async (
|
|
274
|
+
services,
|
|
275
|
+
wire,
|
|
276
|
+
next
|
|
277
|
+
) => {
|
|
278
|
+
await next()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
addMiddleware('billing', [billingMiddleware])
|
|
282
|
+
addMiddleware('billing:read', [billingReadMiddleware])
|
|
283
|
+
addMiddleware('billing:read:admin', [billingReadAdminMiddleware])
|
|
284
|
+
|
|
285
|
+
const result = combineMiddleware('http', Math.random().toString(), {
|
|
286
|
+
wireInheritedMiddleware: [{ type: 'tag', tag: 'billing:read:admin' }],
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
assert.equal(result.length, 3)
|
|
290
|
+
assert.equal(result[0], billingReadAdminMiddleware)
|
|
291
|
+
assert.equal(result[1], billingReadMiddleware)
|
|
292
|
+
assert.equal(result[2], billingMiddleware)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
test('should apply parent tag middleware via funcInheritedMiddleware', () => {
|
|
296
|
+
const billingMiddleware: CorePikkuMiddleware = async (
|
|
297
|
+
services,
|
|
298
|
+
wire,
|
|
299
|
+
next
|
|
300
|
+
) => {
|
|
301
|
+
await next()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
addMiddleware('billing', [billingMiddleware])
|
|
305
|
+
|
|
306
|
+
const result = combineMiddleware('http', Math.random().toString(), {
|
|
307
|
+
funcInheritedMiddleware: [{ type: 'tag', tag: 'billing:write' }],
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
assert.equal(result.length, 1)
|
|
311
|
+
assert.equal(result[0], billingMiddleware)
|
|
312
|
+
})
|
|
313
|
+
|
|
192
314
|
test('should ignore non-existent tags', () => {
|
|
193
315
|
const middleware: CorePikkuMiddleware = async (services, wire, next) => {
|
|
194
316
|
await next()
|
package/src/middleware-runner.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
MiddlewareMetadata,
|
|
8
8
|
} from './types/core.types.js'
|
|
9
9
|
import { pikkuState } from './pikku-state.js'
|
|
10
|
-
import { freezeDedupe } from './utils.js'
|
|
10
|
+
import { freezeDedupe, getTagGroups } from './utils.js'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Runs a chain of middleware functions in sequence before executing the main function.
|
|
@@ -82,9 +82,10 @@ export const runMiddleware = async <Middleware extends CorePikkuMiddleware>(
|
|
|
82
82
|
*/
|
|
83
83
|
export const addMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
|
|
84
84
|
tag: string,
|
|
85
|
-
middleware: CorePikkuMiddlewareGroup
|
|
85
|
+
middleware: CorePikkuMiddlewareGroup,
|
|
86
|
+
packageName: string | null = null
|
|
86
87
|
): CorePikkuMiddlewareGroup => {
|
|
87
|
-
const tagGroups = pikkuState('middleware', 'tagGroup')
|
|
88
|
+
const tagGroups = pikkuState(packageName, 'middleware', 'tagGroup')
|
|
88
89
|
tagGroups[tag] = middleware
|
|
89
90
|
return middleware
|
|
90
91
|
}
|
|
@@ -95,15 +96,13 @@ export const addMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
|
|
|
95
96
|
* This function looks up middleware that was registered with registerMiddleware.
|
|
96
97
|
* It's used internally by the framework to resolve middleware references in metadata.
|
|
97
98
|
*
|
|
98
|
-
* @param {string} name - The unique name (
|
|
99
|
+
* @param {string} name - The unique name (pikkuFuncId) of the middleware function.
|
|
99
100
|
* @returns {CorePikkuMiddleware | undefined} The middleware function, or undefined if not found.
|
|
100
101
|
*
|
|
101
102
|
* @internal
|
|
102
103
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
): CorePikkuMiddleware | undefined => {
|
|
106
|
-
const middlewareStore = pikkuState('misc', 'middleware')
|
|
104
|
+
const getMiddlewareByName = (name: string): CorePikkuMiddleware | undefined => {
|
|
105
|
+
const middlewareStore = pikkuState(null, 'misc', 'middleware')
|
|
107
106
|
const middleware = middlewareStore[name]
|
|
108
107
|
return middleware?.[0]
|
|
109
108
|
}
|
|
@@ -117,7 +116,9 @@ const middlewareCache: Record<
|
|
|
117
116
|
channel: {},
|
|
118
117
|
queue: {},
|
|
119
118
|
scheduler: {},
|
|
119
|
+
trigger: {},
|
|
120
120
|
mcp: {},
|
|
121
|
+
agent: {},
|
|
121
122
|
cli: {},
|
|
122
123
|
workflow: {},
|
|
123
124
|
}
|
|
@@ -152,11 +153,13 @@ export const combineMiddleware = (
|
|
|
152
153
|
wireMiddleware,
|
|
153
154
|
funcInheritedMiddleware,
|
|
154
155
|
funcMiddleware,
|
|
156
|
+
packageName = null,
|
|
155
157
|
}: {
|
|
156
158
|
wireInheritedMiddleware?: MiddlewareMetadata[]
|
|
157
159
|
wireMiddleware?: CorePikkuMiddleware[]
|
|
158
160
|
funcInheritedMiddleware?: MiddlewareMetadata[]
|
|
159
161
|
funcMiddleware?: CorePikkuMiddleware[]
|
|
162
|
+
packageName?: string | null
|
|
160
163
|
} = {}
|
|
161
164
|
): readonly CorePikkuMiddleware[] => {
|
|
162
165
|
if (middlewareCache[wireType][uid]) {
|
|
@@ -170,16 +173,19 @@ export const combineMiddleware = (
|
|
|
170
173
|
for (const meta of wireInheritedMiddleware) {
|
|
171
174
|
if (meta.type === 'http') {
|
|
172
175
|
// Look up HTTP middleware group from pikkuState
|
|
173
|
-
const group = pikkuState('middleware', 'httpGroup')[
|
|
176
|
+
const group = pikkuState(packageName, 'middleware', 'httpGroup')[
|
|
177
|
+
meta.route
|
|
178
|
+
]
|
|
174
179
|
if (group) {
|
|
175
180
|
// At runtime, all factories should be resolved to middleware
|
|
176
181
|
resolved.push(...(group as CorePikkuMiddleware[]))
|
|
177
182
|
}
|
|
178
183
|
} else if (meta.type === 'tag') {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
const groups = getTagGroups(
|
|
185
|
+
pikkuState(packageName, 'middleware', 'tagGroup'),
|
|
186
|
+
meta.tag
|
|
187
|
+
)
|
|
188
|
+
for (const group of groups) {
|
|
183
189
|
resolved.push(...(group as CorePikkuMiddleware[]))
|
|
184
190
|
}
|
|
185
191
|
} else if (meta.type === 'wire') {
|
|
@@ -201,10 +207,11 @@ export const combineMiddleware = (
|
|
|
201
207
|
if (funcInheritedMiddleware) {
|
|
202
208
|
for (const meta of funcInheritedMiddleware) {
|
|
203
209
|
if (meta.type === 'tag') {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
const groups = getTagGroups(
|
|
211
|
+
pikkuState(packageName, 'middleware', 'tagGroup'),
|
|
212
|
+
meta.tag
|
|
213
|
+
)
|
|
214
|
+
for (const group of groups) {
|
|
208
215
|
resolved.push(...(group as CorePikkuMiddleware[]))
|
|
209
216
|
}
|
|
210
217
|
}
|
package/src/permissions.test.ts
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
import { describe, test, beforeEach } from 'node:test'
|
|
2
2
|
import * as assert from 'node:assert'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
getPermissionsForTags,
|
|
6
|
-
runPermissions,
|
|
7
|
-
} from './permissions.js'
|
|
8
|
-
import { resetPikkuState } from './pikku-state.js'
|
|
3
|
+
import { addPermission, runPermissions } from './permissions.js'
|
|
4
|
+
import { pikkuState, resetPikkuState } from './pikku-state.js'
|
|
9
5
|
import { CoreServices, CoreUserSession } from './types/core.types.js'
|
|
10
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CorePermissionGroup,
|
|
8
|
+
CorePikkuPermission,
|
|
9
|
+
} from './function/functions.types.js'
|
|
11
10
|
|
|
12
11
|
beforeEach(() => {
|
|
13
12
|
resetPikkuState()
|
|
14
13
|
})
|
|
15
14
|
|
|
15
|
+
// Test helper to get permissions for tags (not exported from core to keep it lean)
|
|
16
|
+
const getPermissionsForTags = (
|
|
17
|
+
tags?: string[]
|
|
18
|
+
): (CorePermissionGroup | CorePikkuPermission)[] => {
|
|
19
|
+
if (!tags || tags.length === 0) {
|
|
20
|
+
return []
|
|
21
|
+
}
|
|
22
|
+
const permissionsStore = pikkuState(null, 'permissions', 'tagGroup')
|
|
23
|
+
const result: (CorePermissionGroup | CorePikkuPermission)[] = []
|
|
24
|
+
for (const tag of new Set(tags)) {
|
|
25
|
+
const tagPermissions = permissionsStore[tag]
|
|
26
|
+
if (tagPermissions) {
|
|
27
|
+
if (Array.isArray(tagPermissions)) {
|
|
28
|
+
result.push(...tagPermissions)
|
|
29
|
+
} else {
|
|
30
|
+
result.push(tagPermissions)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result
|
|
35
|
+
}
|
|
36
|
+
|
|
16
37
|
const mockServices: CoreServices = {
|
|
17
38
|
logger: {
|
|
18
39
|
info: () => {},
|
|
@@ -61,99 +82,99 @@ describe('addPermission', () => {
|
|
|
61
82
|
assert.equal(permissions[0], mockPermissionGroup)
|
|
62
83
|
})
|
|
63
84
|
|
|
64
|
-
test('should
|
|
65
|
-
const
|
|
85
|
+
test('should apply parent tag permission to namespaced tag via runPermissions', async () => {
|
|
86
|
+
const billingPermission = async () => true
|
|
66
87
|
|
|
67
|
-
addPermission('
|
|
88
|
+
addPermission('billing', [billingPermission])
|
|
68
89
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
"Permissions for tag 'duplicateTag' already exist. Use a different tag or remove the existing permissions first.",
|
|
76
|
-
}
|
|
77
|
-
)
|
|
90
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
91
|
+
wireInheritedPermissions: [{ type: 'tag', tag: 'billing:write' }],
|
|
92
|
+
services: mockServices,
|
|
93
|
+
wire: {},
|
|
94
|
+
data: {},
|
|
95
|
+
})
|
|
78
96
|
})
|
|
79
|
-
})
|
|
80
97
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
test('should apply both parent and exact tag permissions', async () => {
|
|
99
|
+
const executionOrder: string[] = []
|
|
100
|
+
const billingPermission = async () => {
|
|
101
|
+
executionOrder.push('billing')
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
104
|
+
const billingReadPermission = async () => {
|
|
105
|
+
executionOrder.push('billing:read')
|
|
106
|
+
return true
|
|
107
|
+
}
|
|
86
108
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
assert.deepEqual(permissions, [])
|
|
90
|
-
})
|
|
109
|
+
addPermission('billing', [billingPermission])
|
|
110
|
+
addPermission('billing:read', [billingReadPermission])
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
113
|
+
wireInheritedPermissions: [{ type: 'tag', tag: 'billing:read' }],
|
|
114
|
+
services: mockServices,
|
|
115
|
+
wire: {},
|
|
116
|
+
data: {},
|
|
117
|
+
})
|
|
95
118
|
|
|
96
|
-
|
|
97
|
-
assert.equal(permissions.length, 1)
|
|
98
|
-
assert.equal(permissions[0], mockPermission)
|
|
119
|
+
assert.deepEqual(executionOrder, ['billing:read'])
|
|
99
120
|
})
|
|
100
121
|
|
|
101
|
-
test('should
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
122
|
+
test('should resolve multi-level namespace permissions', async () => {
|
|
123
|
+
const executionOrder: string[] = []
|
|
124
|
+
const billingPermission = async () => {
|
|
125
|
+
executionOrder.push('billing')
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
const billingReadPermission = async () => {
|
|
129
|
+
executionOrder.push('billing:read')
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
const billingReadAdminPermission = async () => {
|
|
133
|
+
executionOrder.push('billing:read:admin')
|
|
134
|
+
return true
|
|
135
|
+
}
|
|
113
136
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
addPermission('billing', [billingPermission])
|
|
138
|
+
addPermission('billing:read', [billingReadPermission])
|
|
139
|
+
addPermission('billing:read:admin', [billingReadAdminPermission])
|
|
117
140
|
|
|
118
|
-
|
|
141
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
142
|
+
wireInheritedPermissions: [{ type: 'tag', tag: 'billing:read:admin' }],
|
|
143
|
+
services: mockServices,
|
|
144
|
+
wire: {},
|
|
145
|
+
data: {},
|
|
146
|
+
})
|
|
119
147
|
|
|
120
|
-
|
|
121
|
-
assert.equal(permissions.length, 2)
|
|
122
|
-
assert.equal(permissions[0], mockPermission1)
|
|
123
|
-
assert.equal(permissions[1], mockPermission2)
|
|
148
|
+
assert.deepEqual(executionOrder, ['billing:read:admin'])
|
|
124
149
|
})
|
|
125
150
|
|
|
126
|
-
test('should
|
|
127
|
-
const
|
|
128
|
-
permissions: [async () => true],
|
|
129
|
-
}
|
|
151
|
+
test('should apply parent tag permission via funcInheritedPermissions', async () => {
|
|
152
|
+
const billingPermission = async () => true
|
|
130
153
|
|
|
131
|
-
addPermission('
|
|
154
|
+
addPermission('billing', [billingPermission])
|
|
132
155
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
157
|
+
funcInheritedPermissions: [{ type: 'tag', tag: 'billing:delete' }],
|
|
158
|
+
services: mockServices,
|
|
159
|
+
wire: {},
|
|
160
|
+
data: {},
|
|
161
|
+
})
|
|
136
162
|
})
|
|
137
163
|
|
|
138
|
-
test('should
|
|
164
|
+
test('should throw error when tag already exists', () => {
|
|
139
165
|
const mockPermission = async () => true
|
|
140
|
-
addPermission('dedupeTestTag', [mockPermission])
|
|
141
|
-
|
|
142
|
-
const permissions = getPermissionsForTags([
|
|
143
|
-
'dedupeTestTag',
|
|
144
|
-
'dedupeTestTag',
|
|
145
|
-
])
|
|
146
|
-
assert.equal(permissions.length, 1)
|
|
147
|
-
assert.equal(permissions[0], mockPermission)
|
|
148
|
-
})
|
|
149
166
|
|
|
150
|
-
|
|
151
|
-
const mockPermission = async () => true
|
|
152
|
-
addPermission('existingTag', [mockPermission])
|
|
167
|
+
addPermission('duplicateTag', [mockPermission])
|
|
153
168
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
169
|
+
assert.throws(
|
|
170
|
+
() => {
|
|
171
|
+
addPermission('duplicateTag', [mockPermission])
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
message:
|
|
175
|
+
"Permissions for tag 'duplicateTag' already exist. Use a different tag or remove the existing permissions first.",
|
|
176
|
+
}
|
|
177
|
+
)
|
|
157
178
|
})
|
|
158
179
|
})
|
|
159
180
|
|
|
@@ -229,13 +250,8 @@ describe('runPermissions', () => {
|
|
|
229
250
|
data: {},
|
|
230
251
|
})
|
|
231
252
|
|
|
232
|
-
// Order: wireInheritedPermissions (wiringTag)
|
|
233
|
-
assert.deepEqual(executionOrder, [
|
|
234
|
-
'wiringTag',
|
|
235
|
-
'wiringPermission',
|
|
236
|
-
'funcTag',
|
|
237
|
-
'funcPermission',
|
|
238
|
-
])
|
|
253
|
+
// Order: wireInheritedPermissions (wiringTag) only (short-circuit on first passing group)
|
|
254
|
+
assert.deepEqual(executionOrder, ['wiringTag'])
|
|
239
255
|
})
|
|
240
256
|
|
|
241
257
|
test('should implement "at least one must pass" logic for tag permissions', async () => {
|