flex-md 1.0.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 +312 -6
- package/SPEC.md +559 -0
- package/dist/detection/detector.d.ts +6 -0
- package/dist/detection/detector.js +104 -0
- package/dist/detection/extractor.d.ts +10 -0
- package/dist/detection/extractor.js +54 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +17 -0
- package/dist/ofs/enricher.d.ts +6 -0
- package/dist/ofs/enricher.js +29 -0
- package/dist/ofs/extractor.d.ts +9 -0
- package/dist/ofs/extractor.js +75 -0
- package/dist/ofs/parser.d.ts +21 -0
- package/dist/ofs/parser.js +64 -0
- package/dist/ofs/stringify.d.ts +5 -0
- package/dist/ofs/stringify.js +30 -0
- package/dist/ofs/validator.d.ts +10 -0
- package/dist/ofs/validator.js +91 -0
- package/dist/outline/builder.d.ts +10 -0
- package/dist/outline/builder.js +85 -0
- package/dist/outline/renderer.d.ts +6 -0
- package/dist/outline/renderer.js +23 -0
- package/dist/parser.js +58 -10
- package/dist/parsers/lists.d.ts +6 -0
- package/dist/parsers/lists.js +36 -0
- package/dist/parsers/tables.d.ts +10 -0
- package/dist/parsers/tables.js +58 -0
- package/dist/test-runner.d.ts +1 -0
- package/dist/test-runner.js +328 -0
- package/dist/types.d.ts +91 -0
- package/dist/validator.d.ts +2 -0
- package/dist/validator.js +80 -0
- package/package.json +20 -6
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# flex-md
|
|
2
2
|
|
|
3
|
-
Parse and stringify **FlexMD
|
|
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
|
-
##
|
|
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,12 +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);
|
|
31
40
|
```
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
**Validation:**
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import { validateFlexMd } from "flex-md";
|
|
46
|
+
|
|
47
|
+
const result = validateFlexMd(md);
|
|
48
|
+
if (!result.valid) {
|
|
49
|
+
console.log("Validation errors:", result.errors);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
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
|