import {
  importPKCS8,
  jwtVerify
} from "./chunk-D6EKRYGP.js";
import {
  Hono,
  cors
} from "./chunk-AQ2CLRU3.js";

// ../@emulators/core/dist/index.js
import { AsyncLocalStorage } from "async_hooks";
import { createHmac } from "crypto";
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
function serializeValue(value) {
  if (value instanceof Map) {
    return { __type: "Map", entries: [...value.entries()].map(([k, v]) => [k, serializeValue(v)]) };
  }
  if (value instanceof Set) {
    return { __type: "Set", values: [...value.values()] };
  }
  return value;
}
function deserializeValue(value) {
  if (value !== null && typeof value === "object" && "__type" in value) {
    const tagged = value;
    if (tagged.__type === "Map") {
      const entries = tagged.entries;
      return new Map(entries.map(([k, v]) => [k, deserializeValue(v)]));
    }
    if (tagged.__type === "Set") {
      return new Set(tagged.values);
    }
  }
  return value;
}
var Collection = class {
  constructor(indexFields = []) {
    this.indexFields = indexFields;
    this.fieldNames = indexFields.map(String).sort();
    for (const field of indexFields) {
      this.indexes.set(String(field), /* @__PURE__ */ new Map());
    }
  }
  items = /* @__PURE__ */ new Map();
  indexes = /* @__PURE__ */ new Map();
  autoId = 1;
  fieldNames;
  addToIndex(item) {
    for (const field of this.indexFields) {
      const value = item[field];
      if (value === void 0 || value === null) continue;
      const indexMap = this.indexes.get(String(field));
      const key = String(value);
      if (!indexMap.has(key)) {
        indexMap.set(key, /* @__PURE__ */ new Set());
      }
      indexMap.get(key).add(item.id);
    }
  }
  removeFromIndex(item) {
    for (const field of this.indexFields) {
      const value = item[field];
      if (value === void 0 || value === null) continue;
      const indexMap = this.indexes.get(String(field));
      const key = String(value);
      indexMap.get(key)?.delete(item.id);
    }
  }
  insert(data) {
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const explicitId = data.id != null && data.id > 0 ? data.id : void 0;
    const id = explicitId ?? this.autoId++;
    if (id >= this.autoId) {
      this.autoId = id + 1;
    }
    const item = {
      ...data,
      id,
      created_at: now,
      updated_at: now
    };
    this.items.set(id, item);
    this.addToIndex(item);
    return item;
  }
  get(id) {
    return this.items.get(id);
  }
  findBy(field, value) {
    if (this.indexes.has(String(field))) {
      const ids = this.indexes.get(String(field)).get(String(value));
      if (!ids) return [];
      return Array.from(ids).map((id) => this.items.get(id)).filter(Boolean);
    }
    return this.all().filter((item) => item[field] === value);
  }
  findOneBy(field, value) {
    return this.findBy(field, value)[0];
  }
  update(id, data) {
    const existing = this.items.get(id);
    if (!existing) return void 0;
    this.removeFromIndex(existing);
    const updated = {
      ...existing,
      ...data,
      id,
      updated_at: (/* @__PURE__ */ new Date()).toISOString()
    };
    this.items.set(id, updated);
    this.addToIndex(updated);
    return updated;
  }
  delete(id) {
    const existing = this.items.get(id);
    if (!existing) return false;
    this.removeFromIndex(existing);
    return this.items.delete(id);
  }
  all() {
    return Array.from(this.items.values());
  }
  query(options = {}) {
    let results = this.all();
    if (options.filter) {
      results = results.filter(options.filter);
    }
    const total_count = results.length;
    if (options.sort) {
      results.sort(options.sort);
    }
    const page = options.page ?? 1;
    const per_page = Math.min(options.per_page ?? 30, 100);
    const start = (page - 1) * per_page;
    const paged = results.slice(start, start + per_page);
    return {
      items: paged,
      total_count,
      page,
      per_page,
      has_next: start + per_page < total_count,
      has_prev: page > 1
    };
  }
  count(filter) {
    if (!filter) return this.items.size;
    return this.all().filter(filter).length;
  }
  clear() {
    this.items.clear();
    for (const indexMap of this.indexes.values()) {
      indexMap.clear();
    }
    this.autoId = 1;
  }
  snapshot() {
    return {
      items: this.all(),
      autoId: this.autoId,
      indexFields: this.fieldNames
    };
  }
  restore(snap) {
    this.clear();
    this.autoId = snap.autoId;
    for (const item of snap.items) {
      this.items.set(item.id, item);
      this.addToIndex(item);
    }
  }
};
var Store = class {
  collections = /* @__PURE__ */ new Map();
  _data = /* @__PURE__ */ new Map();
  collection(name, indexFields = []) {
    const existing = this.collections.get(name);
    if (existing) {
      if (indexFields.length > 0) {
        const requested = indexFields.map(String).sort();
        if (existing.fieldNames.length !== requested.length || existing.fieldNames.some((f, i) => f !== requested[i])) {
          throw new Error(
            `Collection "${name}" already exists with indexes [${existing.fieldNames}] but was requested with [${requested}]`
          );
        }
      }
      return existing;
    }
    const col = new Collection(indexFields);
    this.collections.set(name, col);
    return col;
  }
  getData(key) {
    return this._data.get(key);
  }
  setData(key, value) {
    this._data.set(key, value);
  }
  reset() {
    for (const collection of this.collections.values()) {
      collection.clear();
    }
    for (const value of this._data.values()) {
      if (value instanceof Map || value instanceof Set) {
        value.clear();
      } else if (Array.isArray(value)) {
        value.length = 0;
      }
    }
  }
  snapshot() {
    const collections = {};
    for (const [name, col] of this.collections) {
      collections[name] = col.snapshot();
    }
    const data = {};
    for (const [key, value] of this._data) {
      data[key] = serializeValue(value);
    }
    return { collections, data };
  }
  restore(snap) {
    const snapshotNames = new Set(Object.keys(snap.collections));
    for (const name of this.collections.keys()) {
      if (!snapshotNames.has(name)) {
        this.collections.delete(name);
      }
    }
    for (const [name, colSnap] of Object.entries(snap.collections)) {
      const indexFields = colSnap.indexFields;
      const col = this.collection(name, indexFields);
      col.restore(colSnap);
    }
    this._data.clear();
    for (const [key, value] of Object.entries(snap.data)) {
      this._data.set(key, deserializeValue(value));
    }
  }
};
var tenantContext = new AsyncLocalStorage();
var DEFAULT_TENANT = "default";
function currentTenant() {
  return tenantContext.getStore() ?? DEFAULT_TENANT;
}
function withTenant(tenant, fn) {
  return tenantContext.run(tenant && tenant.length > 0 ? tenant : DEFAULT_TENANT, fn);
}
var TenantStore = class extends Store {
  tenants = /* @__PURE__ */ new Map();
  /** Resolve (lazily creating) the backing `Store` for the active tenant. */
  backing() {
    const key = currentTenant();
    let s = this.tenants.get(key);
    if (!s) {
      s = new Store();
      this.tenants.set(key, s);
    }
    return s;
  }
  /** Tenant ids that currently have a backing store (for inspection/tests). */
  tenantIds() {
    return [...this.tenants.keys()];
  }
  collection(name, indexFields = []) {
    return this.backing().collection(name, indexFields);
  }
  getData(key) {
    return this.backing().getData(key);
  }
  setData(key, value) {
    this.backing().setData(key, value);
  }
  reset() {
    this.backing().reset();
  }
  snapshot() {
    return this.backing().snapshot();
  }
  restore(snap) {
    this.backing().restore(snap);
  }
};
function webhookRetryConfig() {
  const attempts = Math.max(1, Math.floor(Number(process.env.EMULATE_WEBHOOK_RETRIES) || 3));
  const raw = process.env.EMULATE_WEBHOOK_RETRY_BACKOFF_MS;
  const backoffMs = Math.max(0, Number(raw === void 0 || raw === "" ? 100 : raw) || 0);
  return { attempts, backoffMs };
}
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function deliverWithRetry(url, init, deps) {
  const { attempts, backoffMs } = webhookRetryConfig();
  const doFetch = deps?.fetch ?? globalThis.fetch;
  const sleep = deps?.sleep ?? defaultSleep;
  const result = { status_code: null, success: false, attempts: 0 };
  for (let i = 0; i < attempts; i++) {
    result.attempts = i + 1;
    try {
      const res = await doFetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
      result.status_code = res.status;
      result.success = res.ok;
      if (res.ok) return result;
    } catch {
      result.status_code = null;
      result.success = false;
    }
    if (i < attempts - 1 && backoffMs > 0) await sleep(backoffMs * 2 ** i);
  }
  return result;
}
var MAX_DELIVERIES = 1e3;
var WebhookDispatcher = class {
  /** `fetch`/`sleep` are injectable so retry behaviour is testable fast. */
  constructor(deps) {
    this.deps = deps;
  }
  subscriptions = [];
  deliveries = [];
  subscriptionIdCounter = 1;
  deliveryIdCounter = 1;
  register(sub) {
    const { id: explicitId, ...rest } = sub;
    const id = explicitId !== void 0 ? explicitId : this.subscriptionIdCounter++;
    if (id >= this.subscriptionIdCounter) {
      this.subscriptionIdCounter = id + 1;
    }
    const subscription = { ...rest, id };
    this.subscriptions.push(subscription);
    return subscription;
  }
  unregister(id) {
    const idx = this.subscriptions.findIndex((s) => s.id === id);
    if (idx === -1) return false;
    this.subscriptions.splice(idx, 1);
    return true;
  }
  getSubscription(id) {
    return this.subscriptions.find((s) => s.id === id);
  }
  getSubscriptions(owner, repo) {
    return this.subscriptions.filter((s) => {
      if (owner && s.owner !== owner) return false;
      if (repo !== void 0 && s.repo !== repo) return false;
      return true;
    });
  }
  updateSubscription(id, data) {
    const sub = this.subscriptions.find((s) => s.id === id);
    if (!sub) return void 0;
    Object.assign(sub, data);
    return sub;
  }
  async dispatch(event, action, payload, owner, repo) {
    const matchingSubs = this.subscriptions.filter((s) => {
      if (!s.active) return false;
      if (s.owner !== owner) return false;
      if (repo !== void 0) {
        if (s.repo !== repo) return false;
      } else if (s.repo !== void 0) {
        return false;
      }
      return event === "ping" || s.events.includes("*") || s.events.includes(event);
    });
    for (const sub of matchingSubs) {
      const delivery = {
        id: this.deliveryIdCounter++,
        hook_id: sub.id,
        event,
        action,
        payload,
        status_code: null,
        delivered_at: (/* @__PURE__ */ new Date()).toISOString(),
        duration: null,
        success: false,
        attempts: 0
      };
      const body = JSON.stringify(payload);
      const signatureHeaders = {};
      if (sub.secret) {
        const hmac = createHmac("sha256", sub.secret).update(body).digest("hex");
        signatureHeaders["X-Hub-Signature-256"] = `sha256=${hmac}`;
      }
      const start = Date.now();
      const outcome = await deliverWithRetry(
        sub.url,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-GitHub-Event": event,
            "X-GitHub-Delivery": String(delivery.id),
            ...signatureHeaders
          },
          body
        },
        this.deps
      );
      delivery.duration = Date.now() - start;
      delivery.status_code = outcome.status_code;
      delivery.success = outcome.success;
      delivery.attempts = outcome.attempts;
      this.deliveries.push(delivery);
      if (this.deliveries.length > MAX_DELIVERIES) {
        this.deliveries.splice(0, this.deliveries.length - MAX_DELIVERIES);
      }
    }
  }
  getDeliveries(hookId) {
    if (hookId !== void 0) {
      return this.deliveries.filter((d) => d.hook_id === hookId);
    }
    return [...this.deliveries];
  }
  clear() {
    this.subscriptions.length = 0;
    this.deliveries.length = 0;
    this.subscriptionIdCounter = 1;
    this.deliveryIdCounter = 1;
  }
};
var DEFAULT_DOCS_URL = "https://github.com/Vinniai/agent-emulate#readme";
function getDocsUrl(c) {
  return c.get("docsUrl") ?? DEFAULT_DOCS_URL;
}
function errorStatus(err) {
  if (err && typeof err === "object" && "status" in err) {
    const s = err.status;
    if (typeof s === "number" && Number.isFinite(s)) return s;
  }
  return 500;
}
function createApiErrorHandler(documentationUrl) {
  return (err, c) => {
    if (documentationUrl) {
      c.set("docsUrl", documentationUrl);
    }
    const status = errorStatus(err);
    const message = err instanceof Error ? err.message : "Internal Server Error";
    return c.json(
      {
        message,
        documentation_url: getDocsUrl(c)
      },
      status
    );
  };
}
function createErrorHandler(documentationUrl) {
  return async (c, next) => {
    if (documentationUrl) {
      c.set("docsUrl", documentationUrl);
    }
    await next();
  };
}
var errorHandler = createErrorHandler();
var isDebug = typeof process !== "undefined" && (process.env.DEBUG === "1" || process.env.DEBUG === "true" || process.env.EMULATE_DEBUG === "1");
function debug(label, ...args) {
  if (isDebug) {
    console.log(`[${label}]`, ...args);
  }
}
function authLax() {
  const v = process.env.EMULATE_AUTH_LAX;
  return v === "1" || v === "true";
}
function authMiddleware(tokens, appKeyResolver, fallbackUser) {
  return async (c, next) => {
    const authHeader = c.req.header("Authorization");
    if (authHeader) {
      const token = authHeader.replace(/^(Bearer|token)\s+/i, "").trim();
      if (token.startsWith("eyJ") && appKeyResolver) {
        try {
          const [, payloadB64] = token.split(".");
          const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
          const appId = typeof payload.iss === "string" ? parseInt(payload.iss, 10) : payload.iss;
          if (typeof appId === "number" && !isNaN(appId)) {
            const appInfo = appKeyResolver(appId);
            if (appInfo) {
              const key = await importPKCS8(appInfo.privateKey, "RS256");
              await jwtVerify(token, key, { algorithms: ["RS256"] });
              c.set("authApp", {
                appId,
                slug: appInfo.slug,
                name: appInfo.name
              });
            }
          }
        } catch {
        }
      } else {
        let user = tokens.get(token);
        let expiredKnownToken = false;
        if (user && user.expiresAt !== void 0 && !authLax() && Date.now() > user.expiresAt) {
          debug("auth", "expired token rejected", { login: user.login, expiredMsAgo: Date.now() - user.expiresAt });
          tokens.delete(token);
          user = void 0;
          expiredKnownToken = true;
        }
        if (!user && !expiredKnownToken && fallbackUser && token.length > 0) {
          debug("auth", "fallback user for unknown token", { login: fallbackUser.login, id: fallbackUser.id });
          user = { login: fallbackUser.login, id: fallbackUser.id, scopes: fallbackUser.scopes };
        }
        if (user) {
          c.set("authUser", user);
          c.set("authToken", token);
          c.set("authScopes", user.scopes);
        }
      }
    }
    await next();
  };
}
var __dirname = dirname(fileURLToPath(import.meta.url));
var FONTS = {
  "geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
  "GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
};
var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
function registerFontRoutes(app) {
  app.get("/_emulate/fonts/:name", (c) => {
    const name = c.req.param("name");
    const buf = FONTS[name];
    if (!buf) return c.notFound();
    return new Response(buf, {
      headers: {
        "Content-Type": "font/woff2",
        "Cache-Control": "public, max-age=31536000, immutable",
        "Access-Control-Allow-Origin": "*"
      }
    });
  });
  app.get("/_emulate/favicon.ico", (c) => {
    return new Response(FAVICON, {
      headers: {
        "Content-Type": "image/x-icon",
        "Cache-Control": "public, max-age=31536000, immutable"
      }
    });
  });
}
var GITHUB = {
  limit: 5e3,
  windowSec: 3600,
  exceededStatus: 403,
  rateLimitHeaders: true,
  body: (_retry, docsUrl) => ({ message: "API rate limit exceeded", documentation_url: docsUrl })
};
var STRIPE = {
  limit: 100,
  windowSec: 1,
  exceededStatus: 429,
  rateLimitHeaders: false,
  body: () => ({
    error: {
      type: "rate_limit_error",
      code: "rate_limit",
      message: "Too many requests hit the API too quickly. We recommend an exponential backoff of your requests."
    }
  })
};
var SLACK = {
  limit: 100,
  windowSec: 60,
  exceededStatus: 429,
  rateLimitHeaders: false,
  body: () => ({ ok: false, error: "ratelimited" })
};
var GOOGLE = {
  limit: 1200,
  windowSec: 60,
  exceededStatus: 429,
  rateLimitHeaders: false,
  body: () => ({
    error: {
      code: 429,
      message: "Rate Limit Exceeded",
      errors: [{ message: "Rate Limit Exceeded", domain: "usageLimits", reason: "rateLimitExceeded" }],
      status: "RESOURCE_EXHAUSTED"
    }
  })
};
var MICROSOFT = {
  limit: 1e4,
  windowSec: 600,
  exceededStatus: 429,
  rateLimitHeaders: false,
  body: () => ({
    error: {
      code: "TooManyRequests",
      message: "Too many requests. Please retry after the time specified in the Retry-After header.",
      innerError: { code: "TooManyRequests", date: (/* @__PURE__ */ new Date()).toISOString() }
    }
  })
};
var SALESFORCE = {
  limit: 1e3,
  windowSec: 60,
  exceededStatus: 403,
  rateLimitHeaders: false,
  body: () => [{ message: "TotalRequests Limit exceeded.", errorCode: "REQUEST_LIMIT_EXCEEDED" }]
};
var HUBSPOT = {
  limit: 100,
  windowSec: 10,
  exceededStatus: 429,
  rateLimitHeaders: false,
  extraHeaders: (state, profile) => ({
    "X-HubSpot-RateLimit-Max": String(profile.limit),
    "X-HubSpot-RateLimit-Remaining": String(state.remaining),
    "X-HubSpot-RateLimit-Interval-Milliseconds": String(profile.windowSec * 1e3)
  }),
  body: () => ({
    status: "error",
    message: "You have reached your secondly limit.",
    errorType: "RATE_LIMIT",
    category: "RATE_LIMITS"
  })
};
var ZENDESK = {
  limit: 700,
  windowSec: 60,
  exceededStatus: 429,
  rateLimitHeaders: false,
  body: () => ({ error: "TooManyRequests", description: "Number of allowed requests per minute exceeded." })
};
var INTERCOM = {
  limit: 1e3,
  windowSec: 60,
  exceededStatus: 429,
  rateLimitHeaders: false,
  extraHeaders: (state, profile) => ({
    "X-RateLimit-Limit": String(profile.limit),
    "X-RateLimit-Remaining": String(state.remaining),
    "X-RateLimit-Reset": String(state.resetAt)
  }),
  body: () => ({
    type: "error.list",
    errors: [{ code: "rate_limit_exceeded", message: "The rate limit for this resource has been exceeded." }]
  })
};
var DISCORD = {
  limit: 50,
  windowSec: 1,
  exceededStatus: 429,
  rateLimitHeaders: false,
  extraHeaders: (state, profile, now) => ({
    "X-RateLimit-Limit": String(profile.limit),
    "X-RateLimit-Remaining": String(state.remaining),
    "X-RateLimit-Reset": String(state.resetAt),
    "X-RateLimit-Reset-After": String(Math.max(0, state.resetAt - now))
  }),
  body: (retry) => ({
    message: "You are being rate limited.",
    retry_after: retry,
    global: false,
    code: 0
  })
};
var PIPEDRIVE = {
  limit: 80,
  windowSec: 2,
  exceededStatus: 429,
  rateLimitHeaders: false,
  extraHeaders: (state, profile) => ({
    "x-ratelimit-limit": String(profile.limit),
    "x-ratelimit-remaining": String(state.remaining)
  }),
  body: () => ({
    success: false,
    error: "rate limit exceeded",
    error_info: "Please refer to https://developers.pipedrive.com/docs/api/v1/#rate-limiting"
  })
};
var RESEND = {
  limit: 2,
  windowSec: 1,
  exceededStatus: 429,
  rateLimitHeaders: false,
  extraHeaders: (state, profile) => ({
    "ratelimit-limit": String(profile.limit),
    "ratelimit-remaining": String(state.remaining)
  }),
  body: () => ({
    statusCode: 429,
    name: "rate_limit_exceeded",
    message: "Too many requests. You have reached your rate limit."
  })
};
var PROFILES = {
  github: GITHUB,
  stripe: STRIPE,
  slack: SLACK,
  google: GOOGLE,
  microsoft: MICROSOFT,
  salesforce: SALESFORCE,
  hubspot: HUBSPOT,
  zendesk: ZENDESK,
  intercom: INTERCOM,
  discord: DISCORD,
  pipedrive: PIPEDRIVE,
  resend: RESEND
};
function rateLimitProfile(name) {
  const base = PROFILES[name.toLowerCase()] ?? GITHUB;
  return { name, ...base };
}
function rateLimitHeaders(profile, state, now) {
  const headers = {};
  if (profile.rateLimitHeaders) {
    headers["X-RateLimit-Limit"] = String(profile.limit);
    headers["X-RateLimit-Remaining"] = String(state.remaining);
    headers["X-RateLimit-Reset"] = String(state.resetAt);
    headers["X-RateLimit-Resource"] = "core";
  }
  if (profile.extraHeaders) {
    Object.assign(headers, profile.extraHeaders(state, profile, now));
  }
  if (state.remaining <= 0) {
    headers["Retry-After"] = String(Math.max(0, state.resetAt - now));
  }
  return headers;
}
function createServer(plugin, options = {}) {
  const port = options.port ?? 4e3;
  const baseUrl = options.baseUrl ?? `http://localhost:${port}`;
  const multiTenant = options.multiTenant ?? (process.env.EMULATE_MULTI_TENANT === "1" || process.env.EMULATE_MULTI_TENANT === "true");
  const app = new Hono();
  const store = multiTenant ? new TenantStore() : new Store();
  const webhooks = new WebhookDispatcher();
  const tokenMap = /* @__PURE__ */ new Map();
  if (options.tokens) {
    for (const [token, user] of Object.entries(options.tokens)) {
      tokenMap.set(token, {
        login: user.login,
        id: user.id,
        scopes: user.scopes ?? ["repo", "user", "admin:org", "admin:repo_hook"]
      });
    }
  }
  const docsUrl = options.docsUrl ?? "https://github.com/Vinniai/agent-emulate#readme";
  registerFontRoutes(app);
  app.onError(createApiErrorHandler(docsUrl));
  app.use("*", cors());
  app.use("*", createErrorHandler(docsUrl));
  app.use("*", authMiddleware(tokenMap, options.appKeyResolver, options.fallbackUser));
  if (multiTenant) {
    app.use("*", (c, next) => withTenant(c.req.header("X-Emulate-Tenant"), () => next()));
  }
  const rlProfile = rateLimitProfile(plugin.name);
  const rlLimit = options.rateLimit?.limit ?? rlProfile.limit;
  const rlWindow = options.rateLimit?.windowSec ?? rlProfile.windowSec;
  const rateLimitCounters = /* @__PURE__ */ new Map();
  let lastPruneAt = Math.floor(Date.now() / 1e3);
  app.use("*", async (c, next) => {
    const token = c.get("authToken") ?? "__anonymous__";
    const now = Math.floor(Date.now() / 1e3);
    if (now - lastPruneAt > 3600) {
      for (const [key, val] of rateLimitCounters) {
        if (val.resetAt <= now) rateLimitCounters.delete(key);
      }
      lastPruneAt = now;
    }
    let counter = rateLimitCounters.get(token);
    if (!counter || counter.resetAt <= now) {
      counter = { remaining: rlLimit, resetAt: now + rlWindow };
      rateLimitCounters.set(token, counter);
    }
    counter.remaining = Math.max(0, counter.remaining - 1);
    for (const [k, v] of Object.entries(rateLimitHeaders(rlProfile, counter, now))) {
      c.header(k, v);
    }
    if (counter.remaining === 0) {
      const retryAfter = Math.max(0, counter.resetAt - now);
      return c.json(rlProfile.body(retryAfter, docsUrl), rlProfile.exceededStatus);
    }
    await next();
  });
  plugin.register(app, store, webhooks, baseUrl, tokenMap);
  app.notFound(
    (c) => c.json(
      {
        message: "Not Found",
        documentation_url: docsUrl
      },
      404
    )
  );
  return { app, store, webhooks, port, baseUrl, tokenMap };
}
var RING_SIZE = 200;
var ActivityBus = class {
  buffer = [];
  subscribers = /* @__PURE__ */ new Set();
  publish(event) {
    this.buffer.push(event);
    if (this.buffer.length > RING_SIZE) this.buffer.splice(0, this.buffer.length - RING_SIZE);
    for (const cb of this.subscribers) {
      try {
        cb(event);
      } catch {
      }
    }
  }
  subscribe(cb) {
    this.subscribers.add(cb);
    return () => this.subscribers.delete(cb);
  }
  recent(limit = 50, service) {
    const filtered = service ? this.buffer.filter((e) => e.service === service) : this.buffer;
    return filtered.slice(-limit).reverse();
  }
};
var activityBus = globalThis.__emulate_activity_bus__ ?? (globalThis.__emulate_activity_bus__ = new ActivityBus());

// src/registry.ts
var SERVICE_NAME_LIST = [
  "vercel",
  "github",
  "google",
  "slack",
  "apple",
  "microsoft",
  "okta",
  "aws",
  "resend",
  "stripe",
  "mongoatlas",
  "clerk",
  "workos",
  "nango",
  "simpro",
  "uptick"
];
var SERVICE_NAMES = SERVICE_NAME_LIST;
var SERVICE_REGISTRY = {
  vercel: {
    label: "Vercel REST API emulator",
    endpoints: "projects, deployments, domains, env vars, users, teams, file uploads, protection bypass",
    async load() {
      const mod = await import("./dist-FLBB3GSD.js");
      return { plugin: mod.vercelPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback(cfg) {
      const firstLogin = cfg?.users?.[0]?.username ?? "admin";
      return { login: firstLogin, id: 1, scopes: [] };
    },
    initConfig: {
      vercel: {
        users: [{ username: "developer", name: "Developer", email: "dev@agent-emulate.dev" }],
        teams: [{ slug: "my-team", name: "My Team" }],
        projects: [{ name: "my-app", team: "my-team", framework: "nextjs" }],
        integrations: [
          {
            client_id: "oac_example_client_id",
            client_secret: "example_client_secret",
            name: "My Vercel App",
            redirect_uris: ["http://localhost:3000/api/auth/callback/vercel"]
          }
        ]
      }
    }
  },
  github: {
    label: "GitHub REST API emulator",
    endpoints: "users, repos, issues, PRs, comments, reviews, labels, milestones, branches, git data, orgs, teams, releases, webhooks, search, actions, checks, rate limit",
    async load() {
      const mod = await import("./dist-Z6QRJFXP.js");
      return {
        plugin: mod.githubPlugin,
        seedFromConfig: mod.seedFromConfig,
        createAppKeyResolver(store) {
          return (appId) => {
            try {
              const gh = mod.getGitHubStore(store);
              const ghApp = gh.apps.all().find((a) => a.app_id === appId);
              if (!ghApp) return null;
              return { privateKey: ghApp.private_key, slug: ghApp.slug, name: ghApp.name };
            } catch {
              return null;
            }
          };
        }
      };
    },
    defaultFallback(cfg) {
      const firstLogin = cfg?.users?.[0]?.login ?? "admin";
      return { login: firstLogin, id: 1, scopes: ["repo", "user", "admin:org", "admin:repo_hook"] };
    },
    initConfig: {
      github: {
        users: [
          {
            login: "octocat",
            name: "The Octocat",
            email: "octocat@github.com",
            bio: "I am the Octocat",
            company: "GitHub",
            location: "San Francisco"
          }
        ],
        orgs: [{ login: "my-org", name: "My Organization", description: "A test organization" }],
        repos: [
          {
            owner: "octocat",
            name: "hello-world",
            description: "My first repository",
            language: "JavaScript",
            topics: ["hello", "world"],
            auto_init: true
          },
          {
            owner: "my-org",
            name: "org-repo",
            description: "An organization repository",
            language: "TypeScript",
            auto_init: true
          }
        ],
        oauth_apps: [
          {
            client_id: "Iv1.example_client_id",
            client_secret: "example_client_secret",
            name: "My App",
            redirect_uris: ["http://localhost:3000/api/auth/callback/github"]
          }
        ]
      }
    }
  },
  google: {
    label: "Google OAuth 2.0 / OpenID Connect + Gmail, Calendar, and Drive emulator",
    endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, token revocation, Gmail messages/drafts/threads/labels/history/settings, Calendar lists/events/freebusy, Drive files/uploads",
    async load() {
      const mod = await import("./dist-JMVH3PLE.js");
      return {
        plugin: mod.googlePlugin,
        seedFromConfig: mod.seedFromConfig,
        storeToSeedConfig: mod.storeToSeedConfig
      };
    },
    defaultFallback(cfg) {
      const firstEmail = cfg?.users?.[0]?.email ?? "testuser@agent-emulate.dev";
      return { login: firstEmail, id: 1, scopes: ["openid", "email", "profile"] };
    },
    initConfig: {
      google: {
        users: [
          {
            email: "testuser@agent-emulate.dev",
            name: "Test User",
            picture: "https://lh3.googleusercontent.com/a/default-user",
            email_verified: true
          }
        ],
        oauth_clients: [
          {
            client_id: "example-client-id.apps.googleusercontent.com",
            client_secret: "GOCSPX-example_secret",
            name: "Code App (Google)",
            redirect_uris: ["http://localhost:3000/api/auth/callback/google"]
          }
        ],
        labels: [
          {
            id: "Label_ops",
            user_email: "testuser@agent-emulate.dev",
            name: "Ops/Review",
            color_background: "#DDEEFF",
            color_text: "#111111"
          }
        ],
        messages: [
          {
            id: "msg_welcome",
            user_email: "testuser@agent-emulate.dev",
            from: "welcome@agent-emulate.dev",
            to: "testuser@agent-emulate.dev",
            subject: "Welcome to the Gmail emulator",
            body_text: "You can now test Gmail, Calendar, and Drive flows locally.",
            label_ids: ["INBOX", "UNREAD", "CATEGORY_UPDATES"],
            date: "2025-01-04T10:00:00.000Z"
          }
        ],
        calendars: [
          {
            id: "primary",
            user_email: "testuser@agent-emulate.dev",
            summary: "testuser@agent-emulate.dev",
            primary: true,
            selected: true,
            time_zone: "UTC"
          }
        ],
        calendar_events: [
          {
            id: "evt_kickoff",
            user_email: "testuser@agent-emulate.dev",
            calendar_id: "primary",
            summary: "Project Kickoff",
            start_date_time: "2025-01-10T09:00:00.000Z",
            end_date_time: "2025-01-10T09:30:00.000Z"
          }
        ],
        drive_items: [
          {
            id: "drv_docs",
            user_email: "testuser@agent-emulate.dev",
            name: "Docs",
            mime_type: "application/vnd.google-apps.folder",
            parent_ids: ["root"]
          }
        ]
      }
    }
  },
  slack: {
    label: "Slack API emulator",
    endpoints: "auth, chat, conversations, users, reactions, team, OAuth, incoming webhooks",
    async load() {
      const mod = await import("./dist-FX6UF4OV.js");
      return { plugin: mod.slackPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "U000000001", id: 1, scopes: ["chat:write", "channels:read", "users:read", "reactions:write"] };
    },
    initConfig: {
      slack: {
        team: { name: "My Workspace", domain: "my-workspace" },
        users: [{ name: "developer", real_name: "Developer", email: "dev@agent-emulate.dev" }],
        channels: [
          { name: "general", topic: "General discussion" },
          { name: "random", topic: "Random stuff" }
        ],
        bots: [{ name: "my-bot" }],
        oauth_apps: [
          {
            client_id: "12345.67890",
            client_secret: "example_client_secret",
            name: "My Slack App",
            redirect_uris: ["http://localhost:3000/api/auth/callback/slack"]
          }
        ]
      }
    }
  },
  apple: {
    label: "Apple Sign In / OAuth emulator",
    endpoints: "OAuth authorize, token exchange, JWKS",
    async load() {
      const mod = await import("./dist-QF4SNCHE.js");
      return { plugin: mod.applePlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback(cfg) {
      const firstEmail = cfg?.users?.[0]?.email ?? "testuser@agent-emulate.dev";
      return { login: firstEmail, id: 1, scopes: ["openid", "email", "name"] };
    },
    initConfig: {
      apple: {
        users: [{ email: "testuser@agent-emulate.dev", name: "Test User" }],
        oauth_clients: [
          {
            client_id: "com.example.app",
            team_id: "TEAM001",
            name: "My Apple App",
            redirect_uris: ["http://localhost:3000/api/auth/callback/apple"]
          }
        ]
      }
    }
  },
  microsoft: {
    label: "Microsoft Entra ID OAuth 2.0 / OpenID Connect emulator",
    endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, Graph /me, logout, token revocation",
    async load() {
      const mod = await import("./dist-W7ACDXHD.js");
      return {
        plugin: mod.microsoftPlugin,
        seedFromConfig: mod.seedFromConfig,
        storeToSeedConfig: mod.storeToSeedConfig
      };
    },
    defaultFallback(cfg) {
      const firstEmail = cfg?.users?.[0]?.email ?? "testuser@agent-emulate.dev";
      return { login: firstEmail, id: 1, scopes: ["openid", "email", "profile", "User.Read"] };
    },
    initConfig: {
      microsoft: {
        users: [{ email: "testuser@agent-emulate.dev", name: "Test User" }],
        oauth_clients: [
          {
            client_id: "example-client-id",
            client_secret: "example-client-secret",
            name: "My Microsoft App",
            redirect_uris: ["http://localhost:3000/api/auth/callback/microsoft-entra-id"]
          }
        ]
      }
    }
  },
  okta: {
    label: "Okta OAuth 2.0 / OpenID Connect + management API emulator",
    endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo/introspect/revoke/logout, users, groups, apps, authorization servers",
    async load() {
      const mod = await import("./dist-BUC3G72U.js");
      return { plugin: mod.oktaPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback(cfg) {
      const firstLogin = cfg?.users?.[0]?.login ?? cfg?.users?.[0]?.email ?? "testuser@agent-emulate.dev";
      return { login: firstLogin, id: 1, scopes: ["openid", "profile", "email", "groups"] };
    },
    initConfig: {
      okta: {
        users: [
          {
            login: "testuser@agent-emulate.dev",
            email: "testuser@agent-emulate.dev",
            first_name: "Test",
            last_name: "User"
          }
        ],
        groups: [{ name: "Everyone", description: "All users", type: "BUILT_IN", okta_id: "00g_everyone" }],
        authorization_servers: [{ id: "default", name: "default", audiences: ["api://default"] }],
        oauth_clients: [
          {
            client_id: "okta-test-client",
            client_secret: "okta-test-secret",
            name: "Sample OIDC Client",
            redirect_uris: ["http://localhost:3000/callback"],
            auth_server_id: "default"
          }
        ]
      }
    }
  },
  aws: {
    label: "AWS cloud service emulator",
    endpoints: "S3 (buckets, objects), SQS (queues, messages), IAM (users, roles, access keys), STS (assume role, caller identity)",
    async load() {
      const mod = await import("./dist-FZFLJSKS.js");
      return { plugin: mod.awsPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "admin", id: 1, scopes: ["s3:*", "sqs:*", "iam:*", "sts:*"] };
    },
    initConfig: {
      aws: {
        region: "us-east-1",
        s3: { buckets: [{ name: "my-app-bucket" }, { name: "my-app-uploads" }] },
        sqs: { queues: [{ name: "my-app-events" }, { name: "my-app-dlq" }] },
        iam: {
          users: [{ user_name: "developer", create_access_key: true }],
          roles: [{ role_name: "lambda-execution-role", description: "Role for Lambda function execution" }]
        }
      }
    }
  },
  resend: {
    label: "Resend email API emulator",
    endpoints: "emails, domains, contacts, API keys, inbox UI",
    async load() {
      const mod = await import("./dist-LCZCBSMN.js");
      return { plugin: mod.resendPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "re_test_admin", id: 1, scopes: [] };
    },
    initConfig: {
      resend: {
        domains: [{ name: "agent-emulate.dev", region: "us-east-1" }],
        contacts: [{ email: "test@agent-emulate.dev", first_name: "Test", last_name: "User" }]
      }
    }
  },
  stripe: {
    label: "Stripe payments emulator",
    endpoints: "customers, payment methods, customer sessions, payment intents, charges, products, prices, checkout sessions, webhooks",
    async load() {
      const mod = await import("./dist-JUJAT6CZ.js");
      return { plugin: mod.stripePlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "sk_test_admin", id: 1, scopes: [] };
    },
    initConfig: {
      stripe: {
        customers: [{ email: "test@agent-emulate.dev", name: "Test Customer" }],
        products: [{ name: "Pro Plan", description: "Monthly pro subscription" }],
        prices: [{ product_name: "Pro Plan", currency: "usd", unit_amount: 2e3 }]
      }
    }
  },
  mongoatlas: {
    label: "MongoDB Atlas service emulator",
    endpoints: "Atlas Admin API v2 (projects, clusters, database users, databases, collections), Atlas Data API v1 (findOne, find, insertOne, insertMany, updateOne, updateMany, deleteOne, deleteMany, aggregate)",
    async load() {
      const mod = await import("./dist-PWGOAQC6.js");
      return { plugin: mod.mongoatlasPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "admin", id: 1, scopes: [] };
    },
    initConfig: {
      mongoatlas: {
        projects: [{ name: "Project0" }],
        clusters: [{ name: "Cluster0", project: "Project0" }],
        database_users: [{ username: "admin", project: "Project0" }],
        databases: [{ cluster: "Cluster0", name: "test", collections: ["items"] }]
      }
    }
  },
  clerk: {
    label: "Clerk authentication and user management emulator",
    endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo, users, email addresses, organizations, memberships, invitations, sessions",
    async load() {
      const mod = await import("./dist-AMC3DYXT.js");
      return { plugin: mod.clerkPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback(cfg) {
      const firstEmail = cfg?.users?.[0]?.email_addresses?.[0] ?? "test@agent-emulate.dev";
      return { login: firstEmail, id: 1, scopes: [] };
    },
    initConfig: {
      clerk: {
        users: [
          {
            first_name: "Test",
            last_name: "User",
            email_addresses: ["test@agent-emulate.dev"],
            password: "clerk_test_password"
          }
        ],
        organizations: [
          {
            name: "My Company",
            slug: "my-company",
            members: [{ email: "test@agent-emulate.dev", role: "admin" }]
          }
        ],
        oauth_applications: [
          {
            client_id: "clerk_emulate_client",
            client_secret: "clerk_emulate_secret",
            name: "Emulate App",
            redirect_uris: ["http://localhost:3000/api/auth/callback/clerk"]
          }
        ]
      }
    }
  },
  workos: {
    label: "WorkOS User Management emulator",
    endpoints: "OAuth 2.0/OIDC authorize, token exchange (code/password/org-selection/refresh), JWKS, organization memberships, session revocation, invitations, webhook simulation",
    async load() {
      const mod = await import("./dist-TIVSMHZE.js");
      return { plugin: mod.workosPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback(cfg) {
      const firstEmail = cfg?.users?.[0]?.email ?? "test@agent-emulate.dev";
      return { login: firstEmail, id: 1, scopes: [] };
    },
    initConfig: {
      workos: {
        users: [
          {
            id: "user_dev",
            email: "test@agent-emulate.dev",
            first_name: "Test",
            last_name: "User",
            password: "TestPassword123!"
          }
        ],
        organizations: [{ id: "org_dev", name: "My Company", slug: "my-company" }],
        memberships: [{ user_email: "test@agent-emulate.dev", organization_slug: "my-company", role: "owner" }],
        oauth_clients: [
          {
            client_id: "client_emulate_01",
            client_secret: "sk_emulate_secret",
            name: "Emulate App",
            redirect_uris: ["http://localhost:3000/callback"]
          }
        ]
      }
    }
  },
  nango: {
    label: "Nango integration platform emulator",
    endpoints: "connections (get/list/create/metadata), connect sessions (create/reconnect), proxy (QuickBooks /v3/company/:id/query, Xero /api.xro/2.0/*, MYOB, generic)",
    async load() {
      const mod = await import("./dist-JOJDVHTY.js");
      return { plugin: mod.nangoPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "nango-emulator", id: 1, scopes: [] };
    },
    initConfig: {
      nango: {
        connections: [
          {
            id: "xero-demo",
            provider: "xero",
            provider_config_key: "xero",
            metadata: { activeTenantId: "demo-tenant-xero-001" },
            records: {
              Invoice: [
                {
                  id: "INV-001",
                  InvoiceNumber: "INV-001",
                  Total: 1100,
                  AmountDue: 1100,
                  Status: "AUTHORISED",
                  DateString: "2024-01-15",
                  DueDateString: "2024-02-15",
                  Contact: { ContactID: "CONTACT-001", Name: "Demo Customer" }
                }
              ],
              Contact: [{ id: "CONTACT-001", Name: "Demo Customer", EmailAddress: "customer@agent-emulate.dev" }]
            }
          },
          {
            id: "quickbooks-demo",
            provider: "quickbooks",
            provider_config_key: "quickbooks-sandbox",
            connection_config: { realmId: "sandbox-realm-001" },
            records: {
              Invoice: [
                {
                  Id: "1",
                  DocNumber: "1001",
                  TotalAmt: 550,
                  Balance: 550,
                  TxnDate: "2024-01-10",
                  DueDate: "2024-02-10",
                  CustomerRef: { value: "1", name: "Test Customer" }
                }
              ],
              Customer: [
                {
                  Id: "1",
                  DisplayName: "Test Customer",
                  PrimaryEmailAddr: { Address: "customer@agent-emulate.dev" }
                }
              ]
            }
          }
        ]
      }
    }
  },
  simpro: {
    label: "SimPRO field service management API emulator",
    endpoints: "customers, sites, jobs (sections/cost centres), quotes (convert to job), invoices, staff, contractors, schedules, assets, cost centres, labour rates, tax codes, catalogue, statuses, zones, custom fields, webhooks",
    async load() {
      const mod = await import("./dist-N3FVJYEZ.js");
      return { plugin: mod.simproPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "sk_test_simpro", id: 1, scopes: [] };
    },
    initConfig: {
      simpro: {
        customers: [
          {
            type: "Company",
            company_name: "Demo Building Services",
            email: "accounts@agent-emulate.dev",
            mail_suburb: "Melbourne",
            mail_state: "VIC"
          }
        ],
        staff: [
          {
            given_name: "Demo",
            family_name: "Tech",
            email: "tech@agent-emulate.dev",
            role_name: "Technician"
          }
        ],
        jobs: [
          {
            order_no: "J-2025-001",
            description: "Annual Fire Inspection",
            customer_name: "Demo Building Services",
            stage: "Progress",
            total_ex_tax: 500,
            total_inc_tax: 550
          }
        ],
        cost_centers: [{ name: "General" }, { name: "Fire Safety" }],
        tax_codes: [
          { name: "GST", rate: 10, description: "Goods and Services Tax" },
          { name: "GST Free", rate: 0, description: "GST exempt" }
        ],
        labor_rates: [
          { name: "Standard", rate: 95 },
          { name: "After Hours", rate: 142.5 }
        ]
      }
    }
  },
  uptick: {
    label: "Uptick fire protection field service API emulator",
    endpoints: "clients, properties, assets, defects, asset types, users, oauth2 token",
    async load() {
      const mod = await import("./dist-IJ2NCR7X.js");
      return { plugin: mod.uptickPlugin, seedFromConfig: mod.seedFromConfig };
    },
    defaultFallback() {
      return { login: "sk_test_uptick", id: 1, scopes: [] };
    },
    initConfig: {
      uptick: {
        clients: [
          {
            name: "Demo Property Group",
            contact_email: "admin@agent-emulate.dev",
            contact_name: "Demo Admin"
          }
        ],
        properties: [
          {
            name: "Demo Building A",
            client_name: "Demo Property Group",
            address_city: "Melbourne",
            address_state: "VIC"
          }
        ],
        assets: [
          {
            name: "Fire Hose Reel 01",
            property_name: "Demo Building A",
            client_name: "Demo Property Group",
            asset_type_name: "Fire Hose Reel"
          }
        ],
        asset_types: [{ name: "Fire Hose Reel" }, { name: "Fire Extinguisher" }, { name: "Fire Panel" }],
        users: [
          {
            username: "tech1",
            email: "tech@agent-emulate.dev",
            first_name: "Demo",
            last_name: "Tech"
          }
        ]
      }
    }
  }
};

// src/api.ts
import { serve } from "@hono/node-server";

// src/base-url.ts
function resolveBaseUrl(opts) {
  if (opts.seedBaseUrl) {
    return opts.seedBaseUrl.replace(/\{service\}/g, opts.service);
  }
  if (opts.baseUrl) {
    return opts.baseUrl.replace(/\{service\}/g, opts.service);
  }
  const envBaseUrl = process.env.EMULATE_BASE_URL;
  if (envBaseUrl) {
    return envBaseUrl.replace(/\{service\}/g, opts.service);
  }
  const portlessUrl = process.env.PORTLESS_URL;
  if (portlessUrl) {
    return portlessUrl.replace(/\{service\}/g, opts.service);
  }
  return `http://localhost:${opts.port}`;
}

// src/api.ts
async function createEmulator(options) {
  const { service, port = 4e3, seed: seedConfig } = options;
  const entry = SERVICE_REGISTRY[service];
  if (!entry) {
    throw new Error(`Unknown service: ${service}`);
  }
  const loaded = await entry.load();
  const tokens = {};
  if (seedConfig?.tokens) {
    let tokenId = 100;
    for (const [token, user] of Object.entries(seedConfig.tokens)) {
      tokens[token] = { login: user.login, id: tokenId++, scopes: user.scopes };
    }
  } else {
    tokens["test_token_admin"] = { login: "admin", id: 2, scopes: ["repo", "user", "admin:org", "admin:repo_hook"] };
  }
  const svcSeedConfig = seedConfig?.[service];
  const seedBaseUrl = typeof svcSeedConfig?.baseUrl === "string" && svcSeedConfig.baseUrl.length > 0 ? svcSeedConfig.baseUrl : void 0;
  const baseUrl = resolveBaseUrl({ service, port, baseUrl: options.baseUrl, seedBaseUrl });
  let cachedResolver;
  const appKeyResolver = loaded.createAppKeyResolver ? (appId) => cachedResolver(appId) : void 0;
  const fallbackUser = entry.defaultFallback(svcSeedConfig);
  const { app, store, webhooks } = createServer(loaded.plugin, { port, baseUrl, tokens, appKeyResolver, fallbackUser });
  cachedResolver = loaded.createAppKeyResolver?.(store);
  const seed = () => {
    loaded.plugin.seed?.(store, baseUrl);
    if (svcSeedConfig && loaded.seedFromConfig) {
      loaded.seedFromConfig(store, baseUrl, svcSeedConfig, webhooks);
    }
  };
  seed();
  const httpServer = serve({ fetch: app.fetch, port });
  return {
    url: baseUrl,
    reset() {
      store.reset();
      seed();
    },
    close() {
      return new Promise((resolve, reject) => {
        httpServer.close((err) => {
          if (err) reject(err);
          else resolve();
        });
      });
    }
  };
}
export {
  SERVICE_NAMES,
  SERVICE_REGISTRY,
  createEmulator
};
//# sourceMappingURL=api.js.map