@executor-js/emulate 0.6.0 → 0.7.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/dist/api.d.ts CHANGED
@@ -1,3 +1,310 @@
1
+ interface Entity {
2
+ id: number;
3
+ created_at: string;
4
+ updated_at: string;
5
+ }
6
+ interface CollectionSnapshot<T extends Entity = Entity> {
7
+ items: T[];
8
+ autoId: number;
9
+ indexFields: string[];
10
+ }
11
+ interface StoreSnapshot {
12
+ collections: Record<string, CollectionSnapshot>;
13
+ data: Record<string, unknown>;
14
+ }
15
+ interface WebhookDelivery {
16
+ id: number;
17
+ hook_id: number;
18
+ event: string;
19
+ action?: string;
20
+ payload: unknown;
21
+ status_code: number | null;
22
+ delivered_at: string;
23
+ duration: number | null;
24
+ success: boolean;
25
+ }
26
+
27
+ interface LedgerIdentity {
28
+ user?: Pick<AuthUser, "login" | "id" | "scopes">;
29
+ app?: Pick<AuthApp, "appId" | "slug" | "name">;
30
+ }
31
+ interface LedgerSideEffect {
32
+ type: "create" | "update" | "delete" | "custom";
33
+ collection?: string;
34
+ id?: string | number;
35
+ summary?: string;
36
+ }
37
+ interface LedgerWebhookDelivery {
38
+ id: number;
39
+ hook_id: number;
40
+ event: string;
41
+ action?: string;
42
+ status_code: number | null;
43
+ success: boolean;
44
+ }
45
+ interface LedgerEntry {
46
+ id: string;
47
+ /** Correlation id: honored from X-Correlation-Id / X-Request-Id or generated. */
48
+ correlationId: string;
49
+ timestamp: string;
50
+ method: string;
51
+ host: string;
52
+ path: string;
53
+ query: string;
54
+ /** Matched route pattern, e.g. /repos/:owner/:repo/issues. */
55
+ route?: string;
56
+ /** Provider operation id, when the handler advertises one. */
57
+ operationId?: string;
58
+ request: {
59
+ headers: Record<string, string>;
60
+ body?: unknown;
61
+ bodyTruncated?: boolean;
62
+ };
63
+ identity: LedgerIdentity;
64
+ response: {
65
+ status: number;
66
+ headers: Record<string, string>;
67
+ body?: unknown;
68
+ bodyTruncated?: boolean;
69
+ };
70
+ /** Human/agent-readable one-liner, e.g. "POST /repos/:owner/:repo -> 201". */
71
+ summary: string;
72
+ sideEffects: LedgerSideEffect[];
73
+ webhookDeliveries: LedgerWebhookDelivery[];
74
+ durationMs: number;
75
+ }
76
+
77
+ interface AuthUser {
78
+ login: string;
79
+ id: number;
80
+ scopes: string[];
81
+ }
82
+ interface AuthApp {
83
+ appId: number;
84
+ slug: string;
85
+ name: string;
86
+ }
87
+
88
+ type SpecKind = "openapi" | "graphql" | "mcp" | "google-discovery" | "oauth-metadata" | "manual";
89
+ type SpecCoverage = "generated" | "hand-authored" | "partial" | "unsupported";
90
+ /**
91
+ * Per-operation coverage. The vision asks for honest, operation-level coverage
92
+ * boundaries (generated / hand-authored / partial / unsupported) instead of a
93
+ * single label stamped across an entire spec. A plugin declares the operations
94
+ * it actually implements; `GET /_emulate/coverage` reports them with a summary.
95
+ */
96
+ interface OperationCoverage {
97
+ operationId: string;
98
+ method?: string;
99
+ path?: string;
100
+ status: SpecCoverage;
101
+ summary?: string;
102
+ }
103
+ interface SpecManifest {
104
+ kind: SpecKind;
105
+ title: string;
106
+ url?: string;
107
+ coverage: SpecCoverage;
108
+ operations?: OperationCoverage[];
109
+ notes?: string;
110
+ }
111
+ interface SurfaceManifest {
112
+ id: string;
113
+ kind: "rest" | "oauth" | "oidc" | "graphql" | "mcp" | "webhooks" | "ui" | "provider-specific";
114
+ title: string;
115
+ basePath?: string;
116
+ status: "supported" | "partial" | "unsupported";
117
+ notes?: string;
118
+ }
119
+ interface AuthCapabilityManifest {
120
+ id: string;
121
+ title: string;
122
+ type: "api-key" | "bearer-token" | "oauth-client-credentials" | "oauth-authorization-code" | "oidc" | "jwt-app" | "dynamic-client-registration" | "webhook-secret" | "provider-specific";
123
+ status: "supported" | "partial" | "unsupported";
124
+ notes?: string;
125
+ }
126
+ interface ScenarioManifest {
127
+ id: string;
128
+ title: string;
129
+ description?: string;
130
+ }
131
+ /** A seedable area of the instance, surfaced so agents can discover the seed shape. */
132
+ interface SeedFieldManifest {
133
+ key: string;
134
+ title: string;
135
+ description?: string;
136
+ example?: unknown;
137
+ }
138
+ interface SeedSchemaManifest {
139
+ description?: string;
140
+ fields: SeedFieldManifest[];
141
+ /** A full example seed body that can be POSTed to /_emulate/seed. */
142
+ example?: unknown;
143
+ }
144
+ interface StateCollectionManifest {
145
+ name: string;
146
+ title?: string;
147
+ description?: string;
148
+ }
149
+ interface StateModelManifest {
150
+ description?: string;
151
+ collections: StateCollectionManifest[];
152
+ }
153
+ interface ResetBehaviorManifest {
154
+ description: string;
155
+ reseeds: boolean;
156
+ clearsLedger: boolean;
157
+ clearsWebhooks: boolean;
158
+ }
159
+ type InspectorTabKind = "landing" | "ledger" | "state" | "logs" | "credentials" | "seed" | "spec" | "custom";
160
+ interface InspectorTabManifest {
161
+ id: string;
162
+ title: string;
163
+ kind: InspectorTabKind;
164
+ description?: string;
165
+ }
166
+ /** Describes what the request ledger records and how durable it is. */
167
+ interface LedgerCapabilitiesManifest {
168
+ description?: string;
169
+ recordsFields: string[];
170
+ redactsSensitive: boolean;
171
+ correlationId: boolean;
172
+ webhookDeliveries: boolean;
173
+ sideEffects: boolean;
174
+ persistent: boolean;
175
+ maxEntries?: number;
176
+ }
177
+ type ConnectionKind = "sdk" | "cli" | "env" | "curl" | "config" | "mcp";
178
+ /**
179
+ * A copyable connection snippet. The `template` uses {{placeholders}} that the
180
+ * control plane resolves against the live instance ({{baseUrl}}, {{controlBaseUrl}},
181
+ * {{service}}, {{instance}}, {{token}}, {{clientId}}, {{clientSecret}}). This is
182
+ * how a human or agent copies ready-to-run SDK / CLI / app config without repo
183
+ * context.
184
+ */
185
+ interface ConnectionSnippet {
186
+ id: string;
187
+ title: string;
188
+ kind: ConnectionKind;
189
+ language?: string;
190
+ description?: string;
191
+ template: string;
192
+ }
193
+ interface ServiceManifest {
194
+ id: string;
195
+ name: string;
196
+ description: string;
197
+ surfaces: SurfaceManifest[];
198
+ auth: AuthCapabilityManifest[];
199
+ specs: SpecManifest[];
200
+ scenarios?: ScenarioManifest[];
201
+ seedSchema?: SeedSchemaManifest;
202
+ stateModel?: StateModelManifest;
203
+ resetBehavior?: ResetBehaviorManifest;
204
+ inspectorTabs?: InspectorTabManifest[];
205
+ ledger?: LedgerCapabilitiesManifest;
206
+ connections?: ConnectionSnippet[];
207
+ docsUrl?: string;
208
+ }
209
+ interface EmulatorInstanceInfo {
210
+ service: string;
211
+ instance?: string;
212
+ baseUrl: string;
213
+ controlBaseUrl: string;
214
+ providerBaseUrl: string;
215
+ }
216
+ interface ResolvedConnection extends ConnectionSnippet {
217
+ body: string;
218
+ }
219
+
220
+ interface CredentialRequest {
221
+ type?: string;
222
+ login?: string;
223
+ name?: string;
224
+ scopes?: string[];
225
+ client_id?: string;
226
+ client_secret?: string;
227
+ redirect_uris?: string[];
228
+ [key: string]: unknown;
229
+ }
230
+ interface IssuedCredential {
231
+ type: string;
232
+ token?: string;
233
+ login?: string;
234
+ scopes?: string[];
235
+ client_id?: string;
236
+ client_secret?: string;
237
+ redirect_uris?: string[];
238
+ token_url?: string;
239
+ authorization_url?: string;
240
+ notes?: string;
241
+ [key: string]: unknown;
242
+ }
243
+
244
+ interface ManifestResponse {
245
+ manifest: ServiceManifest;
246
+ instance: EmulatorInstanceInfo;
247
+ connections: ResolvedConnection[];
248
+ }
249
+ interface SpecsResponse {
250
+ specs: SpecManifest[];
251
+ surfaces: SurfaceManifest[];
252
+ }
253
+ interface CoverageResponse {
254
+ operations: OperationCoverage[];
255
+ summary: Record<SpecCoverage, number>;
256
+ specs: Array<{
257
+ kind: SpecKind;
258
+ title: string;
259
+ coverage: SpecCoverage;
260
+ operationCount: number;
261
+ }>;
262
+ }
263
+ interface LogsResponse {
264
+ webhooks: WebhookDelivery[];
265
+ requests: LedgerEntry[];
266
+ }
267
+ interface ConnectionsQuery {
268
+ token?: string;
269
+ clientId?: string;
270
+ clientSecret?: string;
271
+ }
272
+ declare class EmulatorControlError extends Error {
273
+ readonly method: string;
274
+ readonly url: string;
275
+ readonly status: number;
276
+ readonly body: string;
277
+ constructor(method: string, url: string, status: number, body: string);
278
+ }
279
+ /** fetch-compatible function, injectable for tests (e.g. a Hono app.request). */
280
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
281
+ declare class EmulatorClient {
282
+ #private;
283
+ /** Provider base URL — what an SDK or app under test points at. */
284
+ readonly baseUrl: string;
285
+ constructor(baseUrl: string, options?: {
286
+ fetch?: FetchLike;
287
+ });
288
+ /** The /_emulate/openapi URL — feed it to anything that ingests a spec. */
289
+ get openapiUrl(): string;
290
+ manifest(): Promise<ManifestResponse>;
291
+ quickstart(): Promise<string>;
292
+ specs(): Promise<SpecsResponse>;
293
+ coverage(): Promise<CoverageResponse>;
294
+ connections(query?: ConnectionsQuery): Promise<ResolvedConnection[]>;
295
+ state(): Promise<StoreSnapshot>;
296
+ logs(): Promise<LogsResponse>;
297
+ readonly ledger: {
298
+ list: (limit?: number) => Promise<LedgerEntry[]>;
299
+ clear: () => Promise<void>;
300
+ };
301
+ readonly credentials: {
302
+ mint: (request?: CredentialRequest) => Promise<IssuedCredential>;
303
+ };
304
+ seed(seed: unknown): Promise<void>;
305
+ reset(): Promise<void>;
306
+ }
307
+
1
308
  declare const SERVICE_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "okta", "aws", "resend", "stripe", "mongoatlas", "clerk", "spotify", "x", "workos", "autumn"];
2
309
  type ServiceName = (typeof SERVICE_NAME_LIST)[number];
3
310
 
@@ -14,11 +321,34 @@ interface EmulatorOptions {
14
321
  seed?: SeedConfig;
15
322
  baseUrl?: string;
16
323
  }
17
- interface Emulator {
324
+ /**
325
+ * A handle on a running emulator. Extends the typed /_emulate control-plane
326
+ * client (credentials.mint, ledger.list/clear, seed, reset, manifest, ...)
327
+ * with lifecycle for the locally spawned process.
328
+ */
329
+ interface Emulator extends EmulatorClient {
18
330
  url: string;
19
- reset(): void;
20
331
  close(): Promise<void>;
21
332
  }
333
+ interface ConnectOptions {
334
+ /** Provider base URL of a running emulator (local or hosted). */
335
+ baseUrl: string;
336
+ /**
337
+ * Expected service id; when given, the manifest is fetched on connect and a
338
+ * mismatch (or unreachable control plane) throws instead of failing later.
339
+ */
340
+ service?: ServiceName;
341
+ /** Override fetch (e.g. an in-process app.request) — mainly for tests. */
342
+ fetch?: FetchLike;
343
+ }
344
+ /**
345
+ * Attach to an already-running emulator — another local process or a hosted
346
+ * instance like `https://resend.<name>.emulators.dev` — and get the same
347
+ * typed control-plane client a locally spawned Emulator carries. Unlike
348
+ * createEmulator, nothing is spawned and close() is not available; lifecycle
349
+ * belongs to whoever started the instance.
350
+ */
351
+ declare function connectEmulator(options: ConnectOptions): Promise<EmulatorClient>;
22
352
  declare function createEmulator(options: EmulatorOptions): Promise<Emulator>;
23
353
 
24
- export { type Emulator, type EmulatorOptions, type SeedConfig, type ServiceName, createEmulator };
354
+ export { type ConnectOptions, type ConnectionsQuery, type CoverageResponse, type CredentialRequest, type Emulator, EmulatorClient, EmulatorControlError, type EmulatorOptions, type IssuedCredential, type LedgerEntry, type LedgerIdentity, type LedgerSideEffect, type LedgerWebhookDelivery, type LogsResponse, type ManifestResponse, type SeedConfig, type ServiceManifest, type ServiceName, type SpecsResponse, type StoreSnapshot, type WebhookDelivery, connectEmulator, createEmulator };
package/dist/api.js CHANGED
@@ -1812,6 +1812,102 @@ function createServer(plugin, options = {}) {
1812
1812
  );
1813
1813
  return { app, store, webhooks, ledger, port, baseUrl, tokenMap };
1814
1814
  }
1815
+ var EmulatorControlError = class extends Error {
1816
+ constructor(method, url, status, body) {
1817
+ super(`${method} ${url} -> ${status}${body ? `: ${body.slice(0, 300)}` : ""}`);
1818
+ this.method = method;
1819
+ this.url = url;
1820
+ this.status = status;
1821
+ this.body = body;
1822
+ this.name = "EmulatorControlError";
1823
+ }
1824
+ };
1825
+ var EmulatorClient = class {
1826
+ /** Provider base URL — what an SDK or app under test points at. */
1827
+ baseUrl;
1828
+ #fetch;
1829
+ constructor(baseUrl, options) {
1830
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
1831
+ this.#fetch = options?.fetch ?? ((input, init) => fetch(input, init));
1832
+ }
1833
+ /** The /_emulate/openapi URL — feed it to anything that ingests a spec. */
1834
+ get openapiUrl() {
1835
+ return `${this.baseUrl}/_emulate/openapi`;
1836
+ }
1837
+ async manifest() {
1838
+ return this.#json("GET", "/_emulate/manifest");
1839
+ }
1840
+ async quickstart() {
1841
+ return this.#text("GET", "/_emulate/quickstart");
1842
+ }
1843
+ async specs() {
1844
+ return this.#json("GET", "/_emulate/specs");
1845
+ }
1846
+ async coverage() {
1847
+ return this.#json("GET", "/_emulate/coverage");
1848
+ }
1849
+ async connections(query) {
1850
+ const params = new URLSearchParams();
1851
+ if (query?.token) params.set("token", query.token);
1852
+ if (query?.clientId) params.set("client_id", query.clientId);
1853
+ if (query?.clientSecret) params.set("client_secret", query.clientSecret);
1854
+ const qs = params.size > 0 ? `?${params}` : "";
1855
+ const body = await this.#json(
1856
+ "GET",
1857
+ `/_emulate/connections${qs}`
1858
+ );
1859
+ return body.connections;
1860
+ }
1861
+ async state() {
1862
+ return this.#json("GET", "/_emulate/state");
1863
+ }
1864
+ async logs() {
1865
+ return this.#json("GET", "/_emulate/logs");
1866
+ }
1867
+ ledger = {
1868
+ list: async (limit) => {
1869
+ const qs = limit !== void 0 ? `?limit=${limit}` : "";
1870
+ const body = await this.#json("GET", `/_emulate/ledger${qs}`);
1871
+ return body.entries;
1872
+ },
1873
+ clear: async () => {
1874
+ await this.#json("DELETE", "/_emulate/ledger");
1875
+ }
1876
+ };
1877
+ credentials = {
1878
+ mint: async (request = {}) => {
1879
+ const body = await this.#json(
1880
+ "POST",
1881
+ "/_emulate/credentials",
1882
+ request
1883
+ );
1884
+ return body.credential;
1885
+ }
1886
+ };
1887
+ async seed(seed) {
1888
+ await this.#json("POST", "/_emulate/seed", seed);
1889
+ }
1890
+ async reset() {
1891
+ await this.#json("POST", "/_emulate/reset", {});
1892
+ }
1893
+ async #request(method, path, body) {
1894
+ const url = `${this.baseUrl}${path}`;
1895
+ const response = await this.#fetch(url, {
1896
+ method,
1897
+ ...body === void 0 ? {} : { headers: { "content-type": "application/json" }, body: JSON.stringify(body) }
1898
+ });
1899
+ if (!response.ok) {
1900
+ throw new EmulatorControlError(method, url, response.status, await response.text());
1901
+ }
1902
+ return response;
1903
+ }
1904
+ async #json(method, path, body) {
1905
+ return (await this.#request(method, path, body)).json();
1906
+ }
1907
+ async #text(method, path, body) {
1908
+ return (await this.#request(method, path, body)).text();
1909
+ }
1910
+ };
1815
1911
 
1816
1912
  // src/registry.ts
1817
1913
  function issueServiceCredential(service, loaded, store, baseUrl, tokenMap, request, webhooks) {
@@ -2163,7 +2259,7 @@ var SERVICE_REGISTRY = {
2163
2259
  label: "Slack API emulator",
2164
2260
  endpoints: "auth, chat, conversations, users, profiles, presence, files, pins, bookmarks, views, reactions, team, OAuth, incoming webhooks, inspector",
2165
2261
  async load() {
2166
- const mod = await import("./dist-YPRJYQHW.js");
2262
+ const mod = await import("./dist-FE2JWST3.js");
2167
2263
  return { plugin: mod.slackPlugin, manifest: mod.manifest, seedFromConfig: mod.seedFromConfig };
2168
2264
  },
2169
2265
  defaultFallback() {
@@ -2294,7 +2390,7 @@ var SERVICE_REGISTRY = {
2294
2390
  label: "Okta OAuth 2.0 / OpenID Connect + management API emulator",
2295
2391
  endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo/introspect/revoke/logout, users, groups, apps, authorization servers",
2296
2392
  async load() {
2297
- const mod = await import("./dist-BTEY33DJ.js");
2393
+ const mod = await import("./dist-QXFWC3LV.js");
2298
2394
  return { plugin: mod.oktaPlugin, manifest: mod.manifest, seedFromConfig: mod.seedFromConfig };
2299
2395
  },
2300
2396
  defaultFallback(cfg) {
@@ -2363,7 +2459,7 @@ var SERVICE_REGISTRY = {
2363
2459
  label: "Resend email API emulator",
2364
2460
  endpoints: "emails, domains, contacts, API keys, inbox UI",
2365
2461
  async load() {
2366
- const mod = await import("./dist-XM5HSBDC.js");
2462
+ const mod = await import("./dist-TWJXVA7X.js");
2367
2463
  return { plugin: mod.resendPlugin, manifest: mod.manifest, seedFromConfig: mod.seedFromConfig };
2368
2464
  },
2369
2465
  defaultFallback() {
@@ -2380,7 +2476,7 @@ var SERVICE_REGISTRY = {
2380
2476
  label: "Stripe payments emulator",
2381
2477
  endpoints: "customers, payment methods, customer sessions, payment intents, charges, products, prices, checkout sessions, webhooks",
2382
2478
  async load() {
2383
- const mod = await import("./dist-K4CVTD6K.js");
2479
+ const mod = await import("./dist-KBZ6SM7D.js");
2384
2480
  return { plugin: mod.stripePlugin, manifest: mod.manifest, seedFromConfig: mod.seedFromConfig };
2385
2481
  },
2386
2482
  defaultFallback() {
@@ -2398,7 +2494,7 @@ var SERVICE_REGISTRY = {
2398
2494
  label: "MongoDB Atlas service emulator",
2399
2495
  endpoints: "Atlas Admin API v2 (projects, clusters, database users, databases, collections), Atlas Data API v1 (findOne, find, insertOne, insertMany, updateOne, updateMany, deleteOne, deleteMany, aggregate)",
2400
2496
  async load() {
2401
- const mod = await import("./dist-RMPDKZUA.js");
2497
+ const mod = await import("./dist-OT6R2YO2.js");
2402
2498
  return { plugin: mod.mongoatlasPlugin, manifest: mod.manifest, seedFromConfig: mod.seedFromConfig };
2403
2499
  },
2404
2500
  defaultFallback() {
@@ -2417,7 +2513,7 @@ var SERVICE_REGISTRY = {
2417
2513
  label: "Clerk authentication and user management emulator",
2418
2514
  endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo, users, email addresses, organizations, memberships, invitations, sessions",
2419
2515
  async load() {
2420
- const mod = await import("./dist-WBKONLOE.js");
2516
+ const mod = await import("./dist-OVTPVMMW.js");
2421
2517
  return { plugin: mod.clerkPlugin, manifest: mod.manifest, seedFromConfig: mod.seedFromConfig };
2422
2518
  },
2423
2519
  defaultFallback(cfg) {
@@ -2522,7 +2618,7 @@ var SERVICE_REGISTRY = {
2522
2618
  label: "WorkOS emulator",
2523
2619
  endpoints: "AuthKit user management (hosted login, code/refresh grants, sealed-session JWKS), organizations, memberships, invitations, API keys, Vault KV, OAuth authorization server",
2524
2620
  async load() {
2525
- const mod = await import("./dist-IYZPDKJW.js");
2621
+ const mod = await import("./dist-S47YJ552.js");
2526
2622
  return {
2527
2623
  plugin: mod.workosPlugin,
2528
2624
  manifest: mod.manifest,
@@ -2547,7 +2643,7 @@ var SERVICE_REGISTRY = {
2547
2643
  label: "Autumn billing emulator",
2548
2644
  endpoints: "customers (get_or_create with seedable subscriptions), usage tracking, plans/features/events lists",
2549
2645
  async load() {
2550
- const mod = await import("./dist-JJ2ZRCAX.js");
2646
+ const mod = await import("./dist-YXHZTLFR.js");
2551
2647
  return {
2552
2648
  plugin: mod.autumnPlugin,
2553
2649
  manifest: mod.manifest,
@@ -2585,6 +2681,18 @@ function resolveBaseUrl(opts) {
2585
2681
  }
2586
2682
 
2587
2683
  // src/api.ts
2684
+ async function connectEmulator(options) {
2685
+ const client = new EmulatorClient(options.baseUrl, { fetch: options.fetch });
2686
+ if (options.service) {
2687
+ const { manifest } = await client.manifest();
2688
+ if (manifest.id !== options.service) {
2689
+ throw new Error(
2690
+ `connectEmulator: ${options.baseUrl} is a "${manifest.id}" emulator, expected "${options.service}"`
2691
+ );
2692
+ }
2693
+ }
2694
+ return client;
2695
+ }
2588
2696
  async function createEmulator(options) {
2589
2697
  const { service, port = 4e3, seed: seedConfig } = options;
2590
2698
  const entry = SERVICE_REGISTRY[service];
@@ -2643,12 +2751,12 @@ async function createEmulator(options) {
2643
2751
  };
2644
2752
  seed();
2645
2753
  const httpServer = serve({ fetch: app.fetch, port });
2646
- return {
2647
- url: baseUrl,
2648
- reset() {
2754
+ class LocalEmulator extends EmulatorClient {
2755
+ url = baseUrl;
2756
+ async reset() {
2649
2757
  store.reset();
2650
2758
  seed();
2651
- },
2759
+ }
2652
2760
  close() {
2653
2761
  return new Promise((resolve, reject) => {
2654
2762
  httpServer.close((err) => {
@@ -2657,9 +2765,13 @@ async function createEmulator(options) {
2657
2765
  });
2658
2766
  });
2659
2767
  }
2660
- };
2768
+ }
2769
+ return new LocalEmulator(baseUrl, { fetch: (input, init) => app.request(input, init) });
2661
2770
  }
2662
2771
  export {
2772
+ EmulatorClient,
2773
+ EmulatorControlError,
2774
+ connectEmulator,
2663
2775
  createEmulator
2664
2776
  };
2665
2777
  //# sourceMappingURL=api.js.map