flex-md 1.1.0 → 2.0.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
@@ -1,6 +1,10 @@
1
1
  # flex-md
2
2
 
3
- Parse and stringify **FlexMD Frames**: semi-structured Markdown that is easy for humans + deterministic for machines.
3
+ Parse and stringify **FlexMD**: semi-structured Markdown with three powerful layers:
4
+
5
+ - **Layer A**: FlexMD Frames - reliable round-trip Markdown ↔ JSON
6
+ - **Layer B**: Output Format Spec (OFS) - LLM-friendly Markdown contracts
7
+ - **Layer C**: Detection & Extraction - find FlexMD in arbitrary text
4
8
 
5
9
  ## Install
6
10
 
@@ -8,7 +12,11 @@ Parse and stringify **FlexMD Frames**: semi-structured Markdown that is easy for
8
12
  npm i flex-md
9
13
  ```
10
14
 
11
- ## Usage
15
+ ## Quick Start
16
+
17
+ ### Layer A: FlexMD Frames
18
+
19
+ Parse and stringify semi-structured Markdown with frames, metadata, and payloads:
12
20
 
13
21
  ```javascript
14
22
  import { parseFlexMd, stringifyFlexMd } from "flex-md";
@@ -25,20 +33,310 @@ Hello
25
33
 
26
34
  const doc = parseFlexMd(md);
27
35
  console.log(doc.frames[0]?.type); // "message"
36
+ console.log(doc.frames[0]?.meta?.tags); // ["auth", "login"]
28
37
 
29
38
  const back = stringifyFlexMd(doc, { skipEmpty: true });
30
39
  console.log(back);
40
+ ```
31
41
 
32
- // New in v1.1.0: Validation
42
+ **Validation:**
43
+
44
+ ```javascript
33
45
  import { validateFlexMd } from "flex-md";
46
+
34
47
  const result = validateFlexMd(md);
35
48
  if (!result.valid) {
36
49
  console.log("Validation errors:", result.errors);
37
50
  }
38
51
  ```
39
52
 
40
- ### Notes
41
- - Frames start with: `[[type key=value ...]]`
42
- - Metadata lines: `@key: value`
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.
53
+ **Datatype Handling:**
54
+
55
+ ```javascript
56
+ // Infer types automatically
57
+ const doc = parseFlexMd(md, { metaTypeMode: "infer" });
58
+ // @priority: 5 → number
59
+ // @enabled: true → boolean
60
+ // @value: null → null
61
+
62
+ // Or use a schema
63
+ const doc2 = parseFlexMd(md, {
64
+ metaTypeMode: "schema",
65
+ metaSchema: {
66
+ priority: "number",
67
+ enabled: "boolean"
68
+ }
69
+ });
70
+ ```
71
+
72
+ ---
73
+
74
+ ### Layer B: Output Format Spec (OFS)
75
+
76
+ Create LLM-friendly Markdown contracts and extract structured data:
77
+
78
+ ```javascript
79
+ import {
80
+ parseOutputFormatSpec,
81
+ stringifyOutputFormatSpec,
82
+ enrichInstructions,
83
+ validateOutput,
84
+ extractOutput
85
+ } from "flex-md";
86
+
87
+ // Define an output format
88
+ const spec = {
89
+ descriptorType: "output_format_spec",
90
+ format: "markdown",
91
+ sectionOrderMatters: false,
92
+ sections: [
93
+ { name: "Short answer", kind: "prose" },
94
+ { name: "Reasoning", kind: "ordered_list" },
95
+ { name: "Assumptions", kind: "list" }
96
+ ],
97
+ tablesOptional: true,
98
+ tables: [],
99
+ emptySectionValue: "None"
100
+ };
101
+
102
+ // Generate minimal LLM instructions
103
+ const instructions = enrichInstructions(spec);
104
+ console.log(instructions);
105
+ // Output:
106
+ // Rules:
107
+ // - If a section is empty, write `None`.
108
+ // - Ordered-list sections must use numbered items (nested allowed).
109
+ // - List sections must use '-' bullets (nested allowed).
110
+
111
+ // Stringify for LLM prompt
112
+ const ofsMarkdown = stringifyOutputFormatSpec(spec);
113
+
114
+ // Later, validate LLM response
115
+ const llmResponse = `
116
+ ## Short answer
117
+ The answer is 42.
118
+
119
+ ## Reasoning
120
+ 1. First, we analyze the question
121
+ 2. Then we compute the result
122
+
123
+ ## Assumptions
124
+ - The universe is deterministic
125
+ - We have sufficient compute
126
+ `;
127
+
128
+ const validation = validateOutput(llmResponse, spec);
129
+ console.log(validation.ok); // true
130
+
131
+ // Extract structured data
132
+ const extracted = extractOutput(llmResponse, spec);
133
+ console.log(extracted.sectionsByName["Short answer"].md);
134
+ // "The answer is 42."
135
+
136
+ console.log(extracted.sectionsByName["Reasoning"].list);
137
+ // { kind: "list", ordered: true, items: [...] }
138
+ ```
139
+
140
+ ---
141
+
142
+ ### Outline & Tree Building
143
+
144
+ Build nested heading trees from any Markdown:
145
+
146
+ ```javascript
147
+ import { buildOutline, renderOutline } from "flex-md";
148
+
149
+ const md = `
150
+ # Introduction
151
+ Some intro text
152
+
153
+ ## Background
154
+ More details
155
+
156
+ ### History
157
+ Even more details
158
+
159
+ ## Methodology
160
+ Our approach
161
+ `;
162
+
163
+ const outline = buildOutline(md);
164
+ console.log(outline.nodes[0].title); // "Introduction"
165
+ console.log(outline.nodes[0].children[0].title); // "Background"
166
+ console.log(outline.nodes[0].children[0].children[0].title); // "History"
167
+
168
+ // Render back to Markdown
169
+ const rendered = renderOutline(outline);
170
+ ```
171
+
172
+ ---
173
+
174
+ ### List & Table Parsing
175
+
176
+ Parse nested lists and tables:
177
+
178
+ ```javascript
179
+ import { parseList, parsePipeTable, extractAllTables } from "flex-md";
180
+
181
+ // Parse nested lists
182
+ const listMd = `
183
+ - Item 1
184
+ - Nested 1.1
185
+ - Nested 1.2
186
+ - Item 2
187
+ `;
188
+
189
+ const list = parseList(listMd);
190
+ console.log(list.items[0].children.length); // 2
191
+
192
+ // Parse ordered lists
193
+ const orderedMd = `
194
+ 1. First step
195
+ 1. Sub-step 1.1
196
+ 2. Sub-step 1.2
197
+ 2. Second step
198
+ `;
199
+
200
+ const orderedList = parseList(orderedMd);
201
+ console.log(orderedList.ordered); // true
202
+
203
+ // Parse tables
204
+ const tableMd = `
205
+ | Name | Age |
206
+ |------|-----|
207
+ | Alice | 30 |
208
+ | Bob | 25 |
209
+ `;
210
+
211
+ const table = parsePipeTable(tableMd);
212
+ console.log(table.columns); // ["Name", "Age"]
213
+ console.log(table.rows); // [["Alice", "30"], ["Bob", "25"]]
214
+
215
+ // Extract all tables from a document
216
+ const allTables = extractAllTables(documentMd);
217
+ ```
218
+
219
+ ---
220
+
221
+ ### Layer C: Detection & Extraction
222
+
223
+ Find and parse FlexMD from arbitrary text:
224
+
225
+ ```javascript
226
+ import { detectObjects, parseAny } from "flex-md";
227
+
228
+ const mixedText = `
229
+ Some random text here.
230
+
231
+ \`\`\`flexmd
232
+ [[message role=user]]
233
+ Hello from FlexMD!
234
+ \`\`\`
235
+
236
+ More text.
237
+
238
+ \`\`\`json
239
+ {
240
+ "frames": [
241
+ {"type": "message", "body_md": "JSON FlexDocument"}
242
+ ]
243
+ }
244
+ \`\`\`
245
+ `;
246
+
247
+ // Detect all objects
248
+ const detected = detectObjects(mixedText);
249
+ console.log(detected[0].kind); // "flexmd_fence"
250
+ console.log(detected[0].confidence); // 1.0
251
+ console.log(detected[1].kind); // "flexdoc_json_fence"
252
+ console.log(detected[1].confidence); // 0.9
253
+
254
+ // Parse everything
255
+ const result = parseAny(mixedText);
256
+ console.log(result.flexDocs.length); // 2
257
+ console.log(result.remainder); // "Some random text here.\n\nMore text."
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Features
263
+
264
+ - ✅ **Layer A**: FlexMD Frames with metadata and payloads
265
+ - ✅ **Layer B**: Output Format Spec for LLM contracts
266
+ - ✅ **Layer C**: Multi-tier detection from arbitrary text
267
+ - ✅ **Nested heading trees**: Build and render outline structures
268
+ - ✅ **List parsing**: Support for nested ordered/unordered lists
269
+ - ✅ **Table parsing**: GFM pipe tables with ordered table support
270
+ - ✅ **Datatype handling**: Infer or schema-based type conversion
271
+ - ✅ **Validation**: Validate FlexMD syntax and OFS compliance
272
+ - ✅ **TypeScript**: Full type definitions included
273
+
274
+ ## Documentation
275
+
276
+ - [SPEC.md](./SPEC.md) - Complete specification with all details
277
+ - [Examples](./examples/) - More usage examples (coming soon)
278
+
279
+ ## Use Cases
280
+
281
+ ### LLM Prompting & Response Parsing
282
+
283
+ ```javascript
284
+ // 1. Define what you want from the LLM
285
+ const spec = {
286
+ descriptorType: "output_format_spec",
287
+ format: "markdown",
288
+ sectionOrderMatters: false,
289
+ sections: [
290
+ { name: "Analysis", kind: "prose" },
291
+ { name: "Steps", kind: "ordered_list" },
292
+ { name: "Risks", kind: "list" }
293
+ ],
294
+ tablesOptional: true,
295
+ tables: [],
296
+ emptySectionValue: "None"
297
+ };
298
+
299
+ // 2. Generate instructions
300
+ const instructions = enrichInstructions(spec);
301
+
302
+ // 3. Send to LLM with your prompt + instructions
303
+
304
+ // 4. Validate and extract response
305
+ const validation = validateOutput(llmResponse, spec);
306
+ const data = extractOutput(llmResponse, spec);
307
+ ```
308
+
309
+ ### Structured Conversation Storage
310
+
311
+ ```javascript
312
+ const conversation = parseFlexMd(`
313
+ [[message role=user id=msg1]]
314
+ What is the capital of France?
315
+
316
+ [[message role=assistant id=msg2]]
317
+ The capital of France is Paris.
318
+ `);
319
+
320
+ // Store as JSON, query, manipulate, then stringify back
321
+ const md = stringifyFlexMd(conversation);
322
+ ```
323
+
324
+ ### Markdown Documentation Processing
325
+
326
+ ```javascript
327
+ // Build outline from docs
328
+ const outline = buildOutline(documentationMd);
329
+
330
+ // Extract specific sections
331
+ const extracted = extractOutput(documentationMd, {
332
+ sections: [
333
+ { name: "Installation", kind: "prose" },
334
+ { name: "API Reference", kind: "list" }
335
+ ],
336
+ // ... rest of spec
337
+ });
338
+ ```
339
+
340
+ ## License
341
+
342
+ MIT