import https from 'node:https';
import { Nango, getUserAgent } from '@nangohq/node';
import paginateService from '../services/paginate.service.js';
import proxyService from '../services/proxy.service.js';
import axios, { AxiosError } from 'axios';
import { getPersistAPIUrl } from '../utils/utils.js';
import { getLogger, httpRetryStrategy, metrics, retryWithBackoff, MAX_LOG_PAYLOAD, stringifyAndTruncateValue, stringifyObject, truncateJson } from '@nangohq/utils';
import { validateData } from './dataValidation.js';
import { NangoError } from '../utils/error.js';
import { getProvider } from '../services/providers.js';
import { redactHeaders, redactURL } from '../utils/http.js';
const logger = getLogger('SDK');
export const oldLevelToNewLevel = {
    debug: 'debug',
    info: 'info',
    warn: 'warn',
    error: 'error',
    verbose: 'debug',
    silly: 'debug',
    http: 'info'
};
const logLevelToLogger = {
    info: 'info',
    debug: 'debug',
    error: 'error',
    warn: 'warning',
    http: 'info',
    verbose: 'debug',
    silly: 'debug'
};
var PaginationType;
(function (PaginationType) {
    PaginationType["CURSOR"] = "cursor";
    PaginationType["LINK"] = "link";
    PaginationType["OFFSET"] = "offset";
})(PaginationType || (PaginationType = {}));
export class ActionError extends Error {
    type;
    payload;
    constructor(payload) {
        super();
        this.type = 'action_script_runtime_error';
        if (payload) {
            this.payload = payload;
        }
    }
}
const MEMOIZED_CONNECTION_TTL = 60000;
const MEMOIZED_INTEGRATION_TTL = 10 * 60 * 1000;
const RECORDS_VALIDATION_SAMPLE = 5;
export const defaultPersistApi = axios.create({
    baseURL: getPersistAPIUrl(),
    httpsAgent: new https.Agent({ keepAlive: true }),
    headers: {
        'User-Agent': getUserAgent('sdk')
    },
    validateStatus: (_status) => {
        return true;
    }
});
export class NangoAction {
    nango;
    attributes = {};
    persistApi;
    activityLogId;
    syncId;
    nangoConnectionId;
    environmentId;
    environmentName;
    syncJobId;
    dryRun;
    abortSignal;
    dryRunService;
    syncConfig;
    runnerFlags;
    connectionId;
    providerConfigKey;
    provider;
    ActionError = ActionError;
    memoizedConnections = new Map();
    memoizedIntegration = new Map();
    constructor(config, { persistApi } = { persistApi: defaultPersistApi }) {
        this.connectionId = config.connectionId;
        this.environmentId = config.environmentId;
        this.providerConfigKey = config.providerConfigKey;
        this.persistApi = persistApi;
        this.runnerFlags = config.runnerFlags;
        if (config.activityLogId) {
            this.activityLogId = config.activityLogId;
        }
        const axiosSettings = {
            userAgent: 'sdk'
        };
        if (config.syncId) {
            this.syncId = config.syncId;
        }
        if (config.nangoConnectionId) {
            this.nangoConnectionId = config.nangoConnectionId;
        }
        if (config.syncJobId) {
            this.syncJobId = config.syncJobId;
        }
        if (config.dryRun) {
            this.dryRun = config.dryRun;
            if (config.axios?.response) {
                axiosSettings.interceptors = {
                    response: {
                        onFulfilled: config.axios.response.onFulfilled
                    }
                };
            }
        }
        if (!config.axios?.response) {
            // Leave the priority to saving response instead of logging
            axiosSettings.interceptors = {
                response: {
                    onFulfilled: this.logAPICall.bind(this)
                }
            };
        }
        if (config.environmentName) {
            this.environmentName = config.environmentName;
        }
        if (config.provider) {
            this.provider = config.provider;
        }
        if (config.attributes) {
            this.attributes = config.attributes;
        }
        if (config.abortSignal) {
            this.abortSignal = config.abortSignal;
        }
        if (config.dryRunService) {
            this.dryRunService = config.dryRunService;
        }
        if (config.syncConfig) {
            this.syncConfig = config.syncConfig;
        }
        this.nango = new Nango({ isSync: true, ...config }, axiosSettings);
        if (this.dryRun !== true) {
            if (!this.activityLogId)
                throw new Error('Parameter activityLogId is required when not in dryRun');
            if (!this.environmentId)
                throw new Error('Parameter environmentId is required when not in dryRun');
            if (!this.nangoConnectionId)
                throw new Error('Parameter nangoConnectionId is required when not in dryRun');
            if (!this.syncConfig)
                throw new Error('Parameter syncConfig is required when not in dryRun');
        }
    }
    stringify() {
        return JSON.stringify(this, (key, value) => {
            if (key === 'secretKey') {
                return '********';
            }
            return value;
        });
    }
    proxyConfig(config) {
        if (!config.connectionId && this.connectionId) {
            config.connectionId = this.connectionId;
        }
        if (!config.providerConfigKey && this.providerConfigKey) {
            config.providerConfigKey = this.providerConfigKey;
        }
        if (!config.connectionId) {
            throw new Error('Missing connection id');
        }
        if (!config.providerConfigKey) {
            throw new Error('Missing provider config key');
        }
        return {
            method: 'GET',
            ...config,
            providerConfigKey: config.providerConfigKey,
            connectionId: config.connectionId,
            headers: {
                ...(config.headers || {}),
                'user-agent': this.nango.userAgent
            }
        };
    }
    throwIfAborted() {
        if (this.abortSignal?.aborted) {
            throw new NangoError('script_aborted');
        }
    }
    async proxy(config) {
        this.throwIfAborted();
        if (!config.method) {
            config.method = 'GET';
        }
        if (this.dryRun) {
            return this.nango.proxy(config);
        }
        else {
            const { connectionId, providerConfigKey } = config;
            const connection = await this.getConnection(providerConfigKey, connectionId);
            if (!connection) {
                throw new Error(`Connection not found using the provider config key ${this.providerConfigKey} and connection id ${this.connectionId}`);
            }
            const proxyConfig = this.proxyConfig(config);
            const { response, logs } = await proxyService.route(proxyConfig, {
                existingActivityLogId: this.activityLogId,
                connection,
                providerName: this.provider
            });
            // We batch save, since we have buffered the createdAt it shouldn't impact order
            await Promise.all(logs.map(async (log) => {
                if (log.level === 'debug') {
                    return;
                }
                await this.sendLogToPersist(log);
            }));
            if (response instanceof Error) {
                throw response;
            }
            return response;
        }
    }
    async get(config) {
        return this.proxy({
            ...config,
            method: 'GET'
        });
    }
    async post(config) {
        return this.proxy({
            ...config,
            method: 'POST'
        });
    }
    async put(config) {
        return this.proxy({
            ...config,
            method: 'PUT'
        });
    }
    async patch(config) {
        return this.proxy({
            ...config,
            method: 'PATCH'
        });
    }
    async delete(config) {
        return this.proxy({
            ...config,
            method: 'DELETE'
        });
    }
    async getToken() {
        this.throwIfAborted();
        return this.nango.getToken(this.providerConfigKey, this.connectionId);
    }
    /**
     * Get current integration
     */
    async getIntegration(queries) {
        this.throwIfAborted();
        const key = queries?.include?.join(',') || 'default';
        const has = this.memoizedIntegration.get(key);
        if (has && MEMOIZED_INTEGRATION_TTL > Date.now() - has.timestamp) {
            return has.integration;
        }
        const { data: integration } = await this.nango.getIntegration({ uniqueKey: this.providerConfigKey }, queries);
        this.memoizedIntegration.set(key, { integration, timestamp: Date.now() });
        return integration;
    }
    async getConnection(providerConfigKeyOverride, connectionIdOverride) {
        this.throwIfAborted();
        const providerConfigKey = providerConfigKeyOverride || this.providerConfigKey;
        const connectionId = connectionIdOverride || this.connectionId;
        const credentialsPair = `${providerConfigKey}${connectionId}`;
        const cachedConnection = this.memoizedConnections.get(credentialsPair);
        if (!cachedConnection || Date.now() - cachedConnection.timestamp > MEMOIZED_CONNECTION_TTL) {
            const connection = await this.nango.getConnection(providerConfigKey, connectionId);
            this.memoizedConnections.set(credentialsPair, { connection, timestamp: Date.now() });
            return connection;
        }
        return cachedConnection.connection;
    }
    async setMetadata(metadata) {
        this.throwIfAborted();
        try {
            return await this.nango.setMetadata(this.providerConfigKey, this.connectionId, metadata);
        }
        finally {
            this.memoizedConnections.delete(`${this.providerConfigKey}${this.connectionId}`);
        }
    }
    async updateMetadata(metadata) {
        this.throwIfAborted();
        try {
            return await this.nango.updateMetadata(this.providerConfigKey, this.connectionId, metadata);
        }
        finally {
            this.memoizedConnections.delete(`${this.providerConfigKey}${this.connectionId}`);
        }
    }
    /**
     * @deprecated please use setMetadata instead.
     */
    async setFieldMapping(fieldMapping) {
        logger.warning('setFieldMapping is deprecated. Please use setMetadata instead.');
        return this.setMetadata(fieldMapping);
    }
    async getMetadata() {
        this.throwIfAborted();
        return (await this.getConnection(this.providerConfigKey, this.connectionId)).metadata;
    }
    async getWebhookURL() {
        this.throwIfAborted();
        const integration = await this.getIntegration({ include: ['webhook'] });
        return integration.webhook_url;
    }
    /**
     * @deprecated please use getMetadata instead.
     */
    async getFieldMapping() {
        logger.warning('getFieldMapping is deprecated. Please use getMetadata instead.');
        const metadata = await this.getMetadata();
        return metadata['fieldMapping'] || {};
    }
    async log(...args) {
        this.throwIfAborted();
        if (args.length === 0) {
            return;
        }
        const lastArg = args[args.length - 1];
        const isUserDefinedLevel = (object) => {
            return lastArg && typeof lastArg === 'object' && 'level' in object;
        };
        const userDefinedLevel = isUserDefinedLevel(lastArg) ? lastArg : undefined;
        if (userDefinedLevel) {
            args.pop();
        }
        const level = userDefinedLevel?.level ?? 'info';
        if (this.dryRun) {
            const logLevel = logLevelToLogger[level] ?? 'info';
            // TODO: we shouldn't use a floating logger, it should be passed from dryrun or runner
            if (args.length > 1 && 'type' in args[1] && args[1].type === 'http') {
                logger[logLevel].apply(null, [args[0], { status: args[1]?.response?.code || 'xxx' }]);
            }
            else {
                logger[logLevel].apply(null, args);
            }
            return;
        }
        const [message, payload] = args;
        // arrays are not supported in the log meta, so we convert them to objects
        const meta = Array.isArray(payload) ? Object.fromEntries(payload.map((e, i) => [i, e])) : payload || null;
        await this.sendLogToPersist({
            type: 'log',
            level: oldLevelToNewLevel[level],
            source: 'user',
            message: stringifyAndTruncateValue(message),
            meta,
            createdAt: new Date().toISOString(),
            environmentId: this.environmentId
        });
    }
    async getEnvironmentVariables() {
        if (!this.environmentId) {
            throw new Error('There is no current environment to get variables from');
        }
        return await this.nango.getEnvironmentVariables();
    }
    getFlowAttributes() {
        if (!this.syncJobId) {
            throw new Error('There is no current sync to get attributes from');
        }
        return this.attributes;
    }
    async *paginate(config) {
        const provider = getProvider(this.provider);
        if (!provider) {
            throw new NangoError('unknown_provider_template_in_config');
        }
        const templatePaginationConfig = provider.proxy?.paginate;
        if (!templatePaginationConfig && (!config.paginate || !config.paginate.type)) {
            throw Error('There was no pagination configuration for this integration or configuration passed in.');
        }
        const paginationConfig = {
            ...(templatePaginationConfig || {}),
            ...(config.paginate || {})
        };
        paginateService.validateConfiguration(paginationConfig);
        config.method = config.method || 'GET';
        const configMethod = config.method.toLocaleLowerCase();
        const passPaginationParamsInBody = ['post', 'put', 'patch'].includes(configMethod);
        const updatedBodyOrParams = (passPaginationParamsInBody ? config.data : config.params) ?? {};
        const limitParameterName = paginationConfig.limit_name_in_request;
        if (paginationConfig['limit']) {
            updatedBodyOrParams[limitParameterName] = paginationConfig['limit'];
        }
        const proxyConfig = this.proxyConfig(config);
        switch (paginationConfig.type.toLowerCase()) {
            case PaginationType.CURSOR:
                return yield* paginateService.cursor(proxyConfig, paginationConfig, updatedBodyOrParams, passPaginationParamsInBody, this.proxy.bind(this));
            case PaginationType.LINK:
                return yield* paginateService.link(proxyConfig, paginationConfig, updatedBodyOrParams, passPaginationParamsInBody, this.proxy.bind(this));
            case PaginationType.OFFSET:
                return yield* paginateService.offset(proxyConfig, paginationConfig, updatedBodyOrParams, passPaginationParamsInBody, this.proxy.bind(this));
            default:
                throw Error(`'${paginationConfig.type} ' pagination is not supported. Please, make sure it's one of ${Object.values(PaginationType)}`);
        }
    }
    async triggerAction(providerConfigKey, connectionId, actionName, input) {
        return await this.nango.triggerAction(providerConfigKey, connectionId, actionName, input);
    }
    async triggerSync(providerConfigKey, connectionId, syncName, fullResync) {
        if (this.dryRun && this.dryRunService) {
            return this.dryRunService.run({
                sync: syncName,
                connectionId,
                autoConfirm: true,
                debug: false
            });
        }
        else {
            return this.nango.triggerSync(providerConfigKey, [syncName], connectionId, fullResync);
        }
    }
    async sendLogToPersist(log) {
        let response;
        try {
            response = await retryWithBackoff(async () => {
                let data = stringifyObject({ activityLogId: this.activityLogId, log });
                // We try to keep log object under an acceptable size, before reaching network
                // The idea is to always log something instead of silently crashing without overloading persist
                if (data.length > MAX_LOG_PAYLOAD) {
                    log.message += ` ... (truncated, payload was too large)`;
                    // Truncating can remove mandatory field so we only try to truncate meta
                    if (log.meta) {
                        data = stringifyObject({
                            activityLogId: this.activityLogId,
                            log: { ...log, meta: truncateJson(log.meta) }
                        });
                    }
                }
                return await this.persistApi({
                    method: 'POST',
                    url: `/environment/${this.environmentId}/log`,
                    headers: {
                        Authorization: `Bearer ${this.nango.secretKey}`,
                        'Content-Type': 'application/json'
                    },
                    data
                });
            }, { retry: httpRetryStrategy });
        }
        catch (err) {
            logger.error('Failed to log to persist, due to an internal error', err instanceof AxiosError ? err.code : err);
            // We don't want to block a sync because logging failed, so we fail silently until we have a way to report error
            // TODO: find a way to report that
            return;
        }
        if (response.status > 299) {
            logger.error(`Request to persist API (log) failed: errorCode=${response.status} response='${JSON.stringify(response.data)}' log=${JSON.stringify(log)}`, this.stringify());
            throw new Error(`Failed to log: ${JSON.stringify(response.data)}`);
        }
    }
    logAPICall(res) {
        if (!res.config.url) {
            return res;
        }
        // We compte on the fly because connection's credentials can change during a single run
        // We could further optimize this and cache it when the memoizedConnection is updated
        const valuesToFilter = [
            ...Array.from(this.memoizedConnections.values()).reduce((acc, conn) => {
                if (!conn) {
                    return acc;
                }
                acc.push(...Object.values(conn.connection.credentials));
                return acc;
            }, []),
            this.nango.secretKey
        ];
        const method = res.config.method?.toLocaleUpperCase(); // axios put it in lowercase;
        void this.log(`${method} ${res.config.url}`, {
            type: 'http',
            request: {
                method: method,
                url: redactURL({ url: res.config.url, valuesToFilter }),
                headers: redactHeaders({ headers: res.config.headers, valuesToFilter })
            },
            response: {
                code: res.status,
                headers: redactHeaders({ headers: res.headers, valuesToFilter })
            }
        }, { level: res.status > 299 ? 'error' : 'info' });
        return res;
    }
}
export class NangoSync extends NangoAction {
    lastSyncDate;
    track_deletes = false;
    logMessages = {
        counts: { updated: 0, added: 0, deleted: 0 },
        messages: []
    };
    rawSaveOutput;
    rawDeleteOutput;
    stubbedMetadata = undefined;
    batchSize = 1000;
    constructor(config) {
        super(config);
        if (config.lastSyncDate) {
            this.lastSyncDate = config.lastSyncDate;
        }
        if (config.track_deletes) {
            this.track_deletes = config.track_deletes;
        }
        if (config.logMessages) {
            this.logMessages = config.logMessages;
        }
        if (config.rawSaveOutput) {
            this.rawSaveOutput = config.rawSaveOutput;
        }
        if (config.rawDeleteOutput) {
            this.rawDeleteOutput = config.rawDeleteOutput;
        }
        if (config.stubbedMetadata) {
            this.stubbedMetadata = config.stubbedMetadata;
        }
        if (!config.dryRun) {
            if (!this.syncId)
                throw new Error('Parameter syncId is required when not in dryRun');
            if (!this.syncJobId)
                throw new Error('Parameter syncJobId is required when not in dryRun');
        }
    }
    /**
     * @deprecated please use batchSave
     */
    async batchSend(results, model) {
        logger.warning('batchSend will be deprecated in future versions. Please use batchSave instead.');
        return this.batchSave(results, model);
    }
    async batchSave(results, model) {
        this.throwIfAborted();
        if (!results || results.length === 0) {
            if (this.dryRun) {
                logger.info('batchSave received an empty array. No records to save.');
            }
            return true;
        }
        // Validate records
        const hasErrors = [];
        for (const record of results) {
            const validation = validateData({
                version: this.syncConfig?.version || '1',
                input: JSON.parse(JSON.stringify(record)),
                jsonSchema: this.syncConfig.models_json_schema,
                modelName: model
            });
            if (validation === true) {
                continue;
            }
            hasErrors.push({ data: record, validation });
            metrics.increment(metrics.Types.RUNNER_INVALID_SYNCS_RECORDS);
            if (this.runnerFlags?.validateSyncRecords) {
                break;
            }
        }
        if (hasErrors.length > 0) {
            if (this.dryRun) {
                await this.log('Invalid record payload. Use `--validation` option to see the details', { level: 'warn' });
            }
            if (this.runnerFlags?.validateSyncRecords) {
                throw new NangoError(`invalid_sync_record`, { ...hasErrors[0], model });
            }
            const sampled = hasErrors.length > RECORDS_VALIDATION_SAMPLE;
            const sample = sampled ? hasErrors.slice(0, RECORDS_VALIDATION_SAMPLE) : hasErrors;
            if (sampled) {
                await this.log(`Invalid records: ${hasErrors.length} failed ${sampled ? `(sampled to ${RECORDS_VALIDATION_SAMPLE})` : ''}`, { level: 'warn' });
            }
            await Promise.all(sample.map((log) => {
                return this.log(`Invalid record payload`, { ...log, model }, { level: 'warn' });
            }));
        }
        if (this.dryRun) {
            this.logMessages?.messages.push(`A batch save call would save the following data to the ${model} model:`);
            for (const msg of results) {
                this.logMessages?.messages.push(msg);
            }
            if (this.logMessages && this.logMessages.counts) {
                this.logMessages.counts.added = Number(this.logMessages.counts.added) + results.length;
            }
            if (this.rawSaveOutput) {
                if (!this.rawSaveOutput.has(model)) {
                    this.rawSaveOutput.set(model, []);
                }
                this.rawSaveOutput.get(model)?.push(...results);
            }
            return null;
        }
        for (let i = 0; i < results.length; i += this.batchSize) {
            const batch = results.slice(i, i + this.batchSize);
            let response;
            try {
                response = await retryWithBackoff(() => {
                    return this.persistApi({
                        method: 'POST',
                        url: `/environment/${this.environmentId}/connection/${this.nangoConnectionId}/sync/${this.syncId}/job/${this.syncJobId}/records`,
                        headers: {
                            Authorization: `Bearer ${this.nango.secretKey}`
                        },
                        data: {
                            model,
                            records: batch,
                            providerConfigKey: this.providerConfigKey,
                            connectionId: this.connectionId,
                            activityLogId: this.activityLogId
                        }
                    });
                }, { retry: httpRetryStrategy });
            }
            catch (err) {
                logger.error('Internal error', err instanceof AxiosError ? err.code : err);
                throw new Error('Failed to save records due to an internal error', { cause: err });
            }
            if (response.status > 299) {
                logger.error(`Request to persist API (batchSave) failed: errorCode=${response.status} response='${JSON.stringify(response.data)}'`, this.stringify());
                if (response.status === 400) {
                    throw new Error(`Records invalid format. Please make sure you are sending an array of objects that each contain an 'id' property with type string`);
                }
                else {
                    const message = 'error' in response.data && 'message' in response.data.error ? response.data.error.message : JSON.stringify(response.data);
                    throw new Error(message);
                }
            }
        }
        return true;
    }
    async batchDelete(results, model) {
        this.throwIfAborted();
        if (!results || results.length === 0) {
            if (this.dryRun) {
                logger.info('batchDelete received an empty array. No records to delete.');
            }
            return true;
        }
        if (this.dryRun) {
            this.logMessages?.messages.push(`A batch delete call would delete the following data:`);
            for (const msg of results) {
                this.logMessages?.messages.push(msg);
            }
            if (this.logMessages && this.logMessages.counts) {
                this.logMessages.counts.deleted = Number(this.logMessages.counts.deleted) + results.length;
            }
            if (this.rawDeleteOutput) {
                if (!this.rawDeleteOutput.has(model)) {
                    this.rawDeleteOutput.set(model, []);
                }
                this.rawDeleteOutput.get(model)?.push(...results);
            }
            return null;
        }
        for (let i = 0; i < results.length; i += this.batchSize) {
            const batch = results.slice(i, i + this.batchSize);
            let response;
            try {
                response = await retryWithBackoff(async () => {
                    return await this.persistApi({
                        method: 'DELETE',
                        url: `/environment/${this.environmentId}/connection/${this.nangoConnectionId}/sync/${this.syncId}/job/${this.syncJobId}/records`,
                        headers: {
                            Authorization: `Bearer ${this.nango.secretKey}`
                        },
                        data: {
                            model,
                            records: batch,
                            providerConfigKey: this.providerConfigKey,
                            connectionId: this.connectionId,
                            activityLogId: this.activityLogId
                        }
                    });
                }, { retry: httpRetryStrategy });
            }
            catch (err) {
                logger.error('Internal error', err instanceof AxiosError ? err.code : err);
                throw new Error('Failed to delete records due to an internal error', { cause: err });
            }
            if (response.status > 299) {
                logger.error(`Request to persist API (batchDelete) failed: errorCode=${response.status} response='${JSON.stringify(response.data)}'`, this.stringify());
                const message = 'error' in response.data && 'message' in response.data.error ? response.data.error.message : JSON.stringify(response.data);
                throw new Error(message);
            }
        }
        return true;
    }
    async batchUpdate(results, model) {
        this.throwIfAborted();
        if (!results || results.length === 0) {
            if (this.dryRun) {
                logger.info('batchUpdate received an empty array. No records to update.');
            }
            return true;
        }
        if (this.dryRun) {
            this.logMessages?.messages.push(`A batch update call would update the following data to the ${model} model:`);
            for (const msg of results) {
                this.logMessages?.messages.push(msg);
            }
            if (this.logMessages && this.logMessages.counts) {
                this.logMessages.counts.updated = Number(this.logMessages.counts.updated) + results.length;
            }
            return null;
        }
        for (let i = 0; i < results.length; i += this.batchSize) {
            const batch = results.slice(i, i + this.batchSize);
            let response;
            try {
                response = await retryWithBackoff(async () => {
                    return await this.persistApi({
                        method: 'PUT',
                        url: `/environment/${this.environmentId}/connection/${this.nangoConnectionId}/sync/${this.syncId}/job/${this.syncJobId}/records`,
                        headers: {
                            Authorization: `Bearer ${this.nango.secretKey}`
                        },
                        data: {
                            model,
                            records: batch,
                            providerConfigKey: this.providerConfigKey,
                            connectionId: this.connectionId,
                            activityLogId: this.activityLogId
                        }
                    });
                }, { retry: httpRetryStrategy });
            }
            catch (err) {
                logger.error('Internal error', err instanceof AxiosError ? err.code : err);
                throw new Error('Failed to update records due to an internal error', { cause: err });
            }
            if (response.status > 299) {
                logger.error(`Request to persist API (batchUpdate) failed: errorCode=${response.status} response='${JSON.stringify(response.data)}'`, this.stringify());
                const message = 'error' in response.data && 'message' in response.data.error ? response.data.error.message : JSON.stringify(response.data);
                throw new Error(message);
            }
        }
        return true;
    }
    async getMetadata() {
        this.throwIfAborted();
        if (this.dryRun && this.stubbedMetadata) {
            return this.stubbedMetadata;
        }
        return super.getMetadata();
    }
}
const TELEMETRY_ALLOWED_METHODS = [
    'batchDelete',
    'batchSave',
    'batchSend',
    'getConnection',
    'getEnvironmentVariables',
    'getMetadata',
    'proxy',
    'log',
    'triggerAction',
    'triggerSync'
];
/* eslint-disable no-inner-declarations */
/**
 * @internal
 *
 * This function will enable tracing on the SDK
 * It has been split from the actual code to avoid making the code too dirty and to easily enable/disable tracing if there is an issue with it
 */
export function instrumentSDK(rawNango) {
    return new Proxy(rawNango, {
        get(target, propKey) {
            // Method name is not matching the allowList we don't do anything else
            if (!TELEMETRY_ALLOWED_METHODS.includes(propKey)) {
                return target[propKey];
            }
            return metrics.time(`${metrics.Types.RUNNER_SDK}.${propKey}`, target[propKey].bind(target));
        }
    });
}
/* eslint-enable no-inner-declarations */
//# sourceMappingURL=sync.js.map