@mdsnai/sdk 0.1.0 → 0.2.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.
Files changed (78) hide show
  1. package/README.md +40 -0
  2. package/dist/core/document/markdown.js +2 -4
  3. package/dist/core/document/page-definition.js +1 -2
  4. package/dist/core/index.d.ts +2 -1
  5. package/dist/core/index.js +9 -1
  6. package/dist/core/model/block.d.ts +4 -8
  7. package/dist/core/model/block.js +6 -0
  8. package/dist/core/model/document.d.ts +0 -2
  9. package/dist/core/model/index.d.ts +1 -2
  10. package/dist/core/model/input.d.ts +1 -2
  11. package/dist/core/protocol/mdsn.d.ts +0 -2
  12. package/dist/core/protocol/mdsn.js +3 -17
  13. package/dist/core/protocol/statements.d.ts +3 -8
  14. package/dist/core/protocol/statements.js +46 -71
  15. package/dist/core/protocol/validation.d.ts +2 -3
  16. package/dist/core/protocol/validation.js +21 -11
  17. package/dist/core/utils/html.d.ts +6 -0
  18. package/dist/core/utils/html.js +28 -0
  19. package/dist/core/utils/index.d.ts +2 -0
  20. package/dist/core/utils/index.js +12 -0
  21. package/dist/core/utils/logger.d.ts +12 -0
  22. package/dist/core/utils/logger.js +45 -0
  23. package/dist/framework/create-framework-app.d.ts +1 -0
  24. package/dist/framework/create-framework-app.js +1 -0
  25. package/dist/framework/hosted-app.d.ts +21 -0
  26. package/dist/framework/hosted-app.js +123 -33
  27. package/dist/framework/index.d.ts +2 -0
  28. package/dist/framework/index.js +3 -1
  29. package/dist/framework/site-app.d.ts +1 -0
  30. package/dist/framework/site-app.js +1 -0
  31. package/dist/index.d.ts +5 -5
  32. package/dist/index.js +18 -1
  33. package/dist/server/action-context.d.ts +11 -0
  34. package/dist/server/action-context.js +26 -0
  35. package/dist/server/action-host.d.ts +2 -3
  36. package/dist/server/action-host.js +4 -2
  37. package/dist/server/action-inputs.d.ts +3 -0
  38. package/dist/server/action-inputs.js +178 -0
  39. package/dist/server/action-runtime.d.ts +3 -3
  40. package/dist/server/action-runtime.js +2 -21
  41. package/dist/server/action.d.ts +1 -9
  42. package/dist/server/action.js +5 -1
  43. package/dist/server/build.js +4 -0
  44. package/dist/server/error-fragments.d.ts +46 -0
  45. package/dist/server/error-fragments.js +77 -0
  46. package/dist/server/index.d.ts +9 -2
  47. package/dist/server/index.js +17 -1
  48. package/dist/server/init.js +13 -13
  49. package/dist/server/markdown.d.ts +2 -6
  50. package/dist/server/markdown.js +11 -10
  51. package/dist/server/server.d.ts +2 -1
  52. package/dist/server/server.js +17 -8
  53. package/dist/server/session.d.ts +40 -0
  54. package/dist/server/session.js +220 -0
  55. package/dist/web/block-runtime.js +15 -17
  56. package/dist/web/fragment-render.d.ts +0 -2
  57. package/dist/web/fragment-render.js +0 -1
  58. package/dist/web/i18n.d.ts +0 -2
  59. package/dist/web/i18n.js +0 -4
  60. package/dist/web/index.d.ts +1 -1
  61. package/dist/web/index.js +2 -1
  62. package/dist/web/page-bootstrap.js +0 -1
  63. package/dist/web/page-client-runtime.d.ts +2 -13
  64. package/dist/web/page-client-runtime.js +1 -16
  65. package/dist/web/page-client-script.d.ts +0 -1
  66. package/dist/web/page-client-script.js +172 -160
  67. package/dist/web/page-html.js +4 -11
  68. package/dist/web/page-render.d.ts +1 -1
  69. package/dist/web/page-render.js +13 -21
  70. package/package.json +1 -1
  71. package/dist/core/action/execution.d.ts +0 -4
  72. package/dist/core/action/execution.js +0 -57
  73. package/dist/core/action/index.d.ts +0 -2
  74. package/dist/core/action/index.js +0 -7
  75. package/dist/core/action/types.d.ts +0 -19
  76. package/dist/core/action/types.js +0 -2
  77. package/dist/core/model/schema.d.ts +0 -4
  78. package/dist/core/model/schema.js +0 -2
@@ -42,7 +42,11 @@ function actionExportNameToActionId(filePath, actionsDir, exportName) {
42
42
  return baseId ? `${baseId}/${exportName}` : exportName;
43
43
  }
44
44
  function isActionDefinition(value) {
45
- return !!value && typeof value === "object" && typeof value.run === "function";
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)) {
@@ -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
+ }
@@ -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, ActionResult, } from "./action";
7
- export type { MarkdownImageValue, MarkdownTableValue, MarkdownValueType, RenderMarkdownFragmentOptions, SerializableBlock, SerializableInput, SerializableRead, SerializableRedirect, SerializableWrite, } from "./markdown";
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";
@@ -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");
@@ -29,7 +29,7 @@ function createStarterSite(targetDir) {
29
29
  start: "mdsn start",
30
30
  },
31
31
  dependencies: {
32
- "@mdsnai/sdk": "^0.1.0",
32
+ "@mdsnai/sdk": "^0.2.0",
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,7 +42,7 @@ 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
- - \`read\` / \`write\` both return Markdown fragments
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
 
@@ -50,10 +50,10 @@ Start with a working guestbook and reshape it into your own app.
50
50
 
51
51
  \`\`\`mdsn
52
52
  block guestbook {
53
- input nickname: text
54
- input message!: text
55
- read refresh: "/list"
56
- write submit: "/post" (nickname, message)
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
- ok: false,
135
- errorCode: "EMPTY_MESSAGE",
136
- fieldErrors: {
137
- message: "Please enter a message.",
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: string;
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[];
@@ -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
- return inputs && inputs.length > 0 ? ` (${inputs.join(", ")})` : "";
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) {
@@ -45,20 +48,18 @@ function renderMarkdownValue(type, value, options) {
45
48
  function serializeBlock(block) {
46
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
- const schemaName = input.schema ? ` ${input.schema}` : "";
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
- lines.push(` read ${read.name}: "${read.target}"${formatIdentifierList(read.inputs)}`);
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(` write ${write.name}: "${write.target}"${formatIdentifierList(write.inputs)}`);
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");
@@ -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;
@@ -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
- return await loadConfigFromFile(configPath);
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
- // Ignore watcher setup failures for now; the dev server remains usable without auto-reload.
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
- // Keep the dev server usable even if the opener is unavailable.
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>;