@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,660 @@
|
|
|
1
|
+
//#region src/modules/functions/sandbox/guest.ts
|
|
2
|
+
/**
|
|
3
|
+
* The runtime a space's code finds in an isolate: the web APIs a bare V8 lacks (`Headers`, `Request`, `Response`,
|
|
4
|
+
* `TextEncoder`, `AbortSignal`, timers, `crypto`) and the `ctx` its functions are handed.
|
|
5
|
+
*
|
|
6
|
+
* It is installed by PRINTING this function into the isolate (`installGuest.toString()`), after the prelude that brings
|
|
7
|
+
* `URL`, `atob`, `structuredClone` and `DOMException`. So it may use nothing from outside its own body — no import,
|
|
8
|
+
* no helper beside it — and all it has of the runner is `host`, and the functions in `shared`. Streams are not
|
|
9
|
+
* provided: a body is text or bytes.
|
|
10
|
+
*/
|
|
11
|
+
var installGuest = (scope, host, shared) => {
|
|
12
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
/**
|
|
14
|
+
* A refusal: what the code threw to tell whoever asked — a wrong password, a full board — rather than a fault. It is
|
|
15
|
+
* `ActionRefusal` from `@plitzi/sdk-server/functions`, known here by its name: the bundle carries its own copy of the
|
|
16
|
+
* class, printed from the platform's (`build.ts`).
|
|
17
|
+
*/
|
|
18
|
+
const REFUSAL = "ActionRefusal";
|
|
19
|
+
const isRefusal = (error) => error instanceof Error && error.name === REFUSAL;
|
|
20
|
+
const refusal = (message) => Object.assign(new Error(message), { name: REFUSAL });
|
|
21
|
+
const encodeUtf8 = (text) => {
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const char of text) {
|
|
24
|
+
let code = char.codePointAt(0) ?? 0;
|
|
25
|
+
if (code >= 55296 && code <= 57343) code = 65533;
|
|
26
|
+
if (code < 128) out.push(code);
|
|
27
|
+
else if (code < 2048) out.push(192 | code >> 6, 128 | code & 63);
|
|
28
|
+
else if (code < 65536) out.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
|
29
|
+
else out.push(240 | code >> 18, 128 | code >> 12 & 63, 128 | code >> 6 & 63, 128 | code & 63);
|
|
30
|
+
}
|
|
31
|
+
return new Uint8Array(out);
|
|
32
|
+
};
|
|
33
|
+
/** UTF-8, with U+FFFD for every byte that does not belong to a well-formed sequence — what a web `TextDecoder` does. */
|
|
34
|
+
const decodeUtf8 = (bytes) => {
|
|
35
|
+
const points = [];
|
|
36
|
+
let text = "";
|
|
37
|
+
let index = 0;
|
|
38
|
+
const continuation = (at) => at < bytes.length && ((bytes[at] ?? 0) & 192) === 128;
|
|
39
|
+
while (index < bytes.length) {
|
|
40
|
+
const lead = bytes[index] ?? 0;
|
|
41
|
+
const length = lead < 128 ? 1 : lead >= 194 && lead < 224 ? 2 : lead >= 224 && lead < 240 ? 3 : lead >= 240 && lead < 245 ? 4 : 0;
|
|
42
|
+
let code = 65533;
|
|
43
|
+
let used = 1;
|
|
44
|
+
if (length === 1) code = lead;
|
|
45
|
+
else if (length > 1) {
|
|
46
|
+
let valid = true;
|
|
47
|
+
let value = lead & 255 >> length + 1;
|
|
48
|
+
for (let offset = 1; offset < length; offset++) {
|
|
49
|
+
if (!continuation(index + offset)) {
|
|
50
|
+
valid = false;
|
|
51
|
+
used = offset;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
value = value << 6 | (bytes[index + offset] ?? 0) & 63;
|
|
55
|
+
}
|
|
56
|
+
if (valid && value >= (length === 3 ? 2048 : length === 4 ? 65536 : 128) && value <= 1114111 && (value < 55296 || value > 57343)) {
|
|
57
|
+
code = value;
|
|
58
|
+
used = length;
|
|
59
|
+
} else if (valid) used = 1;
|
|
60
|
+
}
|
|
61
|
+
points.push(code);
|
|
62
|
+
index += used;
|
|
63
|
+
if (points.length >= 4096) {
|
|
64
|
+
text += String.fromCodePoint(...points);
|
|
65
|
+
points.length = 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return text + String.fromCodePoint(...points);
|
|
69
|
+
};
|
|
70
|
+
const bytesToBase64 = (bytes) => {
|
|
71
|
+
let binary = "";
|
|
72
|
+
for (let index = 0; index < bytes.length; index += 32768) binary += String.fromCharCode(...bytes.subarray(index, index + 32768));
|
|
73
|
+
return btoa(binary);
|
|
74
|
+
};
|
|
75
|
+
const base64ToBytes = (base64) => {
|
|
76
|
+
const binary = atob(base64);
|
|
77
|
+
const bytes = new Uint8Array(binary.length);
|
|
78
|
+
for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
|
|
79
|
+
return bytes;
|
|
80
|
+
};
|
|
81
|
+
const copyBytes = (view) => {
|
|
82
|
+
const source = view instanceof ArrayBuffer ? new Uint8Array(view) : new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
83
|
+
const bytes = new Uint8Array(source.length);
|
|
84
|
+
bytes.set(source);
|
|
85
|
+
return bytes;
|
|
86
|
+
};
|
|
87
|
+
class GuestTextEncoder {
|
|
88
|
+
encoding = "utf-8";
|
|
89
|
+
encode(text = "") {
|
|
90
|
+
return encodeUtf8(typeof text === "string" ? text : String(text));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
class GuestTextDecoder {
|
|
94
|
+
encoding = "utf-8";
|
|
95
|
+
constructor(label = "utf-8") {
|
|
96
|
+
if (![
|
|
97
|
+
"utf-8",
|
|
98
|
+
"utf8",
|
|
99
|
+
"unicode-1-1-utf-8"
|
|
100
|
+
].includes(label.toLowerCase())) throw new RangeError(`This runtime decodes UTF-8 only, not "${label}"`);
|
|
101
|
+
}
|
|
102
|
+
decode(input) {
|
|
103
|
+
if (!input) return "";
|
|
104
|
+
const text = decodeUtf8(copyBytes(input));
|
|
105
|
+
return text.startsWith("") ? text.slice(1) : text;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
class GuestEvent {
|
|
109
|
+
type;
|
|
110
|
+
constructor(type) {
|
|
111
|
+
this.type = type;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
class GuestEventTarget {
|
|
115
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
116
|
+
addEventListener(type, listener, options) {
|
|
117
|
+
if (!listener) return;
|
|
118
|
+
const listeners = this.#listeners.get(type) ?? /* @__PURE__ */ new Map();
|
|
119
|
+
listeners.set(listener, { once: typeof options === "object" && options.once === true });
|
|
120
|
+
this.#listeners.set(type, listeners);
|
|
121
|
+
}
|
|
122
|
+
removeEventListener(type, listener) {
|
|
123
|
+
if (listener) this.#listeners.get(type)?.delete(listener);
|
|
124
|
+
}
|
|
125
|
+
dispatchEvent(event) {
|
|
126
|
+
const listeners = this.#listeners.get(event.type);
|
|
127
|
+
[...listeners ?? /* @__PURE__ */ new Map()].forEach(([listener, { once }]) => {
|
|
128
|
+
if (once) listeners?.delete(listener);
|
|
129
|
+
listener.call(this, event);
|
|
130
|
+
});
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const signals = /* @__PURE__ */ new WeakMap();
|
|
135
|
+
const CONSTRUCT = Symbol("AbortSignal");
|
|
136
|
+
class GuestAbortSignal extends GuestEventTarget {
|
|
137
|
+
onabort = null;
|
|
138
|
+
constructor(token) {
|
|
139
|
+
super();
|
|
140
|
+
if (token !== CONSTRUCT) throw new TypeError("Illegal constructor: an AbortSignal comes from an AbortController");
|
|
141
|
+
signals.set(this, {
|
|
142
|
+
aborted: false,
|
|
143
|
+
reason: void 0
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
get aborted() {
|
|
147
|
+
return signals.get(this)?.aborted ?? false;
|
|
148
|
+
}
|
|
149
|
+
get reason() {
|
|
150
|
+
return signals.get(this)?.reason;
|
|
151
|
+
}
|
|
152
|
+
throwIfAborted() {
|
|
153
|
+
if (this.aborted) throw this.reason;
|
|
154
|
+
}
|
|
155
|
+
static abort(reason) {
|
|
156
|
+
const signal = new GuestAbortSignal(CONSTRUCT);
|
|
157
|
+
abortSignal(signal, reason);
|
|
158
|
+
return signal;
|
|
159
|
+
}
|
|
160
|
+
static timeout(ms) {
|
|
161
|
+
const signal = new GuestAbortSignal(CONSTRUCT);
|
|
162
|
+
guestSetTimeout(() => {
|
|
163
|
+
abortSignal(signal, new DOMException("The operation timed out.", "TimeoutError"));
|
|
164
|
+
}, ms);
|
|
165
|
+
return signal;
|
|
166
|
+
}
|
|
167
|
+
static any(sources) {
|
|
168
|
+
const signal = new GuestAbortSignal(CONSTRUCT);
|
|
169
|
+
const aborted = sources.find((source) => source.aborted);
|
|
170
|
+
if (aborted) {
|
|
171
|
+
abortSignal(signal, aborted.reason);
|
|
172
|
+
return signal;
|
|
173
|
+
}
|
|
174
|
+
sources.forEach((source) => {
|
|
175
|
+
source.addEventListener("abort", () => abortSignal(signal, source.reason), { once: true });
|
|
176
|
+
});
|
|
177
|
+
return signal;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const abortSignal = (signal, reason) => {
|
|
181
|
+
const state = signals.get(signal);
|
|
182
|
+
if (!state || state.aborted) return;
|
|
183
|
+
state.aborted = true;
|
|
184
|
+
state.reason = reason === void 0 ? new DOMException("This operation was aborted", "AbortError") : reason;
|
|
185
|
+
const event = new GuestEvent("abort");
|
|
186
|
+
signal.onabort?.call(signal, event);
|
|
187
|
+
signal.dispatchEvent(event);
|
|
188
|
+
};
|
|
189
|
+
class GuestAbortController {
|
|
190
|
+
signal = new GuestAbortSignal(CONSTRUCT);
|
|
191
|
+
abort(reason) {
|
|
192
|
+
abortSignal(this.signal, reason);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const timers = /* @__PURE__ */ new Set();
|
|
196
|
+
let nextTimer = 1;
|
|
197
|
+
const delayOf = (ms) => Math.max(0, Number(ms) || 0);
|
|
198
|
+
const guestSetTimeout = (callback, ms, ...args) => {
|
|
199
|
+
const id = nextTimer++;
|
|
200
|
+
timers.add(id);
|
|
201
|
+
host.sleep(delayOf(ms)).then(() => {
|
|
202
|
+
if (timers.delete(id)) callback(...args);
|
|
203
|
+
});
|
|
204
|
+
return id;
|
|
205
|
+
};
|
|
206
|
+
const guestSetInterval = (callback, ms, ...args) => {
|
|
207
|
+
const id = nextTimer++;
|
|
208
|
+
timers.add(id);
|
|
209
|
+
const tick = () => {
|
|
210
|
+
host.sleep(Math.max(1, delayOf(ms))).then(() => {
|
|
211
|
+
if (timers.has(id)) {
|
|
212
|
+
callback(...args);
|
|
213
|
+
tick();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
tick();
|
|
218
|
+
return id;
|
|
219
|
+
};
|
|
220
|
+
const clearTimer = (id) => {
|
|
221
|
+
if (id !== void 0) timers.delete(id);
|
|
222
|
+
};
|
|
223
|
+
const TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
224
|
+
class GuestHeaders {
|
|
225
|
+
#values = /* @__PURE__ */ new Map();
|
|
226
|
+
constructor(init) {
|
|
227
|
+
if (init instanceof GuestHeaders) init.forEach((value, name) => this.append(name, value));
|
|
228
|
+
else if (Array.isArray(init)) init.forEach(([name, value]) => this.append(name, value));
|
|
229
|
+
else if (init) Object.entries(init).forEach(([name, value]) => this.append(name, value));
|
|
230
|
+
}
|
|
231
|
+
#name(name) {
|
|
232
|
+
if (!TOKEN.test(name)) throw new TypeError(`"${name}" is not a valid header name`);
|
|
233
|
+
return name.toLowerCase();
|
|
234
|
+
}
|
|
235
|
+
append(name, value) {
|
|
236
|
+
const key = this.#name(name);
|
|
237
|
+
this.#values.set(key, [...this.#values.get(key) ?? [], String(value).trim()]);
|
|
238
|
+
}
|
|
239
|
+
set(name, value) {
|
|
240
|
+
this.#values.set(this.#name(name), [String(value).trim()]);
|
|
241
|
+
}
|
|
242
|
+
get(name) {
|
|
243
|
+
return this.#values.get(this.#name(name))?.join(", ") ?? null;
|
|
244
|
+
}
|
|
245
|
+
getSetCookie() {
|
|
246
|
+
return [...this.#values.get("set-cookie") ?? []];
|
|
247
|
+
}
|
|
248
|
+
has(name) {
|
|
249
|
+
return this.#values.has(this.#name(name));
|
|
250
|
+
}
|
|
251
|
+
delete(name) {
|
|
252
|
+
this.#values.delete(this.#name(name));
|
|
253
|
+
}
|
|
254
|
+
*entries() {
|
|
255
|
+
for (const name of [...this.#values.keys()].sort()) if (name === "set-cookie") for (const value of this.#values.get(name) ?? []) yield [name, value];
|
|
256
|
+
else yield [name, this.#values.get(name)?.join(", ") ?? ""];
|
|
257
|
+
}
|
|
258
|
+
*keys() {
|
|
259
|
+
for (const [name] of this.entries()) yield name;
|
|
260
|
+
}
|
|
261
|
+
*values() {
|
|
262
|
+
for (const [, value] of this.entries()) yield value;
|
|
263
|
+
}
|
|
264
|
+
forEach(callback) {
|
|
265
|
+
for (const [name, value] of this.entries()) callback(value, name, this);
|
|
266
|
+
}
|
|
267
|
+
[Symbol.iterator]() {
|
|
268
|
+
return this.entries();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const sourceOf = (body, headers) => {
|
|
272
|
+
const typed = (type) => {
|
|
273
|
+
if (!headers.has("content-type")) headers.set("content-type", type);
|
|
274
|
+
};
|
|
275
|
+
if (body === null || body === void 0) return null;
|
|
276
|
+
if (typeof body === "string") {
|
|
277
|
+
typed("text/plain;charset=UTF-8");
|
|
278
|
+
return { text: body };
|
|
279
|
+
}
|
|
280
|
+
if (body instanceof URLSearchParams) {
|
|
281
|
+
typed("application/x-www-form-urlencoded;charset=UTF-8");
|
|
282
|
+
return { text: body.toString() };
|
|
283
|
+
}
|
|
284
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return { bytes: copyBytes(body) };
|
|
285
|
+
throw new TypeError("A body is a string, bytes or URLSearchParams — this runtime has no streams, Blob or FormData");
|
|
286
|
+
};
|
|
287
|
+
/** Each body's source and whether it was read, kept out of the objects the code holds. */
|
|
288
|
+
const bodies = /* @__PURE__ */ new WeakMap();
|
|
289
|
+
const take = (body) => {
|
|
290
|
+
const state = bodies.get(body);
|
|
291
|
+
if (!state) return null;
|
|
292
|
+
if (state.used) throw new TypeError("Body has already been read");
|
|
293
|
+
state.used = state.source !== null;
|
|
294
|
+
return state.source;
|
|
295
|
+
};
|
|
296
|
+
/** A body as it was written, to copy it: text stays text and bytes stay bytes. */
|
|
297
|
+
const peek = (body) => {
|
|
298
|
+
const source = bodies.get(body)?.source ?? null;
|
|
299
|
+
if (!source) return null;
|
|
300
|
+
return "text" in source ? source.text : source.bytes;
|
|
301
|
+
};
|
|
302
|
+
class GuestBody {
|
|
303
|
+
headers;
|
|
304
|
+
constructor(body, headers) {
|
|
305
|
+
this.headers = new GuestHeaders(headers);
|
|
306
|
+
bodies.set(this, {
|
|
307
|
+
source: sourceOf(body, this.headers),
|
|
308
|
+
used: false
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
get bodyUsed() {
|
|
312
|
+
return bodies.get(this)?.used ?? false;
|
|
313
|
+
}
|
|
314
|
+
get body() {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
text() {
|
|
318
|
+
try {
|
|
319
|
+
const source = take(this);
|
|
320
|
+
if (!source) return Promise.resolve("");
|
|
321
|
+
return Promise.resolve("text" in source ? source.text : decodeUtf8(source.bytes));
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return Promise.reject(error instanceof Error ? error : new TypeError(String(error)));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async json() {
|
|
327
|
+
return JSON.parse(await this.text());
|
|
328
|
+
}
|
|
329
|
+
bytes() {
|
|
330
|
+
try {
|
|
331
|
+
const source = take(this);
|
|
332
|
+
if (!source) return Promise.resolve(/* @__PURE__ */ new Uint8Array(0));
|
|
333
|
+
return Promise.resolve("text" in source ? encodeUtf8(source.text) : copyBytes(source.bytes));
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return Promise.reject(error instanceof Error ? error : new TypeError(String(error)));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async arrayBuffer() {
|
|
339
|
+
return (await this.bytes()).buffer;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const METHODS = [
|
|
343
|
+
"DELETE",
|
|
344
|
+
"GET",
|
|
345
|
+
"HEAD",
|
|
346
|
+
"OPTIONS",
|
|
347
|
+
"POST",
|
|
348
|
+
"PUT",
|
|
349
|
+
"PATCH"
|
|
350
|
+
];
|
|
351
|
+
class GuestRequest extends GuestBody {
|
|
352
|
+
url;
|
|
353
|
+
method;
|
|
354
|
+
signal;
|
|
355
|
+
constructor(input, init = {}) {
|
|
356
|
+
const from = input instanceof GuestRequest ? input : void 0;
|
|
357
|
+
const method = init.method ?? from?.method ?? "GET";
|
|
358
|
+
const upper = method.toUpperCase();
|
|
359
|
+
const normal = METHODS.includes(upper) ? upper : method;
|
|
360
|
+
if ((normal === "GET" || normal === "HEAD") && init.body !== void 0 && init.body !== null) throw new TypeError(`A ${normal} request has no body`);
|
|
361
|
+
super(init.body, init.headers ?? from?.headers);
|
|
362
|
+
this.url = new URL(input instanceof GuestRequest ? input.url : input).href;
|
|
363
|
+
this.method = normal;
|
|
364
|
+
this.signal = init.signal ?? from?.signal ?? new GuestAbortController().signal;
|
|
365
|
+
}
|
|
366
|
+
clone() {
|
|
367
|
+
return new GuestRequest(this.url, {
|
|
368
|
+
method: this.method,
|
|
369
|
+
headers: this.headers,
|
|
370
|
+
body: peek(this),
|
|
371
|
+
signal: this.signal
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
class GuestResponse extends GuestBody {
|
|
376
|
+
status;
|
|
377
|
+
statusText;
|
|
378
|
+
type = "default";
|
|
379
|
+
url = "";
|
|
380
|
+
redirected = false;
|
|
381
|
+
constructor(body, init = {}) {
|
|
382
|
+
super(body, init.headers);
|
|
383
|
+
const status = init.status ?? 200;
|
|
384
|
+
if (!Number.isInteger(status) || status < 200 || status > 599) throw new RangeError(`A response status is 200–599, not ${String(status)}`);
|
|
385
|
+
this.status = status;
|
|
386
|
+
this.statusText = init.statusText ?? "";
|
|
387
|
+
}
|
|
388
|
+
get ok() {
|
|
389
|
+
return this.status >= 200 && this.status < 300;
|
|
390
|
+
}
|
|
391
|
+
clone() {
|
|
392
|
+
return new GuestResponse(peek(this), {
|
|
393
|
+
status: this.status,
|
|
394
|
+
statusText: this.statusText,
|
|
395
|
+
headers: this.headers
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
static json(data, init = {}) {
|
|
399
|
+
const headers = new GuestHeaders(init.headers);
|
|
400
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
401
|
+
return new GuestResponse(JSON.stringify(data), {
|
|
402
|
+
...init,
|
|
403
|
+
headers
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
static redirect(url, status = 302) {
|
|
407
|
+
if (![
|
|
408
|
+
301,
|
|
409
|
+
302,
|
|
410
|
+
303,
|
|
411
|
+
307,
|
|
412
|
+
308
|
|
413
|
+
].includes(status)) throw new RangeError(`${String(status)} is not a redirect status`);
|
|
414
|
+
return new GuestResponse(null, {
|
|
415
|
+
status,
|
|
416
|
+
headers: { location: new URL(String(url)).href }
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/** Text back as text and bytes as base64: what the code wrote crosses unchanged, and nothing is decided from its type. */
|
|
421
|
+
const wireOf = (body) => {
|
|
422
|
+
const source = take(body);
|
|
423
|
+
if (!source) return null;
|
|
424
|
+
return "text" in source ? { text: source.text } : { base64: bytesToBase64(source.bytes) };
|
|
425
|
+
};
|
|
426
|
+
const headerPairs = (value) => Array.isArray(value) ? value.flatMap((pair) => Array.isArray(pair) && typeof pair[0] === "string" && typeof pair[1] === "string" ? [[pair[0], pair[1]]] : []) : [];
|
|
427
|
+
const bodyOfWire = (body) => {
|
|
428
|
+
if (!isRecord(body)) return null;
|
|
429
|
+
if (typeof body.text === "string") return body.text;
|
|
430
|
+
return typeof body.base64 === "string" ? base64ToBytes(body.base64) : null;
|
|
431
|
+
};
|
|
432
|
+
const getRandomValues = (array) => {
|
|
433
|
+
if (array.byteLength > 65536) throw new DOMException("At most 65536 bytes at a time", "QuotaExceededError");
|
|
434
|
+
new Uint8Array(array.buffer, array.byteOffset, array.byteLength).set(host.random(array.byteLength));
|
|
435
|
+
return array;
|
|
436
|
+
};
|
|
437
|
+
const randomUUID = () => {
|
|
438
|
+
const bytes = getRandomValues(/* @__PURE__ */ new Uint8Array(16));
|
|
439
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
440
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
441
|
+
const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
442
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
443
|
+
};
|
|
444
|
+
const HASHES = [
|
|
445
|
+
"SHA-1",
|
|
446
|
+
"SHA-256",
|
|
447
|
+
"SHA-384",
|
|
448
|
+
"SHA-512"
|
|
449
|
+
];
|
|
450
|
+
const nameOf = (algorithm) => {
|
|
451
|
+
const name = typeof algorithm === "string" ? algorithm : isRecord(algorithm) ? algorithm.name : void 0;
|
|
452
|
+
return typeof name === "string" ? name.toUpperCase() : "";
|
|
453
|
+
};
|
|
454
|
+
const hashOf = (algorithm) => {
|
|
455
|
+
const upper = nameOf(algorithm);
|
|
456
|
+
if (!HASHES.includes(upper)) throw new DOMException(`${upper || String(algorithm)} is not a supported hash (${HASHES.join(", ")})`, "NotSupportedError");
|
|
457
|
+
return upper;
|
|
458
|
+
};
|
|
459
|
+
/** An HMAC key's length when its algorithm names none: the hash's block, as Web Crypto has it. */
|
|
460
|
+
const blockBitsOf = (hash) => hash === "SHA-384" || hash === "SHA-512" ? 1024 : 512;
|
|
461
|
+
/**
|
|
462
|
+
* The two kinds of key this runtime's `crypto.subtle` makes: HMAC, what signing a webhook needs, and PBKDF2, a
|
|
463
|
+
* password to derive from — what keeping a password needs.
|
|
464
|
+
*/
|
|
465
|
+
class GuestCryptoKey {
|
|
466
|
+
type = "secret";
|
|
467
|
+
algorithm;
|
|
468
|
+
extractable;
|
|
469
|
+
usages;
|
|
470
|
+
raw;
|
|
471
|
+
constructor(raw, algorithm, extractable, usages) {
|
|
472
|
+
this.raw = raw;
|
|
473
|
+
this.algorithm = algorithm;
|
|
474
|
+
this.extractable = extractable;
|
|
475
|
+
this.usages = usages;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const bytesOf = (data) => {
|
|
479
|
+
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return copyBytes(data);
|
|
480
|
+
throw new TypeError("crypto.subtle takes bytes: an ArrayBuffer or a typed array");
|
|
481
|
+
};
|
|
482
|
+
const cryptoCall = async (message) => {
|
|
483
|
+
const answer = JSON.parse(await host.crypto(JSON.stringify(message)));
|
|
484
|
+
if (!isRecord(answer) || answer.ok !== true || typeof answer.value !== "string") throw new DOMException(isRecord(answer) && typeof answer.error === "string" ? answer.error : "The operation failed", "OperationError");
|
|
485
|
+
return base64ToBytes(answer.value);
|
|
486
|
+
};
|
|
487
|
+
const isKeyOf = (key, name) => key instanceof GuestCryptoKey && key.algorithm.name === name;
|
|
488
|
+
/** The key an operation was handed, when it is of the kind the operation takes and was imported for it. */
|
|
489
|
+
const keyFor = (key, name, usage) => {
|
|
490
|
+
if (!isKeyOf(key, name)) throw new DOMException(`${usage} takes a ${name} key, from crypto.subtle.importKey`, "InvalidAccessError");
|
|
491
|
+
if (!key.usages.includes(usage)) throw new DOMException(`The key was not imported for ${usage}`, "InvalidAccessError");
|
|
492
|
+
return key;
|
|
493
|
+
};
|
|
494
|
+
const hmacOf = (key, usage, data) => {
|
|
495
|
+
const { algorithm, raw } = keyFor(key, "HMAC", usage);
|
|
496
|
+
return cryptoCall({
|
|
497
|
+
op: "hmac",
|
|
498
|
+
hash: algorithm.hash.name,
|
|
499
|
+
key: bytesToBase64(raw),
|
|
500
|
+
data: bytesToBase64(bytesOf(data))
|
|
501
|
+
});
|
|
502
|
+
};
|
|
503
|
+
/** How many rounds and bits a derivation may take is the runner's to say: it is the one doing the work. */
|
|
504
|
+
const pbkdf2Of = (algorithm, baseKey, bits, usage) => {
|
|
505
|
+
if (!isRecord(algorithm) || nameOf(algorithm) !== "PBKDF2") throw new DOMException("This runtime derives with PBKDF2 only", "NotSupportedError");
|
|
506
|
+
const { raw } = keyFor(baseKey, "PBKDF2", usage);
|
|
507
|
+
if (typeof algorithm.iterations !== "number" || typeof bits !== "number") throw new TypeError("PBKDF2 takes { name, hash, salt, iterations } and a length in bits");
|
|
508
|
+
return cryptoCall({
|
|
509
|
+
op: "pbkdf2",
|
|
510
|
+
hash: hashOf(algorithm.hash),
|
|
511
|
+
key: bytesToBase64(raw),
|
|
512
|
+
salt: bytesToBase64(bytesOf(algorithm.salt)),
|
|
513
|
+
iterations: String(algorithm.iterations),
|
|
514
|
+
bits: String(bits)
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
/** The key `importKey` makes — thrown, not answered, when it is none this runtime has. */
|
|
518
|
+
const importedKey = (format, keyData, algorithm, extractable, usages) => {
|
|
519
|
+
const name = nameOf(algorithm);
|
|
520
|
+
if (format === "raw" && name === "HMAC" && isRecord(algorithm)) return new GuestCryptoKey(bytesOf(keyData), {
|
|
521
|
+
name,
|
|
522
|
+
hash: { name: hashOf(algorithm.hash) }
|
|
523
|
+
}, extractable, usages);
|
|
524
|
+
if (format === "raw" && name === "PBKDF2") {
|
|
525
|
+
if (extractable) throw new DOMException("A PBKDF2 key is never extractable", "SyntaxError");
|
|
526
|
+
return new GuestCryptoKey(bytesOf(keyData), { name }, false, usages);
|
|
527
|
+
}
|
|
528
|
+
throw new DOMException("This runtime imports raw keys for HMAC and PBKDF2 only (format \"raw\", { name: \"HMAC\", hash } or \"PBKDF2\")", "NotSupportedError");
|
|
529
|
+
};
|
|
530
|
+
const subtle = {
|
|
531
|
+
digest: async (algorithm, data) => (await cryptoCall({
|
|
532
|
+
op: "digest",
|
|
533
|
+
hash: hashOf(algorithm),
|
|
534
|
+
data: bytesToBase64(bytesOf(data))
|
|
535
|
+
})).buffer,
|
|
536
|
+
importKey: (format, keyData, algorithm, extractable, usages) => new Promise((resolve) => {
|
|
537
|
+
resolve(importedKey(format, keyData, algorithm, extractable, usages));
|
|
538
|
+
}),
|
|
539
|
+
sign: async (_algorithm, key, data) => (await hmacOf(key, "sign", data)).buffer,
|
|
540
|
+
verify: async (_algorithm, key, signature, data) => {
|
|
541
|
+
const expected = await hmacOf(key, "verify", data);
|
|
542
|
+
const given = bytesOf(signature);
|
|
543
|
+
let difference = expected.length ^ given.length;
|
|
544
|
+
expected.forEach((byte, index) => {
|
|
545
|
+
difference |= byte ^ (given[index] ?? 0);
|
|
546
|
+
});
|
|
547
|
+
return difference === 0;
|
|
548
|
+
},
|
|
549
|
+
deriveBits: async (algorithm, baseKey, length) => (await pbkdf2Of(algorithm, baseKey, length, "deriveBits")).buffer,
|
|
550
|
+
/** Derives an HMAC key — the one other kind this runtime has. */
|
|
551
|
+
deriveKey: async (algorithm, baseKey, derivedKeyAlgorithm, extractable, usages) => {
|
|
552
|
+
if (!isRecord(derivedKeyAlgorithm) || nameOf(derivedKeyAlgorithm) !== "HMAC") throw new DOMException("This runtime derives HMAC keys only", "NotSupportedError");
|
|
553
|
+
const hash = hashOf(derivedKeyAlgorithm.hash);
|
|
554
|
+
const length = derivedKeyAlgorithm.length ?? blockBitsOf(hash);
|
|
555
|
+
const raw = await pbkdf2Of(algorithm, baseKey, length, "deriveKey");
|
|
556
|
+
return new GuestCryptoKey(raw, {
|
|
557
|
+
name: "HMAC",
|
|
558
|
+
hash: { name: hash }
|
|
559
|
+
}, extractable, usages);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const call = async (message) => {
|
|
563
|
+
const answer = JSON.parse(await host.call(JSON.stringify(message)));
|
|
564
|
+
if (!isRecord(answer) || answer.ok !== true) throw new Error(isRecord(answer) && typeof answer.error === "string" ? answer.error : "The platform refused the call");
|
|
565
|
+
return answer.value;
|
|
566
|
+
};
|
|
567
|
+
const run = new GuestAbortController();
|
|
568
|
+
const driver = shared.createFunctionsDriver({
|
|
569
|
+
call,
|
|
570
|
+
signal: run.signal,
|
|
571
|
+
responseOf: (wire) => new GuestResponse(bodyOfWire(wire.body), {
|
|
572
|
+
status: typeof wire.status === "number" ? wire.status : 502,
|
|
573
|
+
statusText: typeof wire.statusText === "string" ? wire.statusText : "",
|
|
574
|
+
headers: headerPairs(wire.headers)
|
|
575
|
+
}),
|
|
576
|
+
requestOf: (wire) => new GuestRequest(String(wire.url), {
|
|
577
|
+
method: String(wire.method),
|
|
578
|
+
headers: headerPairs(wire.headers),
|
|
579
|
+
body: wire.method === "GET" || wire.method === "HEAD" ? null : bodyOfWire(wire.body),
|
|
580
|
+
signal: run.signal
|
|
581
|
+
}),
|
|
582
|
+
wireOfResponse: (response) => Promise.resolve(response instanceof GuestResponse ? {
|
|
583
|
+
status: response.status,
|
|
584
|
+
statusText: response.statusText,
|
|
585
|
+
headers: [...response.headers],
|
|
586
|
+
body: wireOf(response)
|
|
587
|
+
} : void 0),
|
|
588
|
+
changeKv: shared.changeKv,
|
|
589
|
+
refusal
|
|
590
|
+
});
|
|
591
|
+
const { log } = driver;
|
|
592
|
+
const refuseFetch = () => Promise.reject(/* @__PURE__ */ new TypeError("Functions reach the network through ctx.fetch, which knows the hosts they may reach"));
|
|
593
|
+
Object.assign(scope, {
|
|
594
|
+
TextEncoder: GuestTextEncoder,
|
|
595
|
+
TextDecoder: GuestTextDecoder,
|
|
596
|
+
Event: GuestEvent,
|
|
597
|
+
EventTarget: GuestEventTarget,
|
|
598
|
+
AbortSignal: GuestAbortSignal,
|
|
599
|
+
AbortController: GuestAbortController,
|
|
600
|
+
Headers: GuestHeaders,
|
|
601
|
+
Request: GuestRequest,
|
|
602
|
+
Response: GuestResponse,
|
|
603
|
+
setTimeout: guestSetTimeout,
|
|
604
|
+
clearTimeout: clearTimer,
|
|
605
|
+
setInterval: guestSetInterval,
|
|
606
|
+
clearInterval: clearTimer,
|
|
607
|
+
queueMicrotask: (callback) => {
|
|
608
|
+
Promise.resolve().then(callback);
|
|
609
|
+
},
|
|
610
|
+
crypto: {
|
|
611
|
+
getRandomValues,
|
|
612
|
+
randomUUID,
|
|
613
|
+
subtle
|
|
614
|
+
},
|
|
615
|
+
console: {
|
|
616
|
+
log,
|
|
617
|
+
info: log,
|
|
618
|
+
warn: log,
|
|
619
|
+
error: log,
|
|
620
|
+
debug: log
|
|
621
|
+
},
|
|
622
|
+
fetch: refuseFetch
|
|
623
|
+
});
|
|
624
|
+
const describe = (definition) => JSON.stringify({
|
|
625
|
+
ok: true,
|
|
626
|
+
value: shared.describeFunctions(definition)
|
|
627
|
+
});
|
|
628
|
+
return {
|
|
629
|
+
describe,
|
|
630
|
+
invoke: async (definition, invocationText) => {
|
|
631
|
+
let answer;
|
|
632
|
+
try {
|
|
633
|
+
const invocation = JSON.parse(invocationText);
|
|
634
|
+
answer = {
|
|
635
|
+
ok: true,
|
|
636
|
+
value: await driver.invoke(definition, isRecord(invocation) ? invocation : {})
|
|
637
|
+
};
|
|
638
|
+
} catch (error) {
|
|
639
|
+
answer = {
|
|
640
|
+
ok: false,
|
|
641
|
+
error: error instanceof Error ? error.message : String(error),
|
|
642
|
+
...isRefusal(error) ? { refused: true } : {}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
try {
|
|
646
|
+
return JSON.stringify(answer);
|
|
647
|
+
} catch (error) {
|
|
648
|
+
return JSON.stringify({
|
|
649
|
+
ok: false,
|
|
650
|
+
error: `What the function answered is not JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
abort: (reason) => {
|
|
655
|
+
run.abort(new DOMException(reason, "AbortError"));
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
//#endregion
|
|
660
|
+
export { installGuest };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FunctionRunner } from '../protocol';
|
|
2
|
+
export type IsolateRunnerOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Invocations running at once; the rest wait their turn. Each isolate may take about twice its `memoryMb`, so this
|
|
5
|
+
* times that times two is what the runner needs.
|
|
6
|
+
*/
|
|
7
|
+
concurrency?: number;
|
|
8
|
+
/**
|
|
9
|
+
* How many bytes of bundles this runner keeps — their code and V8's cache of it — least recently used let go first.
|
|
10
|
+
* A bundle it no longer has is asked of the platform again; one it has is never sent twice.
|
|
11
|
+
*/
|
|
12
|
+
cacheBytes?: number;
|
|
13
|
+
};
|
|
14
|
+
/** The isolates, and what readies them before the first invocation pays for it. */
|
|
15
|
+
export type IsolateRunner = FunctionRunner & {
|
|
16
|
+
/**
|
|
17
|
+
* Builds the prelude, the heap every isolate starts from and V8's cache of the guest, and runs a first bundle — what
|
|
18
|
+
* the first invocation of a fresh process would otherwise spend (~70 ms) on top of its own.
|
|
19
|
+
*/
|
|
20
|
+
warm: () => Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The most a PBKDF2 derivation may ask for. The runner does the work, outside the isolate's clock, so each is bounded
|
|
24
|
+
* here and its time is charged to the run's CPU: a million rounds is past any password guidance, and 1024 bits past
|
|
25
|
+
* any key.
|
|
26
|
+
*/
|
|
27
|
+
export declare const PBKDF2_MAX_ITERATIONS = 1000000;
|
|
28
|
+
export declare const PBKDF2_MAX_BITS = 1024;
|
|
29
|
+
/**
|
|
30
|
+
* The runner as V8 isolates in this process — what the runner service serves, and what a deployment may use directly
|
|
31
|
+
* when the process holds nothing a space's code must not reach (tests, a sandbox of its own).
|
|
32
|
+
*
|
|
33
|
+
* ONE ISOLATE PER INVOCATION: each limit is then exactly that invocation's — its CPU, its memory — and stopping it
|
|
34
|
+
* disposes nothing anybody else was using. No state survives an invocation, by construction. The cost is ~2 ms: the
|
|
35
|
+
* prelude comes from a V8 snapshot, the guest and the bundle from V8's code cache after their first compile.
|
|
36
|
+
*/
|
|
37
|
+
export declare const createIsolateRunner: ({ concurrency, cacheBytes }?: IsolateRunnerOptions) => IsolateRunner;
|