@emulators/adapter-next 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @emulators/adapter-next
2
+
3
+ Next.js App Router integration for emulate. Embed emulators directly in your Next.js app so they run on the same origin, solving the Vercel preview deployment problem where OAuth callback URLs change with every deployment.
4
+
5
+ Part of [emulate](https://github.com/vercel-labs/emulate) — local drop-in replacement services for CI and no-network sandboxes.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @emulators/adapter-next
11
+ ```
12
+
13
+ Only install the emulators you need alongside the adapter:
14
+
15
+ ```bash
16
+ npm install @emulators/adapter-next @emulators/github @emulators/google
17
+ ```
18
+
19
+ ## Route handler
20
+
21
+ Create a catch-all route that serves emulator traffic:
22
+
23
+ ```typescript
24
+ // app/emulate/[...path]/route.ts
25
+ import { createEmulateHandler } from '@emulators/adapter-next'
26
+ import * as github from '@emulators/github'
27
+ import * as google from '@emulators/google'
28
+
29
+ export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
30
+ services: {
31
+ github: {
32
+ emulator: github,
33
+ seed: {
34
+ users: [{ login: 'octocat', name: 'The Octocat' }],
35
+ repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],
36
+ },
37
+ },
38
+ google: {
39
+ emulator: google,
40
+ seed: {
41
+ users: [{ email: 'test@example.com', name: 'Test User' }],
42
+ },
43
+ },
44
+ },
45
+ })
46
+ ```
47
+
48
+ ## Auth.js / NextAuth configuration
49
+
50
+ Point your provider at the emulator paths on the same origin:
51
+
52
+ ```typescript
53
+ import GitHub from 'next-auth/providers/github'
54
+
55
+ const baseUrl = process.env.VERCEL_URL
56
+ ? `https://${process.env.VERCEL_URL}`
57
+ : 'http://localhost:3000'
58
+
59
+ GitHub({
60
+ clientId: 'any-value',
61
+ clientSecret: 'any-value',
62
+ authorization: { url: `${baseUrl}/emulate/github/login/oauth/authorize` },
63
+ token: { url: `${baseUrl}/emulate/github/login/oauth/access_token` },
64
+ userinfo: { url: `${baseUrl}/emulate/github/user` },
65
+ })
66
+ ```
67
+
68
+ No `oauth_apps` need to be seeded. When none are configured, the emulator skips `client_id`, `client_secret`, and `redirect_uri` validation.
69
+
70
+ ## Font files in serverless
71
+
72
+ Emulator UI pages use bundled fonts. Wrap your Next.js config to include them in the serverless trace:
73
+
74
+ ```typescript
75
+ // next.config.mjs
76
+ import { withEmulate } from '@emulators/adapter-next'
77
+
78
+ export default withEmulate({
79
+ // your normal Next.js config
80
+ })
81
+ ```
82
+
83
+ If you mount the catch-all at a custom path, pass the matching prefix:
84
+
85
+ ```typescript
86
+ export default withEmulate(nextConfig, { routePrefix: '/api/emulate' })
87
+ ```
88
+
89
+ ## Persistence
90
+
91
+ By default, emulator state is in-memory and resets on every cold start. To persist state across restarts, pass a `persistence` adapter:
92
+
93
+ ```typescript
94
+ import { createEmulateHandler } from '@emulators/adapter-next'
95
+ import * as github from '@emulators/github'
96
+
97
+ const kvAdapter = {
98
+ async load() { return await kv.get('emulate-state') },
99
+ async save(data: string) { await kv.set('emulate-state', data) },
100
+ }
101
+
102
+ export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
103
+ services: { github: { emulator: github } },
104
+ persistence: kvAdapter,
105
+ })
106
+ ```
107
+
108
+ For local development, `@emulators/core` ships `filePersistence`:
109
+
110
+ ```typescript
111
+ import { filePersistence } from '@emulators/core'
112
+
113
+ // ...
114
+ persistence: filePersistence('.emulate/state.json'),
115
+ ```
116
+
117
+ ## Links
118
+
119
+ - [Full documentation](https://emulate.dev)
120
+ - [GitHub](https://github.com/vercel-labs/emulate)
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { ServicePlugin, Store, AppKeyResolver, PersistenceAdapter } from '@emulators/core';
1
+ import { ServicePlugin, Store, WebhookDispatcher, AppKeyResolver, PersistenceAdapter } from '@emulators/core';
2
2
  export { PersistenceAdapter } from '@emulators/core';
3
3
 
4
4
  interface EmulatorModule {
5
5
  plugin?: ServicePlugin;
6
6
  default?: ServicePlugin;
7
- seedFromConfig?(store: Store, baseUrl: string, config: unknown): void;
7
+ seedFromConfig?(store: Store, baseUrl: string, config: unknown, webhooks?: WebhookDispatcher): void;
8
8
  createAppKeyResolver?(store: Store): AppKeyResolver;
9
9
  }
10
10
  interface EmulatorEntry {
package/dist/index.js CHANGED
@@ -127,14 +127,14 @@ function createEmulateHandler(config) {
127
127
  const servicePrefix = `${mountPath2}/${name}`;
128
128
  const baseUrl = `${origin}${servicePrefix}`;
129
129
  let appKeyResolver;
130
- const { app, store, tokenMap } = createServer(plugin, {
130
+ const { app, store, tokenMap, webhooks } = createServer(plugin, {
131
131
  baseUrl,
132
132
  appKeyResolver: entry.emulator.createAppKeyResolver ? (appId) => appKeyResolver(appId) : void 0
133
133
  });
134
134
  if (entry.emulator.createAppKeyResolver) {
135
135
  appKeyResolver = entry.emulator.createAppKeyResolver(store);
136
136
  }
137
- serviceApps.set(name, { hono: app, store, tokenMap, plugin });
137
+ serviceApps.set(name, { hono: app, store, tokenMap, plugin, webhooks });
138
138
  }
139
139
  let restored = false;
140
140
  if (persistence) {
@@ -155,7 +155,7 @@ function createEmulateHandler(config) {
155
155
  const baseUrl = `${origin}${servicePrefix}`;
156
156
  sa.plugin.seed?.(sa.store, baseUrl);
157
157
  if (entry.seed && entry.emulator.seedFromConfig) {
158
- entry.emulator.seedFromConfig(sa.store, baseUrl, entry.seed);
158
+ entry.emulator.seedFromConfig(sa.store, baseUrl, entry.seed, sa.webhooks);
159
159
  }
160
160
  }
161
161
  if (persistence) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n createServer,\n debug,\n serializeTokenMap,\n restoreTokenMap,\n type ServicePlugin,\n type Store,\n type TokenMap,\n type TokenEntry,\n type StoreSnapshot,\n type PersistenceAdapter,\n type AppKeyResolver,\n} from \"@emulators/core\";\n\nexport type { PersistenceAdapter } from \"@emulators/core\";\n\nexport interface EmulatorModule {\n plugin?: ServicePlugin;\n default?: ServicePlugin;\n seedFromConfig?(store: Store, baseUrl: string, config: unknown): void;\n createAppKeyResolver?(store: Store): AppKeyResolver;\n}\n\ninterface EmulatorEntry {\n emulator: EmulatorModule;\n seed?: Record<string, unknown>;\n}\n\nexport interface EmulateHandlerConfig {\n services: Record<string, EmulatorEntry>;\n persistence?: PersistenceAdapter;\n}\n\ninterface Fetchable {\n fetch(request: Request, ...rest: unknown[]): Response | Promise<Response>;\n}\n\ninterface ServiceApp {\n hono: Fetchable;\n store: Store;\n tokenMap: TokenMap;\n plugin: ServicePlugin;\n}\n\ninterface FullSnapshot {\n store: StoreSnapshot;\n tokens: Record<string, TokenEntry[]>;\n}\n\ntype NextRequest = Request;\ntype NextResponse = Response;\ntype RouteHandler = (req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) => Promise<NextResponse>;\n\nconst MUTATING_METHODS = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\nfunction resolvePlugin(mod: EmulatorModule): ServicePlugin {\n const plugin = mod.plugin ?? mod.default;\n if (!plugin) {\n throw new Error(\"Emulator module must export `plugin` or a default export implementing ServicePlugin\");\n }\n return plugin;\n}\n\nfunction takeSnapshot(apps: Map<string, ServiceApp>): FullSnapshot {\n const mergedStore: StoreSnapshot = { collections: {}, data: {} };\n const tokens: Record<string, TokenEntry[]> = {};\n\n for (const [name, sa] of apps) {\n const snap = sa.store.snapshot();\n for (const [colName, colSnap] of Object.entries(snap.collections)) {\n mergedStore.collections[`${name}:${colName}`] = colSnap;\n }\n for (const [key, val] of Object.entries(snap.data)) {\n mergedStore.data[`${name}:${key}`] = val;\n }\n tokens[name] = serializeTokenMap(sa.tokenMap);\n }\n\n return { store: mergedStore, tokens };\n}\n\nfunction restoreFromSnapshot(apps: Map<string, ServiceApp>, snapshot: FullSnapshot): void {\n const storesByName = new Map<string, StoreSnapshot>();\n for (const [qualifiedName, colSnap] of Object.entries(snapshot.store.collections)) {\n const sepIdx = qualifiedName.indexOf(\":\");\n const name = qualifiedName.slice(0, sepIdx);\n const colName = qualifiedName.slice(sepIdx + 1);\n if (!storesByName.has(name)) {\n storesByName.set(name, { collections: {}, data: {} });\n }\n storesByName.get(name)!.collections[colName] = colSnap;\n }\n for (const [qualifiedKey, val] of Object.entries(snapshot.store.data)) {\n const sepIdx = qualifiedKey.indexOf(\":\");\n const name = qualifiedKey.slice(0, sepIdx);\n const dataKey = qualifiedKey.slice(sepIdx + 1);\n if (!storesByName.has(name)) {\n storesByName.set(name, { collections: {}, data: {} });\n }\n storesByName.get(name)!.data[dataKey] = val;\n }\n\n for (const [name, sa] of apps) {\n const snap = storesByName.get(name);\n if (snap) {\n sa.store.restore(snap);\n }\n restoreTokenMap(sa.tokenMap, snapshot.tokens[name] ?? []);\n }\n}\n\nfunction detectPrefix(url: string, pathSegments: string[]): string {\n const parsed = new URL(url);\n const fullPath = parsed.pathname;\n const restPath = \"/\" + pathSegments.join(\"/\");\n const idx = fullPath.lastIndexOf(restPath);\n if (idx > 0) {\n return fullPath.slice(0, idx);\n }\n throw new Error(`Could not detect mount path from URL: ${url}`);\n}\n\nasync function rewriteResponse(response: Response, servicePrefix: string): Promise<Response> {\n const contentType = response.headers.get(\"Content-Type\") ?? \"\";\n const location = response.headers.get(\"Location\");\n const isHtml = contentType.includes(\"text/html\");\n const locationChanged = location != null && location.startsWith(\"/\");\n\n if (!isHtml) {\n if (!locationChanged) return response;\n const headers = new Headers(response.headers);\n headers.set(\"Location\", servicePrefix + location);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n let html = await response.text();\n\n // Skip paths already carrying the service prefix to avoid double-prefixing\n // (e.g., redirects that already went through rewriting).\n html = html.replace(/(action|href)=\"(\\/[^\"]*?)\"/g, (_match, attr, path) => {\n if (path.startsWith(servicePrefix)) return `${attr}=\"${path}\"`;\n return `${attr}=\"${servicePrefix}${path}\"`;\n });\n\n html = html.replace(/url\\('(\\/[^']*?)'\\)/g, (_match, path) => {\n if (path.startsWith(servicePrefix)) return `url('${path}')`;\n return `url('${servicePrefix}${path}')`;\n });\n\n const headers = new Headers(response.headers);\n if (locationChanged) {\n headers.set(\"Location\", servicePrefix + location);\n }\n headers.delete(\"Content-Length\");\n\n return new Response(html, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\nexport function createEmulateHandler(config: EmulateHandlerConfig) {\n const { services: serviceEntries, persistence } = config;\n\n let apps: Map<string, ServiceApp> | null = null;\n let mountPath: string | null = null;\n let initPromise: Promise<void> | null = null;\n let pendingSave: Promise<void> = Promise.resolve();\n\n function enqueueSave(): void {\n if (!persistence || !apps) return;\n pendingSave = pendingSave.then(async () => {\n if (!apps) return;\n const snapshot = takeSnapshot(apps);\n const json = JSON.stringify(snapshot);\n try {\n await persistence.save(json);\n } catch (err) {\n debug(\"persistence\", \"save failed: %o\", err);\n }\n });\n }\n\n async function initApps(origin: string, mountPath: string): Promise<Map<string, ServiceApp>> {\n const serviceApps = new Map<string, ServiceApp>();\n\n for (const [name, entry] of Object.entries(serviceEntries)) {\n const plugin = resolvePlugin(entry.emulator);\n const servicePrefix = `${mountPath}/${name}`;\n const baseUrl = `${origin}${servicePrefix}`;\n\n let appKeyResolver: AppKeyResolver | undefined;\n const { app, store, tokenMap } = createServer(plugin, {\n baseUrl,\n appKeyResolver: entry.emulator.createAppKeyResolver\n ? (appId) => appKeyResolver!(appId)\n : undefined,\n });\n\n if (entry.emulator.createAppKeyResolver) {\n appKeyResolver = entry.emulator.createAppKeyResolver(store);\n }\n\n serviceApps.set(name, { hono: app, store, tokenMap, plugin });\n }\n\n let restored = false;\n if (persistence) {\n const raw = await persistence.load();\n if (raw) {\n try {\n const snapshot = JSON.parse(raw) as FullSnapshot;\n restoreFromSnapshot(serviceApps, snapshot);\n restored = true;\n } catch {\n // Corrupted data, fall through to seeding\n }\n }\n }\n\n if (!restored) {\n for (const [name, entry] of Object.entries(serviceEntries)) {\n const sa = serviceApps.get(name)!;\n const servicePrefix = `${mountPath}/${name}`;\n const baseUrl = `${origin}${servicePrefix}`;\n sa.plugin.seed?.(sa.store, baseUrl);\n if (entry.seed && entry.emulator.seedFromConfig) {\n entry.emulator.seedFromConfig(sa.store, baseUrl, entry.seed);\n }\n }\n if (persistence) {\n enqueueSave();\n }\n }\n\n return serviceApps;\n }\n\n async function ensureInit(req: Request, pathSegments: string[]): Promise<Map<string, ServiceApp>> {\n if (apps) return apps;\n if (!initPromise) {\n const url = new URL(req.url);\n const origin = url.origin;\n mountPath = detectPrefix(req.url, pathSegments);\n initPromise = initApps(origin, mountPath).then((result) => {\n apps = result;\n });\n }\n await initPromise;\n return apps!;\n }\n\n async function handleRequest(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }): Promise<NextResponse> {\n const { path: pathSegments } = await ctx.params;\n const serviceApps = await ensureInit(req, pathSegments);\n\n if (pathSegments.length === 0) {\n return new Response(\"Not found\", { status: 404 });\n }\n\n const serviceName = pathSegments[0];\n const sa = serviceApps.get(serviceName);\n if (!sa) {\n return new Response(`Unknown service: ${serviceName}`, { status: 404 });\n }\n\n const restPath = \"/\" + pathSegments.slice(1).join(\"/\");\n const url = new URL(req.url);\n const strippedUrl = new URL(restPath + url.search, url.origin);\n\n const strippedReq = new Request(strippedUrl.toString(), {\n method: req.method,\n headers: req.headers,\n body: req.body,\n duplex: \"half\",\n } as RequestInit & { duplex: string });\n\n let response = await sa.hono.fetch(strippedReq);\n\n const servicePrefix = `${mountPath!}/${serviceName}`;\n response = await rewriteResponse(response, servicePrefix);\n\n if (persistence && MUTATING_METHODS.has(req.method)) {\n enqueueSave();\n }\n\n return response;\n }\n\n const handler: RouteHandler = handleRequest;\n\n return {\n GET: handler,\n POST: handler,\n PUT: handler,\n PATCH: handler,\n DELETE: handler,\n };\n}\n\nexport function withEmulate<T>(nextConfig: T, options?: { routePrefix?: string }): T {\n const config = nextConfig as Record<string, unknown>;\n const prefix = options?.routePrefix ?? \"/emulate\";\n const routePattern = `${prefix}/**`;\n const fontGlob = \"./node_modules/@emulators/core/dist/fonts/**\";\n\n const topLevel = { ...((config.outputFileTracingIncludes as Record<string, string[]> | undefined) ?? {}) };\n const existing = topLevel[routePattern] ?? [];\n if (!existing.includes(fontGlob)) {\n topLevel[routePattern] = [...existing, fontGlob];\n }\n\n return { ...config, outputFileTracingIncludes: topLevel } as T;\n}\n"],"mappings":"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","names":["headers","mountPath"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n createServer,\n debug,\n serializeTokenMap,\n restoreTokenMap,\n type ServicePlugin,\n type Store,\n type TokenMap,\n type TokenEntry,\n type StoreSnapshot,\n type PersistenceAdapter,\n type AppKeyResolver,\n type WebhookDispatcher,\n} from \"@emulators/core\";\n\nexport type { PersistenceAdapter } from \"@emulators/core\";\n\nexport interface EmulatorModule {\n plugin?: ServicePlugin;\n default?: ServicePlugin;\n seedFromConfig?(store: Store, baseUrl: string, config: unknown, webhooks?: WebhookDispatcher): void;\n createAppKeyResolver?(store: Store): AppKeyResolver;\n}\n\ninterface EmulatorEntry {\n emulator: EmulatorModule;\n seed?: Record<string, unknown>;\n}\n\nexport interface EmulateHandlerConfig {\n services: Record<string, EmulatorEntry>;\n persistence?: PersistenceAdapter;\n}\n\ninterface Fetchable {\n fetch(request: Request, ...rest: unknown[]): Response | Promise<Response>;\n}\n\ninterface ServiceApp {\n hono: Fetchable;\n store: Store;\n tokenMap: TokenMap;\n plugin: ServicePlugin;\n webhooks: WebhookDispatcher;\n}\n\ninterface FullSnapshot {\n store: StoreSnapshot;\n tokens: Record<string, TokenEntry[]>;\n}\n\ntype NextRequest = Request;\ntype NextResponse = Response;\ntype RouteHandler = (req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) => Promise<NextResponse>;\n\nconst MUTATING_METHODS = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\nfunction resolvePlugin(mod: EmulatorModule): ServicePlugin {\n const plugin = mod.plugin ?? mod.default;\n if (!plugin) {\n throw new Error(\"Emulator module must export `plugin` or a default export implementing ServicePlugin\");\n }\n return plugin;\n}\n\nfunction takeSnapshot(apps: Map<string, ServiceApp>): FullSnapshot {\n const mergedStore: StoreSnapshot = { collections: {}, data: {} };\n const tokens: Record<string, TokenEntry[]> = {};\n\n for (const [name, sa] of apps) {\n const snap = sa.store.snapshot();\n for (const [colName, colSnap] of Object.entries(snap.collections)) {\n mergedStore.collections[`${name}:${colName}`] = colSnap;\n }\n for (const [key, val] of Object.entries(snap.data)) {\n mergedStore.data[`${name}:${key}`] = val;\n }\n tokens[name] = serializeTokenMap(sa.tokenMap);\n }\n\n return { store: mergedStore, tokens };\n}\n\nfunction restoreFromSnapshot(apps: Map<string, ServiceApp>, snapshot: FullSnapshot): void {\n const storesByName = new Map<string, StoreSnapshot>();\n for (const [qualifiedName, colSnap] of Object.entries(snapshot.store.collections)) {\n const sepIdx = qualifiedName.indexOf(\":\");\n const name = qualifiedName.slice(0, sepIdx);\n const colName = qualifiedName.slice(sepIdx + 1);\n if (!storesByName.has(name)) {\n storesByName.set(name, { collections: {}, data: {} });\n }\n storesByName.get(name)!.collections[colName] = colSnap;\n }\n for (const [qualifiedKey, val] of Object.entries(snapshot.store.data)) {\n const sepIdx = qualifiedKey.indexOf(\":\");\n const name = qualifiedKey.slice(0, sepIdx);\n const dataKey = qualifiedKey.slice(sepIdx + 1);\n if (!storesByName.has(name)) {\n storesByName.set(name, { collections: {}, data: {} });\n }\n storesByName.get(name)!.data[dataKey] = val;\n }\n\n for (const [name, sa] of apps) {\n const snap = storesByName.get(name);\n if (snap) {\n sa.store.restore(snap);\n }\n restoreTokenMap(sa.tokenMap, snapshot.tokens[name] ?? []);\n }\n}\n\nfunction detectPrefix(url: string, pathSegments: string[]): string {\n const parsed = new URL(url);\n const fullPath = parsed.pathname;\n const restPath = \"/\" + pathSegments.join(\"/\");\n const idx = fullPath.lastIndexOf(restPath);\n if (idx > 0) {\n return fullPath.slice(0, idx);\n }\n throw new Error(`Could not detect mount path from URL: ${url}`);\n}\n\nasync function rewriteResponse(response: Response, servicePrefix: string): Promise<Response> {\n const contentType = response.headers.get(\"Content-Type\") ?? \"\";\n const location = response.headers.get(\"Location\");\n const isHtml = contentType.includes(\"text/html\");\n const locationChanged = location != null && location.startsWith(\"/\");\n\n if (!isHtml) {\n if (!locationChanged) return response;\n const headers = new Headers(response.headers);\n headers.set(\"Location\", servicePrefix + location);\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n let html = await response.text();\n\n // Skip paths already carrying the service prefix to avoid double-prefixing\n // (e.g., redirects that already went through rewriting).\n html = html.replace(/(action|href)=\"(\\/[^\"]*?)\"/g, (_match, attr, path) => {\n if (path.startsWith(servicePrefix)) return `${attr}=\"${path}\"`;\n return `${attr}=\"${servicePrefix}${path}\"`;\n });\n\n html = html.replace(/url\\('(\\/[^']*?)'\\)/g, (_match, path) => {\n if (path.startsWith(servicePrefix)) return `url('${path}')`;\n return `url('${servicePrefix}${path}')`;\n });\n\n const headers = new Headers(response.headers);\n if (locationChanged) {\n headers.set(\"Location\", servicePrefix + location);\n }\n headers.delete(\"Content-Length\");\n\n return new Response(html, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n}\n\nexport function createEmulateHandler(config: EmulateHandlerConfig) {\n const { services: serviceEntries, persistence } = config;\n\n let apps: Map<string, ServiceApp> | null = null;\n let mountPath: string | null = null;\n let initPromise: Promise<void> | null = null;\n let pendingSave: Promise<void> = Promise.resolve();\n\n function enqueueSave(): void {\n if (!persistence || !apps) return;\n pendingSave = pendingSave.then(async () => {\n if (!apps) return;\n const snapshot = takeSnapshot(apps);\n const json = JSON.stringify(snapshot);\n try {\n await persistence.save(json);\n } catch (err) {\n debug(\"persistence\", \"save failed: %o\", err);\n }\n });\n }\n\n async function initApps(origin: string, mountPath: string): Promise<Map<string, ServiceApp>> {\n const serviceApps = new Map<string, ServiceApp>();\n\n for (const [name, entry] of Object.entries(serviceEntries)) {\n const plugin = resolvePlugin(entry.emulator);\n const servicePrefix = `${mountPath}/${name}`;\n const baseUrl = `${origin}${servicePrefix}`;\n\n let appKeyResolver: AppKeyResolver | undefined;\n const { app, store, tokenMap, webhooks } = createServer(plugin, {\n baseUrl,\n appKeyResolver: entry.emulator.createAppKeyResolver ? (appId) => appKeyResolver!(appId) : undefined,\n });\n\n if (entry.emulator.createAppKeyResolver) {\n appKeyResolver = entry.emulator.createAppKeyResolver(store);\n }\n\n serviceApps.set(name, { hono: app, store, tokenMap, plugin, webhooks });\n }\n\n let restored = false;\n if (persistence) {\n const raw = await persistence.load();\n if (raw) {\n try {\n const snapshot = JSON.parse(raw) as FullSnapshot;\n restoreFromSnapshot(serviceApps, snapshot);\n restored = true;\n } catch {\n // Corrupted data, fall through to seeding\n }\n }\n }\n\n if (!restored) {\n for (const [name, entry] of Object.entries(serviceEntries)) {\n const sa = serviceApps.get(name)!;\n const servicePrefix = `${mountPath}/${name}`;\n const baseUrl = `${origin}${servicePrefix}`;\n sa.plugin.seed?.(sa.store, baseUrl);\n if (entry.seed && entry.emulator.seedFromConfig) {\n entry.emulator.seedFromConfig(sa.store, baseUrl, entry.seed, sa.webhooks);\n }\n }\n if (persistence) {\n enqueueSave();\n }\n }\n\n return serviceApps;\n }\n\n async function ensureInit(req: Request, pathSegments: string[]): Promise<Map<string, ServiceApp>> {\n if (apps) return apps;\n if (!initPromise) {\n const url = new URL(req.url);\n const origin = url.origin;\n mountPath = detectPrefix(req.url, pathSegments);\n initPromise = initApps(origin, mountPath).then((result) => {\n apps = result;\n });\n }\n await initPromise;\n return apps!;\n }\n\n async function handleRequest(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }): Promise<NextResponse> {\n const { path: pathSegments } = await ctx.params;\n const serviceApps = await ensureInit(req, pathSegments);\n\n if (pathSegments.length === 0) {\n return new Response(\"Not found\", { status: 404 });\n }\n\n const serviceName = pathSegments[0];\n const sa = serviceApps.get(serviceName);\n if (!sa) {\n return new Response(`Unknown service: ${serviceName}`, { status: 404 });\n }\n\n const restPath = \"/\" + pathSegments.slice(1).join(\"/\");\n const url = new URL(req.url);\n const strippedUrl = new URL(restPath + url.search, url.origin);\n\n const strippedReq = new Request(strippedUrl.toString(), {\n method: req.method,\n headers: req.headers,\n body: req.body,\n duplex: \"half\",\n } as RequestInit & { duplex: string });\n\n let response = await sa.hono.fetch(strippedReq);\n\n const servicePrefix = `${mountPath!}/${serviceName}`;\n response = await rewriteResponse(response, servicePrefix);\n\n if (persistence && MUTATING_METHODS.has(req.method)) {\n enqueueSave();\n }\n\n return response;\n }\n\n const handler: RouteHandler = handleRequest;\n\n return {\n GET: handler,\n POST: handler,\n PUT: handler,\n PATCH: handler,\n DELETE: handler,\n };\n}\n\nexport function withEmulate<T>(nextConfig: T, options?: { routePrefix?: string }): T {\n const config = nextConfig as Record<string, unknown>;\n const prefix = options?.routePrefix ?? \"/emulate\";\n const routePattern = `${prefix}/**`;\n const fontGlob = \"./node_modules/@emulators/core/dist/fonts/**\";\n\n const topLevel = { ...((config.outputFileTracingIncludes as Record<string, string[]> | undefined) ?? {}) };\n const existing = topLevel[routePattern] ?? [];\n if (!existing.includes(fontGlob)) {\n topLevel[routePattern] = [...existing, fontGlob];\n }\n\n return { ...config, outputFileTracingIncludes: topLevel } as T;\n}\n"],"mappings":"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","names":["headers","mountPath"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emulators/adapter-next",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "dist"
29
29
  ],
30
30
  "dependencies": {
31
- "@emulators/core": "0.4.0"
31
+ "@emulators/core": "0.5.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8",
@@ -37,6 +37,8 @@
37
37
  "scripts": {
38
38
  "build": "tsup --clean",
39
39
  "dev": "tsup --watch",
40
- "clean": "rm -rf dist .turbo"
40
+ "clean": "rm -rf dist .turbo",
41
+ "type-check": "tsc --noEmit",
42
+ "lint": "eslint src"
41
43
  }
42
44
  }