"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.jsonableCodecTree = exports.currentVersion = exports.FluidClientVersion = exports.withSchemaValidation = exports.unitCodec = exports.makeCodecFamily = exports.DependentFormatVersion = exports.MappedDependentFormatVersion = exports.UniqueDependentFormatVersion = exports.eraseEncodedType = exports.extractJsonValidator = exports.toFormatValidator = exports.FormatValidatorNoOp = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/runtime-utils/internal");
/**
 * A {@link JsonValidator} implementation which performs no validation and accepts all data as valid.
 * @privateRemarks Having this as an option unifies opting out of validation with selection of
 * validators, simplifying code performing validation.
 */
const noopValidator = {
    compile: () => ({ check: (data) => true }),
};
/**
 * A {@link FormatValidator} which does no validation.
 * @alpha
 */
exports.FormatValidatorNoOp = toFormatValidator(noopValidator);
/**
 * Type erase a {@link JsonValidator} to a {@link FormatValidator}.
 */
function toFormatValidator(factory) {
    return factory;
}
exports.toFormatValidator = toFormatValidator;
/**
 * Un-type-erase the {@link FormatValidator}.
 */
function extractJsonValidator(input) {
    return input;
}
exports.extractJsonValidator = extractJsonValidator;
/**
 * Type erase the more detailed encoded type from a codec.
 */
function eraseEncodedType(codec) {
    return codec;
}
exports.eraseEncodedType = eraseEncodedType;
class UniqueDependentFormatVersion {
    constructor(child) {
        this.child = child;
    }
    lookup(_parent) {
        return this.child;
    }
}
exports.UniqueDependentFormatVersion = UniqueDependentFormatVersion;
class MappedDependentFormatVersion {
    constructor(map) {
        this.map = map;
    }
    lookup(parent) {
        return this.map.get(parent) ?? (0, internal_1.fail)(0xc73 /* Unknown parent version */);
    }
}
exports.MappedDependentFormatVersion = MappedDependentFormatVersion;
exports.DependentFormatVersion = {
    fromUnique: (child) => new UniqueDependentFormatVersion(child),
    fromMap: (map) => new MappedDependentFormatVersion(map),
    fromPairs: (pairs) => new MappedDependentFormatVersion(new Map(pairs)),
};
/**
 * Creates a codec family from a registry of codecs.
 */
function makeCodecFamily(registry) {
    const codecs = new Map();
    for (const [formatVersion, codec] of registry) {
        if (codecs.has(formatVersion)) {
            (0, internal_1.fail)(0xabf /* Duplicate codecs specified. */);
        }
        codecs.set(formatVersion, codec);
    }
    return {
        resolve(formatVersion) {
            const codec = codecs.get(formatVersion);
            (0, internal_1.assert)(codec !== undefined, 0x5e6 /* Requested codec for unsupported format. */);
            return codec;
        },
        getSupportedFormats() {
            return codecs.keys();
        },
    };
}
exports.makeCodecFamily = makeCodecFamily;
/**
 * Codec for objects which carry no information.
 */
exports.unitCodec = {
    encode: () => 0,
    decode: () => 0,
};
/**
 * Wraps a codec with JSON schema validation for its encoded type.
 * @returns An {@link IJsonCodec} which validates the data it encodes and decodes matches the provided schema.
 * @remarks
 * Eventually all codecs should use the same pattern implemented by ClientVersionDispatchingCodecBuilder, resulting in that having the only use of this API.
 */
function withSchemaValidation(schema, codec, validator) {
    if (!validator) {
        return codec;
    }
    const compiledFormat = extractJsonValidator(validator).compile(schema);
    return {
        encode: (obj, context) => {
            const encoded = codec.encode(obj, context);
            if (!compiledFormat.check(encoded)) {
                (0, internal_1.fail)(0xac0 /* Encoded data should validate */);
            }
            return encoded;
        },
        decode: (encoded, context) => {
            if (!compiledFormat.check(encoded)) {
                (0, internal_1.fail)(0xac1 /* Data being decoded should validate */);
            }
            return codec.decode(encoded, context);
        },
        encodedSchema: schema,
    };
}
exports.withSchemaValidation = withSchemaValidation;
/**
 * Versions of Fluid Framework client packages.
 * @remarks
 * Used to express compatibility requirements by indicating the oldest version with which compatibility must be maintained.
 *
 * When no compatibility-impacting change is made in a given version, the value associated with its enum entry may point to the older version which it's fully compatible with.
 * Note that this can change if a future version of the framework introduces an option to use something which is only supported at a particular version. In which case, the values of the enum may shift,
 * but the semantics of keys in this enum will not change.
 *
 * Do not depend on the value of this enums's entries: only depend on the keys (enum members) themselves.
 *
 * Some release may also be omitted if there is currently no need to express that specific version.
 * If the need arises, they might be added in the future.
 *
 * @privateRemarks
 * The entries in these enums should document the following:
 * - The user facing impact of opting into a particular version. This will help customers decide if they want to opt into
 * a new version. For example, document if there is an encoding efficiency improvement of oping into that version or newer.
 * - Any new data formats that are introduced in that version. This will help developers tell which data formats a given
 * version will write. For example, document if a new summary or encoding format is added in a version.
 * - Whether the above features or data formats introduced in a version are enabled by default or require the
 * {@link minVersionForCollab} option to be set to that particular version.
 *
 * Versions with no notable impact can be omitted.
 *
 * This scheme assumes a single version will always be enough to communicate compatibility.
 * For this to work, compatibility has to be strictly increasing.
 * If this is violated (for example a subset of incompatible features from 3.x that are not in 3.0 are back ported to 2.x),
 * a more complex scheme may be needed to allow safely opting into incompatible features in those cases:
 * such a system can be added if/when its needed since it will be opt in and thus non-breaking.
 *
 * TODO: this should likely be defined higher in the stack and specified when creating the container, possibly as part of its schema.
 * TODO: compatibility requirements for how this enum can and cannot be changed should be clarified when/if it's used across multiple layers in the stack.
 * For example, if needed, would adding more leading zeros to the minor version break things.
 * @alpha
 */
exports.FluidClientVersion = {
    /**
     * Fluid Framework Client 1.4 and newer.
     * @remarks
     * This opts into support for the 1.4 LTS branch.
     * @privateRemarks
     * As long as this code is in Tree, there is no reason to have this option as SharedTree did not exist in 1.4.
     */
    // v1_4 = 1.004,
    /**
     * Fluid Framework Client 2.0 and newer.
     */
    v2_0: "2.0.0",
    /** Fluid Framework Client 2.1 and newer. */
    // If we think we might want to start allowing opting into something that landed in 2.1 (without opting into something newer),
    // we could add an entry like this to allow users to indicate that they can be opted in once we are ready,
    // then update it to "2.001" once we actually have the opt in working.
    // v2_1 = v2_0,
    /**
     * Fluid Framework Client 2.43 and newer.
     * @remarks
     * New formats introduced in 2.43:
     * - SchemaFormatVersion.v2 - written when minVersionForCollab \>= 2.43
     * - MessageFormatVersion.v4 - written when minVersionForCollab \>= 2.43
     * - EditManagerFormatVersion.v4 - written when minVersionForCollab \>= 2.43
     * - sequence-field/formatV3 - written when minVersionForCollab \>= 2.43
     */
    v2_43: "2.43.0",
    /**
     * Fluid Framework Client 2.52 and newer.
     * @remarks
     * New formats introduced in 2.52:
     * - DetachedFieldIndexFormatVersion.v2 - written when minVersionForCollab \>= 2.52
     */
    v2_52: "2.52.0",
    /**
     * Fluid Framework Client 2.73 and newer.
     * @remarks
     * New formats introduced in 2.73:
     * - FieldBatchFormatVersion.v2 - written when minVersionForCollab \>= 2.73
     */
    v2_73: "2.73.0",
    /**
     * Fluid Framework Client 2.74 and newer.
     * @remarks
     * New formats introduced in 2.74:
     * - SharedTreeSummaryFormatVersion.v2 - written by default
     * - DetachedFieldIndexSummaryFormatVersion.v2 - written by default
     * - SchemaSummaryFormatVersion.v2 - written by default
     * - EditManagerSummaryFormatVersion.v2 - written by default
     * - ForestSummaryFormatVersion.v2 - written by default
     * - ForestFormatVersion.v2 - written when minVersionForCollab \>= 2.74
     * - ForestSummaryFormatVersion.v3 - written when minVersionForCollab \>= 2.74
     */
    v2_74: "2.74.0",
    /**
     * Fluid Framework Client 2.80 and newer.
     * @remarks
     * New formats introduced in 2.80:
     * - MessageFormatVersion.v6 - written when minVersionForCollab \>= 2.80
     * - EditManagerFormatVersion.v6 - written when minVersionForCollab \>= 2.80
     * - SharedTreeChangeFormatVersion.v5 - written when minVersionForCollab \>= 2.80
     * - ModularChangeFormatVersion.v5 - written when minVersionForCollab \>= 2.80
     */
    v2_80: "2.80.0",
};
/**
 * An up to date version which includes all stable features.
 * @remarks
 * Use for cases when data is not persisted and thus would only ever be read by the the same version of the code which read this value.
 *
 * The pkgVersion from this package (tree) can not be used here as it is not guaranteed to be a valid MinimumVersionForCollab
 * and would also unexpectedly disable features in prereleases and on CI if it didn't fail validation.
 * See {@link @fluidframework/runtime-utils/internal#cleanedPackageVersion} for more details on why cleanedPackageVersion is preferred over pkgVersion.
 *
 * @privateRemarks
 * It is safe to use CleanedPackageVersion from runtime-utils here since features are enabled in minor versions,
 * and this package (tree) depends on runtime-utils with a `~` semver range
 * ensuring that the version of runtime-utils this was imported from will match the version of this (tree) package at least up to the minor version.
 * Reusing this from runtime-utils avoids duplicating the cleanup logic here as well as the cost or recomputing it.
 * If in the future for some reason this becomes not okay, runtime-utils could instead export a function that performs that cleanup logic which could be reused here.
 */
exports.currentVersion = internal_2.cleanedPackageVersion;
function jsonableCodecTree(tree) {
    return {
        name: tree.name,
        version: tree.version ?? "null",
        children: tree.children?.map(jsonableCodecTree),
    };
}
exports.jsonableCodecTree = jsonableCodecTree;
//# sourceMappingURL=codec.js.map