@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
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { FunctionContext, FunctionFetchInit } from './contract';
|
|
2
|
+
import { RateLimit } from '../actions/runtime/rateLimit';
|
|
3
|
+
import { FunctionsManifest } from '@plitzi/sdk-shared';
|
|
4
|
+
/**
|
|
5
|
+
* The messages between the platform and whatever runs a space's code — the one protocol every runner speaks, whether
|
|
6
|
+
* it is in the process, a service of ours or a provider's. Versioned, because a runner is deployed apart from the
|
|
7
|
+
* platform that calls it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FUNCTIONS_PROTOCOL = 1;
|
|
10
|
+
/** A built bundle: `id` is its content digest, so a runner that has one never needs the code again. */
|
|
11
|
+
export type FunctionsBundle = {
|
|
12
|
+
id: string;
|
|
13
|
+
code: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A bundle as a run knows it: its id, and how to fetch its code — which only a runner that has never seen that id asks
|
|
17
|
+
* for. A run carries no code of its own: up to a megabyte read and sent on every run would be the run's whole cost.
|
|
18
|
+
*/
|
|
19
|
+
export type FunctionsBundleRef = {
|
|
20
|
+
id: string;
|
|
21
|
+
load: () => Promise<string>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* What one invocation may spend — the portable subset every runner can enforce, each mapped to its own knobs. The
|
|
25
|
+
* platform sets them per space (its plan) and never above the deployment's ceilings.
|
|
26
|
+
*/
|
|
27
|
+
export type FunctionLimits = {
|
|
28
|
+
/** CPU time the code may use, awaits not counted. */
|
|
29
|
+
cpuMs: number;
|
|
30
|
+
/** Wall time, from the moment it starts: the run's remaining budget, at most. */
|
|
31
|
+
wallMs: number;
|
|
32
|
+
memoryMb: number;
|
|
33
|
+
/** The size of what it answers, as JSON. */
|
|
34
|
+
outputBytes: number;
|
|
35
|
+
/** Calls back to the platform: `kv`, `fetch`, `publish`… */
|
|
36
|
+
calls: number;
|
|
37
|
+
};
|
|
38
|
+
/** Text travels as text; anything else base64, so a body is never mangled crossing a boundary that only speaks JSON. */
|
|
39
|
+
export type WireBody = {
|
|
40
|
+
text: string;
|
|
41
|
+
} | {
|
|
42
|
+
base64: string;
|
|
43
|
+
};
|
|
44
|
+
export type WireHeaders = [string, string][];
|
|
45
|
+
export type WireRequest = {
|
|
46
|
+
method: string;
|
|
47
|
+
url: string;
|
|
48
|
+
headers: WireHeaders;
|
|
49
|
+
body: WireBody | null;
|
|
50
|
+
};
|
|
51
|
+
export type WireResponse = {
|
|
52
|
+
status: number;
|
|
53
|
+
statusText: string;
|
|
54
|
+
headers: WireHeaders;
|
|
55
|
+
body: WireBody | null;
|
|
56
|
+
};
|
|
57
|
+
/** Who and what an invocation is for: the facts a {@link FunctionContext} carries, sent with it rather than asked for. */
|
|
58
|
+
export type FunctionInvocationContext = Pick<FunctionContext, 'spaceId' | 'environment' | 'runId' | 'trigger' | 'callerId' | 'user'>;
|
|
59
|
+
export type FunctionInvocation = {
|
|
60
|
+
kind: 'task';
|
|
61
|
+
name: string;
|
|
62
|
+
params: Record<string, unknown>;
|
|
63
|
+
context: FunctionInvocationContext;
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'route';
|
|
66
|
+
key: string;
|
|
67
|
+
params: Record<string, string>;
|
|
68
|
+
request: WireRequest;
|
|
69
|
+
context: FunctionInvocationContext;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* One call back from the code to the platform: a {@link FunctionContext} method, by name, with its arguments. The
|
|
73
|
+
* platform answers it with the run's own context — scoped to the space and the run before anything is done.
|
|
74
|
+
*/
|
|
75
|
+
export type FunctionCall = {
|
|
76
|
+
op: 'kv';
|
|
77
|
+
method: string;
|
|
78
|
+
args: unknown[];
|
|
79
|
+
} | {
|
|
80
|
+
op: 'fetch';
|
|
81
|
+
url: string;
|
|
82
|
+
init: FunctionFetchInit;
|
|
83
|
+
} | {
|
|
84
|
+
op: 'publish';
|
|
85
|
+
topic: string;
|
|
86
|
+
type: string;
|
|
87
|
+
data: unknown;
|
|
88
|
+
} | {
|
|
89
|
+
op: 'grant';
|
|
90
|
+
topic: string;
|
|
91
|
+
ttlSeconds?: number;
|
|
92
|
+
} | {
|
|
93
|
+
op: 'revoke';
|
|
94
|
+
topic: string;
|
|
95
|
+
grant?: string;
|
|
96
|
+
} | {
|
|
97
|
+
op: 'rateLimit';
|
|
98
|
+
bucket: string;
|
|
99
|
+
limit: RateLimit;
|
|
100
|
+
} | {
|
|
101
|
+
op: 'sign';
|
|
102
|
+
value: string;
|
|
103
|
+
} | {
|
|
104
|
+
op: 'verify';
|
|
105
|
+
value: string;
|
|
106
|
+
signature: string;
|
|
107
|
+
}
|
|
108
|
+
/** Each value as the guest could send it: JSON, with what JSON cannot say already said as text. */
|
|
109
|
+
| {
|
|
110
|
+
op: 'log';
|
|
111
|
+
values: unknown[];
|
|
112
|
+
} | {
|
|
113
|
+
op: 'emit';
|
|
114
|
+
chunk: unknown;
|
|
115
|
+
};
|
|
116
|
+
/** How a call or an invocation ended: its value, or why not — never a thrown object crossing the boundary. */
|
|
117
|
+
export type FunctionAnswer = {
|
|
118
|
+
ok: true;
|
|
119
|
+
value: unknown;
|
|
120
|
+
} | {
|
|
121
|
+
ok: false;
|
|
122
|
+
error: string;
|
|
123
|
+
};
|
|
124
|
+
/** Why the runner stopped an invocation, when it was not the code's own error. */
|
|
125
|
+
export type FunctionStopReason = 'cpu' | 'wall' | 'memory' | 'output' | 'calls' | 'aborted';
|
|
126
|
+
/** What the platform asks a runner for: every runner implements this, and nothing else of it is assumed. */
|
|
127
|
+
export type FunctionRunner = {
|
|
128
|
+
/** Loads a bundle and answers what it declares — when the space's functions are saved, never on a run. */
|
|
129
|
+
describe: (bundle: FunctionsBundle) => Promise<unknown>;
|
|
130
|
+
/**
|
|
131
|
+
* Runs one task or route of a bundle. Each call the code makes is handed to `answer`, whose result goes back to it;
|
|
132
|
+
* `signal` aborting ends the invocation. Resolves with what the code returned, and rejects with a
|
|
133
|
+
* {@link FunctionFailure}.
|
|
134
|
+
*/
|
|
135
|
+
invoke: (request: FunctionInvokeRequest) => Promise<unknown>;
|
|
136
|
+
close?: () => Promise<void>;
|
|
137
|
+
};
|
|
138
|
+
/** What one invocation spent — what a deployment meters and budgets by. */
|
|
139
|
+
export type FunctionUsage = {
|
|
140
|
+
cpuMs: number;
|
|
141
|
+
wallMs: number;
|
|
142
|
+
calls: number;
|
|
143
|
+
};
|
|
144
|
+
export type FunctionInvokeRequest = {
|
|
145
|
+
bundle: FunctionsBundleRef;
|
|
146
|
+
invocation: FunctionInvocation;
|
|
147
|
+
limits: FunctionLimits;
|
|
148
|
+
/** Handed each call exactly as the code sent it: the platform reads it (`readCall`), trusting no runner to have. */
|
|
149
|
+
answer: (call: unknown) => Promise<unknown>;
|
|
150
|
+
signal: AbortSignal;
|
|
151
|
+
/** Told what the invocation spent once it ends, however it ended — before `invoke` settles. */
|
|
152
|
+
onUsage?: (usage: FunctionUsage) => void;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Why an invocation did not answer: the code refused (`refused` — an `ActionRefusal`, its message written for whoever
|
|
156
|
+
* asked), it threw (`error`), or the runner stopped it (a {@link FunctionStopReason}).
|
|
157
|
+
*/
|
|
158
|
+
export type FunctionFailureReason = FunctionStopReason | 'error' | 'refused';
|
|
159
|
+
/** An invocation that did not answer, and why ({@link FunctionFailureReason}). */
|
|
160
|
+
export declare class FunctionFailure extends Error {
|
|
161
|
+
readonly reason: FunctionFailureReason;
|
|
162
|
+
constructor(reason: FunctionFailureReason, message: string);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* A space's functions as a run reaches them: the bundle by reference, what it declared when it was saved, and the limits
|
|
166
|
+
* its plan sets.
|
|
167
|
+
*/
|
|
168
|
+
export type SpaceFunctions = {
|
|
169
|
+
bundle: FunctionsBundleRef;
|
|
170
|
+
manifest: FunctionsManifest;
|
|
171
|
+
limits?: Partial<FunctionLimits>;
|
|
172
|
+
/**
|
|
173
|
+
* What runs them, when it is not the deployment's sandbox: the space's own runtime (`createRemoteRunner` to it). Its
|
|
174
|
+
* bundle id then names that runtime's code, so a registry built for one never answers for another.
|
|
175
|
+
*/
|
|
176
|
+
runner?: FunctionRunner;
|
|
177
|
+
};
|
|
178
|
+
/** The messages of one runner connection (our own runner service), each side's in turn. */
|
|
179
|
+
export type RunnerRequestMessage = {
|
|
180
|
+
type: 'describe';
|
|
181
|
+
protocol: number;
|
|
182
|
+
bundle: FunctionsBundle;
|
|
183
|
+
} | {
|
|
184
|
+
type: 'invoke';
|
|
185
|
+
protocol: number;
|
|
186
|
+
bundleId: string;
|
|
187
|
+
invocation: FunctionInvocation;
|
|
188
|
+
limits: FunctionLimits;
|
|
189
|
+
} | {
|
|
190
|
+
type: 'bundle';
|
|
191
|
+
bundle: FunctionsBundle;
|
|
192
|
+
} | {
|
|
193
|
+
type: 'answer';
|
|
194
|
+
id: number;
|
|
195
|
+
answer: FunctionAnswer;
|
|
196
|
+
} | {
|
|
197
|
+
type: 'abort';
|
|
198
|
+
};
|
|
199
|
+
export type RunnerResponseMessage = {
|
|
200
|
+
type: 'needBundle';
|
|
201
|
+
}
|
|
202
|
+
/** As the code sent it: the platform reads it, trusting no runner to have. */
|
|
203
|
+
| {
|
|
204
|
+
type: 'call';
|
|
205
|
+
id: number;
|
|
206
|
+
call: unknown;
|
|
207
|
+
} | {
|
|
208
|
+
type: 'done';
|
|
209
|
+
value: unknown;
|
|
210
|
+
usage?: FunctionUsage;
|
|
211
|
+
} | {
|
|
212
|
+
type: 'failed';
|
|
213
|
+
reason: FunctionFailureReason;
|
|
214
|
+
error: string;
|
|
215
|
+
usage?: FunctionUsage;
|
|
216
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/modules/functions/protocol.ts
|
|
2
|
+
/**
|
|
3
|
+
* The messages between the platform and whatever runs a space's code — the one protocol every runner speaks, whether
|
|
4
|
+
* it is in the process, a service of ours or a provider's. Versioned, because a runner is deployed apart from the
|
|
5
|
+
* platform that calls it.
|
|
6
|
+
*/
|
|
7
|
+
var FUNCTIONS_PROTOCOL = 1;
|
|
8
|
+
/** An invocation that did not answer, and why ({@link FunctionFailureReason}). */
|
|
9
|
+
var FunctionFailure = class extends Error {
|
|
10
|
+
reason;
|
|
11
|
+
constructor(reason, message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "FunctionFailure";
|
|
14
|
+
this.reason = reason;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { FUNCTIONS_PROTOCOL, FunctionFailure };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FunctionsConfig } from './config';
|
|
2
|
+
import { SpaceFunctions } from './protocol';
|
|
3
|
+
import { TaskContextSource } from '../actions/runtime/runAction';
|
|
4
|
+
import { Environment, SpaceRevision, SSRUser } from '@plitzi/sdk-shared';
|
|
5
|
+
/** Who is asking a route, as the transport knows them. */
|
|
6
|
+
export type RouteVisit = {
|
|
7
|
+
spaceId: number;
|
|
8
|
+
environment: Environment;
|
|
9
|
+
at?: SpaceRevision;
|
|
10
|
+
user?: SSRUser;
|
|
11
|
+
/** `user:<id>` or `ip:<address>`, as for an action call. */
|
|
12
|
+
callerId: string;
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
};
|
|
15
|
+
export type RouteHandler = {
|
|
16
|
+
key: string;
|
|
17
|
+
handle: (request: Request) => Promise<Response>;
|
|
18
|
+
};
|
|
19
|
+
/** The first of `keys` that answers the request, with its params — declared order, as a space reads them. */
|
|
20
|
+
export declare const matchRoute: (keys: readonly string[], method: string, path: string) => {
|
|
21
|
+
key: string;
|
|
22
|
+
params: Record<string, string>;
|
|
23
|
+
} | undefined;
|
|
24
|
+
export type RouteSources = {
|
|
25
|
+
config: FunctionsConfig;
|
|
26
|
+
getFunctions?: (spaceId: number, at?: SpaceRevision) => Promise<SpaceFunctions | undefined>;
|
|
27
|
+
taskContext: TaskContextSource;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The route a request reaches, if one does: the deployment's own (its native functions') first, then the space's —
|
|
31
|
+
* each run with the same context a task of theirs gets, `trigger: 'route'`.
|
|
32
|
+
*/
|
|
33
|
+
export declare const createRoutes: ({ config, getFunctions, taskContext }: RouteSources) => {
|
|
34
|
+
/** The route that answers `method path` (the path after `/api`), for this visit — or none. */
|
|
35
|
+
routeFor: (visit: RouteVisit, method: string, path: string) => Promise<RouteHandler | undefined>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { serverLog } from "../../helpers/serverLog.js";
|
|
2
|
+
import { ActionRefusal } from "../actions/runtime/errors.js";
|
|
3
|
+
import { functionContextFor } from "./context.js";
|
|
4
|
+
import { parseRouteKey } from "./manifest.js";
|
|
5
|
+
import { answerCall, readCall, responseOf, wireRequestOf, wireResponseFrom } from "./capabilities.js";
|
|
6
|
+
import { functionLimitsFor } from "./config.js";
|
|
7
|
+
import { FunctionFailure } from "./protocol.js";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
//#region src/modules/functions/routes.ts
|
|
10
|
+
/** A route key's params from a path it answers, or nothing when it does not answer it. */
|
|
11
|
+
var matchKey = (key, method, path) => {
|
|
12
|
+
const segments = path.replace(/^\/+/, "").split("/");
|
|
13
|
+
if (key.method !== method || segments.length !== key.segments.length) return;
|
|
14
|
+
const params = {};
|
|
15
|
+
for (const [index, expected] of key.segments.entries()) {
|
|
16
|
+
let actual;
|
|
17
|
+
try {
|
|
18
|
+
actual = decodeURIComponent(segments[index] ?? "");
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (expected.startsWith(":")) {
|
|
23
|
+
if (!actual) return;
|
|
24
|
+
params[expected.slice(1)] = actual;
|
|
25
|
+
} else if (expected !== actual) return;
|
|
26
|
+
}
|
|
27
|
+
return params;
|
|
28
|
+
};
|
|
29
|
+
/** The first of `keys` that answers the request, with its params — declared order, as a space reads them. */
|
|
30
|
+
var matchRoute = (keys, method, path) => {
|
|
31
|
+
for (const key of keys) {
|
|
32
|
+
const parsed = parseRouteKey(key);
|
|
33
|
+
const params = parsed ? matchKey(parsed, method, path) : void 0;
|
|
34
|
+
if (params) return {
|
|
35
|
+
key,
|
|
36
|
+
params
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* What of a visitor's request a function sees: everything but their credentials. The session cookie and any bearer
|
|
42
|
+
* are the platform's — `ctx.user` says who is asking — and a function holding them could spend them anywhere its
|
|
43
|
+
* hosts reach.
|
|
44
|
+
*/
|
|
45
|
+
var withoutCredentials = (request) => {
|
|
46
|
+
const headers = new Headers(request.headers);
|
|
47
|
+
[
|
|
48
|
+
"cookie",
|
|
49
|
+
"authorization",
|
|
50
|
+
"proxy-authorization"
|
|
51
|
+
].forEach((name) => headers.delete(name));
|
|
52
|
+
[...headers.keys()].filter((name) => name.startsWith("x-plitzi-")).forEach((name) => headers.delete(name));
|
|
53
|
+
return new Request(request, { headers });
|
|
54
|
+
};
|
|
55
|
+
/** What of a function's answer reaches the visitor: everything but cookies — the host's cookies are the platform's. */
|
|
56
|
+
var withoutCookies = (response) => {
|
|
57
|
+
const headers = new Headers(response.headers);
|
|
58
|
+
headers.delete("set-cookie");
|
|
59
|
+
return new Response(response.body, {
|
|
60
|
+
status: response.status,
|
|
61
|
+
statusText: response.statusText,
|
|
62
|
+
headers
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* A route that refused, threw or was stopped. A refusal's message was written for the visitor, who gets it with a 400;
|
|
67
|
+
* of anything else the visitor gets that it failed, and the server's log gets why.
|
|
68
|
+
*/
|
|
69
|
+
var failed = (visit, key, error) => {
|
|
70
|
+
if (error instanceof ActionRefusal || error instanceof FunctionFailure && error.reason === "refused") return Response.json({ error: error.message }, { status: 400 });
|
|
71
|
+
serverLog.error("Functions", `route ${key} of space ${String(visit.spaceId)} failed`, error);
|
|
72
|
+
const stopped = error instanceof FunctionFailure && error.reason !== "error";
|
|
73
|
+
return Response.json({ error: stopped ? "This route ran out of what it may spend" : "This route failed" }, { status: stopped ? 503 : 500 });
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The route a request reaches, if one does: the deployment's own (its native functions') first, then the space's —
|
|
77
|
+
* each run with the same context a task of theirs gets, `trigger: 'route'`.
|
|
78
|
+
*/
|
|
79
|
+
var createRoutes = ({ config, getFunctions, taskContext }) => {
|
|
80
|
+
const native = (config.native ?? []).flatMap((definition) => Object.entries(definition.routes ?? {}).map(([key, handler]) => ({
|
|
81
|
+
key,
|
|
82
|
+
handler,
|
|
83
|
+
hosts: definition.allow?.hosts ?? []
|
|
84
|
+
})));
|
|
85
|
+
const contextOf = (visit, key, hosts) => {
|
|
86
|
+
const ctx = taskContext({
|
|
87
|
+
runId: randomUUID(),
|
|
88
|
+
spaceId: visit.spaceId,
|
|
89
|
+
environment: visit.environment,
|
|
90
|
+
trigger: "call",
|
|
91
|
+
callerId: visit.callerId,
|
|
92
|
+
...visit.user ? { user: visit.user } : {},
|
|
93
|
+
...visit.at ? { at: visit.at } : {}
|
|
94
|
+
}, visit.signal, [`route:${key}`]);
|
|
95
|
+
return {
|
|
96
|
+
...functionContextFor({
|
|
97
|
+
...ctx,
|
|
98
|
+
log: (line) => {
|
|
99
|
+
serverLog.info("Functions", `route ${key} of space ${String(visit.spaceId)}: ${line}`);
|
|
100
|
+
}
|
|
101
|
+
}, hosts),
|
|
102
|
+
trigger: "route"
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
const nativeRoute = (visit, method, path) => {
|
|
106
|
+
const match = matchRoute(native.map((route) => route.key), method, path);
|
|
107
|
+
const route = match ? native.find((entry) => entry.key === match.key) : void 0;
|
|
108
|
+
if (!match || !route) return;
|
|
109
|
+
const handler = route.handler;
|
|
110
|
+
return {
|
|
111
|
+
key: match.key,
|
|
112
|
+
handle: async (request) => {
|
|
113
|
+
try {
|
|
114
|
+
return withoutCookies(await handler(withoutCredentials(request), {
|
|
115
|
+
...contextOf(visit, match.key, route.hosts),
|
|
116
|
+
params: match.params
|
|
117
|
+
}));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
return failed(visit, match.key, error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
const spaceRoute = async (visit, method, path) => {
|
|
125
|
+
const functions = await getFunctions?.(visit.spaceId, visit.at);
|
|
126
|
+
const runner = functions?.runner ?? config.runner;
|
|
127
|
+
const match = functions ? matchRoute(functions.manifest.routes, method, path) : void 0;
|
|
128
|
+
if (!runner || !functions || !match) return;
|
|
129
|
+
return {
|
|
130
|
+
key: match.key,
|
|
131
|
+
handle: async (request) => {
|
|
132
|
+
const refusal = await config.admit?.(visit.spaceId);
|
|
133
|
+
if (refusal) return Response.json({ error: refusal }, { status: 429 });
|
|
134
|
+
const fnCtx = contextOf(visit, match.key, functions.manifest.hosts);
|
|
135
|
+
let ok = false;
|
|
136
|
+
let spent;
|
|
137
|
+
try {
|
|
138
|
+
const value = await runner.invoke({
|
|
139
|
+
bundle: functions.bundle,
|
|
140
|
+
invocation: {
|
|
141
|
+
kind: "route",
|
|
142
|
+
key: match.key,
|
|
143
|
+
params: match.params,
|
|
144
|
+
request: await wireRequestOf(withoutCredentials(request)),
|
|
145
|
+
context: {
|
|
146
|
+
spaceId: fnCtx.spaceId,
|
|
147
|
+
environment: fnCtx.environment,
|
|
148
|
+
runId: fnCtx.runId,
|
|
149
|
+
trigger: fnCtx.trigger,
|
|
150
|
+
callerId: fnCtx.callerId,
|
|
151
|
+
...fnCtx.user ? { user: fnCtx.user } : {}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
limits: functionLimitsFor(config.limits, functions.limits),
|
|
155
|
+
answer: (call) => answerCall(fnCtx, readCall(call)),
|
|
156
|
+
signal: visit.signal,
|
|
157
|
+
onUsage: (usage) => {
|
|
158
|
+
spent = usage;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
ok = true;
|
|
162
|
+
return withoutCookies(responseOf(wireResponseFrom(value)));
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return failed(visit, match.key, error);
|
|
165
|
+
} finally {
|
|
166
|
+
if (spent) config.onUsage?.({
|
|
167
|
+
spaceId: visit.spaceId,
|
|
168
|
+
task: `route ${match.key}`,
|
|
169
|
+
ok,
|
|
170
|
+
usage: spent
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
/** The route that answers `method path` (the path after `/api`), for this visit — or none. */
|
|
178
|
+
routeFor: async (visit, method, path) => nativeRoute(visit, method, path) ?? await spaceRoute(visit, method, path) };
|
|
179
|
+
};
|
|
180
|
+
//#endregion
|
|
181
|
+
export { createRoutes, matchRoute };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { RunnerRequestMessage, RunnerResponseMessage } from '../protocol';
|
|
2
|
+
/** What the platform sent the runner, or nothing when it is not one of the protocol's messages. */
|
|
3
|
+
export declare const readRequestMessage: (data: string) => RunnerRequestMessage | undefined;
|
|
4
|
+
/** What the runner sent the platform, or nothing when it is not one of the protocol's messages. */
|
|
5
|
+
export declare const readResponseMessage: (data: string) => RunnerResponseMessage | undefined;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
//#region src/modules/functions/runner/messages.ts
|
|
2
|
+
var ENVIRONMENTS = {
|
|
3
|
+
production: true,
|
|
4
|
+
staging: true,
|
|
5
|
+
development: true,
|
|
6
|
+
main: true
|
|
7
|
+
};
|
|
8
|
+
var isEnvironment = (value) => typeof value === "string" && Object.hasOwn(ENVIRONMENTS, value);
|
|
9
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
var parse = (data) => {
|
|
11
|
+
try {
|
|
12
|
+
const value = JSON.parse(data);
|
|
13
|
+
return isRecord(value) ? value : void 0;
|
|
14
|
+
} catch {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var bundleOf = (value) => isRecord(value) && typeof value.id === "string" && typeof value.code === "string" ? {
|
|
19
|
+
id: value.id,
|
|
20
|
+
code: value.code
|
|
21
|
+
} : void 0;
|
|
22
|
+
var limitOf = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
23
|
+
var limitsOf = (value) => {
|
|
24
|
+
if (!isRecord(value)) return;
|
|
25
|
+
const cpuMs = limitOf(value.cpuMs);
|
|
26
|
+
const wallMs = limitOf(value.wallMs);
|
|
27
|
+
const memoryMb = limitOf(value.memoryMb);
|
|
28
|
+
const outputBytes = limitOf(value.outputBytes);
|
|
29
|
+
const calls = limitOf(value.calls);
|
|
30
|
+
return cpuMs !== void 0 && wallMs !== void 0 && memoryMb !== void 0 && outputBytes !== void 0 && calls !== void 0 ? {
|
|
31
|
+
cpuMs,
|
|
32
|
+
wallMs,
|
|
33
|
+
memoryMb,
|
|
34
|
+
outputBytes,
|
|
35
|
+
calls
|
|
36
|
+
} : void 0;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* An invocation as the platform sent it. The runner only carries it into the isolate, where the guest reads it again;
|
|
40
|
+
* what is checked here is what the runner itself acts on.
|
|
41
|
+
*/
|
|
42
|
+
var invocationOf = (value) => {
|
|
43
|
+
if (!isRecord(value) || !isRecord(value.context) || value.kind !== "task" && value.kind !== "route") return;
|
|
44
|
+
const { context } = value;
|
|
45
|
+
if (typeof context.spaceId !== "number" || typeof context.runId !== "string" || typeof context.trigger !== "string" || typeof context.callerId !== "string" || !isEnvironment(context.environment)) return;
|
|
46
|
+
const facts = {
|
|
47
|
+
spaceId: context.spaceId,
|
|
48
|
+
environment: context.environment,
|
|
49
|
+
runId: context.runId,
|
|
50
|
+
trigger: context.trigger,
|
|
51
|
+
callerId: context.callerId
|
|
52
|
+
};
|
|
53
|
+
const user = isRecord(context.user) ? context.user : void 0;
|
|
54
|
+
const withUser = user && typeof user.id === "number" && typeof user.username === "string" && typeof user.email === "string" && typeof user.verified === "boolean" && Array.isArray(user.permissions) && Array.isArray(user.roles) ? {
|
|
55
|
+
...facts,
|
|
56
|
+
user: {
|
|
57
|
+
id: user.id,
|
|
58
|
+
username: user.username,
|
|
59
|
+
email: user.email,
|
|
60
|
+
verified: user.verified,
|
|
61
|
+
permissions: user.permissions.filter((entry) => typeof entry === "string"),
|
|
62
|
+
roles: user.roles.filter((entry) => typeof entry === "string")
|
|
63
|
+
}
|
|
64
|
+
} : facts;
|
|
65
|
+
if (value.kind === "task") return typeof value.name === "string" && isRecord(value.params) ? {
|
|
66
|
+
kind: "task",
|
|
67
|
+
name: value.name,
|
|
68
|
+
params: value.params,
|
|
69
|
+
context: withUser
|
|
70
|
+
} : void 0;
|
|
71
|
+
const request = isRecord(value.request) ? value.request : void 0;
|
|
72
|
+
if (!request || typeof value.key !== "string" || typeof request.method !== "string" || typeof request.url !== "string") return;
|
|
73
|
+
const params = isRecord(value.params) ? value.params : {};
|
|
74
|
+
const body = isRecord(request.body) ? request.body : void 0;
|
|
75
|
+
return {
|
|
76
|
+
kind: "route",
|
|
77
|
+
key: value.key,
|
|
78
|
+
params: Object.fromEntries(Object.entries(params).map(([name, param]) => [name, String(param)])),
|
|
79
|
+
request: {
|
|
80
|
+
method: request.method,
|
|
81
|
+
url: request.url,
|
|
82
|
+
headers: Array.isArray(request.headers) ? request.headers.flatMap((pair) => Array.isArray(pair) && typeof pair[0] === "string" && typeof pair[1] === "string" ? [[pair[0], pair[1]]] : []) : [],
|
|
83
|
+
body: body && typeof body.text === "string" ? { text: body.text } : body && typeof body.base64 === "string" ? { base64: body.base64 } : null
|
|
84
|
+
},
|
|
85
|
+
context: withUser
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
var answerOf = (value) => {
|
|
89
|
+
if (!isRecord(value)) return;
|
|
90
|
+
if (value.ok === true) return {
|
|
91
|
+
ok: true,
|
|
92
|
+
value: value.value
|
|
93
|
+
};
|
|
94
|
+
return value.ok === false && typeof value.error === "string" ? {
|
|
95
|
+
ok: false,
|
|
96
|
+
error: value.error
|
|
97
|
+
} : void 0;
|
|
98
|
+
};
|
|
99
|
+
/** What the platform sent the runner, or nothing when it is not one of the protocol's messages. */
|
|
100
|
+
var readRequestMessage = (data) => {
|
|
101
|
+
const message = parse(data);
|
|
102
|
+
switch (message?.type) {
|
|
103
|
+
case "describe": {
|
|
104
|
+
const bundle = bundleOf(message.bundle);
|
|
105
|
+
return bundle && typeof message.protocol === "number" ? {
|
|
106
|
+
type: "describe",
|
|
107
|
+
protocol: message.protocol,
|
|
108
|
+
bundle
|
|
109
|
+
} : void 0;
|
|
110
|
+
}
|
|
111
|
+
case "invoke": {
|
|
112
|
+
const invocation = invocationOf(message.invocation);
|
|
113
|
+
const limits = limitsOf(message.limits);
|
|
114
|
+
return invocation && limits && typeof message.bundleId === "string" && typeof message.protocol === "number" ? {
|
|
115
|
+
type: "invoke",
|
|
116
|
+
protocol: message.protocol,
|
|
117
|
+
bundleId: message.bundleId,
|
|
118
|
+
invocation,
|
|
119
|
+
limits
|
|
120
|
+
} : void 0;
|
|
121
|
+
}
|
|
122
|
+
case "bundle": {
|
|
123
|
+
const bundle = bundleOf(message.bundle);
|
|
124
|
+
return bundle ? {
|
|
125
|
+
type: "bundle",
|
|
126
|
+
bundle
|
|
127
|
+
} : void 0;
|
|
128
|
+
}
|
|
129
|
+
case "answer": {
|
|
130
|
+
const answer = answerOf(message.answer);
|
|
131
|
+
return answer && typeof message.id === "number" ? {
|
|
132
|
+
type: "answer",
|
|
133
|
+
id: message.id,
|
|
134
|
+
answer
|
|
135
|
+
} : void 0;
|
|
136
|
+
}
|
|
137
|
+
case "abort": return { type: "abort" };
|
|
138
|
+
default: return;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
var FAILURE_REASONS = [
|
|
142
|
+
"cpu",
|
|
143
|
+
"wall",
|
|
144
|
+
"memory",
|
|
145
|
+
"output",
|
|
146
|
+
"calls",
|
|
147
|
+
"aborted",
|
|
148
|
+
"error",
|
|
149
|
+
"refused"
|
|
150
|
+
];
|
|
151
|
+
var reasonOf = (value) => FAILURE_REASONS.find((reason) => reason === value) ?? "error";
|
|
152
|
+
var usageOf = (value) => {
|
|
153
|
+
if (!isRecord(value)) return {};
|
|
154
|
+
const cpuMs = limitOf(value.cpuMs);
|
|
155
|
+
const wallMs = limitOf(value.wallMs);
|
|
156
|
+
const calls = limitOf(value.calls);
|
|
157
|
+
return cpuMs !== void 0 && wallMs !== void 0 && calls !== void 0 ? { usage: {
|
|
158
|
+
cpuMs,
|
|
159
|
+
wallMs,
|
|
160
|
+
calls
|
|
161
|
+
} } : {};
|
|
162
|
+
};
|
|
163
|
+
/** What the runner sent the platform, or nothing when it is not one of the protocol's messages. */
|
|
164
|
+
var readResponseMessage = (data) => {
|
|
165
|
+
const message = parse(data);
|
|
166
|
+
switch (message?.type) {
|
|
167
|
+
case "needBundle": return { type: "needBundle" };
|
|
168
|
+
case "call": return typeof message.id === "number" ? {
|
|
169
|
+
type: "call",
|
|
170
|
+
id: message.id,
|
|
171
|
+
call: message.call
|
|
172
|
+
} : void 0;
|
|
173
|
+
case "done": return {
|
|
174
|
+
type: "done",
|
|
175
|
+
value: message.value,
|
|
176
|
+
...usageOf(message.usage)
|
|
177
|
+
};
|
|
178
|
+
case "failed": return typeof message.error === "string" ? {
|
|
179
|
+
type: "failed",
|
|
180
|
+
reason: reasonOf(message.reason),
|
|
181
|
+
error: message.error,
|
|
182
|
+
...usageOf(message.usage)
|
|
183
|
+
} : void 0;
|
|
184
|
+
default: return;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
//#endregion
|
|
188
|
+
export { readRequestMessage, readResponseMessage };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FunctionRunner } from '../protocol';
|
|
2
|
+
export type RemoteRunnerOptions = {
|
|
3
|
+
/** The runner service, `ws://functions-runner:8790`. */
|
|
4
|
+
url: string;
|
|
5
|
+
secret: string;
|
|
6
|
+
/** How long to wait for the runner to accept a connection. */
|
|
7
|
+
connectTimeoutMs?: number;
|
|
8
|
+
/**
|
|
9
|
+
* How long past an invocation's own wall limit this side waits before giving up on the runner and closing the socket.
|
|
10
|
+
* The runner stops an invocation at its limit and says so; this is for a runner that no longer says anything, so a
|
|
11
|
+
* connection never outlives the work it was for.
|
|
12
|
+
*/
|
|
13
|
+
graceMs?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The platform's side of the runner service: a {@link FunctionRunner} over one WebSocket per operation. Each call the
|
|
17
|
+
* code makes arrives on it and is answered on it, by this process — the one holding the run.
|
|
18
|
+
*/
|
|
19
|
+
export declare const createRemoteRunner: ({ url, secret, connectTimeoutMs, graceMs }: RemoteRunnerOptions) => FunctionRunner;
|