"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
    if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
    return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SharedTreeFactory = exports.defaultSharedTreeOptions = exports.ForestType = exports.SharedTreeFormatVersion = exports.SharedTree = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/telemetry-utils/internal");
const index_js_1 = require("../codec/index.js");
const index_js_2 = require("../core/index.js");
const index_js_3 = require("../events/index.js");
const index_js_4 = require("../feature-libraries/index.js");
const index_js_5 = require("../shared-tree-core/index.js");
const schematizingTreeView_js_1 = require("./schematizingTreeView.js");
const sharedTreeChangeEnricher_js_1 = require("./sharedTreeChangeEnricher.js");
const sharedTreeChangeFamily_js_1 = require("./sharedTreeChangeFamily.js");
const treeCheckout_js_1 = require("./treeCheckout.js");
const index_js_6 = require("../util/index.js");
const formatVersionToTopLevelCodecVersions = new Map([
    [
        1,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 1, message: 1, fieldBatch: 1 },
    ],
    [
        2,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 2, message: 2, fieldBatch: 1 },
    ],
    [
        3,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 3, message: 3, fieldBatch: 1 },
    ],
    [
        4,
        { forest: 1, schema: 1, detachedFieldIndex: 1, editManager: 4, message: 4, fieldBatch: 1 },
    ],
]);
function getCodecVersions(formatVersion) {
    const versions = formatVersionToTopLevelCodecVersions.get(formatVersion);
    (0, internal_1.assert)(versions !== undefined, 0x90e /* Unknown format version */);
    return versions;
}
/**
 * Shared tree, configured with a good set of indexes and field kinds which will maintain compatibility over time.
 *
 * TODO: detail compatibility requirements.
 */
let SharedTree = (() => {
    let _classDecorators = [index_js_6.breakingClass];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = index_js_5.SharedTreeCore;
    let _instanceExtraInitializers = [];
    let _contentSnapshot_decorators;
    var SharedTree = _classThis = class extends _classSuper {
        get storedSchema() {
            return this.checkout.storedSchema;
        }
        constructor(id, runtime, attributes, optionsParam, telemetryContextPrefix = "fluid_sharedTree_") {
            if (runtime.idCompressor === undefined) {
                throw new internal_2.UsageError("IdCompressor must be enabled to use SharedTree");
            }
            const options = { ...exports.defaultSharedTreeOptions, ...optionsParam };
            const codecVersions = getCodecVersions(options.formatVersion);
            const schema = new index_js_2.TreeStoredSchemaRepository();
            const forest = options.forest === ForestType.Optimized
                ? (0, index_js_4.buildChunkedForest)((0, index_js_4.makeTreeChunker)(schema, index_js_4.defaultSchemaPolicy), undefined, runtime.idCompressor)
                : options.forest === ForestType.Reference
                    ? (0, index_js_4.buildForest)()
                    : (0, index_js_4.buildForest)(undefined, true);
            const revisionTagCodec = new index_js_2.RevisionTagCodec(runtime.idCompressor);
            const removedRoots = (0, index_js_2.makeDetachedFieldIndex)("repair", revisionTagCodec, runtime.idCompressor, options);
            const schemaSummarizer = new index_js_4.SchemaSummarizer(runtime, schema, options, {
                getCurrentSeq: () => this.deltaManager.lastSequenceNumber,
            });
            const fieldBatchCodec = (0, index_js_4.makeFieldBatchCodec)(options, codecVersions.fieldBatch);
            const encoderContext = {
                schema: {
                    schema,
                    policy: index_js_4.defaultSchemaPolicy,
                },
                encodeType: options.treeEncodeType,
                originatorId: runtime.idCompressor.localSessionId,
                idCompressor: runtime.idCompressor,
            };
            const forestSummarizer = new index_js_4.ForestSummarizer(forest, revisionTagCodec, fieldBatchCodec, encoderContext, options, runtime.idCompressor);
            const removedRootsSummarizer = new index_js_4.DetachedFieldIndexSummarizer(removedRoots);
            const innerChangeFamily = new sharedTreeChangeFamily_js_1.SharedTreeChangeFamily(revisionTagCodec, fieldBatchCodec, options, options.treeEncodeType, runtime.idCompressor);
            const changeFamily = (0, index_js_4.makeMitigatedChangeFamily)(innerChangeFamily, sharedTreeChangeFamily_js_1.SharedTreeChangeFamily.emptyChange, (error) => {
                // TODO:6344 Add telemetry for these errors.
                // Rethrowing the error has a different effect depending on the context in which the
                // ChangeFamily was invoked:
                // - If the ChangeFamily was invoked as part of incoming op processing, rethrowing the error
                // will cause the runtime to disconnect the client, log a severe error, and not reconnect.
                // This will not cause the host application to crash because it is not on the stack at that time.
                // TODO: let the host application know that the client is now disconnected.
                // - If the ChangeFamily was invoked as part of dealing with a local change, rethrowing the
                // error will cause the host application to crash. This is not ideal, but is better than
                // letting the application either send an invalid change to the server or allowing the
                // application to continue working when its local branches contain edits that cannot be
                // reflected in its views.
                // The best course of action for a host application in such a state is to restart.
                // TODO: let the host application know about this situation and provide a way to
                // programmatically reload the SharedTree container.
                throw error;
            });
            const changeEnricher = new sharedTreeChangeEnricher_js_1.SharedTreeReadonlyChangeEnricher(forest, schema, removedRoots);
            super([schemaSummarizer, forestSummarizer, removedRootsSummarizer], changeFamily, options, codecVersions, id, runtime, attributes, telemetryContextPrefix, schema, index_js_4.defaultSchemaPolicy, new index_js_5.DefaultResubmitMachine(changeFamily.rebaser.invert.bind(changeFamily.rebaser), changeEnricher), changeEnricher);
            this._events = __runInitializers(this, _instanceExtraInitializers);
            this._events = (0, index_js_3.createEmitter)();
            const localBranch = this.getLocalBranch();
            this.checkout = (0, treeCheckout_js_1.createTreeCheckout)(runtime.idCompressor, this.mintRevisionTag, revisionTagCodec, {
                branch: localBranch,
                changeFamily,
                schema,
                forest,
                fieldBatchCodec,
                events: this._events,
                removedRoots,
                chunkCompressionStrategy: options.treeEncodeType,
                logger: this.logger,
            });
        }
        contentSnapshot() {
            const cursor = this.checkout.forest.allocateCursor("contentSnapshot");
            try {
                (0, index_js_2.moveToDetachedField)(this.checkout.forest, cursor);
                return {
                    schema: this.storedSchema.clone(),
                    tree: (0, index_js_4.jsonableTreeFromFieldCursor)(cursor),
                    removed: this.checkout.getRemovedRoots(),
                };
            }
            finally {
                cursor.free();
            }
        }
        viewWith(config) {
            return new schematizingTreeView_js_1.SchematizingSimpleTreeView(this.checkout, config, (0, index_js_4.createNodeKeyManager)(this.runtime.idCompressor), this.breaker);
        }
        async loadCore(services) {
            await super.loadCore(services);
            this.checkout.setTipRevisionForLoadedData(this.trunkHeadRevision);
            this._events.emit("afterBatch");
        }
    };
    __setFunctionName(_classThis, "SharedTree");
    (() => {
        const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
        _contentSnapshot_decorators = [index_js_6.throwIfBroken];
        __esDecorate(_classThis, null, _contentSnapshot_decorators, { kind: "method", name: "contentSnapshot", static: false, private: false, access: { has: obj => "contentSnapshot" in obj, get: obj => obj.contentSnapshot }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
        SharedTree = _classThis = _classDescriptor.value;
        if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        __runInitializers(_classThis, _classExtraInitializers);
    })();
    return SharedTree = _classThis;
})();
exports.SharedTree = SharedTree;
/**
 * Format versions supported by SharedTree.
 *
 * Each version documents a required minimum version of the \@fluidframework/tree package.
 * @internal
 */
exports.SharedTreeFormatVersion = {
    /**
     * Requires \@fluidframework/tree \>= 2.0.0.
     *
     * @deprecated - FF does not currently plan on supporting this format long-term.
     * Do not write production documents using this format, as they may not be loadable in the future.
     */
    v1: 1,
    /**
     * Requires \@fluidframework/tree \>= 2.0.0.
     */
    v2: 2,
    /**
     * Requires \@fluidframework/tree \>= 2.0.0.
     */
    v3: 3,
};
/**
 * Used to distinguish between different forest types.
 * @internal
 */
var ForestType;
(function (ForestType) {
    /**
     * The "ObjectForest" forest type.
     */
    ForestType[ForestType["Reference"] = 0] = "Reference";
    /**
     * The "ChunkedForest" forest type.
     */
    ForestType[ForestType["Optimized"] = 1] = "Optimized";
    /**
     * The "ObjectForest" forest type with expensive asserts for debugging.
     */
    ForestType[ForestType["Expensive"] = 2] = "Expensive";
})(ForestType || (exports.ForestType = ForestType = {}));
exports.defaultSharedTreeOptions = {
    jsonValidator: index_js_1.noopValidator,
    forest: ForestType.Reference,
    treeEncodeType: index_js_4.TreeCompressionStrategy.Compressed,
    formatVersion: exports.SharedTreeFormatVersion.v3,
};
/**
 * A channel factory that creates {@link ISharedTree}s.
 */
class SharedTreeFactory {
    constructor(options = {}) {
        this.options = options;
        this.type = "https://graph.microsoft.com/types/tree";
        this.attributes = {
            type: this.type,
            snapshotFormatVersion: "0.0.0",
            packageVersion: "0.0.0",
        };
    }
    async load(runtime, id, services, channelAttributes) {
        const tree = new SharedTree(id, runtime, channelAttributes, this.options);
        await tree.load(services);
        return tree;
    }
    create(runtime, id) {
        const tree = new SharedTree(id, runtime, this.attributes, this.options);
        tree.initializeLocal();
        return tree;
    }
}
exports.SharedTreeFactory = SharedTreeFactory;
//# sourceMappingURL=sharedTree.js.map