@plitzi/sdk-server 0.38.4 → 0.38.6
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 +350 -0
- package/README.md +126 -17
- package/dist/actions.d.ts +8 -5
- package/dist/actions.js +3 -1
- package/dist/adapters/jsonAdapters.d.ts +1 -1
- package/dist/adapters/jsonAdapters.js +16 -3
- package/dist/core/configSeam.js +20 -4
- package/dist/core/createServer.js +3 -2
- package/dist/core/handlers/socialRouteHandlers.js +6 -1
- package/dist/core/http/stages/devReload.d.ts +11 -0
- package/dist/core/http/stages/devReload.js +17 -0
- package/dist/core/server/baseServer.d.ts +2 -0
- package/dist/core/server/baseServer.js +3 -0
- package/dist/core/server/pageServer.js +18 -0
- package/dist/core/services/registry.js +2 -0
- package/dist/core/services/rsc.js +1 -1
- package/dist/core/services/spaceDocument.d.ts +2 -0
- package/dist/core/services/spaceDocument.js +11 -0
- package/dist/functions-api.d.ts +6 -1
- package/dist/functionsRunner.d.ts +5 -0
- package/dist/functionsRunner.js +2 -1
- package/dist/helpers/hydrationPayload.d.ts +25 -4
- package/dist/helpers/hydrationPayload.js +36 -30
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/modules/actions/index.d.ts +11 -2
- package/dist/modules/actions/index.js +29 -7
- package/dist/modules/actions/runtime/fileKv.d.ts +28 -0
- package/dist/modules/actions/runtime/fileKv.js +95 -0
- package/dist/modules/actions/runtime/fileKv.test.d.ts +1 -0
- package/dist/modules/actions/runtime/memoryKv.d.ts +23 -0
- package/dist/modules/actions/runtime/memoryKv.js +39 -24
- package/dist/modules/actions/runtime/namespaceKv.d.ts +2 -0
- package/dist/modules/actions/runtime/namespaceKv.js +5 -3
- package/dist/modules/actions/runtime/renderResolver.d.ts +3 -2
- package/dist/modules/actions/runtime/renderResolver.js +13 -9
- package/dist/modules/actions/runtime/renderShare.d.ts +7 -24
- package/dist/modules/actions/runtime/renderShare.js +47 -13
- package/dist/modules/actions/runtime/runAction.js +15 -1
- package/dist/modules/actions/runtime/scope.js +16 -1
- package/dist/modules/actions/runtime/signing.js +4 -1
- package/dist/modules/actions/runtime/taskContext.d.ts +7 -1
- package/dist/modules/actions/runtime/taskContext.js +6 -2
- package/dist/modules/actions/tasks/registry.d.ts +3 -1
- package/dist/modules/actions/tasks/registry.js +15 -1
- package/dist/modules/actions/types.d.ts +15 -2
- package/dist/modules/functions/config.d.ts +6 -0
- package/dist/modules/functions/context.d.ts +4 -2
- package/dist/modules/functions/context.js +43 -21
- package/dist/modules/functions/load.d.ts +8 -1
- package/dist/modules/functions/load.js +8 -4
- package/dist/modules/functions/manifest.d.ts +4 -1
- package/dist/modules/functions/manifest.js +14 -2
- package/dist/modules/functions/native.d.ts +2 -1
- package/dist/modules/functions/native.js +2 -2
- package/dist/modules/functions/plugins.test.d.ts +1 -0
- package/dist/modules/functions/routes.d.ts +8 -3
- package/dist/modules/functions/routes.js +41 -21
- package/dist/modules/functions/scope.d.ts +30 -0
- package/dist/modules/functions/scope.js +28 -0
- package/dist/modules/functions/space.d.ts +10 -5
- package/dist/modules/functions/space.js +37 -15
- package/dist/modules/rsc/connectorRscData.d.ts +3 -1
- package/dist/modules/rsc/connectorRscData.js +8 -4
- package/dist/modules/rsc/handler.d.ts +3 -16
- package/dist/modules/rsc/handler.js +10 -4
- package/dist/modules/rsc/publicFileResolver.d.ts +15 -12
- package/dist/modules/rsc/publicFileResolver.js +56 -24
- package/dist/modules/rsc/resolveRscData.d.ts +3 -1
- package/dist/modules/rsc/resolveRscData.js +8 -5
- package/dist/modules/runtime/bundle.d.ts +5 -0
- package/dist/modules/runtime/bundle.js +20 -5
- package/dist/modules/sqlite/kv.d.ts +34 -0
- package/dist/modules/sqlite/kv.js +99 -0
- package/dist/modules/sqlite/kv.test.d.ts +1 -0
- package/dist/modules/ssr/Component.d.ts +2 -1
- package/dist/modules/ssr/Component.js +2 -1
- package/dist/modules/ssr/prepareRender.js +48 -6
- package/dist/modules/ssr/spaceDocument.d.ts +25 -0
- package/dist/modules/ssr/spaceDocument.js +72 -0
- package/dist/modules/ssr/spaceDocument.test.d.ts +1 -0
- package/dist/modules/ssr/testing/offlineData.d.ts +5 -0
- package/dist/modules/ssr/views/template.ejs +122 -20
- package/dist/plugins/compile.js +6 -21
- package/dist/plugins/compile.test.d.ts +1 -0
- package/dist/plugins/copy.d.ts +5 -0
- package/dist/plugins/copy.js +16 -1
- package/dist/plugins/copy.test.d.ts +1 -0
- package/dist/plugins/manager.d.ts +13 -0
- package/dist/plugins/manager.js +40 -11
- package/dist/plugins/watch.d.ts +12 -0
- package/dist/plugins/watch.js +79 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -2
- package/dist/sqlite.d.ts +17 -0
- package/dist/sqlite.js +2 -0
- package/package.json +8 -3
- package/skills/plitzi-authoring/CHEATSHEET.md +4 -2
- package/skills/plitzi-authoring/SKILL.md +20 -19
- package/skills/plitzi-authoring/recipes/plugin.ts +1 -1
- package/skills/plitzi-authoring/recipes/server-data.ts +3 -3
- package/skills/plitzi-authoring/recipes/show-data.ts +3 -2
- package/skills/plitzi-authoring/recipes/typed-data.ts +1 -1
- package/skills/plitzi-authoring/reference/authoring-errors.md +8 -2
- package/skills/plitzi-authoring/reference/colours-and-motion.md +3 -1
- package/skills/plitzi-authoring/reference/data-and-visibility.md +24 -22
- package/skills/plitzi-authoring/reference/drawing.md +24 -0
- package/skills/plitzi-authoring/reference/efficiency.md +19 -2
- package/skills/plitzi-authoring/reference/flows.md +3 -0
- package/skills/plitzi-authoring/reference/layouts.md +8 -23
- package/skills/plitzi-authoring/reference/performance.md +2 -2
- package/skills/plitzi-authoring/reference/plugins.md +51 -38
- package/skills/plitzi-authoring/reference/review-checklist.md +2 -0
- package/skills/plitzi-authoring/reference/structure.md +32 -1
- package/skills/plitzi-authoring/reference/templates.md +5 -2
- package/skills/plitzi-authoring/reference/testing.md +9 -2
- package/skills/plitzi-authoring/reference/typed-sources.md +3 -1
- package/skills/plitzi-authoring/reference/validation.md +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { prefixKv } from "../actions/runtime/namespaceKv.js";
|
|
1
2
|
import { fetchOutbound } from "../../helpers/outboundGuard.js";
|
|
2
3
|
import { countRate } from "../actions/runtime/rateLimit.js";
|
|
4
|
+
import { pluginKvPrefix, pluginSigned } from "./scope.js";
|
|
3
5
|
import { processTwigParam } from "@plitzi/sdk-shared/helpers/twigWrapper";
|
|
4
6
|
//#region src/modules/functions/context.ts
|
|
5
7
|
/**
|
|
@@ -26,7 +28,8 @@ var userOf = ({ id, username, email, verified, permissions, roles }) => ({
|
|
|
26
28
|
* the same run budget `http.request` goes through), and — naming a credential — with its values written in where the
|
|
27
29
|
* request says `{{credential.<key>}}`. The value is resolved here and never handed to the code.
|
|
28
30
|
*/
|
|
29
|
-
var functionFetch = (ctx, hosts) => async (input, init = {}) => {
|
|
31
|
+
var functionFetch = (ctx, hosts, scope) => async (input, init = {}) => {
|
|
32
|
+
if (init.credential && scope) throw new Error(`A plugin's functions name no credential of the space's ("${init.credential}")`);
|
|
30
33
|
const credential = init.credential ? await ctx.credential(init.credential) : void 0;
|
|
31
34
|
if (init.credential && !credential) throw new Error(`Credential "${init.credential}" is not available for this space`);
|
|
32
35
|
const render = (value) => credential ? String(processTwigParam(value, { credential })) : value;
|
|
@@ -63,26 +66,45 @@ var unavailable = (what) => () => Promise.reject(/* @__PURE__ */ new Error(`This
|
|
|
63
66
|
var unsigned = () => Promise.reject(/* @__PURE__ */ new Error("This server signs nothing: its actions were given no signingSecret"));
|
|
64
67
|
/**
|
|
65
68
|
* What a space's code is handed, built from the run's own context: the one place a {@link FunctionContext} is made.
|
|
66
|
-
* A self-hosted server hands it to the code directly; the platform answers the sandbox's calls with it.
|
|
69
|
+
* A self-hosted server hands it to the code directly; the platform answers the sandbox's calls with it. With a `scope`,
|
|
70
|
+
* it is a plugin's code, and gets the plugin's corner of the space (`./scope`).
|
|
67
71
|
*/
|
|
68
|
-
var functionContextFor = (ctx, hosts) =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
var functionContextFor = (ctx, hosts, scope) => {
|
|
73
|
+
const shared = {
|
|
74
|
+
spaceId: ctx.spaceId,
|
|
75
|
+
environment: ctx.environment,
|
|
76
|
+
runId: ctx.runId,
|
|
77
|
+
trigger: ctx.trigger,
|
|
78
|
+
callerId: ctx.callerId,
|
|
79
|
+
...ctx.user ? { user: userOf(ctx.user) } : {},
|
|
80
|
+
fetch: functionFetch(ctx, hosts, scope),
|
|
81
|
+
log: (...values) => ctx.log(lineOf(values)),
|
|
82
|
+
emit: ctx.emit,
|
|
83
|
+
signal: ctx.signal
|
|
84
|
+
};
|
|
85
|
+
if (!scope) return {
|
|
86
|
+
...shared,
|
|
87
|
+
kv: ctx.kv,
|
|
88
|
+
rateLimit: (bucket, limit) => countRate(ctx.kv, ctx.callerId, bucket, limit),
|
|
89
|
+
sign: ctx.sign ?? unsigned,
|
|
90
|
+
verify: ctx.verify ?? unsigned,
|
|
91
|
+
publish: ctx.publish ?? unavailable("publish"),
|
|
92
|
+
grant: ctx.grant ?? unavailable("grant"),
|
|
93
|
+
revoke: ctx.revoke ?? unavailable("revoke")
|
|
94
|
+
};
|
|
95
|
+
const kv = prefixKv(ctx.kv, pluginKvPrefix(scope.plugin));
|
|
96
|
+
const { sign, verify } = ctx;
|
|
97
|
+
const noChannel = () => Promise.reject(/* @__PURE__ */ new Error("The functions of a plugin reach none of the realtime channels of its space"));
|
|
98
|
+
return {
|
|
99
|
+
...shared,
|
|
100
|
+
kv,
|
|
101
|
+
rateLimit: (bucket, limit) => countRate(kv, ctx.callerId, bucket, limit),
|
|
102
|
+
sign: sign ? (value) => sign(pluginSigned(scope.plugin, value)) : unsigned,
|
|
103
|
+
verify: verify ? (value, signature) => verify(pluginSigned(scope.plugin, value), signature) : unsigned,
|
|
104
|
+
publish: noChannel,
|
|
105
|
+
grant: noChannel,
|
|
106
|
+
revoke: noChannel
|
|
107
|
+
};
|
|
108
|
+
};
|
|
87
109
|
//#endregion
|
|
88
110
|
export { functionContextFor, hostAllowed, lineOf };
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { FunctionsSource } from './build';
|
|
2
2
|
import { FunctionsDefinition } from './contract';
|
|
3
|
+
/** Whether a module's default export has a definition's shape: what a server can register without surprises. */
|
|
4
|
+
export declare const isFunctionsDefinition: (value: unknown) => value is FunctionsDefinition;
|
|
3
5
|
/**
|
|
4
6
|
* A `functions/` directory as a source — the files the platform's rule counts, by their path inside it: what a space's
|
|
5
7
|
* functions are saved as, and what `loadFunctions` builds.
|
|
6
8
|
*/
|
|
7
9
|
export declare const readFunctionsDir: (dir: string | URL) => Promise<FunctionsSource>;
|
|
8
10
|
/**
|
|
9
|
-
* A server's own functions from a directory — a project's `functions/`, the working copy `plitzi functions` keeps —
|
|
11
|
+
* A server's own functions from a directory — a project's `src/functions/`, the working copy `plitzi functions` keeps —
|
|
10
12
|
* built exactly as the platform builds a space's (the same rules, one bundle) and loaded natively, for
|
|
11
13
|
* `createServer({ functions: { native } })`. The same on `node src/main.ts` and on a compiled server: nothing here
|
|
12
14
|
* depends on how the server itself runs. A directory with nothing in it is no functions; one that does not build, or
|
|
13
15
|
* whose `index.ts` exports no definition, stops the server with where it is wrong.
|
|
14
16
|
*/
|
|
15
17
|
export declare const loadFunctions: (dir: string | URL) => Promise<FunctionsDefinition[]>;
|
|
18
|
+
/**
|
|
19
|
+
* The same, from the files themselves (`FunctionsSource`, by their path): what a packed plugin carries as
|
|
20
|
+
* `functions.source.json`, loaded by a server that runs the plugin as it was packed.
|
|
21
|
+
*/
|
|
22
|
+
export declare const loadFunctionsSource: (source: FunctionsSource) => Promise<FunctionsDefinition[]>;
|
|
@@ -26,14 +26,18 @@ var isFunctionsDefinition = (value) => {
|
|
|
26
26
|
*/
|
|
27
27
|
var readFunctionsDir = (dir) => readFunctionsSource(typeof dir === "string" ? dir : fileURLToPath(dir), nodeReader);
|
|
28
28
|
/**
|
|
29
|
-
* A server's own functions from a directory — a project's `functions/`, the working copy `plitzi functions` keeps —
|
|
29
|
+
* A server's own functions from a directory — a project's `src/functions/`, the working copy `plitzi functions` keeps —
|
|
30
30
|
* built exactly as the platform builds a space's (the same rules, one bundle) and loaded natively, for
|
|
31
31
|
* `createServer({ functions: { native } })`. The same on `node src/main.ts` and on a compiled server: nothing here
|
|
32
32
|
* depends on how the server itself runs. A directory with nothing in it is no functions; one that does not build, or
|
|
33
33
|
* whose `index.ts` exports no definition, stops the server with where it is wrong.
|
|
34
34
|
*/
|
|
35
|
-
var loadFunctions = async (dir) =>
|
|
36
|
-
|
|
35
|
+
var loadFunctions = async (dir) => loadFunctionsSource(await readFunctionsDir(dir));
|
|
36
|
+
/**
|
|
37
|
+
* The same, from the files themselves (`FunctionsSource`, by their path): what a packed plugin carries as
|
|
38
|
+
* `functions.source.json`, loaded by a server that runs the plugin as it was packed.
|
|
39
|
+
*/
|
|
40
|
+
var loadFunctionsSource = async (source) => {
|
|
37
41
|
if (!Object.keys(source).length) return [];
|
|
38
42
|
const { code } = await buildFunctions(source);
|
|
39
43
|
const loaded = await import(`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`);
|
|
@@ -42,4 +46,4 @@ var loadFunctions = async (dir) => {
|
|
|
42
46
|
return [definition];
|
|
43
47
|
};
|
|
44
48
|
//#endregion
|
|
45
|
-
export { loadFunctions, readFunctionsDir };
|
|
49
|
+
export { isFunctionsDefinition, loadFunctions, loadFunctionsSource, readFunctionsDir };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FunctionLimits } from './protocol';
|
|
2
|
+
import { FunctionScope } from './scope';
|
|
2
3
|
import { FunctionsManifest } from '@plitzi/sdk-shared';
|
|
3
4
|
export declare const ROUTE_METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
4
5
|
export type RouteMethod = (typeof ROUTE_METHODS)[number];
|
|
@@ -6,6 +7,8 @@ export type RouteKey = {
|
|
|
6
7
|
method: RouteMethod;
|
|
7
8
|
segments: string[];
|
|
8
9
|
};
|
|
10
|
+
/** Why a space's (or a deployment's) route is refused: `/fn/plugins/` is where plugins answer. */
|
|
11
|
+
export declare const reservedRouteProblem: (route: string) => string;
|
|
9
12
|
/**
|
|
10
13
|
* A route key, `'<METHOD> /<path>'`: literal segments and `:params`, served under `/fn/`. Anything else — a query, a
|
|
11
14
|
* wildcard, `..` — is not a key, so what a space may answer is only ever a path it spelled out.
|
|
@@ -23,4 +26,4 @@ export type ManifestReading = {
|
|
|
23
26
|
* names a platform task always runs the platform's. `ceilings` is the most this deployment gives one invocation: what a
|
|
24
27
|
* task may ask for.
|
|
25
28
|
*/
|
|
26
|
-
export declare const readManifest: (value: unknown, reserved: ReadonlySet<string>, ceilings: FunctionLimits) => ManifestReading;
|
|
29
|
+
export declare const readManifest: (value: unknown, reserved: ReadonlySet<string>, ceilings: FunctionLimits, scope?: FunctionScope) => ManifestReading;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { pluginTaskProblem } from "./scope.js";
|
|
1
2
|
import { taskName, taskNameProblem } from "../actions/tasks/registry.js";
|
|
2
3
|
import { isRecord } from "@plitzi/sdk-shared/helpers/isRecord";
|
|
4
|
+
import { PLUGIN_ROUTES_SEGMENT } from "@plitzi/sdk-shared/actions/functions";
|
|
3
5
|
//#region src/modules/functions/manifest.ts
|
|
4
6
|
var ROUTE_METHODS = [
|
|
5
7
|
"GET",
|
|
@@ -10,6 +12,8 @@ var ROUTE_METHODS = [
|
|
|
10
12
|
];
|
|
11
13
|
var SEGMENT = /^(:[a-zA-Z][a-zA-Z0-9]*|[A-Za-z0-9._~-]+)$/;
|
|
12
14
|
var isRouteMethod = (method) => ROUTE_METHODS.some((known) => known === method);
|
|
15
|
+
/** Why a space's (or a deployment's) route is refused: `/fn/plugins/` is where plugins answer. */
|
|
16
|
+
var reservedRouteProblem = (route) => `Route "${route}" is under /plugins/, where the plugins' routes answer (/fn/plugins/<type>/…)`;
|
|
13
17
|
/**
|
|
14
18
|
* A route key, `'<METHOD> /<path>'`: literal segments and `:params`, served under `/fn/`. Anything else — a query, a
|
|
15
19
|
* wildcard, `..` — is not a key, so what a space may answer is only ever a path it spelled out.
|
|
@@ -117,7 +121,7 @@ var limitsOf = (value, where, ceilings, problems) => {
|
|
|
117
121
|
* names a platform task always runs the platform's. `ceilings` is the most this deployment gives one invocation: what a
|
|
118
122
|
* task may ask for.
|
|
119
123
|
*/
|
|
120
|
-
var readManifest = (value, reserved, ceilings) => {
|
|
124
|
+
var readManifest = (value, reserved, ceilings, scope) => {
|
|
121
125
|
const problems = [];
|
|
122
126
|
const raw = isRecord(value) ? value : {};
|
|
123
127
|
const hosts = Array.isArray(raw.hosts) ? raw.hosts.filter((host) => typeof host === "string") : [];
|
|
@@ -140,6 +144,10 @@ var readManifest = (value, reserved, ceilings) => {
|
|
|
140
144
|
problems.push(nameProblem);
|
|
141
145
|
return;
|
|
142
146
|
}
|
|
147
|
+
if (scope && namespace !== scope.plugin) {
|
|
148
|
+
problems.push(pluginTaskProblem(scope.plugin, namespace, action));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
143
151
|
const name = taskName({
|
|
144
152
|
namespace,
|
|
145
153
|
action
|
|
@@ -175,6 +183,10 @@ var readManifest = (value, reserved, ceilings) => {
|
|
|
175
183
|
problems.push(`Route "${route}" is not "<GET|POST|PUT|PATCH|DELETE> /<path>" of literal segments and :params`);
|
|
176
184
|
return;
|
|
177
185
|
}
|
|
186
|
+
if (!scope && key.segments[0] === PLUGIN_ROUTES_SEGMENT) {
|
|
187
|
+
problems.push(reservedRouteProblem(route));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
178
190
|
const shape = `${key.method} /${key.segments.map((segment) => segment.startsWith(":") ? ":" : segment).join("/")}`;
|
|
179
191
|
if (routed.has(shape)) problems.push(`Route "${route}" answers the same requests as another one`);
|
|
180
192
|
routed.add(shape);
|
|
@@ -191,4 +203,4 @@ var readManifest = (value, reserved, ceilings) => {
|
|
|
191
203
|
};
|
|
192
204
|
};
|
|
193
205
|
//#endregion
|
|
194
|
-
export { ROUTE_METHODS, parseRouteKey, readManifest };
|
|
206
|
+
export { ROUTE_METHODS, parseRouteKey, readManifest, reservedRouteProblem };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { FunctionsDefinition } from './contract';
|
|
2
|
+
import { FunctionScope } from './scope';
|
|
2
3
|
import { ActionTask } from '../actions/types';
|
|
3
4
|
/**
|
|
4
5
|
* A server's own functions, loaded as they are: trusted code the deployment ships — a self-hosted server's, or a
|
|
5
6
|
* platform's own — run in the process with the run's context narrowed to what a function may see. The same code a
|
|
6
7
|
* space runs in the sandbox, loaded without one.
|
|
7
8
|
*/
|
|
8
|
-
export declare const nativeTasks: (definitions?: readonly FunctionsDefinition[]) => ActionTask<Record<string, unknown>>[];
|
|
9
|
+
export declare const nativeTasks: (definitions?: readonly FunctionsDefinition[], scope?: FunctionScope) => ActionTask<Record<string, unknown>>[];
|
|
@@ -5,8 +5,8 @@ import { functionContextFor } from "./context.js";
|
|
|
5
5
|
* platform's own — run in the process with the run's context narrowed to what a function may see. The same code a
|
|
6
6
|
* space runs in the sandbox, loaded without one.
|
|
7
7
|
*/
|
|
8
|
-
var nativeTasks = (definitions = []) => definitions.flatMap((definition) => (definition.tasks ?? []).map((task) => {
|
|
9
|
-
const contextFor = (ctx) => functionContextFor(ctx, definition.allow?.hosts ?? []);
|
|
8
|
+
var nativeTasks = (definitions = [], scope) => definitions.flatMap((definition) => (definition.tasks ?? []).map((task) => {
|
|
9
|
+
const contextFor = (ctx) => functionContextFor(ctx, definition.allow?.hosts ?? [], scope);
|
|
10
10
|
const { run, params } = task;
|
|
11
11
|
return {
|
|
12
12
|
namespace: task.namespace,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FunctionsConfig } from './config';
|
|
2
|
+
import { FunctionsDefinition } from './contract';
|
|
2
3
|
import { SpaceFunctions } from './protocol';
|
|
3
4
|
import { TaskContextSource } from '../actions/runtime/runAction';
|
|
4
5
|
import { Environment, SpaceRevision, SSRUser } from '@plitzi/sdk-shared';
|
|
@@ -23,14 +24,18 @@ export declare const matchRoute: (keys: readonly string[], method: string, path:
|
|
|
23
24
|
} | undefined;
|
|
24
25
|
export type RouteSources = {
|
|
25
26
|
config: FunctionsConfig;
|
|
27
|
+
/** The plugins' server halves this server ships, as they are now — a development server replaces them as they change. */
|
|
28
|
+
plugins?: () => ReadonlyMap<string, FunctionsDefinition>;
|
|
26
29
|
getFunctions?: (spaceId: number, at?: SpaceRevision) => Promise<SpaceFunctions | undefined>;
|
|
30
|
+
getPluginFunctions?: (spaceId: number, at?: SpaceRevision) => Promise<Record<string, SpaceFunctions> | undefined>;
|
|
27
31
|
taskContext: TaskContextSource;
|
|
28
32
|
};
|
|
29
33
|
/**
|
|
30
|
-
* The route a request reaches, if one does:
|
|
31
|
-
*
|
|
34
|
+
* The route a request reaches, if one does: under `/plugins/<type>/`, that plugin's — this server's own, then the one
|
|
35
|
+
* the space's plugin brought; anywhere else, the deployment's own (its native functions') first, then the space's. Each
|
|
36
|
+
* runs with the same context a task of theirs gets, `trigger: 'route'` — a plugin's with a plugin's narrower one.
|
|
32
37
|
*/
|
|
33
|
-
export declare const createRoutes: ({ config, getFunctions, taskContext }: RouteSources) => {
|
|
38
|
+
export declare const createRoutes: ({ config, plugins, getFunctions, getPluginFunctions, taskContext }: RouteSources) => {
|
|
34
39
|
/** The route that answers `method path` (the path after `/fn`), for this visit — or none. */
|
|
35
40
|
routeFor: (visit: RouteVisit, method: string, path: string) => Promise<RouteHandler | undefined>;
|
|
36
41
|
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { serverLog } from "../../helpers/serverLog.js";
|
|
2
2
|
import { ActionRefusal } from "../actions/runtime/errors.js";
|
|
3
|
+
import { pluginOfPath } from "./scope.js";
|
|
3
4
|
import { functionContextFor } from "./context.js";
|
|
4
5
|
import { functionLimitsFor } from "./config.js";
|
|
5
|
-
import { parseRouteKey } from "./manifest.js";
|
|
6
|
+
import { parseRouteKey, reservedRouteProblem } from "./manifest.js";
|
|
6
7
|
import { answerCall, readCall, responseOf, wireRequestOf, wireResponseFrom } from "./capabilities.js";
|
|
7
8
|
import { FunctionFailure } from "./protocol.js";
|
|
8
9
|
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { PLUGIN_ROUTES_SEGMENT } from "@plitzi/sdk-shared/actions/functions";
|
|
9
11
|
//#region src/modules/functions/routes.ts
|
|
10
12
|
/** A route key's params from a path it answers, or nothing when it does not answer it. */
|
|
11
13
|
var matchKey = (key, method, path) => {
|
|
@@ -72,17 +74,22 @@ var failed = (visit, key, error) => {
|
|
|
72
74
|
const stopped = error instanceof FunctionFailure && error.reason !== "error";
|
|
73
75
|
return Response.json({ error: stopped ? "This route ran out of what it may spend" : "This route failed" }, { status: stopped ? 503 : 500 });
|
|
74
76
|
};
|
|
77
|
+
/** A definition's routes, as a table this process answers from. */
|
|
78
|
+
var routesOf = (definition) => Object.entries(definition.routes ?? {}).map(([key, handler]) => ({
|
|
79
|
+
key,
|
|
80
|
+
handler,
|
|
81
|
+
hosts: definition.allow?.hosts ?? []
|
|
82
|
+
}));
|
|
75
83
|
/**
|
|
76
|
-
* The route a request reaches, if one does:
|
|
77
|
-
*
|
|
84
|
+
* The route a request reaches, if one does: under `/plugins/<type>/`, that plugin's — this server's own, then the one
|
|
85
|
+
* the space's plugin brought; anywhere else, the deployment's own (its native functions') first, then the space's. Each
|
|
86
|
+
* runs with the same context a task of theirs gets, `trigger: 'route'` — a plugin's with a plugin's narrower one.
|
|
78
87
|
*/
|
|
79
|
-
var createRoutes = ({ config, getFunctions, taskContext }) => {
|
|
80
|
-
const native = (config.native ?? []).flatMap(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})));
|
|
85
|
-
const contextOf = (visit, key, hosts) => {
|
|
88
|
+
var createRoutes = ({ config, plugins = () => new Map(Object.entries(config.plugins ?? {})), getFunctions, getPluginFunctions, taskContext }) => {
|
|
89
|
+
const native = (config.native ?? []).flatMap(routesOf);
|
|
90
|
+
const reserved = native.find((route) => parseRouteKey(route.key)?.segments[0] === PLUGIN_ROUTES_SEGMENT);
|
|
91
|
+
if (reserved) throw new Error(`[Functions] ${reservedRouteProblem(reserved.key)}`);
|
|
92
|
+
const contextOf = (visit, key, hosts, scope) => {
|
|
86
93
|
const ctx = taskContext({
|
|
87
94
|
runId: randomUUID(),
|
|
88
95
|
spaceId: visit.spaceId,
|
|
@@ -98,21 +105,22 @@ var createRoutes = ({ config, getFunctions, taskContext }) => {
|
|
|
98
105
|
log: (line) => {
|
|
99
106
|
serverLog.info("Functions", `route ${key} of space ${String(visit.spaceId)}: ${line}`);
|
|
100
107
|
}
|
|
101
|
-
}, hosts),
|
|
108
|
+
}, hosts, scope),
|
|
102
109
|
trigger: "route"
|
|
103
110
|
};
|
|
104
111
|
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
112
|
+
/** A route of code this process loaded: the deployment's own, or the server half of a plugin it ships. */
|
|
113
|
+
const loadedRoute = (routes, visit, method, path, scope) => {
|
|
114
|
+
const match = matchRoute(routes.map((route) => route.key), method, path);
|
|
115
|
+
const route = match ? routes.find((entry) => entry.key === match.key) : void 0;
|
|
108
116
|
if (!match || !route) return;
|
|
109
|
-
const handler = route
|
|
117
|
+
const { handler } = route;
|
|
110
118
|
return {
|
|
111
119
|
key: match.key,
|
|
112
120
|
handle: async (request) => {
|
|
113
121
|
try {
|
|
114
122
|
return withoutCookies(await handler(withoutCredentials(request), {
|
|
115
|
-
...contextOf(visit, match.key, route.hosts),
|
|
123
|
+
...contextOf(visit, match.key, route.hosts, scope),
|
|
116
124
|
params: match.params
|
|
117
125
|
}));
|
|
118
126
|
} catch (error) {
|
|
@@ -121,17 +129,18 @@ var createRoutes = ({ config, getFunctions, taskContext }) => {
|
|
|
121
129
|
}
|
|
122
130
|
};
|
|
123
131
|
};
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
/** A route of code the runner runs: the space's own, or the server half of a plugin it uses. */
|
|
133
|
+
const runnerRoute = (functions, visit, method, path, scope) => {
|
|
126
134
|
const runner = functions?.runner ?? config.runner;
|
|
127
135
|
const match = functions ? matchRoute(functions.manifest.routes, method, path) : void 0;
|
|
128
136
|
if (!runner || !functions || !match) return;
|
|
137
|
+
const usageName = scope ? `plugin ${scope.plugin} route ${match.key}` : `route ${match.key}`;
|
|
129
138
|
return {
|
|
130
139
|
key: match.key,
|
|
131
140
|
handle: async (request) => {
|
|
132
141
|
const refusal = await config.admit?.(visit.spaceId);
|
|
133
142
|
if (refusal) return Response.json({ error: refusal }, { status: 429 });
|
|
134
|
-
const fnCtx = contextOf(visit, match.key, functions.manifest.hosts);
|
|
143
|
+
const fnCtx = contextOf(visit, match.key, functions.manifest.hosts, scope);
|
|
135
144
|
let ok = false;
|
|
136
145
|
let spent;
|
|
137
146
|
try {
|
|
@@ -165,7 +174,7 @@ var createRoutes = ({ config, getFunctions, taskContext }) => {
|
|
|
165
174
|
} finally {
|
|
166
175
|
if (spent) config.onUsage?.({
|
|
167
176
|
spaceId: visit.spaceId,
|
|
168
|
-
task:
|
|
177
|
+
task: usageName,
|
|
169
178
|
ok,
|
|
170
179
|
usage: spent
|
|
171
180
|
});
|
|
@@ -173,9 +182,20 @@ var createRoutes = ({ config, getFunctions, taskContext }) => {
|
|
|
173
182
|
}
|
|
174
183
|
};
|
|
175
184
|
};
|
|
185
|
+
const pluginRoute = async (visit, method, { plugin, path }) => {
|
|
186
|
+
const scope = { plugin };
|
|
187
|
+
const own = plugins().get(plugin);
|
|
188
|
+
if (own) return loadedRoute(routesOf(own), visit, method, path, scope);
|
|
189
|
+
const brought = (await getPluginFunctions?.(visit.spaceId, visit.at))?.[plugin];
|
|
190
|
+
return runnerRoute(brought, visit, method, path, scope);
|
|
191
|
+
};
|
|
176
192
|
return {
|
|
177
193
|
/** The route that answers `method path` (the path after `/fn`), for this visit — or none. */
|
|
178
|
-
routeFor: async (visit, method, path) =>
|
|
194
|
+
routeFor: async (visit, method, path) => {
|
|
195
|
+
const addressed = pluginOfPath(path);
|
|
196
|
+
if (addressed) return pluginRoute(visit, method, addressed);
|
|
197
|
+
return loadedRoute(native, visit, method, path) ?? runnerRoute(await getFunctions?.(visit.spaceId, visit.at), visit, method, path);
|
|
198
|
+
} };
|
|
179
199
|
};
|
|
180
200
|
//#endregion
|
|
181
201
|
export { createRoutes, matchRoute };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whose code a function is when it is not the space's: a plugin's server half, which a plugin brings with it and a space
|
|
3
|
+
* only chose to use.
|
|
4
|
+
*
|
|
5
|
+
* The space trusted the plugin to draw something, not with what it keeps — so a plugin's functions are handed a narrower
|
|
6
|
+
* `ctx`: their own corner of the space's `kv` and rate limits, signatures that only verify as theirs, the hosts they
|
|
7
|
+
* declared, and none of the space's credentials or realtime channels.
|
|
8
|
+
*/
|
|
9
|
+
export type FunctionScope = {
|
|
10
|
+
plugin: string;
|
|
11
|
+
};
|
|
12
|
+
/** Where a plugin's routes answer, under `/fn`: `/fn/plugins/<type>/…`. */
|
|
13
|
+
export declare const PLUGIN_ROUTES = "/plugins/";
|
|
14
|
+
/** A plugin's keys inside its space's `kv`: nothing of the space's, or of another plugin's, is reachable from it. */
|
|
15
|
+
export declare const pluginKvPrefix: (plugin: string) => string;
|
|
16
|
+
/**
|
|
17
|
+
* What a plugin signs is signed as ITS: the value is bound to the plugin before the space's key sees it, so a plugin
|
|
18
|
+
* cannot mint a value the space's own code — or another plugin — would accept as its own.
|
|
19
|
+
*/
|
|
20
|
+
export declare const pluginSigned: (plugin: string, value: string) => string;
|
|
21
|
+
/**
|
|
22
|
+
* The plugin a route path addresses — `/plugins/board/layout` is `board`, with `/layout` left for its own routes — or
|
|
23
|
+
* nothing for a path that is not a plugin's.
|
|
24
|
+
*/
|
|
25
|
+
export declare const pluginOfPath: (path: string) => {
|
|
26
|
+
plugin: string;
|
|
27
|
+
path: string;
|
|
28
|
+
} | undefined;
|
|
29
|
+
/** Why a plugin's task is refused: it is named after something else. */
|
|
30
|
+
export declare const pluginTaskProblem: (plugin: string, namespace: string, action: string) => string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PLUGIN_ROUTES_SEGMENT } from "@plitzi/sdk-shared/actions/functions";
|
|
2
|
+
//#region src/modules/functions/scope.ts
|
|
3
|
+
/** Where a plugin's routes answer, under `/fn`: `/fn/plugins/<type>/…`. */
|
|
4
|
+
var PLUGIN_ROUTES = `/${PLUGIN_ROUTES_SEGMENT}/`;
|
|
5
|
+
/** A plugin's keys inside its space's `kv`: nothing of the space's, or of another plugin's, is reachable from it. */
|
|
6
|
+
var pluginKvPrefix = (plugin) => `plugin:${plugin}:`;
|
|
7
|
+
/**
|
|
8
|
+
* What a plugin signs is signed as ITS: the value is bound to the plugin before the space's key sees it, so a plugin
|
|
9
|
+
* cannot mint a value the space's own code — or another plugin — would accept as its own.
|
|
10
|
+
*/
|
|
11
|
+
var pluginSigned = (plugin, value) => `plugin:${plugin}\n${value}`;
|
|
12
|
+
/**
|
|
13
|
+
* The plugin a route path addresses — `/plugins/board/layout` is `board`, with `/layout` left for its own routes — or
|
|
14
|
+
* nothing for a path that is not a plugin's.
|
|
15
|
+
*/
|
|
16
|
+
var pluginOfPath = (path) => {
|
|
17
|
+
const trimmed = `/${path.replace(/^\/+/, "")}`;
|
|
18
|
+
if (!trimmed.startsWith(PLUGIN_ROUTES)) return;
|
|
19
|
+
const [plugin = "", ...rest] = trimmed.slice(PLUGIN_ROUTES.length).split("/");
|
|
20
|
+
return plugin ? {
|
|
21
|
+
plugin,
|
|
22
|
+
path: `/${rest.join("/")}`
|
|
23
|
+
} : void 0;
|
|
24
|
+
};
|
|
25
|
+
/** Why a plugin's task is refused: it is named after something else. */
|
|
26
|
+
var pluginTaskProblem = (plugin, namespace, action) => `Task "${namespace}.${action}" of plugin "${plugin}": a plugin's tasks are named after it — namespace "${plugin}"`;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { PLUGIN_ROUTES, pluginKvPrefix, pluginOfPath, pluginSigned, pluginTaskProblem };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FunctionsSource } from './build';
|
|
2
2
|
import { FunctionsConfig } from './config';
|
|
3
3
|
import { FunctionsBundle, FunctionLimits, FunctionRunner, SpaceFunctions } from './protocol';
|
|
4
|
+
import { FunctionScope } from './scope';
|
|
4
5
|
import { ActionTask, ActionTaskRegistry } from '../actions/types';
|
|
5
6
|
import { FunctionsManifest, FunctionsProblem, FunctionTimeLimits } from '@plitzi/sdk-shared';
|
|
6
7
|
/**
|
|
@@ -8,10 +9,13 @@ import { FunctionsManifest, FunctionsProblem, FunctionTimeLimits } from '@plitzi
|
|
|
8
9
|
* {@link FunctionContext}: the same one native functions get, built by the same function. Each is given what
|
|
9
10
|
* `limitsFor` answers for what it asked: its own limits, over its functions'.
|
|
10
11
|
*/
|
|
11
|
-
export declare const spaceTasks: (functions: SpaceFunctions, runner: FunctionRunner, limitsFor: (asked: FunctionTimeLimits) => FunctionLimits, { admit, onUsage }?: Pick<FunctionsConfig, "admit" | "onUsage"
|
|
12
|
+
export declare const spaceTasks: (functions: SpaceFunctions, runner: FunctionRunner, limitsFor: (asked: FunctionTimeLimits) => FunctionLimits, { admit, onUsage }?: Pick<FunctionsConfig, "admit" | "onUsage">, scope?: FunctionScope) => ActionTask<Record<string, unknown>>[];
|
|
12
13
|
export type SpaceRegistries = {
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The tasks one run of that space can use: the deployment's, its own functions' when it has any, and those of the
|
|
16
|
+
* plugins it uses that bring a server half (by plugin type).
|
|
17
|
+
*/
|
|
18
|
+
registryFor: (functions: SpaceFunctions | undefined, plugins?: Readonly<Record<string, SpaceFunctions>>) => ActionTaskRegistry;
|
|
15
19
|
};
|
|
16
20
|
/**
|
|
17
21
|
* Registries per space bundle, built once per bundle and limits — a space's functions change when they are saved, and
|
|
@@ -37,6 +41,7 @@ export type PreparedFunctions = {
|
|
|
37
41
|
/**
|
|
38
42
|
* A space's source made ready to save: built, read by the runner, and checked — the only way a space's functions are
|
|
39
43
|
* stored, so what is stored always passed every rule. `reserved` is every namespace the deployment's own tasks use;
|
|
40
|
-
* `ceilings`, the most it gives one invocation — what a task may ask for
|
|
44
|
+
* `ceilings`, the most it gives one invocation — what a task may ask for; `scope`, a plugin's server half rather than a
|
|
45
|
+
* space's (its tasks named after it, its routes its own).
|
|
41
46
|
*/
|
|
42
|
-
export declare const prepareFunctions: (source: FunctionsSource, runner: FunctionRunner, reserved: ReadonlySet<string>, ceilings: FunctionLimits) => Promise<PreparedFunctions>;
|
|
47
|
+
export declare const prepareFunctions: (source: FunctionsSource, runner: FunctionRunner, reserved: ReadonlySet<string>, ceilings: FunctionLimits, scope?: FunctionScope) => Promise<PreparedFunctions>;
|
|
@@ -21,7 +21,7 @@ var invocationContextOf = ({ spaceId, environment, runId, trigger, callerId, use
|
|
|
21
21
|
* {@link FunctionContext}: the same one native functions get, built by the same function. Each is given what
|
|
22
22
|
* `limitsFor` answers for what it asked: its own limits, over its functions'.
|
|
23
23
|
*/
|
|
24
|
-
var spaceTasks = (functions, runner, limitsFor, { admit, onUsage } = {}) => functions.manifest.tasks.map((task) => {
|
|
24
|
+
var spaceTasks = (functions, runner, limitsFor, { admit, onUsage } = {}, scope) => functions.manifest.tasks.map((task) => {
|
|
25
25
|
const name = taskName(task);
|
|
26
26
|
const limits = limitsFor({
|
|
27
27
|
...functions.manifest.limits,
|
|
@@ -36,7 +36,7 @@ var spaceTasks = (functions, runner, limitsFor, { admit, onUsage } = {}) => func
|
|
|
36
36
|
run: async (params, ctx) => {
|
|
37
37
|
const refusal = await admit?.(ctx.spaceId);
|
|
38
38
|
if (refusal) throw new Error(refusal);
|
|
39
|
-
const fnCtx = functionContextFor(ctx, functions.manifest.hosts);
|
|
39
|
+
const fnCtx = functionContextFor(ctx, functions.manifest.hosts, scope);
|
|
40
40
|
let ok = false;
|
|
41
41
|
let spent;
|
|
42
42
|
try {
|
|
@@ -71,15 +71,18 @@ var spaceTasks = (functions, runner, limitsFor, { admit, onUsage } = {}) => func
|
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
73
|
});
|
|
74
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* The deployment's tasks, a space's and its plugins', one registry: neither ever answers a name the deployment's already
|
|
76
|
+
* does, and a plugin's are named after it (`<type>.<action>`), so they meet nobody else's.
|
|
77
|
+
*/
|
|
75
78
|
var composeRegistry = (base, tasks) => {
|
|
76
79
|
const own = /* @__PURE__ */ new Map();
|
|
77
|
-
tasks.forEach((task) => {
|
|
80
|
+
tasks.forEach(({ task, origin }) => {
|
|
78
81
|
const name = taskName(task);
|
|
79
|
-
if (!base.get(name)) own.set(name, {
|
|
82
|
+
if (!base.get(name) && !own.has(name)) own.set(name, {
|
|
80
83
|
...task,
|
|
81
84
|
name,
|
|
82
|
-
origin
|
|
85
|
+
origin
|
|
83
86
|
});
|
|
84
87
|
});
|
|
85
88
|
return {
|
|
@@ -94,14 +97,32 @@ var REGISTRY_CACHE = 500;
|
|
|
94
97
|
*/
|
|
95
98
|
var createSpaceRegistries = (base, config = {}) => {
|
|
96
99
|
const cache = /* @__PURE__ */ new Map();
|
|
97
|
-
return { registryFor: (functions) => {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
return { registryFor: (functions, plugins = {}) => {
|
|
101
|
+
const runnable = [...functions ? [{
|
|
102
|
+
functions,
|
|
103
|
+
scope: void 0
|
|
104
|
+
}] : [], ...Object.entries(plugins).map(([plugin, own]) => ({
|
|
105
|
+
functions: own,
|
|
106
|
+
scope: { plugin }
|
|
107
|
+
}))].flatMap(({ functions: found, scope }) => {
|
|
108
|
+
const runner = found.runner ?? config.runner;
|
|
109
|
+
return runner ? [{
|
|
110
|
+
functions: found,
|
|
111
|
+
runner,
|
|
112
|
+
scope
|
|
113
|
+
}] : [];
|
|
114
|
+
});
|
|
115
|
+
if (runnable.length === 0) return base;
|
|
116
|
+
const key = runnable.map(({ functions: found, scope }) => `${scope?.plugin ?? ""}:${found.bundle.id}:${JSON.stringify(found.limits ?? {})}`).join("|");
|
|
101
117
|
const found = cache.get(key);
|
|
102
118
|
if (found) return found;
|
|
103
|
-
const
|
|
104
|
-
|
|
119
|
+
const registry = composeRegistry(base, runnable.flatMap(({ functions: own, runner, scope }) => {
|
|
120
|
+
const limitsFor = (asked) => functionLimitsFor(config.limits, own.limits, asked);
|
|
121
|
+
return spaceTasks(own, runner, limitsFor, config, scope).map((task) => ({
|
|
122
|
+
task,
|
|
123
|
+
origin: scope ? "plugin" : "space"
|
|
124
|
+
}));
|
|
125
|
+
}));
|
|
105
126
|
cache.set(key, registry);
|
|
106
127
|
if (cache.size > REGISTRY_CACHE) {
|
|
107
128
|
const oldest = cache.keys().next().value;
|
|
@@ -121,9 +142,10 @@ var functionsInHand = ({ bundle, manifest }) => ({
|
|
|
121
142
|
/**
|
|
122
143
|
* A space's source made ready to save: built, read by the runner, and checked — the only way a space's functions are
|
|
123
144
|
* stored, so what is stored always passed every rule. `reserved` is every namespace the deployment's own tasks use;
|
|
124
|
-
* `ceilings`, the most it gives one invocation — what a task may ask for
|
|
145
|
+
* `ceilings`, the most it gives one invocation — what a task may ask for; `scope`, a plugin's server half rather than a
|
|
146
|
+
* space's (its tasks named after it, its routes its own).
|
|
125
147
|
*/
|
|
126
|
-
var prepareFunctions = async (source, runner, reserved, ceilings) => {
|
|
148
|
+
var prepareFunctions = async (source, runner, reserved, ceilings, scope) => {
|
|
127
149
|
let code;
|
|
128
150
|
try {
|
|
129
151
|
({code} = await buildFunctions(source));
|
|
@@ -150,7 +172,7 @@ var prepareFunctions = async (source, runner, reserved, ceilings) => {
|
|
|
150
172
|
}]
|
|
151
173
|
};
|
|
152
174
|
}
|
|
153
|
-
const { manifest, problems } = readManifest(declared, reserved, ceilings);
|
|
175
|
+
const { manifest, problems } = readManifest(declared, reserved, ceilings, scope);
|
|
154
176
|
return problems.length ? {
|
|
155
177
|
ok: false,
|
|
156
178
|
problems: problems.map((message) => ({
|
|
@@ -17,7 +17,7 @@ import { SSRAdapters } from '@plitzi/sdk-shared';
|
|
|
17
17
|
*
|
|
18
18
|
* A deployment that resolves server elements some other way still supplies its own `getRscData`, and that one wins.
|
|
19
19
|
*/
|
|
20
|
-
export declare const connectorRscData: ({ connectors, actions, publicDir, elementTimeoutMs }: {
|
|
20
|
+
export declare const connectorRscData: ({ connectors, actions, publicDir, dataDir, elementTimeoutMs }: {
|
|
21
21
|
connectors?: ConnectorLookups;
|
|
22
22
|
actions?: {
|
|
23
23
|
lookups: ActionLookups;
|
|
@@ -25,6 +25,8 @@ export declare const connectorRscData: ({ connectors, actions, publicDir, elemen
|
|
|
25
25
|
};
|
|
26
26
|
/** Where the server's static files are: a provider whose `query` is one of them is read from disk. */
|
|
27
27
|
publicDir?: string;
|
|
28
|
+
/** The project's own data, never served: a provider whose `query` is `/data/<file>` is read from it. */
|
|
29
|
+
dataDir?: string;
|
|
28
30
|
/** The deployment's per-element ceiling, when it set one. `resolveRscData` decides the default. */
|
|
29
31
|
elementTimeoutMs?: number;
|
|
30
32
|
}) => NonNullable<SSRAdapters["getRscData"]>;
|