@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,30 @@
|
|
|
1
|
+
import type { InputDefinition } from "./input";
|
|
2
|
+
export interface ReadDefinition {
|
|
3
|
+
id: string;
|
|
4
|
+
block: string;
|
|
5
|
+
name: string;
|
|
6
|
+
target: string;
|
|
7
|
+
inputs: string[];
|
|
8
|
+
order: number;
|
|
9
|
+
}
|
|
10
|
+
export interface WriteDefinition {
|
|
11
|
+
id: string;
|
|
12
|
+
block: string;
|
|
13
|
+
name: string;
|
|
14
|
+
target: string;
|
|
15
|
+
inputs: string[];
|
|
16
|
+
order: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RedirectDefinition {
|
|
19
|
+
id: string;
|
|
20
|
+
block: string;
|
|
21
|
+
target: string;
|
|
22
|
+
order: number;
|
|
23
|
+
}
|
|
24
|
+
export interface BlockDefinition {
|
|
25
|
+
name: string;
|
|
26
|
+
inputs: InputDefinition[];
|
|
27
|
+
reads: ReadDefinition[];
|
|
28
|
+
writes: WriteDefinition[];
|
|
29
|
+
redirects: RedirectDefinition[];
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BlockDefinition } from "./block";
|
|
2
|
+
import type { SchemaDefinition } from "./schema";
|
|
3
|
+
export type FrontmatterData = Record<string, unknown>;
|
|
4
|
+
export interface BlockAnchorDefinition {
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DocumentDefinition {
|
|
8
|
+
frontmatter: FrontmatterData;
|
|
9
|
+
markdown: string;
|
|
10
|
+
schemas: SchemaDefinition[];
|
|
11
|
+
blocks: BlockDefinition[];
|
|
12
|
+
blockAnchors: BlockAnchorDefinition[];
|
|
13
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { BlockAnchorDefinition, DocumentDefinition, FrontmatterData, } from "./document";
|
|
2
|
+
export type { BlockDefinition, ReadDefinition, RedirectDefinition, WriteDefinition, } from "./block";
|
|
3
|
+
export type { InputDefinition, InputType, } from "./input";
|
|
4
|
+
export type { SchemaDefinition } from "./schema";
|
|
5
|
+
export type { MarkdownFragmentDefinition } from "./fragment";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type InputType = "text" | "number" | "boolean" | "choice" | "file" | "json";
|
|
2
|
+
export interface InputDefinition {
|
|
3
|
+
id: string;
|
|
4
|
+
block: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type: InputType;
|
|
7
|
+
required: boolean;
|
|
8
|
+
secret: boolean;
|
|
9
|
+
options?: string[];
|
|
10
|
+
schema?: string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMdsnBlocks = parseMdsnBlocks;
|
|
4
|
+
const statements_1 = require("./statements");
|
|
5
|
+
function createBlock(name) {
|
|
6
|
+
return {
|
|
7
|
+
name,
|
|
8
|
+
inputs: [],
|
|
9
|
+
reads: [],
|
|
10
|
+
writes: [],
|
|
11
|
+
redirects: [],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function parseMdsnBlocks(blocks) {
|
|
15
|
+
const schemas = [];
|
|
16
|
+
const documentBlocks = [];
|
|
17
|
+
let currentBlock = null;
|
|
18
|
+
for (const blockText of blocks) {
|
|
19
|
+
const lines = blockText.split(/\r?\n/);
|
|
20
|
+
let index = 0;
|
|
21
|
+
while (index < lines.length) {
|
|
22
|
+
const line = lines[index].trim();
|
|
23
|
+
if (!line) {
|
|
24
|
+
index += 1;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!currentBlock) {
|
|
28
|
+
if (line.startsWith("schema ")) {
|
|
29
|
+
const parsed = (0, statements_1.parseSchemaBlock)(lines, index);
|
|
30
|
+
schemas.push(parsed.schema);
|
|
31
|
+
index = parsed.nextIndex;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (line.startsWith("block ")) {
|
|
35
|
+
currentBlock = createBlock((0, statements_1.parseBlockHeaderLine)(line));
|
|
36
|
+
documentBlocks.push(currentBlock);
|
|
37
|
+
index += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (line === "}") {
|
|
41
|
+
throw new Error(`Unexpected block terminator: ${line}`);
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Unsupported MDSN statement: ${line}`);
|
|
44
|
+
}
|
|
45
|
+
if (line === "}") {
|
|
46
|
+
currentBlock = null;
|
|
47
|
+
index += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (line.startsWith("input ")) {
|
|
51
|
+
currentBlock.inputs.push((0, statements_1.parseInputLine)(line, currentBlock.name));
|
|
52
|
+
index += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (line.startsWith("read ")) {
|
|
56
|
+
currentBlock.reads.push((0, statements_1.parseReadOrWriteLine)(line, "read", currentBlock.name, (0, statements_1.getNextOperationOrder)(currentBlock)));
|
|
57
|
+
index += 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (line.startsWith("write ")) {
|
|
61
|
+
currentBlock.writes.push((0, statements_1.parseReadOrWriteLine)(line, "write", currentBlock.name, (0, statements_1.getNextOperationOrder)(currentBlock)));
|
|
62
|
+
index += 1;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (line.startsWith("redirect ")) {
|
|
66
|
+
currentBlock.redirects.push((0, statements_1.parseRedirectLine)(line, currentBlock.name, (0, statements_1.getNextOperationOrder)(currentBlock)));
|
|
67
|
+
index += 1;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Unsupported MDSN statement: ${line}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (currentBlock) {
|
|
74
|
+
throw new Error(`Unterminated block declaration: ${currentBlock.name}`);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
schemas,
|
|
78
|
+
blocks: documentBlocks,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BlockDefinition, ReadDefinition, RedirectDefinition, WriteDefinition } from "../model/block";
|
|
2
|
+
import type { InputDefinition } from "../model/input";
|
|
3
|
+
import type { SchemaDefinition } from "../model/schema";
|
|
4
|
+
export declare function parseBlockHeaderLine(line: string): string;
|
|
5
|
+
export declare function parseInputLine(line: string, blockName: string): InputDefinition;
|
|
6
|
+
export declare function parseReadOrWriteLine(line: string, kind: "read" | "write", blockName: string, index: number): ReadDefinition | WriteDefinition;
|
|
7
|
+
export declare function parseRedirectLine(line: string, blockName: string, index: number): RedirectDefinition;
|
|
8
|
+
export declare function parseSchemaBlock(lines: string[], startIndex: number): {
|
|
9
|
+
schema: SchemaDefinition;
|
|
10
|
+
nextIndex: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function getNextOperationOrder(block: BlockDefinition): number;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseBlockHeaderLine = parseBlockHeaderLine;
|
|
4
|
+
exports.parseInputLine = parseInputLine;
|
|
5
|
+
exports.parseReadOrWriteLine = parseReadOrWriteLine;
|
|
6
|
+
exports.parseRedirectLine = parseRedirectLine;
|
|
7
|
+
exports.parseSchemaBlock = parseSchemaBlock;
|
|
8
|
+
exports.getNextOperationOrder = getNextOperationOrder;
|
|
9
|
+
function isIdentifier(value) {
|
|
10
|
+
return /^[a-zA-Z_][\w-]*$/.test(value);
|
|
11
|
+
}
|
|
12
|
+
function parseIdentifierList(raw) {
|
|
13
|
+
const trimmed = raw.trim();
|
|
14
|
+
if (!trimmed)
|
|
15
|
+
return [];
|
|
16
|
+
const items = trimmed.split(",").map((part) => part.trim()).filter(Boolean);
|
|
17
|
+
for (const item of items) {
|
|
18
|
+
if (!isIdentifier(item)) {
|
|
19
|
+
throw new Error(`Invalid identifier in argument list: ${item}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return items;
|
|
23
|
+
}
|
|
24
|
+
function parseStringArrayLiteral(raw) {
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === "string")) {
|
|
27
|
+
throw new Error(`Invalid array literal: ${raw}`);
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
function parseSchemaLiteral(raw) {
|
|
32
|
+
const parsed = JSON.parse(raw);
|
|
33
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
34
|
+
throw new Error("Schema body must be a JSON object");
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
function parseBlockHeaderLine(line) {
|
|
39
|
+
const match = line.trim().match(/^block\s+([a-zA-Z_][\w-]*)\s*\{$/);
|
|
40
|
+
if (!match) {
|
|
41
|
+
throw new Error(`Invalid block declaration: ${line}`);
|
|
42
|
+
}
|
|
43
|
+
return match[1];
|
|
44
|
+
}
|
|
45
|
+
function parseInputLine(line, blockName) {
|
|
46
|
+
const match = line
|
|
47
|
+
.trim()
|
|
48
|
+
.match(/^input\s+([a-zA-Z_][\w-]*)(!)?:\s*(text|number|boolean|choice|file|json)(?:\s+(secret))?(?:\s+(\[.*\]))?(?:\s+([a-zA-Z_][\w-]*))?$/);
|
|
49
|
+
if (!match) {
|
|
50
|
+
throw new Error(`Invalid input declaration: ${line}`);
|
|
51
|
+
}
|
|
52
|
+
const [, name, requiredMarker, type, secretMarker, optionsLiteral, schemaName] = match;
|
|
53
|
+
const input = {
|
|
54
|
+
id: `${blockName}::input::${name}`,
|
|
55
|
+
block: blockName,
|
|
56
|
+
name,
|
|
57
|
+
type: type,
|
|
58
|
+
required: requiredMarker === "!",
|
|
59
|
+
secret: secretMarker === "secret",
|
|
60
|
+
};
|
|
61
|
+
if (optionsLiteral) {
|
|
62
|
+
input.options = parseStringArrayLiteral(optionsLiteral);
|
|
63
|
+
}
|
|
64
|
+
if (schemaName) {
|
|
65
|
+
input.schema = schemaName;
|
|
66
|
+
}
|
|
67
|
+
if (input.type === "choice" && (!input.options || input.options.length === 0)) {
|
|
68
|
+
throw new Error(`Choice input ${name} in block ${blockName} must define options`);
|
|
69
|
+
}
|
|
70
|
+
if (input.type === "json" && !input.schema) {
|
|
71
|
+
throw new Error(`JSON input ${name} in block ${blockName} must define schema`);
|
|
72
|
+
}
|
|
73
|
+
return input;
|
|
74
|
+
}
|
|
75
|
+
function parseReadOrWriteLine(line, kind, blockName, index) {
|
|
76
|
+
const match = line
|
|
77
|
+
.trim()
|
|
78
|
+
.match(/^(read|write)\s+([a-zA-Z_][\w-]*)\s*:\s*"([^"]+)"(?:\s*\(([^)]*)\))?$/);
|
|
79
|
+
if (!match) {
|
|
80
|
+
throw new Error(`Invalid ${kind} declaration: ${line}`);
|
|
81
|
+
}
|
|
82
|
+
const [, parsedKind, name, target, rawInputs] = match;
|
|
83
|
+
if (parsedKind !== kind) {
|
|
84
|
+
throw new Error(`Expected ${kind} declaration: ${line}`);
|
|
85
|
+
}
|
|
86
|
+
const base = {
|
|
87
|
+
id: `${blockName}::${kind}::${index}`,
|
|
88
|
+
block: blockName,
|
|
89
|
+
name,
|
|
90
|
+
target,
|
|
91
|
+
inputs: parseIdentifierList(rawInputs ?? ""),
|
|
92
|
+
order: index,
|
|
93
|
+
};
|
|
94
|
+
return kind === "read" ? base : base;
|
|
95
|
+
}
|
|
96
|
+
function parseRedirectLine(line, blockName, index) {
|
|
97
|
+
const match = line.trim().match(/^redirect\s+"([^"]+)"$/);
|
|
98
|
+
if (!match) {
|
|
99
|
+
throw new Error(`Invalid redirect declaration: ${line}`);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
id: `${blockName}::redirect::${index}`,
|
|
103
|
+
block: blockName,
|
|
104
|
+
target: match[1],
|
|
105
|
+
order: index,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function parseSchemaBlock(lines, startIndex) {
|
|
109
|
+
const firstLine = lines[startIndex].trim();
|
|
110
|
+
const match = firstLine.match(/^schema\s+([a-zA-Z_][\w-]*)\s*(\{.*)?$/);
|
|
111
|
+
if (!match) {
|
|
112
|
+
throw new Error(`Invalid schema declaration: ${firstLine}`);
|
|
113
|
+
}
|
|
114
|
+
const name = match[1];
|
|
115
|
+
let literal = (match[2] ?? "").trim();
|
|
116
|
+
let braceDepth = (literal.match(/\{/g) ?? []).length - (literal.match(/\}/g) ?? []).length;
|
|
117
|
+
let index = startIndex;
|
|
118
|
+
while (braceDepth > 0) {
|
|
119
|
+
index += 1;
|
|
120
|
+
if (index >= lines.length) {
|
|
121
|
+
throw new Error(`Unterminated schema declaration: ${name}`);
|
|
122
|
+
}
|
|
123
|
+
literal += "\n" + lines[index];
|
|
124
|
+
braceDepth += (lines[index].match(/\{/g) ?? []).length;
|
|
125
|
+
braceDepth -= (lines[index].match(/\}/g) ?? []).length;
|
|
126
|
+
}
|
|
127
|
+
if (!literal.startsWith("{")) {
|
|
128
|
+
throw new Error(`schema ${name} must start with {`);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
schema: {
|
|
132
|
+
name,
|
|
133
|
+
shape: parseSchemaLiteral(literal),
|
|
134
|
+
},
|
|
135
|
+
nextIndex: index + 1,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function getNextOperationOrder(block) {
|
|
139
|
+
return block.reads.length + block.writes.length + block.redirects.length;
|
|
140
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BlockDefinition } from "../model/block";
|
|
2
|
+
import type { BlockAnchorDefinition } from "../model/document";
|
|
3
|
+
import type { SchemaDefinition } from "../model/schema";
|
|
4
|
+
export declare function validateDocumentStructure(schemas: SchemaDefinition[], blocks: BlockDefinition[], blockAnchors: BlockAnchorDefinition[]): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateDocumentStructure = validateDocumentStructure;
|
|
4
|
+
function validateDocumentStructure(schemas, blocks, blockAnchors) {
|
|
5
|
+
const schemaNames = new Set();
|
|
6
|
+
for (const schema of schemas) {
|
|
7
|
+
if (schemaNames.has(schema.name)) {
|
|
8
|
+
throw new Error(`Duplicate schema name: ${schema.name}`);
|
|
9
|
+
}
|
|
10
|
+
schemaNames.add(schema.name);
|
|
11
|
+
}
|
|
12
|
+
const blockNames = new Set();
|
|
13
|
+
for (const block of blocks) {
|
|
14
|
+
if (blockNames.has(block.name)) {
|
|
15
|
+
throw new Error(`Duplicate block name: ${block.name}`);
|
|
16
|
+
}
|
|
17
|
+
blockNames.add(block.name);
|
|
18
|
+
}
|
|
19
|
+
const anchorNames = new Set();
|
|
20
|
+
for (const anchor of blockAnchors) {
|
|
21
|
+
if (anchorNames.has(anchor.name)) {
|
|
22
|
+
throw new Error(`Duplicate block anchor: ${anchor.name}`);
|
|
23
|
+
}
|
|
24
|
+
anchorNames.add(anchor.name);
|
|
25
|
+
if (!blockNames.has(anchor.name)) {
|
|
26
|
+
throw new Error(`Unknown block ${anchor.name} referenced by mdsn:block anchor`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (blockAnchors.length > 0) {
|
|
30
|
+
for (const block of blocks) {
|
|
31
|
+
if (!anchorNames.has(block.name)) {
|
|
32
|
+
throw new Error(`Missing mdsn:block anchor for block ${block.name}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const block of blocks) {
|
|
37
|
+
const inputNames = new Set();
|
|
38
|
+
const operationNames = new Set();
|
|
39
|
+
for (const input of block.inputs) {
|
|
40
|
+
if (inputNames.has(input.name)) {
|
|
41
|
+
throw new Error(`Duplicate input name in block ${block.name}: ${input.name}`);
|
|
42
|
+
}
|
|
43
|
+
inputNames.add(input.name);
|
|
44
|
+
if (input.schema && !schemaNames.has(input.schema)) {
|
|
45
|
+
throw new Error(`Unknown schema ${input.schema} referenced by input ${input.name}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const operation of [...block.reads, ...block.writes]) {
|
|
49
|
+
if (operationNames.has(operation.name)) {
|
|
50
|
+
throw new Error(`Duplicate operation name in block ${block.name}: ${operation.name}`);
|
|
51
|
+
}
|
|
52
|
+
operationNames.add(operation.name);
|
|
53
|
+
for (const inputName of operation.inputs) {
|
|
54
|
+
if (!inputNames.has(inputName)) {
|
|
55
|
+
throw new Error(`Unknown input ${inputName} referenced by ${operation.id}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ActionHandler } from "../server/action-host";
|
|
2
|
+
import type { MdsnConfig } from "../server/config";
|
|
3
|
+
export interface CreateFrameworkAppOptions {
|
|
4
|
+
rootDir: string;
|
|
5
|
+
config?: MdsnConfig;
|
|
6
|
+
actions?: Record<string, ActionHandler<{
|
|
7
|
+
inputs: Record<string, unknown>;
|
|
8
|
+
}>>;
|
|
9
|
+
mode?: "dev" | "start";
|
|
10
|
+
devState?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export declare function createFrameworkApp(options: CreateFrameworkAppOptions): import("express-serve-static-core").Express;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFrameworkApp = createFrameworkApp;
|
|
4
|
+
const site_app_1 = require("./site-app");
|
|
5
|
+
function createFrameworkApp(options) {
|
|
6
|
+
return (0, site_app_1.createSiteApp)({
|
|
7
|
+
rootDir: options.rootDir,
|
|
8
|
+
config: options.config,
|
|
9
|
+
actions: options.actions,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ActionHandler } from "../server/action-host";
|
|
2
|
+
import { type RenderHostedPageOptions } from "../server/page-host";
|
|
3
|
+
export interface CreateHostedAppOptions {
|
|
4
|
+
pages: Record<string, string>;
|
|
5
|
+
actions?: Record<string, ActionHandler<{
|
|
6
|
+
inputs: Record<string, unknown>;
|
|
7
|
+
}>>;
|
|
8
|
+
publicDir?: string;
|
|
9
|
+
render?: Omit<RenderHostedPageOptions, "accept" | "routePath" | "layoutTemplate"> & {
|
|
10
|
+
resolveLayoutTemplate?: (routePath: string, rawPage: string) => string | undefined;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare function createHostedApp(options: CreateHostedAppOptions): import("express-serve-static-core").Express;
|
|
@@ -0,0 +1,133 @@
|
|
|
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.createHostedApp = createHostedApp;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const page_definition_1 = require("../core/document/page-definition");
|
|
9
|
+
const action_host_1 = require("../server/action-host");
|
|
10
|
+
const page_links_1 = require("../server/page-links");
|
|
11
|
+
const page_host_1 = require("../server/page-host");
|
|
12
|
+
const negotiate_1 = require("../server/negotiate");
|
|
13
|
+
const route_matcher_1 = require("../server/route-matcher");
|
|
14
|
+
const fragment_render_1 = require("../web/fragment-render");
|
|
15
|
+
const page_client_script_1 = require("../web/page-client-script");
|
|
16
|
+
function normalizeActionId(target) {
|
|
17
|
+
const trimmed = String(target).trim();
|
|
18
|
+
if (!trimmed || /^https?:\/\//i.test(trimmed)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (trimmed.toLowerCase().endsWith(".md")) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return trimmed.replace(/^\/+/u, "");
|
|
25
|
+
}
|
|
26
|
+
function buildActionBindings(pages) {
|
|
27
|
+
const bindings = new Map();
|
|
28
|
+
for (const pageSource of Object.values(pages)) {
|
|
29
|
+
let document;
|
|
30
|
+
try {
|
|
31
|
+
document = (0, page_definition_1.parsePageDefinition)(pageSource);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
for (const block of document.blocks) {
|
|
37
|
+
for (const operation of [...block.reads, ...block.writes]) {
|
|
38
|
+
const targetPath = (0, page_links_1.mapPageTargetToHttpPath)(operation.target);
|
|
39
|
+
const actionId = normalizeActionId(operation.target);
|
|
40
|
+
if (!actionId) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const existing = bindings.get(targetPath);
|
|
44
|
+
if (existing && (existing.actionId !== actionId || existing.blockName !== block.name)) {
|
|
45
|
+
throw new Error(`Action target must bind to one stable block context: ${targetPath}`);
|
|
46
|
+
}
|
|
47
|
+
bindings.set(targetPath, {
|
|
48
|
+
actionId,
|
|
49
|
+
blockName: block.name,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return bindings;
|
|
55
|
+
}
|
|
56
|
+
function createHostedApp(options) {
|
|
57
|
+
const app = (0, express_1.default)();
|
|
58
|
+
const actionBindings = buildActionBindings(options.pages);
|
|
59
|
+
const routedPages = (0, route_matcher_1.sortRoutedPagesForMatching)(Object.entries(options.pages).map(([routePath, source]) => ({
|
|
60
|
+
routePath,
|
|
61
|
+
source,
|
|
62
|
+
})));
|
|
63
|
+
app.use(express_1.default.json());
|
|
64
|
+
if (typeof options.publicDir === "string") {
|
|
65
|
+
app.use(express_1.default.static(options.publicDir));
|
|
66
|
+
}
|
|
67
|
+
app.get("/__mdsn/client.js", (_req, res) => {
|
|
68
|
+
res.type("application/javascript; charset=utf-8").send((0, page_client_script_1.getPageClientRuntimeScript)());
|
|
69
|
+
});
|
|
70
|
+
app.post("*", async (req, res, next) => {
|
|
71
|
+
const binding = actionBindings.get(req.path);
|
|
72
|
+
if (!binding) {
|
|
73
|
+
next();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const action = options.actions?.[binding.actionId];
|
|
77
|
+
if (!action) {
|
|
78
|
+
res.status(404).json({
|
|
79
|
+
ok: false,
|
|
80
|
+
errorCode: "NOT_FOUND",
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const result = await (0, action_host_1.executeActionHandler)(action, {
|
|
86
|
+
inputs: (req.body?.inputs ?? {}),
|
|
87
|
+
});
|
|
88
|
+
if (result.ok && result.kind === "fragment") {
|
|
89
|
+
if ((0, negotiate_1.wantsMarkdown)(req.headers.accept)) {
|
|
90
|
+
res.status(200).type("text/markdown; charset=utf-8").send(result.markdown);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
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
|
+
}
|
|
102
|
+
res.status(result.ok ? 200 : 400).json(result);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
res.status(500).json({
|
|
106
|
+
ok: false,
|
|
107
|
+
errorCode: "INTERNAL_ERROR",
|
|
108
|
+
message: error instanceof Error ? error.message : String(error),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
app.get("*", (req, res) => {
|
|
113
|
+
const matchedPage = (0, route_matcher_1.resolveRoutedPageForPath)(req.path, routedPages);
|
|
114
|
+
if (!matchedPage) {
|
|
115
|
+
res.status(404).type("text/plain; charset=utf-8").send("Not Found");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const rendered = (0, page_host_1.renderHostedPage)(matchedPage.source, {
|
|
119
|
+
accept: req.headers.accept,
|
|
120
|
+
routePath: req.path,
|
|
121
|
+
siteTitle: options.render?.siteTitle,
|
|
122
|
+
siteDescription: options.render?.siteDescription,
|
|
123
|
+
siteBaseUrl: options.render?.siteBaseUrl,
|
|
124
|
+
locales: options.render?.locales,
|
|
125
|
+
defaultLocale: options.render?.defaultLocale,
|
|
126
|
+
markdown: options.render?.markdown,
|
|
127
|
+
mapActionTarget: options.render?.mapActionTarget ?? page_links_1.mapPageTargetToHttpPath,
|
|
128
|
+
layoutTemplate: options.render?.resolveLayoutTemplate?.(req.path, matchedPage.source),
|
|
129
|
+
});
|
|
130
|
+
res.status(rendered.status).type(rendered.contentType).send(rendered.body);
|
|
131
|
+
});
|
|
132
|
+
return app;
|
|
133
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineConfig = exports.createFrameworkApp = void 0;
|
|
4
|
+
var create_framework_app_1 = require("./create-framework-app");
|
|
5
|
+
Object.defineProperty(exports, "createFrameworkApp", { enumerable: true, get: function () { return create_framework_app_1.createFrameworkApp; } });
|
|
6
|
+
var config_1 = require("../server/config");
|
|
7
|
+
Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return config_1.defineConfig; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CreateHostedAppOptions } from "./hosted-app";
|
|
2
|
+
import type { ActionHandler } from "../server/action-host";
|
|
3
|
+
import { type MdsnConfig } from "../server/config";
|
|
4
|
+
export declare function loadActionHandlers(serverDir: string): Record<string, ActionHandler<{
|
|
5
|
+
inputs: Record<string, unknown>;
|
|
6
|
+
}>>;
|
|
7
|
+
export interface CreateSiteAppOptions {
|
|
8
|
+
rootDir: string;
|
|
9
|
+
config?: MdsnConfig;
|
|
10
|
+
actions?: CreateHostedAppOptions["actions"];
|
|
11
|
+
}
|
|
12
|
+
export declare function createSiteApp(options: CreateSiteAppOptions): import("express-serve-static-core").Express;
|