@mdsnai/sdk 0.1.0 → 0.2.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/README.md +40 -0
- package/dist/core/document/markdown.js +2 -4
- package/dist/core/document/page-definition.js +1 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +9 -1
- package/dist/core/model/block.d.ts +4 -8
- package/dist/core/model/block.js +6 -0
- package/dist/core/model/document.d.ts +0 -2
- package/dist/core/model/index.d.ts +1 -2
- package/dist/core/model/input.d.ts +1 -2
- package/dist/core/protocol/mdsn.d.ts +0 -2
- package/dist/core/protocol/mdsn.js +4 -18
- package/dist/core/protocol/statements.d.ts +3 -8
- package/dist/core/protocol/statements.js +47 -72
- package/dist/core/protocol/validation.d.ts +2 -3
- package/dist/core/protocol/validation.js +21 -11
- package/dist/core/utils/html.d.ts +6 -0
- package/dist/core/utils/html.js +28 -0
- package/dist/core/utils/index.d.ts +2 -0
- package/dist/core/utils/index.js +12 -0
- package/dist/core/utils/logger.d.ts +12 -0
- package/dist/core/utils/logger.js +45 -0
- package/dist/framework/create-framework-app.d.ts +1 -0
- package/dist/framework/create-framework-app.js +1 -0
- package/dist/framework/hosted-app.d.ts +21 -0
- package/dist/framework/hosted-app.js +123 -33
- package/dist/framework/index.d.ts +2 -0
- package/dist/framework/index.js +3 -1
- package/dist/framework/site-app.d.ts +1 -0
- package/dist/framework/site-app.js +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +18 -1
- package/dist/server/action-context.d.ts +11 -0
- package/dist/server/action-context.js +26 -0
- package/dist/server/action-host.d.ts +2 -3
- package/dist/server/action-host.js +4 -2
- package/dist/server/action-inputs.d.ts +3 -0
- package/dist/server/action-inputs.js +178 -0
- package/dist/server/action-runtime.d.ts +3 -3
- package/dist/server/action-runtime.js +2 -21
- package/dist/server/action.d.ts +1 -9
- package/dist/server/action.js +5 -1
- package/dist/server/build.js +4 -0
- package/dist/server/error-fragments.d.ts +46 -0
- package/dist/server/error-fragments.js +77 -0
- package/dist/server/index.d.ts +9 -2
- package/dist/server/index.js +17 -1
- package/dist/server/init.js +14 -14
- package/dist/server/markdown.d.ts +2 -6
- package/dist/server/markdown.js +12 -11
- package/dist/server/server.d.ts +2 -1
- package/dist/server/server.js +17 -8
- package/dist/server/session.d.ts +40 -0
- package/dist/server/session.js +220 -0
- package/dist/web/block-runtime.js +15 -17
- package/dist/web/fragment-render.d.ts +0 -2
- package/dist/web/fragment-render.js +0 -1
- package/dist/web/i18n.d.ts +0 -2
- package/dist/web/i18n.js +0 -4
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +2 -1
- package/dist/web/page-bootstrap.js +0 -1
- package/dist/web/page-client-runtime.d.ts +2 -13
- package/dist/web/page-client-runtime.js +1 -16
- package/dist/web/page-client-script.d.ts +0 -1
- package/dist/web/page-client-script.js +172 -160
- package/dist/web/page-html.js +4 -11
- package/dist/web/page-render.d.ts +1 -1
- package/dist/web/page-render.js +13 -21
- package/package.json +1 -1
- package/dist/core/action/execution.d.ts +0 -4
- package/dist/core/action/execution.js +0 -57
- package/dist/core/action/index.d.ts +0 -2
- package/dist/core/action/index.js +0 -7
- package/dist/core/action/types.d.ts +0 -19
- package/dist/core/action/types.js +0 -2
- package/dist/core/model/schema.d.ts +0 -4
- package/dist/core/model/schema.js +0 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ActionHandler } from "../server/action-host";
|
|
2
|
+
import { type SerializableBlock } from "../server/markdown";
|
|
2
3
|
import { type RenderHostedPageOptions } from "../server/page-host";
|
|
3
4
|
export interface CreateHostedAppOptions {
|
|
4
5
|
pages: Record<string, string>;
|
|
@@ -6,8 +7,28 @@ export interface CreateHostedAppOptions {
|
|
|
6
7
|
inputs: Record<string, unknown>;
|
|
7
8
|
}>>;
|
|
8
9
|
publicDir?: string;
|
|
10
|
+
errorFragments?: Partial<HostedAppErrorFragments>;
|
|
9
11
|
render?: Omit<RenderHostedPageOptions, "accept" | "routePath" | "layoutTemplate"> & {
|
|
10
12
|
resolveLayoutTemplate?: (routePath: string, rawPage: string) => string | undefined;
|
|
11
13
|
};
|
|
12
14
|
}
|
|
15
|
+
export interface HostedAppErrorFragmentContext {
|
|
16
|
+
actionId: string;
|
|
17
|
+
block: SerializableBlock;
|
|
18
|
+
method: "GET" | "POST";
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
export interface HostedAppActionNotAvailableContext extends HostedAppErrorFragmentContext {
|
|
22
|
+
}
|
|
23
|
+
export interface HostedAppUnsupportedContentTypeContext extends HostedAppErrorFragmentContext {
|
|
24
|
+
contentType: string;
|
|
25
|
+
}
|
|
26
|
+
export interface HostedAppInternalErrorContext extends HostedAppErrorFragmentContext {
|
|
27
|
+
error: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface HostedAppErrorFragments {
|
|
30
|
+
actionNotAvailable: (ctx: HostedAppActionNotAvailableContext) => string;
|
|
31
|
+
unsupportedContentType: (ctx: HostedAppUnsupportedContentTypeContext) => string;
|
|
32
|
+
internalError: (ctx: HostedAppInternalErrorContext) => string;
|
|
33
|
+
}
|
|
13
34
|
export declare function createHostedApp(options: CreateHostedAppOptions): import("express-serve-static-core").Express;
|
|
@@ -6,7 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.createHostedApp = createHostedApp;
|
|
7
7
|
const express_1 = __importDefault(require("express"));
|
|
8
8
|
const page_definition_1 = require("../core/document/page-definition");
|
|
9
|
+
const block_1 = require("../core/model/block");
|
|
9
10
|
const action_host_1 = require("../server/action-host");
|
|
11
|
+
const action_inputs_1 = require("../server/action-inputs");
|
|
12
|
+
const error_fragments_1 = require("../server/error-fragments");
|
|
10
13
|
const page_links_1 = require("../server/page-links");
|
|
11
14
|
const page_host_1 = require("../server/page-host");
|
|
12
15
|
const negotiate_1 = require("../server/negotiate");
|
|
@@ -23,6 +26,9 @@ function normalizeActionId(target) {
|
|
|
23
26
|
}
|
|
24
27
|
return trimmed.replace(/^\/+/u, "");
|
|
25
28
|
}
|
|
29
|
+
function createActionBindingKey(method, path) {
|
|
30
|
+
return `${method}:${path}`;
|
|
31
|
+
}
|
|
26
32
|
function buildActionBindings(pages) {
|
|
27
33
|
const bindings = new Map();
|
|
28
34
|
for (const pageSource of Object.values(pages)) {
|
|
@@ -34,82 +40,166 @@ function buildActionBindings(pages) {
|
|
|
34
40
|
continue;
|
|
35
41
|
}
|
|
36
42
|
for (const block of document.blocks) {
|
|
37
|
-
for (const operation of
|
|
43
|
+
for (const operation of block.reads) {
|
|
44
|
+
if (!operation.name || (0, block_1.isStreamAccept)(operation.accept)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const targetPath = (0, page_links_1.mapPageTargetToHttpPath)(operation.target);
|
|
48
|
+
const actionId = normalizeActionId(operation.target);
|
|
49
|
+
if (!actionId) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const key = createActionBindingKey("GET", targetPath);
|
|
53
|
+
const existing = bindings.get(key);
|
|
54
|
+
if (existing
|
|
55
|
+
&& (existing.actionId !== actionId || existing.blockName !== block.name || existing.kind !== "read")) {
|
|
56
|
+
throw new Error(`Action target must bind to one stable block context: GET ${targetPath}`);
|
|
57
|
+
}
|
|
58
|
+
bindings.set(key, {
|
|
59
|
+
actionId,
|
|
60
|
+
blockName: block.name,
|
|
61
|
+
block,
|
|
62
|
+
kind: "read",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
for (const operation of block.writes) {
|
|
38
66
|
const targetPath = (0, page_links_1.mapPageTargetToHttpPath)(operation.target);
|
|
39
67
|
const actionId = normalizeActionId(operation.target);
|
|
40
68
|
if (!actionId) {
|
|
41
69
|
continue;
|
|
42
70
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
71
|
+
const key = createActionBindingKey("POST", targetPath);
|
|
72
|
+
const existing = bindings.get(key);
|
|
73
|
+
if (existing
|
|
74
|
+
&& (existing.actionId !== actionId || existing.blockName !== block.name || existing.kind !== "write")) {
|
|
75
|
+
throw new Error(`Action target must bind to one stable block context: POST ${targetPath}`);
|
|
46
76
|
}
|
|
47
|
-
bindings.set(
|
|
77
|
+
bindings.set(key, {
|
|
48
78
|
actionId,
|
|
49
79
|
blockName: block.name,
|
|
80
|
+
block,
|
|
81
|
+
kind: "write",
|
|
50
82
|
});
|
|
51
83
|
}
|
|
52
84
|
}
|
|
53
85
|
}
|
|
54
86
|
return bindings;
|
|
55
87
|
}
|
|
88
|
+
function blockToSerializableBlock(block) {
|
|
89
|
+
return {
|
|
90
|
+
name: block.name,
|
|
91
|
+
inputs: block.inputs.map((input) => ({
|
|
92
|
+
name: input.name,
|
|
93
|
+
type: input.type,
|
|
94
|
+
required: input.required,
|
|
95
|
+
secret: input.secret,
|
|
96
|
+
options: input.options,
|
|
97
|
+
})),
|
|
98
|
+
reads: block.reads.map((read) => ({
|
|
99
|
+
name: read.name,
|
|
100
|
+
target: read.target,
|
|
101
|
+
inputs: read.inputs,
|
|
102
|
+
accept: read.accept,
|
|
103
|
+
})),
|
|
104
|
+
writes: block.writes.map((write) => ({
|
|
105
|
+
name: write.name,
|
|
106
|
+
target: write.target,
|
|
107
|
+
inputs: write.inputs,
|
|
108
|
+
})),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function sendActionFragment(reqAccept, status, markdown, binding, res, options) {
|
|
112
|
+
if ((0, negotiate_1.wantsMarkdown)(reqAccept) || !(0, negotiate_1.wantsHtml)(reqAccept)) {
|
|
113
|
+
res.status(status).type("text/markdown; charset=utf-8").send(markdown);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
res.status(status).type("text/html; charset=utf-8").send((0, fragment_render_1.renderBlockFragmentHtml)(markdown, binding.blockName, {
|
|
117
|
+
mapActionTarget: options.render?.mapActionTarget ?? page_links_1.mapPageTargetToHttpPath,
|
|
118
|
+
markdown: options.render?.markdown,
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
56
121
|
function createHostedApp(options) {
|
|
57
122
|
const app = (0, express_1.default)();
|
|
58
123
|
const actionBindings = buildActionBindings(options.pages);
|
|
124
|
+
const errorFragments = {
|
|
125
|
+
actionNotAvailable: (ctx) => (0, error_fragments_1.renderActionNotAvailableFragment)({ block: ctx.block }),
|
|
126
|
+
unsupportedContentType: (ctx) => (0, error_fragments_1.renderUnsupportedContentTypeFragment)({ block: ctx.block }),
|
|
127
|
+
internalError: (ctx) => (0, error_fragments_1.renderInternalErrorFragment)({ block: ctx.block, error: ctx.error }),
|
|
128
|
+
...options.errorFragments,
|
|
129
|
+
};
|
|
59
130
|
const routedPages = (0, route_matcher_1.sortRoutedPagesForMatching)(Object.entries(options.pages).map(([routePath, source]) => ({
|
|
60
131
|
routePath,
|
|
61
132
|
source,
|
|
62
133
|
})));
|
|
63
|
-
app.use(express_1.default.
|
|
134
|
+
app.use(express_1.default.text({
|
|
135
|
+
type: ["text/markdown"],
|
|
136
|
+
limit: "1mb",
|
|
137
|
+
}));
|
|
64
138
|
if (typeof options.publicDir === "string") {
|
|
65
139
|
app.use(express_1.default.static(options.publicDir));
|
|
66
140
|
}
|
|
67
141
|
app.get("/__mdsn/client.js", (_req, res) => {
|
|
68
142
|
res.type("application/javascript; charset=utf-8").send((0, page_client_script_1.getPageClientRuntimeScript)());
|
|
69
143
|
});
|
|
70
|
-
|
|
71
|
-
const binding = actionBindings.get(req.path);
|
|
144
|
+
async function handleActionRequest(req, res, method, next) {
|
|
145
|
+
const binding = actionBindings.get(createActionBindingKey(method, req.path));
|
|
72
146
|
if (!binding) {
|
|
73
147
|
next();
|
|
74
148
|
return;
|
|
75
149
|
}
|
|
76
150
|
const action = options.actions?.[binding.actionId];
|
|
77
151
|
if (!action) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
152
|
+
const block = blockToSerializableBlock(binding.block);
|
|
153
|
+
sendActionFragment(req.headers.accept, 404, errorFragments.actionNotAvailable({
|
|
154
|
+
actionId: binding.actionId,
|
|
155
|
+
block,
|
|
156
|
+
method,
|
|
157
|
+
path: req.path,
|
|
158
|
+
}), binding, res, options);
|
|
82
159
|
return;
|
|
83
160
|
}
|
|
84
161
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
162
|
+
if (method === "POST") {
|
|
163
|
+
const contentType = req.headers["content-type"] ?? "";
|
|
164
|
+
const normalized = String(contentType).toLowerCase();
|
|
165
|
+
if (!normalized.includes("text/markdown")) {
|
|
166
|
+
const block = blockToSerializableBlock(binding.block);
|
|
167
|
+
sendActionFragment(req.headers.accept, 415, errorFragments.unsupportedContentType({
|
|
168
|
+
actionId: binding.actionId,
|
|
169
|
+
block,
|
|
170
|
+
method,
|
|
171
|
+
path: req.path,
|
|
172
|
+
contentType: String(contentType),
|
|
173
|
+
}), binding, res, options);
|
|
91
174
|
return;
|
|
92
175
|
}
|
|
93
|
-
res.status(200).json({
|
|
94
|
-
...result,
|
|
95
|
-
html: (0, fragment_render_1.renderBlockFragmentHtml)(result.markdown, binding.blockName, {
|
|
96
|
-
mapActionTarget: options.render?.mapActionTarget ?? page_links_1.mapPageTargetToHttpPath,
|
|
97
|
-
markdown: options.render?.markdown,
|
|
98
|
-
}),
|
|
99
|
-
});
|
|
100
|
-
return;
|
|
101
176
|
}
|
|
102
|
-
|
|
177
|
+
const result = await (0, action_host_1.executeActionHandler)(action, {
|
|
178
|
+
inputs: method === "GET"
|
|
179
|
+
? (0, action_inputs_1.parseActionInputs)(req.query)
|
|
180
|
+
: (0, action_inputs_1.parseActionInputs)(typeof req.body === "string" ? req.body : ""),
|
|
181
|
+
});
|
|
182
|
+
sendActionFragment(req.headers.accept, 200, result, binding, res, options);
|
|
103
183
|
}
|
|
104
184
|
catch (error) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
185
|
+
sendActionFragment(req.headers.accept, 500, errorFragments.internalError({
|
|
186
|
+
actionId: binding.actionId,
|
|
187
|
+
block: blockToSerializableBlock(binding.block),
|
|
188
|
+
method,
|
|
189
|
+
path: req.path,
|
|
190
|
+
error,
|
|
191
|
+
}), binding, res, options);
|
|
110
192
|
}
|
|
193
|
+
}
|
|
194
|
+
app.post("*", async (req, res, next) => {
|
|
195
|
+
await handleActionRequest(req, res, "POST", next);
|
|
111
196
|
});
|
|
112
|
-
app.get("*", (req, res) => {
|
|
197
|
+
app.get("*", async (req, res) => {
|
|
198
|
+
const actionBinding = actionBindings.get(createActionBindingKey("GET", req.path));
|
|
199
|
+
if (actionBinding) {
|
|
200
|
+
await handleActionRequest(req, res, "GET", () => undefined);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
113
203
|
const matchedPage = (0, route_matcher_1.resolveRoutedPageForPath)(req.path, routedPages);
|
|
114
204
|
if (!matchedPage) {
|
|
115
205
|
res.status(404).type("text/plain; charset=utf-8").send("Not Found");
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export { createHostedApp } from "./hosted-app";
|
|
1
2
|
export { createFrameworkApp } from "./create-framework-app";
|
|
3
|
+
export type { CreateHostedAppOptions } from "./hosted-app";
|
|
2
4
|
export type { CreateFrameworkAppOptions } from "./create-framework-app";
|
|
3
5
|
export { defineConfig } from "../server/config";
|
|
4
6
|
export type { MdsnConfig } from "../server/config";
|
package/dist/framework/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defineConfig = exports.createFrameworkApp = void 0;
|
|
3
|
+
exports.defineConfig = exports.createFrameworkApp = exports.createHostedApp = void 0;
|
|
4
|
+
var hosted_app_1 = require("./hosted-app");
|
|
5
|
+
Object.defineProperty(exports, "createHostedApp", { enumerable: true, get: function () { return hosted_app_1.createHostedApp; } });
|
|
4
6
|
var create_framework_app_1 = require("./create-framework-app");
|
|
5
7
|
Object.defineProperty(exports, "createFrameworkApp", { enumerable: true, get: function () { return create_framework_app_1.createFrameworkApp; } });
|
|
6
8
|
var config_1 = require("../server/config");
|
|
@@ -8,5 +8,6 @@ export interface CreateSiteAppOptions {
|
|
|
8
8
|
rootDir: string;
|
|
9
9
|
config?: MdsnConfig;
|
|
10
10
|
actions?: CreateHostedAppOptions["actions"];
|
|
11
|
+
errorFragments?: CreateHostedAppOptions["errorFragments"];
|
|
11
12
|
}
|
|
12
13
|
export declare function createSiteApp(options: CreateSiteAppOptions): import("express-serve-static-core").Express;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { parsePageDefinition } from "./core";
|
|
2
|
-
export type { BlockAnchorDefinition, BlockDefinition, DocumentDefinition, FrontmatterData, InputDefinition, InputType, ReadDefinition,
|
|
2
|
+
export type { BlockAnchorDefinition, BlockDefinition, DocumentDefinition, FrontmatterData, InputDefinition, InputType, ReadDefinition, WriteDefinition, } from "./core";
|
|
3
3
|
export { createRenderModel, getClientRuntimeScript, parseFragment, parseMarkdown, parsePage, renderDefaultHtmlDocument, renderPageHtml, } from "./web";
|
|
4
4
|
export type { CreateRenderModelOptions, ParsedFragment, ParsedPage, RenderModel, } from "./web";
|
|
5
|
-
export { createFrameworkApp, defineConfig, } from "./framework";
|
|
6
|
-
export type { CreateFrameworkAppOptions, MdsnConfig, } from "./framework";
|
|
7
|
-
export { defineAction } from "./server";
|
|
8
|
-
export type { ActionContext, ActionDefinition,
|
|
5
|
+
export { createHostedApp, createFrameworkApp, defineConfig, } from "./framework";
|
|
6
|
+
export type { CreateHostedAppOptions, CreateFrameworkAppOptions, MdsnConfig, } from "./framework";
|
|
7
|
+
export { HttpCookieJar, createActionContextFromRequest, defineAction, defineActions, parseActionInputs, parseCookieHeader, renderActionNotAvailableFragment, renderAuthRequiredFragment, renderErrorFragment, renderHostedPage, renderInternalErrorFragment, renderMarkdownFragment, renderMarkdownValue, renderUnsupportedContentTypeFragment, requireSessionFromCookie, serializeActionInputsAsMarkdown, serializeBlock, } from "./server";
|
|
8
|
+
export type { ActionContext, ActionDefinition, ActionDefinitionMap, CreateActionContextFromRequestOptions, HostedPageResponse, SerializableBlock, SerializableInput, SerializableRead, SerializableWrite, } from "./server";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defineAction = exports.defineConfig = exports.createFrameworkApp = exports.renderPageHtml = exports.renderDefaultHtmlDocument = exports.parsePage = exports.parseMarkdown = exports.parseFragment = exports.getClientRuntimeScript = exports.createRenderModel = exports.parsePageDefinition = void 0;
|
|
3
|
+
exports.serializeBlock = exports.serializeActionInputsAsMarkdown = exports.requireSessionFromCookie = exports.renderUnsupportedContentTypeFragment = exports.renderMarkdownValue = exports.renderMarkdownFragment = exports.renderInternalErrorFragment = exports.renderHostedPage = exports.renderErrorFragment = exports.renderAuthRequiredFragment = exports.renderActionNotAvailableFragment = exports.parseCookieHeader = exports.parseActionInputs = exports.defineActions = exports.defineAction = exports.createActionContextFromRequest = exports.HttpCookieJar = exports.defineConfig = exports.createFrameworkApp = exports.createHostedApp = exports.renderPageHtml = exports.renderDefaultHtmlDocument = exports.parsePage = exports.parseMarkdown = exports.parseFragment = exports.getClientRuntimeScript = exports.createRenderModel = exports.parsePageDefinition = void 0;
|
|
4
4
|
var core_1 = require("./core");
|
|
5
5
|
Object.defineProperty(exports, "parsePageDefinition", { enumerable: true, get: function () { return core_1.parsePageDefinition; } });
|
|
6
6
|
var web_1 = require("./web");
|
|
@@ -12,7 +12,24 @@ Object.defineProperty(exports, "parsePage", { enumerable: true, get: function ()
|
|
|
12
12
|
Object.defineProperty(exports, "renderDefaultHtmlDocument", { enumerable: true, get: function () { return web_1.renderDefaultHtmlDocument; } });
|
|
13
13
|
Object.defineProperty(exports, "renderPageHtml", { enumerable: true, get: function () { return web_1.renderPageHtml; } });
|
|
14
14
|
var framework_1 = require("./framework");
|
|
15
|
+
Object.defineProperty(exports, "createHostedApp", { enumerable: true, get: function () { return framework_1.createHostedApp; } });
|
|
15
16
|
Object.defineProperty(exports, "createFrameworkApp", { enumerable: true, get: function () { return framework_1.createFrameworkApp; } });
|
|
16
17
|
Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return framework_1.defineConfig; } });
|
|
17
18
|
var server_1 = require("./server");
|
|
19
|
+
Object.defineProperty(exports, "HttpCookieJar", { enumerable: true, get: function () { return server_1.HttpCookieJar; } });
|
|
20
|
+
Object.defineProperty(exports, "createActionContextFromRequest", { enumerable: true, get: function () { return server_1.createActionContextFromRequest; } });
|
|
18
21
|
Object.defineProperty(exports, "defineAction", { enumerable: true, get: function () { return server_1.defineAction; } });
|
|
22
|
+
Object.defineProperty(exports, "defineActions", { enumerable: true, get: function () { return server_1.defineActions; } });
|
|
23
|
+
Object.defineProperty(exports, "parseActionInputs", { enumerable: true, get: function () { return server_1.parseActionInputs; } });
|
|
24
|
+
Object.defineProperty(exports, "parseCookieHeader", { enumerable: true, get: function () { return server_1.parseCookieHeader; } });
|
|
25
|
+
Object.defineProperty(exports, "renderActionNotAvailableFragment", { enumerable: true, get: function () { return server_1.renderActionNotAvailableFragment; } });
|
|
26
|
+
Object.defineProperty(exports, "renderAuthRequiredFragment", { enumerable: true, get: function () { return server_1.renderAuthRequiredFragment; } });
|
|
27
|
+
Object.defineProperty(exports, "renderErrorFragment", { enumerable: true, get: function () { return server_1.renderErrorFragment; } });
|
|
28
|
+
Object.defineProperty(exports, "renderHostedPage", { enumerable: true, get: function () { return server_1.renderHostedPage; } });
|
|
29
|
+
Object.defineProperty(exports, "renderInternalErrorFragment", { enumerable: true, get: function () { return server_1.renderInternalErrorFragment; } });
|
|
30
|
+
Object.defineProperty(exports, "renderMarkdownFragment", { enumerable: true, get: function () { return server_1.renderMarkdownFragment; } });
|
|
31
|
+
Object.defineProperty(exports, "renderMarkdownValue", { enumerable: true, get: function () { return server_1.renderMarkdownValue; } });
|
|
32
|
+
Object.defineProperty(exports, "renderUnsupportedContentTypeFragment", { enumerable: true, get: function () { return server_1.renderUnsupportedContentTypeFragment; } });
|
|
33
|
+
Object.defineProperty(exports, "requireSessionFromCookie", { enumerable: true, get: function () { return server_1.requireSessionFromCookie; } });
|
|
34
|
+
Object.defineProperty(exports, "serializeActionInputsAsMarkdown", { enumerable: true, get: function () { return server_1.serializeActionInputsAsMarkdown; } });
|
|
35
|
+
Object.defineProperty(exports, "serializeBlock", { enumerable: true, get: function () { return server_1.serializeBlock; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type express from "express";
|
|
2
|
+
import type { ActionContext } from "./action";
|
|
3
|
+
export interface CreateActionContextFromRequestOptions {
|
|
4
|
+
inputs?: Record<string, unknown>;
|
|
5
|
+
pathname?: string;
|
|
6
|
+
cookies?: Record<string, string>;
|
|
7
|
+
env?: Record<string, string | undefined>;
|
|
8
|
+
siteTitle?: string;
|
|
9
|
+
siteBaseUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createActionContextFromRequest(req: express.Request, options?: CreateActionContextFromRequestOptions): ActionContext;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createActionContextFromRequest = createActionContextFromRequest;
|
|
4
|
+
function createActionContextFromRequest(req, options = {}) {
|
|
5
|
+
const body = (req.body ?? {});
|
|
6
|
+
return {
|
|
7
|
+
inputs: options.inputs ?? body.inputs ?? {},
|
|
8
|
+
params: Object.fromEntries(Object.entries(req.params).map(([key, value]) => [key, String(value)])),
|
|
9
|
+
query: new URLSearchParams(Object.entries(req.query).flatMap(([key, value]) => {
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return [];
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return value.map((item) => [key, String(item)]);
|
|
14
|
+
}
|
|
15
|
+
return [[key, String(value)]];
|
|
16
|
+
})),
|
|
17
|
+
pathname: options.pathname ?? (typeof body.pathname === "string" ? body.pathname : req.path),
|
|
18
|
+
request: req,
|
|
19
|
+
cookies: options.cookies ?? {},
|
|
20
|
+
env: options.env ?? process.env,
|
|
21
|
+
site: {
|
|
22
|
+
title: options.siteTitle,
|
|
23
|
+
baseUrl: options.siteBaseUrl,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export declare function executeActionHandler<Context>(handler: ActionHandler<Context>, ctx?: Context): Promise<ActionResult>;
|
|
1
|
+
export type ActionHandler<Context = unknown> = (ctx: Context) => Promise<string> | string;
|
|
2
|
+
export declare function executeActionHandler<Context>(handler: ActionHandler<Context>, ctx?: Context): Promise<string>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.executeActionHandler = executeActionHandler;
|
|
4
|
-
const action_1 = require("../core/action");
|
|
5
4
|
async function executeActionHandler(handler, ctx) {
|
|
6
5
|
const result = await handler(ctx);
|
|
7
|
-
|
|
6
|
+
if (typeof result !== "string") {
|
|
7
|
+
throw new Error("Invalid action result: expected markdown string");
|
|
8
|
+
}
|
|
9
|
+
return result;
|
|
8
10
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function serializeActionInputsAsMarkdown(inputs: Record<string, unknown>): string;
|
|
2
|
+
export declare function parseActionInputs(payload: unknown): Record<string, unknown>;
|
|
3
|
+
export declare function normalizeActionInputPayloadToMarkdown(payload: unknown): string;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializeActionInputsAsMarkdown = serializeActionInputsAsMarkdown;
|
|
4
|
+
exports.parseActionInputs = parseActionInputs;
|
|
5
|
+
exports.normalizeActionInputPayloadToMarkdown = normalizeActionInputPayloadToMarkdown;
|
|
6
|
+
const core_1 = require("../core");
|
|
7
|
+
function isPlainObject(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function parseScalarToken(raw) {
|
|
11
|
+
const trimmed = raw.trim();
|
|
12
|
+
if (!trimmed) {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
(0, core_1.validateInputLength)(trimmed, core_1.MAX_INPUT_LENGTH);
|
|
16
|
+
if (trimmed.startsWith("{")
|
|
17
|
+
|| trimmed.startsWith("[")
|
|
18
|
+
|| trimmed.startsWith("\"")
|
|
19
|
+
|| trimmed === "true"
|
|
20
|
+
|| trimmed === "false"
|
|
21
|
+
|| trimmed === "null"
|
|
22
|
+
|| /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/u.test(trimmed)) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(trimmed);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Fall through to plain string.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if ((trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
31
|
+
|| (trimmed.startsWith("\"") && trimmed.endsWith("\""))) {
|
|
32
|
+
return trimmed.slice(1, -1);
|
|
33
|
+
}
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
36
|
+
function parseMarkdownInputs(source) {
|
|
37
|
+
(0, core_1.validateInputLength)(source, core_1.MAX_INPUT_LENGTH * 10);
|
|
38
|
+
const inputs = {};
|
|
39
|
+
const segments = splitInputPairs(source);
|
|
40
|
+
for (const segment of segments) {
|
|
41
|
+
const separator = segment.indexOf(":");
|
|
42
|
+
if (separator <= 0) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const name = segment.slice(0, separator).trim();
|
|
46
|
+
if (!/^[a-zA-Z_][\w-]*$/u.test(name)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (name.length > core_1.MAX_IDENTIFIER_LENGTH) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const value = segment.slice(separator + 1);
|
|
53
|
+
inputs[name] = parseScalarToken(value);
|
|
54
|
+
}
|
|
55
|
+
return inputs;
|
|
56
|
+
}
|
|
57
|
+
function splitInputPairs(source) {
|
|
58
|
+
const pairs = [];
|
|
59
|
+
let current = "";
|
|
60
|
+
let depthObject = 0;
|
|
61
|
+
let depthArray = 0;
|
|
62
|
+
let inSingleQuote = false;
|
|
63
|
+
let inDoubleQuote = false;
|
|
64
|
+
let escaped = false;
|
|
65
|
+
const flushCurrent = () => {
|
|
66
|
+
const trimmed = current.trim();
|
|
67
|
+
if (trimmed) {
|
|
68
|
+
pairs.push(trimmed);
|
|
69
|
+
}
|
|
70
|
+
current = "";
|
|
71
|
+
};
|
|
72
|
+
for (const char of source) {
|
|
73
|
+
if (escaped) {
|
|
74
|
+
current += char;
|
|
75
|
+
escaped = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (inSingleQuote) {
|
|
79
|
+
current += char;
|
|
80
|
+
if (char === "\\") {
|
|
81
|
+
escaped = true;
|
|
82
|
+
}
|
|
83
|
+
else if (char === "'") {
|
|
84
|
+
inSingleQuote = false;
|
|
85
|
+
}
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (inDoubleQuote) {
|
|
89
|
+
current += char;
|
|
90
|
+
if (char === "\\") {
|
|
91
|
+
escaped = true;
|
|
92
|
+
}
|
|
93
|
+
else if (char === "\"") {
|
|
94
|
+
inDoubleQuote = false;
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (char === "'") {
|
|
99
|
+
inSingleQuote = true;
|
|
100
|
+
current += char;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (char === "\"") {
|
|
104
|
+
inDoubleQuote = true;
|
|
105
|
+
current += char;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (char === "{") {
|
|
109
|
+
depthObject += 1;
|
|
110
|
+
current += char;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (char === "}") {
|
|
114
|
+
depthObject = Math.max(0, depthObject - 1);
|
|
115
|
+
current += char;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (char === "[") {
|
|
119
|
+
depthArray += 1;
|
|
120
|
+
current += char;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (char === "]") {
|
|
124
|
+
depthArray = Math.max(0, depthArray - 1);
|
|
125
|
+
current += char;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const isTopLevel = depthObject === 0 && depthArray === 0;
|
|
129
|
+
if (isTopLevel && (char === "," || char === "," || char === "\n" || char === "\r")) {
|
|
130
|
+
flushCurrent();
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
current += char;
|
|
134
|
+
}
|
|
135
|
+
flushCurrent();
|
|
136
|
+
return pairs;
|
|
137
|
+
}
|
|
138
|
+
function normalizeObjectInputs(payload) {
|
|
139
|
+
const inputs = {};
|
|
140
|
+
for (const [name, value] of Object.entries(payload)) {
|
|
141
|
+
if (!/^[a-zA-Z_][\w-]*$/u.test(name)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (Array.isArray(value)) {
|
|
145
|
+
if (value.length > 0) {
|
|
146
|
+
inputs[name] = value[0];
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (typeof value === "string") {
|
|
151
|
+
inputs[name] = parseScalarToken(value);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
155
|
+
inputs[name] = value;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return inputs;
|
|
160
|
+
}
|
|
161
|
+
function serializeActionInputsAsMarkdown(inputs) {
|
|
162
|
+
return Object.entries(inputs)
|
|
163
|
+
.filter(([, value]) => value !== undefined)
|
|
164
|
+
.map(([name, value]) => `${name}: ${JSON.stringify(value)}`)
|
|
165
|
+
.join(", ");
|
|
166
|
+
}
|
|
167
|
+
function parseActionInputs(payload) {
|
|
168
|
+
if (typeof payload === "string") {
|
|
169
|
+
return parseMarkdownInputs(payload);
|
|
170
|
+
}
|
|
171
|
+
if (isPlainObject(payload)) {
|
|
172
|
+
return normalizeObjectInputs(payload);
|
|
173
|
+
}
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
function normalizeActionInputPayloadToMarkdown(payload) {
|
|
177
|
+
return serializeActionInputsAsMarkdown(parseActionInputs(payload));
|
|
178
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { type ActionRegistry } from "./action";
|
|
2
|
+
import { createActionContextFromRequest } from "./action-context";
|
|
3
3
|
export declare function summarizeActionInputs(inputs: Record<string, unknown>): string;
|
|
4
4
|
export declare function createActionFilesSignature(actionFiles: string[], actionsDir: string): string;
|
|
5
5
|
export declare function loadActionRegistry(actionFiles: string[], actionsDir: string, options?: {
|
|
6
6
|
fresh?: boolean;
|
|
7
7
|
}): Promise<ActionRegistry>;
|
|
8
|
-
export declare function createActionContext(req:
|
|
8
|
+
export declare function createActionContext(req: Parameters<typeof createActionContextFromRequest>[0], siteTitle?: string, siteBaseUrl?: string): import("./action").ActionContext;
|
|
@@ -10,6 +10,7 @@ exports.createActionContext = createActionContext;
|
|
|
10
10
|
const node_fs_1 = require("node:fs");
|
|
11
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
12
|
const action_1 = require("./action");
|
|
13
|
+
const action_context_1 = require("./action-context");
|
|
13
14
|
const module_loader_1 = require("./module-loader");
|
|
14
15
|
function toPosixRelativePath(baseDir, filePath) {
|
|
15
16
|
return node_path_1.default.relative(baseDir, filePath).split(node_path_1.default.sep).join("/");
|
|
@@ -57,25 +58,5 @@ async function loadActionRegistry(actionFiles, actionsDir, options = {}) {
|
|
|
57
58
|
return (0, action_1.createActionRegistry)(entries);
|
|
58
59
|
}
|
|
59
60
|
function createActionContext(req, siteTitle, siteBaseUrl) {
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
inputs: body.inputs ?? {},
|
|
63
|
-
params: Object.fromEntries(Object.entries(req.params).map(([key, value]) => [key, String(value)])),
|
|
64
|
-
query: new URLSearchParams(Object.entries(req.query).flatMap(([key, value]) => {
|
|
65
|
-
if (value === undefined)
|
|
66
|
-
return [];
|
|
67
|
-
if (Array.isArray(value)) {
|
|
68
|
-
return value.map((item) => [key, String(item)]);
|
|
69
|
-
}
|
|
70
|
-
return [[key, String(value)]];
|
|
71
|
-
})),
|
|
72
|
-
pathname: typeof body.pathname === "string" ? body.pathname : req.path,
|
|
73
|
-
request: req,
|
|
74
|
-
cookies: {},
|
|
75
|
-
env: process.env,
|
|
76
|
-
site: {
|
|
77
|
-
title: siteTitle,
|
|
78
|
-
baseUrl: siteBaseUrl,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
61
|
+
return (0, action_context_1.createActionContextFromRequest)(req, { siteTitle, siteBaseUrl });
|
|
81
62
|
}
|
package/dist/server/action.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ActionResult as HostedActionResult } from "../core/action";
|
|
2
1
|
export type ActionContext = {
|
|
3
2
|
inputs: Record<string, unknown>;
|
|
4
3
|
params: Record<string, string>;
|
|
@@ -12,14 +11,7 @@ export type ActionContext = {
|
|
|
12
11
|
baseUrl?: string;
|
|
13
12
|
};
|
|
14
13
|
};
|
|
15
|
-
export type
|
|
16
|
-
ok: false;
|
|
17
|
-
errorCode: string;
|
|
18
|
-
message?: string;
|
|
19
|
-
fieldErrors?: Record<string, string>;
|
|
20
|
-
};
|
|
21
|
-
export type ActionResult = HostedActionResult | ActionFailure;
|
|
22
|
-
export type ActionReturnValue = string | ActionResult;
|
|
14
|
+
export type ActionReturnValue = string;
|
|
23
15
|
export type ActionDefinition = {
|
|
24
16
|
name?: string;
|
|
25
17
|
auth?: boolean;
|