@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
package/dist/server/action.js
CHANGED
|
@@ -42,7 +42,11 @@ function actionExportNameToActionId(filePath, actionsDir, exportName) {
|
|
|
42
42
|
return baseId ? `${baseId}/${exportName}` : exportName;
|
|
43
43
|
}
|
|
44
44
|
function isActionDefinition(value) {
|
|
45
|
-
|
|
45
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const obj = value;
|
|
49
|
+
return typeof obj.run === "function";
|
|
46
50
|
}
|
|
47
51
|
function actionDefinitionEntriesFromMap(candidate) {
|
|
48
52
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
package/dist/server/build.js
CHANGED
|
@@ -9,6 +9,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
const action_1 = require("./action");
|
|
10
10
|
const config_1 = require("./config");
|
|
11
11
|
const page_definition_1 = require("../core/document/page-definition");
|
|
12
|
+
const block_1 = require("../core/model/block");
|
|
12
13
|
const routes_1 = require("./routes");
|
|
13
14
|
const site_1 = require("./site");
|
|
14
15
|
function copyDirectoryContents(sourceDir, destinationDir) {
|
|
@@ -94,6 +95,9 @@ async function buildFrameworkSite(options) {
|
|
|
94
95
|
}
|
|
95
96
|
for (const block of page.blocks) {
|
|
96
97
|
for (const operation of [...block.reads, ...block.writes]) {
|
|
98
|
+
if ("accept" in operation && (!operation.name || (0, block_1.isStreamAccept)(operation.accept))) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
97
101
|
const actionId = normalizeDeclaredActionId(operation.target);
|
|
98
102
|
if (!actionId) {
|
|
99
103
|
continue;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type SerializableBlock } from "./markdown";
|
|
2
|
+
export interface RenderErrorFragmentOptions {
|
|
3
|
+
preface?: string[];
|
|
4
|
+
heading?: string;
|
|
5
|
+
message: string;
|
|
6
|
+
nextStep?: string;
|
|
7
|
+
details?: string[];
|
|
8
|
+
block?: SerializableBlock;
|
|
9
|
+
}
|
|
10
|
+
export declare function renderErrorFragment(options: RenderErrorFragmentOptions): string;
|
|
11
|
+
export interface RenderActionNotAvailableFragmentOptions {
|
|
12
|
+
heading?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
nextStep?: string;
|
|
15
|
+
block?: SerializableBlock;
|
|
16
|
+
}
|
|
17
|
+
export declare function renderActionNotAvailableFragment(options?: RenderActionNotAvailableFragmentOptions): string;
|
|
18
|
+
export interface RenderUnsupportedContentTypeFragmentOptions {
|
|
19
|
+
heading?: string;
|
|
20
|
+
message?: string;
|
|
21
|
+
nextStep?: string;
|
|
22
|
+
block?: SerializableBlock;
|
|
23
|
+
}
|
|
24
|
+
export declare function renderUnsupportedContentTypeFragment(options?: RenderUnsupportedContentTypeFragmentOptions): string;
|
|
25
|
+
export interface RenderInternalErrorFragmentOptions {
|
|
26
|
+
heading?: string;
|
|
27
|
+
message?: string;
|
|
28
|
+
nextStep?: string;
|
|
29
|
+
error?: unknown;
|
|
30
|
+
block?: SerializableBlock;
|
|
31
|
+
}
|
|
32
|
+
export declare function renderInternalErrorFragment(options?: RenderInternalErrorFragmentOptions): string;
|
|
33
|
+
export interface RenderAuthRequiredFragmentOptions {
|
|
34
|
+
heading?: string;
|
|
35
|
+
message?: string;
|
|
36
|
+
nextStep?: string;
|
|
37
|
+
blockName?: string;
|
|
38
|
+
emailInputName?: string;
|
|
39
|
+
passwordInputName?: string;
|
|
40
|
+
loginActionName?: string;
|
|
41
|
+
loginTarget?: string;
|
|
42
|
+
registerActionName?: string;
|
|
43
|
+
registerTarget?: string;
|
|
44
|
+
includeRegisterAction?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function renderAuthRequiredFragment(options?: RenderAuthRequiredFragmentOptions): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderErrorFragment = renderErrorFragment;
|
|
4
|
+
exports.renderActionNotAvailableFragment = renderActionNotAvailableFragment;
|
|
5
|
+
exports.renderUnsupportedContentTypeFragment = renderUnsupportedContentTypeFragment;
|
|
6
|
+
exports.renderInternalErrorFragment = renderInternalErrorFragment;
|
|
7
|
+
exports.renderAuthRequiredFragment = renderAuthRequiredFragment;
|
|
8
|
+
const markdown_1 = require("./markdown");
|
|
9
|
+
function renderErrorFragment(options) {
|
|
10
|
+
return (0, markdown_1.renderMarkdownFragment)({
|
|
11
|
+
body: [
|
|
12
|
+
...(options.preface ?? []),
|
|
13
|
+
options.heading ?? "## Action Status",
|
|
14
|
+
options.message,
|
|
15
|
+
...(options.details ?? []),
|
|
16
|
+
options.nextStep,
|
|
17
|
+
].filter((value) => typeof value === "string" && value.trim().length > 0),
|
|
18
|
+
block: options.block,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function renderActionNotAvailableFragment(options = {}) {
|
|
22
|
+
return renderErrorFragment({
|
|
23
|
+
heading: options.heading ?? "## Action Status",
|
|
24
|
+
message: options.message ?? "This action is not available on the current server.",
|
|
25
|
+
nextStep: options.nextStep ?? "Next step: follow another declared action or reload the current page definition.",
|
|
26
|
+
block: options.block,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function renderUnsupportedContentTypeFragment(options = {}) {
|
|
30
|
+
return renderErrorFragment({
|
|
31
|
+
heading: options.heading ?? "## Action Status",
|
|
32
|
+
message: options.message ?? "Unsupported content type for write action.",
|
|
33
|
+
nextStep: options.nextStep
|
|
34
|
+
?? "Next step: resend with `Content-Type: text/markdown` and a plain-text `key: value` body.",
|
|
35
|
+
block: options.block,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function renderInternalErrorFragment(options = {}) {
|
|
39
|
+
return renderErrorFragment({
|
|
40
|
+
heading: options.heading ?? "## Action Status",
|
|
41
|
+
message: options.message ?? "The action failed due to an internal error.",
|
|
42
|
+
nextStep: options.nextStep ?? "Next step: retry the action, or inspect server logs if the problem persists.",
|
|
43
|
+
details: options.error == null
|
|
44
|
+
? []
|
|
45
|
+
: [options.error instanceof Error ? options.error.message : String(options.error)],
|
|
46
|
+
block: options.block,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function renderAuthRequiredFragment(options = {}) {
|
|
50
|
+
const emailInputName = options.emailInputName ?? "email";
|
|
51
|
+
const passwordInputName = options.passwordInputName ?? "password";
|
|
52
|
+
const loginActionName = options.loginActionName ?? "login";
|
|
53
|
+
const loginTarget = options.loginTarget ?? "/login";
|
|
54
|
+
const includeRegisterAction = options.includeRegisterAction ?? true;
|
|
55
|
+
const registerActionName = options.registerActionName ?? "go_register";
|
|
56
|
+
const registerTarget = options.registerTarget ?? "/register";
|
|
57
|
+
const block = {
|
|
58
|
+
name: options.blockName ?? "auth",
|
|
59
|
+
inputs: [
|
|
60
|
+
{ name: emailInputName, type: "text", required: true },
|
|
61
|
+
{ name: passwordInputName, type: "text", required: true, secret: true },
|
|
62
|
+
],
|
|
63
|
+
reads: includeRegisterAction
|
|
64
|
+
? [{ name: registerActionName, target: registerTarget }]
|
|
65
|
+
: [],
|
|
66
|
+
writes: [
|
|
67
|
+
{ name: loginActionName, target: loginTarget, inputs: [emailInputName, passwordInputName] },
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
return renderErrorFragment({
|
|
71
|
+
heading: options.heading ?? "## Login Status",
|
|
72
|
+
message: options.message ?? "Login required: sign in before continuing.",
|
|
73
|
+
nextStep: options.nextStep
|
|
74
|
+
?? "Next step: enter email/password and run login, or go to register if no account exists.",
|
|
75
|
+
block,
|
|
76
|
+
});
|
|
77
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
export { defineAction, defineActions } from "./action";
|
|
2
|
+
export { normalizeActionInputPayloadToMarkdown, parseActionInputs, serializeActionInputsAsMarkdown, } from "./action-inputs";
|
|
3
|
+
export { createActionContextFromRequest, } from "./action-context";
|
|
2
4
|
export { renderMarkdownFragment, renderMarkdownValue, serializeBlock, } from "./markdown";
|
|
5
|
+
export { HttpCookieJar, parseCookieHeader, requireSessionFromCookie, } from "./session";
|
|
6
|
+
export { renderActionNotAvailableFragment, renderAuthRequiredFragment, renderErrorFragment, renderInternalErrorFragment, renderUnsupportedContentTypeFragment, } from "./error-fragments";
|
|
3
7
|
export { wantsHtml } from "./negotiate";
|
|
4
8
|
export { executeActionHandler } from "./action-host";
|
|
5
9
|
export { renderHostedPage } from "./page-host";
|
|
6
|
-
export type { ActionContext, ActionDefinition, ActionDefinitionMap,
|
|
7
|
-
export type {
|
|
10
|
+
export type { ActionContext, ActionDefinition, ActionDefinitionMap, } from "./action";
|
|
11
|
+
export type { CreateActionContextFromRequestOptions, } from "./action-context";
|
|
12
|
+
export type { MarkdownImageValue, MarkdownTableValue, MarkdownValueType, RenderMarkdownFragmentOptions, SerializableBlock, SerializableInput, SerializableRead, SerializableWrite, } from "./markdown";
|
|
13
|
+
export type { HeaderCarrier, RequireSessionFromCookieOptions, SessionGuardFailure, SessionGuardResult, SessionGuardSuccess, } from "./session";
|
|
14
|
+
export type { RenderActionNotAvailableFragmentOptions, RenderAuthRequiredFragmentOptions, RenderErrorFragmentOptions, RenderInternalErrorFragmentOptions, RenderUnsupportedContentTypeFragmentOptions, } from "./error-fragments";
|
|
8
15
|
export type { HostedPageResponse } from "./page-host";
|
package/dist/server/index.js
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderHostedPage = exports.executeActionHandler = exports.wantsHtml = exports.serializeBlock = exports.renderMarkdownValue = exports.renderMarkdownFragment = exports.defineActions = exports.defineAction = void 0;
|
|
3
|
+
exports.renderHostedPage = exports.executeActionHandler = exports.wantsHtml = exports.renderUnsupportedContentTypeFragment = exports.renderInternalErrorFragment = exports.renderErrorFragment = exports.renderAuthRequiredFragment = exports.renderActionNotAvailableFragment = exports.requireSessionFromCookie = exports.parseCookieHeader = exports.HttpCookieJar = exports.serializeBlock = exports.renderMarkdownValue = exports.renderMarkdownFragment = exports.createActionContextFromRequest = exports.serializeActionInputsAsMarkdown = exports.parseActionInputs = exports.normalizeActionInputPayloadToMarkdown = exports.defineActions = exports.defineAction = void 0;
|
|
4
4
|
var action_1 = require("./action");
|
|
5
5
|
Object.defineProperty(exports, "defineAction", { enumerable: true, get: function () { return action_1.defineAction; } });
|
|
6
6
|
Object.defineProperty(exports, "defineActions", { enumerable: true, get: function () { return action_1.defineActions; } });
|
|
7
|
+
var action_inputs_1 = require("./action-inputs");
|
|
8
|
+
Object.defineProperty(exports, "normalizeActionInputPayloadToMarkdown", { enumerable: true, get: function () { return action_inputs_1.normalizeActionInputPayloadToMarkdown; } });
|
|
9
|
+
Object.defineProperty(exports, "parseActionInputs", { enumerable: true, get: function () { return action_inputs_1.parseActionInputs; } });
|
|
10
|
+
Object.defineProperty(exports, "serializeActionInputsAsMarkdown", { enumerable: true, get: function () { return action_inputs_1.serializeActionInputsAsMarkdown; } });
|
|
11
|
+
var action_context_1 = require("./action-context");
|
|
12
|
+
Object.defineProperty(exports, "createActionContextFromRequest", { enumerable: true, get: function () { return action_context_1.createActionContextFromRequest; } });
|
|
7
13
|
var markdown_1 = require("./markdown");
|
|
8
14
|
Object.defineProperty(exports, "renderMarkdownFragment", { enumerable: true, get: function () { return markdown_1.renderMarkdownFragment; } });
|
|
9
15
|
Object.defineProperty(exports, "renderMarkdownValue", { enumerable: true, get: function () { return markdown_1.renderMarkdownValue; } });
|
|
10
16
|
Object.defineProperty(exports, "serializeBlock", { enumerable: true, get: function () { return markdown_1.serializeBlock; } });
|
|
17
|
+
var session_1 = require("./session");
|
|
18
|
+
Object.defineProperty(exports, "HttpCookieJar", { enumerable: true, get: function () { return session_1.HttpCookieJar; } });
|
|
19
|
+
Object.defineProperty(exports, "parseCookieHeader", { enumerable: true, get: function () { return session_1.parseCookieHeader; } });
|
|
20
|
+
Object.defineProperty(exports, "requireSessionFromCookie", { enumerable: true, get: function () { return session_1.requireSessionFromCookie; } });
|
|
21
|
+
var error_fragments_1 = require("./error-fragments");
|
|
22
|
+
Object.defineProperty(exports, "renderActionNotAvailableFragment", { enumerable: true, get: function () { return error_fragments_1.renderActionNotAvailableFragment; } });
|
|
23
|
+
Object.defineProperty(exports, "renderAuthRequiredFragment", { enumerable: true, get: function () { return error_fragments_1.renderAuthRequiredFragment; } });
|
|
24
|
+
Object.defineProperty(exports, "renderErrorFragment", { enumerable: true, get: function () { return error_fragments_1.renderErrorFragment; } });
|
|
25
|
+
Object.defineProperty(exports, "renderInternalErrorFragment", { enumerable: true, get: function () { return error_fragments_1.renderInternalErrorFragment; } });
|
|
26
|
+
Object.defineProperty(exports, "renderUnsupportedContentTypeFragment", { enumerable: true, get: function () { return error_fragments_1.renderUnsupportedContentTypeFragment; } });
|
|
11
27
|
var negotiate_1 = require("./negotiate");
|
|
12
28
|
Object.defineProperty(exports, "wantsHtml", { enumerable: true, get: function () { return negotiate_1.wantsHtml; } });
|
|
13
29
|
var action_host_1 = require("./action-host");
|
package/dist/server/init.js
CHANGED
|
@@ -29,7 +29,7 @@ function createStarterSite(targetDir) {
|
|
|
29
29
|
start: "mdsn start",
|
|
30
30
|
},
|
|
31
31
|
dependencies: {
|
|
32
|
-
"@mdsnai/sdk": "^0.1
|
|
32
|
+
"@mdsnai/sdk": "^0.2.1",
|
|
33
33
|
},
|
|
34
34
|
}, null, 2) + "\n", "utf8");
|
|
35
35
|
(0, node_fs_1.writeFileSync)(node_path_1.default.join(targetDir, "pages", "index.md"), `---
|
|
@@ -42,18 +42,18 @@ description: A runnable MDSN guestbook starter
|
|
|
42
42
|
|
|
43
43
|
Start with a working guestbook and reshape it into your own app.
|
|
44
44
|
|
|
45
|
-
- \`
|
|
45
|
+
- \`GET\` / \`POST\` both return Markdown fragments
|
|
46
46
|
- the Host only replaces the current \`guestbook\` block region
|
|
47
47
|
- page content stays static while the block keeps updating
|
|
48
48
|
|
|
49
49
|
<!-- mdsn:block guestbook -->
|
|
50
50
|
|
|
51
51
|
\`\`\`mdsn
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
BLOCK guestbook {
|
|
53
|
+
INPUT text -> nickname
|
|
54
|
+
INPUT text required -> message
|
|
55
|
+
GET "/list" -> refresh
|
|
56
|
+
POST "/post" (nickname, message) -> submit
|
|
57
57
|
}
|
|
58
58
|
\`\`\`
|
|
59
59
|
`, "utf8");
|
|
@@ -130,13 +130,13 @@ module.exports = defineActions({
|
|
|
130
130
|
const message = String(ctx.inputs.message ?? "").trim();
|
|
131
131
|
|
|
132
132
|
if (!message) {
|
|
133
|
-
return {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
};
|
|
133
|
+
return renderMarkdownFragment({
|
|
134
|
+
body: [
|
|
135
|
+
"## Action Status",
|
|
136
|
+
"Please enter a message before submitting.",
|
|
137
|
+
],
|
|
138
|
+
block: guestbookBlock,
|
|
139
|
+
});
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
addGuestbookMessage({ nickname, message });
|
|
@@ -16,27 +16,23 @@ export type SerializableInput = {
|
|
|
16
16
|
required?: boolean;
|
|
17
17
|
secret?: boolean;
|
|
18
18
|
options?: string[];
|
|
19
|
-
schema?: string;
|
|
20
19
|
};
|
|
21
20
|
export type SerializableRead = {
|
|
22
|
-
name
|
|
21
|
+
name?: string;
|
|
23
22
|
target: string;
|
|
24
23
|
inputs?: string[];
|
|
24
|
+
accept?: string;
|
|
25
25
|
};
|
|
26
26
|
export type SerializableWrite = {
|
|
27
27
|
name: string;
|
|
28
28
|
target: string;
|
|
29
29
|
inputs?: string[];
|
|
30
30
|
};
|
|
31
|
-
export type SerializableRedirect = {
|
|
32
|
-
target: string;
|
|
33
|
-
};
|
|
34
31
|
export type SerializableBlock = {
|
|
35
32
|
name: string;
|
|
36
33
|
inputs?: SerializableInput[];
|
|
37
34
|
reads?: SerializableRead[];
|
|
38
35
|
writes?: SerializableWrite[];
|
|
39
|
-
redirects?: SerializableRedirect[];
|
|
40
36
|
};
|
|
41
37
|
export type RenderMarkdownFragmentOptions = {
|
|
42
38
|
body?: string | string[];
|
package/dist/server/markdown.js
CHANGED
|
@@ -14,8 +14,11 @@ function joinBody(body) {
|
|
|
14
14
|
function escapeTableCell(value) {
|
|
15
15
|
return String(value ?? "").replace(/\|/g, "\\|").replace(/\n/g, "<br />");
|
|
16
16
|
}
|
|
17
|
-
function formatIdentifierList(inputs) {
|
|
18
|
-
|
|
17
|
+
function formatIdentifierList(inputs, options) {
|
|
18
|
+
if (inputs && inputs.length > 0) {
|
|
19
|
+
return ` (${inputs.join(", ")})`;
|
|
20
|
+
}
|
|
21
|
+
return options?.always ? " ()" : "";
|
|
19
22
|
}
|
|
20
23
|
function renderMarkdownValue(type, value, options) {
|
|
21
24
|
switch (type) {
|
|
@@ -43,22 +46,20 @@ function renderMarkdownValue(type, value, options) {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
function serializeBlock(block) {
|
|
46
|
-
const lines = ["```mdsn", `
|
|
49
|
+
const lines = ["```mdsn", `BLOCK ${block.name} {`];
|
|
47
50
|
for (const input of block.inputs ?? []) {
|
|
48
|
-
const requiredMarker = input.required ? "
|
|
51
|
+
const requiredMarker = input.required ? " required" : "";
|
|
49
52
|
const secretMarker = input.secret ? " secret" : "";
|
|
50
53
|
const optionsLiteral = input.options ? ` ${JSON.stringify(input.options)}` : "";
|
|
51
|
-
|
|
52
|
-
lines.push(` input ${input.name}${requiredMarker}: ${input.type}${secretMarker}${optionsLiteral}${schemaName}`);
|
|
54
|
+
lines.push(` INPUT ${input.type}${requiredMarker}${secretMarker}${optionsLiteral} -> ${input.name}`);
|
|
53
55
|
}
|
|
54
56
|
for (const read of block.reads ?? []) {
|
|
55
|
-
|
|
57
|
+
const acceptClause = read.accept ? ` accept:${JSON.stringify(read.accept)}` : "";
|
|
58
|
+
const nameClause = read.name ? ` -> ${read.name}` : "";
|
|
59
|
+
lines.push(` GET "${read.target}"${formatIdentifierList(read.inputs)}${acceptClause}${nameClause}`);
|
|
56
60
|
}
|
|
57
61
|
for (const write of block.writes ?? []) {
|
|
58
|
-
lines.push(`
|
|
59
|
-
}
|
|
60
|
-
for (const redirect of block.redirects ?? []) {
|
|
61
|
-
lines.push(` redirect "${redirect.target}"`);
|
|
62
|
+
lines.push(` POST "${write.target}"${formatIdentifierList(write.inputs, { always: true })} -> ${write.name}`);
|
|
62
63
|
}
|
|
63
64
|
lines.push("}", "```");
|
|
64
65
|
return lines.join("\n");
|
package/dist/server/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Express } from "express";
|
|
2
2
|
import { type CreateFrameworkAppOptions } from "../framework/create-framework-app";
|
|
3
3
|
import { type MdsnConfig } from "./config";
|
|
4
|
+
import { type Logger } from "../core";
|
|
4
5
|
export type CreateFrameworkAppFn = (options: CreateFrameworkAppOptions) => Express;
|
|
5
6
|
export type ListenFn = (options: {
|
|
6
7
|
app: Express;
|
|
@@ -8,7 +9,7 @@ export type ListenFn = (options: {
|
|
|
8
9
|
log: (message: string) => void;
|
|
9
10
|
}) => Promise<void>;
|
|
10
11
|
export type OpenBrowserFn = (url: string) => Promise<void> | void;
|
|
11
|
-
export declare function loadUserConfig(rootDir: string): Promise<MdsnConfig>;
|
|
12
|
+
export declare function loadUserConfig(rootDir: string, logger?: Logger): Promise<MdsnConfig>;
|
|
12
13
|
export declare function loadBuiltConfig(distDir: string): MdsnConfig | null;
|
|
13
14
|
export declare function listenOnPort(options: {
|
|
14
15
|
app: Express;
|
package/dist/server/server.js
CHANGED
|
@@ -15,6 +15,7 @@ const create_framework_app_1 = require("../framework/create-framework-app");
|
|
|
15
15
|
const config_1 = require("./config");
|
|
16
16
|
const dev_1 = require("./dev");
|
|
17
17
|
const module_loader_1 = require("./module-loader");
|
|
18
|
+
const core_1 = require("../core");
|
|
18
19
|
const USER_CONFIG_CANDIDATES = [
|
|
19
20
|
"mdsn.config.cjs",
|
|
20
21
|
"mdsn.config.js",
|
|
@@ -33,7 +34,7 @@ function formatConfigLoadError(configPath, error) {
|
|
|
33
34
|
}
|
|
34
35
|
return new Error(`Failed to load ${node_path_1.default.basename(configPath)}: ${message}`);
|
|
35
36
|
}
|
|
36
|
-
async function loadConfigFromFile(configPath) {
|
|
37
|
+
async function loadConfigFromFile(configPath, logger) {
|
|
37
38
|
const extension = node_path_1.default.extname(configPath).toLowerCase();
|
|
38
39
|
if (extension === ".json") {
|
|
39
40
|
return JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf8"));
|
|
@@ -43,20 +44,24 @@ async function loadConfigFromFile(configPath) {
|
|
|
43
44
|
return (loadedModule.default ?? loadedModule);
|
|
44
45
|
}
|
|
45
46
|
catch (error) {
|
|
47
|
+
logger.debug("Failed to load config file", { path: configPath });
|
|
46
48
|
throw formatConfigLoadError(configPath, error);
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
|
-
async function loadUserConfig(rootDir) {
|
|
51
|
+
async function loadUserConfig(rootDir, logger) {
|
|
52
|
+
const log = logger ?? (0, core_1.createLogger)({ prefix: "config" });
|
|
50
53
|
const availableConfigPaths = USER_CONFIG_CANDIDATES
|
|
51
54
|
.map((name) => node_path_1.default.join(rootDir, name))
|
|
52
55
|
.filter((candidatePath) => (0, node_fs_1.existsSync)(candidatePath));
|
|
53
56
|
if (availableConfigPaths.length === 0) {
|
|
57
|
+
log.debug("No config file found, using defaults");
|
|
54
58
|
return {};
|
|
55
59
|
}
|
|
56
60
|
const errors = [];
|
|
57
61
|
for (const configPath of availableConfigPaths) {
|
|
58
62
|
try {
|
|
59
|
-
|
|
63
|
+
log.debug("Loading config file", { path: configPath });
|
|
64
|
+
return await loadConfigFromFile(configPath, log);
|
|
60
65
|
}
|
|
61
66
|
catch (error) {
|
|
62
67
|
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -98,12 +103,13 @@ function openBrowser(url) {
|
|
|
98
103
|
child.unref();
|
|
99
104
|
}
|
|
100
105
|
async function startFrameworkServer(options = {}) {
|
|
106
|
+
const logger = (0, core_1.createLogger)({ prefix: "server" });
|
|
101
107
|
const cwd = node_path_1.default.resolve(options.cwd ?? process.cwd());
|
|
102
108
|
const distDir = node_path_1.default.join(cwd, "dist");
|
|
103
109
|
const shouldUseDist = options.mode === "start";
|
|
104
110
|
const builtConfig = shouldUseDist ? loadBuiltConfig(distDir) : null;
|
|
105
111
|
const rootDir = builtConfig ? distDir : cwd;
|
|
106
|
-
const config = builtConfig ?? await loadUserConfig(rootDir);
|
|
112
|
+
const config = builtConfig ?? await loadUserConfig(rootDir, logger);
|
|
107
113
|
const resolvedConfig = (0, config_1.resolveConfig)(config);
|
|
108
114
|
const devState = options.mode === "dev" ? (0, dev_1.createDevState)() : undefined;
|
|
109
115
|
const app = (options.createApp ?? create_framework_app_1.createFrameworkApp)({
|
|
@@ -128,10 +134,12 @@ async function startFrameworkServer(options = {}) {
|
|
|
128
134
|
? node_path_1.default.join(directoryName, fileName).split(node_path_1.default.sep).join("/")
|
|
129
135
|
: directoryName;
|
|
130
136
|
devState.bumpVersion(relativeName);
|
|
137
|
+
logger.debug("File changed, bumping version", { file: relativeName });
|
|
131
138
|
});
|
|
139
|
+
logger.info("Watching directory for changes", { directory: directoryName });
|
|
132
140
|
}
|
|
133
|
-
catch {
|
|
134
|
-
|
|
141
|
+
catch (error) {
|
|
142
|
+
logger.warn("Failed to setup file watcher", { directory: directoryName, error: String(error) });
|
|
135
143
|
}
|
|
136
144
|
}
|
|
137
145
|
}
|
|
@@ -144,9 +152,10 @@ async function startFrameworkServer(options = {}) {
|
|
|
144
152
|
if (options.mode === "dev" && resolvedConfig.dev.openBrowser) {
|
|
145
153
|
try {
|
|
146
154
|
await (options.openBrowser ?? openBrowser)(`http://localhost:${port}`);
|
|
155
|
+
logger.info("Opening browser", { url: `http://localhost:${port}` });
|
|
147
156
|
}
|
|
148
|
-
catch {
|
|
149
|
-
|
|
157
|
+
catch (error) {
|
|
158
|
+
logger.warn("Failed to open browser", { error: String(error) });
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type HeaderCarrier = {
|
|
2
|
+
get(name: string): string | null;
|
|
3
|
+
getSetCookie?: () => string[];
|
|
4
|
+
};
|
|
5
|
+
export declare function parseCookieHeader(cookieHeader: string | undefined): Record<string, string>;
|
|
6
|
+
export declare class HttpCookieJar {
|
|
7
|
+
private readonly values;
|
|
8
|
+
constructor(initialValues?: Record<string, string>);
|
|
9
|
+
get(name: string): string | undefined;
|
|
10
|
+
set(name: string, value: string): void;
|
|
11
|
+
delete(name: string): void;
|
|
12
|
+
clear(): void;
|
|
13
|
+
toCookieHeader(): string;
|
|
14
|
+
applyToHeaders(headers?: Record<string, string>): Record<string, string>;
|
|
15
|
+
ingestSetCookieHeader(setCookieHeader: string | string[] | null | undefined): void;
|
|
16
|
+
ingestFromResponse(response: {
|
|
17
|
+
headers: HeaderCarrier;
|
|
18
|
+
}): void;
|
|
19
|
+
}
|
|
20
|
+
export type SessionGuardSuccess<Session> = {
|
|
21
|
+
ok: true;
|
|
22
|
+
cookies: Record<string, string>;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
session: Session;
|
|
25
|
+
};
|
|
26
|
+
export type SessionGuardFailure = {
|
|
27
|
+
ok: false;
|
|
28
|
+
status: 401;
|
|
29
|
+
cookies: Record<string, string>;
|
|
30
|
+
markdown: string;
|
|
31
|
+
};
|
|
32
|
+
export type SessionGuardResult<Session> = SessionGuardSuccess<Session> | SessionGuardFailure;
|
|
33
|
+
export interface RequireSessionFromCookieOptions<Session> {
|
|
34
|
+
cookieHeader?: string;
|
|
35
|
+
cookieName: string;
|
|
36
|
+
resolveSession: (sessionId: string) => Session | null;
|
|
37
|
+
unauthorizedMarkdown?: string;
|
|
38
|
+
unauthorizedMessage?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function requireSessionFromCookie<Session>(options: RequireSessionFromCookieOptions<Session>): SessionGuardResult<Session>;
|