mdld-parse 0.2.2 → 0.2.4

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.
Files changed (4) hide show
  1. package/LICENCE +167 -0
  2. package/README.md +341 -190
  3. package/index.js +722 -284
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,300 +1,451 @@
1
- # MD-LD Parse
1
+ # MD-LD Parse v0.2
2
2
 
3
- **Markdown-Linked Data (MD-LD)** — a human-friendly RDF authoring format that extends Markdown with semantic annotations.
3
+ **Markdown-Linked Data (MD-LD)** — a deterministic, streaming-friendly RDF authoring format that extends Markdown with explicit `{}` annotations.
4
4
 
5
- [NPM](https://www.npmjs.com/package/mdld-parse)
5
+ [![NPM](https://img.shields.io/npm/v/mdld-parse)](https://www.npmjs.com/package/mdld-parse)
6
+ [![License](https://img.shields.io/npm/l/mdld-parse)](https://github.com/mdld-js/mdld-parse)
6
7
 
7
- [Website](https://mdld.js.org)
8
+ [Documentation](https://mdld.js.org) | [Specification](https://mdld.js.org/spec) | [Playground](https://mdld.js.org/playground)
8
9
 
9
10
  ## What is MD-LD?
10
11
 
11
- MD-LD allows you to author RDF graphs directly in Markdown using familiar syntax:
12
+ MD-LD allows you to author RDF graphs directly in Markdown using explicit `{}` annotations:
12
13
 
13
14
  ```markdown
14
- # My Note {=urn:mdld:my-note-20251231 .NoteDigitalDocument}
15
+ # Apollo 11 {=ex:apollo11 .SpaceMission}
15
16
 
16
- [ex]{: http://example.org/}
17
+ Launch: [1969-07-16] {startDate ^^xsd:date}
18
+ Crew: [Neil Armstrong](ex:armstrong) {?crewMember}
19
+ Description: [First crewed Moon landing] {description}
20
+ ```
21
+
22
+ Generates valid RDF triples:
23
+
24
+ ```turtle
25
+ ex:apollo11 a schema:SpaceMission ;
26
+ schema:startDate "1969-07-16"^^xsd:date ;
27
+ schema:crewMember ex:armstrong ;
28
+ schema:description "First crewed Moon landing" .
29
+ ```
17
30
 
18
- Written by [Alice Johnson](=ex:alice){author .Person}
31
+ ## Core Guarantees
19
32
 
20
- ## Alice's biography {=ex:alice}
33
+ MD-LD v0.2 provides strict semantic guarantees:
21
34
 
22
- [Alice](ex:alice){name} works at [Tech Corp](=ex:tech-corp){worksFor .Organization}
35
+ 1. **CommonMark-preserving** Removing `{}` yields valid Markdown
36
+ 2. **Explicit semantics** — Every quad originates from explicit `{}`
37
+ 3. **Single-pass parsing** — Streaming-friendly, deterministic
38
+ 4. **No blank nodes** — All subjects are stable IRIs
39
+ 5. **Complete traceability** — Every quad maps to source location
40
+ 6. **Round-trip capable** — Markdown ↔ RDF ↔ Markdown preserves structure
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ npm install mdld-parse
23
46
  ```
24
47
 
25
- This generates valid RDF triples while remaining readable as plain Markdown.
48
+ ### Node.js
49
+
50
+ ```javascript
51
+ import { parse } from 'mdld-parse';
52
+
53
+ const markdown = `# Document {=ex:doc .Article}
54
+
55
+ [Alice] {author}`;
56
+
57
+ const result = parse(markdown, {
58
+ context: { ex: 'http://example.org/' }
59
+ });
26
60
 
27
- ```n-quads
28
- <urn:mdld:my-note-20251231> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/NoteDigitalDocument> .
29
- <urn:mdld:my-note-20251231> <http://schema.org/author> <http://example.org/alice> .
30
- <http://example.org/alice> <http://schema.org/name> "Alice" .
31
- <http://example.org/alice> <http://schema.org/worksFor> <http://example.org/tech-corp> .
61
+ console.log(result.quads);
62
+ // RDF/JS quads ready for n3.js, rdflib, etc.
32
63
  ```
33
64
 
34
- ## Architecture
65
+ ### Browser (ES Modules)
35
66
 
36
- ### Design Principles
67
+ ```html
68
+ <script type="module">
69
+ import { parse } from 'https://cdn.jsdelivr.net/npm/mdld-parse/+esm';
70
+
71
+ const result = parse('# Hello {=ex:hello}');
72
+ </script>
73
+ ```
37
74
 
38
- 1. **Streaming First** — Process documents incrementally without loading entire AST into memory
39
- 2. **Zero Dependencies** — Pure JavaScript, runs in Node.js and browsers
40
- 3. **Standards Compliant** — Outputs RDF quads compatible with RDFa semantics
41
- 4. **Markdown Native** — Plain Markdown yields minimal but valid RDF
42
- 5. **Progressive Enhancement** — Add semantics incrementally via attributes
43
- 6. **BaseIRI Inference** — Automatically infers baseIRI from document structure
44
- 7. **Default Vocabulary** — Provides default vocabulary for common properties, extensible via options
75
+ ## Semantic Model
45
76
 
46
- ### Stack Choices
77
+ MD-LD encodes a directed labeled multigraph where three nodes may be in scope:
47
78
 
48
- #### Parser: Custom Zero-Dependency Tokenizer
79
+ - **S** current subject (IRI)
80
+ - **O** — object resource (IRI from link/image)
81
+ - **L** — literal value (string + optional datatype/language)
49
82
 
50
- We implement a **minimal, purpose-built parser** for maximum control and zero dependencies:
83
+ ### Predicate Routing (§8.1)
51
84
 
52
- - **Custom Markdown tokenizer** Line-by-line parsing of headings, lists, paragraphs, code blocks
53
- - **Inline attribute parser** — Pandoc-style `{=iri .class key="value"}` attribute extraction
54
- - **RDF quad generator** — Direct mapping from tokens to RDF/JS quads
85
+ Each predicate form determines the graph edge:
55
86
 
56
- **Why custom?**
87
+ | Form | Edge | Example | Meaning |
88
+ |-------|---------|------------------------------|------------------|
89
+ | `p` | S → L | `[Alice] {name}` | literal property |
90
+ | `?p` | S → O | `[NASA](ex:nasa) {?org}` | object property |
91
+ | `^p` | *(none)*| *(literals can't be subjects)* | reverse literal |
92
+ | `^?p` | O → S | `[Parent](ex:p) {^?hasPart}` | reverse object |
57
93
 
58
- - **Zero dependencies** — Runs anywhere JavaScript runs
59
- - **Lightweight** — ~15KB minified, no AST overhead
60
- - **Focused** — Optimized specifically for MD-LD semantics
61
- - **Transparent** — Easy to understand and extend
62
- - **Fast** — Single-pass parsing with minimal allocations
94
+ ## Syntax Reference
63
95
 
64
- #### RDF Output: RDF/JS Data Model
96
+ ### Subject Declaration
65
97
 
66
- We implement the [RDF/JS specification](https://rdf.js.org/data-model-spec/):
98
+ Set the current subject (emits no quads):
67
99
 
68
- ```javascript
69
- {
70
- termType: 'NamedNode' | 'BlankNode' | 'Literal',
71
- value: string,
72
- language?: string,
73
- datatype?: NamedNode
74
- }
100
+ ```markdown
101
+ ## Apollo 11 {=ex:apollo11}
75
102
  ```
76
103
 
77
- This ensures compatibility with:
104
+ Subject remains in scope until reset with `{=}` or new subject declared.
78
105
 
79
- - `n3.js` — Turtle/N-Triples serialization
80
- - `rdflib.js` — RDF store and reasoning
81
- - `sparqljs` — SPARQL query parsing
82
- - `rdf-ext` — Extended RDF utilities
106
+ ### Type Declaration
83
107
 
84
- ### Processing Pipeline
108
+ Emit `rdf:type` triple:
85
109
 
110
+ ```markdown
111
+ ## Apollo 11 {=ex:apollo11 .SpaceMission .Event}
86
112
  ```
87
- Markdown Text
88
-
89
- [Custom Tokenizer] — Extract headings, lists, paragraphs, code blocks
90
-
91
- [Attribute Parser] — Parse {=iri .class key="value"} from tokens
92
-
93
- [Inline Parser] — Extract [text](url){attrs} spans
94
-
95
- [RDF Quad Generator] — Map tokens to RDF/JS quads
96
-
97
- RDF Quads (RDF/JS format)
98
-
99
- [Optional] n3.js Writer → Turtle/N-Triples
113
+
114
+ ```turtle
115
+ ex:apollo11 a schema:SpaceMission, schema:Event .
100
116
  ```
101
117
 
102
- ### Architecture Benefits
118
+ ### Literal Properties
103
119
 
104
- The zero-dependency design provides:
120
+ Inline value carriers emit literal properties:
105
121
 
106
- 1. **Single-pass parsing** — Process document once, emit quads immediately
107
- 2. **Minimal memory** — No AST construction, only token stream
108
- 3. **Predictable performance** — Linear time complexity, bounded memory
109
- 4. **Easy integration** — Works in Node.js, browsers, and edge runtimes
122
+ ```markdown
123
+ # Mission {=ex:apollo11}
110
124
 
111
- ## Installation
125
+ [Neil Armstrong] {commander}
126
+ [1969] {year ^^xsd:gYear}
127
+ [Historic mission] {description @en}
128
+ ```
112
129
 
113
- ### Node.js
130
+ ```turtle
131
+ ex:apollo11 schema:commander "Neil Armstrong" ;
132
+ schema:year "1969"^^xsd:gYear ;
133
+ schema:description "Historic mission"@en .
134
+ ```
114
135
 
115
- ```bash
116
- npm install mdld-parse
136
+ ### Object Properties
137
+
138
+ Links create relationships (use `?` prefix):
139
+
140
+ ```markdown
141
+ # Mission {=ex:apollo11}
142
+
143
+ [NASA](ex:nasa) {?organizer}
117
144
  ```
118
145
 
119
- ```javascript
120
- import { parse } from "mdld-parse";
146
+ ```turtle
147
+ ex:apollo11 schema:organizer ex:nasa .
148
+ ```
149
+
150
+ ### Resource Declaration
151
+
152
+ Declare resources inline with `{=iri}`:
153
+
154
+ ```markdown
155
+ # Mission {=ex:apollo11}
121
156
 
122
- const markdown = `# Hello {=urn:mdld:hello .Article}`;
123
- const result = parse(markdown);
124
- const quads = result.quads;
157
+ [Neil Armstrong] {=ex:armstrong ?commander .Person}
125
158
  ```
126
159
 
127
- ### Browser (via CDN)
160
+ ```turtle
161
+ ex:apollo11 schema:commander ex:armstrong .
162
+ ex:armstrong a schema:Person .
163
+ ```
128
164
 
129
- ```html
130
- <script type="importmap">
131
- {
132
- "imports": {
133
- "mdld-parse": "https://cdn.jsdelivr.net/npm/mdld-parse/+esm"
134
- }
135
- }
136
- </script>
165
+ ### Lists
137
166
 
138
- <script type="module">
139
- import { parse } from "mdld-parse";
140
- // use parse...
141
- </script>
167
+ Lists require explicit subjects per item:
168
+
169
+ ```markdown
170
+ # Recipe {=ex:recipe}
171
+
172
+ Ingredients: {?ingredient .Ingredient}
173
+
174
+ - Flour {=ex:flour name}
175
+ - Water {=ex:water name}
176
+ ```
177
+
178
+ ```turtle
179
+ ex:recipe schema:ingredient ex:flour, ex:water .
180
+ ex:flour a schema:Ingredient ; schema:name "Flour" .
181
+ ex:water a schema:Ingredient ; schema:name "Water" .
142
182
  ```
143
183
 
144
- ## API
184
+ ### Code Blocks
185
+
186
+ Code blocks are value carriers:
187
+
188
+ ````markdown
189
+ # Example {=ex:example}
190
+
191
+ ```javascript {=ex:code .SoftwareSourceCode text}
192
+ console.log("hello");
193
+ ```
194
+ ````
195
+
196
+ ```turtle
197
+ ex:code a schema:SoftwareSourceCode ;
198
+ schema:text "console.log(\"hello\")" .
199
+ ```
200
+
201
+ ### Blockquotes
202
+
203
+ ```markdown
204
+ # Article {=ex:article}
205
+
206
+ > MD-LD bridges Markdown and RDF. {abstract}
207
+ ```
208
+
209
+ ```turtle
210
+ ex:article schema:abstract "MD-LD bridges Markdown and RDF." .
211
+ ```
212
+
213
+ ### Reverse Relations
214
+
215
+ Reverse the relationship direction:
216
+
217
+ ```markdown
218
+ # Part {=ex:part}
219
+
220
+ Part of: {^?hasPart}
221
+
222
+ - Book {=ex:book}
223
+ ```
224
+
225
+ ```turtle
226
+ ex:book schema:hasPart ex:part .
227
+ ```
228
+
229
+ ### Prefix Declarations
230
+
231
+ ```markdown
232
+ [ex] {: http://example.org/}
233
+ [foaf] {: http://xmlns.com/foaf/0.1/}
234
+
235
+ # Person {=ex:alice .foaf:Person}
236
+ ```
237
+
238
+ ## API Reference
145
239
 
146
240
  ### `parse(markdown, options)`
147
241
 
148
- Parse MD-LD markdown and return parsing result.
242
+ Parse MD-LD markdown and return RDF quads with origin tracking.
149
243
 
150
244
  **Parameters:**
151
245
 
152
246
  - `markdown` (string) — MD-LD formatted text
153
247
  - `options` (object, optional):
154
- - `baseIRI` (string) — Base IRI for relative references
155
- - `context` (object) — Additional context to merge with default context
156
- - `dataFactory` (object) — Custom RDF/JS DataFactory (default: built-in)
248
+ - `context` (object) — Prefix mappings (default: `{ '@vocab': 'http://schema.org/', rdf, rdfs, xsd, schema }`)
249
+ - `dataFactory` (object) — Custom RDF/JS DataFactory
250
+
251
+ **Returns:** `{ quads, origin, context }`
157
252
 
158
- **Returns:** Object containing:
159
253
  - `quads` — Array of RDF/JS Quads
160
- - `origin` — Object with `blocks` and `quadIndex` for serialization
161
- - `context` — Final context used for parsing
254
+ - `origin` — Origin tracking object with:
255
+ - `blocks` — Map of block IDs to source locations
256
+ - `quadIndex` — Map of quads to block IDs
257
+ - `context` — Final context used (includes prefixes)
258
+
259
+ **Example:**
260
+
261
+ ```javascript
262
+ const result = parse(
263
+ `# Article {=ex:article .Article}
264
+
265
+ [Alice](ex:alice) {?author}`,
266
+ { context: { ex: 'http://example.org/' } }
267
+ );
268
+
269
+ console.log(result.quads);
270
+ // [
271
+ // {
272
+ // subject: { termType: 'NamedNode', value: 'http://example.org/article' },
273
+ // predicate: { termType: 'NamedNode', value: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' },
274
+ // object: { termType: 'NamedNode', value: 'http://schema.org/Article' }
275
+ // },
276
+ // ...
277
+ // ]
278
+ ```
162
279
 
163
280
  ### `serialize({ text, diff, origin, options })`
164
281
 
165
- Serialize RDF changes back to markdown with proper positioning.
282
+ Apply RDF changes back to markdown with proper positioning.
166
283
 
167
284
  **Parameters:**
168
285
 
169
- - `text` (string) — Original markdown text
286
+ - `text` (string) — Original markdown
170
287
  - `diff` (object) — Changes to apply:
171
- - `add` — Array of quads to add
172
- - `delete` — Array of quads to remove
173
- - `origin` (object) — Origin object from parse result
174
- - `options` (object, optional) — Additional options:
175
- - `context` (object) — Context for IRI shortening (default: empty object)
288
+ - `add` (array) Quads to add
289
+ - `delete` (array) Quads to remove
290
+ - `origin` (object) — Origin from `parse()` result
291
+ - `options` (object, optional):
292
+ - `context` (object) — Context for IRI shortening
293
+
294
+ **Returns:** `{ text, origin }`
176
295
 
177
- **Returns:** Object containing:
178
- - `text` — Updated markdown text
179
- - `origin` — Updated origin object
296
+ - `text` — Updated markdown
297
+ - `origin` — Updated origin tracking
298
+
299
+ **Example:**
180
300
 
181
301
  ```javascript
182
- const result = parse(
183
- `
184
- # Article Title {=ex:article .Article}
185
-
186
- Written by [Alice](ex:alice) {ex:author}
187
- `,
188
- {
189
- baseIRI: "http://example.org/doc",
190
- context: {
191
- '@vocab': 'http://schema.org/',
192
- },
193
- }
194
- );
302
+ const original = `# Article {=ex:article}
303
+
304
+ [Alice] {author}`;
195
305
 
196
- // result.quads[0] = {
197
- // subject: { termType: 'NamedNode', value: 'http://example.org/doc#article' },
198
- // predicate: { termType: 'NamedNode', value: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' },
199
- // object: { termType: 'NamedNode', value: 'http://schema.org/Article' },
200
- // graph: { termType: 'DefaultGraph' }
201
- // }
306
+ const result = parse(original, { context: { ex: 'http://example.org/' } });
202
307
 
203
- // Add a new quad with proper IRI shortening
308
+ // Add a new property
204
309
  const newQuad = {
205
- subject: { termType: 'NamedNode', value: 'http://example.org/doc#article' },
206
- predicate: { termType: 'NamedNode', value: 'http://schema.org/dateCreated' },
207
- object: { termType: 'Literal', value: '2024-01-01' }
310
+ subject: { termType: 'NamedNode', value: 'http://example.org/article' },
311
+ predicate: { termType: 'NamedNode', value: 'http://schema.org/datePublished' },
312
+ object: { termType: 'Literal', value: '2024-01-01' }
208
313
  };
209
314
 
210
- const serialized = serialize({
211
- text: originalText,
212
- diff: { add: [newQuad] },
213
- origin: result.origin,
214
- options: { context: result.context } // Important: pass context for IRI shortening
315
+ const updated = serialize({
316
+ text: original,
317
+ diff: { add: [newQuad] },
318
+ origin: result.origin,
319
+ options: { context: result.context }
215
320
  });
216
321
 
217
- // Result: [2024-01-01] {dateCreated} // Properly shortened!
322
+ console.log(updated.text);
323
+ // # Article {=ex:article}
324
+ //
325
+ // [Alice] {author}
326
+ // [2024-01-01] {datePublished}
218
327
  ```
219
328
 
220
- ## Implementation Details
329
+ ## Value Carriers
221
330
 
222
- ### Subject Resolution
331
+ Only specific markdown elements can carry semantic values:
223
332
 
224
- MD-LD follows a clear subject inheritance model:
333
+ **Inline:**
334
+ - `[text] {...}` — span with annotation
335
+ - `[text](url) {...}` — link to external resource
336
+ - `[text] {...}` — inline resource declaration
337
+ - `![alt text](image.png) {...}` — embedding with annotation
225
338
 
226
- 1. **Root subject** — Declared in the first heading of the document or inferred it's text content
227
- 2. **Heading subjects** — `## Title {=ex:title .Type}`
228
- 3. **Inline subjects** `[text](=ex:text) {.Type}`
229
- 4. **Blank nodes** — Generated for incomplete triples
339
+ **Block:**
340
+ - Headings (`# Title`)
341
+ - List items (`- item`, `1. item`) (single-level)
342
+ - Blockquotes (`> quote`)
343
+ - Code blocks (` ```lang `)
230
344
 
231
- ```markdown
232
- # Document {=urn:mdld:doc .Article}
345
+ ## Architecture
233
346
 
234
- ## Section 1 {=urn:mdld:sec1 .Section}
347
+ ### Design Principles
235
348
 
236
- [Text] {name} property of sec1
349
+ - **Zero dependencies** Pure JavaScript, ~15KB minified
350
+ - **Streaming-first** — Single-pass parsing, O(n) complexity
351
+ - **Standards-compliant** — RDF/JS data model
352
+ - **Origin tracking** — Full round-trip support with source maps
353
+ - **Explicit semantics** — No guessing, inference, or heuristics
237
354
 
238
- Back to [doc](=urn:mdld:doc) {hasPart}
239
- ```
355
+ ### RDF/JS Compatibility
240
356
 
241
- ### List Handling
357
+ Quads are compatible with:
242
358
 
243
- ```markdown {item}
244
- - Item 1
245
- - Item 2
246
- ```
359
+ - [`n3.js`](https://github.com/rdfjs/N3.js) — Turtle/N-Triples serialization
360
+ - [`rdflib.js`](https://github.com/linkeddata/rdflib.js) — RDF store and reasoning
361
+ - [`sparqljs`](https://github.com/RubenVerborgh/SPARQL.js) — SPARQL queries
362
+ - [`rdf-ext`](https://github.com/rdf-ext/rdf-ext) — Extended RDF utilities
247
363
 
248
- Creates **multiple triples** with same predicate (not RDF lists):
364
+ ## Forbidden Constructs
249
365
 
250
- ```turtle
251
- <subject> schema:item "Item 1" .
252
- <subject> schema:item "Item 2" .
253
- ```
366
+ MD-LD explicitly forbids to ensure deterministic parsing:
367
+
368
+ - Implicit semantics or structural inference
369
+ - ❌ Auto-generated subjects or blank nodes
370
+ - ❌ Predicate guessing from context
371
+ - ❌ Multi-pass or backtracking parsers
254
372
 
255
- ### Code Block Semantics
373
+ ## Use Cases
374
+
375
+ ### Personal Knowledge Management
256
376
 
257
377
  ```markdown
258
- \`\`\`sparql {=ex:query-1 .SoftwareSourceCode}
259
- SELECT \* WHERE { ?s ?p ?o }
260
- \`\`\`
261
- ```
378
+ # Meeting Notes {=urn:note:2024-01-15 .Meeting}
262
379
 
263
- Creates:
380
+ Attendees: {?attendee}
264
381
 
265
- - A `schema:SoftwareSourceCode` resource (or custom type via `typeof`)
266
- - `schema:programmingLanguage` from the info string (`sparql`)
267
- - `schema:text` with the raw source code
268
- - `schema:hasPart` link from the surrounding section
382
+ - Alice {=urn:person:alice name}
383
+ - Bob {=urn:person:bob name}
269
384
 
270
- This enables semantic queries like "find all SPARQL queries in my notes."
385
+ Action items: {?actionItem}
271
386
 
272
- ## Syntax Overview
387
+ - Review proposal {=urn:task:1 name}
388
+ ```
273
389
 
274
- ### Core Features
390
+ ### Developer Documentation
275
391
 
276
- **Subject Declaration** — Headings create typed subjects:
392
+ ````markdown
393
+ # API Endpoint {=api:/users/:id .APIEndpoint}
277
394
 
278
- ```markdown
279
- ## Alice Johnson {=ex:alice .Person}
280
- ```
395
+ [GET] {method}
396
+ [/users/:id] {path}
281
397
 
282
- **Literal Properties** — Inline spans create properties:
398
+ Example:
283
399
 
284
- ```markdown
285
- [Alice Johnson] {name}
286
- [30] {age ^^xsd:integer}
400
+ ```bash {=api:/users/:id#example .CodeExample text}
401
+ curl https://api.example.com/users/123
287
402
  ```
403
+ ````
288
404
 
289
- **Object Properties** — Links create relationships:
405
+ ### Academic Research
290
406
 
291
407
  ```markdown
292
- [Tech Corp](=ex:company) {worksFor}
408
+ # Paper {=doi:10.1234/example .ScholarlyArticle}
409
+
410
+ [Semantic Web] {about}
411
+ [Alice Johnson] {=orcid:0000-0001-2345-6789 author}
412
+ [2024-01] {datePublished ^^xsd:gYearMonth}
413
+
414
+ > This paper explores semantic markup in Markdown. {abstract @en}
293
415
  ```
294
416
 
295
- **Lists** — Repeated properties:
417
+ ## Testing
296
418
 
297
- ```markdown {tag}
298
- - Item 1
299
- - Item 2
419
+ The parser includes comprehensive tests covering all spec requirements:
420
+
421
+ ```bash
422
+ npm test
300
423
  ```
424
+
425
+ Tests validate:
426
+ - Subject declaration and context
427
+ - All predicate forms (p, ?p, ^p, ^?p)
428
+ - Datatypes and language tags
429
+ - List processing
430
+ - Code blocks and blockquotes
431
+ - Round-trip serialization
432
+
433
+ ## Contributing
434
+
435
+ Contributions welcome! Please:
436
+
437
+ 1. Read the [specification](https://mdld.js.org/spec)
438
+ 2. Add tests for new features
439
+ 3. Ensure all tests pass
440
+ 4. Follow existing code style
441
+
442
+ ## Acknowledgments
443
+
444
+ Inspired by:
445
+ - Thomas Francart's [Semantic Markdown](https://blog.sparna.fr/2020/02/20/semantic-markdown/)
446
+ - RDFa decades of structured data experience
447
+ - CommonMark's rigorous parsing approach
448
+
449
+ ## License
450
+
451
+ See [LICENCE](./LICENCE)