import { createTypeSpecLibrary, paramMessage, compilerAssert, getTypeName, getNamespaceFullName, navigateProgram, isTemplateInstance, getService, NoTarget, isType } from '@typespec/compiler';

const { reportDiagnostic, createStateSymbol, stateKeys: VersioningStateKeys, } = createTypeSpecLibrary({
    name: "@typespec/versioning",
    diagnostics: {
        "versioned-dependency-tuple": {
            severity: "error",
            messages: {
                default: `Versioned dependency mapping must be a tuple [SourceVersion, TargetVersion].`,
            },
        },
        "versioned-dependency-tuple-enum-member": {
            severity: "error",
            messages: {
                default: `Versioned dependency mapping must be between enum members.`,
            },
        },
        "versioned-dependency-same-namespace": {
            severity: "error",
            messages: {
                default: `Versioned dependency mapping must all point to the same namespace but 2 versions have different namespaces '${"namespace1"}' and '${"namespace2"}'.`,
            },
        },
        "versioned-dependency-record-not-mapping": {
            severity: "error",
            messages: {
                default: paramMessage `The versionedDependency decorator must provide a model mapping local versions to dependency '${"dependency"}' versions`,
            },
        },
        "versioned-dependency-not-picked": {
            severity: "error",
            messages: {
                default: paramMessage `The versionedDependency decorator must provide a version of the dependency '${"dependency"}'.`,
            },
        },
        "version-not-found": {
            severity: "error",
            messages: {
                default: paramMessage `The provided version '${"version"}' from '${"enumName"}' is not declared as a version enum. Use '@versioned(${"enumName"})' on the containing namespace.`,
            },
        },
        "version-duplicate": {
            severity: "error",
            messages: {
                default: paramMessage `Multiple versions from '${"name"}' resolve to the same value. Version enums must resolve to unique values.`,
            },
        },
        "using-versioned-library": {
            severity: "error",
            messages: {
                default: paramMessage `Namespace '${"sourceNs"}' is referencing types from versioned namespace '${"targetNs"}' but didn't specify which versions with @useDependency.`,
            },
        },
        "invalid-renamed-from-value": {
            severity: "error",
            messages: {
                default: "@renamedFrom.oldName cannot be empty string.",
            },
        },
        "no-service-fixed-version": {
            severity: "error",
            messages: {
                default: paramMessage `Namespace '${"name"}' cannot specify a fixed service version with @service({version: ${"version"}}) while using @versioned. Remove the version argument from @service.`,
            },
        },
        "incompatible-versioned-reference": {
            severity: "error",
            messages: {
                default: paramMessage `'${"sourceName"}' is referencing versioned type '${"targetName"}' but is not versioned itself.`,
                addedAfter: paramMessage `'${"sourceName"}' was added in version '${"sourceAddedOn"}' but referencing type '${"targetName"}' added in version '${"targetAddedOn"}'.`,
                dependentAddedAfter: paramMessage `'${"sourceName"}' was added in version '${"sourceAddedOn"}' but contains type '${"targetName"}' added in version '${"targetAddedOn"}'.`,
                removedBefore: paramMessage `'${"sourceName"}' was removed in version '${"sourceRemovedOn"}' but referencing type '${"targetName"}' removed in version '${"targetRemovedOn"}'.`,
                dependentRemovedBefore: paramMessage `'${"sourceName"}' was removed in version '${"sourceRemovedOn"}' but contains type '${"targetName"}' removed in version '${"targetRemovedOn"}'.`,
                versionedDependencyAddedAfter: paramMessage `'${"sourceName"}' is referencing type '${"targetName"}' added in version '${"targetAddedOn"}' but version used is ${"dependencyVersion"}.`,
                versionedDependencyRemovedBefore: paramMessage `'${"sourceName"}' is referencing type '${"targetName"}' removed in version '${"targetAddedOn"}' but version used is ${"dependencyVersion"}.`,
                doesNotExist: paramMessage `'${"sourceName"}' is referencing type '${"targetName"}' which does not exist in version '${"version"}'.`,
            },
        },
        "incompatible-versioned-namespace-use-dependency": {
            severity: "error",
            messages: {
                default: "The useDependency decorator can only be used on a Namespace if the namespace is unversioned. For versioned namespaces, put the useDependency decorator on the version enum members.",
            },
        },
        "made-optional-not-optional": {
            severity: "error",
            messages: {
                default: paramMessage `Property '${"name"}' marked with @madeOptional but is required. Should be '${"name"}?'`,
            },
        },
        "made-required-optional": {
            severity: "error",
            messages: {
                default: paramMessage `Property '${"name"}?' marked with @madeRequired but is optional. Should be '${"name"}'`,
            },
        },
        "renamed-duplicate-property": {
            severity: "error",
            messages: {
                default: paramMessage `Property '${"name"}' marked with '@renamedFrom' conflicts with existing property in version ${"version"}.`,
            },
        },
    },
    state: {
        versionIndex: { description: "Version index" },
        addedOn: { description: "State for @addedOn decorator" },
        removedOn: { description: "State for @removedOn decorator" },
        versions: { description: "State for @versioned decorator" },
        useDependencyNamespace: { description: "State for @useDependency decorator on Namespaces" },
        useDependencyEnum: { description: "State for @useDependency decorator on Enums" },
        renamedFrom: { description: "State for @renamedFrom decorator" },
        madeOptional: { description: "State for @madeOptional decorator" },
        madeRequired: { description: "State for @madeRequired decorator" },
        typeChangedFrom: { description: "State for @typeChangedFrom decorator" },
        returnTypeChangedFrom: { description: "State for @returnTypeChangedFrom decorator" },
    },
});

/**
 * Represent a timeline of all the version involved in the versioning of a namespace
 *
 * @example
 * Given the following namespaces with their versions
 * ```
 * Library:
 *  l1
 *  l2
 *  l3
 *  l4
 *
 * Service:
 *  v1  -> (using) l1
 *  v2  -> (using) l3
 *  v3  -> (using) l3
 * ```
 *
 * This would be the data passed to the constructor
 * ```ts
 * new VersioningTimeline(program, [
 *   new Map([[serviceNs, v1], [libraryNs, l1]]),
 *   new Map([[serviceNs, v2], [libraryNs, l3]]),
 *   new Map([[serviceNs, v3], [libraryNs, l3]]),
 * ])
 * ```
 *
 * The following timeline is going to be represented
 *
 * | Service | Library |
 * |---------|---------|
 * |   v1    |   l1    |
 * |         |   l2    |
 * |   v2    |   l3    |
 * |   v3    |   l3    |
 * |         |   l4    |
 */
class VersioningTimeline {
    #namespaces;
    #timeline;
    #momentIndex;
    #versionIndex;
    constructor(program, resolutions) {
        const indexedVersions = new Set();
        const namespaces = new Set();
        const timeline = (this.#timeline = resolutions.map((x) => new TimelineMoment(x)));
        for (const resolution of resolutions) {
            for (const [namespace, version] of resolution.entries()) {
                indexedVersions.add(version);
                namespaces.add(namespace);
            }
        }
        this.#namespaces = [...namespaces];
        function findIndexToInsert(version) {
            for (const [index, moment] of timeline.entries()) {
                const versionAtMoment = moment.getVersion(version.namespace);
                if (versionAtMoment && version.index < versionAtMoment.index) {
                    return index;
                }
            }
            return -1;
        }
        for (const namespace of namespaces) {
            const [, versions] = getVersions(program, namespace);
            if (versions === undefined) {
                continue;
            }
            for (const version of versions.getVersions()) {
                if (!indexedVersions.has(version)) {
                    indexedVersions.add(version);
                    const index = findIndexToInsert(version);
                    const newMoment = new TimelineMoment(new Map([[version.namespace, version]]));
                    if (index === -1) {
                        timeline.push(newMoment);
                    }
                    else {
                        timeline.splice(index, 0, newMoment);
                    }
                }
            }
        }
        this.#versionIndex = new Map();
        this.#momentIndex = new Map();
        for (const [index, moment] of timeline.entries()) {
            this.#momentIndex.set(moment, index);
            for (const version of moment.versions()) {
                if (!this.#versionIndex.has(version)) {
                    this.#versionIndex.set(version, index);
                }
            }
        }
    }
    prettySerialize() {
        const hSep = "-".repeat(this.#namespaces.length * 13 + 1);
        const content = this.#timeline
            .map((moment) => {
            return ("| " +
                this.#namespaces
                    .map((x) => (moment.getVersion(x)?.name ?? "").padEnd(10, " "))
                    .join(" | ") +
                " |");
        })
            .join(`\n${hSep}\n`);
        return ["", hSep, content, hSep].join("\n");
    }
    get(version) {
        const index = this.getIndex(version);
        if (index === -1) {
            if (version instanceof TimelineMoment) {
                compilerAssert(false, `Timeline moment "${version?.name}" should have been resolved`);
            }
            else {
                compilerAssert(false, `Version "${version?.name}" from ${getTypeName(version.namespace)} should have been resolved. ${this.prettySerialize()}`);
            }
        }
        return this.#timeline[index];
    }
    /**
     * Return index in the timeline that this version points to
     * Returns -1 if version is not found.
     */
    getIndex(version) {
        const index = version instanceof TimelineMoment
            ? this.#momentIndex.get(version)
            : this.#versionIndex.get(version);
        if (index === undefined) {
            return -1;
        }
        return index;
    }
    /**
     * Return true if {@link isBefore} is before {@link base}
     * @param isBefore
     * @param base
     */
    isBefore(isBefore, base) {
        const isBeforeIndex = this.getIndex(isBefore);
        const baseIndex = this.getIndex(base);
        return isBeforeIndex < baseIndex;
    }
    first() {
        return this.#timeline[0];
    }
    [Symbol.iterator]() {
        return this.#timeline[Symbol.iterator]();
    }
    entries() {
        return this.#timeline.entries();
    }
}
class TimelineMoment {
    name;
    #versionMap;
    constructor(versionMap) {
        this.#versionMap = versionMap;
        this.name = versionMap.values().next().value ?? "";
    }
    getVersion(namespace) {
        return this.#versionMap.get(namespace);
    }
    versions() {
        return this.#versionMap.values();
    }
}

function getVersionDependencies(program, namespace) {
    const useDeps = getUseDependencies(program, namespace);
    if (useDeps) {
        return useDeps;
    }
    return undefined;
}
/**
 * Resolve the version of dependencies
 * @param initialResolutions
 */
function resolveDependencyVersions(program, initialResolutions) {
    const resolutions = new Map(initialResolutions);
    const namespacesToCheck = [...initialResolutions.entries()];
    while (namespacesToCheck.length > 0) {
        const [current, currentVersion] = namespacesToCheck.pop();
        const dependencies = getVersionDependencies(program, current);
        for (const [dependencyNs, versionMap] of dependencies ?? new Map()) {
            if (resolutions.has(dependencyNs)) {
                continue; // Already resolved.
            }
            if (!(versionMap instanceof Map)) {
                const rootNsName = getNamespaceFullName(current);
                const dependencyNsName = getNamespaceFullName(dependencyNs);
                throw new Error(`Unexpected error: Namespace ${rootNsName} version dependency to ${dependencyNsName} should be a mapping of version.`);
            }
            const dependencyVersion = versionMap.get(currentVersion);
            namespacesToCheck.push([dependencyNs, dependencyVersion]);
            resolutions.set(dependencyNs, dependencyVersion);
        }
    }
    return resolutions;
}
/**
 * Resolve the version to use for all namespace for each of the root namespace versions.
 * @param program
 * @param rootNs Root namespace.
 */
function resolveVersions(program, namespace) {
    const [rootNs, versions] = getVersions(program, namespace);
    const dependencies = (rootNs && getVersionDependencies(program, rootNs)) ??
        new Map();
    if (!versions) {
        if (dependencies.size === 0) {
            return [{ rootVersion: undefined, versions: new Map() }];
        }
        else {
            const map = new Map();
            for (const [dependencyNs, version] of dependencies) {
                if (version instanceof Map) {
                    const rootNsName = getNamespaceFullName(namespace);
                    const dependencyNsName = getNamespaceFullName(dependencyNs);
                    throw new Error(`Unexpected error: Namespace ${rootNsName} version dependency to ${dependencyNsName} should be a picked version.`);
                }
                map.set(dependencyNs, version);
            }
            return [{ rootVersion: undefined, versions: resolveDependencyVersions(program, map) }];
        }
    }
    else {
        return versions.getVersions().map((version) => {
            const resolutions = resolveDependencyVersions(program, new Map([[rootNs, version]]));
            return {
                rootVersion: version,
                versions: resolutions,
            };
        });
    }
}
const versionCache = new WeakMap();
function cacheVersion(key, versions) {
    versionCache.set(key, versions);
    return versions;
}
function getVersionsForEnum(program, en) {
    const namespace = en.namespace;
    if (namespace === undefined) {
        return [];
    }
    const nsVersion = getVersion(program, namespace);
    if (nsVersion === undefined) {
        return [];
    }
    return [namespace, nsVersion];
}
function getVersions(p, t) {
    const existing = versionCache.get(t);
    if (existing) {
        return existing;
    }
    switch (t.kind) {
        case "Namespace":
            return resolveVersionsForNamespace(p, t);
        case "Operation":
        case "Interface":
        case "Model":
        case "Union":
        case "Scalar":
        case "Enum":
            if (t.namespace) {
                return cacheVersion(t, getVersions(p, t.namespace) || []);
            }
            else if (t.kind === "Operation" && t.interface) {
                return cacheVersion(t, getVersions(p, t.interface) || []);
            }
            else {
                return cacheVersion(t, []);
            }
        case "ModelProperty":
            if (t.sourceProperty) {
                return getVersions(p, t.sourceProperty);
            }
            else if (t.model) {
                return getVersions(p, t.model);
            }
            else {
                return cacheVersion(t, []);
            }
        case "EnumMember":
            return cacheVersion(t, getVersions(p, t.enum) || []);
        case "UnionVariant":
            return cacheVersion(t, getVersions(p, t.union) || []);
        default:
            return cacheVersion(t, []);
    }
}
function resolveVersionsForNamespace(program, namespace) {
    const nsVersion = getVersion(program, namespace);
    if (nsVersion !== undefined) {
        return cacheVersion(namespace, [namespace, nsVersion]);
    }
    const parentNamespaceVersion = namespace.namespace && getVersions(program, namespace.namespace)[1];
    const hasDependencies = getUseDependencies(program, namespace);
    if (parentNamespaceVersion || hasDependencies) {
        return cacheVersion(namespace, [namespace, parentNamespaceVersion]);
    }
    else {
        return cacheVersion(namespace, [namespace, undefined]);
    }
}
function getAllVersions$1(p, t) {
    const [namespace, _] = getVersions(p, t);
    if (namespace === undefined)
        return undefined;
    return getVersion(p, namespace)?.getVersions();
}
var Availability;
(function (Availability) {
    Availability["Unavailable"] = "Unavailable";
    Availability["Added"] = "Added";
    Availability["Available"] = "Available";
    Availability["Removed"] = "Removed";
})(Availability || (Availability = {}));
function getParentAddedVersion(program, type, versions) {
    let parentMap = undefined;
    if (type.kind === "ModelProperty" && type.model !== undefined) {
        parentMap = getAvailabilityMap(program, type.model);
    }
    else if (type.kind === "Operation" && type.interface !== undefined) {
        parentMap = getAvailabilityMap(program, type.interface);
    }
    if (parentMap === undefined)
        return undefined;
    for (const [key, value] of parentMap.entries()) {
        if (value === Availability.Added) {
            return versions.find((x) => x.name === key);
        }
    }
    return undefined;
}
function getParentAddedVersionInTimeline(program, type, timeline) {
    let parentMap = undefined;
    if (type.kind === "ModelProperty" && type.model !== undefined) {
        parentMap = getAvailabilityMapInTimeline(program, type.model, timeline);
    }
    else if (type.kind === "Operation" && type.interface !== undefined) {
        parentMap = getAvailabilityMapInTimeline(program, type.interface, timeline);
    }
    if (parentMap === undefined)
        return undefined;
    for (const [moment, availability] of parentMap.entries()) {
        if (availability === Availability.Added) {
            return moment.versions().next().value;
        }
    }
    return undefined;
}
/**
 * Uses the added, removed and parent metadata to resolve any issues with
 * implicit versioning and return the added array with this taken into account.
 * @param added the array of versions from the `@added` decorator
 * @param removed the array of versions from the `@removed` decorator
 * @param parentAdded the version when the parent type was added
 * @returns the added array, with any implicit versioning taken into consideration.
 */
function resolveWhenFirstAdded(added, removed, parentAdded) {
    const implicitlyAvailable = !added.length && !removed.length;
    if (implicitlyAvailable) {
        // if type has no version info, it inherits from the parent
        return [parentAdded];
    }
    if (added.length) {
        const addedFirst = !removed.length || added[0].index < removed[0].index;
        if (addedFirst) {
            // if the type was added first, then implicitly it wasn't available before
            // and thus should NOT inherit from its parent
            return added;
        }
    }
    if (removed.length) {
        const removedFirst = !added.length || removed[0].index < added[0].index;
        if (removedFirst) {
            // if the type was removed first the implicitly it was available before
            // and thus SHOULD inherit from its parent
            return [parentAdded, ...added];
        }
    }
    // we shouldn't get here, but if we do, then make no change to the added array
    return added;
}
function getAvailabilityMap(program, type) {
    const avail = new Map();
    const allVersions = getAllVersions$1(program, type);
    // if unversioned then everything exists
    if (allVersions === undefined)
        return undefined;
    const firstVersion = allVersions[0];
    const parentAdded = getParentAddedVersion(program, type, allVersions) ?? firstVersion;
    let added = getAddedOnVersions(program, type) ?? [];
    const removed = getRemovedOnVersions(program, type) ?? [];
    const typeChanged = getTypeChangedFrom(program, type);
    const returnTypeChanged = getReturnTypeChangedFrom(program, type);
    // if there's absolutely no versioning information, return undefined
    // contextually, this might mean it inherits its versioning info from a parent
    // or that it is treated as unversioned
    if (!added.length &&
        !removed.length &&
        typeChanged === undefined &&
        returnTypeChanged === undefined)
        return undefined;
    added = resolveWhenFirstAdded(added, removed, parentAdded);
    // something isn't available by default
    let isAvail = false;
    for (const ver of allVersions) {
        const add = added.find((x) => x.index === ver.index);
        const rem = removed.find((x) => x.index === ver.index);
        if (rem) {
            isAvail = false;
            avail.set(ver.name, Availability.Removed);
        }
        else if (add) {
            isAvail = true;
            avail.set(ver.name, Availability.Added);
        }
        else if (isAvail) {
            avail.set(ver.name, Availability.Available);
        }
        else {
            avail.set(ver.name, Availability.Unavailable);
        }
    }
    return avail;
}
function getAvailabilityMapInTimeline(program, type, timeline) {
    const avail = new Map();
    const firstVersion = timeline.first().versions().next().value;
    const parentAdded = getParentAddedVersionInTimeline(program, type, timeline) ?? firstVersion;
    let added = getAddedOnVersions(program, type) ?? [];
    const removed = getRemovedOnVersions(program, type) ?? [];
    const typeChanged = getTypeChangedFrom(program, type);
    const returnTypeChanged = getReturnTypeChangedFrom(program, type);
    // if there's absolutely no versioning information, return undefined
    // contextually, this might mean it inherits its versioning info from a parent
    // or that it is treated as unversioned
    if (!added.length &&
        !removed.length &&
        typeChanged === undefined &&
        returnTypeChanged === undefined)
        return undefined;
    added = resolveWhenFirstAdded(added, removed, parentAdded);
    // something isn't available by default
    let isAvail = false;
    for (const [index, moment] of timeline.entries()) {
        const add = added.find((x) => timeline.getIndex(x) === index);
        const rem = removed.find((x) => timeline.getIndex(x) === index);
        if (rem) {
            isAvail = false;
            avail.set(moment, Availability.Removed);
        }
        else if (add) {
            isAvail = true;
            avail.set(moment, Availability.Added);
        }
        else if (isAvail) {
            avail.set(moment, Availability.Available);
        }
        else {
            avail.set(moment, Availability.Unavailable);
        }
    }
    return avail;
}
function getVersionForEnumMember(program, member) {
    // Always lookup for the original type. This ensure reference equality when comparing versions.
    member = member.projectionBase ?? member;
    const parentEnum = member.enum;
    const [, versions] = getVersionsForEnum(program, parentEnum);
    return versions?.getVersionForEnumMember(member);
}

const namespace$1 = "TypeSpec.Versioning";
function checkIsVersion(program, enumMember, diagnosticTarget) {
    const version = getVersionForEnumMember(program, enumMember);
    if (!version) {
        reportDiagnostic(program, {
            code: "version-not-found",
            target: diagnosticTarget,
            format: { version: enumMember.name, enumName: enumMember.enum.name },
        });
    }
    return version;
}
const $added = (context, t, v) => {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    // retrieve statemap to update or create a new one
    const record = program.stateMap(VersioningStateKeys.addedOn).get(t) ?? new Array();
    record.push(version);
    // ensure that records are stored in ascending order
    record.sort((a, b) => a.index - b.index);
    program.stateMap(VersioningStateKeys.addedOn).set(t, record);
};
function $removed(context, t, v) {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    // retrieve statemap to update or create a new one
    const record = program.stateMap(VersioningStateKeys.removedOn).get(t) ?? new Array();
    record.push(version);
    // ensure that records are stored in ascending order
    record.sort((a, b) => a.index - b.index);
    program.stateMap(VersioningStateKeys.removedOn).set(t, record);
}
/**
 * Returns the mapping of versions to old type values, if applicable
 * @param p TypeSpec program
 * @param t type to query
 * @returns Map of versions to old types, if any
 */
function getTypeChangedFrom(p, t) {
    return p.stateMap(VersioningStateKeys.typeChangedFrom).get(t);
}
const $typeChangedFrom = (context, prop, v, oldType) => {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    // retrieve statemap to update or create a new one
    let record = getTypeChangedFrom(program, prop) ?? new Map();
    record.set(version, oldType);
    // ensure the map is sorted by version
    record = new Map([...record.entries()].sort((a, b) => a[0].index - b[0].index));
    program.stateMap(VersioningStateKeys.typeChangedFrom).set(prop, record);
};
/**
 * Returns the mapping of versions to old return type values, if applicable
 * @param p TypeSpec program
 * @param t type to query
 * @returns Map of versions to old types, if any
 */
function getReturnTypeChangedFrom(p, t) {
    return p.stateMap(VersioningStateKeys.returnTypeChangedFrom).get(t);
}
const $returnTypeChangedFrom = (context, op, v, oldReturnType) => {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    // retrieve statemap to update or create a new one
    let record = getReturnTypeChangedFrom(program, op) ?? new Map();
    record.set(version, oldReturnType);
    // ensure the map is sorted by version
    record = new Map([...record.entries()].sort((a, b) => a[0].index - b[0].index));
    program.stateMap(VersioningStateKeys.returnTypeChangedFrom).set(op, record);
};
const $renamedFrom = (context, t, v, oldName) => {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    if (oldName === "") {
        reportDiagnostic(program, {
            code: "invalid-renamed-from-value",
            target: t,
        });
    }
    // retrieve statemap to update or create a new one
    const record = getRenamedFrom(program, t) ?? [];
    record.push({ version: version, oldName: oldName });
    // ensure that records are stored in ascending order
    record.sort((a, b) => a.version.index - b.version.index);
    program.stateMap(VersioningStateKeys.renamedFrom).set(t, record);
};
const $madeOptional = (context, t, v) => {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    program.stateMap(VersioningStateKeys.madeOptional).set(t, version);
};
const $madeRequired = (context, t, v) => {
    const { program } = context;
    const version = checkIsVersion(context.program, v, context.getArgumentTarget(0));
    if (!version) {
        return;
    }
    program.stateMap(VersioningStateKeys.madeRequired).set(t, version);
};
/**
 * @returns version when the given type was made required if applicable.
 */
function getMadeRequiredOn(p, t) {
    return p.stateMap(VersioningStateKeys.madeRequired).get(t);
}
/**
 * @returns the array of RenamedFrom metadata if applicable.
 */
function getRenamedFrom(p, t) {
    return p.stateMap(VersioningStateKeys.renamedFrom).get(t);
}
/**
 * @returns the list of versions for which this decorator has been applied
 */
function getRenamedFromVersions(p, t) {
    return getRenamedFrom(p, t)?.map((x) => x.version);
}
function getAddedOnVersions(p, t) {
    return p.stateMap(VersioningStateKeys.addedOn).get(t);
}
function getRemovedOnVersions(p, t) {
    return p.stateMap(VersioningStateKeys.removedOn).get(t);
}
/**
 * @returns version when the given type was made optional if applicable.
 */
function getMadeOptionalOn(p, t) {
    return p.stateMap(VersioningStateKeys.madeOptional).get(t);
}
class VersionMap {
    map = new Map();
    constructor(namespace, enumType) {
        let index = 0;
        for (const member of enumType.members.values()) {
            this.map.set(member, {
                name: member.name,
                value: member.value?.toString() ?? member.name,
                enumMember: member,
                index,
                namespace,
            });
            index++;
        }
    }
    getVersionForEnumMember(member) {
        return this.map.get(member);
    }
    getVersions() {
        return [...this.map.values()];
    }
    get size() {
        return this.map.size;
    }
}
const $versioned = (context, t, versions) => {
    context.program.stateMap(VersioningStateKeys.versions).set(t, new VersionMap(t, versions));
};
/**
 * Get the version map of the namespace.
 */
function getVersion(program, namespace) {
    return program.stateMap(VersioningStateKeys.versions).get(namespace);
}
function findVersionedNamespace(program, namespace) {
    let current = namespace;
    while (current) {
        if (program.stateMap(VersioningStateKeys.versions).has(current)) {
            return current;
        }
        current = current.namespace;
    }
    return undefined;
}
function $useDependency(context, target, ...versionRecords) {
    const versions = [];
    // ensure only valid versions are passed in
    for (const record of versionRecords) {
        const ver = checkIsVersion(context.program, record, context.getArgumentTarget(0));
        if (ver) {
            versions.push(ver);
        }
    }
    if (target.kind === "Namespace") {
        let state = getNamespaceUseDependencyState(context.program, target);
        if (!state) {
            state = versions;
        }
        else {
            state.push(...versions);
        }
        context.program.stateMap(VersioningStateKeys.useDependencyNamespace).set(target, state);
    }
    else if (target.kind === "EnumMember") {
        const targetEnum = target.enum;
        let state = context.program
            .stateMap(VersioningStateKeys.useDependencyEnum)
            .get(targetEnum);
        if (!state) {
            state = new Map();
        }
        // get any existing versions and combine them
        const currentVersions = state.get(target) ?? [];
        currentVersions.push(...versions);
        state.set(target, currentVersions);
        context.program.stateMap(VersioningStateKeys.useDependencyEnum).set(targetEnum, state);
    }
}
function getNamespaceUseDependencyState(program, target) {
    return program.stateMap(VersioningStateKeys.useDependencyNamespace).get(target);
}
function getUseDependencies(program, target, searchEnum = true) {
    const result = new Map();
    if (target.kind === "Namespace") {
        let current = target;
        while (current) {
            const data = getNamespaceUseDependencyState(program, current);
            if (!data) {
                // See if the namspace has a version enum
                if (searchEnum) {
                    const versions = getVersion(program, current)?.getVersions();
                    if (versions?.length) {
                        const enumDeps = getUseDependencies(program, versions[0].enumMember.enum);
                        if (enumDeps) {
                            return enumDeps;
                        }
                    }
                }
                current = current.namespace;
            }
            else {
                for (const v of data) {
                    result.set(v.namespace, v);
                }
                return result;
            }
        }
        return undefined;
    }
    else if (target.kind === "Enum") {
        const data = program.stateMap(VersioningStateKeys.useDependencyEnum).get(target);
        if (!data) {
            return undefined;
        }
        const resolved = resolveVersionDependency(program, data);
        if (resolved instanceof Map) {
            for (const [enumVer, value] of resolved) {
                for (const val of value) {
                    const targetNamespace = val.enumMember.enum.namespace;
                    if (!targetNamespace) {
                        reportDiagnostic(program, {
                            code: "version-not-found",
                            target: val.enumMember.enum,
                            format: { version: val.enumMember.name, enumName: val.enumMember.enum.name },
                        });
                        return undefined;
                    }
                    let subMap = result.get(targetNamespace);
                    if (subMap) {
                        subMap.set(enumVer, val);
                    }
                    else {
                        subMap = new Map([[enumVer, val]]);
                    }
                    result.set(targetNamespace, subMap);
                }
            }
        }
    }
    return result;
}
function resolveVersionDependency(program, data) {
    if (!(data instanceof Map)) {
        return data;
    }
    const mapping = new Map();
    for (const [key, value] of data) {
        const sourceVersion = getVersionForEnumMember(program, key);
        if (sourceVersion !== undefined) {
            mapping.set(sourceVersion, value);
        }
    }
    return mapping;
}

var f0 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    $added: $added,
    $madeOptional: $madeOptional,
    $madeRequired: $madeRequired,
    $removed: $removed,
    $renamedFrom: $renamedFrom,
    $returnTypeChangedFrom: $returnTypeChangedFrom,
    $typeChangedFrom: $typeChangedFrom,
    $useDependency: $useDependency,
    $versioned: $versioned,
    VersionMap: VersionMap,
    findVersionedNamespace: findVersionedNamespace,
    getAddedOnVersions: getAddedOnVersions,
    getMadeOptionalOn: getMadeOptionalOn,
    getMadeRequiredOn: getMadeRequiredOn,
    getRemovedOnVersions: getRemovedOnVersions,
    getRenamedFrom: getRenamedFrom,
    getRenamedFromVersions: getRenamedFromVersions,
    getReturnTypeChangedFrom: getReturnTypeChangedFrom,
    getTypeChangedFrom: getTypeChangedFrom,
    getUseDependencies: getUseDependencies,
    getVersion: getVersion,
    namespace: namespace$1
});

/**
 * @internal
 */
function indexTimeline(program, timeline, projectingMoment) {
    const versionKey = program.checker.createType({
        kind: "Object",
        properties: {},
    });
    program
        .stateMap(VersioningStateKeys.versionIndex)
        .set(versionKey, { timeline, projectingMoment });
    return versionKey;
}
function buildVersionProjections(program, rootNs) {
    const resolutions = resolveVersions(program, rootNs);
    const timeline = new VersioningTimeline(program, resolutions.map((x) => x.versions));
    return resolutions.map((resolution) => {
        if (resolution.versions.size === 0) {
            return { version: undefined, projections: [] };
        }
        else {
            const versionKey = indexTimeline(program, timeline, timeline.get(resolution.versions.values().next().value));
            return {
                version: resolution.rootVersion?.value,
                projections: [
                    {
                        projectionName: "v",
                        arguments: [versionKey],
                    },
                ],
            };
        }
    });
}

function $onValidate(program) {
    const namespaceDependencies = new Map();
    function addNamespaceDependency(source, target) {
        if (!target || !("namespace" in target) || !target.namespace) {
            return;
        }
        const set = namespaceDependencies.get(source) ?? new Set();
        if (target.namespace !== source) {
            set.add(target.namespace);
        }
        namespaceDependencies.set(source, set);
    }
    navigateProgram(program, {
        model: (model) => {
            // If this is an instantiated type we don't want to keep the mapping.
            if (isTemplateInstance(model)) {
                return;
            }
            addNamespaceDependency(model.namespace, model.sourceModel);
            addNamespaceDependency(model.namespace, model.baseModel);
            for (const prop of model.properties.values()) {
                addNamespaceDependency(model.namespace, prop.type);
                // Validate model -> property have correct versioning
                validateTargetVersionCompatible(program, model, prop, {
                    isTargetADependent: true,
                });
                // Validate model property -> type have correct versioning
                const typeChangedFrom = getTypeChangedFrom(program, prop);
                if (typeChangedFrom !== undefined) {
                    validateMultiTypeReference(program, prop);
                }
                else {
                    validateReference(program, prop, prop.type);
                }
                // Validate model property type is correct when madeOptional
                validateMadeOptional(program, prop);
                // Validate model property type is correct when madeRequired
                validateMadeRequired(program, prop);
            }
            validateVersionedPropertyNames(program, model);
        },
        union: (union) => {
            // If this is an instantiated type we don't want to keep the mapping.
            if (isTemplateInstance(union)) {
                return;
            }
            if (union.namespace === undefined) {
                return;
            }
            for (const variant of union.variants.values()) {
                addNamespaceDependency(union.namespace, variant.type);
            }
            validateVersionedPropertyNames(program, union);
        },
        operation: (op) => {
            // If this is an instantiated type we don't want to keep the mapping.
            if (isTemplateInstance(op)) {
                return;
            }
            const namespace = op.namespace ?? op.interface?.namespace;
            addNamespaceDependency(namespace, op.sourceOperation);
            addNamespaceDependency(namespace, op.returnType);
            if (op.interface) {
                validateTargetVersionCompatible(program, op.interface, op, { isTargetADependent: true });
            }
            validateReference(program, op, op.returnType);
        },
        interface: (iface) => {
            for (const source of iface.sourceInterfaces) {
                validateReference(program, iface, source);
            }
        },
        namespace: (namespace) => {
            const [_, versionMap] = getVersions(program, namespace);
            validateVersionEnumValuesUnique(program, namespace);
            const serviceProps = getService(program, namespace);
            // eslint-disable-next-line deprecation/deprecation
            if (serviceProps?.version !== undefined && versionMap !== undefined) {
                reportDiagnostic(program, {
                    code: "no-service-fixed-version",
                    format: {
                        name: getNamespaceFullName(namespace),
                        // eslint-disable-next-line deprecation/deprecation
                        version: serviceProps.version,
                    },
                    target: namespace,
                });
            }
            const versionedNamespace = findVersionedNamespace(program, namespace);
            const dependencies = getVersionDependencies(program, namespace);
            if (dependencies === undefined) {
                return;
            }
            for (const [dependencyNs, value] of dependencies.entries()) {
                if (versionedNamespace) {
                    const usingUseDependency = getUseDependencies(program, namespace, false) !== undefined;
                    if (usingUseDependency) {
                        reportDiagnostic(program, {
                            code: "incompatible-versioned-namespace-use-dependency",
                            target: namespace,
                        });
                    }
                    else if (!(value instanceof Map)) {
                        reportDiagnostic(program, {
                            code: "versioned-dependency-record-not-mapping",
                            format: { dependency: getNamespaceFullName(dependencyNs) },
                            target: namespace,
                        });
                    }
                }
                else {
                    if (value instanceof Map) {
                        reportDiagnostic(program, {
                            code: "versioned-dependency-not-picked",
                            format: { dependency: getNamespaceFullName(dependencyNs) },
                            target: namespace,
                        });
                    }
                }
            }
        },
        enum: (en) => {
            validateVersionedPropertyNames(program, en);
            // construct the list of tuples in the old format if version
            // information is placed in the Version enum members
            const useDependencies = getUseDependencies(program, en);
            if (!useDependencies) {
                return;
            }
            for (const [depNs, deps] of useDependencies) {
                const set = new Set();
                if (deps instanceof Map) {
                    for (const val of deps.values()) {
                        set.add(val.namespace);
                    }
                }
                else {
                    set.add(deps.namespace);
                }
                namespaceDependencies.set(depNs, set);
            }
        },
    }, { includeTemplateDeclaration: true });
    validateVersionedNamespaceUsage(program, namespaceDependencies);
}
function getAllVersions(p, t) {
    const [namespace, _] = getVersions(p, t);
    if (namespace === undefined)
        return undefined;
    return getVersion(p, namespace)?.getVersions();
}
/**
 * Ensures that properties whose type has changed with versioning are valid.
 */
function validateMultiTypeReference(program, source, options) {
    const versionTypeMap = getVersionedTypeMap(program, source);
    if (versionTypeMap === undefined)
        return;
    for (const [version, type] of versionTypeMap) {
        if (type === undefined)
            continue;
        const availMap = getAvailabilityMap(program, type);
        const availability = availMap?.get(version.name) ?? Availability.Available;
        if ([Availability.Added, Availability.Available].includes(availability)) {
            continue;
        }
        reportDiagnostic(program, {
            code: "incompatible-versioned-reference",
            messageId: "doesNotExist",
            format: {
                sourceName: getTypeName(source, options),
                targetName: getTypeName(type, options),
                version: prettyVersion(version),
            },
            target: source,
        });
    }
}
/**
 * Constructs a map of version to name for the the source.
 */
function getVersionedNameMap(program, source) {
    const allVersions = getAllVersions(program, source);
    if (allVersions === undefined)
        return undefined;
    const map = new Map(allVersions.map((v) => [v, undefined]));
    const availMap = getAvailabilityMap(program, source);
    const alwaysAvail = availMap === undefined;
    // Populate the map with any RenamedFrom data, which may have holes.
    // We will fill these holes in a later pass.
    const renamedFrom = getRenamedFrom(program, source);
    if (renamedFrom !== undefined) {
        for (const rename of renamedFrom) {
            const version = rename.version;
            const oldName = rename.oldName;
            const versionIndex = allVersions.indexOf(version);
            if (versionIndex !== -1) {
                map.set(allVersions[versionIndex - 1], oldName);
            }
        }
    }
    let lastName = undefined;
    switch (source.kind) {
        case "ModelProperty":
            lastName = source.name;
            break;
        case "UnionVariant":
            if (typeof source.name === "string") {
                lastName = source.name;
            }
            break;
        case "EnumMember":
            lastName = source.name;
            break;
        default:
            throw new Error(`Not implemented '${source.kind}'.`);
    }
    for (const version of allVersions.reverse()) {
        const isAvail = alwaysAvail ||
            [Availability.Added, Availability.Available].includes(availMap.get(version.name));
        // If property is unavailable in this version, it can't have a type
        if (!isAvail) {
            map.set(version, undefined);
            continue;
        }
        // Working backwards, we fill in any holes from the last type we encountered. Since we expect
        // to encounter a hole at the start, we use the raw property type
        const mapType = map.get(version);
        if (mapType !== undefined) {
            lastName = mapType;
        }
        else {
            map.set(version, lastName);
        }
    }
    return map;
}
/**
 * Constructs a map of version to type for the the source.
 */
function getVersionedTypeMap(program, source) {
    const allVersions = getAllVersions(program, source);
    if (allVersions === undefined)
        return undefined;
    const map = new Map(allVersions.map((v) => [v, undefined]));
    const availMap = getAvailabilityMap(program, source);
    const alwaysAvail = availMap === undefined;
    // Populate the map with any typeChangedFrom data, which may have holes.
    // We will fill these holes in a later pass.
    const typeChangedFrom = getTypeChangedFrom(program, source);
    if (typeChangedFrom !== undefined) {
        for (const [version, type] of typeChangedFrom) {
            const versionIndex = allVersions.indexOf(version);
            if (versionIndex !== -1) {
                map.set(allVersions[versionIndex - 1], type);
            }
        }
    }
    let lastType = undefined;
    switch (source.kind) {
        case "ModelProperty":
            lastType = source.type;
            break;
        default:
            throw new Error(`Not implemented '${source.kind}'.`);
    }
    for (const version of allVersions.reverse()) {
        const isAvail = alwaysAvail ||
            [Availability.Added, Availability.Available].includes(availMap.get(version.name));
        // If property is unavailable in this version, it can't have a type
        if (!isAvail) {
            map.set(version, undefined);
            continue;
        }
        // Working backwards, we fill in any holes from the last type we encountered. Since we expect
        // to encounter a hole at the start, we use the raw property type
        const mapType = map.get(version);
        if (mapType !== undefined) {
            lastType = mapType;
        }
        else {
            map.set(version, lastType);
        }
    }
    return map;
}
/**
 * Ensures that the version enum for a @versioned namespace has unique values.
 */
function validateVersionEnumValuesUnique(program, namespace) {
    const [_, versionMap] = getVersions(program, namespace);
    if (versionMap === undefined)
        return;
    const values = new Set(versionMap.getVersions().map((v) => v.value));
    if (versionMap.size !== values.size) {
        const enumName = versionMap.getVersions()[0].enumMember.enum.name;
        reportDiagnostic(program, {
            code: "version-duplicate",
            format: { name: enumName },
            target: namespace,
        });
    }
}
function validateVersionedNamespaceUsage(program, namespaceDependencies) {
    for (const [source, targets] of namespaceDependencies.entries()) {
        const dependencies = source && getVersionDependencies(program, source);
        for (const target of targets) {
            const targetVersionedNamespace = findVersionedNamespace(program, target);
            if (targetVersionedNamespace !== undefined &&
                !(source && (isSubNamespace(target, source) || isSubNamespace(source, target))) &&
                dependencies?.get(targetVersionedNamespace) === undefined) {
                reportDiagnostic(program, {
                    code: "using-versioned-library",
                    format: {
                        sourceNs: source ? getNamespaceFullName(source) : "global",
                        targetNs: getNamespaceFullName(target),
                    },
                    target: source ?? NoTarget,
                });
            }
        }
    }
}
function validateVersionedPropertyNames(program, source) {
    const allVersions = getAllVersions(program, source);
    if (allVersions === undefined)
        return;
    const versionedNameMap = new Map(allVersions.map((v) => [v, []]));
    let values = [];
    if (source.kind === "Model") {
        values = source.properties.values();
    }
    else if (source.kind === "Enum") {
        values = source.members.values();
    }
    else if (source.kind === "Union") {
        values = source.variants.values();
    }
    for (const value of values) {
        const nameMap = getVersionedNameMap(program, value);
        if (nameMap === undefined)
            continue;
        for (const [version, name] of nameMap) {
            if (name === undefined)
                continue;
            versionedNameMap.get(version)?.push(name);
        }
    }
    // for each version, ensure there are no duplicate property names
    for (const [version, names] of versionedNameMap.entries()) {
        // create a map with names to count of occurrences
        const nameCounts = new Map();
        for (const name of names) {
            const count = nameCounts.get(name) ?? 0;
            nameCounts.set(name, count + 1);
        }
        // emit diagnostic for each duplicate name
        for (const [name, count] of nameCounts.entries()) {
            if (name === undefined)
                continue;
            if (count > 1) {
                reportDiagnostic(program, {
                    code: "renamed-duplicate-property",
                    format: {
                        name: name,
                        version: prettyVersion(version),
                    },
                    target: source,
                });
            }
        }
    }
}
function isSubNamespace(parent, child) {
    let current = child;
    while (current && current.name !== "") {
        if (current === parent) {
            return true;
        }
        current = current.namespace;
    }
    return false;
}
function validateMadeOptional(program, target) {
    if (target.kind === "ModelProperty") {
        const madeOptionalOn = getMadeOptionalOn(program, target);
        if (!madeOptionalOn) {
            return;
        }
        // if the @madeOptional decorator is on a property it MUST be optional
        if (!target.optional) {
            reportDiagnostic(program, {
                code: "made-optional-not-optional",
                format: {
                    name: target.name,
                },
                target: target,
            });
            return;
        }
    }
}
function validateMadeRequired(program, target) {
    if (target.kind === "ModelProperty") {
        const madeRequiredOn = getMadeRequiredOn(program, target);
        if (!madeRequiredOn) {
            return;
        }
        // if the @madeRequired decorator is on a property, it MUST NOT be optional
        if (target.optional) {
            reportDiagnostic(program, {
                code: "made-required-optional",
                format: {
                    name: target.name,
                },
                target: target,
            });
            return;
        }
    }
}
/**
 * Validate the target reference versioning is compatible with the source versioning.
 * This will also validate any template arguments used in the reference.
 * e.g. The target cannot be added after the source was added.
 * @param source Source type referencing the target type.
 * @param target Type being referenced from the source
 */
function validateReference(program, source, target) {
    validateTargetVersionCompatible(program, source, target);
    if ("templateMapper" in target) {
        for (const param of target.templateMapper?.args ?? []) {
            if (isType(param)) {
                validateTargetVersionCompatible(program, source, param);
            }
        }
    }
    switch (target.kind) {
        case "Union":
            for (const variant of target.variants.values()) {
                validateTargetVersionCompatible(program, source, variant.type);
            }
            break;
        case "Tuple":
            for (const value of target.values) {
                validateTargetVersionCompatible(program, source, value);
            }
            break;
    }
}
function getAvailabilityMapWithParentInfo(program, type) {
    const base = getAvailabilityMap(program, type);
    // get any parent availability information
    let parentMap = undefined;
    switch (type.kind) {
        case "Operation":
            const parentInterface = type.interface;
            if (parentInterface) {
                parentMap = getAvailabilityMap(program, parentInterface);
            }
            break;
        case "ModelProperty":
            const parentModel = type.model;
            if (parentModel) {
                parentMap = getAvailabilityMapWithParentInfo(program, parentModel);
            }
            break;
    }
    return base ?? parentMap;
}
/**
 * Validate the target versioning is compatible with the versioning of the source.
 * e.g. The target cannot be added after the source was added.
 * @param source Source type referencing the target type.
 * @param target Type being referenced from the source
 */
function validateTargetVersionCompatible(program, source, target, validateOptions = {}) {
    const sourceAvailability = getAvailabilityMapWithParentInfo(program, source);
    const [sourceNamespace] = getVersions(program, source);
    let targetAvailability = getAvailabilityMapWithParentInfo(program, target);
    const [targetNamespace] = getVersions(program, target);
    if (!targetAvailability || !targetNamespace)
        return;
    if (sourceNamespace !== targetNamespace) {
        const dependencies = sourceNamespace && getVersionDependencies(program, sourceNamespace);
        const versionMap = dependencies?.get(targetNamespace);
        if (versionMap === undefined)
            return;
        targetAvailability = translateAvailability(program, targetAvailability, versionMap, source, target);
        if (!targetAvailability) {
            return;
        }
    }
    if (validateOptions.isTargetADependent) {
        validateAvailabilityForContains(program, sourceAvailability, targetAvailability, source, target);
    }
    else {
        validateAvailabilityForRef(program, sourceAvailability, targetAvailability, source, target);
    }
}
function translateAvailability(program, avail, versionMap, source, target) {
    if (!(versionMap instanceof Map)) {
        const version = versionMap;
        if ([Availability.Removed, Availability.Unavailable].includes(avail.get(version.name))) {
            const addedAfter = findAvailabilityAfterVersion(version.name, Availability.Added, avail);
            const removedBefore = findAvailabilityOnOrBeforeVersion(version.name, Availability.Removed, avail);
            if (addedAfter) {
                reportDiagnostic(program, {
                    code: "incompatible-versioned-reference",
                    messageId: "versionedDependencyAddedAfter",
                    format: {
                        sourceName: getTypeName(source),
                        targetName: getTypeName(target),
                        dependencyVersion: prettyVersion(version),
                        targetAddedOn: addedAfter,
                    },
                    target: source,
                });
            }
            if (removedBefore) {
                reportDiagnostic(program, {
                    code: "incompatible-versioned-reference",
                    messageId: "versionedDependencyRemovedBefore",
                    format: {
                        sourceName: getTypeName(source),
                        targetName: getTypeName(target),
                        dependencyVersion: prettyVersion(version),
                        targetAddedOn: removedBefore,
                    },
                    target: source,
                });
            }
        }
        return undefined;
    }
    else {
        const newAvail = new Map();
        for (const [key, val] of versionMap) {
            const isAvail = avail.get(val.name);
            newAvail.set(key.name, isAvail);
        }
        return newAvail;
    }
}
function findAvailabilityAfterVersion(version, status, avail) {
    let search = false;
    for (const [key, val] of avail) {
        if (version === key) {
            search = true;
            continue;
        }
        if (!search)
            continue;
        if (val === status)
            return key;
    }
    return undefined;
}
function findAvailabilityOnOrBeforeVersion(version, status, avail) {
    let search = false;
    for (const [key, val] of avail) {
        if ([Availability.Added, Availability.Added].includes(val)) {
            search = true;
        }
        if (!search)
            continue;
        if (val === status) {
            return key;
        }
        if (key === version) {
            break;
        }
    }
    return undefined;
}
function validateAvailabilityForRef(program, sourceAvail, targetAvail, source, target, sourceOptions, targetOptions) {
    // if source is unversioned and target is versioned
    if (sourceAvail === undefined) {
        if (!isAvailableInAllVersion(targetAvail)) {
            reportDiagnostic(program, {
                code: "incompatible-versioned-reference",
                messageId: "default",
                format: {
                    sourceName: getTypeName(source),
                    targetName: getTypeName(target),
                },
                target: source,
            });
        }
        return;
    }
    let keyValSource = [...sourceAvail.keys(), ...targetAvail.keys()];
    const sourceTypeChanged = getTypeChangedFrom(program, source);
    if (sourceTypeChanged !== undefined) {
        const sourceTypeChangedKeys = [...sourceTypeChanged.keys()].map((item) => item.name);
        keyValSource = [...keyValSource, ...sourceTypeChangedKeys];
    }
    const sourceReturnTypeChanged = getReturnTypeChangedFrom(program, source);
    if (sourceReturnTypeChanged !== undefined) {
        const sourceReturnTypeChangedKeys = [...sourceReturnTypeChanged.keys()].map((item) => item.name);
        keyValSource = [...keyValSource, ...sourceReturnTypeChangedKeys];
    }
    const keySet = new Set(keyValSource);
    for (const key of keySet) {
        const sourceVal = sourceAvail.get(key);
        const targetVal = targetAvail.get(key);
        if ([Availability.Added].includes(sourceVal) &&
            [Availability.Removed, Availability.Unavailable].includes(targetVal)) {
            const targetAddedOn = findAvailabilityAfterVersion(key, Availability.Added, targetAvail);
            reportDiagnostic(program, {
                code: "incompatible-versioned-reference",
                messageId: "addedAfter",
                format: {
                    sourceName: getTypeName(source),
                    targetName: getTypeName(target),
                    sourceAddedOn: key,
                    targetAddedOn: targetAddedOn,
                },
                target: source,
            });
        }
        if ([Availability.Removed].includes(sourceVal) &&
            [Availability.Unavailable].includes(targetVal)) {
            const targetRemovedOn = findAvailabilityOnOrBeforeVersion(key, Availability.Removed, targetAvail);
            reportDiagnostic(program, {
                code: "incompatible-versioned-reference",
                messageId: "removedBefore",
                format: {
                    sourceName: getTypeName(source, sourceOptions),
                    targetName: getTypeName(target, targetOptions),
                    sourceRemovedOn: key,
                    targetRemovedOn: targetRemovedOn,
                },
                target: source,
            });
        }
    }
}
function validateAvailabilityForContains(program, sourceAvail, targetAvail, source, target, sourceOptions, targetOptions) {
    if (!sourceAvail)
        return;
    const keySet = new Set([...sourceAvail.keys(), ...targetAvail.keys()]);
    for (const key of keySet) {
        const sourceVal = sourceAvail.get(key);
        const targetVal = targetAvail.get(key);
        if (sourceVal === targetVal)
            continue;
        if ([Availability.Added].includes(targetVal) &&
            [Availability.Removed, Availability.Unavailable].includes(sourceVal)) {
            const sourceAddedOn = findAvailabilityOnOrBeforeVersion(key, Availability.Added, sourceAvail);
            reportDiagnostic(program, {
                code: "incompatible-versioned-reference",
                messageId: "dependentAddedAfter",
                format: {
                    sourceName: getTypeName(source, sourceOptions),
                    targetName: getTypeName(target, targetOptions),
                    sourceAddedOn: sourceAddedOn,
                    targetAddedOn: key,
                },
                target: target,
            });
        }
        if ([Availability.Removed].includes(sourceVal) &&
            [Availability.Added, Availability.Available].includes(targetVal)) {
            const targetRemovedOn = findAvailabilityAfterVersion(key, Availability.Removed, targetAvail);
            reportDiagnostic(program, {
                code: "incompatible-versioned-reference",
                messageId: "dependentRemovedBefore",
                format: {
                    sourceName: getTypeName(source),
                    targetName: getTypeName(target),
                    sourceRemovedOn: key,
                    targetRemovedOn: targetRemovedOn,
                },
                target: target,
            });
        }
    }
}
function isAvailableInAllVersion(avail) {
    for (const val of avail.values()) {
        if ([Availability.Removed, Availability.Unavailable].includes(val))
            return false;
    }
    return true;
}
function prettyVersion(version) {
    return version?.value ?? "<n/a>";
}

var f2 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    $onValidate: $onValidate
});

const namespace = "TypeSpec.Versioning";
function getVersioningState(program, versionKey) {
    return program.stateMap(VersioningStateKeys.versionIndex).get(versionKey);
}
/**
 * @returns get old name if applicable.
 */
function getNameAtVersion(p, t, versionKey) {
    const versioningState = getVersioningState(p, versionKey);
    const allValues = getRenamedFrom(p, t);
    if (!allValues)
        return "";
    for (const val of allValues) {
        if (versioningState.timeline.isBefore(versioningState.projectingMoment, val.version)) {
            return val.oldName;
        }
    }
    return "";
}
/**
 * @returns get old type if applicable.
 */
function getTypeBeforeVersion(p, t, versionKey) {
    const versioningState = getVersioningState(p, versionKey);
    const map = getTypeChangedFrom(p, t);
    if (!map)
        return undefined;
    for (const [changedAtVersion, oldType] of map) {
        if (versioningState.timeline.isBefore(versioningState.projectingMoment, changedAtVersion)) {
            return oldType;
        }
    }
    return undefined;
}
/**
 * @returns get old type if applicable.
 */
function getReturnTypeBeforeVersion(p, t, versionKey) {
    const versioningState = getVersioningState(p, versionKey);
    const map = getReturnTypeChangedFrom(p, t);
    if (!map)
        return "";
    for (const [changedAtVersion, val] of map) {
        if (versioningState.timeline.isBefore(versioningState.projectingMoment, changedAtVersion)) {
            return val;
        }
    }
    return "";
}
function madeOptionalAfter(program, type, versionKey) {
    const versioningState = getVersioningState(program, versionKey);
    const madeOptionalAtVersion = getMadeOptionalOn(program, type);
    if (madeOptionalAtVersion === undefined) {
        return false;
    }
    return versioningState.timeline.isBefore(versioningState.projectingMoment, madeOptionalAtVersion);
}
function madeRequiredAfter(program, type, versionKey) {
    const versioningState = getVersioningState(program, versionKey);
    const madeRequiredAtVersion = getMadeRequiredOn(program, type);
    if (madeRequiredAtVersion === undefined) {
        return false;
    }
    return versioningState.timeline.isBefore(versioningState.projectingMoment, madeRequiredAtVersion);
}
function existsAtVersion(p, type, versionKey) {
    const versioningState = getVersioningState(p, versionKey);
    // if unversioned then everything exists
    const availability = getAvailabilityMapInTimeline(p, type, versioningState.timeline);
    if (!availability)
        return true;
    const isAvail = availability.get(versioningState.projectingMoment);
    return isAvail === Availability.Added || isAvail === Availability.Available;
}
function hasDifferentNameAtVersion(p, type, version) {
    return getNameAtVersion(p, type, version) !== "";
}
function hasDifferentTypeAtVersion(p, type, version) {
    return getTypeBeforeVersion(p, type, version) !== undefined;
}
function hasDifferentReturnTypeAtVersion(p, type, version) {
    return getReturnTypeBeforeVersion(p, type, version) !== "";
}

var f1 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    existsAtVersion: existsAtVersion,
    getNameAtVersion: getNameAtVersion,
    getReturnTypeBeforeVersion: getReturnTypeBeforeVersion,
    getTypeBeforeVersion: getTypeBeforeVersion,
    hasDifferentNameAtVersion: hasDifferentNameAtVersion,
    hasDifferentReturnTypeAtVersion: hasDifferentReturnTypeAtVersion,
    hasDifferentTypeAtVersion: hasDifferentTypeAtVersion,
    madeOptionalAfter: madeOptionalAfter,
    madeRequiredAfter: madeRequiredAfter,
    namespace: namespace
});

const TypeSpecJSSources = {
"dist/src/decorators.js": f0,
"dist/src/internal-projection-functions.js": f1,
"dist/src/validate.js": f2,
};
const TypeSpecSources = {
  "package.json": "{\"name\":\"@typespec/versioning\",\"version\":\"0.58.0\",\"author\":\"Microsoft Corporation\",\"description\":\"TypeSpec library for declaring and emitting versioned APIs\",\"homepage\":\"https://typespec.io\",\"readme\":\"https://github.com/microsoft/typespec/blob/main/README.md\",\"license\":\"MIT\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/microsoft/typespec.git\"},\"bugs\":{\"url\":\"https://github.com/microsoft/typespec/issues\"},\"keywords\":[\"typespec\"],\"type\":\"module\",\"main\":\"dist/src/index.js\",\"tspMain\":\"lib/main.tsp\",\"exports\":{\".\":{\"types\":\"./dist/src/index.d.ts\",\"default\":\"./dist/src/index.js\"},\"./testing\":{\"types\":\"./dist/src/testing/index.d.ts\",\"default\":\"./dist/src/testing/index.js\"}},\"engines\":{\"node\":\">=18.0.0\"},\"scripts\":{\"clean\":\"rimraf ./dist ./temp\",\"build\":\"npm run gen-extern-signature && tsc -p . && npm run lint-typespec-library\",\"watch\":\"tsc -p . --watch\",\"gen-extern-signature\":\"tspd --enable-experimental gen-extern-signature .\",\"lint-typespec-library\":\"tsp compile . --warn-as-error --import @typespec/library-linter --no-emit\",\"test\":\"vitest run\",\"test:ui\":\"vitest --ui\",\"test:ci\":\"vitest run --coverage --reporter=junit --reporter=default\",\"lint\":\"eslint . --max-warnings=0\",\"lint:fix\":\"eslint . --fix\",\"regen-docs\":\"tspd doc .  --enable-experimental  --output-dir ../../docs/libraries/versioning/reference\"},\"files\":[\"lib/*.tsp\",\"dist/**\",\"!dist/test/**\"],\"peerDependencies\":{\"@typespec/compiler\":\"workspace:~\"},\"devDependencies\":{\"@types/node\":\"~18.11.19\",\"@typespec/compiler\":\"workspace:~\",\"@typespec/library-linter\":\"workspace:~\",\"@typespec/tspd\":\"workspace:~\",\"@vitest/coverage-v8\":\"^1.6.0\",\"@vitest/ui\":\"^1.6.0\",\"c8\":\"^10.1.2\",\"rimraf\":\"~5.0.7\",\"typescript\":\"~5.5.3\",\"vitest\":\"^1.6.0\"}}",
  "../compiler/lib/intrinsics.tsp": "import \"../dist/src/lib/intrinsic-decorators.js\";\n\n// This file contains all the intrinsic types of typespec. Everything here will always be loaded\nnamespace TypeSpec;\n\n/**\n * Represent a byte array\n */\nscalar bytes;\n\n/**\n * A numeric type\n */\nscalar numeric;\n\n/**\n * A whole number. This represent any `integer` value possible.\n * It is commonly represented as `BigInteger` in some languages.\n */\nscalar integer extends numeric;\n\n/**\n * A number with decimal value\n */\nscalar float extends numeric;\n\n/**\n * A 64-bit integer. (`-9,223,372,036,854,775,808` to `9,223,372,036,854,775,807`)\n */\nscalar int64 extends integer;\n\n/**\n * A 32-bit integer. (`-2,147,483,648` to `2,147,483,647`)\n */\nscalar int32 extends int64;\n\n/**\n * A 16-bit integer. (`-32,768` to `32,767`)\n */\nscalar int16 extends int32;\n\n/**\n * A 8-bit integer. (`-128` to `127`)\n */\nscalar int8 extends int16;\n\n/**\n * A 64-bit unsigned integer (`0` to `18,446,744,073,709,551,615`)\n */\nscalar uint64 extends integer;\n\n/**\n * A 32-bit unsigned integer (`0` to `4,294,967,295`)\n */\nscalar uint32 extends uint64;\n\n/**\n * A 16-bit unsigned integer (`0` to `65,535`)\n */\nscalar uint16 extends uint32;\n\n/**\n * A 8-bit unsigned integer (`0` to `255`)\n */\nscalar uint8 extends uint16;\n\n/**\n * An integer that can be serialized to JSON (`−9007199254740991 (−(2^53 − 1))` to `9007199254740991 (2^53 − 1)` )\n */\nscalar safeint extends int64;\n\n/**\n * A 32 bit floating point number. (`±1.5 x 10^−45` to `±3.4 x 10^38`)\n */\nscalar float64 extends float;\n\n/**\n * A 32 bit floating point number. (`±5.0 × 10^−324` to `±1.7 × 10^308`)\n */\nscalar float32 extends float64;\n\n/**\n * A decimal number with any length and precision. This represent any `decimal` value possible.\n * It is commonly represented as `BigDecimal` in some languages.\n */\nscalar decimal extends numeric;\n\n/**\n * A 128-bit decimal number.\n */\nscalar decimal128 extends decimal;\n\n/**\n * A sequence of textual characters.\n */\nscalar string;\n\n/**\n * A date on a calendar without a time zone, e.g. \"April 10th\"\n */\nscalar plainDate {\n  /**\n   * Create a plain date from an ISO 8601 string.\n   * @example\n   *\n   * ```tsp\n   * const time = plainTime.fromISO(\"2024-05-06\");\n   * ```\n   */\n  init fromISO(value: string);\n}\n\n/**\n * A time on a clock without a time zone, e.g. \"3:00 am\"\n */\nscalar plainTime {\n  /**\n   * Create a plain time from an ISO 8601 string.\n   * @example\n   *\n   * ```tsp\n   * const time = plainTime.fromISO(\"12:34\");\n   * ```\n   */\n  init fromISO(value: string);\n}\n\n/**\n * An instant in coordinated universal time (UTC)\"\n */\nscalar utcDateTime {\n  /**\n   * Create a date from an ISO 8601 string.\n   * @example\n   *\n   * ```tsp\n   * const time = utcDateTime.fromISO(\"2024-05-06T12:20-12Z\");\n   * ```\n   */\n  init fromISO(value: string);\n}\n\n/**\n * A date and time in a particular time zone, e.g. \"April 10th at 3:00am in PST\"\n */\nscalar offsetDateTime {\n  /**\n   * Create a date from an ISO 8601 string.\n   * @example\n   *\n   * ```tsp\n   * const time = offsetDateTime.fromISO(\"2024-05-06T12:20-12-0700\");\n   * ```\n   */\n  init fromISO(value: string);\n}\n\n/**\n * A duration/time period. e.g 5s, 10h\n */\nscalar duration {\n  /**\n   * Create a duration from an ISO 8601 string.\n   * @example\n   *\n   * ```tsp\n   * const time = duration.fromISO(\"P1Y1D\");\n   * ```\n   */\n  init fromISO(value: string);\n}\n\n/**\n * Boolean with `true` and `false` values.\n */\nscalar boolean;\n\n/**\n * @dev Array model type, equivalent to `Element[]`\n * @template Element The type of the array elements\n */\n@indexer(integer, Element)\nmodel Array<Element> {}\n\n/**\n * @dev Model with string properties where all the properties have type `Property`\n * @template Element The type of the properties\n */\n@indexer(string, Element)\nmodel Record<Element> {}\n",
  "../compiler/lib/std/main.tsp": "// TypeSpec standard library. Everything in here can be omitted by using `--nostdlib` cli flag or `nostdlib` in the config.\nimport \"./types.tsp\";\nimport \"./decorators.tsp\";\nimport \"./reflection.tsp\";\nimport \"./projected-names.tsp\";\n",
  "../compiler/lib/std/types.tsp": "namespace TypeSpec;\n\n/**\n * Represent a 32-bit unix timestamp datetime with 1s of granularity.\n * It measures time by the number of seconds that have elapsed since 00:00:00 UTC on 1 January 1970.\n */\n@encode(\"unixTimestamp\", int32)\nscalar unixTimestamp32 extends utcDateTime;\n\n/**\n * Represent a model\n */\n// Deprecated June 2023 sprint\n#deprecated \"object is deprecated. Please use {} for an empty model, `Record<unknown>` for a record with unknown property types, `unknown[]` for an array.\"\nmodel object {}\n\n/**\n * Represent a URL string as described by https://url.spec.whatwg.org/\n */\nscalar url extends string;\n\n/**\n * Represents a collection of optional properties.\n *\n * @template Source An object whose spread properties are all optional.\n */\n@doc(\"The template for adding optional properties.\")\n@withOptionalProperties\nmodel OptionalProperties<Source> {\n  ...Source;\n}\n\n/**\n * Represents a collection of updateable properties.\n *\n * @template Source An object whose spread properties are all updateable.\n */\n@doc(\"The template for adding updateable properties.\")\n@withUpdateableProperties\nmodel UpdateableProperties<Source> {\n  ...Source;\n}\n\n/**\n * Represents a collection of omitted properties.\n *\n * @template Source An object whose properties are spread.\n * @template Keys The property keys to omit.\n */\n@doc(\"The template for omitting properties.\")\n@withoutOmittedProperties(Keys)\nmodel OmitProperties<Source, Keys extends string> {\n  ...Source;\n}\n\n/**\n * Represents a collection of properties with only the specified keys included.\n *\n * @template Source An object whose properties are spread.\n * @template Keys The property keys to include.\n */\n@doc(\"The template for picking properties.\")\n@withPickedProperties(Keys)\nmodel PickProperties<Source, Keys extends string> {\n  ...Source;\n}\n\n/**\n * Represents a collection of properties with default values omitted.\n *\n * @template Source An object whose spread property defaults are all omitted.\n */\n@withoutDefaultValues\nmodel OmitDefaults<Source> {\n  ...Source;\n}\n\n/**\n * Applies a visibility setting to a collection of properties.\n *\n * @template Source An object whose properties are spread.\n * @template Visibility The visibility to apply to all properties.\n */\n@doc(\"The template for setting the default visibility of key properties.\")\n@withDefaultKeyVisibility(Visibility)\nmodel DefaultKeyVisibility<Source, Visibility extends valueof string> {\n  ...Source;\n}\n",
  "../compiler/lib/std/decorators.tsp": "import \"../../dist/src/lib/decorators.js\";\n\nusing TypeSpec.Reflection;\n\nnamespace TypeSpec;\n\n/**\n * Typically a short, single-line description.\n * @param summary Summary string.\n *\n * @example\n * ```typespec\n * @summary(\"This is a pet\")\n * model Pet {}\n * ```\n */\nextern dec summary(target: unknown, summary: valueof string);\n\n/**\n * Attach a documentation string.\n * @param doc Documentation string\n * @param formatArgs Record with key value pair that can be interpolated in the doc.\n *\n * @example\n * ```typespec\n * @doc(\"Represent a Pet available in the PetStore\")\n * model Pet {}\n * ```\n */\nextern dec doc(target: unknown, doc: valueof string, formatArgs?: {});\n\n/**\n * Attach a documentation string to describe the successful return types of an operation.\n * If an operation returns a union of success and errors it only describes the success. See `@errorsDoc` for error documentation.\n * @param doc Documentation string\n *\n * @example\n * ```typespec\n * @returnsDoc(\"Returns doc\")\n * op get(): Pet | NotFound;\n * ```\n */\nextern dec returnsDoc(target: Operation, doc: valueof string);\n\n/**\n * Attach a documentation string to describe the error return types of an operation.\n * If an operation returns a union of success and errors it only describes the errors. See `@returnsDoc` for success documentation.\n * @param doc Documentation string\n *\n * @example\n * ```typespec\n * @errorsDoc(\"Errors doc\")\n * op get(): Pet | NotFound;\n * ```\n */\nextern dec errorsDoc(target: Operation, doc: valueof string);\n\n/**\n * Mark this type as deprecated.\n *\n * NOTE: This decorator **should not** be used, use the `#deprecated` directive instead.\n *\n * @deprecated Use the `#deprecated` directive instead.\n * @param message Deprecation message.\n *\n * @example\n *\n * Use the `#deprecated` directive instead:\n *\n * ```typespec\n * #deprecated \"Use ActionV2\"\n * op Action<Result>(): Result;\n * ```\n */\n#deprecated \"@deprecated decorator is deprecated. Use the `#deprecated` directive instead.\"\nextern dec deprecated(target: unknown, message: valueof string);\n\n/**\n * Service options.\n */\nmodel ServiceOptions {\n  /**\n   * Title of the service.\n   */\n  title?: string;\n\n  /**\n   * Version of the service.\n   */\n  version?: string;\n}\n\n/**\n * Mark this namespace as describing a service and configure service properties.\n * @param options Optional configuration for the service.\n *\n * @example\n * ```typespec\n * @service\n * namespace PetStore;\n * ```\n *\n * @example Setting service title\n * ```typespec\n * @service({title: \"Pet store\"})\n * namespace PetStore;\n * ```\n *\n * @example Setting service version\n * ```typespec\n * @service({version: \"1.0\"})\n * namespace PetStore;\n * ```\n */\nextern dec service(target: Namespace, options?: ServiceOptions);\n\n/**\n * Specify that this model is an error type. Operations return error types when the operation has failed.\n *\n * @example\n * ```typespec\n * @error\n * model PetStoreError {\n *   code: string;\n *   message: string;\n * }\n * ```\n */\nextern dec error(target: Model);\n\n/**\n * Specify a known data format hint for this string type. For example `uuid`, `uri`, etc.\n * This differs from the `@pattern` decorator which is meant to specify a regular expression while `@format` accepts a known format name.\n * The format names are open ended and are left to emitter to interpret.\n *\n * @param format format name.\n *\n * @example\n * ```typespec\n * @format(\"uuid\")\n * scalar uuid extends string;\n * ```\n */\nextern dec format(target: string | bytes | ModelProperty, format: valueof string);\n\n/**\n * Specify the the pattern this string should respect using simple regular expression syntax.\n * The following syntax is allowed: alternations (`|`), quantifiers (`?`, `*`, `+`, and `{ }`), wildcard (`.`), and grouping parentheses.\n * Advanced features like look-around, capture groups, and references are not supported.\n *\n * This decorator may optionally provide a custom validation _message_. Emitters may choose to use the message to provide\n * context when pattern validation fails. For the sake of consistency, the message should be a phrase that describes in\n * plain language what sort of content the pattern attempts to validate. For example, a complex regular expression that\n * validates a GUID string might have a message like \"Must be a valid GUID.\"\n *\n * @param pattern Regular expression.\n * @param validationMessage Optional validation message that may provide context when validation fails.\n *\n * @example\n * ```typespec\n * @pattern(\"[a-z]+\", \"Must be a string consisting of only lower case letters and of at least one character.\")\n * scalar LowerAlpha extends string;\n * ```\n */\nextern dec pattern(\n  target: string | bytes | ModelProperty,\n  pattern: valueof string,\n  validationMessage?: valueof string\n);\n\n/**\n * Specify the minimum length this string type should be.\n * @param value Minimum length\n *\n * @example\n * ```typespec\n * @minLength(2)\n * scalar Username extends string;\n * ```\n */\nextern dec minLength(target: string | ModelProperty, value: valueof integer);\n\n/**\n * Specify the maximum length this string type should be.\n * @param value Maximum length\n *\n * @example\n * ```typespec\n * @maxLength(20)\n * scalar Username extends string;\n * ```\n */\nextern dec maxLength(target: string | ModelProperty, value: valueof integer);\n\n/**\n * Specify the minimum number of items this array should have.\n * @param value Minimum number\n *\n * @example\n * ```typespec\n * @minItems(1)\n * model Endpoints is string[];\n * ```\n */\nextern dec minItems(target: unknown[] | ModelProperty, value: valueof integer);\n\n/**\n * Specify the maximum number of items this array should have.\n * @param value Maximum number\n *\n * @example\n * ```typespec\n * @maxItems(5)\n * model Endpoints is string[];\n * ```\n */\nextern dec maxItems(target: unknown[] | ModelProperty, value: valueof integer);\n\n/**\n * Specify the minimum value this numeric type should be.\n * @param value Minimum value\n *\n * @example\n * ```typespec\n * @minValue(18)\n * scalar Age is int32;\n * ```\n */\nextern dec minValue(target: numeric | ModelProperty, value: valueof numeric);\n\n/**\n * Specify the maximum value this numeric type should be.\n * @param value Maximum value\n *\n * @example\n * ```typespec\n * @maxValue(200)\n * scalar Age is int32;\n * ```\n */\nextern dec maxValue(target: numeric | ModelProperty, value: valueof numeric);\n\n/**\n * Specify the minimum value this numeric type should be, exclusive of the given\n * value.\n * @param value Minimum value\n *\n * @example\n * ```typespec\n * @minValueExclusive(0)\n * scalar distance is float64;\n * ```\n */\nextern dec minValueExclusive(target: numeric | ModelProperty, value: valueof numeric);\n\n/**\n * Specify the maximum value this numeric type should be, exclusive of the given\n * value.\n * @param value Maximum value\n *\n * @example\n * ```typespec\n * @maxValueExclusive(50)\n * scalar distance is float64;\n * ```\n */\nextern dec maxValueExclusive(target: numeric | ModelProperty, value: valueof numeric);\n\n/**\n * Mark this string as a secret value that should be treated carefully to avoid exposure\n *\n * @example\n * ```typespec\n * @secret\n * scalar Password is string;\n * ```\n */\nextern dec secret(target: string | ModelProperty);\n\n/**\n * Mark this operation as a `list` operation for resource types.\n * @deprecated Use the `listsResource` decorator in `@typespec/rest` instead.\n * @param listedType Optional type of the items in the list.\n */\nextern dec list(target: Operation, listedType?: Model);\n\n/**\n * Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element.\n * @param tag Tag value\n */\nextern dec tag(target: Namespace | Interface | Operation, tag: valueof string);\n\n/**\n * Specifies how a templated type should name their instances.\n * @param name name the template instance should take\n * @param formatArgs Model with key value used to interpolate the name\n *\n * @example\n * ```typespec\n * @friendlyName(\"{name}List\", T)\n * model List<Item> {\n *   value: Item[];\n *   nextLink: string;\n * }\n * ```\n */\nextern dec friendlyName(target: unknown, name: valueof string, formatArgs?: unknown);\n\n/**\n * Provide a set of known values to a string type.\n * @param values Known values enum.\n *\n * @example\n * ```typespec\n * @knownValues(KnownErrorCode)\n * scalar ErrorCode extends string;\n *\n * enum KnownErrorCode {\n *   NotFound,\n *   Invalid,\n * }\n * ```\n */\n#deprecated \"This decorator has been deprecated. Use a named union of string literals with a string variant to achieve the same result without a decorator.\"\nextern dec knownValues(target: string | numeric | ModelProperty, values: Enum);\n\n/**\n * Mark a model property as the key to identify instances of that type\n * @param altName Name of the property. If not specified, the decorated property name is used.\n *\n * @example\n * ```typespec\n * model Pet {\n *   @key id: string;\n * }\n * ```\n */\nextern dec key(target: ModelProperty, altName?: valueof string);\n\n/**\n * Specify this operation is an overload of the given operation.\n * @param overloadbase Base operation that should be a union of all overloads\n *\n * @example\n * ```typespec\n * op upload(data: string | bytes, @header contentType: \"text/plain\" | \"application/octet-stream\"): void;\n * @overload(upload)\n * op uploadString(data: string, @header contentType: \"text/plain\" ): void;\n * @overload(upload)\n * op uploadBytes(data: bytes, @header contentType: \"application/octet-stream\"): void;\n * ```\n */\nextern dec overload(target: Operation, overloadbase: Operation);\n\n/**\n * DEPRECATED: Use `@encodedName` instead.\n *\n * Provide an alternative name for this type.\n * @param targetName Projection target\n * @param projectedName Alternative name\n *\n * @example\n * ```typespec\n * model Certificate {\n *   @projectedName(\"json\", \"exp\")\n *   expireAt: int32;\n * }\n * ```\n */\n#deprecated \"Use `@encodedName` instead for changing the name over the wire.\"\nextern dec projectedName(\n  target: unknown,\n  targetName: valueof string,\n  projectedName: valueof string\n);\n\n/**\n * Provide an alternative name for this type when serialized to the given mime type.\n * @param mimeType Mime type this should apply to. The mime type should be a known mime type as described here https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types without any suffix (e.g. `+json`)\n * @param name Alternative name\n *\n * @example\n *\n * ```typespec\n * model Certificate {\n *   @encodedName(\"application/json\", \"exp\")\n *   @encodedName(\"application/xml\", \"expiry\")\n *   expireAt: int32;\n * }\n * ```\n *\n * @example Invalid values\n *\n * ```typespec\n * @encodedName(\"application/merge-patch+json\", \"exp\")\n *              ^ error cannot use subtype\n * ```\n */\nextern dec encodedName(target: unknown, mimeType: valueof string, name: valueof string);\n\n/**\n * Specify the property to be used to discriminate this type.\n * @param propertyName The property name to use for discrimination\n *\n * @example\n *\n * ```typespec\n * @discriminator(\"kind\")\n * union Pet{ cat: Cat, dog: Dog }\n *\n * model Cat {kind: \"cat\", meow: boolean}\n * model Dog {kind: \"dog\", bark: boolean}\n * ```\n *\n * ```typespec\n * @discriminator(\"kind\")\n * model Pet{ kind: string }\n *\n * model Cat extends Pet {kind: \"cat\", meow: boolean}\n * model Dog extends Pet  {kind: \"dog\", bark: boolean}\n * ```\n */\nextern dec discriminator(target: Model | Union, propertyName: valueof string);\n\n/**\n * Known encoding to use on utcDateTime or offsetDateTime\n */\nenum DateTimeKnownEncoding {\n  /**\n   * RFC 3339 standard. https://www.ietf.org/rfc/rfc3339.txt\n   * Encode to string.\n   */\n  rfc3339: \"rfc3339\",\n\n  /**\n   * RFC 7231 standard. https://www.ietf.org/rfc/rfc7231.txt\n   * Encode to string.\n   */\n  rfc7231: \"rfc7231\",\n\n  /**\n   * Encode a datetime to a unix timestamp.\n   * Unix timestamps are represented as an integer number of seconds since the Unix epoch and usually encoded as an int32.\n   */\n  unixTimestamp: \"unixTimestamp\",\n}\n\n/**\n * Known encoding to use on duration\n */\nenum DurationKnownEncoding {\n  /**\n   * ISO8601 duration\n   */\n  ISO8601: \"ISO8601\",\n\n  /**\n   * Encode to integer or float\n   */\n  seconds: \"seconds\",\n}\n\n/**\n * Known encoding to use on bytes\n */\nenum BytesKnownEncoding {\n  /**\n   * Encode to Base64\n   */\n  base64: \"base64\",\n\n  /**\n   * Encode to Base64 Url\n   */\n  base64url: \"base64url\",\n}\n\n/**\n * Specify how to encode the target type.\n * @param encoding Known name of an encoding.\n * @param encodedAs What target type is this being encoded as. Default to string.\n *\n * @example offsetDateTime encoded with rfc7231\n *\n * ```tsp\n * @encode(\"rfc7231\")\n * scalar myDateTime extends offsetDateTime;\n * ```\n *\n * @example utcDateTime encoded with unixTimestamp\n *\n * ```tsp\n * @encode(\"unixTimestamp\", int32)\n * scalar myDateTime extends unixTimestamp;\n * ```\n */\nextern dec encode(\n  target: Scalar | ModelProperty,\n  encoding: string | EnumMember,\n  encodedAs?: Scalar\n);\n\n/** Options for example decorators */\nmodel ExampleOptions {\n  /** The title of the example */\n  title?: string;\n\n  /** Description of the example */\n  description?: string;\n}\n\n/**\n * Provide an example value for a data type.\n *\n * @param example Example value.\n * @param options Optional metadata for the example.\n *\n * @example\n *\n * ```tsp\n * @example(#{name: \"Fluffy\", age: 2})\n * model Pet {\n *  name: string;\n *  age: int32;\n * }\n * ```\n */\nextern dec example(\n  target: Model | Enum | Scalar | Union | ModelProperty | UnionVariant,\n  example: valueof unknown,\n  options?: valueof ExampleOptions\n);\n\n/**\n * Operation example configuration.\n */\nmodel OperationExample {\n  /** Example request body. */\n  parameters?: unknown;\n\n  /** Example response body. */\n  returnType?: unknown;\n}\n\n/**\n * Provide example values for an operation's parameters and corresponding return type.\n *\n * @param example Example value.\n * @param options Optional metadata for the example.\n *\n * @example\n *\n * ```tsp\n * @example(#{parameters: #{name: \"Fluffy\", age: 2}, returnType: #{name: \"Fluffy\", age: 2, id: \"abc\"})\n * op createPet(pet: Pet): Pet;\n * ```\n */\nextern dec opExample(\n  target: Operation,\n  example: valueof OperationExample,\n  options?: valueof ExampleOptions\n);\n/**\n * Indicates that a property is only considered to be present or applicable (\"visible\") with\n * the in the given named contexts (\"visibilities\"). When a property has no visibilities applied\n * to it, it is implicitly visible always.\n *\n * As far as the TypeSpec core library is concerned, visibilities are open-ended and can be arbitrary\n * strings, but  the following visibilities are well-known to standard libraries and should be used\n * with standard emitters that interpret them as follows:\n *\n * - \"read\": output of any operation.\n * - \"create\": input to operations that create an entity..\n * - \"query\": input to operations that read data.\n * - \"update\": input to operations that update data.\n * - \"delete\": input to operations that delete data.\n *\n * See also: [Automatic visibility](https://typespec.io/docs/libraries/http/operations#automatic-visibility)\n *\n * @param visibilities List of visibilities which apply to this property.\n *\n * @example\n *\n * ```typespec\n * model Dog {\n *   // the service will generate an ID, so you don't need to send it.\n *   @visibility(\"read\") id: int32;\n *   // the service will store this secret name, but won't ever return it\n *   @visibility(\"create\", \"update\") secretName: string;\n *   // the regular name is always present\n *   name: string;\n * }\n * ```\n */\nextern dec visibility(target: ModelProperty, ...visibilities: valueof string[]);\n\n/**\n * Removes properties that are not considered to be present or applicable\n * (\"visible\") in the given named contexts (\"visibilities\"). Can be used\n * together with spread to effectively spread only visible properties into\n * a new model.\n *\n * See also: [Automatic visibility](https://typespec.io/docs/libraries/http/operations#automatic-visibility)\n *\n * When using an emitter that applies visibility automatically, it is generally\n * not necessary to use this decorator.\n *\n * @param visibilities List of visibilities which apply to this property.\n *\n * @example\n * ```typespec\n * model Dog {\n *   @visibility(\"read\") id: int32;\n *   @visibility(\"create\", \"update\") secretName: string;\n *   name: string;\n * }\n *\n * // The spread operator will copy all the properties of Dog into DogRead,\n * // and @withVisibility will then remove those that are not visible with\n * // create or update visibility.\n * //\n * // In this case, the id property is removed, and the name and secretName\n * // properties are kept.\n * @withVisibility(\"create\", \"update\")\n * model DogCreateOrUpdate {\n *   ...Dog;\n * }\n *\n * // In this case the id and name properties are kept and the secretName property\n * // is removed.\n * @withVisibility(\"read\")\n * model DogRead {\n *   ...Dog;\n * }\n * ```\n */\nextern dec withVisibility(target: Model, ...visibilities: valueof string[]);\n\n/**\n * Set the visibility of key properties in a model if not already set.\n *\n * @param visibility The desired default visibility value. If a key property already has a `visibility` decorator then the default visibility is not applied.\n */\nextern dec withDefaultKeyVisibility(target: Model, visibility: valueof string);\n\n/**\n * Returns the model with non-updateable properties removed.\n */\nextern dec withUpdateableProperties(target: Model);\n\n/**\n * Returns the model with required properties removed.\n */\nextern dec withOptionalProperties(target: Model);\n\n/**\n * Returns the model with any default values removed.\n */\nextern dec withoutDefaultValues(target: Model);\n\n/**\n * Returns the model with the given properties omitted.\n * @param omit List of properties to omit\n */\nextern dec withoutOmittedProperties(target: Model, omit: string | Union);\n\n/**\n * Returns the model with only the given properties included.\n * @param pick List of properties to include\n */\nextern dec withPickedProperties(target: Model, pick: string | Union);\n\n//---------------------------------------------------------------------------\n// Debugging\n//---------------------------------------------------------------------------\n\n/**\n * A debugging decorator used to inspect a type.\n * @param text Custom text to log\n */\nextern dec inspectType(target: unknown, text: valueof string);\n\n/**\n * A debugging decorator used to inspect a type name.\n * @param text Custom text to log\n */\nextern dec inspectTypeName(target: unknown, text: valueof string);\n\n/**\n * Sets which visibilities apply to parameters for the given operation.\n * @param visibilities List of visibility strings which apply to this operation.\n */\nextern dec parameterVisibility(target: Operation, ...visibilities: valueof string[]);\n\n/**\n * Sets which visibilities apply to the return type for the given operation.\n * @param visibilities List of visibility strings which apply to this operation.\n */\nextern dec returnTypeVisibility(target: Operation, ...visibilities: valueof string[]);\n",
  "../compiler/lib/std/reflection.tsp": "namespace TypeSpec.Reflection;\n\nmodel Enum {}\nmodel EnumMember {}\nmodel Interface {}\nmodel Model {}\nmodel ModelProperty {}\nmodel Namespace {}\nmodel Operation {}\nmodel Scalar {}\nmodel Union {}\nmodel UnionVariant {}\nmodel StringTemplate {}\n",
  "../compiler/lib/std/projected-names.tsp": "// Set of projections consuming the @projectedName decorator\n#suppress \"projections-are-experimental\"\nprojection op#target {\n  to(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(getProjectedName(self, targetName));\n    };\n  }\n  from(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(self::projectionBase::name);\n    };\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection interface#target {\n  to(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(getProjectedName(self, targetName));\n    };\n  }\n  from(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(self::projectionBase::name);\n    };\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection model#target {\n  to(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(getProjectedName(self, targetName));\n    };\n\n    self::properties::forEach((p) => {\n      if hasProjectedName(p, targetName) {\n        self::renameProperty(p::name, getProjectedName(p, targetName));\n      };\n    });\n  }\n  from(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(self::projectionBase::name);\n    };\n\n    self::projectionBase::properties::forEach((p) => {\n      if hasProjectedName(p, targetName) {\n        self::renameProperty(getProjectedName(p, targetName), p::name);\n      };\n    });\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection enum#target {\n  to(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(getProjectedName(self, targetName));\n    };\n\n    self::members::forEach((p) => {\n      if hasProjectedName(p, targetName) {\n        self::renameMember(p::name, getProjectedName(p, targetName));\n      };\n    });\n  }\n  from(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(self::projectionBase::name);\n    };\n\n    self::projectionBase::members::forEach((p) => {\n      if hasProjectedName(p, targetName) {\n        self::renameMember(getProjectedName(p, targetName), p::name);\n      };\n    });\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection union#target {\n  to(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(getProjectedName(self, targetName));\n    };\n  }\n  from(targetName) {\n    if hasProjectedName(self, targetName) {\n      self::rename(self::projectionBase::name);\n    };\n  }\n}\n",
  "lib/main.tsp": "import \"./decorators.tsp\";\nimport \"./projection.tsp\";\nimport \"../dist/src/validate.js\";\n",
  "lib/decorators.tsp": "import \"../dist/src/decorators.js\";\n\nusing TypeSpec.Reflection;\n\nnamespace TypeSpec.Versioning;\n\n/**\n * Identifies that the decorated namespace is versioned by the provided enum.\n * @param versions The enum that describes the supported versions.\n *\n * @example\n *\n * ```tsp\n * @versioned(Versions)\n * namespace MyService;\n * enum Versions {\n *   v1,\n *   v2,\n *   v3,\n * }\n * ```\n */\nextern dec versioned(target: Namespace, versions: Enum);\n\n/**\n * Identifies that a namespace or a given versioning enum member relies upon a versioned package.\n * @param versionRecords The dependent library version(s) for the target namespace or version.\n *\n * @example Select a single version of `MyLib` to use\n *\n * ```tsp\n * @useDependency(MyLib.Versions.v1_1)\n * namespace NonVersionedService;\n * ```\n *\n * @example Select which version of the library match to which version of the service.\n *\n * ```tsp\n * @versioned(Versions)\n * namespace MyService1;\n * enum Version {\n *   @useDependency(MyLib.Versions.v1_1) // V1 use lib v1_1\n *   v1,\n *   @useDependency(MyLib.Versions.v1_1) // V2 use lib v1_1\n *   v2,\n *   @useDependency(MyLib.Versions.v2) // V3 use lib v2\n *   v3,\n * }\n * ```\n */\nextern dec useDependency(target: EnumMember | Namespace, ...versionRecords: EnumMember[]);\n\n/**\n * Identifies when the target was added.\n * @param version The version that the target was added in.\n *\n * @example\n *\n * ```tsp\n * @added(Versions.v2)\n * op addedInV2(): void;\n *\n * @added(Versions.v2)\n * model AlsoAddedInV2 {}\n *\n * model Foo {\n *   name: string;\n *\n *   @added(Versions.v3)\n *   addedInV3: string;\n * }\n * ```\n */\nextern dec added(\n  target:\n    | Model\n    | ModelProperty\n    | Operation\n    | Enum\n    | EnumMember\n    | Union\n    | UnionVariant\n    | Scalar\n    | Interface,\n  version: EnumMember\n);\n\n/**\n * Identifies when the target was removed.\n * @param version The version that the target was removed in.\n *\n * @example\n * ```tsp\n * @removed(Versions.v2)\n * op removedInV2(): void;\n *\n * @removed(Versions.v2)\n * model AlsoRemovedInV2 {}\n *\n * model Foo {\n *   name: string;\n *\n *   @removed(Versions.v3)\n *   removedInV3: string;\n * }\n * ```\n */\nextern dec removed(\n  target:\n    | Model\n    | ModelProperty\n    | Operation\n    | Enum\n    | EnumMember\n    | Union\n    | UnionVariant\n    | Scalar\n    | Interface,\n  version: EnumMember\n);\n\n/**\n * Identifies when the target has been renamed.\n * @param version The version that the target was renamed in.\n * @param oldName The previous name of the target.\n *\n * @example\n * ```tsp\n * @renamedFrom(Versions.v2, \"oldName\")\n * op newName(): void;\n * ```\n */\nextern dec renamedFrom(\n  target:\n    | Model\n    | ModelProperty\n    | Operation\n    | Enum\n    | EnumMember\n    | Union\n    | UnionVariant\n    | Scalar\n    | Interface,\n  version: EnumMember,\n  oldName: valueof string\n);\n\n/**\n * Identifies when a target was made optional.\n * @param version The version that the target was made optional in.\n *\n * @example\n *\n * ```tsp\n * model Foo {\n *   name: string;\n *   @madeOptional(Versions.v2)\n *   nickname?: string;\n * }\n * ```\n */\nextern dec madeOptional(target: ModelProperty, version: EnumMember);\n\n/**\n * Identifies when a target was made required.\n * @param version The version that the target was made required in.\n *\n * @example\n *\n * ```tsp\n * model Foo {\n *   name: string;\n *   @madeRequired(Versions.v2)\n *   nickname: string;\n * }\n * ```\n */\nextern dec madeRequired(target: ModelProperty, version: EnumMember);\n\n/**\n * Identifies when the target type changed.\n * @param version The version that the target type changed in.\n * @param oldType The previous type of the target.\n */\nextern dec typeChangedFrom(target: ModelProperty, version: EnumMember, oldType: unknown);\n\n/**\n * Identifies when the target type changed.\n * @param version The version that the target type changed in.\n * @param oldType The previous type of the target.\n */\nextern dec returnTypeChangedFrom(target: Operation, version: EnumMember, oldType: unknown);\n",
  "lib/projection.tsp": "import \"../dist/src/internal-projection-functions.js\";\n\nusing TypeSpec.Versioning;\nusing TypeSpec.Reflection;\n\nnamespace TypeSpec.Versioning;\n\n/**\n * Returns whether the target exists for the given version.\n * @param version The version to check.\n */\nextern fn existsAtVersion(target: unknown, version: EnumMember): boolean;\n\n/**\n * Returns whether the target has a different name for the given version.\n * @param version The version to check.\n */\nextern fn hasDifferentNameAtVersion(target: unknown, version: EnumMember): boolean;\n\n/**\n * Returns whether the target has a different return type for the given version.\n * @param version The version to check.\n */\nextern fn hasDifferentReturnTypeAtVersion(target: unknown, version: EnumMember): boolean;\n\n/**\n * Returns whether the target was made optional after the given version.\n * @param version The version to check.\n */\nextern fn madeOptionalAfter(target: unknown, version: EnumMember): boolean;\n\n/**\n * Returns whether the target was made required after the given version.\n * @param version The version to check.\n */\nextern fn madeRequiredAfter(target: unknown, version: EnumMember): boolean;\n\n#suppress \"projections-are-experimental\"\nprojection op#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  to(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(getNameAtVersion(self, version));\n    };\n    if hasDifferentReturnTypeAtVersion(self, version) {\n      self::changeReturnType(getReturnTypeBeforeVersion(self, version));\n    };\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  from(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(self::projectionBase::name);\n    };\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection interface#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  to(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(getNameAtVersion(self, version));\n    };\n    self::operations::forEach((operation) => {\n      if !existsAtVersion(operation, version) {\n        self::deleteOperation(operation::name);\n      };\n      if hasDifferentReturnTypeAtVersion(operation, version) {\n        operation::changeReturnType(getReturnTypeBeforeVersion(operation, version));\n      };\n    });\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  from(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(self::projectionBase::name);\n    };\n\n    self::projectionBase::operations::forEach((operation) => {\n      if !existsAtVersion(operation, version) {\n        self::addOperation(operation::name, operation::parameters, operation::returnType);\n      };\n    });\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection union#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  to(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(getNameAtVersion(self, version));\n    };\n\n    self::variants::forEach((variant) => {\n      if hasDifferentNameAtVersion(variant, version) {\n        self::renameVariant(variant::name, getNameAtVersion(variant, version));\n      };\n    });\n  }\n  from(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(self::projectionBase::name);\n    };\n\n    self::projectionBase::variants::forEach((variant) => {\n      if !existsAtVersion(variant, version) {\n        self::addVariant(variant::name, variant::type);\n      } else if hasDifferentNameAtVersion(variant, version) {\n        self::renameVariant(getNameAtVersion(variant, version), variant::name);\n      };\n    });\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection unionvariant#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection model#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  to(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(getNameAtVersion(self, version));\n    };\n    self::properties::forEach((p) => {\n      if hasDifferentNameAtVersion(p, version) {\n        self::renameProperty(p::name, getNameAtVersion(p, version));\n      };\n\n      if madeOptionalAfter(p, version) {\n        p::setOptional(false);\n      };\n\n      if madeRequiredAfter(p, version) {\n        p::setOptional(true);\n      };\n\n      if hasDifferentTypeAtVersion(p, version) {\n        self::changePropertyType(p::name, getTypeBeforeVersion(p, version));\n      };\n    });\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  from(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(self::projectionBase::name);\n    };\n\n    self::projectionBase::properties::forEach((p) => {\n      if !existsAtVersion(p, version) {\n        self::addProperty(p::name, p::type);\n      };\n\n      if hasDifferentNameAtVersion(p, version) {\n        self::renameProperty(getNameAtVersion(p, version), p::name);\n      };\n\n      if madeOptionalAfter(p, version) {\n        p::setOptional(true);\n      };\n\n      if madeRequiredAfter(p, version) {\n        p::setOptional(false);\n      };\n\n      if hasDifferentTypeAtVersion(p, version) {\n        self::changePropertyType(p::name, p::type);\n      };\n    });\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection modelproperty#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection enum#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  to(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(getNameAtVersion(self, version));\n    };\n\n    self::members::forEach((m) => {\n      if hasDifferentNameAtVersion(m, version) {\n        self::renameMember(m::name, getNameAtVersion(m, version));\n      };\n    });\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  from(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(self::projectionBase::name);\n    };\n\n    self::projectionBase::members::forEach((m) => {\n      if !existsAtVersion(m, version, self::projectionBase) {\n        self::addMember(m::name, m::type);\n      };\n\n      if hasDifferentNameAtVersion(m, version, self::projectionBase) {\n        self::renameMember(getNameAtVersion(m, version), m::name);\n      };\n    });\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection enummember#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n}\n\n#suppress \"projections-are-experimental\"\nprojection scalar#v {\n  pre to(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  to(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(getNameAtVersion(self, version));\n    };\n    if hasDifferentReturnTypeAtVersion(self, version) {\n      self::changeReturnType(getReturnTypeBeforeVersion(self, version));\n    };\n  }\n  pre from(version) {\n    if !existsAtVersion(self, version) {\n      return never;\n    };\n  }\n  from(version) {\n    if hasDifferentNameAtVersion(self, version) {\n      self::rename(self::projectionBase::name);\n    };\n  }\n}\n"
};
const _TypeSpecLibrary_ = {
  jsSourceFiles: TypeSpecJSSources,
  typespecSourceFiles: TypeSpecSources,
};

export { $added, $madeOptional, $madeRequired, $onValidate, $removed, $renamedFrom, $returnTypeChangedFrom, $typeChangedFrom, $useDependency, $versioned, Availability, VersionMap, _TypeSpecLibrary_, buildVersionProjections, findVersionedNamespace, getAddedOnVersions, getAvailabilityMap, getAvailabilityMapInTimeline, getMadeOptionalOn, getRemovedOnVersions, getRenamedFrom, getRenamedFromVersions, getReturnTypeChangedFrom, getTypeChangedFrom, getUseDependencies, getVersion, getVersionDependencies, getVersionForEnumMember, getVersions, getVersionsForEnum, resolveVersions };
