"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaEditor = exports.SchemaSummarizer = void 0;
const client_utils_1 = require("@fluid-internal/client-utils");
const core_utils_1 = require("@fluidframework/core-utils");
const shared_object_base_1 = require("@fluidframework/shared-object-base");
const core_1 = require("../core");
const util_1 = require("../util");
const schemaIndexFormat_1 = require("./schemaIndexFormat");
/**
 * The storage key for the blob in the summary containing schema data
 */
const schemaBlobKey = "SchemaBlob";
const schemaStringKey = "SchemaString";
/**
 * Provides methods for summarizing and loading a schema repository.
 */
class SchemaSummarizer {
    constructor(runtime, schema, options) {
        this.runtime = runtime;
        this.schema = schema;
        this.key = "Schema";
        this.codec = (0, schemaIndexFormat_1.makeSchemaCodec)(options);
        this.schemaBlob = (0, core_1.cachedValue)(async (observer) => {
            (0, core_1.recordDependency)(observer, this.schema);
            const schemaText = this.codec.encode(this.schema);
            // For now we are not chunking the the schema, but still put it in a reusable blob:
            return this.runtime.uploadBlob(client_utils_1.IsoBuffer.from(schemaText));
        });
    }
    getAttachSummary(stringify, fullTree, trackState, telemetryContext) {
        const dataString = this.codec.encode(this.schema);
        return (0, shared_object_base_1.createSingleBlobSummary)(schemaStringKey, dataString);
    }
    async summarize(stringify, fullTree, trackState, telemetryContext) {
        const schemaBlobHandle = await this.schemaBlob.get();
        return (0, shared_object_base_1.createSingleBlobSummary)(schemaBlobKey, stringify(schemaBlobHandle));
    }
    getGCData(fullGC) {
        // TODO: Properly implement garbage collection. Right now, garbage collection is performed automatically
        // by the code in SharedObject (from which SharedTreeCore extends). The `runtime.uploadBlob` API delegates
        // to the `BlobManager`, which automatically populates the summary with ISummaryAttachment entries for each
        // blob.
        return {
            gcNodes: {},
        };
    }
    async load(services, parse) {
        const [hasString, hasBlob] = await Promise.all([
            services.contains(schemaStringKey),
            services.contains(schemaBlobKey),
        ]);
        (0, core_utils_1.assert)(hasString || hasBlob, 0x3d8 /* Schema is required in summary */);
        let schemaBuffer;
        if (hasBlob) {
            const handleBuffer = await services.readBlob(schemaBlobKey);
            const handleString = (0, client_utils_1.bufferToString)(handleBuffer, "utf-8");
            const handle = parse(handleString);
            schemaBuffer = await handle.get();
        }
        else {
            schemaBuffer = await services.readBlob(schemaStringKey);
        }
        // After the awaits, validate that the schema is in a clean state.
        // This detects any schema that could have been accidentally added through
        // invalid means and are about to be overwritten.
        (0, core_utils_1.assert)((0, core_1.schemaDataIsEmpty)(this.schema), 0x3da /* there should not already be stored schema when loading stored schema */);
        const schemaString = (0, client_utils_1.bufferToString)(schemaBuffer, "utf-8");
        const decoded = this.codec.decode(schemaString);
        this.schema.update(decoded);
    }
}
exports.SchemaSummarizer = SchemaSummarizer;
/**
 * Wraps a StoredSchemaRepository, adjusting its "update" function to hook into Fluid Ops.
 *
 * TODO: this should be more integrated with transactions.
 */
class SchemaEditor {
    constructor(inner, submit, options) {
        this.inner = inner;
        this.submit = submit;
        this.codec = (0, schemaIndexFormat_1.makeSchemaCodec)(options);
    }
    on(eventName, listener) {
        return this.inner.on(eventName, listener);
    }
    /**
     * @returns true if this is a schema op and was handled.
     *
     * TODO: Shared tree needs a pattern for handling non-changeset operations.
     * See TODO on `SharedTree.processCore`.
     */
    tryHandleOp(encodedOp) {
        const op = this.tryDecodeOp(encodedOp);
        if (op !== undefined) {
            // TODO: This does not correctly handle concurrency of schema edits.
            this.inner.update(op);
            return true;
        }
        return false;
    }
    tryApplyStashedOp(encodedOp) {
        return this.tryHandleOp(encodedOp);
    }
    /**
     * @returns true iff this is a schema op and was submitted.
     *
     * TODO: Shared tree needs a pattern for handling non-changeset operations.
     * See TODO on `SharedTree.processCore`.
     */
    tryResubmitOp(content) {
        const op = content;
        if ((0, util_1.isJsonObject)(op) && op.type === "SchemaOp") {
            (0, core_utils_1.assert)(typeof op.data === "string", 0x5e3 /* expected string data for resubmitted schema op */);
            const schemaOp = {
                type: op.type,
                data: op.data,
            };
            this.submit(schemaOp);
            return true;
        }
        return false;
    }
    update(newSchema) {
        const op = { type: "SchemaOp", data: this.codec.encode(newSchema) };
        this.submit(op);
        this.inner.update(newSchema);
    }
    registerDependent(dependent) {
        return this.inner.registerDependent(dependent);
    }
    removeDependent(dependent) {
        return this.inner.removeDependent(dependent);
    }
    get computationName() {
        return this.inner.computationName;
    }
    get listDependees() {
        return this.inner.listDependees?.bind(this.inner);
    }
    get rootFieldSchema() {
        return this.inner.rootFieldSchema;
    }
    get treeSchema() {
        return this.inner.treeSchema;
    }
    tryDecodeOp(encodedOp) {
        if ((0, util_1.isJsonObject)(encodedOp) && encodedOp.type === "SchemaOp") {
            (0, core_utils_1.assert)(typeof encodedOp.data === "string", 0x6ca /* SchemaOps should have string data */);
            return this.codec.decode(encodedOp.data);
        }
        return undefined;
    }
}
exports.SchemaEditor = SchemaEditor;
//# sourceMappingURL=schemaSummarizer.js.map