Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });

const breadcrumbs = require('../breadcrumbs.js');
const currentScopes = require('../currentScopes.js');
const debugBuild = require('../debug-build.js');
const exports$1 = require('../exports.js');
const integration = require('../integration.js');
const semanticAttributes = require('../semanticAttributes.js');
const debugLogger = require('../utils/debug-logger.js');
const is = require('../utils/is.js');
const misc = require('../utils/misc.js');
const spanstatus = require('../tracing/spanstatus.js');
const trace = require('../tracing/trace.js');

const AUTH_OPERATIONS_TO_INSTRUMENT = [
  "reauthenticate",
  "signInAnonymously",
  "signInWithOAuth",
  "signInWithIdToken",
  "signInWithOtp",
  "signInWithPassword",
  "signInWithSSO",
  "signOut",
  "signUp",
  "verifyOtp"
];
const AUTH_ADMIN_OPERATIONS_TO_INSTRUMENT = [
  "createUser",
  "deleteUser",
  "listUsers",
  "getUserById",
  "updateUserById",
  "inviteUserByEmail"
];
const FILTER_MAPPINGS = {
  eq: "eq",
  neq: "neq",
  gt: "gt",
  gte: "gte",
  lt: "lt",
  lte: "lte",
  like: "like",
  "like(all)": "likeAllOf",
  "like(any)": "likeAnyOf",
  ilike: "ilike",
  "ilike(all)": "ilikeAllOf",
  "ilike(any)": "ilikeAnyOf",
  is: "is",
  in: "in",
  cs: "contains",
  cd: "containedBy",
  sr: "rangeGt",
  nxl: "rangeGte",
  sl: "rangeLt",
  nxr: "rangeLte",
  adj: "rangeAdjacent",
  ov: "overlaps",
  fts: "",
  plfts: "plain",
  phfts: "phrase",
  wfts: "websearch",
  not: "not"
};
const DB_OPERATIONS_TO_INSTRUMENT = ["select", "insert", "upsert", "update", "delete"];
function markAsInstrumented(fn) {
  try {
    fn.__SENTRY_INSTRUMENTED__ = true;
  } catch {
  }
}
function isInstrumented(fn) {
  try {
    return fn.__SENTRY_INSTRUMENTED__;
  } catch {
    return false;
  }
}
function getMutationBodyPayloadForTelemetry(rawBody, plainBody) {
  if (Object.keys(plainBody).length > 0) {
    return plainBody;
  }
  if (Array.isArray(rawBody) && rawBody.length > 0) {
    return rawBody;
  }
  return void 0;
}
function hasMutationBodyForDescription(rawBody, plainBody) {
  return getMutationBodyPayloadForTelemetry(rawBody, plainBody) !== void 0;
}
function extractOperation(method, headers = {}) {
  switch (method) {
    case "GET": {
      return "select";
    }
    case "POST": {
      if (headers["Prefer"]?.includes("resolution=")) {
        return "upsert";
      } else {
        return "insert";
      }
    }
    case "PATCH": {
      return "update";
    }
    case "DELETE": {
      return "delete";
    }
    default: {
      return "<unknown-op>";
    }
  }
}
function translateFiltersIntoMethods(key, query) {
  if (query === "" || query === "*") {
    return "select(*)";
  }
  if (key === "select") {
    return `select(${query})`;
  }
  if (key === "or" || key.endsWith(".or")) {
    return `${key}${query}`;
  }
  const [filter, ...value] = query.split(".");
  let method;
  if (filter?.startsWith("fts")) {
    method = "textSearch";
  } else if (filter?.startsWith("plfts")) {
    method = "textSearch[plain]";
  } else if (filter?.startsWith("phfts")) {
    method = "textSearch[phrase]";
  } else if (filter?.startsWith("wfts")) {
    method = "textSearch[websearch]";
  } else {
    method = filter && FILTER_MAPPINGS[filter] || "filter";
  }
  return `${method}(${key}, ${value.join(".")})`;
}
function instrumentAuthOperation(operation, isAdmin = false) {
  return new Proxy(operation, {
    apply(target, thisArg, argumentsList) {
      return trace.startSpan(
        {
          name: `auth ${isAdmin ? "(admin) " : ""}${operation.name}`,
          attributes: {
            [semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.db.supabase",
            [semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db",
            "db.system": "postgresql",
            "db.operation": `auth.${isAdmin ? "admin." : ""}${operation.name}`
          }
        },
        (span) => {
          return Reflect.apply(target, thisArg, argumentsList).then((res) => {
            if (res && typeof res === "object" && "error" in res && res.error) {
              span.setStatus({ code: spanstatus.SPAN_STATUS_ERROR });
              exports$1.captureException(res.error, {
                mechanism: {
                  handled: false,
                  type: "auto.db.supabase.auth"
                }
              });
            } else {
              span.setStatus({ code: spanstatus.SPAN_STATUS_OK });
            }
            span.end();
            return res;
          }).catch((err) => {
            span.setStatus({ code: spanstatus.SPAN_STATUS_ERROR });
            span.end();
            exports$1.captureException(err, {
              mechanism: {
                handled: false,
                type: "auto.db.supabase.auth"
              }
            });
            throw err;
          }).then(...argumentsList);
        }
      );
    }
  });
}
function instrumentSupabaseAuthClient(supabaseClientInstance) {
  const auth = supabaseClientInstance.auth;
  if (!auth || isInstrumented(supabaseClientInstance.auth)) {
    return;
  }
  for (const operation of AUTH_OPERATIONS_TO_INSTRUMENT) {
    const authOperation = auth[operation];
    if (!authOperation) {
      continue;
    }
    if (typeof supabaseClientInstance.auth[operation] === "function") {
      supabaseClientInstance.auth[operation] = instrumentAuthOperation(authOperation);
    }
  }
  for (const operation of AUTH_ADMIN_OPERATIONS_TO_INSTRUMENT) {
    const authOperation = auth.admin[operation];
    if (!authOperation) {
      continue;
    }
    if (typeof supabaseClientInstance.auth.admin[operation] === "function") {
      supabaseClientInstance.auth.admin[operation] = instrumentAuthOperation(authOperation, true);
    }
  }
  markAsInstrumented(supabaseClientInstance.auth);
}
function instrumentSupabaseClientConstructor(SupabaseClient, _options) {
  if (isInstrumented(SupabaseClient.prototype.from)) {
    return;
  }
  SupabaseClient.prototype.from = new Proxy(
    SupabaseClient.prototype.from,
    {
      apply(target, thisArg, argumentsList) {
        const rv = Reflect.apply(target, thisArg, argumentsList);
        const PostgRESTQueryBuilder = rv.constructor;
        instrumentPostgRESTQueryBuilder(PostgRESTQueryBuilder, _options);
        return rv;
      }
    }
  );
  markAsInstrumented(SupabaseClient.prototype.from);
}
function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder, _options) {
  if (isInstrumented(PostgRESTFilterBuilder.prototype.then)) {
    return;
  }
  PostgRESTFilterBuilder.prototype.then = new Proxy(
    PostgRESTFilterBuilder.prototype.then,
    {
      apply(target, thisArg, argumentsList) {
        const operations = DB_OPERATIONS_TO_INSTRUMENT;
        const typedThis = thisArg;
        const operation = extractOperation(typedThis.method, typedThis.headers);
        if (!operations.includes(operation)) {
          return Reflect.apply(target, thisArg, argumentsList);
        }
        if (!typedThis?.url?.pathname || typeof typedThis.url.pathname !== "string") {
          return Reflect.apply(target, thisArg, argumentsList);
        }
        const pathParts = typedThis.url.pathname.split("/");
        const table = pathParts.length > 0 ? pathParts[pathParts.length - 1] : "";
        const queryItems = [];
        for (const [key, value] of typedThis.url.searchParams.entries()) {
          queryItems.push(translateFiltersIntoMethods(key, value));
        }
        const body = /* @__PURE__ */ Object.create(null);
        if (is.isPlainObject(typedThis.body)) {
          for (const [key, value] of Object.entries(typedThis.body)) {
            body[key] = value;
          }
        }
        const client = currentScopes.getClient();
        const shouldSendData = _options.sendOperationData ?? client?.getDataCollectionOptions().userInfo === true;
        const bodyPayload = getMutationBodyPayloadForTelemetry(typedThis.body, body);
        const mutationPart = operation === "select" ? "" : `${operation}${hasMutationBodyForDescription(typedThis.body, body) ? "(...) " : ""}`;
        const queryPart = shouldSendData ? queryItems.join(" ") : queryItems.length > 0 ? "[redacted]" : "";
        const descriptionMiddle = [mutationPart.trimEnd(), queryPart].filter(Boolean).join(" ");
        const description = descriptionMiddle ? `${descriptionMiddle} from(${table})` : `from(${table})`;
        const attributes = {
          "db.table": table,
          "db.schema": typedThis.schema,
          "db.url": typedThis.url.origin,
          "db.sdk": typedThis.headers["X-Client-Info"],
          "db.system": "postgresql",
          "db.operation": operation,
          [semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.db.supabase",
          [semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db"
        };
        if (queryItems.length && shouldSendData) {
          attributes["db.query"] = queryItems;
        }
        if (bodyPayload !== void 0 && shouldSendData) {
          attributes["db.body"] = bodyPayload;
        }
        return trace.startSpan(
          {
            name: description,
            attributes
          },
          (span) => {
            return Reflect.apply(target, thisArg, []).then(
              (res) => {
                if (span) {
                  if (res && typeof res === "object" && "status" in res) {
                    spanstatus.setHttpStatus(span, res.status || 500);
                  }
                  span.end();
                }
                if (res?.error) {
                  const err = new Error(res.error.message);
                  if (res.error.code) {
                    err.code = res.error.code;
                  }
                  if (res.error.details) {
                    err.details = res.error.details;
                  }
                  const supabaseContext = {};
                  if (queryItems.length && shouldSendData) {
                    supabaseContext.query = queryItems;
                  }
                  if (bodyPayload !== void 0 && shouldSendData) {
                    supabaseContext.body = bodyPayload;
                  }
                  exports$1.captureException(err, (scope) => {
                    scope.addEventProcessor((e) => {
                      misc.addExceptionMechanism(e, {
                        handled: false,
                        type: "auto.db.supabase.postgres"
                      });
                      return e;
                    });
                    scope.setContext("supabase", supabaseContext);
                    return scope;
                  });
                }
                const breadcrumb = {
                  type: "supabase",
                  category: `db.${operation}`,
                  message: description
                };
                const data = {};
                if (queryItems.length && shouldSendData) {
                  data.query = queryItems;
                }
                if (bodyPayload !== void 0 && shouldSendData) {
                  data.body = bodyPayload;
                }
                if (Object.keys(data).length) {
                  breadcrumb.data = data;
                }
                breadcrumbs.addBreadcrumb(breadcrumb);
                return res;
              },
              (err) => {
                if (span) {
                  spanstatus.setHttpStatus(span, 500);
                  span.end();
                }
                throw err;
              }
            ).then(...argumentsList);
          }
        );
      }
    }
  );
  markAsInstrumented(PostgRESTFilterBuilder.prototype.then);
}
function instrumentPostgRESTQueryBuilder(PostgRESTQueryBuilder, _options) {
  for (const operation of DB_OPERATIONS_TO_INSTRUMENT) {
    if (isInstrumented(PostgRESTQueryBuilder.prototype[operation])) {
      continue;
    }
    PostgRESTQueryBuilder.prototype[operation] = new Proxy(
      PostgRESTQueryBuilder.prototype[operation],
      {
        apply(target, thisArg, argumentsList) {
          const rv = Reflect.apply(target, thisArg, argumentsList);
          const PostgRESTFilterBuilder = rv.constructor;
          debugBuild.DEBUG_BUILD && debugLogger.debug.log(`Instrumenting ${operation} operation's PostgRESTFilterBuilder`);
          instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder, _options);
          return rv;
        }
      }
    );
    markAsInstrumented(PostgRESTQueryBuilder.prototype[operation]);
  }
}
const instrumentSupabaseClient = (supabaseClient, options = {}) => {
  if (!supabaseClient) {
    debugBuild.DEBUG_BUILD && debugLogger.debug.warn("Supabase integration was not installed because no Supabase client was provided.");
    return;
  }
  const SupabaseClientConstructor = supabaseClient.constructor === Function ? supabaseClient : supabaseClient.constructor;
  instrumentSupabaseClientConstructor(SupabaseClientConstructor, options);
  instrumentSupabaseAuthClient(supabaseClient);
};
const INTEGRATION_NAME = "Supabase";
const _supabaseIntegration = ((supabaseClient, options) => {
  return {
    setupOnce() {
      instrumentSupabaseClient(supabaseClient, options);
    },
    name: INTEGRATION_NAME
  };
});
const supabaseIntegration = integration.defineIntegration((options) => {
  return _supabaseIntegration(options.supabaseClient, { sendOperationData: options.sendOperationData });
});

exports.DB_OPERATIONS_TO_INSTRUMENT = DB_OPERATIONS_TO_INSTRUMENT;
exports.FILTER_MAPPINGS = FILTER_MAPPINGS;
exports.extractOperation = extractOperation;
exports.instrumentSupabaseClient = instrumentSupabaseClient;
exports.supabaseIntegration = supabaseIntegration;
exports.translateFiltersIntoMethods = translateFiltersIntoMethods;
//# sourceMappingURL=supabase.js.map
