{"_id":"@app-theforge/sdk","name":"@app-theforge/sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@app-theforge/sdk","version":"0.1.0","description":"Forge integration SDK for prototype repos — bridges navigation, comments, and test-user sessions between Forge's Kiln preview and your app.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/app-theforge/the-forge.git","directory":"packages/forge-sdk"},"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"publishConfig":{"access":"public"},"dependencies":{"jose":"^6.2.3"},"devDependencies":{"tsup":"^8.3.0","typescript":"^5.5.0","vitest":"^2.0.0","happy-dom":"^15.0.0"},"engines":{"node":">=20"},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest"},"_id":"@app-theforge/sdk@0.1.0","bugs":{"url":"https://github.com/app-theforge/the-forge/issues"},"homepage":"https://github.com/app-theforge/the-forge#readme","_integrity":"sha512-53BJpJEZHwAIdxsj60k5RR/zDXDo16crN2kHBJih6S/0RRMTA2i1tFMkMpTeDytxXHhh4uCSty5nrqyyHFe3DA==","_resolved":"/private/tmp/claude-501/d013b10e5f22a3ae5de13461a9c28f6b/app-theforge-sdk-0.1.0.tgz","_from":"file:app-theforge-sdk-0.1.0.tgz","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-53BJpJEZHwAIdxsj60k5RR/zDXDo16crN2kHBJih6S/0RRMTA2i1tFMkMpTeDytxXHhh4uCSty5nrqyyHFe3DA==","shasum":"6b7873d3f7d43bf5855c3f1a231f089069a41e1a","tarball":"https://registry.npmjs.org/@app-theforge/sdk/-/sdk-0.1.0.tgz","fileCount":8,"unpackedSize":38401,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBqcqYUL6DLUIqYcvhHKJ1q58NqVrfEzUwrj1yE3Dk9OAiA18rBHk9hLIra4oKBRXcNKdOJrlwxp4ZpEF9nxMUJ9EA=="}]},"_npmUser":{"name":"samblandford","email":"samwatsonblandford@gmail.com"},"directories":{},"maintainers":[{"name":"samblandford","email":"samwatsonblandford@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.1.0_1780096174321_0.605365951715227"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-29T23:09:34.213Z","0.1.0":"2026-05-29T23:09:34.453Z","modified":"2026-05-29T23:09:34.631Z"},"maintainers":[{"name":"samblandford","email":"samwatsonblandford@gmail.com"}],"description":"Forge integration SDK for prototype repos — bridges navigation, comments, and test-user sessions between Forge's Kiln preview and your app.","homepage":"https://github.com/app-theforge/the-forge#readme","repository":{"type":"git","url":"git+https://github.com/app-theforge/the-forge.git","directory":"packages/forge-sdk"},"bugs":{"url":"https://github.com/app-theforge/the-forge/issues"},"license":"MIT","readme":"# @app-theforge/sdk\n\nForge integration for prototype repos. One install + one line in your entry file gives Forge:\n\n- Test-user session bootstrap (skip login when running inside Forge)\n- Navigation reporting (Forge's URL pill follows your in-app routes)\n- DOM element selection (pin comments to specific buttons in Kiln)\n\n## Install\n\n```sh\npnpm add @app-theforge/sdk\n```\n\n## Use\n\n```ts\n// e.g. apps/frontend/src/main.tsx\nimport { initForgeBridge } from '@app-theforge/sdk'\n\ninitForgeBridge({\n  allowedOrigins: [\n    'http://localhost:3000',       // local Forge dev\n    'https://app-theforge.com',     // Forge production\n  ],\n})\n```\n\nThat's it. The SDK is a no-op when your app is opened directly (not inside Forge's iframe).\n\n## Test-user session bootstrap\n\nWhen Forge opens your app in Kiln, it appends `?forge_session=<token>` to the URL.\n\nYou need ONE small backend endpoint on your app that accepts the token, validates it via Forge's JWKS, and creates a session for the configured test user. The SDK calls this endpoint at `POST /forge/session` by default (configurable).\n\nSee the example FastAPI implementation in [the Train repo](https://github.com/sam-blandford/train/blob/main/backend/services/forge/session.py).\n\n## Two backend flows\n\nThe SDK supports both ways of establishing the session, depending on what your prototype's backend prefers to return:\n\n### Cookie flow (Next.js / SSR-style auth)\n\nYour `/forge/session` endpoint sets the session cookie directly on the response, then returns:\n\n```json\n{ \"ok\": true, \"user\": { \"id\": \"...\", \"issued_at\": \"...\" } }\n```\n\nThe SDK doesn't need any extra callback — the cookie is in place, the user is signed in.\n\n### Ticket flow (SPA-style auth — e.g. @clerk/clerk-react)\n\nYour `/forge/session` returns a single-use sign-in ticket (the auth SDK exchanges it for a session client-side):\n\n```json\n{ \"ok\": true, \"user\": { \"id\": \"...\", \"issued_at\": \"...\" }, \"ticket\": \"<sign_in_ticket>\" }\n```\n\nYou provide an `onTicket` callback to `initForgeBridge` that exchanges the ticket:\n\n```ts\ninitForgeBridge({\n  allowedOrigins: ['http://localhost:3000', 'https://app-theforge.com'],\n  onTicket: async (ticket) => {\n    // e.g. for Clerk:\n    const result = await window.Clerk.client.signIn.create({ strategy: 'ticket', ticket })\n    await window.Clerk.setActive({ session: result.createdSessionId })\n  },\n})\n```\n\nIf your backend returns a ticket but you forgot to wire `onTicket`, the SDK returns `{ status: 'failed', reason: 'ticket_received_no_handler' }`. If `onTicket` throws, the SDK returns `{ status: 'failed', reason: 'ticket_exchange_failed: <message>' }`.\n\n## Security notes\n\nThe session token is a 15-minute, single-use, audience-bound RS256 JWT minted by Forge. The SDK:\n\n- Reads the token from the URL on first load\n- Strips it via `history.replaceState` (so it doesn't leak via referrer / analytics / browser history)\n- POSTs it to your `/forge/session` endpoint for backend validation\n\nAfter bootstrap, your app's normal session cookies handle auth. The token is never used again.\n\nYour `/forge/session` endpoint must call Forge's `/api/forge-sessions/<jti>/redeem` to mark the token consumed. A second redemption attempt returns 409.\n\n## Reading the session state\n\n```ts\nimport { isForgeSession, getForgeUser } from '@app-theforge/sdk'\n\nif (isForgeSession()) {\n  // Disable destructive actions, show a \"test mode\" banner, etc.\n  console.log('Running under Forge as', getForgeUser()?.id)\n}\n```\n\n## Hardening recommendation\n\nIf your app sends real emails, charges real payment methods, or deletes real data, gate those operations on `isForgeSession()` to avoid Forge test sessions causing real-world side effects.\n","readmeFilename":"README.md","_rev":"1-9055c51a1825588b360c7b74ea67bc85"}