{"_id":"bbmd","_rev":"4-6eb6adf3e097733ec1e8cf63927501e1","name":"bbmd","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.1":{"name":"bbmd","version":"0.0.1","keywords":["md","markdown","document","typescript","toolkit","blocks","bomd"],"author":{"name":"John Harlow","email":"hello@jharlow.dev"},"license":"MIT","_id":"bbmd@0.0.1","maintainers":[{"name":"jharlow","email":"hello@jharlow.dev"}],"homepage":"https://github.com/jharlow/bamd","bugs":{"url":"https://github.com/jharlow/bamd/issues"},"dist":{"shasum":"843fceac9a6a22d720daf05d105ebe87bfe513bc","tarball":"https://registry.npmjs.org/bbmd/-/bbmd-0.0.1.tgz","fileCount":6,"integrity":"sha512-BZbKf68b6tX4DicDoa3caHMPd1DWhCj/dXNFauhxaEyrayAuIqbWMEI64QnbI5ynASqaJ87pwtI3r8sLw1GIPw==","signatures":[{"sig":"MEUCICLTL3y7KJAb/p9usEE+fDFYMx41H1g4UGhe3/0QYxMEAiEAwZcE43TA5tTF08ub9LaBohTGPlVH4YDnQkq3Iu4PXEI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":590188},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"fea85dedb4735c5b729ec4006e7ac7217d11ac4f","scripts":{"lint":"eslint . --ext .ts --max-warnings 0","test":"vitest","build":"tsup","coverage":"vitest run --coverage","typecheck":"tsc --noEmit"},"_npmUser":{"name":"jharlow","email":"hello@jharlow.dev"},"repository":{"url":"git+https://github.com/jharlow/bamd.git","type":"git"},"_npmVersion":"10.9.2","description":"The Markdown toolkit for writing complex, embeddable documents in Typescript","directories":{},"_nodeVersion":"23.7.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","eslint":"^9.39.1","vitest":"^4.0.8","typescript":"5.9.2","@types/node":"^24.10.0","@bamd/eslint-config":"workspace:*","@vitest/coverage-v8":"^4.0.8","@bamd/typescript-config":"workspace:*"},"_npmOperationalInternal":{"tmp":"tmp/bbmd_0.0.1_1773279686716_0.04551419409779722","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"bbmd","version":"0.0.2","keywords":["md","markdown","document","typescript","toolkit","blocks","bomd"],"author":{"name":"John Harlow","email":"hello@jharlow.dev"},"license":"MIT","_id":"bbmd@0.0.2","maintainers":[{"name":"jharlow","email":"hello@jharlow.dev"}],"homepage":"https://github.com/jharlow/bbmd","bugs":{"url":"https://github.com/jharlow/bbmd/issues"},"dist":{"shasum":"bd4dd1279c27e557772e596c0d5865f45f3c2050","tarball":"https://registry.npmjs.org/bbmd/-/bbmd-0.0.2.tgz","fileCount":6,"integrity":"sha512-JPsH0/QcUv+3w9UJp+9UimMyxxpmMbLixB5jMLLDw3x+Zoa2HqF6qQVqxawdStIwanO0rSHkGmxPK5wWM6wY2Q==","signatures":[{"sig":"MEYCIQD7i72MQVHyiLWAEotymXAhQtNWp4XuI9HyveAFvpCNywIhAPy/yCGdc1WRwojJ1lImNSkiA55pkEI+NS/NJ9RcS9JY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":593340},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"062748ef687a1737b7e132a8fb5014fccf2e7fc2","scripts":{"lint":"eslint . --ext .ts --max-warnings 0","test":"vitest","build":"tsup","coverage":"vitest run --coverage","typecheck":"tsc --noEmit"},"_npmUser":{"name":"jharlow","email":"hello@jharlow.dev"},"repository":{"url":"git+https://github.com/jharlow/bbmd.git","type":"git"},"_npmVersion":"10.9.2","description":"The Markdown toolkit for writing complex, embeddable documents in Typescript","directories":{},"_nodeVersion":"23.7.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","eslint":"^9.39.1","vitest":"^4.0.8","typescript":"5.9.2","@types/node":"^24.10.0","@bbmd/eslint-config":"workspace:*","@vitest/coverage-v8":"^4.0.8","@bbmd/typescript-config":"workspace:*"},"_npmOperationalInternal":{"tmp":"tmp/bbmd_0.0.2_1773283802827_0.5648176338639568","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"bbmd","version":"0.0.3","keywords":["md","markdown","document","typescript","toolkit","blocks","bomd"],"author":{"name":"John Harlow","email":"hello@jharlow.dev"},"license":"MIT","_id":"bbmd@0.0.3","maintainers":[{"name":"jharlow","email":"hello@jharlow.dev"}],"homepage":"https://bbmd.jharlow.dev","bugs":{"url":"https://github.com/jharlow/bbmd/issues"},"dist":{"shasum":"e0f527d3be8d6be50525e13339f6db4b9579f242","tarball":"https://registry.npmjs.org/bbmd/-/bbmd-0.0.3.tgz","fileCount":6,"integrity":"sha512-LtfoXK+leMMLPNt8YXOGzrUXFcq9rinY5Bwng10+n2/CnBOfdtPXC7haDbRktmwktpJSTSDVbJCaKjD6gSJccQ==","signatures":[{"sig":"MEYCIQC2YPU8cYs1lPRE3vAZsq4yTMkdNZueM6q90W9SWQBjrQIhAJ5FihU0mGaHnUYv8f5NRsBOFE9Ow/31BbLDzw4G7V+A","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":598199},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"4a3527cb364c7befd95ee151f833ef2a37b4112d","scripts":{"lint":"eslint . --ext .ts --max-warnings 0","test":"vitest","build":"tsup","coverage":"vitest run --coverage","typecheck":"tsc --noEmit"},"_npmUser":{"name":"jharlow","email":"hello@jharlow.dev"},"repository":{"url":"git+https://github.com/jharlow/bbmd.git","type":"git"},"_npmVersion":"10.9.2","description":"The Markdown toolkit for writing complex, embeddable documents in Typescript","directories":{},"_nodeVersion":"23.7.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","eslint":"^9.39.1","vitest":"^4.0.8","typescript":"5.9.2","@types/node":"^24.10.0","@bbmd/eslint-config":"workspace:*","@vitest/coverage-v8":"^4.0.8","@bbmd/typescript-config":"workspace:*"},"_npmOperationalInternal":{"tmp":"tmp/bbmd_0.0.3_1775498410538_0.47607210442391534","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"bbmd","version":"0.0.4","description":"The Markdown toolkit for writing complex, embeddable documents in Typescript","keywords":["md","markdown","document","typescript","toolkit","blocks","bomd"],"author":{"name":"John Harlow","email":"hello@jharlow.dev"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/jharlow/bbmd.git"},"homepage":"https://bbmd.jharlow.dev","bugs":{"url":"https://github.com/jharlow/bbmd/issues"},"main":"./dist/index.js","module":"./dist/index.mjs","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"types":"./dist/index.d.ts","scripts":{"typecheck":"tsc --noEmit","lint":"eslint . --ext .ts --max-warnings 0","build":"tsup","test":"vitest","coverage":"vitest run --coverage"},"devDependencies":{"@bbmd/eslint-config":"workspace:*","@bbmd/typescript-config":"workspace:*","@types/node":"^24.10.0","@vitest/coverage-v8":"^4.0.8","eslint":"^9.39.1","tsup":"^8.5.1","typescript":"5.9.2","vitest":"^4.0.8"},"_id":"bbmd@0.0.4","gitHead":"39de3749986abc445a5af6910479355727029c44","_nodeVersion":"23.7.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-RV0wxXhiLo3weRXhltJHO/SpH33VS5QU1qrv9/Uh2AggIIAj7O8ZEIcUFGfb+kpSw54GRuzPMzOXaL9pzlc1Hg==","shasum":"8f422b8f9693aac0733d613c4b1d9753695765f0","tarball":"https://registry.npmjs.org/bbmd/-/bbmd-0.0.4.tgz","fileCount":6,"unpackedSize":598199,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDpZLfrJiKfdHplLIasPVi4YaqRkLf8nIJ+V5Y8EQzxKAiBRNgokVr05d8RQyePQ3fhMwYMnMiW/c+KKsQ3zcJPqmw=="}]},"_npmUser":{"name":"jharlow","email":"hello@jharlow.dev"},"directories":{},"maintainers":[{"name":"jharlow","email":"hello@jharlow.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bbmd_0.0.4_1775531535641_0.56157917667857"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-12T01:41:26.576Z","modified":"2026-04-07T03:12:15.939Z","0.0.1":"2026-03-12T01:41:26.883Z","0.0.2":"2026-03-12T02:50:03.017Z","0.0.3":"2026-04-06T18:00:10.719Z","0.0.4":"2026-04-07T03:12:15.823Z"},"bugs":{"url":"https://github.com/jharlow/bbmd/issues"},"author":{"name":"John Harlow","email":"hello@jharlow.dev"},"license":"MIT","homepage":"https://bbmd.jharlow.dev","keywords":["md","markdown","document","typescript","toolkit","blocks","bomd"],"repository":{"type":"git","url":"git+https://github.com/jharlow/bbmd.git"},"description":"The Markdown toolkit for writing complex, embeddable documents in Typescript","maintainers":[{"name":"jharlow","email":"hello@jharlow.dev"}],"readme":"# `bbmd` - block based markdown documents\n\nThe Markdown toolkit for writing complex, embeddable documents in Typescript\n\n## What is `bbmd`?\n\nBy using a block architecture, `bbmd` allows you to write complex Markdown documents that are context-agnostic and always render well. Your documents can easily interlace defaults, hide blocks conditionally, and adjust their structure and styling automatically when injected into other `bbmd` documents.\n\n```ts\nimport { b } from \"bbmd\";\n\ntype User = { name?: string; email?: string; alternateEmail?: string };\n\nconst createUserDoc = (user: User): MarkdownDocument => {\n  const footnote = b\n    .footnote(`Alternate email: ${user.alternateEmail}`)\n    .if(user.alternateEmail);\n  return b\n    .doc(\n      b.heading(\"User details\"),\n      b.p`The users name is ${b.p(user.name).default(\"unknown\")}`,\n      b.p`${b.b`The users email is`}: ${user.email}${footnote}`.if(user.email),\n    )\n    .if(user.name || user.email);\n};\n\nconst user: User = { email: \"john@email.com\", alternateEmail: \"john@work.com\" };\nconst userDoc = createUserDoc(user);\n\nconsole.log(`${userDoc}`);\n// # User details\n// The users name is unknown\n// **The users email is**: john@email.com[^1]\n//\n// [^1]: Alternate email: john@work.com\n\nconst prompt = b\n  .doc(\n    b.heading(\"Instructions\"),\n    \"Greet the user and introduce yourself as a helpful AI assistant.\",\n    userDoc,\n    \"Once you have done this, call the `welcomeGiven` tool.\",\n  )\n  .setRenderingOptions({\n    enforce: { bold: { style: \"__\" } },\n    newlineStrategy: \"between_blocks\",\n  });\n\nconsole.log(String(prompt));\n// # Instructions\n//\n// Greet the user and introduce yourself as a helpful AI assistant.\n//\n// ## User details\n//\n// The users name is unknown\n//\n// __The users email is__: john@email.com[^1]\n//\n// Once you have done this, call the `welcomeGiven` tool.\n//\n// [^1]: Alternate email: john@work.com\n```\n\nNotice that in this example:\n\n- The user document reacts to the data using a simple, declarative syntax\n- Embedded documents get structurally reorganized to make sense in their host context. In this example, the `userDoc` heading was shifted up its the footnote moved to the bottom of the document\n- You can enforce consistent rendering at the root document level, which applies recursively to sub-documents\n- The document is readable by embedding it in template literals, passing to `String()`, or calling the `.toString()` method directly from the document\n\nThis is the magic of a block-based architecture. By keeping a walkable block structure until the document needs to be rendered, documents are able to be context-agnostic and react to where they're embedded in their hosts.\n\n```ts\nconsole.log(userDoc.inspect());\n// MarkdownDocument\n// ├── MarkdownHeadingBlock\n// │   └── \"User details\"\n// ├── MarkdownLiteral [trimmed]\n// │   ├── \"The users name is \"\n// │   └── MarkdownParagraphBlock\n// │       └── \"unknown\"\n// └── MarkdownLiteral [trimmed]\n//     ├── MarkdownBoldBlock\n//     │   └── \"The users email is\"\n//     ├── \": \"\n//     ├── \"john@email.com\"\n//     └── MarkdownFootnoteBlock\n//         └── footer\n//             └── \"Alternate email: john@work.com\"\n```\n\nIt also enables you to use a comfortable, chainable syntax to create your documents, which is perfect for building complex prompts programmatically.\n\n```ts\ntype PullRequest = { title: string; reviewer: string; approved: boolean };\nconst createPrDoc = (pr: PullRequest): MarkdownDocument => {\n  const reviewer = b.b(pr.reviewer).change((block) => {\n    if (pr.approved) return block.strikethrough();\n    return block;\n  });\n  return b.doc(b.b(pr.title).h(), b.p(\"Reviewer: \", reviewer));\n};\n\nconst pr: PullRequest = { title: \"100\", reviewer: \"John\", approved: true };\nconsole.log(String(createPrDoc(pr)));\n// # **100**\n// Reviewer: ~~**John**~~\n\nconsole.log(String(createPrDoc({ ...pr, approved: false })));\n// # **100**\n// Reviewer: **John**\n```\n\nBecause `bbmd` allows all primitive data types as inputs, and it's resulting documents are swappable to anywhere you currently use `string`s, it's very easy to incrementally adopt `bbmd`.\n\nFor near instant adoption anywhere you use template literals today, just prefix them with `b.md`. By default, `b.md` detects injected blocks and encapsulates them (see the first example above), but if you would prefer to write markdown as you normally would, you can combine `b.md` with the `.parse()` method.\n\n```ts\n//                       👇 add `b.md` to existing template literals\nconst existingPrompt = b.md`\n  # About our company\n  We are a company that makes widgets.\n\n  ## Our process\n  We follow a _rigorous_ process to make ==widgets==.\n\n  ## Our customers\n  | Name       | Email                  |\n  |------------|------------------------|\n  | John Doe   | john.doe@example.com   |\n  | Jane Smith | jane.smith@example.com |\n`.parse(); // 👈 and then `.parse()` it to convert it automatically\n\nconsole.log(existingPrompt.inspect());\n// MarkdownDocument\n// ├── MarkdownHeadingBlock\n// │   └── \"About our company\"\n// ├── MarkdownParagraphBlock\n// │   └── \"We are a company that makes widgets.\"\n// ├── MarkdownLineBreakBlock\n// ├── MarkdownSectionBlock\n// │   ├── MarkdownHeadingBlock\n// │   │   └── \"Our process\"\n// │   ├── MarkdownParagraphBlock\n// │   │   ├── \"We follow a \"\n// │   │   ├── MarkdownItalicBlock [style=_]\n// │   │   │   └── \"rigorous\"\n// │   │   ├── \" process to make \"\n// │   │   ├── MarkdownHighlightBlock\n// │   │   │   └── \"widgets\"\n// │   │   └── \".\"\n// │   └── MarkdownLineBreakBlock\n// └── MarkdownSectionBlock\n//     ├── MarkdownHeadingBlock\n//     │   └── \"Our customers\"\n//     └── MarkdownTableBlock [columns=Name,Email, rows=2]\n//         ├── columns\n//         │   ├── \"Name\"\n//         │   └── \"Email\"\n//         └── rows\n//             ├── row 0\n//             │   ├── \"John Doe\"\n//             │   └── \"john.doe@example.com\"\n//             └── row 1\n//                 ├── \"Jane Smith\"\n//                 └── \"jane.smith@example.com\"\n```\n\nDid you notice the extra bit of magic in the example above? `b.md` also improves on standard template literals by automatically removing empty lines at the top and bottom of your document, and removes leading whitespace from each line, unless it's a code block or indented list, meaning you can forget about causing indentation issues.\n\n## Features\n\n- Full support for standard, extended, and Github Flavored Markdown syntax specifications\n- Additional support for common Markdown hacks like underlines, comments, details, and image captions\n- Sub-document and section handling renders your blocks perfectly wherever they're injected\n- Concise chaining API that focuses on terseness\n- Simple return interfaces enable easy typing for document factories\n- Automatic parsing using `b.md``.parse()` enables quick adoption\n- Documents convert to strings automatically using `String(doc)` or template literals using `${doc}` and support `.toString()`\n- Zero dependencies and minimal bundle size\n\n## Getting started\n\nRun the install command using your package manger of choice:\n\n```bash\nnpm install bbmd\n```\n\n```bash\nyarn add bbmd\n```\n\n```bash\npnpm add bbmd\n```\n\nThen import `b` anywhere in your application:\n\n```ts\nimport { b } from \"bbmd\";\n```\n\n## Advice\n\n### Encode as much block data as possible\n\nThe most important thing to understand about `bbmd` is that the more metadata you encode in the block system, the more able it is to ensure that your documents are truly context-agnostic.\n\n`bbmd` exposes many ways to achieve this, which allows you to pick whichever one suits your use case best. Parse encoding is best for quickly adopting existing template literals, functional encoding works best for constructing complex, conditional documents, and template encoding offers a mix of the conveniences of both syntaxes.\n\n```ts\nconst templateEncoding = b.md`\n${b.h(\"Example document\").l(2).id(\"example-document\")}\n${b.b(\"Important text\")}${b.fn(\"example footnote\")}\n`;\n\nconst functionalEncoding = b.doc(\n  b.h(\"Example document\").l(2).id(\"example-document\"),\n  b.p(b.b(\"Important text\"), b.fn(\"example footnote\")),\n);\n\nconst parseEncoding = b.md`\n## Example document {#example-document}\n**Important text**[^1]\n\n[^1]: example footnote\n`.parse();\n\nexpect(String(templateEncoding)).toBe(String(functionalEncoding));\nexpect(String(functionalEncoding)).toBe(String(parseEncoding));\n```\n\n### Use chaining to express conditions\n\nWhen creating complex documents that need to respond to state, use the provided methods to easily handle most scenarios. `.if()`, `.default()`, and `.change()` should cover most use cases.\n\n```ts\nconst createUserTemplate = (\n  userName: string,\n  isWorking: boolean,\n  status: string,\n): MarkdownInlineBlock => {\n  return b.p`${userName}`\n    .if(isWorking)\n    .default(\"Unknown\")\n    .change((block) => {\n      if (status === \"active\") return block.bold();\n      if (status === \"inactive\") return block.strikethrough();\n      return block;\n    });\n};\n\nexpect(String(createUserTemplate(\"\", true, \"unknown\"))).toBe(\"Unknown\");\nexpect(String(createUserTemplate(\"John\", false, \"active\"))).toBe(\"**Unknown**\");\nexpect(String(createUserTemplate(\"John\", true, \"active\"))).toBe(\"**John**\");\nexpect(String(createUserTemplate(\"John\", true, \"inactive\"))).toBe(\"~~John~~\");\nexpect(String(createUserTemplate(\"John\", true, \"unknown\"))).toBe(\"John\");\n```\n\n### Keep typing as simple as possible\n\nTypes in `bbmd` have been designed carefully to avoid complexity. There is a three-tier hierarchy of types which will help keep your I/O extremely lean when embedding/returning `bbmd` blocks.\n\n```bash\nTier 1 (all blocks): MarkdownBlock\n└── Tier 2 (structural blocks): MarkdownInlineBlock | MarkdownLineBlock | MarkdownMultilineBlock\n    └── Tier 3 (concrete implementations): Specific Markdown blocks (MarkdownBoldBlock etc)\n```\n\nAs a general rule, use the highest level of specificity that it is convenient for a function to accept/return. For the most part, `bbmd` should type to tier 3 for you automatically, however if you need to declare type signatures yourself, it can be more convenient to duck down to the next lowest tier.\n\n```ts\nconst createUserTemplate = (\n  userName: string,\n  status: string,\n): MarkdownInlineBlock => {\n  // 👆 the inferred return type is\n  //    MarkdownParagraphBlock | MarkdownBoldBlock | MarkdownStrikethroughBlock\n  //    however, it was more convenient to explicitly type the return as MarkdownInlineBlock\n  return b.p(userName).change((block) => {\n    if (status === \"active\") return block.bold();\n    if (status === \"inactive\") return block.strikethrough();\n    return block;\n  });\n};\n```\n\n### Set rendering options at call time\n\nBecause rendering a document walks the entire block structure, documents can adjust both structurally and stylistically to ensure they look good however they're embedded/shared.\n\nStructural adjustments (like keeping footers at the bottom of the document) occur automatically for you, however styling adjustments are left to the root document to define.\n\nYou can override a variety of styling options, which are enforced on the entire document at render time.\n\n```ts\n// You can define a re-usable set of rendering options\nconst defaultRenderingOptions = b.renderingOptions({\n  enforce: {\n    bold: { style: \"__\" },\n    horizontalRule: { style: \"*\" },\n    unorderedListItem: { style: \"+\" },\n    list: { indent: 2 },\n  },\n  newlineStrategy: \"between_blocks\",\n});\n\n// Your actual documents can use inconsistent styling\nconst exampleDoc = b.md`\n  # Heading 1\n  This paragraph has some **bold** text. This __bold__ text uses inconsistent styling.\n  - This list\n  * Has different bullet styles\n      - Nested list with 4 tab indent\n  + For each point\n  ---\n  The document feels crammed at first.\n\n\n\n  But then includes lot's of newlines\n\n\n\n  ...between every line.\n`\n  .parse()\n  .setRenderingOptions(defaultRenderingOptions);\n\n// But you can always enforce consistency and newline strategies at your final call-sites\nconsole.log(String(exampleDoc));\n// # Heading 1\n//\n// This paragraph has some __bold__ text. This __bold__ text uses inconsistent styling.\n//\n// + This list\n// + Has different bullet styles\n//   + Nested list with 4 tab indent\n// + For each point\n//\n// ***\n//\n// The document feels crammed at first.\n//\n// But then includes lot's of newlines\n//\n// ...between every line.\n```\n","readmeFilename":"README.md"}