@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,228 @@
|
|
|
1
|
+
//#region src/modules/functions/capabilities.ts
|
|
2
|
+
var TEXTUAL = /^(text\/|application\/([\w.+-]*\+)?(json|xml|javascript|x-www-form-urlencoded))/i;
|
|
3
|
+
/** A body as the wire carries it: as text when its type says it is text, base64 otherwise. */
|
|
4
|
+
var wireBodyOf = async (body, contentType) => {
|
|
5
|
+
const bytes = Buffer.from(await body.arrayBuffer());
|
|
6
|
+
if (!bytes.length) return null;
|
|
7
|
+
return contentType && TEXTUAL.test(contentType) ? { text: bytes.toString("utf8") } : { base64: bytes.toString("base64") };
|
|
8
|
+
};
|
|
9
|
+
var bodyOf = (body) => {
|
|
10
|
+
if (!body) return null;
|
|
11
|
+
return "text" in body ? body.text : new Uint8Array(Buffer.from(body.base64, "base64"));
|
|
12
|
+
};
|
|
13
|
+
var wireResponseOf = async (response) => ({
|
|
14
|
+
status: response.status,
|
|
15
|
+
statusText: response.statusText,
|
|
16
|
+
headers: [...response.headers],
|
|
17
|
+
body: await wireBodyOf(response, response.headers.get("content-type"))
|
|
18
|
+
});
|
|
19
|
+
var wireRequestOf = async (request) => ({
|
|
20
|
+
method: request.method,
|
|
21
|
+
url: request.url,
|
|
22
|
+
headers: [...request.headers],
|
|
23
|
+
body: await wireBodyOf(request, request.headers.get("content-type"))
|
|
24
|
+
});
|
|
25
|
+
var responseOf = ({ status, statusText, headers, body }) => new Response(bodyOf(body), {
|
|
26
|
+
status,
|
|
27
|
+
statusText,
|
|
28
|
+
headers
|
|
29
|
+
});
|
|
30
|
+
var CallError = class extends Error {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "CallError";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var stringArg = (value, what) => {
|
|
37
|
+
if (typeof value !== "string") throw new CallError(`${what} is a string`);
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
var numberArg = (value, what) => {
|
|
41
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new CallError(`${what} is a number`);
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
var optionalNumber = (value, what) => value === void 0 || value === null ? void 0 : numberArg(value, what);
|
|
45
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
|
+
var headerPairsOf = (value) => Array.isArray(value) ? value.flatMap((pair) => Array.isArray(pair) && typeof pair[0] === "string" && typeof pair[1] === "string" ? [[pair[0], pair[1]]] : []) : [];
|
|
47
|
+
var wireBodyFrom = (value) => {
|
|
48
|
+
const body = isRecord(value) ? value : void 0;
|
|
49
|
+
return body && typeof body.text === "string" ? { text: body.text } : body && typeof body.base64 === "string" ? { base64: body.base64 } : null;
|
|
50
|
+
};
|
|
51
|
+
/** An answer that crossed a boundary as a wire response — from a space's code, or from the platform — read, not trusted. */
|
|
52
|
+
var wireResponseFrom = (value) => {
|
|
53
|
+
const answer = isRecord(value) ? value : {};
|
|
54
|
+
return {
|
|
55
|
+
status: typeof answer.status === "number" && answer.status >= 200 && answer.status <= 599 ? answer.status : 500,
|
|
56
|
+
statusText: typeof answer.statusText === "string" ? answer.statusText : "",
|
|
57
|
+
headers: headerPairsOf(answer.headers),
|
|
58
|
+
body: wireBodyFrom(answer.body)
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/** A request that crossed as a wire request, as a web `Request` again — what a route handler outside the sandbox takes. */
|
|
62
|
+
var requestFromWire = (value) => {
|
|
63
|
+
const wire = isRecord(value) ? value : {};
|
|
64
|
+
const method = typeof wire.method === "string" ? wire.method : "GET";
|
|
65
|
+
return new Request(typeof wire.url === "string" ? wire.url : "http://runtime.invalid/", {
|
|
66
|
+
method,
|
|
67
|
+
headers: headerPairsOf(wire.headers),
|
|
68
|
+
body: method === "GET" || method === "HEAD" ? null : bodyOf(wireBodyFrom(wire.body))
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
var recordArg = (value, what) => {
|
|
72
|
+
if (!isRecord(value)) throw new CallError(`${what} is an object`);
|
|
73
|
+
return value;
|
|
74
|
+
};
|
|
75
|
+
var listEntryOf = (value) => {
|
|
76
|
+
const entry = recordArg(value, "A list entry");
|
|
77
|
+
return {
|
|
78
|
+
id: stringArg(entry.id, "A list entry’s id"),
|
|
79
|
+
score: numberArg(entry.score, "A list entry’s score"),
|
|
80
|
+
value: entry.value
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
var putOptionsOf = (value) => {
|
|
84
|
+
if (value === void 0 || value === null) return {};
|
|
85
|
+
const options = recordArg(value, "List options");
|
|
86
|
+
const keep = optionalNumber(options.keep, "keep");
|
|
87
|
+
return {
|
|
88
|
+
...keep === void 0 ? {} : { keep },
|
|
89
|
+
...options.higherOnly === true ? { higherOnly: true } : {}
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
var rangeOf = (value) => {
|
|
93
|
+
if (value === void 0 || value === null) return {};
|
|
94
|
+
const range = recordArg(value, "A list range");
|
|
95
|
+
const offset = optionalNumber(range.offset, "offset");
|
|
96
|
+
const limit = optionalNumber(range.limit, "limit");
|
|
97
|
+
return {
|
|
98
|
+
...offset === void 0 ? {} : { offset },
|
|
99
|
+
...limit === void 0 ? {} : { limit },
|
|
100
|
+
...range.order === "asc" || range.order === "desc" ? { order: range.order } : {}
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* One `kv` call, its arguments read rather than trusted: they were written by the space's code, and the store below
|
|
105
|
+
* takes typed values. The space's namespace is the store's, applied by the platform before the code ever had it.
|
|
106
|
+
*/
|
|
107
|
+
var kvCall = (kv, method, args) => {
|
|
108
|
+
const [first, second, third, fourth] = args;
|
|
109
|
+
switch (method) {
|
|
110
|
+
case "get": return kv.get(stringArg(first, "A key"));
|
|
111
|
+
case "set": return kv.set(stringArg(first, "A key"), second, optionalNumber(third, "A lifetime"));
|
|
112
|
+
case "delete": return kv.delete(stringArg(first, "A key"));
|
|
113
|
+
case "increment": return kv.increment(stringArg(first, "A key"), numberArg(second, "An amount"), optionalNumber(third, "A lifetime"));
|
|
114
|
+
case "swap": return kv.swap(stringArg(first, "A key"), second ?? void 0, third, optionalNumber(fourth, "A lifetime"));
|
|
115
|
+
case "listPut": return kv.listPut(stringArg(first, "A list"), listEntryOf(second), putOptionsOf(third));
|
|
116
|
+
case "listRange": return kv.listRange(stringArg(first, "A list"), rangeOf(second));
|
|
117
|
+
case "listRemove": return kv.listRemove(stringArg(first, "A list"), stringArg(second, "An entry id"));
|
|
118
|
+
default: return Promise.reject(new CallError(`kv has no "${method}"`));
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var fetchInitOf = (value) => {
|
|
122
|
+
const init = recordArg(value, "A request");
|
|
123
|
+
const headers = init.headers === void 0 ? {} : recordArg(init.headers, "Headers");
|
|
124
|
+
return {
|
|
125
|
+
...init.method === void 0 ? {} : { method: stringArg(init.method, "A method") },
|
|
126
|
+
headers: Object.fromEntries(Object.entries(headers).map(([name, header]) => [name, stringArg(header, `Header "${name}"`)])),
|
|
127
|
+
...init.body === void 0 || init.body === null ? {} : { body: stringArg(init.body, "A body") },
|
|
128
|
+
...init.credential === void 0 ? {} : { credential: stringArg(init.credential, "A credential") }
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
var optionalString = (value, what) => value === void 0 || value === null ? void 0 : stringArg(value, what);
|
|
132
|
+
/** A limit's numbers are checked where it is counted (`countRate`); here only that they are what they claim. */
|
|
133
|
+
var rateLimitOf = (value) => {
|
|
134
|
+
const limit = recordArg(value, "A rate limit");
|
|
135
|
+
return {
|
|
136
|
+
most: numberArg(limit.most, "most"),
|
|
137
|
+
perSeconds: numberArg(limit.perSeconds, "perSeconds"),
|
|
138
|
+
...limit.per === "everyone" || limit.per === "caller" ? { per: limit.per } : {}
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* A call as the code sent it, read into what it claims to be — every field checked, because it was written by the
|
|
143
|
+
* space's code and carried by a runner, and neither is trusted here. The only place a call is read.
|
|
144
|
+
*/
|
|
145
|
+
var readCall = (value) => {
|
|
146
|
+
const call = recordArg(value, "A call");
|
|
147
|
+
switch (call.op) {
|
|
148
|
+
case "kv": return {
|
|
149
|
+
op: "kv",
|
|
150
|
+
method: stringArg(call.method, "A kv method"),
|
|
151
|
+
args: Array.isArray(call.args) ? call.args : []
|
|
152
|
+
};
|
|
153
|
+
case "fetch": return {
|
|
154
|
+
op: "fetch",
|
|
155
|
+
url: stringArg(call.url, "A URL"),
|
|
156
|
+
init: fetchInitOf(call.init ?? {})
|
|
157
|
+
};
|
|
158
|
+
case "publish": return {
|
|
159
|
+
op: "publish",
|
|
160
|
+
topic: stringArg(call.topic, "A topic"),
|
|
161
|
+
type: stringArg(call.type, "A type"),
|
|
162
|
+
data: call.data
|
|
163
|
+
};
|
|
164
|
+
case "grant": {
|
|
165
|
+
const ttlSeconds = optionalNumber(call.ttlSeconds, "A lifetime");
|
|
166
|
+
return {
|
|
167
|
+
op: "grant",
|
|
168
|
+
topic: stringArg(call.topic, "A topic"),
|
|
169
|
+
...ttlSeconds === void 0 ? {} : { ttlSeconds }
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
case "revoke": {
|
|
173
|
+
const grant = optionalString(call.grant, "A grant");
|
|
174
|
+
return {
|
|
175
|
+
op: "revoke",
|
|
176
|
+
topic: stringArg(call.topic, "A topic"),
|
|
177
|
+
...grant === void 0 ? {} : { grant }
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
case "rateLimit": return {
|
|
181
|
+
op: "rateLimit",
|
|
182
|
+
bucket: stringArg(call.bucket, "A bucket"),
|
|
183
|
+
limit: rateLimitOf(call.limit)
|
|
184
|
+
};
|
|
185
|
+
case "sign": return {
|
|
186
|
+
op: "sign",
|
|
187
|
+
value: stringArg(call.value, "A value")
|
|
188
|
+
};
|
|
189
|
+
case "verify": return {
|
|
190
|
+
op: "verify",
|
|
191
|
+
value: stringArg(call.value, "A value"),
|
|
192
|
+
signature: stringArg(call.signature, "A signature")
|
|
193
|
+
};
|
|
194
|
+
case "log": return {
|
|
195
|
+
op: "log",
|
|
196
|
+
values: Array.isArray(call.values) ? call.values : []
|
|
197
|
+
};
|
|
198
|
+
case "emit": return {
|
|
199
|
+
op: "emit",
|
|
200
|
+
chunk: call.chunk
|
|
201
|
+
};
|
|
202
|
+
default: throw new CallError("Not a call the platform answers");
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Answers one call from a space's code with the run's own {@link FunctionContext} — the same object a self-hosted
|
|
207
|
+
* server hands its functions, so the sandbox can do exactly what native code can and not one thing more.
|
|
208
|
+
*/
|
|
209
|
+
var answerCall = async (ctx, call) => {
|
|
210
|
+
switch (call.op) {
|
|
211
|
+
case "kv": return kvCall(ctx.kv, call.method, call.args);
|
|
212
|
+
case "fetch": return wireResponseOf(await ctx.fetch(call.url, call.init));
|
|
213
|
+
case "publish": return ctx.publish(call.topic, call.type, call.data);
|
|
214
|
+
case "grant": return ctx.grant(call.topic, call.ttlSeconds);
|
|
215
|
+
case "revoke": return ctx.revoke(call.topic, call.grant);
|
|
216
|
+
case "rateLimit": return ctx.rateLimit(call.bucket, call.limit);
|
|
217
|
+
case "sign": return ctx.sign(call.value);
|
|
218
|
+
case "verify": return ctx.verify(call.value, call.signature);
|
|
219
|
+
case "log":
|
|
220
|
+
ctx.log(...call.values);
|
|
221
|
+
return;
|
|
222
|
+
case "emit":
|
|
223
|
+
ctx.emit(call.chunk);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
//#endregion
|
|
228
|
+
export { answerCall, bodyOf, readCall, requestFromWire, responseOf, wireBodyOf, wireRequestOf, wireResponseFrom, wireResponseOf };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { FunctionsDefinition } from './contract';
|
|
2
|
+
import { FunctionLimits, FunctionRunner, FunctionUsage } from './protocol';
|
|
3
|
+
/** One invocation of a space's task, as metering sees it. */
|
|
4
|
+
export type FunctionUsageRecord = {
|
|
5
|
+
spaceId: number;
|
|
6
|
+
task: string;
|
|
7
|
+
ok: boolean;
|
|
8
|
+
usage: FunctionUsage;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A server's functions. `native` is code the deployment trusts, loaded in its own process with the context narrowed to
|
|
12
|
+
* a function's; the spaces' own code — which nobody here reviewed — only ever runs in the sandbox (`runner`).
|
|
13
|
+
*/
|
|
14
|
+
export type FunctionsConfig = {
|
|
15
|
+
native?: FunctionsDefinition[];
|
|
16
|
+
/**
|
|
17
|
+
* Where the spaces' own functions run: `createRemoteRunner` for the runner service, a provider's adapter, or
|
|
18
|
+
* `createIsolateRunner` only where this process holds nothing a space must not reach. Absent, spaces have no
|
|
19
|
+
* functions here — their tasks are not offered and none of their code runs.
|
|
20
|
+
*/
|
|
21
|
+
runner?: FunctionRunner;
|
|
22
|
+
/** The most one invocation may spend on this deployment. A space's plan may ask for less, never more. */
|
|
23
|
+
limits?: Partial<FunctionLimits>;
|
|
24
|
+
/**
|
|
25
|
+
* Asked before each invocation: why this space may not run code right now — its account spent its CPU for the minute,
|
|
26
|
+
* say — or nothing. What keeps one space's loop from slowing everybody else's on the same runner.
|
|
27
|
+
*/
|
|
28
|
+
admit?: (spaceId: number) => Promise<string | undefined>;
|
|
29
|
+
/** Told what each invocation spent, however it ended: what `admit` budgets by, and what a deployment meters. */
|
|
30
|
+
onUsage?: (record: FunctionUsageRecord) => void;
|
|
31
|
+
};
|
|
32
|
+
export declare const DEFAULT_FUNCTION_LIMITS: FunctionLimits;
|
|
33
|
+
/** What one invocation of a space may spend: its own limits, never above the deployment's. */
|
|
34
|
+
export declare const functionLimitsFor: (ceilings?: Partial<FunctionLimits>, space?: Partial<FunctionLimits>) => FunctionLimits;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/modules/functions/config.ts
|
|
2
|
+
var DEFAULT_FUNCTION_LIMITS = {
|
|
3
|
+
cpuMs: 100,
|
|
4
|
+
wallMs: 1e4,
|
|
5
|
+
memoryMb: 64,
|
|
6
|
+
outputBytes: 1e6,
|
|
7
|
+
calls: 100
|
|
8
|
+
};
|
|
9
|
+
/** What one invocation of a space may spend: its own limits, never above the deployment's. */
|
|
10
|
+
var functionLimitsFor = (ceilings = {}, space = {}) => {
|
|
11
|
+
const ceiling = {
|
|
12
|
+
...DEFAULT_FUNCTION_LIMITS,
|
|
13
|
+
...ceilings
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
cpuMs: Math.min(space.cpuMs ?? ceiling.cpuMs, ceiling.cpuMs),
|
|
17
|
+
wallMs: Math.min(space.wallMs ?? ceiling.wallMs, ceiling.wallMs),
|
|
18
|
+
memoryMb: Math.min(space.memoryMb ?? ceiling.memoryMb, ceiling.memoryMb),
|
|
19
|
+
outputBytes: Math.min(space.outputBytes ?? ceiling.outputBytes, ceiling.outputBytes),
|
|
20
|
+
calls: Math.min(space.calls ?? ceiling.calls, ceiling.calls)
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { DEFAULT_FUNCTION_LIMITS, functionLimitsFor };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FunctionContext } from './contract';
|
|
2
|
+
import { ActionTaskContext } from '../actions/types';
|
|
3
|
+
/**
|
|
4
|
+
* Whether `hostname` is one the space declared: exactly, or under a `*.` wildcard (`*.example.com` is every subdomain of
|
|
5
|
+
* it, and not `example.com` itself).
|
|
6
|
+
*/
|
|
7
|
+
export declare const hostAllowed: (hostname: string, hosts: readonly string[]) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* How the values a function logs read as one line — the one formatting of it, for native functions and for the
|
|
10
|
+
* sandbox's, whose guest only makes each value safe to send.
|
|
11
|
+
*/
|
|
12
|
+
export declare const lineOf: (values: readonly unknown[]) => string;
|
|
13
|
+
/**
|
|
14
|
+
* What a space's code is handed, built from the run's own context: the one place a {@link FunctionContext} is made.
|
|
15
|
+
* A self-hosted server hands it to the code directly; the platform answers the sandbox's calls with it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const functionContextFor: (ctx: ActionTaskContext, hosts: readonly string[]) => FunctionContext;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { fetchOutbound } from "../../helpers/outboundGuard.js";
|
|
2
|
+
import { countRate } from "../actions/runtime/rateLimit.js";
|
|
3
|
+
import { processTwigParam } from "@plitzi/sdk-shared/helpers/twigWrapper";
|
|
4
|
+
//#region src/modules/functions/context.ts
|
|
5
|
+
/**
|
|
6
|
+
* Whether `hostname` is one the space declared: exactly, or under a `*.` wildcard (`*.example.com` is every subdomain of
|
|
7
|
+
* it, and not `example.com` itself).
|
|
8
|
+
*/
|
|
9
|
+
var hostAllowed = (hostname, hosts) => {
|
|
10
|
+
const host = hostname.toLowerCase();
|
|
11
|
+
return hosts.some((entry) => {
|
|
12
|
+
const allowed = entry.toLowerCase();
|
|
13
|
+
return allowed.startsWith("*.") ? host.endsWith(allowed.slice(1)) : host === allowed;
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var userOf = ({ id, username, email, verified, permissions, roles }) => ({
|
|
17
|
+
id,
|
|
18
|
+
username,
|
|
19
|
+
email,
|
|
20
|
+
verified,
|
|
21
|
+
permissions,
|
|
22
|
+
roles
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* A function's `fetch`: only to the hosts the space declared, through the platform's outbound guard (the same rule and
|
|
26
|
+
* the same run budget `http.request` goes through), and — naming a credential — with its values written in where the
|
|
27
|
+
* request says `{{credential.<key>}}`. The value is resolved here and never handed to the code.
|
|
28
|
+
*/
|
|
29
|
+
var functionFetch = (ctx, hosts) => async (input, init = {}) => {
|
|
30
|
+
const credential = init.credential ? await ctx.credential(init.credential) : void 0;
|
|
31
|
+
if (init.credential && !credential) throw new Error(`Credential "${init.credential}" is not available for this space`);
|
|
32
|
+
const render = (value) => credential ? String(processTwigParam(value, { credential })) : value;
|
|
33
|
+
let url;
|
|
34
|
+
try {
|
|
35
|
+
url = new URL(render(typeof input === "string" ? input : input.href));
|
|
36
|
+
} catch {
|
|
37
|
+
throw new Error("A function fetches an absolute URL");
|
|
38
|
+
}
|
|
39
|
+
if (!hostAllowed(url.hostname, hosts)) throw new Error(`"${url.hostname}" is not among the hosts this space's functions may reach (allow.hosts)`);
|
|
40
|
+
const headers = Object.fromEntries(Object.entries(init.headers ?? {}).map(([name, value]) => [name, render(value)]));
|
|
41
|
+
return fetchOutbound(ctx.fetch, url, {
|
|
42
|
+
method: init.method ?? "GET",
|
|
43
|
+
headers,
|
|
44
|
+
body: init.body === void 0 ? void 0 : render(init.body),
|
|
45
|
+
signal: ctx.signal
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* How the values a function logs read as one line — the one formatting of it, for native functions and for the
|
|
50
|
+
* sandbox's, whose guest only makes each value safe to send.
|
|
51
|
+
*/
|
|
52
|
+
var lineOf = (values) => values.map((value) => {
|
|
53
|
+
if (typeof value === "string") return value;
|
|
54
|
+
if (value instanceof Error) return `${value.name}: ${value.message}`;
|
|
55
|
+
if (value === void 0 || typeof value === "function" || typeof value === "symbol") return typeof value;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.stringify(value);
|
|
58
|
+
} catch {
|
|
59
|
+
return Object.prototype.toString.call(value);
|
|
60
|
+
}
|
|
61
|
+
}).join(" ");
|
|
62
|
+
var unavailable = (what) => () => Promise.reject(/* @__PURE__ */ new Error(`This server has no realtime channels (${what})`));
|
|
63
|
+
var unsigned = () => Promise.reject(/* @__PURE__ */ new Error("This server signs nothing: its actions were given no signingSecret"));
|
|
64
|
+
/**
|
|
65
|
+
* What a space's code is handed, built from the run's own context: the one place a {@link FunctionContext} is made.
|
|
66
|
+
* A self-hosted server hands it to the code directly; the platform answers the sandbox's calls with it.
|
|
67
|
+
*/
|
|
68
|
+
var functionContextFor = (ctx, hosts) => ({
|
|
69
|
+
spaceId: ctx.spaceId,
|
|
70
|
+
environment: ctx.environment,
|
|
71
|
+
runId: ctx.runId,
|
|
72
|
+
trigger: ctx.trigger,
|
|
73
|
+
callerId: ctx.callerId,
|
|
74
|
+
...ctx.user ? { user: userOf(ctx.user) } : {},
|
|
75
|
+
kv: ctx.kv,
|
|
76
|
+
rateLimit: (bucket, limit) => countRate(ctx.kv, ctx.callerId, bucket, limit),
|
|
77
|
+
sign: ctx.sign ?? unsigned,
|
|
78
|
+
verify: ctx.verify ?? unsigned,
|
|
79
|
+
fetch: functionFetch(ctx, hosts),
|
|
80
|
+
publish: ctx.publish ?? unavailable("publish"),
|
|
81
|
+
grant: ctx.grant ?? unavailable("grant"),
|
|
82
|
+
revoke: ctx.revoke ?? unavailable("revoke"),
|
|
83
|
+
log: (...values) => ctx.log(lineOf(values)),
|
|
84
|
+
emit: ctx.emit,
|
|
85
|
+
signal: ctx.signal
|
|
86
|
+
});
|
|
87
|
+
//#endregion
|
|
88
|
+
export { functionContextFor, hostAllowed, lineOf };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { RateCount, RateLimit } from '../actions/runtime/rateLimit';
|
|
2
|
+
import { ActionKvStore } from '../actions/types';
|
|
3
|
+
import { Environment, InteractionCallbackParam } from '@plitzi/sdk-shared';
|
|
4
|
+
/**
|
|
5
|
+
* Who started the run, as a space's own code may know them: everything a function decides by — who, their roles and
|
|
6
|
+
* permissions — and never their session. A token inside the sandbox is a token a function could send anywhere.
|
|
7
|
+
*/
|
|
8
|
+
export type FunctionUser = {
|
|
9
|
+
id: number;
|
|
10
|
+
username: string;
|
|
11
|
+
email: string;
|
|
12
|
+
verified: boolean;
|
|
13
|
+
permissions: string[];
|
|
14
|
+
roles: string[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* A request a function makes: `fetch`'s own init, and `credential` — the id of one of the space's credentials, whose
|
|
18
|
+
* values the platform writes into the request where it says `{{credential.<key>}}` (in the URL, a header or the
|
|
19
|
+
* body). The code never holds the value; it names it.
|
|
20
|
+
*/
|
|
21
|
+
export type FunctionFetchInit = {
|
|
22
|
+
method?: string;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
body?: string;
|
|
25
|
+
credential?: string;
|
|
26
|
+
};
|
|
27
|
+
/** What a function's `fetch` answers: a web `Response`, whichever runner it is in. */
|
|
28
|
+
export type FunctionFetch = (url: string | URL, init?: FunctionFetchInit) => Promise<Response>;
|
|
29
|
+
/**
|
|
30
|
+
* Everything a space's code can do besides computing — one object, the same wherever it runs. In a self-hosted server
|
|
31
|
+
* it is built from the run's own context; in the sandbox each method is a call back to the platform, which scopes it
|
|
32
|
+
* to the space and the run before doing it.
|
|
33
|
+
*/
|
|
34
|
+
export type FunctionContext = {
|
|
35
|
+
spaceId: number;
|
|
36
|
+
environment: Environment;
|
|
37
|
+
runId: string;
|
|
38
|
+
/** How the run was started: `call`, `render`, `schedule`, `webhook`… */
|
|
39
|
+
trigger: string;
|
|
40
|
+
/** `user:<id>` for a session, `ip:<address>` for everyone else — what a limit per person keys on. */
|
|
41
|
+
callerId: string;
|
|
42
|
+
user?: FunctionUser;
|
|
43
|
+
/**
|
|
44
|
+
* The space's own key/value store: `get`, `set`, `swap`, `increment`, lists — and `change`, to read, change and write
|
|
45
|
+
* a value back without undoing a writer that got there first.
|
|
46
|
+
*/
|
|
47
|
+
kv: ActionKvStore;
|
|
48
|
+
/**
|
|
49
|
+
* Counts one more of `bucket` — per caller unless `per: 'everyone'` — and says whether it was within `most` in every
|
|
50
|
+
* `perSeconds`. The same count as a flow's `flow.rateLimit` step on that bucket. What to answer when it is not is the
|
|
51
|
+
* code's.
|
|
52
|
+
*/
|
|
53
|
+
rateLimit: (bucket: string, limit: RateLimit) => Promise<RateCount>;
|
|
54
|
+
/**
|
|
55
|
+
* Signs `value` with a key of the space's own that the platform keeps and the code never sees: HMAC-SHA-256,
|
|
56
|
+
* base64url. What a link, an invitation or a key handed to a page is made of. Refused on a server with no signing
|
|
57
|
+
* secret.
|
|
58
|
+
*/
|
|
59
|
+
sign: (value: string) => Promise<string>;
|
|
60
|
+
/** Whether `signature` is what `sign` answered for `value`, compared in constant time. */
|
|
61
|
+
verify: (value: string, signature: string) => Promise<boolean>;
|
|
62
|
+
/** To the hosts the space declared (`allow.hosts`) — and, with `credential`, with one of its secrets written in. */
|
|
63
|
+
fetch: FunctionFetch;
|
|
64
|
+
/** Says something on one of the space's realtime channels, as the server. */
|
|
65
|
+
publish: (topic: string, type: string, data: unknown) => Promise<void>;
|
|
66
|
+
/** Lets the visitor into one topic of a private (`grant: true`) channel. */
|
|
67
|
+
grant: (topic: string, ttlSeconds?: number) => Promise<string>;
|
|
68
|
+
/** Takes a grant back — or every grant for the topic, naming none. */
|
|
69
|
+
revoke: (topic: string, grant?: string) => Promise<void>;
|
|
70
|
+
/** A line for whoever reads the run: the builder's Try, and the deployment's logs. */
|
|
71
|
+
log: (...values: unknown[]) => void;
|
|
72
|
+
/** Progress for a caller that asked for a stream — a no-op when nobody did. */
|
|
73
|
+
emit: (chunk: unknown) => void;
|
|
74
|
+
/** Aborted when the run is: cancelled, or out of time. Hand it to what can stop early. */
|
|
75
|
+
signal: AbortSignal;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* One step a server action can take, written by the space. The shape of the platform's own tasks — addressed as
|
|
79
|
+
* `<namespace>.<action>`, drawn in the builder from `params` — run with a {@link FunctionContext} rather than the
|
|
80
|
+
* platform's full one.
|
|
81
|
+
*/
|
|
82
|
+
export type FunctionTask<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
83
|
+
namespace: string;
|
|
84
|
+
action: string;
|
|
85
|
+
title: string;
|
|
86
|
+
description?: string;
|
|
87
|
+
params: Record<keyof T, InteractionCallbackParam<T>>;
|
|
88
|
+
run: (params: T, ctx: FunctionContext) => unknown;
|
|
89
|
+
};
|
|
90
|
+
/** What a route's handler gets besides the request: the context, and the `:params` its key named, by name. */
|
|
91
|
+
export type FunctionRouteContext = FunctionContext & {
|
|
92
|
+
params: Record<string, string>;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* An HTTP endpoint of the space, under `/api/`: a web-standard handler, the shape every edge runtime runs. The request
|
|
96
|
+
* carries no credentials — `ctx.user` says who is asking — and the response sets no cookies: the host's are the
|
|
97
|
+
* platform's.
|
|
98
|
+
*/
|
|
99
|
+
export type FunctionRoute = (request: Request, ctx: FunctionRouteContext) => Response | Promise<Response>;
|
|
100
|
+
/** What a space's `functions/index.ts` exports: its tasks, its routes, and where its code may reach. */
|
|
101
|
+
export type FunctionsDefinition = {
|
|
102
|
+
/** The hosts `ctx.fetch` may reach. Anything else is refused before it leaves. */
|
|
103
|
+
allow?: {
|
|
104
|
+
hosts?: string[];
|
|
105
|
+
};
|
|
106
|
+
tasks?: FunctionTask<never>[];
|
|
107
|
+
/** `'GET /board-assets/:board/:asset'` → a handler, served at `/api/board-assets/…`. */
|
|
108
|
+
routes?: Record<string, FunctionRoute>;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* What a function throws to refuse, with a reason written for whoever asked — a wrong password, a board that is
|
|
112
|
+
* read-only. Its message reaches the page as the step's error (`{{ step.error }}`), and a route answers it with a 400;
|
|
113
|
+
* anything else a function throws stays in the run's record, since an error can carry what a visitor must not read.
|
|
114
|
+
* The platform's own class: the same natively and, printed into the bundle, in the sandbox.
|
|
115
|
+
*/
|
|
116
|
+
export { ActionRefusal } from '../actions/runtime/errors';
|
|
117
|
+
/**
|
|
118
|
+
* Declares a space's functions. An identity at run time: it is here for the types, and so the same file is valid
|
|
119
|
+
* wherever it is loaded — natively in a self-hosted server, or built by the platform and run in its sandbox.
|
|
120
|
+
*/
|
|
121
|
+
export declare const defineFunctions: (definition: FunctionsDefinition) => FunctionsDefinition;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ActionRefusal } from "../actions/runtime/errors.js";
|
|
2
|
+
//#region src/modules/functions/contract.ts
|
|
3
|
+
/**
|
|
4
|
+
* Declares a space's functions. An identity at run time: it is here for the types, and so the same file is valid
|
|
5
|
+
* wherever it is loaded — natively in a self-hosted server, or built by the platform and run in its sandbox.
|
|
6
|
+
*/
|
|
7
|
+
var defineFunctions = (definition) => definition;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { ActionRefusal, defineFunctions };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { changeKv } from '../actions/runtime/kvChange';
|
|
2
|
+
/**
|
|
3
|
+
* What a driver needs of where it runs: the way to the platform, and that place's own `Request` and `Response`.
|
|
4
|
+
*/
|
|
5
|
+
export type FunctionsDriverEnvironment = {
|
|
6
|
+
/** A call to the platform (a `FunctionCall`): its value, or it throws what the platform said. */
|
|
7
|
+
call: (message: Record<string, unknown>) => Promise<unknown>;
|
|
8
|
+
/** Aborted when the invocation is: `ctx.signal`, this environment's own `AbortSignal`, handed through untouched. */
|
|
9
|
+
signal: unknown;
|
|
10
|
+
/** What `ctx.fetch` was answered, as this environment's `Response`. */
|
|
11
|
+
responseOf: (wire: Record<string, unknown>) => unknown;
|
|
12
|
+
/** A route's request, as this environment's `Request`. */
|
|
13
|
+
requestOf: (wire: Record<string, unknown>) => unknown;
|
|
14
|
+
/** What a route answered, as the wire carries it — `undefined` when it is not this environment's `Response`. */
|
|
15
|
+
wireOfResponse: (response: unknown) => Promise<Record<string, unknown> | undefined>;
|
|
16
|
+
changeKv: typeof changeKv;
|
|
17
|
+
/** The error a refusal is: what the code throws to tell whoever asked, not a fault. */
|
|
18
|
+
refusal: (message: string) => Error;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* What a definition declares, as the platform reads a manifest (`readManifest`) — nothing of it trusted there. Printed
|
|
22
|
+
* into the sandbox's guest and imported by a space runtime, like the driver below: self-contained for that.
|
|
23
|
+
*/
|
|
24
|
+
export declare const describeFunctions: (definition: unknown) => Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* A space's functions, driven: one invocation of a definition — a task or a route, with a `ctx` whose every method is
|
|
27
|
+
* a call to the platform.
|
|
28
|
+
*
|
|
29
|
+
* The one driver there is. The sandbox's guest is handed this function's SOURCE and runs it inside the isolate
|
|
30
|
+
* (`isolate.ts`), and a space runtime imports it and runs it in its own process — so code behaves the same in both, down
|
|
31
|
+
* to `ctx`. Self-contained by contract for that: no import, nothing of this module's, only what `environment` brings.
|
|
32
|
+
*/
|
|
33
|
+
export declare const createFunctionsDriver: (environment: FunctionsDriverEnvironment) => {
|
|
34
|
+
invoke: (definition: unknown, invocation: Record<string, unknown>) => Promise<unknown>;
|
|
35
|
+
log: (...values: unknown[]) => void;
|
|
36
|
+
};
|