@plitzi/sdk-server 0.33.2 → 0.34.1
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 +54 -0
- package/README.md +283 -8
- package/dist/actions.d.ts +62 -0
- package/dist/actions.js +12 -0
- package/dist/adapters/cloudAdapters.d.ts +72 -0
- package/dist/adapters/cloudAdapters.js +303 -0
- package/dist/adapters/space/index.d.ts +93 -0
- package/dist/adapters/space/index.js +122 -0
- package/dist/adapters/space/resolvers.d.ts +69 -0
- package/dist/adapters/space/resolvers.js +79 -0
- package/dist/adapters/space/space.test.d.ts +1 -0
- package/dist/adapters/space/types.d.ts +64 -0
- package/dist/adapters/space/types.js +41 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.js +19 -0
- package/dist/core/auth/api.d.ts +400 -11
- package/dist/core/auth/api.js +1044 -50
- package/dist/core/auth/createAuth.d.ts +135 -30
- package/dist/core/auth/createAuth.js +96 -24
- package/dist/core/auth/credentials.d.ts +9 -3
- package/dist/core/auth/credentials.js +10 -4
- package/dist/core/auth/csrf.d.ts +89 -0
- package/dist/core/auth/csrf.js +234 -0
- package/dist/core/auth/csrf.test.d.ts +1 -0
- package/dist/core/auth/identity.d.ts +6 -0
- package/dist/core/auth/identity.js +37 -8
- package/dist/core/auth/lifecycle.test.d.ts +1 -0
- package/dist/core/auth/oauth/index.d.ts +8 -0
- package/dist/core/auth/oauth/index.js +13 -15
- package/dist/core/auth/passwords.d.ts +15 -0
- package/dist/core/auth/passwords.js +32 -0
- package/dist/core/auth/redirects.d.ts +18 -0
- package/dist/core/auth/redirects.js +31 -0
- package/dist/core/auth/redirects.test.d.ts +1 -0
- package/dist/core/auth/routes.d.ts +4 -2
- package/dist/core/auth/routes.js +234 -9
- package/dist/core/auth/session.d.ts +11 -0
- package/dist/core/auth/session.js +1 -1
- package/dist/core/auth/spaceTokens.d.ts +38 -2
- package/dist/core/auth/spaceTokens.js +54 -2
- package/dist/core/auth/throttle.d.ts +5 -0
- package/dist/core/auth/throttle.js +81 -0
- package/dist/core/auth/throttle.test.d.ts +1 -0
- package/dist/core/auth/tokens.d.ts +83 -12
- package/dist/core/auth/tokens.js +51 -9
- package/dist/core/auth/totp.d.ts +48 -0
- package/dist/core/auth/totp.js +118 -0
- package/dist/core/auth/totp.test.d.ts +1 -0
- package/dist/core/createServer.d.ts +0 -6
- package/dist/core/createServer.js +35 -3
- package/dist/core/fontStore.d.ts +33 -0
- package/dist/core/fontStore.js +53 -0
- package/dist/core/fontStore.test.d.ts +1 -0
- package/dist/core/handlers/authMiddleware.js +1 -1
- package/dist/core/handlers/authRouteHandlers.d.ts +9 -1
- package/dist/core/handlers/authRouteHandlers.js +4 -3
- package/dist/core/handlers/csrfMiddleware.d.ts +20 -0
- package/dist/core/handlers/csrfMiddleware.js +32 -0
- package/dist/core/handlers/oauthRouteHandlers.d.ts +46 -0
- package/dist/core/handlers/oauthRouteHandlers.js +159 -0
- package/dist/core/handlers/socialRouteHandlers.d.ts +40 -0
- package/dist/core/handlers/socialRouteHandlers.js +129 -0
- package/dist/core/handlers/socialRouteHandlers.test.d.ts +1 -0
- package/dist/core/handlers/types.d.ts +22 -5
- package/dist/core/health.d.ts +11 -1
- package/dist/core/health.js +27 -3
- package/dist/core/http/dispatcher.d.ts +1 -1
- package/dist/core/http/dispatcher.js +20 -1
- package/dist/core/http/stages/authApi.js +1 -1
- package/dist/core/http/stages/fontAssets.d.ts +9 -0
- package/dist/core/http/stages/fontAssets.js +27 -0
- package/dist/core/http/stages/oauth.d.ts +19 -0
- package/dist/core/http/stages/oauth.js +118 -0
- package/dist/core/http/types.d.ts +12 -0
- package/dist/core/oauth/authorize.d.ts +22 -0
- package/dist/core/oauth/authorize.js +254 -0
- package/dist/core/oauth/authorize.test.d.ts +1 -0
- package/dist/core/oauth/challenge.d.ts +11 -0
- package/dist/core/oauth/challenge.js +33 -0
- package/dist/core/oauth/consentPage.d.ts +12 -0
- package/dist/core/oauth/consentPage.js +131 -0
- package/dist/core/oauth/metadata.d.ts +41 -0
- package/dist/core/oauth/metadata.js +71 -0
- package/dist/core/oauth/params.d.ts +5 -0
- package/dist/core/oauth/params.js +5 -0
- package/dist/core/oauth/pkce.d.ts +6 -0
- package/dist/core/oauth/pkce.js +15 -0
- package/dist/core/oauth/records.d.ts +72 -0
- package/dist/core/oauth/records.js +49 -0
- package/dist/core/oauth/register.d.ts +5 -0
- package/dist/core/oauth/register.js +55 -0
- package/dist/core/oauth/respond.d.ts +21 -0
- package/dist/core/oauth/respond.js +59 -0
- package/dist/core/oauth/token.d.ts +14 -0
- package/dist/core/oauth/token.js +130 -0
- package/dist/core/previewToken.d.ts +20 -0
- package/dist/core/previewToken.js +29 -1
- package/dist/core/server/pageServer.js +4 -1
- package/dist/core/services/action.d.ts +6 -4
- package/dist/core/services/action.js +103 -10
- package/dist/core/services/registry.js +2 -0
- package/dist/core/services/resolve.d.ts +8 -0
- package/dist/core/services/resolve.js +12 -1
- package/dist/core/staticFiles.js +1 -2
- package/dist/handlers.d.ts +7 -1
- package/dist/handlers.js +4 -1
- package/dist/helpers/buildResponseHelpers.d.ts +8 -0
- package/dist/helpers/buildResponseHelpers.js +9 -1
- package/dist/helpers/buildServerInfo.d.ts +8 -3
- package/dist/helpers/buildServerInfo.js +18 -14
- package/dist/helpers/cache/keys.d.ts +13 -0
- package/dist/helpers/cache/keys.js +12 -1
- package/dist/helpers/onAbort.d.ts +12 -0
- package/dist/helpers/onAbort.js +22 -0
- package/dist/helpers/onAbort.test.d.ts +1 -0
- package/dist/helpers/outboundGuard.d.ts +27 -0
- package/dist/helpers/outboundGuard.js +49 -0
- package/dist/helpers/outboundGuard.test.d.ts +1 -0
- package/dist/helpers/serverLog.d.ts +6 -3
- package/dist/helpers/serverLog.js +20 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -4
- package/dist/kernel.d.ts +5 -22
- package/dist/kernel.js +1 -14
- package/dist/modules/actions/{handler.js → connectorWrite.js} +5 -2
- package/dist/modules/actions/connectorWrite.test.d.ts +1 -0
- package/dist/modules/actions/index.d.ts +40 -0
- package/dist/modules/actions/index.js +46 -0
- package/dist/modules/actions/moduleFor.d.ts +3 -0
- package/dist/modules/actions/moduleFor.js +35 -0
- package/dist/modules/actions/moduleFor.test.d.ts +1 -0
- package/dist/modules/actions/runtime/check.d.ts +24 -0
- package/dist/modules/actions/runtime/check.js +111 -0
- package/dist/modules/actions/runtime/check.test.d.ts +1 -0
- package/dist/modules/actions/runtime/errors.d.ts +11 -0
- package/dist/modules/actions/runtime/errors.js +17 -0
- package/dist/modules/actions/runtime/guards.d.ts +126 -0
- package/dist/modules/actions/runtime/guards.js +246 -0
- package/dist/modules/actions/runtime/guards.test.d.ts +1 -0
- package/dist/modules/actions/runtime/kvStore.d.ts +27 -0
- package/dist/modules/actions/runtime/kvStore.js +41 -0
- package/dist/modules/actions/runtime/kvStore.test.d.ts +1 -0
- package/dist/modules/actions/runtime/limits.d.ts +11 -0
- package/dist/modules/actions/runtime/limits.js +35 -0
- package/dist/modules/actions/runtime/memoryKv.d.ts +2 -0
- package/dist/modules/actions/runtime/memoryKv.js +77 -0
- package/dist/modules/actions/runtime/namespaceKv.d.ts +9 -0
- package/dist/modules/actions/runtime/namespaceKv.js +19 -0
- package/dist/modules/actions/runtime/precheck.d.ts +27 -0
- package/dist/modules/actions/runtime/precheck.js +45 -0
- package/dist/modules/actions/runtime/renderResolver.d.ts +15 -0
- package/dist/modules/actions/runtime/renderResolver.js +130 -0
- package/dist/modules/actions/runtime/renderResolver.test.d.ts +1 -0
- package/dist/modules/actions/runtime/renderShare.d.ts +29 -0
- package/dist/modules/actions/runtime/renderShare.js +36 -0
- package/dist/modules/actions/runtime/renderShare.test.d.ts +1 -0
- package/dist/modules/actions/runtime/report.d.ts +14 -0
- package/dist/modules/actions/runtime/report.js +22 -0
- package/dist/modules/actions/runtime/runAction.d.ts +12 -0
- package/dist/modules/actions/runtime/runAction.js +405 -0
- package/dist/modules/actions/runtime/runAction.test.d.ts +1 -0
- package/dist/modules/actions/runtime/runLogger.d.ts +31 -0
- package/dist/modules/actions/runtime/runLogger.js +66 -0
- package/dist/modules/actions/runtime/runLogger.test.d.ts +1 -0
- package/dist/modules/actions/runtime/schedule.d.ts +35 -0
- package/dist/modules/actions/runtime/schedule.js +76 -0
- package/dist/modules/actions/runtime/schedule.test.d.ts +1 -0
- package/dist/modules/actions/runtime/scope.d.ts +40 -0
- package/dist/modules/actions/runtime/scope.js +103 -0
- package/dist/modules/actions/runtime/triggers.d.ts +12 -0
- package/dist/modules/actions/runtime/triggers.js +14 -0
- package/dist/modules/actions/taskCatalog.d.ts +26 -0
- package/dist/modules/actions/taskCatalog.js +24 -0
- package/dist/modules/actions/taskCatalog.test.d.ts +1 -0
- package/dist/modules/actions/tasks/auth.d.ts +2 -0
- package/dist/modules/actions/tasks/auth.js +40 -0
- package/dist/modules/actions/tasks/builtins.d.ts +8 -0
- package/dist/modules/actions/tasks/builtins.js +24 -0
- package/dist/modules/actions/tasks/connector.d.ts +2 -0
- package/dist/modules/actions/tasks/connector.js +141 -0
- package/dist/modules/actions/tasks/db.d.ts +2 -0
- package/dist/modules/actions/tasks/db.js +59 -0
- package/dist/modules/actions/tasks/db.test.d.ts +1 -0
- package/dist/modules/actions/tasks/flow.d.ts +3 -0
- package/dist/modules/actions/tasks/flow.js +109 -0
- package/dist/modules/actions/tasks/helpers.d.ts +16 -0
- package/dist/modules/actions/tasks/helpers.js +30 -0
- package/dist/modules/actions/tasks/http.d.ts +2 -0
- package/dist/modules/actions/tasks/http.js +111 -0
- package/dist/modules/actions/tasks/http.test.d.ts +1 -0
- package/dist/modules/actions/tasks/kv.d.ts +2 -0
- package/dist/modules/actions/tasks/kv.js +102 -0
- package/dist/modules/actions/tasks/kv.test.d.ts +1 -0
- package/dist/modules/actions/tasks/registry.d.ts +10 -0
- package/dist/modules/actions/tasks/registry.js +36 -0
- package/dist/modules/actions/tasks/transform.d.ts +2 -0
- package/dist/modules/actions/tasks/transform.js +34 -0
- package/dist/modules/actions/transport/callHandler.d.ts +24 -0
- package/dist/modules/actions/transport/callHandler.js +233 -0
- package/dist/modules/actions/transport/callHandler.test.d.ts +1 -0
- package/dist/modules/actions/transport/cancelHandler.d.ts +23 -0
- package/dist/modules/actions/transport/cancelHandler.js +23 -0
- package/dist/modules/actions/transport/catalogHandler.d.ts +18 -0
- package/dist/modules/actions/transport/catalogHandler.js +21 -0
- package/dist/modules/actions/transport/stream.d.ts +23 -0
- package/dist/modules/actions/transport/stream.js +100 -0
- package/dist/modules/actions/transport/verifySignature.d.ts +16 -0
- package/dist/modules/actions/transport/verifySignature.js +72 -0
- package/dist/modules/actions/transport/webhookHandler.d.ts +25 -0
- package/dist/modules/actions/transport/webhookHandler.js +206 -0
- package/dist/modules/actions/transport/webhookHandler.test.d.ts +1 -0
- package/dist/modules/actions/types.d.ts +234 -0
- package/dist/modules/connectors/engine.js +4 -1
- package/dist/modules/connectors/resolver.d.ts +10 -2
- package/dist/modules/connectors/resolver.js +8 -3
- package/dist/modules/mysql/accounts.d.ts +5 -0
- package/dist/modules/mysql/accounts.js +365 -0
- package/dist/modules/mysql/accounts.test.d.ts +1 -0
- package/dist/modules/mysql/admin.d.ts +58 -0
- package/dist/modules/mysql/admin.js +133 -0
- package/dist/modules/mysql/config.d.ts +82 -0
- package/dist/modules/mysql/config.js +30 -0
- package/dist/modules/mysql/identities.d.ts +35 -0
- package/dist/modules/mysql/identities.js +43 -0
- package/dist/modules/mysql/index.d.ts +67 -0
- package/dist/modules/mysql/index.js +62 -0
- package/dist/modules/mysql/pool.d.ts +3 -0
- package/dist/modules/mysql/pool.js +88 -0
- package/dist/modules/mysql/query.d.ts +12 -0
- package/dist/modules/mysql/query.js +21 -0
- package/dist/modules/mysql/schema.d.ts +43 -0
- package/dist/modules/mysql/schema.js +340 -0
- package/dist/modules/mysql/schema.test.d.ts +1 -0
- package/dist/modules/mysql/sessions.d.ts +33 -0
- package/dist/modules/mysql/sessions.js +46 -0
- package/dist/modules/mysql/spaceTokens.d.ts +11 -0
- package/dist/modules/mysql/spaceTokens.js +59 -0
- package/dist/modules/mysql/store.integration.test.d.ts +1 -0
- package/dist/modules/rsc/connectorRscData.d.ts +25 -0
- package/dist/modules/rsc/connectorRscData.js +51 -0
- package/dist/modules/rsc/connectorRscData.test.d.ts +1 -0
- package/dist/modules/rsc/handler.js +16 -3
- package/dist/modules/rsc/matchRscPage.d.ts +14 -0
- package/dist/modules/rsc/matchRscPage.js +26 -0
- package/dist/modules/rsc/resolveRscData.d.ts +14 -1
- package/dist/modules/rsc/resolveRscData.js +23 -34
- package/dist/modules/ssr/Component.d.ts +6 -2
- package/dist/modules/ssr/Component.js +3 -1
- package/dist/modules/ssr/draftSession.test.d.ts +1 -0
- package/dist/modules/ssr/handler.js +15 -2
- package/dist/modules/ssr/loadPluginComponents.js +22 -5
- package/dist/modules/ssr/prepareRender.js +87 -8
- package/dist/modules/ssr/prepareRender.test.d.ts +1 -0
- package/dist/modules/ssr/preview.d.ts +21 -8
- package/dist/modules/ssr/preview.js +38 -14
- package/dist/modules/ssr/registerExternalPlugins.js +2 -2
- package/dist/modules/ssr/resolvePageSeo.d.ts +18 -0
- package/dist/modules/ssr/resolvePageSeo.js +26 -0
- package/dist/modules/ssr/resolvePageSeo.test.d.ts +1 -0
- package/dist/modules/ssr/template.test.d.ts +1 -0
- package/dist/modules/ssr/views/template.ejs +71 -5
- package/dist/mysql.d.ts +26 -0
- package/dist/mysql.js +4 -0
- package/dist/oauth.d.ts +24 -0
- package/dist/oauth.js +10 -0
- package/dist/plugins/compile.d.ts +1 -0
- package/dist/plugins/compile.js +37 -3
- package/dist/plugins/manager.d.ts +40 -0
- package/dist/plugins/manager.js +113 -13
- package/dist/plugins/manager.test.d.ts +1 -0
- package/package.json +43 -14
- package/skills/plitzi-authoring/SKILL.md +259 -0
- /package/dist/{modules/actions/handler.test.d.ts → adapters/cloudAdapters.test.d.ts} +0 -0
- /package/dist/modules/actions/{handler.d.ts → connectorWrite.d.ts} +0 -0
|
@@ -1,20 +1,113 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { handleActionCall } from "../../modules/actions/transport/callHandler.js";
|
|
2
|
+
import { handleActionCatalog } from "../../modules/actions/transport/catalogHandler.js";
|
|
3
|
+
import { handleActionWebhook } from "../../modules/actions/transport/webhookHandler.js";
|
|
4
|
+
import { handleActionCancel } from "../../modules/actions/transport/cancelHandler.js";
|
|
5
|
+
import { clientIp, readRawBody } from "../requestParser.js";
|
|
6
|
+
import { handleAction } from "../../modules/actions/connectorWrite.js";
|
|
3
7
|
//#region src/core/services/action.ts
|
|
8
|
+
var LINEAGE_HEADER = "x-plitzi-action-lineage";
|
|
9
|
+
var safeParse = (body) => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(body);
|
|
12
|
+
} catch {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
4
16
|
/**
|
|
5
|
-
*
|
|
6
|
-
* connected backend — and it answers POST on its own path alone, never falling through to SSR.
|
|
17
|
+
* Who is asking, for the single-flight key and for cancellation ownership.
|
|
7
18
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
19
|
+
* A signed-in visitor is themselves; anyone else is their address. The address is a coarse identity — a shared
|
|
20
|
+
* NAT makes two visitors look like one, which single-flight would read as a duplicate — so it is only ever used
|
|
21
|
+
* for a `public` action, and the derived key also folds in the input: two people submitting different forms do
|
|
22
|
+
* not collide.
|
|
23
|
+
*/
|
|
24
|
+
var callerIdOf = (ctx, req) => req.ctx.user ? `user:${req.ctx.user.id}` : `ip:${clientIp(ctx.raw, req)}`;
|
|
25
|
+
var lineageOf = (req) => {
|
|
26
|
+
const raw = req.headers[LINEAGE_HEADER];
|
|
27
|
+
return (Array.isArray(raw) ? raw.join(",") : raw ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The action endpoint: server-driven provider writes, and server actions.
|
|
31
|
+
*
|
|
32
|
+
* Two addressing modes on one path, because both are "the browser asks the server to change something, naming it
|
|
33
|
+
* rather than addressing it". An element-addressed body reaches a connector's declared write; an action-addressed
|
|
34
|
+
* one runs a stored flow. Each mode is inert unless its own half of the config is present, so a deployment that
|
|
35
|
+
* configures neither has no endpoint at all — and guessing what an unconfigured one should do would be the whole
|
|
36
|
+
* vulnerability.
|
|
10
37
|
*/
|
|
11
38
|
var actionStage = async (ctx) => {
|
|
12
|
-
const { config, req } = ctx;
|
|
39
|
+
const { config, req, actions } = ctx;
|
|
13
40
|
const actionPath = config.action?.path ?? "/_action";
|
|
14
|
-
|
|
15
|
-
|
|
41
|
+
const hasConnectors = !!config.connectors;
|
|
42
|
+
if (!hasConnectors && !actions) return false;
|
|
43
|
+
if (actions && req.method === "GET" && req.path === `${actionPath}/catalog`) {
|
|
44
|
+
ctx.operation = "action:catalog";
|
|
45
|
+
if (!req.ctx.user) {
|
|
46
|
+
ctx.res.setStatus(401);
|
|
47
|
+
ctx.res.send("");
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
handleActionCatalog({
|
|
51
|
+
res: ctx.res,
|
|
52
|
+
module: actions
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (actions && req.method === "POST" && req.path.startsWith(`${actionPath}/hook/`)) {
|
|
57
|
+
ctx.operation = "action:webhook";
|
|
58
|
+
req.body = await readRawBody(ctx.raw);
|
|
59
|
+
await handleActionWebhook({
|
|
60
|
+
req,
|
|
61
|
+
res: ctx.res,
|
|
62
|
+
config,
|
|
63
|
+
module: actions,
|
|
64
|
+
signal: ctx.signal,
|
|
65
|
+
actionId: req.path.slice(`${actionPath}/hook/`.length),
|
|
66
|
+
callerId: `ip:${clientIp(ctx.raw, req)}`,
|
|
67
|
+
lineage: lineageOf(req)
|
|
68
|
+
});
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (actions && req.method === "DELETE" && req.path.startsWith(`${actionPath}/run/`)) {
|
|
72
|
+
ctx.operation = "action:cancel";
|
|
73
|
+
await handleActionCancel({
|
|
74
|
+
res: ctx.res,
|
|
75
|
+
module: actions,
|
|
76
|
+
runId: req.path.slice(`${actionPath}/run/`.length),
|
|
77
|
+
callerId: callerIdOf(ctx, req)
|
|
78
|
+
});
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (req.method !== "POST" || req.path !== actionPath) return false;
|
|
16
82
|
req.body = await readRawBody(ctx.raw);
|
|
17
|
-
|
|
83
|
+
const body = req.body ? safeParse(req.body) : void 0;
|
|
84
|
+
const addressesAction = body !== void 0 && typeof body === "object" && body !== null && "actionId" in body;
|
|
85
|
+
if (addressesAction && actions) {
|
|
86
|
+
ctx.operation = "action:run";
|
|
87
|
+
await handleActionCall({
|
|
88
|
+
req,
|
|
89
|
+
res: ctx.res,
|
|
90
|
+
raw: ctx.rawRes,
|
|
91
|
+
config,
|
|
92
|
+
module: actions,
|
|
93
|
+
signal: ctx.signal,
|
|
94
|
+
callerId: callerIdOf(ctx, req),
|
|
95
|
+
lineage: lineageOf(req)
|
|
96
|
+
});
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (addressesAction || !hasConnectors) {
|
|
100
|
+
ctx.operation = "action";
|
|
101
|
+
ctx.res.setStatus(404);
|
|
102
|
+
ctx.res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
103
|
+
ctx.res.send(JSON.stringify({
|
|
104
|
+
error: "This server runs no actions",
|
|
105
|
+
reason: "not_found"
|
|
106
|
+
}));
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
ctx.operation = "action";
|
|
110
|
+
await handleAction(req, ctx.res, config, config.connectors);
|
|
18
111
|
return true;
|
|
19
112
|
};
|
|
20
113
|
//#endregion
|
|
@@ -2,6 +2,7 @@ import { actionStage } from "./action.js";
|
|
|
2
2
|
import { rscStage } from "./rsc.js";
|
|
3
3
|
import { notFoundStage, ssrStage } from "./ssr.js";
|
|
4
4
|
import { authRoutesStages } from "../http/stages/authRoutes.js";
|
|
5
|
+
import { fontAssetsStage } from "../http/stages/fontAssets.js";
|
|
5
6
|
import { healthStage } from "../http/stages/health.js";
|
|
6
7
|
import { middlewaresStage } from "../http/stages/middlewares.js";
|
|
7
8
|
import { pluginAssetsStage } from "../http/stages/pluginAssets.js";
|
|
@@ -13,6 +14,7 @@ var buildPagePipeline = (services, extensions = {}) => {
|
|
|
13
14
|
publicDirStage,
|
|
14
15
|
wellKnownStage,
|
|
15
16
|
pluginAssetsStage,
|
|
17
|
+
fontAssetsStage,
|
|
16
18
|
...authRoutesStages,
|
|
17
19
|
configStaticStage,
|
|
18
20
|
sdkAssetsStage
|
|
@@ -2,3 +2,11 @@ import { ServerServices, SSRServerConfig } from '@plitzi/sdk-shared';
|
|
|
2
2
|
export type ResolvedServices = Required<ServerServices>;
|
|
3
3
|
export declare const resolveServices: (config: SSRServerConfig) => ResolvedServices;
|
|
4
4
|
export declare const resolveRscEndpoint: (config: SSRServerConfig) => string | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Where this server runs actions, or undefined when it runs none.
|
|
7
|
+
*
|
|
8
|
+
* Published to the client as `server.ssr.actionPath` for the same reason `rscPath` is: absence is how a page
|
|
9
|
+
* learns, once, that a `serverAction` step has nowhere to run — instead of every click discovering it through a
|
|
10
|
+
* 404. A static export or an embed with no server tier publishes nothing here.
|
|
11
|
+
*/
|
|
12
|
+
export declare const resolveActionEndpoint: (config: SSRServerConfig) => string | undefined;
|
|
@@ -10,5 +10,16 @@ var resolveRscEndpoint = (config) => {
|
|
|
10
10
|
if (!resolveServices(config).rsc || !(config.rsc?.enabled ?? true) || !config.adapters.getRscData) return;
|
|
11
11
|
return config.rsc?.path ?? "/_rsc";
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Where this server runs actions, or undefined when it runs none.
|
|
15
|
+
*
|
|
16
|
+
* Published to the client as `server.ssr.actionPath` for the same reason `rscPath` is: absence is how a page
|
|
17
|
+
* learns, once, that a `serverAction` step has nowhere to run — instead of every click discovering it through a
|
|
18
|
+
* 404. A static export or an embed with no server tier publishes nothing here.
|
|
19
|
+
*/
|
|
20
|
+
var resolveActionEndpoint = (config) => {
|
|
21
|
+
if (!config.action?.lookups) return;
|
|
22
|
+
return config.action.path ?? "/_action";
|
|
23
|
+
};
|
|
13
24
|
//#endregion
|
|
14
|
-
export { resolveRscEndpoint, resolveServices };
|
|
25
|
+
export { resolveActionEndpoint, resolveRscEndpoint, resolveServices };
|
package/dist/core/staticFiles.js
CHANGED
|
@@ -16,8 +16,7 @@ var serveFile = (req, res, filePath, stat) => {
|
|
|
16
16
|
}
|
|
17
17
|
const content = fs.readFileSync(filePath);
|
|
18
18
|
res.setHeader("Content-Type", getMimeType(filePath));
|
|
19
|
-
res.
|
|
20
|
-
res.send(content.toString("utf-8"));
|
|
19
|
+
res.send(content);
|
|
21
20
|
return true;
|
|
22
21
|
};
|
|
23
22
|
var serveStatic = (req, res, rootDir) => {
|
package/dist/handlers.d.ts
CHANGED
|
@@ -52,7 +52,13 @@
|
|
|
52
52
|
* ```
|
|
53
53
|
*/
|
|
54
54
|
export { createAuthRouteHandlers, mountAuthRoutes } from './core/handlers/authRouteHandlers';
|
|
55
|
+
export { createSocialAuthRouteHandlers, mountSocialAuthRoutes } from './core/handlers/socialRouteHandlers';
|
|
56
|
+
export { createOAuthRouteHandlers, mountOAuthRoutes, OAUTH_ROUTE_PATHS } from './core/handlers/oauthRouteHandlers';
|
|
55
57
|
export { createAuthMiddleware } from './core/handlers/authMiddleware';
|
|
58
|
+
export { createCsrfMiddleware } from './core/handlers/csrfMiddleware';
|
|
56
59
|
export type { AuthRouteHandlersOptions } from './core/handlers/authRouteHandlers';
|
|
60
|
+
export type { SocialRouteHandlersOptions } from './core/handlers/socialRouteHandlers';
|
|
61
|
+
export type { OAuthResponse, OAuthRouteHandler, OAuthRouteHandlersOptions } from './core/handlers/oauthRouteHandlers';
|
|
57
62
|
export type { AuthMiddlewareOptions } from './core/handlers/authMiddleware';
|
|
58
|
-
export type {
|
|
63
|
+
export type { CsrfMiddlewareOptions } from './core/handlers/csrfMiddleware';
|
|
64
|
+
export type { AuthedRequest, HttpRoute, JsonResponse, RedirectResponse, RouteHandler, RouterLike, SocialRouteHandler } from './core/handlers/types';
|
package/dist/handlers.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { createAuthRouteHandlers, mountAuthRoutes } from "./core/handlers/authRouteHandlers.js";
|
|
2
|
+
import { createSocialAuthRouteHandlers, mountSocialAuthRoutes } from "./core/handlers/socialRouteHandlers.js";
|
|
3
|
+
import { OAUTH_ROUTE_PATHS, createOAuthRouteHandlers, mountOAuthRoutes } from "./core/handlers/oauthRouteHandlers.js";
|
|
2
4
|
import { createAuthMiddleware } from "./core/handlers/authMiddleware.js";
|
|
3
|
-
|
|
5
|
+
import { createCsrfMiddleware } from "./core/handlers/csrfMiddleware.js";
|
|
6
|
+
export { OAUTH_ROUTE_PATHS, createAuthMiddleware, createAuthRouteHandlers, createCsrfMiddleware, createOAuthRouteHandlers, createSocialAuthRouteHandlers, mountAuthRoutes, mountOAuthRoutes, mountSocialAuthRoutes };
|
|
@@ -9,5 +9,13 @@ export type RawResponse = {
|
|
|
9
9
|
writeHead(statusCode: number, headers?: Record<string, string | number | readonly string[]>): unknown;
|
|
10
10
|
write(chunk: string | Buffer): unknown;
|
|
11
11
|
end(chunk?: string | Buffer): unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Optional, because this type is the MINIMUM a host has to provide — node's own, http2's, or a fake in a test.
|
|
14
|
+
*
|
|
15
|
+
* They are how the dispatcher tells "the caller left" from "the answer was sent": a host that exposes neither
|
|
16
|
+
* simply never cancels early, which is the behaviour of every host that had no such notion to begin with.
|
|
17
|
+
*/
|
|
18
|
+
once?(event: 'close', listener: () => void): unknown;
|
|
19
|
+
writableFinished?: boolean;
|
|
12
20
|
};
|
|
13
21
|
export declare const buildResponseHelpers: (raw: RawResponse, acceptEncoding?: string, compression?: ResolvedCompression) => SSRResponseHelpers;
|
|
@@ -13,7 +13,15 @@ var buildResponseHelpers = (raw, acceptEncoding, compression = DEFAULT_COMPRESSI
|
|
|
13
13
|
*/
|
|
14
14
|
const transformable = () => !String(raw.getHeaders()["cache-control"]).includes("no-transform");
|
|
15
15
|
const writeSend = (body) => {
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* A Buffer goes out untouched.
|
|
18
|
+
*
|
|
19
|
+
* It is the only way a binary reaches the wire — this response object had `send(body: string)` and every
|
|
20
|
+
* caller went through a UTF-8 round trip, which silently replaces every byte that is not valid UTF-8 and so
|
|
21
|
+
* corrupts any font, image or archive served through it. What a Buffer holds is also compressed already
|
|
22
|
+
* (woff2, png), so re-encoding it would cost CPU to make it bigger.
|
|
23
|
+
*/
|
|
24
|
+
const compressed = typeof body === "string" && transformable() ? compressBody(body, encoding, compression) : body;
|
|
17
25
|
if (compressed !== body) {
|
|
18
26
|
raw.setHeader("Content-Encoding", encoding);
|
|
19
27
|
raw.setHeader("Vary", "Accept-Encoding");
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Server, ServerSSR, SSRRequest, SSRServerConfig } from '@plitzi/sdk-shared';
|
|
2
|
+
/**
|
|
3
|
+
* The endpoints, the session and the location this render happens at.
|
|
4
|
+
*
|
|
5
|
+
* Synchronous, and takes the RSC bootstrap already resolved rather than resolving it: the only async thing here was
|
|
6
|
+
* ever the `getRscData` call, so the caller starting that read is also what decides whether it happens at all.
|
|
7
|
+
*/
|
|
8
|
+
export declare const buildServerInfo: (req: SSRRequest, config: SSRServerConfig, ssr: ServerSSR) => Partial<Server>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { resolveRscEndpoint } from "../core/services/resolve.js";
|
|
2
1
|
//#region src/helpers/buildServerInfo.ts
|
|
3
2
|
var getEnvironment = (env = "production", server) => {
|
|
4
3
|
switch (env) {
|
|
@@ -29,16 +28,31 @@ var getEnvironment = (env = "production", server) => {
|
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
30
|
};
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The endpoints, the session and the location this render happens at.
|
|
33
|
+
*
|
|
34
|
+
* Synchronous, and takes the RSC bootstrap already resolved rather than resolving it: the only async thing here was
|
|
35
|
+
* ever the `getRscData` call, so the caller starting that read is also what decides whether it happens at all.
|
|
36
|
+
*/
|
|
37
|
+
var buildServerInfo = (req, config, ssr) => {
|
|
33
38
|
const accessToken = req.query["access-token"];
|
|
34
39
|
const origin = `${req.protocol}://${req.hostname}`;
|
|
35
40
|
const user = req.ctx.user;
|
|
36
|
-
const { environment = "main", spaceId, revision = 0 } = req.ctx.spaceDeployment ?? {};
|
|
37
41
|
return getEnvironment(config.environment, {
|
|
38
42
|
basePath: "/",
|
|
39
43
|
requestUrl: req.url || "/",
|
|
40
44
|
origin,
|
|
45
|
+
/**
|
|
46
|
+
* Complete enough to be the same object a browser hands the SDK, `origin` included.
|
|
47
|
+
*
|
|
48
|
+
* It used to carry the hostname and the path only, which reads as a location right up until something needs to
|
|
49
|
+
* name THIS page from the server: a sign-in link that sends somebody back where they were had nothing to build
|
|
50
|
+
* an absolute URL out of, so a space had to name its own address in a per-environment variable instead.
|
|
51
|
+
*/
|
|
41
52
|
location: {
|
|
53
|
+
origin,
|
|
54
|
+
protocol: `${req.protocol}:`,
|
|
55
|
+
host: req.hostname,
|
|
42
56
|
hostname: req.hostname,
|
|
43
57
|
pathname: req.path || "/",
|
|
44
58
|
search: req.search
|
|
@@ -57,17 +71,7 @@ var buildServerInfo = async (req, config, loadOfflineData) => {
|
|
|
57
71
|
accessToken: user.token,
|
|
58
72
|
expiresAt: user.expiresAt
|
|
59
73
|
} : void 0,
|
|
60
|
-
ssr
|
|
61
|
-
rscPath: resolveRscEndpoint(config),
|
|
62
|
-
rscData: await config.adapters.getRscData?.({
|
|
63
|
-
req,
|
|
64
|
-
spaceId,
|
|
65
|
-
environment,
|
|
66
|
-
revision,
|
|
67
|
-
user,
|
|
68
|
-
loadOfflineData
|
|
69
|
-
})
|
|
70
|
-
}
|
|
74
|
+
ssr
|
|
71
75
|
});
|
|
72
76
|
};
|
|
73
77
|
//#endregion
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key a rendered document is cached under.
|
|
3
|
+
*
|
|
4
|
+
* The visitor's theme is part of it because it is part of the DOCUMENT: `prepareRender` reads the `theme` cookie and
|
|
5
|
+
* writes it onto `<html>` and into the props the SDK hydrates with. Keyed without it, the first visitor's choice was
|
|
6
|
+
* served to everybody behind them — a dark page for a visitor who chose light, then corrected on hydration.
|
|
7
|
+
*
|
|
8
|
+
* Read from the request here rather than handed in, so a call site cannot key the page without it. Only the theme
|
|
9
|
+
* cookie is read: keying the whole header would split the cache on every analytics cookie a visitor carries.
|
|
10
|
+
*/
|
|
1
11
|
export declare const buildHtmlCacheKey: (accessToken: string | undefined, spaceId: number | string | null, environment: string, revision: number, req: {
|
|
2
12
|
hostname: string;
|
|
3
13
|
path: string;
|
|
4
14
|
search: string;
|
|
15
|
+
headers: {
|
|
16
|
+
cookie?: string;
|
|
17
|
+
};
|
|
5
18
|
}) => string;
|
|
6
19
|
export declare const buildOfflineDataCacheKey: (spaceId: number, environment: string, revision: number) => string;
|
|
7
20
|
export declare const buildRscCacheKey: (spaceId: number, environment: string, revision: number, userId: string | number | undefined, idsParam: string | undefined, req: {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
import { themeFromCookies } from "@plitzi/sdk-shared/theme";
|
|
1
2
|
//#region src/helpers/cache/keys.ts
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The key a rendered document is cached under.
|
|
5
|
+
*
|
|
6
|
+
* The visitor's theme is part of it because it is part of the DOCUMENT: `prepareRender` reads the `theme` cookie and
|
|
7
|
+
* writes it onto `<html>` and into the props the SDK hydrates with. Keyed without it, the first visitor's choice was
|
|
8
|
+
* served to everybody behind them — a dark page for a visitor who chose light, then corrected on hydration.
|
|
9
|
+
*
|
|
10
|
+
* Read from the request here rather than handed in, so a call site cannot key the page without it. Only the theme
|
|
11
|
+
* cookie is read: keying the whole header would split the cache on every analytics cookie a visitor carries.
|
|
12
|
+
*/
|
|
13
|
+
var buildHtmlCacheKey = (accessToken = "anonymous", spaceId, environment, revision, req) => `${accessToken}\0${spaceId ?? 1}\0${environment}\0${revision}\0${themeFromCookies(req.headers.cookie) ?? ""}\0${req.hostname}\0${req.path}\0${req.search}`;
|
|
3
14
|
var buildOfflineDataCacheKey = (spaceId, environment, revision) => `${spaceId}|${environment}|${revision}`;
|
|
4
15
|
var buildRscCacheKey = (spaceId, environment, revision, userId, idsParam, req) => `${spaceId}\0${environment}\0${revision}\0${userId ?? "anon"}\0${idsParam ?? ""}\0${req.hostname}\0${req.path}\0${req.search}`;
|
|
5
16
|
//#endregion
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscribes to a signal, and covers the case every hand-rolled subscription misses: one that is ALREADY aborted.
|
|
3
|
+
*
|
|
4
|
+
* `addEventListener('abort', …)` on a signal that has already fired never calls back — the event is long gone — so
|
|
5
|
+
* a listener attached after any `await` is a cancellation silently dropped. That window is not theoretical: an
|
|
6
|
+
* action is READ before its run begins, and a page that gave up during that read left the run to carry on holding
|
|
7
|
+
* a slot and a connection for nobody.
|
|
8
|
+
*
|
|
9
|
+
* Returns the unsubscribe, so a caller that outlives the signal does not leak a listener onto it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const onAbort: (signal: AbortSignal | undefined, stop: () => void) => (() => void);
|
|
12
|
+
export default onAbort;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/helpers/onAbort.ts
|
|
2
|
+
/**
|
|
3
|
+
* Subscribes to a signal, and covers the case every hand-rolled subscription misses: one that is ALREADY aborted.
|
|
4
|
+
*
|
|
5
|
+
* `addEventListener('abort', …)` on a signal that has already fired never calls back — the event is long gone — so
|
|
6
|
+
* a listener attached after any `await` is a cancellation silently dropped. That window is not theoretical: an
|
|
7
|
+
* action is READ before its run begins, and a page that gave up during that read left the run to carry on holding
|
|
8
|
+
* a slot and a connection for nobody.
|
|
9
|
+
*
|
|
10
|
+
* Returns the unsubscribe, so a caller that outlives the signal does not leak a listener onto it.
|
|
11
|
+
*/
|
|
12
|
+
var onAbort = (signal, stop) => {
|
|
13
|
+
if (!signal) return () => {};
|
|
14
|
+
if (signal.aborted) {
|
|
15
|
+
stop();
|
|
16
|
+
return () => {};
|
|
17
|
+
}
|
|
18
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
19
|
+
return () => signal.removeEventListener("abort", stop);
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { onAbort as default, onAbort };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a customer-authored request may go.
|
|
3
|
+
*
|
|
4
|
+
* Two things reach the outside world on somebody else's say-so: the `http.request` task, whose URL is typed into
|
|
5
|
+
* a flow, and the connector engine, whose `baseUrl` is typed into a manifest. Both run inside the cluster, from a
|
|
6
|
+
* trusted network position, which is what turns "fetch this URL for me" into a way to read the instance's cloud
|
|
7
|
+
* credentials at `169.254.169.254` or talk to Redis on `localhost`. One rule, in one place, for both of them.
|
|
8
|
+
*/
|
|
9
|
+
/** Literal forms an authored URL can hold, and the ones this refuses outright. */
|
|
10
|
+
export declare const isBlockedHost: (hostname: string) => boolean;
|
|
11
|
+
export type HostLookup = (hostname: string) => Promise<{
|
|
12
|
+
address: string;
|
|
13
|
+
}[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Checks the URL an authored document asked for, including what its hostname RESOLVES to.
|
|
16
|
+
*
|
|
17
|
+
* The literal check alone was never enough and said so: `http://metadata.example.com/` is a public-looking name
|
|
18
|
+
* whose A record can be `169.254.169.254`, and nothing about the string gives that away. So the name is resolved
|
|
19
|
+
* and every address it answers is judged — a name that resolves to a private address is refused whatever it is
|
|
20
|
+
* spelled like.
|
|
21
|
+
*
|
|
22
|
+
* A name that does not resolve is ALLOWED through: there is nothing to judge, and the request is about to fail on
|
|
23
|
+
* its own with a better error than this could give. What remains open is DNS rebinding — an attacker's own server
|
|
24
|
+
* answering a public address here and a private one when the socket is opened a moment later — which is closed
|
|
25
|
+
* only by resolving and connecting to the same address, and that belongs in the transport rather than here.
|
|
26
|
+
*/
|
|
27
|
+
export declare const assertOutboundAllowed: (url: URL, lookup?: HostLookup) => Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
//#region src/helpers/outboundGuard.ts
|
|
3
|
+
/**
|
|
4
|
+
* Where a customer-authored request may go.
|
|
5
|
+
*
|
|
6
|
+
* Two things reach the outside world on somebody else's say-so: the `http.request` task, whose URL is typed into
|
|
7
|
+
* a flow, and the connector engine, whose `baseUrl` is typed into a manifest. Both run inside the cluster, from a
|
|
8
|
+
* trusted network position, which is what turns "fetch this URL for me" into a way to read the instance's cloud
|
|
9
|
+
* credentials at `169.254.169.254` or talk to Redis on `localhost`. One rule, in one place, for both of them.
|
|
10
|
+
*/
|
|
11
|
+
/** Literal forms an authored URL can hold, and the ones this refuses outright. */
|
|
12
|
+
var isBlockedHost = (hostname) => {
|
|
13
|
+
const host = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
14
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".internal") || host === "::1") return true;
|
|
15
|
+
const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
16
|
+
if (!ipv4) return /^f[cd]/.test(host) || host.startsWith("fe8") || host.startsWith("fe9") || /^fe[ab]/.test(host);
|
|
17
|
+
const [a = 0, b = 0] = ipv4.slice(1).map((part) => Number.parseInt(part, 10));
|
|
18
|
+
return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168;
|
|
19
|
+
};
|
|
20
|
+
var resolveAll = (hostname) => lookup(hostname, {
|
|
21
|
+
all: true,
|
|
22
|
+
verbatim: true
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Checks the URL an authored document asked for, including what its hostname RESOLVES to.
|
|
26
|
+
*
|
|
27
|
+
* The literal check alone was never enough and said so: `http://metadata.example.com/` is a public-looking name
|
|
28
|
+
* whose A record can be `169.254.169.254`, and nothing about the string gives that away. So the name is resolved
|
|
29
|
+
* and every address it answers is judged — a name that resolves to a private address is refused whatever it is
|
|
30
|
+
* spelled like.
|
|
31
|
+
*
|
|
32
|
+
* A name that does not resolve is ALLOWED through: there is nothing to judge, and the request is about to fail on
|
|
33
|
+
* its own with a better error than this could give. What remains open is DNS rebinding — an attacker's own server
|
|
34
|
+
* answering a public address here and a private one when the socket is opened a moment later — which is closed
|
|
35
|
+
* only by resolving and connecting to the same address, and that belongs in the transport rather than here.
|
|
36
|
+
*/
|
|
37
|
+
var assertOutboundAllowed = async (url, lookup = resolveAll) => {
|
|
38
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") throw new Error(`Request protocol "${url.protocol}" is not allowed`);
|
|
39
|
+
if (isBlockedHost(url.hostname)) throw new Error("Request host is not allowed");
|
|
40
|
+
let addresses;
|
|
41
|
+
try {
|
|
42
|
+
addresses = await lookup(url.hostname.replace(/^\[|\]$/g, ""));
|
|
43
|
+
} catch {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (addresses.some(({ address }) => isBlockedHost(address))) throw new Error("Request host is not allowed");
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { assertOutboundAllowed, isBlockedHost };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { ServerLogEvent, ServerLogger } from '@plitzi/sdk-shared';
|
|
2
2
|
/** One line for any {@link ServerLogEvent}: an HTTP request reads as an access-log line
|
|
3
3
|
* (`[SSR] 203.0.113.7 GET /pricing 200 12ms ok`), the MCP events as what happened inside one
|
|
4
|
-
* (`[MCP] tools/call plitzi_apply {operations:[3]} 41ms ok`)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* (`[MCP] tools/call plitzi_apply {operations:[3]} 41ms ok`), a server action as what its flow did
|
|
5
|
+
* (`[Action] shipping-quote via call space=1 completed 12ms ok`) and a refused one as what turned it away
|
|
6
|
+
* (`[Action] checkout via webhook space=1 REFUSED invalid_signature`). Rendering is a pure format — the dispatcher
|
|
7
|
+
* already stripped query values, collected no headers, cookies or tokens, summarised tool args by shape and
|
|
8
|
+
* reduced a run to its steps; the client IP it does carry is personal data, so a sink that persists these lines
|
|
9
|
+
* must say so. */
|
|
7
10
|
export declare const renderLogEvent: (event: ServerLogEvent) => string;
|
|
8
11
|
/** A drop-in `SSRServerConfig.logger` for consumers that just want the log on the console. Consumers with their
|
|
9
12
|
* own logging stack should pass their own sink instead and read the structured event. */
|
|
@@ -11,16 +11,33 @@ var renderTool = (event) => {
|
|
|
11
11
|
return `[MCP] tools/call ${event.name}${args} ${Math.round(event.durationMs)}ms ${outcomeOf(event)}`;
|
|
12
12
|
};
|
|
13
13
|
var renderResource = (event) => `[MCP] resources/read ${event.name} ${Math.round(event.durationMs)}ms ${outcomeOf(event)}`;
|
|
14
|
+
/** The steps go on the line only when the run ended badly. On a run that worked they are noise on every single
|
|
15
|
+
* call; on one that did not they are the answer — which step stopped, and what the ones before it did. */
|
|
16
|
+
var renderRun = (event) => {
|
|
17
|
+
const steps = event.ok || event.steps.length === 0 ? "" : ` [${event.steps.join(" → ")}]`;
|
|
18
|
+
return `[Action] ${event.name} via ${event.trigger} space=${event.spaceId} ${event.status} ${Math.round(event.durationMs)}ms ${outcomeOf(event)}${steps}`;
|
|
19
|
+
};
|
|
20
|
+
/** A refusal reads as what refused it: `[Action] checkout via webhook space=1 REFUSED invalid_signature`. The
|
|
21
|
+
* reason is the whole point of the line — "401" is what the request log already said. */
|
|
22
|
+
var renderReject = (event) => {
|
|
23
|
+
const caller = event.callerId ? ` from ${event.callerId}` : "";
|
|
24
|
+
return `[Action] ${event.name} via ${event.trigger} space=${event.spaceId} REFUSED ${event.reason}${caller} ${outcomeOf(event)}`;
|
|
25
|
+
};
|
|
14
26
|
/** One line for any {@link ServerLogEvent}: an HTTP request reads as an access-log line
|
|
15
27
|
* (`[SSR] 203.0.113.7 GET /pricing 200 12ms ok`), the MCP events as what happened inside one
|
|
16
|
-
* (`[MCP] tools/call plitzi_apply {operations:[3]} 41ms ok`)
|
|
17
|
-
*
|
|
18
|
-
*
|
|
28
|
+
* (`[MCP] tools/call plitzi_apply {operations:[3]} 41ms ok`), a server action as what its flow did
|
|
29
|
+
* (`[Action] shipping-quote via call space=1 completed 12ms ok`) and a refused one as what turned it away
|
|
30
|
+
* (`[Action] checkout via webhook space=1 REFUSED invalid_signature`). Rendering is a pure format — the dispatcher
|
|
31
|
+
* already stripped query values, collected no headers, cookies or tokens, summarised tool args by shape and
|
|
32
|
+
* reduced a run to its steps; the client IP it does carry is personal data, so a sink that persists these lines
|
|
33
|
+
* must say so. */
|
|
19
34
|
var renderLogEvent = (event) => {
|
|
20
35
|
switch (event.kind) {
|
|
21
36
|
case "request": return renderRequest(event);
|
|
22
37
|
case "tool": return renderTool(event);
|
|
23
38
|
case "resource": return renderResource(event);
|
|
39
|
+
case "run": return renderRun(event);
|
|
40
|
+
case "reject": return renderReject(event);
|
|
24
41
|
}
|
|
25
42
|
};
|
|
26
43
|
/** A drop-in `SSRServerConfig.logger` for consumers that just want the log on the console. Consumers with their
|
package/dist/index.d.ts
CHANGED
|
@@ -2,15 +2,21 @@ export { createServer, resolveServices } from './core/createServer';
|
|
|
2
2
|
export { registerHealthCheck, buildHealthPayload } from './core/health';
|
|
3
3
|
export { consoleLogger, renderLogEvent } from './helpers/serverLog';
|
|
4
4
|
export type { HealthCheckApp, HealthIdentity } from './core/health';
|
|
5
|
+
export { createCloudAdapters } from './adapters/cloudAdapters';
|
|
5
6
|
export { createJsonAdapters } from './adapters/jsonAdapters';
|
|
6
7
|
export { createAuthAdapters } from './adapters/authAdapters';
|
|
8
|
+
export { authoringPreview, createMemoryCache, createSpaceAdapters, fixedSpace, presentsCredential, refuse, verifiedDomain, wildcardSubdomain } from './adapters/space';
|
|
9
|
+
export { createLocalFontStore, hostedPathsOf } from './core/fontStore';
|
|
7
10
|
export { createMemoryDraftStore, takeDraftOverride } from './modules/ssr/preview';
|
|
8
11
|
export { PREVIEW_TOKEN_PARAM } from './core/previewToken';
|
|
9
12
|
export { resolveRscData } from './modules/rsc/resolveRscData';
|
|
10
13
|
export { fetchConnectorRecords, rebaseMedia, writeConnectorRecord, createConnectorResolver } from './modules/connectors';
|
|
14
|
+
export type { CloudAdaptersConfig, CloudSpaceCache } from './adapters/cloudAdapters';
|
|
11
15
|
export type { JsonAdaptersConfig } from './adapters/jsonAdapters';
|
|
12
16
|
export type { AuthAdapters, AuthAdaptersConfig } from './adapters/authAdapters';
|
|
17
|
+
export type { AuthoringPreviewOptions, DeploymentRow, FrameAncestorsConfig, SpaceAdapters, SpaceAdaptersConfig, SpaceCache, SpaceRefusal, SpaceResolution, SpaceResolver, WildcardSubdomainOptions } from './adapters/space';
|
|
13
18
|
export type { RscElementResolver, RscResolveContext, ResolveRscDataOptions } from './modules/rsc/resolveRscData';
|
|
14
19
|
export type { ConnectorAuth, ConnectorCredential, ConnectorEndpoints, ConnectorFilter, ConnectorLookups, ConnectorManifest, ConnectorManifestDraft, ConnectorPageInfo, ConnectorPagination, ConnectorQuery, ConnectorReadEndpoint, ConnectorRecord, ConnectorResponseMapping, ConnectorResult, ProviderPagination } from './modules/connectors';
|
|
20
|
+
export type { FontStore, StoredFont } from './core/fontStore';
|
|
15
21
|
export type { ResolvedServices } from './core/createServer';
|
|
16
22
|
export type { BaseContext, PipelineExtensions, SSRContext, Stage } from './core/http/types';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { fetchConnectorRecords, rebaseMedia, writeConnectorRecord } from "./modules/connectors/engine.js";
|
|
2
|
+
import { createConnectorResolver } from "./modules/connectors/resolver.js";
|
|
3
|
+
import { resolveRscData } from "./modules/rsc/resolveRscData.js";
|
|
1
4
|
import { PREVIEW_TOKEN_PARAM } from "./core/previewToken.js";
|
|
2
5
|
import { createMemoryDraftStore, takeDraftOverride } from "./modules/ssr/preview.js";
|
|
3
|
-
import { fetchConnectorRecords, rebaseMedia, writeConnectorRecord } from "./modules/connectors/engine.js";
|
|
4
6
|
import { resolveServices } from "./core/services/resolve.js";
|
|
5
7
|
import { buildHealthPayload, registerHealthCheck } from "./core/health.js";
|
|
6
8
|
import { createServer } from "./core/createServer.js";
|
|
7
9
|
import { consoleLogger, renderLogEvent } from "./helpers/serverLog.js";
|
|
10
|
+
import { createCloudAdapters } from "./adapters/cloudAdapters.js";
|
|
8
11
|
import { createJsonAdapters } from "./adapters/jsonAdapters.js";
|
|
9
12
|
import { createAuthAdapters } from "./adapters/authAdapters.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
+
import { createMemoryCache, refuse } from "./adapters/space/types.js";
|
|
14
|
+
import { authoringPreview, fixedSpace, presentsCredential, verifiedDomain, wildcardSubdomain } from "./adapters/space/resolvers.js";
|
|
15
|
+
import { createSpaceAdapters } from "./adapters/space/index.js";
|
|
16
|
+
import { createLocalFontStore, hostedPathsOf } from "./core/fontStore.js";
|
|
17
|
+
export { PREVIEW_TOKEN_PARAM, authoringPreview, buildHealthPayload, consoleLogger, createAuthAdapters, createCloudAdapters, createConnectorResolver, createJsonAdapters, createLocalFontStore, createMemoryCache, createMemoryDraftStore, createServer, createSpaceAdapters, fetchConnectorRecords, fixedSpace, hostedPathsOf, presentsCredential, rebaseMedia, refuse, registerHealthCheck, renderLogEvent, resolveRscData, resolveServices, takeDraftOverride, verifiedDomain, wildcardSubdomain, writeConnectorRecord };
|
package/dist/kernel.d.ts
CHANGED
|
@@ -5,37 +5,20 @@
|
|
|
5
5
|
* from there would load that whole graph. This entry's closure is the dispatcher, the transports and the base
|
|
6
6
|
* stages: no React, no template, no plugin manager.
|
|
7
7
|
*
|
|
8
|
-
* A server built on it supplies its own pipeline; `@plitzi/sdk-mcp` is the in-tree example.
|
|
8
|
+
* A server built on it supplies its own pipeline; `@plitzi/sdk-mcp` is the in-tree example.
|
|
9
|
+
*
|
|
10
|
+
* Auth is **not** here — it is `@plitzi/sdk-server/auth`. The two shared this entry for a while and the name
|
|
11
|
+
* described neither: a deployment wiring up sessions loaded the dispatcher to get them, and a server that wants
|
|
12
|
+
* the dispatcher and no auth at all had to name the entry that carried both. */
|
|
9
13
|
export { createHttpServer } from './core/server/baseServer';
|
|
10
14
|
export { makeHandler } from './core/http/dispatcher';
|
|
11
15
|
export { healthStage } from './core/http/stages/health';
|
|
12
16
|
export { configStaticStage, publicDirStage, wellKnownStage } from './core/http/stages/static';
|
|
13
17
|
export { buildHealthPayload, registerHealthCheck } from './core/health';
|
|
14
18
|
export { clientIp, parseRequest, readRawBody, requestOrigin } from './core/requestParser';
|
|
15
|
-
export { clearFlowCookie, clearSessionCookies, createSessionCookies, isLocalHost, readFlowCookie, readRefreshToken, readSessionToken, sessionCookieParams, sessionHintValue, writeFlowCookie, writeSessionCookies } from './core/auth/session';
|
|
16
|
-
export { SCOPES, authFailureMessage, createTokens, userIdOf } from './core/auth/tokens';
|
|
17
|
-
export { createCarriers, presentedOrigin } from './core/auth/credentials';
|
|
18
|
-
export { ANY_DOMAIN, corsOrigins, domainAllowed, frameAncestors, hostnameOf, normalizeDomain } from './core/auth/domains';
|
|
19
|
-
export { createIdentity } from './core/auth/identity';
|
|
20
|
-
export { checkPermission, checkSpaceAccess, createAuthorizer, requirementFor } from './core/auth/authorize';
|
|
21
|
-
export { createAuth } from './core/auth/createAuth';
|
|
22
|
-
export { createAuthApi } from './core/auth/api';
|
|
23
|
-
export { applySessionOutcome, authPolicyRules, authRoutes } from './core/auth/routes';
|
|
24
|
-
export { createSpaceTokenApi } from './core/auth/spaceTokens';
|
|
25
|
-
export { BUILT_IN_PROVIDERS, OAuthFailure, createSocialAuth, requestProfileJson } from './core/auth/oauth';
|
|
26
19
|
export { consoleLogger, renderLogEvent } from './helpers/serverLog';
|
|
27
20
|
export { PREVIEW_TOKEN_PARAM } from './core/previewToken';
|
|
28
21
|
export type { HttpServerParts } from './core/server/baseServer';
|
|
29
22
|
export type { BuildContext } from './core/http/dispatcher';
|
|
30
23
|
export type { HealthCheckApp, HealthIdentity } from './core/health';
|
|
31
24
|
export type { BaseContext, PipelineExtensions, SSRContext, Stage } from './core/http/types';
|
|
32
|
-
export type { CookieCarrier, CookieSink, SessionCookieParams, SessionCookies } from './core/auth/session';
|
|
33
|
-
export type { CredentialCarrier } from './core/auth/credentials';
|
|
34
|
-
export type { Actor, ActorResult, Grant, GrantOptions, GrantResult, Identity, IdentityAdapters, IdentityConfig, SpaceMembership, StoredSpaceToken } from './core/auth/identity';
|
|
35
|
-
export type { AuthPolicy, AuthorizeResult, Authorizer, MembershipFacts, PathMatcher, PermissionCheck, Requirement, SpaceAccessCheck } from './core/auth/authorize';
|
|
36
|
-
export type { AuthRequest, AuthRoute } from './core/auth/routes';
|
|
37
|
-
export type { Auth, AuthConfig } from './core/auth/createAuth';
|
|
38
|
-
export type { CompletedFlow, OAuthFailureReason, OAuthProfile, OAuthProvider, OAuthProviderConfig, SocialAuth, SocialAuthAdapters, SocialAuthConfig, StartedFlow } from './core/auth/oauth';
|
|
39
|
-
export type { AccountAccess, AccountAdapters, AccountRecord, AuthApi, AuthApiConfig, AuthOutcome } from './core/auth/api';
|
|
40
|
-
export type { SpaceTokenAdapters, SpaceTokenApi, SpaceTokenContext, SpaceTokenOutcome, SpaceTokenRecord, SpaceTokenSummary } from './core/auth/spaceTokens';
|
|
41
|
-
export type { AuthFailure, RefreshTokenPayload, SpaceScope, SpaceTokenOptions, SpaceTokenPayload, TokenConfig, TokenScope, Tokens, UserTokenPayload, VerifyResult, WidgetTokenPayload } from './core/auth/tokens';
|