{"_id":"@bantam-hq/featherweight","_rev":"4-8f122222dac4f2a217fd93319b80355d","name":"@bantam-hq/featherweight","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"@bantam-hq/featherweight","version":"0.1.0","keywords":["pdf","screenplay","fountain","fdx","final-draft","pdf-to-text","scriptwriting"],"author":{"name":"Bantam HQ"},"license":"MIT","_id":"@bantam-hq/featherweight@0.1.0","maintainers":[{"name":"kylemorck","email":"kyle@morck.io"}],"homepage":"https://github.com/bantamhq/featherweight#readme","bugs":{"url":"https://github.com/bantamhq/featherweight/issues"},"bin":{"featherweight":"dist/cli.js"},"dist":{"shasum":"4c89e55e3eb39893ec0f8e33dd2302da45a3212c","tarball":"https://registry.npmjs.org/@bantam-hq/featherweight/-/featherweight-0.1.0.tgz","fileCount":73,"integrity":"sha512-b4myVmpgJcs9xh1Cpud3HNgH6mS7LPG1NGqC42RBBirSsygc9lhkuZ52Rr5LDibtQNOeUjUHjA9zgB0piKJT3g==","signatures":[{"sig":"MEUCIQDoOLGbZ4zuFZa/D7wd3ADBvbQCKvZsDp8Zs2f0xUCYrgIgM4H0SwPX9c8zTWgOWcLfqhZeZgU3ULvjI8vcH+MqoD0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":169482},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"08459dbb5c2ed32f4023e81eadb87d0b3d2cf82a","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","check":"pnpm typecheck && pnpm test && pnpm build && pnpm test:cli","test:cli":"node --test test/cli/featherweight-cli.test.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"pnpm check","test:integration":"vitest run --project integration"},"_npmUser":{"name":"kylemorck","email":"kyle@morck.io"},"repository":{"url":"git+https://github.com/bantamhq/featherweight.git","type":"git"},"_npmVersion":"11.17.0","description":"Recover semantic screenplay documents from PDFs.","directories":{},"sideEffects":false,"_nodeVersion":"26.5.0","dependencies":{"@firecrawl/pdf-inspector":"1.11.2"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@11.18.0","devDependencies":{"vitest":"4.1.10","typescript":"7.0.2","@types/node":"24.13.3","fast-xml-parser":"5.10.1","@vitest/coverage-v8":"4.1.10"},"_npmOperationalInternal":{"tmp":"tmp/featherweight_0.1.0_1785978856524_0.38974833807841414","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bantam-hq/featherweight","version":"0.1.1","keywords":["pdf","screenplay","fountain","fdx","final-draft","pdf-to-text","scriptwriting"],"author":{"name":"Bantam HQ"},"license":"MIT","_id":"@bantam-hq/featherweight@0.1.1","maintainers":[{"name":"kylemorck","email":"kyle@morck.io"}],"homepage":"https://github.com/bantamhq/featherweight#readme","bugs":{"url":"https://github.com/bantamhq/featherweight/issues"},"bin":{"featherweight":"dist/cli.js"},"dist":{"shasum":"cfd7eacd43359b07f3673b46a712d14170a5f315","tarball":"https://registry.npmjs.org/@bantam-hq/featherweight/-/featherweight-0.1.1.tgz","fileCount":69,"integrity":"sha512-EGvTJldgC/DZtF7NGXOFl6tBz+l3mA52B6Y9bgOi7g8KPR/mLPYLwj+xUnk2A5cHuCXNgk76stJLnyJCUH+vew==","signatures":[{"sig":"MEQCIGwI5z7BCzAR6SUjS2azPhvI2iguRH+lBY7uBtvwjAtNAiAw0kSwRCq92ylXRrNI8ghLjmDGQi45d6p2pT4vAIx2RQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bantam-hq%2ffeatherweight@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":161035},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"5c38bbfb4af993fdd81bae3b0424c2c97f22c672","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","check":"pnpm typecheck && pnpm test && pnpm build && pnpm test:cli","test:cli":"node --test test/cli/featherweight-cli.test.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"pnpm check","test:integration":"vitest run --project integration"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fc5c4434-6a56-4f5e-9ea9-fb0df95fe8a7"}},"repository":{"url":"git+https://github.com/bantamhq/featherweight.git","type":"git"},"_npmVersion":"11.16.0","description":"Recover semantic screenplay documents from PDFs.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"@firecrawl/pdf-inspector":"1.11.2"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@11.18.0","devDependencies":{"vitest":"4.1.10","typescript":"7.0.2","@types/node":"24.13.3","fast-xml-parser":"5.10.1","@vitest/coverage-v8":"4.1.10"},"_npmOperationalInternal":{"tmp":"tmp/featherweight_0.1.1_1785983307991_0.2629000539458819","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@bantam-hq/featherweight","version":"0.1.2","keywords":["pdf","screenplay","fountain","fdx","final-draft","pdf-to-text","scriptwriting"],"author":{"name":"Bantam HQ"},"license":"MIT","_id":"@bantam-hq/featherweight@0.1.2","maintainers":[{"name":"kylemorck","email":"kyle@morck.io"}],"homepage":"https://github.com/bantamhq/featherweight#readme","bugs":{"url":"https://github.com/bantamhq/featherweight/issues"},"bin":{"featherweight":"dist/cli.js"},"dist":{"shasum":"78da116121bede9192b7bdc06e3b6c285a66fb11","tarball":"https://registry.npmjs.org/@bantam-hq/featherweight/-/featherweight-0.1.2.tgz","fileCount":69,"integrity":"sha512-S3clXh4yWkBpdb+5ZYrzPi3WME7GZrlT1KlAq1SQFn5Sb/M+hpbrokPTVhPXC8iCdui+LM8dcEsanydCJdhVYA==","signatures":[{"sig":"MEQCICbam5awK4KNfcQ4fDyyEHVg2DLZotyg7Xv8qSn7TjBwAiBIctIOUMrFbFG9J9khf0ZBgHMjUyGxGxv8My9lMdg6jQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bantam-hq%2ffeatherweight@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":163274},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"029727df782210a032ff67d9c1fa8a45c9b643ba","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","check":"pnpm typecheck && pnpm test && pnpm build && pnpm test:cli","test:cli":"node --test test/cli/featherweight-cli.test.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"pnpm check","test:integration":"vitest run --project integration"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fc5c4434-6a56-4f5e-9ea9-fb0df95fe8a7"}},"repository":{"url":"git+https://github.com/bantamhq/featherweight.git","type":"git"},"_npmVersion":"11.16.0","description":"Recover semantic screenplay documents from PDFs.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"@firecrawl/pdf-inspector":"1.11.2"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@11.18.0","devDependencies":{"vitest":"4.1.10","typescript":"7.0.2","@types/node":"24.13.3","fast-xml-parser":"5.10.1","@vitest/coverage-v8":"4.1.10"},"_npmOperationalInternal":{"tmp":"tmp/featherweight_0.1.2_1786042121737_0.7168573086410333","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@bantam-hq/featherweight","version":"0.1.3","description":"Turn a screenplay PDF into Fountain, FDX, or structured JSON.","type":"module","license":"MIT","author":{"name":"Bantam HQ"},"repository":{"type":"git","url":"git+https://github.com/bantamhq/featherweight.git"},"homepage":"https://github.com/bantamhq/featherweight#readme","bugs":{"url":"https://github.com/bantamhq/featherweight/issues"},"sideEffects":false,"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"bin":{"featherweight":"dist/cli.js"},"engines":{"node":">=24"},"packageManager":"pnpm@11.18.0","publishConfig":{"access":"public","provenance":true},"keywords":["pdf","screenplay","fountain","fdx","final-draft","pdf-to-text","scriptwriting"],"scripts":{"build":"tsc -p tsconfig.build.json","test":"vitest run","test:cli":"node --test test/cli/featherweight-cli.test.mjs","test:watch":"vitest","test:integration":"vitest run --project integration","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit","check":"pnpm typecheck && pnpm build && pnpm test && pnpm test:cli","prepublishOnly":"pnpm check"},"dependencies":{"@firecrawl/pdf-inspector":"1.11.2"},"devDependencies":{"@types/node":"24.13.3","@vitest/coverage-v8":"4.1.10","fast-xml-parser":"5.10.1","typescript":"7.0.2","vitest":"4.1.10"},"gitHead":"492860233cc54a950b528e93462da4f8cf1e11eb","_id":"@bantam-hq/featherweight@0.1.3","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-myy+7F5mzvpE7UXttssqG7wEtu94IdXyhmIFqwiEqv4dLlLREnSuoc9vXLakG252iwDyp2wwIcuEJnh4L0j4NA==","shasum":"0ae2cf369fa435d11734c4b5d672852107c8a5ce","tarball":"https://registry.npmjs.org/@bantam-hq/featherweight/-/featherweight-0.1.3.tgz","fileCount":99,"unpackedSize":183509,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bantam-hq%2ffeatherweight@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBzijlivXJa4etFo2nz14IMx2li0yJuGFoICCGecatc0AiEA7M0aYVwqI3c618NXOe5UVaSlZeuz3SnQXDtlRrEq+/4="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fc5c4434-6a56-4f5e-9ea9-fb0df95fe8a7"}},"directories":{},"maintainers":[{"name":"kylemorck","email":"kyle@morck.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/featherweight_0.1.3_1786226239664_0.23767804596130504"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-06T01:14:16.329Z","modified":"2026-08-08T21:57:20.184Z","0.1.0":"2026-08-06T01:14:16.693Z","0.1.1":"2026-08-06T02:28:28.140Z","0.1.2":"2026-08-06T18:48:41.885Z","0.1.3":"2026-08-08T21:57:19.822Z"},"bugs":{"url":"https://github.com/bantamhq/featherweight/issues"},"author":{"name":"Bantam HQ"},"license":"MIT","homepage":"https://github.com/bantamhq/featherweight#readme","keywords":["pdf","screenplay","fountain","fdx","final-draft","pdf-to-text","scriptwriting"],"repository":{"type":"git","url":"git+https://github.com/bantamhq/featherweight.git"},"description":"Turn a screenplay PDF into Fountain, FDX, or structured JSON.","maintainers":[{"name":"kylemorck","email":"kyle@morck.io"}],"readme":"# Featherweight\n\nTurn a screenplay PDF into Fountain, FDX, or structured JSON.\n\nFeatherweight reads the text, styling, and layout already in a PDF and puts the\nscreenplay back together. It knows the difference between a scene heading and\naction, a character cue and dialogue, or a centered title and a transition.\nThe result is an editable screenplay—not a pile of lines copied out of a PDF.\n\n## Features\n\n- **Fast** — converts a 120-page feature screenplay into an editable file in an\n  average of 177 ms, with no model or external service.\n- **Screenplay-aware** — recognizes title pages, scene headings, action,\n  characters, parentheticals, dialogue, dual dialogue, lyrics, transitions,\n  centered text, and page breaks.\n- **Keeps the writing intact** — preserves inline emphasis, scene numbers,\n  character extensions, deliberate spacing, and source ordering.\n- **Three useful outputs** — clean Fountain, ready-to-open FDX, or canonical\n  JSON for your own application.\n- **Flexible page routing** — choose native extraction or reserve a page for\n  OCR through the same conversion API.\n- **Works where you do** — ESM and TypeScript types for Node.js, plus a small\n  command-line tool.\n\n## Install\n\nFeatherweight requires Node.js 24 or newer.\n\n```sh\nnpm install @bantam-hq/featherweight\n```\n\nFor the command-line tool:\n\n```sh\nnpm install --global @bantam-hq/featherweight\n```\n\n## Command line\n\nPoint Featherweight at a PDF. Fountain goes to stdout by default.\n\n```sh\nfeatherweight screenplay.pdf\n```\n\nChoose another format or write straight to a file:\n\n```sh\nfeatherweight screenplay.pdf --output screenplay.fountain\nfeatherweight screenplay.pdf --format json --output screenplay.json\nfeatherweight screenplay.pdf --format fdx --output screenplay.fdx\n```\n\n```text\nUsage: featherweight <input.pdf> [--format fountain|json|fdx] [--output <path>]\n```\n\nThe format comes from `--format`, not the output filename. An existing output\nfile is replaced, and its parent directory must already exist. Run\n`featherweight --help` for help or `featherweight --version` for the installed\nversion.\n\n## Node.js API\n\nThe API has two jobs: inspect the PDF, then turn it into the format you want.\n\n### Inspect a PDF\n\n```ts\nimport { readFile } from \"node:fs/promises\";\nimport { inspectScreenplayPdf } from \"@bantam-hq/featherweight\";\n\nconst pdfBytes = new Uint8Array(await readFile(\"screenplay.pdf\"));\nconst inspection = inspectScreenplayPdf(pdfBytes);\n\nconsole.log(inspection.pageCount);\nconsole.log(inspection.pagesNeedingOcr);\n```\n\n```ts\nfunction inspectScreenplayPdf(pdfBytes: Uint8Array): PdfInspection;\n\ninterface PdfInspection {\n  readonly pageCount: number;\n  readonly pagesNeedingOcr: readonly number[];\n}\n```\n\nPage indexes are zero-based.\n\n### Convert a screenplay\n\nPass the original PDF and choose which pages should use native text extraction\nand which should be reserved for OCR. Featherweight handles extraction,\nscreenplay recognition, and serialization behind the public API.\n\n```ts\nimport { readFile } from \"node:fs/promises\";\nimport {\n  inspectScreenplayPdf,\n  screenplayToFDX,\n  screenplayToFountain,\n  screenplayToJSON,\n} from \"@bantam-hq/featherweight\";\n\nconst pdfBytes = new Uint8Array(await readFile(\"screenplay.pdf\"));\nconst inspection = inspectScreenplayPdf(pdfBytes);\nconst nativePageIndexes = Array.from(\n  { length: inspection.pageCount },\n  (_, pageIndex) => pageIndex,\n);\n\nconst fountain = screenplayToFountain(pdfBytes, nativePageIndexes, []);\nconst json = screenplayToJSON(pdfBytes, nativePageIndexes, []);\nconst fdx = screenplayToFDX(pdfBytes, nativePageIndexes, []);\n```\n\n```ts\nfunction screenplayToJSON(\n  pdfBytes: Uint8Array,\n  nativePageIndexes: readonly number[],\n  ocrPageIndexes: readonly number[],\n): string;\n\nfunction screenplayToFountain(\n  pdfBytes: Uint8Array,\n  nativePageIndexes: readonly number[],\n  ocrPageIndexes: readonly number[],\n): string;\n\nfunction screenplayToFDX(\n  pdfBytes: Uint8Array,\n  nativePageIndexes: readonly number[],\n  ocrPageIndexes: readonly number[],\n): string;\n```\n\nPage indexes are zero-based. Each index belongs in one routing array at most,\nand the arrays can arrive in any order. Pages routed to native extraction are\nread directly from the PDF. OCR-routed pages currently preserve their physical\nplace without contributing text; an OCR service adapter will fill that route in\na later release. Featherweight never mutates the PDF bytes or routing arrays.\n\nOnly physical page 0 is treated as a possible title page. Every title field is\noptional.\n\n## Outputs\n\n### Fountain\n\nPlain-text Fountain that keeps the screenplay editable and portable.\nFeatherweight adds syntax only where Fountain needs it to preserve the\nrecognized screenplay.\nScreenplay text that resembles Fountain syntax is emitted verbatim because\nFountain defines escaping only for emphasis, so Fountain applications may\ninterpret that text as syntax.\n\n### FDX\n\nA valid Final Draft document with the screenplay's complete text, title\ninformation, element structure, styles, scene numbers, dual dialogue, and page\nbreaks.\n\n### JSON\n\nThe canonical screenplay document for applications that want the structure\ndirectly. It includes title fields, semantic elements, styled text runs, scene\nnumbers, alignment, dual-dialogue ownership, and page breaks.\n\n## Errors\n\nPDF inspection throws `PdfInspectionError`. Screenplay conversion throws\n`ScreenplayConversionError`. Both expose a stable `.code`:\n\n```ts\ntype PdfInspectionErrorCode =\n  | \"PDF_BINDING_UNAVAILABLE\"\n  | \"PDF_INSPECTION_FAILED\";\n\ntype ScreenplayConversionErrorCode =\n  | \"INVALID_PAGE_INDEX\"\n  | \"DUPLICATE_PAGE_INDEX\"\n  | \"OVERLAPPING_PAGE_INDEX\"\n  | \"PDF_PROCESSING_FAILED\"\n  | \"INVALID_FDX_TEXT\";\n```\n\n## Development\n\n```sh\npnpm install --frozen-lockfile\npnpm check\n```\n\n`pnpm check` runs type checking, the full unit and integration suite, a\nproduction build, and packaged CLI tests. CI runs it on Linux, macOS, and\nWindows.\n\n## Acknowledgements\n\nFeatherweight builds on and learned from some excellent open-source work:\n\n- [PDF Inspector](https://github.com/firecrawl/pdf-inspector) by\n  [Firecrawl](https://firecrawl.dev) powers fast native PDF classification and\n  text extraction.\n- [Fountain](https://fountain.io), created by John August, Stu Maschwitz, and\n  Nima Yousefi, defines the plain-text screenplay format. Stu and John also created a number of fountain docs used for testing.\n- [Screenplain](https://github.com/vilcans/screenplain) by Martin Vilcans was a\n  useful reference for simple FDX output.\n\nLicenses and fixture attribution are in\n[THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md).\n\n## License\n\n[MIT](./LICENSE) © 2026 Bantam HQ\n","readmeFilename":"README.md"}