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 CHANGED
@@ -105,7 +105,7 @@ npm install @api-emulator/adapter-next @api-emulator/core
105
105
  ```
106
106
 
107
107
  ```ts
108
- import { createEmulateHandler } from '@api-emulator/adapter-next'
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 } = createEmulateHandler({
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`. Older `emulate.config.*` and `service-emulator.config.*` names still work as migration aliases.
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 ?? process.env.EMULATE_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 ?? process.env.EMULATE_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.5.2" };
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.5.2" };
681
- var defaultPort = process.env.API_EMULATOR_PORT ?? process.env.EMULATE_PORT ?? process.env.PORT ?? "4000";
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":";AAAA,SAAS,eAAe;;;ACCxB,SAAS,YAAY,eAAe;;;ACO7B,SAAS,mBAAmB,KAAoD;AACrF,SAAO,IAAI,YAAY,CAAC;AAC1B;AAEO,SAAS,uBACd,UACA,YAE2D;AAC3D,MAAI,SAAS,QAAQ,SAAS,SAAS,YAAY;AACjD,UAAM,IAAI,MAAM,yBAAyB,SAAS,IAAI,iCAAiC,UAAU,GAAG;AAAA,EACtG;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,SAAS,SAAS,GAAG,UAAU;AAAA,IACtC,WAAW,SAAS,aAAa;AAAA,IACjC,YAAY,SAAS,cAAc,CAAC;AAAA,EACtC;AACF;;;ADdA,eAAsB,yBAAyB,WAA0C;AACvF,QAAM,aAAa,UAAU,WAAW,GAAG,KAAK,WAAW,SAAS,IAAI,QAAQ,SAAS,IAAI;AAE7F,QAAM,MAAO,MAAM,OAAO;AAC1B,QAAM,SAAS,IAAI,UAAU,IAAI;AACjC,MAAI,CAAC,UAAU,OAAO,OAAO,aAAa,cAAc,OAAO,OAAO,SAAS,UAAU;AACvF,UAAM,IAAI,MAAM,WAAW,SAAS,+DAA+D;AAAA,EACrG;AAEA,QAAM,OAAO,OAAO;AACpB,QAAM,WAAW,uBAAuB,mBAAmB,GAAG,GAAG,IAAI;AACrE,SAAO;AAAA,IACL;AAAA,IACA,OAAO,SAAS;AAAA,IAChB,WAAW,SAAS;AAAA,IACpB;AAAA,IACA,MAAM,OAAO;AACX,aAAO;AAAA,QACL;AAAA,QACA,gBAAgB,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,IACA,iBAAiB,IAAI,oBAAoB,OAAO,EAAE,OAAO,SAAS,IAAI,GAAG,QAAQ,CAAC,EAAE;AAAA,IACpF,YAAY,SAAS;AAAA,EACvB;AACF;;;AEtCA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,WAAAA,gBAAe;AAEjB,IAAM,mBAAmB;AAgBzB,SAAS,wBAAoC;AAClD,SAAO,EAAE,SAAS,GAAG,SAAS,CAAC,EAAE;AACnC;AAEO,SAAS,eAAe,MAAM,QAAQ,IAAI,GAAe;AAC9D,QAAM,OAAOA,SAAQ,KAAK,gBAAgB;AAC1C,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,sBAAsB;AAEpD,QAAM,SAAS,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AACrD,MAAI,OAAO,YAAY,KAAK,OAAO,OAAO,YAAY,YAAY,OAAO,YAAY,MAAM;AACzF,UAAM,IAAI,MAAM,WAAW,gBAAgB,EAAE;AAAA,EAC/C;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,MAAkB,MAAM,QAAQ,IAAI,GAAS;AAC3E,QAAM,gBAAgB,OAAO,YAAY,OAAO,QAAQ,KAAK,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAC5G,QAAM,UAAU,GAAG,KAAK,UAAU,EAAE,SAAS,GAAG,SAAS,cAAc,GAAG,MAAM,CAAC,CAAC;AAAA;AAClF,gBAAcA,SAAQ,KAAK,gBAAgB,GAAG,SAAS,OAAO;AAChE;AAEO,SAAS,0BAA0B,MAAM,QAAQ,IAAI,GAAa;AACvE,SAAO,OAAO,OAAO,eAAe,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,MAAM,SAAS;AAClF;;;ACvCO,IAAM,uBAA+C,CAAC;AAEtD,IAAM,0BAA6D,CAAC;;;ACWpE,IAAM,iBAAiB;AAAA,EAC5B,QAAQ;AAAA,IACN,kBAAkB;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ,CAAC,QAAQ,QAAQ,aAAa,iBAAiB;AAAA,IACzD;AAAA,IACA,kBAAkB;AAAA,MAChB,OAAO;AAAA,MACP,QAAQ,CAAC,QAAQ,MAAM;AAAA,IACzB;AAAA,EACF;AACF;AAEA,eAAsB,qBACpB,mBAA6B,CAAC,GAC9B,UAAuC,CAAC,GACD;AACvC,QAAM,sBAAsB,QAAQ,mBAAmB,0BAA0B,IAAI,CAAC;AACtF,QAAM,gBAAgB,CAAC,GAAG,qBAAqB,GAAG,gBAAgB;AAClE,QAAM,UAAU,MAAM,QAAQ,IAAI,cAAc,IAAI,wBAAwB,CAAC;AAE7E,QAAM,kBAAgD,CAAC;AACvD,aAAW,gBAAgB,SAAS;AAClC,QAAI,aAAa,QAAQ,yBAAyB;AAChD,YAAM,IAAI,MAAM,WAAW,aAAa,IAAI,oCAAoC,aAAa,IAAI,GAAG;AAAA,IACtG;AACA,QAAI,aAAa,QAAQ,iBAAiB;AACxC,YAAM,IAAI,MAAM,0BAA0B,aAAa,IAAI,GAAG;AAAA,IAChE;AACA,oBAAgB,aAAa,IAAI,IAAI;AAAA,EACvC;AAEA,SAAO,EAAE,GAAG,yBAAyB,GAAG,gBAAgB;AAC1D;AAIO,SAAS,wBAAkC;AAChD,SAAO,CAAC,GAAG,oBAAoB;AACjC;;;ACtDA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,WAAAC,gBAAe;AACxB,SAAS,SAAS,iBAAiB;AACnC,OAAO,QAAQ;;;ACJf,SAAS,UAAU,iBAAiB;AACpC,SAAS,uBAAuB;AAEhC,SAAS,gBAAyB;AAChC,SAAO,QAAQ,QAAQ,MAAM,KAAK,KAAK,CAAC,QAAQ,IAAI;AACtD;AAEA,SAAS,cAAuB;AAC9B,QAAM,SAAS,UAAU,YAAY,CAAC,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC;AACvE,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,YAAY,UAAoC;AACvD,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,UAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,SAAG,MAAM;AACT,YAAM,aAAa,OAAO,KAAK,EAAE,YAAY;AAC7C,MAAAA,SAAQ,eAAe,MAAM,eAAe,OAAO,eAAe,KAAK;AAAA,IACzE,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,iBAA0B;AACjC,QAAM,SAAS,UAAU,YAAY,CAAC,MAAM,GAAG,EAAE,OAAO,SAAS,CAAC;AAClE,SAAO,OAAO,WAAW;AAC3B;AAEA,eAAsB,iBAAgC;AACpD,MAAI,CAAC,YAAY,GAAG;AAClB,QAAI,CAAC,cAAc,GAAG;AACpB,cAAQ,MAAM,gEAAgE;AAC9E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,MAAM,MAAM,YAAY,uEAAuE;AACrG,QAAI,CAAC,KAAK;AACR,cAAQ,MAAM,mCAAmC;AACjD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI;AACF,eAAS,qBAAqB,EAAE,OAAO,UAAU,CAAC;AAAA,IACpD,QAAQ;AACN,cAAQ,MAAM,6BAA6B;AAC3C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,CAAC,YAAY,GAAG;AAClB,cAAQ,MAAM,wDAAwD;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,CAAC,eAAe,GAAG;AACrB,YAAQ,MAAM,oEAAoE;AAClF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAOO,SAAS,gBAAgB,SAAgC;AAC9D,QAAM,aAA8B,CAAC;AACrC,aAAW,EAAE,MAAM,KAAK,KAAK,SAAS;AACpC,UAAM,SAAS,UAAU,YAAY,CAAC,SAAS,MAAM,OAAO,IAAI,GAAG,SAAS,GAAG;AAAA,MAC7E,OAAO;AAAA,IACT,CAAC;AACD,QAAI,OAAO,WAAW,GAAG;AACvB,UAAI,WAAW,SAAS,GAAG;AACzB,sBAAc,UAAU;AAAA,MAC1B;AACA,YAAM,IAAI,MAAM,sCAAsC,IAAI,OAAO,IAAI,EAAE;AAAA,IACzE;AACA,eAAW,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,EAChC;AACF;AAEO,SAAS,cAAc,SAAgC;AAC5D,aAAW,EAAE,KAAK,KAAK,SAAS;AAC9B,UAAM,SAAS,UAAU,YAAY,CAAC,SAAS,YAAY,IAAI,GAAG,EAAE,OAAO,SAAS,CAAC;AACrF,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,MAAM,6CAA6C,IAAI,EAAE;AAAA,IACnE;AAAA,EACF;AACF;AAEO,SAAS,gBAAgB,aAA6B;AAC3D,SAAO,WAAW,WAAW;AAC/B;;;AC7EO,SAAS,eAAe,MAAqC;AAClE,MAAI,KAAK,aAAa;AACpB,WAAO,KAAK,YAAY,QAAQ,gBAAgB,KAAK,OAAO;AAAA,EAC9D;AACA,MAAI,KAAK,SAAS;AAChB,WAAO,KAAK,QAAQ,QAAQ,gBAAgB,KAAK,OAAO;AAAA,EAC1D;AACA,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,YAAY;AACd,WAAO,WAAW,QAAQ,gBAAgB,KAAK,OAAO;AAAA,EACxD;AACA,QAAM,cAAc,QAAQ,IAAI;AAChC,MAAI,aAAa;AACf,WAAO,YAAY,QAAQ,gBAAgB,KAAK,OAAO;AAAA,EACzD;AACA,SAAO,oBAAoB,KAAK,IAAI;AACtC;;;AC/BA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAQK;AACP,SAAS,aAAa;AAgCf,SAAS,iBAAiB,YAA0C;AACzE,QAAM,SAAmB,CAAC;AAC1B,MAAI,YAAY,QAAQ;AACtB,QAAI,UAAU;AACd,eAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,WAAW,MAAM,GAAG;AAC7D,aAAO,KAAK,IAAI,EAAE,OAAO,KAAK,OAAO,IAAI,WAAW,QAAQ,KAAK,OAAO;AAAA,IAC1E;AAAA,EACF,OAAO;AACL,WAAO,kBAAkB,IAAI,EAAE,OAAO,SAAS,IAAI,GAAG,QAAQ,CAAC,QAAQ,QAAQ,aAAa,iBAAiB,EAAE;AAAA,EACjH;AACA,SAAO;AACT;AAEO,SAAS,qBAAqB,SAAgD;AACnF,QAAM,EAAE,SAAS,cAAc,cAAc,MAAM,SAAS,QAAQ,WAAW,IAAI;AAEnF,QAAM,cAA4C,CAAC;AACnD,QAAM,iBAA6C,aAAa,uBAC5D,CAAC,UAAU,YAAY,QAAS,KAAK,IACrC;AACJ,QAAM,eAAe,aAAa,gBAAgB,UAAU;AAE5D,QAAM,EAAE,KAAK,OAAO,SAAS,IAAI,aAAa,aAAa,QAAQ;AAAA,IACjE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,cAAY,UAAU,aAAa,uBAAuB,KAAK;AAE/D,QAAM,OAAO,MAAM;AACjB,iBAAa,OAAO,OAAO,OAAO,OAAO;AACzC,QAAI,cAAc,aAAa,gBAAgB;AAC7C,mBAAa,eAAe,OAAO,SAAS,YAAY,QAAQ;AAAA,IAClE;AAAA,EACF;AACA,OAAK;AAEL,QAAM,aAAa,MAAM,EAAE,OAAO,IAAI,OAAO,KAAK,CAAC;AAEnD,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,IACL;AAAA,IACA,WAAW;AACT,aAAO,MAAM,SAAS;AAAA,IACxB;AAAA,IACA,QAAQ,SAAS;AACf,YAAM,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IAC7C;AAAA,IACA,cAAcC,WAAU,CAAC,GAAG;AAC1B,YAAM,eAAe,MAAM,QAA8B,2BAA2B;AACpF,aAAO,mBAAmB,SAAS,MAAM,SAAS,GAAG;AAAA,QACnD,GAAGA;AAAA,QACH,cAAcA,SAAQ,gBAAgB;AAAA,MACxC,CAAC;AAAA,IACH;AAAA,IACA,eAAe,SAAS;AACtB,YAAM,MAAM;AACZ,YAAM,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IAC7C;AAAA,IACA,QAAQ;AACN,YAAM,MAAM;AACZ,WAAK;AAAA,IACP;AAAA,IACA,QAAuB;AACrB,aAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,mBAAW,MAAM,CAAC,QAAQ;AACxB,cAAI,IAAK,QAAO,GAAG;AAAA,cACd,CAAAA,SAAQ;AAAA,QACf,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AH7GA,IAAM,MAAM,EAAE,SAAS,QAAY;AAgBnC,SAAS,eAAe,UAAsC;AAC5D,MAAI,UAAU;AACZ,UAAM,WAAWC,SAAQ,QAAQ;AACjC,QAAI,CAACC,YAAW,QAAQ,GAAG;AACzB,cAAQ,MAAM,wBAAwB,QAAQ,EAAE;AAChD,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,UAAUC,cAAa,UAAU,OAAO;AAC9C,QAAI;AACF,YAAM,SAAS,SAAS,SAAS,OAAO,IAAI,KAAK,MAAM,OAAO,IAAI,UAAU,OAAO;AACnF,aAAO,EAAE,QAAQ,QAAQ,SAAS;AAAA,IACpC,SAAS,KAAK;AACZ,cAAQ,MAAM,mBAAmB,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACxF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,4BAA4B,2BAA2B,0BAA0B;AAEpG,aAAW,QAAQ,WAAW;AAC5B,UAAM,WAAWF,SAAQ,IAAI;AAC7B,QAAIC,YAAW,QAAQ,GAAG;AACxB,YAAM,UAAUC,cAAa,UAAU,OAAO;AAC9C,UAAI;AACF,cAAM,SAAS,SAAS,SAAS,OAAO,IAAI,KAAK,MAAM,OAAO,IAAI,UAAU,OAAO;AACnF,eAAO,EAAE,QAAQ,QAAQ,KAAK;AAAA,MAChC,SAAS,KAAK;AACZ,gBAAQ,MAAM,mBAAmB,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACpF,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,wBAAwB,QAAoB,mBAA8C;AACjG,QAAM,QAAQ,kBAAkB,OAAO,CAAC,MAAM,KAAK,MAAM;AACzD,SAAO,MAAM,SAAS,IAAI,CAAC,GAAG,KAAK,IAAI;AACzC;AAEA,eAAsB,aAAa,SAAsC;AACvE,QAAM,EAAE,MAAM,SAAS,IAAI;AAE3B,MAAI,QAAQ,YAAY,QAAQ,SAAS;AACvC,YAAQ,MAAM,mDAAmD;AACjE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,eAAe,QAAQ,IAAI;AAC1C,QAAM,aAAa,QAAQ,UAAU;AACrC,QAAM,eAAe,QAAQ,UAAU;AAEvC,QAAM,mBACJ,QAAQ,QACJ,MAAM,GAAG,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,KAAK,CAAC;AACzB,MAAI;AACJ,MAAI;AACF,uBAAmB,MAAM,qBAAqB,kBAAkB,EAAE,kBAAkB,KAAK,CAAC;AAAA,EAC5F,SAAS,KAAK;AACZ,YAAQ,MAAM,2BAA2B,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACnF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,iBAAiB,sBAAsB;AAC7C,QAAM,mBAAmB,OAAO,KAAK,gBAAgB,EAAE,OAAO,CAAC,SAAS,CAAC,eAAe,SAAS,IAAI,CAAC;AAEtG,MAAI;AACJ,MAAI,QAAQ,SAAS;AACnB,eAAW,QAAQ,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,EAC3D,WAAW,YAAY;AACrB,eAAW,wBAAwB,YAAY,OAAO,KAAK,gBAAgB,CAAC,KAAK;AAAA,MAC/E,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,EACF,OAAO;AACL,eAAW,CAAC,GAAG,gBAAgB,GAAG,gBAAgB;AAAA,EACpD;AAEA,aAAW,OAAO,UAAU;AAC1B,QAAI,CAAC,iBAAiB,GAAG,GAAG;AAC1B,cAAQ,MAAM,oBAAoB,GAAG,EAAE;AACvC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,SAAS,iBAAiB,UAAU;AAE1C,MAAI,QAAQ,UAAU;AACpB,UAAM,eAAe;AAAA,EACvB;AAWA,QAAM,kBAAmC,CAAC;AAC1C,QAAM,WAA8B,CAAC;AAErC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,MAAM,SAAS,CAAC;AACtB,UAAM,eAAe,iBAAiB,GAAG;AACzC,UAAM,eAAe,MAAM,aAAa,KAAK;AAE7C,UAAM,gBAAgB,aAAa,GAAG;AACtC,UAAM,OAAQ,eAAe,QAA+B,WAAW;AAEvE,QAAI,QAAQ,UAAU;AACpB,sBAAgB,KAAK,EAAE,MAAM,GAAG,GAAG,iBAAiB,KAAK,CAAC;AAAA,IAC5D;AAEA,UAAM,cACJ,OAAO,eAAe,YAAY,YAAY,cAAc,QAAQ,SAAS,IACzE,cAAc,UACd;AACN,UAAM,mBAAmB,QAAQ,WAAW,gBAAgB,GAAG,IAAI,QAAQ;AAC3E,UAAM,UAAU,eAAe,EAAE,SAAS,KAAK,MAAM,SAAS,kBAAkB,YAAY,CAAC;AAE7F,aAAS,KAAK,EAAE,KAAK,cAAc,cAAc,eAAe,MAAM,QAAQ,CAAC;AAAA,EACjF;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,oBAAgB,eAAe;AAAA,EACjC;AAEA,QAAM,cAAoD,CAAC;AAC3D,QAAM,kBAAoC,CAAC;AAE3C,aAAW,EAAE,KAAK,cAAc,cAAc,eAAe,MAAM,QAAQ,KAAK,UAAU;AACxF,gBAAY,KAAK,EAAE,MAAM,KAAK,KAAK,QAAQ,CAAC;AAE5C,UAAM,UAAU,qBAAqB;AAAA,MACnC,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,IACd,CAAC;AACD,oBAAgB,KAAK,OAAO;AAAA,EAC9B;AAEA,cAAY,aAAa,QAAQ,YAAY;AAE7C,QAAM,WAAW,MAAM;AACrB,YAAQ,IAAI;AAAA,EAAK,GAAG,IAAI,kBAAkB,CAAC,EAAE;AAC7C,QAAI,gBAAgB,SAAS,GAAG;AAC9B,oBAAc,eAAe;AAAA,IAC/B;AACA,eAAW,WAAW,iBAAiB;AACrC,WAAK,QAAQ,MAAM;AAAA,IACrB;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,KAAK,UAAU,QAAQ;AAC/B,UAAQ,KAAK,WAAW,QAAQ;AAClC;AAEA,SAAS,YACP,UACA,QACA,cACM;AACN,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,KAAK,GAAG,KAAK,cAAc,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,EAAE,CAAC,EAAE;AACtE,QAAM,KAAK,EAAE;AAEb,QAAM,aAAa,KAAK,IAAI,GAAG,SAAS,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AACjE,aAAW,EAAE,MAAM,IAAI,KAAK,UAAU;AACpC,UAAM,KAAK,KAAK,GAAG,KAAK,KAAK,OAAO,aAAa,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,GAAG,CAAC,EAAE;AAAA,EACvE;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,eAAe,OAAO,QAAQ,MAAM;AAC1C,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC,EAAE;AAClC,eAAW,CAAC,OAAO,IAAI,KAAK,cAAc;AACxC,YAAM,KAAK,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE;AAAA,IAC/D;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,cAAc;AAChB,UAAM,KAAK,KAAK,GAAG,IAAI,SAAS,CAAC,IAAI,YAAY,EAAE;AAAA,EACrD,OAAO;AACL,UAAM;AAAA,MACJ,KAAK,GAAG,IAAI,SAAS,CAAC,aAAa,GAAG,IAAI,MAAM,CAAC,iCAAiC,GAAG,IAAI,eAAe,CAAC;AAAA,IAC3G;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,UAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAC9B;;;AInOA,SAAS,iBAAAC,gBAAe,cAAAC,mBAAkB;AAC1C,SAAS,WAAAC,gBAAe;AACxB,SAAS,aAAa,qBAAqB;AAQ3C,eAAsB,YAAY,SAAqC;AACrE,QAAM,WAAW;AACjB,QAAM,WAAWC,SAAQ,QAAQ;AAEjC,MAAIC,YAAW,QAAQ,GAAG;AACxB,YAAQ,MAAM,+BAA+B,QAAQ,EAAE;AACvD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,mBACJ,QAAQ,QACJ,MAAM,GAAG,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,KAAK,CAAC;AACzB,QAAM,gBAAgB,MAAM,qBAAqB,kBAAkB,EAAE,kBAAkB,KAAK,CAAC;AAC7F,QAAM,oBAAoB,OAAO,KAAK,aAAa;AAEnD,MAAI;AACJ,MAAI,QAAQ,YAAY,OAAO;AAC7B,aAAS,EAAE,GAAG,eAAe;AAC7B,eAAW,QAAQ,mBAAmB;AACpC,aAAO,OAAO,QAAQ,cAAc,IAAI,EAAE,UAAU;AAAA,IACtD;AAAA,EACF,OAAO;AACL,UAAM,eAAe,cAAc,QAAQ,OAAO;AAClD,QAAI,CAAC,cAAc;AACjB,cAAQ,MAAM,oBAAoB,QAAQ,OAAO,gBAAgB,kBAAkB,KAAK,IAAI,CAAC,OAAO;AACpG,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,aAAS,EAAE,GAAG,gBAAgB,GAAG,aAAa,WAAW;AAAA,EAC3D;AAEA,QAAM,UAAU,cAAc,MAAM;AACpC,EAAAC,eAAc,UAAU,SAAS,OAAO;AAExC,UAAQ,IAAI,WAAW,QAAQ,EAAE;AACjC,UAAQ,IAAI;AAAA,qDAAwD;AACtE;;;ACzCA,eAAsB,YAAY,UAAuB,CAAC,GAAkB;AAC1E,QAAM,mBACJ,QAAQ,QACJ,MAAM,GAAG,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,KAAK,CAAC;AACzB,QAAM,gBAAgB,MAAM,qBAAqB,kBAAkB,EAAE,kBAAkB,KAAK,CAAC;AAE7F,UAAQ,IAAI,yBAAyB;AACrC,aAAW,gBAAgB,OAAO,OAAO,aAAa,GAAG;AACvD,YAAQ,IAAI,KAAK,aAAa,KAAK,OAAO,EAAE,CAAC,GAAG,aAAa,KAAK,EAAE;AACpE,YAAQ,IAAI,0BAA0B,aAAa,SAAS,EAAE;AAC9D,YAAQ,IAAI;AAAA,EACd;AACF;;;ACpBA,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,WAAAC,gBAAe;;;ACFxB,SAAS,cAAAC,aAAY,aAAa,gBAAAC,eAAc,gBAAgB;AAChE,SAAS,SAAS,MAAM,WAAAC,gBAAe;AAcvC,IAAM,iBAA+C;AAAA,EACnD,YAAY;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AACF;AAEA,IAAM,uBAAuB,CAAC,wBAAwB,uBAAuB;AAC7E,IAAM,yBAAyB,CAAC,2BAA2B;AAC3D,IAAM,qBAAqB,CAAC,oBAAoB,wBAAwB;AACxE,IAAM,qBAAqB,CAAC,gBAAgB,kBAAkB;AAE9D,SAAS,sBAAsB,MAAM,QAAQ,IAAI,GAAa;AAC5D,QAAM,WACJ,QAAQ,IAAI,8BAA8B,MAAM,GAAG,EAChD,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO,KAAK,CAAC;AACzB,QAAM,QAAQ,CAAC,GAAG,QAAQ;AAC1B,MAAI,UAAUA,SAAQ,GAAG;AACzB,aAAS;AACP,eAAW,OAAO,sBAAsB;AACtC,YAAM,KAAK,KAAK,SAAS,GAAG,CAAC;AAC7B,YAAM,KAAK,KAAK,QAAQ,OAAO,GAAG,GAAG,CAAC;AAAA,IACxC;AACA,UAAM,SAAS,QAAQ,OAAO;AAC9B,QAAI,WAAW,QAAS;AACxB,cAAU;AAAA,EACZ;AACA,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,CAAC,SAASA,SAAQ,IAAI,CAAC,CAAC,CAAC;AACxD;AAEA,SAAS,gBAAgB,MAAsB;AAC7C,QAAM,OAAO,KAAK,MAAM,OAAO,EAAE,IAAI,KAAK;AAC1C,MAAI,SAAS,uBAAwB,QAAO;AAC5C,MAAI,SAAS,wBAAyB,QAAO;AAC7C,SAAO;AACT;AAEA,SAAS,sBAAsB,aAAqBC,MAAmD;AACrG,MAAI,OAAOA,KAAI,YAAY,SAAU,QAAOD,SAAQ,aAAaC,KAAI,OAAO;AAC5E,MACE,OAAOA,KAAI,YAAY,YACvBA,KAAI,YAAY,QAChB,OAAOA,KAAI,WACX,OAAOA,KAAI,QAAQ,GAAG,MAAM,UAC5B;AACA,WAAOD,SAAQ,aAAaC,KAAI,QAAQ,GAAG,CAAC;AAAA,EAC9C;AACA,MACE,OAAOA,KAAI,YAAY,YACvBA,KAAI,YAAY,QAChB,OAAOA,KAAI,WACX,OAAOA,KAAI,QAAQ,GAAG,MAAM,YAC5BA,KAAI,QAAQ,GAAG,MAAM,QACrB,YAAYA,KAAI,QAAQ,GAAG,KAC3B,OAAOA,KAAI,QAAQ,GAAG,EAAE,WAAW,UACnC;AACA,WAAOD,SAAQ,aAAaC,KAAI,QAAQ,GAAG,EAAE,MAAM;AAAA,EACrD;AACA,MAAI,OAAOA,KAAI,SAAS,SAAU,QAAOD,SAAQ,aAAaC,KAAI,IAAI;AACtE,SAAO;AACT;AAcA,SAAS,iBAAiB,MAAc,UAAkC;AACxE,QAAM,UAA0B,CAAC;AACjC,aAAW,gBAAgB,wBAAwB;AACjD,UAAM,eAAe,KAAK,MAAM,YAAY;AAC5C,QAAI,CAACH,YAAW,YAAY,EAAG;AAC/B,UAAM,WAAW,KAAK,MAAMC,cAAa,cAAc,OAAO,CAAC;AAC/D,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,SAAS,WAAW,CAAC,CAAC,GAAG;AAClE,cAAQ,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,MAAM,MAAM,SAAS,MAAM,cAAc,YAAY;AAAA,QACrD,aAAa,MAAM;AAAA,QACnB,WAAW,MAAM,UAAU,WAAW,GAAG,IAAIC,SAAQ,MAAM,MAAM,SAAS,IAAI,MAAM;AAAA,QACpF,aAAa,MAAM,eAAe,GAAG,IAAI,gBAAgB,QAAQ;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,MAA8B;AACrD,MAAI,CAACF,YAAW,IAAI,KAAK,CAAC,SAAS,IAAI,EAAE,YAAY,EAAG,QAAO,CAAC;AAEhE,QAAM,WAAW,gBAAgB,IAAI;AACrC,QAAM,UAA0B,iBAAiB,MAAM,QAAQ;AAC/D,aAAW,SAAS,YAAY,MAAM,EAAE,eAAe,KAAK,CAAC,GAAG;AAC9D,QAAI,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,KAAK,WAAW,GAAG,EAAG;AACzD,UAAM,OAAO,MAAM,KAAK,MAAM,CAAC;AAC/B,UAAM,aAAa,KAAK,MAAM,MAAM,IAAI;AAExC,eAAW,aAAa,oBAAoB;AAC1C,YAAM,YAAY,KAAK,YAAY,SAAS;AAC5C,UAAIA,YAAW,SAAS,GAAG;AACzB,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA,aAAa,GAAG,IAAI,gBAAgB,QAAQ;AAAA,QAC9C,CAAC;AACD;AAAA,MACF;AAAA,IACF;AAEA,eAAW,YAAY,oBAAoB;AACzC,YAAM,cAAc,KAAK,YAAY,QAAQ;AAC7C,YAAM,kBAAkB,KAAK,aAAa,cAAc;AACxD,UAAI,CAACA,YAAW,eAAe,EAAG;AAClC,YAAMG,OAAM,KAAK,MAAMF,cAAa,iBAAiB,OAAO,CAAC;AAK7D,cAAQ,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,aAAaE,KAAI;AAAA,QACjB;AAAA,QACA,WAAW,sBAAsB,aAAaA,IAAG;AAAA,QACjD,aAAaA,KAAI,eAAe,GAAG,IAAI,iBAAiB,QAAQ;AAAA,MAClE,CAAC;AACD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAA8C;AACrD,QAAM,UAAU,EAAE,GAAG,eAAe;AACpC,aAAW,QAAQ,sBAAsB,GAAG;AAC1C,eAAW,UAAU,gBAAgB,IAAI,GAAG;AAC1C,cAAQ,OAAO,IAAI,MAAM;AAAA,IAC3B;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,oBAAoB,MAAmC;AACrE,SAAO,cAAc,EAAE,IAAI,KAAK;AAClC;;;ADxKA,SAAS,uBAA+B;AACtC,MAAIC,YAAWC,SAAQ,UAAU,CAAC,KAAKD,YAAWC,SAAQ,WAAW,CAAC,EAAG,QAAO;AAChF,MAAID,YAAWC,SAAQ,gBAAgB,CAAC,EAAG,QAAO;AAClD,MAAID,YAAWC,SAAQ,WAAW,CAAC,EAAG,QAAO;AAC7C,SAAO;AACT;AAEA,SAAS,eAAe,gBAAwB,aAA2B;AACzE,QAAM,OACJ,mBAAmB,QACf,CAAC,OAAO,MAAM,WAAW,IACzB,mBAAmB,SACjB,CAAC,OAAO,MAAM,WAAW,IACzB,mBAAmB,SACjB,CAAC,OAAO,MAAM,WAAW,IACzB,CAAC,WAAW,MAAM,WAAW;AAEvC,QAAM,SAASC,WAAU,gBAAgB,MAAM,EAAE,OAAO,UAAU,CAAC;AACnE,MAAI,OAAO,OAAO;AAChB,UAAM,OAAO;AAAA,EACf;AACA,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,GAAG,cAAc,IAAI,KAAK,KAAK,GAAG,CAAC,SAAS;AAAA,EAC9D;AACF;AAEA,eAAsB,eAAe,MAAc,UAA0B,CAAC,GAAkB;AAC9F,QAAM,SAAS,oBAAoB,IAAI;AACvC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,0BAA0B,IAAI,EAAE;AAAA,EAClD;AAEA,QAAM,iBAAiB,QAAQ,mBAAmB,SAAY,qBAAqB,IAAI,QAAQ;AAC/F,MAAI,kBAAkB,OAAO,aAAa;AACxC,mBAAe,gBAAgB,OAAO,WAAW;AAAA,EACnD,WAAW,kBAAkB,OAAO,SAAS,aAAa,CAAC,OAAO,aAAa;AAC7E,UAAM,IAAI,MAAM,kBAAkB,IAAI,6CAA6C;AAAA,EACrF;AAEA,QAAM,OAAO,eAAe;AAC5B,OAAK,QAAQ,OAAO,IAAI,IAAI;AAAA,IAC1B,MAAM,OAAO;AAAA,IACb,QAAQ,OAAO,SAAS,SAAS,cAAc;AAAA,IAC/C,UAAU,OAAO;AAAA,IACjB,aAAa,OAAO;AAAA,IACpB,WAAW,kBAAkB,OAAO,cAAc,OAAO,cAAc,OAAO;AAAA,IAC9E,SAAS;AAAA,EACX;AACA,kBAAgB,IAAI;AAEpB,UAAQ,IAAI,aAAa,OAAO,IAAI,SAAS,OAAO,QAAQ,EAAE;AAC9D,UAAQ,IAAI,sBAAsB,gBAAgB,EAAE;AACtD;;;AZvDA,IAAMC,OAAM,EAAE,SAAS,QAAY;AAEnC,IAAM,cAAc,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,QAAQ;AAEzE,IAAM,UAAU,IAAI,QAAQ;AAE5B,QAAQ,KAAK,KAAK,EAAE,YAAY,iEAAiE,EAAE,QAAQA,KAAI,OAAO;AAEtH,QACG,QAAQ,SAAS,EAAE,WAAW,KAAK,CAAC,EACpC,YAAY,2BAA2B,EACvC,OAAO,qBAAqB,aAAa,WAAW,EACpD,OAAO,4BAA4B,oCAAoC,EACvE,OAAO,iBAAiB,0BAA0B,EAClD,OAAO,oBAAoB,4DAA4D,EACvF,OAAO,cAAc,wDAAwD,EAC7E,OAAO,sBAAsB,wDAAwD,EACrF,OAAO,OAAO,SAAS;AACtB,QAAM,OAAO,SAAS,KAAK,MAAM,EAAE;AACnC,MAAI,OAAO,MAAM,IAAI,KAAK,OAAO,KAAK,OAAO,OAAO;AAClD,YAAQ,MAAM,iBAAiB,KAAK,IAAI,EAAE;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,UAAU,KAAK;AAAA,IACf,QAAQ,KAAK;AAAA,EACf,CAAC;AACH,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,OAAO,2BAA2B,kCAAkC,KAAK,EACzE,OAAO,sBAAsB,wDAAwD,EACrF,OAAO,OAAO,SAAS;AACtB,QAAM,YAAY,EAAE,SAAS,KAAK,SAAS,QAAQ,KAAK,OAAO,CAAC;AAClE,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,MAAM,eAAe,EACrB,YAAY,yBAAyB,EACrC,OAAO,sBAAsB,wDAAwD,EACrF,OAAO,OAAO,SAAS;AACtB,QAAM,YAAY,EAAE,QAAQ,KAAK,OAAO,CAAC;AAC3C,CAAC;AAEH,QACG,QAAQ,kBAAkB,EAC1B,YAAY,mCAAmC,EAC/C,OAAO,4BAA4B,wBAAwB,EAC3D,OAAO,wBAAwB,6CAA6C,EAC5E,OAAO,OAAO,QAAQ,SAAS;AAC9B,MAAI;AACF,UAAM,eAAe,QAAQ;AAAA,MAC3B,gBAAgB,KAAK,mBAAmB,QAAQ,QAAQ,KAAK;AAAA,IAC/D,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,GAAG;AACtD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QAAQ,MAAM;","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.5.2",
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.5.2",
58
+ "@api-emulator/core": "0.6.0",
59
59
  "commander": "^14",
60
60
  "picocolors": "^1.1.1",
61
61
  "yaml": "^2"