@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
package/README.md
CHANGED
|
@@ -106,9 +106,12 @@ server.listen(3001, '127.0.0.1');
|
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
yarn add @plitzi/sdk-server react react-dom
|
|
109
|
-
|
|
109
|
+
node server.ts # http://127.0.0.1:3001/ — Node 22.18+ runs TypeScript itself
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
No transpiler in front of it: Node strips the types. A loader such as `tsx` costs a page server more memory than the
|
|
113
|
+
server itself — on a 128 MB host, the difference between starting and being killed on boot.
|
|
114
|
+
|
|
112
115
|
Both pages render server-side, with the CSS the space declared. `authorSpace` refuses to hand back a space that
|
|
113
116
|
would not render — a CSS property the style editor could not read back, a class nothing declares, a binding
|
|
114
117
|
pointing at an element that is not there — so a mistake is an error at the line that made it rather than a blank
|
|
@@ -158,26 +161,32 @@ serve it — worth running over anything that arrives as a file.
|
|
|
158
161
|
|---|---|---|---|
|
|
159
162
|
| `httpVersion` | `1 \| 2 \| 3` | `2` with `tls`, else `1` | HTTP protocol version. Falls back to the nearest available lower version. |
|
|
160
163
|
| `tls` | `{ key, cert, minVersion? }` | — | TLS key and certificate. Required for versions 2 and 3; optional for version 1. |
|
|
161
|
-
| `devMode` | `boolean` | `
|
|
164
|
+
| `devMode` | `boolean` | `false` | Enables development mode: appends `?dev` to esm.sh CDN URLs for React, and activates per-request timing metrics (see [Dev metrics](#dev-metrics)). Off, the process must run with `NODE_ENV=production` or React renders with its development build; the server says so once, at `error`. |
|
|
162
165
|
| `assetVersion` | `string` | — | Cache-buster appended as `?v=<assetVersion>` to all default SDK asset URLs. Compute from file mtime or package version at startup. |
|
|
163
166
|
| `cacheTtlMs` | `number` | `300000` | TTL in milliseconds for the SSR render cache. Set to `0` to disable. |
|
|
164
167
|
| `loginPath` | `string \| false` | `'/auth/login'` | Path for the built-in login endpoint. Set to `false` to disable it entirely. |
|
|
165
168
|
| `logoutPath` | `string \| false` | `'/auth/logout'` | Path for the built-in logout endpoint. Set to `false` to disable it entirely. |
|
|
169
|
+
| `signIn` | `SSRSignInConfig` | — | Sign visitors in by redirect through an OAuth 2.1 authorization server: `GET /auth/sign-in?return=/x` registers this host as a client, sends the browser out with PKCE (state in a `__Host-` cookie), and `/auth/sign-in/callback` redeems the code server to server and hands the token to `exchangeCredential`, which makes the session here. Needs the `exchangeCredential` adapter. |
|
|
166
170
|
| `templateFn` | `SSRTemplateFn` | built-in EJS template | Custom render function. Receives all template params and returns an HTML string. |
|
|
167
171
|
| `plugins` | `Record<string, PluginSource>` | — | Named plugin definitions. Compiled or copied on first use and cached for `pluginsTtlMs`. |
|
|
168
172
|
| `pluginsCacheDir` | `string` | `.sdk-plugins` | Directory where compiled plugin files are stored. |
|
|
169
173
|
| `pluginsTtlMs` | `number` | `604800000` | TTL in milliseconds for compiled plugins (default: 1 week). |
|
|
170
174
|
| `autoLoadSchemaPlugins` | `boolean` | `true` | Auto-download and cache plugins declared in the schema's `offlineData.plugins` list. Set to `false` to manage plugin loading manually. |
|
|
175
|
+
| `allowPrivatePluginHosts` | `boolean` | `false` | Read a schema plugin from a private address (`localhost`, this server's own network). A plugin's address is typed by whoever edits a space, so leave it off anywhere but a development machine whose bucket is local. |
|
|
171
176
|
| `publicDir` | `string` | — | Absolute path to a directory served at the root URL level (e.g. `robots.txt`, `favicon.png`). Files are checked before `static` prefix routes. |
|
|
172
177
|
| `static` | `Record<string, string>` | — | URL prefix → filesystem path mappings for static file serving. |
|
|
173
178
|
| `ssrOnly` | `boolean` | `false` | Omit client-side JS from the rendered page. Useful for verifying SSR HTML without hydration. |
|
|
174
179
|
| `streaming` | `boolean` | `false` | Stream HTML to the browser incrementally to reduce TTFB. See [Streaming](#streaming). |
|
|
175
180
|
| `middlewares` | `SSRMiddleware[]` | — | Array of custom middleware functions executed before the SSR renderer on every request (see [Custom middlewares](#custom-middlewares)). |
|
|
176
181
|
| `rsc` | `SSRRscConfig` | — | React Server Components endpoint configuration (see [RSC](#react-server-components-rsc)). |
|
|
182
|
+
| `realtime` | `{ pubsub?, path?, transport?, allowedOrigins? } \| false` | in memory, at `/_realtime`, `sse` | Realtime channels the space declares. `transport` is what pages connect with: `sse` (a stream, publishes over `POST`) or `websocket` (one socket both ways, HTTP/1.1 only; pages fall back to the stream where a socket cannot open). `allowedOrigins` lists the other origins whose pages may open a socket. `pubsub` is how messages reach other processes — `createMemoryPubSub()` (the default, across workers) or `createRedisPubSub({ publisher, subscriber })` for several replicas. `false` serves no endpoint. See [`docs/en/realtime.md`](../../docs/en/realtime.md). |
|
|
177
183
|
| `compression` | `SSRCompressionConfig \| false` | Brotli, then gzip | Response compression (see [Compression](#compression)). `false` never compresses. |
|
|
178
184
|
| `health` | `SSRHealthConfig` | identity payload | The `/health` endpoint. `check` adds live state per probe and turns it into a readiness probe (see [Health](#health)). |
|
|
179
185
|
| `adapters` | `SSRAdapters` | — | Required. Adapter callbacks for data fetching. |
|
|
186
|
+
| `logLevel` | `'silent' \| 'error' \| 'warn' \| 'info' \| 'debug'` | `info` with `devMode`, else `error` | How much the server says, for the whole process (see [Logging](#logging)). |
|
|
187
|
+
| `logger` | `ServerLogger` | the console | Where it says it: one structured `ServerLogEvent` stream. `consoleLogger` prints each as one line. |
|
|
180
188
|
| `onListenError` | `(error, { port, host, label }) => void` | exits non-zero | What to do when the server cannot take its port. By default it prints what went wrong and what to do about it, then exits — a process whose server never bound is not running. Supply this to keep it alive and decide yourself. |
|
|
189
|
+
| `workers` | `boolean \| number \| 'auto'` | `'auto'` under `NODE_ENV=production`, else `false` | How many processes serve the port: one per core, a number (lowered to the cores there are), or one. `SDK_SERVER_WORKERS` sets it when the config does not. See [Using every core](#using-every-core). |
|
|
181
190
|
|
|
182
191
|
### HTTP version behaviour
|
|
183
192
|
|
|
@@ -409,7 +418,8 @@ createServer({ compression: false, adapters });
|
|
|
409
418
|
|---|---|---|---|
|
|
410
419
|
| `encodings` | `('br' \| 'gzip')[]` | `['br', 'gzip']` | What this server offers, most preferred first; the first one the client accepts wins. `[]` disables compression. |
|
|
411
420
|
| `threshold` | `number` | `1024` | Responses smaller than this many bytes go out uncompressed. |
|
|
412
|
-
| `brotliQuality` | `number` | `
|
|
421
|
+
| `brotliQuality` | `number` | `2` | Brotli quality, 0–11, for a body compressed on every request (a page rendered for that request alone). Measured on a quarter core, 4 cost a tenth of the pages a second to save half a kilobyte each. |
|
|
422
|
+
| `keptBrotliQuality` | `number` | `6` | Brotli quality for a body compressed once and kept: a cached page, a static file such as the SDK bundle. Paid once; the bundle comes out 10% smaller than at 4, for the same memory. 9 needs ~40 MB more to compress the bundle, which a 128 MB server does not have. |
|
|
413
423
|
| `gzipLevel` | `number` | `6` | Gzip level, 0–9. |
|
|
414
424
|
|
|
415
425
|
A response that sets `Cache-Control: no-transform` is never compressed, whatever the settings say — that header is
|
|
@@ -546,7 +556,7 @@ const getSpaceDeployment = async (req): Promise<SSRSpaceDeployment> => {
|
|
|
546
556
|
};
|
|
547
557
|
```
|
|
548
558
|
|
|
549
|
-
Plugins listed in `pluginSources` are registered into the plugin manager on-the-fly using `ensure()`,
|
|
559
|
+
Plugins listed in `pluginSources` — and the external plugins a space's schema lists — are registered into the plugin manager on-the-fly using `ensure()`, under a key that carries the source's identity as well as its name and version (`name@1.0.0+<digest>` of its `js`, `css`, `action` and `props`). A plugin published again under the same version, or another space's plugin with the same name and version, is therefore a source of its own rather than whichever this process met first, and is built only once. Both `pluginNames` and `pluginSources` entries are resolved in parallel before the HTML is rendered.
|
|
550
560
|
|
|
551
561
|
### Plugin sources
|
|
552
562
|
|
|
@@ -966,8 +976,8 @@ and every refusal names a machine-readable `reason`, so a client can tell "renew
|
|
|
966
976
|
| `identity` | Your own hosts and origins — the floor for domain binding and framing |
|
|
967
977
|
| `tokens.lifetimes` | How long each credential lives |
|
|
968
978
|
|
|
969
|
-
Working examples are in [`examples/
|
|
970
|
-
write, `
|
|
979
|
+
Working examples are in [`examples/self-hosting`](../../examples/self-hosting): `03-sessions` over a store you
|
|
980
|
+
write, `04-mysql` over one you do not.
|
|
971
981
|
|
|
972
982
|
### Cross-site request forgery
|
|
973
983
|
|
|
@@ -1134,6 +1144,54 @@ A stage receives the `SSRContext` — the request, the config, and the render si
|
|
|
1134
1144
|
**is** the decision to mount them: there is no config flag mirroring it, and a server that never passes them
|
|
1135
1145
|
never loads them.
|
|
1136
1146
|
|
|
1147
|
+
## Using every core
|
|
1148
|
+
|
|
1149
|
+
Node runs JavaScript on one thread, so one process renders on one core however many the machine has. With
|
|
1150
|
+
`workers`, the server runs one process per core and the connections are spread between them: more requests a second,
|
|
1151
|
+
and less time waiting behind another page's render.
|
|
1152
|
+
|
|
1153
|
+
```ts
|
|
1154
|
+
createServer({ adapters, workers: 'auto' }); // one per core — the default under NODE_ENV=production
|
|
1155
|
+
createServer({ adapters, workers: 4 }); // at most four, and never more than the cores there are
|
|
1156
|
+
createServer({ adapters, workers: false }); // one process, exactly as before
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
`SDK_SERVER_WORKERS=auto|false|<n>` sets it from the environment. The cores are the ones the process may use: a
|
|
1160
|
+
container's CPU quota counts, so a pod limited to one CPU runs one process whatever the node has.
|
|
1161
|
+
|
|
1162
|
+
**One process is the server as it always was.** Workers off, one asked for, or one core to run on: nothing is forked,
|
|
1163
|
+
nothing is wrapped, and every in-memory default is this process's own.
|
|
1164
|
+
|
|
1165
|
+
**Several are still one server.** The process you started serves nothing itself; it starts the workers, restarts them
|
|
1166
|
+
and keeps what they must share:
|
|
1167
|
+
|
|
1168
|
+
- The in-memory defaults — an action's `kv`, the job queue, draft previews, the sign-in rate limit — are kept once, by
|
|
1169
|
+
that process, and every worker reaches the same copy. A counter counts every request once; a draft written through
|
|
1170
|
+
one worker is read through another. A store you supply (Redis, a table) is used as it is.
|
|
1171
|
+
- The scheduler and the job consumers run in one worker only: a schedule fires once, and `jobs.workers` is the whole
|
|
1172
|
+
server's concurrency.
|
|
1173
|
+
- `server.cache.invalidate()`, `server.plugins.register()` and `server.plugins.invalidate()` reach every worker,
|
|
1174
|
+
whichever process calls them — a publish webhook lands on one of them.
|
|
1175
|
+
- Plugins are built once, before the workers start.
|
|
1176
|
+
|
|
1177
|
+
Per worker: the rendered-page cache (each renders a page once), the plugin components, and the action caps
|
|
1178
|
+
(`action.concurrency`). A plugin registered with a `component` stays in the process that registered it — register
|
|
1179
|
+
those where the server is created.
|
|
1180
|
+
|
|
1181
|
+
**When a worker dies** — a crash, an uncaught error, the OOM killer — it is replaced, and its replacement takes over the
|
|
1182
|
+
jobs it carried (a claimed job goes back to the queue when its lease lapses). Past one death per worker a minute the
|
|
1183
|
+
replacements wait, doubling from half a second up to thirty, so a request that crashes every worker it reaches cannot
|
|
1184
|
+
turn the server into a fork loop. A replacement that fails to start is retried the same way while the rest keep
|
|
1185
|
+
serving. Two exceptions: a worker that dies before any has served stops the server with a non-zero exit (a port in use
|
|
1186
|
+
would fail the same way every time), and a server that is killed outright takes its workers with it.
|
|
1187
|
+
|
|
1188
|
+
`closeOnSignals(server)` stops them all on SIGTERM, each finishing what it is serving. The workers of one machine are
|
|
1189
|
+
one replica; several machines are several replicas, and what must hold across those still needs a shared store — see
|
|
1190
|
+
below.
|
|
1191
|
+
|
|
1192
|
+
A process that starts servers of its own beside this one (an Express app on another port) should say `workers:
|
|
1193
|
+
false`: each worker runs the whole entry file again.
|
|
1194
|
+
|
|
1137
1195
|
## Scheduled jobs across replicas
|
|
1138
1196
|
|
|
1139
1197
|
Server actions with a `schedule` trigger, and jobs queued for later, run with no configuration — over an in-process
|
|
@@ -1156,6 +1214,12 @@ server.listen(3000);
|
|
|
1156
1214
|
closeOnSignals(server, { afterClose: () => mongoClient.close() });
|
|
1157
1215
|
```
|
|
1158
1216
|
|
|
1217
|
+
`close()` does not wait for what never ends on its own: it stops taking connections, tells every realtime socket and
|
|
1218
|
+
stream to go (a WebSocket is closed with 1001, "going away", which the pages reconnect from once the server is back),
|
|
1219
|
+
ends any other event stream, lets what is being answered finish, and cuts whatever is still open after ten seconds
|
|
1220
|
+
(`SHUTDOWN_GRACE_MS`). A page left open on a board, or an agent's app listening, no longer holds a Ctrl+C or a
|
|
1221
|
+
SIGTERM until something kills the process.
|
|
1222
|
+
|
|
1159
1223
|
`@plitzi/sdk-server/mysql` has the same pair over a `mysql2` pool (`createMysqlJobQueue`, `createMysqlKv`). Anything
|
|
1160
1224
|
else is the `ActionJobQueue` contract written against your store — see
|
|
1161
1225
|
[server actions § self-hosted](https://github.com/plitzi/plitzi-workspace/blob/main/docs/en/server-actions.md#13-for-a-self-hosted-deployment).
|
|
@@ -1291,12 +1355,41 @@ How it works:
|
|
|
1291
1355
|
|
|
1292
1356
|
**Compression**: streaming responses use chunked transfer encoding and skip Brotli/gzip compression. A `Content-Length` header cannot be set before the body is complete, so compression is intentionally bypassed for streaming responses.
|
|
1293
1357
|
|
|
1358
|
+
## Logging
|
|
1359
|
+
|
|
1360
|
+
One stream and one threshold, the usual hierarchy: `silent` < `error` < `warn` < `info` < `debug`. In production the
|
|
1361
|
+
server says only what went wrong — a failed request, a failed run, a server that could not start — and everything
|
|
1362
|
+
below it is one `logLevel` away:
|
|
1363
|
+
|
|
1364
|
+
| Level | What it adds |
|
|
1365
|
+
|---|---|
|
|
1366
|
+
| `error` | Requests answered 5xx or that threw, runs that did not complete, anything the server could not do. |
|
|
1367
|
+
| `warn` | Refused actions (a bad webhook signature, a missing session), plugin manifests it could not fetch, HTTP/3 falling back. |
|
|
1368
|
+
| `info` | Every request line, every run, plugin builds, `listening on`. |
|
|
1369
|
+
| `debug` | The per-render phase breakdown of [Dev metrics](#dev-metrics). |
|
|
1370
|
+
|
|
1371
|
+
```ts
|
|
1372
|
+
createServer({ adapters, logLevel: process.env.LOG_LEVEL === 'info' ? 'info' : 'error', logger: consoleLogger });
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
A request below the threshold is not even turned into an event, so a quiet server pays nothing per request. Everything goes to the `logger` —
|
|
1376
|
+
requests, MCP tool calls, runs, and the server's own messages as `kind: 'message'` events — and `logLevelOf(event)`
|
|
1377
|
+
tells a sink how severe each one is. The run and refusal loggers a deployment wires itself (`createRunLogger`,
|
|
1378
|
+
`createRejectLogger`) are held to the same threshold, and so is `serverLog`, the server's own voice, which a
|
|
1379
|
+
deployment's adapters can speak through too:
|
|
1380
|
+
|
|
1381
|
+
```ts
|
|
1382
|
+
import { serverLog } from '@plitzi/sdk-server';
|
|
1383
|
+
|
|
1384
|
+
serverLog.warn('Adapters', 'space 12 has no deployment, serving main');
|
|
1385
|
+
```
|
|
1386
|
+
|
|
1294
1387
|
## Dev metrics
|
|
1295
1388
|
|
|
1296
1389
|
When `devMode: true`, per-phase timing is instrumented on every render and reported in two ways:
|
|
1297
1390
|
|
|
1298
1391
|
- A `Server-Timing` header is set on the response, visible in the browser's DevTools under **Network → Timing**.
|
|
1299
|
-
- A one-line summary is logged
|
|
1392
|
+
- A one-line summary is logged at `debug` (see [Logging](#logging)):
|
|
1300
1393
|
|
|
1301
1394
|
```
|
|
1302
1395
|
[SSR] GET / — schema=1ms rsc=0ms extPlugins=0ms plugins=0ms template=2ms react=16ms | total=19ms
|
package/dist/actions.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* from somewhere else — a queue consumer, or the API role answering a builder's test run. Same runner, same
|
|
16
16
|
* checks: a trigger that skipped them would be a weaker path to the same work.
|
|
17
17
|
*/
|
|
18
|
-
export { createActionsModule } from './modules/actions';
|
|
18
|
+
export { createActionsModule, spaceKvPatterns } from './modules/actions';
|
|
19
19
|
/**
|
|
20
20
|
* Scheduled runs: a durable queue, a leaderless producer and the workers that drain it.
|
|
21
21
|
*
|
|
@@ -39,7 +39,7 @@ export { cronFiresBetween, cronMatches, cronNextFire, parseCron } from '@plitzi/
|
|
|
39
39
|
*/
|
|
40
40
|
export { checkAction } from './modules/actions/runtime/check';
|
|
41
41
|
export type { ActionCheckDeps } from './modules/actions/runtime/check';
|
|
42
|
-
export { ActionRunError } from './modules/actions/runtime/errors';
|
|
42
|
+
export { ActionRefusal, ActionRunError } from './modules/actions/runtime/errors';
|
|
43
43
|
export { DEFAULT_LIMITS } from './modules/actions/runtime/limits';
|
|
44
44
|
export { createTaskRegistry, taskName } from './modules/actions/tasks/registry';
|
|
45
45
|
export { describeCatalog, describeTask } from './modules/actions/taskCatalog';
|
|
@@ -54,6 +54,10 @@ export { describeCatalog, describeTask } from './modules/actions/taskCatalog';
|
|
|
54
54
|
*/
|
|
55
55
|
export { createMemoryKv } from './modules/actions/runtime/memoryKv';
|
|
56
56
|
export { createKvStore } from './modules/actions/runtime/kvStore';
|
|
57
|
+
export { createRedisKv } from './modules/actions/runtime/redisKv';
|
|
58
|
+
export type { RedisKvClient, RedisKvOptions } from './modules/actions/runtime/redisKv';
|
|
59
|
+
export { MAX_LIST_BYTES, MAX_LIST_ENTRIES } from './modules/actions/runtime/kvList';
|
|
60
|
+
export type { KvListEntry, KvListPut, KvListPutOptions, KvListRange } from './modules/actions/runtime/kvList';
|
|
57
61
|
/**
|
|
58
62
|
* `onRun` and `onReject` for a deployment that wants to SEE its flows without building somewhere to keep them: a
|
|
59
63
|
* run — and a request that never became one — becomes one more event on the log stream the server already
|
package/dist/actions.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { createMemoryJobQueue } from "./modules/actions/jobs/memoryQueue.js";
|
|
2
2
|
import { DEFAULT_MAX_ATTEMPTS, scheduleFor, scheduleJobId, schedulesFor } from "./modules/actions/jobs/schedules.js";
|
|
3
3
|
import { createScheduler } from "./modules/actions/jobs/scheduler.js";
|
|
4
|
-
import { ActionRunError } from "./modules/actions/runtime/errors.js";
|
|
4
|
+
import { ActionRefusal, ActionRunError } from "./modules/actions/runtime/errors.js";
|
|
5
5
|
import { createJobWorker } from "./modules/actions/jobs/worker.js";
|
|
6
6
|
import { createActionJobs } from "./modules/actions/jobs/index.js";
|
|
7
|
+
import { MAX_LIST_BYTES, MAX_LIST_ENTRIES } from "./modules/actions/runtime/kvList.js";
|
|
7
8
|
import { createKvStore } from "./modules/actions/runtime/kvStore.js";
|
|
8
9
|
import { DEFAULT_LIMITS } from "./modules/actions/runtime/limits.js";
|
|
9
10
|
import { createMemoryKv } from "./modules/actions/runtime/memoryKv.js";
|
|
11
|
+
import { spaceKvPatterns } from "./modules/actions/runtime/namespaceKv.js";
|
|
10
12
|
import { createTaskRegistry, taskName } from "./modules/actions/tasks/registry.js";
|
|
11
13
|
import { checkAction } from "./modules/actions/runtime/check.js";
|
|
12
14
|
import { createRejectLogger, createRunLogger } from "./modules/actions/runtime/runLogger.js";
|
|
13
15
|
import { describeCatalog, describeTask } from "./modules/actions/taskCatalog.js";
|
|
14
16
|
import { createActionsModule } from "./modules/actions/index.js";
|
|
17
|
+
import { createRedisKv } from "./modules/actions/runtime/redisKv.js";
|
|
15
18
|
import { cronFiresBetween, cronMatches, cronNextFire, parseCron } from "@plitzi/sdk-shared/actions";
|
|
16
|
-
export { ActionRunError, DEFAULT_LIMITS, DEFAULT_MAX_ATTEMPTS, checkAction, createActionJobs, createActionsModule, createJobWorker, createKvStore, createMemoryJobQueue, createMemoryKv, createRejectLogger, createRunLogger, createScheduler, createTaskRegistry, cronFiresBetween, cronMatches, cronNextFire, describeCatalog, describeTask, parseCron, scheduleFor, scheduleJobId, schedulesFor, taskName };
|
|
19
|
+
export { ActionRefusal, ActionRunError, DEFAULT_LIMITS, DEFAULT_MAX_ATTEMPTS, MAX_LIST_BYTES, MAX_LIST_ENTRIES, checkAction, createActionJobs, createActionsModule, createJobWorker, createKvStore, createMemoryJobQueue, createMemoryKv, createRedisKv, createRejectLogger, createRunLogger, createScheduler, createTaskRegistry, cronFiresBetween, cronMatches, cronNextFire, describeCatalog, describeTask, parseCron, scheduleFor, scheduleJobId, schedulesFor, spaceKvPatterns, taskName };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { serverLog } from "../helpers/serverLog.js";
|
|
1
2
|
//#region src/adapters/cloudAdapters.ts
|
|
2
3
|
/**
|
|
3
4
|
* A space fetched from Plitzi, for a server that is not Plitzi's.
|
|
@@ -150,17 +151,17 @@ var createCloudAdapters = (config) => {
|
|
|
150
151
|
})
|
|
151
152
|
});
|
|
152
153
|
if (!response.ok) {
|
|
153
|
-
|
|
154
|
+
serverLog.error("CloudAdapters", `${what} answered ${response.status}`);
|
|
154
155
|
return;
|
|
155
156
|
}
|
|
156
157
|
const payload = await response.json();
|
|
157
158
|
if (payload.errors?.length) {
|
|
158
|
-
|
|
159
|
+
serverLog.error("CloudAdapters", `${what}: ${payload.errors.map((error) => error.message).join("; ")}`);
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
161
162
|
return payload.data;
|
|
162
163
|
} catch (error) {
|
|
163
|
-
|
|
164
|
+
serverLog.error("CloudAdapters", `${what} could not be reached`, error);
|
|
164
165
|
return;
|
|
165
166
|
}
|
|
166
167
|
};
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { serverLog } from "../helpers/serverLog.js";
|
|
2
|
+
import { readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
3
|
//#region src/adapters/jsonAdapters.ts
|
|
3
4
|
var isDeploymentObject = (v) => typeof v === "object" && ("spaceId" in v || "environment" in v || "error" in v);
|
|
4
|
-
var
|
|
5
|
+
var createJsonReader = () => {
|
|
6
|
+
const held = /* @__PURE__ */ new Map();
|
|
7
|
+
return {
|
|
8
|
+
read: (filePath) => {
|
|
9
|
+
const { mtimeMs, size } = statSync(filePath);
|
|
10
|
+
const hit = held.get(filePath);
|
|
11
|
+
if (hit?.mtimeMs === mtimeMs && hit.size === size) return hit.value;
|
|
12
|
+
const value = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
13
|
+
held.set(filePath, {
|
|
14
|
+
mtimeMs,
|
|
15
|
+
size,
|
|
16
|
+
value
|
|
17
|
+
});
|
|
18
|
+
return value;
|
|
19
|
+
},
|
|
20
|
+
forget: (filePath) => {
|
|
21
|
+
held.delete(filePath);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
5
25
|
var createJsonAdapters = (config) => {
|
|
26
|
+
const json = createJsonReader();
|
|
6
27
|
const pathFor = (spaceId, environment, revision) => {
|
|
7
28
|
if (typeof config.offlineData === "function") return config.offlineData(spaceId, environment, revision);
|
|
8
29
|
return typeof config.offlineData === "string" ? config.offlineData : void 0;
|
|
@@ -11,9 +32,9 @@ var createJsonAdapters = (config) => {
|
|
|
11
32
|
try {
|
|
12
33
|
const filePath = pathFor(spaceId, environment, revision);
|
|
13
34
|
if (!filePath) return Promise.resolve(config.offlineData);
|
|
14
|
-
return Promise.resolve(
|
|
35
|
+
return Promise.resolve(json.read(filePath));
|
|
15
36
|
} catch (err) {
|
|
16
|
-
|
|
37
|
+
serverLog.error("JsonAdapters", "Failed to read offlineData", err);
|
|
17
38
|
return Promise.resolve(void 0);
|
|
18
39
|
}
|
|
19
40
|
};
|
|
@@ -21,6 +42,7 @@ var createJsonAdapters = (config) => {
|
|
|
21
42
|
const filePath = pathFor(spaceId, environment);
|
|
22
43
|
if (!filePath) return Promise.resolve();
|
|
23
44
|
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
45
|
+
json.forget(filePath);
|
|
24
46
|
return Promise.resolve();
|
|
25
47
|
};
|
|
26
48
|
const getSpaceDeployment = (req) => {
|
|
@@ -31,9 +53,9 @@ var createJsonAdapters = (config) => {
|
|
|
31
53
|
revision: 0
|
|
32
54
|
});
|
|
33
55
|
if (typeof deployment === "string") try {
|
|
34
|
-
return Promise.resolve(
|
|
56
|
+
return Promise.resolve(json.read(deployment));
|
|
35
57
|
} catch (err) {
|
|
36
|
-
|
|
58
|
+
serverLog.error("JsonAdapters", "Failed to read deployment file", err);
|
|
37
59
|
return Promise.resolve({ error: {
|
|
38
60
|
code: 500,
|
|
39
61
|
message: "Deployment config unreadable"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/auth.d.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
export { createAuth } from './core/auth/createAuth';
|
|
24
24
|
export { createAuthApi } from './core/auth/api';
|
|
25
25
|
export { createCsrf, csrfFailureMessage } from './core/auth/csrf';
|
|
26
|
+
export { SESSION_ACTIVITY_RESOLUTION_SECONDS, activityDue } from './core/auth/sessionActivity';
|
|
26
27
|
export { createAuthorizer, checkPermission, checkSpaceAccess, requirementFor } from './core/auth/authorize';
|
|
27
28
|
export { createCarriers, presentedOrigin } from './core/auth/credentials';
|
|
28
29
|
export { ANY_DOMAIN, corsOrigins, domainAllowed, frameAncestors, hostnameOf, normalizeDomain } from './core/auth/domains';
|
|
@@ -30,21 +31,23 @@ export { createIdentity } from './core/auth/identity';
|
|
|
30
31
|
export { generateToken, hashPassword, verifyPassword } from './core/auth/passwords';
|
|
31
32
|
export { BUILT_IN_PROVIDERS, OAuthFailure, createSocialAuth, requestProfileJson } from './core/auth/oauth';
|
|
32
33
|
export { createRedirectPolicy } from './core/auth/redirects';
|
|
34
|
+
export { isDocumentNavigation, renewForNavigation } from './core/auth/renewal';
|
|
35
|
+
export type { RenewalOptions } from './core/auth/renewal';
|
|
33
36
|
export type { RedirectPolicyConfig } from './core/auth/redirects';
|
|
34
37
|
export { applySessionOutcome, authPolicyRules, authRoutes } from './core/auth/routes';
|
|
35
|
-
export { appendCookies, clearFlowCookie, clearSessionCookies, createSessionCookies, isLocalHost, readFlowCookie, readRefreshToken, readSessionToken, sessionCookieParams, sessionHintValue, writeFlowCookie, writeSessionCookies } from './core/auth/session';
|
|
38
|
+
export { appendCookies, clearFlowCookie, clearSessionCookies, createSessionCookies, isLocalHost, parseSessionHint, readFlowCookie, readRefreshToken, readSessionHint, readSessionToken, sessionCookieParams, sessionHintValue, sessionReturnTarget, writeFlowCookie, writeSessionCookies } from './core/auth/session';
|
|
36
39
|
export { createMemoryRateLimit } from './core/auth/throttle';
|
|
37
40
|
export { createSpaceTokenApi } from './core/auth/spaceTokens';
|
|
38
41
|
export { SCOPES, authFailureMessage, createTokens, userIdOf } from './core/auth/tokens';
|
|
39
|
-
export { generateRecoveryCodes, generateTotpSecret, normalizeRecoveryCode, randomCode, totpCode, totpUri, verifyTotp } from './core/auth/totp';
|
|
42
|
+
export { generateRecoveryCodes, generateTotpSecret, normalizeRecoveryCode, randomCode, totpCode, totpStep, totpUri, verifyTotp } from './core/auth/totp';
|
|
40
43
|
export type { Auth, AuthConfig } from './core/auth/createAuth';
|
|
41
|
-
export type { AccountAccess, AccountAdapters, AccountQuery, AccountRecord, AccountStatus, AuthApi, AuthApiConfig, AuthOutcome, ExchangeResult, MfaRecord, PasswordPolicy, SecurityEvent, SessionClient, SessionContext, SessionSummary, ThrottleAttempt, ThrottledAction } from './core/auth/api';
|
|
44
|
+
export type { AccountAccess, AccountAdapters, AccountQuery, AccountRecord, AccountStatus, AuthApi, AuthApiConfig, AuthOutcome, ExchangeResult, MfaRecord, PasswordPolicy, SecurityEvent, SessionApp, SessionClient, SessionContext, SessionSummary, ThrottleAttempt, ThrottledAction } from './core/auth/api';
|
|
42
45
|
export type { AuthPolicy, AuthorizeResult, Authorizer, MembershipFacts, PathMatcher, PermissionCheck, Requirement, SpaceAccessCheck } from './core/auth/authorize';
|
|
43
46
|
export type { CredentialCarrier } from './core/auth/credentials';
|
|
44
47
|
export type { Csrf, CsrfCarrier, CsrfConfig, CsrfFailure, CsrfResult, CsrfSubject } from './core/auth/csrf';
|
|
45
48
|
export type { Actor, ActorResult, Grant, GrantOptions, GrantResult, Identity, IdentityAdapters, IdentityConfig, SpaceMembership, StoredSpaceToken } from './core/auth/identity';
|
|
46
49
|
export type { CompletedFlow, OAuthFailureReason, OAuthProfile, OAuthProvider, OAuthProviderConfig, SocialAuth, SocialAuthAdapters, SocialAuthConfig, StartedFlow } from './core/auth/oauth';
|
|
47
50
|
export type { AuthRequest, AuthRoute } from './core/auth/routes';
|
|
48
|
-
export type { CookieCarrier, CookieSink, SessionCookieParams, SessionCookies } from './core/auth/session';
|
|
51
|
+
export type { CookieCarrier, CookieSink, SessionCookieParams, SessionCookies, SessionHint } from './core/auth/session';
|
|
49
52
|
export type { SpaceTokenAdapters, SpaceTokenApi, SpaceTokenContext, SpaceTokenOutcome, SpaceTokenRecord, SpaceTokenSummary } from './core/auth/spaceTokens';
|
|
50
53
|
export type { AuthFailure, MfaChallengePayload, RefreshTokenPayload, SpaceScope, SpaceTokenOptions, SpaceTokenPayload, TokenConfig, TokenScope, Tokens, UserTokenOptions, UserTokenPayload, VerifyResult, WidgetTokenPayload } from './core/auth/tokens';
|
package/dist/auth.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import { appendCookies, clearFlowCookie, clearSessionCookies, createSessionCookies, isLocalHost, readFlowCookie, readRefreshToken, readSessionToken, sessionCookieParams, sessionHintValue, writeFlowCookie, writeSessionCookies } from "./core/auth/session.js";
|
|
1
|
+
import { appendCookies, clearFlowCookie, clearSessionCookies, createSessionCookies, isLocalHost, parseSessionHint, readFlowCookie, readRefreshToken, readSessionHint, readSessionToken, sessionCookieParams, sessionHintValue, sessionReturnTarget, writeFlowCookie, writeSessionCookies } from "./core/auth/session.js";
|
|
2
|
+
import { createCsrf, csrfFailureMessage } from "./core/auth/csrf.js";
|
|
3
|
+
import { applySessionOutcome, authPolicyRules, authRoutes } from "./core/auth/routes.js";
|
|
4
|
+
import { isDocumentNavigation, renewForNavigation } from "./core/auth/renewal.js";
|
|
5
|
+
import { createCarriers, presentedOrigin } from "./core/auth/credentials.js";
|
|
2
6
|
import { ANY_DOMAIN, corsOrigins, domainAllowed, frameAncestors, hostnameOf, normalizeDomain } from "./core/auth/domains.js";
|
|
3
7
|
import { checkPermission, checkSpaceAccess, createAuthorizer, requirementFor } from "./core/auth/authorize.js";
|
|
4
8
|
import { generateToken, hashPassword, verifyPassword } from "./core/auth/passwords.js";
|
|
5
9
|
import { createMemoryRateLimit } from "./core/auth/throttle.js";
|
|
6
10
|
import { SCOPES, authFailureMessage, createTokens, userIdOf } from "./core/auth/tokens.js";
|
|
7
|
-
import { generateRecoveryCodes, generateTotpSecret, normalizeRecoveryCode, randomCode, totpCode, totpUri, verifyTotp } from "./core/auth/totp.js";
|
|
11
|
+
import { generateRecoveryCodes, generateTotpSecret, normalizeRecoveryCode, randomCode, totpCode, totpStep, totpUri, verifyTotp } from "./core/auth/totp.js";
|
|
8
12
|
import { createAuthApi } from "./core/auth/api.js";
|
|
9
|
-
import { createCarriers, presentedOrigin } from "./core/auth/credentials.js";
|
|
10
|
-
import { createCsrf, csrfFailureMessage } from "./core/auth/csrf.js";
|
|
11
13
|
import { createIdentity } from "./core/auth/identity.js";
|
|
12
|
-
import { applySessionOutcome, authPolicyRules, authRoutes } from "./core/auth/routes.js";
|
|
13
14
|
import { createAuth } from "./core/auth/createAuth.js";
|
|
15
|
+
import { SESSION_ACTIVITY_RESOLUTION_SECONDS, activityDue } from "./core/auth/sessionActivity.js";
|
|
14
16
|
import { OAuthFailure } from "./core/auth/oauth/types.js";
|
|
15
17
|
import { requestProfileJson } from "./core/auth/oauth/client.js";
|
|
16
18
|
import { createRedirectPolicy } from "./core/auth/redirects.js";
|
|
17
19
|
import { BUILT_IN_PROVIDERS, createSocialAuth } from "./core/auth/oauth/index.js";
|
|
18
20
|
import { createSpaceTokenApi } from "./core/auth/spaceTokens.js";
|
|
19
|
-
export { ANY_DOMAIN, BUILT_IN_PROVIDERS, OAuthFailure, SCOPES, appendCookies, applySessionOutcome, authFailureMessage, authPolicyRules, authRoutes, checkPermission, checkSpaceAccess, clearFlowCookie, clearSessionCookies, corsOrigins, createAuth, createAuthApi, createAuthorizer, createCarriers, createCsrf, createIdentity, createMemoryRateLimit, createRedirectPolicy, createSessionCookies, createSocialAuth, createSpaceTokenApi, createTokens, csrfFailureMessage, domainAllowed, frameAncestors, generateRecoveryCodes, generateToken, generateTotpSecret, hashPassword, hostnameOf, isLocalHost, normalizeDomain, normalizeRecoveryCode, presentedOrigin, randomCode, readFlowCookie, readRefreshToken, readSessionToken, requestProfileJson, requirementFor, sessionCookieParams, sessionHintValue, totpCode, totpUri, userIdOf, verifyPassword, verifyTotp, writeFlowCookie, writeSessionCookies };
|
|
21
|
+
export { ANY_DOMAIN, BUILT_IN_PROVIDERS, OAuthFailure, SCOPES, SESSION_ACTIVITY_RESOLUTION_SECONDS, activityDue, appendCookies, applySessionOutcome, authFailureMessage, authPolicyRules, authRoutes, checkPermission, checkSpaceAccess, clearFlowCookie, clearSessionCookies, corsOrigins, createAuth, createAuthApi, createAuthorizer, createCarriers, createCsrf, createIdentity, createMemoryRateLimit, createRedirectPolicy, createSessionCookies, createSocialAuth, createSpaceTokenApi, createTokens, csrfFailureMessage, domainAllowed, frameAncestors, generateRecoveryCodes, generateToken, generateTotpSecret, hashPassword, hostnameOf, isDocumentNavigation, isLocalHost, normalizeDomain, normalizeRecoveryCode, parseSessionHint, presentedOrigin, randomCode, readFlowCookie, readRefreshToken, readSessionHint, readSessionToken, renewForNavigation, requestProfileJson, requirementFor, sessionCookieParams, sessionHintValue, sessionReturnTarget, totpCode, totpStep, totpUri, userIdOf, verifyPassword, verifyTotp, writeFlowCookie, writeSessionCookies };
|
package/dist/core/auth/api.d.ts
CHANGED
|
@@ -36,6 +36,16 @@ export interface AccountAccess {
|
|
|
36
36
|
export interface SessionClient {
|
|
37
37
|
userAgent?: string;
|
|
38
38
|
ip?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The application holding it, when it is not a browser: a native client names itself when it registers
|
|
41
|
+
* (`Plitzi CLI on carlos-mbp`, software `plitzi-cli`). A user agent says which browser; this says which app.
|
|
42
|
+
*/
|
|
43
|
+
app?: SessionApp;
|
|
44
|
+
}
|
|
45
|
+
export interface SessionApp {
|
|
46
|
+
name: string;
|
|
47
|
+
/** A stable name for the software, the same on every machine — what a list picks an icon by. */
|
|
48
|
+
softwareId?: string;
|
|
39
49
|
}
|
|
40
50
|
/**
|
|
41
51
|
* What an account is allowed to be. `inactive` is the account's own doing (deactivated, never confirmed);
|
|
@@ -47,7 +57,10 @@ export interface SessionSummary {
|
|
|
47
57
|
id: number;
|
|
48
58
|
userAgent?: string;
|
|
49
59
|
ip?: string;
|
|
60
|
+
app?: SessionApp;
|
|
50
61
|
createdAt: number;
|
|
62
|
+
/** When it was last used, where the store keeps it; a session never seen since it was made has none. */
|
|
63
|
+
lastActiveAt?: number;
|
|
51
64
|
expiresAt: number;
|
|
52
65
|
/** The session asking. A device list without it invites someone to revoke the one they are using. */
|
|
53
66
|
current: boolean;
|
|
@@ -58,6 +71,12 @@ export interface MfaRecord {
|
|
|
58
71
|
/** Unix seconds the enrolment was proven with a real code. Absent means started and never finished. */
|
|
59
72
|
confirmedAt?: number;
|
|
60
73
|
recoveryCodes?: string[];
|
|
74
|
+
/**
|
|
75
|
+
* The time step of the last code that was accepted. A code of that step or an earlier one is refused: a code is valid
|
|
76
|
+
* for its whole window, and one watched over a shoulder or lifted from a phishing page would otherwise sign a second
|
|
77
|
+
* person in within that window (RFC 6238 §5.2).
|
|
78
|
+
*/
|
|
79
|
+
lastUsedStep?: number;
|
|
61
80
|
}
|
|
62
81
|
/** Something worth writing down. Fed to an audit log, a webhook, a SIEM — whatever the deployment has. */
|
|
63
82
|
export interface SecurityEvent {
|
|
@@ -138,6 +157,15 @@ export interface AccountAdapters {
|
|
|
138
157
|
* session, which deleting the account's sessions does.
|
|
139
158
|
*/
|
|
140
159
|
deleteAccount?: (userId: number) => Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* What would be lost if this account were deleted now, in sentences its owner can act on — "“Studio” has 3 spaces;
|
|
162
|
+
* delete them or make somebody else an owner". Any at all and the deletion is refused, with the list.
|
|
163
|
+
*
|
|
164
|
+
* The account is what holds things a deployment cannot simply drop: a workspace nobody else owns, the spaces in it,
|
|
165
|
+
* a plan still being paid for. Deleting it anyway leaves them orphaned — unreachable by anyone, and possibly still
|
|
166
|
+
* billed. Optional, for a deployment whose accounts own nothing.
|
|
167
|
+
*/
|
|
168
|
+
deletionBlockers?: (userId: number) => Promise<string[]>;
|
|
141
169
|
/** Page through accounts, for an administrator. `total` is the count before the page was taken. */
|
|
142
170
|
listAccounts?: (query: AccountQuery) => Promise<{
|
|
143
171
|
accounts: AccountRecord[];
|
|
@@ -298,7 +326,7 @@ export interface AuthApiConfig {
|
|
|
298
326
|
allowed: boolean;
|
|
299
327
|
retryAfter?: number;
|
|
300
328
|
}>;
|
|
301
|
-
/** Where a failed delivery is reported. Defaults to `
|
|
329
|
+
/** Where a failed delivery is reported. Defaults to the server log at `error`; it is never thrown — see `deliver`. */
|
|
302
330
|
onMailError?: (error: unknown, message: {
|
|
303
331
|
to: string;
|
|
304
332
|
template: string;
|
package/dist/core/auth/api.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
import { serverLog } from "../../helpers/serverLog.js";
|
|
2
|
+
import { forwardedIp } from "../requestParser.js";
|
|
1
3
|
import { checkPermission } from "./authorize.js";
|
|
2
4
|
import { generateToken, hashPassword, verifyPassword } from "./passwords.js";
|
|
3
|
-
import { createMemoryRateLimit } from "./throttle.js";
|
|
5
|
+
import { createMemoryRateLimit, fleetRateLimit } from "./throttle.js";
|
|
4
6
|
import { authFailureMessage } from "./tokens.js";
|
|
5
|
-
import { generateRecoveryCodes, generateTotpSecret, normalizeRecoveryCode, randomCode,
|
|
7
|
+
import { generateRecoveryCodes, generateTotpSecret, normalizeRecoveryCode, randomCode, totpStep, totpUri } from "./totp.js";
|
|
6
8
|
import { createHash } from "node:crypto";
|
|
7
9
|
//#region src/core/auth/api.ts
|
|
8
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* The user agent and the address, off whatever carried the request — what lets somebody tell their devices apart.
|
|
12
|
+
* Nothing else here reads headers, so it is done once. The address is the host's when it resolved one, the proxies'
|
|
13
|
+
* otherwise; it names a session in a list and decides nothing.
|
|
14
|
+
*/
|
|
9
15
|
var clientOf = (carrier) => {
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
if (!carrier) return;
|
|
17
|
+
const userAgent = carrier.headers["user-agent"];
|
|
18
|
+
const ip = carrier.ip || forwardedIp(carrier.headers);
|
|
19
|
+
const client = {
|
|
20
|
+
...typeof userAgent === "string" && userAgent ? { userAgent } : {},
|
|
21
|
+
...ip ? { ip } : {}
|
|
22
|
+
};
|
|
23
|
+
return client.userAgent || client.ip ? client : void 0;
|
|
12
24
|
};
|
|
13
25
|
var refuse = (status, error, reason) => ({
|
|
14
26
|
ok: false,
|
|
@@ -88,7 +100,7 @@ var profileOf = (account) => ({
|
|
|
88
100
|
* form without either owning it.
|
|
89
101
|
*/
|
|
90
102
|
var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
91
|
-
const { verifyPassword: verifyPassword$1 = verifyPassword, hashPassword: hashPassword$1 = hashPassword, generateToken: generateToken$1 = generateToken, verifyOnSignup = false, adminPermission = "userManage", impersonationPermission, password: policy = {}, rateLimit = createMemoryRateLimit(), onMailError, onEvent, mfaIssuer, csrf } = config;
|
|
103
|
+
const { verifyPassword: verifyPassword$1 = verifyPassword, hashPassword: hashPassword$1 = hashPassword, generateToken: generateToken$1 = generateToken, verifyOnSignup = false, adminPermission = "userManage", impersonationPermission, password: policy = {}, rateLimit = fleetRateLimit() ?? createMemoryRateLimit(), onMailError, onEvent, mfaIssuer, csrf } = config;
|
|
92
104
|
/** Never awaited, never able to throw: a logging outage must not become an authentication outage. */
|
|
93
105
|
const record = (event) => {
|
|
94
106
|
if (!onEvent) return;
|
|
@@ -98,7 +110,7 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
98
110
|
at: Math.floor(Date.now() / 1e3)
|
|
99
111
|
});
|
|
100
112
|
} catch (error) {
|
|
101
|
-
|
|
113
|
+
serverLog.error("auth", "security event handler threw", error);
|
|
102
114
|
}
|
|
103
115
|
};
|
|
104
116
|
/**
|
|
@@ -132,7 +144,7 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
132
144
|
try {
|
|
133
145
|
await adapters.sendMail?.(message);
|
|
134
146
|
} catch (error) {
|
|
135
|
-
(onMailError ?? ((cause) =>
|
|
147
|
+
(onMailError ?? ((cause) => serverLog.error("auth", "could not send mail", cause)))(error, {
|
|
136
148
|
to: message.to,
|
|
137
149
|
template: message.template
|
|
138
150
|
});
|
|
@@ -386,6 +398,11 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
386
398
|
return refuse(401, authFailureMessage[reason], reason);
|
|
387
399
|
}
|
|
388
400
|
if ((capabilities.mfa ? await adapters.loadMfa?.(account.id) : void 0)?.confirmedAt) {
|
|
401
|
+
throttleSucceeded({
|
|
402
|
+
action: "login",
|
|
403
|
+
key: username,
|
|
404
|
+
carrier
|
|
405
|
+
});
|
|
389
406
|
record({
|
|
390
407
|
type: "login.mfa-required",
|
|
391
408
|
userId: account.id,
|
|
@@ -444,17 +461,23 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
444
461
|
const stored = mfa.recoveryCodes ?? [];
|
|
445
462
|
const supplied = digestRecoveryCode(code);
|
|
446
463
|
const usedRecovery = stored.includes(supplied);
|
|
447
|
-
|
|
464
|
+
const step = usedRecovery ? void 0 : totpStep(mfa.secret, code);
|
|
465
|
+
const replayed = step !== void 0 && mfa.lastUsedStep !== void 0 && step <= mfa.lastUsedStep;
|
|
466
|
+
if (!usedRecovery && (step === void 0 || replayed)) {
|
|
448
467
|
record({
|
|
449
468
|
type: "mfa.failed",
|
|
450
469
|
userId,
|
|
451
|
-
carrier
|
|
470
|
+
carrier,
|
|
471
|
+
...replayed ? { detail: { replayed: true } } : {}
|
|
452
472
|
});
|
|
453
473
|
return refuse(401, "Invalid code");
|
|
454
474
|
}
|
|
455
|
-
|
|
475
|
+
await adapters.saveMfa?.(userId, usedRecovery ? {
|
|
456
476
|
...mfa,
|
|
457
477
|
recoveryCodes: stored.filter((entry) => entry !== supplied)
|
|
478
|
+
} : {
|
|
479
|
+
...mfa,
|
|
480
|
+
lastUsedStep: step
|
|
458
481
|
});
|
|
459
482
|
const session = await issue(userId, { client: clientOf(carrier) });
|
|
460
483
|
throttleSucceeded({
|
|
@@ -631,7 +654,8 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
631
654
|
const mfa = await adapters.loadMfa?.(actor.id);
|
|
632
655
|
if (!mfa) return refuse(400, "Start the enrolment first");
|
|
633
656
|
if (mfa.confirmedAt) return refuse(409, "A second factor is already set up");
|
|
634
|
-
|
|
657
|
+
const step = totpStep(mfa.secret, code);
|
|
658
|
+
if (step === void 0) {
|
|
635
659
|
record({
|
|
636
660
|
type: "mfa.failed",
|
|
637
661
|
userId: actor.id
|
|
@@ -642,7 +666,8 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
642
666
|
await adapters.saveMfa?.(actor.id, {
|
|
643
667
|
...mfa,
|
|
644
668
|
confirmedAt: Math.floor(Date.now() / 1e3),
|
|
645
|
-
recoveryCodes: plain.map(digestRecoveryCode)
|
|
669
|
+
recoveryCodes: plain.map(digestRecoveryCode),
|
|
670
|
+
lastUsedStep: step
|
|
646
671
|
});
|
|
647
672
|
record({
|
|
648
673
|
type: "mfa.enabled",
|
|
@@ -1070,6 +1095,15 @@ var createAuthApi = ({ tokens, identity, adapters, config = {} }) => {
|
|
|
1070
1095
|
if (account?.passwordHash) {
|
|
1071
1096
|
if (!password || !await verifyPassword$1(password, account.passwordHash)) return refuse(401, "Invalid credentials");
|
|
1072
1097
|
}
|
|
1098
|
+
const blockers = await adapters.deletionBlockers?.(actor.id) ?? [];
|
|
1099
|
+
if (blockers.length > 0) return {
|
|
1100
|
+
ok: false,
|
|
1101
|
+
status: 409,
|
|
1102
|
+
body: {
|
|
1103
|
+
error: "This account still owns things that would be lost with it",
|
|
1104
|
+
blockers
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1073
1107
|
await adapters.deleteAccount(actor.id);
|
|
1074
1108
|
record({
|
|
1075
1109
|
type: "account.deleted",
|
|
@@ -177,6 +177,9 @@ export declare const createAuth: (config: AuthConfig) => {
|
|
|
177
177
|
hostname: string;
|
|
178
178
|
}, res: CookieSink) => void;
|
|
179
179
|
readFlow: (req: import('./session').CookieCarrier) => string | undefined;
|
|
180
|
+
returnTarget: (req: {
|
|
181
|
+
hostname: string;
|
|
182
|
+
}, target: unknown) => string;
|
|
180
183
|
resolveSessionToken: (req: import('./session').CookieCarrier) => string | undefined;
|
|
181
184
|
resolveRefreshToken: (req: import('./session').CookieCarrier, body?: unknown) => string | undefined;
|
|
182
185
|
};
|
|
@@ -202,6 +205,7 @@ export declare const createAuth: (config: AuthConfig) => {
|
|
|
202
205
|
}, res: CookieSink) => void;
|
|
203
206
|
read: (carrier: import('./csrf').CsrfCarrier) => string | undefined;
|
|
204
207
|
required: (carrier: import('./csrf').CsrfCarrier, subject?: import('./csrf').CsrfSubject) => boolean;
|
|
208
|
+
crossSite: (carrier: import('./csrf').CsrfCarrier, alsoAllowed?: readonly string[]) => boolean;
|
|
205
209
|
verify: (carrier: import('./csrf').CsrfCarrier, sessionToken?: string) => import('./csrf').CsrfResult;
|
|
206
210
|
} | undefined;
|
|
207
211
|
/** The `/auth` flows as descriptors, ready for whatever serves HTTP here. */
|