{"_id":"@ambicuity/matterforge","name":"@ambicuity/matterforge","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@ambicuity/matterforge","description":"Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, TOML or Coffee Front-Matter, with options to set custom delimiters.","version":"1.0.0","homepage":"https://github.com/ambicuity/matterforge","author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"repository":{"type":"git","url":"git+https://github.com/ambicuity/matterforge.git"},"bugs":{"url":"https://github.com/ambicuity/matterforge/issues"},"license":"MIT","bin":{"matterforge":"dist/commonjs/bin/matterforge.cjs"},"tshy":{"exports":{".":"./src/index.ts","./package.json":"./package.json"},"dialects":["esm","commonjs"]},"sideEffects":false,"engines":{"node":">=20.0"},"scripts":{"clean":"rm -rf dist .tshy-build","build":"tshy","lint":"eslint . && prettier --check .","lint:fix":"eslint . --fix && prettier --write .","prepare":"tshy","pretest":"npm run build","test":"node --test --test-reporter spec --import tsx 'test/*.ts' 'test/user-perspective/**/*.ts'","test:prod":"node -e \"const matter = require('./dist/commonjs/index.js'); if (matter('---\\nfoo: bar\\n---\\ncontent').data.foo !== 'bar') { console.error('CJS test failed'); process.exit(1); } else { console.log('CJS test passed'); }\"","test:property":"node --test --test-reporter spec --import tsx 'test/property/**/*.ts'","test:edge":"node --test --test-reporter spec --import tsx 'test/edge-runtime/**/*.ts'","test:fuzz":"node --test --test-reporter spec --import tsx 'test/fuzz/**/*.ts'","test:published":"node --test --test-reporter spec --import tsx 'test/published/**/*.ts'","test:browser":"vitest run --config vitest.config.ts","bench":"node --import tsx benchmark/baseline-2026-05-12/run.ts","bench:regression":"node --import tsx benchmark/regression.ts","typecheck":"tsc -p tsconfig.test.json --noEmit"},"dependencies":{"glob":"^11.1.0","yaml":"^2.9.0"},"devDependencies":{"@edge-runtime/vm":"^5.0.0","@eslint/js":"^9.39.4","@types/node":"^20.11.0","@vitest/browser":"^4.1.6","@vitest/browser-playwright":"^4.1.6","benchmarked":"^2.0.0","coffeescript":"^2.2.3","delimiter-regex":"^2.0.0","eslint":"^9.39.4","eslint-plugin-import":"^2.32.0","eslint-plugin-n":"^18.0.1","extend-shallow":"^3.0.2","fast-check":"^4.8.0","front-matter":"^2.3.0","minimist":"^1.2.0","playwright":"^1.60.0","prettier":"^3.8.3","toml":"^2.3.3","tshy":"^4.1.2","tsx":"^4.7.0","typescript":"^5.7.0","typescript-eslint":"^8.59.3","vinyl":"^2.1.0","vitest":"^4.1.6","write":"^1.0.3","zod":"^4.4.3"},"keywords":["front-matter","frontmatter","yaml","json","toml","markdown","parse","parser","extract","data","matterforge","static-site","mdx","content","edge-runtime","esm","typescript"],"browser":{"fs":false},"type":"module","exports":{".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.js"}},"./package.json":"./package.json"},"main":"./dist/commonjs/index.js","types":"./dist/commonjs/index.d.ts","module":"./dist/esm/index.js","gitHead":"6414bfda15fddb1c0492d9bfa74a1a6a682f8b67","_id":"@ambicuity/matterforge@1.0.0","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-9xUfA8EIPzwNm63Fkc7XEiu9u0Zg/4jixnNrD6Slr855Omdp3bke7h9H9HAK1G617hp8NKVOzhwmPGnP9DTeEg==","shasum":"4121bb0b0be6e84bc16dd7dcfd2e42870e34bc77","tarball":"https://registry.npmjs.org/@ambicuity/matterforge/-/matterforge-1.0.0.tgz","fileCount":137,"unpackedSize":251534,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC0DgkEVKn39NO1oVy+Nv3GTholZsvUUzIO4KMhT4VEnAIhANqff/33fhZOo8vNmgYAoifFjghjiaAcTNCVM3D1Gk+s"}]},"_npmUser":{"name":"ambicuity","email":"riteshrana36@gmail.com"},"directories":{},"maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/matterforge_1.0.0_1778686779444_0.8676982060421414"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-13T15:39:39.374Z","1.0.0":"2026-05-13T15:39:39.712Z","modified":"2026-05-13T15:39:39.955Z"},"maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"description":"Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, TOML or Coffee Front-Matter, with options to set custom delimiters.","homepage":"https://github.com/ambicuity/matterforge","keywords":["front-matter","frontmatter","yaml","json","toml","markdown","parse","parser","extract","data","matterforge","static-site","mdx","content","edge-runtime","esm","typescript"],"repository":{"type":"git","url":"git+https://github.com/ambicuity/matterforge.git"},"author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"bugs":{"url":"https://github.com/ambicuity/matterforge/issues"},"license":"MIT","readme":"# @ambicuity/matterforge\n\n[![npm version](https://img.shields.io/npm/v/@ambicuity/matterforge.svg)](https://www.npmjs.com/package/@ambicuity/matterforge)\n[![downloads/month](https://img.shields.io/npm/dm/@ambicuity/matterforge.svg)](https://www.npmjs.com/package/@ambicuity/matterforge)\n[![license](https://img.shields.io/npm/l/@ambicuity/matterforge.svg)](https://github.com/ambicuity/matterforge/blob/main/LICENSE)\n[![types](https://img.shields.io/npm/types/@ambicuity/matterforge.svg)](https://www.npmjs.com/package/@ambicuity/matterforge)\n\n> Parse front-matter from a string or file. Fast, reliable, and easy to use. Parses **YAML** front matter by default, with built-in support for **JSON** and **JavaScript** (eval-gated). Extensible to **TOML** or any custom format via pluggable engines.\n\nmatterforge is for developers building content-heavy sites, docs platforms, static-site generators, MDX pipelines, and Edge-rendered publishing tools. Ships with TypeScript types, dual ESM/CJS exports, browser + edge-runtime support, and built-in schema validation.\n\n## Install\n\nRequires Node.js ≥20. For older Node versions, use [`@ambicuity/matterforge@1.x`](https://www.npmjs.com/package/@ambicuity/matterforge/v/1.2.1).\n\n```sh\nnpm install @ambicuity/matterforge\n```\n\n> **Upgrading from v1?** See [MIGRATION.md](./MIGRATION.md) — the YAML engine\n> swap and a couple of internals changed. Most consumers need no code changes.\n\n## Quick Start\n\n```js\nimport matter from '@ambicuity/matterforge';\n\nconst result = matter(`---\ntitle: Hello\nslug: home\n---\nContent here`);\n\nconsole.log(result.data.title); // Hello\nconsole.log(result.content);    // Content here\n```\n\n## Overview\n\nConverts a string with front-matter, like this:\n\n```handlebars\n---\ntitle: Hello\nslug: home\n---\n<h1>Hello world!</h1>\n```\n\nInto an object like this:\n\n```js\n{\n  content: '<h1>Hello world!</h1>',\n  data: {\n    title: 'Hello',\n    slug: 'home'\n  }\n}\n```\n\n## Why matterforge?\n\n| Feature | matterforge | gray-matter |\n| --- | --- | --- |\n| Built-in schema validation (Zod, Valibot, ArkType) | ✅ | ❌ |\n| Error code frames & diagnostic CLI | ✅ | ❌ |\n| Deterministic stringify (`sortKeys`) | ✅ | ❌ |\n| ESM + CJS dual exports (via `tshy`) | ✅ | ❌ |\n| Edge-runtime support (Cloudflare/Vercel/Deno) | ✅ | ❌ |\n| Browser support (verified in headless Chromium) | ✅ | ❌ |\n| YAML 1.2 (via [`yaml`](https://eemeli.org/yaml)) | ✅ | ❌ (YAML 1.1 via js-yaml) |\n| TypeScript generics (typed `data` field) | ✅ | ❌ |\n| Async file reading | ✅ | ❌ |\n| Property-based + fuzz tests | ✅ | ❌ |\n| Regex-free parsing | ✅ | ✅ |\n| Custom engines | ✅ | ✅ |\n| Tree-shakeable (ESM, `sideEffects: false`) | ✅ | ❌ |\n| Node ≥20 (modern toolchain) | ✅ | ❌ (Node ≥14) |\n\n## API Summary\n\n| Method | Purpose | Runtime |\n|---|---|---|\n| `matter(input, options?)` | Parse front matter from a string | All |\n| `matter.stringify(file, data, options?)` | Convert data back into front matter | All |\n| `matter.read(filepath, options?)` | Read and parse a file synchronously | Node.js |\n| `matter.readAsync(filepath, options?)` | Read and parse a file asynchronously | Node.js |\n| `matter.test(string, options?)` | Check whether a string has front matter | All |\n| `matter.clearCache()` | Clear internal parse cache | All |\n| `matter.language(string, options?)` | Detect language after first delimiter | All |\n| `matter.YAMLException` | **Deprecated.** Alias for `YAMLParseError` from [`yaml`](https://eemeli.org/yaml). Removal planned in v3.0.0. | All |\n| `matter.MatterforgeError` | Enriched error class with code frames | All |\n| `matter.MatterforgeValidationError` | Validation error class with issues array | All |\n\n## Runtime Compatibility\n\n| API | Node.js | Browser | Edge Runtime |\n|---|:---:|:---:|:---:|\n| `matter()` | ✅ | ✅ | ✅ |\n| `matter.stringify()` | ✅ | ✅ | ✅ |\n| `matter.test()` | ✅ | ✅ | ✅ |\n| `matter.language()` | ✅ | ✅ | ✅ |\n| `matter.clearCache()` | ✅ | ✅ | ✅ |\n| `matter.read()` | ✅ | ❌ | ❌ |\n| `matter.readAsync()` | ✅ | ❌ | ❌ |\n\n> **Note:** `read()` and `readAsync()` require the `fs` module and are only available in Node.js.\n\n## Usage\n\n### CommonJS (Node.js)\n\n```js\nconst matter = require('@ambicuity/matterforge');\n```\n\n### ESM / ES Modules\n\n```js\nimport matter from '@ambicuity/matterforge';\n\n// Named type + error class imports:\nimport { MatterforgeError, MatterforgeValidationError } from '@ambicuity/matterforge';\nimport type { MatterforgeFile, MatterforgeOptions, MatterEngine } from '@ambicuity/matterforge';\n\n// All runtime methods are available on the default export:\nmatter.read('./file.md');\nmatter.stringify('content', { title: 'Hello' });\nmatter.test('---\\ntitle: X\\n---');\n```\n\n### TypeScript\n\n```ts\nimport matter from '@ambicuity/matterforge';\nimport type { MatterFile, MatterOptions, MatterEngine } from '@ambicuity/matterforge';\n\n// Generic data typing:\ninterface FrontMatter {\n  title: string;\n  slug: string;\n  tags?: string[];\n}\n\nconst file = matter<FrontMatter>('---\\ntitle: Hello\\nslug: home\\n---\\nContent');\nfile.data.title; // string — fully typed!\n```\n\n## Framework Examples\n\n### Next.js Blog Post\n\n```ts\nimport matter from '@ambicuity/matterforge';\nimport fs from 'fs';\n\ninterface PostMeta {\n  title: string;\n  date: string;\n  tags?: string[];\n}\n\nexport async function getPost(slug: string) {\n  const raw = await fs.promises.readFile(`./posts/${slug}.md`, 'utf8');\n  const { data, content } = matter<PostMeta>(raw);\n  return { metadata: data, body: content };\n}\n```\n\n### Astro Content Collection\n\n```ts\nimport matter from '@ambicuity/matterforge';\n\nexport function parsePost(raw: string) {\n  const { data, content } = matter<{\n    title: string;\n    date: string;\n    tags?: string[];\n  }>(raw);\n\n  return { metadata: data, body: content };\n}\n```\n\n### Cloudflare Worker (Edge Runtime)\n\n```js\nimport matter from '@ambicuity/matterforge';\n\nexport default {\n  async fetch(request) {\n    const markdown = await getMarkdownFromKV(request);\n    const { data, content } = matter(markdown);\n\n    return new Response(JSON.stringify({ data, content }), {\n      headers: { 'Content-Type': 'application/json' }\n    });\n  }\n};\n```\n\n### Schema Validation (Zod, Valibot, etc.)\n\nmatterforge includes a universal schema validation adapter protocol. You can pass any object with a `.parse()` or `.safeParse()` method (like a Zod schema) directly to `options.schema`.\n\n```ts\nimport { z } from 'zod';\nimport matter from '@ambicuity/matterforge';\n\nconst PostSchema = z.object({\n  title: z.string(),\n  slug: z.string(),\n  published: z.boolean().default(false),\n  date: z.string().datetime()\n});\n\nconst file = matter(raw, {\n  schema: PostSchema,\n  onValidationError: 'throw' // or 'return' to attach to file.errors\n});\n\n// file.data is fully validated and typed according to PostSchema!\n```\n\n## CLI\n\nmatterforge ships with a built-in CLI to validate markdown content across your repository.\n\n```sh\n# Parse all markdown files, run validation (if a config exists), and report errors with code frames\nnpx matterforge check \"content/**/*.md\"\n\n# Show aggregate statistics (languages used, common fields, error counts)\nnpx matterforge stats \"content/**/*.md\"\n```\n\nCreate a `matterforge.config.cjs` to enable CLI schema validation:\n\n```js\n// matterforge.config.cjs — CJS because matterforge v2 declares \"type\": \"module\"\nconst { z } = require('zod');\n\nmodule.exports = {\n  schema: z.object({\n    title: z.string(),\n    date: z.string().datetime()\n  }),\n  ignore: ['**/drafts/**'] // Optional ignore patterns\n};\n```\n\n> If your project does not use `\"type\": \"module\"`, you can keep the `.js` extension.\n\n## Returned Object\n\nmatterforge returns a `file` object with the following properties.\n\n**Enumerable**\n\n* `file.data` **{Object}**: the object created by parsing front-matter\n* `file.content` **{String}**: the input string, with `matter` stripped\n* `file.excerpt` **{String}**: an excerpt, if [defined on the options](#optionsexcerpt)\n* `file.empty` **{String}**: when the front-matter is \"empty\" (either all whitespace, nothing at all, or just comments and no data), the original string is set on this property.\n* `file.isEmpty` **{Boolean}**: true if front-matter is empty.\n* `file.errors` **{Array}**: an array of validation errors, if `onValidationError: 'return'` is used.\n\n**Non-enumerable**\n\n* `file.orig` **{Buffer|String}**: the original input string (or buffer). String in Edge/browser runtimes.\n* `file.language` **{String}**: the front-matter language that was parsed. `yaml` is the default.\n* `file.matter` **{String}**: the _raw_, un-parsed front-matter string.\n* `file.stringify` **{Function}**: [stringify](#stringify) the file by converting `file.data` to a string in the given language, wrapping it in delimiters and prepending it to `file.content`.\n\n## API\n\n### [matter](index.js#L30)\n\nTakes a string or object with `content` property, extracts and parses front-matter from the string, then returns an object with `data`, `content` and other [useful properties](#returned-object).\n\n**Params**\n\n* `input` **{Object|String}**: String, or object with `content` string\n* `options` **{Object}**\n* `returns` **{Object}**\n\n**Example**\n\n```js\nimport matter from '@ambicuity/matterforge';\nconsole.log(matter('---\\ntitle: Home\\n---\\nOther stuff'));\n//=> { data: { title: 'Home'}, content: 'Other stuff' }\n```\n\n### [.stringify](index.js#L173)\n\nStringify an object to YAML or the specified language, and append it to the given string. By default, only YAML and JSON can be stringified. See the [engines](#optionsengines) section to learn how to stringify other languages.\n\n**Params**\n\n* `file` **{String|Object}**: The content string to append to stringified front-matter, or a file object with `file.content` string.\n* `data` **{Object}**: Front matter to stringify.\n* `options` **{Object}**: [Options](#options) to pass to matterforge and the underlying YAML engine ([eemeli/yaml](https://eemeli.org/yaml)).\n* `returns` **{String}**: Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.\n\n**Example**\n\n```js\nconsole.log(matter.stringify('foo bar baz', {title: 'Home'}, { sortKeys: true }));\n// results in:\n// ---\n// title: Home\n// ---\n// foo bar baz\n```\n\n#### Deterministic Stringify Options\n\nWhen stringifying YAML, you can pass options to enforce deterministic output:\n\n* `sortKeys` **{Boolean|Function}**: Sort object keys alphabetically. Can also be a custom compare function.\n* `lineWidth` **{Number}**: Max line width before wrapping (default: 80).\n* `quotingType` **{String}**: `\"'\"` or `'\"'`.\n* `forceQuotes` **{Boolean}**: Force all strings to be quoted.\n* `noRefs` **{Boolean}**: Do not emit YAML aliases/anchors.\n\n### [.read](index.js#L191)\n\nSynchronously read a file from the file system and parse front matter. Returns the same object as the [main function](#matter). **Node.js only.**\n\n**Params**\n\n* `filepath` **{String}**: file path of the file to read.\n* `options` **{Object}**: [Options](#options) to pass to matterforge.\n* `returns` **{Object}**: Returns [an object](#returned-object) with `data` and `content`\n\n**Example**\n\n```js\nconst file = matter.read('./content/blog-post.md');\n```\n\n### [.readAsync](index.js#L212)\n\nAsynchronously read a file from the file system and parse front matter. Returns a Promise. **Node.js only.**\n\n**Params**\n\n* `filepath` **{String}**: file path of the file to read.\n* `options` **{Object}**: [Options](#options) to pass to matterforge.\n* `returns` **{Promise\\<Object\\>}**: Returns a Promise resolving to [an object](#returned-object) with `data` and `content`\n\n**Example**\n\n```js\nconst file = await matter.readAsync('./content/blog-post.md');\n```\n\n### [.test](index.js#L234)\n\nReturns true if the given `string` has front matter.\n\n**Params**\n\n* `string` **{String}**\n* `options` **{Object}**\n* `returns` **{Boolean}**: True if front matter exists.\n\n### [.clearCache](index.js#L266)\n\nClears the internal parse cache. Useful when the same input string may have been modified or when you need to free memory.\n\n**Example**\n\n```js\nmatter.clearCache();\n```\n\n### .YAMLException (deprecated)\n\nAlias for `YAMLParseError` from the [`yaml`](https://eemeli.org/yaml) package, exposed for backward compatibility with v1.x. Will be removed in v3.0.0; new code should import `YAMLParseError` directly.\n\n**Example**\n\n```js\ntry {\n  matter('---\\ninvalid: yaml: [\\n---\\ncontent');\n} catch (err) {\n  if (err instanceof matter.YAMLException) {\n    console.error('YAML parse error:', err.message);\n  }\n}\n```\n\nOr, for new code:\n\n```js\nimport { YAMLParseError } from 'yaml';\n\ntry {\n  matter('---\\ninvalid: yaml: [\\n---\\ncontent');\n} catch (err) {\n  if (err instanceof YAMLParseError) {\n    console.error('YAML parse error:', err.message);\n  }\n}\n```\n\n## Options\n\n### options.excerpt\n\n**Type**: `Boolean|Function`\n\n**Default**: `undefined`\n\nExtract an excerpt that directly follows front-matter, or is the first thing in the string if no front-matter exists.\n\nIf set to `excerpt: true`, it will look for the frontmatter delimiter, `---` by default and grab everything leading up to it.\n\n**Example**\n\n```js\nconst str = '---\\nfoo: bar\\n---\\nThis is an excerpt.\\n---\\nThis is content';\nconst file = matter(str, { excerpt: true });\n```\n\nResults in:\n\n```js\n{\n  content: 'This is an excerpt.\\n---\\nThis is content',\n  data: { foo: 'bar' },\n  excerpt: 'This is an excerpt.\\n'\n}\n```\n\nYou can also set `excerpt` to a function. This function uses the `file` and `options` that were initially passed to matterforge as parameters. The function can either **mutate** `file.excerpt` directly, or **return** a string:\n\n**Example (mutation pattern)**\n\n```js\nconst file = matter(str, {\n  excerpt: function(file, options) {\n    file.excerpt = file.content.split('\\n').slice(0, 4).join(' ');\n  }\n});\n```\n\n**Example (return pattern)**\n\n```js\nconst file = matter(str, {\n  excerpt: function(file, options) {\n    return file.content.split('\\n').slice(0, 4).join(' ');\n  }\n});\n```\n\n### options.excerpt_separator\n\n**Type**: `String`\n\n**Default**: `undefined`\n\nDefine a custom separator to use for excerpts.\n\n```js\nconsole.log(matter(string, { excerpt_separator: '<!-- end -->' }));\n```\n\n**Example**\n\nThe following HTML string:\n\n```html\n---\ntitle: Blog\n---\nMy awesome blog.\n<!-- end -->\n<h1>Hello world</h1>\n```\n\nResults in:\n\n```js\n{\n  data: { title: 'Blog'},\n  excerpt: 'My awesome blog.',\n  content: 'My awesome blog.\\n<!-- end -->\\n<h1>Hello world</h1>'\n}\n```\n\n### options.engines\n\nDefine custom engines for parsing and/or stringifying front-matter.\n\n**Type**: `Object` Object of engines\n\n**Default**: `JSON`, `YAML` and `JavaScript` are built-in. TOML and CoffeeScript can be added via custom engines (see examples below).\n\n**Engine format**\n\nEngines may either be an object with `parse` and (optionally) `stringify` methods, or a function that will be used for parsing only.\n\n**Examples**\n\n```js\nconst toml = require('toml');\n\n// As a function:\nconst file = matter(str, {\n  engines: {\n    toml: toml.parse.bind(toml),\n  }\n});\n\n// As an object with parse and stringify:\nconst file = matter(str, {\n  engines: {\n    toml: {\n      parse: toml.parse.bind(toml),\n      stringify: function() {\n        throw new Error('cannot stringify to TOML');\n      }\n    }\n  }\n});\n```\n\n### options.language\n\n**Type**: `String`\n\n**Default**: `yaml`\n\nDefine the engine to use for parsing front-matter.\n\n```js\nconsole.log(matter(string, { language: 'toml' }));\n```\n\n**Dynamic language detection**\n\nInstead of defining the language on the options, matterforge will automatically detect the language defined after the first delimiter and select the correct engine to use for parsing.\n\n```html\n---toml\ntitle = \"TOML\"\ndescription = \"Front matter\"\ncategories = \"front matter toml\"\n---\nThis is content\n```\n\n### options.delimiters\n\n**Type**: `String | [String, String]`\n\n**Default**: `---`\n\nOpen and close delimiters can be passed as a string (same for open/close) or an array of two strings.\n\n**Example:**\n\n```js\n// Same delimiter for open and close:\nmatter.read('file.md', { delimiters: '~~~' });\n\n// Different open/close delimiters:\nmatter.read('file.md', { delimiters: ['<<<', '>>>'] });\n```\n\nWould parse:\n\n```html\n~~~\ntitle: Home\n~~~\nThis is the {{title}} page.\n```\n\n## Custom Engines\n\nmatterforge ships with built-in engines for **YAML**, **JSON**, and **JavaScript**\n(eval-gated). Coffee, CSON, and TOML support are intentionally not bundled —\nregister them as user-supplied engines so consumers who don't need them don't\npay the dependency cost.\n\n### TOML\n\n```js\nimport matter from '@ambicuity/matterforge';\nimport toml from 'toml';\nimport * as tomlify from 'tomlify-j0.4';\n\nmatter.engines.toml = {\n  parse: toml.parse.bind(toml),\n  stringify: (data) => tomlify.toToml(data, { space: 2 })\n};\n\nconst file = matter('+++\\ntitle = \"Hello\"\\n+++\\ncontent', { delimiters: '+++' });\n```\n\n### Custom format\n\nAny object with a `parse(str, options?) => object` and optional\n`stringify(data, options?) => string` works:\n\n```js\nmatter.engines.myformat = {\n  parse: (str) => ({ /* your parsed result */ }),\n  stringify: (data) => '/* your serialized result */'\n};\n```\n\n## Error Handling\n\nParsing and validation errors are **thrown** by default. matterforge enriches these errors with diagnostic code frames pointing to the exact line and column where the error occurred.\n\n```js\ntry {\n  matter('---\\ninvalid: yaml: [\\n---\\ncontent');\n} catch (err) {\n  console.error(err.message);\n  // Invalid front matter: missed comma between flow collection entries\n  //\n  //   1 | ---\n  // > 2 | invalid: yaml: [\n  //                    ^\n  //   3 | ---\n  //   4 | content\n}\n```\n\nmatterforge preserves the original error's prototype chain, meaning `err instanceof matter.YAMLException` will still work correctly for YAML syntax errors.\n\n**Error scenarios:**\n\n| Scenario | Error Class |\n|---|---|\n| Invalid YAML syntax | `YAMLException` (with code frames) |\n| Invalid JSON syntax | `SyntaxError` (with code frames) |\n| Schema validation failure | `MatterforgeValidationError` (with `.issues` array) |\n| Unknown engine | `MatterforgeError` |\n| Engine missing `parse` method | `TypeError` |\n| Non-string, non-object input | `TypeError` |\n\n## Deprecated Options\n\nThese continue to work but will be removed in a future major version:\n\n| Deprecated | Use Instead |\n|---|---|\n| `options.delims` | `options.delimiters` |\n| `options.lang` | `options.language` |\n| `options.parsers` | `options.engines` |\n\n## TypeScript Types\n\nmatterforge exports convenience type aliases for framework and library authors:\n\n```ts\nimport type {\n  MatterFile,               // Alias for MatterforgeFile<string, D>\n  MatterOptions,            // Alias for MatterforgeOption<string, any>\n  MatterEngine,             // Engine function or { parse, stringify? } object\n  MatterStringifyOptions,   // Stringify-specific options\n  MatterforgeError,         // Enriched error class with code frames\n  MatterforgeValidationError, // Validation error class with issues array\n  SchemaAdapter             // Duck-typed schema adapter protocol\n} from '@ambicuity/matterforge';\n```\n\n## Security\n\nmatterforge parses YAML with [`eemeli/yaml`](https://eemeli.org/yaml) — the modern YAML 1.2 reference implementation. Defaults are safe:\n\n- **No prototype pollution** — `__proto__`, `constructor`, and `prototype` keys are stripped from parsed objects.\n- **No code execution from YAML** — unknown tags (`!!js/function`, custom tags) degrade to plain strings instead of executing.\n- **Safe integer handling** — values above `Number.MAX_SAFE_INTEGER` are surfaced as `BigInt` or strings rather than silently losing precision.\n\n> **JavaScript front matter** uses `eval()` and is **disabled by default**. You must opt in with `matter(input, { eval: true })`. Only enable for trusted content.\n\nmatterforge parses front matter safely, but does **not** sanitize HTML, validate metadata schemas without one, or prevent unsafe rendering in your application. Use a sanitizer (e.g. DOMPurify) when rendering untrusted HTML, and a schema validator (e.g. Zod, Valibot, ArkType) for type-safe data — pass any `.parse()`-shaped object as `options.schema`.\n\nTo report a security issue, see [SECURITY.md](./SECURITY.md).\n\n## Quality bar\n\nmatterforge is verified by a 5-layer test strategy (see [docs/testing.md](./docs/testing.md) for the full breakdown):\n\n| Layer | Coverage | Run with |\n| --- | --- | --- |\n| **Unit + user-perspective** (~210 tests) | Fixtures, real-world corpus, CLI E2E, dual-export, schema, large-input | `npm test` |\n| **Property-based** (8 invariants × ~300 cases) | Parse/stringify roundtrip, delimiters, BOM/CRLF, cache, options stability | `npm run test:property` |\n| **Multi-runtime** | Headless Chromium (`@vitest/browser`) + edge VM (`@edge-runtime/vm`) | `npm run test:browser` · `npm run test:edge` |\n| **Fuzz** | 100k random inputs against the parser, with crash-corpus replay | `npm run test:fuzz` |\n| **Performance + smoke** | Regression vs v2.0.0 baseline; `npm pack` + install + consumer smoke | `npm run bench:regression` · `npm run test:published` |\n\nCovered edge cases include: front matter not at the beginning of a file; empty and comment-only front matter; custom open/close delimiters; nested `---` inside content and YAML values; code blocks containing `---`; BOM at the start of files; CRLF and mixed line endings; YAML/JSON/TOML front matter; invalid input error paths; large files (10 MB+); deeply nested YAML; special YAML types (null, boolean, arrays, nested objects, multiline strings); browser environments without `Buffer`; Vinyl file objects; cache correctness and bulk eviction.\n\n## Project\n\n* **Contributing:** see [CONTRIBUTING.md](./CONTRIBUTING.md) for dev setup, test layers, and PR conventions.\n* **Architecture overview:** see [docs/architecture.md](./docs/architecture.md) for the module map and data flow.\n* **Testing strategy:** see [docs/testing.md](./docs/testing.md) for the layered test approach.\n* **Security disclosures:** see [SECURITY.md](./SECURITY.md).\n* **Changelog:** see [CHANGELOG.md](./CHANGELOG.md) (and [MIGRATION.md](./MIGRATION.md) for breaking changes).\n* **Issues / feature requests:** [GitHub issues](../../issues/new).\n\n### Author\n\n**Ritesh Rana** — [contact@riteshrana.engineer](mailto:contact@riteshrana.engineer)\n\n### License\n\nCopyright © 2026, [Ritesh Rana](mailto:contact@riteshrana.engineer). Released under the [MIT License](LICENSE).\n","readmeFilename":"README.md","_rev":"1-a0c05e018b35a2f4d74d68b674fe6944"}