api-emulator 0.5.2 → 0.6.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 +3 -3
- package/dist/api.js +2 -8
- package/dist/api.js.map +1 -1
- package/dist/index.js +6 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -105,7 +105,7 @@ npm install @api-emulator/adapter-next @api-emulator/core
|
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
```ts
|
|
108
|
-
import {
|
|
108
|
+
import { createApiEmulatorHandler } from '@api-emulator/adapter-next'
|
|
109
109
|
import type { ServicePlugin } from '@api-emulator/core'
|
|
110
110
|
|
|
111
111
|
const internalPlugin: ServicePlugin = {
|
|
@@ -115,7 +115,7 @@ const internalPlugin: ServicePlugin = {
|
|
|
115
115
|
},
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export const { GET, POST, PUT, PATCH, DELETE } =
|
|
118
|
+
export const { GET, POST, PUT, PATCH, DELETE } = createApiEmulatorHandler({
|
|
119
119
|
services: {
|
|
120
120
|
internal: { emulator: { plugin: internalPlugin } },
|
|
121
121
|
},
|
|
@@ -138,7 +138,7 @@ github:
|
|
|
138
138
|
name: The Octocat
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
The CLI auto-detects `api-emulator.config.yaml`, `.yml`, and `.json`.
|
|
141
|
+
The CLI auto-detects `api-emulator.config.yaml`, `.yml`, and `.json`.
|
|
142
142
|
|
|
143
143
|
## Examples
|
|
144
144
|
|
package/dist/api.js
CHANGED
|
@@ -3,13 +3,7 @@ import { isAbsolute, resolve } from "path";
|
|
|
3
3
|
|
|
4
4
|
// src/plugin-manifest.ts
|
|
5
5
|
function readPluginManifest(mod) {
|
|
6
|
-
return {
|
|
7
|
-
label: mod.label,
|
|
8
|
-
endpoints: mod.endpoints,
|
|
9
|
-
initConfig: mod.initConfig,
|
|
10
|
-
contract: mod.contract,
|
|
11
|
-
...mod.manifest
|
|
12
|
-
};
|
|
6
|
+
return mod.manifest ?? {};
|
|
13
7
|
}
|
|
14
8
|
function validatePluginManifest(manifest, pluginName) {
|
|
15
9
|
if (manifest.name && manifest.name !== pluginName) {
|
|
@@ -98,7 +92,7 @@ function resolveBaseUrl(opts) {
|
|
|
98
92
|
if (opts.baseUrl) {
|
|
99
93
|
return opts.baseUrl.replace(/\{service\}/g, opts.service);
|
|
100
94
|
}
|
|
101
|
-
const envBaseUrl = process.env.API_EMULATOR_BASE_URL
|
|
95
|
+
const envBaseUrl = process.env.API_EMULATOR_BASE_URL;
|
|
102
96
|
if (envBaseUrl) {
|
|
103
97
|
return envBaseUrl.replace(/\{service\}/g, opts.service);
|
|
104
98
|
}
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/external-plugin-adapter.ts","../src/plugin-manifest.ts","../src/plugin-lock.ts","../src/default-plugin-catalog.ts","../src/registry.ts","../src/base-url.ts","../src/service-runtime.ts","../src/api.ts"],"sourcesContent":["import type { ServicePlugin, Store, AuthFallback, WebhookDispatcher } from \"@api-emulator/core\";\nimport { isAbsolute, resolve } from \"path\";\nimport { readPluginManifest, validatePluginManifest, type PluginManifest } from \"./plugin-manifest.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\n\nexport interface ExternalPluginModule {\n plugin?: ServicePlugin;\n default?: ServicePlugin;\n seedFromConfig?(store: Store, baseUrl: string, config: unknown, webhooks?: WebhookDispatcher): void;\n label?: string;\n endpoints?: string;\n manifest?: PluginManifest;\n contract?: unknown;\n defaultFallback?(svcSeedConfig?: Record<string, unknown>): AuthFallback;\n initConfig?: Record<string, unknown>;\n}\n\nexport async function loadExternalPluginModule(specifier: string): Promise<PluginModule> {\n const modulePath = specifier.startsWith(\".\") || isAbsolute(specifier) ? resolve(specifier) : specifier;\n\n const mod = (await import(modulePath)) as ExternalPluginModule;\n const plugin = mod.plugin ?? mod.default;\n if (!plugin || typeof plugin.register !== \"function\" || typeof plugin.name !== \"string\") {\n throw new Error(`Plugin \"${specifier}\" must export a ServicePlugin (as \"plugin\" or default export)`);\n }\n\n const name = plugin.name;\n const manifest = validatePluginManifest(readPluginManifest(mod), name);\n return {\n name,\n label: manifest.label,\n endpoints: manifest.endpoints,\n manifest,\n async load() {\n return {\n plugin,\n seedFromConfig: mod.seedFromConfig,\n };\n },\n defaultFallback: mod.defaultFallback ?? (() => ({ login: \"admin\", id: 1, scopes: [] })),\n initConfig: manifest.initConfig,\n };\n}\n\nexport async function loadExternalPlugin(specifier: string): Promise<{ name: string; entry: PluginModule }> {\n const pluginModule = await loadExternalPluginModule(specifier);\n return { name: pluginModule.name, entry: pluginModule };\n}\n","import type { AuthFallback } from \"@api-emulator/core\";\n\nexport interface PluginManifest {\n name?: string;\n label?: string;\n endpoints?: string;\n initConfig?: Record<string, unknown>;\n contract?: unknown;\n compatibility?: {\n apiEmulator?: string;\n };\n}\n\ninterface LegacyManifestFields {\n label?: string;\n endpoints?: string;\n initConfig?: Record<string, unknown>;\n contract?: unknown;\n defaultFallback?(svcSeedConfig?: Record<string, unknown>): AuthFallback;\n}\n\nexport function readPluginManifest(mod: { manifest?: PluginManifest } & LegacyManifestFields): PluginManifest {\n return {\n label: mod.label,\n endpoints: mod.endpoints,\n initConfig: mod.initConfig,\n contract: mod.contract,\n ...mod.manifest,\n };\n}\n\nexport function validatePluginManifest(\n manifest: PluginManifest,\n pluginName: string,\n): Required<Pick<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\">> &\n Omit<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\"> {\n if (manifest.name && manifest.name !== pluginName) {\n throw new Error(`Plugin manifest name \"${manifest.name}\" does not match plugin name \"${pluginName}\"`);\n }\n\n return {\n ...manifest,\n label: manifest.label ?? `${pluginName} (external plugin)`,\n endpoints: manifest.endpoints ?? \"\",\n initConfig: manifest.initConfig ?? {},\n };\n}\n","import { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { resolve } from \"path\";\n\nexport const PLUGIN_LOCK_FILE = \"api-emulator.lock\";\n\nexport interface PluginLockEntry {\n name: string;\n source: \"registry\" | \"specifier\";\n specifier: string;\n sourceId?: string;\n packageName?: string;\n version?: string;\n}\n\nexport interface PluginLock {\n version: 1;\n plugins: Record<string, PluginLockEntry>;\n}\n\nexport function createEmptyPluginLock(): PluginLock {\n return { version: 1, plugins: {} };\n}\n\nexport function readPluginLock(cwd = process.cwd()): PluginLock {\n const path = resolve(cwd, PLUGIN_LOCK_FILE);\n if (!existsSync(path)) return createEmptyPluginLock();\n\n const parsed = JSON.parse(readFileSync(path, \"utf-8\")) as PluginLock;\n if (parsed.version !== 1 || typeof parsed.plugins !== \"object\" || parsed.plugins === null) {\n throw new Error(`Invalid ${PLUGIN_LOCK_FILE}`);\n }\n return parsed;\n}\n\nexport function writePluginLock(lock: PluginLock, cwd = process.cwd()): void {\n const sortedPlugins = Object.fromEntries(Object.entries(lock.plugins).sort(([a], [b]) => a.localeCompare(b)));\n const content = `${JSON.stringify({ version: 1, plugins: sortedPlugins }, null, 2)}\\n`;\n writeFileSync(resolve(cwd, PLUGIN_LOCK_FILE), content, \"utf-8\");\n}\n\nexport function getLockedPluginSpecifiers(cwd = process.cwd()): string[] {\n return Object.values(readPluginLock(cwd).plugins).map((entry) => entry.specifier);\n}\n","import type { PluginModule } from \"./plugin-types.js\";\n\nexport type ServiceName = string;\nexport const DEFAULT_PLUGIN_NAMES: readonly ServiceName[] = [];\nexport const SERVICE_NAMES: readonly ServiceName[] = DEFAULT_PLUGIN_NAMES;\nexport const DEFAULT_PLUGIN_REGISTRY: Record<ServiceName, PluginModule> = {};\n","import { loadExternalPluginModule } from \"./external-plugin-adapter.js\";\nimport { getLockedPluginSpecifiers } from \"./plugin-lock.js\";\nimport {\n DEFAULT_PLUGIN_REGISTRY,\n DEFAULT_PLUGIN_NAMES,\n SERVICE_NAMES,\n type ServiceName,\n} from \"./default-plugin-catalog.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\nexport { DEFAULT_PLUGIN_REGISTRY, DEFAULT_PLUGIN_NAMES, SERVICE_NAMES, type ServiceName };\nexport type { LoadedPlugin, LoadedService, PluginModule, ServiceEntry } from \"./plugin-types.js\";\n\nexport interface ResolvePluginModulesOptions {\n includeInstalled?: boolean;\n}\n\nexport const DEFAULT_TOKENS = {\n tokens: {\n test_token_admin: {\n login: \"admin\",\n scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"],\n },\n test_token_user1: {\n login: \"octocat\",\n scopes: [\"repo\", \"user\"],\n },\n },\n};\n\nexport async function resolvePluginModules(\n pluginSpecifiers: string[] = [],\n options: ResolvePluginModulesOptions = {},\n): Promise<Record<string, PluginModule>> {\n const installedSpecifiers = options.includeInstalled ? getLockedPluginSpecifiers() : [];\n const allSpecifiers = [...installedSpecifiers, ...pluginSpecifiers];\n const results = await Promise.all(allSpecifiers.map(loadExternalPluginModule));\n\n const externalEntries: Record<string, PluginModule> = {};\n for (const pluginModule of results) {\n if (pluginModule.name in DEFAULT_PLUGIN_REGISTRY) {\n throw new Error(`Plugin \"${pluginModule.name}\" conflicts with default plugin \"${pluginModule.name}\"`);\n }\n if (pluginModule.name in externalEntries) {\n throw new Error(`Duplicate plugin name \"${pluginModule.name}\"`);\n }\n externalEntries[pluginModule.name] = pluginModule;\n }\n\n return { ...DEFAULT_PLUGIN_REGISTRY, ...externalEntries };\n}\n\nexport const resolveServiceEntries = resolvePluginModules;\n\nexport function getDefaultPluginNames(): string[] {\n return [...DEFAULT_PLUGIN_NAMES];\n}\n","export interface ResolveBaseUrlOptions {\n service: string;\n port: number;\n baseUrl?: string;\n seedBaseUrl?: string;\n}\n\n/**\n * Fallback chain:\n * 1. Per-service baseUrl from seed config\n * 2. Explicit baseUrl (CLI flag or programmatic option)\n * 3. API_EMULATOR_BASE_URL env var (with {service} interpolation)\n * 4. EMULATE_BASE_URL env var for backward compatibility (with {service} interpolation)\n * 5. PORTLESS_URL env var (with {service} interpolation)\n * 6. http://localhost:<port>\n */\nexport function resolveBaseUrl(opts: ResolveBaseUrlOptions): string {\n if (opts.seedBaseUrl) {\n return opts.seedBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n if (opts.baseUrl) {\n return opts.baseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const envBaseUrl = process.env.API_EMULATOR_BASE_URL ?? process.env.EMULATE_BASE_URL;\n if (envBaseUrl) {\n return envBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const portlessUrl = process.env.PORTLESS_URL;\n if (portlessUrl) {\n return portlessUrl.replace(/\\{service\\}/g, opts.service);\n }\n return `http://localhost:${opts.port}`;\n}\n","import {\n createServer,\n createStoreFixture,\n fixtureStoreSnapshot,\n type AppKeyResolver,\n type FixtureInteraction,\n type FixtureSource,\n type Store,\n type StoreFixture,\n type StoreFixtureOptions,\n type StoreSnapshot,\n} from \"@api-emulator/core\";\nimport { serve } from \"@hono/node-server\";\nimport type { LoadedPlugin, PluginModule } from \"./registry.js\";\n\nexport interface SeedConfig {\n tokens?: Record<string, { login: string; scopes?: string[] }>;\n [service: string]: unknown;\n}\n\nexport type TokenMap = Record<string, { login: string; id: number; scopes?: string[] }>;\n\nexport interface ServiceRuntimeOptions {\n service: string;\n pluginModule: PluginModule;\n loadedPlugin: LoadedPlugin;\n port: number;\n baseUrl: string;\n tokens: TokenMap;\n seedConfig?: Record<string, unknown>;\n}\n\nexport interface RunningService {\n service: string;\n url: string;\n store: Store;\n snapshot(): StoreSnapshot;\n restore(fixture: FixtureSource): void;\n exportFixture(options?: StoreFixtureOptions): StoreFixture;\n resetToFixture(fixture: FixtureSource): void;\n reset(): void;\n close(): Promise<void>;\n}\n\nexport function createAuthTokens(seedConfig?: SeedConfig | null): TokenMap {\n const tokens: TokenMap = {};\n if (seedConfig?.tokens) {\n let tokenId = 100;\n for (const [token, user] of Object.entries(seedConfig.tokens)) {\n tokens[token] = { login: user.login, id: tokenId++, scopes: user.scopes };\n }\n } else {\n tokens[\"test_token_admin\"] = { login: \"admin\", id: 2, scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"] };\n }\n return tokens;\n}\n\nexport function createServiceRuntime(options: ServiceRuntimeOptions): RunningService {\n const { service, pluginModule, loadedPlugin, port, baseUrl, tokens, seedConfig } = options;\n\n const resolverRef: { current?: AppKeyResolver } = {};\n const appKeyResolver: AppKeyResolver | undefined = loadedPlugin.createAppKeyResolver\n ? (appId) => resolverRef.current!(appId)\n : undefined;\n const fallbackUser = pluginModule.defaultFallback(seedConfig);\n\n const { app, store, webhooks } = createServer(loadedPlugin.plugin, {\n port,\n baseUrl,\n tokens,\n appKeyResolver,\n fallbackUser,\n });\n resolverRef.current = loadedPlugin.createAppKeyResolver?.(store);\n\n const seed = () => {\n loadedPlugin.plugin.seed?.(store, baseUrl);\n if (seedConfig && loadedPlugin.seedFromConfig) {\n loadedPlugin.seedFromConfig(store, baseUrl, seedConfig, webhooks);\n }\n };\n seed();\n\n const httpServer = serve({ fetch: app.fetch, port });\n\n return {\n service,\n url: baseUrl,\n store,\n snapshot() {\n return store.snapshot();\n },\n restore(fixture) {\n store.restore(fixtureStoreSnapshot(fixture));\n },\n exportFixture(options = {}) {\n const interactions = store.getData<FixtureInteraction[]>(\"api-emulator:interactions\");\n return createStoreFixture(service, store.snapshot(), {\n ...options,\n interactions: options.interactions ?? interactions,\n });\n },\n resetToFixture(fixture) {\n store.reset();\n store.restore(fixtureStoreSnapshot(fixture));\n },\n reset() {\n store.reset();\n seed();\n },\n close(): Promise<void> {\n return new Promise((resolve, reject) => {\n httpServer.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n },\n };\n}\n","import { resolvePluginModules } from \"./registry.js\";\nexport type { ServiceName } from \"./registry.js\";\nimport type { ServiceName } from \"./registry.js\";\nimport type { FixtureSource, StoreFixture, StoreFixtureOptions, StoreSnapshot } from \"@api-emulator/core\";\nimport { resolveBaseUrl } from \"./base-url.js\";\nimport { createAuthTokens, createServiceRuntime, type SeedConfig } from \"./service-runtime.js\";\n\nexport type { SeedConfig };\nexport type {\n FixtureInteraction,\n FixtureSource,\n StoreFixture,\n StoreFixtureOptions,\n StoreSnapshot,\n} from \"@api-emulator/core\";\n\nexport interface EmulatorOptions {\n service: ServiceName | (string & {});\n port?: number;\n seed?: SeedConfig;\n baseUrl?: string;\n plugins?: string[];\n}\n\nexport interface Emulator {\n url: string;\n snapshot(): StoreSnapshot;\n restore(fixture: FixtureSource): void;\n exportFixture(options?: StoreFixtureOptions): StoreFixture;\n resetToFixture(fixture: FixtureSource): void;\n reset(): void;\n close(): Promise<void>;\n}\n\nexport async function createEmulator(options: EmulatorOptions): Promise<Emulator> {\n const { service, port = 4000, seed: seedConfig, plugins = [] } = options;\n\n const registry = await resolvePluginModules(plugins);\n const pluginModule = registry[service];\n if (!pluginModule) {\n throw new Error(`Unknown service: ${service}`);\n }\n\n const loadedPlugin = await pluginModule.load();\n\n const svcSeedConfig = seedConfig?.[service] as Record<string, unknown> | undefined;\n const seedBaseUrl =\n typeof svcSeedConfig?.baseUrl === \"string\" && svcSeedConfig.baseUrl.length > 0 ? svcSeedConfig.baseUrl : undefined;\n const baseUrl = resolveBaseUrl({ service, port, baseUrl: options.baseUrl, seedBaseUrl });\n const running = createServiceRuntime({\n service,\n pluginModule,\n loadedPlugin,\n port,\n baseUrl,\n tokens: createAuthTokens(seedConfig),\n seedConfig: svcSeedConfig,\n });\n\n return {\n url: running.url,\n snapshot: running.snapshot,\n restore: running.restore,\n exportFixture: running.exportFixture,\n resetToFixture: running.resetToFixture,\n reset: running.reset,\n close: running.close,\n };\n}\n"],"mappings":"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","names":["resolve","options","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/external-plugin-adapter.ts","../src/plugin-manifest.ts","../src/plugin-lock.ts","../src/default-plugin-catalog.ts","../src/registry.ts","../src/base-url.ts","../src/service-runtime.ts","../src/api.ts"],"sourcesContent":["import type { ServicePlugin, Store, AuthFallback, WebhookDispatcher } from \"@api-emulator/core\";\nimport { isAbsolute, resolve } from \"path\";\nimport { readPluginManifest, validatePluginManifest, type PluginManifest } from \"./plugin-manifest.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\n\nexport interface ExternalPluginModule {\n plugin?: ServicePlugin;\n default?: ServicePlugin;\n seedFromConfig?(store: Store, baseUrl: string, config: unknown, webhooks?: WebhookDispatcher): void;\n manifest?: PluginManifest;\n defaultFallback?(svcSeedConfig?: Record<string, unknown>): AuthFallback;\n}\n\nexport async function loadExternalPluginModule(specifier: string): Promise<PluginModule> {\n const modulePath = specifier.startsWith(\".\") || isAbsolute(specifier) ? resolve(specifier) : specifier;\n\n const mod = (await import(modulePath)) as ExternalPluginModule;\n const plugin = mod.plugin ?? mod.default;\n if (!plugin || typeof plugin.register !== \"function\" || typeof plugin.name !== \"string\") {\n throw new Error(`Plugin \"${specifier}\" must export a ServicePlugin (as \"plugin\" or default export)`);\n }\n\n const name = plugin.name;\n const manifest = validatePluginManifest(readPluginManifest(mod), name);\n return {\n name,\n label: manifest.label,\n endpoints: manifest.endpoints,\n manifest,\n async load() {\n return {\n plugin,\n seedFromConfig: mod.seedFromConfig,\n };\n },\n defaultFallback: mod.defaultFallback ?? (() => ({ login: \"admin\", id: 1, scopes: [] })),\n initConfig: manifest.initConfig,\n };\n}\n\nexport async function loadExternalPlugin(specifier: string): Promise<{ name: string; entry: PluginModule }> {\n const pluginModule = await loadExternalPluginModule(specifier);\n return { name: pluginModule.name, entry: pluginModule };\n}\n","export interface PluginManifest {\n name?: string;\n label?: string;\n endpoints?: string;\n initConfig?: Record<string, unknown>;\n contract?: unknown;\n}\n\nexport function readPluginManifest(mod: { manifest?: PluginManifest }): PluginManifest {\n return mod.manifest ?? {};\n}\n\nexport function validatePluginManifest(\n manifest: PluginManifest,\n pluginName: string,\n): Required<Pick<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\">> &\n Omit<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\"> {\n if (manifest.name && manifest.name !== pluginName) {\n throw new Error(`Plugin manifest name \"${manifest.name}\" does not match plugin name \"${pluginName}\"`);\n }\n\n return {\n ...manifest,\n label: manifest.label ?? `${pluginName} (external plugin)`,\n endpoints: manifest.endpoints ?? \"\",\n initConfig: manifest.initConfig ?? {},\n };\n}\n","import { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { resolve } from \"path\";\n\nexport const PLUGIN_LOCK_FILE = \"api-emulator.lock\";\n\nexport interface PluginLockEntry {\n name: string;\n source: \"registry\" | \"specifier\";\n specifier: string;\n sourceId?: string;\n packageName?: string;\n version?: string;\n}\n\nexport interface PluginLock {\n version: 1;\n plugins: Record<string, PluginLockEntry>;\n}\n\nexport function createEmptyPluginLock(): PluginLock {\n return { version: 1, plugins: {} };\n}\n\nexport function readPluginLock(cwd = process.cwd()): PluginLock {\n const path = resolve(cwd, PLUGIN_LOCK_FILE);\n if (!existsSync(path)) return createEmptyPluginLock();\n\n const parsed = JSON.parse(readFileSync(path, \"utf-8\")) as PluginLock;\n if (parsed.version !== 1 || typeof parsed.plugins !== \"object\" || parsed.plugins === null) {\n throw new Error(`Invalid ${PLUGIN_LOCK_FILE}`);\n }\n return parsed;\n}\n\nexport function writePluginLock(lock: PluginLock, cwd = process.cwd()): void {\n const sortedPlugins = Object.fromEntries(Object.entries(lock.plugins).sort(([a], [b]) => a.localeCompare(b)));\n const content = `${JSON.stringify({ version: 1, plugins: sortedPlugins }, null, 2)}\\n`;\n writeFileSync(resolve(cwd, PLUGIN_LOCK_FILE), content, \"utf-8\");\n}\n\nexport function getLockedPluginSpecifiers(cwd = process.cwd()): string[] {\n return Object.values(readPluginLock(cwd).plugins).map((entry) => entry.specifier);\n}\n","import type { PluginModule } from \"./plugin-types.js\";\n\nexport type ServiceName = string;\nexport const DEFAULT_PLUGIN_NAMES: readonly ServiceName[] = [];\nexport const SERVICE_NAMES: readonly ServiceName[] = DEFAULT_PLUGIN_NAMES;\nexport const DEFAULT_PLUGIN_REGISTRY: Record<ServiceName, PluginModule> = {};\n","import { loadExternalPluginModule } from \"./external-plugin-adapter.js\";\nimport { getLockedPluginSpecifiers } from \"./plugin-lock.js\";\nimport {\n DEFAULT_PLUGIN_REGISTRY,\n DEFAULT_PLUGIN_NAMES,\n SERVICE_NAMES,\n type ServiceName,\n} from \"./default-plugin-catalog.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\nexport { DEFAULT_PLUGIN_REGISTRY, DEFAULT_PLUGIN_NAMES, SERVICE_NAMES, type ServiceName };\nexport type { LoadedPlugin, LoadedService, PluginModule, ServiceEntry } from \"./plugin-types.js\";\n\nexport interface ResolvePluginModulesOptions {\n includeInstalled?: boolean;\n}\n\nexport const DEFAULT_TOKENS = {\n tokens: {\n test_token_admin: {\n login: \"admin\",\n scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"],\n },\n test_token_user1: {\n login: \"octocat\",\n scopes: [\"repo\", \"user\"],\n },\n },\n};\n\nexport async function resolvePluginModules(\n pluginSpecifiers: string[] = [],\n options: ResolvePluginModulesOptions = {},\n): Promise<Record<string, PluginModule>> {\n const installedSpecifiers = options.includeInstalled ? getLockedPluginSpecifiers() : [];\n const allSpecifiers = [...installedSpecifiers, ...pluginSpecifiers];\n const results = await Promise.all(allSpecifiers.map(loadExternalPluginModule));\n\n const externalEntries: Record<string, PluginModule> = {};\n for (const pluginModule of results) {\n if (pluginModule.name in DEFAULT_PLUGIN_REGISTRY) {\n throw new Error(`Plugin \"${pluginModule.name}\" conflicts with default plugin \"${pluginModule.name}\"`);\n }\n if (pluginModule.name in externalEntries) {\n throw new Error(`Duplicate plugin name \"${pluginModule.name}\"`);\n }\n externalEntries[pluginModule.name] = pluginModule;\n }\n\n return { ...DEFAULT_PLUGIN_REGISTRY, ...externalEntries };\n}\n\nexport const resolveServiceEntries = resolvePluginModules;\n\nexport function getDefaultPluginNames(): string[] {\n return [...DEFAULT_PLUGIN_NAMES];\n}\n","export interface ResolveBaseUrlOptions {\n service: string;\n port: number;\n baseUrl?: string;\n seedBaseUrl?: string;\n}\n\n/**\n * Fallback chain:\n * 1. Per-service baseUrl from seed config\n * 2. Explicit baseUrl (CLI flag or programmatic option)\n * 3. API_EMULATOR_BASE_URL env var (with {service} interpolation)\n * 4. PORTLESS_URL env var (with {service} interpolation)\n * 5. http://localhost:<port>\n */\nexport function resolveBaseUrl(opts: ResolveBaseUrlOptions): string {\n if (opts.seedBaseUrl) {\n return opts.seedBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n if (opts.baseUrl) {\n return opts.baseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const envBaseUrl = process.env.API_EMULATOR_BASE_URL;\n if (envBaseUrl) {\n return envBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const portlessUrl = process.env.PORTLESS_URL;\n if (portlessUrl) {\n return portlessUrl.replace(/\\{service\\}/g, opts.service);\n }\n return `http://localhost:${opts.port}`;\n}\n","import {\n createServer,\n createStoreFixture,\n fixtureStoreSnapshot,\n type AppKeyResolver,\n type FixtureInteraction,\n type FixtureSource,\n type Store,\n type StoreFixture,\n type StoreFixtureOptions,\n type StoreSnapshot,\n} from \"@api-emulator/core\";\nimport { serve } from \"@hono/node-server\";\nimport type { LoadedPlugin, PluginModule } from \"./registry.js\";\n\nexport interface SeedConfig {\n tokens?: Record<string, { login: string; scopes?: string[] }>;\n [service: string]: unknown;\n}\n\nexport type TokenMap = Record<string, { login: string; id: number; scopes?: string[] }>;\n\nexport interface ServiceRuntimeOptions {\n service: string;\n pluginModule: PluginModule;\n loadedPlugin: LoadedPlugin;\n port: number;\n baseUrl: string;\n tokens: TokenMap;\n seedConfig?: Record<string, unknown>;\n}\n\nexport interface RunningService {\n service: string;\n url: string;\n store: Store;\n snapshot(): StoreSnapshot;\n restore(fixture: FixtureSource): void;\n exportFixture(options?: StoreFixtureOptions): StoreFixture;\n resetToFixture(fixture: FixtureSource): void;\n reset(): void;\n close(): Promise<void>;\n}\n\nexport function createAuthTokens(seedConfig?: SeedConfig | null): TokenMap {\n const tokens: TokenMap = {};\n if (seedConfig?.tokens) {\n let tokenId = 100;\n for (const [token, user] of Object.entries(seedConfig.tokens)) {\n tokens[token] = { login: user.login, id: tokenId++, scopes: user.scopes };\n }\n } else {\n tokens[\"test_token_admin\"] = { login: \"admin\", id: 2, scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"] };\n }\n return tokens;\n}\n\nexport function createServiceRuntime(options: ServiceRuntimeOptions): RunningService {\n const { service, pluginModule, loadedPlugin, port, baseUrl, tokens, seedConfig } = options;\n\n const resolverRef: { current?: AppKeyResolver } = {};\n const appKeyResolver: AppKeyResolver | undefined = loadedPlugin.createAppKeyResolver\n ? (appId) => resolverRef.current!(appId)\n : undefined;\n const fallbackUser = pluginModule.defaultFallback(seedConfig);\n\n const { app, store, webhooks } = createServer(loadedPlugin.plugin, {\n port,\n baseUrl,\n tokens,\n appKeyResolver,\n fallbackUser,\n });\n resolverRef.current = loadedPlugin.createAppKeyResolver?.(store);\n\n const seed = () => {\n loadedPlugin.plugin.seed?.(store, baseUrl);\n if (seedConfig && loadedPlugin.seedFromConfig) {\n loadedPlugin.seedFromConfig(store, baseUrl, seedConfig, webhooks);\n }\n };\n seed();\n\n const httpServer = serve({ fetch: app.fetch, port });\n\n return {\n service,\n url: baseUrl,\n store,\n snapshot() {\n return store.snapshot();\n },\n restore(fixture) {\n store.restore(fixtureStoreSnapshot(fixture));\n },\n exportFixture(options = {}) {\n const interactions = store.getData<FixtureInteraction[]>(\"api-emulator:interactions\");\n return createStoreFixture(service, store.snapshot(), {\n ...options,\n interactions: options.interactions ?? interactions,\n });\n },\n resetToFixture(fixture) {\n store.reset();\n store.restore(fixtureStoreSnapshot(fixture));\n },\n reset() {\n store.reset();\n seed();\n },\n close(): Promise<void> {\n return new Promise((resolve, reject) => {\n httpServer.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n },\n };\n}\n","import { resolvePluginModules } from \"./registry.js\";\nexport type { ServiceName } from \"./registry.js\";\nimport type { ServiceName } from \"./registry.js\";\nimport type { FixtureSource, StoreFixture, StoreFixtureOptions, StoreSnapshot } from \"@api-emulator/core\";\nimport { resolveBaseUrl } from \"./base-url.js\";\nimport { createAuthTokens, createServiceRuntime, type SeedConfig } from \"./service-runtime.js\";\n\nexport type { SeedConfig };\nexport type {\n FixtureInteraction,\n FixtureSource,\n StoreFixture,\n StoreFixtureOptions,\n StoreSnapshot,\n} from \"@api-emulator/core\";\n\nexport interface EmulatorOptions {\n service: ServiceName | (string & {});\n port?: number;\n seed?: SeedConfig;\n baseUrl?: string;\n plugins?: string[];\n}\n\nexport interface Emulator {\n url: string;\n snapshot(): StoreSnapshot;\n restore(fixture: FixtureSource): void;\n exportFixture(options?: StoreFixtureOptions): StoreFixture;\n resetToFixture(fixture: FixtureSource): void;\n reset(): void;\n close(): Promise<void>;\n}\n\nexport async function createEmulator(options: EmulatorOptions): Promise<Emulator> {\n const { service, port = 4000, seed: seedConfig, plugins = [] } = options;\n\n const registry = await resolvePluginModules(plugins);\n const pluginModule = registry[service];\n if (!pluginModule) {\n throw new Error(`Unknown service: ${service}`);\n }\n\n const loadedPlugin = await pluginModule.load();\n\n const svcSeedConfig = seedConfig?.[service] as Record<string, unknown> | undefined;\n const seedBaseUrl =\n typeof svcSeedConfig?.baseUrl === \"string\" && svcSeedConfig.baseUrl.length > 0 ? svcSeedConfig.baseUrl : undefined;\n const baseUrl = resolveBaseUrl({ service, port, baseUrl: options.baseUrl, seedBaseUrl });\n const running = createServiceRuntime({\n service,\n pluginModule,\n loadedPlugin,\n port,\n baseUrl,\n tokens: createAuthTokens(seedConfig),\n seedConfig: svcSeedConfig,\n });\n\n return {\n url: running.url,\n snapshot: running.snapshot,\n restore: running.restore,\n exportFixture: running.exportFixture,\n resetToFixture: running.resetToFixture,\n reset: running.reset,\n close: running.close,\n };\n}\n"],"mappings":"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","names":["resolve","options","resolve"]}
|
package/dist/index.js
CHANGED
|
@@ -7,13 +7,7 @@ import { isAbsolute, resolve } from "path";
|
|
|
7
7
|
|
|
8
8
|
// src/plugin-manifest.ts
|
|
9
9
|
function readPluginManifest(mod) {
|
|
10
|
-
return {
|
|
11
|
-
label: mod.label,
|
|
12
|
-
endpoints: mod.endpoints,
|
|
13
|
-
initConfig: mod.initConfig,
|
|
14
|
-
contract: mod.contract,
|
|
15
|
-
...mod.manifest
|
|
16
|
-
};
|
|
10
|
+
return mod.manifest ?? {};
|
|
17
11
|
}
|
|
18
12
|
function validatePluginManifest(manifest, pluginName) {
|
|
19
13
|
if (manifest.name && manifest.name !== pluginName) {
|
|
@@ -208,7 +202,7 @@ function resolveBaseUrl(opts) {
|
|
|
208
202
|
if (opts.baseUrl) {
|
|
209
203
|
return opts.baseUrl.replace(/\{service\}/g, opts.service);
|
|
210
204
|
}
|
|
211
|
-
const envBaseUrl = process.env.API_EMULATOR_BASE_URL
|
|
205
|
+
const envBaseUrl = process.env.API_EMULATOR_BASE_URL;
|
|
212
206
|
if (envBaseUrl) {
|
|
213
207
|
return envBaseUrl.replace(/\{service\}/g, opts.service);
|
|
214
208
|
}
|
|
@@ -296,7 +290,7 @@ function createServiceRuntime(options) {
|
|
|
296
290
|
}
|
|
297
291
|
|
|
298
292
|
// src/commands/start.ts
|
|
299
|
-
var pkg = { version: "0.
|
|
293
|
+
var pkg = { version: "0.6.0" };
|
|
300
294
|
function loadSeedConfig(seedPath) {
|
|
301
295
|
if (seedPath) {
|
|
302
296
|
const fullPath = resolve3(seedPath);
|
|
@@ -313,17 +307,7 @@ function loadSeedConfig(seedPath) {
|
|
|
313
307
|
process.exit(1);
|
|
314
308
|
}
|
|
315
309
|
}
|
|
316
|
-
const autoFiles = [
|
|
317
|
-
"api-emulator.config.yaml",
|
|
318
|
-
"api-emulator.config.yml",
|
|
319
|
-
"api-emulator.config.json",
|
|
320
|
-
"emulate.config.yaml",
|
|
321
|
-
"emulate.config.yml",
|
|
322
|
-
"emulate.config.json",
|
|
323
|
-
"service-emulator.config.yaml",
|
|
324
|
-
"service-emulator.config.yml",
|
|
325
|
-
"service-emulator.config.json"
|
|
326
|
-
];
|
|
310
|
+
const autoFiles = ["api-emulator.config.yaml", "api-emulator.config.yml", "api-emulator.config.json"];
|
|
327
311
|
for (const file of autoFiles) {
|
|
328
312
|
const fullPath = resolve3(file);
|
|
329
313
|
if (existsSync2(fullPath)) {
|
|
@@ -677,8 +661,8 @@ async function installCommand(name, options = {}) {
|
|
|
677
661
|
}
|
|
678
662
|
|
|
679
663
|
// src/index.ts
|
|
680
|
-
var pkg2 = { version: "0.
|
|
681
|
-
var defaultPort = process.env.API_EMULATOR_PORT ?? process.env.
|
|
664
|
+
var pkg2 = { version: "0.6.0" };
|
|
665
|
+
var defaultPort = process.env.API_EMULATOR_PORT ?? process.env.PORT ?? "4000";
|
|
682
666
|
var program = new Command();
|
|
683
667
|
program.name("api").description("Local API emulators you can run, share, and extend with plugins").version(pkg2.version);
|
|
684
668
|
program.command("start", { isDefault: true }).description("Start the emulator server").option("-p, --port <port>", "Base port", defaultPort).option("-s, --service <services>", "Comma-separated services to enable").option("--seed <file>", "Path to seed config file").option("--base-url <url>", "Override advertised base URL (supports {service} template)").option("--portless", "Serve over HTTPS via portless (auto-registers aliases)").option("--plugin <plugins>", "Comma-separated external plugin paths or package names").action(async (opts) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/external-plugin-adapter.ts","../src/plugin-manifest.ts","../src/plugin-lock.ts","../src/default-plugin-catalog.ts","../src/registry.ts","../src/commands/start.ts","../src/portless.ts","../src/base-url.ts","../src/service-runtime.ts","../src/commands/init.ts","../src/commands/list.ts","../src/commands/install.ts","../src/plugin-source-registry.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { startCommand } from \"./commands/start.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { listCommand } from \"./commands/list.js\";\nimport { installCommand } from \"./commands/install.js\";\n\ndeclare const PKG_VERSION: string;\nconst pkg = { version: PKG_VERSION };\n\nconst defaultPort = process.env.API_EMULATOR_PORT ?? process.env.EMULATE_PORT ?? process.env.PORT ?? \"4000\";\n\nconst program = new Command();\n\nprogram.name(\"api\").description(\"Local API emulators you can run, share, and extend with plugins\").version(pkg.version);\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"Start the emulator server\")\n .option(\"-p, --port <port>\", \"Base port\", defaultPort)\n .option(\"-s, --service <services>\", \"Comma-separated services to enable\")\n .option(\"--seed <file>\", \"Path to seed config file\")\n .option(\"--base-url <url>\", \"Override advertised base URL (supports {service} template)\")\n .option(\"--portless\", \"Serve over HTTPS via portless (auto-registers aliases)\")\n .option(\"--plugin <plugins>\", \"Comma-separated external plugin paths or package names\")\n .action(async (opts) => {\n const port = parseInt(opts.port, 10);\n if (Number.isNaN(port) || port < 1 || port > 65535) {\n console.error(`Invalid port: ${opts.port}`);\n process.exit(1);\n }\n await startCommand({\n port,\n service: opts.service,\n seed: opts.seed,\n baseUrl: opts.baseUrl,\n portless: opts.portless,\n plugin: opts.plugin,\n });\n });\n\nprogram\n .command(\"init\")\n .description(\"Generate a starter config file\")\n .option(\"-s, --service <service>\", \"Service to generate config for\", \"all\")\n .option(\"--plugin <plugins>\", \"Comma-separated external plugin paths or package names\")\n .action(async (opts) => {\n await initCommand({ service: opts.service, plugin: opts.plugin });\n });\n\nprogram\n .command(\"list\")\n .alias(\"list-services\")\n .description(\"List available services\")\n .option(\"--plugin <plugins>\", \"Comma-separated external plugin paths or package names\")\n .action(async (opts) => {\n await listCommand({ plugin: opts.plugin });\n });\n\nprogram\n .command(\"install <plugin>\")\n .description(\"Install a provider plugin by name\")\n .option(\"--package-manager <name>\", \"Package manager to use\")\n .option(\"--no-package-manager\", \"Only record the plugin in api-emulator.lock\")\n .action(async (plugin, opts) => {\n try {\n await installCommand(plugin, {\n packageManager: opts.packageManager === false ? false : opts.packageManager,\n });\n } catch (err) {\n console.error(err instanceof Error ? err.message : err);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import type { ServicePlugin, Store, AuthFallback, WebhookDispatcher } from \"@api-emulator/core\";\nimport { isAbsolute, resolve } from \"path\";\nimport { readPluginManifest, validatePluginManifest, type PluginManifest } from \"./plugin-manifest.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\n\nexport interface ExternalPluginModule {\n plugin?: ServicePlugin;\n default?: ServicePlugin;\n seedFromConfig?(store: Store, baseUrl: string, config: unknown, webhooks?: WebhookDispatcher): void;\n label?: string;\n endpoints?: string;\n manifest?: PluginManifest;\n contract?: unknown;\n defaultFallback?(svcSeedConfig?: Record<string, unknown>): AuthFallback;\n initConfig?: Record<string, unknown>;\n}\n\nexport async function loadExternalPluginModule(specifier: string): Promise<PluginModule> {\n const modulePath = specifier.startsWith(\".\") || isAbsolute(specifier) ? resolve(specifier) : specifier;\n\n const mod = (await import(modulePath)) as ExternalPluginModule;\n const plugin = mod.plugin ?? mod.default;\n if (!plugin || typeof plugin.register !== \"function\" || typeof plugin.name !== \"string\") {\n throw new Error(`Plugin \"${specifier}\" must export a ServicePlugin (as \"plugin\" or default export)`);\n }\n\n const name = plugin.name;\n const manifest = validatePluginManifest(readPluginManifest(mod), name);\n return {\n name,\n label: manifest.label,\n endpoints: manifest.endpoints,\n manifest,\n async load() {\n return {\n plugin,\n seedFromConfig: mod.seedFromConfig,\n };\n },\n defaultFallback: mod.defaultFallback ?? (() => ({ login: \"admin\", id: 1, scopes: [] })),\n initConfig: manifest.initConfig,\n };\n}\n\nexport async function loadExternalPlugin(specifier: string): Promise<{ name: string; entry: PluginModule }> {\n const pluginModule = await loadExternalPluginModule(specifier);\n return { name: pluginModule.name, entry: pluginModule };\n}\n","import type { AuthFallback } from \"@api-emulator/core\";\n\nexport interface PluginManifest {\n name?: string;\n label?: string;\n endpoints?: string;\n initConfig?: Record<string, unknown>;\n contract?: unknown;\n compatibility?: {\n apiEmulator?: string;\n };\n}\n\ninterface LegacyManifestFields {\n label?: string;\n endpoints?: string;\n initConfig?: Record<string, unknown>;\n contract?: unknown;\n defaultFallback?(svcSeedConfig?: Record<string, unknown>): AuthFallback;\n}\n\nexport function readPluginManifest(mod: { manifest?: PluginManifest } & LegacyManifestFields): PluginManifest {\n return {\n label: mod.label,\n endpoints: mod.endpoints,\n initConfig: mod.initConfig,\n contract: mod.contract,\n ...mod.manifest,\n };\n}\n\nexport function validatePluginManifest(\n manifest: PluginManifest,\n pluginName: string,\n): Required<Pick<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\">> &\n Omit<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\"> {\n if (manifest.name && manifest.name !== pluginName) {\n throw new Error(`Plugin manifest name \"${manifest.name}\" does not match plugin name \"${pluginName}\"`);\n }\n\n return {\n ...manifest,\n label: manifest.label ?? `${pluginName} (external plugin)`,\n endpoints: manifest.endpoints ?? \"\",\n initConfig: manifest.initConfig ?? {},\n };\n}\n","import { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { resolve } from \"path\";\n\nexport const PLUGIN_LOCK_FILE = \"api-emulator.lock\";\n\nexport interface PluginLockEntry {\n name: string;\n source: \"registry\" | \"specifier\";\n specifier: string;\n sourceId?: string;\n packageName?: string;\n version?: string;\n}\n\nexport interface PluginLock {\n version: 1;\n plugins: Record<string, PluginLockEntry>;\n}\n\nexport function createEmptyPluginLock(): PluginLock {\n return { version: 1, plugins: {} };\n}\n\nexport function readPluginLock(cwd = process.cwd()): PluginLock {\n const path = resolve(cwd, PLUGIN_LOCK_FILE);\n if (!existsSync(path)) return createEmptyPluginLock();\n\n const parsed = JSON.parse(readFileSync(path, \"utf-8\")) as PluginLock;\n if (parsed.version !== 1 || typeof parsed.plugins !== \"object\" || parsed.plugins === null) {\n throw new Error(`Invalid ${PLUGIN_LOCK_FILE}`);\n }\n return parsed;\n}\n\nexport function writePluginLock(lock: PluginLock, cwd = process.cwd()): void {\n const sortedPlugins = Object.fromEntries(Object.entries(lock.plugins).sort(([a], [b]) => a.localeCompare(b)));\n const content = `${JSON.stringify({ version: 1, plugins: sortedPlugins }, null, 2)}\\n`;\n writeFileSync(resolve(cwd, PLUGIN_LOCK_FILE), content, \"utf-8\");\n}\n\nexport function getLockedPluginSpecifiers(cwd = process.cwd()): string[] {\n return Object.values(readPluginLock(cwd).plugins).map((entry) => entry.specifier);\n}\n","import type { PluginModule } from \"./plugin-types.js\";\n\nexport type ServiceName = string;\nexport const DEFAULT_PLUGIN_NAMES: readonly ServiceName[] = [];\nexport const SERVICE_NAMES: readonly ServiceName[] = DEFAULT_PLUGIN_NAMES;\nexport const DEFAULT_PLUGIN_REGISTRY: Record<ServiceName, PluginModule> = {};\n","import { loadExternalPluginModule } from \"./external-plugin-adapter.js\";\nimport { getLockedPluginSpecifiers } from \"./plugin-lock.js\";\nimport {\n DEFAULT_PLUGIN_REGISTRY,\n DEFAULT_PLUGIN_NAMES,\n SERVICE_NAMES,\n type ServiceName,\n} from \"./default-plugin-catalog.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\nexport { DEFAULT_PLUGIN_REGISTRY, DEFAULT_PLUGIN_NAMES, SERVICE_NAMES, type ServiceName };\nexport type { LoadedPlugin, LoadedService, PluginModule, ServiceEntry } from \"./plugin-types.js\";\n\nexport interface ResolvePluginModulesOptions {\n includeInstalled?: boolean;\n}\n\nexport const DEFAULT_TOKENS = {\n tokens: {\n test_token_admin: {\n login: \"admin\",\n scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"],\n },\n test_token_user1: {\n login: \"octocat\",\n scopes: [\"repo\", \"user\"],\n },\n },\n};\n\nexport async function resolvePluginModules(\n pluginSpecifiers: string[] = [],\n options: ResolvePluginModulesOptions = {},\n): Promise<Record<string, PluginModule>> {\n const installedSpecifiers = options.includeInstalled ? getLockedPluginSpecifiers() : [];\n const allSpecifiers = [...installedSpecifiers, ...pluginSpecifiers];\n const results = await Promise.all(allSpecifiers.map(loadExternalPluginModule));\n\n const externalEntries: Record<string, PluginModule> = {};\n for (const pluginModule of results) {\n if (pluginModule.name in DEFAULT_PLUGIN_REGISTRY) {\n throw new Error(`Plugin \"${pluginModule.name}\" conflicts with default plugin \"${pluginModule.name}\"`);\n }\n if (pluginModule.name in externalEntries) {\n throw new Error(`Duplicate plugin name \"${pluginModule.name}\"`);\n }\n externalEntries[pluginModule.name] = pluginModule;\n }\n\n return { ...DEFAULT_PLUGIN_REGISTRY, ...externalEntries };\n}\n\nexport const resolveServiceEntries = resolvePluginModules;\n\nexport function getDefaultPluginNames(): string[] {\n return [...DEFAULT_PLUGIN_NAMES];\n}\n","import { resolvePluginModules, getDefaultPluginNames, type LoadedPlugin, type PluginModule } from \"../registry.js\";\nimport { readFileSync, existsSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport pc from \"picocolors\";\nimport { ensurePortless, registerAliases, removeAliases, portlessBaseUrl, type PortlessAlias } from \"../portless.js\";\nimport { resolveBaseUrl } from \"../base-url.js\";\nimport { createAuthTokens, createServiceRuntime, type RunningService, type SeedConfig } from \"../service-runtime.js\";\n\ndeclare const PKG_VERSION: string;\nconst pkg = { version: PKG_VERSION };\n\nexport interface StartOptions {\n port: number;\n service?: string;\n seed?: string;\n baseUrl?: string;\n portless?: boolean;\n plugin?: string;\n}\n\ninterface LoadResult {\n config: SeedConfig;\n source: string;\n}\n\nfunction loadSeedConfig(seedPath?: string): LoadResult | null {\n if (seedPath) {\n const fullPath = resolve(seedPath);\n if (!existsSync(fullPath)) {\n console.error(`Seed file not found: ${fullPath}`);\n process.exit(1);\n }\n const content = readFileSync(fullPath, \"utf-8\");\n try {\n const config = fullPath.endsWith(\".json\") ? JSON.parse(content) : parseYaml(content);\n return { config, source: seedPath };\n } catch (err) {\n console.error(`Failed to parse ${seedPath}: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n }\n\n const autoFiles = [\n \"api-emulator.config.yaml\",\n \"api-emulator.config.yml\",\n \"api-emulator.config.json\",\n \"emulate.config.yaml\",\n \"emulate.config.yml\",\n \"emulate.config.json\",\n \"service-emulator.config.yaml\",\n \"service-emulator.config.yml\",\n \"service-emulator.config.json\",\n ];\n\n for (const file of autoFiles) {\n const fullPath = resolve(file);\n if (existsSync(fullPath)) {\n const content = readFileSync(fullPath, \"utf-8\");\n try {\n const config = fullPath.endsWith(\".json\") ? JSON.parse(content) : parseYaml(content);\n return { config, source: file };\n } catch (err) {\n console.error(`Failed to parse ${file}: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n }\n }\n\n return null;\n}\n\nfunction inferServicesFromConfig(config: SeedConfig, availableServices: string[]): string[] | null {\n const found = availableServices.filter((k) => k in config);\n return found.length > 0 ? [...found] : null;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const { port: basePort } = options;\n\n if (options.portless && options.baseUrl) {\n console.error(\"--portless and --base-url are mutually exclusive.\");\n process.exit(1);\n }\n\n const loaded = loadSeedConfig(options.seed);\n const seedConfig = loaded?.config ?? null;\n const configSource = loaded?.source ?? null;\n\n const pluginSpecifiers =\n options.plugin\n ?.split(\",\")\n .map((s) => s.trim())\n .filter(Boolean) ?? [];\n let allPluginModules: Record<string, PluginModule>;\n try {\n allPluginModules = await resolvePluginModules(pluginSpecifiers, { includeInstalled: true });\n } catch (err) {\n console.error(`Failed to load plugins: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n\n const defaultPlugins = getDefaultPluginNames();\n const externalServices = Object.keys(allPluginModules).filter((name) => !defaultPlugins.includes(name));\n\n let services: string[];\n if (options.service) {\n services = options.service.split(\",\").map((s) => s.trim());\n } else if (seedConfig) {\n services = inferServicesFromConfig(seedConfig, Object.keys(allPluginModules)) ?? [\n ...defaultPlugins,\n ...externalServices,\n ];\n } else {\n services = [...defaultPlugins, ...externalServices];\n }\n\n for (const svc of services) {\n if (!allPluginModules[svc]) {\n console.error(`Unknown service: ${svc}`);\n process.exit(1);\n }\n }\n\n const tokens = createAuthTokens(seedConfig);\n\n if (options.portless) {\n await ensurePortless();\n }\n\n interface PreparedService {\n svc: string;\n pluginModule: PluginModule;\n loadedPlugin: LoadedPlugin;\n svcSeedConfig: Record<string, unknown> | undefined;\n port: number;\n baseUrl: string;\n }\n\n const portlessAliases: PortlessAlias[] = [];\n const prepared: PreparedService[] = [];\n\n for (let i = 0; i < services.length; i++) {\n const svc = services[i];\n const pluginModule = allPluginModules[svc];\n const loadedPlugin = await pluginModule.load();\n\n const svcSeedConfig = seedConfig?.[svc] as Record<string, unknown> | undefined;\n const port = (svcSeedConfig?.port as number | undefined) ?? basePort + i;\n\n if (options.portless) {\n portlessAliases.push({ name: `${svc}.api-emulator`, port });\n }\n\n const seedBaseUrl =\n typeof svcSeedConfig?.baseUrl === \"string\" && svcSeedConfig.baseUrl.length > 0\n ? svcSeedConfig.baseUrl\n : undefined;\n const effectiveBaseUrl = options.portless ? portlessBaseUrl(svc) : options.baseUrl;\n const baseUrl = resolveBaseUrl({ service: svc, port, baseUrl: effectiveBaseUrl, seedBaseUrl });\n\n prepared.push({ svc, pluginModule, loadedPlugin, svcSeedConfig, port, baseUrl });\n }\n\n if (portlessAliases.length > 0) {\n registerAliases(portlessAliases);\n }\n\n const serviceUrls: Array<{ name: string; url: string }> = [];\n const runningServices: RunningService[] = [];\n\n for (const { svc, pluginModule, loadedPlugin, svcSeedConfig, port, baseUrl } of prepared) {\n serviceUrls.push({ name: svc, url: baseUrl });\n\n const running = createServiceRuntime({\n service: svc,\n pluginModule,\n loadedPlugin,\n port,\n baseUrl,\n tokens,\n seedConfig: svcSeedConfig,\n });\n runningServices.push(running);\n }\n\n printBanner(serviceUrls, tokens, configSource);\n\n const shutdown = () => {\n console.log(`\\n${pc.dim(\"Shutting down...\")}`);\n if (portlessAliases.length > 0) {\n removeAliases(portlessAliases);\n }\n for (const running of runningServices) {\n void running.close();\n }\n process.exit(0);\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n}\n\nfunction printBanner(\n services: Array<{ name: string; url: string }>,\n tokens: Record<string, { login: string; id: number; scopes?: string[] }>,\n configSource: string | null,\n): void {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(` ${pc.bold(\"api-emulator\")} ${pc.dim(`v${pkg.version}`)}`);\n lines.push(\"\");\n\n const maxNameLen = Math.max(...services.map((s) => s.name.length));\n for (const { name, url } of services) {\n lines.push(` ${pc.cyan(name.padEnd(maxNameLen + 2))}${pc.bold(url)}`);\n }\n lines.push(\"\");\n\n const tokenEntries = Object.entries(tokens);\n if (tokenEntries.length > 0) {\n lines.push(` ${pc.dim(\"Tokens\")}`);\n for (const [token, user] of tokenEntries) {\n lines.push(` ${pc.dim(token)} ${pc.dim(\"->\")} ${user.login}`);\n }\n lines.push(\"\");\n }\n\n if (configSource) {\n lines.push(` ${pc.dim(\"Config:\")} ${configSource}`);\n } else {\n lines.push(\n ` ${pc.dim(\"Config:\")} defaults ${pc.dim(\"(run\")} npx -p api-emulator api init ${pc.dim(\"to customize)\")}`,\n );\n }\n lines.push(\"\");\n\n console.log(lines.join(\"\\n\"));\n}\n","import { execSync, spawnSync } from \"child_process\";\nimport { createInterface } from \"readline\";\n\nfunction isInteractive(): boolean {\n return Boolean(process.stdin.isTTY) && !process.env.CI;\n}\n\nfunction hasPortless(): boolean {\n const result = spawnSync(\"portless\", [\"--version\"], { stdio: \"ignore\" });\n return result.status === 0;\n}\n\nfunction promptYesNo(question: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(question, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n resolve(normalized === \"\" || normalized === \"y\" || normalized === \"yes\");\n });\n });\n}\n\nfunction isProxyRunning(): boolean {\n const result = spawnSync(\"portless\", [\"list\"], { stdio: \"ignore\" });\n return result.status === 0;\n}\n\nexport async function ensurePortless(): Promise<void> {\n if (!hasPortless()) {\n if (!isInteractive()) {\n console.error(\"portless is required but not installed. Run: npm i -g portless\");\n process.exit(1);\n }\n\n const yes = await promptYesNo(\"portless is not installed. Install it now? (npm i -g portless) [Y/n] \");\n if (!yes) {\n console.error(\"Cannot continue without portless.\");\n process.exit(1);\n }\n\n try {\n execSync(\"npm i -g portless\", { stdio: \"inherit\" });\n } catch {\n console.error(\"Failed to install portless.\");\n process.exit(1);\n }\n\n if (!hasPortless()) {\n console.error(\"portless was installed but could not be found on PATH.\");\n process.exit(1);\n }\n }\n\n if (!isProxyRunning()) {\n console.error(\"portless proxy is not running. Start it with: portless proxy start\");\n process.exit(1);\n }\n}\n\nexport interface PortlessAlias {\n name: string;\n port: number;\n}\n\nexport function registerAliases(aliases: PortlessAlias[]): void {\n const registered: PortlessAlias[] = [];\n for (const { name, port } of aliases) {\n const result = spawnSync(\"portless\", [\"alias\", name, String(port), \"--force\"], {\n stdio: \"inherit\",\n });\n if (result.status !== 0) {\n if (registered.length > 0) {\n removeAliases(registered);\n }\n throw new Error(`Failed to register portless alias: ${name} -> ${port}`);\n }\n registered.push({ name, port });\n }\n}\n\nexport function removeAliases(aliases: PortlessAlias[]): void {\n for (const { name } of aliases) {\n const result = spawnSync(\"portless\", [\"alias\", \"--remove\", name], { stdio: \"ignore\" });\n if (result.status !== 0) {\n console.error(`Warning: failed to remove portless alias: ${name}`);\n }\n }\n}\n\nexport function portlessBaseUrl(serviceName: string): string {\n return `https://${serviceName}.api-emulator.localhost`;\n}\n","export interface ResolveBaseUrlOptions {\n service: string;\n port: number;\n baseUrl?: string;\n seedBaseUrl?: string;\n}\n\n/**\n * Fallback chain:\n * 1. Per-service baseUrl from seed config\n * 2. Explicit baseUrl (CLI flag or programmatic option)\n * 3. API_EMULATOR_BASE_URL env var (with {service} interpolation)\n * 4. EMULATE_BASE_URL env var for backward compatibility (with {service} interpolation)\n * 5. PORTLESS_URL env var (with {service} interpolation)\n * 6. http://localhost:<port>\n */\nexport function resolveBaseUrl(opts: ResolveBaseUrlOptions): string {\n if (opts.seedBaseUrl) {\n return opts.seedBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n if (opts.baseUrl) {\n return opts.baseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const envBaseUrl = process.env.API_EMULATOR_BASE_URL ?? process.env.EMULATE_BASE_URL;\n if (envBaseUrl) {\n return envBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const portlessUrl = process.env.PORTLESS_URL;\n if (portlessUrl) {\n return portlessUrl.replace(/\\{service\\}/g, opts.service);\n }\n return `http://localhost:${opts.port}`;\n}\n","import {\n createServer,\n createStoreFixture,\n fixtureStoreSnapshot,\n type AppKeyResolver,\n type FixtureInteraction,\n type FixtureSource,\n type Store,\n type StoreFixture,\n type StoreFixtureOptions,\n type StoreSnapshot,\n} from \"@api-emulator/core\";\nimport { serve } from \"@hono/node-server\";\nimport type { LoadedPlugin, PluginModule } from \"./registry.js\";\n\nexport interface SeedConfig {\n tokens?: Record<string, { login: string; scopes?: string[] }>;\n [service: string]: unknown;\n}\n\nexport type TokenMap = Record<string, { login: string; id: number; scopes?: string[] }>;\n\nexport interface ServiceRuntimeOptions {\n service: string;\n pluginModule: PluginModule;\n loadedPlugin: LoadedPlugin;\n port: number;\n baseUrl: string;\n tokens: TokenMap;\n seedConfig?: Record<string, unknown>;\n}\n\nexport interface RunningService {\n service: string;\n url: string;\n store: Store;\n snapshot(): StoreSnapshot;\n restore(fixture: FixtureSource): void;\n exportFixture(options?: StoreFixtureOptions): StoreFixture;\n resetToFixture(fixture: FixtureSource): void;\n reset(): void;\n close(): Promise<void>;\n}\n\nexport function createAuthTokens(seedConfig?: SeedConfig | null): TokenMap {\n const tokens: TokenMap = {};\n if (seedConfig?.tokens) {\n let tokenId = 100;\n for (const [token, user] of Object.entries(seedConfig.tokens)) {\n tokens[token] = { login: user.login, id: tokenId++, scopes: user.scopes };\n }\n } else {\n tokens[\"test_token_admin\"] = { login: \"admin\", id: 2, scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"] };\n }\n return tokens;\n}\n\nexport function createServiceRuntime(options: ServiceRuntimeOptions): RunningService {\n const { service, pluginModule, loadedPlugin, port, baseUrl, tokens, seedConfig } = options;\n\n const resolverRef: { current?: AppKeyResolver } = {};\n const appKeyResolver: AppKeyResolver | undefined = loadedPlugin.createAppKeyResolver\n ? (appId) => resolverRef.current!(appId)\n : undefined;\n const fallbackUser = pluginModule.defaultFallback(seedConfig);\n\n const { app, store, webhooks } = createServer(loadedPlugin.plugin, {\n port,\n baseUrl,\n tokens,\n appKeyResolver,\n fallbackUser,\n });\n resolverRef.current = loadedPlugin.createAppKeyResolver?.(store);\n\n const seed = () => {\n loadedPlugin.plugin.seed?.(store, baseUrl);\n if (seedConfig && loadedPlugin.seedFromConfig) {\n loadedPlugin.seedFromConfig(store, baseUrl, seedConfig, webhooks);\n }\n };\n seed();\n\n const httpServer = serve({ fetch: app.fetch, port });\n\n return {\n service,\n url: baseUrl,\n store,\n snapshot() {\n return store.snapshot();\n },\n restore(fixture) {\n store.restore(fixtureStoreSnapshot(fixture));\n },\n exportFixture(options = {}) {\n const interactions = store.getData<FixtureInteraction[]>(\"api-emulator:interactions\");\n return createStoreFixture(service, store.snapshot(), {\n ...options,\n interactions: options.interactions ?? interactions,\n });\n },\n resetToFixture(fixture) {\n store.reset();\n store.restore(fixtureStoreSnapshot(fixture));\n },\n reset() {\n store.reset();\n seed();\n },\n close(): Promise<void> {\n return new Promise((resolve, reject) => {\n httpServer.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n },\n };\n}\n","import { writeFileSync, existsSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { stringify as yamlStringify } from \"yaml\";\nimport { DEFAULT_TOKENS, resolvePluginModules } from \"../registry.js\";\n\ninterface InitOptions {\n service: string;\n plugin?: string;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n const filename = \"api-emulator.config.yaml\";\n const fullPath = resolve(filename);\n\n if (existsSync(fullPath)) {\n console.error(`Config file already exists: ${filename}`);\n process.exit(1);\n }\n\n const pluginSpecifiers =\n options.plugin\n ?.split(\",\")\n .map((s) => s.trim())\n .filter(Boolean) ?? [];\n const pluginModules = await resolvePluginModules(pluginSpecifiers, { includeInstalled: true });\n const availableServices = Object.keys(pluginModules);\n\n let config: Record<string, unknown>;\n if (options.service === \"all\") {\n config = { ...DEFAULT_TOKENS };\n for (const name of availableServices) {\n Object.assign(config, pluginModules[name].initConfig);\n }\n } else {\n const pluginModule = pluginModules[options.service];\n if (!pluginModule) {\n console.error(`Unknown service: ${options.service}. Available: ${availableServices.join(\", \")}, all`);\n process.exit(1);\n }\n config = { ...DEFAULT_TOKENS, ...pluginModule.initConfig };\n }\n\n const content = yamlStringify(config);\n writeFileSync(fullPath, content, \"utf-8\");\n\n console.log(`Created ${filename}`);\n console.log(`\\nRun 'npx -p api-emulator api' to start the emulator.`);\n}\n","import { resolvePluginModules } from \"../registry.js\";\n\ninterface ListOptions {\n plugin?: string;\n}\n\nexport async function listCommand(options: ListOptions = {}): Promise<void> {\n const pluginSpecifiers =\n options.plugin\n ?.split(\",\")\n .map((s) => s.trim())\n .filter(Boolean) ?? [];\n const pluginModules = await resolvePluginModules(pluginSpecifiers, { includeInstalled: true });\n\n console.log(\"\\nAvailable services:\\n\");\n for (const pluginModule of Object.values(pluginModules)) {\n console.log(` ${pluginModule.name.padEnd(10)}${pluginModule.label}`);\n console.log(` Endpoints: ${pluginModule.endpoints}`);\n console.log();\n }\n}\n","import { spawnSync } from \"child_process\";\nimport { existsSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { PLUGIN_LOCK_FILE, readPluginLock, writePluginLock } from \"../plugin-lock.js\";\nimport { resolvePluginSource } from \"../plugin-source-registry.js\";\n\nexport interface InstallOptions {\n packageManager?: string | false;\n}\n\nfunction detectPackageManager(): string {\n if (existsSync(resolve(\"bun.lock\")) || existsSync(resolve(\"bun.lockb\"))) return \"bun\";\n if (existsSync(resolve(\"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(resolve(\"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction installPackage(packageManager: string, packageName: string): void {\n const args =\n packageManager === \"bun\"\n ? [\"add\", \"-D\", packageName]\n : packageManager === \"pnpm\"\n ? [\"add\", \"-D\", packageName]\n : packageManager === \"yarn\"\n ? [\"add\", \"-D\", packageName]\n : [\"install\", \"-D\", packageName];\n\n const result = spawnSync(packageManager, args, { stdio: \"inherit\" });\n if (result.error) {\n throw result.error;\n }\n if (result.status !== 0) {\n throw new Error(`${packageManager} ${args.join(\" \")} failed`);\n }\n}\n\nexport async function installCommand(name: string, options: InstallOptions = {}): Promise<void> {\n const source = resolvePluginSource(name);\n if (!source) {\n throw new Error(`Unknown plugin source: ${name}`);\n }\n\n const packageManager = options.packageManager === undefined ? detectPackageManager() : options.packageManager;\n if (packageManager && source.packageName) {\n installPackage(packageManager, source.packageName);\n } else if (packageManager && source.kind === \"package\" && !source.packageName) {\n throw new Error(`Plugin source \"${name}\" is a local package without a package name`);\n }\n\n const lock = readPluginLock();\n lock.plugins[source.name] = {\n name: source.name,\n source: source.kind === \"file\" ? \"specifier\" : \"registry\",\n sourceId: source.sourceId,\n packageName: source.packageName,\n specifier: packageManager && source.packageName ? source.packageName : source.specifier,\n version: \"latest\",\n };\n writePluginLock(lock);\n\n console.log(`Installed ${source.name} from ${source.sourceId}`);\n console.log(`Recorded plugin in ${PLUGIN_LOCK_FILE}`);\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\n\nexport type PluginSourceKind = \"package\" | \"file\";\n\nexport interface PluginSource {\n name: string;\n sourceId: string;\n kind: PluginSourceKind;\n specifier: string;\n description: string;\n packageName?: string;\n packageRoot?: string;\n}\n\nconst PLUGIN_SOURCES: Record<string, PluginSource> = {\n cloudflare: {\n name: \"cloudflare\",\n sourceId: \"public\",\n kind: \"package\",\n packageName: \"@api-emulator/cloudflare\",\n specifier: \"@api-emulator/cloudflare\",\n description: \"Workers-style bindings, D1, KV, R2, queues, durable objects, and service bindings\",\n },\n};\n\nconst DEFAULT_CATALOG_DIRS = [\"api-emulator-plugins\", \"api-emulator-internal\"];\nconst CATALOG_MANIFEST_FILES = [\"api-emulator.catalog.json\"];\nconst PLUGIN_ENTRY_FILES = [\"api-emulator.mjs\", \"api-emulator/index.mjs\"];\nconst PACKAGE_ENTRY_DIRS = [\"api-emulator\", \"trading-emulator\"];\n\nfunction candidateCatalogRoots(cwd = process.cwd()): string[] {\n const envRoots =\n process.env.API_EMULATOR_PLUGIN_CATALOGS?.split(\",\")\n .map((value) => value.trim())\n .filter(Boolean) ?? [];\n const roots = [...envRoots];\n let current = resolve(cwd);\n for (;;) {\n for (const dir of DEFAULT_CATALOG_DIRS) {\n roots.push(join(current, dir));\n roots.push(join(dirname(current), dir));\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return [...new Set(roots.map((root) => resolve(root)))];\n}\n\nfunction sourceIdForRoot(root: string): string {\n const base = root.split(/[\\\\/]/).pop() ?? \"local\";\n if (base === \"api-emulator-plugins\") return \"public\";\n if (base === \"api-emulator-internal\") return \"internal\";\n return base;\n}\n\nfunction packageEntrySpecifier(packageRoot: string, pkg: { exports?: unknown; main?: string }): string {\n if (typeof pkg.exports === \"string\") return resolve(packageRoot, pkg.exports);\n if (\n typeof pkg.exports === \"object\" &&\n pkg.exports !== null &&\n \".\" in pkg.exports &&\n typeof pkg.exports[\".\"] === \"string\"\n ) {\n return resolve(packageRoot, pkg.exports[\".\"]);\n }\n if (\n typeof pkg.exports === \"object\" &&\n pkg.exports !== null &&\n \".\" in pkg.exports &&\n typeof pkg.exports[\".\"] === \"object\" &&\n pkg.exports[\".\"] !== null &&\n \"import\" in pkg.exports[\".\"] &&\n typeof pkg.exports[\".\"].import === \"string\"\n ) {\n return resolve(packageRoot, pkg.exports[\".\"].import);\n }\n if (typeof pkg.main === \"string\") return resolve(packageRoot, pkg.main);\n return packageRoot;\n}\n\ninterface CatalogManifest {\n plugins?: Record<\n string,\n {\n kind?: PluginSourceKind;\n specifier: string;\n description?: string;\n packageName?: string;\n }\n >;\n}\n\nfunction discoverManifest(root: string, sourceId: string): PluginSource[] {\n const sources: PluginSource[] = [];\n for (const manifestFile of CATALOG_MANIFEST_FILES) {\n const manifestPath = join(root, manifestFile);\n if (!existsSync(manifestPath)) continue;\n const manifest = JSON.parse(readFileSync(manifestPath, \"utf-8\")) as CatalogManifest;\n for (const [name, entry] of Object.entries(manifest.plugins ?? {})) {\n sources.push({\n name,\n sourceId,\n kind: entry.kind ?? (entry.packageName ? \"package\" : \"file\"),\n packageName: entry.packageName,\n specifier: entry.specifier.startsWith(\".\") ? resolve(root, entry.specifier) : entry.specifier,\n description: entry.description ?? `${name} plugin from ${sourceId} catalog`,\n });\n }\n }\n return sources;\n}\n\nfunction discoverCatalog(root: string): PluginSource[] {\n if (!existsSync(root) || !statSync(root).isDirectory()) return [];\n\n const sourceId = sourceIdForRoot(root);\n const sources: PluginSource[] = discoverManifest(root, sourceId);\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n if (!entry.isDirectory() || !entry.name.startsWith(\"@\")) continue;\n const name = entry.name.slice(1);\n const pluginRoot = join(root, entry.name);\n\n for (const entryFile of PLUGIN_ENTRY_FILES) {\n const specifier = join(pluginRoot, entryFile);\n if (existsSync(specifier)) {\n sources.push({\n name,\n sourceId,\n kind: \"file\",\n specifier,\n description: `${name} plugin from ${sourceId} catalog`,\n });\n break;\n }\n }\n\n for (const entryDir of PACKAGE_ENTRY_DIRS) {\n const packageRoot = join(pluginRoot, entryDir);\n const packageJsonPath = join(packageRoot, \"package.json\");\n if (!existsSync(packageJsonPath)) continue;\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as {\n name?: string;\n description?: string;\n main?: string;\n };\n sources.push({\n name,\n sourceId,\n kind: \"package\",\n packageName: pkg.name,\n packageRoot,\n specifier: packageEntrySpecifier(packageRoot, pkg),\n description: pkg.description ?? `${name} package from ${sourceId} catalog`,\n });\n break;\n }\n }\n return sources;\n}\n\nfunction pluginSources(): Record<string, PluginSource> {\n const sources = { ...PLUGIN_SOURCES };\n for (const root of candidateCatalogRoots()) {\n for (const source of discoverCatalog(root)) {\n sources[source.name] ??= source;\n }\n }\n return sources;\n}\n\nexport function listPluginSources(): PluginSource[] {\n return Object.values(pluginSources());\n}\n\nexport function resolvePluginSource(name: string): PluginSource | null {\n return pluginSources()[name] ?? null;\n}\n"],"mappings":"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","names":["resolve","readFileSync","existsSync","resolve","resolve","options","resolve","resolve","existsSync","readFileSync","writeFileSync","existsSync","resolve","resolve","existsSync","writeFileSync","spawnSync","existsSync","resolve","existsSync","readFileSync","resolve","pkg","existsSync","resolve","spawnSync","pkg"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/external-plugin-adapter.ts","../src/plugin-manifest.ts","../src/plugin-lock.ts","../src/default-plugin-catalog.ts","../src/registry.ts","../src/commands/start.ts","../src/portless.ts","../src/base-url.ts","../src/service-runtime.ts","../src/commands/init.ts","../src/commands/list.ts","../src/commands/install.ts","../src/plugin-source-registry.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { startCommand } from \"./commands/start.js\";\nimport { initCommand } from \"./commands/init.js\";\nimport { listCommand } from \"./commands/list.js\";\nimport { installCommand } from \"./commands/install.js\";\n\ndeclare const PKG_VERSION: string;\nconst pkg = { version: PKG_VERSION };\n\nconst defaultPort = process.env.API_EMULATOR_PORT ?? process.env.PORT ?? \"4000\";\n\nconst program = new Command();\n\nprogram.name(\"api\").description(\"Local API emulators you can run, share, and extend with plugins\").version(pkg.version);\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"Start the emulator server\")\n .option(\"-p, --port <port>\", \"Base port\", defaultPort)\n .option(\"-s, --service <services>\", \"Comma-separated services to enable\")\n .option(\"--seed <file>\", \"Path to seed config file\")\n .option(\"--base-url <url>\", \"Override advertised base URL (supports {service} template)\")\n .option(\"--portless\", \"Serve over HTTPS via portless (auto-registers aliases)\")\n .option(\"--plugin <plugins>\", \"Comma-separated external plugin paths or package names\")\n .action(async (opts) => {\n const port = parseInt(opts.port, 10);\n if (Number.isNaN(port) || port < 1 || port > 65535) {\n console.error(`Invalid port: ${opts.port}`);\n process.exit(1);\n }\n await startCommand({\n port,\n service: opts.service,\n seed: opts.seed,\n baseUrl: opts.baseUrl,\n portless: opts.portless,\n plugin: opts.plugin,\n });\n });\n\nprogram\n .command(\"init\")\n .description(\"Generate a starter config file\")\n .option(\"-s, --service <service>\", \"Service to generate config for\", \"all\")\n .option(\"--plugin <plugins>\", \"Comma-separated external plugin paths or package names\")\n .action(async (opts) => {\n await initCommand({ service: opts.service, plugin: opts.plugin });\n });\n\nprogram\n .command(\"list\")\n .alias(\"list-services\")\n .description(\"List available services\")\n .option(\"--plugin <plugins>\", \"Comma-separated external plugin paths or package names\")\n .action(async (opts) => {\n await listCommand({ plugin: opts.plugin });\n });\n\nprogram\n .command(\"install <plugin>\")\n .description(\"Install a provider plugin by name\")\n .option(\"--package-manager <name>\", \"Package manager to use\")\n .option(\"--no-package-manager\", \"Only record the plugin in api-emulator.lock\")\n .action(async (plugin, opts) => {\n try {\n await installCommand(plugin, {\n packageManager: opts.packageManager === false ? false : opts.packageManager,\n });\n } catch (err) {\n console.error(err instanceof Error ? err.message : err);\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import type { ServicePlugin, Store, AuthFallback, WebhookDispatcher } from \"@api-emulator/core\";\nimport { isAbsolute, resolve } from \"path\";\nimport { readPluginManifest, validatePluginManifest, type PluginManifest } from \"./plugin-manifest.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\n\nexport interface ExternalPluginModule {\n plugin?: ServicePlugin;\n default?: ServicePlugin;\n seedFromConfig?(store: Store, baseUrl: string, config: unknown, webhooks?: WebhookDispatcher): void;\n manifest?: PluginManifest;\n defaultFallback?(svcSeedConfig?: Record<string, unknown>): AuthFallback;\n}\n\nexport async function loadExternalPluginModule(specifier: string): Promise<PluginModule> {\n const modulePath = specifier.startsWith(\".\") || isAbsolute(specifier) ? resolve(specifier) : specifier;\n\n const mod = (await import(modulePath)) as ExternalPluginModule;\n const plugin = mod.plugin ?? mod.default;\n if (!plugin || typeof plugin.register !== \"function\" || typeof plugin.name !== \"string\") {\n throw new Error(`Plugin \"${specifier}\" must export a ServicePlugin (as \"plugin\" or default export)`);\n }\n\n const name = plugin.name;\n const manifest = validatePluginManifest(readPluginManifest(mod), name);\n return {\n name,\n label: manifest.label,\n endpoints: manifest.endpoints,\n manifest,\n async load() {\n return {\n plugin,\n seedFromConfig: mod.seedFromConfig,\n };\n },\n defaultFallback: mod.defaultFallback ?? (() => ({ login: \"admin\", id: 1, scopes: [] })),\n initConfig: manifest.initConfig,\n };\n}\n\nexport async function loadExternalPlugin(specifier: string): Promise<{ name: string; entry: PluginModule }> {\n const pluginModule = await loadExternalPluginModule(specifier);\n return { name: pluginModule.name, entry: pluginModule };\n}\n","export interface PluginManifest {\n name?: string;\n label?: string;\n endpoints?: string;\n initConfig?: Record<string, unknown>;\n contract?: unknown;\n}\n\nexport function readPluginManifest(mod: { manifest?: PluginManifest }): PluginManifest {\n return mod.manifest ?? {};\n}\n\nexport function validatePluginManifest(\n manifest: PluginManifest,\n pluginName: string,\n): Required<Pick<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\">> &\n Omit<PluginManifest, \"label\" | \"endpoints\" | \"initConfig\"> {\n if (manifest.name && manifest.name !== pluginName) {\n throw new Error(`Plugin manifest name \"${manifest.name}\" does not match plugin name \"${pluginName}\"`);\n }\n\n return {\n ...manifest,\n label: manifest.label ?? `${pluginName} (external plugin)`,\n endpoints: manifest.endpoints ?? \"\",\n initConfig: manifest.initConfig ?? {},\n };\n}\n","import { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { resolve } from \"path\";\n\nexport const PLUGIN_LOCK_FILE = \"api-emulator.lock\";\n\nexport interface PluginLockEntry {\n name: string;\n source: \"registry\" | \"specifier\";\n specifier: string;\n sourceId?: string;\n packageName?: string;\n version?: string;\n}\n\nexport interface PluginLock {\n version: 1;\n plugins: Record<string, PluginLockEntry>;\n}\n\nexport function createEmptyPluginLock(): PluginLock {\n return { version: 1, plugins: {} };\n}\n\nexport function readPluginLock(cwd = process.cwd()): PluginLock {\n const path = resolve(cwd, PLUGIN_LOCK_FILE);\n if (!existsSync(path)) return createEmptyPluginLock();\n\n const parsed = JSON.parse(readFileSync(path, \"utf-8\")) as PluginLock;\n if (parsed.version !== 1 || typeof parsed.plugins !== \"object\" || parsed.plugins === null) {\n throw new Error(`Invalid ${PLUGIN_LOCK_FILE}`);\n }\n return parsed;\n}\n\nexport function writePluginLock(lock: PluginLock, cwd = process.cwd()): void {\n const sortedPlugins = Object.fromEntries(Object.entries(lock.plugins).sort(([a], [b]) => a.localeCompare(b)));\n const content = `${JSON.stringify({ version: 1, plugins: sortedPlugins }, null, 2)}\\n`;\n writeFileSync(resolve(cwd, PLUGIN_LOCK_FILE), content, \"utf-8\");\n}\n\nexport function getLockedPluginSpecifiers(cwd = process.cwd()): string[] {\n return Object.values(readPluginLock(cwd).plugins).map((entry) => entry.specifier);\n}\n","import type { PluginModule } from \"./plugin-types.js\";\n\nexport type ServiceName = string;\nexport const DEFAULT_PLUGIN_NAMES: readonly ServiceName[] = [];\nexport const SERVICE_NAMES: readonly ServiceName[] = DEFAULT_PLUGIN_NAMES;\nexport const DEFAULT_PLUGIN_REGISTRY: Record<ServiceName, PluginModule> = {};\n","import { loadExternalPluginModule } from \"./external-plugin-adapter.js\";\nimport { getLockedPluginSpecifiers } from \"./plugin-lock.js\";\nimport {\n DEFAULT_PLUGIN_REGISTRY,\n DEFAULT_PLUGIN_NAMES,\n SERVICE_NAMES,\n type ServiceName,\n} from \"./default-plugin-catalog.js\";\nimport type { PluginModule } from \"./plugin-types.js\";\nexport { DEFAULT_PLUGIN_REGISTRY, DEFAULT_PLUGIN_NAMES, SERVICE_NAMES, type ServiceName };\nexport type { LoadedPlugin, LoadedService, PluginModule, ServiceEntry } from \"./plugin-types.js\";\n\nexport interface ResolvePluginModulesOptions {\n includeInstalled?: boolean;\n}\n\nexport const DEFAULT_TOKENS = {\n tokens: {\n test_token_admin: {\n login: \"admin\",\n scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"],\n },\n test_token_user1: {\n login: \"octocat\",\n scopes: [\"repo\", \"user\"],\n },\n },\n};\n\nexport async function resolvePluginModules(\n pluginSpecifiers: string[] = [],\n options: ResolvePluginModulesOptions = {},\n): Promise<Record<string, PluginModule>> {\n const installedSpecifiers = options.includeInstalled ? getLockedPluginSpecifiers() : [];\n const allSpecifiers = [...installedSpecifiers, ...pluginSpecifiers];\n const results = await Promise.all(allSpecifiers.map(loadExternalPluginModule));\n\n const externalEntries: Record<string, PluginModule> = {};\n for (const pluginModule of results) {\n if (pluginModule.name in DEFAULT_PLUGIN_REGISTRY) {\n throw new Error(`Plugin \"${pluginModule.name}\" conflicts with default plugin \"${pluginModule.name}\"`);\n }\n if (pluginModule.name in externalEntries) {\n throw new Error(`Duplicate plugin name \"${pluginModule.name}\"`);\n }\n externalEntries[pluginModule.name] = pluginModule;\n }\n\n return { ...DEFAULT_PLUGIN_REGISTRY, ...externalEntries };\n}\n\nexport const resolveServiceEntries = resolvePluginModules;\n\nexport function getDefaultPluginNames(): string[] {\n return [...DEFAULT_PLUGIN_NAMES];\n}\n","import { resolvePluginModules, getDefaultPluginNames, type LoadedPlugin, type PluginModule } from \"../registry.js\";\nimport { readFileSync, existsSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport pc from \"picocolors\";\nimport { ensurePortless, registerAliases, removeAliases, portlessBaseUrl, type PortlessAlias } from \"../portless.js\";\nimport { resolveBaseUrl } from \"../base-url.js\";\nimport { createAuthTokens, createServiceRuntime, type RunningService, type SeedConfig } from \"../service-runtime.js\";\n\ndeclare const PKG_VERSION: string;\nconst pkg = { version: PKG_VERSION };\n\nexport interface StartOptions {\n port: number;\n service?: string;\n seed?: string;\n baseUrl?: string;\n portless?: boolean;\n plugin?: string;\n}\n\ninterface LoadResult {\n config: SeedConfig;\n source: string;\n}\n\nfunction loadSeedConfig(seedPath?: string): LoadResult | null {\n if (seedPath) {\n const fullPath = resolve(seedPath);\n if (!existsSync(fullPath)) {\n console.error(`Seed file not found: ${fullPath}`);\n process.exit(1);\n }\n const content = readFileSync(fullPath, \"utf-8\");\n try {\n const config = fullPath.endsWith(\".json\") ? JSON.parse(content) : parseYaml(content);\n return { config, source: seedPath };\n } catch (err) {\n console.error(`Failed to parse ${seedPath}: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n }\n\n const autoFiles = [\"api-emulator.config.yaml\", \"api-emulator.config.yml\", \"api-emulator.config.json\"];\n\n for (const file of autoFiles) {\n const fullPath = resolve(file);\n if (existsSync(fullPath)) {\n const content = readFileSync(fullPath, \"utf-8\");\n try {\n const config = fullPath.endsWith(\".json\") ? JSON.parse(content) : parseYaml(content);\n return { config, source: file };\n } catch (err) {\n console.error(`Failed to parse ${file}: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n }\n }\n\n return null;\n}\n\nfunction inferServicesFromConfig(config: SeedConfig, availableServices: string[]): string[] | null {\n const found = availableServices.filter((k) => k in config);\n return found.length > 0 ? [...found] : null;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const { port: basePort } = options;\n\n if (options.portless && options.baseUrl) {\n console.error(\"--portless and --base-url are mutually exclusive.\");\n process.exit(1);\n }\n\n const loaded = loadSeedConfig(options.seed);\n const seedConfig = loaded?.config ?? null;\n const configSource = loaded?.source ?? null;\n\n const pluginSpecifiers =\n options.plugin\n ?.split(\",\")\n .map((s) => s.trim())\n .filter(Boolean) ?? [];\n let allPluginModules: Record<string, PluginModule>;\n try {\n allPluginModules = await resolvePluginModules(pluginSpecifiers, { includeInstalled: true });\n } catch (err) {\n console.error(`Failed to load plugins: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n\n const defaultPlugins = getDefaultPluginNames();\n const externalServices = Object.keys(allPluginModules).filter((name) => !defaultPlugins.includes(name));\n\n let services: string[];\n if (options.service) {\n services = options.service.split(\",\").map((s) => s.trim());\n } else if (seedConfig) {\n services = inferServicesFromConfig(seedConfig, Object.keys(allPluginModules)) ?? [\n ...defaultPlugins,\n ...externalServices,\n ];\n } else {\n services = [...defaultPlugins, ...externalServices];\n }\n\n for (const svc of services) {\n if (!allPluginModules[svc]) {\n console.error(`Unknown service: ${svc}`);\n process.exit(1);\n }\n }\n\n const tokens = createAuthTokens(seedConfig);\n\n if (options.portless) {\n await ensurePortless();\n }\n\n interface PreparedService {\n svc: string;\n pluginModule: PluginModule;\n loadedPlugin: LoadedPlugin;\n svcSeedConfig: Record<string, unknown> | undefined;\n port: number;\n baseUrl: string;\n }\n\n const portlessAliases: PortlessAlias[] = [];\n const prepared: PreparedService[] = [];\n\n for (let i = 0; i < services.length; i++) {\n const svc = services[i];\n const pluginModule = allPluginModules[svc];\n const loadedPlugin = await pluginModule.load();\n\n const svcSeedConfig = seedConfig?.[svc] as Record<string, unknown> | undefined;\n const port = (svcSeedConfig?.port as number | undefined) ?? basePort + i;\n\n if (options.portless) {\n portlessAliases.push({ name: `${svc}.api-emulator`, port });\n }\n\n const seedBaseUrl =\n typeof svcSeedConfig?.baseUrl === \"string\" && svcSeedConfig.baseUrl.length > 0\n ? svcSeedConfig.baseUrl\n : undefined;\n const effectiveBaseUrl = options.portless ? portlessBaseUrl(svc) : options.baseUrl;\n const baseUrl = resolveBaseUrl({ service: svc, port, baseUrl: effectiveBaseUrl, seedBaseUrl });\n\n prepared.push({ svc, pluginModule, loadedPlugin, svcSeedConfig, port, baseUrl });\n }\n\n if (portlessAliases.length > 0) {\n registerAliases(portlessAliases);\n }\n\n const serviceUrls: Array<{ name: string; url: string }> = [];\n const runningServices: RunningService[] = [];\n\n for (const { svc, pluginModule, loadedPlugin, svcSeedConfig, port, baseUrl } of prepared) {\n serviceUrls.push({ name: svc, url: baseUrl });\n\n const running = createServiceRuntime({\n service: svc,\n pluginModule,\n loadedPlugin,\n port,\n baseUrl,\n tokens,\n seedConfig: svcSeedConfig,\n });\n runningServices.push(running);\n }\n\n printBanner(serviceUrls, tokens, configSource);\n\n const shutdown = () => {\n console.log(`\\n${pc.dim(\"Shutting down...\")}`);\n if (portlessAliases.length > 0) {\n removeAliases(portlessAliases);\n }\n for (const running of runningServices) {\n void running.close();\n }\n process.exit(0);\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n}\n\nfunction printBanner(\n services: Array<{ name: string; url: string }>,\n tokens: Record<string, { login: string; id: number; scopes?: string[] }>,\n configSource: string | null,\n): void {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(` ${pc.bold(\"api-emulator\")} ${pc.dim(`v${pkg.version}`)}`);\n lines.push(\"\");\n\n const maxNameLen = Math.max(...services.map((s) => s.name.length));\n for (const { name, url } of services) {\n lines.push(` ${pc.cyan(name.padEnd(maxNameLen + 2))}${pc.bold(url)}`);\n }\n lines.push(\"\");\n\n const tokenEntries = Object.entries(tokens);\n if (tokenEntries.length > 0) {\n lines.push(` ${pc.dim(\"Tokens\")}`);\n for (const [token, user] of tokenEntries) {\n lines.push(` ${pc.dim(token)} ${pc.dim(\"->\")} ${user.login}`);\n }\n lines.push(\"\");\n }\n\n if (configSource) {\n lines.push(` ${pc.dim(\"Config:\")} ${configSource}`);\n } else {\n lines.push(\n ` ${pc.dim(\"Config:\")} defaults ${pc.dim(\"(run\")} npx -p api-emulator api init ${pc.dim(\"to customize)\")}`,\n );\n }\n lines.push(\"\");\n\n console.log(lines.join(\"\\n\"));\n}\n","import { execSync, spawnSync } from \"child_process\";\nimport { createInterface } from \"readline\";\n\nfunction isInteractive(): boolean {\n return Boolean(process.stdin.isTTY) && !process.env.CI;\n}\n\nfunction hasPortless(): boolean {\n const result = spawnSync(\"portless\", [\"--version\"], { stdio: \"ignore\" });\n return result.status === 0;\n}\n\nfunction promptYesNo(question: string): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(question, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n resolve(normalized === \"\" || normalized === \"y\" || normalized === \"yes\");\n });\n });\n}\n\nfunction isProxyRunning(): boolean {\n const result = spawnSync(\"portless\", [\"list\"], { stdio: \"ignore\" });\n return result.status === 0;\n}\n\nexport async function ensurePortless(): Promise<void> {\n if (!hasPortless()) {\n if (!isInteractive()) {\n console.error(\"portless is required but not installed. Run: npm i -g portless\");\n process.exit(1);\n }\n\n const yes = await promptYesNo(\"portless is not installed. Install it now? (npm i -g portless) [Y/n] \");\n if (!yes) {\n console.error(\"Cannot continue without portless.\");\n process.exit(1);\n }\n\n try {\n execSync(\"npm i -g portless\", { stdio: \"inherit\" });\n } catch {\n console.error(\"Failed to install portless.\");\n process.exit(1);\n }\n\n if (!hasPortless()) {\n console.error(\"portless was installed but could not be found on PATH.\");\n process.exit(1);\n }\n }\n\n if (!isProxyRunning()) {\n console.error(\"portless proxy is not running. Start it with: portless proxy start\");\n process.exit(1);\n }\n}\n\nexport interface PortlessAlias {\n name: string;\n port: number;\n}\n\nexport function registerAliases(aliases: PortlessAlias[]): void {\n const registered: PortlessAlias[] = [];\n for (const { name, port } of aliases) {\n const result = spawnSync(\"portless\", [\"alias\", name, String(port), \"--force\"], {\n stdio: \"inherit\",\n });\n if (result.status !== 0) {\n if (registered.length > 0) {\n removeAliases(registered);\n }\n throw new Error(`Failed to register portless alias: ${name} -> ${port}`);\n }\n registered.push({ name, port });\n }\n}\n\nexport function removeAliases(aliases: PortlessAlias[]): void {\n for (const { name } of aliases) {\n const result = spawnSync(\"portless\", [\"alias\", \"--remove\", name], { stdio: \"ignore\" });\n if (result.status !== 0) {\n console.error(`Warning: failed to remove portless alias: ${name}`);\n }\n }\n}\n\nexport function portlessBaseUrl(serviceName: string): string {\n return `https://${serviceName}.api-emulator.localhost`;\n}\n","export interface ResolveBaseUrlOptions {\n service: string;\n port: number;\n baseUrl?: string;\n seedBaseUrl?: string;\n}\n\n/**\n * Fallback chain:\n * 1. Per-service baseUrl from seed config\n * 2. Explicit baseUrl (CLI flag or programmatic option)\n * 3. API_EMULATOR_BASE_URL env var (with {service} interpolation)\n * 4. PORTLESS_URL env var (with {service} interpolation)\n * 5. http://localhost:<port>\n */\nexport function resolveBaseUrl(opts: ResolveBaseUrlOptions): string {\n if (opts.seedBaseUrl) {\n return opts.seedBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n if (opts.baseUrl) {\n return opts.baseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const envBaseUrl = process.env.API_EMULATOR_BASE_URL;\n if (envBaseUrl) {\n return envBaseUrl.replace(/\\{service\\}/g, opts.service);\n }\n const portlessUrl = process.env.PORTLESS_URL;\n if (portlessUrl) {\n return portlessUrl.replace(/\\{service\\}/g, opts.service);\n }\n return `http://localhost:${opts.port}`;\n}\n","import {\n createServer,\n createStoreFixture,\n fixtureStoreSnapshot,\n type AppKeyResolver,\n type FixtureInteraction,\n type FixtureSource,\n type Store,\n type StoreFixture,\n type StoreFixtureOptions,\n type StoreSnapshot,\n} from \"@api-emulator/core\";\nimport { serve } from \"@hono/node-server\";\nimport type { LoadedPlugin, PluginModule } from \"./registry.js\";\n\nexport interface SeedConfig {\n tokens?: Record<string, { login: string; scopes?: string[] }>;\n [service: string]: unknown;\n}\n\nexport type TokenMap = Record<string, { login: string; id: number; scopes?: string[] }>;\n\nexport interface ServiceRuntimeOptions {\n service: string;\n pluginModule: PluginModule;\n loadedPlugin: LoadedPlugin;\n port: number;\n baseUrl: string;\n tokens: TokenMap;\n seedConfig?: Record<string, unknown>;\n}\n\nexport interface RunningService {\n service: string;\n url: string;\n store: Store;\n snapshot(): StoreSnapshot;\n restore(fixture: FixtureSource): void;\n exportFixture(options?: StoreFixtureOptions): StoreFixture;\n resetToFixture(fixture: FixtureSource): void;\n reset(): void;\n close(): Promise<void>;\n}\n\nexport function createAuthTokens(seedConfig?: SeedConfig | null): TokenMap {\n const tokens: TokenMap = {};\n if (seedConfig?.tokens) {\n let tokenId = 100;\n for (const [token, user] of Object.entries(seedConfig.tokens)) {\n tokens[token] = { login: user.login, id: tokenId++, scopes: user.scopes };\n }\n } else {\n tokens[\"test_token_admin\"] = { login: \"admin\", id: 2, scopes: [\"repo\", \"user\", \"admin:org\", \"admin:repo_hook\"] };\n }\n return tokens;\n}\n\nexport function createServiceRuntime(options: ServiceRuntimeOptions): RunningService {\n const { service, pluginModule, loadedPlugin, port, baseUrl, tokens, seedConfig } = options;\n\n const resolverRef: { current?: AppKeyResolver } = {};\n const appKeyResolver: AppKeyResolver | undefined = loadedPlugin.createAppKeyResolver\n ? (appId) => resolverRef.current!(appId)\n : undefined;\n const fallbackUser = pluginModule.defaultFallback(seedConfig);\n\n const { app, store, webhooks } = createServer(loadedPlugin.plugin, {\n port,\n baseUrl,\n tokens,\n appKeyResolver,\n fallbackUser,\n });\n resolverRef.current = loadedPlugin.createAppKeyResolver?.(store);\n\n const seed = () => {\n loadedPlugin.plugin.seed?.(store, baseUrl);\n if (seedConfig && loadedPlugin.seedFromConfig) {\n loadedPlugin.seedFromConfig(store, baseUrl, seedConfig, webhooks);\n }\n };\n seed();\n\n const httpServer = serve({ fetch: app.fetch, port });\n\n return {\n service,\n url: baseUrl,\n store,\n snapshot() {\n return store.snapshot();\n },\n restore(fixture) {\n store.restore(fixtureStoreSnapshot(fixture));\n },\n exportFixture(options = {}) {\n const interactions = store.getData<FixtureInteraction[]>(\"api-emulator:interactions\");\n return createStoreFixture(service, store.snapshot(), {\n ...options,\n interactions: options.interactions ?? interactions,\n });\n },\n resetToFixture(fixture) {\n store.reset();\n store.restore(fixtureStoreSnapshot(fixture));\n },\n reset() {\n store.reset();\n seed();\n },\n close(): Promise<void> {\n return new Promise((resolve, reject) => {\n httpServer.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n },\n };\n}\n","import { writeFileSync, existsSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { stringify as yamlStringify } from \"yaml\";\nimport { DEFAULT_TOKENS, resolvePluginModules } from \"../registry.js\";\n\ninterface InitOptions {\n service: string;\n plugin?: string;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n const filename = \"api-emulator.config.yaml\";\n const fullPath = resolve(filename);\n\n if (existsSync(fullPath)) {\n console.error(`Config file already exists: ${filename}`);\n process.exit(1);\n }\n\n const pluginSpecifiers =\n options.plugin\n ?.split(\",\")\n .map((s) => s.trim())\n .filter(Boolean) ?? [];\n const pluginModules = await resolvePluginModules(pluginSpecifiers, { includeInstalled: true });\n const availableServices = Object.keys(pluginModules);\n\n let config: Record<string, unknown>;\n if (options.service === \"all\") {\n config = { ...DEFAULT_TOKENS };\n for (const name of availableServices) {\n Object.assign(config, pluginModules[name].initConfig);\n }\n } else {\n const pluginModule = pluginModules[options.service];\n if (!pluginModule) {\n console.error(`Unknown service: ${options.service}. Available: ${availableServices.join(\", \")}, all`);\n process.exit(1);\n }\n config = { ...DEFAULT_TOKENS, ...pluginModule.initConfig };\n }\n\n const content = yamlStringify(config);\n writeFileSync(fullPath, content, \"utf-8\");\n\n console.log(`Created ${filename}`);\n console.log(`\\nRun 'npx -p api-emulator api' to start the emulator.`);\n}\n","import { resolvePluginModules } from \"../registry.js\";\n\ninterface ListOptions {\n plugin?: string;\n}\n\nexport async function listCommand(options: ListOptions = {}): Promise<void> {\n const pluginSpecifiers =\n options.plugin\n ?.split(\",\")\n .map((s) => s.trim())\n .filter(Boolean) ?? [];\n const pluginModules = await resolvePluginModules(pluginSpecifiers, { includeInstalled: true });\n\n console.log(\"\\nAvailable services:\\n\");\n for (const pluginModule of Object.values(pluginModules)) {\n console.log(` ${pluginModule.name.padEnd(10)}${pluginModule.label}`);\n console.log(` Endpoints: ${pluginModule.endpoints}`);\n console.log();\n }\n}\n","import { spawnSync } from \"child_process\";\nimport { existsSync } from \"fs\";\nimport { resolve } from \"path\";\nimport { PLUGIN_LOCK_FILE, readPluginLock, writePluginLock } from \"../plugin-lock.js\";\nimport { resolvePluginSource } from \"../plugin-source-registry.js\";\n\nexport interface InstallOptions {\n packageManager?: string | false;\n}\n\nfunction detectPackageManager(): string {\n if (existsSync(resolve(\"bun.lock\")) || existsSync(resolve(\"bun.lockb\"))) return \"bun\";\n if (existsSync(resolve(\"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(resolve(\"yarn.lock\"))) return \"yarn\";\n return \"npm\";\n}\n\nfunction installPackage(packageManager: string, packageName: string): void {\n const args =\n packageManager === \"bun\"\n ? [\"add\", \"-D\", packageName]\n : packageManager === \"pnpm\"\n ? [\"add\", \"-D\", packageName]\n : packageManager === \"yarn\"\n ? [\"add\", \"-D\", packageName]\n : [\"install\", \"-D\", packageName];\n\n const result = spawnSync(packageManager, args, { stdio: \"inherit\" });\n if (result.error) {\n throw result.error;\n }\n if (result.status !== 0) {\n throw new Error(`${packageManager} ${args.join(\" \")} failed`);\n }\n}\n\nexport async function installCommand(name: string, options: InstallOptions = {}): Promise<void> {\n const source = resolvePluginSource(name);\n if (!source) {\n throw new Error(`Unknown plugin source: ${name}`);\n }\n\n const packageManager = options.packageManager === undefined ? detectPackageManager() : options.packageManager;\n if (packageManager && source.packageName) {\n installPackage(packageManager, source.packageName);\n } else if (packageManager && source.kind === \"package\" && !source.packageName) {\n throw new Error(`Plugin source \"${name}\" is a local package without a package name`);\n }\n\n const lock = readPluginLock();\n lock.plugins[source.name] = {\n name: source.name,\n source: source.kind === \"file\" ? \"specifier\" : \"registry\",\n sourceId: source.sourceId,\n packageName: source.packageName,\n specifier: packageManager && source.packageName ? source.packageName : source.specifier,\n version: \"latest\",\n };\n writePluginLock(lock);\n\n console.log(`Installed ${source.name} from ${source.sourceId}`);\n console.log(`Recorded plugin in ${PLUGIN_LOCK_FILE}`);\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\n\nexport type PluginSourceKind = \"package\" | \"file\";\n\nexport interface PluginSource {\n name: string;\n sourceId: string;\n kind: PluginSourceKind;\n specifier: string;\n description: string;\n packageName?: string;\n packageRoot?: string;\n}\n\nconst PLUGIN_SOURCES: Record<string, PluginSource> = {\n cloudflare: {\n name: \"cloudflare\",\n sourceId: \"public\",\n kind: \"package\",\n packageName: \"@api-emulator/cloudflare\",\n specifier: \"@api-emulator/cloudflare\",\n description: \"Workers-style bindings, D1, KV, R2, queues, durable objects, and service bindings\",\n },\n};\n\nconst DEFAULT_CATALOG_DIRS = [\"api-emulator-plugins\", \"api-emulator-internal\"];\nconst CATALOG_MANIFEST_FILES = [\"api-emulator.catalog.json\"];\nconst PLUGIN_ENTRY_FILES = [\"api-emulator.mjs\", \"api-emulator/index.mjs\"];\nconst PACKAGE_ENTRY_DIRS = [\"api-emulator\", \"trading-emulator\"];\n\nfunction candidateCatalogRoots(cwd = process.cwd()): string[] {\n const envRoots =\n process.env.API_EMULATOR_PLUGIN_CATALOGS?.split(\",\")\n .map((value) => value.trim())\n .filter(Boolean) ?? [];\n const roots = [...envRoots];\n let current = resolve(cwd);\n for (;;) {\n for (const dir of DEFAULT_CATALOG_DIRS) {\n roots.push(join(current, dir));\n roots.push(join(dirname(current), dir));\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return [...new Set(roots.map((root) => resolve(root)))];\n}\n\nfunction sourceIdForRoot(root: string): string {\n const base = root.split(/[\\\\/]/).pop() ?? \"local\";\n if (base === \"api-emulator-plugins\") return \"public\";\n if (base === \"api-emulator-internal\") return \"internal\";\n return base;\n}\n\nfunction packageEntrySpecifier(packageRoot: string, pkg: { exports?: unknown; main?: string }): string {\n if (typeof pkg.exports === \"string\") return resolve(packageRoot, pkg.exports);\n if (\n typeof pkg.exports === \"object\" &&\n pkg.exports !== null &&\n \".\" in pkg.exports &&\n typeof pkg.exports[\".\"] === \"string\"\n ) {\n return resolve(packageRoot, pkg.exports[\".\"]);\n }\n if (\n typeof pkg.exports === \"object\" &&\n pkg.exports !== null &&\n \".\" in pkg.exports &&\n typeof pkg.exports[\".\"] === \"object\" &&\n pkg.exports[\".\"] !== null &&\n \"import\" in pkg.exports[\".\"] &&\n typeof pkg.exports[\".\"].import === \"string\"\n ) {\n return resolve(packageRoot, pkg.exports[\".\"].import);\n }\n if (typeof pkg.main === \"string\") return resolve(packageRoot, pkg.main);\n return packageRoot;\n}\n\ninterface CatalogManifest {\n plugins?: Record<\n string,\n {\n kind?: PluginSourceKind;\n specifier: string;\n description?: string;\n packageName?: string;\n }\n >;\n}\n\nfunction discoverManifest(root: string, sourceId: string): PluginSource[] {\n const sources: PluginSource[] = [];\n for (const manifestFile of CATALOG_MANIFEST_FILES) {\n const manifestPath = join(root, manifestFile);\n if (!existsSync(manifestPath)) continue;\n const manifest = JSON.parse(readFileSync(manifestPath, \"utf-8\")) as CatalogManifest;\n for (const [name, entry] of Object.entries(manifest.plugins ?? {})) {\n sources.push({\n name,\n sourceId,\n kind: entry.kind ?? (entry.packageName ? \"package\" : \"file\"),\n packageName: entry.packageName,\n specifier: entry.specifier.startsWith(\".\") ? resolve(root, entry.specifier) : entry.specifier,\n description: entry.description ?? `${name} plugin from ${sourceId} catalog`,\n });\n }\n }\n return sources;\n}\n\nfunction discoverCatalog(root: string): PluginSource[] {\n if (!existsSync(root) || !statSync(root).isDirectory()) return [];\n\n const sourceId = sourceIdForRoot(root);\n const sources: PluginSource[] = discoverManifest(root, sourceId);\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n if (!entry.isDirectory() || !entry.name.startsWith(\"@\")) continue;\n const name = entry.name.slice(1);\n const pluginRoot = join(root, entry.name);\n\n for (const entryFile of PLUGIN_ENTRY_FILES) {\n const specifier = join(pluginRoot, entryFile);\n if (existsSync(specifier)) {\n sources.push({\n name,\n sourceId,\n kind: \"file\",\n specifier,\n description: `${name} plugin from ${sourceId} catalog`,\n });\n break;\n }\n }\n\n for (const entryDir of PACKAGE_ENTRY_DIRS) {\n const packageRoot = join(pluginRoot, entryDir);\n const packageJsonPath = join(packageRoot, \"package.json\");\n if (!existsSync(packageJsonPath)) continue;\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as {\n name?: string;\n description?: string;\n main?: string;\n };\n sources.push({\n name,\n sourceId,\n kind: \"package\",\n packageName: pkg.name,\n packageRoot,\n specifier: packageEntrySpecifier(packageRoot, pkg),\n description: pkg.description ?? `${name} package from ${sourceId} catalog`,\n });\n break;\n }\n }\n return sources;\n}\n\nfunction pluginSources(): Record<string, PluginSource> {\n const sources = { ...PLUGIN_SOURCES };\n for (const root of candidateCatalogRoots()) {\n for (const source of discoverCatalog(root)) {\n sources[source.name] ??= source;\n }\n }\n return sources;\n}\n\nexport function listPluginSources(): PluginSource[] {\n return Object.values(pluginSources());\n}\n\nexport function resolvePluginSource(name: string): PluginSource | null {\n return pluginSources()[name] ?? null;\n}\n"],"mappings":"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","names":["resolve","readFileSync","existsSync","resolve","resolve","options","resolve","resolve","existsSync","readFileSync","writeFileSync","existsSync","resolve","resolve","existsSync","writeFileSync","spawnSync","existsSync","resolve","existsSync","readFileSync","resolve","pkg","existsSync","resolve","spawnSync","pkg"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api-emulator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Local API emulators you can run, share, and extend with plugins",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@hono/node-server": "^1",
|
|
58
|
-
"@api-emulator/core": "0.
|
|
58
|
+
"@api-emulator/core": "0.6.0",
|
|
59
59
|
"commander": "^14",
|
|
60
60
|
"picocolors": "^1.1.1",
|
|
61
61
|
"yaml": "^2"
|