export var ProjectType;
(function (ProjectType) {
    ProjectType["DEFAULT"] = "DEFAULT";
    ProjectType["PREVIEW"] = "PREVIEW";
})(ProjectType || (ProjectType = {}));
export var DbtProjectType;
(function (DbtProjectType) {
    DbtProjectType["DBT"] = "dbt";
    DbtProjectType["DBT_CLOUD_IDE"] = "dbt_cloud_ide";
    DbtProjectType["GITHUB"] = "github";
    DbtProjectType["GITLAB"] = "gitlab";
    DbtProjectType["BITBUCKET"] = "bitbucket";
    DbtProjectType["AZURE_DEVOPS"] = "azure_devops";
    DbtProjectType["NONE"] = "none";
    DbtProjectType["MANIFEST"] = "manifest";
})(DbtProjectType || (DbtProjectType = {}));
export var WarehouseTypes;
(function (WarehouseTypes) {
    WarehouseTypes["BIGQUERY"] = "bigquery";
    WarehouseTypes["POSTGRES"] = "postgres";
    WarehouseTypes["REDSHIFT"] = "redshift";
    WarehouseTypes["SNOWFLAKE"] = "snowflake";
    WarehouseTypes["DATABRICKS"] = "databricks";
    WarehouseTypes["TRINO"] = "trino";
    WarehouseTypes["CLICKHOUSE"] = "clickhouse";
    WarehouseTypes["ATHENA"] = "athena";
    WarehouseTypes["DUCKDB"] = "duckdb";
})(WarehouseTypes || (WarehouseTypes = {}));
export var DuckdbConnectionType;
(function (DuckdbConnectionType) {
    DuckdbConnectionType["MOTHERDUCK"] = "motherduck";
    DuckdbConnectionType["DUCKLAKE"] = "ducklake";
})(DuckdbConnectionType || (DuckdbConnectionType = {}));
export var BigqueryAuthenticationType;
(function (BigqueryAuthenticationType) {
    BigqueryAuthenticationType["SSO"] = "sso";
    BigqueryAuthenticationType["PRIVATE_KEY"] = "private_key";
    BigqueryAuthenticationType["ADC"] = "adc";
})(BigqueryAuthenticationType || (BigqueryAuthenticationType = {}));
export const sensitiveCredentialsFieldNames = [
    'user',
    'password',
    'keyfileContents',
    'personalAccessToken',
    'privateKey',
    'privateKeyPass',
    'sshTunnelPrivateKey',
    'sslcert',
    'sslkey',
    'sslrootcert',
    'token',
    'refreshToken',
    'oauthClientId',
    'oauthClientSecret',
    'accessKeyId',
    'secretAccessKey',
    'sessionToken',
];
export var DatabricksAuthenticationType;
(function (DatabricksAuthenticationType) {
    DatabricksAuthenticationType["PERSONAL_ACCESS_TOKEN"] = "personal_access_token";
    DatabricksAuthenticationType["OAUTH_M2M"] = "oauth_m2m";
    DatabricksAuthenticationType["OAUTH_U2M"] = "oauth_u2m";
})(DatabricksAuthenticationType || (DatabricksAuthenticationType = {}));
export var AthenaAuthenticationType;
(function (AthenaAuthenticationType) {
    AthenaAuthenticationType["ACCESS_KEY"] = "access_key";
    AthenaAuthenticationType["IAM_ROLE"] = "iam_role";
})(AthenaAuthenticationType || (AthenaAuthenticationType = {}));
export var DucklakeCatalogType;
(function (DucklakeCatalogType) {
    DucklakeCatalogType["POSTGRES"] = "postgres";
    DucklakeCatalogType["SQLITE"] = "sqlite";
    DucklakeCatalogType["DUCKDB"] = "duckdb";
})(DucklakeCatalogType || (DucklakeCatalogType = {}));
export var DucklakeDataPathType;
(function (DucklakeDataPathType) {
    DucklakeDataPathType["S3"] = "s3";
    DucklakeDataPathType["GCS"] = "gcs";
    DucklakeDataPathType["AZURE"] = "azure";
    DucklakeDataPathType["LOCAL"] = "local";
})(DucklakeDataPathType || (DucklakeDataPathType = {}));
/**
 * Rows created before the connectionType field was introduced are
 * MotherDuck-shaped DuckDB credentials. Default the field at decrypt time
 * so the discriminated union narrows correctly without a data migration.
 */
export const normalizeWarehouseCredentials = (credentials) => {
    if (credentials.type === WarehouseTypes.DUCKDB &&
        credentials
            .connectionType === undefined) {
        return {
            ...credentials,
            connectionType: DuckdbConnectionType.MOTHERDUCK,
        };
    }
    return credentials;
};
/**
 * Top-level sensitive-field stripping does not reach into the nested
 * catalog/dataPath objects, so call this after the generic strip to
 * scrub nested credentials from a CreateDuckdbDucklakeCredentials.
 */
export const stripDucklakeNestedSensitive = (credentials) => {
    const stripCatalog = (catalog) => {
        switch (catalog.type) {
            case DucklakeCatalogType.POSTGRES: {
                const { user, password, ...rest } = catalog;
                return rest;
            }
            case DucklakeCatalogType.SQLITE:
            case DucklakeCatalogType.DUCKDB:
                return catalog;
            default:
                return catalog;
        }
    };
    const stripDataPath = (dataPath) => {
        switch (dataPath.type) {
            case DucklakeDataPathType.S3: {
                const { accessKeyId, secretAccessKey, ...rest } = dataPath;
                return rest;
            }
            case DucklakeDataPathType.GCS: {
                const { hmacKeyId, hmacSecret, ...rest } = dataPath;
                return rest;
            }
            case DucklakeDataPathType.AZURE: {
                const { connectionString, accountKey, ...rest } = dataPath;
                return rest;
            }
            case DucklakeDataPathType.LOCAL:
                return dataPath;
            default:
                return dataPath;
        }
    };
    return {
        ...credentials,
        catalog: stripCatalog(credentials.catalog),
        dataPath: stripDataPath(credentials.dataPath),
    };
};
export var RedshiftAuthenticationType;
(function (RedshiftAuthenticationType) {
    RedshiftAuthenticationType["PASSWORD"] = "password";
    RedshiftAuthenticationType["IAM"] = "iam";
    RedshiftAuthenticationType["IAM_BROWSER"] = "iam_browser";
})(RedshiftAuthenticationType || (RedshiftAuthenticationType = {}));
// TODO use enum instead
export var SnowflakeAuthenticationType;
(function (SnowflakeAuthenticationType) {
    SnowflakeAuthenticationType["PASSWORD"] = "password";
    SnowflakeAuthenticationType["PRIVATE_KEY"] = "private_key";
    SnowflakeAuthenticationType["SSO"] = "sso";
    SnowflakeAuthenticationType["EXTERNAL_BROWSER"] = "external_browser";
    SnowflakeAuthenticationType["NONE"] = "none";
})(SnowflakeAuthenticationType || (SnowflakeAuthenticationType = {}));
// Returns the timezone the column data is in when the query runs.
// Snowflake's dbt translator wraps timestamps with CONVERT_TIMEZONE('UTC', col),
// so columns are UTC unless `disableTimestampConversion` opts out of that wrap.
export const getColumnTimezone = (credentials) => {
    if (credentials.type === WarehouseTypes.SNOWFLAKE &&
        !credentials.disableTimestampConversion) {
        return 'UTC';
    }
    return credentials.dataTimezone ?? 'UTC';
};
export const maybeOverrideWarehouseConnection = (connection, overrides) => {
    const isBigquery = connection.type === WarehouseTypes.BIGQUERY;
    const overridesSchema = isBigquery
        ? { dataset: overrides.schema }
        : { schema: overrides.schema };
    return {
        ...connection,
        ...(overrides.schema ? overridesSchema : undefined),
    };
};
/**
 * Merges new warehouse credentials with base credentials, preserving advanced settings
 * like requireUserCredentials from the base credentials.
 *
 * This is useful when creating preview projects where we want to use new connection details
 * (like from dbt profiles) but preserve advanced configuration from the parent project.
 */
export const mergeWarehouseCredentials = (baseCredentials, newCredentials) => {
    // If types don't match, return newCredentials as-is (can't merge different warehouse types)
    if (baseCredentials.type !== newCredentials.type) {
        // eslint-disable-next-line no-console
        console.info(`Skipping merge of warehouse credentials due to differing types: ${baseCredentials.type} and ${newCredentials.type}`);
        return newCredentials;
    }
    // Edge case: if the warehouse is snowflake but with a different warehouse, return newCredentials as-is
    // This is to avoid enforcing requireUserCredentials on a different snowflake warehouse that might not have SSO enabled or different roles
    if (baseCredentials.type === WarehouseTypes.SNOWFLAKE &&
        newCredentials.type === WarehouseTypes.SNOWFLAKE &&
        baseCredentials.warehouse.toLowerCase().trim() !==
            newCredentials.warehouse.toLowerCase().trim()) {
        // eslint-disable-next-line no-console
        console.info(`Skipping merge of Snowflake credentials due to differing warehouse names: ${baseCredentials.warehouse} and ${newCredentials.warehouse}`);
        return newCredentials;
    }
    // Only add non sensitive fields from base credentials to avoid conflicts with authentication methods.
    // assumeRoleArn/assumeRoleExternalId are authentication config tied to the base credentials, so they
    // must not be inherited when the preview supplies its own credentials (otherwise the preview tries to
    // assume the parent's role with credentials that aren't authorized to).
    const keysToExclude = [
        ...sensitiveCredentialsFieldNames,
        'authenticationType',
        'assumeRoleArn',
        'assumeRoleExternalId',
    ];
    const filteredBaseCredentials = Object.fromEntries(Object.entries(baseCredentials).filter(([key]) => !keysToExclude.includes(key)));
    // We will use new credentials for connection, this might contain new authentication method
    // do not include all baseCredentials here, to avoid conflicts on authentication (that will cause a mix of serviceaccounts/sso/passwords)
    const merged = {
        ...filteredBaseCredentials, // We copy most of the base config from the parent project, including advanced settings
        ...newCredentials,
        // Keep requireUserCredentials from base credentials, since this is a security setting and should not be overridden
        requireUserCredentials: baseCredentials.requireUserCredentials ||
            newCredentials.requireUserCredentials,
    };
    return merged;
};
export const LIGHTDASH_DBT_PROFILE_ENV_VAR_PREFIX = 'LIGHTDASH_DBT_PROFILE_VAR_';
const DBT_ENVIRONMENT_VARIABLE_KEY_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
const BLOCKED_DBT_ENVIRONMENT_VARIABLE_KEYS = new Set([
    'GIT_ASKPASS',
    'GIT_SSH',
    'GIT_SSH_COMMAND',
    'LD_AUDIT',
    'LD_LIBRARY_PATH',
    'LD_PRELOAD',
    'NODE_OPTIONS',
    'NODE_PATH',
    'PATH',
    'PERL5OPT',
    'PYTHONHOME',
    'PYTHONPATH',
    'RUBYOPT',
    'SHELL',
    'SSH_ASKPASS',
]);
const BLOCKED_DBT_ENVIRONMENT_VARIABLE_KEY_PREFIXES = ['DYLD_', 'GIT_CONFIG_'];
export const getDbtEnvironmentVariableKeyError = (key, options = {}) => {
    if (key.length === 0) {
        return undefined;
    }
    if (!DBT_ENVIRONMENT_VARIABLE_KEY_REGEX.test(key)) {
        return `Environment variable "${key}" must contain only letters, numbers, and underscores, and cannot start with a number`;
    }
    if (!options.allowLightdashProfileEnvironmentVariables &&
        key.startsWith(LIGHTDASH_DBT_PROFILE_ENV_VAR_PREFIX)) {
        return `Environment variable "${key}" is reserved for Lightdash`;
    }
    if (BLOCKED_DBT_ENVIRONMENT_VARIABLE_KEYS.has(key) ||
        BLOCKED_DBT_ENVIRONMENT_VARIABLE_KEY_PREFIXES.some((prefix) => key.startsWith(prefix))) {
        return `Environment variable "${key}" cannot be used because it can change how dbt or its child processes execute`;
    }
    return undefined;
};
export const isSafeDbtEnvironmentVariableKey = (key, options = {}) => getDbtEnvironmentVariableKeyError(key, options) === undefined;
export const getInvalidDbtEnvironmentVariableKeys = (environment) => (environment ?? [])
    .map(({ key }) => key)
    .filter((key) => getDbtEnvironmentVariableKeyError(key) !== undefined);
export var SupportedDbtVersions;
(function (SupportedDbtVersions) {
    SupportedDbtVersions["V1_4"] = "v1.4";
    SupportedDbtVersions["V1_5"] = "v1.5";
    SupportedDbtVersions["V1_6"] = "v1.6";
    SupportedDbtVersions["V1_7"] = "v1.7";
    SupportedDbtVersions["V1_8"] = "v1.8";
    SupportedDbtVersions["V1_9"] = "v1.9";
    SupportedDbtVersions["V1_10"] = "v1.10";
    SupportedDbtVersions["V1_11"] = "v1.11";
    SupportedDbtVersions["V1_12"] = "v1.12";
})(SupportedDbtVersions || (SupportedDbtVersions = {}));
// Make it an enum to avoid TSOA errors
export var DbtVersionOptionLatest;
(function (DbtVersionOptionLatest) {
    DbtVersionOptionLatest["LATEST"] = "latest";
})(DbtVersionOptionLatest || (DbtVersionOptionLatest = {}));
export function isDbtVersion110OrHigher(version) {
    if (!version) {
        return false;
    }
    // Get all enum values as an array in order
    const versions = Object.values(SupportedDbtVersions);
    const v110Index = versions.indexOf(SupportedDbtVersions.V1_10);
    const currentIndex = versions.indexOf(version);
    // If the current version is at or after v1.10 in the enum order
    return currentIndex >= v110Index;
}
const dbtWarehousesExcept = (...unsupported) => Object.values(WarehouseTypes).filter((w) => !unsupported.includes(w));
/**
 * Warehouse adapters installed for each dbt version in the production image
 * (see the "Installing multiple versions of dbt" block in `/dockerfile`). This
 * is the source of truth for which `(dbt version, warehouse)` combinations
 * Lightdash can actually run, and it gates which version `latest` may point to
 * (see `LATEST_SUPPORTED_DBT_VERSION`).
 *
 * Keep this in sync with `/dockerfile` whenever an adapter is added to or
 * dropped from a version. The exhaustive `Record` forces every new
 * `SupportedDbtVersions` member to declare its adapter coverage.
 */
export const DBT_VERSION_SUPPORTED_WAREHOUSES = {
    [SupportedDbtVersions.V1_4]: dbtWarehousesExcept(WarehouseTypes.ATHENA, WarehouseTypes.DUCKDB),
    [SupportedDbtVersions.V1_5]: dbtWarehousesExcept(WarehouseTypes.ATHENA, WarehouseTypes.DUCKDB),
    [SupportedDbtVersions.V1_6]: dbtWarehousesExcept(WarehouseTypes.ATHENA, WarehouseTypes.DUCKDB),
    [SupportedDbtVersions.V1_7]: dbtWarehousesExcept(WarehouseTypes.ATHENA, WarehouseTypes.DUCKDB),
    [SupportedDbtVersions.V1_8]: dbtWarehousesExcept(WarehouseTypes.ATHENA),
    [SupportedDbtVersions.V1_9]: dbtWarehousesExcept(),
    [SupportedDbtVersions.V1_10]: dbtWarehousesExcept(),
    [SupportedDbtVersions.V1_11]: dbtWarehousesExcept(),
    [SupportedDbtVersions.V1_12]: dbtWarehousesExcept(WarehouseTypes.DATABRICKS),
};
export const getDbtVersionSupportedWarehouses = (version) => DBT_VERSION_SUPPORTED_WAREHOUSES[version];
export const isWarehouseSupportedByDbtVersion = (version, warehouseType) => DBT_VERSION_SUPPORTED_WAREHOUSES[version].includes(warehouseType);
export const LATEST_SUPPORTED_DBT_VERSION = SupportedDbtVersions.V1_11;
export const getLatestSupportDbtVersion = () => LATEST_SUPPORTED_DBT_VERSION;
export const resolveDbtVersion = (option) => option === DbtVersionOptionLatest.LATEST
    ? getLatestSupportDbtVersion()
    : option;
export const DefaultSupportedDbtVersion = DbtVersionOptionLatest.LATEST;
export const isGitProjectType = (connection) => [
    DbtProjectType.GITHUB,
    DbtProjectType.GITLAB,
    DbtProjectType.BITBUCKET,
].includes(connection.type);
const isRemoteType = (connection) => [DbtProjectType.DBT, DbtProjectType.DBT_CLOUD_IDE].includes(connection.type);
export const maybeOverrideDbtConnection = (connection, overrides) => {
    // If manifest is provided, create a MANIFEST connection type
    if (overrides.manifest) {
        return {
            type: DbtProjectType.MANIFEST,
            manifest: overrides.manifest,
            hideRefreshButton: true,
        };
    }
    return {
        ...connection,
        ...(isGitProjectType(connection) && overrides.branch
            ? { branch: overrides.branch }
            : undefined),
        ...(!isRemoteType(connection) &&
            overrides.environment &&
            overrides.environment.length > 0
            ? { environment: overrides.environment }
            : undefined),
    };
};
//# sourceMappingURL=projects.js.map