{"_id":"emulate","_rev":"22-d3db4730b5c035f298382b0bb1843788","name":"emulate","dist-tags":{"latest":"0.11.0"},"versions":{"0.0.0":{"name":"emulate","version":"0.0.0","author":"","license":"MIT","_id":"emulate@0.0.0","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"dist":{"shasum":"b5485ff0c1c6b16e6ac91e9bb61c46f8a8cb0214","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.0.0.tgz","fileCount":3,"integrity":"sha512-oRJCB8i1dyDsiIgybeZ9wf7KXWB1dSbRhUj+KX4Vhz88QIatRPSGJ3uHGZY+ejSI0l8JC2gIgZsbvp0VbY8E7A==","signatures":[{"sig":"MEUCIQCjY20W3YVKIrK6uTTKzoZNp6EaU8pvZWD7OlDDoJvT8QIgf/o/i8sTYec3fVelvvCmxfXIFP13kBeYns/XKlre2dI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":251},"main":"index.js","_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"repository":{"url":"","type":"git"},"_npmVersion":"10.9.0","description":"Coming soon","directories":{},"_nodeVersion":"22.12.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/emulate_0.0.0_1773949549162_0.31780200023913774","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"emulate","version":"0.1.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.1.0","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"d14405f4d28010ac2436cc93eb1506aab08da18a","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.1.0.tgz","fileCount":4,"integrity":"sha512-kp8TWaXtnIEl7vpYFxjL4v0V7XhW+0lDVeKIpXvh0fdPLCB3mjIk/aOxe7fH9FdHeVyPbumQx0MSC3tkgJjOVg==","signatures":[{"sig":"MEYCIQDbfivLU/LFhDQVJiD2mObiPCRnP0IckZodgZNlrsZ9NQIhAOgsyMUKh+hWdgk66dPAluHktzHTUQlzR1ogPZQeHrJD","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1905382},"type":"module","_from":"file:emulate-0.1.0.tgz","scripts":{"dev":"tsup --watch","build":"tsup","clean":"rm -rf dist .turbo"},"_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"_resolved":"/private/var/folders/bw/l_mkbwg9061dn0frt5258s180000gn/T/9dd24933f3bd6d883d9819a0fd882fd5/emulate-0.1.0.tgz","_integrity":"sha512-kp8TWaXtnIEl7vpYFxjL4v0V7XhW+0lDVeKIpXvh0fdPLCB3mjIk/aOxe7fH9FdHeVyPbumQx0MSC3tkgJjOVg==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"10.9.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"22.12.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@internal/core":"0.1.0","@internal/github":"0.1.0","@internal/google":"0.1.0","@internal/vercel":"0.1.0","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.1.0_1774031409845_0.7788236920794804","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"emulate","version":"0.1.1","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.1.1","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"eff45b4ec81e372aa5b46f3338044545b9a89517","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.1.1.tgz","fileCount":5,"integrity":"sha512-LNx0D8IdZHQ6NPbOmM13yQ77wPJaaE9ZvXU1u1NKV0Pbmct3mncHgApmX8NDea1zFBOccDbu4hOKaiIsvR9PCQ==","signatures":[{"sig":"MEUCIHOmZnsLdJbtR2gx1z0Nm9Z/hqK0xCBzGGlx/KJuhVu5AiEAj71qOeecz5PHOu9JFjvokV6hIeowE3dY42mHsWlHUaM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1917476},"type":"module","_from":"file:emulate-0.1.1.tgz","scripts":{"dev":"tsup --watch","build":"tsup","clean":"rm -rf dist .turbo"},"_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"_resolved":"/private/var/folders/bw/l_mkbwg9061dn0frt5258s180000gn/T/505886783fbbbd5f3d54b531a1fe960b/emulate-0.1.1.tgz","_integrity":"sha512-LNx0D8IdZHQ6NPbOmM13yQ77wPJaaE9ZvXU1u1NKV0Pbmct3mncHgApmX8NDea1zFBOccDbu4hOKaiIsvR9PCQ==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"10.9.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"22.12.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@internal/core":"0.1.0","@internal/github":"0.1.0","@internal/google":"0.1.0","@internal/vercel":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.1.1_1774031598596_0.678345174515294","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"emulate","version":"0.2.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.2.0","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"e4990c024727228f5249222ec7e9c402c46c38bd","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.2.0.tgz","fileCount":9,"integrity":"sha512-tjiJMgYc0U16g/UDB5jSZjN7QKBDPk1GmRRKOPGkH4GYyu7gx3s775zyJNhWQ2C6/eUTBuFkXjbyB75whS6wUA==","signatures":[{"sig":"MEYCIQDwxdRiqblJ+LPiUJb81T4onqj31TQs7Wk6lVKqr2xd1wIhAOc4tRx2YA00oUgafjKNWSOQAytRdTBbOlJmKbQCfzyh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3840754},"main":"./dist/api.js","type":"module","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"gitHead":"8b52349903a6c1cdd7d7a9f4e559a6368cb5b43e","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","prepack":"cp ../../README.md ."},"_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"10.9.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"22.12.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@internal/core":"workspace:*","@internal/github":"workspace:*","@internal/google":"workspace:*","@internal/vercel":"workspace:*"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.2.0_1774197064272_0.6335003076392822","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"emulate","version":"0.3.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.3.0","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"aa8a5a019709c7121e3e6fd7cd8b378f8cc00b23","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.3.0.tgz","fileCount":28,"integrity":"sha512-UrOmGU0Xi/Ya0ytxh49AzLfx3y0pYhCGvC8ND1KXLB438nwtZp3Gj7y7q7FCiDUM2jJB0FysOnoXe238tvg/rA==","signatures":[{"sig":"MEYCIQDhUjk3htIK7s23jPIVbw3ZoSfA9liHx980ck6dyXrg7gIhAOlD2xVMG5ucFOCLfOslp2UEhLSlRsw25I8lQ613Jj3c","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3045821},"main":"./dist/api.js","type":"module","_from":"file:emulate-0.3.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo"},"_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"_resolved":"/private/var/folders/bw/l_mkbwg9061dn0frt5258s180000gn/T/0085e25ab49a903a3c70963a37b4b701/emulate-0.3.0.tgz","_integrity":"sha512-UrOmGU0Xi/Ya0ytxh49AzLfx3y0pYhCGvC8ND1KXLB438nwtZp3Gj7y7q7FCiDUM2jJB0FysOnoXe238tvg/rA==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"10.9.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"22.12.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.3.0","@emulators/core":"0.3.0","@emulators/apple":"0.3.0","@emulators/slack":"0.3.0","@emulators/github":"0.3.0","@emulators/google":"0.3.0","@emulators/vercel":"0.3.0","@emulators/microsoft":"0.3.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.3.0_1774557323489_0.10866314074714145","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"emulate","version":"0.4.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.4.0","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"e8639da701d98c927b37026cfde91d7fb291dcca","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.4.0.tgz","fileCount":36,"integrity":"sha512-4NJCRTDEXiY27wKC5z5l0XWcNuDC1RSQLMpxAA2AfFhviUq/x3D/eMYGilFgl91fJ2VA8mOYbpyoBSEVDa1lFA==","signatures":[{"sig":"MEUCIQDa9fZrt+RuBXBeEWXfe3TzkYrf5miuVnxN6v0eF9AqbQIgCVOnedRVAvvwTXJwAN+In4iVyRSVukbccyNU+vA9E48=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3710486},"main":"./dist/api.js","type":"module","_from":"file:emulate-0.4.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo"},"_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"_resolved":"/private/var/folders/bw/l_mkbwg9061dn0frt5258s180000gn/T/1345eac0b8429157d3b8d48899b298c6/emulate-0.4.0.tgz","_integrity":"sha512-4NJCRTDEXiY27wKC5z5l0XWcNuDC1RSQLMpxAA2AfFhviUq/x3D/eMYGilFgl91fJ2VA8mOYbpyoBSEVDa1lFA==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"10.9.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"22.12.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.4.0","@emulators/core":"0.4.0","@emulators/okta":"0.4.0","@emulators/apple":"0.4.0","@emulators/slack":"0.4.0","@emulators/github":"0.4.0","@emulators/google":"0.4.0","@emulators/resend":"0.4.0","@emulators/stripe":"0.4.0","@emulators/vercel":"0.4.0","@emulators/microsoft":"0.4.0","@emulators/mongoatlas":"0.4.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.4.0_1775151498819_0.8697589681541578","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"emulate","version":"0.4.1","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.4.1","maintainers":[{"name":"ctate","email":"chris@studiotate.com"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"adea6d495b4c068be515d37c26f0754b84b6f021","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.4.1.tgz","fileCount":36,"integrity":"sha512-96CEZB96M7fC5na1lFl9sL7AS5d2lRgJkZVb/e8ufbLMLLxEy2oHhN4E9d7+eJbtABne/CeumSRX2j7A9UDlEQ==","signatures":[{"sig":"MEYCIQCtIOMEAltM0f8+IJhGmNGP2/rR61iZIIyiUHQIX0fIGQIhAOJC8c34exF1ZicLZpGNZV9le6O6jVQQnwZIIMTzRthN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3710486},"main":"./dist/api.js","type":"module","_from":"file:emulate-0.4.1.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo"},"_npmUser":{"name":"ctate","email":"chris@studiotate.com"},"_resolved":"/private/var/folders/bw/l_mkbwg9061dn0frt5258s180000gn/T/bb2aab8a31929e08c58e1b986b778a20/emulate-0.4.1.tgz","_integrity":"sha512-96CEZB96M7fC5na1lFl9sL7AS5d2lRgJkZVb/e8ufbLMLLxEy2oHhN4E9d7+eJbtABne/CeumSRX2j7A9UDlEQ==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"10.9.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"22.12.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.4.1","@emulators/core":"0.4.1","@emulators/okta":"0.4.1","@emulators/apple":"0.4.1","@emulators/slack":"0.4.1","@emulators/github":"0.4.1","@emulators/google":"0.4.1","@emulators/resend":"0.4.1","@emulators/stripe":"0.4.1","@emulators/vercel":"0.4.1","@emulators/microsoft":"0.4.1","@emulators/mongoatlas":"0.4.1"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.4.1_1775158048209_0.7995764207772571","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"emulate","version":"0.5.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.5.0","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"d6f8eb1c636205613625da56d0390cc2c50cd1aa","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.5.0.tgz","fileCount":43,"integrity":"sha512-2LrOE8sqa1ITQ1aRR3kZhAhOCNz8hu+Kea9oBKdG/jEK6I/RYlMgHbsvQmbTTtr+nx6+fOOWkL6wqvfLeF5vuA==","signatures":[{"sig":"MEUCIFIeUuZutnQejcP2NFIE2I4KQJVwn1whNtWIJSs6ESNMAiEA6R2P2i0Ickc/hvSq2TDMGqqscdHUMHvhAANK2V9p5XE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":4271015},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.5.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.5.0.tgz","_integrity":"sha512-2LrOE8sqa1ITQ1aRR3kZhAhOCNz8hu+Kea9oBKdG/jEK6I/RYlMgHbsvQmbTTtr+nx6+fOOWkL6wqvfLeF5vuA==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.11.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.14.1","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1","@hono/node-server":"^1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.5.0","@emulators/core":"0.5.0","@emulators/okta":"0.5.0","@emulators/apple":"0.5.0","@emulators/clerk":"0.5.0","@emulators/slack":"0.5.0","@emulators/github":"0.5.0","@emulators/google":"0.5.0","@emulators/resend":"0.5.0","@emulators/stripe":"0.5.0","@emulators/vercel":"0.5.0","@emulators/microsoft":"0.5.0","@emulators/mongoatlas":"0.5.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.5.0_1777441775662_0.13145714404655684","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"emulate","version":"0.6.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.6.0","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"4070347c9225b6f23274f852d0f1bfb9ea5899ab","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.6.0.tgz","fileCount":41,"integrity":"sha512-UcuKgskLUCD4TnqMw/EHJRUw8y3E11ks4lzx5MkAq0jJbfl3031aFgJeR3AArl4xB0aABC9Q9RZw00srZbcHgw==","signatures":[{"sig":"MEQCIHUlYray4QMmwR/7g6Pdt+Ue2v0leUyqt3HIcHQMNnStAiBNO0URLMagcdl26eKNvIYLqNONorQCQwxPZaBAUcqJQA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":4811965},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.6.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.6.0.tgz","_integrity":"sha512-UcuKgskLUCD4TnqMw/EHJRUw8y3E11ks4lzx5MkAq0jJbfl3031aFgJeR3AArl4xB0aABC9Q9RZw00srZbcHgw==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.12.1","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.15.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.6.0","@emulators/core":"0.6.0","@emulators/okta":"0.6.0","@emulators/apple":"0.6.0","@emulators/clerk":"0.6.0","@emulators/slack":"0.6.0","@emulators/github":"0.6.0","@emulators/google":"0.6.0","@emulators/resend":"0.6.0","@emulators/stripe":"0.6.0","@emulators/vercel":"0.6.0","@emulators/microsoft":"0.6.0","@emulators/mongoatlas":"0.6.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.6.0_1779645042671_0.6412504065847489","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"emulate","version":"0.6.1","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.6.1","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"8b8522c708573d3c5d446d606788a362b1ec6c1a","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.6.1.tgz","fileCount":41,"integrity":"sha512-rY0ucz8YH2V0X8j39tK3XCMsEIJowLzp7p8bWdArrY+NULlibu8cHPSrYh15pX4aPrNADmMAbBz6cDSXBWIAeQ==","signatures":[{"sig":"MEQCIFNqTqUvBmGVDUlBpZDrguaf7pBvzBRXYLg2uCo27S91AiBZKKuhl7t285vN0h31d2nnYfYf2cLQmwA2mNrTsKEnyQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.6.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":4845899},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.6.1.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.6.1.tgz","_integrity":"sha512-rY0ucz8YH2V0X8j39tK3XCMsEIJowLzp7p8bWdArrY+NULlibu8cHPSrYh15pX4aPrNADmMAbBz6cDSXBWIAeQ==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.13.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.16.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.6.1","@emulators/core":"0.6.1","@emulators/okta":"0.6.1","@emulators/apple":"0.6.1","@emulators/clerk":"0.6.1","@emulators/slack":"0.6.1","@emulators/github":"0.6.1","@emulators/google":"0.6.1","@emulators/resend":"0.6.1","@emulators/stripe":"0.6.1","@emulators/vercel":"0.6.1","@emulators/microsoft":"0.6.1","@emulators/mongoatlas":"0.6.1"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.6.1_1781219473888_0.7283354153874304","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"emulate","version":"0.7.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.7.0","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"c5ec95197140db2dc7b16328969715ea361f8d39","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.7.0.tgz","fileCount":45,"integrity":"sha512-v2/Gefv+i/TUzjEkDQkfE8myS0O0T/rJXeOIpgEpldxNQEYileesaWSmMsmB2DBZ8VHO9Pmqe7R9q+81E8PciA==","signatures":[{"sig":"MEUCIHBZgTpzNULHnuey2aM4D4fKiToE3f9OZES6/kqem2PEAiEA3mJgNRWMWMBSYpM1WtUPB+5dw/nj8hfgkBqYOzCyLn8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":6924034},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.7.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.7.0.tgz","_integrity":"sha512-v2/Gefv+i/TUzjEkDQkfE8myS0O0T/rJXeOIpgEpldxNQEYileesaWSmMsmB2DBZ8VHO9Pmqe7R9q+81E8PciA==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.13.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.16.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.7.0","@emulators/core":"0.7.0","@emulators/okta":"0.7.0","@emulators/apple":"0.7.0","@emulators/clerk":"0.7.0","@emulators/slack":"0.7.0","@emulators/github":"0.7.0","@emulators/google":"0.7.0","@emulators/linear":"0.7.0","@emulators/resend":"0.7.0","@emulators/stripe":"0.7.0","@emulators/vercel":"0.7.0","@emulators/microsoft":"0.7.0","@emulators/mongoatlas":"0.7.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.7.0_1781816034684_0.9759192788801161","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"emulate","version":"0.8.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.8.0","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"284f6d5fdd1fddaad89a6d5ecc1f33d6b7f37065","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.8.0.tgz","fileCount":47,"integrity":"sha512-UMbzvyrSgBVpWciS/I6/XbvsG6CAabKeSctweC328yaMcw5cT86ms50lOjcEWUcGPiXZTefxL4UY2s6tZR8IXA==","signatures":[{"sig":"MEYCIQDFc39rsJyonEH4k9QGd+pYB8xmXgxGvBBsaCOP1DfZlgIhAItFAJQRuiXMBvfRyriqlzpXSW9NVKfZF0jTi8CdBpGk","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":7246314},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.8.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.8.0.tgz","_integrity":"sha512-UMbzvyrSgBVpWciS/I6/XbvsG6CAabKeSctweC328yaMcw5cT86ms50lOjcEWUcGPiXZTefxL4UY2s6tZR8IXA==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.13.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.16.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.8.0","@emulators/core":"0.8.0","@emulators/okta":"0.8.0","@emulators/apple":"0.8.0","@emulators/clerk":"0.8.0","@emulators/slack":"0.8.0","@emulators/github":"0.8.0","@emulators/google":"0.8.0","@emulators/linear":"0.8.0","@emulators/resend":"0.8.0","@emulators/stripe":"0.8.0","@emulators/twilio":"0.8.0","@emulators/vercel":"0.8.0","@emulators/microsoft":"0.8.0","@emulators/mongoatlas":"0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.8.0_1782332637473_0.05284751605254545","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"emulate","version":"0.9.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.9.0","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"38b02a124784ccef2059f8e2990e0ca96bf42a9a","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.9.0.tgz","fileCount":47,"integrity":"sha512-wgOFQSrjAt0NqPDnYUFyCcCwAVw01aNS3M24SZ2C5ViLQW8ECi0qIyUsW+y2uvflBD48JFqomB+jRox+ktKK/w==","signatures":[{"sig":"MEUCIQC1XLX839oU60b1DthOFtxFDhkzfULeelbI2+AR4Miv5QIgUxv3LYkSQeVGhJ45ymXTxxaUVrKSPQi01mdvGrPTeDw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":7249634},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.9.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.9.0.tgz","_integrity":"sha512-wgOFQSrjAt0NqPDnYUFyCcCwAVw01aNS3M24SZ2C5ViLQW8ECi0qIyUsW+y2uvflBD48JFqomB+jRox+ktKK/w==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.16.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.18.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.9.0","@emulators/core":"0.9.0","@emulators/okta":"0.9.0","@emulators/apple":"0.9.0","@emulators/clerk":"0.9.0","@emulators/slack":"0.9.0","@emulators/github":"0.9.0","@emulators/google":"0.9.0","@emulators/linear":"0.9.0","@emulators/resend":"0.9.0","@emulators/stripe":"0.9.0","@emulators/twilio":"0.9.0","@emulators/vercel":"0.9.0","@emulators/microsoft":"0.9.0","@emulators/mongoatlas":"0.9.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.9.0_1783093823333_0.6393572758211801","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"emulate","version":"0.10.0","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"license":"Apache-2.0","_id":"emulate@0.10.0","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"homepage":"https://emulate.dev","bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"bin":{"emulate":"dist/index.js"},"dist":{"shasum":"88e67aa21082304203acdbbc447e6edafec1d7b9","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.10.0.tgz","fileCount":47,"integrity":"sha512-UBep7swzzmwPSdRFudo7jW4ukBFlF6Pk13wgJzQHtjvZm4a5iyP6b0uatj+HZoP6kENeoi4YyLv3ELUl5D6RnA==","signatures":[{"sig":"MEYCIQDbUZ0tLRW+81DwlB9TuVSaRGnF8WW2ggblG6haj5g7ogIhAKuw13bgKw+KwIWdd7+U5NvpndLp7m8unsrMj7YEh00x","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.10.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":7453103},"main":"./dist/api.js","type":"module","_from":"file:/tmp/emulate-0.10.0.tgz","types":"./dist/api.d.ts","exports":{".":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./cli":{"import":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"_resolved":"/tmp/emulate-0.10.0.tgz","_integrity":"sha512-UBep7swzzmwPSdRFudo7jW4ukBFlF6Pk13wgJzQHtjvZm4a5iyP6b0uatj+HZoP6kENeoi4YyLv3ELUl5D6RnA==","repository":{"url":"git+https://github.com/vercel-labs/emulate.git","type":"git","directory":"packages/emulate"},"_npmVersion":"11.17.0","description":"Local drop-in replacement services for CI and no-network sandboxes","directories":{},"_nodeVersion":"24.19.0","dependencies":{"yaml":"^2","commander":"^14","picocolors":"^1.1.1"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/aws":"0.10.0","@emulators/core":"0.10.0","@emulators/okta":"0.10.0","@emulators/apple":"0.10.0","@emulators/clerk":"0.10.0","@emulators/slack":"0.10.0","@emulators/github":"0.10.0","@emulators/google":"0.10.0","@emulators/linear":"0.10.0","@emulators/resend":"0.10.0","@emulators/stripe":"0.10.0","@emulators/twilio":"0.10.0","@emulators/vercel":"0.10.0","@emulators/microsoft":"0.10.0","@emulators/mongoatlas":"0.10.0"},"_npmOperationalInternal":{"tmp":"tmp/emulate_0.10.0_1786996708387_0.33714261304705295","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"emulate","version":"0.11.0","description":"Local drop-in replacement services for CI and no-network sandboxes","license":"Apache-2.0","type":"module","exports":{".":{"import":"./dist/api.js","types":"./dist/api.d.ts"},"./cli":{"import":"./dist/index.js"}},"main":"./dist/api.js","types":"./dist/api.d.ts","homepage":"https://emulate.dev","repository":{"type":"git","url":"git+https://github.com/vercel-labs/emulate.git","directory":"packages/emulate"},"bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"bin":{"emulate":"dist/index.js"},"dependencies":{"commander":"^14","picocolors":"^1.1.1","yaml":"^2"},"devDependencies":{"tsup":"^8","typescript":"^5.7","@emulators/apple":"0.11.0","@emulators/github":"0.11.0","@emulators/microsoft":"0.11.0","@emulators/okta":"0.11.0","@emulators/aws":"0.11.0","@emulators/core":"0.11.0","@emulators/slack":"0.11.0","@emulators/mongoatlas":"0.11.0","@emulators/google":"0.11.0","@emulators/vercel":"0.11.0","@emulators/resend":"0.11.0","@emulators/clerk":"0.11.0","@emulators/linear":"0.11.0","@emulators/twilio":"0.11.0","@emulators/stripe":"0.11.0"},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","clean":"rm -rf dist .turbo","type-check":"tsc --noEmit","lint":"eslint src"},"_id":"emulate@0.11.0","_integrity":"sha512-R2kKebrA5MZRKv0U3vNJuOHPbtK34KZlNCTvVMAC6y/LuAD4jjNFgkMX79fth3ETrCXWA5IB/PwiID0Y04s0cw==","_resolved":"/tmp/emulate-0.11.0.tgz","_from":"file:/tmp/emulate-0.11.0.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-R2kKebrA5MZRKv0U3vNJuOHPbtK34KZlNCTvVMAC6y/LuAD4jjNFgkMX79fth3ETrCXWA5IB/PwiID0Y04s0cw==","shasum":"18db2dcca010bd598809c4df1fdf9fb61a2ac6ac","tarball":"https://registry.npmjs.org/emulate/-/emulate-0.11.0.tgz","fileCount":47,"unpackedSize":7758287,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/emulate@0.11.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCuxQ672CAJJavabzF1uLhocVpN+QNAPWdkngC63Scv2QIgOpKHpdB8z+X9bomgyma3PSgU1t3IEz+Uy9Buu+A6CyY="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:3caa4f87-a22c-4052-b79b-ac8865ac24f7"}},"directories":{},"maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/emulate_0.11.0_1788393163433_0.8485408379148831"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-19T19:45:49.031Z","modified":"2026-09-02T23:52:43.874Z","1.0.0":"2016-11-30T02:52:54.252Z","1.0.1":"2016-12-07T02:32:53.463Z","1.0.2":"2016-12-07T02:37:35.959Z","0.0.0":"2026-03-19T19:45:49.298Z","0.1.0":"2026-03-20T18:30:10.073Z","0.1.1":"2026-03-20T18:33:18.811Z","0.2.0":"2026-03-22T16:31:04.474Z","0.3.0":"2026-03-26T20:35:23.843Z","0.4.0":"2026-04-02T17:38:19.053Z","0.4.1":"2026-04-02T19:27:28.428Z","0.5.0":"2026-04-29T05:49:35.977Z","0.6.0":"2026-05-24T17:50:42.865Z","0.6.1":"2026-06-11T23:11:14.074Z","0.7.0":"2026-06-18T20:53:54.881Z","0.8.0":"2026-06-24T20:23:57.689Z","0.9.0":"2026-07-03T15:50:23.523Z","0.10.0":"2026-08-17T19:58:28.583Z","0.11.0":"2026-09-02T23:52:43.579Z"},"bugs":{"url":"https://github.com/vercel-labs/emulate/issues"},"license":"Apache-2.0","homepage":"https://emulate.dev","keywords":["emulator","mock","api","vercel","github","google","oauth","testing","ci","local-development"],"repository":{"type":"git","url":"git+https://github.com/vercel-labs/emulate.git","directory":"packages/emulate"},"description":"Local drop-in replacement services for CI and no-network sandboxes","maintainers":[{"name":"ctate","email":"chris@ctate.dev"}],"readme":"# emulate\n\nLocal drop-in replacement services for CI and no-network sandboxes. Fully stateful, production-fidelity API emulation. Not mocks.\n\n## Quick Start\n\n```bash\nnpx emulate\n```\n\nAll services start with sensible defaults. No config file needed:\n\n- **Vercel** on `http://localhost:4000`\n- **GitHub** on `http://localhost:4001`\n- **Google** on `http://localhost:4002`\n- **Slack** on `http://localhost:4003`\n- **Apple** on `http://localhost:4004`\n- **Microsoft** on `http://localhost:4005`\n- **Okta** on `http://localhost:4006`\n- **AWS** on `http://localhost:4007`\n- **Resend** on `http://localhost:4008`\n- **Stripe** on `http://localhost:4009`\n- **MongoDB Atlas** on `http://localhost:4010`\n- **Clerk** on `http://localhost:4011`\n- **Linear** on `http://localhost:4012`\n- **Twilio** on `http://localhost:4013`\n\nStripe webhooks configured with a secret include a `Stripe-Signature` header signed over the timestamp and raw request body.\n\n## CLI\n\n```bash\n# Start all services (zero-config)\nnpx emulate\n\n# Start specific services\nnpx emulate --service vercel,github\n\n# Custom port\nnpx emulate --port 3000\n\n# Use a seed config file\nnpx emulate --seed config.yaml\n\n# Generate omitted service secrets into a private file\nnpx emulate start --seed config.yaml --generated-secrets-file .emulate-secrets.json\n\n# Generate a starter config\nnpx emulate init\n\n# Generate config for a specific service\nnpx emulate init --service vercel\n\n# List available services\nnpx emulate list\n```\n\n### Options\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `-p, --port` | `4000` | Base port (auto-increments per service) |\n| `-s, --service` | all | Comma-separated services to enable |\n| `--seed` | auto-detect | Path to seed config (YAML or JSON) |\n| `--base-url` | none | Override advertised base URL (supports `{service}` template) |\n| `--portless` | off | Serve over HTTPS via portless (auto-registers aliases) |\n| `--generated-secrets-file` | none | Generate omitted service secrets and write them to a new owner-only JSON file |\n\nThe port can also be set via `EMULATE_PORT` or `PORT` environment variables.\n\n## HTTPS with portless\n\n[portless](https://github.com/vercel-labs/portless) gives emulators trusted HTTPS URLs with auto-generated certs and no browser warnings.\n\n```bash\n# Start the portless proxy (first time only)\nportless proxy start\n\n# Start emulate with portless integration\nnpx emulate start --portless\n```\n\nEach service registers as a portless alias and gets a named HTTPS URL:\n\n```\ngithub  https://github.emulate.localhost\ngoogle  https://google.emulate.localhost\nslack   https://slack.emulate.localhost\n```\n\nIf portless is not installed, emulate will prompt to install it (`npm i -g portless`).\n\nThe `--portless` flag overwrites any existing portless aliases matching `*.emulate`. Aliases are removed automatically when emulate shuts down.\n\nFor a custom base URL without portless (any reverse proxy), use `--base-url` or the `EMULATE_BASE_URL` env var:\n\n```bash\nnpx emulate start --base-url \"https://{service}.myproxy.test\"\n```\n\nThe `PORTLESS_URL` env var is automatically set by the `portless` CLI wrapper when running a command through it (e.g. `portless github.emulate emulate start`), typically to a value like `https://{service}.emulate.localhost`. It supports `{service}` interpolation, just like `--base-url` and `EMULATE_BASE_URL`. When no explicit `baseUrl` is provided, it is used as a fallback.\n\nPer-service overrides are also supported in the seed config (these take highest priority over all other base URL sources):\n\n```yaml\ngithub:\n  baseUrl: https://github.emulate.localhost\n```\n\n## Programmatic API\n\n```bash\nnpm install emulate\n```\n\nEach call to `createEmulator` starts a single service:\n\n```typescript\nimport { createEmulator } from 'emulate'\n\nconst github = await createEmulator({ service: 'github', port: 4001 })\nconst vercel = await createEmulator({ service: 'vercel', port: 4002 })\n\ngithub.url   // 'http://localhost:4001'\nvercel.url   // 'http://localhost:4002'\n\nawait github.close()\nawait vercel.close()\n```\n\nWhen a GitHub App omits `private_key`, `createEmulator` generates an RSA-2048 PKCS#1 key for that emulator instance:\n\n```typescript\nconst github = await createEmulator({\n  service: 'github',\n  seed: {\n    github: {\n      users: [{ login: 'octocat' }],\n      apps: [{\n        app_id: 12345,\n        slug: 'my-github-app',\n        name: 'My GitHub App',\n        installations: [{ installation_id: 100, account: 'octocat' }],\n      }],\n    },\n  },\n})\n\nconst privateKey = github.generatedSecrets.find(\n  secret => secret.kind === 'github.app_private_key' && secret.id === '12345',\n)?.value\n```\n\nGenerated keys remain stable across `reset()` calls and appear only in `generatedSecrets`. Explicitly configured keys are never returned there. A new `createEmulator` call generates a new key.\n\nThe CLI can also generate omitted GitHub App keys when a delivery file is requested:\n\n```bash\nnpx emulate start --service github --seed config.yaml \\\n  --generated-secrets-file .emulate-secrets.json\n```\n\nThe destination must not exist. emulate removes inherited ACLs, verifies effective owner-only access, and publishes complete JSON before opening listeners or configuring portless. Handled startup failures remove the invocation-owned artifact so the command can be retried immediately. A hard termination such as `SIGKILL` can leave a complete published artifact that must be removed manually after confirming no invocation is using it. Only generated secrets are included. Explicitly configured keys are never copied into the artifact. Linux requires `setfacl` and `getfacl` from the `acl` package. The flag fails closed when access controls cannot be verified and is not supported on Windows. Without `--generated-secrets-file`, CLI seed files keep requiring `private_key`.\n\n### Vitest / Jest setup\n\n```typescript\n// vitest.setup.ts\nimport { createEmulator, type Emulator } from 'emulate'\n\nlet github: Emulator\nlet vercel: Emulator\n\nbeforeAll(async () => {\n  ;[github, vercel] = await Promise.all([\n    createEmulator({ service: 'github', port: 4001 }),\n    createEmulator({ service: 'vercel', port: 4002 }),\n  ])\n  process.env.GITHUB_EMULATOR_URL = github.url\n  process.env.VERCEL_EMULATOR_URL = vercel.url\n})\n\nafterEach(() => { github.reset(); vercel.reset() })\nafterAll(() => Promise.all([github.close(), vercel.close()]))\n```\n\n### Options\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `service` | *(required)* | Service name: `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, `'okta'`, `'aws'`, `'resend'`, `'stripe'`, `'mongoatlas'`, `'clerk'`, `'linear'`, or `'twilio'` |\n| `port` | `4000` | Port for the HTTP server |\n| `seed` | none | Inline seed data (same shape as YAML config) |\n| `baseUrl` | none | Override advertised base URL. Per-service `baseUrl` in seed config takes highest priority, then this option, then `EMULATE_BASE_URL` env var (supports `{service}`), then `PORTLESS_URL` (supports `{service}`, automatically set by the `portless` CLI wrapper), then `http://localhost:<port>`. |\n\n### Instance methods\n\n| Method | Description |\n|--------|-------------|\n| `url` | Base URL of the running server |\n| `generatedSecrets` | Readonly secrets generated while preparing seed data |\n| `reset()` | Wipe the store and replay seed data |\n| `close()` | Shut down the HTTP server, returns a Promise |\n\n## Configuration\n\nConfiguration is optional. The CLI auto-detects config files in this order: `emulate.config.yaml` / `.yml`, `emulate.config.json`, `service-emulator.config.yaml` / `.yml`, `service-emulator.config.json`. Or pass `--seed <file>` explicitly. Run `npx emulate init` to generate a starter file.\n\n```yaml\ntokens:\n  my_token:\n    login: admin\n    scopes: [repo, user]\n\nvercel:\n  users:\n    - username: developer\n      name: Developer\n      email: dev@example.com\n  teams:\n    - slug: my-team\n      name: My Team\n  projects:\n    - name: my-app\n      team: my-team\n      framework: nextjs\n\ngithub:\n  users:\n    - login: octocat\n      name: The Octocat\n      email: octocat@github.com\n  orgs:\n    - login: my-org\n      name: My Organization\n  repos:\n    - owner: octocat\n      name: hello-world\n      language: JavaScript\n      auto_init: true\n\ngoogle:\n  users:\n    - email: testuser@example.com\n      name: Test User\n    - email: admin@acme.com\n      name: Admin\n      hd: acme.com\n  oauth_clients:\n    - client_id: my-client-id.apps.googleusercontent.com\n      client_secret: GOCSPX-secret\n      redirect_uris:\n        - http://localhost:3000/api/auth/callback/google\n  labels:\n    - id: Label_ops\n      user_email: testuser@example.com\n      name: Ops/Review\n      color_background: \"#DDEEFF\"\n      color_text: \"#111111\"\n  messages:\n    - id: msg_welcome\n      user_email: testuser@example.com\n      from: welcome@example.com\n      to: testuser@example.com\n      subject: Welcome to the Gmail emulator\n      body_text: You can now test Gmail, Calendar, and Drive flows locally.\n      label_ids: [INBOX, UNREAD, CATEGORY_UPDATES]\n  calendars:\n    - id: primary\n      user_email: testuser@example.com\n      summary: testuser@example.com\n      primary: true\n      selected: true\n      time_zone: UTC\n  calendar_events:\n    - id: evt_kickoff\n      user_email: testuser@example.com\n      calendar_id: primary\n      summary: Project Kickoff\n      start_date_time: 2025-01-10T09:00:00.000Z\n      end_date_time: 2025-01-10T09:30:00.000Z\n  drive_items:\n    - id: drv_docs\n      user_email: testuser@example.com\n      name: Docs\n      mime_type: application/vnd.google-apps.folder\n      parent_ids: [root]\n\nslack:\n  team:\n    name: My Workspace\n    domain: my-workspace\n  users:\n    - name: developer\n      real_name: Developer\n      email: dev@example.com\n      profile:\n        title: Local Developer\n        status_text: Testing locally\n        status_emoji: \":computer:\"\n      presence: active\n  channels:\n    - name: general\n      topic: General discussion\n    - name: random\n      topic: Random stuff\n  bots:\n    - name: my-bot\n  oauth_apps:\n    - client_id: \"12345.67890\"\n      client_secret: example_client_secret\n      app_id: A000000001\n      name: My Slack App\n      redirect_uris:\n        - http://localhost:3000/api/auth/callback/slack\n      scopes:\n        - chat:write\n        - channels:read\n        - channels:history\n        - channels:join\n        - channels:manage\n        - channels:write\n        - groups:read\n        - groups:history\n        - groups:write\n        - im:read\n        - im:history\n        - im:write\n        - mpim:read\n        - mpim:history\n        - mpim:write\n        - users:read\n        - users:read.email\n        - users.profile:read\n        - users.profile:write\n        - users:write\n        - files:read\n        - files:write\n        - pins:read\n        - pins:write\n        - bookmarks:read\n        - bookmarks:write\n        - reactions:read\n        - reactions:write\n        - team:read\n      user_scopes: [users:read, users.profile:read]\n      bot_name: my-bot\n  tokens:\n    - token: xoxb-local-test\n      user: developer\n      scopes:\n        - chat:write\n        - channels:read\n        - channels:history\n        - channels:join\n        - channels:manage\n        - channels:write\n        - groups:read\n        - groups:history\n        - groups:write\n        - im:read\n        - im:history\n        - im:write\n        - mpim:read\n        - mpim:history\n        - mpim:write\n        - users:read\n        - users:read.email\n        - users.profile:read\n        - users.profile:write\n        - users:write\n        - files:read\n        - files:write\n        - pins:read\n        - pins:write\n        - bookmarks:read\n        - bookmarks:write\n        - reactions:read\n        - reactions:write\n        - team:read\n  strict_scopes: false\n\nlinear:\n  organization:\n    name: Acme\n    url_key: acme\n  users:\n    - email: admin@example.com\n      name: Admin User\n      admin: true\n    - email: dev@example.com\n      name: Developer\n  teams:\n    - key: ENG\n      name: Engineering\n      states:\n        - name: Backlog\n          type: backlog\n        - name: Todo\n          type: unstarted\n        - name: In Progress\n          type: started\n        - name: Done\n          type: completed\n  labels:\n    - name: Bug\n      color: \"#d92d20\"\n      team: ENG\n  issues:\n    - team: ENG\n      title: Fix local checkout test\n      state: Todo\n      assignee: dev@example.com\n      labels: [Bug]\n  oauth_apps:\n    - client_id: lin_example_client_id\n      client_secret: example_client_secret\n      name: My Linear App\n      redirect_uris:\n        - http://localhost:3000/api/auth/callback/linear\n      scopes: [read, write, issues:create, comments:create]\n  tokens:\n    - token: lin_test_admin\n      user: admin@example.com\n      scopes: [read, write, issues:create, comments:create, admin]\n  strict_scopes: false\n\napple:\n  users:\n    - email: testuser@icloud.com\n      name: Test User\n  oauth_clients:\n    - client_id: com.example.app\n      team_id: TEAM001\n      name: My Apple App\n      redirect_uris:\n        - http://localhost:3000/api/auth/callback/apple\n\nmicrosoft:\n  users:\n    - email: testuser@outlook.com\n      name: Test User\n  oauth_clients:\n    - client_id: example-client-id\n      client_secret: example-client-secret\n      name: My Microsoft App\n      redirect_uris:\n        - http://localhost:3000/api/auth/callback/microsoft-entra-id\n\naws:\n  region: us-east-1\n  s3:\n    buckets:\n      - name: my-app-bucket\n      - name: my-app-uploads\n  sqs:\n    queues:\n      - name: my-app-events\n      - name: my-app-dlq\n  iam:\n    users:\n      - user_name: developer\n        create_access_key: true\n    roles:\n      - role_name: lambda-execution-role\n        description: Role for Lambda function execution\n```\n\n## OAuth & Integrations\n\nThe emulator supports configurable OAuth apps and integrations with strict client validation.\n\n### Vercel Integrations\n\n```yaml\nvercel:\n  integrations:\n    - client_id: \"oac_abc123\"\n      client_secret: \"secret_abc123\"\n      name: \"My Vercel App\"\n      redirect_uris:\n        - \"http://localhost:3000/api/auth/callback/vercel\"\n```\n\n### GitHub OAuth Apps\n\n```yaml\ngithub:\n  oauth_apps:\n    - client_id: \"Iv1.abc123\"\n      client_secret: \"secret_abc123\"\n      name: \"My Web App\"\n      redirect_uris:\n        - \"http://localhost:3000/api/auth/callback/github\"\n```\n\nIf no `oauth_apps` are configured, the emulator accepts any `client_id` (backward-compatible). With apps configured, strict validation is enforced.\n\n### GitHub Apps\n\nFull GitHub App support with JWT authentication and installation access tokens:\n\n```yaml\ngithub:\n  apps:\n    - app_id: 12345\n      slug: \"my-github-app\"\n      name: \"My GitHub App\"\n      private_key: |\n        -----BEGIN RSA PRIVATE KEY-----\n        ...your PEM key...\n        -----END RSA PRIVATE KEY-----\n      permissions:\n        contents: read\n        issues: write\n      events: [push, pull_request]\n      webhook_url: \"http://localhost:3000/webhooks/github\"\n      webhook_secret: \"my-secret\"\n      installations:\n        - installation_id: 100\n          account: my-org\n          repository_selection: all\n```\n\nJWT authentication: sign a JWT with `{ iss: \"<app_id>\" }` using the app's private key (RS256). The emulator verifies the signature and resolves the app.\n\nInspect secret-free metadata for minted installation tokens at `GET /_emulate/installation-tokens`.\n\n**App webhook delivery**: When events occur on repos where a GitHub App is installed, the emulator mirrors real GitHub behavior:\n- All webhook payloads (including repo and org hooks) include an `installation` field with `{ id, node_id }`.\n- If the app has a `webhook_url`, the emulator delivers the event there with the `installation` field and (if configured) an `X-Hub-Signature-256` header signed with `webhook_secret`.\n\n### Slack OAuth Apps\n\n```yaml\nslack:\n  oauth_apps:\n    - client_id: \"12345.67890\"\n      client_secret: \"example_client_secret\"\n      name: \"My Slack App\"\n      redirect_uris:\n        - \"http://localhost:3000/api/auth/callback/slack\"\n```\n\n### Linear OAuth Apps\n\n```yaml\nlinear:\n  oauth_apps:\n    - client_id: \"lin_example_client_id\"\n      client_secret: \"example_client_secret\"\n      name: \"My Linear App\"\n      redirect_uris:\n        - \"http://localhost:3000/api/auth/callback/linear\"\n      scopes: [read, write, issues:create, comments:create]\n      actor: user\n```\n\n### Apple OAuth Clients\n\n```yaml\napple:\n  oauth_clients:\n    - client_id: \"com.example.app\"\n      team_id: \"TEAM001\"\n      name: \"My Apple App\"\n      redirect_uris:\n        - \"http://localhost:3000/api/auth/callback/apple\"\n```\n\n### Microsoft OAuth Clients\n\n```yaml\nmicrosoft:\n  oauth_clients:\n    - client_id: \"example-client-id\"\n      client_secret: \"example-client-secret\"\n      name: \"My Microsoft App\"\n      redirect_uris:\n        - \"http://localhost:3000/api/auth/callback/microsoft-entra-id\"\n```\n\n## Vercel API\n\nEvery endpoint below is fully stateful with Vercel-style JSON responses and cursor-based pagination.\n\n### User & Teams\n- `GET /v2/user` - authenticated user\n- `PATCH /v2/user` - update user\n- `GET /v2/teams` - list teams (cursor paginated)\n- `GET /v2/teams/:teamId` - get team (by ID or slug)\n- `POST /v2/teams` - create team\n- `PATCH /v2/teams/:teamId` - update team\n- `GET /v2/teams/:teamId/members` - list members\n- `POST /v2/teams/:teamId/members` - add member\n\n### Projects\n- `POST /v11/projects` - create project (with optional env vars and git integration)\n- `GET /v10/projects` - list projects (search, cursor pagination)\n- `GET /v9/projects/:idOrName` - get project (includes env vars)\n- `PATCH /v9/projects/:idOrName` - update project\n- `DELETE /v9/projects/:idOrName` - delete project (cascades)\n- `GET /v1/projects/:projectId/promote/aliases` - promote aliases status\n- `PATCH /v1/projects/:idOrName/protection-bypass` - manage bypass secrets\n\n### Deployments\n- `POST /v13/deployments` - create deployment (auto-transitions to READY)\n- `GET /v13/deployments/:idOrUrl` - get deployment (by ID or URL)\n- `GET /v6/deployments` - list deployments (filter by project, target, state)\n- `DELETE /v13/deployments/:id` - delete deployment (cascades)\n- `PATCH /v12/deployments/:id/cancel` - cancel building deployment\n- `GET /v2/deployments/:id/aliases` - list deployment aliases\n- `GET /v3/deployments/:idOrUrl/events` - get build events/logs\n- `GET /v6/deployments/:id/files` - list deployment files\n- `POST /v2/files` - upload file (by SHA digest)\n\n### Domains\n- `POST /v10/projects/:idOrName/domains` - add domain (with verification challenge)\n- `GET /v9/projects/:idOrName/domains` - list domains\n- `GET /v9/projects/:idOrName/domains/:domain` - get domain\n- `PATCH /v9/projects/:idOrName/domains/:domain` - update domain\n- `DELETE /v9/projects/:idOrName/domains/:domain` - remove domain\n- `POST /v9/projects/:idOrName/domains/:domain/verify` - verify domain\n\n### Environment Variables\n- `GET /v10/projects/:idOrName/env` - list env vars (with decrypt option)\n- `POST /v10/projects/:idOrName/env` - create env vars (single, batch, upsert)\n- `GET /v10/projects/:idOrName/env/:id` - get env var\n- `PATCH /v9/projects/:idOrName/env/:id` - update env var\n- `DELETE /v9/projects/:idOrName/env/:id` - delete env var\n\n### Blob\nImplements the Vercel Blob API used by the `@vercel/blob` SDK (`put`, `head`, `list`, `del`).\n\n- `PUT /api/blob?pathname=<path>` - upload a blob (honors `x-add-random-suffix`, `x-allow-overwrite`, `x-content-type`, `x-cache-control-max-age`, `x-if-match` headers)\n- `GET /api/blob?url=<urlOrPathname>` - blob metadata (`head()`)\n- `GET /api/blob?prefix=&limit=&cursor=&mode=` - list blobs (`list()`, including folded mode)\n- `POST /api/blob/delete` - delete blobs (`del()`)\n- `GET /blob/:storeId/<pathname>` - serve blob content (public, no auth; `?download=1` adds an attachment disposition)\n\nPoint the SDK at the emulator with two environment variables:\n\n```bash\nVERCEL_BLOB_API_URL=http://localhost:4000/api/blob\nBLOB_READ_WRITE_TOKEN=vercel_blob_rw_mystore_secret\n```\n\nAny token of the form `vercel_blob_rw_<storeId>_<secret>` is accepted; the store id is parsed from the token. Multipart uploads and client (browser) uploads are not supported yet.\n\n## GitHub API\n\nEvery endpoint below is fully stateful. Creates, updates, and deletes persist in memory and affect related entities.\n\n### Users\n- `GET /user` - authenticated user\n- `PATCH /user` - update profile\n- `GET /users/:username` - get user\n- `GET /users` - list users\n- `GET /users/:username/repos` - list user repos\n- `GET /users/:username/orgs` - list user orgs\n- `GET /users/:username/followers` - list followers\n- `GET /users/:username/following` - list following\n\n### Repositories\n- `GET /repos/:owner/:repo` - get repo\n- `GET /repositories/:id` - get repo by numeric ID\n- `POST /user/repos` - create user repo\n- `POST /orgs/:org/repos` - create org repo\n- `PATCH /repos/:owner/:repo` - update repo\n- `DELETE /repos/:owner/:repo` - delete repo (cascades)\n- `GET/PUT /repos/:owner/:repo/topics` - get/replace topics\n- `GET /repos/:owner/:repo/languages` - languages\n- `GET /repos/:owner/:repo/contributors` - contributors\n- `GET /repos/:owner/:repo/forks` - list forks\n- `POST /repos/:owner/:repo/forks` - create fork\n- `GET/PUT/DELETE /repos/:owner/:repo/collaborators/:username` - collaborators\n- `GET /repos/:owner/:repo/collaborators/:username/permission`\n- `POST /repos/:owner/:repo/transfer` - transfer repo\n- `GET /repos/:owner/:repo/tags` - list tags\n\n### Contents & Commit History\n- `GET /repos/:owner/:repo/readme` - get the repository README\n- `GET /repos/:owner/:repo/contents/:path` - get a file or list a directory at a ref\n- `GET /:owner/:repo/raw/:ref/:path` - download file content from advertised raw URLs\n- `PUT/DELETE /repos/:owner/:repo/contents/:path` - create, update, or delete a file and commit the change\n- `GET /repos/:owner/:repo/commits` - list commits with ref, path, author, and date filters\n- `GET /repos/:owner/:repo/commits/:ref` - get a commit with file diffs and stats\n- `GET /repos/:owner/:repo/compare/:base...:head` - compare two refs\n\n### Issues\n- `GET /repos/:owner/:repo/issues` - list (filter by state, labels, assignee, milestone, creator, since)\n- `POST /repos/:owner/:repo/issues` - create\n- `GET /repos/:owner/:repo/issues/:number` - get\n- `PATCH /repos/:owner/:repo/issues/:number` - update (state transitions, events)\n- `PUT/DELETE /repos/:owner/:repo/issues/:number/lock` - lock/unlock\n- `GET /repos/:owner/:repo/issues/:number/timeline` - timeline events\n- `GET /repos/:owner/:repo/issues/:number/events` - events\n- `POST/DELETE /repos/:owner/:repo/issues/:number/assignees` - manage assignees\n\n### Pull Requests\n- `GET /repos/:owner/:repo/pulls` - list (filter by state, head, base)\n- `POST /repos/:owner/:repo/pulls` - create\n- `GET /repos/:owner/:repo/pulls/:number` - get\n- `PATCH /repos/:owner/:repo/pulls/:number` - update\n- `PUT /repos/:owner/:repo/pulls/:number/merge` - merge (with branch protection enforcement)\n- `GET /repos/:owner/:repo/pulls/:number/commits` - list commits\n- `GET /repos/:owner/:repo/pulls/:number/files` - list files\n- `POST/DELETE /repos/:owner/:repo/pulls/:number/requested_reviewers` - manage reviewers\n- `PUT /repos/:owner/:repo/pulls/:number/update-branch` - update branch\n\n### Comments\n- Issue comments: full CRUD on `/repos/:owner/:repo/issues/:number/comments`\n- Review comments: full CRUD on `/repos/:owner/:repo/pulls/:number/comments`\n- Commit comments: full CRUD on `/repos/:owner/:repo/commits/:sha/comments`\n- Repo-wide listings for each type\n\n### Reviews\n- `GET /repos/:owner/:repo/pulls/:number/reviews` - list\n- `POST /repos/:owner/:repo/pulls/:number/reviews` - create (with inline comments)\n- `GET/PUT /repos/:owner/:repo/pulls/:number/reviews/:id` - get/update\n- `POST /repos/:owner/:repo/pulls/:number/reviews/:id/events` - submit\n- `PUT /repos/:owner/:repo/pulls/:number/reviews/:id/dismissals` - dismiss\n\n### Labels & Milestones\n- Labels: full CRUD, add/remove from issues, replace all\n- Milestones: full CRUD, state transitions, issue counts\n\n### Branches & Git Data\n- Branches: list, get, protection CRUD (status checks, PR reviews, enforce admins)\n- Refs: get, match, create, update, delete\n- Commits: get, create\n- Trees: get (with recursive), create (with inline content)\n- Blobs: get, create\n- Tags: get, create\n\n### Organizations & Teams\n- Orgs: get, update, list\n- Org members: list, check, remove, get/set membership\n- Teams: full CRUD, members, repos\n\n### Releases\n- Releases: full CRUD, latest, by tag\n- Release assets: full CRUD, upload\n- Generate release notes\n\n### Webhooks\n- Repo webhooks: full CRUD, ping, test, deliveries\n- Org webhooks: full CRUD, ping\n- Real HTTP delivery to registered URLs on all state changes\n\n### Search\n- `GET /search/repositories` - full query syntax (user, org, language, topic, stars, forks, etc.)\n- `GET /search/issues` - issues + PRs (repo, is, author, label, milestone, state, etc.)\n- `GET /search/users` - users + orgs\n- `GET /search/code` - blob content search\n- `GET /search/commits` - commit message search\n- `GET /search/topics` - topic search\n- `GET /search/labels` - label search\n\n### Actions\n- Workflows: list, get, enable/disable, dispatch\n- Workflow runs: list, get, cancel, rerun, delete, logs\n- Jobs: list, get, logs\n- Artifacts: list, get, delete\n- Secrets: repo + org CRUD\n\n### Checks\n- Check runs: create, update, get, annotations, rerequest, list by ref/suite\n- Check suites: create, get, preferences, rerequest, list by ref\n- Automatic suite status rollup from check run results\n\n### Misc\n- `GET /rate_limit` - rate limit status\n- `GET /meta` - server metadata\n- `GET /octocat` - ASCII art\n- `GET /emojis` - emoji URLs\n- `GET /zen` - random zen phrase\n- `GET /versions` - API versions\n\n## Google OAuth + Gmail, Calendar, and Drive APIs\n\nOAuth 2.0, OpenID Connect, and mutable Google Workspace-style surfaces for local inbox, calendar, and drive flows.\n\n- `GET /o/oauth2/v2/auth` - authorization endpoint\n- `POST /oauth2/token` - token exchange\n- `GET /oauth2/v2/userinfo` - get user info\n- `GET /.well-known/openid-configuration` - OIDC discovery document\n- `GET /oauth2/v3/certs` - JSON Web Key Set (JWKS)\n- `GET /gmail/v1/users/:userId/messages` - list messages with `q`, `labelIds`, `maxResults`, and `pageToken`\n- `GET /gmail/v1/users/:userId/messages/:id` - fetch a Gmail-style message payload in `full`, `metadata`, `minimal`, or `raw` formats\n- `GET /gmail/v1/users/:userId/messages/:messageId/attachments/:id` - fetch attachment bodies\n- `POST /gmail/v1/users/:userId/messages/send` - create sent mail from `raw` MIME or structured fields\n- `POST /gmail/v1/users/:userId/messages/import` - import inbox mail\n- `POST /gmail/v1/users/:userId/messages` - insert a message directly\n- `POST /gmail/v1/users/:userId/messages/:id/modify` - add/remove labels on one message\n- `POST /gmail/v1/users/:userId/messages/batchModify` - add/remove labels across many messages\n- `POST /gmail/v1/users/:userId/messages/:id/trash` and `POST /gmail/v1/users/:userId/messages/:id/untrash`\n- `GET /gmail/v1/users/:userId/drafts`, `POST /gmail/v1/users/:userId/drafts`, `GET /gmail/v1/users/:userId/drafts/:id`, `PUT /gmail/v1/users/:userId/drafts/:id`, `POST /gmail/v1/users/:userId/drafts/:id/send`, `DELETE /gmail/v1/users/:userId/drafts/:id`\n- `POST /gmail/v1/users/:userId/threads/:id/modify` - add/remove labels across a thread\n- `GET /gmail/v1/users/:userId/threads` and `GET /gmail/v1/users/:userId/threads/:id`\n- `GET /gmail/v1/users/:userId/labels`, `POST /gmail/v1/users/:userId/labels`, `PATCH /gmail/v1/users/:userId/labels/:id`, `DELETE /gmail/v1/users/:userId/labels/:id`\n- `GET /gmail/v1/users/:userId/history`, `POST /gmail/v1/users/:userId/watch`, `POST /gmail/v1/users/:userId/stop`\n- `GET /gmail/v1/users/:userId/settings/filters`, `POST /gmail/v1/users/:userId/settings/filters`, `DELETE /gmail/v1/users/:userId/settings/filters/:id`\n- `GET /gmail/v1/users/:userId/settings/forwardingAddresses`, `GET /gmail/v1/users/:userId/settings/sendAs`\n- `GET /calendar/v3/users/:userId/calendarList`, `GET /calendar/v3/calendars/:calendarId/events`, `POST /calendar/v3/calendars/:calendarId/events`, `DELETE /calendar/v3/calendars/:calendarId/events/:eventId`, `POST /calendar/v3/freeBusy`\n- `GET /drive/v3/files`, `GET /drive/v3/files/:fileId`, `POST /drive/v3/files`, `PATCH /drive/v3/files/:fileId`, `PUT /drive/v3/files/:fileId`, `POST /upload/drive/v3/files`\n\n## Slack API\n\nFully stateful Slack Web API emulation with channels, messages, threads, reactions, user profiles, presence, modern file uploads, pins, bookmarks, views, OAuth v2, and incoming webhooks. Chat writes preserve common rich message fields such as `blocks`, `attachments`, `metadata`, formatting flags, unfurl flags, and client message ids. Conversation writes update archive state, names, topics, purposes, membership, DMs, MPIMs, and read cursors. User writes update profile fields, status, custom fields, and deterministic active or away presence. File writes support the current external upload flow with local upload URLs, file share messages, reads, lists, downloads, and deletes. Pin and bookmark writes support channel message pins and link bookmarks. View writes support App Home publishing and modal stacks. Seeded OAuth apps and OAuth installs create bot users and installation records. OAuth exchanges and explicit token seeds create scoped token records. Supported write state changes dispatch Slack `event_callback` payloads to configured webhook URLs.\n\n### Auth & Chat\n- `POST /api/auth.test` - test authentication\n- `POST /api/chat.postMessage` - post message with text or rich payload fields (supports threads via `thread_ts` and DM user IDs)\n- `POST /api/chat.postEphemeral` - post ephemeral message outside channel history\n- `POST /api/chat.update` - update message text and rich payload fields\n- `POST /api/chat.delete` - delete message\n- `GET /api/chat.getPermalink` / `POST /api/chat.getPermalink` - get message permalink\n- `POST /api/chat.scheduleMessage` - schedule pending message\n- `POST /api/chat.deleteScheduledMessage` - delete pending scheduled message\n- `POST /api/chat.scheduledMessages.list` - list pending scheduled messages\n- `POST /api/chat.meMessage` - /me message\n\n### Conversations\n- `POST /api/conversations.list` - list conversations (cursor pagination, `types`, `exclude_archived`)\n- `POST /api/conversations.info` - get channel info\n- `POST /api/conversations.create` - create channel\n- `POST /api/conversations.archive` / `conversations.unarchive` - archive/restore channel\n- `POST /api/conversations.rename` - rename channel\n- `POST /api/conversations.setTopic` / `conversations.setPurpose` - update topic/purpose\n- `POST /api/conversations.history` - channel history with rich message fields\n- `POST /api/conversations.replies` - thread replies with rich message fields\n- `POST /api/conversations.join` / `conversations.leave` - join/leave\n- `POST /api/conversations.invite` / `conversations.kick` - manage membership\n- `POST /api/conversations.open` / `conversations.close` - open/close DMs and MPIMs\n- `POST /api/conversations.mark` - mark read cursor\n- `POST /api/conversations.members` - list members\n\n### Users & Reactions\n- `POST /api/users.list` - list users (cursor pagination)\n- `POST /api/users.info` - get user info\n- `POST /api/users.lookupByEmail` - lookup by email\n- `GET /api/users.profile.get` / `POST /api/users.profile.get` - get user profile fields\n- `POST /api/users.profile.set` - update profile fields, status, and custom fields\n- `GET /api/users.getPresence` / `POST /api/users.getPresence` - get active or away presence\n- `POST /api/users.setPresence` - set the authed user to away or automatic presence\n- `POST /api/reactions.add` / `reactions.remove` / `reactions.get` - manage reactions\n\n### Files\n- `POST /api/files.getUploadURLExternal` - create a local external upload session\n- `POST /upload/v1/:fileId` - receive raw uploaded file bytes\n- `POST /api/files.completeUploadExternal` - complete uploads and optionally share file messages\n- `GET /api/files.info` / `POST /api/files.info` - get file metadata\n- `GET /api/files.list` / `POST /api/files.list` - list completed files\n- `GET /files-pri/:fileId/:filename` - download file bytes with a bearer token that can access the file\n- `POST /api/files.delete` - delete a completed file\n\n### Pins & Bookmarks\n- `POST /api/pins.add` - pin a message to a channel\n- `GET /api/pins.list` / `POST /api/pins.list` - list pinned message items for a channel\n- `POST /api/pins.remove` - remove a message pin from a channel\n- `POST /api/bookmarks.add` - add a link bookmark to a channel\n- `POST /api/bookmarks.edit` - update a link bookmark\n- `POST /api/bookmarks.list` - list channel bookmarks\n- `POST /api/bookmarks.remove` - remove a bookmark from a channel\n\n### Views\n- `POST /api/views.publish` - publish or update an App Home view for a user\n- `POST /api/views.open` - open a modal view\n- `POST /api/views.update` - update a view by `view_id` or `external_id`\n- `POST /api/views.push` - push a modal view onto the current modal stack\n- `POST /api/views.generateTriggerId` - local helper for tests that need a modal trigger id\n\nModal opens and pushes require values from `/api/views.generateTriggerId`. Pass the returned value as `trigger_id` or `interactivity_pointer`; generate push values with an existing `view_id` and use them within 3 seconds.\n\n### Team, Bots & Webhooks\n- `POST /api/team.info` - workspace info\n- `POST /api/bots.info` - bot info\n- `POST /services/:teamId/:botId/:webhookId` - incoming webhook with text or rich payload fields\n\n### OAuth\n- `GET /oauth/v2/authorize` - authorization (shows user picker)\n- `POST /oauth/v2/authorize/callback` - local user picker callback that creates the auth code\n- `POST /api/oauth.v2.access` - token exchange\n\n### Inspector\n- `GET /` - tabbed local inspector for conversations, messages, files, views, auth records, incoming webhooks, event subscriptions, and event deliveries\n\nSlack scope checks are relaxed by default so local tests can use simple bearer tokens. Set `slack.strict_scopes: true` in seed config to make supported Web API methods return Slack-style `missing_scope` errors with `needed` and `provided` fields. Strict mode checks `chat:write`, `channels:read`, `channels:history`, `channels:join`, `channels:manage`, `channels:write`, `groups:read`, `groups:history`, `groups:write`, `im:read`, `im:history`, `im:write`, `mpim:read`, `mpim:history`, `mpim:write`, `users:read`, `users:read.email`, `users.profile:read`, `users.profile:write`, `users:write`, `files:read`, `files:write`, `pins:read`, `pins:write`, `bookmarks:read`, `bookmarks:write`, `reactions:read`, `reactions:write`, and `team:read`. Slack lists no method-specific scopes for `views.publish`, `views.open`, `views.update`, or `views.push`, so the emulator requires auth but does not add strict-scope checks for those methods.\n\nCurrent Slack limits: Slack Connect, Enterprise Grid admin APIs, Audit Logs API, SCIM, Legal Holds, Socket Mode, slash command and interaction simulation, user groups, reminders, stars, calls, canvases, lists, functions, workflows, chat streaming, legacy `files.upload`, exact rate limiting, and paid-plan behavior are not implemented.\n\n## Linear API\n\nStateful Linear GraphQL API emulation with seeded organizations, users, teams, workflow states, issues, comments, labels, projects, cycles, OAuth apps, tokens, webhooks, and basic agent sessions. GraphQL reads and writes mutate in-memory state and use Relay-style connections with opaque cursors. OAuth supports authorization code, PKCE, refresh token, revoke, client credentials, and `actor=app` tokens for local app-actor tests. Supported writes dispatch Linear-shaped webhook payloads with `Linear-Delivery`, `Linear-Event`, and `Linear-Signature` headers when webhooks are configured.\n\n### GraphQL\n\n- `POST /graphql` - GraphQL endpoint for queries and mutations\n- `GET /graphql` - query-string GraphQL endpoint for tooling\n- Queries: `viewer`, `organization`, `users`, `user`, `teams`, `team`, `workflowStates`, `workflowState`, `issues`, `issue`, `comments`, `comment`, `issueLabels`, `issueLabel`, `projects`, `project`, `cycles`, `cycle`, `webhooks`, `webhook`, `agentSessions`, `agentSession`\n- Mutations: `issueCreate`, `issueUpdate`, `issueDelete`, `issueArchive`, `issueUnarchive`, `commentCreate`, `commentUpdate`, `commentDelete`, `issueLabelCreate`, `issueLabelUpdate`, `issueLabelDelete`, `issueAddLabel`, `issueRemoveLabel`, `webhookCreate`, `webhookDelete`, `agentSessionCreateOnIssue`, `agentSessionCreateOnComment`, `agentSessionUpdate`, `agentActivityCreate`\n\nIssue selections expose both numeric `priority` and Linear's derived `priorityLabel` values: `No priority`, `Urgent`, `High`, `Medium`, and `Low`.\n\n### OAuth\n\n- `GET /oauth/authorize` - authorization endpoint with local user picker\n- `POST /oauth/authorize/callback` - local user picker callback that creates an authorization code\n- `POST /oauth/token` - authorization code, refresh token, and client credentials grants\n- `POST /oauth/revoke` - revoke access or refresh tokens\n\nOAuth app `actor` config is authoritative. Apps configured with `actor: user` use authorization code flows. Apps configured with `actor: app` use the app install flow and can request client credentials tokens.\n\n### Webhooks And Inspector\n\n- `webhookCreate` / `webhookDelete` manage local webhook subscriptions\n- `GET /` - tabbed local inspector for issues, teams, users, projects, agents, auth records, webhook subscriptions, and deliveries\n\nLinear scope checks are relaxed by default so local tests can use simple bearer tokens or the seeded `lin_test_admin` token. Set `linear.strict_scopes: true` in seed config to require `read`, `write`, `issues:create`, `comments:create`, or `admin` on supported GraphQL operations.\n\nCurrent Linear limits: full schema coverage, exact production rate limiting, notification inbox behavior, rich document APIs, customer APIs, initiative APIs, exact search relevance, and production agent behavior are not implemented. Agent support is a focused local-test subset.\n\n## Twilio API\n\nStateful Twilio REST emulation with seeded accounts, Auth Tokens, API keys, incoming phone numbers, Programmable Messaging, Messaging Services, Verify, basic Voice calls, Conversations REST resources, signed webhooks, local simulator routes, and an inspector. No real SMS, MMS, WhatsApp, email, voice, carrier, compliance, billing, or SendGrid traffic is performed.\n\nDefault local credentials:\n\n```text\nTWILIO_ACCOUNT_SID=AC00000000000000000000000000000000\nTWILIO_AUTH_TOKEN=twilio_test_auth_token\nTWILIO_API_KEY=SK00000000000000000000000000000000\nTWILIO_API_SECRET=twilio_test_api_secret\nTWILIO_PHONE_NUMBER=+15551234567\nTWILIO_VERIFY_SERVICE_SID=VA00000000000000000000000000000000\n```\n\n### REST Routes\n\n- `GET /2010-04-01/Accounts/{AccountSid}.json` - fetch account\n- `GET /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json` - list phone numbers\n- `POST /2010-04-01/Accounts/{AccountSid}/Messages.json` - create outbound message\n- `GET /2010-04-01/Accounts/{AccountSid}/Messages.json` - list messages\n- `POST /2010-04-01/Accounts/{AccountSid}/Calls.json` - create outbound call\n- `POST /messaging/v1/Services` - create Messaging Service\n- `POST /verify/v2/Services/{ServiceSid}/Verifications` - start verification\n- `POST /verify/v2/Services/{ServiceSid}/VerificationCheck` - check verification code\n- `POST /conversations/v1/Services` - create Conversation Service\n- `POST /conversations/v1/Services/{ServiceSid}/Conversations` - create Conversation\n- `POST /conversations/v1/Services/{ServiceSid}/Conversations/{ConversationSid}/Participants` - add participant\n- `POST /conversations/v1/Services/{ServiceSid}/Conversations/{ConversationSid}/Messages` - add message\n\nTwilio uses multiple product hosts. For local SDK tests, rewrite Twilio SDK requests to the emulator and map `messaging.twilio.com` to `/messaging`, `verify.twilio.com` to `/verify`, and `conversations.twilio.com` to `/conversations`.\n\n### SMS And OTP Testing\n\nFor the common SMS verification loop, the seeded Verify Service uses code `123456`. Start a verification through the normal Verify API, then either submit `123456` in your app test or fetch the latest local code with the authenticated helper route:\n\n```sh\ncurl -u \"$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN\" \\\n  \"http://localhost:4000/_twilio/simulate/verification-code?To=%2B15550002222&ServiceSid=$TWILIO_VERIFY_SERVICE_SID\"\n```\n\nThe helper returns the latest local verification for that phone number, including `verification_sid`, `status`, `attempts`, and `code`. It is local-only test support and is not part of Twilio's production API. The Verify inspector also shows each attempted code.\n\nTo test inbound SMS webhooks, configure a seeded phone number `sms_url`, then call `POST /_twilio/simulate/inbound-message` with `To`, `From`, and `Body`. If the destination number is assigned to a Messaging Service with `inbound_request_url`, the simulator sends the inbound webhook there and includes `MessagingServiceSid`; otherwise it uses the phone number `sms_url`. To test outbound delivery transitions, create a message with `StatusCallback`, then call `POST /_twilio/simulate/message-status`.\n\n### Simulator And Inspector\n\n- `POST /_twilio/simulate/inbound-message` - create an inbound message and invoke the configured SMS webhook\n- `POST /_twilio/simulate/message-status` - advance message status and send status callbacks\n- `GET /_twilio/simulate/verification-code` - fetch the latest local Verify code by `VerificationSid` or `To`\n- `POST /_twilio/simulate/inbound-call` - create an inbound call and invoke the configured voice webhook\n- `POST /_twilio/simulate/call-status` - advance call status\n- `POST /_twilio/simulate/verification-status` - force a verification state by `VerificationSid` or `To`\n- `GET /` - tabbed inspector for messages, Verify, calls, Conversations, phone numbers, services, auth, and webhook deliveries\n\nCurrent Twilio limits: no carrier delivery, A2P 10DLC, toll-free verification, real phone number purchasing, exact rate limits, Studio, Flex, TaskRouter, Video, Sync, Segment, SendGrid, Conversations SDK websocket behavior, or complete TwiML interpreter.\n\n## Apple Sign In\n\nSign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.\n\n- `GET /.well-known/openid-configuration` - OIDC discovery document\n- `GET /auth/keys` - JSON Web Key Set (JWKS)\n- `GET /auth/authorize` - authorization endpoint (shows user picker)\n- `POST /auth/token` - token exchange (authorization code and refresh token grants)\n- `POST /auth/revoke` - token revocation\n\n## Microsoft Entra ID\n\nMicrosoft Entra ID (Azure AD) v2.0 OAuth 2.0 and OpenID Connect emulation with authorization code flow, PKCE, client credentials, RS256 ID tokens, and OIDC discovery.\n\n- `GET /.well-known/openid-configuration` - OIDC discovery document\n- `GET /:tenant/v2.0/.well-known/openid-configuration` - tenant-scoped OIDC discovery\n- `GET /discovery/v2.0/keys` - JSON Web Key Set (JWKS)\n- `GET /oauth2/v2.0/authorize` - authorization endpoint (shows user picker)\n- `POST /oauth2/v2.0/token` - token exchange (authorization code, refresh token, client credentials)\n- `GET /oidc/userinfo` - OpenID Connect user info\n- `GET /v1.0/me` - Microsoft Graph user profile\n- `GET /oauth2/v2.0/logout` - end session / logout\n- `POST /oauth2/v2.0/revoke` - token revocation\n\n## AWS\n\nS3, SQS, IAM, and STS emulation with AWS SDK-compatible S3 paths and query-style SQS/IAM/STS endpoints. All responses use AWS-compatible XML.\n\n### S3\n\nS3 routes use root paths matching the real AWS S3 wire format, so the official AWS SDK works out of the box with `forcePathStyle: true`. Legacy `/s3/` prefixed paths are also supported for backward compatibility.\n\n- `GET /` - list all buckets\n- `PUT /:bucket` - create bucket\n- `DELETE /:bucket` - delete bucket\n- `HEAD /:bucket` - check existence\n- `GET /:bucket` - list objects (prefix, delimiter, max-keys, continuation-token, start-after)\n- `POST /:bucket` - presigned POST upload (browser-style multipart form with policy validation)\n- `PUT /:bucket/:key` - put object (supports copy via `x-amz-copy-source`)\n- `GET /:bucket/:key` - get object\n- `HEAD /:bucket/:key` - head object\n- `DELETE /:bucket/:key` - delete object\n\n### SQS\nAll operations via `POST /sqs/` with `Action` parameter:\n- `CreateQueue`, `ListQueues`, `GetQueueUrl`, `GetQueueAttributes`\n- `SendMessage`, `ReceiveMessage`, `DeleteMessage`\n- `PurgeQueue`, `DeleteQueue`\n\n### IAM\nAll operations via `POST /iam/` with `Action` parameter:\n- `CreateUser`, `GetUser`, `ListUsers`, `DeleteUser`\n- `CreateAccessKey`, `ListAccessKeys`, `DeleteAccessKey`\n- `CreateRole`, `GetRole`, `ListRoles`, `DeleteRole`\n\n### STS\nAll operations via `POST /sts/` with `Action` parameter:\n- `GetCallerIdentity`, `AssumeRole`\n\n## Next.js Integration\n\nEmbed emulators directly in your Next.js app so they run on the same origin. This solves the Vercel preview deployment problem where OAuth callback URLs change with every deployment.\n\n### Install\n\n```bash\nnpm install @emulators/adapter-next @emulators/github @emulators/google\n```\n\nOnly install the emulators you need. Each `@emulators/*` package is published independently.\n\n### Route handler\n\nCreate a catch-all route that serves emulator traffic:\n\n```typescript\n// app/emulate/[...path]/route.ts\nimport { createEmulateHandler } from '@emulators/adapter-next'\nimport * as github from '@emulators/github'\nimport * as google from '@emulators/google'\n\nexport const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({\n  services: {\n    github: {\n      emulator: github,\n      seed: {\n        users: [{ login: 'octocat', name: 'The Octocat' }],\n        repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],\n      },\n    },\n    google: {\n      emulator: google,\n      seed: {\n        users: [{ email: 'test@example.com', name: 'Test User' }],\n      },\n    },\n  },\n})\n```\n\nGitHub App seeds may omit `private_key`. Retain the handler to call server-only `generatedSecrets()`; explicit keys are excluded. Persisted snapshots contain generated keys, so keep the backend private.\n\n### Auth.js / NextAuth configuration\n\nPoint your provider at the emulator paths on the same origin:\n\n```typescript\nimport GitHub from 'next-auth/providers/github'\n\nconst baseUrl = process.env.VERCEL_URL\n  ? `https://${process.env.VERCEL_URL}`\n  : 'http://localhost:3000'\n\nGitHub({\n  clientId: 'any-value',\n  clientSecret: 'any-value',\n  authorization: { url: `${baseUrl}/emulate/github/login/oauth/authorize` },\n  token: { url: `${baseUrl}/emulate/github/login/oauth/access_token` },\n  userinfo: { url: `${baseUrl}/emulate/github/user` },\n})\n```\n\nNo `oauth_apps` need to be seeded. When none are configured, the emulator skips `client_id`, `client_secret`, and `redirect_uri` validation.\n\n### Font files in serverless\n\nEmulator UI pages use bundled fonts. Wrap your Next.js config to include them in the serverless trace:\n\n```typescript\n// next.config.mjs\nimport { withEmulate } from '@emulators/adapter-next'\n\nexport default withEmulate({\n  // your normal Next.js config\n})\n```\n\nIf you mount the catch-all at a custom path, pass the matching prefix:\n\n```typescript\nexport default withEmulate(nextConfig, { routePrefix: '/api/emulate' })\n```\n\n### Persistence\n\nBy default, emulator state is in-memory and resets on every cold start. To persist state across restarts, pass a `persistence` adapter:\n\n```typescript\nimport { createEmulateHandler } from '@emulators/adapter-next'\nimport * as github from '@emulators/github'\n\nconst kvAdapter = {\n  async load() { return await kv.get('emulate-state') },\n  async save(data: string) { await kv.set('emulate-state', data) },\n}\n\nexport const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({\n  services: { github: { emulator: github } },\n  persistence: kvAdapter,\n})\n```\n\nFor local development, `@emulators/core` ships `filePersistence`:\n\n```typescript\nimport { filePersistence } from '@emulators/core'\n\n// ...\npersistence: filePersistence('.emulate/state.json'),\n```\n\nThe persistence adapter loads on cold start and saves after mutations. Generated identities also require atomic create-or-read `initialize`; see `@emulators/core`.\n\n## Nuxt Integration\n\nEmbed emulators directly in your Nuxt app so they run on the same origin. This gives OAuth flows stable callback URLs in local and preview deployments.\n\n### Install\n\n```bash\nnpm install @emulators/adapter-nuxt @emulators/github @emulators/google\n```\n\nOnly install the emulators you need. Each `@emulators/*` package is published independently.\n\n### Server route\n\nCreate a named catch-all route that serves emulator traffic:\n\n```typescript\n// server/routes/emulate/[...path].ts\nimport { createEmulateHandler } from '@emulators/adapter-nuxt'\nimport * as github from '@emulators/github'\nimport * as google from '@emulators/google'\n\nexport default defineEventHandler(createEmulateHandler({\n  services: {\n    github: {\n      emulator: github,\n      seed: {\n        users: [{ login: 'octocat', name: 'The Octocat' }],\n        repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],\n      },\n    },\n    google: {\n      emulator: google,\n      seed: {\n        users: [{ email: 'test@example.com', name: 'Test User' }],\n      },\n    },\n  },\n}))\n```\n\nGitHub App seeds may omit `private_key`. Retain the handler to call server-only `generatedSecrets()`; explicit keys are excluded. Persisted snapshots contain generated keys, so keep the backend private.\n\n### Nuxt config\n\nEmulator UI pages use bundled fonts. Wrap your Nuxt config so Nitro traces the core package assets into production builds:\n\n```typescript\n// nuxt.config.ts\nimport { withEmulate } from '@emulators/adapter-nuxt'\n\nexport default defineNuxtConfig(withEmulate({\n  // your normal Nuxt config\n}))\n```\n\n### OAuth configuration\n\nPoint your OAuth provider at the emulator paths on the same origin:\n\n```typescript\nconst baseUrl = process.env.NUXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'\n\nexport const githubOAuth = {\n  clientId: 'any-value',\n  clientSecret: 'any-value',\n  authorizationUrl: `${baseUrl}/emulate/github/login/oauth/authorize`,\n  tokenUrl: `${baseUrl}/emulate/github/login/oauth/access_token`,\n  userInfoUrl: `${baseUrl}/emulate/github/user`,\n}\n```\n\nNo `oauth_apps` need to be seeded. When none are configured, the emulator skips `client_id`, `client_secret`, and `redirect_uri` validation.\n\n### Persistence\n\nBy default, emulator state is in-memory and resets on every cold start. To persist state across restarts, pass a `persistence` adapter:\n\n```typescript\nimport { createEmulateHandler } from '@emulators/adapter-nuxt'\nimport * as github from '@emulators/github'\n\nconst storageAdapter = {\n  async load() { return await useStorage('emulate').getItem<string>('state') },\n  async save(data: string) { await useStorage('emulate').setItem('state', data) },\n}\n\nexport default defineEventHandler(createEmulateHandler({\n  services: { github: { emulator: github } },\n  persistence: storageAdapter,\n}))\n```\n\nThe persistence adapter loads on cold start and saves after mutations. Generated identities also require atomic create-or-read `initialize`; see `@emulators/core`.\n\n## Architecture\n\n```\npackages/\n  emulate/          # CLI entry point (commander)\n  @emulators/\n    core/           # HTTP server, in-memory store, plugin interface, middleware\n    adapter-next/   # Next.js App Router integration\n    adapter-nuxt/   # Nuxt server route integration\n    vercel/         # Vercel API service\n    github/         # GitHub API service\n    google/         # Google OAuth 2.0 / OIDC + Gmail, Calendar, Drive\n    slack/          # Slack Web API, OAuth v2, incoming webhooks\n    linear/         # Linear GraphQL API, OAuth, webhooks\n    twilio/         # Twilio Messaging, Verify, Voice, webhooks\n    apple/          # Apple Sign In / OIDC\n    microsoft/      # Microsoft Entra ID OAuth 2.0 / OIDC + Graph /me\n    aws/            # AWS S3, SQS, IAM, STS\napps/\n  web/              # Documentation site (Next.js)\n```\n\nThe core provides a generic `Store` with typed `Collection<T>` instances supporting CRUD, indexing, filtering, and pagination. Each service plugin registers its routes with the shared internal app and uses the store for state.\n\n## Auth\n\nTokens are configured in the seed config and map to users. Pass them as `Authorization: Bearer <token>` or `Authorization: token <token>`.\n\n**Vercel**: All endpoints accept `teamId` or `slug` query params for team scoping. Pagination uses cursor-based `limit`/`since`/`until` with `pagination` response objects.\n\n**GitHub**: Public repo endpoints work without auth. Private repos and write operations require a valid token. Pagination uses `page`/`per_page` with `Link` headers.\n\n**Google**: Standard OAuth 2.0 authorization code flow. Configure clients in the seed config.\n\n**Slack**: All Web API endpoints require `Authorization: Bearer <token>`. Seeded OAuth apps create local installation records, and OAuth v2 flow with user picker UI creates scoped bot tokens. Optional strict scope mode returns `missing_scope` when a token lacks a required method scope.\n\n**Linear**: GraphQL accepts `Authorization: Bearer <token>` or a bare personal API key value. Seeded Linear tokens map to users or app actors, OAuth apps support local authorization code and client credentials flows, and optional strict scope mode checks supported GraphQL operations.\n\n**Twilio**: HTTP Basic auth accepts the seeded Account SID/Auth Token pair or API Key/API Secret pair. Product-host APIs are exposed under local prefixes such as `/messaging/v1` and `/verify/v2`; the 2010 API lives at `/2010-04-01`.\n\n**Apple**: OIDC authorization code flow with RS256 ID tokens. On first auth per user/client pair, a `user` JSON blob is included.\n\n**Microsoft**: OIDC authorization code flow with PKCE support. Also supports client credentials grants. Microsoft Graph `/v1.0/me` available.\n\n**AWS**: Bearer tokens or IAM access key credentials. Default key pair always seeded: `AKIAIOSFODNN7EXAMPLE` / `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`.\n","readmeFilename":"README.md"}