@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.
- 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 +3 -17
- package/dist/core/protocol/statements.d.ts +3 -8
- package/dist/core/protocol/statements.js +46 -71
- 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 +13 -13
- package/dist/server/markdown.d.ts +2 -6
- package/dist/server/markdown.js +11 -10
- 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
|
@@ -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(/^
|
|
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
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
const
|
|
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
|
|
177
|
-
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
|
|
193
|
-
throw new Error("
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
|
208
|
-
const
|
|
209
|
-
const
|
|
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
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
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
|
-
}
|
package/dist/web/page-html.js
CHANGED
|
@@ -2,14 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.renderPageHtmlContent = renderPageHtmlContent;
|
|
4
4
|
exports.renderPageHtmlDocument = renderPageHtmlDocument;
|
|
5
|
-
|
|
6
|
-
return value
|
|
7
|
-
.replace(/&/g, "&")
|
|
8
|
-
.replace(/</g, "<")
|
|
9
|
-
.replace(/>/g, ">")
|
|
10
|
-
.replace(/"/g, """)
|
|
11
|
-
.replace(/'/g, "'");
|
|
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
|
|
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";
|
package/dist/web/page-render.js
CHANGED
|
@@ -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, "&")
|
|
15
|
-
.replace(/</g, "<")
|
|
16
|
-
.replace(/>/g, ">")
|
|
17
|
-
.replace(/"/g, """)
|
|
18
|
-
.replace(/'/g, "'");
|
|
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 === "
|
|
41
|
-
? "
|
|
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
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
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,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
|
-
}
|