"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeValueCodec = exports.unitCodec = exports.ensureBinaryEncoding = exports.withDefaultBinaryEncoding = exports.makeCodecFamily = void 0;
const common_utils_1 = require("@fluidframework/common-utils");
const util_1 = require("../util");
/**
 * Creates a codec family from a registry of codecs.
 * Any codec that is not a {@link IMultiFormatCodec} will be wrapped with a default binary encoding.
 */
function makeCodecFamily(registry) {
    const codecs = new Map();
    for (const [formatVersion, codec] of registry) {
        if (codecs.has(formatVersion)) {
            (0, util_1.fail)("Duplicate codecs specified.");
        }
        codecs.set(formatVersion, ensureBinaryEncoding(codec));
    }
    return {
        resolve(formatVersion) {
            const codec = codecs.get(formatVersion);
            (0, common_utils_1.assert)(codec !== undefined, 0x5e6 /* Requested coded for unsupported format. */);
            return codec;
        },
        getSupportedFormats() {
            return codecs.keys();
        },
    };
}
exports.makeCodecFamily = makeCodecFamily;
class DefaultBinaryCodec {
    constructor(jsonCodec) {
        this.jsonCodec = jsonCodec;
    }
    encode(change) {
        const jsonable = this.jsonCodec.encode(change);
        const json = JSON.stringify(jsonable);
        return common_utils_1.IsoBuffer.from(json);
    }
    decode(change) {
        const json = (0, common_utils_1.bufferToString)(change, "utf8");
        const jsonable = JSON.parse(json);
        return this.jsonCodec.decode(jsonable);
    }
}
function isJsonCodec(codec) {
    return typeof codec.encode === "function" && typeof codec.decode === "function";
}
/**
 * Constructs a {@link IMultiFormatCodec} from a `IJsonCodec` using a generic binary encoding that simply writes
 * the json representation of the object to a buffer.
 */
function withDefaultBinaryEncoding(jsonCodec) {
    return {
        json: jsonCodec,
        binary: new DefaultBinaryCodec(jsonCodec),
    };
}
exports.withDefaultBinaryEncoding = withDefaultBinaryEncoding;
/**
 * Ensures that the provided single or multi-format codec has a binary encoding.
 * Adapts the json encoding using {@link withDefaultBinaryEncoding} if necessary.
 */
function ensureBinaryEncoding(codec) {
    return isJsonCodec(codec) ? withDefaultBinaryEncoding(codec) : codec;
}
exports.ensureBinaryEncoding = ensureBinaryEncoding;
/**
 * Codec for objects which carry no information.
 */
exports.unitCodec = {
    json: {
        encode: () => 0,
        decode: () => 0,
    },
    binary: {
        encode: () => common_utils_1.IsoBuffer.from(""),
        decode: () => 0,
    },
};
/**
 * Creates a json codec for objects which are just a json compatible value
 * and can be serialized as-is.
 *
 * This type of encoding is only appropriate if the persisted type (which should be defined in a persisted format file)
 * happens to be convenient for in-memory usage as well.
 *
 * @remarks - Beware that this encoder doesn't validate its input and isn't typesafe.
 * It would be great to be able to constrain T to a reasonable type, but due to how typechecking
 * of index signatures works, JsonCompatibleReadOnly isn't sufficient.
 *
 * TODO: This API is an anti-pattern for production code: using the same type for persisted and in-memory data
 * without any validation is prone to bugs involving extraneous data in the persisted format.
 * Consider the following example:
 * ```typescript
 * interface MyPersistedType {
 *     foo: string;
 *     id: number;
 * }
 *
 * const codec = makeValueCodec<MyPersistedType>();
 *
 * // Later, in some other file...
 * interface SomeInMemoryType extends MyPersistedType {
 *     someOtherProperty: string;
 * }
 *
 * const someInMemoryObject: SomeInMemoryType = {
 *     foo:	"bar",
 *     id: 0,
 *     someOtherProperty: "this shouldn't be here and ends up in the persisted format"
 * }
 *
 * const encoded = codec.encode(someInMemoryObject);
 * ```
 * This all typechecks, but the persisted format will contain the extraneous `someOtherProperty` field.
 * It's unlikely a real-life example would be this simple, but the principle is the same.
 *
 * This issue can be avoided using schema validation with schemas that generally shouldn't accept additional properties.
 *
 * AB#4074 tracks making this function take a typebox schema and using it to validate objects.
 * Note: this might allow usage of Jsonable, since the type parameter would be inferrable from the schema.
 */
function makeValueCodec() {
    return {
        encode: (obj) => obj,
        decode: (obj) => obj,
    };
}
exports.makeValueCodec = makeValueCodec;
//# sourceMappingURL=codec.js.map