@plitzi/sdk-server 0.37.2 → 0.37.4
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 +1170 -0
- package/README.md +100 -7
- package/dist/actions.d.ts +6 -2
- package/dist/actions.js +5 -2
- package/dist/adapters/cloudAdapters.js +4 -3
- package/dist/adapters/jsonAdapters.js +28 -6
- package/dist/adapters/jsonAdapters.test.d.ts +1 -0
- package/dist/auth.d.ts +7 -4
- package/dist/auth.js +8 -6
- package/dist/core/auth/api.d.ts +29 -1
- package/dist/core/auth/api.js +47 -13
- package/dist/core/auth/createAuth.d.ts +4 -0
- package/dist/core/auth/createAuth.js +3 -3
- package/dist/core/auth/credentials.d.ts +3 -0
- package/dist/core/auth/credentials.js +1 -1
- package/dist/core/auth/csrf.d.ts +25 -1
- package/dist/core/auth/csrf.js +29 -4
- package/dist/core/auth/renewal.d.ts +26 -0
- package/dist/core/auth/renewal.js +24 -0
- package/dist/core/auth/routes.js +8 -6
- package/dist/core/auth/session.d.ts +45 -0
- package/dist/core/auth/session.js +47 -1
- package/dist/core/auth/sessionActivity.d.ts +11 -0
- package/dist/core/auth/sessionActivity.js +14 -0
- package/dist/core/auth/throttle.d.ts +15 -0
- package/dist/core/auth/throttle.js +29 -1
- package/dist/core/auth/totp.d.ts +9 -1
- package/dist/core/auth/totp.js +11 -6
- package/dist/core/createServer.actions.test.d.ts +1 -0
- package/dist/core/createServer.d.ts +14 -1
- package/dist/core/createServer.functions.test.d.ts +1 -0
- package/dist/core/createServer.js +22 -12
- package/dist/core/fontStore.js +1 -1
- package/dist/core/handlers/authRouteHandlers.d.ts +1 -1
- package/dist/core/handlers/authRouteHandlers.js +57 -21
- package/dist/core/handlers/csrfMiddleware.d.ts +23 -0
- package/dist/core/handlers/csrfMiddleware.js +31 -7
- package/dist/core/handlers/csrfMiddleware.test.d.ts +1 -0
- package/dist/core/handlers/oauthRouteHandlers.js +5 -3
- package/dist/core/handlers/socialRouteHandlers.d.ts +1 -1
- package/dist/core/handlers/socialRouteHandlers.js +2 -1
- package/dist/core/http/dispatcher.d.ts +9 -1
- package/dist/core/http/dispatcher.js +38 -11
- package/dist/core/http/navigation.d.ts +5 -2
- package/dist/core/http/navigation.js +7 -4
- package/dist/core/http/socketResponse.d.ts +18 -0
- package/dist/core/http/socketResponse.js +59 -0
- package/dist/core/http/sse.d.ts +26 -0
- package/dist/core/http/sse.js +78 -0
- package/dist/core/http/stages/authRoutes.js +3 -1
- package/dist/core/http/stages/exchangeRoute.test.d.ts +1 -0
- package/dist/core/http/stages/middlewares.d.ts +1 -1
- package/dist/core/http/stages/middlewares.js +16 -10
- package/dist/core/http/stages/middlewares.test.d.ts +1 -0
- package/dist/core/http/stages/oauth.js +2 -2
- package/dist/core/http/stages/sessionRenewal.d.ts +16 -0
- package/dist/core/http/stages/sessionRenewal.js +81 -0
- package/dist/core/http/stages/sessionRenewal.test.d.ts +1 -0
- package/dist/core/http/stages/signInRoute.d.ts +2 -0
- package/dist/core/http/stages/signInRoute.js +139 -0
- package/dist/core/http/stages/signInRoute.test.d.ts +1 -0
- package/dist/core/http/types.d.ts +9 -0
- package/dist/core/http/webExchange.d.ts +18 -0
- package/dist/core/http/webExchange.js +53 -0
- package/dist/core/oauth/authorize.js +37 -13
- package/dist/core/oauth/consentPage.js +11 -0
- package/dist/core/oauth/issueContext.d.ts +14 -0
- package/dist/core/oauth/issueContext.js +32 -0
- package/dist/core/oauth/records.d.ts +7 -0
- package/dist/core/oauth/register.d.ts +2 -0
- package/dist/core/oauth/register.js +32 -11
- package/dist/core/oauth/register.test.d.ts +1 -0
- package/dist/core/oauth/token.js +21 -6
- package/dist/core/oauth/token.test.d.ts +1 -0
- package/dist/core/requestParser.d.ts +4 -0
- package/dist/core/requestParser.js +14 -7
- package/dist/core/sdkAssets.js +1 -1
- package/dist/core/server/baseServer.d.ts +10 -0
- package/dist/core/server/baseServer.js +42 -11
- package/dist/core/server/closeOnSignals.js +4 -3
- package/dist/core/server/drain.d.ts +18 -0
- package/dist/core/server/drain.js +62 -0
- package/dist/core/server/drain.test.d.ts +1 -0
- package/dist/core/server/fleet/channel.d.ts +103 -0
- package/dist/core/server/fleet/channel.js +191 -0
- package/dist/core/server/fleet/channel.test.d.ts +1 -0
- package/dist/core/server/fleet/fixtures/fleetServer.d.ts +1 -0
- package/dist/core/server/fleet/fleet.integration.test.d.ts +1 -0
- package/dist/core/server/fleet/link.d.ts +23 -0
- package/dist/core/server/fleet/link.js +70 -0
- package/dist/core/server/fleet/relay.d.ts +11 -0
- package/dist/core/server/fleet/relay.js +104 -0
- package/dist/core/server/fleet/relay.test.d.ts +1 -0
- package/dist/core/server/fleet/role.d.ts +46 -0
- package/dist/core/server/fleet/role.js +59 -0
- package/dist/core/server/fleet/role.test.d.ts +1 -0
- package/dist/core/server/fleet/stores.d.ts +7 -0
- package/dist/core/server/fleet/stores.js +19 -0
- package/dist/core/server/fleet/supervisor.d.ts +26 -0
- package/dist/core/server/fleet/supervisor.js +108 -0
- package/dist/core/server/fleet/supervisor.test.d.ts +1 -0
- package/dist/core/server/pageServer.js +34 -5
- package/dist/core/services/action.js +10 -2
- package/dist/core/services/functionRoutes.d.ts +9 -0
- package/dist/core/services/functionRoutes.js +47 -0
- package/dist/core/services/realtime.d.ts +8 -0
- package/dist/core/services/realtime.js +57 -0
- package/dist/core/services/registry.js +6 -2
- package/dist/core/staticFiles.js +12 -4
- package/dist/core/staticFiles.test.d.ts +1 -0
- package/dist/core/transports.d.ts +13 -3
- package/dist/core/transports.js +14 -5
- package/dist/functions-api.d.ts +281 -0
- package/dist/functions.d.ts +19 -0
- package/dist/functions.js +5 -0
- package/dist/functionsRunner.d.ts +15 -0
- package/dist/functionsRunner.js +6 -0
- package/dist/handlers.d.ts +2 -2
- package/dist/handlers.js +2 -2
- package/dist/helpers/atomicFile.d.ts +10 -0
- package/dist/helpers/atomicFile.js +33 -0
- package/dist/helpers/atomicFile.test.d.ts +1 -0
- package/dist/helpers/buildResponseHelpers.js +35 -11
- package/dist/helpers/buildServerInfo.js +4 -2
- package/dist/helpers/buildServerInfo.test.d.ts +1 -0
- package/dist/helpers/cache/CompressedFileCache.d.ts +18 -0
- package/dist/helpers/cache/CompressedFileCache.js +44 -0
- package/dist/helpers/cache/CompressedFileCache.test.d.ts +1 -0
- package/dist/helpers/cache/cacheManager.d.ts +1 -1
- package/dist/helpers/cache/cacheManager.js +2 -1
- package/dist/helpers/cache/cacheManager.test.d.ts +1 -0
- package/dist/helpers/cache/index.d.ts +1 -1
- package/dist/helpers/cache/keys.d.ts +15 -1
- package/dist/helpers/cache/keys.js +47 -5
- package/dist/helpers/cache/serverCaches.d.ts +7 -1
- package/dist/helpers/compress.d.ts +3 -1
- package/dist/helpers/compress.js +11 -3
- package/dist/helpers/hydrationPayload.d.ts +11 -0
- package/dist/helpers/hydrationPayload.js +51 -0
- package/dist/helpers/hydrationPayload.test.d.ts +1 -0
- package/dist/helpers/metrics.d.ts +1 -1
- package/dist/helpers/metrics.js +3 -2
- package/dist/helpers/outboundGuard.d.ts +14 -6
- package/dist/helpers/outboundGuard.js +132 -8
- package/dist/helpers/paintedCookie.d.ts +8 -0
- package/dist/helpers/paintedCookie.js +13 -0
- package/dist/helpers/reportReactBuild.d.ts +9 -0
- package/dist/helpers/reportReactBuild.js +18 -0
- package/dist/helpers/reportReactBuild.test.d.ts +1 -0
- package/dist/helpers/serverLog.d.ts +25 -1
- package/dist/helpers/serverLog.js +68 -3
- package/dist/helpers/serverLog.test.d.ts +1 -0
- package/dist/helpers/wsFrame.d.ts +3 -0
- package/dist/helpers/wsFrame.js +5 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -2
- package/dist/kernel.d.ts +2 -1
- package/dist/kernel.js +3 -2
- package/dist/modules/actions/connectorWrite.js +2 -1
- package/dist/modules/actions/index.d.ts +29 -10
- package/dist/modules/actions/index.js +43 -6
- package/dist/modules/actions/jobs/failureStreak.d.ts +17 -0
- package/dist/modules/actions/jobs/failureStreak.js +47 -0
- package/dist/modules/actions/jobs/failureStreak.test.d.ts +1 -0
- package/dist/modules/actions/jobs/index.d.ts +2 -1
- package/dist/modules/actions/jobs/index.js +5 -3
- package/dist/modules/actions/jobs/memoryQueue.d.ts +3 -0
- package/dist/modules/actions/jobs/memoryQueue.js +17 -1
- package/dist/modules/actions/jobs/scheduler.d.ts +2 -0
- package/dist/modules/actions/jobs/scheduler.js +12 -7
- package/dist/modules/actions/jobs/worker.d.ts +3 -1
- package/dist/modules/actions/jobs/worker.js +9 -4
- package/dist/modules/actions/moduleFor.js +5 -2
- package/dist/modules/actions/runtime/errors.d.ts +12 -0
- package/dist/modules/actions/runtime/errors.js +16 -1
- package/dist/modules/actions/runtime/kvChange.d.ts +27 -0
- package/dist/modules/actions/runtime/kvChange.js +23 -0
- package/dist/modules/actions/runtime/kvList.d.ts +51 -0
- package/dist/modules/actions/runtime/kvList.js +52 -0
- package/dist/modules/actions/runtime/kvStore.d.ts +8 -0
- package/dist/modules/actions/runtime/kvStore.js +46 -20
- package/dist/modules/actions/runtime/memoryKv.d.ts +3 -0
- package/dist/modules/actions/runtime/memoryKv.js +19 -1
- package/dist/modules/actions/runtime/namespaceKv.d.ts +4 -6
- package/dist/modules/actions/runtime/namespaceKv.js +15 -3
- package/dist/modules/actions/runtime/namespaceKv.test.d.ts +1 -0
- package/dist/modules/actions/runtime/precheck.js +4 -8
- package/dist/modules/actions/runtime/rateLimit.d.ts +19 -0
- package/dist/modules/actions/runtime/rateLimit.js +19 -0
- package/dist/modules/actions/runtime/redisKv.d.ts +27 -0
- package/dist/modules/actions/runtime/redisKv.integration.test.d.ts +1 -0
- package/dist/modules/actions/runtime/redisKv.js +46 -0
- package/dist/modules/actions/runtime/report.js +2 -1
- package/dist/modules/actions/runtime/runAction.d.ts +10 -2
- package/dist/modules/actions/runtime/runAction.js +52 -116
- package/dist/modules/actions/runtime/runLogger.d.ts +5 -0
- package/dist/modules/actions/runtime/runLogger.js +8 -2
- package/dist/modules/actions/runtime/signing.d.ts +20 -0
- package/dist/modules/actions/runtime/signing.js +25 -0
- package/dist/modules/actions/runtime/signing.test.d.ts +1 -0
- package/dist/modules/actions/runtime/taskContext.d.ts +33 -0
- package/dist/modules/actions/runtime/taskContext.js +124 -0
- package/dist/modules/actions/tasks/builtins.js +5 -1
- package/dist/modules/actions/tasks/flow.js +81 -10
- package/dist/modules/actions/tasks/helpers.js +2 -2
- package/dist/modules/actions/tasks/http.js +2 -3
- package/dist/modules/actions/tasks/kv.js +39 -0
- package/dist/modules/actions/tasks/list.d.ts +2 -0
- package/dist/modules/actions/tasks/list.js +145 -0
- package/dist/modules/actions/tasks/realtime.d.ts +2 -0
- package/dist/modules/actions/tasks/realtime.js +104 -0
- package/dist/modules/actions/tasks/registry.d.ts +10 -1
- package/dist/modules/actions/tasks/registry.js +17 -6
- package/dist/modules/actions/transport/callHandler.js +5 -2
- package/dist/modules/actions/transport/catalogHandler.d.ts +5 -2
- package/dist/modules/actions/transport/catalogHandler.js +3 -2
- package/dist/modules/actions/transport/stream.d.ts +4 -5
- package/dist/modules/actions/transport/stream.js +12 -76
- package/dist/modules/actions/transport/webhookHandler.js +4 -3
- package/dist/modules/actions/types.d.ts +86 -3
- package/dist/modules/connectors/engine.js +3 -5
- package/dist/modules/functions/build.d.ts +22 -0
- package/dist/modules/functions/build.js +122 -0
- package/dist/modules/functions/build.test.d.ts +1 -0
- package/dist/modules/functions/capabilities.d.ts +22 -0
- package/dist/modules/functions/capabilities.js +228 -0
- package/dist/modules/functions/config.d.ts +34 -0
- package/dist/modules/functions/config.js +24 -0
- package/dist/modules/functions/context.d.ts +17 -0
- package/dist/modules/functions/context.js +88 -0
- package/dist/modules/functions/contract.d.ts +121 -0
- package/dist/modules/functions/contract.js +9 -0
- package/dist/modules/functions/driver.d.ts +36 -0
- package/dist/modules/functions/driver.js +150 -0
- package/dist/modules/functions/load.d.ts +15 -0
- package/dist/modules/functions/load.js +45 -0
- package/dist/modules/functions/load.test.d.ts +1 -0
- package/dist/modules/functions/local.d.ts +29 -0
- package/dist/modules/functions/local.js +61 -0
- package/dist/modules/functions/local.test.d.ts +1 -0
- package/dist/modules/functions/manifest.d.ts +24 -0
- package/dist/modules/functions/manifest.js +163 -0
- package/dist/modules/functions/manifest.test.d.ts +1 -0
- package/dist/modules/functions/native.d.ts +8 -0
- package/dist/modules/functions/native.js +21 -0
- package/dist/modules/functions/native.test.d.ts +1 -0
- package/dist/modules/functions/protocol.d.ts +216 -0
- package/dist/modules/functions/protocol.js +18 -0
- package/dist/modules/functions/routes.d.ts +36 -0
- package/dist/modules/functions/routes.js +181 -0
- package/dist/modules/functions/routes.test.d.ts +1 -0
- package/dist/modules/functions/runner/messages.d.ts +5 -0
- package/dist/modules/functions/runner/messages.js +188 -0
- package/dist/modules/functions/runner/remote.d.ts +19 -0
- package/dist/modules/functions/runner/remote.js +116 -0
- package/dist/modules/functions/runner/remote.test.d.ts +1 -0
- package/dist/modules/functions/runner/service.d.ts +32 -0
- package/dist/modules/functions/runner/service.js +209 -0
- package/dist/modules/functions/sandbox/guest.d.ts +45 -0
- package/dist/modules/functions/sandbox/guest.js +660 -0
- package/dist/modules/functions/sandbox/isolate.d.ts +37 -0
- package/dist/modules/functions/sandbox/isolate.js +347 -0
- package/dist/modules/functions/sandbox/isolate.test.d.ts +1 -0
- package/dist/modules/functions/sandbox/prelude.d.ts +2 -0
- package/dist/modules/functions/sandbox/prelude.js +49 -0
- package/dist/modules/functions/space.d.ts +40 -0
- package/dist/modules/functions/space.js +162 -0
- package/dist/modules/functions/testing/tasksOf.d.ts +4 -0
- package/dist/modules/functions/tryEntry.d.ts +9 -0
- package/dist/modules/functions/tryEntry.js +49 -0
- package/dist/modules/mongo/kv.js +31 -0
- package/dist/modules/mysql/accounts.js +19 -9
- package/dist/modules/mysql/index.d.ts +2 -1
- package/dist/modules/mysql/jobs/index.d.ts +1 -1
- package/dist/modules/mysql/jobs/kv.js +30 -0
- package/dist/modules/mysql/jobs/schema.d.ts +9 -0
- package/dist/modules/mysql/jobs/schema.js +21 -4
- package/dist/modules/mysql/schema.js +25 -0
- package/dist/modules/mysql/sessions.d.ts +1 -11
- package/dist/modules/mysql/sessions.js +6 -1
- package/dist/modules/realtime/declarations.d.ts +11 -0
- package/dist/modules/realtime/declarations.js +26 -0
- package/dist/modules/realtime/grants.d.ts +23 -0
- package/dist/modules/realtime/grants.js +61 -0
- package/dist/modules/realtime/handlers.d.ts +74 -0
- package/dist/modules/realtime/handlers.js +224 -0
- package/dist/modules/realtime/hub.d.ts +62 -0
- package/dist/modules/realtime/hub.js +141 -0
- package/dist/modules/realtime/index.d.ts +51 -0
- package/dist/modules/realtime/index.js +62 -0
- package/dist/modules/realtime/memoryPubSub.d.ts +10 -0
- package/dist/modules/realtime/memoryPubSub.js +43 -0
- package/dist/modules/realtime/realtime.test.d.ts +1 -0
- package/dist/modules/realtime/redisPubSub.d.ts +24 -0
- package/dist/modules/realtime/redisPubSub.js +36 -0
- package/dist/modules/realtime/redisPubSub.test.d.ts +1 -0
- package/dist/modules/realtime/socket.d.ts +25 -0
- package/dist/modules/realtime/socket.js +122 -0
- package/dist/modules/realtime/socket.test.d.ts +1 -0
- package/dist/modules/rsc/handler.d.ts +16 -0
- package/dist/modules/rsc/handler.js +17 -10
- package/dist/modules/rsc/handler.test.d.ts +1 -0
- package/dist/modules/rsc/resolveRscData.js +2 -1
- package/dist/modules/runtime/bundle.d.ts +24 -0
- package/dist/modules/runtime/bundle.js +102 -0
- package/dist/modules/runtime/contract.d.ts +37 -0
- package/dist/modules/runtime/contract.js +21 -0
- package/dist/modules/runtime/entry.test.d.ts +1 -0
- package/dist/modules/runtime/host.d.ts +38 -0
- package/dist/modules/runtime/host.js +68 -0
- package/dist/modules/runtime/inside.d.ts +15 -0
- package/dist/modules/runtime/inside.js +47 -0
- package/dist/modules/runtime/inside.test.d.ts +1 -0
- package/dist/modules/runtime/runner.d.ts +11 -0
- package/dist/modules/runtime/runner.js +38 -0
- package/dist/modules/runtime/runtime.test.d.ts +1 -0
- package/dist/modules/runtime/stages.d.ts +50 -0
- package/dist/modules/runtime/stages.js +117 -0
- package/dist/modules/ssr/Component.d.ts +3 -1
- package/dist/modules/ssr/Component.js +2 -1
- package/dist/modules/ssr/handler.js +10 -2
- package/dist/modules/ssr/loadPluginComponents.js +2 -1
- package/dist/modules/ssr/prepareRender.js +39 -10
- package/dist/modules/ssr/preview.d.ts +3 -0
- package/dist/modules/ssr/preview.js +17 -7
- package/dist/modules/ssr/redirect.test.d.ts +1 -0
- package/dist/modules/ssr/registerExternalPlugins.d.ts +6 -1
- package/dist/modules/ssr/registerExternalPlugins.js +19 -16
- package/dist/modules/ssr/registerExternalPlugins.test.d.ts +1 -0
- package/dist/modules/ssr/reportMissingPlugins.d.ts +9 -0
- package/dist/modules/ssr/reportMissingPlugins.js +24 -0
- package/dist/modules/ssr/reportMissingPlugins.test.d.ts +1 -0
- package/dist/modules/ssr/streamBody.d.ts +2 -2
- package/dist/modules/ssr/streamBody.js +4 -1
- package/dist/modules/ssr/template.js +1 -1
- package/dist/modules/ssr/views/template.ejs +8 -5
- package/dist/mysql.d.ts +1 -1
- package/dist/mysql.js +2 -2
- package/dist/oauth.js +2 -2
- package/dist/plugins/compile.js +9 -4
- package/dist/plugins/copy.js +3 -3
- package/dist/plugins/manager.d.ts +35 -3
- package/dist/plugins/manager.js +109 -38
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.js +7 -0
- package/package.json +39 -8
- package/skills/plitzi-authoring/SKILL.md +98 -14
- package/skills/plitzi-authoring/reference/accessibility.md +111 -0
- package/skills/plitzi-authoring/reference/authoring-errors.md +70 -0
- package/skills/plitzi-authoring/reference/data-and-visibility.md +84 -18
- package/skills/plitzi-authoring/reference/elements-and-styles.md +61 -7
- package/skills/plitzi-authoring/reference/flows.md +108 -17
- package/skills/plitzi-authoring/reference/lists.md +74 -0
- package/skills/plitzi-authoring/reference/performance.md +67 -0
- package/skills/plitzi-authoring/reference/plugins.md +167 -0
- package/skills/plitzi-authoring/reference/review-checklist.md +19 -2
- package/skills/plitzi-authoring/reference/templates.md +67 -27
- package/skills/plitzi-authoring/reference/testing.md +72 -5
- package/skills/plitzi-authoring/reference/validation.md +73 -0
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { serverLog } from "../../../helpers/serverLog.js";
|
|
2
|
+
import { ActionRefusal, ActionRunError } from "./errors.js";
|
|
2
3
|
import { runCancelKey } from "./guards.js";
|
|
3
4
|
import { createKvStore } from "./kvStore.js";
|
|
4
5
|
import { resolveLimits } from "./limits.js";
|
|
5
6
|
import { createMemoryKv } from "./memoryKv.js";
|
|
6
|
-
import { namespaceKv } from "./namespaceKv.js";
|
|
7
7
|
import { createEmailSender } from "./email.js";
|
|
8
8
|
import { createRedactor, projectUser } from "./scope.js";
|
|
9
9
|
import { precheckRun } from "./precheck.js";
|
|
10
|
+
import { createSigning } from "./signing.js";
|
|
11
|
+
import { createRunFetch, taskContextFor } from "./taskContext.js";
|
|
10
12
|
import onAbort from "../../../helpers/onAbort.js";
|
|
11
13
|
import { FAILURE_HANDLER_TASK } from "@plitzi/sdk-shared/actions";
|
|
12
14
|
import { evaluateRuleGroup } from "@plitzi/sdk-shared/helpers/ruleEvaluator";
|
|
13
|
-
import {
|
|
15
|
+
import { resolveStepParam } from "@plitzi/sdk-shared/helpers/twigWrapper";
|
|
14
16
|
//#region src/modules/actions/runtime/runAction.ts
|
|
15
|
-
var MAX_TWIG_RESOLUTION_PASSES = 5;
|
|
16
17
|
/** How often a run asks the shared store whether it has been cancelled. Once a second is far finer than the
|
|
17
18
|
* boundaries a flow actually has, and it keeps a long run to sixty reads a minute. */
|
|
18
19
|
var CANCEL_POLL_MS = 1e3;
|
|
@@ -77,79 +78,19 @@ var createCancelWatch = (store, runId) => {
|
|
|
77
78
|
/**
|
|
78
79
|
* Resolves twig in a node's params against the flow scope.
|
|
79
80
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* trace, which is how an author sees that
|
|
81
|
+
* With `resolveStepParam`, the resolver the browser's flows use too, so a param means the same on both sides: any
|
|
82
|
+
* template syntax is run, the expression's own type is kept, and a value that resolves to another template is read
|
|
83
|
+
* again up to a ceiling. Unresolved tokens are left as-is and show up in the trace, which is how an author sees that
|
|
84
|
+
* a step referenced something the flow never produced.
|
|
83
85
|
*/
|
|
84
86
|
var resolveParams = (params, scope, raw) => {
|
|
85
87
|
if (raw) return params;
|
|
86
88
|
return Object.entries(params).reduce((acum, [key, param]) => {
|
|
87
|
-
|
|
88
|
-
let passes = MAX_TWIG_RESOLUTION_PASSES;
|
|
89
|
-
while (typeof value === "string" && hasValidToken(value) && passes > 0) {
|
|
90
|
-
value = processTwig(value, scope, false, true);
|
|
91
|
-
passes--;
|
|
92
|
-
}
|
|
93
|
-
acum[key] = value;
|
|
89
|
+
acum[key] = typeof param === "string" ? resolveStepParam(param, scope).value : param;
|
|
94
90
|
return acum;
|
|
95
91
|
}, {});
|
|
96
92
|
};
|
|
97
93
|
/**
|
|
98
|
-
* Holds an answer to what the run is allowed to carry.
|
|
99
|
-
*
|
|
100
|
-
* Two checks rather than one, because a body arrives in two ways. `Content-Length` is refused before a byte is
|
|
101
|
-
* read, which is the cheap half; a chunked answer that declares nothing is counted AS it streams and errored the
|
|
102
|
-
* moment it goes over — so the ceiling holds for a backend that lies about its size or never states one.
|
|
103
|
-
*
|
|
104
|
-
* The cap is on one response and not on the run: it exists so that a single answer cannot be unbounded, which is
|
|
105
|
-
* a different failure from a flow that makes many small calls (that is `maxRequests`).
|
|
106
|
-
*/
|
|
107
|
-
var capped = (response, maxBytes) => {
|
|
108
|
-
const declared = Number(response.headers.get("content-length") ?? "");
|
|
109
|
-
if (Number.isFinite(declared) && declared > maxBytes) throw new ActionRunError("over_capacity", `Response is larger than the ${maxBytes} byte budget`);
|
|
110
|
-
if (!response.body) return response;
|
|
111
|
-
let seen = 0;
|
|
112
|
-
const counted = response.body.pipeThrough(new TransformStream({ transform: (chunk, controller) => {
|
|
113
|
-
seen += chunk.byteLength;
|
|
114
|
-
if (seen > maxBytes) {
|
|
115
|
-
controller.error(new ActionRunError("over_capacity", `Response exceeded the ${maxBytes} byte budget`));
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
controller.enqueue(chunk);
|
|
119
|
-
} }));
|
|
120
|
-
return new Response(counted, {
|
|
121
|
-
status: response.status,
|
|
122
|
-
statusText: response.statusText,
|
|
123
|
-
headers: response.headers
|
|
124
|
-
});
|
|
125
|
-
};
|
|
126
|
-
/**
|
|
127
|
-
* Counts outbound calls, refuses past the budget, caps what one answer may carry back, and stamps the run's
|
|
128
|
-
* lineage on every one of them.
|
|
129
|
-
*
|
|
130
|
-
* The budget stops a loop from turning one run into a hundred requests. The lineage header is what makes the
|
|
131
|
-
* OTHER loop detectable: an action whose HTTP step reaches its own space's webhook arrives carrying the chain
|
|
132
|
-
* that led there, and the run it would start refuses itself. It names the space's own actions to a backend that
|
|
133
|
-
* space configured, which is the cost of catching a cycle nothing else can see.
|
|
134
|
-
*
|
|
135
|
-
* Every ceiling lives HERE rather than in each task, because this is the only door a task has to the outside
|
|
136
|
-
* world — a task that had to remember to count its own bytes is a task that will forget.
|
|
137
|
-
*/
|
|
138
|
-
var createRunFetch = (base, signal, limits, lineage) => {
|
|
139
|
-
let issued = 0;
|
|
140
|
-
return async (input, init) => {
|
|
141
|
-
issued += 1;
|
|
142
|
-
if (issued > limits.maxRequests) throw new ActionRunError("over_capacity", `Action exceeded its ${limits.maxRequests} outbound request budget`);
|
|
143
|
-
const headers = new Headers(init?.headers);
|
|
144
|
-
headers.set("X-Plitzi-Action-Lineage", lineage.join(","));
|
|
145
|
-
return capped(await base(input, {
|
|
146
|
-
...init,
|
|
147
|
-
headers,
|
|
148
|
-
signal: init?.signal ?? signal
|
|
149
|
-
}), limits.maxResponseBytes);
|
|
150
|
-
};
|
|
151
|
-
};
|
|
152
|
-
/**
|
|
153
94
|
* Fills in what the node left out, from the task's own catalog entry.
|
|
154
95
|
*
|
|
155
96
|
* A task declares its params with a `defaultValue`, and a document written before a param existed simply has no
|
|
@@ -192,7 +133,7 @@ var failureHandlerOf = (nodes, trigger) => {
|
|
|
192
133
|
}
|
|
193
134
|
};
|
|
194
135
|
/** A step as a debugger may see it: what it was and how it ended, and nothing it was given or returned. */
|
|
195
|
-
var stepOf = (node, { status, phase, startTime, endTime, error }) => ({
|
|
136
|
+
var stepOf = (node, { status, phase, startTime, endTime, error, logs = [] }) => ({
|
|
196
137
|
id: node.id,
|
|
197
138
|
title: node.title || node.action,
|
|
198
139
|
action: node.action,
|
|
@@ -200,8 +141,12 @@ var stepOf = (node, { status, phase, startTime, endTime, error }) => ({
|
|
|
200
141
|
phase,
|
|
201
142
|
startTime,
|
|
202
143
|
endTime,
|
|
203
|
-
...error === void 0 ? {} : { error }
|
|
144
|
+
...error === void 0 ? {} : { error },
|
|
145
|
+
...logs.length ? { logs } : {}
|
|
204
146
|
});
|
|
147
|
+
/** What one step may log: enough to debug with, bounded so a loop that logs cannot fill a run's record. */
|
|
148
|
+
var MAX_LOG_LINES = 100;
|
|
149
|
+
var MAX_LOG_LINE = 1e3;
|
|
205
150
|
var withDefaults = (task, params) => Object.entries(task.params).reduce((acum, [key, param]) => {
|
|
206
151
|
if (acum[key] === void 0 || acum[key] === "") acum[key] = param.defaultValue ?? "";
|
|
207
152
|
return acum;
|
|
@@ -234,18 +179,19 @@ var runNode = async (node, scope, registry, buildContext) => {
|
|
|
234
179
|
* input contract — lives HERE rather than in a transport, because a deployment mounting its own trigger calls this
|
|
235
180
|
* directly. A check in the endpoint would be a check a custom trigger silently skips.
|
|
236
181
|
*/
|
|
237
|
-
var createActionRunner = (config,
|
|
182
|
+
var createActionRunner = (config, registryFor, baseFetch = fetch) => {
|
|
238
183
|
const kv = createKvStore(config.kv ?? createMemoryKv());
|
|
239
184
|
const emailSender = createEmailSender({
|
|
240
185
|
...config.email,
|
|
241
186
|
kv
|
|
242
187
|
});
|
|
188
|
+
const signing = config.signingSecret ? createSigning(config.signingSecret) : void 0;
|
|
243
189
|
/** Never allowed to fail a run: a logging outage must not take an action down, the same rule metering follows. */
|
|
244
190
|
const record = async (entry) => {
|
|
245
191
|
try {
|
|
246
192
|
await config.onRun?.(entry);
|
|
247
193
|
} catch (error) {
|
|
248
|
-
|
|
194
|
+
serverLog.error("Actions", "run record failed", error);
|
|
249
195
|
}
|
|
250
196
|
};
|
|
251
197
|
const runAction = async (request) => {
|
|
@@ -257,13 +203,13 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
257
203
|
user: request.user,
|
|
258
204
|
lineage: request.lineage
|
|
259
205
|
});
|
|
206
|
+
const registry = await registryFor(request);
|
|
260
207
|
const limits = resolveLimits(config.limits, document.limits);
|
|
261
208
|
const redactor = createRedactor();
|
|
262
209
|
const redact = redactor.redact;
|
|
263
210
|
const controller = new AbortController();
|
|
264
211
|
const deadline = createDeadline(request.emit ? limits.streamTimeoutMs : limits.timeoutMs, controller);
|
|
265
212
|
const releaseOuter = onAbort(request.signal, () => controller.abort());
|
|
266
|
-
const scopedKv = namespaceKv(kv, request.spaceId);
|
|
267
213
|
const lineage = [...request.lineage ?? [], entry.id];
|
|
268
214
|
/**
|
|
269
215
|
* What a step runs with, for one abort signal and one outbound budget.
|
|
@@ -271,44 +217,12 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
271
217
|
* Two of them per run at most: the flow's, and — only when it failed — the undo's, which must still be able to
|
|
272
218
|
* reach the outside world after the flow's signal was aborted or its request budget spent.
|
|
273
219
|
*/
|
|
274
|
-
const contextFor = (signal, runFetch) => (
|
|
275
|
-
|
|
276
|
-
spaceId: request.spaceId,
|
|
277
|
-
environment: request.environment,
|
|
278
|
-
trigger: request.trigger,
|
|
279
|
-
user: request.user,
|
|
280
|
-
callerId: request.callerId,
|
|
281
|
-
signal,
|
|
282
|
-
scope,
|
|
283
|
-
/**
|
|
284
|
-
* The secret a STEP asked for, resolved inside that step and never in the flow scope.
|
|
285
|
-
*
|
|
286
|
-
* There is no allow-list to check it against, deliberately: an action is authored by someone who may edit
|
|
287
|
-
* every action in the space, so a list they can edit is not a boundary — it only ever told the redactor what
|
|
288
|
-
* to look for, and the redactor now learns from what was actually resolved. What IS a boundary is that a
|
|
289
|
-
* credential reaches only the params of the step that named it, which is `renderTaskParams`' whole job.
|
|
290
|
-
*/
|
|
291
|
-
credential: async (identifier) => {
|
|
292
|
-
const credential = await config.lookups.getCredential?.(request.spaceId, identifier);
|
|
293
|
-
if (credential) redactor.add(credential);
|
|
294
|
-
return credential;
|
|
295
|
-
},
|
|
296
|
-
connector: async (connectorId) => {
|
|
297
|
-
const manifest = await config.lookups.getConnector?.(request.spaceId, connectorId, request.at);
|
|
298
|
-
if (!manifest) return;
|
|
299
|
-
const credential = manifest.credential ? await config.lookups.getCredential?.(request.spaceId, manifest.credential) : void 0;
|
|
300
|
-
if (credential) redactor.add(credential);
|
|
301
|
-
return {
|
|
302
|
-
manifest,
|
|
303
|
-
credential
|
|
304
|
-
};
|
|
305
|
-
},
|
|
306
|
-
fetch: runFetch,
|
|
307
|
-
kv: scopedKv,
|
|
308
|
-
dbDrivers: config.dbDrivers ?? [],
|
|
220
|
+
const contextFor = (signal, runFetch) => taskContextFor(config, {
|
|
221
|
+
kv,
|
|
309
222
|
email: emailSender,
|
|
310
|
-
|
|
311
|
-
|
|
223
|
+
redactor,
|
|
224
|
+
signing
|
|
225
|
+
}, request, signal, runFetch);
|
|
312
226
|
const buildContext = contextFor(controller.signal, createRunFetch(baseFetch, controller.signal, limits, lineage));
|
|
313
227
|
const trace = [];
|
|
314
228
|
const steps = [];
|
|
@@ -365,8 +279,15 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
365
279
|
const runStep = async (node, phase, build) => {
|
|
366
280
|
const startTime = Date.now();
|
|
367
281
|
const late = () => phase === "flow" && flowSettled;
|
|
282
|
+
const logs = [];
|
|
283
|
+
const log = (line) => {
|
|
284
|
+
if (logs.length < MAX_LOG_LINES) logs.push(redact(line.length > MAX_LOG_LINE ? `${line.slice(0, MAX_LOG_LINE)}…` : line));
|
|
285
|
+
};
|
|
368
286
|
try {
|
|
369
|
-
const outcome = await runNode(node, scope, registry,
|
|
287
|
+
const outcome = await runNode(node, scope, registry, (stepScope) => ({
|
|
288
|
+
...build(stepScope),
|
|
289
|
+
log
|
|
290
|
+
}));
|
|
370
291
|
if (late()) return outcome;
|
|
371
292
|
const endTime = Date.now();
|
|
372
293
|
trace.push({
|
|
@@ -381,7 +302,8 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
381
302
|
status: outcome.status,
|
|
382
303
|
phase,
|
|
383
304
|
startTime,
|
|
384
|
-
endTime
|
|
305
|
+
endTime,
|
|
306
|
+
logs
|
|
385
307
|
}));
|
|
386
308
|
request.onNode?.(node.id, outcome.status);
|
|
387
309
|
scope[node.id] = outcome.result;
|
|
@@ -403,7 +325,8 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
403
325
|
phase,
|
|
404
326
|
startTime,
|
|
405
327
|
endTime,
|
|
406
|
-
error: message
|
|
328
|
+
error: message,
|
|
329
|
+
logs
|
|
407
330
|
}));
|
|
408
331
|
request.onNode?.(node.id, "failed");
|
|
409
332
|
throw error;
|
|
@@ -495,6 +418,8 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
495
418
|
};
|
|
496
419
|
/** Set when the run ends in a way the CALLER has to hear as a status code rather than as a result. */
|
|
497
420
|
let fatal;
|
|
421
|
+
/** Set when a step refused with a reason written for the caller — the one failure a result may say the why of. */
|
|
422
|
+
let refusal;
|
|
498
423
|
try {
|
|
499
424
|
await Promise.race([runFlow(), deadline.promise]);
|
|
500
425
|
} catch (error) {
|
|
@@ -523,6 +448,7 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
523
448
|
});
|
|
524
449
|
} else {
|
|
525
450
|
failure = error instanceof Error ? error.message : String(error);
|
|
451
|
+
if (error instanceof ActionRefusal) refusal = redact(error.message);
|
|
526
452
|
trace.push({
|
|
527
453
|
node: errorNode("error"),
|
|
528
454
|
status: "failed",
|
|
@@ -584,10 +510,20 @@ var createActionRunner = (config, registry, baseFetch = fetch) => {
|
|
|
584
510
|
status,
|
|
585
511
|
output: redact(output),
|
|
586
512
|
trace: [...trace],
|
|
587
|
-
steps: [...steps]
|
|
513
|
+
steps: [...steps],
|
|
514
|
+
...refusal === void 0 ? {} : { error: refusal }
|
|
588
515
|
};
|
|
589
516
|
};
|
|
590
|
-
|
|
517
|
+
const taskContext = (contextRequest, signal, lineage) => taskContextFor(config, {
|
|
518
|
+
kv,
|
|
519
|
+
email: emailSender,
|
|
520
|
+
redactor: createRedactor(),
|
|
521
|
+
signing
|
|
522
|
+
}, contextRequest, signal, createRunFetch(baseFetch, signal, resolveLimits(config.limits, void 0), lineage))({});
|
|
523
|
+
return {
|
|
524
|
+
runAction,
|
|
525
|
+
taskContext
|
|
526
|
+
};
|
|
591
527
|
};
|
|
592
528
|
//#endregion
|
|
593
529
|
export { createActionRunner };
|
|
@@ -11,6 +11,9 @@ import { ActionRejectRecord, ActionRunRecord, ServerLogger } from '@plitzi/sdk-s
|
|
|
11
11
|
* deployment that wants run HISTORY rather than run logs keeps its own `onRun` and writes rows; the two compose,
|
|
12
12
|
* since `onRun` is one function.
|
|
13
13
|
*
|
|
14
|
+
* Held to the server's `logLevel` like everything else on the stream: a run that completed is `info`, one that did not
|
|
15
|
+
* is `error`.
|
|
16
|
+
*
|
|
14
17
|
* Never throws: the runner treats the record as best-effort, and a logging outage must not take an action down.
|
|
15
18
|
*/
|
|
16
19
|
export declare const createRunLogger: (logger: ServerLogger) => (record: ActionRunRecord) => void;
|
|
@@ -25,6 +28,8 @@ export declare const createRunLogger: (logger: ServerLogger) => (record: ActionR
|
|
|
25
28
|
* It carries no body, no signature and no header — what went wrong is `reason`, and `detail` is the server's own
|
|
26
29
|
* words about it.
|
|
27
30
|
*
|
|
31
|
+
* A refusal is a `warn`: the caller was turned away, the server did not fail.
|
|
32
|
+
*
|
|
28
33
|
* Never throws, for the same reason the run logger does not: the caller was refused either way, and a sink's
|
|
29
34
|
* problem must not become the response's.
|
|
30
35
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { serverLog } from "../../../helpers/serverLog.js";
|
|
1
2
|
//#region src/modules/actions/runtime/runLogger.ts
|
|
2
3
|
/**
|
|
3
4
|
* `onRun`, wired into the log stream the server already reports through.
|
|
@@ -11,11 +12,14 @@
|
|
|
11
12
|
* deployment that wants run HISTORY rather than run logs keeps its own `onRun` and writes rows; the two compose,
|
|
12
13
|
* since `onRun` is one function.
|
|
13
14
|
*
|
|
15
|
+
* Held to the server's `logLevel` like everything else on the stream: a run that completed is `info`, one that did not
|
|
16
|
+
* is `error`.
|
|
17
|
+
*
|
|
14
18
|
* Never throws: the runner treats the record as best-effort, and a logging outage must not take an action down.
|
|
15
19
|
*/
|
|
16
20
|
var createRunLogger = (logger) => (record) => {
|
|
17
21
|
try {
|
|
18
|
-
logger
|
|
22
|
+
serverLog.emit(logger, {
|
|
19
23
|
kind: "run",
|
|
20
24
|
name: record.actionId,
|
|
21
25
|
spaceId: record.spaceId,
|
|
@@ -42,12 +46,14 @@ var createRunLogger = (logger) => (record) => {
|
|
|
42
46
|
* It carries no body, no signature and no header — what went wrong is `reason`, and `detail` is the server's own
|
|
43
47
|
* words about it.
|
|
44
48
|
*
|
|
49
|
+
* A refusal is a `warn`: the caller was turned away, the server did not fail.
|
|
50
|
+
*
|
|
45
51
|
* Never throws, for the same reason the run logger does not: the caller was refused either way, and a sink's
|
|
46
52
|
* problem must not become the response's.
|
|
47
53
|
*/
|
|
48
54
|
var createRejectLogger = (logger) => (record) => {
|
|
49
55
|
try {
|
|
50
|
-
logger
|
|
56
|
+
serverLog.emit(logger, {
|
|
51
57
|
kind: "reject",
|
|
52
58
|
name: record.actionId,
|
|
53
59
|
spaceId: record.spaceId,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** A space's signatures: made with a key of its own that the server keeps, which its code never holds. */
|
|
2
|
+
export type SpaceSigning = {
|
|
3
|
+
/** HMAC-SHA-256 of `value` with the space's key, base64url. */
|
|
4
|
+
sign: (value: string) => Promise<string>;
|
|
5
|
+
/** Whether `signature` is what `sign` answers for `value` — compared in constant time. */
|
|
6
|
+
verify: (value: string, signature: string) => Promise<boolean>;
|
|
7
|
+
};
|
|
8
|
+
/** A signing secret is what every link and key a space hands out stands on: as long as a 256-bit key written out. */
|
|
9
|
+
export declare const MIN_SIGNING_SECRET_LENGTH = 32;
|
|
10
|
+
/**
|
|
11
|
+
* Signing for every space, from the one secret the deployment holds (`signingSecret`).
|
|
12
|
+
*
|
|
13
|
+
* Each space and environment signs with a key derived from it, never with the secret itself: what one space signed
|
|
14
|
+
* another cannot vouch for, nor production for what a preview signed. Every replica derives the same keys from the
|
|
15
|
+
* same secret, so a signature made on one is verified on any.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createSigning: (secret: string) => ({ spaceId, environment }: {
|
|
18
|
+
spaceId: number;
|
|
19
|
+
environment: string;
|
|
20
|
+
}) => SpaceSigning;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Signing for every space, from the one secret the deployment holds (`signingSecret`).
|
|
4
|
+
*
|
|
5
|
+
* Each space and environment signs with a key derived from it, never with the secret itself: what one space signed
|
|
6
|
+
* another cannot vouch for, nor production for what a preview signed. Every replica derives the same keys from the
|
|
7
|
+
* same secret, so a signature made on one is verified on any.
|
|
8
|
+
*/
|
|
9
|
+
var createSigning = (secret) => {
|
|
10
|
+
if (secret.length < 32) throw new Error(`The actions' signingSecret is at least ${String(32)} characters`);
|
|
11
|
+
return ({ spaceId, environment }) => {
|
|
12
|
+
const key = createHmac("sha256", secret).update(`space-signing:${String(spaceId)}:${environment}`).digest();
|
|
13
|
+
const signatureOf = (value) => createHmac("sha256", key).update(value).digest("base64url");
|
|
14
|
+
return {
|
|
15
|
+
sign: (value) => Promise.resolve(signatureOf(value)),
|
|
16
|
+
verify: (value, signature) => {
|
|
17
|
+
const expected = Buffer.from(signatureOf(value));
|
|
18
|
+
const given = Buffer.from(signature);
|
|
19
|
+
return Promise.resolve(given.length === expected.length && timingSafeEqual(given, expected));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { createSigning };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createEmailSender } from './email';
|
|
2
|
+
import { createRedactor } from './scope';
|
|
3
|
+
import { createSigning } from './signing';
|
|
4
|
+
import { ActionKvStore, ActionRunRequest, ActionsConfig, ActionTaskContext, ResolvedActionLimits } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Counts outbound calls, refuses past the budget, caps what one answer may carry back, and stamps the run's
|
|
7
|
+
* lineage on every one of them.
|
|
8
|
+
*
|
|
9
|
+
* The budget stops a loop from turning one run into a hundred requests. The lineage header is what makes the
|
|
10
|
+
* OTHER loop detectable: an action whose HTTP step reaches its own space's webhook arrives carrying the chain
|
|
11
|
+
* that led there, and the run it would start refuses itself. It names the space's own actions to a backend that
|
|
12
|
+
* space configured, which is the cost of catching a cycle nothing else can see.
|
|
13
|
+
*
|
|
14
|
+
* Every ceiling lives HERE rather than in each task, because this is the only door a task has to the outside
|
|
15
|
+
* world — a task that had to remember to count its own bytes is a task that will forget.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createRunFetch: (base: typeof fetch, signal: AbortSignal, limits: ResolvedActionLimits, lineage: string[]) => typeof fetch;
|
|
18
|
+
/** What a task's context is made of that outlives one run: the server's stores and the run's redactor. */
|
|
19
|
+
export type TaskContextDeps = {
|
|
20
|
+
kv: ActionKvStore;
|
|
21
|
+
email: ReturnType<typeof createEmailSender>;
|
|
22
|
+
redactor: ReturnType<typeof createRedactor>;
|
|
23
|
+
/** Absent when the deployment gave no `signingSecret`: its spaces then sign nothing. */
|
|
24
|
+
signing?: ReturnType<typeof createSigning>;
|
|
25
|
+
};
|
|
26
|
+
/** Who and what the context is for — a run's request, or anything shaped like one (a function's route). */
|
|
27
|
+
export type TaskContextRequest = Pick<ActionRunRequest, 'runId' | 'spaceId' | 'environment' | 'trigger' | 'user' | 'callerId' | 'at' | 'emit'>;
|
|
28
|
+
/**
|
|
29
|
+
* What a task runs with — the one place it is made, for a step of a run and for a function answering a route alike:
|
|
30
|
+
* every secret it resolves registered with the redactor, the space's `kv` namespace, the run's outbound budget, the
|
|
31
|
+
* space's channels. Everything but `log`, which is the STEP's: each step keeps what it logged.
|
|
32
|
+
*/
|
|
33
|
+
export declare const taskContextFor: (config: ActionsConfig, { kv, email, redactor, signing }: TaskContextDeps, request: TaskContextRequest, signal: AbortSignal, runFetch: typeof fetch) => (scope: Record<string, unknown>) => Omit<ActionTaskContext, "log">;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { ActionRunError } from "./errors.js";
|
|
2
|
+
import { namespaceKv } from "./namespaceKv.js";
|
|
3
|
+
//#region src/modules/actions/runtime/taskContext.ts
|
|
4
|
+
/**
|
|
5
|
+
* Holds an answer to what the run is allowed to carry.
|
|
6
|
+
*
|
|
7
|
+
* Two checks rather than one, because a body arrives in two ways. `Content-Length` is refused before a byte is
|
|
8
|
+
* read, which is the cheap half; a chunked answer that declares nothing is counted AS it streams and errored the
|
|
9
|
+
* moment it goes over — so the ceiling holds for a backend that lies about its size or never states one.
|
|
10
|
+
*
|
|
11
|
+
* The cap is on one response and not on the run: it exists so that a single answer cannot be unbounded, which is
|
|
12
|
+
* a different failure from a flow that makes many small calls (that is `maxRequests`).
|
|
13
|
+
*/
|
|
14
|
+
var capped = (response, maxBytes) => {
|
|
15
|
+
const declared = Number(response.headers.get("content-length") ?? "");
|
|
16
|
+
if (Number.isFinite(declared) && declared > maxBytes) throw new ActionRunError("over_capacity", `Response is larger than the ${maxBytes} byte budget`);
|
|
17
|
+
if (!response.body) return response;
|
|
18
|
+
let seen = 0;
|
|
19
|
+
const counted = response.body.pipeThrough(new TransformStream({ transform: (chunk, controller) => {
|
|
20
|
+
seen += chunk.byteLength;
|
|
21
|
+
if (seen > maxBytes) {
|
|
22
|
+
controller.error(new ActionRunError("over_capacity", `Response exceeded the ${maxBytes} byte budget`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
controller.enqueue(chunk);
|
|
26
|
+
} }));
|
|
27
|
+
return new Response(counted, {
|
|
28
|
+
status: response.status,
|
|
29
|
+
statusText: response.statusText,
|
|
30
|
+
headers: response.headers
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Counts outbound calls, refuses past the budget, caps what one answer may carry back, and stamps the run's
|
|
35
|
+
* lineage on every one of them.
|
|
36
|
+
*
|
|
37
|
+
* The budget stops a loop from turning one run into a hundred requests. The lineage header is what makes the
|
|
38
|
+
* OTHER loop detectable: an action whose HTTP step reaches its own space's webhook arrives carrying the chain
|
|
39
|
+
* that led there, and the run it would start refuses itself. It names the space's own actions to a backend that
|
|
40
|
+
* space configured, which is the cost of catching a cycle nothing else can see.
|
|
41
|
+
*
|
|
42
|
+
* Every ceiling lives HERE rather than in each task, because this is the only door a task has to the outside
|
|
43
|
+
* world — a task that had to remember to count its own bytes is a task that will forget.
|
|
44
|
+
*/
|
|
45
|
+
var createRunFetch = (base, signal, limits, lineage) => {
|
|
46
|
+
let issued = 0;
|
|
47
|
+
return async (input, init) => {
|
|
48
|
+
issued += 1;
|
|
49
|
+
if (issued > limits.maxRequests) throw new ActionRunError("over_capacity", `Action exceeded its ${limits.maxRequests} outbound request budget`);
|
|
50
|
+
const headers = new Headers(init?.headers);
|
|
51
|
+
headers.set("X-Plitzi-Action-Lineage", lineage.join(","));
|
|
52
|
+
return capped(await base(input, {
|
|
53
|
+
...init,
|
|
54
|
+
headers,
|
|
55
|
+
signal: init?.signal ?? signal
|
|
56
|
+
}), limits.maxResponseBytes);
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* What a task runs with — the one place it is made, for a step of a run and for a function answering a route alike:
|
|
61
|
+
* every secret it resolves registered with the redactor, the space's `kv` namespace, the run's outbound budget, the
|
|
62
|
+
* space's channels. Everything but `log`, which is the STEP's: each step keeps what it logged.
|
|
63
|
+
*/
|
|
64
|
+
var taskContextFor = (config, { kv, email, redactor, signing }, request, signal, runFetch) => (scope) => {
|
|
65
|
+
const { realtime } = config;
|
|
66
|
+
return {
|
|
67
|
+
runId: request.runId,
|
|
68
|
+
spaceId: request.spaceId,
|
|
69
|
+
environment: request.environment,
|
|
70
|
+
trigger: request.trigger,
|
|
71
|
+
user: request.user,
|
|
72
|
+
callerId: request.callerId,
|
|
73
|
+
signal,
|
|
74
|
+
scope,
|
|
75
|
+
/**
|
|
76
|
+
* The secret a STEP asked for, resolved inside that step and never in the flow scope.
|
|
77
|
+
*
|
|
78
|
+
* There is no allow-list to check it against, deliberately: an action is authored by someone who may edit
|
|
79
|
+
* every action in the space, so a list they can edit is not a boundary — it only ever told the redactor what
|
|
80
|
+
* to look for, and the redactor now learns from what was actually resolved. What IS a boundary is that a
|
|
81
|
+
* credential reaches only the params of the step that named it, which is `renderTaskParams`' whole job.
|
|
82
|
+
*/
|
|
83
|
+
credential: async (identifier) => {
|
|
84
|
+
const credential = await config.lookups.getCredential?.(request.spaceId, identifier);
|
|
85
|
+
if (credential) redactor.add(credential);
|
|
86
|
+
return credential;
|
|
87
|
+
},
|
|
88
|
+
connector: async (connectorId) => {
|
|
89
|
+
const manifest = await config.lookups.getConnector?.(request.spaceId, connectorId, request.at);
|
|
90
|
+
if (!manifest) return;
|
|
91
|
+
const credential = manifest.credential ? await config.lookups.getCredential?.(request.spaceId, manifest.credential) : void 0;
|
|
92
|
+
if (credential) redactor.add(credential);
|
|
93
|
+
return {
|
|
94
|
+
manifest,
|
|
95
|
+
credential
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
fetch: runFetch,
|
|
99
|
+
kv: namespaceKv(kv, request.spaceId),
|
|
100
|
+
dbDrivers: config.dbDrivers ?? [],
|
|
101
|
+
email,
|
|
102
|
+
emit: (chunk) => request.emit?.(redactor.redact(chunk)),
|
|
103
|
+
...signing?.({
|
|
104
|
+
spaceId: request.spaceId,
|
|
105
|
+
environment: request.environment
|
|
106
|
+
}),
|
|
107
|
+
...realtime ? {
|
|
108
|
+
publish: (topic, type, data) => realtime.publish({
|
|
109
|
+
spaceId: request.spaceId,
|
|
110
|
+
environment: request.environment
|
|
111
|
+
}, topic, type, data),
|
|
112
|
+
grant: (topic, ttlSeconds) => realtime.grant({
|
|
113
|
+
spaceId: request.spaceId,
|
|
114
|
+
environment: request.environment
|
|
115
|
+
}, topic, ttlSeconds),
|
|
116
|
+
revoke: (topic, grant) => realtime.revoke({
|
|
117
|
+
spaceId: request.spaceId,
|
|
118
|
+
environment: request.environment
|
|
119
|
+
}, topic, grant)
|
|
120
|
+
} : {}
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
//#endregion
|
|
124
|
+
export { createRunFetch, taskContextFor };
|
|
@@ -4,6 +4,8 @@ import { emailTasks } from "./email.js";
|
|
|
4
4
|
import { flowTasks, streamTasks } from "./flow.js";
|
|
5
5
|
import { httpTasks } from "./http.js";
|
|
6
6
|
import { kvTasks } from "./kv.js";
|
|
7
|
+
import { listTasks } from "./list.js";
|
|
8
|
+
import { realtimeTasks } from "./realtime.js";
|
|
7
9
|
import { transformTasks } from "./transform.js";
|
|
8
10
|
//#region src/modules/actions/tasks/builtins.ts
|
|
9
11
|
/**
|
|
@@ -21,8 +23,10 @@ var builtinTasks = [
|
|
|
21
23
|
...connectorTasks,
|
|
22
24
|
...authTasks,
|
|
23
25
|
...kvTasks,
|
|
26
|
+
...listTasks,
|
|
24
27
|
...emailTasks,
|
|
25
|
-
...streamTasks
|
|
28
|
+
...streamTasks,
|
|
29
|
+
...realtimeTasks
|
|
26
30
|
];
|
|
27
31
|
//#endregion
|
|
28
32
|
export { builtinTasks };
|