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 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
@@ -1,3 +1,4 @@
1
1
  export * from "./types.js";
2
2
  export { parseFlexMd } from "./parser.js";
3
3
  export { stringifyFlexMd } from "./stringify.js";
4
+ export { validateFlexMd } from "./validator.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./types.js";
2
2
  export { parseFlexMd } from "./parser.js";
3
3
  export { stringifyFlexMd } from "./stringify.js";
4
+ export { validateFlexMd } from "./validator.js";
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,2 @@
1
+ import type { FlexValidationResult } from "./types.js";
2
+ export declare function validateFlexMd(input: string): FlexValidationResult;
@@ -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.0.0",
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": ["markdown", "parser", "serialization", "llm", "prompting", "semi-structured"],
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": ["dist"],
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
+ }