{"_id":"@aitofy/bugdeck-core","_rev":"2-dedad46cb5607256a077168c5f74e86b","name":"@aitofy/bugdeck-core","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aitofy/bugdeck-core","version":"0.1.0","keywords":["marker-io-alternative","bug-reporting","visual-feedback","self-hosted","issue-tracker","plane","plane-so"],"author":{"name":"aitofy"},"license":"MIT","_id":"@aitofy/bugdeck-core@0.1.0","maintainers":[{"name":"masterpk","email":"huanthuyon671@gmail.com"}],"homepage":"https://github.com/aitofy-dev/bugdeck/tree/main/packages/core#readme","bugs":{"url":"https://github.com/aitofy-dev/bugdeck/issues"},"dist":{"shasum":"0bafb49f8b977f56b43bfd7c33fc4a0a15418d71","tarball":"https://registry.npmjs.org/@aitofy/bugdeck-core/-/bugdeck-core-0.1.0.tgz","fileCount":37,"integrity":"sha512-NKp4VeMB6fL9NoqINdg0k12uAYmmuhjCzi8DNN8gH9rKMs5kwRQvit/pvX12kqY7LUH0lgAGbwj6UNEeitNwyA==","signatures":[{"sig":"MEYCIQDEBCJUNu8NYOaufiMcqW1QvcSOZdGFBWUxTsKHnPj5bwIhAKXygkch90UgAr+r1fz0vlltsggv4eaNjawU/qw4SNQ+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121819},"main":"./dist/index.js","type":"module","_from":"file:aitofy-bugdeck-core-0.1.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./contract":{"types":"./dist/pure.d.ts","import":"./dist/pure.js"},"./package.json":"./package.json"},"scripts":{"test":"tsx --test src/__tests__/*.test.ts src/adapters/plane/__tests__/*.test.ts","build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit"},"_npmUser":{"name":"masterpk","email":"huanthuyon671@gmail.com"},"_resolved":"/private/var/folders/kn/9vdzgqzj7f96007cb3tyr2j40000gn/T/452a06230888334270f81c7fafcf0bf5/aitofy-bugdeck-core-0.1.0.tgz","_integrity":"sha512-NKp4VeMB6fL9NoqINdg0k12uAYmmuhjCzi8DNN8gH9rKMs5kwRQvit/pvX12kqY7LUH0lgAGbwj6UNEeitNwyA==","repository":{"url":"git+https://github.com/aitofy-dev/bugdeck.git","type":"git","directory":"packages/core"},"_npmVersion":"10.9.0","description":"Pure core for bugdeck: the report contract, block parsing, derived titles, image sanitisation and issue rendering.","directories":{},"sideEffects":false,"_nodeVersion":"22.11.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.2","@types/node":"^22.10.2"},"optionalDependencies":{"sharp":"^0.33.5"},"_npmOperationalInternal":{"tmp":"tmp/bugdeck-core_0.1.0_1789161399273_0.09645950838574491","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aitofy/bugdeck-core","version":"0.2.0","description":"Pure core for bugdeck: the report contract, block parsing, derived titles, image sanitisation and issue rendering.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./contract":{"types":"./dist/pure.d.ts","import":"./dist/pure.js"},"./package.json":"./package.json"},"optionalDependencies":{"sharp":"^0.33.5"},"devDependencies":{"@types/node":"^22.10.2","tsx":"^4.19.2","typescript":"^5.7.2"},"engines":{"node":">=20"},"license":"MIT","sideEffects":false,"keywords":["marker-io-alternative","bug-reporting","visual-feedback","self-hosted","issue-tracker","plane","plane-so"],"repository":{"type":"git","url":"git+https://github.com/aitofy-dev/bugdeck.git","directory":"packages/core"},"homepage":"https://github.com/aitofy-dev/bugdeck/tree/main/packages/core#readme","bugs":{"url":"https://github.com/aitofy-dev/bugdeck/issues"},"author":{"name":"aitofy"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","test":"tsx --test src/__tests__/*.test.ts src/adapters/plane/__tests__/*.test.ts src/adapters/github/__tests__/*.test.ts"},"_id":"@aitofy/bugdeck-core@0.2.0","_integrity":"sha512-LldTWvM0WAu42jOmus8XBed1OWhFBjlGNHAazG0Jxuk8ooHIo5DklzUzoZrMFPdl6imQBIJ6iL1Y+3gWv047mg==","_resolved":"/private/var/folders/kn/9vdzgqzj7f96007cb3tyr2j40000gn/T/6834ad2c33ac0322a0d4a6eba3313ca6/aitofy-bugdeck-core-0.2.0.tgz","_from":"file:aitofy-bugdeck-core-0.2.0.tgz","_nodeVersion":"22.11.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-LldTWvM0WAu42jOmus8XBed1OWhFBjlGNHAazG0Jxuk8ooHIo5DklzUzoZrMFPdl6imQBIJ6iL1Y+3gWv047mg==","shasum":"63855aa4a4626aa6475f1ced1c5774ff3ca074e1","tarball":"https://registry.npmjs.org/@aitofy/bugdeck-core/-/bugdeck-core-0.2.0.tgz","fileCount":46,"unpackedSize":168844,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDyrTCxsTx9OHWY4z+EpcjMHxBjU+VqKjjkASFZIK6j2QIgf1+vs26Ithj9w0iyzkLeWpKBREVvM7ZW/VDsRtFQ6TY="}]},"_npmUser":{"name":"masterpk","email":"huanthuyon671@gmail.com"},"directories":{},"maintainers":[{"name":"masterpk","email":"huanthuyon671@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bugdeck-core_0.2.0_1789163892513_0.6622003389363642"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-11T21:16:39.073Z","modified":"2026-09-11T21:58:12.814Z","0.1.0":"2026-09-11T21:16:39.420Z","0.2.0":"2026-09-11T21:58:12.643Z"},"bugs":{"url":"https://github.com/aitofy-dev/bugdeck/issues"},"author":{"name":"aitofy"},"license":"MIT","homepage":"https://github.com/aitofy-dev/bugdeck/tree/main/packages/core#readme","keywords":["marker-io-alternative","bug-reporting","visual-feedback","self-hosted","issue-tracker","plane","plane-so"],"repository":{"type":"git","url":"git+https://github.com/aitofy-dev/bugdeck.git","directory":"packages/core"},"description":"Pure core for bugdeck: the report contract, block parsing, derived titles, image sanitisation and issue rendering.","maintainers":[{"name":"masterpk","email":"huanthuyon671@gmail.com"}],"readme":"# @aitofy/bugdeck-core\n\nThe pure half of [bugdeck](https://github.com/aitofy-dev/bugdeck): the wire contract the widget and\nthe server both speak, the functions over it, and the `IssueTracker` seam every tracker adapter\nplugs into. No framework, no server, no global state.\n\n```bash\npnpm add @aitofy/bugdeck-core\n```\n\nYou need this package directly only if you are writing a tracker adapter, a storage backend, or a\nclient in something other than React. The [widget](https://www.npmjs.com/package/@aitofy/bugdeck) and the\n[server](https://www.npmjs.com/package/@aitofy/bugdeck-server) depend on it for you.\n\n## Two entry points\n\n```ts\nimport { FEEDBACK_MAX_ASSETS, parseFeedbackBlocks } from '@aitofy/bugdeck-core/contract'; // browser-safe\nimport { createPlaneTracker, sanitizeImage } from '@aitofy/bugdeck-core';                 // Node\n```\n\n`@aitofy/bugdeck-core/contract` is the wire contract, block parsing, derived titles and thread folding —\nnothing that has ever heard of a filesystem, so a bundler following it never reaches `sharp`. The\nroot adds image sanitisation and the adapters.\n\n## What is in it\n\n| Module | What it decides |\n|---|---|\n| `contract` | Field names, limits, `FeedbackState`, `ErrorCode`, the report and context shapes. One fact, one place — the widget and the route cannot drift. |\n| `blocks` | Parses the untrusted ordered text+image document. An image index becomes the asset id just minted for it; an id this report does not own is dropped. |\n| `derive-title` | The one line triage reads, derived from the first sentence. Nobody filing a bug also writes a headline. |\n| `thread` | Folds legacy appends and tracker replies into one chronological conversation, deduped by comment id. |\n| `sanitize-image` | Re-encodes every upload through `sharp`: magic bytes, no SVG or GIF, 5000×5000 ceiling. `sharp` is an optional dependency — install it only if you accept images. |\n| `issue-body` | One report as plain HTML: who, what they wrote, where it happened. The default for any tracker with no markup of its own. |\n| `tracker` | `IssueTracker` — two required methods, the rest optional. Failures are values (`Result<T>`), never throws. |\n| `adapters/plane` | Plane as an `IssueTracker`: idempotent create, three-step attachments, state discovery by group, comment polling. |\n| `adapters/github` | GitHub Issues as an `IssueTracker`: Markdown bodies, a hidden marker for idempotency, `open`/`closed` mapped to states, Link-header polling. |\n\n## Adapters: Plane, GitHub\n\n```ts\nimport { createPlaneTracker, createGithubTracker } from '@aitofy/bugdeck-core';\n\nconst plane = createPlaneTracker({\n  baseUrl: 'https://plane.example.com',\n  apiKey: process.env.PLANE_API_KEY!,\n  workspaceSlug: 'acme',\n  projectId: '0d4f…',            // the project uuid, not its identifier\n  publicUrl: 'https://bugs.example.com', // asset links when an upload is refused\n});\n\nconst github = createGithubTracker({\n  owner: 'acme',\n  repo: 'app',\n  token: process.env.GITHUB_TOKEN!, // PAT or app token with `issues: write`\n  labels: ['bugdeck'],              // put on every issue, and the poll filter\n  publicUrl: 'https://bugs.example.com', // screenshots are links: GitHub has no upload API\n});\n```\n\n|  | Plane | GitHub |\n|---|---|---|\n| Body | HTML, images inline | Markdown, images as links to `publicUrl/assets/:id` |\n| Comments | HTML, images inline again | Markdown |\n| Editing | `updateIssue` PATCHes name + description | `updateIssue` PATCHes title + body, marker kept |\n| Screenshots | uploaded as attachments | not uploaded — the REST API has none |\n| Idempotency | `external_id` on the create | a hidden `<!-- bugdeck:report:<id> -->` in the body, found by search |\n| Code | `DEMO-42`, read off the project | `#42`, the issue number |\n| States | the board's columns, matched by name then group | `open` → `doing`, `closed` → `done`, closed as *not planned* → `fail`; a `pending` or `review` **label** overrides (`stateLabels`) |\n| Polling | `listUpdates` over the project's issues | `listUpdates` over `since` + `labels`, Link-header paged |\n\nReplying to a reporter is the same gesture on both: write a comment that **starts with `@user`**.\nNothing else on the tracker is ever shown to them — comments there routinely name other people's\naccounts. On GitHub that comment is Markdown and reaches the widget as written. Rename the marker\nwith `publicReplyMarker` on either config.\n\nBoth are optional: importing one is what ships it. A server that imports neither files nothing.\n\n## Writing an adapter\n\n```ts\nimport { ok, fail, type IssueTracker } from '@aitofy/bugdeck-core';\n\nexport function createMyTracker(config: MyConfig): IssueTracker {\n  return {\n    async createIssue(job) {\n      // job.descriptionHtml is a string, or a function to render again once\n      // your uploads have ids. job.externalSource + job.externalId are the\n      // idempotency key: a retry must adopt the issue, never file a second one.\n      return ok({ externalId: '42', code: 'MY-42' });\n    },\n    async addComment(externalId, html) {\n      return ok({ commentId: '…' });\n    },\n    // Optional. Omit uploadAttachment and the caller links the images instead;\n    // omit listUpdates and nothing polls; omit updateIssue and an edited report\n    // stays local; omit renderBody / renderComment and both are written as the\n    // plain HTML in `issue-body`.\n  };\n}\n```\n\nOne adapter is one file plus one registration line. An adapter never reaches back into the server\nor the widget.\n\n## States\n\nFive, from the reporter's point of view: `pending · doing · review · done · fail`. `review` means\nthe issue has been **fixed and the reporter is asked to confirm** — not that someone is still\nlooking at it; any label you write for it has to say so. A report body is editable only while\n`pending`; a comment is allowed in every state, including `done`, because *\"it is still broken\"* is\nthe most valuable message on the page and it always arrives late.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}