@hideandseekdigital/comet
v0.4.1
Published
Self-hosted feedback widget for Next.js. Submissions land in your Linear workspace using your own credentials.
Downloads
1,112
Maintainers
Readme
@hideandseekdigital/comet
Self-contained feedback widget for Next.js. Drop in a React component, re-export a server handler, submissions become Linear issues in your own workspace.
Install
npm install @hideandseekdigital/cometClient
import { FeedbackWidget } from '@hideandseekdigital/comet'
export default function Layout({ children }) {
return (
<>
{children}
<FeedbackWidget />
</>
)
}Server
Create app/api/comet/route.ts:
import { createHandler } from '@hideandseekdigital/comet/server'
export const runtime = 'nodejs'
export const { POST } = createHandler({
allowedOrigins: ['https://yourdomain.com'],
rateLimit: { perMinute: 10 },
})The
nodejsruntime declaration is required — the Linear SDK uses Node APIs and will fail silently on Edge.
Environment
LINEAR_API_KEY=lin_api_xxx
LINEAR_TEAM_ID=abc123On first submission, Comet creates four labels in your Linear workspace if they don't exist: Comet, Bug Report, Feature Request, Feedback. Every issue gets the Comet label plus the category-specific one.
Props
All props are optional.
| Prop | Type | Default | Purpose |
|---|---|---|---|
| endpoint | string | /api/comet | Where the widget POSTs submissions |
| categories | Category[] | all three | Which form tabs to show (pass a single-element array to lock the form to one category) |
| getDebugState | () => Record<string, unknown> | — | Called when the user opens the form. Return any JSON-serializable object — it's surfaced in the Linear issue and Claude Code prompt. |
| onReady | () => void | — | Fires once the SDK is attached to window |
Server config
interface CreateHandlerConfig {
allowedOrigins: string[]
rateLimit?: { perMinute: number }
labels?: {
always?: string // default: 'Comet'
bug?: string // default: 'Bug Report'
feature?: string // default: 'Feature Request'
feedback?: string // default: 'Feedback'
}
}SDK
window.FeedbackWidget.setUser({ email, name })
window.FeedbackWidget.setCustomData({ plan: 'pro' })
window.FeedbackWidget.capture()
window.FeedbackWidget.on('submit', cb)The SDK is attached during the component's first render. If you call SDK methods at app startup, use the onReady prop to wait.
Security
- Origin check (primary): requests must come from a domain in
allowedOrigins. The check is required in production —createHandlerthrows if omitted. - Rate limiting (opt-in): per-IP, in-memory, best-effort on serverless.
- Token storage: your
LINEAR_API_KEYlives in your.env.localonly. Comet never sees it — there is no hosted Comet backend.
License
MIT
