import {
  connectionIdentifier
} from "./chunk-7GJMCW76.js";
import "./chunk-7D4SUZUM.js";

// src/migration-spec.ts
import { createHash } from "crypto";

// src/migration-oauth-metadata.ts
import { Schema } from "effect";
var OAuthAuthorizationServerMetadata = Schema.Struct({
  authorization_endpoint: Schema.String
});
var decodeOAuthAuthorizationServerMetadata = Schema.decodeUnknownSync(
  OAuthAuthorizationServerMetadata
);
var OAuthProtectedResourceMetadata = Schema.Struct({
  authorization_servers: Schema.optional(Schema.Array(Schema.String))
});
var decodeOAuthProtectedResourceMetadata = Schema.decodeUnknownSync(
  OAuthProtectedResourceMetadata
);
var DEFAULT_OAUTH_METADATA_TIMEOUT_MS = 2e4;
var migrationOAuthClientPlanKey = (client) => `${client.ownerKeys.tenant}\0${client.ownerKeys.owner}\0${client.ownerKeys.subject}\0${client.slug}`;
var validateMigrationOAuthUrl = (value, label) => {
  const trimmed = value.trim();
  const url = new URL(trimmed);
  const loopbackHttp = url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]");
  if (url.protocol !== "https:" && !loopbackHttp) {
    throw new Error(`${label} must use https: or loopback http: ${trimmed}`);
  }
  return trimmed;
};
var fetchOAuthJson = async (url, fetchImpl, timeoutMs) => {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const response = await fetchImpl(url, {
      headers: { accept: "application/json" },
      signal: controller.signal
    });
    if (!response.ok) {
      throw new Error(`OAuth metadata ${url} returned HTTP ${response.status}`);
    }
    return await response.json();
  } finally {
    clearTimeout(timeout);
  }
};
var fetchOAuthAuthorizationEndpoint = async (metadataUrl, fetchImpl, timeoutMs) => {
  const metadata = decodeOAuthAuthorizationServerMetadata(
    await fetchOAuthJson(metadataUrl, fetchImpl, timeoutMs)
  );
  return validateMigrationOAuthUrl(metadata.authorization_endpoint, "authorization_endpoint");
};
var wellKnownAuthorizationServerUrlFor = (issuer, algorithm) => {
  const url = new URL(validateMigrationOAuthUrl(issuer, "issuer"));
  const origin = `${url.protocol}//${url.host}`;
  const path = url.pathname.replace(/\/+$/, "");
  const suffix = algorithm === "oauth2" ? "oauth-authorization-server" : "openid-configuration";
  return path && path !== "/" ? `${origin}/.well-known/${suffix}${path}` : `${origin}/.well-known/${suffix}`;
};
var resolveAuthorizationEndpointFromIssuer = async (issuer, fetchImpl, timeoutMs) => {
  for (const algorithm of ["oauth2", "oidc"]) {
    try {
      return await fetchOAuthAuthorizationEndpoint(
        wellKnownAuthorizationServerUrlFor(issuer, algorithm),
        fetchImpl,
        timeoutMs
      );
    } catch {
    }
  }
  return null;
};
var protectedResourceMetadataUrlsFor = (resource) => {
  const url = new URL(validateMigrationOAuthUrl(resource, "resource"));
  const origin = `${url.protocol}//${url.host}`;
  const path = url.pathname.replace(/\/+$/, "");
  const urls = [];
  if (path && path !== "/") {
    urls.push(`${origin}/.well-known/oauth-protected-resource${path}`);
  }
  urls.push(`${origin}/.well-known/oauth-protected-resource`);
  return urls;
};
var discoverAuthorizationServersFromResource = async (resource, fetchImpl, timeoutMs) => {
  for (const metadataUrl of protectedResourceMetadataUrlsFor(resource)) {
    try {
      const metadata = decodeOAuthProtectedResourceMetadata(
        await fetchOAuthJson(metadataUrl, fetchImpl, timeoutMs)
      );
      return metadata.authorization_servers ?? [];
    } catch {
    }
  }
  return [];
};
var pushCandidate = (candidates, value) => {
  const trimmed = value?.trim();
  if (!trimmed || candidates.includes(trimmed)) return;
  candidates.push(trimmed);
};
var resolveMigrationOAuthAuthorizationUrl = async (client, fetchImpl, timeoutMs) => {
  const metadataUrl = client.authorizationServerMetadataUrl?.trim();
  if (metadataUrl) {
    try {
      const endpoint = await fetchOAuthAuthorizationEndpoint(
        validateMigrationOAuthUrl(metadataUrl, "authorizationServerMetadataUrl"),
        fetchImpl,
        timeoutMs
      );
      if (endpoint) return endpoint;
    } catch {
    }
  }
  if (client.grant !== "authorization_code") return null;
  const candidates = [];
  if (client.resource?.trim()) {
    try {
      for (const issuer of await discoverAuthorizationServersFromResource(
        client.resource,
        fetchImpl,
        timeoutMs
      )) {
        pushCandidate(candidates, issuer);
      }
    } catch {
    }
  }
  pushCandidate(candidates, client.authorizationUrl);
  for (const candidate of candidates) {
    const endpoint = await resolveAuthorizationEndpointFromIssuer(candidate, fetchImpl, timeoutMs);
    if (endpoint) return endpoint;
  }
  return null;
};
var migrationOAuthClientNeedsAuthorizationUrlResolution = (client) => !!client.authorizationServerMetadataUrl?.trim() || client.grant === "authorization_code" && (!!client.resource?.trim() || !!client.authorizationUrl.trim());
var migrationOAuthClientAuthorizationUrlResolutionSource = (client) => client.authorizationServerMetadataUrl?.trim() || client.resource?.trim() || client.authorizationUrl.trim();
var resolveMigrationOAuthAuthorizationUrls = async (plan, options = {}) => {
  const clientsToResolve = plan.oauthClients.filter(
    migrationOAuthClientNeedsAuthorizationUrlResolution
  );
  if (clientsToResolve.length === 0) return /* @__PURE__ */ new Map();
  const fetchImpl = options.fetch;
  if (!fetchImpl) {
    throw new Error("OAuth metadata resolution requires an injected fetch implementation.");
  }
  const timeoutMs = options.timeoutMs ?? DEFAULT_OAUTH_METADATA_TIMEOUT_MS;
  const endpointByPlanKey = /* @__PURE__ */ new Map();
  const resolved = /* @__PURE__ */ new Map();
  for (const client of clientsToResolve) {
    const key = migrationOAuthClientPlanKey(client);
    let endpoint = endpointByPlanKey.get(key);
    if (!endpoint) {
      endpoint = resolveMigrationOAuthAuthorizationUrl(client, fetchImpl, timeoutMs);
      endpointByPlanKey.set(key, endpoint);
    }
    const authorizationUrl = await endpoint;
    if (authorizationUrl) resolved.set(key, authorizationUrl);
  }
  return resolved;
};
var migrationOAuthAuthorizationUrlFor = (client, resolvedUrls) => resolvedUrls.get(migrationOAuthClientPlanKey(client)) ?? client.authorizationUrl;

// src/migration-spec.ts
var ORG_SUBJECT = "";
var parseScope = (scopeId) => {
  if (scopeId.startsWith("user-org:")) {
    const parts = scopeId.split(":");
    if (parts.length !== 3) return null;
    const [, user, org] = parts;
    if (!user || !org || !user.startsWith("user_") || !org.startsWith("org_")) return null;
    return { owner: "user", subject: user, tenant: org };
  }
  if (scopeId.startsWith("org_") && !scopeId.includes(":")) {
    return { owner: "org", subject: ORG_SUBJECT, tenant: scopeId };
  }
  return null;
};
var ownerPartitionKey = (keys) => keys.owner === "org" ? `org:${keys.tenant}` : `user:${keys.subject}:${keys.tenant}`;
var DEFAULT_VAULT_PREFIX = "executor";
var enc = encodeURIComponent;
var vaultV1ObjectName = (prefix, scopeId, secretId) => `${prefix}/${enc(scopeId)}/secrets/${enc(secretId)}`;
var vaultV1LegacyObjectName = (prefix, scopeId, secretId) => `${prefix}/${scopeId}/secrets/${secretId}`;
var vaultV2ObjectName = (prefix, itemId) => `${prefix}/secrets/${enc(itemId)}`;
var oauthClientDedupKey = (partition, clientId, tokenEndpoint) => `${partition} ${clientId} ${tokenEndpoint}`;
var serializeOAuthScopes = (scopes) => [...new Set(scopes.filter((s) => s.length > 0))].join(" ");
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
var DEFAULT_STATIC_NAMESPACES = ["executor", "openapi"];
var MICROSOFT_GRAPH_LEGACY_SLUG = "microsoft_graph";
var MICROSOFT_GRAPH_CURATED_SLUG = "microsoft_graph_v1_0_sharepoint_files_excel_outlook_combined_curated";
var migratePolicyPattern = (pattern, slugMap, staticNamespaces = DEFAULT_STATIC_NAMESPACES) => {
  if (pattern === "*") return { kind: "ok", pattern: "*" };
  const firstDot = pattern.indexOf(".");
  const slug = firstDot === -1 ? pattern : pattern.slice(0, firstDot);
  if (staticNamespaces.includes(slug)) return { kind: "static", pattern };
  const newSlug = slugMap.get(slug);
  if (newSlug === void 0) return { kind: "dead", slug };
  const rest = firstDot === -1 ? "" : pattern.slice(firstDot + 1);
  if (rest === "") return { kind: "ok", pattern: newSlug };
  if (rest === "*") return { kind: "ok", pattern: `${newSlug}.*` };
  return { kind: "ok", pattern: `${newSlug}.*.*.${rest}` };
};
var GRAPHQL_GREENFIELD_V1_PLUGIN_ID = "graphql-greenfield";
var GRAPHQL_V2_PLUGIN_ID = "graphql";
var OPENAPI_PLUGIN_ID = "openapi";
var MCP_PLUGIN_ID = "mcp";
var OPERATION_COLLECTION = "operation";
var MCP_BINDING_COLLECTION = "binding";
var normalizeRuntimePluginId = (pluginId) => pluginId === GRAPHQL_GREENFIELD_V1_PLUGIN_ID ? GRAPHQL_V2_PLUGIN_ID : pluginId;
var runtimeStorageKey = (scopeId, key) => `${scopeId}\0${key}`;
var fullToolKey = (sourceId, toolName) => `${sourceId}.${toolName}`;
var stripToolPrefix = (sourceId, toolId) => toolId.startsWith(`${sourceId}.`) ? toolId.slice(sourceId.length + 1) : toolId;
var legacyMcpToolBinding = (data) => {
  if (!isRecord(data) || !isRecord(data.binding)) return null;
  const toolName = data.binding.toolName;
  if (typeof toolName !== "string" || toolName.length === 0) return null;
  const annotations = isRecord(data.binding.annotations) ? data.binding.annotations : void 0;
  return {
    toolName,
    ...annotations ? { annotations } : {}
  };
};
var buildV1RuntimeMetadataIndex = (rows) => {
  const mcpBindings = /* @__PURE__ */ new Map();
  for (const row of rows) {
    if (normalizeRuntimePluginId(row.pluginId) !== MCP_PLUGIN_ID) continue;
    if (row.collection !== MCP_BINDING_COLLECTION) continue;
    const binding = legacyMcpToolBinding(row.data);
    if (!binding) continue;
    mcpBindings.set(runtimeStorageKey(row.scopeId, row.key), binding);
  }
  return { mcpBindings };
};
var migrateV1ToolAnnotations = (tool, index) => {
  if (normalizeRuntimePluginId(tool.pluginId) !== MCP_PLUGIN_ID) return tool.annotations;
  if (isRecord(tool.annotations) && isRecord(tool.annotations.mcp)) return tool.annotations;
  const binding = index.mcpBindings.get(
    runtimeStorageKey(tool.scopeId, fullToolKey(tool.sourceId, tool.name))
  );
  if (!binding) return tool.annotations;
  const base = isRecord(tool.annotations) ? tool.annotations : {};
  const destructive = binding.annotations?.destructiveHint === true;
  return {
    ...base,
    requiresApproval: base.requiresApproval ?? destructive,
    ...destructive && base.approvalDescription === void 0 ? { approvalDescription: binding.annotations?.title ?? binding.toolName } : {},
    mcp: {
      toolName: binding.toolName,
      ...binding.annotations ? { upstream: binding.annotations } : {}
    }
  };
};
var migrateOperationStorageRow = (row) => {
  const pluginId = normalizeRuntimePluginId(row.pluginId);
  if (row.collection !== OPERATION_COLLECTION || pluginId !== OPENAPI_PLUGIN_ID && pluginId !== GRAPHQL_V2_PLUGIN_ID) {
    return null;
  }
  if (!isRecord(row.data)) return null;
  const existingIntegration = row.data.integration;
  const existingToolName = row.data.toolName;
  if (typeof existingIntegration === "string" && typeof existingToolName === "string" && "binding" in row.data) {
    return {
      pluginId,
      collection: row.collection,
      key: fullToolKey(existingIntegration, existingToolName),
      data: {
        integration: existingIntegration,
        toolName: existingToolName,
        binding: row.data.binding
      },
      owner: "catalog"
    };
  }
  const sourceId = row.data.sourceId;
  const toolId = row.data.toolId;
  if (typeof sourceId !== "string" || typeof toolId !== "string" || !("binding" in row.data)) {
    return null;
  }
  const toolName = stripToolPrefix(sourceId, toolId);
  return {
    pluginId,
    collection: row.collection,
    key: fullToolKey(sourceId, toolName),
    data: {
      integration: sourceId,
      toolName,
      binding: row.data.binding
    },
    owner: "catalog"
  };
};
var migrateV1PluginStorageRuntimeRow = (row) => {
  const operation = migrateOperationStorageRow(row);
  if (operation) return operation;
  return {
    pluginId: normalizeRuntimePluginId(row.pluginId),
    collection: row.collection,
    key: row.key,
    data: row.data,
    owner: "source"
  };
};
var API_KEY_TEMPLATE_SLUG = "apiKey";
var PRIMARY_INPUT_VARIABLE = "token";
var isCredentialPlacement = (value) => typeof value !== "string";
var slugifyVariable = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
var legacyOAuthSlotSchemeVariants = (securitySchemeName) => {
  const lower = securitySchemeName.toLowerCase();
  const hyphenated = lower.replaceAll("_", "-");
  return [.../* @__PURE__ */ new Set([lower, hyphenated])];
};
var migrateOpenApiAuthTemplate = (config) => {
  const template = [];
  const staticHeaders = {};
  const staticQueryParams = {};
  const slotToTemplateSlug = {};
  const slotToVariable = {};
  const warnings = [];
  const placements = [];
  for (const [name, value] of Object.entries(config.headers ?? {})) {
    if (isCredentialPlacement(value)) {
      placements.push({
        carrier: "header",
        name,
        prefix: value.prefix,
        slot: value.kind === "binding" ? value.slot : void 0
      });
    } else {
      staticHeaders[name] = value;
    }
  }
  for (const [name, value] of Object.entries(config.queryParams ?? {})) {
    if (isCredentialPlacement(value)) {
      placements.push({
        carrier: "query",
        name,
        prefix: value.prefix,
        slot: value.kind === "binding" ? value.slot : void 0
      });
    } else {
      staticQueryParams[name] = value;
    }
  }
  const taken = /* @__PURE__ */ new Set();
  const variableFor = (placement) => {
    if (placements.length <= 1) return PRIMARY_INPUT_VARIABLE;
    const base = slugifyVariable(placement.name) || "input";
    let candidate = base;
    let n = 2;
    while (taken.has(candidate)) candidate = `${base}_${n++}`;
    taken.add(candidate);
    return candidate;
  };
  const apiKeyHeaders = {};
  const apiKeyQueryParams = {};
  for (const placement of placements) {
    const variable = variableFor(placement);
    const part = { type: "variable", name: variable };
    const parts = placement.prefix && placement.prefix.length > 0 ? [placement.prefix, part] : [part];
    if (placement.carrier === "header") apiKeyHeaders[placement.name] = parts;
    else apiKeyQueryParams[placement.name] = parts;
    if (placement.slot) {
      slotToTemplateSlug[placement.slot] = API_KEY_TEMPLATE_SLUG;
      slotToVariable[placement.slot] = variable;
    }
  }
  if (placements.length > 0) {
    template.push({
      slug: API_KEY_TEMPLATE_SLUG,
      type: "apiKey",
      ...Object.keys(apiKeyHeaders).length > 0 ? { headers: apiKeyHeaders } : {},
      ...Object.keys(apiKeyQueryParams).length > 0 ? { queryParams: apiKeyQueryParams } : {}
    });
  }
  if (config.oauth2) {
    const o = config.oauth2;
    template.push({
      slug: o.securitySchemeName,
      type: "oauth",
      authorizationUrl: o.authorizationUrl ?? "",
      tokenUrl: o.tokenUrl,
      scopes: o.scopes ?? []
    });
    for (const scheme of legacyOAuthSlotSchemeVariants(o.securitySchemeName)) {
      const slot = `oauth2:${scheme}:connection`;
      slotToTemplateSlug[slot] = o.securitySchemeName;
      slotToVariable[slot] = PRIMARY_INPUT_VARIABLE;
    }
  }
  return {
    authenticationTemplate: template,
    staticHeaders,
    staticQueryParams,
    slotToTemplateSlug,
    slotToVariable,
    warnings
  };
};
var migrateGrant = (kind) => kind === "client-credentials" ? "client_credentials" : "authorization_code";
var SYNTHETIC_CLIENT_CREDENTIALS_TTL_MS = 60 * 60 * 1e3;
var migrateExpiresAt = (input) => input.grant === "client_credentials" && input.v1ExpiresAt == null ? input.nowMs + SYNTHETIC_CLIENT_CREDENTIALS_TTL_MS : input.v1ExpiresAt;
var migrateOpenApiSourceConfig = (v1) => {
  const auth = migrateOpenApiAuthTemplate(v1);
  const config = {
    ...v1.spec !== void 0 ? { spec: v1.spec } : {},
    ...v1.sourceUrl !== void 0 ? { sourceUrl: v1.sourceUrl } : {},
    ...v1.baseUrl !== void 0 ? { baseUrl: v1.baseUrl } : {},
    ...v1.googleDiscoveryUrls !== void 0 ? { googleDiscoveryUrls: v1.googleDiscoveryUrls } : {},
    ...Object.keys(auth.staticHeaders).length > 0 ? { headers: auth.staticHeaders } : {},
    ...Object.keys(auth.staticQueryParams).length > 0 ? { queryParams: auth.staticQueryParams } : {},
    ...auth.authenticationTemplate.length > 0 ? { authenticationTemplate: auth.authenticationTemplate } : {}
  };
  return {
    config,
    slotToTemplateSlug: auth.slotToTemplateSlug,
    slotToVariable: auth.slotToVariable,
    warnings: auth.warnings
  };
};
var migrateSourceAuth = (auth) => auth?.kind === "oauth2" ? { kind: "oauth2" } : { kind: "none" };
var classifyBindingSlot = (slotKey) => {
  if (slotKey.includes("client-secret")) return "client-secret";
  if (slotKey.includes("client-id")) return "client-id";
  if (slotKey.startsWith("header:") || slotKey.startsWith("query_param:") || slotKey.startsWith("spec_fetch_header:")) {
    return "apikey";
  }
  if (slotKey.endsWith(":connection")) return "oauth-access";
  return "apikey";
};
var isOAuthClientCredentialSlot = (slotKey) => slotKey.startsWith("oauth2:") && (slotKey.endsWith(":client-id") || slotKey.endsWith(":client-secret"));
var hostSlug = (url) => {
  const match = /^[a-z]+:\/\/([^/:]+)/i.exec(url);
  const host = match?.[1] ?? "client";
  const label = host.split(".").length > 1 ? host.split(".").slice(-2, -1)[0] : host;
  return slugifyVariable(label ?? "client") || "client";
};
var dedupeOAuthClients = (inputs) => {
  const slugByDedupKey = {};
  const clients = [];
  const takenByPartition = /* @__PURE__ */ new Map();
  for (const input of inputs) {
    const partition = ownerPartitionKey(input.ownerKeys);
    const key = oauthClientPlanDedupKey(input);
    if (slugByDedupKey[key]) continue;
    const taken = takenByPartition.get(partition) ?? /* @__PURE__ */ new Set();
    const base = hostSlug(input.tokenUrl);
    let slug = base;
    let n = 2;
    while (taken.has(slug)) slug = `${base}_${n++}`;
    taken.add(slug);
    takenByPartition.set(partition, taken);
    slugByDedupKey[key] = slug;
    clients.push({ ...input, slug });
  }
  return { clients, slugByDedupKey };
};
var secretReadRefKey = (ref) => `${ref.provider}\0${ref.scopeId}\0${ref.secretId}`;
var oauthClientDedupeIdentity = (input) => input.clientId.length > 0 ? `literal:${input.clientId}` : input.clientIdSecretRef ? `secret:${secretReadRefKey(input.clientIdSecretRef)}` : "missing:";
var oauthClientPlanDedupKey = (input) => oauthClientDedupKey(
  ownerPartitionKey(input.ownerKeys),
  oauthClientDedupeIdentity(input),
  input.tokenUrl
);
var oauthClientSlugKey = (input) => oauthClientPlanDedupKey(input);
var planIntegrationRow = (input) => {
  const keys = (input.ownerForScope ?? parseScope)(input.scopeId);
  if (!keys) return null;
  return {
    tenant: keys.tenant,
    owner: keys.owner,
    subject: keys.subject,
    slug: input.sourceId,
    plugin_id: input.pluginId,
    description: input.description,
    config: input.config
  };
};
var planConnectionRow = (input) => {
  const keys = (input.ownerForScope ?? parseScope)(input.scopeId);
  if (!keys) return null;
  return {
    tenant: keys.tenant,
    owner: keys.owner,
    subject: keys.subject,
    integration: input.integration,
    name: input.name,
    template: input.template,
    provider: input.provider,
    identityLabel: input.identityLabel,
    oauthClientSlug: input.oauthClientSlug,
    oauthClientOwner: input.oauthClientOwner ? input.oauthClientOwner.owner : null,
    oauthScope: input.oauthScopes.length > 0 ? serializeOAuthScopes(input.oauthScopes) : null,
    expiresAt: migrateExpiresAt({
      grant: input.grant,
      v1ExpiresAt: input.v1ExpiresAt,
      nowMs: input.nowMs
    })
  };
};
var stableMigrationHash = (...parts) => {
  const hash = createHash("sha256");
  for (const part of parts) hash.update(part).update("\0");
  return hash.digest("base64url");
};
var migratedItemId = (scopeId, secretId) => `secret_${stableMigrationHash(scopeId, secretId)}`;
var fallbackPolicyId = (scopeId, pattern, action) => `policy_${stableMigrationHash(scopeId, pattern, action)}`;
var OAUTH_TEMPLATE_SLUG = "oauth2";
var withSourceAuthOauth = (auth, slotToTemplateSlug, slotToVariable) => {
  if (auth?.kind === "oauth2" && auth.connectionSlot) {
    slotToTemplateSlug[auth.connectionSlot] = OAUTH_TEMPLATE_SLUG;
    slotToVariable[auth.connectionSlot] = PRIMARY_INPUT_VARIABLE;
  }
};
var migrateMcpSourceConfig = (v1) => {
  const apikey = migrateOpenApiAuthTemplate({ headers: v1.headers, queryParams: v1.queryParams });
  const slotToTemplateSlug = { ...apikey.slotToTemplateSlug };
  const slotToVariable = { ...apikey.slotToVariable };
  withSourceAuthOauth(v1.auth, slotToTemplateSlug, slotToVariable);
  const config = {
    ...v1.endpoint !== void 0 ? { endpoint: v1.endpoint } : {},
    ...v1.transport !== void 0 ? { transport: v1.transport } : {},
    ...v1.remoteTransport !== void 0 ? { remoteTransport: v1.remoteTransport } : {},
    ...Object.keys(apikey.staticHeaders).length > 0 ? { headers: apikey.staticHeaders } : {},
    ...Object.keys(apikey.staticQueryParams).length > 0 ? { queryParams: apikey.staticQueryParams } : {},
    auth: migrateSourceAuth(v1.auth),
    ...apikey.authenticationTemplate.length > 0 ? { authenticationTemplate: apikey.authenticationTemplate } : {}
  };
  return { config, slotToTemplateSlug, slotToVariable, warnings: apikey.warnings };
};
var migrateGraphqlSourceConfig = (v1) => {
  const apikey = migrateOpenApiAuthTemplate({ headers: v1.headers, queryParams: v1.queryParams });
  const slotToTemplateSlug = { ...apikey.slotToTemplateSlug };
  const slotToVariable = { ...apikey.slotToVariable };
  withSourceAuthOauth(v1.auth, slotToTemplateSlug, slotToVariable);
  const config = {
    ...v1.endpoint !== void 0 ? { endpoint: v1.endpoint } : {},
    ...v1.name !== void 0 ? { name: v1.name } : {},
    ...Object.keys(apikey.staticHeaders).length > 0 ? { headers: apikey.staticHeaders } : {},
    ...Object.keys(apikey.staticQueryParams).length > 0 ? { queryParams: apikey.staticQueryParams } : {},
    auth: migrateSourceAuth(v1.auth),
    ...apikey.authenticationTemplate.length > 0 ? { authenticationTemplate: apikey.authenticationTemplate } : {}
  };
  return { config, slotToTemplateSlug, slotToVariable, warnings: apikey.warnings };
};
var migrationSourceKey = (scopeId, sourceId) => `${scopeId} ${sourceId}`;
var sourceKey = migrationSourceKey;
var bindingSourceScope = (binding) => binding.sourceScopeId ?? binding.scopeId;
var secretExists = (secrets, scopeId, secretId) => secrets.some((secret) => secret.scopeId === scopeId && secret.id === secretId);
var resolveProviderStateSecretScope = (secrets, options) => {
  if (options.explicitScopeId) return options.explicitScopeId;
  if (secretExists(secrets, options.sourceScopeId, options.secretId)) return options.sourceScopeId;
  return options.connectionScopeId;
};
var bindingSecretScope = (binding) => binding.secretScopeId ?? binding.scopeId;
var slugifyName = (name, fallback = "account") => String(connectionIdentifier(name, fallback));
var secretRefKey = (scopeId, secretId) => `${scopeId}\0${secretId}`;
var staticConnectionNameForSecretBindings = (bindings, secrets) => {
  const refs = /* @__PURE__ */ new Map();
  for (const binding of bindings) {
    if (!binding.secretId) continue;
    const scopeId = bindingSecretScope(binding);
    refs.set(secretRefKey(scopeId, binding.secretId), { scopeId, secretId: binding.secretId });
  }
  if (refs.size !== 1) return "api-key";
  const [ref] = refs.values();
  if (!ref) return "api-key";
  const secret = secrets.find((s) => s.scopeId === ref.scopeId && s.id === ref.secretId);
  const nameSlug = secret?.name.trim() ? slugifyName(secret.name, "") : "";
  return nameSlug || slugifyName(ref.secretId, "api-key");
};
var scopesFromProviderState = (ps) => {
  if (!ps) return [];
  if (ps.scopes && ps.scopes.length > 0) return ps.scopes;
  if (ps.scope) return ps.scope.split(/\s+/).filter((s) => s.length > 0);
  return [];
};
var nonEmptyString = (value) => {
  const trimmed = value?.trim();
  return trimmed ? trimmed : null;
};
var isBareOrigin = (url) => /^https?:\/\/[^/]+\/?$/.test(url);
var authorizationUrlFromProviderState = (ps, grant) => {
  const explicit = nonEmptyString(ps?.authorizationEndpoint) ?? nonEmptyString(ps?.authorizationServerMetadata?.authorization_endpoint);
  if (explicit) return explicit;
  const fallback = nonEmptyString(ps?.authorizationServerUrl) ?? nonEmptyString(ps?.issuerUrl) ?? "";
  if (grant === "client_credentials") return fallback;
  const token = nonEmptyString(ps?.tokenEndpoint);
  if ((fallback === "" || isBareOrigin(fallback)) && token?.endsWith("/token") && (fallback === "" || token.startsWith(fallback.replace(/\/$/, "") + "/"))) {
    return token.replace(/\/token$/, "/authorize");
  }
  return fallback;
};
var secretOpDedupeKey = (op) => `${op.targetProvider}\0${op.owner.tenant}\0${op.owner.owner}\0${op.owner.subject}\0${op.itemId}`;
var planMigration = (input) => {
  const warnings = [];
  const secretOps = [];
  const connections = [];
  const ownerForScope = input.ownerForScope ?? parseScope;
  const defaultWritableProvider = input.defaultWritableProvider ?? "workos-vault";
  const integrations = [];
  const slugMap = /* @__PURE__ */ new Map();
  const sourceIdsByTenant = /* @__PURE__ */ new Map();
  const addSourceIdForOwner = (owner, sourceId) => {
    if (!owner) return;
    const set = sourceIdsByTenant.get(owner.tenant) ?? /* @__PURE__ */ new Set();
    set.add(sourceId);
    sourceIdsByTenant.set(owner.tenant, set);
  };
  for (const id of input.toolSourceIds) slugMap.set(id, id);
  for (const source of input.sources) {
    slugMap.set(source.id, source.id);
    addSourceIdForOwner(ownerForScope(source.scopeId), source.id);
    const migrated = input.migratedConfigs.get(sourceKey(source.scopeId, source.id));
    const row = planIntegrationRow({
      scopeId: source.scopeId,
      sourceId: source.id,
      pluginId: source.pluginId,
      description: source.name,
      config: migrated?.config ?? {},
      ownerForScope
    });
    if (!row) {
      warnings.push(`Skipped source "${source.id}": unparseable scope "${source.scopeId}".`);
      continue;
    }
    integrations.push(row);
    for (const w of migrated?.warnings ?? []) warnings.push(`[${source.id}] ${w}`);
  }
  const policySlugMapForOwner = (owner) => {
    const tenantSourceIds = sourceIdsByTenant.get(owner.tenant);
    if (tenantSourceIds?.has(MICROSOFT_GRAPH_CURATED_SLUG) && !tenantSourceIds.has(MICROSOFT_GRAPH_LEGACY_SLUG)) {
      return new Map(slugMap).set(MICROSOFT_GRAPH_LEGACY_SLUG, MICROSOFT_GRAPH_CURATED_SLUG);
    }
    return slugMap;
  };
  const groups = /* @__PURE__ */ new Map();
  for (const b of input.bindings) {
    const key = sourceKey(b.scopeId, b.sourceId);
    const list = groups.get(key) ?? [];
    list.push(b);
    groups.set(key, list);
  }
  const connectionById = /* @__PURE__ */ new Map();
  for (const c of input.connections) connectionById.set(`${c.scopeId} ${c.id}`, c);
  const consumed = /* @__PURE__ */ new Set();
  const consume = (scopeId, secretId) => {
    consumed.add(`${scopeId} ${secretId}`);
  };
  const boundConnections = /* @__PURE__ */ new Set();
  const oauthClientInputs = [];
  const clientSecretItemIdByKey = /* @__PURE__ */ new Map();
  const pendingClientSlug = [];
  for (const [key, bindings] of groups) {
    const [scopeId, sourceId] = key.split(" ");
    if (!scopeId || !sourceId) continue;
    const sourceScopeId = bindings[0] ? bindingSourceScope(bindings[0]) : scopeId;
    const owner = ownerForScope(scopeId);
    if (!owner) {
      warnings.push(`Skipped binding group "${sourceId}": unparseable scope "${scopeId}".`);
      continue;
    }
    const config = input.migratedConfigs.get(sourceKey(sourceScopeId, sourceId));
    const slotTemplate = (slot) => config?.slotToTemplateSlug[slot] ?? API_KEY_TEMPLATE_SLUG;
    const slotVar = (slot) => config?.slotToVariable[slot] ?? PRIMARY_INPUT_VARIABLE;
    const connBinding = bindings.find((b) => b.kind === "connection");
    const secretBindings = bindings.filter(
      (b) => b.kind === "secret" && !isOAuthClientCredentialSlot(b.slotKey)
    );
    const textBindings = bindings.filter((b) => b.kind === "text");
    if (connBinding && connBinding.connectionId) {
      const conn = connectionById.get(`${scopeId} ${connBinding.connectionId}`);
      if (!conn) {
        warnings.push(
          `Connection "${connBinding.connectionId}" referenced by a binding is missing.`
        );
        continue;
      }
      boundConnections.add(`${conn.scopeId} ${conn.id}`);
      const ps = conn.providerState;
      const grant = migrateGrant(ps?.kind ?? "authorization-code");
      const oauthTargetProvider = conn.provider === "oauth2" ? defaultWritableProvider : conn.provider;
      const itemIds = {};
      let refreshItemId = null;
      if (conn.accessTokenSecretId) {
        const itemId = migratedItemId(scopeId, conn.accessTokenSecretId);
        const provider = providerForSecret(input.secrets, scopeId, conn.accessTokenSecretId);
        secretOps.push({
          itemId,
          role: "oauth-access",
          owner,
          targetProvider: oauthTargetProvider,
          fromSecret: { scopeId, secretId: conn.accessTokenSecretId, provider }
        });
        itemIds[PRIMARY_INPUT_VARIABLE] = itemId;
        consume(scopeId, conn.accessTokenSecretId);
      }
      if (conn.refreshTokenSecretId) {
        refreshItemId = migratedItemId(scopeId, conn.refreshTokenSecretId);
        const provider = providerForSecret(input.secrets, scopeId, conn.refreshTokenSecretId);
        secretOps.push({
          itemId: refreshItemId,
          role: "oauth-refresh",
          owner,
          targetProvider: oauthTargetProvider,
          fromSecret: { scopeId, secretId: conn.refreshTokenSecretId, provider }
        });
        consume(scopeId, conn.refreshTokenSecretId);
      }
      let clientSecretItemId = null;
      if (ps?.clientSecretSecretId) {
        const clientSecretScopeId = resolveProviderStateSecretScope(input.secrets, {
          explicitScopeId: ps.clientSecretSecretScopeId,
          connectionScopeId: scopeId,
          sourceScopeId,
          secretId: ps.clientSecretSecretId
        });
        clientSecretItemId = migratedItemId(clientSecretScopeId, ps.clientSecretSecretId);
        const provider = providerForSecret(
          input.secrets,
          clientSecretScopeId,
          ps.clientSecretSecretId
        );
        secretOps.push({
          itemId: clientSecretItemId,
          role: "client-secret",
          owner,
          targetProvider: oauthTargetProvider,
          fromSecret: { scopeId: clientSecretScopeId, secretId: ps.clientSecretSecretId, provider }
        });
        consume(clientSecretScopeId, ps.clientSecretSecretId);
      }
      const clientIdSecretRef = ps?.clientIdSecretId != null ? (() => {
        const clientIdScopeId = resolveProviderStateSecretScope(input.secrets, {
          explicitScopeId: ps.clientIdSecretScopeId,
          connectionScopeId: scopeId,
          sourceScopeId,
          secretId: ps.clientIdSecretId
        });
        return {
          scopeId: clientIdScopeId,
          secretId: ps.clientIdSecretId,
          provider: providerForSecret(input.secrets, clientIdScopeId, ps.clientIdSecretId)
        };
      })() : null;
      if (clientIdSecretRef) consume(clientIdSecretRef.scopeId, clientIdSecretRef.secretId);
      if (!ps?.clientId && !clientIdSecretRef) {
        warnings.push(
          `OAuth connection "${conn.id}" has no client id or client-id secret reference; migrated oauth_client will need repair.`
        );
      }
      const clientInput = {
        ownerKeys: owner,
        clientId: ps?.clientId ?? "",
        clientIdSecretRef,
        tokenUrl: ps?.tokenEndpoint ?? "",
        authorizationUrl: authorizationUrlFromProviderState(ps, grant),
        authorizationServerMetadataUrl: nonEmptyString(ps?.authorizationServerMetadataUrl),
        grant,
        resource: input.oauthResourceOverrides?.get(sourceKey(sourceScopeId, sourceId)) ?? ps?.resource ?? null,
        clientSecretRef: ps?.clientSecretSecretId ?? null
      };
      oauthClientInputs.push(clientInput);
      const dedupKey = oauthClientSlugKey({
        ownerKeys: owner,
        clientId: clientInput.clientId,
        clientIdSecretRef: clientInput.clientIdSecretRef,
        tokenUrl: clientInput.tokenUrl
      });
      clientSecretItemIdByKey.set(dedupKey, clientSecretItemId);
      const row = planConnectionRow({
        scopeId,
        integration: sourceId,
        name: slugifyName(conn.identityLabel ?? "account"),
        template: slotTemplate(connBinding.slotKey),
        provider: oauthTargetProvider,
        identityLabel: conn.identityLabel,
        grant,
        v1ExpiresAt: conn.expiresAt,
        oauthScopes: scopesFromProviderState(ps),
        oauthClientSlug: null,
        // wired after dedup
        oauthClientOwner: owner,
        nowMs: input.nowMs,
        ownerForScope
      });
      if (!row) continue;
      const index = connections.push({
        credentialScopeId: scopeId,
        sourceScopeId,
        sourceId,
        row,
        itemIds,
        refreshItemId
      }) - 1;
      pendingClientSlug.push({ index, key: dedupKey });
    } else if (secretBindings.length > 0) {
      const itemIds = {};
      let template = API_KEY_TEMPLATE_SLUG;
      const providers = /* @__PURE__ */ new Set();
      let targetProvider = null;
      const connectionName = staticConnectionNameForSecretBindings(secretBindings, input.secrets);
      for (const b of secretBindings) {
        if (!b.secretId) continue;
        const secretScopeId = bindingSecretScope(b);
        const variable = slotVar(b.slotKey);
        const itemId = migratedItemId(secretScopeId, b.secretId);
        const provider2 = providerForSecret(input.secrets, secretScopeId, b.secretId);
        targetProvider ??= provider2;
        providers.add(provider2);
        secretOps.push({
          itemId,
          role: "apikey",
          owner,
          targetProvider,
          fromSecret: { scopeId: secretScopeId, secretId: b.secretId, provider: provider2 }
        });
        itemIds[variable] = itemId;
        template = slotTemplate(b.slotKey);
        consume(secretScopeId, b.secretId);
      }
      if (Object.keys(itemIds).length === 0) {
        warnings.push(
          `Skipped binding group "${sourceId}" in scope "${scopeId}": its secret bindings reference no secrets.`
        );
        continue;
      }
      const provider = targetProvider ?? defaultWritableProvider;
      if (providers.size > 1) {
        warnings.push(
          `Connection "${sourceId}" in scope "${scopeId}" uses multiple secret providers; v2 supports one provider per connection and will use "${provider}".`
        );
      }
      const row = planConnectionRow({
        scopeId,
        integration: sourceId,
        name: connectionName,
        template,
        provider,
        identityLabel: null,
        grant: "authorization_code",
        v1ExpiresAt: null,
        oauthScopes: [],
        oauthClientSlug: null,
        oauthClientOwner: null,
        nowMs: input.nowMs,
        ownerForScope
      });
      if (row) {
        connections.push({
          credentialScopeId: scopeId,
          sourceScopeId,
          sourceId,
          row,
          itemIds,
          refreshItemId: null
        });
      }
    } else if (textBindings.length > 0) {
      const itemIds = {};
      let template = API_KEY_TEMPLATE_SLUG;
      for (const b of textBindings) {
        const variable = slotVar(b.slotKey);
        const itemId = migratedItemId(scopeId, `text:${sourceId}:${b.slotKey}`);
        secretOps.push({
          itemId,
          role: "apikey",
          owner,
          targetProvider: defaultWritableProvider,
          fromText: b.textValue ?? ""
        });
        itemIds[variable] = itemId;
        template = slotTemplate(b.slotKey);
      }
      const row = planConnectionRow({
        scopeId,
        integration: sourceId,
        name: "inline",
        template,
        provider: defaultWritableProvider,
        identityLabel: null,
        grant: "authorization_code",
        v1ExpiresAt: null,
        oauthScopes: [],
        oauthClientSlug: null,
        oauthClientOwner: null,
        nowMs: input.nowMs,
        ownerForScope
      });
      if (row) {
        connections.push({
          credentialScopeId: scopeId,
          sourceScopeId,
          sourceId,
          row,
          itemIds,
          refreshItemId: null
        });
      }
    } else {
      warnings.push(
        `Skipped binding group "${sourceId}" in scope "${scopeId}": no migratable credential binding (its secrets fall to the orphan re-key).`
      );
    }
  }
  for (const source of input.sources) {
    if (groups.has(sourceKey(source.scopeId, source.id))) continue;
    const migrated = input.migratedConfigs.get(sourceKey(source.scopeId, source.id));
    const auth = migrated?.config?.auth;
    if (auth?.kind !== "none") continue;
    const row = planConnectionRow({
      scopeId: source.scopeId,
      integration: source.id,
      name: "workspace",
      template: "none",
      provider: defaultWritableProvider,
      identityLabel: null,
      grant: "authorization_code",
      v1ExpiresAt: null,
      oauthScopes: [],
      oauthClientSlug: null,
      oauthClientOwner: null,
      nowMs: input.nowMs,
      ownerForScope
    });
    if (row) {
      connections.push({
        credentialScopeId: source.scopeId,
        sourceScopeId: source.scopeId,
        sourceId: source.id,
        row,
        itemIds: {},
        refreshItemId: null
      });
    }
  }
  const dedup = dedupeOAuthClients(oauthClientInputs);
  const oauthClients = dedup.clients.map((c) => {
    const dedupKey = oauthClientSlugKey({
      ownerKeys: c.ownerKeys,
      clientId: c.clientId,
      clientIdSecretRef: c.clientIdSecretRef,
      tokenUrl: c.tokenUrl
    });
    return { ...c, clientSecretItemId: clientSecretItemIdByKey.get(dedupKey) ?? null };
  });
  for (const { index, key } of pendingClientSlug) {
    const slug = dedup.slugByDedupKey[key] ?? null;
    const existing = connections[index];
    if (existing) {
      connections[index] = { ...existing, row: { ...existing.row, oauthClientSlug: slug } };
    }
  }
  for (const s of input.secrets) {
    if (consumed.has(`${s.scopeId} ${s.id}`)) continue;
    const owner = ownerForScope(s.scopeId);
    if (!owner) {
      warnings.push(`Skipped orphan secret "${s.id}": unparseable scope "${s.scopeId}".`);
      continue;
    }
    secretOps.push({
      itemId: migratedItemId(s.scopeId, s.id),
      role: "orphan",
      owner,
      targetProvider: s.provider,
      fromSecret: { scopeId: s.scopeId, secretId: s.id, provider: s.provider }
    });
  }
  const policies = [];
  let ok = 0;
  let staticN = 0;
  let deadInert = 0;
  for (const p of input.policies) {
    const owner = ownerForScope(p.scopeId);
    if (!owner) {
      warnings.push(`Skipped policy "${p.pattern}": unparseable scope "${p.scopeId}".`);
      continue;
    }
    const result = migratePolicyPattern(p.pattern, policySlugMapForOwner(owner));
    if (result.kind === "dead") {
      deadInert += 1;
      warnings.push(
        `Dead-source policy kept inert: "${p.pattern}" (slug "${result.slug}" removed).`
      );
      policies.push({
        owner,
        id: p.id ?? fallbackPolicyId(p.scopeId, p.pattern, p.action),
        pattern: p.pattern,
        action: p.action,
        position: p.position ?? String(policies.length).padStart(6, "0"),
        status: "dead-inert"
      });
    } else {
      if (result.kind === "static") staticN += 1;
      else ok += 1;
      policies.push({
        owner,
        id: p.id ?? fallbackPolicyId(p.scopeId, p.pattern, p.action),
        pattern: result.pattern,
        action: p.action,
        position: p.position ?? String(policies.length).padStart(6, "0"),
        status: result.kind
      });
    }
  }
  const staleConnections = input.connections.filter(
    (c) => !boundConnections.has(`${c.scopeId} ${c.id}`)
  ).length;
  if (staleConnections > 0) {
    warnings.push(
      `${staleConnections} unbound v1 connection row(s) were not migrated (no source binding); their tokens migrate as orphan secrets.`
    );
  }
  const dedupedSecretOps = [...new Map(secretOps.map((o) => [secretOpDedupeKey(o), o])).values()];
  return {
    integrations,
    oauthClients,
    connections,
    secretOps: dedupedSecretOps,
    policies,
    report: {
      integrations: integrations.length,
      connections: connections.length,
      oauthClients: oauthClients.length,
      secretOps: dedupedSecretOps.length,
      staleConnections,
      policies: { ok, static: staticN, deadInert },
      warnings
    }
  };
};
var providerForSecret = (secrets, scopeId, secretId) => secrets.find((s) => s.scopeId === scopeId && s.id === secretId)?.provider ?? "workos-vault";
export {
  API_KEY_TEMPLATE_SLUG,
  DEFAULT_STATIC_NAMESPACES,
  DEFAULT_VAULT_PREFIX,
  OAUTH_TEMPLATE_SLUG,
  PRIMARY_INPUT_VARIABLE,
  SYNTHETIC_CLIENT_CREDENTIALS_TTL_MS,
  buildV1RuntimeMetadataIndex,
  classifyBindingSlot,
  dedupeOAuthClients,
  migrateExpiresAt,
  migrateGrant,
  migrateGraphqlSourceConfig,
  migrateMcpSourceConfig,
  migrateOpenApiAuthTemplate,
  migrateOpenApiSourceConfig,
  migratePolicyPattern,
  migrateSourceAuth,
  migrateV1PluginStorageRuntimeRow,
  migrateV1ToolAnnotations,
  migratedItemId,
  migrationOAuthAuthorizationUrlFor,
  migrationOAuthClientAuthorizationUrlResolutionSource,
  migrationOAuthClientNeedsAuthorizationUrlResolution,
  migrationOAuthClientPlanKey,
  migrationSourceKey,
  oauthClientDedupKey,
  oauthClientSlugKey,
  ownerPartitionKey,
  parseScope,
  planConnectionRow,
  planIntegrationRow,
  planMigration,
  resolveMigrationOAuthAuthorizationUrls,
  serializeOAuthScopes,
  vaultV1LegacyObjectName,
  vaultV1ObjectName,
  vaultV2ObjectName
};
//# sourceMappingURL=migration-spec.js.map