@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
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getPageClientRuntimeScript = getPageClientRuntimeScript;
4
- exports.serializePageClientRuntimeScript = serializePageClientRuntimeScript;
5
4
  function serializeForInlineScript(value) {
6
5
  return JSON.stringify(value)
7
6
  .replace(/</g, "\\u003C")
@@ -138,14 +137,6 @@ function getPageClientRuntimeScript() {
138
137
  return parsed;
139
138
  }
140
139
 
141
- function parseSchemaLiteral(raw) {
142
- const parsed = JSON.parse(raw);
143
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
144
- throw new Error("Schema body must be a JSON object");
145
- }
146
- return parsed;
147
- }
148
-
149
140
  function parseBlockHeaderLine(line) {
150
141
  const match = String(line).trim().match(/^block\\s+([a-zA-Z_][\\w-]*)\\s*\\{$/);
151
142
  if (!match) {
@@ -157,60 +148,87 @@ function getPageClientRuntimeScript() {
157
148
  function parseInputLine(line, blockName) {
158
149
  const match = String(line)
159
150
  .trim()
160
- .match(/^input\\s+([a-zA-Z_][\\w-]*)(!)?:\\s*(text|number|boolean|choice|file|json)(?:\\s+(secret))?(?:\\s+(\\[.*\\]))?(?:\\s+([a-zA-Z_][\\w-]*))?$/);
151
+ .match(/^INPUT\\s+(text|number|boolean|choice|asset)(?:\\s+(.*?))?\\s*->\\s*([a-zA-Z_][\\w-]*)$/);
161
152
  if (!match) {
162
153
  throw new Error("Invalid input declaration: " + line);
163
154
  }
164
155
 
165
- const name = match[1];
166
- const requiredMarker = match[2];
167
- const type = match[3];
168
- const secretMarker = match[4];
169
- const optionsLiteral = match[5];
170
- const schemaName = match[6];
156
+ const type = match[1];
157
+ const trailing = String(match[2] || "").trim();
158
+ const name = match[3];
159
+ const required = /\\brequired\\b/u.test(trailing);
160
+ const secret = /\\bsecret\\b/u.test(trailing);
161
+ const optionsLiteral = (trailing.match(/(\\[.*\\])/u) || [])[1];
162
+ const normalizedTrailing = trailing
163
+ .replace(/\\brequired\\b/gu, "")
164
+ .replace(/\\bsecret\\b/gu, "")
165
+ .replace(/(\\[.*\\])/u, "")
166
+ .trim();
167
+ if (normalizedTrailing) {
168
+ throw new Error("Invalid input declaration: " + line);
169
+ }
171
170
  const input = {
172
171
  id: blockName + "::input::" + name,
173
172
  block: blockName,
174
173
  name,
175
174
  type,
176
- required: requiredMarker === "!",
177
- secret: secretMarker === "secret",
175
+ required,
176
+ secret,
178
177
  };
179
178
 
180
179
  if (optionsLiteral) {
181
180
  input.options = parseStringArrayLiteral(optionsLiteral);
182
181
  }
183
182
 
184
- if (schemaName) {
185
- input.schema = schemaName;
186
- }
187
-
188
183
  if (input.type === "choice" && (!input.options || input.options.length === 0)) {
189
184
  throw new Error("Choice input " + name + " in block " + blockName + " must define options");
190
185
  }
191
186
 
192
- if (input.type === "json" && !input.schema) {
193
- throw new Error("JSON input " + name + " in block " + blockName + " must define schema");
187
+ if (input.type !== "choice" && input.options) {
188
+ throw new Error("Only choice input " + name + " in block " + blockName + " can define options");
194
189
  }
195
190
 
196
191
  return input;
197
192
  }
198
193
 
199
194
  function parseReadOrWriteLine(line, kind, blockName, index) {
200
- const match = String(line)
201
- .trim()
202
- .match(/^(read|write)\\s+([a-zA-Z_][\\w-]*)\\s*:\\s*"([^"]+)"(?:\\s*\\(([^)]*)\\))?$/);
195
+ if (kind === "read") {
196
+ const match = String(line)
197
+ .trim()
198
+ .match(/^GET\\s+"([^"]+)"(?:\\s*\\(([^)]*)\\))?(?:\\s+accept:"([^"]+)")?(?:\\s*->\\s*([a-zA-Z_][\\w-]*))?$/);
199
+ if (!match) {
200
+ throw new Error("Invalid " + kind + " declaration: " + line);
201
+ }
202
+
203
+ const target = match[1];
204
+ const rawInputs = match[2];
205
+ const accept = match[3];
206
+ const name = match[4];
207
+ const isStream = String(accept || "").toLowerCase() === "text/event-stream";
208
+
209
+ if ((!name && !isStream) || (isStream && name)) {
210
+ throw new Error("Invalid " + kind + " declaration: " + line);
211
+ }
212
+
213
+ return {
214
+ id: blockName + "::" + kind + "::" + index,
215
+ block: blockName,
216
+ name,
217
+ target,
218
+ inputs: parseIdentifierList(rawInputs || ""),
219
+ accept,
220
+ order: index,
221
+ };
222
+ }
223
+
224
+ const match = String(line).trim().match(/^POST\\s+"([^"]+)"\\s*\\(([^)]*)\\)\\s*->\\s*([a-zA-Z_][\\w-]*)$/);
203
225
  if (!match) {
204
226
  throw new Error("Invalid " + kind + " declaration: " + line);
205
227
  }
206
228
 
207
- const parsedKind = match[1];
208
- const name = match[2];
209
- const target = match[3];
210
- const rawInputs = match[4];
211
- if (parsedKind !== kind) {
212
- throw new Error("Expected " + kind + " declaration: " + line);
213
- }
229
+ const target = match[1];
230
+ const rawInputs = match[2];
231
+ const name = match[3];
214
232
 
215
233
  return {
216
234
  id: blockName + "::" + kind + "::" + index,
@@ -222,61 +240,11 @@ function getPageClientRuntimeScript() {
222
240
  };
223
241
  }
224
242
 
225
- function parseRedirectLine(line, blockName, index) {
226
- const match = String(line).trim().match(/^redirect\\s+"([^"]+)"$/);
227
- if (!match) {
228
- throw new Error("Invalid redirect declaration: " + line);
229
- }
230
-
231
- return {
232
- id: blockName + "::redirect::" + index,
233
- block: blockName,
234
- target: match[1],
235
- order: index,
236
- };
237
- }
238
-
239
- function parseSchemaBlock(lines, startIndex) {
240
- const firstLine = String(lines[startIndex]).trim();
241
- const match = firstLine.match(/^schema\\s+([a-zA-Z_][\\w-]*)\\s*(\\{.*)?$/);
242
- if (!match) {
243
- throw new Error("Invalid schema declaration: " + firstLine);
244
- }
245
-
246
- const name = match[1];
247
- let literal = (match[2] || "").trim();
248
- let braceDepth = (literal.match(/\\{/g) || []).length - (literal.match(/\\}/g) || []).length;
249
- let index = startIndex;
250
-
251
- while (braceDepth > 0) {
252
- index += 1;
253
- if (index >= lines.length) {
254
- throw new Error("Unterminated schema declaration: " + name);
255
- }
256
- literal += "\\n" + lines[index];
257
- braceDepth += (lines[index].match(/\\{/g) || []).length;
258
- braceDepth -= (lines[index].match(/\\}/g) || []).length;
259
- }
260
-
261
- if (!literal.startsWith("{")) {
262
- throw new Error("schema " + name + " must start with {");
263
- }
264
-
265
- return {
266
- schema: {
267
- name,
268
- shape: parseSchemaLiteral(literal),
269
- },
270
- nextIndex: index + 1,
271
- };
272
- }
273
-
274
243
  function getNextOperationOrder(block) {
275
- return block.reads.length + block.writes.length + block.redirects.length;
244
+ return block.reads.length + block.writes.length;
276
245
  }
277
246
 
278
247
  function parseMdsnBlocks(blocks) {
279
- const schemas = [];
280
248
  const documentBlocks = [];
281
249
  let currentBlock = null;
282
250
 
@@ -292,20 +260,12 @@ function getPageClientRuntimeScript() {
292
260
  }
293
261
 
294
262
  if (!currentBlock) {
295
- if (line.startsWith("schema ")) {
296
- const parsedSchema = parseSchemaBlock(lines, index);
297
- schemas.push(parsedSchema.schema);
298
- index = parsedSchema.nextIndex;
299
- continue;
300
- }
301
-
302
263
  if (line.startsWith("block ")) {
303
264
  currentBlock = {
304
265
  name: parseBlockHeaderLine(line),
305
266
  inputs: [],
306
267
  reads: [],
307
268
  writes: [],
308
- redirects: [],
309
269
  };
310
270
  documentBlocks.push(currentBlock);
311
271
  index += 1;
@@ -325,30 +285,24 @@ function getPageClientRuntimeScript() {
325
285
  continue;
326
286
  }
327
287
 
328
- if (line.startsWith("input ")) {
288
+ if (line.startsWith("INPUT ")) {
329
289
  currentBlock.inputs.push(parseInputLine(line, currentBlock.name));
330
290
  index += 1;
331
291
  continue;
332
292
  }
333
293
 
334
- if (line.startsWith("read ")) {
294
+ if (line.startsWith("GET ")) {
335
295
  currentBlock.reads.push(parseReadOrWriteLine(line, "read", currentBlock.name, getNextOperationOrder(currentBlock)));
336
296
  index += 1;
337
297
  continue;
338
298
  }
339
299
 
340
- if (line.startsWith("write ")) {
300
+ if (line.startsWith("POST ")) {
341
301
  currentBlock.writes.push(parseReadOrWriteLine(line, "write", currentBlock.name, getNextOperationOrder(currentBlock)));
342
302
  index += 1;
343
303
  continue;
344
304
  }
345
305
 
346
- if (line.startsWith("redirect ")) {
347
- currentBlock.redirects.push(parseRedirectLine(line, currentBlock.name, getNextOperationOrder(currentBlock)));
348
- index += 1;
349
- continue;
350
- }
351
-
352
306
  throw new Error("Unsupported MDSN statement: " + line);
353
307
  }
354
308
  }
@@ -358,7 +312,6 @@ function getPageClientRuntimeScript() {
358
312
  }
359
313
 
360
314
  return {
361
- schemas,
362
315
  blocks: documentBlocks,
363
316
  };
364
317
  }
@@ -404,9 +357,6 @@ function getPageClientRuntimeScript() {
404
357
  for (const write of block.writes || []) {
405
358
  operationsById.set(write.id, { ...write, kind: "write" });
406
359
  }
407
- for (const redirect of block.redirects || []) {
408
- operationsById.set(redirect.id, { ...redirect, kind: "redirect" });
409
- }
410
360
  }
411
361
 
412
362
  function clearBlock(blockName) {
@@ -422,6 +372,70 @@ function getPageClientRuntimeScript() {
422
372
  }
423
373
  }
424
374
 
375
+ function parseOperationOrder(operationId, kind) {
376
+ const pattern = new RegExp("::" + kind + "::(\\\\d+)$");
377
+ const match = String(operationId || "").match(pattern);
378
+ return match ? Number(match[1]) : 0;
379
+ }
380
+
381
+ function registerBlockFromDom(blockName) {
382
+ clearBlock(blockName);
383
+ for (const element of document.querySelectorAll("[data-mdsn-input]")) {
384
+ const inputId = element.getAttribute("data-mdsn-input");
385
+ const name = element.getAttribute("data-input-name");
386
+ const type = element.getAttribute("data-input-type");
387
+ if (!inputId || !inputId.startsWith(blockName + "::input::") || !name || !type) {
388
+ continue;
389
+ }
390
+
391
+ inputDefinitionsById.set(inputId, {
392
+ id: inputId,
393
+ block: blockName,
394
+ name,
395
+ type,
396
+ required: element.getAttribute("data-required") === "true",
397
+ secret: element.getAttribute("data-secret") === "true",
398
+ });
399
+ }
400
+
401
+ for (const element of document.querySelectorAll("[data-mdsn-read]")) {
402
+ const operationId = element.getAttribute("data-mdsn-read");
403
+ const target = element.getAttribute("data-target");
404
+ if (!operationId || !operationId.startsWith(blockName + "::read::") || !target) {
405
+ continue;
406
+ }
407
+
408
+ operationsById.set(operationId, {
409
+ id: operationId,
410
+ block: blockName,
411
+ kind: "read",
412
+ name: element.getAttribute("data-op-name") || element.textContent || "read",
413
+ target,
414
+ inputs: parseIdentifierList(element.getAttribute("data-inputs") || ""),
415
+ order: parseOperationOrder(operationId, "read"),
416
+ });
417
+ }
418
+
419
+ for (const element of document.querySelectorAll("[data-mdsn-write]")) {
420
+ const operationId = element.getAttribute("data-mdsn-write");
421
+ const target = element.getAttribute("data-target");
422
+ if (!operationId || !operationId.startsWith(blockName + "::write::") || !target) {
423
+ continue;
424
+ }
425
+
426
+ operationsById.set(operationId, {
427
+ id: operationId,
428
+ block: blockName,
429
+ kind: "write",
430
+ name: element.getAttribute("data-op-name") || element.textContent || "write",
431
+ target,
432
+ inputs: parseIdentifierList(element.getAttribute("data-inputs") || ""),
433
+ order: parseOperationOrder(operationId, "write"),
434
+ });
435
+ }
436
+
437
+ }
438
+
425
439
  for (const block of bootstrap.blocks || []) {
426
440
  registerBlock(block);
427
441
  }
@@ -462,6 +476,33 @@ function getPageClientRuntimeScript() {
462
476
  return element.value;
463
477
  }
464
478
 
479
+ function serializeInputsAsMarkdown(inputs) {
480
+ return Object.entries(inputs)
481
+ .filter((entry) => entry[1] !== undefined)
482
+ .map((entry) => entry[0] + ": " + JSON.stringify(entry[1]))
483
+ .join(", ");
484
+ }
485
+
486
+ function applyQueryParams(target, inputs) {
487
+ const source = String(target || "");
488
+ if (!inputs || Object.keys(inputs).length === 0) {
489
+ return source;
490
+ }
491
+ const parts = source.split("?");
492
+ const basePath = parts[0] || "";
493
+ const existingQuery = parts[1] ? new URLSearchParams(parts[1]) : new URLSearchParams();
494
+ for (const entry of Object.entries(inputs)) {
495
+ const name = entry[0];
496
+ const value = entry[1];
497
+ if (value === undefined || value === null || value === "") {
498
+ continue;
499
+ }
500
+ existingQuery.set(name, String(value));
501
+ }
502
+ const queryString = existingQuery.toString();
503
+ return queryString ? basePath + "?" + queryString : basePath;
504
+ }
505
+
465
506
  async function runOperation(operationId, button) {
466
507
  const operation = operationsById.get(operationId);
467
508
  if (!operation) return;
@@ -473,11 +514,6 @@ function getPageClientRuntimeScript() {
473
514
  updateStatus("Loading...", "loading");
474
515
 
475
516
  try {
476
- if (operation.kind === "redirect") {
477
- window.location.assign(mapTargetToHttpPath(operation.target));
478
- return;
479
- }
480
-
481
517
  const inputs = {};
482
518
  for (const inputName of operation.inputs || []) {
483
519
  const inputId = operation.block + "::input::" + inputName;
@@ -486,54 +522,34 @@ function getPageClientRuntimeScript() {
486
522
  inputs[inputName] = getInputValue(definition);
487
523
  }
488
524
 
489
- const response = await fetch(mapTargetToHttpPath(operation.target), {
490
- method: "POST",
491
- headers: {
492
- "content-type": "application/json",
493
- Accept: "application/json",
494
- },
495
- body: JSON.stringify({
496
- block: operation.block,
497
- pathname: window.location.pathname,
498
- inputs,
499
- }),
500
- });
501
- const result = await response.json();
502
-
503
- if (!result || result.ok !== true) {
504
- const message = result && typeof result.message === "string"
505
- ? result.message
506
- : result && typeof result.errorCode === "string"
507
- ? result.errorCode
508
- : "Action failed.";
509
- updateStatus("Failed: " + message, "error");
510
- return;
511
- }
512
-
513
- if (result.kind === "redirect" && typeof result.location === "string") {
514
- window.location.assign(result.location);
515
- return;
516
- }
517
-
518
- if (
519
- result.kind === "fragment"
520
- && typeof result.markdown === "string"
521
- && typeof result.html === "string"
522
- ) {
523
- const fragment = parseBlockFragment(result.markdown);
524
- root.innerHTML = replaceBlockRegionMarkup(root.innerHTML, operation.block, result.html);
525
- clearBlock(operation.block);
526
- if (fragment.blocks[0]) {
527
- registerBlock(fragment.blocks[0]);
525
+ const method = operation.kind === "read" ? "GET" : "POST";
526
+ const mappedTarget = mapTargetToHttpPath(operation.target);
527
+ const requestTarget = method === "GET" ? applyQueryParams(mappedTarget, inputs) : mappedTarget;
528
+ const requestInit = method === "GET"
529
+ ? {
530
+ method,
531
+ headers: { Accept: "text/html" },
528
532
  }
529
- bindActions("[data-mdsn-read]", "data-mdsn-read");
530
- bindActions("[data-mdsn-write]", "data-mdsn-write");
531
- bindActions("[data-mdsn-redirect]", "data-mdsn-redirect");
532
- updateStatus("Updated.", "success");
533
- return;
533
+ : {
534
+ method,
535
+ headers: {
536
+ "content-type": "text/markdown",
537
+ Accept: "text/html",
538
+ },
539
+ body: serializeInputsAsMarkdown(inputs),
540
+ };
541
+ const response = await fetch(requestTarget, requestInit);
542
+ const contentType = response.headers.get("content-type") || "";
543
+ const payload = await response.text();
544
+ if (contentType && !contentType.includes("text/html")) {
545
+ throw new Error("Invalid action response: expected text/html fragment");
534
546
  }
535
547
 
536
- updateStatus("Failed: Invalid action response.", "error");
548
+ root.innerHTML = replaceBlockRegionMarkup(root.innerHTML, operation.block, payload);
549
+ registerBlockFromDom(operation.block);
550
+ bindActions("[data-mdsn-read]", "data-mdsn-read");
551
+ bindActions("[data-mdsn-write]", "data-mdsn-write");
552
+ updateStatus(response.ok ? "Updated." : "Failed: action returned an error fragment.", response.ok ? "success" : "error");
537
553
  } catch (error) {
538
554
  updateStatus("Failed: " + (error instanceof Error ? error.message : String(error)), "error");
539
555
  } finally {
@@ -558,10 +574,6 @@ function getPageClientRuntimeScript() {
558
574
 
559
575
  bindActions("[data-mdsn-read]", "data-mdsn-read");
560
576
  bindActions("[data-mdsn-write]", "data-mdsn-write");
561
- bindActions("[data-mdsn-redirect]", "data-mdsn-redirect");
562
577
  })();
563
578
  `;
564
579
  }
565
- function serializePageClientRuntimeScript() {
566
- return serializeForInlineScript(getPageClientRuntimeScript());
567
- }
@@ -2,14 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderPageHtmlContent = renderPageHtmlContent;
4
4
  exports.renderPageHtmlDocument = renderPageHtmlDocument;
5
- function escapeHtml(value) {
6
- return value
7
- .replace(/&/g, "&amp;")
8
- .replace(/</g, "&lt;")
9
- .replace(/>/g, "&gt;")
10
- .replace(/"/g, "&quot;")
11
- .replace(/'/g, "&#39;");
12
- }
5
+ const core_1 = require("../core");
13
6
  function serializeInlineJson(value) {
14
7
  return JSON.stringify(value)
15
8
  .replace(/</g, "\\u003C")
@@ -23,7 +16,7 @@ function renderPageHtmlContent(model, options) {
23
16
  <div data-mdsn-status hidden></div>
24
17
  </main>
25
18
  <script id="mdsn-bootstrap" type="application/json">${serializeInlineJson(model.bootstrap)}</script>
26
- <script src="${escapeHtml(clientScriptPath)}" defer></script>`;
19
+ <script src="${(0, core_1.escapeHtml)(clientScriptPath)}" defer></script>`;
27
20
  }
28
21
  function renderPageHtmlDocument(model, options) {
29
22
  const title = typeof options?.title === "string" && options.title.trim().length > 0
@@ -36,11 +29,11 @@ function renderPageHtmlDocument(model, options) {
36
29
  : "en";
37
30
  const clientScriptPath = options?.clientScriptPath ?? "/__mdsn/client.js";
38
31
  return `<!doctype html>
39
- <html lang="${escapeHtml(lang)}">
32
+ <html lang="${(0, core_1.escapeHtml)(lang)}">
40
33
  <head>
41
34
  <meta charset="utf-8" />
42
35
  <meta name="viewport" content="width=device-width, initial-scale=1" />
43
- <title>${escapeHtml(title)}</title>
36
+ <title>${(0, core_1.escapeHtml)(title)}</title>
44
37
  </head>
45
38
  <body>
46
39
  ${renderPageHtmlContent(model, { clientScriptPath })}
@@ -1,4 +1,4 @@
1
- import type { BlockDefinition } from "../core/model/block";
1
+ import { type BlockDefinition } from "../core/model/block";
2
2
  import type { DocumentDefinition } from "../core/model/document";
3
3
  import { type PageBootstrap } from "./page-bootstrap";
4
4
  import { type ParsedPage } from "./headless";
@@ -6,17 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.renderBlockPanelHtml = renderBlockPanelHtml;
7
7
  exports.createPageRenderModel = createPageRenderModel;
8
8
  const markdown_it_1 = __importDefault(require("markdown-it"));
9
+ const block_1 = require("../core/model/block");
10
+ const core_1 = require("../core");
9
11
  const page_bootstrap_1 = require("./page-bootstrap");
10
12
  const block_runtime_1 = require("./block-runtime");
11
13
  const headless_1 = require("./headless");
12
- function escapeHtml(value) {
13
- return value
14
- .replace(/&/g, "&amp;")
15
- .replace(/</g, "&lt;")
16
- .replace(/>/g, "&gt;")
17
- .replace(/"/g, "&quot;")
18
- .replace(/'/g, "&#39;");
19
- }
20
14
  function createMarkdownRenderer(options) {
21
15
  return new markdown_it_1.default({
22
16
  html: true,
@@ -37,31 +31,29 @@ function renderInput(block) {
37
31
  ? "password"
38
32
  : input.type === "number"
39
33
  ? "number"
40
- : input.type === "file"
41
- ? "file"
34
+ : input.type === "asset"
35
+ ? "url"
42
36
  : "text";
43
37
  if (input.type === "choice") {
44
38
  const options = (input.options ?? [])
45
- .map((option) => `<option value="${escapeHtml(option)}">${escapeHtml(option)}</option>`)
39
+ .map((option) => `<option value="${(0, core_1.escapeHtml)(option)}">${(0, core_1.escapeHtml)(option)}</option>`)
46
40
  .join("");
47
- return `<label>${escapeHtml(input.name)}<select id="${escapeHtml(input.id)}" data-mdsn-input="${escapeHtml(input.id)}" data-input-name="${escapeHtml(input.name)}" data-input-type="${escapeHtml(input.type)}"${requiredFlag}${secretFlag}${required}><option value=""></option>${options}</select></label>`;
48
- }
49
- if (input.type === "json") {
50
- return `<label>${escapeHtml(input.name)}<textarea id="${escapeHtml(input.id)}" data-mdsn-input="${escapeHtml(input.id)}" data-input-name="${escapeHtml(input.name)}" data-input-type="${escapeHtml(input.type)}"${requiredFlag}${secretFlag}${required} rows="8" spellcheck="false"></textarea></label>`;
41
+ return `<label>${(0, core_1.escapeHtml)(input.name)}<select id="${(0, core_1.escapeHtml)(input.id)}" data-mdsn-input="${(0, core_1.escapeHtml)(input.id)}" data-input-name="${(0, core_1.escapeHtml)(input.name)}" data-input-type="${(0, core_1.escapeHtml)(input.type)}"${requiredFlag}${secretFlag}${required}><option value=""></option>${options}</select></label>`;
51
42
  }
52
- return `<label>${escapeHtml(input.name)}<input id="${escapeHtml(input.id)}" type="${inputType}" data-mdsn-input="${escapeHtml(input.id)}" data-input-name="${escapeHtml(input.name)}" data-input-type="${escapeHtml(input.type)}"${requiredFlag}${secretFlag}${required} /></label>`;
43
+ return `<label>${(0, core_1.escapeHtml)(input.name)}<input id="${(0, core_1.escapeHtml)(input.id)}" type="${inputType}" data-mdsn-input="${(0, core_1.escapeHtml)(input.id)}" data-input-name="${(0, core_1.escapeHtml)(input.name)}" data-input-type="${(0, core_1.escapeHtml)(input.type)}"${requiredFlag}${secretFlag}${required} /></label>`;
53
44
  }).join("");
54
45
  return `<div class="mdsn-block-inputs">${items}</div>`;
55
46
  }
56
47
  function renderActions(block, options) {
57
- const readButtons = block.reads.map((read) => `<button type="button" data-mdsn-read="${escapeHtml(read.id)}" data-target="${escapeHtml(options?.mapActionTarget?.(read.target) ?? read.target)}">${escapeHtml(read.name)}</button>`);
58
- const writeButtons = block.writes.map((write) => `<button type="button" data-mdsn-write="${escapeHtml(write.id)}" data-target="${escapeHtml(options?.mapActionTarget?.(write.target) ?? write.target)}">${escapeHtml(write.name)}</button>`);
59
- const redirectButtons = block.redirects.map((redirect) => `<button type="button" data-mdsn-redirect="${escapeHtml(redirect.id)}" data-target="${escapeHtml(options?.mapActionTarget?.(redirect.target) ?? redirect.target)}">${escapeHtml(`redirect ${redirect.target}`)}</button>`);
60
- const buttons = [...readButtons, ...writeButtons, ...redirectButtons].join("");
48
+ const readButtons = block.reads
49
+ .filter((read) => read.name && !(0, block_1.isStreamAccept)(read.accept))
50
+ .map((read) => `<button type="button" data-mdsn-read="${(0, core_1.escapeHtml)(read.id)}" data-op-name="${(0, core_1.escapeHtml)(read.name)}" data-target="${(0, core_1.escapeHtml)(options?.mapActionTarget?.(read.target) ?? read.target)}" data-inputs="${(0, core_1.escapeHtml)(read.inputs.join(","))}">${(0, core_1.escapeHtml)(read.name)}</button>`);
51
+ const writeButtons = block.writes.map((write) => `<button type="button" data-mdsn-write="${(0, core_1.escapeHtml)(write.id)}" data-op-name="${(0, core_1.escapeHtml)(write.name)}" data-target="${(0, core_1.escapeHtml)(options?.mapActionTarget?.(write.target) ?? write.target)}" data-inputs="${(0, core_1.escapeHtml)(write.inputs.join(","))}">${(0, core_1.escapeHtml)(write.name)}</button>`);
52
+ const buttons = [...readButtons, ...writeButtons].join("");
61
53
  return buttons.length > 0 ? `<div class="mdsn-block-actions">${buttons}</div>` : "";
62
54
  }
63
55
  function renderBlockPanelContent(block, options) {
64
- return `<section class="mdsn-block-panel" data-mdsn-block-panel="${escapeHtml(block.name)}"><header><strong>${escapeHtml(block.name)}</strong></header>${renderInput(block)}${renderActions(block, options)}</section>`;
56
+ return `<section class="mdsn-block-panel" data-mdsn-block-panel="${(0, core_1.escapeHtml)(block.name)}"><header><strong>${(0, core_1.escapeHtml)(block.name)}</strong></header>${renderInput(block)}${renderActions(block, options)}</section>`;
65
57
  }
66
58
  function renderBlockPanelHtml(block, options, mode = "region") {
67
59
  const content = renderBlockPanelContent(block, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdsnai/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "SDK, framework host, parser, renderer, and CLI for building MDSN sites.",
6
6
  "license": "MIT",
@@ -1,4 +0,0 @@
1
- import type { ActionResult, FragmentActionSuccess, RedirectActionSuccess } from "./types";
2
- export declare function fragmentActionResult(markdown: string): FragmentActionSuccess;
3
- export declare function redirectActionResult(location: string): RedirectActionSuccess;
4
- export declare function normalizeActionResult(value: unknown): ActionResult;
@@ -1,57 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fragmentActionResult = fragmentActionResult;
4
- exports.redirectActionResult = redirectActionResult;
5
- exports.normalizeActionResult = normalizeActionResult;
6
- function isFailure(value) {
7
- return !!value && typeof value === "object" && value.ok === false;
8
- }
9
- function isFragmentSuccess(value) {
10
- return !!value
11
- && typeof value === "object"
12
- && value.ok === true
13
- && value.kind === "fragment"
14
- && typeof value.markdown === "string";
15
- }
16
- function isRedirectSuccess(value) {
17
- return !!value
18
- && typeof value === "object"
19
- && value.ok === true
20
- && value.kind === "redirect"
21
- && typeof value.location === "string";
22
- }
23
- function fragmentActionResult(markdown) {
24
- return {
25
- ok: true,
26
- kind: "fragment",
27
- markdown,
28
- };
29
- }
30
- function redirectActionResult(location) {
31
- return {
32
- ok: true,
33
- kind: "redirect",
34
- location,
35
- };
36
- }
37
- function normalizeActionResult(value) {
38
- if (typeof value === "string") {
39
- return fragmentActionResult(value);
40
- }
41
- if (isFailure(value) || isFragmentSuccess(value) || isRedirectSuccess(value)) {
42
- return value;
43
- }
44
- if (value
45
- && typeof value === "object"
46
- && value.ok === true
47
- && value.kind === "fragment") {
48
- throw new Error("Invalid fragment action result");
49
- }
50
- if (value
51
- && typeof value === "object"
52
- && value.ok === true
53
- && value.kind === "redirect") {
54
- throw new Error("Invalid redirect action result");
55
- }
56
- throw new Error("Invalid action result");
57
- }
@@ -1,2 +0,0 @@
1
- export { fragmentActionResult, normalizeActionResult, redirectActionResult, } from "./execution";
2
- export type { ActionFailure, ActionFieldErrors, ActionResult, ActionSuccess, FragmentActionSuccess, RedirectActionSuccess, } from "./types";