/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import type { SemanticVersion } from "@fluidframework/runtime-utils/internal";
import type { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
/**
 * Descripe allowed type for properties in document schema.
 * Please note that for all property types we should use undefined to indicate that particular capability is off.
 * Using false, or some string value (like "off") will result in clients who do not understand that property failing, whereas
 * we want them to continue to collaborate alongside clients who support that capability, but such capability is shipping dark for now.
 * @internal
 */
export type DocumentSchemaValueType = string | string[] | true | number | undefined;
/**
 * ID Compressor mode.
 * "on" - compressor is On. It's loaded as part of container load. This mode is sticky - once on, compressor is On for all
 * sessions for a given document. This results in IContainerRuntime.idCompressor to be always available.
 * "delayed" - ID compressor bundle is loaded only on establishing of first delta connection, i.e. it does not impact boot of cotnainer.
 * In such mode IContainerRuntime.idCompressor is not made available (unless previous sessions of same document had it "On").
 * The only thing that is available is IContainerRuntime.generateDocumentUniqueId() that provides opportunistically short IDs.
 * undefined - ID compressor is not loaded.
 * While IContainerRuntime.generateDocumentUniqueId() is available, it will produce long IDs that are do not compress well.
 *
 * @legacy @beta
 */
export type IdCompressorMode = "on" | "delayed" | undefined;
/**
 * Document schema information.
 * Describes overall shape of document schema, including unknown (to this version) properties.
 *
 * Used by runtime to make a call if it can understand document schema.
 * If it can't, it should not continue with document and immediately fail, preventing random (cryptic) failures
 * down the road and potentially corrupting documents.
 * For now this structure and appropriate interpretation / behavior is focused only on runtime features.
 * In the future that could be interpolated to more areas, including DDSs used, and even possibly - application
 * schema.
 *
 * Runtime will ignore any properties at the root that it does not understand (i.e. IDocumentSchema.app), but will
 * stop (and fail session) on any unknown properties within "runtime" sub-tree.
 *
 * In most cases values preserved in the document will not dictate if such features should be enabled in a given session.
 * I.e. if compression is mentioned in document schema, this means that runtime version that opens such document must know
 * how to interpret such ops, but does not need to actually use compression itself. That said, some options could be
 * sticky, i.e. influence feature selection for all runtimes opening a document. ID compression is one such example.
 * Currently there is no mechanism to remove feature from this property bag, i.e. once compression was used, even if it's
 * disabled (through feature gate or code deployment), all existing documents that used compression will continue to fail
 * if opened by clients who do not support compression.
 *
 * For now we are limiting it to just plain properties, and only really simple types, but that can be changed in the future.
 *
 * @internal
 */
export interface IDocumentSchema {
    /**
     * Describes how data needed to understand the schema is stored in this structure.
     * If runtime sees a version it does not understand, it should immediately fail and not
     * attempt to interpret any further data.
     */
    version: number;
    /**
     * Sequence number when this schema became active.
     */
    refSeq: number;
    /**
     * Runtime configurations that affect the document schema. Other clients must understand these
     * properties to be able to open the document.
     */
    runtime: Record<string, DocumentSchemaValueType>;
    /**
     * Info about this document that can be updated via Document Schema change op, but isn't required
     * to be understood by all clients (unlike the rest of IDocumentSchema properties). Because of this,
     * some older documents may not have this property, so it's an optional property.
     */
    info?: IDocumentSchemaInfo;
}
/**
 * Informational properties of the document that are not subject to strict schema enforcement.
 *
 * @internal
 */
export interface IDocumentSchemaInfo {
    /**
     * The minimum version of the FF runtime that should be used to load this document.
     * Will likely be advanced over time as applications pick up later FF versions.
     *
     * We use this to issue telemetry warning events if a client tries to open a document
     * with a runtime version lower than this.
     *
     * See {@link @fluidframework/container-runtime#LoadContainerRuntimeParams} for additional details on `minVersionForCollab`.
     *
     * @remarks
     * We use `SemanticVersion` instead of `MinimumVersionForCollab` since we may open future documents with a
     * minVersionForCollab version that `MinimumVersionForCollab` does not support.
     * Note that in such a case (where minVersionForCollab is not a valid `MinimumVersionForCollab`),
     * loading the document might not work since this version of the runtime may not support it.
     */
    minVersionForCollab: SemanticVersion;
}
/**
 * Content of the type=ContainerMessageType.DocumentSchemaChange ops.
 * The meaning of refSeq field is different in such messages (compared to other usages of IDocumentSchemaCurrent)
 * ContainerMessageType.DocumentSchemaChange messages use CAS (Compare-and-swap) semantics, and convey
 * regSeq of last known schema change (known to a client proposing schema change).
 * @see InboundContainerRuntimeDocumentSchemaMessage
 * @internal
 */
export type IDocumentSchemaChangeMessageIncoming = IDocumentSchema;
/**
 * Similar to {@link IDocumentSchemaChangeMessageIncoming}, but used for outgoing schema messages.
 * @see OutboundContainerRuntimeDocumentSchemaMessage
 * @internal
 */
export type IDocumentSchemaChangeMessageOutgoing = IDocumentSchemaCurrent;
/**
 * Settings that this session would like to have, based on options and feature gates.
 *
 * WARNING: This type is used to infer IDocumentSchemaCurrent type!
 * Any changes here (including renaming of properties) are potentially changing document format and should be considered carefully!
 *
 * @internal
 */
export interface IDocumentSchemaFeatures {
    explicitSchemaControl: boolean;
    compressionLz4: boolean;
    idCompressorMode: IdCompressorMode;
    opGroupingEnabled: boolean;
    createBlobPayloadPending: true | undefined;
    /**
     * List of disallowed versions of the runtime.
     * This option is sticky. Once a version of runtime is added to this list (when supplied to DocumentsSchemaController's constructor)
     * it will be added to the list of disallowed versions and stored in document metadata.
     * Each runtime checks if its version is in this list on container open. If it is, it immediately exits with error message
     * indicating to the user that this version is no longer supported.
     * Currently there is no mechanism to remove version from this list. I.e. If it was once added to the list,
     * it gets added to any document metadata (documents that gets open by this runtime) and there is no way to clear it from document's
     * metadata.
     */
    disallowedVersions: string[];
}
/**
 * Current version known properties that define document schema
 * This must be bumped whenever the format of document schema or protocol for changing the current document schema changes
 * in a way that all old/new clients are required to understand.
 * Ex: Adding a new configuration property (under IDocumentSchema.runtime) does not require changing this version since there is logic
 * in old clients for handling new/unknown properties.
 * Ex: Adding a new property to IDocumentSchema.info does not require changing this version, since info properties are not required to be
 * understood by all clients.
 * Ex: Changing the 'document schema acceptance' mechanism from convert-and-swap to one requiring consensus does require changing this version
 * since all clients need to understand the new protocol.
 * @internal
 */
export declare const currentDocumentVersionSchema = 1;
/**
 * Current document schema.
 * This interface represents the schema that we currently understand and know the
 * structure of (which properties will be present).
 *
 * @internal
 */
export interface IDocumentSchemaCurrent extends Required<IDocumentSchema> {
    version: typeof currentDocumentVersionSchema;
    runtime: {
        [P in keyof IDocumentSchemaFeatures]?: IDocumentSchemaFeatures[P] extends boolean ? true : IDocumentSchemaFeatures[P];
    };
}
/**
 * Controller of document schema.
 *
 * Recommended pre-reading: https://github.com/microsoft/FluidFramework/blob/main/packages/dds/SchemaVersioning.md
 *
 * This class manages current document schema and transitions between document schemas.
 * At the moment, it only focuses on subset of document schema, specifically - how FluidFramework runtime serializes data
 * (summary and op format), features & capabilities that a version of runtime has to support and understand in
 * order to collaborate on a document.
 * New features that modify document format have to be included in document schema definition.
 * Usage of such features could only happen after document schema has been updated to reflect such feature.
 *
 * This formality allows clients that do not understand such features to fail right away when they observe
 * document schema listing capabilities that such client does not understand.
 * Old clients will fail in predictable way. This allows us to
 * 1) Immediately see such issues and adjust if features are enabled too early, before changes have been saturated.
 * 2) There is no way to get to 100% saturation with new code. Even if we have 99.99% saturation, there are
 *    still 0.01% of clients who will fail. Failing early and predictably ensures they have no chance to limp along
 *    and potentially corrupt the document. This is especially true for summarizer client, who could simply "undo"
 *    changes it does not understands.
 *
 * It's important to note how it overlaps with feature gates and safe velocity.
 * If new feature was in use, that resulted in a number of documents referencing such feature in document schema.
 * But, developers (through code deployment or feature gates) could disable usage of such features.
 * That will stop a process of further document schema changes (for documents that were not using such feature).
 * And documents that already list such capability in their schema will continue to do so. Later ensures that old
 * clients who do not understand such feature will continue to fail to open such documents, as such documents very
 * likely contain data in a new format.
 *
 * Controller operates with 4 schemas:
 * - document schema: whatever we loaded from summary metadata + ops. It follows eventuall consistency rules (i.e. like DDS).
 * - desired schema - what client is asking for to have (i.e. all the desired settings, based on runtime options / feature gates).
 * - session schema - current session schema. It's "and" of the above two schemas.
 * - future schema - "or" of document and desires schemas.
 *
 * "or" & "and" operators are defined individually for each property. For Boolean properties it's literally &&, || operators.
 * But for other properties it's more nuanced.
 *
 * Whenver document schema does not match future schema, controller will send an op that attempts to changs documents schema to
 * future schema.
 *
 * Users of this class need to use DocumentsSchemaController.sessionSchema to determine what features can be used.
 *
 * There are two modes this class can operate:
 * 1) Legacy mode. In such mode it does not issue any ops to change document schema. Any changes happen implicitly,
 *    right away, and new features are available right away
 * 2) Non-legacy mode. In such mode any changes to schema require an op roundtrip. This class will manage such transitions.
 *    However code should assume that any new features that were not enabled in a given document will not be available
 *    for a given session. That's because this session may never send any ops (including read-only documents). Or it may
 *    fail to convert schema.
 *    This class promises eventual movement forward. I.e. if new feature is allowed (let's say - through feature gates),
 *    then eventually all documents that are modified will have that feature reflected in their schema. It could require
 *    multiple reloads / new sessions to get there (depends on if code reacts to schema changes right away, or only consults
 *    schema on document load).
 *
 * How schemas are changed (in non-legacy mode):
 * If a client needs to change a schema, it will attempt to do so as part of normal ops sending process.
 * Changes happen in CAS (Compare-and-swap) fashion, i.e. client tells current schema and schema it wants to change to.
 * When a number of clients race to change a schema, then only one of them will win, all others will fail because they will
 * reference old schema that is no longer in effect.
 * Clients can retry, but current implementation is simply - they will not (and will rely on next session / reload to do
 * recalc and decide if schema needs to be changed or not).
 *
 * @internal
 * @sealed
 */
export declare class DocumentsSchemaController {
    private readonly onSchemaChange;
    private explicitSchemaControl;
    /**
     * Have we generated a DocumentSchemaChange op and we're waiting for the ack?
     * This is used to ensure that we do not generate multiple schema change ops - this client should only ever send one (if any).
     */
    private opPending;
    private documentSchema;
    private readonly desiredSchema;
    private futureSchema;
    sessionSchema: IDocumentSchemaCurrent;
    /**
     * Constructs DocumentsSchemaController that controls current schema and processes around it, including changes in schema.
     * @param existing - Is the document existing document, or a new doc.
     * @param documentMetadataSchema - current document's schema, if present.
     * @param features - features of the document schema that current session wants to see enabled.
     * @param onSchemaChange - callback that is called whenever schema is changed (not called on creation / load, only when processing document schema change ops)
     * @param info - Informational properties of the document that are not subject to strict schema enforcement
     * @param logger - telemetry logger from the runtime
     */
    constructor(existing: boolean, snapshotSequenceNumber: number, documentMetadataSchema: IDocumentSchema | undefined, features: IDocumentSchemaFeatures, onSchemaChange: (schema: IDocumentSchemaCurrent) => void, info: IDocumentSchemaInfo, logger: ITelemetryLoggerExt);
    summarizeDocumentSchema(refSeq: number): IDocumentSchema | IDocumentSchemaCurrent | undefined;
    /**
     * Called by Container runtime whenever it is about to send some op.
     * It gives opportunity for controller to issue its own ops - we do not want to send ops if there are no local changes in document.
     * Please consider note above constructor about race conditions - current design is to generate op only once in a session lifetime.
     * @returns Optional message to send.
     */
    maybeGenerateSchemaMessage(): IDocumentSchemaChangeMessageOutgoing | undefined;
    private validateSeqNumber;
    /**
     * Process document schema change messages
     * Called by ContainerRuntime whenever it sees document schema messages.
     * @param contents - contents of the messages
     * @param local - whether op is local
     * @param sequenceNumber - sequence number of the op
     * @returns true if schema was accepted, otherwise false (rejected due to failed CAS)
     */
    processDocumentSchemaMessages(contents: IDocumentSchemaChangeMessageIncoming[], local: boolean, sequenceNumber: number): boolean;
    /**
     * Indicates the pending op was not ack'd and we may try to send it again if needed.
     */
    pendingOpNotAcked(): void;
}
//# sourceMappingURL=documentSchema.d.ts.map