@mdfn/testing 0.1.0 → 0.1.1
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 +8 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +41 -9
package/README.md
CHANGED
|
@@ -3,3 +3,11 @@
|
|
|
3
3
|
Reusable preservation, rendering-security, extension, adapter-trace, official
|
|
4
4
|
CommonMark, fuzz, performance, mounted-framework, real-browser, SSR, runtime
|
|
5
5
|
export, and packed-consumer conformance helpers.
|
|
6
|
+
|
|
7
|
+
## Large-document performance budget
|
|
8
|
+
|
|
9
|
+
The large-document test builds a ~200k-character fixture and times the full
|
|
10
|
+
`parseMarkdown` → `serializeMarkdown` → `renderHtml` pipeline. Local development
|
|
11
|
+
must stay under **5s**; CI shared runners use a **20s** ceiling. Scoped GHA runs
|
|
12
|
+
finish in ~12s, but full-graph turbo CI (300+ tasks) was observed at ~12.1s on
|
|
13
|
+
PR 133, so the ceiling leaves headroom for runner contention.
|
package/dist/index.cjs
CHANGED
|
@@ -211,7 +211,7 @@ function deterministicFuzzCorpus(seed = 1296320078, cases = 100) {
|
|
|
211
211
|
return { id: `fuzz-${seed}-${index}`, source: `${lines.join(newline)}${newline}` };
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
|
-
var MDFN_TESTING_VERSION = "0.1.
|
|
214
|
+
var MDFN_TESTING_VERSION = "0.1.1";
|
|
215
215
|
// Annotate the CommonJS export names for ESM import in node:
|
|
216
216
|
0 && (module.exports = {
|
|
217
217
|
MDFN_TESTING_VERSION,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { resolveExtensions, type EditorState, type MdfnDocument, type MdfnExtension, type MdfnJsonValue, type MdfnNode } from \"@mdfn/core\";\nimport { parseMarkdown, serializeMarkdown, type MarkdownOptions } from \"@mdfn/markdown\";\nimport { renderHtml, type RenderPolicy } from \"@mdfn/render\";\nimport { parseFragment, serialize } from \"parse5\";\n\nexport interface PreservationFixture {\n readonly id: string;\n readonly source: string;\n readonly options?: MarkdownOptions;\n}\n\nexport interface ConformanceFinding {\n readonly code: string;\n readonly fixture: string;\n readonly message: string;\n}\n\nexport interface ConformanceResult {\n readonly ok: boolean;\n readonly passed: number;\n readonly failed: number;\n readonly findings: readonly ConformanceFinding[];\n}\n\nexport function runPreservationCorpus(fixtures: readonly PreservationFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const serialized = serializeMarkdown({ document: parsed.document, originalSource: fixture.source, options: fixture.options });\n if (serialized.markdown !== fixture.source) {\n findings.push({ code: \"MDFN_CONFORMANCE_NO_EDIT_CHANGED\", fixture: fixture.id, message: \"No-edit round trip changed source bytes\" });\n } else if (!serialized.preservation.exactUntouched) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXACT_NOT_REPORTED\", fixture: fixture.id, message: \"Exact preservation was not reported\" });\n } else {\n passed += 1;\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport function certifyExtension(extension: MdfnExtension, fixture: PreservationFixture): ConformanceResult {\n const configured = fixture.options?.extensions;\n const existing = configured ? (\"schemaHash\" in configured ? configured.extensions : configured) : [];\n const options = { ...(fixture.options ?? {}), extensions: [...existing, extension] };\n const findings = [...runPreservationCorpus([{ ...fixture, options }]).findings];\n let passed = findings.length === 0 ? 1 : 0;\n try {\n const registry = resolveExtensions(options.extensions);\n const parsed = parseMarkdown(fixture.source, options);\n const owns = (node: MdfnNode): boolean => extension.serializeMarkdown?.({ node }) != null || (node.content?.some(owns) ?? false);\n const dirty = (node: MdfnNode): MdfnNode => owns(node)\n ? { ...node, source: node.source ? { ...node.source, dirty: true } : undefined, content: node.content?.map(dirty) }\n : { ...node, content: node.content?.map(dirty) };\n const edited = dirty(parsed.document) as MdfnDocument;\n const ownedNodes: MdfnNode[] = [];\n const collectOwned = (node: MdfnNode): void => {\n if (extension.serializeMarkdown?.({ node }) != null) ownedNodes.push(node);\n node.content?.forEach(collectOwned);\n };\n collectOwned(parsed.document);\n if (ownedNodes.length === 0) findings.push({ code: \"MDFN_CERT_FIXTURE_NOT_OWNED\", fixture: fixture.id, message: \"Extension did not claim any syntax in its certification fixture\" });\n const serialized = serializeMarkdown({ document: edited, originalSource: fixture.source, options });\n if (!serialized.markdown) findings.push({ code: \"MDFN_CERT_EDITED_SERIALIZATION_EMPTY\", fixture: fixture.id, message: \"Edited serialization produced no source\" });\n else passed += 1;\n renderHtml(edited, { extensions: registry, rawHtml: { enabled: false } });\n passed += 1;\n if (extension.visual) {\n const visual = ownedNodes.map((node) => extension.visual?.({ node, escape: (value) => value })).filter(Boolean);\n if (visual.length === 0 || visual.some((node) => typeof node !== \"object\" || !node?.tag)) findings.push({ code: \"MDFN_CERT_VISUAL_INVALID\", fixture: fixture.id, message: \"Visual lifecycle did not produce a structured node\" });\n else passed += 1;\n }\n registry.diagnose(edited);\n passed += 1;\n for (const migration of extension.migrations ?? []) migration.migrate(parsed.document);\n passed += 1;\n const manifest = extension.certification;\n if (manifest) {\n if (manifest.schemaVersion !== 1 || !manifest.fixtures.includes(fixture.id)) findings.push({ code: \"MDFN_CERT_MANIFEST_INVALID\", fixture: fixture.id, message: \"Certification manifest does not cover the fixture\" });\n const hooks: Record<string, unknown> = { parse: extension.parseMarkdown, serialize: extension.serializeMarkdown, render: extension.render, visual: extension.visual, commands: extension.commands, keymap: extension.keymap, input: extension.inputRules, paste: extension.pasteRules, plugins: extension.plugins, diagnostics: extension.diagnostics, migrations: extension.migrations, security: extension.security };\n for (const capability of manifest.capabilities) if (!hooks[capability]) findings.push({ code: \"MDFN_CERT_CAPABILITY_MISSING\", fixture: fixture.id, message: `Manifest capability ${capability} has no hook` });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CERT_LIFECYCLE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport interface SemanticFixture extends PreservationFixture { readonly expectedHtml: string; }\n\nfunction canonicalHtml(value: string): string {\n const fragment = parseFragment(value);\n const visit = (node: unknown, preserveWhitespace = false): void => {\n if (!node || typeof node !== \"object\") return;\n const candidate = node as { nodeName?: string; value?: string; attrs?: Array<{ name: string; value: string }>; childNodes?: unknown[] };\n const preserve = preserveWhitespace || candidate.nodeName === \"pre\" || candidate.nodeName === \"code\";\n if (candidate.nodeName === \"#text\" && !preserve && typeof candidate.value === \"string\") candidate.value = candidate.value.replace(/\\s+/g, \" \");\n if (candidate.attrs) candidate.attrs = candidate.attrs.filter((attr) => ![\"target\", \"rel\", \"loading\", \"decoding\", \"href\", \"src\"].includes(attr.name)).sort((a, b) => a.name.localeCompare(b.name));\n if (candidate.childNodes) {\n candidate.childNodes = candidate.childNodes.filter((child) => {\n const text = child as { nodeName?: string; value?: string };\n return preserve || text.nodeName !== \"#text\" || Boolean(text.value?.trim());\n });\n for (let index = 0; index < candidate.childNodes.length; index += 1) {\n const child = candidate.childNodes[index] as { nodeName?: string; value?: string };\n const previous = candidate.childNodes[index - 1] as { nodeName?: string } | undefined;\n const next = candidate.childNodes[index + 1] as { nodeName?: string } | undefined;\n if (child.nodeName === \"#text\" && typeof child.value === \"string\") {\n if (previous?.nodeName === \"br\" || previous?.nodeName === \"ul\" || previous?.nodeName === \"ol\" || previous?.nodeName === \"blockquote\" || previous?.nodeName === \"pre\" || /^h[1-6]$/.test(previous?.nodeName ?? \"\")) child.value = child.value.trimStart();\n if (next?.nodeName === \"ul\" || next?.nodeName === \"ol\" || next?.nodeName === \"blockquote\" || next?.nodeName === \"pre\" || /^h[1-6]$/.test(next?.nodeName ?? \"\")) child.value = child.value.trimEnd();\n }\n }\n candidate.childNodes.forEach((child) => visit(child, preserve));\n }\n };\n visit(fragment);\n return serialize(fragment).trim();\n}\n\n/** Compare MDFN's projected semantics with authoritative expected HTML. */\nexport function runSemanticCorpus(fixtures: readonly SemanticFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const neutralizeUrls = (node: MdfnNode): MdfnNode => node.type === \"link\"\n ? { ...node, attrs: { ...node.attrs, url: \"#\" }, content: node.content?.map(neutralizeUrls) }\n : node.type === \"image\" ? { ...node, attrs: { ...node.attrs, url: \"/image\" } }\n : { ...node, content: node.content?.map(neutralizeUrls) };\n const document = neutralizeUrls(parsed.document) as MdfnDocument;\n const actual = renderHtml(document, { extensions: fixture.options?.extensions, rawHtml: { enabled: true, sanitize: (raw) => raw }, links: { externalTarget: null, externalRel: \"\" } }).html;\n if (canonicalHtml(actual) !== canonicalHtml(fixture.expectedHtml)) findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_MISMATCH\", fixture: fixture.id, message: `Expected ${canonicalHtml(fixture.expectedHtml)} but received ${canonicalHtml(actual)}` });\n else passed += 1;\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\n/** Representative examples copied from the normative GFM 0.29 test blocks. */\nexport const authoritativeGfmCorpus: readonly SemanticFixture[] = Object.freeze([\n { id: \"gfm-autolink-www\", source: \"www.commonmark.org\\n\", expectedHtml: '<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-autolink-email\", source: \"foo.bar.baz@google.com\\n\", expectedHtml: '<p><a href=\"mailto:foo.bar.baz@google.com\">foo.bar.baz@google.com</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-strikethrough\", source: \"~~Hi~~ Hello, world!\\n\", expectedHtml: \"<p><del>Hi</del> Hello, world!</p>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table\", source: \"| foo | bar |\\n| --- | --- |\\n| baz | bim |\\n\", expectedHtml: \"<table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>baz</td><td>bim</td></tr></tbody></table>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table-align\", source: \"| abc | defghi |\\n:-: | -----------:\\nbar | baz\\n\", expectedHtml: '<table><thead><tr><th align=\"center\">abc</th><th align=\"right\">defghi</th></tr></thead><tbody><tr><td align=\"center\">bar</td><td align=\"right\">baz</td></tr></tbody></table>', options: { dialect: \"gfm\" } },\n { id: \"gfm-task-list\", source: \"- [x] foo\\n- [ ] bar\\n\", expectedHtml: '<ul class=\"contains-task-list\"><li class=\"task-list-item\"><input type=\"checkbox\" disabled checked> foo</li><li class=\"task-list-item\"><input type=\"checkbox\" disabled> bar</li></ul>', options: { dialect: \"gfm\" } },\n]);\n\nexport function runSecurityCorpus(document: MdfnDocument, policy?: RenderPolicy): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n try {\n const result = renderHtml(document, policy);\n if (/<script\\b|\\son[a-z]+\\s*=|javascript:/i.test(result.html)) {\n findings.push({ code: \"MDFN_CONFORMANCE_UNSAFE_RENDER\", fixture: \"security\", message: \"Unsafe executable output was rendered\" });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_RENDER_EXCEPTION\", fixture: \"security\", message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed: findings.length === 0 ? 1 : 0, failed: findings.length, findings };\n}\n\nexport interface SemanticTraceStep {\n readonly name: string;\n readonly state: MdfnJsonValue;\n}\n\nexport interface AdapterSemanticTrace {\n readonly schemaVersion: 1;\n readonly framework: \"vanilla\" | \"react\" | \"svelte\" | \"solid\";\n readonly fixture: string;\n readonly steps: readonly SemanticTraceStep[];\n readonly finalMarkdown: string;\n readonly finalSelection: MdfnJsonValue;\n readonly diagnostics: readonly string[];\n readonly cleanup: { readonly subscriptions: number; readonly mounted: boolean };\n}\n\nexport function traceState(framework: AdapterSemanticTrace[\"framework\"], fixture: string, states: readonly EditorState[]): AdapterSemanticTrace {\n const final = states.at(-1);\n if (!final) throw new Error(\"MDFN_TRACE_STATE_MISSING\");\n return {\n schemaVersion: 1,\n framework,\n fixture,\n steps: states.map((state) => ({ name: `version:${state.version}`, state: { markdown: state.markdown, dirty: state.dirty, selection: state.selection as MdfnJsonValue, diagnostics: state.diagnostics.map((entry) => entry.code) } })),\n finalMarkdown: final.markdown,\n finalSelection: final.selection as MdfnJsonValue,\n diagnostics: final.diagnostics.map((entry) => entry.code),\n cleanup: { subscriptions: 0, mounted: false },\n };\n}\n\nexport function compareSemanticTraces(reference: AdapterSemanticTrace, traces: readonly AdapterSemanticTrace[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n for (const trace of traces) {\n const comparable = (value: AdapterSemanticTrace) => JSON.stringify({ steps: value.steps, finalMarkdown: value.finalMarkdown, finalSelection: value.finalSelection, diagnostics: value.diagnostics, cleanup: value.cleanup });\n if (comparable(trace) !== comparable(reference)) findings.push({ code: \"MDFN_ADAPTER_TRACE_DIVERGED\", fixture: trace.fixture, message: `${trace.framework} diverged from vanilla semantics` });\n }\n return { ok: findings.length === 0, passed: traces.length - findings.length, failed: findings.length, findings };\n}\n\nexport const representativeCorpus: readonly PreservationFixture[] = Object.freeze([\n { id: \"commonmark\", source: \"# Heading\\n\\nParagraph with **bold** and *emphasis*.\\n\" },\n { id: \"gfm-table\", source: \"| A | B |\\n| - | - |\\n| 1 | 2 |\\n\" },\n { id: \"unicode-crlf\", source: \"# 日本語\\r\\n\\r\\nمرحبا 👋\\r\\n\" },\n { id: \"lone-cr\", source: \"# Heading\\r\\rParagraph.\\r\" },\n { id: \"opaque-html\", source: \"<custom-element unsafe=\\\"no\\\">value</custom-element>\\n\" },\n { id: \"malformed\", source: \"```ts\\nconst value = 1;\\n\" },\n]);\n\nexport function deterministicFuzzCorpus(seed = 0x4d44464e, cases = 100): readonly PreservationFixture[] {\n let value = seed >>> 0;\n const next = (): number => {\n value = (Math.imul(value, 1664525) + 1013904223) >>> 0;\n return value;\n };\n const tokens = [\"plain\", \"**bold**\", \"_em_\", \"`code`\", \"[link](https://example.com)\", \"日本語\", \"👋\", \"<opaque-tag>\", \"\\\\*escaped\", \"~~strike~~\"];\n return Array.from({ length: Math.max(0, cases) }, (_, index) => {\n const lines = Array.from({ length: 1 + (next() % 8) }, () => {\n const prefix = [\"\", \"# \", \"- \", \"> \", \"1. \"][next() % 5];\n return `${prefix}${tokens[next() % tokens.length]} ${tokens[next() % tokens.length]}`;\n });\n const newline = next() % 3 === 0 ? \"\\r\\n\" : \"\\n\";\n return { id: `fuzz-${seed}-${index}`, source: `${lines.join(newline)}${newline}` };\n });\n}\n\nexport const MDFN_TESTING_VERSION = \"0.1.0\" as const;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { resolveExtensions, type EditorState, type MdfnDocument, type MdfnExtension, type MdfnJsonValue, type MdfnNode } from \"@mdfn/core\";\nimport { parseMarkdown, serializeMarkdown, type MarkdownOptions } from \"@mdfn/markdown\";\nimport { renderHtml, type RenderPolicy } from \"@mdfn/render\";\nimport { parseFragment, serialize } from \"parse5\";\n\nexport interface PreservationFixture {\n readonly id: string;\n readonly source: string;\n readonly options?: MarkdownOptions;\n}\n\nexport interface ConformanceFinding {\n readonly code: string;\n readonly fixture: string;\n readonly message: string;\n}\n\nexport interface ConformanceResult {\n readonly ok: boolean;\n readonly passed: number;\n readonly failed: number;\n readonly findings: readonly ConformanceFinding[];\n}\n\nexport function runPreservationCorpus(fixtures: readonly PreservationFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const serialized = serializeMarkdown({ document: parsed.document, originalSource: fixture.source, options: fixture.options });\n if (serialized.markdown !== fixture.source) {\n findings.push({ code: \"MDFN_CONFORMANCE_NO_EDIT_CHANGED\", fixture: fixture.id, message: \"No-edit round trip changed source bytes\" });\n } else if (!serialized.preservation.exactUntouched) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXACT_NOT_REPORTED\", fixture: fixture.id, message: \"Exact preservation was not reported\" });\n } else {\n passed += 1;\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport function certifyExtension(extension: MdfnExtension, fixture: PreservationFixture): ConformanceResult {\n const configured = fixture.options?.extensions;\n const existing = configured ? (\"schemaHash\" in configured ? configured.extensions : configured) : [];\n const options = { ...(fixture.options ?? {}), extensions: [...existing, extension] };\n const findings = [...runPreservationCorpus([{ ...fixture, options }]).findings];\n let passed = findings.length === 0 ? 1 : 0;\n try {\n const registry = resolveExtensions(options.extensions);\n const parsed = parseMarkdown(fixture.source, options);\n const owns = (node: MdfnNode): boolean => extension.serializeMarkdown?.({ node }) != null || (node.content?.some(owns) ?? false);\n const dirty = (node: MdfnNode): MdfnNode => owns(node)\n ? { ...node, source: node.source ? { ...node.source, dirty: true } : undefined, content: node.content?.map(dirty) }\n : { ...node, content: node.content?.map(dirty) };\n const edited = dirty(parsed.document) as MdfnDocument;\n const ownedNodes: MdfnNode[] = [];\n const collectOwned = (node: MdfnNode): void => {\n if (extension.serializeMarkdown?.({ node }) != null) ownedNodes.push(node);\n node.content?.forEach(collectOwned);\n };\n collectOwned(parsed.document);\n if (ownedNodes.length === 0) findings.push({ code: \"MDFN_CERT_FIXTURE_NOT_OWNED\", fixture: fixture.id, message: \"Extension did not claim any syntax in its certification fixture\" });\n const serialized = serializeMarkdown({ document: edited, originalSource: fixture.source, options });\n if (!serialized.markdown) findings.push({ code: \"MDFN_CERT_EDITED_SERIALIZATION_EMPTY\", fixture: fixture.id, message: \"Edited serialization produced no source\" });\n else passed += 1;\n renderHtml(edited, { extensions: registry, rawHtml: { enabled: false } });\n passed += 1;\n if (extension.visual) {\n const visual = ownedNodes.map((node) => extension.visual?.({ node, escape: (value) => value })).filter(Boolean);\n if (visual.length === 0 || visual.some((node) => typeof node !== \"object\" || !node?.tag)) findings.push({ code: \"MDFN_CERT_VISUAL_INVALID\", fixture: fixture.id, message: \"Visual lifecycle did not produce a structured node\" });\n else passed += 1;\n }\n registry.diagnose(edited);\n passed += 1;\n for (const migration of extension.migrations ?? []) migration.migrate(parsed.document);\n passed += 1;\n const manifest = extension.certification;\n if (manifest) {\n if (manifest.schemaVersion !== 1 || !manifest.fixtures.includes(fixture.id)) findings.push({ code: \"MDFN_CERT_MANIFEST_INVALID\", fixture: fixture.id, message: \"Certification manifest does not cover the fixture\" });\n const hooks: Record<string, unknown> = { parse: extension.parseMarkdown, serialize: extension.serializeMarkdown, render: extension.render, visual: extension.visual, commands: extension.commands, keymap: extension.keymap, input: extension.inputRules, paste: extension.pasteRules, plugins: extension.plugins, diagnostics: extension.diagnostics, migrations: extension.migrations, security: extension.security };\n for (const capability of manifest.capabilities) if (!hooks[capability]) findings.push({ code: \"MDFN_CERT_CAPABILITY_MISSING\", fixture: fixture.id, message: `Manifest capability ${capability} has no hook` });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CERT_LIFECYCLE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport interface SemanticFixture extends PreservationFixture { readonly expectedHtml: string; }\n\nfunction canonicalHtml(value: string): string {\n const fragment = parseFragment(value);\n const visit = (node: unknown, preserveWhitespace = false): void => {\n if (!node || typeof node !== \"object\") return;\n const candidate = node as { nodeName?: string; value?: string; attrs?: Array<{ name: string; value: string }>; childNodes?: unknown[] };\n const preserve = preserveWhitespace || candidate.nodeName === \"pre\" || candidate.nodeName === \"code\";\n if (candidate.nodeName === \"#text\" && !preserve && typeof candidate.value === \"string\") candidate.value = candidate.value.replace(/\\s+/g, \" \");\n if (candidate.attrs) candidate.attrs = candidate.attrs.filter((attr) => ![\"target\", \"rel\", \"loading\", \"decoding\", \"href\", \"src\"].includes(attr.name)).sort((a, b) => a.name.localeCompare(b.name));\n if (candidate.childNodes) {\n candidate.childNodes = candidate.childNodes.filter((child) => {\n const text = child as { nodeName?: string; value?: string };\n return preserve || text.nodeName !== \"#text\" || Boolean(text.value?.trim());\n });\n for (let index = 0; index < candidate.childNodes.length; index += 1) {\n const child = candidate.childNodes[index] as { nodeName?: string; value?: string };\n const previous = candidate.childNodes[index - 1] as { nodeName?: string } | undefined;\n const next = candidate.childNodes[index + 1] as { nodeName?: string } | undefined;\n if (child.nodeName === \"#text\" && typeof child.value === \"string\") {\n if (previous?.nodeName === \"br\" || previous?.nodeName === \"ul\" || previous?.nodeName === \"ol\" || previous?.nodeName === \"blockquote\" || previous?.nodeName === \"pre\" || /^h[1-6]$/.test(previous?.nodeName ?? \"\")) child.value = child.value.trimStart();\n if (next?.nodeName === \"ul\" || next?.nodeName === \"ol\" || next?.nodeName === \"blockquote\" || next?.nodeName === \"pre\" || /^h[1-6]$/.test(next?.nodeName ?? \"\")) child.value = child.value.trimEnd();\n }\n }\n candidate.childNodes.forEach((child) => visit(child, preserve));\n }\n };\n visit(fragment);\n return serialize(fragment).trim();\n}\n\n/** Compare MDFN's projected semantics with authoritative expected HTML. */\nexport function runSemanticCorpus(fixtures: readonly SemanticFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const neutralizeUrls = (node: MdfnNode): MdfnNode => node.type === \"link\"\n ? { ...node, attrs: { ...node.attrs, url: \"#\" }, content: node.content?.map(neutralizeUrls) }\n : node.type === \"image\" ? { ...node, attrs: { ...node.attrs, url: \"/image\" } }\n : { ...node, content: node.content?.map(neutralizeUrls) };\n const document = neutralizeUrls(parsed.document) as MdfnDocument;\n const actual = renderHtml(document, { extensions: fixture.options?.extensions, rawHtml: { enabled: true, sanitize: (raw) => raw }, links: { externalTarget: null, externalRel: \"\" } }).html;\n if (canonicalHtml(actual) !== canonicalHtml(fixture.expectedHtml)) findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_MISMATCH\", fixture: fixture.id, message: `Expected ${canonicalHtml(fixture.expectedHtml)} but received ${canonicalHtml(actual)}` });\n else passed += 1;\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\n/** Representative examples copied from the normative GFM 0.29 test blocks. */\nexport const authoritativeGfmCorpus: readonly SemanticFixture[] = Object.freeze([\n { id: \"gfm-autolink-www\", source: \"www.commonmark.org\\n\", expectedHtml: '<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-autolink-email\", source: \"foo.bar.baz@google.com\\n\", expectedHtml: '<p><a href=\"mailto:foo.bar.baz@google.com\">foo.bar.baz@google.com</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-strikethrough\", source: \"~~Hi~~ Hello, world!\\n\", expectedHtml: \"<p><del>Hi</del> Hello, world!</p>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table\", source: \"| foo | bar |\\n| --- | --- |\\n| baz | bim |\\n\", expectedHtml: \"<table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>baz</td><td>bim</td></tr></tbody></table>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table-align\", source: \"| abc | defghi |\\n:-: | -----------:\\nbar | baz\\n\", expectedHtml: '<table><thead><tr><th align=\"center\">abc</th><th align=\"right\">defghi</th></tr></thead><tbody><tr><td align=\"center\">bar</td><td align=\"right\">baz</td></tr></tbody></table>', options: { dialect: \"gfm\" } },\n { id: \"gfm-task-list\", source: \"- [x] foo\\n- [ ] bar\\n\", expectedHtml: '<ul class=\"contains-task-list\"><li class=\"task-list-item\"><input type=\"checkbox\" disabled checked> foo</li><li class=\"task-list-item\"><input type=\"checkbox\" disabled> bar</li></ul>', options: { dialect: \"gfm\" } },\n]);\n\nexport function runSecurityCorpus(document: MdfnDocument, policy?: RenderPolicy): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n try {\n const result = renderHtml(document, policy);\n if (/<script\\b|\\son[a-z]+\\s*=|javascript:/i.test(result.html)) {\n findings.push({ code: \"MDFN_CONFORMANCE_UNSAFE_RENDER\", fixture: \"security\", message: \"Unsafe executable output was rendered\" });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_RENDER_EXCEPTION\", fixture: \"security\", message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed: findings.length === 0 ? 1 : 0, failed: findings.length, findings };\n}\n\nexport interface SemanticTraceStep {\n readonly name: string;\n readonly state: MdfnJsonValue;\n}\n\nexport interface AdapterSemanticTrace {\n readonly schemaVersion: 1;\n readonly framework: \"vanilla\" | \"react\" | \"svelte\" | \"solid\";\n readonly fixture: string;\n readonly steps: readonly SemanticTraceStep[];\n readonly finalMarkdown: string;\n readonly finalSelection: MdfnJsonValue;\n readonly diagnostics: readonly string[];\n readonly cleanup: { readonly subscriptions: number; readonly mounted: boolean };\n}\n\nexport function traceState(framework: AdapterSemanticTrace[\"framework\"], fixture: string, states: readonly EditorState[]): AdapterSemanticTrace {\n const final = states.at(-1);\n if (!final) throw new Error(\"MDFN_TRACE_STATE_MISSING\");\n return {\n schemaVersion: 1,\n framework,\n fixture,\n steps: states.map((state) => ({ name: `version:${state.version}`, state: { markdown: state.markdown, dirty: state.dirty, selection: state.selection as MdfnJsonValue, diagnostics: state.diagnostics.map((entry) => entry.code) } })),\n finalMarkdown: final.markdown,\n finalSelection: final.selection as MdfnJsonValue,\n diagnostics: final.diagnostics.map((entry) => entry.code),\n cleanup: { subscriptions: 0, mounted: false },\n };\n}\n\nexport function compareSemanticTraces(reference: AdapterSemanticTrace, traces: readonly AdapterSemanticTrace[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n for (const trace of traces) {\n const comparable = (value: AdapterSemanticTrace) => JSON.stringify({ steps: value.steps, finalMarkdown: value.finalMarkdown, finalSelection: value.finalSelection, diagnostics: value.diagnostics, cleanup: value.cleanup });\n if (comparable(trace) !== comparable(reference)) findings.push({ code: \"MDFN_ADAPTER_TRACE_DIVERGED\", fixture: trace.fixture, message: `${trace.framework} diverged from vanilla semantics` });\n }\n return { ok: findings.length === 0, passed: traces.length - findings.length, failed: findings.length, findings };\n}\n\nexport const representativeCorpus: readonly PreservationFixture[] = Object.freeze([\n { id: \"commonmark\", source: \"# Heading\\n\\nParagraph with **bold** and *emphasis*.\\n\" },\n { id: \"gfm-table\", source: \"| A | B |\\n| - | - |\\n| 1 | 2 |\\n\" },\n { id: \"unicode-crlf\", source: \"# 日本語\\r\\n\\r\\nمرحبا 👋\\r\\n\" },\n { id: \"lone-cr\", source: \"# Heading\\r\\rParagraph.\\r\" },\n { id: \"opaque-html\", source: \"<custom-element unsafe=\\\"no\\\">value</custom-element>\\n\" },\n { id: \"malformed\", source: \"```ts\\nconst value = 1;\\n\" },\n]);\n\nexport function deterministicFuzzCorpus(seed = 0x4d44464e, cases = 100): readonly PreservationFixture[] {\n let value = seed >>> 0;\n const next = (): number => {\n value = (Math.imul(value, 1664525) + 1013904223) >>> 0;\n return value;\n };\n const tokens = [\"plain\", \"**bold**\", \"_em_\", \"`code`\", \"[link](https://example.com)\", \"日本語\", \"👋\", \"<opaque-tag>\", \"\\\\*escaped\", \"~~strike~~\"];\n return Array.from({ length: Math.max(0, cases) }, (_, index) => {\n const lines = Array.from({ length: 1 + (next() % 8) }, () => {\n const prefix = [\"\", \"# \", \"- \", \"> \", \"1. \"][next() % 5];\n return `${prefix}${tokens[next() % tokens.length]} ${tokens[next() % tokens.length]}`;\n });\n const newline = next() % 3 === 0 ? \"\\r\\n\" : \"\\n\";\n return { id: `fuzz-${seed}-${index}`, source: `${lines.join(newline)}${newline}` };\n });\n}\n\nexport const MDFN_TESTING_VERSION = \"0.1.1\" as const;\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -49,6 +49,6 @@ declare function traceState(framework: AdapterSemanticTrace["framework"], fixtur
|
|
|
49
49
|
declare function compareSemanticTraces(reference: AdapterSemanticTrace, traces: readonly AdapterSemanticTrace[]): ConformanceResult;
|
|
50
50
|
declare const representativeCorpus: readonly PreservationFixture[];
|
|
51
51
|
declare function deterministicFuzzCorpus(seed?: number, cases?: number): readonly PreservationFixture[];
|
|
52
|
-
declare const MDFN_TESTING_VERSION: "0.1.
|
|
52
|
+
declare const MDFN_TESTING_VERSION: "0.1.1";
|
|
53
53
|
|
|
54
54
|
export { type AdapterSemanticTrace, type ConformanceFinding, type ConformanceResult, MDFN_TESTING_VERSION, type PreservationFixture, type SemanticFixture, type SemanticTraceStep, authoritativeGfmCorpus, certifyExtension, compareSemanticTraces, deterministicFuzzCorpus, representativeCorpus, runPreservationCorpus, runSecurityCorpus, runSemanticCorpus, traceState };
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,6 @@ declare function traceState(framework: AdapterSemanticTrace["framework"], fixtur
|
|
|
49
49
|
declare function compareSemanticTraces(reference: AdapterSemanticTrace, traces: readonly AdapterSemanticTrace[]): ConformanceResult;
|
|
50
50
|
declare const representativeCorpus: readonly PreservationFixture[];
|
|
51
51
|
declare function deterministicFuzzCorpus(seed?: number, cases?: number): readonly PreservationFixture[];
|
|
52
|
-
declare const MDFN_TESTING_VERSION: "0.1.
|
|
52
|
+
declare const MDFN_TESTING_VERSION: "0.1.1";
|
|
53
53
|
|
|
54
54
|
export { type AdapterSemanticTrace, type ConformanceFinding, type ConformanceResult, MDFN_TESTING_VERSION, type PreservationFixture, type SemanticFixture, type SemanticTraceStep, authoritativeGfmCorpus, certifyExtension, compareSemanticTraces, deterministicFuzzCorpus, representativeCorpus, runPreservationCorpus, runSecurityCorpus, runSemanticCorpus, traceState };
|
package/dist/index.js
CHANGED
|
@@ -178,7 +178,7 @@ function deterministicFuzzCorpus(seed = 1296320078, cases = 100) {
|
|
|
178
178
|
return { id: `fuzz-${seed}-${index}`, source: `${lines.join(newline)}${newline}` };
|
|
179
179
|
});
|
|
180
180
|
}
|
|
181
|
-
var MDFN_TESTING_VERSION = "0.1.
|
|
181
|
+
var MDFN_TESTING_VERSION = "0.1.1";
|
|
182
182
|
export {
|
|
183
183
|
MDFN_TESTING_VERSION,
|
|
184
184
|
authoritativeGfmCorpus,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { resolveExtensions, type EditorState, type MdfnDocument, type MdfnExtension, type MdfnJsonValue, type MdfnNode } from \"@mdfn/core\";\nimport { parseMarkdown, serializeMarkdown, type MarkdownOptions } from \"@mdfn/markdown\";\nimport { renderHtml, type RenderPolicy } from \"@mdfn/render\";\nimport { parseFragment, serialize } from \"parse5\";\n\nexport interface PreservationFixture {\n readonly id: string;\n readonly source: string;\n readonly options?: MarkdownOptions;\n}\n\nexport interface ConformanceFinding {\n readonly code: string;\n readonly fixture: string;\n readonly message: string;\n}\n\nexport interface ConformanceResult {\n readonly ok: boolean;\n readonly passed: number;\n readonly failed: number;\n readonly findings: readonly ConformanceFinding[];\n}\n\nexport function runPreservationCorpus(fixtures: readonly PreservationFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const serialized = serializeMarkdown({ document: parsed.document, originalSource: fixture.source, options: fixture.options });\n if (serialized.markdown !== fixture.source) {\n findings.push({ code: \"MDFN_CONFORMANCE_NO_EDIT_CHANGED\", fixture: fixture.id, message: \"No-edit round trip changed source bytes\" });\n } else if (!serialized.preservation.exactUntouched) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXACT_NOT_REPORTED\", fixture: fixture.id, message: \"Exact preservation was not reported\" });\n } else {\n passed += 1;\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport function certifyExtension(extension: MdfnExtension, fixture: PreservationFixture): ConformanceResult {\n const configured = fixture.options?.extensions;\n const existing = configured ? (\"schemaHash\" in configured ? configured.extensions : configured) : [];\n const options = { ...(fixture.options ?? {}), extensions: [...existing, extension] };\n const findings = [...runPreservationCorpus([{ ...fixture, options }]).findings];\n let passed = findings.length === 0 ? 1 : 0;\n try {\n const registry = resolveExtensions(options.extensions);\n const parsed = parseMarkdown(fixture.source, options);\n const owns = (node: MdfnNode): boolean => extension.serializeMarkdown?.({ node }) != null || (node.content?.some(owns) ?? false);\n const dirty = (node: MdfnNode): MdfnNode => owns(node)\n ? { ...node, source: node.source ? { ...node.source, dirty: true } : undefined, content: node.content?.map(dirty) }\n : { ...node, content: node.content?.map(dirty) };\n const edited = dirty(parsed.document) as MdfnDocument;\n const ownedNodes: MdfnNode[] = [];\n const collectOwned = (node: MdfnNode): void => {\n if (extension.serializeMarkdown?.({ node }) != null) ownedNodes.push(node);\n node.content?.forEach(collectOwned);\n };\n collectOwned(parsed.document);\n if (ownedNodes.length === 0) findings.push({ code: \"MDFN_CERT_FIXTURE_NOT_OWNED\", fixture: fixture.id, message: \"Extension did not claim any syntax in its certification fixture\" });\n const serialized = serializeMarkdown({ document: edited, originalSource: fixture.source, options });\n if (!serialized.markdown) findings.push({ code: \"MDFN_CERT_EDITED_SERIALIZATION_EMPTY\", fixture: fixture.id, message: \"Edited serialization produced no source\" });\n else passed += 1;\n renderHtml(edited, { extensions: registry, rawHtml: { enabled: false } });\n passed += 1;\n if (extension.visual) {\n const visual = ownedNodes.map((node) => extension.visual?.({ node, escape: (value) => value })).filter(Boolean);\n if (visual.length === 0 || visual.some((node) => typeof node !== \"object\" || !node?.tag)) findings.push({ code: \"MDFN_CERT_VISUAL_INVALID\", fixture: fixture.id, message: \"Visual lifecycle did not produce a structured node\" });\n else passed += 1;\n }\n registry.diagnose(edited);\n passed += 1;\n for (const migration of extension.migrations ?? []) migration.migrate(parsed.document);\n passed += 1;\n const manifest = extension.certification;\n if (manifest) {\n if (manifest.schemaVersion !== 1 || !manifest.fixtures.includes(fixture.id)) findings.push({ code: \"MDFN_CERT_MANIFEST_INVALID\", fixture: fixture.id, message: \"Certification manifest does not cover the fixture\" });\n const hooks: Record<string, unknown> = { parse: extension.parseMarkdown, serialize: extension.serializeMarkdown, render: extension.render, visual: extension.visual, commands: extension.commands, keymap: extension.keymap, input: extension.inputRules, paste: extension.pasteRules, plugins: extension.plugins, diagnostics: extension.diagnostics, migrations: extension.migrations, security: extension.security };\n for (const capability of manifest.capabilities) if (!hooks[capability]) findings.push({ code: \"MDFN_CERT_CAPABILITY_MISSING\", fixture: fixture.id, message: `Manifest capability ${capability} has no hook` });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CERT_LIFECYCLE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport interface SemanticFixture extends PreservationFixture { readonly expectedHtml: string; }\n\nfunction canonicalHtml(value: string): string {\n const fragment = parseFragment(value);\n const visit = (node: unknown, preserveWhitespace = false): void => {\n if (!node || typeof node !== \"object\") return;\n const candidate = node as { nodeName?: string; value?: string; attrs?: Array<{ name: string; value: string }>; childNodes?: unknown[] };\n const preserve = preserveWhitespace || candidate.nodeName === \"pre\" || candidate.nodeName === \"code\";\n if (candidate.nodeName === \"#text\" && !preserve && typeof candidate.value === \"string\") candidate.value = candidate.value.replace(/\\s+/g, \" \");\n if (candidate.attrs) candidate.attrs = candidate.attrs.filter((attr) => ![\"target\", \"rel\", \"loading\", \"decoding\", \"href\", \"src\"].includes(attr.name)).sort((a, b) => a.name.localeCompare(b.name));\n if (candidate.childNodes) {\n candidate.childNodes = candidate.childNodes.filter((child) => {\n const text = child as { nodeName?: string; value?: string };\n return preserve || text.nodeName !== \"#text\" || Boolean(text.value?.trim());\n });\n for (let index = 0; index < candidate.childNodes.length; index += 1) {\n const child = candidate.childNodes[index] as { nodeName?: string; value?: string };\n const previous = candidate.childNodes[index - 1] as { nodeName?: string } | undefined;\n const next = candidate.childNodes[index + 1] as { nodeName?: string } | undefined;\n if (child.nodeName === \"#text\" && typeof child.value === \"string\") {\n if (previous?.nodeName === \"br\" || previous?.nodeName === \"ul\" || previous?.nodeName === \"ol\" || previous?.nodeName === \"blockquote\" || previous?.nodeName === \"pre\" || /^h[1-6]$/.test(previous?.nodeName ?? \"\")) child.value = child.value.trimStart();\n if (next?.nodeName === \"ul\" || next?.nodeName === \"ol\" || next?.nodeName === \"blockquote\" || next?.nodeName === \"pre\" || /^h[1-6]$/.test(next?.nodeName ?? \"\")) child.value = child.value.trimEnd();\n }\n }\n candidate.childNodes.forEach((child) => visit(child, preserve));\n }\n };\n visit(fragment);\n return serialize(fragment).trim();\n}\n\n/** Compare MDFN's projected semantics with authoritative expected HTML. */\nexport function runSemanticCorpus(fixtures: readonly SemanticFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const neutralizeUrls = (node: MdfnNode): MdfnNode => node.type === \"link\"\n ? { ...node, attrs: { ...node.attrs, url: \"#\" }, content: node.content?.map(neutralizeUrls) }\n : node.type === \"image\" ? { ...node, attrs: { ...node.attrs, url: \"/image\" } }\n : { ...node, content: node.content?.map(neutralizeUrls) };\n const document = neutralizeUrls(parsed.document) as MdfnDocument;\n const actual = renderHtml(document, { extensions: fixture.options?.extensions, rawHtml: { enabled: true, sanitize: (raw) => raw }, links: { externalTarget: null, externalRel: \"\" } }).html;\n if (canonicalHtml(actual) !== canonicalHtml(fixture.expectedHtml)) findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_MISMATCH\", fixture: fixture.id, message: `Expected ${canonicalHtml(fixture.expectedHtml)} but received ${canonicalHtml(actual)}` });\n else passed += 1;\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\n/** Representative examples copied from the normative GFM 0.29 test blocks. */\nexport const authoritativeGfmCorpus: readonly SemanticFixture[] = Object.freeze([\n { id: \"gfm-autolink-www\", source: \"www.commonmark.org\\n\", expectedHtml: '<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-autolink-email\", source: \"foo.bar.baz@google.com\\n\", expectedHtml: '<p><a href=\"mailto:foo.bar.baz@google.com\">foo.bar.baz@google.com</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-strikethrough\", source: \"~~Hi~~ Hello, world!\\n\", expectedHtml: \"<p><del>Hi</del> Hello, world!</p>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table\", source: \"| foo | bar |\\n| --- | --- |\\n| baz | bim |\\n\", expectedHtml: \"<table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>baz</td><td>bim</td></tr></tbody></table>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table-align\", source: \"| abc | defghi |\\n:-: | -----------:\\nbar | baz\\n\", expectedHtml: '<table><thead><tr><th align=\"center\">abc</th><th align=\"right\">defghi</th></tr></thead><tbody><tr><td align=\"center\">bar</td><td align=\"right\">baz</td></tr></tbody></table>', options: { dialect: \"gfm\" } },\n { id: \"gfm-task-list\", source: \"- [x] foo\\n- [ ] bar\\n\", expectedHtml: '<ul class=\"contains-task-list\"><li class=\"task-list-item\"><input type=\"checkbox\" disabled checked> foo</li><li class=\"task-list-item\"><input type=\"checkbox\" disabled> bar</li></ul>', options: { dialect: \"gfm\" } },\n]);\n\nexport function runSecurityCorpus(document: MdfnDocument, policy?: RenderPolicy): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n try {\n const result = renderHtml(document, policy);\n if (/<script\\b|\\son[a-z]+\\s*=|javascript:/i.test(result.html)) {\n findings.push({ code: \"MDFN_CONFORMANCE_UNSAFE_RENDER\", fixture: \"security\", message: \"Unsafe executable output was rendered\" });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_RENDER_EXCEPTION\", fixture: \"security\", message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed: findings.length === 0 ? 1 : 0, failed: findings.length, findings };\n}\n\nexport interface SemanticTraceStep {\n readonly name: string;\n readonly state: MdfnJsonValue;\n}\n\nexport interface AdapterSemanticTrace {\n readonly schemaVersion: 1;\n readonly framework: \"vanilla\" | \"react\" | \"svelte\" | \"solid\";\n readonly fixture: string;\n readonly steps: readonly SemanticTraceStep[];\n readonly finalMarkdown: string;\n readonly finalSelection: MdfnJsonValue;\n readonly diagnostics: readonly string[];\n readonly cleanup: { readonly subscriptions: number; readonly mounted: boolean };\n}\n\nexport function traceState(framework: AdapterSemanticTrace[\"framework\"], fixture: string, states: readonly EditorState[]): AdapterSemanticTrace {\n const final = states.at(-1);\n if (!final) throw new Error(\"MDFN_TRACE_STATE_MISSING\");\n return {\n schemaVersion: 1,\n framework,\n fixture,\n steps: states.map((state) => ({ name: `version:${state.version}`, state: { markdown: state.markdown, dirty: state.dirty, selection: state.selection as MdfnJsonValue, diagnostics: state.diagnostics.map((entry) => entry.code) } })),\n finalMarkdown: final.markdown,\n finalSelection: final.selection as MdfnJsonValue,\n diagnostics: final.diagnostics.map((entry) => entry.code),\n cleanup: { subscriptions: 0, mounted: false },\n };\n}\n\nexport function compareSemanticTraces(reference: AdapterSemanticTrace, traces: readonly AdapterSemanticTrace[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n for (const trace of traces) {\n const comparable = (value: AdapterSemanticTrace) => JSON.stringify({ steps: value.steps, finalMarkdown: value.finalMarkdown, finalSelection: value.finalSelection, diagnostics: value.diagnostics, cleanup: value.cleanup });\n if (comparable(trace) !== comparable(reference)) findings.push({ code: \"MDFN_ADAPTER_TRACE_DIVERGED\", fixture: trace.fixture, message: `${trace.framework} diverged from vanilla semantics` });\n }\n return { ok: findings.length === 0, passed: traces.length - findings.length, failed: findings.length, findings };\n}\n\nexport const representativeCorpus: readonly PreservationFixture[] = Object.freeze([\n { id: \"commonmark\", source: \"# Heading\\n\\nParagraph with **bold** and *emphasis*.\\n\" },\n { id: \"gfm-table\", source: \"| A | B |\\n| - | - |\\n| 1 | 2 |\\n\" },\n { id: \"unicode-crlf\", source: \"# 日本語\\r\\n\\r\\nمرحبا 👋\\r\\n\" },\n { id: \"lone-cr\", source: \"# Heading\\r\\rParagraph.\\r\" },\n { id: \"opaque-html\", source: \"<custom-element unsafe=\\\"no\\\">value</custom-element>\\n\" },\n { id: \"malformed\", source: \"```ts\\nconst value = 1;\\n\" },\n]);\n\nexport function deterministicFuzzCorpus(seed = 0x4d44464e, cases = 100): readonly PreservationFixture[] {\n let value = seed >>> 0;\n const next = (): number => {\n value = (Math.imul(value, 1664525) + 1013904223) >>> 0;\n return value;\n };\n const tokens = [\"plain\", \"**bold**\", \"_em_\", \"`code`\", \"[link](https://example.com)\", \"日本語\", \"👋\", \"<opaque-tag>\", \"\\\\*escaped\", \"~~strike~~\"];\n return Array.from({ length: Math.max(0, cases) }, (_, index) => {\n const lines = Array.from({ length: 1 + (next() % 8) }, () => {\n const prefix = [\"\", \"# \", \"- \", \"> \", \"1. \"][next() % 5];\n return `${prefix}${tokens[next() % tokens.length]} ${tokens[next() % tokens.length]}`;\n });\n const newline = next() % 3 === 0 ? \"\\r\\n\" : \"\\n\";\n return { id: `fuzz-${seed}-${index}`, source: `${lines.join(newline)}${newline}` };\n });\n}\n\nexport const MDFN_TESTING_VERSION = \"0.1.0\" as const;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { resolveExtensions, type EditorState, type MdfnDocument, type MdfnExtension, type MdfnJsonValue, type MdfnNode } from \"@mdfn/core\";\nimport { parseMarkdown, serializeMarkdown, type MarkdownOptions } from \"@mdfn/markdown\";\nimport { renderHtml, type RenderPolicy } from \"@mdfn/render\";\nimport { parseFragment, serialize } from \"parse5\";\n\nexport interface PreservationFixture {\n readonly id: string;\n readonly source: string;\n readonly options?: MarkdownOptions;\n}\n\nexport interface ConformanceFinding {\n readonly code: string;\n readonly fixture: string;\n readonly message: string;\n}\n\nexport interface ConformanceResult {\n readonly ok: boolean;\n readonly passed: number;\n readonly failed: number;\n readonly findings: readonly ConformanceFinding[];\n}\n\nexport function runPreservationCorpus(fixtures: readonly PreservationFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const serialized = serializeMarkdown({ document: parsed.document, originalSource: fixture.source, options: fixture.options });\n if (serialized.markdown !== fixture.source) {\n findings.push({ code: \"MDFN_CONFORMANCE_NO_EDIT_CHANGED\", fixture: fixture.id, message: \"No-edit round trip changed source bytes\" });\n } else if (!serialized.preservation.exactUntouched) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXACT_NOT_REPORTED\", fixture: fixture.id, message: \"Exact preservation was not reported\" });\n } else {\n passed += 1;\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport function certifyExtension(extension: MdfnExtension, fixture: PreservationFixture): ConformanceResult {\n const configured = fixture.options?.extensions;\n const existing = configured ? (\"schemaHash\" in configured ? configured.extensions : configured) : [];\n const options = { ...(fixture.options ?? {}), extensions: [...existing, extension] };\n const findings = [...runPreservationCorpus([{ ...fixture, options }]).findings];\n let passed = findings.length === 0 ? 1 : 0;\n try {\n const registry = resolveExtensions(options.extensions);\n const parsed = parseMarkdown(fixture.source, options);\n const owns = (node: MdfnNode): boolean => extension.serializeMarkdown?.({ node }) != null || (node.content?.some(owns) ?? false);\n const dirty = (node: MdfnNode): MdfnNode => owns(node)\n ? { ...node, source: node.source ? { ...node.source, dirty: true } : undefined, content: node.content?.map(dirty) }\n : { ...node, content: node.content?.map(dirty) };\n const edited = dirty(parsed.document) as MdfnDocument;\n const ownedNodes: MdfnNode[] = [];\n const collectOwned = (node: MdfnNode): void => {\n if (extension.serializeMarkdown?.({ node }) != null) ownedNodes.push(node);\n node.content?.forEach(collectOwned);\n };\n collectOwned(parsed.document);\n if (ownedNodes.length === 0) findings.push({ code: \"MDFN_CERT_FIXTURE_NOT_OWNED\", fixture: fixture.id, message: \"Extension did not claim any syntax in its certification fixture\" });\n const serialized = serializeMarkdown({ document: edited, originalSource: fixture.source, options });\n if (!serialized.markdown) findings.push({ code: \"MDFN_CERT_EDITED_SERIALIZATION_EMPTY\", fixture: fixture.id, message: \"Edited serialization produced no source\" });\n else passed += 1;\n renderHtml(edited, { extensions: registry, rawHtml: { enabled: false } });\n passed += 1;\n if (extension.visual) {\n const visual = ownedNodes.map((node) => extension.visual?.({ node, escape: (value) => value })).filter(Boolean);\n if (visual.length === 0 || visual.some((node) => typeof node !== \"object\" || !node?.tag)) findings.push({ code: \"MDFN_CERT_VISUAL_INVALID\", fixture: fixture.id, message: \"Visual lifecycle did not produce a structured node\" });\n else passed += 1;\n }\n registry.diagnose(edited);\n passed += 1;\n for (const migration of extension.migrations ?? []) migration.migrate(parsed.document);\n passed += 1;\n const manifest = extension.certification;\n if (manifest) {\n if (manifest.schemaVersion !== 1 || !manifest.fixtures.includes(fixture.id)) findings.push({ code: \"MDFN_CERT_MANIFEST_INVALID\", fixture: fixture.id, message: \"Certification manifest does not cover the fixture\" });\n const hooks: Record<string, unknown> = { parse: extension.parseMarkdown, serialize: extension.serializeMarkdown, render: extension.render, visual: extension.visual, commands: extension.commands, keymap: extension.keymap, input: extension.inputRules, paste: extension.pasteRules, plugins: extension.plugins, diagnostics: extension.diagnostics, migrations: extension.migrations, security: extension.security };\n for (const capability of manifest.capabilities) if (!hooks[capability]) findings.push({ code: \"MDFN_CERT_CAPABILITY_MISSING\", fixture: fixture.id, message: `Manifest capability ${capability} has no hook` });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CERT_LIFECYCLE_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\nexport interface SemanticFixture extends PreservationFixture { readonly expectedHtml: string; }\n\nfunction canonicalHtml(value: string): string {\n const fragment = parseFragment(value);\n const visit = (node: unknown, preserveWhitespace = false): void => {\n if (!node || typeof node !== \"object\") return;\n const candidate = node as { nodeName?: string; value?: string; attrs?: Array<{ name: string; value: string }>; childNodes?: unknown[] };\n const preserve = preserveWhitespace || candidate.nodeName === \"pre\" || candidate.nodeName === \"code\";\n if (candidate.nodeName === \"#text\" && !preserve && typeof candidate.value === \"string\") candidate.value = candidate.value.replace(/\\s+/g, \" \");\n if (candidate.attrs) candidate.attrs = candidate.attrs.filter((attr) => ![\"target\", \"rel\", \"loading\", \"decoding\", \"href\", \"src\"].includes(attr.name)).sort((a, b) => a.name.localeCompare(b.name));\n if (candidate.childNodes) {\n candidate.childNodes = candidate.childNodes.filter((child) => {\n const text = child as { nodeName?: string; value?: string };\n return preserve || text.nodeName !== \"#text\" || Boolean(text.value?.trim());\n });\n for (let index = 0; index < candidate.childNodes.length; index += 1) {\n const child = candidate.childNodes[index] as { nodeName?: string; value?: string };\n const previous = candidate.childNodes[index - 1] as { nodeName?: string } | undefined;\n const next = candidate.childNodes[index + 1] as { nodeName?: string } | undefined;\n if (child.nodeName === \"#text\" && typeof child.value === \"string\") {\n if (previous?.nodeName === \"br\" || previous?.nodeName === \"ul\" || previous?.nodeName === \"ol\" || previous?.nodeName === \"blockquote\" || previous?.nodeName === \"pre\" || /^h[1-6]$/.test(previous?.nodeName ?? \"\")) child.value = child.value.trimStart();\n if (next?.nodeName === \"ul\" || next?.nodeName === \"ol\" || next?.nodeName === \"blockquote\" || next?.nodeName === \"pre\" || /^h[1-6]$/.test(next?.nodeName ?? \"\")) child.value = child.value.trimEnd();\n }\n }\n candidate.childNodes.forEach((child) => visit(child, preserve));\n }\n };\n visit(fragment);\n return serialize(fragment).trim();\n}\n\n/** Compare MDFN's projected semantics with authoritative expected HTML. */\nexport function runSemanticCorpus(fixtures: readonly SemanticFixture[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n let passed = 0;\n for (const fixture of fixtures) {\n try {\n const parsed = parseMarkdown(fixture.source, fixture.options);\n const neutralizeUrls = (node: MdfnNode): MdfnNode => node.type === \"link\"\n ? { ...node, attrs: { ...node.attrs, url: \"#\" }, content: node.content?.map(neutralizeUrls) }\n : node.type === \"image\" ? { ...node, attrs: { ...node.attrs, url: \"/image\" } }\n : { ...node, content: node.content?.map(neutralizeUrls) };\n const document = neutralizeUrls(parsed.document) as MdfnDocument;\n const actual = renderHtml(document, { extensions: fixture.options?.extensions, rawHtml: { enabled: true, sanitize: (raw) => raw }, links: { externalTarget: null, externalRel: \"\" } }).html;\n if (canonicalHtml(actual) !== canonicalHtml(fixture.expectedHtml)) findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_MISMATCH\", fixture: fixture.id, message: `Expected ${canonicalHtml(fixture.expectedHtml)} but received ${canonicalHtml(actual)}` });\n else passed += 1;\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_SEMANTIC_EXCEPTION\", fixture: fixture.id, message: error instanceof Error ? error.message : String(error) });\n }\n }\n return { ok: findings.length === 0, passed, failed: findings.length, findings };\n}\n\n/** Representative examples copied from the normative GFM 0.29 test blocks. */\nexport const authoritativeGfmCorpus: readonly SemanticFixture[] = Object.freeze([\n { id: \"gfm-autolink-www\", source: \"www.commonmark.org\\n\", expectedHtml: '<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-autolink-email\", source: \"foo.bar.baz@google.com\\n\", expectedHtml: '<p><a href=\"mailto:foo.bar.baz@google.com\">foo.bar.baz@google.com</a></p>', options: { dialect: \"gfm\" } },\n { id: \"gfm-strikethrough\", source: \"~~Hi~~ Hello, world!\\n\", expectedHtml: \"<p><del>Hi</del> Hello, world!</p>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table\", source: \"| foo | bar |\\n| --- | --- |\\n| baz | bim |\\n\", expectedHtml: \"<table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>baz</td><td>bim</td></tr></tbody></table>\", options: { dialect: \"gfm\" } },\n { id: \"gfm-table-align\", source: \"| abc | defghi |\\n:-: | -----------:\\nbar | baz\\n\", expectedHtml: '<table><thead><tr><th align=\"center\">abc</th><th align=\"right\">defghi</th></tr></thead><tbody><tr><td align=\"center\">bar</td><td align=\"right\">baz</td></tr></tbody></table>', options: { dialect: \"gfm\" } },\n { id: \"gfm-task-list\", source: \"- [x] foo\\n- [ ] bar\\n\", expectedHtml: '<ul class=\"contains-task-list\"><li class=\"task-list-item\"><input type=\"checkbox\" disabled checked> foo</li><li class=\"task-list-item\"><input type=\"checkbox\" disabled> bar</li></ul>', options: { dialect: \"gfm\" } },\n]);\n\nexport function runSecurityCorpus(document: MdfnDocument, policy?: RenderPolicy): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n try {\n const result = renderHtml(document, policy);\n if (/<script\\b|\\son[a-z]+\\s*=|javascript:/i.test(result.html)) {\n findings.push({ code: \"MDFN_CONFORMANCE_UNSAFE_RENDER\", fixture: \"security\", message: \"Unsafe executable output was rendered\" });\n }\n } catch (error) {\n findings.push({ code: \"MDFN_CONFORMANCE_RENDER_EXCEPTION\", fixture: \"security\", message: error instanceof Error ? error.message : String(error) });\n }\n return { ok: findings.length === 0, passed: findings.length === 0 ? 1 : 0, failed: findings.length, findings };\n}\n\nexport interface SemanticTraceStep {\n readonly name: string;\n readonly state: MdfnJsonValue;\n}\n\nexport interface AdapterSemanticTrace {\n readonly schemaVersion: 1;\n readonly framework: \"vanilla\" | \"react\" | \"svelte\" | \"solid\";\n readonly fixture: string;\n readonly steps: readonly SemanticTraceStep[];\n readonly finalMarkdown: string;\n readonly finalSelection: MdfnJsonValue;\n readonly diagnostics: readonly string[];\n readonly cleanup: { readonly subscriptions: number; readonly mounted: boolean };\n}\n\nexport function traceState(framework: AdapterSemanticTrace[\"framework\"], fixture: string, states: readonly EditorState[]): AdapterSemanticTrace {\n const final = states.at(-1);\n if (!final) throw new Error(\"MDFN_TRACE_STATE_MISSING\");\n return {\n schemaVersion: 1,\n framework,\n fixture,\n steps: states.map((state) => ({ name: `version:${state.version}`, state: { markdown: state.markdown, dirty: state.dirty, selection: state.selection as MdfnJsonValue, diagnostics: state.diagnostics.map((entry) => entry.code) } })),\n finalMarkdown: final.markdown,\n finalSelection: final.selection as MdfnJsonValue,\n diagnostics: final.diagnostics.map((entry) => entry.code),\n cleanup: { subscriptions: 0, mounted: false },\n };\n}\n\nexport function compareSemanticTraces(reference: AdapterSemanticTrace, traces: readonly AdapterSemanticTrace[]): ConformanceResult {\n const findings: ConformanceFinding[] = [];\n for (const trace of traces) {\n const comparable = (value: AdapterSemanticTrace) => JSON.stringify({ steps: value.steps, finalMarkdown: value.finalMarkdown, finalSelection: value.finalSelection, diagnostics: value.diagnostics, cleanup: value.cleanup });\n if (comparable(trace) !== comparable(reference)) findings.push({ code: \"MDFN_ADAPTER_TRACE_DIVERGED\", fixture: trace.fixture, message: `${trace.framework} diverged from vanilla semantics` });\n }\n return { ok: findings.length === 0, passed: traces.length - findings.length, failed: findings.length, findings };\n}\n\nexport const representativeCorpus: readonly PreservationFixture[] = Object.freeze([\n { id: \"commonmark\", source: \"# Heading\\n\\nParagraph with **bold** and *emphasis*.\\n\" },\n { id: \"gfm-table\", source: \"| A | B |\\n| - | - |\\n| 1 | 2 |\\n\" },\n { id: \"unicode-crlf\", source: \"# 日本語\\r\\n\\r\\nمرحبا 👋\\r\\n\" },\n { id: \"lone-cr\", source: \"# Heading\\r\\rParagraph.\\r\" },\n { id: \"opaque-html\", source: \"<custom-element unsafe=\\\"no\\\">value</custom-element>\\n\" },\n { id: \"malformed\", source: \"```ts\\nconst value = 1;\\n\" },\n]);\n\nexport function deterministicFuzzCorpus(seed = 0x4d44464e, cases = 100): readonly PreservationFixture[] {\n let value = seed >>> 0;\n const next = (): number => {\n value = (Math.imul(value, 1664525) + 1013904223) >>> 0;\n return value;\n };\n const tokens = [\"plain\", \"**bold**\", \"_em_\", \"`code`\", \"[link](https://example.com)\", \"日本語\", \"👋\", \"<opaque-tag>\", \"\\\\*escaped\", \"~~strike~~\"];\n return Array.from({ length: Math.max(0, cases) }, (_, index) => {\n const lines = Array.from({ length: 1 + (next() % 8) }, () => {\n const prefix = [\"\", \"# \", \"- \", \"> \", \"1. \"][next() % 5];\n return `${prefix}${tokens[next() % tokens.length]} ${tokens[next() % tokens.length]}`;\n });\n const newline = next() % 3 === 0 ? \"\\r\\n\" : \"\\n\";\n return { id: `fuzz-${seed}-${index}`, source: `${lines.join(newline)}${newline}` };\n });\n}\n\nexport const MDFN_TESTING_VERSION = \"0.1.1\" as const;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mdfn/testing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Corpus, semantic traces, fixtures, and extension certification for mdfn",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
-
"exports": {
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
10
16
|
"scripts": {
|
|
11
17
|
"build": "tsup src/index.ts --format esm,cjs --dts --sourcemap --clean",
|
|
12
18
|
"test": "vitest run",
|
|
@@ -14,13 +20,39 @@
|
|
|
14
20
|
"lint": "tsc --noEmit -p tsconfig.json",
|
|
15
21
|
"prepack": "npm run build"
|
|
16
22
|
},
|
|
17
|
-
"dependencies": {
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@mdfn/core": "0.1.1",
|
|
25
|
+
"@mdfn/markdown": "0.1.1",
|
|
26
|
+
"@mdfn/render": "0.1.1",
|
|
27
|
+
"parse5": "^7.3.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"commonmark-spec": "0.31.2",
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.3.0",
|
|
33
|
+
"vitest": "3.2.4"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
20
39
|
"license": "MIT",
|
|
21
|
-
"engines": {
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20 <25"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/21nCo/superfunctions.git",
|
|
46
|
+
"directory": "mdfn/testing"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
24
51
|
"sideEffects": false,
|
|
25
|
-
"mdfn": {
|
|
52
|
+
"mdfn": {
|
|
53
|
+
"status": "stable-candidate",
|
|
54
|
+
"layer": "testing",
|
|
55
|
+
"schemaVersion": 1
|
|
56
|
+
},
|
|
57
|
+
"gitHead": "699d9d09932199d660333c57fe7bc646492ea3bf"
|
|
26
58
|
}
|