flex-md 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/validator.d.ts +2 -0
- package/dist/validator.js +80 -0
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -28,9 +28,17 @@ console.log(doc.frames[0]?.type); // "message"
|
|
|
28
28
|
|
|
29
29
|
const back = stringifyFlexMd(doc, { skipEmpty: true });
|
|
30
30
|
console.log(back);
|
|
31
|
+
|
|
32
|
+
// New in v1.1.0: Validation
|
|
33
|
+
import { validateFlexMd } from "flex-md";
|
|
34
|
+
const result = validateFlexMd(md);
|
|
35
|
+
if (!result.valid) {
|
|
36
|
+
console.log("Validation errors:", result.errors);
|
|
37
|
+
}
|
|
31
38
|
```
|
|
32
39
|
|
|
33
40
|
### Notes
|
|
34
41
|
- Frames start with: `[[type key=value ...]]`
|
|
35
42
|
- Metadata lines: `@key: value`
|
|
36
43
|
- Payload binding: `@payload:name: X` then the next fenced block is payload X
|
|
44
|
+
- **Validation**: `validateFlexMd` checks for unterminated frames, dangling payload declarations, and unterminated code fences.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -47,3 +47,12 @@ export interface StringifyOptions {
|
|
|
47
47
|
*/
|
|
48
48
|
fence?: "```" | "~~~";
|
|
49
49
|
}
|
|
50
|
+
export interface FlexValidationError {
|
|
51
|
+
line: number;
|
|
52
|
+
message: string;
|
|
53
|
+
severity: "error" | "warning";
|
|
54
|
+
}
|
|
55
|
+
export interface FlexValidationResult {
|
|
56
|
+
valid: boolean;
|
|
57
|
+
errors: FlexValidationError[];
|
|
58
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const HEADER_START = "[[";
|
|
2
|
+
const HEADER_END = "]]";
|
|
3
|
+
const PAYLOAD_DECL_RE = /^@payload:name:\s*(.+)\s*$/;
|
|
4
|
+
const FENCE_RE = /^(```|~~~)/;
|
|
5
|
+
export function validateFlexMd(input) {
|
|
6
|
+
const lines = input.split("\n");
|
|
7
|
+
const errors = [];
|
|
8
|
+
let inFence = null;
|
|
9
|
+
let pendingPayloadLine = null;
|
|
10
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11
|
+
const line = lines[i];
|
|
12
|
+
const lineNum = i + 1;
|
|
13
|
+
// Check for unterminated headers
|
|
14
|
+
if (line.includes(HEADER_START) && !line.includes(HEADER_END)) {
|
|
15
|
+
// Small check: if it really looks like a header start at beginning of line
|
|
16
|
+
if (line.trim().startsWith(HEADER_START)) {
|
|
17
|
+
errors.push({
|
|
18
|
+
line: lineNum,
|
|
19
|
+
message: "Possible unterminated frame header (missing ']]')",
|
|
20
|
+
severity: "error",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Handle code fences
|
|
25
|
+
const fenceMatch = line.match(FENCE_RE);
|
|
26
|
+
if (fenceMatch) {
|
|
27
|
+
const fence = fenceMatch[1];
|
|
28
|
+
if (inFence) {
|
|
29
|
+
if (line.trimEnd() === inFence) {
|
|
30
|
+
inFence = null;
|
|
31
|
+
pendingPayloadLine = null; // Payload satisfied
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
inFence = fence;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else if (pendingPayloadLine !== null && !inFence && line.trim().length > 0) {
|
|
39
|
+
// We had a @payload:name: but the next non-empty line wasn't a fence
|
|
40
|
+
errors.push({
|
|
41
|
+
line: pendingPayloadLine,
|
|
42
|
+
message: "Payload declaration not followed by a code fence",
|
|
43
|
+
severity: "warning",
|
|
44
|
+
});
|
|
45
|
+
pendingPayloadLine = null;
|
|
46
|
+
}
|
|
47
|
+
// Check for payload declarations
|
|
48
|
+
const payloadMatch = line.match(PAYLOAD_DECL_RE);
|
|
49
|
+
if (payloadMatch) {
|
|
50
|
+
if (inFence) {
|
|
51
|
+
errors.push({
|
|
52
|
+
line: lineNum,
|
|
53
|
+
message: "Payload declaration inside a code fence",
|
|
54
|
+
severity: "warning",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
pendingPayloadLine = lineNum;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (inFence) {
|
|
63
|
+
errors.push({
|
|
64
|
+
line: lines.length,
|
|
65
|
+
message: "Unterminated code fence at end of document",
|
|
66
|
+
severity: "error",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (pendingPayloadLine !== null) {
|
|
70
|
+
errors.push({
|
|
71
|
+
line: pendingPayloadLine,
|
|
72
|
+
message: "Dangling payload declaration at end of document",
|
|
73
|
+
severity: "warning",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
valid: !errors.some((e) => e.severity === "error"),
|
|
78
|
+
errors,
|
|
79
|
+
};
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flex-md",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Parse and stringify FlexMD Frames (semi-structured Markdown) to/from JSON.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
7
|
-
"keywords": [
|
|
7
|
+
"keywords": [
|
|
8
|
+
"markdown",
|
|
9
|
+
"parser",
|
|
10
|
+
"serialization",
|
|
11
|
+
"llm",
|
|
12
|
+
"prompting",
|
|
13
|
+
"semi-structured"
|
|
14
|
+
],
|
|
8
15
|
"type": "module",
|
|
9
16
|
"main": "./dist/index.cjs",
|
|
10
17
|
"module": "./dist/index.js",
|
|
@@ -16,7 +23,9 @@
|
|
|
16
23
|
"require": "./dist/index.cjs"
|
|
17
24
|
}
|
|
18
25
|
},
|
|
19
|
-
"files": [
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
20
29
|
"scripts": {
|
|
21
30
|
"build": "tsc && node ./scripts/cjs-bridge.mjs",
|
|
22
31
|
"test": "node ./dist/index.js"
|
|
@@ -24,4 +33,4 @@
|
|
|
24
33
|
"devDependencies": {
|
|
25
34
|
"typescript": "^5.6.3"
|
|
26
35
|
}
|
|
27
|
-
}
|
|
36
|
+
}
|