/**
 * @fileoverview Rule to enforce the use of baseline features.
 * @author Nicholas C. Zakas
 */
//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------
import { BASELINE_HIGH, BASELINE_LOW, properties, propertyValues, atRules, mediaConditions, functions, units, selectors, } from "../data/baseline-data.js";
import { namedColors } from "../data/colors.js";
//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------
/**
 * @import { CSSRuleDefinition } from "../types.js"
 * @import { Identifier, FunctionNodePlain, Dimension } from "@eslint/css-tree"
 * @typedef {"notBaselineProperty" | "notBaselinePropertyValue" | "notBaselineAtRule" | "notBaselineFunction" | "notBaselineMediaCondition" | "notBaselineSelector" | "notBaselineUnit"} UseBaselineMessageIds
 * @typedef {[{
 *     available?: "widely" | "newly" | number,
 *     allowAtRules?: string[],
 *     allowFunctions?: string[],
 *     allowMediaConditions?: string[],
 *     allowProperties?: string[],
 *     allowPropertyValues?: { [property: string]: string[] },
 *     allowSelectors?: string[],
 *     allowUnits?: string[]
 * }]} UseBaselineOptions
 * @typedef {CSSRuleDefinition<{ RuleOptions: UseBaselineOptions, MessageIds: UseBaselineMessageIds }>} UseBaselineRuleDefinition
 */
//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------
/**
 * Represents a property that is supported via `@supports`.
 */
class SupportedProperty {
    /**
     * The name of the property.
     * @type {string}
     */
    name;
    /**
     * Supported identifier values.
     * @type {Set<string>}
     */
    #identifiers = new Set();
    /**
     * Supported units.
     * @type {Set<string>}
     */
    #units = new Set();
    /**
     * Supported function types.
     * @type {Set<string>}
     */
    #functions = new Set();
    /**
     * Creates a new instance.
     * @param {string} name The name of the property.
     */
    constructor(name) {
        this.name = name;
    }
    /**
     * Adds an identifier to the list of supported identifiers.
     * @param {string} identifier The identifier to add.
     * @returns {void}
     */
    addIdentifier(identifier) {
        this.#identifiers.add(identifier);
    }
    /**
     * Determines if an identifier is supported.
     * @param {string} identifier The identifier to check.
     * @returns {boolean} `true` if the identifier is supported, `false` if not.
     */
    hasIdentifier(identifier) {
        return this.#identifiers.has(identifier);
    }
    /**
     * Determines if any identifiers are supported.
     * @returns {boolean} `true` if any identifiers are supported, `false` if not.
     */
    hasIdentifiers() {
        return this.#identifiers.size > 0;
    }
    /**
     * Adds a unit to the list of supported units.
     * @param {string} unit The unit to add.
     * @returns {void}
     */
    addUnit(unit) {
        this.#units.add(unit);
    }
    /**
     * Determines if a unit is supported.
     * @param {string} unit The unit to check.
     * @returns {boolean} `true` if the unit is supported, `false` if not.
     */
    hasUnit(unit) {
        return this.#units.has(unit);
    }
    /**
     * Determines if any units are supported.
     * @returns {boolean} `true` if any units are supported, `false` if not.
     */
    hasUnits() {
        return this.#units.size > 0;
    }
    /**
     * Adds a function to the list of supported functions.
     * @param {string} func The function to add.
     * @returns {void}
     */
    addFunction(func) {
        this.#functions.add(func);
    }
    /**
     * Determines if a function is supported.
     * @param {string} func The function to check.
     * @returns {boolean} `true` if the function is supported, `false` if not.
     */
    hasFunction(func) {
        return this.#functions.has(func);
    }
    /**
     * Determines if any functions are supported.
     * @returns {boolean} `true` if any functions are supported, `false` if not.
     */
    hasFunctions() {
        return this.#functions.size > 0;
    }
}
/**
 * Represents an `@supports` rule and everything it enables.
 */
class SupportsRule {
    /**
     * The properties supported by this rule.
     * @type {Map<string, SupportedProperty>}
     */
    #properties = new Map();
    /**
     * The selectors supported by this rule.
     * @type {Set<string>}
     */
    #selectors = new Set();
    /**
     * Adds a property to the rule.
     * @param {string} property The name of the property.
     * @returns {SupportedProperty} The supported property object.
     */
    addProperty(property) {
        if (this.#properties.has(property)) {
            return this.#properties.get(property);
        }
        const supportedProperty = new SupportedProperty(property);
        this.#properties.set(property, supportedProperty);
        return supportedProperty;
    }
    /**
     * Determines if the rule supports a property.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if the property is supported, `false` if not.
     */
    hasProperty(property) {
        return this.#properties.has(property);
    }
    /**
     * Gets the supported property.
     * @param {string} property The name of the property.
     * @returns {SupportedProperty} The supported property.
     */
    getProperty(property) {
        return this.#properties.get(property);
    }
    /**
     * Determines if the rule supports a property value.
     * @param {string} property The name of the property.
     * @param {string} identifier The identifier to check.
     * @returns {boolean} `true` if the property value is supported, `false` if not.
     */
    hasPropertyIdentifier(property, identifier) {
        const supportedProperty = this.#properties.get(property);
        if (!supportedProperty) {
            return false;
        }
        return supportedProperty.hasIdentifier(identifier);
    }
    /**
     * Determines if the rule supports any property values.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if any property values are supported, `false` if not.
     */
    hasPropertyIdentifiers(property) {
        const supportedProperty = this.#properties.get(property);
        if (!supportedProperty) {
            return false;
        }
        return supportedProperty.hasIdentifiers();
    }
    /**
     * Determines if the rule supports a function.
     * @param {string} property The name of the property.
     * @param {string} func The function to check.
     * @returns {boolean} `true` if the function is supported, `false` if not.
     */
    hasFunction(property, func) {
        const supportedProperty = this.#properties.get(property);
        if (!supportedProperty) {
            return false;
        }
        return supportedProperty.hasFunction(func);
    }
    /**
     * Determines if the rule supports any functions.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if any functions are supported, `false` if not.
     */
    hasFunctions(property) {
        const supportedProperty = this.#properties.get(property);
        if (!supportedProperty) {
            return false;
        }
        return supportedProperty.hasFunctions();
    }
    /**
     * Determines if the rule supports a unit.
     * @param {string} property The name of the property.
     * @param {string} unit The unit to check.
     * @returns {boolean} `true` if the unit is supported, `false` if not.
     */
    hasPropertyUnit(property, unit) {
        const supportedProperty = this.#properties.get(property);
        if (!supportedProperty) {
            return false;
        }
        return supportedProperty.hasUnit(unit);
    }
    /**
     * Determines if the rule supports any units.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if any units are supported, `false` if not.
     */
    hasPropertyUnits(property) {
        const supportedProperty = this.#properties.get(property);
        if (!supportedProperty) {
            return false;
        }
        return supportedProperty.hasUnits();
    }
    /**
     * Adds a selector to the rule.
     * @param {string} selector The name of the selector.
     * @returns {void}
     */
    addSelector(selector) {
        this.#selectors.add(selector);
    }
    /**
     * Determines if the rule supports a selector.
     * @param {string} selector The name of the selector.
     * @returns {boolean} `true` if the selector is supported, `false` if not.
     */
    hasSelector(selector) {
        return this.#selectors.has(selector);
    }
}
/**
 * Represents a collection of supports rules.
 */
class SupportsRules {
    /**
     * A collection of supports rules.
     * @type {Array<SupportsRule>}
     */
    #rules = [];
    /**
     * Adds a rule to the collection.
     * @param {SupportsRule} rule The rule to add.
     * @returns {void}
     */
    push(rule) {
        this.#rules.push(rule);
    }
    /**
     * Removes the last rule from the collection.
     * @returns {SupportsRule} The last rule in the collection.
     */
    pop() {
        return this.#rules.pop();
    }
    /**
     * Retrieves the last rule in the collection.
     * @returns {SupportsRule} The last rule in the collection.
     */
    last() {
        return this.#rules.at(-1);
    }
    /**
     * Determines if any rule supports a property.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if any rule supports the property, `false` if not.
     */
    hasProperty(property) {
        return this.#rules.some(rule => rule.hasProperty(property));
    }
    /**
     * Determines if any rule supports a property identifier.
     * @param {string} property The name of the property.
     * @param {string} identifier The identifier to check.
     * @returns {boolean} `true` if any rule supports the property value, `false` if not.
     */
    hasPropertyIdentifier(property, identifier) {
        return this.#rules.some(rule => rule.hasPropertyIdentifier(property, identifier));
    }
    /**
     * Determines if any rule supports any property identifiers.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if any rule supports the property values, `false` if not.
     */
    hasPropertyIdentifiers(property) {
        return this.#rules.some(rule => rule.hasPropertyIdentifiers(property));
    }
    /**
     * Determines if any rule supports a function.
     * @param {string} property The name of the property.
     * @param {string} func The function to check.
     * @returns {boolean} `true` if any rule supports the function, `false` if not.
     */
    hasPropertyFunction(property, func) {
        return this.#rules.some(rule => rule.hasFunction(property, func));
    }
    /**
     * Determines if any rule supports any functions.
     * @param {string} property The name of the property.
     * @returns {boolean} `true` if any rule supports the functions, `false` if not.
     */
    hasPropertyFunctions(property) {
        return this.#rules.some(rule => rule.hasFunctions(property));
    }
    /**
     * Determines if any rule supports a unit.
     * @param {string} property The name of the property.
     * @param {string} unit The unit to check.
     * @returns {boolean} `true` if any rule supports the unit, `false` if not.
     */
    hasPropertyUnit(property, unit) {
        return this.#rules.some(rule => rule.hasPropertyUnit(property, unit));
    }
    /**
     * Determines if any rule supports a selector.
     * @param {string} selector The name of the selector.
     * @returns {boolean} `true` if any rule supports the selector, `false` if not.
     */
    hasSelector(selector) {
        return this.#rules.some(rule => rule.hasSelector(selector));
    }
}
/**
 * Represents the required availability of a feature.
 */
class BaselineAvailability {
    /**
     * The preferred Baseline year.
     * @type {number}
     */
    #baselineYear = undefined;
    /**
     * The preferred Baseline status.
     * @type {number}
     */
    #baselineStatus = undefined;
    /**
     * @param {string | number} availability The required level of feature availability.
     */
    constructor(availability) {
        this.availability = availability;
        if (typeof availability === "number") {
            this.#baselineYear = availability;
        }
        else {
            this.#baselineStatus =
                availability === "widely" ? BASELINE_HIGH : BASELINE_LOW;
        }
    }
    /**
     * Determines whether a feature meets the required availability.
     * @param {Object} encodedStatus A feature's encoded baseline status and year.
     * @returns {boolean} `true` if the feature is supported, `false` if not.
     */
    isSupported(encodedStatus) {
        if (!encodedStatus) {
            // if we don't know the status, assume it's supported
            return true;
        }
        const parts = encodedStatus.split(":");
        const status = Number(parts[0]);
        const year = Number(parts[1] || NaN);
        if (this.#baselineYear) {
            return year <= this.#baselineYear;
        }
        return status >= this.#baselineStatus;
    }
}
//-----------------------------------------------------------------------------
// Rule Definition
//-----------------------------------------------------------------------------
export default /** @satisfies {UseBaselineRuleDefinition} */ ({
    meta: {
        type: "problem",
        docs: {
            description: "Enforce the use of baseline features",
            recommended: true,
            url: "https://github.com/eslint/css/blob/main/docs/rules/use-baseline.md",
        },
        schema: [
            {
                type: "object",
                properties: {
                    available: {
                        anyOf: [
                            {
                                enum: ["widely", "newly"],
                            },
                            {
                                // baseline year
                                type: "integer",
                                minimum: 2000,
                                maximum: new Date().getFullYear(),
                            },
                        ],
                    },
                    allowAtRules: {
                        type: "array",
                        items: {
                            enum: Array.from(atRules.keys()),
                        },
                        uniqueItems: true,
                    },
                    allowFunctions: {
                        type: "array",
                        items: {
                            enum: Array.from(functions.keys()),
                        },
                        uniqueItems: true,
                    },
                    allowMediaConditions: {
                        type: "array",
                        items: {
                            enum: Array.from(mediaConditions.keys()),
                        },
                        uniqueItems: true,
                    },
                    allowProperties: {
                        type: "array",
                        items: {
                            enum: Array.from(properties.keys()),
                        },
                        uniqueItems: true,
                    },
                    allowPropertyValues: {
                        type: "object",
                        properties: Object.fromEntries(Array.from(propertyValues.entries()).map(([prop, valuesMap]) => [
                            prop,
                            {
                                type: "array",
                                items: {
                                    enum: Array.from(valuesMap.keys()),
                                },
                                uniqueItems: true,
                            },
                        ])),
                        additionalProperties: false,
                    },
                    allowSelectors: {
                        type: "array",
                        items: {
                            enum: Array.from(selectors.keys()),
                        },
                        uniqueItems: true,
                    },
                    allowUnits: {
                        type: "array",
                        items: {
                            enum: Array.from(units.keys()),
                        },
                        uniqueItems: true,
                    },
                },
                additionalProperties: false,
            },
        ],
        defaultOptions: [
            {
                available: "widely",
                allowAtRules: [],
                allowFunctions: [],
                allowMediaConditions: [],
                allowProperties: [],
                allowPropertyValues: {},
                allowSelectors: [],
                allowUnits: [],
            },
        ],
        messages: {
            notBaselineProperty: "Property '{{property}}' is not a {{availability}} available baseline feature.",
            notBaselinePropertyValue: "Value '{{value}}' of property '{{property}}' is not a {{availability}} available baseline feature.",
            notBaselineAtRule: "At-rule '@{{atRule}}' is not a {{availability}} available baseline feature.",
            notBaselineFunction: "Function '{{function}}' is not a {{availability}} available baseline feature.",
            notBaselineMediaCondition: "Media condition '{{condition}}' is not a {{availability}} available baseline feature.",
            notBaselineSelector: "Selector '{{selector}}' is not a {{availability}} available baseline feature.",
            notBaselineUnit: "Unit '{{unit}}' is not a {{availability}} available baseline feature.",
        },
    },
    create(context) {
        const baselineAvailability = new BaselineAvailability(context.options[0].available);
        const supportsRules = new SupportsRules();
        const allowAtRules = new Set(context.options[0].allowAtRules);
        const allowProperties = new Set(context.options[0].allowProperties);
        const allowSelectors = new Set(context.options[0].allowSelectors);
        const allowFunctions = new Set(context.options[0].allowFunctions);
        const allowMediaConditions = new Set(context.options[0].allowMediaConditions);
        const allowUnits = new Set(context.options[0].allowUnits);
        const allowPropertyValuesMap = new Map();
        for (const [prop, values] of Object.entries(context.options[0].allowPropertyValues)) {
            allowPropertyValuesMap.set(prop, new Set(values));
        }
        /**
         * Checks a property value identifier to see if it's a baseline feature.
         * @param {string} property The name of the property.
         * @param {Identifier} child The node to check.
         * @returns {void}
         */
        function checkPropertyValueIdentifier(property, child) {
            // named colors are always valid
            if (namedColors.has(child.name)) {
                return;
            }
            const allowedValues = allowPropertyValuesMap.get(property);
            if (allowedValues?.has(child.name)) {
                return;
            }
            const possiblePropertyValues = propertyValues.get(property);
            // if we don't know of any possible property values, just skip it
            if (!possiblePropertyValues) {
                return;
            }
            const featureStatus = possiblePropertyValues.get(child.name);
            // if we don't know of any possible property values, just skip it
            if (featureStatus === undefined) {
                return;
            }
            if (!baselineAvailability.isSupported(featureStatus)) {
                context.report({
                    loc: child.loc,
                    messageId: "notBaselinePropertyValue",
                    data: {
                        property,
                        value: child.name,
                        availability: baselineAvailability.availability,
                    },
                });
            }
        }
        /**
         * Checks a property value function to see if it's a baseline feature.
         * @param {FunctionNodePlain} child The node to check.
         * @returns {void}
         */
        function checkPropertyValueFunction(child) {
            if (allowFunctions.has(child.name)) {
                return;
            }
            const featureStatus = functions.get(child.name);
            // if we don't know of any possible property values, just skip it
            if (featureStatus === undefined) {
                return;
            }
            if (!baselineAvailability.isSupported(featureStatus)) {
                context.report({
                    loc: child.loc,
                    messageId: "notBaselineFunction",
                    data: {
                        function: child.name,
                        availability: baselineAvailability.availability,
                    },
                });
            }
        }
        /**
         * Checks a property value unit to see if it's a baseline feature.
         * @param {string} property The name of the property.
         * @param {Dimension} child The node to check.
         * @returns {void}
         */
        function checkPropertyValueUnit(property, child) {
            if (allowUnits.has(child.unit)) {
                return;
            }
            const featureStatus = units.get(child.unit);
            // if we don't know of this unit, just skip it
            if (featureStatus === undefined) {
                return;
            }
            if (!baselineAvailability.isSupported(featureStatus)) {
                context.report({
                    loc: child.loc,
                    messageId: "notBaselineUnit",
                    data: {
                        unit: child.unit,
                        availability: baselineAvailability.availability,
                    },
                });
            }
        }
        return {
            "Atrule[name=/^supports$/i]"() {
                supportsRules.push(new SupportsRule());
            },
            "Atrule[name=/^supports$/i] > AtrulePrelude > Condition"(node) {
                const supportsRule = supportsRules.last();
                for (let i = 0; i < node.children.length; i++) {
                    const conditionChild = node.children[i];
                    // if a SupportsDeclaration is preceded by "not" then we don't consider it
                    if (conditionChild.type === "Identifier" &&
                        conditionChild.name === "not") {
                        i++;
                        continue;
                    }
                    // save the supported properties and values for this at-rule
                    if (conditionChild.type === "SupportsDeclaration") {
                        const { declaration } = conditionChild;
                        const property = declaration.property;
                        const supportedProperty = supportsRule.addProperty(property);
                        declaration.value.children.forEach(child => {
                            if (child.type === "Identifier") {
                                supportedProperty.addIdentifier(child.name);
                                return;
                            }
                            if (child.type === "Dimension") {
                                supportedProperty.addUnit(child.unit);
                                return;
                            }
                            if (child.type === "Function") {
                                supportedProperty.addFunction(child.name);
                            }
                        });
                        continue;
                    }
                    if (conditionChild.type === "FeatureFunction" &&
                        conditionChild.feature === "selector") {
                        for (const selectorChild of conditionChild.value
                            .children) {
                            supportsRule.addSelector(selectorChild.name);
                        }
                    }
                }
            },
            "Rule > Block > Declaration"(node) {
                const property = node.property;
                // ignore unknown properties - no-invalid-properties already catches this
                if (!properties.has(property)) {
                    return;
                }
                if (allowProperties.has(property)) {
                    return;
                }
                /*
                 * Step 1: Check that the property is in the baseline.
                 *
                 * If the property has been tested in a @supports rule, we don't need to
                 * check it because it won't be applied if the browser doesn't support it.
                 */
                if (!supportsRules.hasProperty(property)) {
                    const featureStatus = properties.get(property);
                    if (!baselineAvailability.isSupported(featureStatus)) {
                        context.report({
                            loc: {
                                start: node.loc.start,
                                end: {
                                    line: node.loc.start.line,
                                    column: node.loc.start.column +
                                        node.property.length,
                                },
                            },
                            messageId: "notBaselineProperty",
                            data: {
                                property,
                                availability: baselineAvailability.availability,
                            },
                        });
                        /*
                         * If the property isn't in baseline, then we don't go
                         * on to check the values. If the property itself isn't
                         * in baseline then chances are the values aren't too,
                         * and there's no need to report multiple errors for the
                         * same property.
                         */
                        return;
                    }
                }
                /*
                 * With tolerant parsing, it's possible that the value is `Raw`
                 * and therefore doesn't have children. If that's the case then
                 * we just exit.
                 */
                if (!node.value?.children) {
                    return;
                }
                /*
                 * Step 2: Check that the property values are in the baseline.
                 */
                for (const child of node.value.children) {
                    if (child.type === "Identifier") {
                        // if the property value has been tested in a @supports rule, don't check it
                        if (!supportsRules.hasPropertyIdentifier(property, child.name)) {
                            checkPropertyValueIdentifier(property, child);
                        }
                        continue;
                    }
                    if (child.type === "Dimension") {
                        if (!supportsRules.hasPropertyUnit(property, child.unit)) {
                            checkPropertyValueUnit(property, child);
                        }
                        continue;
                    }
                    if (child.type === "Function") {
                        if (!supportsRules.hasPropertyFunction(property, child.name)) {
                            checkPropertyValueFunction(child);
                        }
                    }
                }
            },
            "Atrule[name=/^supports$/i]:exit"() {
                supportsRules.pop();
            },
            "Atrule[name=/^media$/i] > AtrulePrelude > MediaQueryList > MediaQuery > Condition"(node) {
                for (const child of node.children) {
                    // ignore unknown media conditions - no-invalid-at-rules already catches this
                    if (!mediaConditions.has(child.name)) {
                        continue;
                    }
                    if (child.type !== "Feature") {
                        continue;
                    }
                    if (allowMediaConditions.has(child.name)) {
                        continue;
                    }
                    const featureStatus = mediaConditions.get(child.name);
                    if (!baselineAvailability.isSupported(featureStatus)) {
                        const loc = child.loc;
                        context.report({
                            loc: {
                                start: {
                                    line: loc.start.line,
                                    // add 1 to account for the @ symbol
                                    column: loc.start.column + 1,
                                },
                                end: {
                                    line: loc.start.line,
                                    column: 
                                    // add 1 to account for the @ symbol
                                    loc.start.column +
                                        child.name.length +
                                        1,
                                },
                            },
                            messageId: "notBaselineMediaCondition",
                            data: {
                                condition: child.name,
                                availability: baselineAvailability.availability,
                            },
                        });
                    }
                }
            },
            Atrule(node) {
                // ignore unknown at-rules - no-invalid-at-rules already catches this
                const atRuleName = node.name.toLowerCase();
                if (!atRules.has(atRuleName)) {
                    return;
                }
                if (allowAtRules.has(atRuleName)) {
                    return;
                }
                const featureStatus = atRules.get(atRuleName);
                if (!baselineAvailability.isSupported(featureStatus)) {
                    const loc = node.loc;
                    context.report({
                        loc: {
                            start: loc.start,
                            end: {
                                line: loc.start.line,
                                // add 1 to account for the @ symbol
                                column: loc.start.column + node.name.length + 1,
                            },
                        },
                        messageId: "notBaselineAtRule",
                        data: {
                            atRule: node.name,
                            availability: baselineAvailability.availability,
                        },
                    });
                }
            },
            "PseudoClassSelector,PseudoElementSelector"(node) {
                const selector = node.name;
                if (!selectors.has(selector)) {
                    return;
                }
                if (allowSelectors.has(selector)) {
                    return;
                }
                // if the selector has been tested in a @supports rule, don't check it
                if (supportsRules.hasSelector(selector)) {
                    return;
                }
                const featureStatus = selectors.get(selector);
                if (!baselineAvailability.isSupported(featureStatus)) {
                    const loc = node.loc;
                    // some selectors are prefixed with the : or :: symbols
                    let prefixSymbolLength = 0;
                    if (node.type.startsWith("PseudoClass")) {
                        prefixSymbolLength = 1;
                    }
                    else if (node.type.startsWith("PseudoElement")) {
                        prefixSymbolLength = 2;
                    }
                    context.report({
                        loc: {
                            start: loc.start,
                            end: {
                                line: loc.start.line,
                                column: loc.start.column +
                                    selector.length +
                                    prefixSymbolLength,
                            },
                        },
                        messageId: "notBaselineSelector",
                        data: {
                            selector,
                            availability: baselineAvailability.availability,
                        },
                    });
                }
            },
            NestingSelector(node) {
                // NestingSelector implies CSS nesting
                const selector = "nesting";
                if (allowSelectors.has(selector)) {
                    return;
                }
                const featureStatus = selectors.get(selector);
                if (baselineAvailability.isSupported(featureStatus)) {
                    return;
                }
                context.report({
                    loc: node.loc,
                    messageId: "notBaselineSelector",
                    data: {
                        selector,
                        availability: baselineAvailability.availability,
                    },
                });
            },
        };
    },
});
