@mdsnai/sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.js +63 -0
- package/dist/cli/commands/build.d.ts +5 -0
- package/dist/cli/commands/build.js +19 -0
- package/dist/cli/commands/create.d.ts +2 -0
- package/dist/cli/commands/create.js +39 -0
- package/dist/cli/commands/dev.d.ts +10 -0
- package/dist/cli/commands/dev.js +13 -0
- package/dist/cli/commands/start.d.ts +9 -0
- package/dist/cli/commands/start.js +13 -0
- package/dist/cli/entry.d.ts +2 -0
- package/dist/cli/entry.js +8 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +58 -0
- package/dist/core/action/execution.d.ts +4 -0
- package/dist/core/action/execution.js +57 -0
- package/dist/core/action/index.d.ts +2 -0
- package/dist/core/action/index.js +7 -0
- package/dist/core/action/types.d.ts +19 -0
- package/dist/core/action/types.js +2 -0
- package/dist/core/document/frontmatter.d.ts +5 -0
- package/dist/core/document/frontmatter.js +41 -0
- package/dist/core/document/markdown.d.ts +5 -0
- package/dist/core/document/markdown.js +83 -0
- package/dist/core/document/page-definition.d.ts +2 -0
- package/dist/core/document/page-definition.js +24 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +5 -0
- package/dist/core/model/block.d.ts +30 -0
- package/dist/core/model/block.js +2 -0
- package/dist/core/model/document.d.ts +13 -0
- package/dist/core/model/document.js +2 -0
- package/dist/core/model/fragment.d.ts +4 -0
- package/dist/core/model/fragment.js +2 -0
- package/dist/core/model/index.d.ts +5 -0
- package/dist/core/model/index.js +2 -0
- package/dist/core/model/input.d.ts +11 -0
- package/dist/core/model/input.js +2 -0
- package/dist/core/model/schema.d.ts +4 -0
- package/dist/core/model/schema.js +2 -0
- package/dist/core/protocol/mdsn.d.ts +6 -0
- package/dist/core/protocol/mdsn.js +80 -0
- package/dist/core/protocol/statements.d.ts +12 -0
- package/dist/core/protocol/statements.js +140 -0
- package/dist/core/protocol/validation.d.ts +4 -0
- package/dist/core/protocol/validation.js +60 -0
- package/dist/framework/create-framework-app.d.ts +12 -0
- package/dist/framework/create-framework-app.js +11 -0
- package/dist/framework/hosted-app.d.ts +13 -0
- package/dist/framework/hosted-app.js +133 -0
- package/dist/framework/index.d.ts +4 -0
- package/dist/framework/index.js +7 -0
- package/dist/framework/site-app.d.ts +12 -0
- package/dist/framework/site-app.js +146 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +18 -0
- package/dist/server/action-host.d.ts +3 -0
- package/dist/server/action-host.js +8 -0
- package/dist/server/action-runtime.d.ts +8 -0
- package/dist/server/action-runtime.js +81 -0
- package/dist/server/action.d.ts +41 -0
- package/dist/server/action.js +97 -0
- package/dist/server/build.d.ts +10 -0
- package/dist/server/build.js +166 -0
- package/dist/server/config.d.ts +56 -0
- package/dist/server/config.js +42 -0
- package/dist/server/dev.d.ts +48 -0
- package/dist/server/dev.js +90 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.js +16 -0
- package/dist/server/init.d.ts +1 -0
- package/dist/server/init.js +176 -0
- package/dist/server/layout.d.ts +17 -0
- package/dist/server/layout.js +40 -0
- package/dist/server/markdown.d.ts +53 -0
- package/dist/server/markdown.js +76 -0
- package/dist/server/module-loader.d.ts +4 -0
- package/dist/server/module-loader.js +71 -0
- package/dist/server/negotiate.d.ts +3 -0
- package/dist/server/negotiate.js +55 -0
- package/dist/server/page-host.d.ts +21 -0
- package/dist/server/page-host.js +66 -0
- package/dist/server/page-links.d.ts +10 -0
- package/dist/server/page-links.js +80 -0
- package/dist/server/route-matcher.d.ts +6 -0
- package/dist/server/route-matcher.js +73 -0
- package/dist/server/routes.d.ts +6 -0
- package/dist/server/routes.js +73 -0
- package/dist/server/server.d.ts +27 -0
- package/dist/server/server.js +152 -0
- package/dist/server/site.d.ts +11 -0
- package/dist/server/site.js +59 -0
- package/dist/server/targets.d.ts +7 -0
- package/dist/server/targets.js +21 -0
- package/dist/web/block-runtime.d.ts +2 -0
- package/dist/web/block-runtime.js +27 -0
- package/dist/web/fragment-render.d.ts +10 -0
- package/dist/web/fragment-render.js +59 -0
- package/dist/web/headless.d.ts +95 -0
- package/dist/web/headless.js +370 -0
- package/dist/web/i18n.d.ts +31 -0
- package/dist/web/i18n.js +69 -0
- package/dist/web/index.d.ts +11 -0
- package/dist/web/index.js +22 -0
- package/dist/web/navigation.d.ts +3 -0
- package/dist/web/navigation.js +32 -0
- package/dist/web/page-bootstrap.d.ts +6 -0
- package/dist/web/page-bootstrap.js +29 -0
- package/dist/web/page-client-runtime.d.ts +15 -0
- package/dist/web/page-client-runtime.js +22 -0
- package/dist/web/page-client-script.d.ts +2 -0
- package/dist/web/page-client-script.js +567 -0
- package/dist/web/page-html.d.ts +8 -0
- package/dist/web/page-html.js +49 -0
- package/dist/web/page-render.d.ts +20 -0
- package/dist/web/page-render.js +92 -0
- package/dist/web/public-client-runtime.d.ts +1 -0
- package/dist/web/public-client-runtime.js +5 -0
- package/dist/web/public-render.d.ts +12 -0
- package/dist/web/public-render.js +18 -0
- package/dist/web/target-path.d.ts +1 -0
- package/dist/web/target-path.js +35 -0
- package/package.json +91 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadActionHandlers = loadActionHandlers;
|
|
7
|
+
exports.createSiteApp = createSiteApp;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const hosted_app_1 = require("./hosted-app");
|
|
11
|
+
const frontmatter_1 = require("../core/document/frontmatter");
|
|
12
|
+
const config_1 = require("../server/config");
|
|
13
|
+
const module_loader_1 = require("../server/module-loader");
|
|
14
|
+
const site_1 = require("../server/site");
|
|
15
|
+
const routes_1 = require("../server/routes");
|
|
16
|
+
function walkMarkdownFiles(directory) {
|
|
17
|
+
const files = [];
|
|
18
|
+
for (const entry of (0, node_fs_1.readdirSync)(directory, { withFileTypes: true })) {
|
|
19
|
+
const absolutePath = node_path_1.default.join(directory, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
files.push(...walkMarkdownFiles(absolutePath));
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (entry.isFile() && absolutePath.endsWith(".md")) {
|
|
25
|
+
files.push(absolutePath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return files;
|
|
29
|
+
}
|
|
30
|
+
function actionFileToId(filePath, serverDir) {
|
|
31
|
+
return node_path_1.default.relative(serverDir, filePath).split(node_path_1.default.sep).join("/").replace(/\.[^.]+$/u, "");
|
|
32
|
+
}
|
|
33
|
+
function resolveActionLocation(actionId, actionFilesById) {
|
|
34
|
+
const directFile = actionFilesById.get(actionId);
|
|
35
|
+
if (directFile) {
|
|
36
|
+
return { filePath: directFile };
|
|
37
|
+
}
|
|
38
|
+
const segments = actionId.split("/").filter(Boolean);
|
|
39
|
+
const exportName = segments.pop();
|
|
40
|
+
if (!exportName) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const actionsFileId = segments.length > 0 ? `${segments.join("/")}/actions` : "actions";
|
|
44
|
+
const actionsFile = actionFilesById.get(actionsFileId);
|
|
45
|
+
if (!actionsFile) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
filePath: actionsFile,
|
|
50
|
+
exportName,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function resolveLayoutTemplate(layoutsDir, rawPage) {
|
|
54
|
+
const { frontmatter } = (0, frontmatter_1.parseFrontmatter)(rawPage);
|
|
55
|
+
const value = frontmatter.layout;
|
|
56
|
+
if (typeof value !== "string") {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
const normalized = value.trim();
|
|
60
|
+
if (!normalized || normalized.toLowerCase() === "none") {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
if (!/^[a-zA-Z0-9/_-]+$/u.test(normalized)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const root = node_path_1.default.resolve(layoutsDir);
|
|
67
|
+
const candidate = node_path_1.default.resolve(root, `${normalized}.html`);
|
|
68
|
+
const relative = node_path_1.default.relative(root, candidate);
|
|
69
|
+
if (relative.startsWith("..") || node_path_1.default.isAbsolute(relative) || !(0, node_fs_1.existsSync)(candidate)) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return (0, node_fs_1.readFileSync)(candidate, "utf8");
|
|
73
|
+
}
|
|
74
|
+
function loadActionHandlers(serverDir) {
|
|
75
|
+
const actionFiles = (0, site_1.findActionFiles)(serverDir);
|
|
76
|
+
const actionFilesById = new Map(actionFiles.map((filePath) => [actionFileToId(filePath, serverDir), filePath]));
|
|
77
|
+
const handlers = new Map();
|
|
78
|
+
return new Proxy({}, {
|
|
79
|
+
get(_target, property) {
|
|
80
|
+
if (typeof property !== "string") {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const cached = handlers.get(property);
|
|
84
|
+
if (cached) {
|
|
85
|
+
return cached;
|
|
86
|
+
}
|
|
87
|
+
const resolved = resolveActionLocation(property, actionFilesById);
|
|
88
|
+
if (!resolved) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
const handler = async (ctx) => {
|
|
92
|
+
const mod = await (0, module_loader_1.importModuleFromFile)(resolved.filePath);
|
|
93
|
+
const actionModule = (resolved.exportName
|
|
94
|
+
? mod.default?.[resolved.exportName]
|
|
95
|
+
?? mod.actions?.[resolved.exportName]
|
|
96
|
+
?? mod[resolved.exportName]
|
|
97
|
+
: mod.default?.action
|
|
98
|
+
?? mod.default
|
|
99
|
+
?? mod.action
|
|
100
|
+
?? mod);
|
|
101
|
+
if (typeof actionModule?.run !== "function") {
|
|
102
|
+
throw new Error(resolved.exportName
|
|
103
|
+
? `Action module must export ${resolved.exportName} from ${resolved.filePath}`
|
|
104
|
+
: `Action module must export a run() function: ${resolved.filePath}`);
|
|
105
|
+
}
|
|
106
|
+
return actionModule.run(ctx);
|
|
107
|
+
};
|
|
108
|
+
handlers.set(property, handler);
|
|
109
|
+
return handler;
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function createSiteApp(options) {
|
|
114
|
+
const resolvedConfig = (0, config_1.resolveConfig)(options.config ?? {});
|
|
115
|
+
const pagesDir = node_path_1.default.join(options.rootDir, resolvedConfig.dirs.pages);
|
|
116
|
+
const serverDir = node_path_1.default.join(options.rootDir, resolvedConfig.dirs.server);
|
|
117
|
+
const publicDir = node_path_1.default.join(options.rootDir, resolvedConfig.dirs.public);
|
|
118
|
+
const layoutsDir = node_path_1.default.join(options.rootDir, resolvedConfig.dirs.layouts);
|
|
119
|
+
const pageFiles = walkMarkdownFiles(pagesDir);
|
|
120
|
+
const pages = {};
|
|
121
|
+
const actions = options.actions ?? loadActionHandlers(serverDir);
|
|
122
|
+
for (const filePath of pageFiles) {
|
|
123
|
+
const routePath = (0, routes_1.pagePathToRoutePath)(filePath, pagesDir);
|
|
124
|
+
const pageSource = (0, node_fs_1.readFileSync)(filePath, "utf8");
|
|
125
|
+
pages[routePath] = pageSource;
|
|
126
|
+
const fallbackRoutePath = (0, routes_1.defaultLocaleRouteToFallbackPath)(routePath, resolvedConfig.i18n.defaultLocale);
|
|
127
|
+
if (fallbackRoutePath && !pages[fallbackRoutePath]) {
|
|
128
|
+
pages[fallbackRoutePath] = pageSource;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const app = (0, hosted_app_1.createHostedApp)({
|
|
132
|
+
pages,
|
|
133
|
+
actions,
|
|
134
|
+
publicDir,
|
|
135
|
+
render: {
|
|
136
|
+
siteTitle: resolvedConfig.site.title,
|
|
137
|
+
siteDescription: resolvedConfig.site.description,
|
|
138
|
+
siteBaseUrl: resolvedConfig.site.baseUrl,
|
|
139
|
+
locales: resolvedConfig.i18n.locales,
|
|
140
|
+
defaultLocale: resolvedConfig.i18n.defaultLocale,
|
|
141
|
+
markdown: resolvedConfig.markdown,
|
|
142
|
+
resolveLayoutTemplate: (_routePath, rawPage) => resolveLayoutTemplate(layoutsDir, rawPage),
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
return app;
|
|
146
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { parsePageDefinition } from "./core";
|
|
2
|
+
export type { BlockAnchorDefinition, BlockDefinition, DocumentDefinition, FrontmatterData, InputDefinition, InputType, ReadDefinition, RedirectDefinition, SchemaDefinition, WriteDefinition, } from "./core";
|
|
3
|
+
export { createRenderModel, getClientRuntimeScript, parseFragment, parseMarkdown, parsePage, renderDefaultHtmlDocument, renderPageHtml, } from "./web";
|
|
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, ActionResult, } from "./server";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
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;
|
|
4
|
+
var core_1 = require("./core");
|
|
5
|
+
Object.defineProperty(exports, "parsePageDefinition", { enumerable: true, get: function () { return core_1.parsePageDefinition; } });
|
|
6
|
+
var web_1 = require("./web");
|
|
7
|
+
Object.defineProperty(exports, "createRenderModel", { enumerable: true, get: function () { return web_1.createRenderModel; } });
|
|
8
|
+
Object.defineProperty(exports, "getClientRuntimeScript", { enumerable: true, get: function () { return web_1.getClientRuntimeScript; } });
|
|
9
|
+
Object.defineProperty(exports, "parseFragment", { enumerable: true, get: function () { return web_1.parseFragment; } });
|
|
10
|
+
Object.defineProperty(exports, "parseMarkdown", { enumerable: true, get: function () { return web_1.parseMarkdown; } });
|
|
11
|
+
Object.defineProperty(exports, "parsePage", { enumerable: true, get: function () { return web_1.parsePage; } });
|
|
12
|
+
Object.defineProperty(exports, "renderDefaultHtmlDocument", { enumerable: true, get: function () { return web_1.renderDefaultHtmlDocument; } });
|
|
13
|
+
Object.defineProperty(exports, "renderPageHtml", { enumerable: true, get: function () { return web_1.renderPageHtml; } });
|
|
14
|
+
var framework_1 = require("./framework");
|
|
15
|
+
Object.defineProperty(exports, "createFrameworkApp", { enumerable: true, get: function () { return framework_1.createFrameworkApp; } });
|
|
16
|
+
Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return framework_1.defineConfig; } });
|
|
17
|
+
var server_1 = require("./server");
|
|
18
|
+
Object.defineProperty(exports, "defineAction", { enumerable: true, get: function () { return server_1.defineAction; } });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeActionHandler = executeActionHandler;
|
|
4
|
+
const action_1 = require("../core/action");
|
|
5
|
+
async function executeActionHandler(handler, ctx) {
|
|
6
|
+
const result = await handler(ctx);
|
|
7
|
+
return (0, action_1.normalizeActionResult)(result);
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type express from "express";
|
|
2
|
+
import { type ActionContext, type ActionRegistry } from "./action";
|
|
3
|
+
export declare function summarizeActionInputs(inputs: Record<string, unknown>): string;
|
|
4
|
+
export declare function createActionFilesSignature(actionFiles: string[], actionsDir: string): string;
|
|
5
|
+
export declare function loadActionRegistry(actionFiles: string[], actionsDir: string, options?: {
|
|
6
|
+
fresh?: boolean;
|
|
7
|
+
}): Promise<ActionRegistry>;
|
|
8
|
+
export declare function createActionContext(req: express.Request, siteTitle?: string, siteBaseUrl?: string): ActionContext;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.summarizeActionInputs = summarizeActionInputs;
|
|
7
|
+
exports.createActionFilesSignature = createActionFilesSignature;
|
|
8
|
+
exports.loadActionRegistry = loadActionRegistry;
|
|
9
|
+
exports.createActionContext = createActionContext;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const action_1 = require("./action");
|
|
13
|
+
const module_loader_1 = require("./module-loader");
|
|
14
|
+
function toPosixRelativePath(baseDir, filePath) {
|
|
15
|
+
return node_path_1.default.relative(baseDir, filePath).split(node_path_1.default.sep).join("/");
|
|
16
|
+
}
|
|
17
|
+
function summarizeActionInputs(inputs) {
|
|
18
|
+
try {
|
|
19
|
+
const serialized = JSON.stringify(inputs);
|
|
20
|
+
if (!serialized) {
|
|
21
|
+
return "{}";
|
|
22
|
+
}
|
|
23
|
+
return serialized.length > 240 ? `${serialized.slice(0, 237)}...` : serialized;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return "[unserializable inputs]";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function createActionFilesSignature(actionFiles, actionsDir) {
|
|
30
|
+
return actionFiles.map((filePath) => {
|
|
31
|
+
const relativePath = toPosixRelativePath(actionsDir, filePath);
|
|
32
|
+
try {
|
|
33
|
+
const stats = (0, node_fs_1.statSync)(filePath);
|
|
34
|
+
return `${relativePath}:${stats.mtimeMs}`;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code === "ENOENT") {
|
|
38
|
+
return `${relativePath}:missing`;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}).join("|");
|
|
43
|
+
}
|
|
44
|
+
async function loadActionRegistry(actionFiles, actionsDir, options = {}) {
|
|
45
|
+
const entries = [];
|
|
46
|
+
for (const filePath of actionFiles) {
|
|
47
|
+
const actionModule = await (0, module_loader_1.importModuleFromFile)(filePath, { fresh: options.fresh });
|
|
48
|
+
const actionEntries = (0, action_1.resolveActionModuleEntries)(actionModule, filePath);
|
|
49
|
+
const relativePath = toPosixRelativePath(actionsDir, filePath);
|
|
50
|
+
for (const entry of actionEntries) {
|
|
51
|
+
entries.push({
|
|
52
|
+
id: (0, action_1.actionExportNameToActionId)(relativePath, "", entry.exportName),
|
|
53
|
+
action: entry.action,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return (0, action_1.createActionRegistry)(entries);
|
|
58
|
+
}
|
|
59
|
+
function createActionContext(req, siteTitle, siteBaseUrl) {
|
|
60
|
+
const body = (req.body ?? {});
|
|
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
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ActionResult as HostedActionResult } from "../core/action";
|
|
2
|
+
export type ActionContext = {
|
|
3
|
+
inputs: Record<string, unknown>;
|
|
4
|
+
params: Record<string, string>;
|
|
5
|
+
query: URLSearchParams;
|
|
6
|
+
pathname: string;
|
|
7
|
+
request: unknown;
|
|
8
|
+
cookies: unknown;
|
|
9
|
+
env: Record<string, string | undefined>;
|
|
10
|
+
site: {
|
|
11
|
+
title?: string;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export type ActionFailure = {
|
|
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;
|
|
23
|
+
export type ActionDefinition = {
|
|
24
|
+
name?: string;
|
|
25
|
+
auth?: boolean;
|
|
26
|
+
run: (ctx: ActionContext) => Promise<ActionReturnValue> | ActionReturnValue;
|
|
27
|
+
};
|
|
28
|
+
export type ActionDefinitionMap = Record<string, ActionDefinition>;
|
|
29
|
+
export type RegisteredAction = {
|
|
30
|
+
id: string;
|
|
31
|
+
action: ActionDefinition;
|
|
32
|
+
exportName?: string;
|
|
33
|
+
};
|
|
34
|
+
export type ActionRegistry = Map<string, ActionDefinition>;
|
|
35
|
+
export declare function defineAction(action: ActionDefinition): ActionDefinition;
|
|
36
|
+
export declare function defineActions(actions: ActionDefinitionMap): ActionDefinitionMap;
|
|
37
|
+
export declare function actionFilePathToActionId(filePath: string, actionsDir: string): string;
|
|
38
|
+
export declare function actionExportNameToActionId(filePath: string, actionsDir: string, exportName?: string): string;
|
|
39
|
+
export declare function resolveActionModuleEntries(actionModule: Record<string, unknown>, filePath: string): RegisteredAction[];
|
|
40
|
+
export declare function createActionRegistry(entries: RegisteredAction[]): ActionRegistry;
|
|
41
|
+
export declare function resolveActionById(registry: ActionRegistry, id: string): ActionDefinition | undefined;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.defineAction = defineAction;
|
|
7
|
+
exports.defineActions = defineActions;
|
|
8
|
+
exports.actionFilePathToActionId = actionFilePathToActionId;
|
|
9
|
+
exports.actionExportNameToActionId = actionExportNameToActionId;
|
|
10
|
+
exports.resolveActionModuleEntries = resolveActionModuleEntries;
|
|
11
|
+
exports.createActionRegistry = createActionRegistry;
|
|
12
|
+
exports.resolveActionById = resolveActionById;
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
function defineAction(action) {
|
|
15
|
+
return action;
|
|
16
|
+
}
|
|
17
|
+
function defineActions(actions) {
|
|
18
|
+
return actions;
|
|
19
|
+
}
|
|
20
|
+
function actionFilePathToActionId(filePath, actionsDir) {
|
|
21
|
+
const normalizedPath = filePath.split(node_path_1.default.sep).join("/");
|
|
22
|
+
const normalizedActionsDir = actionsDir.split(node_path_1.default.sep).join("/");
|
|
23
|
+
const prefix = normalizedActionsDir.endsWith("/") ? normalizedActionsDir : `${normalizedActionsDir}/`;
|
|
24
|
+
const withoutPrefix = normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : normalizedPath;
|
|
25
|
+
return withoutPrefix.replace(/\.[^.]+$/, "");
|
|
26
|
+
}
|
|
27
|
+
function normalizeMultiActionBaseId(actionId) {
|
|
28
|
+
if (actionId === "actions") {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
if (actionId.endsWith("/actions")) {
|
|
32
|
+
return actionId.slice(0, -"/actions".length);
|
|
33
|
+
}
|
|
34
|
+
return actionId;
|
|
35
|
+
}
|
|
36
|
+
function actionExportNameToActionId(filePath, actionsDir, exportName) {
|
|
37
|
+
const fileActionId = actionFilePathToActionId(filePath, actionsDir);
|
|
38
|
+
if (!exportName) {
|
|
39
|
+
return fileActionId;
|
|
40
|
+
}
|
|
41
|
+
const baseId = normalizeMultiActionBaseId(fileActionId);
|
|
42
|
+
return baseId ? `${baseId}/${exportName}` : exportName;
|
|
43
|
+
}
|
|
44
|
+
function isActionDefinition(value) {
|
|
45
|
+
return !!value && typeof value === "object" && typeof value.run === "function";
|
|
46
|
+
}
|
|
47
|
+
function actionDefinitionEntriesFromMap(candidate) {
|
|
48
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const entries = Object.entries(candidate)
|
|
52
|
+
.filter(([key]) => !["default", "action", "actions", "__esModule"].includes(key));
|
|
53
|
+
if (entries.length === 0 || !entries.every(([, value]) => isActionDefinition(value))) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return entries.map(([exportName, action]) => ({
|
|
57
|
+
id: exportName,
|
|
58
|
+
exportName,
|
|
59
|
+
action: action,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
function resolveActionModuleEntries(actionModule, filePath) {
|
|
63
|
+
const singleCandidates = [
|
|
64
|
+
actionModule.default,
|
|
65
|
+
actionModule.action,
|
|
66
|
+
actionModule,
|
|
67
|
+
];
|
|
68
|
+
for (const candidate of singleCandidates) {
|
|
69
|
+
if (isActionDefinition(candidate)) {
|
|
70
|
+
return [{ id: "", action: candidate }];
|
|
71
|
+
}
|
|
72
|
+
if (candidate
|
|
73
|
+
&& typeof candidate === "object"
|
|
74
|
+
&& isActionDefinition(candidate.action)) {
|
|
75
|
+
return [{ id: "", action: candidate.action }];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const mapCandidates = [
|
|
79
|
+
actionModule.default?.actions,
|
|
80
|
+
actionModule.actions,
|
|
81
|
+
actionModule.default,
|
|
82
|
+
actionModule,
|
|
83
|
+
];
|
|
84
|
+
for (const candidate of mapCandidates) {
|
|
85
|
+
const entries = actionDefinitionEntriesFromMap(candidate);
|
|
86
|
+
if (entries) {
|
|
87
|
+
return entries;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`Expected action module at ${filePath} to export defineAction() or defineActions() results`);
|
|
91
|
+
}
|
|
92
|
+
function createActionRegistry(entries) {
|
|
93
|
+
return new Map(entries.map((entry) => [entry.id, entry.action]));
|
|
94
|
+
}
|
|
95
|
+
function resolveActionById(registry, id) {
|
|
96
|
+
return registry.get(id);
|
|
97
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type MdsnConfig } from "./config";
|
|
2
|
+
export type BuildOutput = {
|
|
3
|
+
outDir: string;
|
|
4
|
+
pagesManifestPath: string;
|
|
5
|
+
actionsManifestPath: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function buildFrameworkSite(options: {
|
|
8
|
+
rootDir: string;
|
|
9
|
+
config?: MdsnConfig;
|
|
10
|
+
}): Promise<BuildOutput>;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildFrameworkSite = buildFrameworkSite;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const action_1 = require("./action");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
const page_definition_1 = require("../core/document/page-definition");
|
|
12
|
+
const routes_1 = require("./routes");
|
|
13
|
+
const site_1 = require("./site");
|
|
14
|
+
function copyDirectoryContents(sourceDir, destinationDir) {
|
|
15
|
+
const entries = (0, node_fs_1.readdirSync)(sourceDir, { withFileTypes: true });
|
|
16
|
+
(0, node_fs_1.mkdirSync)(destinationDir, { recursive: true });
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const sourcePath = node_path_1.default.join(sourceDir, entry.name);
|
|
19
|
+
const destinationPath = node_path_1.default.join(destinationDir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
copyDirectoryContents(sourcePath, destinationPath);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (entry.isFile()) {
|
|
25
|
+
(0, node_fs_1.mkdirSync)(node_path_1.default.dirname(destinationPath), { recursive: true });
|
|
26
|
+
(0, node_fs_1.copyFileSync)(sourcePath, destinationPath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function buildFrameworkSite(options) {
|
|
31
|
+
const resolvedConfig = (0, config_1.resolveConfig)(options.config ?? {});
|
|
32
|
+
const site = (0, site_1.resolveSitePaths)(options.rootDir, options.config ?? {});
|
|
33
|
+
const outDir = node_path_1.default.join(options.rootDir, "dist");
|
|
34
|
+
const manifestDir = node_path_1.default.join(outDir, "manifest");
|
|
35
|
+
const pagesOutDir = node_path_1.default.join(outDir, resolvedConfig.dirs.pages);
|
|
36
|
+
const serverOutDir = node_path_1.default.join(outDir, resolvedConfig.dirs.server);
|
|
37
|
+
const publicOutDir = node_path_1.default.join(outDir, resolvedConfig.dirs.public);
|
|
38
|
+
const layoutsOutDir = node_path_1.default.join(outDir, resolvedConfig.dirs.layouts);
|
|
39
|
+
const pageFiles = (0, site_1.findPageFiles)(site.pagesDir);
|
|
40
|
+
const actionFiles = (0, site_1.findActionFiles)(site.serverDir);
|
|
41
|
+
(0, node_fs_1.rmSync)(outDir, { recursive: true, force: true });
|
|
42
|
+
(0, node_fs_1.mkdirSync)(manifestDir, { recursive: true });
|
|
43
|
+
const actionFilesById = new Map(actionFiles.map((filePath) => {
|
|
44
|
+
const relativeToServerDir = node_path_1.default.relative(site.serverDir, filePath).split(node_path_1.default.sep).join("/");
|
|
45
|
+
return [(0, action_1.actionFilePathToActionId)(relativeToServerDir, ""), relativeToServerDir];
|
|
46
|
+
}));
|
|
47
|
+
const pagesManifest = pageFiles.map((filePath) => {
|
|
48
|
+
const relativeToPagesDir = node_path_1.default.relative(site.pagesDir, filePath).split(node_path_1.default.sep).join("/");
|
|
49
|
+
return {
|
|
50
|
+
file: relativeToPagesDir,
|
|
51
|
+
routePath: (0, routes_1.pagePathToRoutePath)(`${resolvedConfig.dirs.pages}/${relativeToPagesDir}`, resolvedConfig.dirs.pages),
|
|
52
|
+
};
|
|
53
|
+
}).sort((left, right) => left.routePath.localeCompare(right.routePath));
|
|
54
|
+
const actionsManifest = new Map();
|
|
55
|
+
function normalizeDeclaredActionId(target) {
|
|
56
|
+
const trimmed = String(target).trim();
|
|
57
|
+
if (!trimmed || /^https?:\/\//i.test(trimmed) || trimmed.toLowerCase().endsWith(".md")) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return trimmed.replace(/^\/+/u, "");
|
|
61
|
+
}
|
|
62
|
+
function addManifestEntry(entry) {
|
|
63
|
+
actionsManifest.set(entry.id, entry);
|
|
64
|
+
}
|
|
65
|
+
function resolveActionsFileEntry(actionId) {
|
|
66
|
+
const directFile = actionFilesById.get(actionId);
|
|
67
|
+
if (directFile) {
|
|
68
|
+
return { id: actionId, file: directFile };
|
|
69
|
+
}
|
|
70
|
+
const segments = actionId.split("/").filter(Boolean);
|
|
71
|
+
const exportName = segments.pop();
|
|
72
|
+
if (!exportName) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const actionsFileId = segments.length > 0 ? `${segments.join("/")}/actions` : "actions";
|
|
76
|
+
const actionsFile = actionFilesById.get(actionsFileId);
|
|
77
|
+
if (!actionsFile) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
id: actionId,
|
|
82
|
+
file: actionsFile,
|
|
83
|
+
exportName,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
for (const filePath of pageFiles) {
|
|
87
|
+
const pageSource = (0, node_fs_1.readFileSync)(filePath, "utf8");
|
|
88
|
+
let page;
|
|
89
|
+
try {
|
|
90
|
+
page = (0, page_definition_1.parsePageDefinition)(pageSource);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const block of page.blocks) {
|
|
96
|
+
for (const operation of [...block.reads, ...block.writes]) {
|
|
97
|
+
const actionId = normalizeDeclaredActionId(operation.target);
|
|
98
|
+
if (!actionId) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const entry = resolveActionsFileEntry(actionId);
|
|
102
|
+
if (entry) {
|
|
103
|
+
addManifestEntry(entry);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const [actionId, relativeToServerDir] of actionFilesById.entries()) {
|
|
109
|
+
if (actionId === "actions" || actionId.endsWith("/actions")) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!actionsManifest.has(actionId)) {
|
|
113
|
+
addManifestEntry({ id: actionId, file: relativeToServerDir });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const pagesManifestPath = node_path_1.default.join(manifestDir, "pages.json");
|
|
117
|
+
const actionsManifestPath = node_path_1.default.join(manifestDir, "actions.json");
|
|
118
|
+
(0, node_fs_1.writeFileSync)(pagesManifestPath, JSON.stringify(pagesManifest, null, 2) + "\n", "utf8");
|
|
119
|
+
(0, node_fs_1.writeFileSync)(actionsManifestPath, JSON.stringify(Array.from(actionsManifest.values()).sort((left, right) => left.id.localeCompare(right.id)), null, 2) + "\n", "utf8");
|
|
120
|
+
(0, node_fs_1.writeFileSync)(node_path_1.default.join(outDir, "mdsn.config.json"), JSON.stringify(resolvedConfig, null, 2) + "\n", "utf8");
|
|
121
|
+
try {
|
|
122
|
+
if ((0, node_fs_1.statSync)(site.pagesDir).isDirectory()) {
|
|
123
|
+
copyDirectoryContents(site.pagesDir, pagesOutDir);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (error.code !== "ENOENT") {
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
if ((0, node_fs_1.statSync)(site.serverDir).isDirectory()) {
|
|
133
|
+
copyDirectoryContents(site.serverDir, serverOutDir);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (error.code !== "ENOENT") {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
if ((0, node_fs_1.statSync)(site.publicDir).isDirectory()) {
|
|
143
|
+
copyDirectoryContents(site.publicDir, publicOutDir);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error.code !== "ENOENT") {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
if ((0, node_fs_1.statSync)(site.layoutsDir).isDirectory()) {
|
|
153
|
+
copyDirectoryContents(site.layoutsDir, layoutsOutDir);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (error.code !== "ENOENT") {
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
outDir,
|
|
163
|
+
pagesManifestPath,
|
|
164
|
+
actionsManifestPath,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type MdsnConfig = {
|
|
2
|
+
site?: {
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
};
|
|
7
|
+
server?: {
|
|
8
|
+
port?: number;
|
|
9
|
+
};
|
|
10
|
+
dirs?: {
|
|
11
|
+
pages?: string;
|
|
12
|
+
server?: string;
|
|
13
|
+
public?: string;
|
|
14
|
+
layouts?: string;
|
|
15
|
+
};
|
|
16
|
+
markdown?: {
|
|
17
|
+
linkify?: boolean;
|
|
18
|
+
typographer?: boolean;
|
|
19
|
+
};
|
|
20
|
+
dev?: {
|
|
21
|
+
openBrowser?: boolean;
|
|
22
|
+
};
|
|
23
|
+
i18n?: {
|
|
24
|
+
defaultLocale?: string;
|
|
25
|
+
locales?: string[];
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type ResolvedMdsnConfig = {
|
|
29
|
+
site: {
|
|
30
|
+
title?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
baseUrl?: string;
|
|
33
|
+
};
|
|
34
|
+
server: {
|
|
35
|
+
port: number;
|
|
36
|
+
};
|
|
37
|
+
dirs: {
|
|
38
|
+
pages: string;
|
|
39
|
+
server: string;
|
|
40
|
+
public: string;
|
|
41
|
+
layouts: string;
|
|
42
|
+
};
|
|
43
|
+
markdown: {
|
|
44
|
+
linkify: boolean;
|
|
45
|
+
typographer: boolean;
|
|
46
|
+
};
|
|
47
|
+
dev: {
|
|
48
|
+
openBrowser: boolean;
|
|
49
|
+
};
|
|
50
|
+
i18n: {
|
|
51
|
+
defaultLocale: string;
|
|
52
|
+
locales: string[];
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export declare function defineConfig(config: MdsnConfig): MdsnConfig;
|
|
56
|
+
export declare function resolveConfig(config: MdsnConfig): ResolvedMdsnConfig;
|