mdld-parse 0.2.2 → 0.2.3

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 +345 -190
  3. package/index.js +264 -248
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,300 +1,455 @@
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" .
182
+ ```
183
+
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\")" .
142
199
  ```
143
200
 
144
- ## API
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 }`
295
+
296
+ - `text` — Updated markdown
297
+ - `origin` — Updated origin tracking
176
298
 
177
- **Returns:** Object containing:
178
- - `text` — Updated markdown text
179
- - `origin` — Updated origin object
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}
195
303
 
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
- // }
304
+ [Alice] {author}`;
202
305
 
203
- // Add a new quad with proper IRI shortening
306
+ const result = parse(original, { context: { ex: 'http://example.org/' } });
307
+
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](=iri)` — inline resource declaration
225
337
 
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
338
+ **Block:**
339
+ - Headings (`# Title`)
340
+ - List items (`- item`)
341
+ - Blockquotes (`> quote`)
342
+ - Code blocks (` ```lang `)
230
343
 
231
- ```markdown
232
- # Document {=urn:mdld:doc .Article}
344
+ **Non-carriers:**
345
+ - Plain paragraphs without `[...]`
346
+ - Images (future)
347
+ - Tables (future)
233
348
 
234
- ## Section 1 {=urn:mdld:sec1 .Section}
349
+ ## Architecture
235
350
 
236
- [Text] {name} ← property of sec1
351
+ ### Design Principles
237
352
 
238
- Back to [doc](=urn:mdld:doc) {hasPart}
239
- ```
353
+ - **Zero dependencies** — Pure JavaScript, ~15KB minified
354
+ - **Streaming-first** — Single-pass parsing, O(n) complexity
355
+ - **Standards-compliant** — RDF/JS data model
356
+ - **Origin tracking** — Full round-trip support with source maps
357
+ - **Explicit semantics** — No guessing, inference, or heuristics
240
358
 
241
- ### List Handling
359
+ ### RDF/JS Compatibility
242
360
 
243
- ```markdown {item}
244
- - Item 1
245
- - Item 2
246
- ```
361
+ Quads are compatible with:
247
362
 
248
- Creates **multiple triples** with same predicate (not RDF lists):
363
+ - [`n3.js`](https://github.com/rdfjs/N3.js) Turtle/N-Triples serialization
364
+ - [`rdflib.js`](https://github.com/linkeddata/rdflib.js) — RDF store and reasoning
365
+ - [`sparqljs`](https://github.com/RubenVerborgh/SPARQL.js) — SPARQL queries
366
+ - [`rdf-ext`](https://github.com/rdf-ext/rdf-ext) — Extended RDF utilities
249
367
 
250
- ```turtle
251
- <subject> schema:item "Item 1" .
252
- <subject> schema:item "Item 2" .
253
- ```
368
+ ## Forbidden Constructs
254
369
 
255
- ### Code Block Semantics
370
+ MD-LD explicitly forbids to ensure deterministic parsing:
256
371
 
257
- ```markdown
258
- \`\`\`sparql {=ex:query-1 .SoftwareSourceCode}
259
- SELECT \* WHERE { ?s ?p ?o }
260
- \`\`\`
261
- ```
372
+ - ❌ Implicit semantics or structural inference
373
+ - ❌ Auto-generated subjects or blank nodes
374
+ - Predicate guessing from context
375
+ - ❌ Multi-pass or backtracking parsers
262
376
 
263
- Creates:
377
+ ## Use Cases
264
378
 
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
379
+ ### Personal Knowledge Management
269
380
 
270
- This enables semantic queries like "find all SPARQL queries in my notes."
381
+ ```markdown
382
+ # Meeting Notes {=urn:note:2024-01-15 .Meeting}
271
383
 
272
- ## Syntax Overview
384
+ Attendees: {?attendee}
273
385
 
274
- ### Core Features
386
+ - [Alice](=urn:person:alice) {name}
387
+ - [Bob](=urn:person:bob) {name}
275
388
 
276
- **Subject Declaration** — Headings create typed subjects:
389
+ Action items: {?actionItem}
277
390
 
278
- ```markdown
279
- ## Alice Johnson {=ex:alice .Person}
391
+ - [Review proposal](=urn:task:1) {name}
280
392
  ```
281
393
 
282
- **Literal Properties** — Inline spans create properties:
394
+ ### Developer Documentation
283
395
 
284
- ```markdown
285
- [Alice Johnson] {name}
286
- [30] {age ^^xsd:integer}
396
+ ````markdown
397
+ # API Endpoint {=api:/users/:id .APIEndpoint}
398
+
399
+ [GET] {method}
400
+ [/users/:id] {path}
401
+
402
+ Example:
403
+
404
+ ```bash {=api:/users/:id/example .CodeExample programmingLanguage}
405
+ curl https://api.example.com/users/123
287
406
  ```
407
+ ````
288
408
 
289
- **Object Properties** — Links create relationships:
409
+ ### Academic Research
290
410
 
291
411
  ```markdown
292
- [Tech Corp](=ex:company) {worksFor}
412
+ # Paper {=doi:10.1234/example .ScholarlyArticle}
413
+
414
+ [Semantic Web] {about}
415
+ [Alice Johnson](=orcid:0000-0001-2345-6789) {?author .Person}
416
+ [2024-01] {datePublished ^^xsd:gYearMonth}
417
+
418
+ > This paper explores semantic markup in Markdown. {abstract @en}
293
419
  ```
294
420
 
295
- **Lists** — Repeated properties:
421
+ ## Testing
296
422
 
297
- ```markdown {tag}
298
- - Item 1
299
- - Item 2
423
+ The parser includes comprehensive tests covering all spec requirements:
424
+
425
+ ```bash
426
+ npm test
300
427
  ```
428
+
429
+ Tests validate:
430
+ - Subject declaration and context
431
+ - All predicate forms (p, ?p, ^p, ^?p)
432
+ - Datatypes and language tags
433
+ - List processing
434
+ - Code blocks and blockquotes
435
+ - Round-trip serialization
436
+
437
+ ## Contributing
438
+
439
+ Contributions welcome! Please:
440
+
441
+ 1. Read the [specification](https://mdld.js.org/spec)
442
+ 2. Add tests for new features
443
+ 3. Ensure all tests pass
444
+ 4. Follow existing code style
445
+
446
+ ## Acknowledgments
447
+
448
+ Inspired by:
449
+ - Thomas Francart's [Semantic Markdown](https://blog.sparna.fr/2020/02/20/semantic-markdown/)
450
+ - RDFa decades of structured data experience
451
+ - CommonMark's rigorous parsing approach
452
+
453
+ ## License
454
+
455
+ See [LICENCE](./LICENCE)