@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,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `kv` tasks' own view: values rather than strings, and a TTL that means what a caller expects.
|
|
3
|
+
*
|
|
4
|
+
* Built by `createKvStore` over an {@link ActionKvAdapter}, and then narrowed to one space by `namespaceKv`. A
|
|
5
|
+
* task never sees an adapter.
|
|
6
|
+
*/
|
|
7
|
+
declare type ActionKvStore = {
|
|
8
|
+
get: (key: string) => Promise<unknown>;
|
|
9
|
+
set: (key: string, value: unknown, ttlSeconds?: number) => Promise<void>;
|
|
10
|
+
delete: (key: string) => Promise<void>;
|
|
11
|
+
increment: (key: string, amount: number, ttlSeconds?: number) => Promise<number>;
|
|
12
|
+
/**
|
|
13
|
+
* Writes `next` only if the key still holds `expected` — the value `get` answered — or, with `expected` undefined,
|
|
14
|
+
* only if it holds nothing. Answers whether it wrote: read, change, and write back with this, and a writer that
|
|
15
|
+
* lost the race is told so instead of undoing the other.
|
|
16
|
+
*/
|
|
17
|
+
swap: (key: string, expected: unknown, next: unknown, ttlSeconds?: number) => Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Puts `entry` in the list — replacing the one with its id — keeping the highest `keep` by score, and answers what
|
|
20
|
+
* that dropped, for whatever it named to be let go of too. A list has no lifetime: it lives until its entries are
|
|
21
|
+
* removed.
|
|
22
|
+
*/
|
|
23
|
+
listPut: (list: string, entry: KvListEntry, options?: KvListPutOptions) => Promise<KvListPut>;
|
|
24
|
+
listRange: (list: string, range?: KvListRange) => Promise<KvListEntry[]>;
|
|
25
|
+
/** Takes the entry with `id` out of the list, and answers whether it was there. */
|
|
26
|
+
listRemove: (list: string, id: string) => Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Reads the key, hands its value to `change` and writes back what that answers — reading and asking again when
|
|
29
|
+
* somebody wrote in between, so no writer undoes another. `change` answering `undefined` writes nothing. Answers what
|
|
30
|
+
* was written. `lifetime` is seconds, or worked out from the value written.
|
|
31
|
+
*/
|
|
32
|
+
change: <T>(key: string, change: (current: unknown) => T | undefined | Promise<T | undefined>, lifetime?: KvChangeLifetime<T>) => Promise<T | undefined>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A step refusing to go on, with a reason written for whoever called — a password too easy to guess, a board that is
|
|
37
|
+
* read-only, a quota reached.
|
|
38
|
+
*
|
|
39
|
+
* What any other step throws stays in the run's record and never reaches the caller: an error can carry a query, a
|
|
40
|
+
* URL, a credential's name, and a page is shown to anybody. This one says, by its type, that its message was written
|
|
41
|
+
* to be read. A run that ends on it answers `status: 'failed'` with the message as `error`, which a page's flow reads
|
|
42
|
+
* as `{{ step.error }}` — the same place a refusal before the run began puts its own.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ActionRefusal extends Error {
|
|
45
|
+
constructor(message: string);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Declares a space's functions. An identity at run time: it is here for the types, and so the same file is valid
|
|
50
|
+
* wherever it is loaded — natively in a self-hosted server, or built by the platform and run in its sandbox.
|
|
51
|
+
*/
|
|
52
|
+
export declare const defineFunctions: (definition: FunctionsDefinition) => FunctionsDefinition;
|
|
53
|
+
|
|
54
|
+
declare type Environment = 'production' | 'staging' | 'development' | 'main';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Everything a space's code can do besides computing — one object, the same wherever it runs. In a self-hosted server
|
|
58
|
+
* it is built from the run's own context; in the sandbox each method is a call back to the platform, which scopes it
|
|
59
|
+
* to the space and the run before doing it.
|
|
60
|
+
*/
|
|
61
|
+
export declare type FunctionContext = {
|
|
62
|
+
spaceId: number;
|
|
63
|
+
environment: Environment;
|
|
64
|
+
runId: string;
|
|
65
|
+
/** How the run was started: `call`, `render`, `schedule`, `webhook`… */
|
|
66
|
+
trigger: string;
|
|
67
|
+
/** `user:<id>` for a session, `ip:<address>` for everyone else — what a limit per person keys on. */
|
|
68
|
+
callerId: string;
|
|
69
|
+
user?: FunctionUser;
|
|
70
|
+
/**
|
|
71
|
+
* The space's own key/value store: `get`, `set`, `swap`, `increment`, lists — and `change`, to read, change and write
|
|
72
|
+
* a value back without undoing a writer that got there first.
|
|
73
|
+
*/
|
|
74
|
+
kv: ActionKvStore;
|
|
75
|
+
/**
|
|
76
|
+
* Counts one more of `bucket` — per caller unless `per: 'everyone'` — and says whether it was within `most` in every
|
|
77
|
+
* `perSeconds`. The same count as a flow's `flow.rateLimit` step on that bucket. What to answer when it is not is the
|
|
78
|
+
* code's.
|
|
79
|
+
*/
|
|
80
|
+
rateLimit: (bucket: string, limit: RateLimit) => Promise<RateCount>;
|
|
81
|
+
/**
|
|
82
|
+
* Signs `value` with a key of the space's own that the platform keeps and the code never sees: HMAC-SHA-256,
|
|
83
|
+
* base64url. What a link, an invitation or a key handed to a page is made of. Refused on a server with no signing
|
|
84
|
+
* secret.
|
|
85
|
+
*/
|
|
86
|
+
sign: (value: string) => Promise<string>;
|
|
87
|
+
/** Whether `signature` is what `sign` answered for `value`, compared in constant time. */
|
|
88
|
+
verify: (value: string, signature: string) => Promise<boolean>;
|
|
89
|
+
/** To the hosts the space declared (`allow.hosts`) — and, with `credential`, with one of its secrets written in. */
|
|
90
|
+
fetch: FunctionFetch;
|
|
91
|
+
/** Says something on one of the space's realtime channels, as the server. */
|
|
92
|
+
publish: (topic: string, type: string, data: unknown) => Promise<void>;
|
|
93
|
+
/** Lets the visitor into one topic of a private (`grant: true`) channel. */
|
|
94
|
+
grant: (topic: string, ttlSeconds?: number) => Promise<string>;
|
|
95
|
+
/** Takes a grant back — or every grant for the topic, naming none. */
|
|
96
|
+
revoke: (topic: string, grant?: string) => Promise<void>;
|
|
97
|
+
/** A line for whoever reads the run: the builder's Try, and the deployment's logs. */
|
|
98
|
+
log: (...values: unknown[]) => void;
|
|
99
|
+
/** Progress for a caller that asked for a stream — a no-op when nobody did. */
|
|
100
|
+
emit: (chunk: unknown) => void;
|
|
101
|
+
/** Aborted when the run is: cancelled, or out of time. Hand it to what can stop early. */
|
|
102
|
+
signal: AbortSignal;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/** What a function's `fetch` answers: a web `Response`, whichever runner it is in. */
|
|
106
|
+
export declare type FunctionFetch = (url: string | URL, init?: FunctionFetchInit) => Promise<Response>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A request a function makes: `fetch`'s own init, and `credential` — the id of one of the space's credentials, whose
|
|
110
|
+
* values the platform writes into the request where it says `{{credential.<key>}}` (in the URL, a header or the
|
|
111
|
+
* body). The code never holds the value; it names it.
|
|
112
|
+
*/
|
|
113
|
+
export declare type FunctionFetchInit = {
|
|
114
|
+
method?: string;
|
|
115
|
+
headers?: Record<string, string>;
|
|
116
|
+
body?: string;
|
|
117
|
+
credential?: string;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* An HTTP endpoint of the space, under `/api/`: a web-standard handler, the shape every edge runtime runs. The request
|
|
122
|
+
* carries no credentials — `ctx.user` says who is asking — and the response sets no cookies: the host's are the
|
|
123
|
+
* platform's.
|
|
124
|
+
*/
|
|
125
|
+
export declare type FunctionRoute = (request: Request, ctx: FunctionRouteContext) => Response | Promise<Response>;
|
|
126
|
+
|
|
127
|
+
/** What a route's handler gets besides the request: the context, and the `:params` its key named, by name. */
|
|
128
|
+
export declare type FunctionRouteContext = FunctionContext & {
|
|
129
|
+
params: Record<string, string>;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** What a space's `functions/index.ts` exports: its tasks, its routes, and where its code may reach. */
|
|
133
|
+
export declare type FunctionsDefinition = {
|
|
134
|
+
/** The hosts `ctx.fetch` may reach. Anything else is refused before it leaves. */
|
|
135
|
+
allow?: {
|
|
136
|
+
hosts?: string[];
|
|
137
|
+
};
|
|
138
|
+
tasks?: FunctionTask<never>[];
|
|
139
|
+
/** `'GET /board-assets/:board/:asset'` → a handler, served at `/api/board-assets/…`. */
|
|
140
|
+
routes?: Record<string, FunctionRoute>;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* One step a server action can take, written by the space. The shape of the platform's own tasks — addressed as
|
|
145
|
+
* `<namespace>.<action>`, drawn in the builder from `params` — run with a {@link FunctionContext} rather than the
|
|
146
|
+
* platform's full one.
|
|
147
|
+
*/
|
|
148
|
+
export declare type FunctionTask<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
149
|
+
namespace: string;
|
|
150
|
+
action: string;
|
|
151
|
+
title: string;
|
|
152
|
+
description?: string;
|
|
153
|
+
params: Record<keyof T, InteractionCallbackParam<T>>;
|
|
154
|
+
run: (params: T, ctx: FunctionContext) => unknown;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Who started the run, as a space's own code may know them: everything a function decides by — who, their roles and
|
|
159
|
+
* permissions — and never their session. A token inside the sandbox is a token a function could send anywhere.
|
|
160
|
+
*/
|
|
161
|
+
export declare type FunctionUser = {
|
|
162
|
+
id: number;
|
|
163
|
+
username: string;
|
|
164
|
+
email: string;
|
|
165
|
+
verified: boolean;
|
|
166
|
+
permissions: string[];
|
|
167
|
+
roles: string[];
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
declare type InteractionCallbackParam<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
171
|
+
canBind?: boolean;
|
|
172
|
+
label?: string;
|
|
173
|
+
when?: boolean | ((params: InteractionCallbackParamValues<T>) => boolean);
|
|
174
|
+
/**
|
|
175
|
+
* This param names one of the space's credentials, of this provider. Serializable on purpose: an editor offers the
|
|
176
|
+
* space's credentials of that kind to pick from, rather than a text box somebody has to type an identifier into.
|
|
177
|
+
*/
|
|
178
|
+
credentialProvider?: SpaceCredentialProvider;
|
|
179
|
+
} & ({
|
|
180
|
+
type: 'text';
|
|
181
|
+
defaultValue?: string | number;
|
|
182
|
+
} | {
|
|
183
|
+
type: 'textarea';
|
|
184
|
+
defaultValue?: string | number;
|
|
185
|
+
} | {
|
|
186
|
+
type: 'codemirror-text';
|
|
187
|
+
defaultValue?: string;
|
|
188
|
+
} | {
|
|
189
|
+
type: 'codemirror-json';
|
|
190
|
+
defaultValue?: string;
|
|
191
|
+
} | {
|
|
192
|
+
type: 'boolean';
|
|
193
|
+
defaultValue?: boolean;
|
|
194
|
+
} | {
|
|
195
|
+
/**
|
|
196
|
+
* Several elements of the space, stored as their ids. The editor offers the elements of `elementType` to pick
|
|
197
|
+
* from — it is the one that knows the page — so a step never asks for ids typed by hand.
|
|
198
|
+
*/
|
|
199
|
+
type: 'elements';
|
|
200
|
+
defaultValue?: string[];
|
|
201
|
+
elementType?: string;
|
|
202
|
+
} | {
|
|
203
|
+
type: 'select';
|
|
204
|
+
defaultValue?: string;
|
|
205
|
+
options: {
|
|
206
|
+
label: string;
|
|
207
|
+
value: string;
|
|
208
|
+
}[] | ((params: InteractionCallbackParamValues<T>) => {
|
|
209
|
+
label: string;
|
|
210
|
+
value: string;
|
|
211
|
+
}[]);
|
|
212
|
+
} | {
|
|
213
|
+
type: (params: InteractionCallbackParamValues<T>) => InteractionParamType;
|
|
214
|
+
defaultValue?: string | number | boolean | string[];
|
|
215
|
+
options?: {
|
|
216
|
+
label: string;
|
|
217
|
+
value: string;
|
|
218
|
+
}[] | ((params: InteractionCallbackParamValues<T>) => {
|
|
219
|
+
label: string;
|
|
220
|
+
value: string;
|
|
221
|
+
}[]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
declare type InteractionCallbackParamValues<T extends Record<string, unknown> = Record<string, unknown>> = T;
|
|
225
|
+
|
|
226
|
+
declare type InteractionParamType = 'boolean' | 'select' | 'text' | 'textarea' | 'codemirror-text' | 'codemirror-json' | 'elements';
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The lifetime a change writes with: a number of seconds, one worked out from the value written, or none.
|
|
230
|
+
*/
|
|
231
|
+
declare type KvChangeLifetime<T> = number | ((next: T) => number | undefined);
|
|
232
|
+
|
|
233
|
+
/** One entry of a list: what it is called, where it sorts, and what it carries. */
|
|
234
|
+
declare type KvListEntry = {
|
|
235
|
+
id: string;
|
|
236
|
+
score: number;
|
|
237
|
+
value: unknown;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** What a put did: whether the entry went in, and what `keep` dropped to make room. */
|
|
241
|
+
declare type KvListPut = {
|
|
242
|
+
stored: boolean;
|
|
243
|
+
dropped: KvListEntry[];
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
declare type KvListPutOptions = {
|
|
247
|
+
/** How many to keep, the highest scores; what falls past it is dropped. {@link MAX_LIST_ENTRIES} at most. */
|
|
248
|
+
keep?: number;
|
|
249
|
+
/**
|
|
250
|
+
* Replace an entry already there only with a score at least as high — a player's best, or the latest of two writes
|
|
251
|
+
* that arrived out of order. Otherwise the one there stays and nothing is written.
|
|
252
|
+
*/
|
|
253
|
+
higherOnly?: boolean;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
declare type KvListRange = {
|
|
257
|
+
/** How many entries to skip first. 0 when absent. */
|
|
258
|
+
offset?: number;
|
|
259
|
+
/** How many to answer. All of them when absent. */
|
|
260
|
+
limit?: number;
|
|
261
|
+
/** `desc` — the highest score first — when absent. */
|
|
262
|
+
order?: 'asc' | 'desc';
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/** Where a limit stands after counting one more: whether that one was within it, and how many are left. */
|
|
266
|
+
declare type RateCount = {
|
|
267
|
+
allowed: boolean;
|
|
268
|
+
count: number;
|
|
269
|
+
remaining: number;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/** How much of something is allowed: `most` in every `perSeconds`, for each caller or for everyone together. */
|
|
273
|
+
declare type RateLimit = {
|
|
274
|
+
most: number;
|
|
275
|
+
perSeconds: number;
|
|
276
|
+
per?: 'caller' | 'everyone';
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
declare type SpaceCredentialProvider = 's3' | 'r2' | 'ssr' | 'custom' | 'smtp';
|
|
280
|
+
|
|
281
|
+
export { }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions: a server's own code, and the contract a space's code is written against — the same file whether a
|
|
3
|
+
* self-hosted server loads it natively or the platform runs it in its sandbox. Its own entry so a space's code imports
|
|
4
|
+
* the contract alone, and nothing that runs it.
|
|
5
|
+
*/
|
|
6
|
+
export { ActionRefusal, defineFunctions } from './modules/functions/contract';
|
|
7
|
+
export type { FunctionContext, FunctionFetch, FunctionFetchInit, FunctionRoute, FunctionRouteContext, FunctionsDefinition, FunctionTask, FunctionUser } from './modules/functions/contract';
|
|
8
|
+
export type { FunctionsConfig, FunctionUsageRecord } from './modules/functions/config';
|
|
9
|
+
export { DEFAULT_FUNCTION_LIMITS } from './modules/functions/config';
|
|
10
|
+
/**
|
|
11
|
+
* What the platform keeps of a space's functions, and what it hands a runner: the built bundle and what it declared
|
|
12
|
+
* when it was saved (`prepareFunctions` on the actions module is the one way to make one).
|
|
13
|
+
*/
|
|
14
|
+
export type { FunctionsSource } from './modules/functions/build';
|
|
15
|
+
export type { FunctionsManifest, FunctionTaskManifest, FunctionsProblem } from '@plitzi/sdk-shared';
|
|
16
|
+
export type { FunctionInvocation, FunctionInvokeRequest, FunctionLimits, FunctionRunner, FunctionsBundle, FunctionStopReason, FunctionUsage, SpaceFunctions } from './modules/functions/protocol';
|
|
17
|
+
export type { PreparedFunctions } from './modules/functions/space';
|
|
18
|
+
export type { ManifestReading } from './modules/functions/manifest';
|
|
19
|
+
export { functionTryEntry, TRY_STEP } from './modules/functions/tryEntry';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ActionRefusal } from "./modules/actions/runtime/errors.js";
|
|
2
|
+
import { DEFAULT_FUNCTION_LIMITS } from "./modules/functions/config.js";
|
|
3
|
+
import { defineFunctions } from "./modules/functions/contract.js";
|
|
4
|
+
import { TRY_STEP, functionTryEntry } from "./modules/functions/tryEntry.js";
|
|
5
|
+
export { ActionRefusal, DEFAULT_FUNCTION_LIMITS, TRY_STEP, defineFunctions, functionTryEntry };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running the spaces' functions: the runner service (its own process, holding nothing), the platform's client to it,
|
|
3
|
+
* and the isolates both are made of. Its own entry, apart from the contract, so a space's code and a self-hosted
|
|
4
|
+
* server's native functions never load a line of it — nor need `isolated-vm` or `core-js`, which only the process
|
|
5
|
+
* that runs isolates installs.
|
|
6
|
+
*/
|
|
7
|
+
export { startFunctionsRunnerService } from './modules/functions/runner/service';
|
|
8
|
+
export type { FunctionsRunnerService, FunctionsRunnerServiceOptions } from './modules/functions/runner/service';
|
|
9
|
+
export { createRemoteRunner } from './modules/functions/runner/remote';
|
|
10
|
+
export type { RemoteRunnerOptions } from './modules/functions/runner/remote';
|
|
11
|
+
export { createIsolateRunner } from './modules/functions/sandbox/isolate';
|
|
12
|
+
export type { IsolateRunnerOptions } from './modules/functions/sandbox/isolate';
|
|
13
|
+
export { FUNCTIONS_PROTOCOL, FunctionFailure } from './modules/functions/protocol';
|
|
14
|
+
export { createLocalFunctions } from './modules/functions/local';
|
|
15
|
+
export type { LocalFunctions, LocalFunctionsOptions } from './modules/functions/local';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FUNCTIONS_PROTOCOL, FunctionFailure } from "./modules/functions/protocol.js";
|
|
2
|
+
import { createIsolateRunner } from "./modules/functions/sandbox/isolate.js";
|
|
3
|
+
import { startFunctionsRunnerService } from "./modules/functions/runner/service.js";
|
|
4
|
+
import { createRemoteRunner } from "./modules/functions/runner/remote.js";
|
|
5
|
+
import { createLocalFunctions } from "./modules/functions/local.js";
|
|
6
|
+
export { FUNCTIONS_PROTOCOL, FunctionFailure, createIsolateRunner, createLocalFunctions, createRemoteRunner, startFunctionsRunnerService };
|
package/dist/handlers.d.ts
CHANGED
|
@@ -55,10 +55,10 @@ export { createAuthRouteHandlers, mountAuthRoutes } from './core/handlers/authRo
|
|
|
55
55
|
export { createSocialAuthRouteHandlers, mountSocialAuthRoutes } from './core/handlers/socialRouteHandlers';
|
|
56
56
|
export { createOAuthRouteHandlers, mountOAuthRoutes, OAUTH_ROUTE_PATHS } from './core/handlers/oauthRouteHandlers';
|
|
57
57
|
export { createAuthMiddleware } from './core/handlers/authMiddleware';
|
|
58
|
-
export { createCsrfMiddleware } from './core/handlers/csrfMiddleware';
|
|
58
|
+
export { createCsrfMiddleware, createOriginGuardMiddleware } from './core/handlers/csrfMiddleware';
|
|
59
59
|
export type { AuthRouteHandlersOptions } from './core/handlers/authRouteHandlers';
|
|
60
60
|
export type { SocialRouteHandlersOptions } from './core/handlers/socialRouteHandlers';
|
|
61
61
|
export type { OAuthResponse, OAuthRouteHandler, OAuthRouteHandlersOptions } from './core/handlers/oauthRouteHandlers';
|
|
62
62
|
export type { AuthMiddlewareOptions } from './core/handlers/authMiddleware';
|
|
63
|
-
export type { CsrfMiddlewareOptions } from './core/handlers/csrfMiddleware';
|
|
63
|
+
export type { CsrfMiddlewareOptions, OriginGuardOptions } from './core/handlers/csrfMiddleware';
|
|
64
64
|
export type { AuthedRequest, HttpRoute, JsonResponse, RedirectResponse, RouteHandler, RouterLike, SocialRouteHandler } from './core/handlers/types';
|
package/dist/handlers.js
CHANGED
|
@@ -2,5 +2,5 @@ import { createAuthRouteHandlers, mountAuthRoutes } from "./core/handlers/authRo
|
|
|
2
2
|
import { createSocialAuthRouteHandlers, mountSocialAuthRoutes } from "./core/handlers/socialRouteHandlers.js";
|
|
3
3
|
import { OAUTH_ROUTE_PATHS, createOAuthRouteHandlers, mountOAuthRoutes } from "./core/handlers/oauthRouteHandlers.js";
|
|
4
4
|
import { createAuthMiddleware } from "./core/handlers/authMiddleware.js";
|
|
5
|
-
import { createCsrfMiddleware } from "./core/handlers/csrfMiddleware.js";
|
|
6
|
-
export { OAUTH_ROUTE_PATHS, createAuthMiddleware, createAuthRouteHandlers, createCsrfMiddleware, createOAuthRouteHandlers, createSocialAuthRouteHandlers, mountAuthRoutes, mountOAuthRoutes, mountSocialAuthRoutes };
|
|
5
|
+
import { createCsrfMiddleware, createOriginGuardMiddleware } from "./core/handlers/csrfMiddleware.js";
|
|
6
|
+
export { OAUTH_ROUTE_PATHS, createAuthMiddleware, createAuthRouteHandlers, createCsrfMiddleware, createOAuthRouteHandlers, createOriginGuardMiddleware, createSocialAuthRouteHandlers, mountAuthRoutes, mountOAuthRoutes, mountSocialAuthRoutes };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes a file whole or not at all: to a temporary name beside it, then renamed over it.
|
|
3
|
+
*
|
|
4
|
+
* Several workers can build or download the same plugin into the same folder at once, and a reader can open a file
|
|
5
|
+
* while another process is writing it. A rename replaces the file in one step, so whoever reads it sees the old
|
|
6
|
+
* version or the new one — never half of either. Same-folder temporary, so the rename never crosses a filesystem.
|
|
7
|
+
*/
|
|
8
|
+
export declare const writeFileAtomic: (file: string, data: string | Uint8Array) => Promise<void>;
|
|
9
|
+
/** `copyFile`, with the same whole-or-nothing guarantee. */
|
|
10
|
+
export declare const copyFileAtomic: (source: string, file: string) => Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
//#region src/helpers/atomicFile.ts
|
|
4
|
+
/**
|
|
5
|
+
* Writes a file whole or not at all: to a temporary name beside it, then renamed over it.
|
|
6
|
+
*
|
|
7
|
+
* Several workers can build or download the same plugin into the same folder at once, and a reader can open a file
|
|
8
|
+
* while another process is writing it. A rename replaces the file in one step, so whoever reads it sees the old
|
|
9
|
+
* version or the new one — never half of either. Same-folder temporary, so the rename never crosses a filesystem.
|
|
10
|
+
*/
|
|
11
|
+
var writeFileAtomic = async (file, data) => {
|
|
12
|
+
const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
13
|
+
try {
|
|
14
|
+
await fs.writeFile(temporary, data);
|
|
15
|
+
await fs.rename(temporary, file);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
await fs.rm(temporary, { force: true });
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/** `copyFile`, with the same whole-or-nothing guarantee. */
|
|
22
|
+
var copyFileAtomic = async (source, file) => {
|
|
23
|
+
const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
24
|
+
try {
|
|
25
|
+
await fs.copyFile(source, temporary);
|
|
26
|
+
await fs.rename(temporary, file);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
await fs.rm(temporary, { force: true });
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { copyFileAtomic, writeFileAtomic };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { DEFAULT_COMPRESSION, compressBody, selectEncoding } from "./compress.js";
|
|
2
2
|
//#region src/helpers/buildResponseHelpers.ts
|
|
3
|
+
/**
|
|
4
|
+
* `compressBody`, remembered in `store` when the caller keeps one: the same body in the same encoding is compressed
|
|
5
|
+
* once. What comes back uncompressed (identity, or a body under the threshold) is never stored — there is nothing to
|
|
6
|
+
* reuse, and a stored copy would only be the body again.
|
|
7
|
+
*/
|
|
8
|
+
var compressOnce = (body, encoding, compression, store) => {
|
|
9
|
+
if (!store || encoding === "identity") return compressBody(body, encoding, compression);
|
|
10
|
+
const kept = store[encoding];
|
|
11
|
+
if (kept) return kept;
|
|
12
|
+
const compressed = compressBody(body, encoding, compression, true);
|
|
13
|
+
if (typeof compressed !== "string") store[encoding] = compressed;
|
|
14
|
+
return compressed;
|
|
15
|
+
};
|
|
3
16
|
var buildResponseHelpers = (raw, acceptEncoding, compression = DEFAULT_COMPRESSION) => {
|
|
4
17
|
let statusCode = 200;
|
|
5
18
|
const encoding = selectEncoding(acceptEncoding, compression);
|
|
@@ -12,7 +25,24 @@ var buildResponseHelpers = (raw, acceptEncoding, compression = DEFAULT_COMPRESSI
|
|
|
12
25
|
* already said so in a header.
|
|
13
26
|
*/
|
|
14
27
|
const transformable = () => !String(raw.getHeaders()["cache-control"]).includes("no-transform");
|
|
15
|
-
|
|
28
|
+
/** A stored form this request can take as it is, without the body it was made from. */
|
|
29
|
+
const storedFor = (store) => encoding === "identity" || !transformable() ? void 0 : store?.[encoding];
|
|
30
|
+
const writeBody = (payload, encoded) => {
|
|
31
|
+
if (encoded) {
|
|
32
|
+
raw.setHeader("Content-Encoding", encoding);
|
|
33
|
+
raw.setHeader("Vary", "Accept-Encoding");
|
|
34
|
+
}
|
|
35
|
+
raw.setHeader("Content-Length", Buffer.byteLength(payload).toString());
|
|
36
|
+
if (!raw.headersSent) raw.writeHead(statusCode);
|
|
37
|
+
raw.end(payload);
|
|
38
|
+
};
|
|
39
|
+
const writeSend = (content, store) => {
|
|
40
|
+
const stored = typeof content === "function" ? storedFor(store) : void 0;
|
|
41
|
+
if (stored) {
|
|
42
|
+
writeBody(stored, true);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const body = typeof content === "function" ? content() : content;
|
|
16
46
|
/**
|
|
17
47
|
* A Buffer goes out untouched.
|
|
18
48
|
*
|
|
@@ -21,14 +51,8 @@ var buildResponseHelpers = (raw, acceptEncoding, compression = DEFAULT_COMPRESSI
|
|
|
21
51
|
* corrupts any font, image or archive served through it. What a Buffer holds is also compressed already
|
|
22
52
|
* (woff2, png), so re-encoding it would cost CPU to make it bigger.
|
|
23
53
|
*/
|
|
24
|
-
const compressed = typeof body === "string" && transformable() ?
|
|
25
|
-
|
|
26
|
-
raw.setHeader("Content-Encoding", encoding);
|
|
27
|
-
raw.setHeader("Vary", "Accept-Encoding");
|
|
28
|
-
}
|
|
29
|
-
raw.setHeader("Content-Length", Buffer.byteLength(compressed).toString());
|
|
30
|
-
if (!raw.headersSent) raw.writeHead(statusCode);
|
|
31
|
-
raw.end(compressed);
|
|
54
|
+
const compressed = typeof body === "string" && transformable() ? compressOnce(body, encoding, compression, store) : body;
|
|
55
|
+
writeBody(compressed, compressed !== body);
|
|
32
56
|
};
|
|
33
57
|
return {
|
|
34
58
|
get status() {
|
|
@@ -43,8 +67,8 @@ var buildResponseHelpers = (raw, acceptEncoding, compression = DEFAULT_COMPRESSI
|
|
|
43
67
|
setStatus(code) {
|
|
44
68
|
statusCode = code;
|
|
45
69
|
},
|
|
46
|
-
send(body) {
|
|
47
|
-
writeSend(body);
|
|
70
|
+
send(body, options) {
|
|
71
|
+
writeSend(body, options?.compressed);
|
|
48
72
|
},
|
|
49
73
|
write(chunk) {
|
|
50
74
|
if (!raw.headersSent) raw.writeHead(statusCode);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { requestOrigin } from "../core/requestParser.js";
|
|
1
2
|
//#region src/helpers/buildServerInfo.ts
|
|
2
3
|
var getEnvironment = (env = "production", server) => {
|
|
3
4
|
switch (env) {
|
|
@@ -36,7 +37,8 @@ var getEnvironment = (env = "production", server) => {
|
|
|
36
37
|
*/
|
|
37
38
|
var buildServerInfo = (req, config, ssr) => {
|
|
38
39
|
const accessToken = req.query["access-token"];
|
|
39
|
-
const origin = `${req.protocol}://${req.hostname}`;
|
|
40
|
+
const origin = requestOrigin(req) || `${req.protocol}://${req.hostname}`;
|
|
41
|
+
const host = origin.slice(`${req.protocol}://`.length);
|
|
40
42
|
const user = req.ctx.user;
|
|
41
43
|
return getEnvironment(config.environment, {
|
|
42
44
|
basePath: "/",
|
|
@@ -52,7 +54,7 @@ var buildServerInfo = (req, config, ssr) => {
|
|
|
52
54
|
location: {
|
|
53
55
|
origin,
|
|
54
56
|
protocol: `${req.protocol}:`,
|
|
55
|
-
host
|
|
57
|
+
host,
|
|
56
58
|
hostname: req.hostname,
|
|
57
59
|
pathname: req.path || "/",
|
|
58
60
|
search: req.search
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CompressedBodies } from '@plitzi/sdk-shared';
|
|
2
|
+
/**
|
|
3
|
+
* The compressed forms of static files, one entry per file, within a byte budget.
|
|
4
|
+
*
|
|
5
|
+
* A file is compressed once per version and encoding instead of on every request — the SDK bundle alone is megabytes
|
|
6
|
+
* of JavaScript. Keyed by path with the version beside it, so a file that changed replaces its old forms rather than
|
|
7
|
+
* sitting next to them. Past the budget the least recently served files go first.
|
|
8
|
+
*/
|
|
9
|
+
export declare class CompressedFileCache {
|
|
10
|
+
private readonly maxBytes;
|
|
11
|
+
private readonly entries;
|
|
12
|
+
constructor(maxBytes: number);
|
|
13
|
+
/** Where the compressed forms of this version of the file are kept; `send` fills it as encodings are asked for. */
|
|
14
|
+
storeFor(filePath: string, version: string): CompressedBodies;
|
|
15
|
+
/** Drops the least recently served files until the rest fit the budget. Called after a send may have grown one. */
|
|
16
|
+
trim(): void;
|
|
17
|
+
get bytes(): number;
|
|
18
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/helpers/cache/CompressedFileCache.ts
|
|
2
|
+
var sizeOf = (bodies) => (bodies.br?.length ?? 0) + (bodies.gzip?.length ?? 0);
|
|
3
|
+
/**
|
|
4
|
+
* The compressed forms of static files, one entry per file, within a byte budget.
|
|
5
|
+
*
|
|
6
|
+
* A file is compressed once per version and encoding instead of on every request — the SDK bundle alone is megabytes
|
|
7
|
+
* of JavaScript. Keyed by path with the version beside it, so a file that changed replaces its old forms rather than
|
|
8
|
+
* sitting next to them. Past the budget the least recently served files go first.
|
|
9
|
+
*/
|
|
10
|
+
var CompressedFileCache = class {
|
|
11
|
+
maxBytes;
|
|
12
|
+
entries = /* @__PURE__ */ new Map();
|
|
13
|
+
constructor(maxBytes) {
|
|
14
|
+
this.maxBytes = maxBytes;
|
|
15
|
+
}
|
|
16
|
+
/** Where the compressed forms of this version of the file are kept; `send` fills it as encodings are asked for. */
|
|
17
|
+
storeFor(filePath, version) {
|
|
18
|
+
const existing = this.entries.get(filePath);
|
|
19
|
+
this.entries.delete(filePath);
|
|
20
|
+
const entry = existing?.version === version ? existing : {
|
|
21
|
+
version,
|
|
22
|
+
bodies: {}
|
|
23
|
+
};
|
|
24
|
+
this.entries.set(filePath, entry);
|
|
25
|
+
return entry.bodies;
|
|
26
|
+
}
|
|
27
|
+
/** Drops the least recently served files until the rest fit the budget. Called after a send may have grown one. */
|
|
28
|
+
trim() {
|
|
29
|
+
let total = 0;
|
|
30
|
+
for (const entry of this.entries.values()) total += sizeOf(entry.bodies);
|
|
31
|
+
for (const [filePath, entry] of this.entries) {
|
|
32
|
+
if (total <= this.maxBytes) return;
|
|
33
|
+
total -= sizeOf(entry.bodies);
|
|
34
|
+
this.entries.delete(filePath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
get bytes() {
|
|
38
|
+
let total = 0;
|
|
39
|
+
for (const entry of this.entries.values()) total += sizeOf(entry.bodies);
|
|
40
|
+
return total;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { CompressedFileCache };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readHtmlCacheKey } from "./keys.js";
|
|
1
2
|
//#region src/helpers/cache/cacheManager.ts
|
|
2
3
|
var buildCacheManager = (store) => ({
|
|
3
4
|
invalidate(filter) {
|
|
@@ -7,7 +8,7 @@ var buildCacheManager = (store) => ({
|
|
|
7
8
|
return count;
|
|
8
9
|
}
|
|
9
10
|
return store.invalidateWhere((key) => {
|
|
10
|
-
const
|
|
11
|
+
const { spaceId: keySpaceId, environment: keyEnvironment, hostname: keyHostname } = readHtmlCacheKey(key);
|
|
11
12
|
if (filter.spaceId !== void 0 && keySpaceId !== String(filter.spaceId)) return false;
|
|
12
13
|
if (filter.environment !== void 0 && keyEnvironment !== filter.environment) return false;
|
|
13
14
|
if (filter.hostname !== void 0 && keyHostname !== filter.hostname) return false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,5 +2,5 @@ export { DEFAULT_TTL_MS } from './defaults';
|
|
|
2
2
|
export { TtlCache } from './TtlCache';
|
|
3
3
|
export { buildHtmlCacheKey, buildOfflineDataCacheKey, buildRscCacheKey } from './keys';
|
|
4
4
|
export { buildCacheManager } from './cacheManager';
|
|
5
|
-
export type { ServerCaches } from './serverCaches';
|
|
5
|
+
export type { CachedPage, ServerCaches } from './serverCaches';
|
|
6
6
|
export { createServerCaches, destroyServerCaches } from './serverCaches';
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
* one keyed — a space that switched dev tools on for its published site would otherwise hand the first visitor's
|
|
11
11
|
* choice to the rest.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* So is the kept state the first paint depends on (`settings.paintedState`): `prepareRender` renders with that cookie's
|
|
14
|
+
* values, so a page drawn with one visitor's toolbar must not be served to the next. Keyed by its raw value — this
|
|
15
|
+
* key does not know which keys a space declares, and a space that declares none has no such cookie to split on.
|
|
16
|
+
*
|
|
17
|
+
* Read from the request here rather than handed in, so a call site cannot key the page without it. Only those
|
|
14
18
|
* cookies are read: keying the whole header would split the cache on every analytics cookie a visitor carries.
|
|
15
19
|
*/
|
|
16
20
|
export declare const buildHtmlCacheKey: (accessToken: string | undefined, spaceId: number | string | null, environment: string, revision: number, req: {
|
|
@@ -22,6 +26,16 @@ export declare const buildHtmlCacheKey: (accessToken: string | undefined, spaceI
|
|
|
22
26
|
host?: string;
|
|
23
27
|
};
|
|
24
28
|
}) => string;
|
|
29
|
+
/**
|
|
30
|
+
* What an HTML key says about the page it holds, read by the one list that also writes it. `server.cache.invalidate`
|
|
31
|
+
* filters on these; it used to split the key by positions of its own, and when the key grew a field in front the
|
|
32
|
+
* filter went on reading the wrong ones — every invalidation by space matched nothing.
|
|
33
|
+
*/
|
|
34
|
+
export declare const readHtmlCacheKey: (key: string) => {
|
|
35
|
+
spaceId: string;
|
|
36
|
+
environment: string;
|
|
37
|
+
hostname: string;
|
|
38
|
+
};
|
|
25
39
|
export declare const buildOfflineDataCacheKey: (spaceId: number, environment: string, revision: number) => string;
|
|
26
40
|
export declare const buildRscCacheKey: (spaceId: number, environment: string, revision: number, userId: string | number | undefined, idsParam: string | undefined, req: {
|
|
27
41
|
hostname: string;
|