UNPKG

@babylonjs/viewer

Version:

The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.

5,235 lines 254 kB
import { y as Logger, M as Matrix, b3 as Vector2, V as Vector3, bc as Vector4, aD as Quaternion, i as Color3, b1 as Color4, l as __runInitializers, v as RandomGUID, O as Observable, q as __esDecorate, s as serialize, cI as IsNavigatorAvailable, by as PointerEventTypes, cd as KeyboardEventTypes, aY as Tools, A as AbstractEngine, bY as registeredGLTFExtensions, C as Constants, bo as unregisterGLTFExtension, bp as registerGLTFExtension } from './index-HyNDfLMI.esm.js';
import { F as FlowGraphInteger, R as RegisterFlowGraphInteger, G as GetPathToObjectConverter, A as AddObjectAccessorToKey } from './objectModelMapping-OlchA9xj.esm.js';
import { F as FlowGraphMatrix2D, j as FlowGraphMatrix3D, g as getRichTypeByFlowGraphType, q as getMappingForDeclaration, r as getMappingForFullOperationName } from './declarationMapper-mPOKbCS_.esm.js';

function IsVectorClassName(className) {
    return (className === "Vector2" /* FlowGraphTypes.Vector2 */ ||
        className === "Vector3" /* FlowGraphTypes.Vector3 */ ||
        className === "Vector4" /* FlowGraphTypes.Vector4 */ ||
        className === "Quaternion" /* FlowGraphTypes.Quaternion */ ||
        className === "Color3" /* FlowGraphTypes.Color3 */ ||
        className === "Color4" /* FlowGraphTypes.Color4 */);
}
function IsMatrixClassName(className) {
    return className === "Matrix" /* FlowGraphTypes.Matrix */ || className === "Matrix2D" /* FlowGraphTypes.Matrix2D */ || className === "Matrix3D" /* FlowGraphTypes.Matrix3D */;
}
function IsAnimationGroupClassName(className) {
    return className === "AnimationGroup";
}
/**
 * Resolves a serialized node reference (`{ id, name, className, uniqueId }`) to an actual scene node.
 * Matching prefers `id` (falling back to `name`), then narrows by class name, then by `uniqueId`.
 * Because `uniqueId` is reassigned every time a scene is built, it is only used as a tie-breaker so
 * that references still resolve after a scene is reloaded (e.g. an editor preview).
 * @param serializedReference the serialized reference to resolve
 * @param scene the scene to resolve the reference against
 * @returns the matching node, or undefined when none is found
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function GetSceneNodeFromSerializedReference(serializedReference, scene) {
    if (!serializedReference || (!serializedReference.id && !serializedReference.name)) {
        return undefined;
    }
    const nodes = scene.getNodes().filter((node) => (serializedReference.id ? node.id === serializedReference.id : node.name === serializedReference.name));
    if (nodes.length === 0) {
        return undefined;
    }
    const className = serializedReference.type ?? serializedReference.className;
    const classMatches = className ? nodes.filter((node) => node.getClassName() === className) : [];
    const candidates = classMatches.length > 0 ? classMatches : nodes;
    return (serializedReference.uniqueId ? candidates.find((node) => node.uniqueId === serializedReference.uniqueId) : undefined) ?? candidates[0];
}
function ParseVector(className, value, flipHandedness = false) {
    if (className === "Vector2" /* FlowGraphTypes.Vector2 */) {
        return Vector2.FromArray(value);
    }
    else if (className === "Vector3" /* FlowGraphTypes.Vector3 */) {
        if (flipHandedness) {
            value[2] *= -1;
        }
        return Vector3.FromArray(value);
    }
    else if (className === "Vector4" /* FlowGraphTypes.Vector4 */) {
        return Vector4.FromArray(value);
    }
    else if (className === "Quaternion" /* FlowGraphTypes.Quaternion */) {
        if (flipHandedness) {
            value[2] *= -1;
            value[3] *= -1;
        }
        return Quaternion.FromArray(value);
    }
    else if (className === "Color3" /* FlowGraphTypes.Color3 */) {
        return new Color3(value[0], value[1], value[2]);
    }
    else if (className === "Color4" /* FlowGraphTypes.Color4 */) {
        return new Color4(value[0], value[1], value[2], value[3]);
    }
    else {
        throw new Error(`Unknown vector class name ${className}`);
    }
}
/**
 * The default function that serializes values in a context object to a serialization object
 * @param key the key where the value should be stored in the serialization object
 * @param value the value to store
 * @param serializationObject the object where the value will be stored
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function defaultValueSerializationFunction(key, value, serializationObject) {
    const className = value?.getClassName?.() ?? "";
    if (IsVectorClassName(className) || IsMatrixClassName(className)) {
        serializationObject[key] = {
            value: value.asArray(),
            className,
        };
    }
    else if (className === "FlowGraphInteger" /* FlowGraphTypes.Integer */) {
        serializationObject[key] = {
            value: value.value,
            className,
        };
    }
    else {
        if (className && (value.id || value.name)) {
            serializationObject[key] = {
                id: value.id,
                name: value.name,
                className,
                uniqueId: value.uniqueId,
            };
        }
        else {
            if (typeof value !== "object" || value === null) {
                serializationObject[key] = value;
            }
            else {
                // Skip known non-serializable keys immediately to avoid
                // expensive JSON.stringify attempts on large object trees
                // (e.g. pathConverter holds the entire glTF parse tree).
                if (key === "pathConverter") {
                    return;
                }
                // Quick check: if any own property is a function, the object
                // is not JSON-safe and stringify would be wasteful.
                const hasFunction = Object.values(value).some((v) => typeof v === "function");
                if (hasFunction) {
                    return;
                }
                // Plain object (e.g. parsed event config) — store it if JSON-safe.
                try {
                    serializationObject[key] = JSON.parse(JSON.stringify(value));
                }
                catch {
                    Logger.Warn(`FlowGraph serialization: value for key "${key}" is not JSON-serializable and was skipped.`);
                }
            }
        }
    }
}
/**
 * The default function that parses values stored in a serialization object
 * @param key the key to the value that will be parsed
 * @param serializationObject the object that will be parsed
 * @param assetsContainer the assets container that will be used to find the objects
 * @param scene
 * @returns
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function defaultValueParseFunction(key, serializationObject, assetsContainer, scene) {
    const intermediateValue = serializationObject[key];
    let finalValue;
    const className = intermediateValue?.type ?? intermediateValue?.className;
    const sceneNode = GetSceneNodeFromSerializedReference(intermediateValue, scene);
    if (sceneNode) {
        finalValue = sceneNode;
    }
    else if (IsVectorClassName(className)) {
        finalValue = ParseVector(className, intermediateValue.value);
    }
    else if (IsAnimationGroupClassName(className)) {
        // do not use the scene.getAnimationGroupByName because it is possible that two AGs will have the same name
        const ags = scene.animationGroups.filter((ag) => ag.name === intermediateValue.name);
        // uniqueId changes on each load. this is used for the glTF loader, that uses serialization after the scene was loaded.
        finalValue = ags.length === 1 ? ags[0] : ags.find((ag) => ag.uniqueId === intermediateValue.uniqueId);
    }
    else if (className === "Matrix" /* FlowGraphTypes.Matrix */) {
        finalValue = Matrix.FromArray(intermediateValue.value);
    }
    else if (className === "Matrix2D" /* FlowGraphTypes.Matrix2D */) {
        finalValue = new FlowGraphMatrix2D(intermediateValue.value);
    }
    else if (className === "Matrix3D" /* FlowGraphTypes.Matrix3D */) {
        finalValue = new FlowGraphMatrix3D(intermediateValue.value);
    }
    else if (className === "FlowGraphInteger" /* FlowGraphTypes.Integer */) {
        finalValue = FlowGraphInteger.FromValue(intermediateValue.value);
    }
    else if (className === "number" /* FlowGraphTypes.Number */ || className === "string" /* FlowGraphTypes.String */ || className === "boolean" /* FlowGraphTypes.Boolean */) {
        finalValue = intermediateValue.value[0];
    }
    else if (intermediateValue && intermediateValue.value !== undefined) {
        finalValue = intermediateValue.value;
    }
    else {
        if (Array.isArray(intermediateValue)) {
            // Check if this is an event configuration array (objects with id/eventData)
            // versus a plain array of primitives (e.g. variable name lists)
            if (intermediateValue.length > 0 && typeof intermediateValue[0] === "object" && intermediateValue[0] !== null && "eventData" in intermediateValue[0]) {
                // configuration data of an event
                finalValue = intermediateValue.reduce((acc, val) => {
                    if (!val.eventData) {
                        return acc;
                    }
                    acc[val.id] = {
                        type: getRichTypeByFlowGraphType(val.type),
                    };
                    if (typeof val.value !== "undefined") {
                        acc[val.id].value = defaultValueParseFunction("value", val, assetsContainer, scene);
                    }
                    return acc;
                }, {});
            }
            else {
                // Plain array of primitives — return as-is
                finalValue = intermediateValue;
            }
        }
        else {
            finalValue = intermediateValue;
        }
    }
    return finalValue;
}
/**
 * Given a name of a flow graph block class, return if this
 * class needs to be created with a path converter. Used in
 * parsing.
 * @param className the name of the flow graph block class
 * @returns a boolean indicating if the class needs a path converter
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function needsPathConverter(className) {
    // I am not using the ClassName property here because it was causing a circular dependency
    return className === "FlowGraphJsonPointerParserBlock" /* FlowGraphBlockNames.JsonPointerParser */;
}

/**
 * The type of the assets that flow graph supports
 */
var FlowGraphAssetType;
(function (FlowGraphAssetType) {
    FlowGraphAssetType["Animation"] = "Animation";
    FlowGraphAssetType["AnimationGroup"] = "AnimationGroup";
    FlowGraphAssetType["Mesh"] = "Mesh";
    FlowGraphAssetType["Material"] = "Material";
    FlowGraphAssetType["Camera"] = "Camera";
    FlowGraphAssetType["Light"] = "Light";
    // Further asset types will be added here when needed.
})(FlowGraphAssetType || (FlowGraphAssetType = {}));
/**
 * Returns the asset with the given index and type from the assets context.
 * @param assetsContext The assets context to get the asset from
 * @param type The type of the asset
 * @param index The index of the asset
 * @param useIndexAsUniqueId If set to true, instead of the index in the array it will search for the unique id of the asset.
 * @returns The asset or null if not found
 */
function GetFlowGraphAssetWithType(assetsContext, type, index, useIndexAsUniqueId) {
    switch (type) {
        case "Animation" /* FlowGraphAssetType.Animation */:
            return useIndexAsUniqueId
                ? (assetsContext.animations.find((a) => a.uniqueId === index) ?? null)
                : (assetsContext.animations[index] ?? null);
        case "AnimationGroup" /* FlowGraphAssetType.AnimationGroup */:
            return useIndexAsUniqueId
                ? (assetsContext.animationGroups.find((a) => a.uniqueId === index) ?? null)
                : (assetsContext.animationGroups[index] ?? null);
        case "Mesh" /* FlowGraphAssetType.Mesh */:
            return useIndexAsUniqueId
                ? (assetsContext.meshes.find((a) => a.uniqueId === index) ?? null)
                : (assetsContext.meshes[index] ?? null);
        case "Material" /* FlowGraphAssetType.Material */:
            return useIndexAsUniqueId
                ? (assetsContext.materials.find((a) => a.uniqueId === index) ?? null)
                : (assetsContext.materials[index] ?? null);
        case "Camera" /* FlowGraphAssetType.Camera */:
            return useIndexAsUniqueId
                ? (assetsContext.cameras.find((a) => a.uniqueId === index) ?? null)
                : (assetsContext.cameras[index] ?? null);
        case "Light" /* FlowGraphAssetType.Light */:
            return useIndexAsUniqueId
                ? (assetsContext.lights.find((a) => a.uniqueId === index) ?? null)
                : (assetsContext.lights[index] ?? null);
        default:
            return null;
    }
}

var FlowGraphAction;
(function (FlowGraphAction) {
    FlowGraphAction["ExecuteBlock"] = "ExecuteBlock";
    FlowGraphAction["ExecuteEvent"] = "ExecuteEvent";
    FlowGraphAction["TriggerConnection"] = "TriggerConnection";
    FlowGraphAction["ContextVariableSet"] = "ContextVariableSet";
    FlowGraphAction["GlobalVariableSet"] = "GlobalVariableSet";
    FlowGraphAction["GlobalVariableDelete"] = "GlobalVariableDelete";
    FlowGraphAction["GlobalVariableGet"] = "GlobalVariableGet";
    FlowGraphAction["AddConnection"] = "AddConnection";
    FlowGraphAction["GetConnectionValue"] = "GetConnectionValue";
    FlowGraphAction["SetConnectionValue"] = "SetConnectionValue";
    FlowGraphAction["ActivateSignal"] = "ActivateSignal";
    FlowGraphAction["ContextVariableGet"] = "ContextVariableGet";
})(FlowGraphAction || (FlowGraphAction = {}));
/**
 * This class will be responsible of logging the flow graph activity.
 * Note that using this class might reduce performance, as it will log every action, according to the configuration.
 * It attaches to a flow graph and uses meta-programming to replace the methods of the flow graph to add logging abilities.
 */
class FlowGraphLogger {
    constructor() {
        /**
         * Whether to log to the console.
         */
        this.logToConsole = false;
        /**
         * The log cache of the flow graph.
         * Each item is a logged item, in order of execution.
         */
        this.log = [];
    }
    addLogItem(item) {
        if (!item.time) {
            item.time = Date.now();
        }
        this.log.push(item);
        if (this.logToConsole) {
            const value = item.payload?.value;
            if (typeof value === "object" && value.getClassName) {
                Logger.Log(`[FGLog] ${item.className}:${item.uniqueId.split("-")[0]} ${item.action} - ${JSON.stringify(value.getClassName())}: ${value.toString()}`);
            }
            else {
                Logger.Log(`[FGLog] ${item.className}:${item.uniqueId.split("-")[0]} ${item.action} - ${JSON.stringify(item.payload)}`);
            }
        }
    }
    getItemsOfType(action) {
        return this.log.filter((i) => i.action === action);
    }
}

/**
 * The context represents the current state and execution of the flow graph.
 * It contains both user-defined variables, which are derived from
 * a more general variable definition, and execution variables that
 * are set by the blocks.
 */
let FlowGraphContext = (() => {
    var _a;
    let _uniqueId_decorators;
    let _uniqueId_initializers = [];
    let _uniqueId_extraInitializers = [];
    let _name_decorators;
    let _name_initializers = [];
    let _name_extraInitializers = [];
    return _a = class FlowGraphContext {
            /**
             * Enable logging on this context
             */
            get enableLogging() {
                return this._enableLogging;
            }
            set enableLogging(value) {
                if (this._enableLogging === value) {
                    return;
                }
                this._enableLogging = value;
                if (this._enableLogging) {
                    this.logger = new FlowGraphLogger();
                    this.logger.logToConsole = true;
                }
                else {
                    this.logger = null;
                }
            }
            constructor(params) {
                /**
                 * A randomly generated GUID for each context.
                 */
                this.uniqueId = __runInitializers(this, _uniqueId_initializers, RandomGUID());
                /**
                 * An optional user-facing name for the context.
                 * Defaults to an empty string; the editor may assign a label like "Context 0".
                 */
                this.name = (__runInitializers(this, _uniqueId_extraInitializers), __runInitializers(this, _name_initializers, ""));
                /**
                 * These are the variables defined by a user.
                 */
                this._userVariables = (__runInitializers(this, _name_extraInitializers), {});
                /**
                 * Optional type annotations for user variables.
                 * Keys are variable names; values are type name strings (e.g. "number", "Vector3", "Mesh").
                 * This map is maintained by the editor and persisted through serialization so
                 * that a variable's declared type survives even when its value is undefined.
                 */
                this._variableTypes = {};
                /**
                 * These are the variables set by the blocks.
                 */
                this._executionVariables = {};
                /**
                 * A context-specific global variables, available to all blocks in the context.
                 */
                this._globalContextVariables = {};
                /**
                 * These are the values for the data connection points
                 */
                this._connectionValues = {};
                /**
                 * These are blocks that have currently pending tasks/listeners that need to be cleaned up.
                 */
                this._pendingBlocks = [];
                /**
                 * A monotonically increasing ID for each execution.
                 * Incremented for every block executed.
                 */
                this._executionId = 0;
                /**
                 * Observable that is triggered when a node is executed.
                 */
                this.onNodeExecutedObservable = new Observable();
                /**
                 * Observable triggered when a breakpoint is hit.
                 * Observers receive the pending activation (block, context, signal) that was paused.
                 */
                this.onBreakpointHitObservable = new Observable();
                /**
                 * A predicate called before each execution block runs.
                 * If it returns true, execution is paused before the block and a
                 * pending activation is stored, which can be resumed via
                 * {@link continueExecution} or {@link stepExecution}.
                 *
                 * Set to `null` to disable breakpoint checking.
                 */
                this.breakpointPredicate = null;
                /**
                 * The activation that is currently paused due to a breakpoint hit.
                 * `null` when execution is not paused on a breakpoint.
                 */
                this._pendingActivation = null;
                /**
                 * When true, the next activation will pause regardless of the breakpoint predicate.
                 * Set by {@link stepExecution}.
                 */
                this._stepMode = false;
                /**
                 * When set, the breakpoint check is skipped for this specific block uniqueId
                 * on the very next call to {@link _shouldBreak}. Used by continue/step to avoid
                 * immediately re-hitting the breakpoint on the block being resumed.
                 */
                this._skipBreakpointForBlockId = null;
                /**
                 * Whether to treat data as right-handed.
                 * This is used when serializing data from a right-handed system, while running the context in a left-handed system, for example in glTF parsing.
                 * Default is false.
                 */
                this.treatDataAsRightHanded = false;
                this._enableLogging = false;
                this._configuration = params;
                this.assetsContext = params.assetsContext ?? params.scene;
            }
            /**
             * Check if a user-defined variable is defined.
             * @param name the name of the variable
             * @returns true if the variable is defined
             */
            hasVariable(name) {
                return name in this._userVariables;
            }
            /**
             * Set a user-defined variable.
             * @param name the name of the variable
             * @param value the value of the variable
             */
            setVariable(name, value) {
                this._userVariables[name] = value;
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "ContextVariableSet" /* FlowGraphAction.ContextVariableSet */,
                    payload: {
                        name,
                        value,
                    },
                });
            }
            /**
             * Get an assets from the assets context based on its type and index in the array
             * @param type The type of the asset
             * @param index The index of the asset
             * @returns The asset or null if not found
             */
            getAsset(type, index) {
                return GetFlowGraphAssetWithType(this.assetsContext, type, index);
            }
            /**
             * Get a user-defined variable.
             * @param name the name of the variable
             * @returns the value of the variable
             */
            getVariable(name) {
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "ContextVariableGet" /* FlowGraphAction.ContextVariableGet */,
                    payload: {
                        name,
                        value: this._userVariables[name],
                    },
                });
                return this._userVariables[name];
            }
            /**
             * Gets all user variables map
             */
            get userVariables() {
                return this._userVariables;
            }
            /**
             * Set the declared type annotation for a user variable.
             * @param name - the variable name
             * @param typeName - the type name string (e.g. "number", "Vector3", "Mesh")
             */
            setVariableType(name, typeName) {
                this._variableTypes[name] = typeName;
            }
            /**
             * Get the declared type annotation for a user variable.
             * @param name - the variable name
             * @returns the type name string, or undefined if no type was declared
             */
            getVariableType(name) {
                return this._variableTypes[name];
            }
            /**
             * Gets all variable type annotations.
             */
            get variableTypes() {
                return this._variableTypes;
            }
            /**
             * Get the scene that the context belongs to.
             * @returns the scene
             */
            getScene() {
                return this._configuration.scene;
            }
            /**
             * Encodes an event source key as the opaque reference exposed by event blocks on their `event`
             * output. Delegates to the {@link IFlowGraphHostResolver} configured on the coordinator, so the
             * reference format is owned by the environment hosting the graph.
             * @param key the event source key (e.g. `"sceneReady"`, `"sceneTick"`, or a custom event id)
             * @returns the event reference
             */
            getEventReference(key) {
                return this._configuration.coordinator._getEventReference(key);
            }
            /**
             * Decodes the array index denoted by a reference. Returns `undefined` when no host resolver is
             * configured or the host does not recognise the value as an indexed reference.
             * @param reference the reference to decode
             * @returns the index the reference denotes, or `undefined` when it does not denote one
             */
            decodeIndexReference(reference) {
                return this._configuration.coordinator.config.hostResolver?.decodeIndexReference?.(reference);
            }
            /**
             * Maps a runtime object to the reference the host addresses it by. Returns `undefined` when no
             * host resolver is configured or the host cannot address the object.
             * @param object the runtime object to address
             * @param hint optional disambiguation hint telling the host which kind of reference is wanted
             * @returns the reference for the object, or `undefined` when it cannot be addressed
             */
            getObjectReference(object, hint) {
                return this._configuration.coordinator.config.hostResolver?.getObjectReference?.(object, hint);
            }
            _getUniqueIdPrefixedName(obj, name) {
                return `${obj.uniqueId}_${name}`;
            }
            /**
             * @internal
             * @param name name of the variable
             * @param defaultValue default value to return if the variable is not defined
             * @returns the variable value or the default value if the variable is not defined
             */
            _getGlobalContextVariable(name, defaultValue) {
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "GlobalVariableGet" /* FlowGraphAction.GlobalVariableGet */,
                    payload: {
                        name,
                        defaultValue,
                        possibleValue: this._globalContextVariables[name],
                    },
                });
                if (this._hasGlobalContextVariable(name)) {
                    return this._globalContextVariables[name];
                }
                else {
                    return defaultValue;
                }
            }
            /**
             * Set a global context variable
             * @internal
             * @param name the name of the variable
             * @param value the value of the variable
             */
            _setGlobalContextVariable(name, value) {
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "GlobalVariableSet" /* FlowGraphAction.GlobalVariableSet */,
                    payload: { name, value },
                });
                this._globalContextVariables[name] = value;
            }
            /**
             * Delete a global context variable
             * @internal
             * @param name the name of the variable
             */
            _deleteGlobalContextVariable(name) {
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "GlobalVariableDelete" /* FlowGraphAction.GlobalVariableDelete */,
                    payload: { name },
                });
                delete this._globalContextVariables[name];
            }
            /**
             * Check if a global context variable is defined
             * @internal
             * @param name the name of the variable
             * @returns true if the variable is defined
             */
            _hasGlobalContextVariable(name) {
                return name in this._globalContextVariables;
            }
            /**
             * Set an internal execution variable
             * @internal
             * @param name
             * @param value
             */
            _setExecutionVariable(block, name, value) {
                this._executionVariables[this._getUniqueIdPrefixedName(block, name)] = value;
            }
            /**
             * Get an internal execution variable
             * @internal
             * @param name
             * @returns
             */
            _getExecutionVariable(block, name, defaultValue) {
                if (this._hasExecutionVariable(block, name)) {
                    return this._executionVariables[this._getUniqueIdPrefixedName(block, name)];
                }
                else {
                    return defaultValue;
                }
            }
            /**
             * Delete an internal execution variable
             * @internal
             * @param block
             * @param name
             */
            _deleteExecutionVariable(block, name) {
                delete this._executionVariables[this._getUniqueIdPrefixedName(block, name)];
            }
            /**
             * Check if an internal execution variable is defined
             * @internal
             * @param block
             * @param name
             * @returns
             */
            _hasExecutionVariable(block, name) {
                return this._getUniqueIdPrefixedName(block, name) in this._executionVariables;
            }
            /**
             * Check if a connection value is defined
             * @internal
             * @param connectionPoint
             * @returns
             */
            _hasConnectionValue(connectionPoint) {
                return connectionPoint.uniqueId in this._connectionValues;
            }
            /**
             * Set a connection value
             * @internal
             * @param connectionPoint
             * @param value
             */
            _setConnectionValue(connectionPoint, value) {
                this._connectionValues[connectionPoint.uniqueId] = value;
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "SetConnectionValue" /* FlowGraphAction.SetConnectionValue */,
                    payload: {
                        connectionPointId: connectionPoint.uniqueId,
                        value,
                    },
                });
            }
            /**
             * Set a connection value by key
             * @internal
             * @param key the key of the connection value
             * @param value the value of the connection
             */
            _setConnectionValueByKey(key, value) {
                this._connectionValues[key] = value;
            }
            /**
             * Get a connection value
             * @internal
             * @param connectionPoint
             * @returns
             */
            _getConnectionValue(connectionPoint) {
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: this.getClassName(),
                    uniqueId: this.uniqueId,
                    action: "GetConnectionValue" /* FlowGraphAction.GetConnectionValue */,
                    payload: {
                        connectionPointId: connectionPoint.uniqueId,
                        value: this._connectionValues[connectionPoint.uniqueId],
                    },
                });
                return this._connectionValues[connectionPoint.uniqueId];
            }
            /**
             * Get the configuration
             * @internal
             * @param name
             * @param value
             */
            get configuration() {
                return this._configuration;
            }
            /**
             * Check if there are any pending blocks in this context
             * @returns true if there are pending blocks
             */
            get hasPendingBlocks() {
                return this._pendingBlocks.length > 0;
            }
            /**
             * Add a block to the list of blocks that have pending tasks.
             * @internal
             * @param block
             */
            _addPendingBlock(block) {
                // check if block is already in the array
                if (this._pendingBlocks.includes(block)) {
                    return;
                }
                this._pendingBlocks.push(block);
                // sort pending blocks by priority
                this._pendingBlocks.sort((a, b) => a.priority - b.priority);
            }
            /**
             * Remove a block from the list of blocks that have pending tasks.
             * @internal
             * @param block
             */
            _removePendingBlock(block) {
                const index = this._pendingBlocks.indexOf(block);
                if (index !== -1) {
                    this._pendingBlocks.splice(index, 1);
                }
            }
            /**
             * Clear all pending blocks.
             * @internal
             */
            _clearPendingBlocks() {
                for (const block of this._pendingBlocks) {
                    block._cancelPendingTasks(this);
                }
                this._pendingBlocks.length = 0;
            }
            /**
             * @internal
             * Function that notifies the node executed observable
             * @param node
             */
            _notifyExecuteNode(node) {
                this.onNodeExecutedObservable.notifyObservers(node);
                this.logger?.addLogItem({
                    time: Date.now(),
                    className: node.getClassName(),
                    uniqueId: node.uniqueId,
                    action: "ExecuteBlock" /* FlowGraphAction.ExecuteBlock */,
                });
            }
            _notifyOnTick(framePayload) {
                // set the values as global variables
                this._setGlobalContextVariable("timeSinceStart", framePayload.timeSinceStart);
                this._setGlobalContextVariable("deltaTime", framePayload.deltaTime);
                // iterate the pending blocks and run each one's onFrame function
                for (const block of this._pendingBlocks) {
                    block._executeOnTick?.(this);
                }
            }
            /**
             * @internal
             */
            _increaseExecutionId() {
                this._executionId++;
            }
            /**
             * A monotonically increasing ID for each execution.
             * Incremented for every block executed.
             */
            get executionId() {
                return this._executionId;
            }
            // ── Breakpoint API ─────────────────────────────────────────────────
            /**
             * Check whether the given block should break before executing.
             * Called by the signal connection infrastructure.
             * @internal
             * @param block the block about to execute
             * @param signal the signal that is triggering the execution
             * @returns true if execution should be paused (breakpoint hit)
             */
            _shouldBreak(block, signal) {
                // If continue/step just resumed this specific block, let it through
                if (this._skipBreakpointForBlockId === block.uniqueId) {
                    this._skipBreakpointForBlockId = null;
                    return false;
                }
                // If already paused on a breakpoint, silently block further execution
                // without overwriting the pending activation or re-notifying observers.
                if (this._pendingActivation) {
                    return true;
                }
                if (this._stepMode) {
                    this._stepMode = false;
                    this._pendingActivation = { block, context: this, signal };
                    this.onBreakpointHitObservable.notifyObservers(this._pendingActivation);
                    return true;
                }
                if (this.breakpointPredicate && this.breakpointPredicate(block)) {
                    this._pendingActivation = { block, context: this, signal };
                    this.onBreakpointHitObservable.notifyObservers(this._pendingActivation);
                    return true;
                }
                return false;
            }
            /**
             * Returns the currently paused activation, or null if not paused.
             */
            get pendingActivation() {
                return this._pendingActivation;
            }
            /**
             * Resume execution from a breakpoint hit.
             * The paused block and all downstream blocks will execute normally until
             * the next breakpoint (if any) is hit.
             */
            continueExecution() {
                const pending = this._pendingActivation;
                if (!pending) {
                    return;
                }
                this._pendingActivation = null;
                // Tell _shouldBreak to skip the breakpoint for this block on re-entry
                this._skipBreakpointForBlockId = pending.block.uniqueId;
                pending.signal._activateSignal(this);
                // Clear in case no re-entry happened (shouldn't linger)
                this._skipBreakpointForBlockId = null;
            }
            /**
             * Execute exactly the paused block and then pause again before the next
             * execution block fires. If no activation is pending, this is a no-op.
             */
            stepExecution() {
                const pending = this._pendingActivation;
                if (!pending) {
                    return;
                }
                this._pendingActivation = null;
                // Enable step mode so the very next input-signal activation will pause
                this._stepMode = true;
                // Tell _shouldBreak to skip the breakpoint for this block on re-entry
                this._skipBreakpointForBlockId = pending.block.uniqueId;
                pending.signal._activateSignal(this);
                // If nothing further executed (end of chain), clear step mode
                this._stepMode = false;
                this._skipBreakpointForBlockId = null;
            }
            /**
             * Discard any pending breakpoint activation without resuming.
             * Used when stopping or resetting the graph.
             * @internal
             */
            _clearPendingActivation() {
                this._pendingActivation = null;
                this._stepMode = false;
                this._skipBreakpointForBlockId = null;
            }
            /**
             * Serializes a context
             * @param serializationObject the object to write the values in
             * @param valueSerializationFunction a function to serialize complex values
             */
            serialize(serializationObject = {}, valueSerializationFunction = defaultValueSerializationFunction) {
                serializationObject.uniqueId = this.uniqueId;
                serializationObject.name = this.name;
                serializationObject._userVariables = {};
                for (const key in this._userVariables) {
                    valueSerializationFunction(key, this._userVariables[key], serializationObject._userVariables);
                }
                // Persist variable type annotations (editor metadata)
                if (Object.keys(this._variableTypes).length > 0) {
                    serializationObject._variableTypes = { ...this._variableTypes };
                }
                serializationObject._connectionValues = {};
                for (const key in this._connectionValues) {
                    valueSerializationFunction(key, this._connectionValues[key], serializationObject._connectionValues);
                }
                // serialize assets context, if not scene
                if (this.assetsContext !== this.getScene()) {
                    serializationObject._assetsContext = {
                        meshes: this.assetsContext.meshes.map((m) => m.id),
                        materials: this.assetsContext.materials.map((m) => m.id),
                        textures: this.assetsContext.textures.map((m) => m.name),
                        animations: this.assetsContext.animations.map((m) => m.name),
                        lights: this.assetsContext.lights.map((m) => m.id),
                        cameras: this.assetsContext.cameras.map((m) => m.id),
                        sounds: this.assetsContext.sounds?.map((m) => m.name),
                        skeletons: this.assetsContext.skeletons.map((m) => m.id),
                        particleSystems: this.assetsContext.particleSystems.map((m) => m.name),
                        geometries: this.assetsContext.geometries.map((m) => m.id),
                        multiMaterials: this.assetsContext.multiMaterials.map((m) => m.id),
                        transformNodes: this.assetsContext.transformNodes.map((m) => m.id),
                    };
                }
            }
            /**
             * @returns the class name of the object.
             */
            getClassName() {
                return "FlowGraphContext";
            }
        },
        (() => {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
            _uniqueId_decorators = [serialize()];
            _name_decorators = [serialize()];
            __esDecorate(null, null, _uniqueId_decorators, { kind: "field", name: "uniqueId", static: false, private: false, access: { has: obj => "uniqueId" in obj, get: obj => obj.uniqueId, set: (obj, value) => { obj.uniqueId = value; } }, metadata: _metadata }, _uniqueId_initializers, _uniqueId_extraInitializers);
            __esDecorate(null, null, _name_decorators, { kind: "field", name: "name", static: false, private: false, access: { has: obj => "name" in obj, get: obj => obj.name, set: (obj, value) => { obj.name = value; } }, metadata: _metadata }, _name_initializers, _name_extraInitializers);
            if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        })(),
        _a;
})();

/**
 * The type of a connection point - input or output.
 */
var FlowGraphConnectionType;
(function (FlowGraphConnectionType) {
    FlowGraphConnectionType[FlowGraphConnectionType["Input"] = 0] = "Input";
    FlowGraphConnectionType[FlowGraphConnectionType["Output"] = 1] = "Output";
})(FlowGraphConnectionType || (FlowGraphConnectionType = {}));
/**
 * The base connection class.
 */
class FlowGraphConnection {
    constructor(name, _connectionType, 
    /* @internal */ _ownerBlock) {
        this._ownerBlock = _ownerBlock;
        /** @internal */
        this._connectedPoint = [];
        /**
         * A uniquely identifying string for the connection.
         */
        this.uniqueId = RandomGUID();
        /**
         * Used for parsing connections.
         * @internal
         */
        // disable warning as this is used for parsing
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        this.connectedPointIds = [];
        this.name = name;
        this._connectionType = _connectionType;
    }
    /**
     * The type of the connection
     */
    get connectionType() {
        return this._connectionType;
    }
    /**
     * @internal
     * Override this to indicate if a point can connect to more than one point.
     */
    _isSingularConnection() {
        return true;
    }
    /**
     * Returns if a point is connected to any other point.
     * @returns boolean indicating if the point is connected.
     */
    isConnected() {
        return this._connectedPoint.length > 0;
    }
    /**
     * Connects two connections together.
     * @param point the connection to connect to.
     */
    connectTo(point) {
        if (this._connectionType === point._connectionType) {
            throw new Error(`Cannot connect two points of type ${this.connectionType}`);
        }
        if ((this._isSingularConnection() && this._connectedPoint.length > 0) || (point._isSingularConnection() && point._connectedPoint.length > 0)) {
            throw new Error("Max number of connections for point reached");
        }
        this._connectedPoint.push(point);
        point._connectedPoint.push(this);
    }
    /**
     * Disconnects two connections.
     * @param point the connection to disconnect from.
     * @param removeFromLocal if true, the connection will be removed from the local connection list.
     */
    disconnectFrom(point, removeFromLocal = true) {
        const indexLocal = this._connectedPoint.indexOf(point);
        const indexConnected = point._connectedPoint.indexOf(this);
        if (indexLocal === -1 || indexConnected === -1) {
            return;
        }
        if (removeFromLocal) {
            this._connectedPoint.splice(indexLocal, 1);
        }
        point._connectedPoint.splice(indexConnected, 1);
    }
    /**
     * Disconnects all connected points.
     */
    disconnectFromAll() {
        for (const point of this._connectedPoint) {
            this.disconnectFrom(point, false);
        }
        this._connectedPoint.length = 0;
    }
    dispose() {
        for (const point of this._connectedPoint) {
            this.disconnectFrom(point);
        }
    }
    /**
     * Saves the connection to a JSON object.
     * @param serializationObject the object to serialize to.
     */
    serialize(serializationObject = {}) {
        serializationObject.uniqueId = this.uniqueId;
        serializationObject.name = this.name;
        serializationObject._connectionType = this._connectionType;
        serializationObject.connectedPointIds = [];
        serializationObject.className = this.getClassName();
        for (const point of this._connectedPoint) {
            serializationObject.connectedPointIds.push(point.uniqueId);
        }
    }
    /**
     * @returns class name of the connection.
     */
    getClassName() {
        return "FGConnection";
    }
    /**
     * Deserialize from a object into this
     * @param serializationObject the object to deserialize from.
     */
    deserialize(serializationObject) {
        this.uniqueId = serializationObject.uniqueId;
        this.name = serializationObject.name;
        this._connectionType = serializationObject._connectionType;
        this.connectedPointIds = serializationObject.connectedPointIds;
    }
}

/** This file must only contain pure code and pure imports */
/**
 * Represents a connection point for data.
 * An unconnected input point can have a default value.
 * An output point will only have a value if it is connected to an input point. Furthermore,
 * if the point belongs to a "function" node, the node will run its function to update the value.
 */
class FlowGraphDataConnection extends FlowGraphConnection {
    /**
     * Create a new data connection point.
     * @param name the name of the connection
     * @param connectionType the type of the connection
     * @param ownerBlock the block that owns this connection
     * @param richType the type of the data in this block
     * @param _defaultValue the default value of the connection
     * @param _optional if the connection is optional
     */
    constructor(name, connectionType, ownerBlock, 
    /**
     * the type of the data in this block
     */
    richType, 
    /**
     * [any] the default value of the connection
     */
    _defaultValue = richType.defaultValue, 
    /**
     * [false] if the connection is optional
     */
    _optional = false) {
        super(name, connectionType, ownerBlock);
        this.richType = richType;
        this._defaultValue = _defaultValue;
        this._optional = _optional;
        this._isDisabled = false;
        /**
         * This is used for debugging purposes! It is the last value that was set to this connection with ANY context.
         * Do not use this value for anything else, as it might be wrong if used in a different context.
         */
        this._lastValue = null;
        /**
         * a data transformer function, if needed.
         * This can be used, for example, to force seconds into milliseconds output, if it makes sense in your case.
         */
        this.dataTransformer = null;
        /**
         * An observable that is triggered when the value of the connection changes.
         */
        this.onValueChangedObservable = new Observable();
    }
    /**
     * Whether or not the connection is optional.
     * Currently only used for UI control.
     */
    get optional() {
        return this._optional;
    }
    /**
     * is this connection disabled
     * If the connection is disabled you will not be able to connect anything to it.
     */
    get isDisabled() {
        return this._isDisabled;
    }
    set isDisabled(value) {
        if (this._isDisabled === value) {
            return;
        }
        this._isDisabled = value;
        if (this._isDisabled) {
            this.disconnectFromAll();
        }
    }
    /**
     * An output data block can connect to multiple input data blocks,
     * but an input data block can only connect to one output data block.
     * @returns true if the connection is singular
     */
    _isSingularConnection() {
        return this.connectionType === 0 /* FlowGraphConnectionType.Input */;
    }
    /**
     * Set the value of the connection in a specific context.
     * @param value the value to set
     * @param context the context to which the value is set
     */
    setValue(value, context) {
        // check if the value is different
        if (context._getConnectionValue(this) === value) {
            return;
        }
        context._setConnectionValue(this, value);
        this.onValueChangedObservable.notifyObservers(value);
    }
    /**
     * Reset the value of the connection to the default value.
     * @param context the context in which the value is reset
     */
    resetToDefaultValue(context) {
        context._setConnectionValue(this, this._defaultValue);
    }
    /**
     * Re-resolves this input's default value against a (new) scene when the value is a node reference.
     * This handles two cases that occur when a graph is moved to a different scene (see
     * {@link FlowGraph.setScene}):
     * - the value is still an unresolved serialized reference (`{ id, name, className, uniqueId }`)
     *   because the node did not yet exist in the scene at parse time, or
     * - the value is a node that belongs to a different (e.g. disposed) scene.
     * Values that are not node references (numbers, vectors, matrices, etc.) are left untouched, and
     * the default value is only replaced when a matching node is found in the new scene.
     * @param scene the scene to resolve the reference against
     * @internal
     */
    _reresolveDefaultValueForScene(scene) {
        const value = this._defaultValue;
        if (!value || typeof value !== "object") {
            return;
        }
        let reference;
        if (typeof value.getClassName === "function" && typeof value.getScene === "function") {
            // A scene node — only rebind when it belongs to a different scene.
            if (value.getScene() === scene) {
                return;
            }
            reference = { id: value.id, name: value.name, className: value.getClassName(), uniqueId: value.uniqueId };
        }
        else if (typeof value.className === "string" && (value.id || value.name) && value.value === undefined) {
            // An unresolved serialized node reference left by the parser.
            reference = value;
        }
        else {
            return;
        }
        const node = GetSceneNodeFromSerializedReference(reference, scene);
        if (node) {
            this._defaultValue = node;
        }
    }
    /**
     * Connect this point to another point.
     * @param point the point to connect to.
     */
    connectTo(point) {
        if (this._isDisabled) {
            return;
        }
        super.connectTo(point);
    }
    _getValueOrDefault(context) {
        const val = context._getConnectionValue(this) ?? this._defaultValue;
        return this.dataTransformer ? this.dataTransformer(val) : val;
    }
    /**
     * Gets the value of the connection in a specific context.
     * @param context the context from which the value is retrieved
     * @returns the value of the connection
     */
    getValue(context) {
        if (this.connectionType === 1 /* FlowGraphConnectionType.Output */) {
            context._notifyExecuteNode(this._ownerBlock);
            this._ownerBlock._updateOutputs(context);
            const value = this._getValueOrDefault(context);
            this._lastValue = value;
            return this.richType.typeTransformer ? this.richType.typeTransformer(value) : value;
        }
        const value = !this.isConnected() ? this._getValueOrDefault(context) : this._connectedPoint[0].getValue(context);
        this._lastValue = value;
        return this.richType.typeTransformer ? this.richType.typeTransformer(value) : value;
    }
    /**
     * @internal
     */
    _getLastValue() {
        return this._lastValue;
    }
    /**
     * @returns class name of the object.
     */
    getClassName() {
        return "FlowGraphDataConnection";
    }
    /**
     * Serializes this object.
     * @param serializationObject the object to serialize to
     */
    serialize(serializationObject = {}) {
        super.serialize(serializationObject);
        serializationObject.richType = {};
        this.richType.serialize(serializationObject.richType);
        serializationObject.optional = this._optional;
        defaultValueSerializationFunction("defaultValue", this._defaultValue, serializationObject);
    }
}

/**
 * A block in a flow graph. The most basic form
 * of a block has inputs and outputs that contain
 * data.
 */
class FlowGraphBlock {
    /** Constructor is protected so only subclasses can be instantiated
     * @param config optional configuration for this block
     * @internal - do not use directly. Extend this class instead.
     */
    constructor(
    /**
     * the configuration of the block
     */
    config) {
        this.config = config;
        /**
         * A randomly generated GUID for each block.
         */
        this.uniqueId = RandomGUID();
        this.name = this.config?.name ?? this.getClassName();
        this.dataInputs = [];
        this.dataOutputs = [];
    }
    /**
     * @internal
     * This function is called when the block needs to update its output flows.
     * @param _context the context in which it is running
     */
    _updateOutputs(_context) {
        // empty by default, overridden in data blocks
    }
    /**
     * Registers a data input on the block.
     * @param name the name of the input
     * @param richType the type of the input
     * @param defaultValue optional default value of the input. If not set, the rich type's default value will be used.
     * @returns the created connection
     */
    registerDataInput(name, richType, defaultValue) {
        const input = new FlowGraphDataConnection(name, 0 /* FlowGraphConnectionType.Input */, this, richType, defaultValue);
        this.dataInputs.push(input);
        return input;
    }
    /**
     * Registers a data output on the block.
     * @param name the name of the input
     * @param richType the type of the input
     * @param defaultValue optional default value of the input. If not set, the rich type's default value will be used.
     * @returns the created connection
     */
    registerDataOutput(name, richType, defaultValue) {
        const output = new FlowGraphDataConnection(name, 1 /* FlowGraphConnectionType.Output */, this, richType, defaultValue);
        this.dataOutputs.push(output);
        return output;
    }
    /**
     * Given the name of a data input, returns the connection if it exists
     * @param name the name of the input
     * @returns the connection if it exists, undefined otherwise
     */
    getDataInput(name) {
        return this.dataInputs.find((i) => i.name === name);
    }
    /**
     * Given the name of a data output, returns the connection if it exists
     * @param name the name of the output
     * @returns the connection if it exists, undefined otherwise
     */
    getDataOutput(name) {
        return this.dataOutputs.find((i) => i.name === name);
    }
    /**
     * Serializes this block
     * @param serializationObject the object to serialize to
     * @param _valueSerializeFunction a function that serializes a specific value
     */
    serialize(serializationObject = {}, _valueSerializeFunction = defaultValueSerializationFunction) {
        serializationObject.uniqueId = this.uniqueId;
        serializationObject.config = {};
        if (this.config) {
            const config = this.config;
            const keys = Object.keys(config);
            for (const key of keys) {
                _valueSerializeFunction(key, config[key], serializationObject.config);
            }
        }
        serializationObject.dataInputs = [];
        serializationObject.dataOutputs = [];
        serializationObject.className = this.getClassName();
        for (const input of this.dataInputs) {
            const serializedInput = {};
            input.serialize(serializedInput);
            serializationObject.dataInputs.push(serializedInput);
        }
        for (const output of this.dataOutputs) {
            const serializedOutput = {};
            output.serialize(serializedOutput);
            serializationObject.dataOutputs.push(serializedOutput);
        }
    }
    /**
     * Deserializes this block
     * @param _serializationObject the object to deserialize from
     */
    deserialize(_serializationObject) {
        // no-op by default
    }
    _log(context, action, payload) {
        context.logger?.addLogItem({
            action,
            payload,
            className: this.getClassName(),
            uniqueId: this.uniqueId,
        });
    }
    /**
     * Gets the class name of this block
     * @returns the class name
     */
    getClassName() {
        return "FlowGraphBlock";
    }
}

/** This file must only contain pure code and pure imports */
/**
 * Represents a connection point for a signal.
 * When an output point is activated, it will activate the connected input point.
 * When an input point is activated, it will execute the block it belongs to.
 */
class FlowGraphSignalConnection extends FlowGraphConnection {
    constructor() {
        super(...arguments);
        /**
         * The priority of the signal. Signals with higher priority will be executed first.
         * Set priority before adding the connection as sorting happens only when the connection is added.
         */
        this.priority = 0;
        /**
         * Timestamp of the last activation (set on output signals when they fire).
         * @internal
         */
        this._lastActivationTime = -1;
    }
    _isSingularConnection() {
        return false;
    }
    connectTo(point) {
        super.connectTo(point);
        // sort according to priority to handle execution order
        this._connectedPoint.sort((a, b) => b.priority - a.priority);
    }
    /**
     * @internal
     */
    _activateSignal(context) {
        this._lastActivationTime = performance.now();
        context.logger?.addLogItem({
            action: "ActivateSignal" /* FlowGraphAction.ActivateSignal */,
            className: this._ownerBlock.getClassName(),
            uniqueId: this._ownerBlock.uniqueId,
            payload: {
                connectionType: this.connectionType,
                name: this.name,
            },
        });
        if (this.connectionType === 0 /* FlowGraphConnectionType.Input */) {
            // Check breakpoint before executing
            if (context._shouldBreak(this._ownerBlock, this)) {
                return; // Execution paused — stored as pending activation
            }
            // Start a new execution frame BEFORE executing the node: an output value socket (a random
            // value, for instance) retains its value until a node with one or more flow sockets is
            // executed, after which it is recomputed on the next access. Because flow execution is
            // synchronous and nested, the id must be increased before the block runs so each flow
            // socket activation (including loop self-activations) observes a fresh frame, while
            // staying constant within a single block execution so per-frame value caching still works.
            context._increaseExecutionId();
            context._notifyExecuteNode(this._ownerBlock);
            const startTime = performance.now();
            this._ownerBlock._execute(context, this);
            this._ownerBlock._lastExecutionTime = performance.now() - startTime;
        }
        else {
            for (const connectedPoint of this._connectedPoint) {
                connectedPoint._activateSignal(context);
            }
        }
    }
}

/**
 * A block that executes some action. Always has an input signal (which is not used by event blocks).
 * Can have one or more output signals.
 */
class FlowGraphExecutionBlock extends FlowGraphBlock {
    constructor(config) {
        super(config);
        /**
         * The priority of the block. Higher priority blocks will be executed first.
         * Note that priority cannot be change AFTER the block was added as sorting happens when the block is added to the execution queue.
         */
        this.priority = 0;
        /**
         * The last measured execution time in milliseconds.
         * Updated by the signal connection when the block is executed.
         * A value of -1 means no measurement has been taken yet.
         * @internal
         */
        this._lastExecutionTime = -1;
        this.signalInputs = [];
        this.signalOutputs = [];
        this.in = this._registerSignalInput("in");
        this.error = this._registerSignalOutput("error");
    }
    _registerSignalInput(name) {
        const input = new FlowGraphSignalConnection(name, 0 /* FlowGraphConnectionType.Input */, this);
        this.signalInputs.push(input);
        return input;
    }
    _registerSignalOutput(name) {
        const output = new FlowGraphSignalConnection(name, 1 /* FlowGraphConnectionType.Output */, this);
        this.signalOutputs.push(output);
        return output;
    }
    _unregisterSignalInput(name) {
        const index = this.signalInputs.findIndex((input) => input.name === name);
        if (index !== -1) {
            this.signalInputs[index].dispose();
            this.signalInputs.splice(index, 1);
        }
    }
    _unregisterSignalOutput(name) {
        const index = this.signalOutputs.findIndex((output) => output.name === name);
        if (index !== -1) {
            this.signalOutputs[index].dispose();
            this.signalOutputs.splice(index, 1);
        }
    }
    _reportError(context, error) {
        this.error.payload = typeof error === "string" ? new Error(error) : error;
        this.error._activateSignal(context);
    }
    /**
     * Given a name of a signal input, return that input if it exists
     * @param name the name of the input
     * @returns if the input exists, the input. Otherwise, undefined.
     */
    getSignalInput(name) {
        return this.signalInputs.find((input) => input.name === name);
    }
    /**
     * Given a name of a signal output, return that input if it exists
     * @param name the name of the input
     * @returns if the input exists, the input. Otherwise, undefined.
     */
    getSignalOutput(name) {
        return this.signalOutputs.find((output) => output.name === name);
    }
    /**
     * Serializes this block
     * @param serializationObject the object to serialize in
     */
    serialize(serializationObject = {}) {
        super.serialize(serializationObject);
        serializationObject.signalInputs = [];
        serializationObject.signalOutputs = [];
        for (const input of this.signalInputs) {
            const serializedInput = {};
            input.serialize(serializedInput);
            serializationObject.signalInputs.push(serializedInput);
        }
        for (const output of this.signalOutputs) {
            const serializedOutput = {};
            output.serialize(serializedOutput);
            serializationObject.signalOutputs.push(serializedOutput);
        }
    }
    /**
     * Deserializes from an object
     * @param serializationObject the object to deserialize from
     */
    deserialize(serializationObject) {
        for (let i = 0; i < serializationObject.signalInputs.length; i++) {
            const signalInput = this.getSignalInput(serializationObject.signalInputs[i].name);
            if (signalInput) {
                signalInput.deserialize(serializationObject.signalInputs[i]);
            }
            else {
                throw new Error("Could not find signal input with name " + serializationObject.signalInputs[i].name + " in block " + serializationObject.className);
            }
        }
        for (let i = 0; i < serializationObject.signalOutputs.length; i++) {
            const signalOutput = this.getSignalOutput(serializationObject.signalOutputs[i].name);
            if (signalOutput) {
                signalOutput.deserialize(serializationObject.signalOutputs[i]);
            }
            else {
                throw new Error("Could not find signal output with name " + serializationObject.signalOutputs[i].name + " in block " + serializationObject.className);
            }
        }
    }
    /**
     * @returns the class name
     */
    getClassName() {
        return "FlowGraphExecutionBlock";
    }
}

/**
 * An execution block that has an out signal. This signal is triggered when the synchronous execution of this block is done.
 * Most execution blocks will inherit from this, except for the ones that have multiple signals to be triggered.
 * (such as if blocks)
 */
class FlowGraphExecutionBlockWithOutSignal extends FlowGraphExecutionBlock {
    constructor(config) {
        super(config);
        this.out = this._registerSignalOutput("out");
    }
}

/**
 * An async execution block can start tasks that will be executed asynchronously.
 * It should also be responsible for clearing it in _cancelPendingTasks.
 */
class FlowGraphAsyncExecutionBlock extends FlowGraphExecutionBlockWithOutSignal {
    constructor(config, events) {
        super(config);
        this._eventsSignalOutputs = {};
        this.done = this._registerSignalOutput("done");
        if (events) {
            for (const eventName of events) {
                this._eventsSignalOutputs[eventName] = this._registerSignalOutput(eventName + "Event");
            }
        }
    }
    /**
     * @internal
     * This function can be overridden to execute any
     * logic that should be executed on every frame
     * while the async task is pending.
     * @param context the context in which it is running
     */
    _executeOnTick(_context) { }
    /**
     * @internal
     * @param context
     */
    _startPendingTasks(context) {
        if (context._getExecutionVariable(this, "_initialized", false)) {
            this._cancelPendingTasks(context);
            this._resetAfterCanceled(context);
        }
        this._preparePendingTasks(context);
        context._addPendingBlock(this);
        this.out._activateSignal(context);
        context._setExecutionVariable(this, "_initialized", true);
    }
    _resetAfterCanceled(context) {
        context._deleteExecutionVariable(this, "_initialized");
        context._removePendingBlock(this);
    }
}

/**
 * Whether the current platform is macOS / iOS.
 * Used by keyboard blocks to resolve the platform-appropriate
 * "command or control" modifier (Cmd on Mac, Ctrl elsewhere).
 * @internal
 */
const _IsMacPlatform = IsNavigatorAvailable() && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
/**
 * @internal
 * Returns if mesh1 is a descendant of mesh2
 * @param mesh1
 * @param mesh2
 * @returns
 */
function _IsDescendantOf(mesh1, mesh2) {
    return !!(mesh1.parent && (mesh1.parent === mesh2 || _IsDescendantOf(mesh1.parent, mesh2)));
}
/**
 * @internal
 */
function _GetClassNameOf(v) {
    if (v.getClassName) {
        return v.getClassName();
    }
    return;
}
/**
 * @internal
 * Check if two classname are the same and are vector or quaternion classes.
 * @param className the first class name
 * @param className2 the second class name
 * @returns whether the two class names are the same and are vector or quaternion classes.
 */
function _AreSameVectorOrQuaternionClass(className, className2) {
    return (className === className2 &&
        (className === "Vector2" /* FlowGraphTypes.Vector2 */ || className === "Vector3" /* FlowGraphTypes.Vector3 */ || className === "Vector4" /* FlowGraphTypes.Vector4 */ || className === "Quaternion" /* FlowGraphTypes.Quaternion */));
}
/**
 * @internal
 * Check if two classname are the same and are matrix classes.
 * @param className the first class name
 * @param className2 the second class name
 * @returns whether the two class names are the same and are matrix classes.
 */
function _AreSameMatrixClass(className, className2) {
    return className === className2 && (className === "Matrix" /* FlowGraphTypes.Matrix */ || className === "Matrix2D" /* FlowGraphTypes.Matrix2D */ || className === "Matrix3D" /* FlowGraphTypes.Matrix3D */);
}
/**
 * @internal
 * Check if two classname are the same and are integer classes.
 * @param className the first class name
 * @param className2 the second class name
 * @returns whether the two class names are the same and are integer classes.
 */
function _AreSameIntegerClass(className, className2) {
    return className === "FlowGraphInteger" && className2 === "FlowGraphInteger";
}
/**
 * Check if an object has a numeric value.
 * @param a the object to check if it is a number.
 * @param validIfNaN whether to consider NaN as a valid number.
 * @returns whether a is a FlowGraphNumber (Integer or number).
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function isNumeric(a, validIfNaN) {
    const isNumeric = typeof a === "number" || typeof a?.value === "number";
    if (isNumeric && !validIfNaN) {
        return !isNaN(getNumericValue(a));
    }
    return isNumeric;
}
/**
 * Get the numeric value of a FlowGraphNumber.
 * @param a the object to get the numeric value from.
 * @returns the numeric value.
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function getNumericValue(a) {
    return typeof a === "number" ? a : a.value;
}

/**
 * This class is responsible for coordinating the events that are triggered in the scene.
 * It registers all observers needed to track certain events and triggers the blocks that are listening to them.
 * Abstracting the events from the class will allow us to easily change the events that are being listened to, and trigger them in any order.
 */
class FlowGraphSceneEventCoordinator {
    constructor(scene) {
        /**
         * register to this observable to get flow graph event notifications.
         */
        this.onEventTriggeredObservable = new Observable();
        /**
         * Was scene-ready already triggered?
         */
        this.sceneReadyTriggered = false;
        this._pointerUnderMeshState = {};
        this._onBlurHandler = null;
        /**
         * The set of keys currently pressed, keyed by `event.code`.
         * Keyboard event blocks use this to determine whether a key is held.
         *
         * In addition to physical key codes, a virtual `"CommandOrControl"` entry
         * is maintained: it tracks Meta (Cmd) on macOS and Ctrl on Windows/Linux,
         * enabling platform-agnostic shortcut checks via the IsKeyPressed block.
         */
        this.pressedKeys = new Set();
        this._startingTime = 0;
        this._scene = scene;
        this._initialize();
    }
    _initialize() {
        this._sceneReadyObserver = this._scene.onReadyObservable.addOnce(() => {
            if (!this.sceneReadyTriggered) {
                this.onEventTriggeredObservable.notifyObservers({ type: "SceneReady" /* FlowGraphEventType.SceneReady */ });
                this.sceneReadyTriggered = true;
            }
        });
        this._sceneDisposeObserver = this._scene.onDisposeObservable.add(() => {
            this.onEventTriggeredObservable.notifyObservers({ type: "SceneDispose" /* FlowGraphEventType.SceneDispose */ });
        });
        this._sceneOnBeforeRenderObserver = this._scene.onBeforeRenderObservable.add(() => {
            const deltaTime = this._scene.getEngine().getDeltaTime() / 1000; // set in seconds
            this.onEventTriggeredObservable.notifyObservers({
                type: "SceneBeforeRender" /* FlowGraphEventType.SceneBeforeRender */,
                payload: {
                    timeSinceStart: this._startingTime,
                    deltaTime,
                },
            });
            this._startingTime += deltaTime;
        });
        this._meshPickedObserver = this._scene.onPointerObservable.add((pointerInfo) => {
            this.onEventTriggeredObservable.notifyObservers({ type: "MeshPick" /* FlowGraphEventType.MeshPick */, payload: pointerInfo });
        }, PointerEventTypes.POINTERPICK); // should it be pointerdown?
        this._pointerDownObserver = this._scene.onPointerObservable.add((pointerInfo) => {
            this.onEventTriggeredObservable.notifyObservers({ type: "PointerDown" /* FlowGraphEventType.PointerDown */, payload: pointerInfo });
        }, PointerEventTypes.POINTERDOWN);
        this._pointerUpObserver = this._scene.onPointerObservable.add((pointerInfo) => {
            this.onEventTriggeredObservable.notifyObservers({ type: "PointerUp" /* FlowGraphEventType.PointerUp */, payload: pointerInfo });
        }, PointerEventTypes.POINTERUP);
        this._pointerMoveObserver = this._scene.onPointerObservable.add((pointerInfo) => {
            this.onEventTriggeredObservable.notifyObservers({ type: "PointerMove" /* FlowGraphEventType.PointerMove */, payload: pointerInfo });
        }, PointerEventTypes.POINTERMOVE);
        this._meshUnderPointerObserver = this._scene.onMeshUnderPointerUpdatedObservable.add((data) => {
            // check if the data has changed. Check the state of the last change and see if it is a mesh or null.
            // if it is a mesh and the previous state was null, trigger over event. If it is null and the previous state was a mesh, trigger out event.
            // if it is a mesh and the previous state was a mesh, trigger out from the old mesh and over the new mesh
            // if it is null and the previous state was null, do nothing.
            const pointerId = data.pointerId;
            const mesh = data.mesh;
            const previousState = this._pointerUnderMeshState[pointerId];
            if (!previousState && mesh) {
                this.onEventTriggeredObservable.notifyObservers({ type: "PointerOver" /* FlowGraphEventType.PointerOver */, payload: { pointerId, mesh } });
            }
            else if (previousState && !mesh) {
                this.onEventTriggeredObservable.notifyObservers({ type: "PointerOut" /* FlowGraphEventType.PointerOut */, payload: { pointerId, mesh: previousState } });
            }
            else if (previousState && mesh && previousState !== mesh) {
                this.onEventTriggeredObservable.notifyObservers({ type: "PointerOut" /* FlowGraphEventType.PointerOut */, payload: { pointerId, mesh: previousState, over: mesh } });
                this.onEventTriggeredObservable.notifyObservers({ type: "PointerOver" /* FlowGraphEventType.PointerOver */, payload: { pointerId, mesh, out: previousState } });
            }
            this._pointerUnderMeshState[pointerId] = mesh;
        }, PointerEventTypes.POINTERMOVE);
        this._keyDownObserver = this._scene.onKeyboardObservable.add((keyboardInfo) => {
            const code = keyboardInfo.event.code;
            this.pressedKeys.add(code);
            if (FlowGraphSceneEventCoordinator._COMMAND_OR_CTRL_CODES.has(code)) {
                this.pressedKeys.add("CommandOrControl");
            }
            this.onEventTriggeredObservable.notifyObservers({ type: "KeyDown" /* FlowGraphEventType.KeyDown */, payload: keyboardInfo });
        }, KeyboardEventTypes.KEYDOWN);
        this._keyUpObserver = this._scene.onKeyboardObservable.add((keyboardInfo) => {
            const code = keyboardInfo.event.code;
            this.pressedKeys.delete(code);
            if (FlowGraphSceneEventCoordinator._COMMAND_OR_CTRL_CODES.has(code)) {
                // Only remove CommandOrControl if neither left nor right is still held
                let stillHeld = false;
                for (const c of Array.from(FlowGraphSceneEventCoordinator._COMMAND_OR_CTRL_CODES)) {
                    if (c !== code && this.pressedKeys.has(c)) {
                        stillHeld = true;
                        break;
                    }
                }
                if (!stillHeld) {
                    this.pressedKeys.delete("CommandOrControl");
                }
            }
            this.onEventTriggeredObservable.notifyObservers({ type: "KeyUp" /* FlowGraphEventType.KeyUp */, payload: keyboardInfo });
        }, KeyboardEventTypes.KEYUP);
        // Clear all tracked keys when the window/tab loses focus.
        // Without this, held keys would appear "stuck" after an Alt-Tab
        // because the keyup event fires in the other window.
        const canvas = this._scene.getEngine().getRenderingCanvas();
        if (canvas) {
            this._onBlurHandler = () => this.pressedKeys.clear();
            canvas.addEventListener("blur", this._onBlurHandler);
        }
    }
    dispose() {
        this._sceneDisposeObserver?.remove();
        this._sceneReadyObserver?.remove();
        this._sceneOnBeforeRenderObserver?.remove();
        this._meshPickedObserver?.remove();
        this._meshUnderPointerObserver?.remove();
        this._pointerDownObserver?.remove();
        this._pointerUpObserver?.remove();
        this._pointerMoveObserver?.remove();
        this._keyDownObserver?.remove();
        this._keyUpObserver?.remove();
        if (this._onBlurHandler) {
            const canvas = this._scene.getEngine().getRenderingCanvas();
            canvas?.removeEventListener("blur", this._onBlurHandler);
            this._onBlurHandler = null;
        }
        this.pressedKeys.clear();
        this.onEventTriggeredObservable.clear();
    }
}
/** The physical key codes that map to the virtual CommandOrControl key on this platform. */
FlowGraphSceneEventCoordinator._COMMAND_OR_CTRL_CODES = _IsMacPlatform ? new Set(["MetaLeft", "MetaRight"]) : new Set(["ControlLeft", "ControlRight"]);

/**
 * A type of block that listens to an event observable and activates
 * its output signal when the event is triggered.
 */
class FlowGraphEventBlock extends FlowGraphAsyncExecutionBlock {
    /**
     * Creates a new event block.
     * @param config optional configuration
     */
    constructor(config) {
        super(config);
        /**
         * the priority of initialization of this block.
         * For example, scene start should have a negative priority because it should be initialized last.
         */
        this.initPriority = 0;
        /**
         * The type of the event
         */
        this.type = "NoTrigger" /* FlowGraphEventType.NoTrigger */;
        // Event blocks are driven by scene events, not by an incoming signal.
        // Remove the inherited `in` port so it is not shown in the editor UI
        // and cannot be accidentally wired.
        this._unregisterSignalInput("in");
    }
    /**
     * Deserializes from an object.
     * Filters out the legacy "in" signal input that existed before event blocks
     * stopped exposing it, so old serialized graphs load without error.
     * @param serializationObject the object to deserialize from
     */
    deserialize(serializationObject) {
        const filtered = { ...serializationObject };
        filtered.signalInputs = (serializationObject.signalInputs ?? []).filter((s) => s.name !== "in");
        super.deserialize(filtered);
    }
    /**
     * @internal
     */
    _execute(context) {
        context._notifyExecuteNode(this);
        // Fire both signals: KHR_interactivity graphs connect to `done`,
        // while editor-authored graphs typically connect to `out`.
        // Both must fire so that either wiring style works correctly.
        this.done._activateSignal(context);
        this.out._activateSignal(context);
    }
    /**
     * @internal
     * Override _startPendingTasks so that event blocks do NOT fire the
     * `out` signal at graph-start time.  The base FlowGraphAsyncExecutionBlock
     * fires `out` immediately in _startPendingTasks (useful for async blocks
     * like PlayAnimation that start a task and let sync flow continue).
     * Event blocks should only fire their output signals when the actual
     * event occurs, which is handled by _execute.
     */
    _startPendingTasks(context) {
        if (context._getExecutionVariable(this, "_initialized", false)) {
            this._cancelPendingTasks(context);
            this._resetAfterCanceled(context);
        }
        this._preparePendingTasks(context);
        context._addPendingBlock(this);
        // Do NOT fire out._activateSignal — event blocks fire both out and
        // done in _execute when the actual event triggers.
        context._setExecutionVariable(this, "_initialized", true);
    }
}

/**
 * Severity level for a validation issue.
 */
var FlowGraphValidationSeverity;
(function (FlowGraphValidationSeverity) {
    /** A critical issue that will cause runtime failure. */
    FlowGraphValidationSeverity[FlowGraphValidationSeverity["Error"] = 0] = "Error";
    /** A potential issue that may indicate a mistake. */
    FlowGraphValidationSeverity[FlowGraphValidationSeverity["Warning"] = 1] = "Warning";
})(FlowGraphValidationSeverity || (FlowGraphValidationSeverity = {}));
// Types that are mutually coercible and should not produce type-mismatch warnings
const NumericLikeTypes = new Set(["number" /* FlowGraphTypes.Number */, "FlowGraphInteger" /* FlowGraphTypes.Integer */, "boolean" /* FlowGraphTypes.Boolean */]);
const VectorLikeTypes = new Set(["Vector4" /* FlowGraphTypes.Vector4 */, "Quaternion" /* FlowGraphTypes.Quaternion */]);
const ColorLikeTypes = new Set(["Color3" /* FlowGraphTypes.Color3 */, "Color4" /* FlowGraphTypes.Color4 */]);
/**
 * @internal
 */
function _AreTypesCompatible(sourceType, targetType) {
    if (sourceType === targetType) {
        return true;
    }
    // "any" is compatible with everything
    if (sourceType === "any" /* FlowGraphTypes.Any */ || targetType === "any" /* FlowGraphTypes.Any */) {
        return true;
    }
    // Numeric coercion group
    if (NumericLikeTypes.has(sourceType) && NumericLikeTypes.has(targetType)) {
        return true;
    }
    // Vector4 / Quaternion are interchangeable
    if (VectorLikeTypes.has(sourceType) && VectorLikeTypes.has(targetType)) {
        return true;
    }
    // Color3 → Color4 widening is safe
    if (ColorLikeTypes.has(sourceType) && ColorLikeTypes.has(targetType)) {
        return true;
    }
    return false;
}
/**
 * Validates a flow graph and returns all issues found.
 *
 * The following checks are performed:
 * 1. **No event blocks** — the graph has no entry points.
 * 2. **Unconnected required data inputs** — a non-optional data input with no connection.
 * 3. **Unconnected signal inputs** — an execution block whose `in` signal has no connection and
 *    that is not an event block (entry point).
 * 4. **Data type mismatches** — a data connection whose source richType is incompatible with the
 *    target richType.
 * 5. **Unreachable blocks** — blocks not reachable from any event block via signal/data traversal.
 * 6. **Data dependency cycles** — circular data-only connections that would cause infinite recursion.
 *
 * @param flowGraph - The flow graph to validate.
 * @returns The validation result.
 */
function ValidateFlowGraph(flowGraph) {
    const issues = [];
    const issuesByBlock = new Map();
    const addIssue = (issue) => {
        issues.push(issue);
        if (issue.block) {
            let arr = issuesByBlock.get(issue.block.uniqueId);
            if (!arr) {
                arr = [];
                issuesByBlock.set(issue.block.uniqueId, arr);
            }
            arr.push(issue);
        }
    };
    // Collect ALL blocks via visitAllBlocks
    const allBlocks = [];
    flowGraph.visitAllBlocks((block) => {
        allBlocks.push(block);
    });
    // ── Check 1: No event blocks ───────────────────────────────────────
    const eventBlocks = _GetEventBlocks(flowGraph);
    if (eventBlocks.length === 0) {
        addIssue({
            severity: 0 /* FlowGraphValidationSeverity.Error */,
            message: "Graph has no event blocks — nothing will trigger execution.",
        });
    }
    // ── Check 2: Unconnected required data inputs ──────────────────────
    for (const block of allBlocks) {
        for (const input of block.dataInputs) {
            if (!input.optional && !input.isDisabled && !input.isConnected()) {
                addIssue({
                    severity: 1 /* FlowGraphValidationSeverity.Warning */,
                    message: `"${input.name}" is not connected and will use its default value.`,
                    block,
                    connectionName: input.name,
                });
            }
        }
    }
    // ── Check 3: Unconnected signal inputs on non-event execution blocks
    for (const block of allBlocks) {
        if (block instanceof FlowGraphExecutionBlock) {
            // Skip event blocks — they are entry points and don't need an incoming signal.
            if (_IsEventBlock(block)) {
                continue;
            }
            const inSignal = block.signalInputs.find((s) => s.name === "in");
            if (inSignal && !inSignal.isConnected()) {
                addIssue({
                    severity: 0 /* FlowGraphValidationSeverity.Error */,
                    message: `Execution block has no incoming signal — it will never execute.`,
                    block,
                    connectionName: "in",
                });
            }
        }
    }
    // ── Check 4: Data type mismatches ──────────────────────────────────
    for (const block of allBlocks) {
        for (const input of block.dataInputs) {
            if (!input.isConnected()) {
                continue;
            }
            const source = input._connectedPoint[0];
            const srcType = source.richType?.typeName;
            const tgtType = input.richType?.typeName;
            if (srcType && tgtType && !_AreTypesCompatible(srcType, tgtType)) {
                // If either side has a typeTransformer, it's intentionally converted
                if (source.richType.typeTransformer !== undefined || input.richType.typeTransformer !== undefined) {
                    continue;
                }
                addIssue({
                    severity: 1 /* FlowGraphValidationSeverity.Warning */,
                    message: `Type mismatch: "${source._ownerBlock.name}.${source.name}" (${srcType}) → "${block.name}.${input.name}" (${tgtType}).`,
                    block,
                    connectionName: input.name,
                });
            }
        }
    }
    // ── Check 5: Unreachable blocks ────────────────────────────────────
    const reachableIds = new Set();
    flowGraph.visitAllBlocks((block) => {
        reachableIds.add(block.uniqueId);
    });
    // We need to also check standalone data blocks not visited by visitAllBlocks.
    // visitAllBlocks starts from event blocks; any block not found is unreachable.
    // Since we can only iterate blocks we know about, the allBlocks list IS
    // from visitAllBlocks, so everything in it IS reachable.
    // There's no separate registry of "all added blocks" in the FlowGraph.
    // So: unreachable blocks are blocks that exist but aren't visited.
    // Currently visitAllBlocks IS our source, so this check is a no-op for
    // blocks added via the graph. However, the editor creates nodes and adds
    // execution blocks, so we should compare against the editor's full list.
    // For the core validator, we expose a variant that accepts an external block list.
    // ── Check 6: Data dependency cycles ────────────────────────────────
    _DetectDataCycles(allBlocks, addIssue);
    // Sort: errors first, then warnings
    issues.sort((a, b) => a.severity - b.severity);
    return {
        isValid: issues.every((i) => i.severity !== 0 /* FlowGraphValidationSeverity.Error */),
        issues,
        errorCount: issues.filter((i) => i.severity === 0 /* FlowGraphValidationSeverity.Error */).length,
        warningCount: issues.filter((i) => i.severity === 1 /* FlowGraphValidationSeverity.Warning */).length,
        issuesByBlock,
    };
}
/**
 * Extended validation that also checks for unreachable blocks.
 * Requires a full list of all blocks in the graph (including those not reachable
 * from event blocks via the normal traversal).
 *
 * @param flowGraph - The flow graph to validate.
 * @param allKnownBlocks - Complete list of all blocks (e.g., from the editor's node set).
 * @returns The validation result.
 */
function ValidateFlowGraphWithBlockList(flowGraph, allKnownBlocks) {
    const result = ValidateFlowGraph(flowGraph);
    // Check for unreachable blocks
    const reachableIds = new Set();
    flowGraph.visitAllBlocks((block) => {
        reachableIds.add(block.uniqueId);
    });
    for (const block of allKnownBlocks) {
        if (!reachableIds.has(block.uniqueId)) {
            const issue = {
                severity: 1 /* FlowGraphValidationSeverity.Warning */,
                message: `Block is unreachable from any event block.`,
                block,
            };
            result.issues.push(issue);
            result.warningCount++;
            let arr = result.issuesByBlock.get(block.uniqueId);
            if (!arr) {
                arr = [];
                result.issuesByBlock.set(block.uniqueId, arr);
            }
            arr.push(issue);
            // Also run Check 2 and Check 3 on unreachable blocks so the editor
            // can display all issues, not just reachability ones.
            for (const input of block.dataInputs) {
                if (!input.optional && !input.isDisabled && !input.isConnected()) {
                    const dataIssue = {
                        severity: 1 /* FlowGraphValidationSeverity.Warning */,
                        message: `"${input.name}" is not connected and will use its default value.`,
                        block,
                        connectionName: input.name,
                    };
                    result.issues.push(dataIssue);
                    result.warningCount++;
                    arr.push(dataIssue);
                }
            }
            if (block instanceof FlowGraphExecutionBlock && !_IsEventBlock(block)) {
                const inSignal = block.signalInputs.find((s) => s.name === "in");
                if (inSignal && !inSignal.isConnected()) {
                    const signalIssue = {
                        severity: 0 /* FlowGraphValidationSeverity.Error */,
                        message: `Execution block has no incoming signal — it will never execute.`,
                        block,
                        connectionName: "in",
                    };
                    result.issues.push(signalIssue);
                    result.errorCount++;
                    arr.push(signalIssue);
                }
            }
        }
    }
    result.isValid = result.issues.every((i) => i.severity !== 0 /* FlowGraphValidationSeverity.Error */);
    result.issues.sort((a, b) => a.severity - b.severity);
    return result;
}
/**
 * Get all event blocks from a flow graph.
 * @param flowGraph - the flow graph
 * @returns the event blocks
 */
function _GetEventBlocks(flowGraph) {
    const eventBlocks = [];
    for (const type in flowGraph._eventBlocks) {
        for (const block of flowGraph._eventBlocks[type]) {
            eventBlocks.push(block);
        }
    }
    return eventBlocks;
}
/**
 * Detect whether a block is an event block (entry point).
 * @param block - the block to check
 * @returns true if it is an event block
 */
function _IsEventBlock(block) {
    return block instanceof FlowGraphEventBlock;
}
/**
 * Detects cycles among data-only blocks.
 * A data cycle means that block A's output feeds into block B's input, and
 * block B's output feeds back into block A (directly or indirectly).
 * This would cause infinite recursion during getValue().
 * @param allBlocks - all blocks to check
 * @param addIssue - callback to report issues
 */
function _DetectDataCycles(allBlocks, addIssue) {
    // Build adjacency: for each block, which blocks do its data inputs depend on?
    // (Data inputs pull values from connected output blocks.)
    const white = 0; // unvisited
    const gray = 1; // in current DFS path
    const black = 2; // fully explored
    const color = new Map();
    for (const block of allBlocks) {
        color.set(block.uniqueId, white);
    }
    const blockMap = new Map();
    for (const block of allBlocks) {
        blockMap.set(block.uniqueId, block);
    }
    const reportedCycleBlocks = new Set();
    function dfs(block) {
        color.set(block.uniqueId, gray);
        for (const input of block.dataInputs) {
            if (!input.isConnected()) {
                continue;
            }
            for (const connected of input._connectedPoint) {
                const dep = connected._ownerBlock;
                // Only consider data-only blocks (not execution blocks which are driven by signals)
                if (dep instanceof FlowGraphExecutionBlock) {
                    continue;
                }
                const depColor = color.get(dep.uniqueId);
                if (depColor === gray) {
                    // Cycle found
                    if (!reportedCycleBlocks.has(block.uniqueId)) {
                        reportedCycleBlocks.add(block.uniqueId);
                        addIssue({
                            severity: 0 /* FlowGraphValidationSeverity.Error */,
                            message: `Data dependency cycle detected — getValue() will recurse infinitely.`,
                            block,
                        });
                    }
                    if (!reportedCycleBlocks.has(dep.uniqueId)) {
                        reportedCycleBlocks.add(dep.uniqueId);
                        addIssue({
                            severity: 0 /* FlowGraphValidationSeverity.Error */,
                            message: `Data dependency cycle detected — getValue() will recurse infinitely.`,
                            block: dep,
                        });
                    }
                    return true;
                }
                if (depColor === white) {
                    dfs(dep);
                }
            }
        }
        color.set(block.uniqueId, black);
        return false;
    }
    for (const block of allBlocks) {
        if (color.get(block.uniqueId) === white) {
            dfs(block);
        }
    }
}

var FlowGraphState;
(function (FlowGraphState) {
    /**
     * The graph is stopped
     */
    FlowGraphState[FlowGraphState["Stopped"] = 0] = "Stopped";
    /**
     * The graph is running
     */
    FlowGraphState[FlowGraphState["Started"] = 1] = "Started";
    /**
     * The graph is paused (contexts kept, pending tasks cancelled)
     */
    FlowGraphState[FlowGraphState["Paused"] = 2] = "Paused";
})(FlowGraphState || (FlowGraphState = {}));
/**
 * Class used to represent a flow graph.
 * A flow graph is a graph of blocks that can be used to create complex logic.
 * Blocks can be added to the graph and connected to each other.
 * The graph can then be started, which will init and start all of its event blocks.
 *
 * @experimental FlowGraph is still in development and is subject to change.
 */
class FlowGraph {
    /** @returns the flow graph editor from a UMD bundle or the BABYLON global, or undefined if not loaded */
    _getGlobalFlowGraphEditor() {
        // UMD global name detection from bundle metadata. rollup-built UMD bundles may expose the
        // editor class on `.default.FlowGraphEditor`, so unwrap that case before falling back to
        // the BABYLON global emitted from the editor entry point.
        if (typeof FLOWGRAPHEDITOR !== "undefined") {
            if (FLOWGRAPHEDITOR.FlowGraphEditor) {
                return FLOWGRAPHEDITOR;
            }
            if (FLOWGRAPHEDITOR.default?.FlowGraphEditor) {
                return FLOWGRAPHEDITOR.default;
            }
        }
        if (typeof BABYLON !== "undefined" && typeof BABYLON.FlowGraphEditor !== "undefined") {
            return BABYLON;
        }
        return undefined;
    }
    /**
     * The scene associated with this flow graph.
     */
    get scene() {
        return this._scene;
    }
    /**
     * The coordinator that owns this flow graph.
     */
    get coordinator() {
        return this._coordinator;
    }
    /**
     * The scene event coordinator for this graph.
     * Provides access to runtime event state such as currently pressed keys.
     */
    get sceneEventCoordinator() {
        return this._sceneEventCoordinator;
    }
    /**
     * The state of the graph
     */
    get state() {
        return this._state;
    }
    /**
     * The state of the graph
     */
    set state(value) {
        this._state = value;
        this.onStateChangedObservable.notifyObservers(value);
    }
    /**
     * Construct a Flow Graph
     * @param params construction parameters. currently only the scene
     */
    constructor(params) {
        this._BJSFLOWGRAPHEDITOR = this._getGlobalFlowGraphEditor();
        /**
         * An observable that is triggered when the state of the graph changes.
         */
        this.onStateChangedObservable = new Observable();
        /** @internal */
        this._eventBlocks = {
            ["SceneReady" /* FlowGraphEventType.SceneReady */]: [],
            ["SceneDispose" /* FlowGraphEventType.SceneDispose */]: [],
            ["SceneBeforeRender" /* FlowGraphEventType.SceneBeforeRender */]: [],
            ["MeshPick" /* FlowGraphEventType.MeshPick */]: [],
            ["PointerDown" /* FlowGraphEventType.PointerDown */]: [],
            ["PointerUp" /* FlowGraphEventType.PointerUp */]: [],
            ["PointerMove" /* FlowGraphEventType.PointerMove */]: [],
            ["PointerOver" /* FlowGraphEventType.PointerOver */]: [],
            ["PointerOut" /* FlowGraphEventType.PointerOut */]: [],
            ["KeyDown" /* FlowGraphEventType.KeyDown */]: [],
            ["KeyUp" /* FlowGraphEventType.KeyUp */]: [],
            ["SceneAfterRender" /* FlowGraphEventType.SceneAfterRender */]: [],
            ["NoTrigger" /* FlowGraphEventType.NoTrigger */]: [],
        };
        /**
         * All blocks that belong to this graph, including unreachable ones.
         * @internal
         */
        this._allBlocks = [];
        this._executionContexts = [];
        /**
         * The state of the graph
         */
        this._state = 0 /* FlowGraphState.Stopped */;
        this._scene = params.scene;
        this._sceneEventCoordinator = new FlowGraphSceneEventCoordinator(this._scene);
        this._coordinator = params.coordinator;
        this.name = params.name ?? "Graph";
        this.uniqueId = params.uniqueId ?? RandomGUID();
    }
    _attachEventObserver() {
        if (this._eventObserver) {
            return;
        }
        this._eventObserver = this._sceneEventCoordinator.onEventTriggeredObservable.add((event) => {
            if (event.type === "SceneDispose" /* FlowGraphEventType.SceneDispose */) {
                this.dispose();
                return;
            }
            if (this.state !== 1 /* FlowGraphState.Started */) {
                return;
            }
            for (const context of this._executionContexts) {
                const order = this._getContextualOrder(event.type, context);
                for (const block of order) {
                    // iterate contexts
                    if (!block._executeEvent(context, event.payload)) {
                        break;
                    }
                }
            }
            // custom behavior(s) of specific events
            switch (event.type) {
                case "SceneReady" /* FlowGraphEventType.SceneReady */:
                    this._sceneEventCoordinator.sceneReadyTriggered = true;
                    break;
                case "SceneBeforeRender" /* FlowGraphEventType.SceneBeforeRender */:
                    for (const context of this._executionContexts) {
                        context._notifyOnTick(event.payload);
                    }
                    break;
            }
        });
    }
    _detachEventObserver() {
        this._eventObserver?.remove();
        this._eventObserver = null;
    }
    /**
     * Sets a new scene for this flow graph, re-wiring all event listeners.
     * This is useful when the scene the flow graph should listen to changes
     * (e.g. when a new scene is loaded in an editor preview).
     * If the graph is currently running, it will be stopped first and must be
     * restarted manually after calling this method.
     * @param scene the new scene to attach to
     */
    setScene(scene) {
        if (scene === this._scene) {
            return;
        }
        if (this.state === 1 /* FlowGraphState.Started */) {
            this.stop();
        }
        // Tear down old event coordinator
        this._detachEventObserver();
        this._sceneEventCoordinator.dispose();
        // Clear execution contexts so start() creates fresh ones with the new scene.
        // NOTE: This intentionally discards user variables and connection values.
        // Callers that need to preserve them (e.g. the Flow Graph Editor) should
        // snapshot context state BEFORE calling setScene() and restore it in a
        // wrapped createContext() callback after start() re-creates contexts.
        this._executionContexts.length = 0;
        // Rebuild with the new scene
        this._scene = scene;
        this._scene.constantlyUpdateMeshUnderPointer = true; // ensure pointer info is always up to date for event blocks that need it
        // Re-resolve node references (e.g. meshes targeted by Get/Set property blocks) against the
        // new scene. This is required when the graph was parsed before its scene was populated (for
        // example an editor that loads a graph from a snippet first and the referenced scene second):
        // the references would otherwise stay bound to nodes from the old/disposed scene.
        for (const block of this._allBlocks) {
            for (const input of block.dataInputs) {
                input._reresolveDefaultValueForScene(scene);
            }
        }
        this._sceneEventCoordinator = new FlowGraphSceneEventCoordinator(this._scene);
        // Pre-attach the event observer so that events from the new
        // coordinator are routed to the graph immediately.  The handler
        // guards against processing events while the graph is stopped,
        // but having the observer in place ensures no events are lost
        // when start() is called shortly after.
        this._attachEventObserver();
    }
    /**
     * Create a context. A context represents one self contained execution for the graph, with its own variables.
     * @returns the context, where you can get and set variables
     */
    createContext() {
        const context = new FlowGraphContext({ scene: this._scene, coordinator: this._coordinator, sceneEventCoordinator: this._sceneEventCoordinator });
        this._executionContexts.push(context);
        return context;
    }
    /**
     * Returns the execution context at a given index
     * @param index the index of the context
     * @returns the execution context at that index
     */
    getContext(index) {
        return this._executionContexts[index];
    }
    /**
     * Returns the number of execution contexts currently attached to this graph.
     */
    get contextCount() {
        return this._executionContexts.length;
    }
    /**
     * Remove an execution context by index. Any pending async blocks on
     * the context are cleared before removal.
     * @param index the index of the context to remove
     * @returns the removed context, or undefined if the index was out of range
     */
    removeContext(index) {
        if (index < 0 || index >= this._executionContexts.length) {
            return undefined;
        }
        const [removed] = this._executionContexts.splice(index, 1);
        removed._clearPendingBlocks();
        return removed;
    }
    /**
     * Returns all blocks registered in this graph, including disconnected ones.
     * @returns a read-only array of all blocks
     */
    getAllBlocks() {
        return this._allBlocks;
    }
    /**
     * Register a block with the graph. This does not wire any connections;
     * it simply ensures the block is tracked so that serialization, editor
     * display, and validation see it even when it is not reachable from an
     * event block.
     * @param block the block to register
     */
    addBlock(block) {
        if (this._allBlocks.indexOf(block) === -1) {
            this._allBlocks.push(block);
        }
    }
    /**
     * Remove a block from the graph. Disconnects all of its ports and, if it
     * is an event block, unregisters it from the event-block lists.
     * @param block the block to remove
     */
    removeBlock(block) {
        const idx = this._allBlocks.indexOf(block);
        if (idx !== -1) {
            this._allBlocks.splice(idx, 1);
        }
        // If it is an event block, remove from the event-block registry
        if (block instanceof FlowGraphExecutionBlock && "type" in block) {
            const eventBlock = block;
            const list = this._eventBlocks[eventBlock.type];
            if (list) {
                const eIdx = list.indexOf(eventBlock);
                if (eIdx !== -1) {
                    list.splice(eIdx, 1);
                }
            }
        }
        // If the block has pending async tasks (e.g. event subscriptions),
        // cancel them in all active execution contexts so deletion takes
        // effect immediately even while the graph is running.
        if (block instanceof FlowGraphAsyncExecutionBlock) {
            for (const context of this._executionContexts) {
                block._cancelPendingTasks(context);
                block._resetAfterCanceled(context);
            }
        }
        // Disconnect all ports
        for (const input of block.dataInputs) {
            input.disconnectFromAll();
        }
        for (const output of block.dataOutputs) {
            output.disconnectFromAll();
        }
        if (block instanceof FlowGraphExecutionBlock) {
            for (const signalIn of block.signalInputs) {
                signalIn.disconnectFromAll();
            }
            for (const signalOut of block.signalOutputs) {
                signalOut.disconnectFromAll();
            }
        }
    }
    /**
     * Add an event block. When the graph is started, it will start listening to events
     * from the block and execute the graph when they are triggered.
     * @param block the event block to be added
     */
    addEventBlock(block) {
        this.addBlock(block);
        if (block.type === "PointerOver" /* FlowGraphEventType.PointerOver */ || block.type === "PointerOut" /* FlowGraphEventType.PointerOut */) {
            this._scene.constantlyUpdateMeshUnderPointer = true;
        }
        this._eventBlocks[block.type].push(block);
        // if already started, sort and add to the pending
        if (this.state === 1 /* FlowGraphState.Started */) {
            for (const context of this._executionContexts) {
                block._startPendingTasks(context);
            }
        }
        else {
            this.onStateChangedObservable.addOnce((state) => {
                if (state === 1 /* FlowGraphState.Started */) {
                    for (const context of this._executionContexts) {
                        block._startPendingTasks(context);
                    }
                }
            });
        }
    }
    /**
     * Stops the flow graph. Cancels all pending tasks and clears execution contexts,
     * but keeps event blocks so the graph can be restarted.
     */
    stop() {
        if (this.state === 0 /* FlowGraphState.Stopped */) {
            return;
        }
        this._detachEventObserver();
        this.state = 0 /* FlowGraphState.Stopped */;
        for (const context of this._executionContexts) {
            context._clearPendingBlocks();
            context._clearPendingActivation();
        }
        this._executionContexts.length = 0;
    }
    /**
     * Pauses the flow graph. Cancels pending tasks but keeps execution contexts and event blocks.
     * Call start() to resume.
     */
    pause() {
        if (this.state !== 1 /* FlowGraphState.Started */) {
            return;
        }
        this._detachEventObserver();
        this.state = 2 /* FlowGraphState.Paused */;
        for (const context of this._executionContexts) {
            context._clearPendingBlocks();
        }
    }
    /**
     * Starts the flow graph. Initializes the event blocks and starts listening to events.
     * Can also be called to resume from a paused state.
     */
    start() {
        if (this.state === 1 /* FlowGraphState.Started */) {
            return;
        }
        const resumingFromPause = this.state === 2 /* FlowGraphState.Paused */;
        if (this._executionContexts.length === 0) {
            this.createContext();
        }
        this._attachEventObserver();
        this.state = 1 /* FlowGraphState.Started */;
        this._startPendingEvents();
        // On a fresh start (not resume), fire the SceneReady event.
        // The coordinator's own scene-ready observer may have already
        // fired (and been lost) while the graph was stopped, so reset
        // the flag and handle the ready state ourselves.
        if (!resumingFromPause) {
            this._sceneEventCoordinator.sceneReadyTriggered = false;
            if (this._scene.isReady(true)) {
                this._sceneEventCoordinator.sceneReadyTriggered = true;
                this._sceneEventCoordinator.onEventTriggeredObservable.notifyObservers({ type: "SceneReady" /* FlowGraphEventType.SceneReady */ });
            }
            else {
                // Scene isn't ready yet (e.g. pending shader compilations after
                // a scene swap).  Use executeWhenReady(true) which restarts the
                // readiness check loop — a plain addOnce on onReadyObservable
                // may never fire if the check loop already completed.
                this._scene.executeWhenReady(() => {
                    if (this.state === 1 /* FlowGraphState.Started */ && !this._sceneEventCoordinator.sceneReadyTriggered) {
                        this._sceneEventCoordinator.sceneReadyTriggered = true;
                        this._sceneEventCoordinator.onEventTriggeredObservable.notifyObservers({ type: "SceneReady" /* FlowGraphEventType.SceneReady */ });
                    }
                }, true);
            }
        }
    }
    _startPendingEvents() {
        for (const context of this._executionContexts) {
            for (const type in this._eventBlocks) {
                const order = this._getContextualOrder(type, context);
                for (const block of order) {
                    block._startPendingTasks(context);
                }
            }
        }
    }
    _getContextualOrder(type, context) {
        const order = this._eventBlocks[type].sort((a, b) => b.initPriority - a.initPriority);
        if (type === "MeshPick" /* FlowGraphEventType.MeshPick */) {
            const meshPickOrder = [];
            for (const block1 of order) {
                // If the block is a mesh pick, guarantee that picks of children meshes come before picks of parent meshes
                const mesh1 = block1.asset.getValue(context);
                let i = 0;
                for (; i < order.length; i++) {
                    const block2 = order[i];
                    const mesh2 = block2.asset.getValue(context);
                    if (mesh1 && mesh2 && _IsDescendantOf(mesh1, mesh2)) {
                        break;
                    }
                }
                meshPickOrder.splice(i, 0, block1);
            }
            return meshPickOrder;
        }
        return order;
    }
    /**
     * Disposes of the flow graph. Cancels any pending tasks and removes all event listeners.
     */
    dispose() {
        // Always release the scene-event wiring, even for a stopped or never-started graph.
        // The scene event coordinator attaches per-frame, pointer and keyboard observers to the
        // scene in its constructor, so a graph that is removed from its coordinator (or disposed)
        // while stopped would otherwise leak those observers and keep incurring per-frame overhead.
        this._detachEventObserver();
        this._sceneEventCoordinator.dispose();
        if (this.state === 0 /* FlowGraphState.Stopped */) {
            // Nothing is executing, so there is no run state to clear. Authored blocks are left
            // intact on purpose: the editor re-points a stopped graph across preview scenes via
            // setScene(), and each preview scene disposal raises a SceneDispose event that calls
            // dispose() here. Wiping the blocks would destroy the user's graph on every reset.
            return;
        }
        this.state = 0 /* FlowGraphState.Stopped */;
        for (const context of this._executionContexts) {
            context._clearPendingBlocks();
            context._clearPendingActivation();
        }
        this._executionContexts.length = 0;
        for (const type in this._eventBlocks) {
            this._eventBlocks[type].length = 0;
        }
        this._allBlocks.length = 0;
    }
    /**
     * Executes a function in all blocks of a flow graph, starting with the event blocks.
     * @param visitor the function to execute.
     */
    visitAllBlocks(visitor) {
        const visitList = [];
        const idsAddedToVisitList = new Set();
        for (const type in this._eventBlocks) {
            for (const block of this._eventBlocks[type]) {
                visitList.push(block);
                idsAddedToVisitList.add(block.uniqueId);
            }
        }
        while (visitList.length > 0) {
            const block = visitList.pop();
            visitor(block);
            for (const dataIn of block.dataInputs) {
                for (const connection of dataIn._connectedPoint) {
                    if (!idsAddedToVisitList.has(connection._ownerBlock.uniqueId)) {
                        visitList.push(connection._ownerBlock);
                        idsAddedToVisitList.add(connection._ownerBlock.uniqueId);
                    }
                }
            }
            if (block instanceof FlowGraphExecutionBlock) {
                for (const signalOut of block.signalOutputs) {
                    for (const connection of signalOut._connectedPoint) {
                        if (!idsAddedToVisitList.has(connection._ownerBlock.uniqueId)) {
                            visitList.push(connection._ownerBlock);
                            idsAddedToVisitList.add(connection._ownerBlock.uniqueId);
                        }
                    }
                }
            }
        }
    }
    /**
     * Validates the flow graph and returns all issues found.
     * Uses the tracked block list for complete validation including unreachable block detection.
     * @returns The validation result containing errors and warnings.
     */
    validate() {
        return ValidateFlowGraphWithBlockList(this, this._allBlocks);
    }
    /**
     * Serializes a graph
     * @param serializationObject the object to write the values in
     * @param valueSerializeFunction a function to serialize complex values
     */
    serialize(serializationObject = {}, valueSerializeFunction) {
        serializationObject.name = this.name;
        serializationObject.uniqueId = this.uniqueId;
        serializationObject.allBlocks = [];
        // Collect all blocks: traversal-reachable ones plus any registered
        // orphans in _allBlocks (e.g. disconnected blocks in the editor).
        const seen = new Set();
        const serializeBlock = (block) => {
            if (seen.has(block.uniqueId)) {
                return;
            }
            seen.add(block.uniqueId);
            const serializedBlock = {};
            block.serialize(serializedBlock);
            serializationObject.allBlocks.push(serializedBlock);
        };
        this.visitAllBlocks(serializeBlock);
        for (const block of this._allBlocks) {
            serializeBlock(block);
        }
        serializationObject.executionContexts = [];
        for (const context of this._executionContexts) {
            const serializedContext = {};
            context.serialize(serializedContext, valueSerializeFunction);
            serializationObject.executionContexts.push(serializedContext);
        }
    }
    /**
     * Launches the flow graph editor for this graph.
     * The editor is lazy-loaded from {@link FlowGraph.EditorURL} the first time it is used.
     * @param config defines the configuration of the editor
     * @returns a promise fulfilled when the editor is visible
     */
    // eslint-disable-next-line @typescript-eslint/naming-convention
    async edit(config) {
        return await new Promise((resolve) => {
            this._BJSFLOWGRAPHEDITOR = this._BJSFLOWGRAPHEDITOR || this._getGlobalFlowGraphEditor();
            if (typeof this._BJSFLOWGRAPHEDITOR === "undefined") {
                const editorUrl = config && config.editorURL ? config.editorURL : FlowGraph.EditorURL;
                // Load the editor bundle and add it to the DOM.
                Tools.LoadBabylonScript(editorUrl, () => {
                    this._BJSFLOWGRAPHEDITOR = this._BJSFLOWGRAPHEDITOR || this._getGlobalFlowGraphEditor();
                    this._createFlowGraphEditor(config?.flowGraphEditorConfig);
                    resolve();
                });
            }
            else {
                this._createFlowGraphEditor(config?.flowGraphEditorConfig);
                resolve();
            }
        });
    }
    /**
     * Creates the flow graph editor window.
     * @param additionalConfig additional configuration forwarded to `FlowGraphEditor.Show()`
     */
    _createFlowGraphEditor(additionalConfig) {
        const editorConfig = {
            flowGraph: this,
            hostScene: this._scene,
            // edit() always targets the developer's own live graph and scene, so the editor should
            // attach to that scene instead of spinning up a throwaway preview scene. A caller can
            // still override this via additionalConfig.
            attachToLiveScene: true,
            ...additionalConfig,
        };
        this._BJSFLOWGRAPHEDITOR.FlowGraphEditor.Show(editorConfig);
    }
}
/**
 * Define the URL to load the flow graph editor script from.
 */
FlowGraph.EditorURL = `${Tools._DefaultCdnUrl}/v${AbstractEngine.Version}/flowGraphEditor/babylon.flowGraphEditor.js`;

/** This file must only contain pure code and pure imports */
/**
 * Prefix used by the default event-reference format.
 *
 * A host that maps behavior graphs onto its own object model (for example the glTF
 * `KHR_interactivity` loader) supplies its own format through {@link IFlowGraphHostResolver}.
 */
const FlowGraphDefaultEventReferencePrefix = "flowgraph://events/";
/**
 * Builds the default reference for an event source key.
 * @param key the event source key (e.g. `"sceneReady"`, `"sceneTick"`, or a custom event id)
 * @returns the event reference
 */
function GetDefaultEventReference(key) {
    return FlowGraphDefaultEventReferencePrefix + key;
}
/**
 * Extracts the event source key from a default-format event reference.
 * @param reference the value to decode
 * @returns the event source key, or `undefined` when the value is not an event reference
 */
function GetDefaultEventReferenceKey(reference) {
    return reference.startsWith(FlowGraphDefaultEventReferencePrefix) ? reference.substring(FlowGraphDefaultEventReferencePrefix.length) : undefined;
}

/**
 * This class holds all of the existing flow graphs and is responsible for creating new ones.
 * It also handles starting/stopping multiple graphs and communication between them through an Event Coordinator
 * This is the entry point for the flow graph system.
 * @experimental This class is still in development and is subject to change.
 */
class FlowGraphCoordinator {
    /**
     * Observable raised when a flow graph is added to any coordinator. Used by the inspector to keep
     * the flow graph list in sync. The payload is the newly added flow graph.
     */
    static get OnFlowGraphAddedObservable() {
        return this._OnFlowGraphAddedObservable;
    }
    /**
     * Observable raised when a flow graph is removed from any coordinator. Used by the inspector to keep
     * the flow graph list in sync. The payload is the removed flow graph.
     */
    static get OnFlowGraphRemovedObservable() {
        return this._OnFlowGraphRemovedObservable;
    }
    constructor(
    /**
     * the configuration of the block
     */
    config) {
        this.config = config;
        /**
         * When set to true (default) custom events will be dispatched synchronously.
         * This means that the events will be dispatched immediately when they are triggered.
         */
        this.dispatchEventsSynchronously = true;
        this._flowGraphs = [];
        this._customEventsMap = new Map();
        this._eventExecutionCounter = new Map();
        this._executeOnNextFrame = [];
        this._eventUniqueId = 0;
        /**
         * Stack of custom-event dispatches currently in progress. Each entry pairs the
         * dispatched event id with the Observable's EventState so that
         * `event/stopPropagation` can stop the remaining handlers of an in-flight
         * dispatch. A stack (rather than a single value) tolerates re-entrant
         * dispatching, e.g. an event handler synchronously sending another event.
         * @internal
         */
        this._eventDispatchStack = [];
        // When the scene is disposed, dispose all graphs currently running on it.
        this._disposeObserver = this.config.scene.onDisposeObservable.add(() => {
            this.dispose();
        });
        this._onBeforeRenderObserver = this.config.scene.onBeforeRenderObservable.add(() => {
            // Reset the event execution counter at the beginning of each frame.
            this._eventExecutionCounter.clear();
            // duplicate the _executeOnNextFrame array to avoid modifying it while iterating over it
            const executeOnNextFrame = this._executeOnNextFrame.slice(0);
            if (executeOnNextFrame.length) {
                // Execute the events that were triggered on the next frame.
                for (const event of executeOnNextFrame) {
                    this.notifyCustomEvent(event.id, event.data, false);
                    // remove the event from the array
                    const index = this._executeOnNextFrame.findIndex((e) => e.uniqueId === event.uniqueId);
                    if (index !== -1) {
                        this._executeOnNextFrame.splice(index, 1);
                    }
                }
            }
        });
        // Add itself to the SceneCoordinators list for the Inspector.
        let coordinators = FlowGraphCoordinator.SceneCoordinators.get(this.config.scene);
        if (!coordinators) {
            coordinators = [];
            FlowGraphCoordinator.SceneCoordinators.set(this.config.scene, coordinators);
        }
        coordinators.push(this);
    }
    /**
     * Creates a new flow graph and adds it to the list of existing flow graphs
     * @param name - optional name for the new graph. If not provided, an auto-generated name is used.
     * @returns a new flow graph
     */
    createGraph(name) {
        const graphName = name ?? `Graph ${this._flowGraphs.length + 1}`;
        const graph = new FlowGraph({ scene: this.config.scene, coordinator: this, name: graphName });
        this._flowGraphs.push(graph);
        FlowGraphCoordinator._OnFlowGraphAddedObservable.notifyObservers(graph);
        return graph;
    }
    /**
     * Removes a flow graph from the list of existing flow graphs and disposes it
     * @param graph the graph to remove
     */
    removeGraph(graph) {
        const index = this._flowGraphs.indexOf(graph);
        if (index !== -1) {
            graph.dispose();
            this._flowGraphs.splice(index, 1);
            FlowGraphCoordinator._OnFlowGraphRemovedObservable.notifyObservers(graph);
        }
    }
    /**
     * Starts all graphs
     */
    start() {
        for (const graph of this._flowGraphs) {
            graph.start();
        }
    }
    /**
     * Disposes all graphs
     */
    dispose() {
        for (const graph of this._flowGraphs) {
            graph.dispose();
            FlowGraphCoordinator._OnFlowGraphRemovedObservable.notifyObservers(graph);
        }
        this._flowGraphs.length = 0;
        this._disposeObserver?.remove();
        this._onBeforeRenderObserver?.remove();
        // Remove itself from the SceneCoordinators list for the Inspector.
        const coordinators = FlowGraphCoordinator.SceneCoordinators.get(this.config.scene) ?? [];
        const index = coordinators.indexOf(this);
        if (index !== -1) {
            coordinators.splice(index, 1);
        }
    }
    /**
     * Serializes this coordinator to a JSON object.
     * @param serializationObject the object to serialize to
     * @param valueSerializeFunction the function to use to serialize the value
     */
    serialize(serializationObject, valueSerializeFunction) {
        serializationObject._flowGraphs = [];
        for (const graph of this._flowGraphs) {
            const serializedGraph = {};
            graph.serialize(serializedGraph, valueSerializeFunction);
            serializationObject._flowGraphs.push(serializedGraph);
        }
        serializationObject.dispatchEventsSynchronously = this.dispatchEventsSynchronously;
    }
    /**
     * Gets the list of flow graphs
     */
    get flowGraphs() {
        return this._flowGraphs;
    }
    /**
     * Get an observable that will be notified when the event with the given id is fired.
     * @param id the id of the event
     * @returns the observable for the event
     */
    getCustomEventObservable(id) {
        let observable = this._customEventsMap.get(id);
        if (!observable) {
            // receive event is initialized before scene start, so no need to notify if triggered. but possible!
            observable = new Observable( /*undefined, true*/);
            this._customEventsMap.set(id, observable);
        }
        return observable;
    }
    /**
     * Notifies the observable for the given event id with the given data.
     * @param id the id of the event
     * @param data the data to send with the event
     * @param async if true, the event will be dispatched asynchronously
     */
    notifyCustomEvent(id, data, async = !this.dispatchEventsSynchronously) {
        if (async) {
            this._executeOnNextFrame.push({ id, data, uniqueId: this._eventUniqueId++ });
            return;
        }
        // check if we are not exceeding the max number of events
        if (this._eventExecutionCounter.has(id)) {
            const count = this._eventExecutionCounter.get(id);
            this._eventExecutionCounter.set(id, count + 1);
            if (count >= FlowGraphCoordinator.MaxEventTypeExecutionPerFrame) {
                if (count === FlowGraphCoordinator.MaxEventTypeExecutionPerFrame) {
                    Logger.Warn(`FlowGraphCoordinator: Too many executions of event "${id}".`);
                }
                return;
            }
        }
        else {
            this._eventExecutionCounter.set(id, 1);
        }
        const observable = this._customEventsMap.get(id);
        if (observable) {
            observable.notifyObservers(data);
        }
    }
    /**
     * @internal
     * Marks the beginning of a custom-event dispatch. Called by event receiver
     * blocks from within their Observable callback so that the dispatch's
     * EventState becomes reachable by `event/stopPropagation` while the receiver
     * flow executes synchronously.
     * @param eventId the id of the event being dispatched
     * @param state the Observable EventState for this dispatch
     */
    _beginEventDispatch(eventId, state) {
        this._eventDispatchStack.push({ eventId, state });
    }
    /**
     * @internal
     * Marks the end of the most recent custom-event dispatch started with
     * {@link _beginEventDispatch}.
     */
    _endEventDispatch() {
        this._eventDispatchStack.pop();
    }
    /**
     * Stops the propagation of an in-flight custom event, preventing any event
     * handler nodes that have not been activated yet from running for the current
     * dispatch.
     *
     * The `event` argument is the opaque event reference produced by an event block on its `event`
     * output. If it does not reference an event that is currently being dispatched, this is a no-op.
     *
     * Babylon custom events have no scene-graph propagation layer, so there are
     * no transitive activations to cancel when `stopImmediate` is false. When it
     * is true, the remaining handlers in the Observable dispatch are skipped.
     * @param event the event reference to stop propagation for
     * @param stopImmediate whether to also stop remaining immediate handlers
     */
    stopEventPropagation(event, stopImmediate) {
        if (typeof event !== "string" || !stopImmediate) {
            return;
        }
        const decode = this.config.hostResolver?.decodeEventReference ?? GetDefaultEventReferenceKey;
        const eventId = decode(event);
        if (eventId === undefined) {
            return;
        }
        // Find the most recent matching in-flight dispatch and skip its remaining observers.
        for (let i = this._eventDispatchStack.length - 1; i >= 0; i--) {
            if (this._eventDispatchStack[i].eventId === eventId) {
                this._eventDispatchStack[i].state.skipNextObservers = true;
                return;
            }
        }
    }
    /**
     * @internal
     * Encodes an event source key as the opaque reference exposed on an event block's `event`
     * output, delegating to the host resolver when one is configured.
     * @param key the event source key
     * @returns the event reference
     */
    _getEventReference(key) {
        const encode = this.config.hostResolver?.encodeEventReference;
        return encode ? encode(key) : GetDefaultEventReference(key);
    }
}
/**
 * The maximum number of events per type.
 * This is used to limit the number of events that can be created in a single scene.
 * This is to prevent infinite loops.
 */
FlowGraphCoordinator.MaxEventsPerType = 30;
/**
 * The maximum number of execution of a specific event in a single frame.
 */
FlowGraphCoordinator.MaxEventTypeExecutionPerFrame = 30;
/**
 * @internal
 * A list of all the coordinators per scene. Will be used by the inspector
 */
FlowGraphCoordinator.SceneCoordinators = new Map();
FlowGraphCoordinator._OnFlowGraphAddedObservable = new Observable();
FlowGraphCoordinator._OnFlowGraphRemovedObservable = new Observable();

/**
 * Any external module that wishes to add a new block to the flow graph can add to this object using the helper function.
 */
const CustomBlocks = {};
/**
 * Reverse lookup: short block name → full "module/blockName" key, for O(1) fallback.
 */
const ShortNameToFullKey = {};
/**
 * If you want to add a new block to the block factory, you should use this function.
 * Please be sure to choose a unique name and define the responsible module.
 * @param module the name of the module that is responsible for the block
 * @param blockName the name of the block. This should be unique.
 * @param factory an async factory function to generate the block
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function addToBlockFactory(module, blockName, factory) {
    const fullKey = `${module}/${blockName}`;
    CustomBlocks[fullKey] = factory;
    ShortNameToFullKey[blockName] = fullKey;
}
/**
 * a function to get a factory function for a block.
 * @param blockName the block name to initialize. If the block comes from an external module, the name should be in the format "module/blockName"
 * @returns an async factory function that will return the block class when called.
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function blockFactory(blockName) {
    switch (blockName) {
        case "FlowGraphPlayAnimationBlock" /* FlowGraphBlockNames.PlayAnimation */:
            return async () => (await import('./flowGraphPlayAnimationBlock-d1k5wk6H.esm.js')).FlowGraphPlayAnimationBlock;
        case "FlowGraphStopAnimationBlock" /* FlowGraphBlockNames.StopAnimation */:
            return async () => (await import('./flowGraphStopAnimationBlock-BnsDCX2U.esm.js')).FlowGraphStopAnimationBlock;
        case "FlowGraphPauseAnimationBlock" /* FlowGraphBlockNames.PauseAnimation */:
            return async () => (await import('./flowGraphPauseAnimationBlock-DDsTiXhy.esm.js')).FlowGraphPauseAnimationBlock;
        case "FlowGraphInterpolationBlock" /* FlowGraphBlockNames.ValueInterpolation */:
            return async () => (await import('./flowGraphInterpolationBlock-sgRFUAoN.esm.js')).FlowGraphInterpolationBlock;
        case "FlowGraphSceneReadyEventBlock" /* FlowGraphBlockNames.SceneReadyEvent */:
            return async () => (await import('./flowGraphSceneReadyEventBlock-DtQcuZ7j.esm.js')).FlowGraphSceneReadyEventBlock;
        case "FlowGraphSceneTickEventBlock" /* FlowGraphBlockNames.SceneTickEvent */:
            return async () => (await import('./flowGraphSceneTickEventBlock-D0RQq3os.esm.js')).FlowGraphSceneTickEventBlock;
        case "FlowGraphSendCustomEventBlock" /* FlowGraphBlockNames.SendCustomEvent */:
            return async () => (await import('./flowGraphSendCustomEventBlock-vzdxKyJG.esm.js')).FlowGraphSendCustomEventBlock;
        case "FlowGraphReceiveCustomEventBlock" /* FlowGraphBlockNames.ReceiveCustomEvent */:
            return async () => (await import('./flowGraphReceiveCustomEventBlock-DgmNCogD.esm.js')).FlowGraphReceiveCustomEventBlock;
        case "FlowGraphStopEventPropagationBlock" /* FlowGraphBlockNames.StopEventPropagation */:
            return async () => (await import('./flowGraphStopEventPropagationBlock-DOU1mIhZ.esm.js')).FlowGraphStopEventPropagationBlock;
        case "FlowGraphMeshPickEventBlock" /* FlowGraphBlockNames.MeshPickEvent */:
            return async () => (await import('./flowGraphMeshPickEventBlock-BxUXkzcO.esm.js')).FlowGraphMeshPickEventBlock;
        case "FlowGraphEBlock" /* FlowGraphBlockNames.E */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphEBlock;
        case "FlowGraphPIBlock" /* FlowGraphBlockNames.PI */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphPiBlock;
        case "FlowGraphTauBlock" /* FlowGraphBlockNames.Tau */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphTauBlock;
        case "FlowGraphInfBlock" /* FlowGraphBlockNames.Inf */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphInfBlock;
        case "FlowGraphNaNBlock" /* FlowGraphBlockNames.NaN */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphNaNBlock;
        case "FlowGraphRandomBlock" /* FlowGraphBlockNames.Random */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphRandomBlock;
        case "FlowGraphAddBlock" /* FlowGraphBlockNames.Add */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAddBlock;
        case "FlowGraphSubtractBlock" /* FlowGraphBlockNames.Subtract */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphSubtractBlock;
        case "FlowGraphMultiplyBlock" /* FlowGraphBlockNames.Multiply */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphMultiplyBlock;
        case "FlowGraphDivideBlock" /* FlowGraphBlockNames.Divide */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphDivideBlock;
        case "FlowGraphAbsBlock" /* FlowGraphBlockNames.Abs */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAbsBlock;
        case "FlowGraphSignBlock" /* FlowGraphBlockNames.Sign */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphSignBlock;
        case "FlowGraphTruncBlock" /* FlowGraphBlockNames.Trunc */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphTruncBlock;
        case "FlowGraphFloorBlock" /* FlowGraphBlockNames.Floor */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphFloorBlock;
        case "FlowGraphCeilBlock" /* FlowGraphBlockNames.Ceil */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphCeilBlock;
        case "FlowGraphRoundBlock" /* FlowGraphBlockNames.Round */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphRoundBlock;
        case "FlowGraphFractBlock" /* FlowGraphBlockNames.Fraction */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphFractionBlock;
        case "FlowGraphNegationBlock" /* FlowGraphBlockNames.Negation */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphNegationBlock;
        case "FlowGraphModuloBlock" /* FlowGraphBlockNames.Modulo */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphModuloBlock;
        case "FlowGraphMinBlock" /* FlowGraphBlockNames.Min */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphMinBlock;
        case "FlowGraphMaxBlock" /* FlowGraphBlockNames.Max */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphMaxBlock;
        case "FlowGraphClampBlock" /* FlowGraphBlockNames.Clamp */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphClampBlock;
        case "FlowGraphSaturateBlock" /* FlowGraphBlockNames.Saturate */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphSaturateBlock;
        case "FlowGraphMathInterpolationBlock" /* FlowGraphBlockNames.MathInterpolation */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphMathInterpolationBlock;
        case "FlowGraphMathSlerpBlock" /* FlowGraphBlockNames.MathSlerp */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphMathSlerpBlock;
        case "FlowGraphSmoothStepBlock" /* FlowGraphBlockNames.SmoothStep */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphMathSmoothStepBlock;
        case "FlowGraphRGBToOkLChBlock" /* FlowGraphBlockNames.RGBToOkLCh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphRGBToOkLChBlock;
        case "FlowGraphRGBFromOkLChBlock" /* FlowGraphBlockNames.RGBFromOkLCh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphRGBFromOkLChBlock;
        case "FlowGraphEqualityBlock" /* FlowGraphBlockNames.Equality */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphEqualityBlock;
        case "FlowGraphLessThanBlock" /* FlowGraphBlockNames.LessThan */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphLessThanBlock;
        case "FlowGraphLessThanOrEqualBlock" /* FlowGraphBlockNames.LessThanOrEqual */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphLessThanOrEqualBlock;
        case "FlowGraphGreaterThanBlock" /* FlowGraphBlockNames.GreaterThan */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphGreaterThanBlock;
        case "FlowGraphGreaterThanOrEqualBlock" /* FlowGraphBlockNames.GreaterThanOrEqual */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphGreaterThanOrEqualBlock;
        case "FlowGraphIsNaNBlock" /* FlowGraphBlockNames.IsNaN */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphIsNanBlock;
        case "FlowGraphIsInfBlock" /* FlowGraphBlockNames.IsInfinity */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphIsInfinityBlock;
        case "FlowGraphDegToRadBlock" /* FlowGraphBlockNames.DegToRad */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphDegToRadBlock;
        case "FlowGraphRadToDegBlock" /* FlowGraphBlockNames.RadToDeg */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphRadToDegBlock;
        case "FlowGraphSinBlock" /* FlowGraphBlockNames.Sin */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphSinBlock;
        case "FlowGraphCosBlock" /* FlowGraphBlockNames.Cos */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphCosBlock;
        case "FlowGraphTanBlock" /* FlowGraphBlockNames.Tan */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphTanBlock;
        case "FlowGraphASinBlock" /* FlowGraphBlockNames.Asin */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAsinBlock;
        case "FlowGraphACosBlock" /* FlowGraphBlockNames.Acos */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAcosBlock;
        case "FlowGraphATanBlock" /* FlowGraphBlockNames.Atan */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAtanBlock;
        case "FlowGraphATan2Block" /* FlowGraphBlockNames.Atan2 */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAtan2Block;
        case "FlowGraphSinhBlock" /* FlowGraphBlockNames.Sinh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphSinhBlock;
        case "FlowGraphCoshBlock" /* FlowGraphBlockNames.Cosh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphCoshBlock;
        case "FlowGraphTanhBlock" /* FlowGraphBlockNames.Tanh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphTanhBlock;
        case "FlowGraphASinhBlock" /* FlowGraphBlockNames.Asinh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAsinhBlock;
        case "FlowGraphACoshBlock" /* FlowGraphBlockNames.Acosh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAcoshBlock;
        case "FlowGraphATanhBlock" /* FlowGraphBlockNames.Atanh */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphAtanhBlock;
        case "FlowGraphExponentialBlock" /* FlowGraphBlockNames.Exponential */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphExpBlock;
        case "FlowGraphLogBlock" /* FlowGraphBlockNames.Log */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphLogBlock;
        case "FlowGraphLog2Block" /* FlowGraphBlockNames.Log2 */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphLog2Block;
        case "FlowGraphLog10Block" /* FlowGraphBlockNames.Log10 */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphLog10Block;
        case "FlowGraphSquareRootBlock" /* FlowGraphBlockNames.SquareRoot */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphSquareRootBlock;
        case "FlowGraphPowerBlock" /* FlowGraphBlockNames.Power */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphPowerBlock;
        case "FlowGraphCubeRootBlock" /* FlowGraphBlockNames.CubeRoot */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphCubeRootBlock;
        case "FlowGraphBitwiseAndBlock" /* FlowGraphBlockNames.BitwiseAnd */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphBitwiseAndBlock;
        case "FlowGraphBitwiseOrBlock" /* FlowGraphBlockNames.BitwiseOr */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphBitwiseOrBlock;
        case "FlowGraphBitwiseNotBlock" /* FlowGraphBlockNames.BitwiseNot */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphBitwiseNotBlock;
        case "FlowGraphBitwiseXorBlock" /* FlowGraphBlockNames.BitwiseXor */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphBitwiseXorBlock;
        case "FlowGraphBitwiseLeftShiftBlock" /* FlowGraphBlockNames.BitwiseLeftShift */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphBitwiseLeftShiftBlock;
        case "FlowGraphBitwiseRightShiftBlock" /* FlowGraphBlockNames.BitwiseRightShift */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphBitwiseRightShiftBlock;
        case "FlowGraphLengthBlock" /* FlowGraphBlockNames.Length */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphLengthBlock;
        case "FlowGraphNormalizeBlock" /* FlowGraphBlockNames.Normalize */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphNormalizeBlock;
        case "FlowGraphDotBlock" /* FlowGraphBlockNames.Dot */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphDotBlock;
        case "FlowGraphCrossBlock" /* FlowGraphBlockNames.Cross */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphCrossBlock;
        case "FlowGraphRotate2DBlock" /* FlowGraphBlockNames.Rotate2D */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphRotate2DBlock;
        case "FlowGraphRotate3DBlock" /* FlowGraphBlockNames.Rotate3D */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphRotate3DBlock;
        case "FlowGraphTransposeBlock" /* FlowGraphBlockNames.Transpose */:
            return async () => (await import('./flowGraphMatrixMathBlocks-BMIK3kSc.esm.js')).FlowGraphTransposeBlock;
        case "FlowGraphDeterminantBlock" /* FlowGraphBlockNames.Determinant */:
            return async () => (await import('./flowGraphMatrixMathBlocks-BMIK3kSc.esm.js')).FlowGraphDeterminantBlock;
        case "FlowGraphInvertMatrixBlock" /* FlowGraphBlockNames.InvertMatrix */:
            return async () => (await import('./flowGraphMatrixMathBlocks-BMIK3kSc.esm.js')).FlowGraphInvertMatrixBlock;
        case "FlowGraphMatrixMultiplicationBlock" /* FlowGraphBlockNames.MatrixMultiplication */:
            return async () => (await import('./flowGraphMatrixMathBlocks-BMIK3kSc.esm.js')).FlowGraphMatrixMultiplicationBlock;
        case "FlowGraphBranchBlock" /* FlowGraphBlockNames.Branch */:
            return async () => (await import('./flowGraphBranchBlock-DxO1jzHy.esm.js')).FlowGraphBranchBlock;
        case "FlowGraphSetDelayBlock" /* FlowGraphBlockNames.SetDelay */:
            return async () => (await import('./flowGraphSetDelayBlock-DqPbHWtP.esm.js')).FlowGraphSetDelayBlock;
        case "FlowGraphCancelDelayBlock" /* FlowGraphBlockNames.CancelDelay */:
            return async () => (await import('./flowGraphCancelDelayBlock-DgGum9Vr.esm.js')).FlowGraphCancelDelayBlock;
        case "FlowGraphCallCounterBlock" /* FlowGraphBlockNames.CallCounter */:
            return async () => (await import('./flowGraphCounterBlock-DK6ihu-d.esm.js')).FlowGraphCallCounterBlock;
        case "FlowGraphDebounceBlock" /* FlowGraphBlockNames.Debounce */:
            return async () => (await import('./flowGraphDebounceBlock-C_LYVYth.esm.js')).FlowGraphDebounceBlock;
        case "FlowGraphThrottleBlock" /* FlowGraphBlockNames.Throttle */:
            return async () => (await import('./flowGraphThrottleBlock-Lp5_DoJy.esm.js')).FlowGraphThrottleBlock;
        case "FlowGraphDoNBlock" /* FlowGraphBlockNames.DoN */:
            return async () => (await import('./flowGraphDoNBlock-DwusKkno.esm.js')).FlowGraphDoNBlock;
        case "FlowGraphFlipFlopBlock" /* FlowGraphBlockNames.FlipFlop */:
            return async () => (await import('./flowGraphFlipFlopBlock-B5noEwrN.esm.js')).FlowGraphFlipFlopBlock;
        case "FlowGraphForLoopBlock" /* FlowGraphBlockNames.ForLoop */:
            return async () => (await import('./flowGraphForLoopBlock-BNgAtFv_.esm.js')).FlowGraphForLoopBlock;
        case "FlowGraphMultiGateBlock" /* FlowGraphBlockNames.MultiGate */:
            return async () => (await import('./flowGraphMultiGateBlock-lpraZdhE.esm.js')).FlowGraphMultiGateBlock;
        case "FlowGraphSequenceBlock" /* FlowGraphBlockNames.Sequence */:
            return async () => (await import('./flowGraphSequenceBlock-D4dpXa0H.esm.js')).FlowGraphSequenceBlock;
        case "FlowGraphSwitchBlock" /* FlowGraphBlockNames.Switch */:
            return async () => (await import('./flowGraphSwitchBlock-BlUVsmb-.esm.js')).FlowGraphSwitchBlock;
        case "FlowGraphWaitAllBlock" /* FlowGraphBlockNames.WaitAll */:
            return async () => (await import('./flowGraphWaitAllBlock-DLrzFSJK.esm.js')).FlowGraphWaitAllBlock;
        case "FlowGraphWhileLoopBlock" /* FlowGraphBlockNames.WhileLoop */:
            return async () => (await import('./flowGraphWhileLoopBlock-CC6-HMMz.esm.js')).FlowGraphWhileLoopBlock;
        case "FlowGraphConsoleLogBlock" /* FlowGraphBlockNames.ConsoleLog */:
            return async () => (await import('./flowGraphConsoleLogBlock-DUSw-m_S.esm.js')).FlowGraphConsoleLogBlock;
        case "FlowGraphConditionalBlock" /* FlowGraphBlockNames.Conditional */:
            return async () => (await import('./flowGraphConditionalDataBlock-Dp_5SXsj.esm.js')).FlowGraphConditionalDataBlock;
        case "FlowGraphConstantBlock" /* FlowGraphBlockNames.Constant */:
            return async () => (await import('./flowGraphConstantBlock-BV_0JdWL.esm.js')).FlowGraphConstantBlock;
        case "FlowGraphTransformCoordinatesSystemBlock" /* FlowGraphBlockNames.TransformCoordinatesSystem */:
            return async () => (await import('./flowGraphTransformCoordinatesSystemBlock-DcDQEz4w.esm.js')).FlowGraphTransformCoordinatesSystemBlock;
        case "FlowGraphGetAssetBlock" /* FlowGraphBlockNames.GetAsset */:
            return async () => (await import('./flowGraphGetAssetBlock-BqOEUETJ.esm.js')).FlowGraphGetAssetBlock;
        case "FlowGraphGetPropertyBlock" /* FlowGraphBlockNames.GetProperty */:
            return async () => (await import('./flowGraphGetPropertyBlock-BbVPhC8i.esm.js')).FlowGraphGetPropertyBlock;
        case "FlowGraphSetPropertyBlock" /* FlowGraphBlockNames.SetProperty */:
            return async () => (await import('./flowGraphSetPropertyBlock-nhaK717d.esm.js')).FlowGraphSetPropertyBlock;
        case "FlowGraphGetVariableBlock" /* FlowGraphBlockNames.GetVariable */:
            return async () => (await import('./flowGraphGetVariableBlock-hZ54i0bK.esm.js')).FlowGraphGetVariableBlock;
        case "FlowGraphSetVariableBlock" /* FlowGraphBlockNames.SetVariable */:
            return async () => (await import('./flowGraphSetVariableBlock-DFofSi0h.esm.js')).FlowGraphSetVariableBlock;
        case "FlowGraphJsonPointerParserBlock" /* FlowGraphBlockNames.JsonPointerParser */:
            return async () => (await import('./flowGraphJsonPointerParserBlock-ahphPax2.esm.js')).FlowGraphJsonPointerParserBlock;
        case "FlowGraphLeadingZerosBlock" /* FlowGraphBlockNames.LeadingZeros */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphLeadingZerosBlock;
        case "FlowGraphTrailingZerosBlock" /* FlowGraphBlockNames.TrailingZeros */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphTrailingZerosBlock;
        case "FlowGraphOneBitsCounterBlock" /* FlowGraphBlockNames.OneBitsCounter */:
            return async () => (await import('./flowGraphMathBlocks-BDTYND1S.esm.js')).FlowGraphOneBitsCounterBlock;
        case "FlowGraphCombineVector2Block" /* FlowGraphBlockNames.CombineVector2 */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphCombineVector2Block;
        case "FlowGraphCombineVector3Block" /* FlowGraphBlockNames.CombineVector3 */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphCombineVector3Block;
        case "FlowGraphCombineVector4Block" /* FlowGraphBlockNames.CombineVector4 */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphCombineVector4Block;
        case "FlowGraphCombineMatrixBlock" /* FlowGraphBlockNames.CombineMatrix */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphCombineMatrixBlock;
        case "FlowGraphCombineMatrix2DBlock" /* FlowGraphBlockNames.CombineMatrix2D */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphCombineMatrix2DBlock;
        case "FlowGraphCombineMatrix3DBlock" /* FlowGraphBlockNames.CombineMatrix3D */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphCombineMatrix3DBlock;
        case "FlowGraphExtractVector2Block" /* FlowGraphBlockNames.ExtractVector2 */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphExtractVector2Block;
        case "FlowGraphExtractVector3Block" /* FlowGraphBlockNames.ExtractVector3 */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphExtractVector3Block;
        case "FlowGraphExtractVector4Block" /* FlowGraphBlockNames.ExtractVector4 */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphExtractVector4Block;
        case "FlowGraphExtractMatrixBlock" /* FlowGraphBlockNames.ExtractMatrix */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphExtractMatrixBlock;
        case "FlowGraphExtractMatrix2DBlock" /* FlowGraphBlockNames.ExtractMatrix2D */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphExtractMatrix2DBlock;
        case "FlowGraphExtractMatrix3DBlock" /* FlowGraphBlockNames.ExtractMatrix3D */:
            return async () => (await import('./flowGraphMathCombineExtractBlocks-Ddl0WonZ.esm.js')).FlowGraphExtractMatrix3DBlock;
        case "FlowGraphTransformVectorBlock" /* FlowGraphBlockNames.TransformVector */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphTransformBlock;
        case "FlowGraphTransformCoordinatesBlock" /* FlowGraphBlockNames.TransformCoordinates */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphTransformCoordinatesBlock;
        case "FlowGraphConjugateBlock" /* FlowGraphBlockNames.Conjugate */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphConjugateBlock;
        case "FlowGraphAngleBetweenBlock" /* FlowGraphBlockNames.AngleBetween */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphAngleBetweenBlock;
        case "FlowGraphQuaternionFromAxisAngleBlock" /* FlowGraphBlockNames.QuaternionFromAxisAngle */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphQuaternionFromAxisAngleBlock;
        case "FlowGraphAxisAngleFromQuaternionBlock" /* FlowGraphBlockNames.AxisAngleFromQuaternion */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphAxisAngleFromQuaternionBlock;
        case "FlowGraphQuaternionFromDirectionsBlock" /* FlowGraphBlockNames.QuaternionFromDirections */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphQuaternionFromDirectionsBlock;
        case "FlowGraphQuaternionFromUpForwardBlock" /* FlowGraphBlockNames.QuaternionFromUpForward */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphQuaternionFromUpForwardBlock;
        case "FlowGraphQuaternionFromAnglesBlock" /* FlowGraphBlockNames.QuaternionFromAngles */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphQuaternionFromAnglesBlock;
        case "FlowGraphVectorSlerpBlock" /* FlowGraphBlockNames.VectorSlerp */:
            return async () => (await import('./flowGraphVectorMathBlocks-DvMUrnBo.esm.js')).FlowGraphVectorSlerpBlock;
        case "FlowGraphMatrixDecompose" /* FlowGraphBlockNames.MatrixDecompose */:
            return async () => (await import('./flowGraphMatrixMathBlocks-BMIK3kSc.esm.js')).FlowGraphMatrixDecomposeBlock;
        case "FlowGraphMatrixCompose" /* FlowGraphBlockNames.MatrixCompose */:
            return async () => (await import('./flowGraphMatrixMathBlocks-BMIK3kSc.esm.js')).FlowGraphMatrixComposeBlock;
        case "FlowGraphBooleanToFloat" /* FlowGraphBlockNames.BooleanToFloat */:
            return async () => (await import('./flowGraphTypeToTypeBlocks-CkFWZ_dC.esm.js')).FlowGraphBooleanToFloat;
        case "FlowGraphBooleanToInt" /* FlowGraphBlockNames.BooleanToInt */:
            return async () => (await import('./flowGraphTypeToTypeBlocks-CkFWZ_dC.esm.js')).FlowGraphBooleanToInt;
        case "FlowGraphFloatToBoolean" /* FlowGraphBlockNames.FloatToBoolean */:
            return async () => (await import('./flowGraphTypeToTypeBlocks-CkFWZ_dC.esm.js')).FlowGraphFloatToBoolean;
        case "FlowGraphIntToBoolean" /* FlowGraphBlockNames.IntToBoolean */:
            return async () => (await import('./flowGraphTypeToTypeBlocks-CkFWZ_dC.esm.js')).FlowGraphIntToBoolean;
        case "FlowGraphIntToFloat" /* FlowGraphBlockNames.IntToFloat */:
            return async () => (await import('./flowGraphTypeToTypeBlocks-CkFWZ_dC.esm.js')).FlowGraphIntToFloat;
        case "FlowGraphFloatToInt" /* FlowGraphBlockNames.FloatToInt */:
            return async () => (await import('./flowGraphTypeToTypeBlocks-CkFWZ_dC.esm.js')).FlowGraphFloatToInt;
        case "FlowGraphEasingBlock" /* FlowGraphBlockNames.Easing */:
            return async () => (await import('./flowGraphEasingBlock-D_6nEkZQ.esm.js')).FlowGraphEasingBlock;
        case "FlowGraphBezierCurveEasing" /* FlowGraphBlockNames.BezierCurveEasing */:
            return async () => (await import('./flowGraphBezierCurveEasingBlock-SoRIZncN.esm.js')).FlowGraphBezierCurveEasingBlock;
        case "FlowGraphPointerOverEventBlock" /* FlowGraphBlockNames.PointerOverEvent */:
            return async () => (await import('./flowGraphPointerOverEventBlock-DR4vOs7j.esm.js')).FlowGraphPointerOverEventBlock;
        case "FlowGraphPointerOutEventBlock" /* FlowGraphBlockNames.PointerOutEvent */:
            return async () => (await import('./flowGraphPointerOutEventBlock-DpaOBmIK.esm.js')).FlowGraphPointerOutEventBlock;
        case "FlowGraphPointerDownEventBlock" /* FlowGraphBlockNames.PointerDownEvent */:
            return async () => (await import('./flowGraphPointerDownEventBlock-B6SgAOwb.esm.js')).FlowGraphPointerDownEventBlock;
        case "FlowGraphPointerUpEventBlock" /* FlowGraphBlockNames.PointerUpEvent */:
            return async () => (await import('./flowGraphPointerUpEventBlock-WwPQkh8z.esm.js')).FlowGraphPointerUpEventBlock;
        case "FlowGraphPointerMoveEventBlock" /* FlowGraphBlockNames.PointerMoveEvent */:
            return async () => (await import('./flowGraphPointerMoveEventBlock-B6ubscL2.esm.js')).FlowGraphPointerMoveEventBlock;
        // Keyboard
        case "FlowGraphKeyDownEventBlock" /* FlowGraphBlockNames.KeyDownEvent */:
            return async () => (await import('./flowGraphKeyDownEventBlock-Abo_0e_4.esm.js')).FlowGraphKeyDownEventBlock;
        case "FlowGraphKeyUpEventBlock" /* FlowGraphBlockNames.KeyUpEvent */:
            return async () => (await import('./flowGraphKeyUpEventBlock-CP3E9hrS.esm.js')).FlowGraphKeyUpEventBlock;
        case "FlowGraphIsKeyPressedBlock" /* FlowGraphBlockNames.IsKeyPressed */:
            return async () => (await import('./flowGraphIsKeyPressedBlock-B-3DTnpV.esm.js')).FlowGraphIsKeyPressedBlock;
        case "FlowGraphContextBlock" /* FlowGraphBlockNames.Context */:
            return async () => (await import('./flowGraphContextBlock-hDsOqbME.esm.js')).FlowGraphContextBlock;
        case "FlowGraphArrayIndexBlock" /* FlowGraphBlockNames.ArrayIndex */:
            return async () => (await import('./flowGraphArrayIndexBlock-tuNRc3eV.esm.js')).FlowGraphArrayIndexBlock;
        case "FlowGraphCodeExecutionBlock" /* FlowGraphBlockNames.CodeExecution */:
            return async () => (await import('./flowGraphCodeExecutionBlock-8l1PD6ZN.esm.js')).FlowGraphCodeExecutionBlock;
        case "FlowGraphIndexOfBlock" /* FlowGraphBlockNames.IndexOf */:
            return async () => (await import('./flowGraphIndexOfBlock-MGb91idx.esm.js')).FlowGraphIndexOfBlock;
        case "FlowGraphFunctionReference" /* FlowGraphBlockNames.FunctionReference */:
            return async () => (await import('./flowGraphFunctionReferenceBlock--PLw9Z30.esm.js')).FlowGraphFunctionReferenceBlock;
        case "FlowGraphDataSwitchBlock" /* FlowGraphBlockNames.DataSwitch */:
            return async () => (await import('./flowGraphDataSwitchBlock-64zJnDEY.esm.js')).FlowGraphDataSwitchBlock;
        case "FlowGraphDebugBlock" /* FlowGraphBlockNames.DebugBlock */:
            return async () => (await import('./flowGraphDebugBlock-CME6jITo.esm.js')).FlowGraphDebugBlock;
        // Physics
        case "FlowGraphPhysicsCollisionEventBlock" /* FlowGraphBlockNames.PhysicsCollisionEvent */:
            return async () => (await import('./flowGraphPhysicsCollisionEventBlock-TO83qUvs.esm.js')).FlowGraphPhysicsCollisionEventBlock;
        case "FlowGraphApplyForceBlock" /* FlowGraphBlockNames.PhysicsApplyForce */:
            return async () => (await import('./flowGraphApplyForceBlock-8nrNgh23.esm.js')).FlowGraphApplyForceBlock;
        case "FlowGraphApplyImpulseBlock" /* FlowGraphBlockNames.PhysicsApplyImpulse */:
            return async () => (await import('./flowGraphApplyImpulseBlock-DSxcZULB.esm.js')).FlowGraphApplyImpulseBlock;
        case "FlowGraphSetLinearVelocityBlock" /* FlowGraphBlockNames.PhysicsSetLinearVelocity */:
            return async () => (await import('./flowGraphSetLinearVelocityBlock-Uo4csTpf.esm.js')).FlowGraphSetLinearVelocityBlock;
        case "FlowGraphSetAngularVelocityBlock" /* FlowGraphBlockNames.PhysicsSetAngularVelocity */:
            return async () => (await import('./flowGraphSetAngularVelocityBlock-CNZKhaKa.esm.js')).FlowGraphSetAngularVelocityBlock;
        case "FlowGraphSetPhysicsMotionTypeBlock" /* FlowGraphBlockNames.PhysicsSetMotionType */:
            return async () => (await import('./flowGraphSetPhysicsMotionTypeBlock-DbYIz3h5.esm.js')).FlowGraphSetPhysicsMotionTypeBlock;
        case "FlowGraphGetLinearVelocityBlock" /* FlowGraphBlockNames.PhysicsGetLinearVelocity */:
            return async () => (await import('./flowGraphGetLinearVelocityBlock-CRj6oPCs.esm.js')).FlowGraphGetLinearVelocityBlock;
        case "FlowGraphGetAngularVelocityBlock" /* FlowGraphBlockNames.PhysicsGetAngularVelocity */:
            return async () => (await import('./flowGraphGetAngularVelocityBlock-PzYKURPt.esm.js')).FlowGraphGetAngularVelocityBlock;
        case "FlowGraphGetPhysicsMassPropertiesBlock" /* FlowGraphBlockNames.PhysicsGetMassProperties */:
            return async () => (await import('./flowGraphGetPhysicsMassPropertiesBlock-rySFKYtV.esm.js')).FlowGraphGetPhysicsMassPropertiesBlock;
        // Audio
        case "FlowGraphPlaySoundBlock" /* FlowGraphBlockNames.AudioPlaySound */:
            return async () => (await import('./flowGraphPlaySoundBlock-Ddff8tDS.esm.js')).FlowGraphPlaySoundBlock;
        case "FlowGraphStopSoundBlock" /* FlowGraphBlockNames.AudioStopSound */:
            return async () => (await import('./flowGraphStopSoundBlock-DeW5mwsO.esm.js')).FlowGraphStopSoundBlock;
        case "FlowGraphPauseSoundBlock" /* FlowGraphBlockNames.AudioPauseSound */:
            return async () => (await import('./flowGraphPauseSoundBlock-9tczKg13.esm.js')).FlowGraphPauseSoundBlock;
        case "FlowGraphSetSoundVolumeBlock" /* FlowGraphBlockNames.AudioSetVolume */:
            return async () => (await import('./flowGraphSetSoundVolumeBlock-Ca4E6JJf.esm.js')).FlowGraphSetSoundVolumeBlock;
        case "FlowGraphSoundEndedEventBlock" /* FlowGraphBlockNames.AudioSoundEndedEvent */:
            return async () => (await import('./flowGraphSoundEndedEventBlock-C_ThwUV6.esm.js')).FlowGraphSoundEndedEventBlock;
        case "FlowGraphGetSoundVolumeBlock" /* FlowGraphBlockNames.AudioGetVolume */:
            return async () => (await import('./flowGraphGetSoundVolumeBlock-BInKUV8E.esm.js')).FlowGraphGetSoundVolumeBlock;
        case "FlowGraphIsSoundPlayingBlock" /* FlowGraphBlockNames.AudioIsSoundPlaying */:
            return async () => (await import('./flowGraphIsSoundPlayingBlock-DJHfbI8V.esm.js')).FlowGraphIsSoundPlayingBlock;
        default:
            // check if the block is a custom block
            if (CustomBlocks[blockName]) {
                return CustomBlocks[blockName];
            }
            // Fallback: O(1) reverse lookup by short name (e.g. "FlowGraphGLTFDataProvider" → "KHR_interactivity/FlowGraphGLTFDataProvider")
            if (!blockName.includes("/")) {
                const fullKey = ShortNameToFullKey[blockName];
                if (fullKey && CustomBlocks[fullKey]) {
                    return CustomBlocks[fullKey];
                }
            }
            throw new Error(`Unknown block name ${blockName}`);
    }
}

/**
 * Parses a graph from a given serialization object
 * @param serializationObject the object where the values are written
 * @param options options for parsing the graph
 * @returns the parsed graph
 */
async function ParseFlowGraphAsync(serializationObject, options) {
    // get all classes types needed for the blocks using the block factory
    const resolvedClasses = await Promise.all(serializationObject.allBlocks.map(async (serializedBlock) => {
        const classFactory = blockFactory(serializedBlock.className);
        return await classFactory();
    }));
    // async will be used when we start using the block async factory
    return ParseFlowGraph(serializationObject, options, resolvedClasses);
}
/**
 * Parses a graph from a given serialization object
 * @param serializationObject the object where the values are written
 * @param options options for parsing the graph
 * @param resolvedClasses the resolved classes for the blocks
 * @returns the parsed graph
 */
function ParseFlowGraph(serializationObject, options, resolvedClasses) {
    const graph = options.coordinator.createGraph();
    // Restore graph identity from serialized data
    if (serializationObject.name) {
        graph.name = serializationObject.name;
    }
    if (serializationObject.uniqueId) {
        graph.uniqueId = serializationObject.uniqueId;
    }
    const blocks = [];
    const valueParseFunction = options.valueParseFunction ?? defaultValueParseFunction;
    // Parse all blocks
    // for (const serializedBlock of serializationObject.allBlocks) {
    for (let i = 0; i < serializationObject.allBlocks.length; i++) {
        const serializedBlock = serializationObject.allBlocks[i];
        const block = ParseFlowGraphBlockWithClassType(serializedBlock, { scene: options.coordinator.config.scene, pathConverter: options.pathConverter, assetsContainer: options.coordinator.config.scene, valueParseFunction }, resolvedClasses[i]);
        blocks.push(block);
        graph.addBlock(block);
        if (block instanceof FlowGraphEventBlock) {
            graph.addEventBlock(block);
        }
    }
    // After parsing all blocks, connect them.
    // Build lookup maps for O(1) connection resolution instead of O(B*P) linear scans.
    const dataInMap = new Map();
    const dataOutMap = new Map();
    const signalInMap = new Map();
    const signalOutMap = new Map();
    for (const block of blocks) {
        for (const dataIn of block.dataInputs) {
            dataInMap.set(dataIn.uniqueId, dataIn);
        }
        for (const dataOut of block.dataOutputs) {
            dataOutMap.set(dataOut.uniqueId, dataOut);
        }
        if (block instanceof FlowGraphExecutionBlock) {
            for (const signalIn of block.signalInputs) {
                signalInMap.set(signalIn.uniqueId, signalIn);
            }
            for (const signalOut of block.signalOutputs) {
                signalOutMap.set(signalOut.uniqueId, signalOut);
            }
        }
    }
    const connectIfNeeded = (connection, connectedConnection) => {
        if (connection._connectedPoint.indexOf(connectedConnection) !== -1) {
            return;
        }
        connection.connectTo(connectedConnection);
    };
    for (const block of blocks) {
        for (const dataIn of block.dataInputs) {
            for (const serializedConnection of dataIn.connectedPointIds) {
                const connection = dataOutMap.get(serializedConnection);
                if (!connection) {
                    throw new Error("Could not find data out connection with unique id " + serializedConnection);
                }
                connectIfNeeded(dataIn, connection);
            }
        }
        for (const dataOut of block.dataOutputs) {
            for (const serializedConnection of dataOut.connectedPointIds) {
                const connection = dataInMap.get(serializedConnection);
                if (!connection) {
                    throw new Error("Could not find data in connection with unique id " + serializedConnection);
                }
                connectIfNeeded(dataOut, connection);
            }
        }
        if (block instanceof FlowGraphExecutionBlock) {
            for (const signalOut of block.signalOutputs) {
                for (const serializedConnection of signalOut.connectedPointIds) {
                    const connection = signalInMap.get(serializedConnection);
                    if (!connection) {
                        throw new Error("Could not find signal in connection with unique id " + serializedConnection);
                    }
                    connectIfNeeded(signalOut, connection);
                }
            }
            for (const signalIn of block.signalInputs) {
                for (const serializedConnection of signalIn.connectedPointIds) {
                    const connection = signalOutMap.get(serializedConnection);
                    if (!connection) {
                        throw new Error("Could not find signal out connection with unique id " + serializedConnection);
                    }
                    connectIfNeeded(connection, signalIn);
                }
            }
        }
    }
    for (const serializedContext of serializationObject.executionContexts ?? []) {
        ParseFlowGraphContext(serializedContext, { graph, valueParseFunction }, serializationObject.rightHanded);
    }
    return graph;
}
/**
 * Parses a context
 * @param serializationObject the object containing the context serialization values
 * @param options the options for parsing the context
 * @param rightHanded whether the serialized data is right handed
 * @returns
 */
function ParseFlowGraphContext(serializationObject, options, rightHanded) {
    const result = options.graph.createContext();
    if (serializationObject.enableLogging) {
        result.enableLogging = true;
    }
    result.treatDataAsRightHanded = rightHanded || false;
    const valueParseFunction = options.valueParseFunction ?? defaultValueParseFunction;
    result.uniqueId = serializationObject.uniqueId;
    result.name = serializationObject.name ?? "";
    const scene = result.getScene();
    // check if assets context is available
    if (serializationObject._assetsContext) {
        const ac = serializationObject._assetsContext;
        const assetsContext = {
            meshes: ac.meshes?.map((m) => scene.getMeshById(m)),
            lights: ac.lights?.map((l) => scene.getLightByName(l)),
            cameras: ac.cameras?.map((c) => scene.getCameraByName(c)),
            materials: ac.materials?.map((m) => scene.getMaterialById(m)),
            textures: ac.textures?.map((t) => scene.getTextureByName(t)),
            animations: ac.animations?.map((a) => scene.animations.find((anim) => anim.name === a)),
            skeletons: ac.skeletons?.map((s) => scene.getSkeletonByName(s)),
            particleSystems: ac.particleSystems?.map((ps) => scene.getParticleSystemById(ps)),
            animationGroups: ac.animationGroups?.map((ag) => scene.getAnimationGroupByName(ag)),
            transformNodes: ac.transformNodes?.map((tn) => scene.getTransformNodeById(tn)),
            rootNodes: [],
            multiMaterials: [],
            morphTargetManagers: [],
            geometries: [],
            actionManagers: [],
            environmentTexture: null,
            postProcesses: [],
            sounds: null,
            effectLayers: [],
            layers: [],
            reflectionProbes: [],
            lensFlareSystems: [],
            proceduralTextures: [],
            getNodes: function () {
                throw new Error("Function not implemented.");
            },
        };
        result.assetsContext = assetsContext;
    }
    for (const key in serializationObject._userVariables) {
        const value = valueParseFunction(key, serializationObject._userVariables, result.assetsContext, scene);
        result.userVariables[key] = value;
    }
    // Restore variable type annotations
    if (serializationObject._variableTypes) {
        for (const key in serializationObject._variableTypes) {
            result.setVariableType(key, serializationObject._variableTypes[key]);
        }
    }
    for (const key in serializationObject._connectionValues) {
        const value = valueParseFunction(key, serializationObject._connectionValues, result.assetsContext, scene);
        result._setConnectionValueByKey(key, value);
    }
    return result;
}
/**
 * Parses a block from a serialization object
 * @param serializationObject the object to parse from
 * @param parseOptions options for parsing the block
 * @param classType the class type of the block. This is used when the class is not loaded asynchronously
 * @returns the parsed block
 */
function ParseFlowGraphBlockWithClassType(serializationObject, parseOptions, classType) {
    const parsedConfig = {};
    const valueParseFunction = parseOptions.valueParseFunction ?? defaultValueParseFunction;
    if (serializationObject.config) {
        for (const key in serializationObject.config) {
            parsedConfig[key] = valueParseFunction(key, serializationObject.config, parseOptions.assetsContainer || parseOptions.scene, parseOptions.scene);
        }
    }
    if (needsPathConverter(serializationObject.className)) {
        if (!parseOptions.pathConverter) {
            throw new Error("Block " + serializationObject.className + " requires a path converter to be provided in parse options.");
        }
        parsedConfig.pathConverter = parseOptions.pathConverter;
    }
    const obj = new classType(parsedConfig);
    obj.uniqueId = serializationObject.uniqueId;
    for (let i = 0; i < serializationObject.dataInputs.length; i++) {
        const dataInput = obj.getDataInput(serializationObject.dataInputs[i].name);
        if (dataInput) {
            dataInput.deserialize(serializationObject.dataInputs[i]);
            // Restore _defaultValue if it was serialized.  Without this, the
            // user-set inline value (e.g. "2" on an Add input, or "position"
            // on a GetProperty's propertyName) is lost during round-trips.
            if (serializationObject.dataInputs[i].defaultValue !== undefined) {
                dataInput._defaultValue = valueParseFunction("defaultValue", serializationObject.dataInputs[i], parseOptions.assetsContainer || parseOptions.scene, parseOptions.scene);
            }
        }
        else {
            throw new Error("Could not find data input with name " + serializationObject.dataInputs[i].name + " in block " + serializationObject.className);
        }
    }
    for (let i = 0; i < serializationObject.dataOutputs.length; i++) {
        const dataOutput = obj.getDataOutput(serializationObject.dataOutputs[i].name);
        if (dataOutput) {
            dataOutput.deserialize(serializationObject.dataOutputs[i]);
        }
        else {
            throw new Error("Could not find data output with name " + serializationObject.dataOutputs[i].name + " in block " + serializationObject.className);
        }
    }
    obj.metadata = serializationObject.metadata;
    obj.deserialize && obj.deserialize(serializationObject);
    return obj;
}

// eslint-disable-next-line @typescript-eslint/naming-convention
const gltfTypeToBabylonType = {
    float: { length: 1, flowGraphType: "number" /* FlowGraphTypes.Number */, elementType: "number" },
    bool: { length: 1, flowGraphType: "boolean" /* FlowGraphTypes.Boolean */, elementType: "boolean" },
    float2: { length: 2, flowGraphType: "Vector2" /* FlowGraphTypes.Vector2 */, elementType: "number" },
    float3: { length: 3, flowGraphType: "Vector3" /* FlowGraphTypes.Vector3 */, elementType: "number" },
    float4: { length: 4, flowGraphType: "Vector4" /* FlowGraphTypes.Vector4 */, elementType: "number" },
    float4x4: { length: 16, flowGraphType: "Matrix" /* FlowGraphTypes.Matrix */, elementType: "number" },
    float2x2: { length: 4, flowGraphType: "Matrix2D" /* FlowGraphTypes.Matrix2D */, elementType: "number" },
    float3x3: { length: 9, flowGraphType: "Matrix3D" /* FlowGraphTypes.Matrix3D */, elementType: "number" },
    int: { length: 1, flowGraphType: "FlowGraphInteger" /* FlowGraphTypes.Integer */, elementType: "number" },
    // KHR_interactivity opaque reference type. Represented as a JSON Pointer string
    // (e.g. "/nodes/17/") that addresses a glTF object. The empty string is the
    // canonical "null reference" sentinel used by the parser.
    ref: { length: 1, flowGraphType: "string" /* FlowGraphTypes.String */, elementType: "string" },
};
/**
 * Parses a KHR_interactivity graph definition (the raw glTF JSON object) into
 * the serialized FlowGraph form consumed by {@link ParseFlowGraphAsync}.
 *
 * The class walks the interactivity types, declarations, variables, events
 * and nodes in order and emits an {@link ISerializedFlowGraph} via
 * {@link serializeToFlowGraph}.
 */
class InteractivityGraphToFlowGraphParser {
    constructor(_interactivityGraph, _gltf, _animationTargetFps = 60) {
        this._interactivityGraph = _interactivityGraph;
        this._gltf = _gltf;
        this._animationTargetFps = _animationTargetFps;
        /**
         * Note - the graph should be rejected if the same type is defined twice.
         * We currently don't validate that.
         */
        this._types = [];
        this._mappings = [];
        this._staticVariables = [];
        this._events = [];
        this._internalEventsCounter = 0;
        this._nodes = [];
        /**
         * Extra blocks the parser inserts between existing nodes (e.g. the seconds→frames multiply for
         * connected animation-time inputs). Kept separate from any node's `blocks` array so per-node
         * post-processing that indexes into that array (such as the animation extraProcessors targeting
         * the last block) is not disturbed, then concatenated into the serialized graph.
         */
        this._insertedBlocks = [];
        // start with types
        this._parseTypes();
        // continue with declarations
        this._parseDeclarations();
        this._parseVariables();
        this._parseEvents();
        this._parseNodes();
    }
    get arrays() {
        return {
            types: this._types,
            mappings: this._mappings,
            staticVariables: this._staticVariables,
            events: this._events,
            nodes: this._nodes,
        };
    }
    _parseTypes() {
        if (!this._interactivityGraph.types) {
            return;
        }
        for (const type of this._interactivityGraph.types) {
            this._types.push(gltfTypeToBabylonType[type.signature]);
        }
    }
    _parseDeclarations() {
        if (!this._interactivityGraph.declarations) {
            return;
        }
        for (const declaration of this._interactivityGraph.declarations) {
            // make sure we have the mapping for this operation
            const mapping = getMappingForDeclaration(declaration);
            // mapping is defined, because we generate an empty mapping if it's not found
            if (!mapping) {
                Logger.Error(["No mapping found for declaration", declaration]);
                throw new Error("Error parsing declarations");
            }
            this._mappings.push({
                flowGraphMapping: mapping,
                fullOperationName: declaration.extension ? declaration.op + ":" + declaration.extension : declaration.op,
            });
        }
    }
    _parseVariables() {
        if (!this._interactivityGraph.variables) {
            return;
        }
        for (const variable of this._interactivityGraph.variables) {
            const parsed = this._parseVariable(variable);
            // set the default values here
            this._staticVariables.push(parsed);
        }
    }
    _parseVariable(variable, dataTransform) {
        const type = this._types[variable.type];
        if (!type) {
            Logger.Error(["No type found for variable", variable]);
            throw new Error("Error parsing variables");
        }
        if (variable.value) {
            if (variable.value.length !== type.length) {
                Logger.Error(["Invalid value length for variable", variable, type]);
                throw new Error("Error parsing variables");
            }
        }
        const value = variable.value || [];
        if (!value.length) {
            switch (type.flowGraphType) {
                case "boolean" /* FlowGraphTypes.Boolean */:
                    value.push(false);
                    break;
                case "FlowGraphInteger" /* FlowGraphTypes.Integer */:
                    value.push(0);
                    break;
                case "number" /* FlowGraphTypes.Number */:
                    value.push(NaN);
                    break;
                case "string" /* FlowGraphTypes.String */:
                    // Default for a `ref`-typed value is the null reference, encoded as the empty string.
                    value.push("");
                    break;
                case "Vector2" /* FlowGraphTypes.Vector2 */:
                    value.push(NaN, NaN);
                    break;
                case "Vector3" /* FlowGraphTypes.Vector3 */:
                    value.push(NaN, NaN, NaN);
                    break;
                case "Vector4" /* FlowGraphTypes.Vector4 */:
                case "Matrix2D" /* FlowGraphTypes.Matrix2D */:
                case "Quaternion" /* FlowGraphTypes.Quaternion */:
                    value.fill(NaN, 0, 4);
                    break;
                case "Matrix" /* FlowGraphTypes.Matrix */:
                    value.fill(NaN, 0, 16);
                    break;
                case "Matrix3D" /* FlowGraphTypes.Matrix3D */:
                    value.fill(NaN, 0, 9);
                    break;
            }
        }
        // in case of NaN, Infinity, we need to parse the string to the object itself
        if (type.elementType === "number" && typeof value[0] === "string") {
            value[0] = parseFloat(value[0]);
        }
        return { type: type.flowGraphType, value: dataTransform ? dataTransform(value, this) : value };
    }
    _parseEvents() {
        if (!this._interactivityGraph.events) {
            return;
        }
        for (const event of this._interactivityGraph.events) {
            const converted = {
                eventId: event.id || "internalEvent_" + this._internalEventsCounter++,
            };
            if (event.values) {
                converted.eventData = Object.keys(event.values).map((key) => {
                    const eventValue = event.values?.[key];
                    if (!eventValue) {
                        Logger.Error(["No value found for event key", key]);
                        throw new Error("Error parsing events");
                    }
                    const type = this._types[eventValue.type];
                    if (!type) {
                        Logger.Error(["No type found for event value", eventValue]);
                        throw new Error("Error parsing events");
                    }
                    const value = typeof eventValue.value !== "undefined" ? this._parseVariable(eventValue) : undefined;
                    return {
                        id: key,
                        type: type.flowGraphType,
                        eventData: true,
                        value,
                    };
                });
            }
            this._events.push(converted);
        }
    }
    _parseNodes() {
        if (!this._interactivityGraph.nodes) {
            return;
        }
        for (const node of this._interactivityGraph.nodes) {
            // some validation
            if (typeof node.declaration !== "number") {
                Logger.Error(["No declaration found for node", node]);
                throw new Error("Error parsing nodes");
            }
            const mapping = this._mappings[node.declaration];
            if (!mapping) {
                Logger.Error(["No mapping found for node", node]);
                throw new Error("Error parsing nodes");
            }
            if (mapping.flowGraphMapping.validation) {
                const validationResult = mapping.flowGraphMapping.validation(node, this._interactivityGraph, this._gltf);
                if (!validationResult.valid) {
                    throw new Error(`Error validating interactivity node ${this._interactivityGraph.declarations?.[node.declaration].op} - ${validationResult.error}`);
                }
            }
            const blocks = [];
            // create block(s) for this node using the mapping
            for (const blockType of mapping.flowGraphMapping.blocks) {
                const block = this._getEmptyBlock(blockType, mapping.fullOperationName);
                this._parseNodeConfiguration(node, block, mapping.flowGraphMapping, blockType);
                blocks.push(block);
            }
            this._nodes.push({ blocks, fullOperationName: mapping.fullOperationName });
        }
    }
    _getEmptyBlock(className, type) {
        return {
            uniqueId: RandomGUID(),
            className,
            dataInputs: [],
            dataOutputs: [],
            signalInputs: [],
            signalOutputs: [],
            config: {},
            type,
            metadata: {},
        };
    }
    _parseNodeConfiguration(node, block, nodeMapping, blockType) {
        const gltfConfiguration = node.configuration;
        if (gltfConfiguration) {
            for (const key in gltfConfiguration) {
                const gltfProperty = gltfConfiguration[key];
                if (!gltfProperty) {
                    throw new Error("Error parsing node configuration");
                }
                const propertyMapping = nodeMapping.configuration?.[key];
                const belongsToBlock = propertyMapping && propertyMapping.toBlock ? propertyMapping.toBlock === blockType : nodeMapping.blocks.indexOf(blockType) === 0;
                if (belongsToBlock) {
                    let value = propertyMapping?.defaultValue;
                    if (gltfProperty?.value) {
                        value = gltfProperty.value;
                    }
                    if (!propertyMapping?.isArray) {
                        if (value.length !== 1) {
                            Logger.Warn(`Invalid non-array value length: ${value.length}`);
                        }
                        value = value[0];
                    }
                    if (propertyMapping?.dataTransformer) {
                        value = propertyMapping.dataTransformer(value, this);
                    }
                    if (value !== undefined) {
                        // Update the flow graph block config.
                        block.config[propertyMapping?.name || key] = {
                            value: value,
                        };
                    }
                }
            }
        }
    }
    _parseNodeConnections(context) {
        for (let i = 0; i < this._nodes.length; i++) {
            // get the corresponding gltf node
            const gltfNode = this._interactivityGraph.nodes?.[i];
            if (!gltfNode) {
                // should never happen but let's still check
                Logger.Error(["No node found for interactivity node", this._nodes[i]]);
                throw new Error("Error parsing node connections");
            }
            const flowGraphBlocks = this._nodes[i];
            const outputMapper = this._mappings[gltfNode.declaration];
            // validate
            if (!outputMapper) {
                Logger.Error(["No mapping found for node", gltfNode]);
                throw new Error("Error parsing node connections");
            }
            // KHR_interactivity spec section 3.2.4 "Unsupported Operations":
            // nodes referring to unsupported operations are demoted to no-ops.
            // Activations of their input flow sockets are ignored, their output
            // flow sockets are never activated, and their output value sockets
            // return constant type-default values. They have no backing
            // FlowGraph blocks (blocks.length === 0), so there is nothing to
            // wire for this node — skip all of its connections.
            if (flowGraphBlocks.blocks.length === 0) {
                Logger.Warn(`Skipping connections for no-op node #${i} (unsupported operation: ${flowGraphBlocks.fullOperationName})`);
                continue;
            }
            const flowsFromGLTF = gltfNode.flows || {};
            const flowsKeys = Object.keys(flowsFromGLTF).sort(); // sorting as some operations require sorted keys
            // connect the flows
            for (const flowKey of flowsKeys) {
                const flow = flowsFromGLTF[flowKey];
                const flowMapping = outputMapper.flowGraphMapping.outputs?.flows?.[flowKey];
                const socketOutName = flowMapping?.name || flowKey;
                // get the input node of this block
                const inputNodeId = flow.node;
                const nodeIn = this._nodes[inputNodeId];
                if (!nodeIn) {
                    Logger.Error(["No node found for input node id", inputNodeId]);
                    throw new Error("Error parsing node connections");
                }
                // Spec 3.2.4: input flow activations on no-op nodes are ignored,
                // so a flow connection into a no-op target is itself a no-op.
                // Drop it instead of crashing on the missing target block.
                if (nodeIn.blocks.length === 0) {
                    Logger.Warn(`Dropping flow connection from node #${i} "${flowKey}" to no-op node #${inputNodeId} (unsupported operation: ${nodeIn.fullOperationName})`);
                    continue;
                }
                // create a serialized socket
                const socketOut = this._createNewSocketConnection(socketOutName, true);
                const block = (flowMapping && flowMapping.toBlock && flowGraphBlocks.blocks.find((b) => b.className === flowMapping.toBlock)) || flowGraphBlocks.blocks[0];
                block.signalOutputs.push(socketOut);
                // get the mapper for the input node - in case it mapped to multiple blocks
                const inputMapper = getMappingForFullOperationName(nodeIn.fullOperationName);
                if (!inputMapper) {
                    Logger.Error(["No mapping found for input node", nodeIn]);
                    throw new Error("Error parsing node connections");
                }
                let flowInMapping = inputMapper.inputs?.flows?.[flow.socket || "in"];
                let arrayMapping = false;
                if (!flowInMapping) {
                    for (const key in inputMapper.inputs?.flows) {
                        if (key.startsWith("[") && key.endsWith("]")) {
                            arrayMapping = true;
                            flowInMapping = inputMapper.inputs?.flows?.[key];
                        }
                    }
                }
                const nodeInSocketName = flowInMapping ? (arrayMapping ? flowInMapping.name.replace("$1", flow.socket || "") : flowInMapping.name) : flow.socket || "in";
                const inputBlock = (flowInMapping && flowInMapping.toBlock && nodeIn.blocks.find((b) => b.className === flowInMapping.toBlock)) || nodeIn.blocks[0];
                // in all of the flow graph input connections, find the one with the same name as the socket
                let socketIn = inputBlock.signalInputs.find((s) => s.name === nodeInSocketName);
                // if the socket doesn't exist, create the input socket for the connection
                if (!socketIn) {
                    socketIn = this._createNewSocketConnection(nodeInSocketName);
                    inputBlock.signalInputs.push(socketIn);
                }
                // connect the sockets
                socketIn.connectedPointIds.push(socketOut.uniqueId);
                socketOut.connectedPointIds.push(socketIn.uniqueId);
            }
            // connect the values
            const valuesFromGLTF = gltfNode.values || {};
            const valuesKeys = Object.keys(valuesFromGLTF);
            for (const valueKey of valuesKeys) {
                const value = valuesFromGLTF[valueKey];
                let valueMapping = outputMapper.flowGraphMapping.inputs?.values?.[valueKey];
                let arrayMapping = false;
                if (!valueMapping) {
                    for (const key in outputMapper.flowGraphMapping.inputs?.values) {
                        if (key.startsWith("[") && key.endsWith("]")) {
                            arrayMapping = true;
                            valueMapping = outputMapper.flowGraphMapping.inputs?.values?.[key];
                        }
                    }
                }
                const socketInName = valueMapping ? (arrayMapping ? valueMapping.name.replace("$1", valueKey) : valueMapping.name) : valueKey;
                // create a serialized socket
                const socketIn = this._createNewSocketConnection(socketInName);
                const block = (valueMapping && valueMapping.toBlock && flowGraphBlocks.blocks.find((b) => b.className === valueMapping.toBlock)) || flowGraphBlocks.blocks[0];
                block.dataInputs.push(socketIn);
                // Captured before the connected branch below shadows `valueMapping`. When set and the
                // value is supplied by a connection, the seconds→frames `dataTransformer` cannot run
                // (it is parse-time only), so the raw connected value is scaled by a runtime multiply.
                const convertConnectedTimeToFrames = !!valueMapping?.convertConnectedTimeToFrames;
                if (value.value !== undefined) {
                    const convertedValue = this._parseVariable(value, valueMapping && valueMapping.dataTransformer);
                    context._connectionValues[socketIn.uniqueId] = convertedValue;
                }
                else if (typeof value.node !== "undefined") {
                    const nodeOutId = value.node;
                    const nodeOutSocketName = value.socket || "value";
                    const nodeOut = this._nodes[nodeOutId];
                    if (!nodeOut) {
                        Logger.Error(["No node found for output socket reference", value]);
                        throw new Error("Error parsing node connections");
                    }
                    // Spec 3.2.4: output value sockets of no-op nodes return
                    // constant type-default values. Leave the consumer's
                    // dataInput unconnected (no connectedPointIds) so the
                    // FlowGraph runtime falls back to the RichType default.
                    if (nodeOut.blocks.length === 0) {
                        Logger.Warn(`Dropping value connection from no-op node #${nodeOutId} (unsupported operation: ${nodeOut.fullOperationName}) into node #${i} "${valueKey}"; consumer will use type-default value`);
                        continue;
                    }
                    const outputMapper = getMappingForFullOperationName(nodeOut.fullOperationName);
                    if (!outputMapper) {
                        Logger.Error(["No mapping found for output socket reference", value]);
                        throw new Error("Error parsing node connections");
                    }
                    let valueMapping = outputMapper.outputs?.values?.[nodeOutSocketName];
                    let arrayMapping = false;
                    // check if there is an array mapping defined
                    if (!valueMapping) {
                        // search for a value mapping that has an array mapping
                        for (const key in outputMapper.outputs?.values) {
                            if (key.startsWith("[") && key.endsWith("]")) {
                                arrayMapping = true;
                                valueMapping = outputMapper.outputs?.values?.[key];
                            }
                        }
                    }
                    const socketOutName = valueMapping ? (arrayMapping ? valueMapping.name.replace("$1", nodeOutSocketName) : valueMapping?.name) : nodeOutSocketName;
                    const outBlock = (valueMapping && valueMapping.toBlock && nodeOut.blocks.find((b) => b.className === valueMapping.toBlock)) || nodeOut.blocks[0];
                    let socketOut = outBlock.dataOutputs.find((s) => s.name === socketOutName);
                    // if the socket doesn't exist, create it
                    if (!socketOut) {
                        socketOut = this._createNewSocketConnection(socketOutName, true);
                        outBlock.dataOutputs.push(socketOut);
                    }
                    // connect the sockets
                    if (convertConnectedTimeToFrames) {
                        this._connectWithSecondsToFramesConversion(context, socketOut, socketIn);
                    }
                    else {
                        socketIn.connectedPointIds.push(socketOut.uniqueId);
                        socketOut.connectedPointIds.push(socketIn.uniqueId);
                    }
                }
                else {
                    Logger.Error(["Invalid value for value connection", value]);
                    throw new Error("Error parsing node connections");
                }
            }
            // inter block connections
            if (outputMapper.flowGraphMapping.interBlockConnectors) {
                for (const connector of outputMapper.flowGraphMapping.interBlockConnectors) {
                    const input = connector.input;
                    const output = connector.output;
                    const isVariable = connector.isVariable;
                    this._connectFlowGraphNodes(input, output, flowGraphBlocks.blocks[connector.inputBlockIndex], flowGraphBlocks.blocks[connector.outputBlockIndex], isVariable);
                }
            }
            if (outputMapper.flowGraphMapping.extraProcessor) {
                const declaration = this._interactivityGraph.declarations?.[gltfNode.declaration];
                if (!declaration) {
                    Logger.Error(["No declaration found for extra processor", gltfNode]);
                    throw new Error("Error parsing node connections");
                }
                flowGraphBlocks.blocks = outputMapper.flowGraphMapping.extraProcessor(gltfNode, declaration, outputMapper.flowGraphMapping, this, flowGraphBlocks.blocks, context, this._gltf);
            }
        }
    }
    _createNewSocketConnection(name, isOutput) {
        return {
            uniqueId: RandomGUID(),
            name,
            _connectionType: isOutput ? 1 /* FlowGraphConnectionType.Output */ : 0 /* FlowGraphConnectionType.Input */,
            connectedPointIds: [],
        };
    }
    /**
     * Wires an upstream data output into a downstream data input through a runtime multiply block that
     * scales the value by the animation target fps. This converts a KHR animation time (seconds),
     * delivered by a connection (e.g. a `pointer/get` on the `maxTime` animation pointer), into the
     * Babylon animation frames expected by the play/stop-animation blocks. Literal times are already
     * converted at parse time by the input's `dataTransformer`, so this is only used for connections.
     * @param context the serialized flow graph context that stores literal socket values
     * @param upstreamOutput the data output socket providing the time value (in seconds)
     * @param downstreamInput the data input socket that expects the time in frames
     */
    _connectWithSecondsToFramesConversion(context, upstreamOutput, downstreamInput) {
        const multiplyBlock = this._getEmptyBlock("FlowGraphMultiplyBlock" /* FlowGraphBlockNames.Multiply */, "FlowGraphMultiplyBlock" /* FlowGraphBlockNames.Multiply */);
        // Scalar (float) multiply; matches how the `math/mul` mapping configures the block.
        multiplyBlock.config = { type: "number" /* FlowGraphTypes.Number */ };
        const inputA = this._createNewSocketConnection("a");
        const inputB = this._createNewSocketConnection("b");
        const output = this._createNewSocketConnection("value", true);
        multiplyBlock.dataInputs.push(inputA, inputB);
        multiplyBlock.dataOutputs.push(output);
        // The second factor is the constant animation target fps.
        context._connectionValues[inputB.uniqueId] = { type: "number" /* FlowGraphTypes.Number */, value: [this._animationTargetFps] };
        // upstream time output -> multiply.a
        inputA.connectedPointIds.push(upstreamOutput.uniqueId);
        upstreamOutput.connectedPointIds.push(inputA.uniqueId);
        // multiply.value (frames) -> downstream time input
        downstreamInput.connectedPointIds.push(output.uniqueId);
        output.connectedPointIds.push(downstreamInput.uniqueId);
        // Register the inserted block separately so serializeToFlowGraph picks it up without
        // appending to any node's block list (which would break per-node extraProcessors).
        this._insertedBlocks.push(multiplyBlock);
    }
    _connectFlowGraphNodes(input, output, serializedInput, serializedOutput, isVariable) {
        const inputArray = isVariable ? serializedInput.dataInputs : serializedInput.signalInputs;
        const outputArray = isVariable ? serializedOutput.dataOutputs : serializedOutput.signalOutputs;
        const inputConnection = inputArray.find((s) => s.name === input) || this._createNewSocketConnection(input);
        const outputConnection = outputArray.find((s) => s.name === output) || this._createNewSocketConnection(output, true);
        // of not found add it to the array
        if (!inputArray.find((s) => s.name === input)) {
            inputArray.push(inputConnection);
        }
        if (!outputArray.find((s) => s.name === output)) {
            outputArray.push(outputConnection);
        }
        // connect the sockets
        inputConnection.connectedPointIds.push(outputConnection.uniqueId);
        outputConnection.connectedPointIds.push(inputConnection.uniqueId);
    }
    /**
     * Returns the deterministic FlowGraph user-variable name used for the
     * static variable at the given declaration index.
     * @param index zero-based index into the interactivity graph's `variables` array.
     * @returns the FlowGraph variable name (e.g. `staticVariable_3`).
     */
    getVariableName(index) {
        return "staticVariable_" + index;
    }
    /**
     * Serializes the parsed interactivity graph into the {@link ISerializedFlowGraph}
     * payload consumed by `ParseFlowGraphAsync`. Performs node-connection wiring
     * and seeds the execution context with the graph's static variables.
     * @returns the serialized FlowGraph for the parsed KHR_interactivity graph.
     */
    serializeToFlowGraph() {
        const context = {
            uniqueId: RandomGUID(),
            _userVariables: {},
            _connectionValues: {},
        };
        this._parseNodeConnections(context);
        for (let i = 0; i < this._staticVariables.length; i++) {
            const variable = this._staticVariables[i];
            context._userVariables[this.getVariableName(i)] = variable;
        }
        const allBlocks = this._nodes.reduce((acc, val) => acc.concat(val.blocks), []).concat(this._insertedBlocks);
        return {
            rightHanded: true,
            allBlocks,
            executionContexts: [context],
        };
    }
}

/**
 * Composite path-to-object converter that dispatches by path prefix.
 *
 * The KHR_interactivity object model lives at the top of the JSON tree
 * (`/nodes/...`, `/materials/...`, `/extensions/...`) and is resolved by
 * `GLTFPathToObjectConverter` (see `gltfPathToObjectConverter`).
 *
 * Babylon-specific extensions can register additional namespaces here
 * (for example `/extensions/BABYLON_scene_objects/...` for refs that point
 * at scene objects not described by the source glTF) without forcing every
 * caller to know about them — `FlowGraphJsonPointerParserBlock` and the
 * existing template substitution machinery treat any path uniformly.
 *
 * Prefix entries are tried in order; if none matches, the fallback converter
 * is used. The fallback is typically the glTF converter, since standard
 * KHR_interactivity pointer paths sit at the JSON root and have no shared
 * prefix that would distinguish them from a missing namespace.
 */
class CompositePathToObjectConverter {
    /**
     * @param _prefixes prefix-keyed converter table, tried in order
     * @param _fallback converter used when no prefix entry matches
     */
    constructor(_prefixes, _fallback) {
        this._prefixes = _prefixes;
        this._fallback = _fallback;
    }
    /**
     * Adds a new prefix entry at the front of the lookup list so it is tried
     * before any entries registered earlier. Useful for late-registered
     * loader extensions that want to override or augment a previously
     * registered namespace.
     * @param entry the entry to add
     */
    addPrefix(entry) {
        this._prefixes.unshift(entry);
    }
    /**
     * @param path the JSON Pointer path to resolve
     * @returns an object accessor for the resolved property
     */
    convert(path) {
        for (const { prefix, converter } of this._prefixes) {
            if (path.startsWith(prefix)) {
                return converter.convert(path);
            }
        }
        return this._fallback.convert(path);
    }
}

/* eslint-disable @typescript-eslint/naming-convention */
/**
 * Root of the JSON-Pointer namespace under which Babylon-scene objects are
 * addressed by KHR_interactivity refs that did not originate from the source
 * glTF asset (e.g. refs emitted by engine-specific event blocks).
 *
 * Trailing `/` is intentional: it lets path-prefix dispatchers like
 * {@link CompositePathToObjectConverter} match cleanly.
 */
const BABYLON_SCENE_OBJECT_MODEL_PREFIX = "/extensions/BABYLON_scene_objects/";
/**
 * Resolves JSON Pointer paths in the `/extensions/BABYLON_scene_objects/...`
 * namespace to Babylon scene objects.
 *
 * The path layout is `/{root}/{collection}/{uniqueId}/{property}` where
 * `{root}` is the literal `extensions/BABYLON_scene_objects` prefix and
 * `{uniqueId}` is the Babylon `uniqueId` (stable per session) of the target
 * instance. For example:
 *
 * - `/extensions/BABYLON_scene_objects/transformNodes/42/translation`
 * - `/extensions/BABYLON_scene_objects/meshes/17/visible`
 *
 * Composite path dispatchers (see {@link CompositePathToObjectConverter})
 * route paths starting with the prefix here; everything else continues to be
 * resolved by the standard glTF converter.
 */
class BabylonScenePathToObjectConverter {
    constructor(_scene, _tree) {
        this._scene = _scene;
        this._tree = _tree;
    }
    /**
     * @param path the full JSON Pointer (must start with the Babylon prefix)
     * @returns an object-info container holding the resolved instance and accessor
     */
    convert(path) {
        if (!path.startsWith(BABYLON_SCENE_OBJECT_MODEL_PREFIX)) {
            throw new Error(`BabylonScenePathToObjectConverter: path "${path}" does not start with the expected prefix "${BABYLON_SCENE_OBJECT_MODEL_PREFIX}".`);
        }
        // Strip the namespace prefix and split. Ignore trailing empty segments
        // so refs of the form "/extensions/BABYLON_scene_objects/transformNodes/42/" parse cleanly.
        const tail = path.slice(BABYLON_SCENE_OBJECT_MODEL_PREFIX.length);
        const parts = tail.split("/").filter((p) => p.length > 0);
        if (parts.length === 0) {
            throw new Error(`BabylonScenePathToObjectConverter: path "${path}" is missing a collection name.`);
        }
        const collectionName = parts[0];
        const collection = this._tree[collectionName];
        if (!collection) {
            throw new Error(`BabylonScenePathToObjectConverter: unknown collection "${collectionName}" in path "${path}".`);
        }
        // Handle `<collection>.length` (no instance lookup).
        if (parts.length === 2 && parts[1] === "length") {
            const arr = this._getCollectionArray(collectionName);
            return { object: arr, info: collection.length };
        }
        if (parts.length < 2) {
            throw new Error(`BabylonScenePathToObjectConverter: path "${path}" is missing an instance id.`);
        }
        // parseInt would accept "12abc" as 12; require an all-digits id so a malformed path fails
        // loudly instead of binding to the wrong instance.
        if (!/^\d+$/.test(parts[1])) {
            throw new Error(`BabylonScenePathToObjectConverter: invalid uniqueId "${parts[1]}" in path "${path}".`);
        }
        const uniqueId = parseInt(parts[1], 10);
        if (!Number.isFinite(uniqueId) || uniqueId < 0) {
            throw new Error(`BabylonScenePathToObjectConverter: invalid uniqueId "${parts[1]}" in path "${path}".`);
        }
        const instance = this._lookupInstanceByUniqueId(collectionName, uniqueId);
        if (!instance) {
            throw new Error(`BabylonScenePathToObjectConverter: no ${collectionName} instance found with uniqueId ${uniqueId} (path "${path}").`);
        }
        // No property after the id → the ref itself is just a handle to the instance.
        // The accessor's `get` and `getTarget` both return the instance.
        if (parts.length === 2) {
            return {
                object: instance,
                info: this._buildIdentityAccessor(instance),
            };
        }
        // Walk the leaf descriptors for the requested property path. We keep this
        // very simple right now: only one segment after the id is supported, which
        // covers every property the initial leaves expose. Nested paths can be
        // added later by extending the walker.
        if (parts.length > 3) {
            throw new Error(`BabylonScenePathToObjectConverter: nested property paths are not yet supported (path "${path}").`);
        }
        const propertyName = parts[2];
        const leaf = collection.__array__[propertyName];
        if (!leaf || typeof leaf === "boolean") {
            throw new Error(`BabylonScenePathToObjectConverter: property "${propertyName}" is not registered on ${collectionName} (path "${path}").`);
        }
        return {
            object: instance,
            info: leaf,
        };
    }
    _getCollectionArray(collectionName) {
        switch (collectionName) {
            case "transformNodes":
                return this._scene.transformNodes;
            case "meshes":
                return this._scene.meshes;
            case "materials":
                return this._scene.materials;
            default:
                return [];
        }
    }
    _lookupInstanceByUniqueId(collectionName, uniqueId) {
        switch (collectionName) {
            case "transformNodes": {
                const direct = this._scene.transformNodes.find((n) => n.uniqueId === uniqueId);
                if (direct) {
                    return direct;
                }
                // Meshes are also transform nodes; allow the same path to resolve them.
                return this._scene.meshes.find((m) => m.uniqueId === uniqueId);
            }
            case "meshes":
                return this._scene.meshes.find((m) => m.uniqueId === uniqueId);
            case "materials":
                return this._scene.materials.find((m) => m.uniqueId === uniqueId);
            default:
                return undefined;
        }
    }
    _buildIdentityAccessor(instance) {
        return {
            type: "object",
            get: () => instance,
            getTarget: () => instance,
            isReadOnly: true,
        };
    }
}
/**
 * Builds the default Babylon-scene object-model tree.
 *
 * We deliberately start with a minimal set of properties: the goal of this
 * tree is to prove the seam (refs in the BABYLON namespace resolving through
 * the same `FlowGraphJsonPointerParserBlock` that the glTF refs use) without
 * committing to a complete property surface in this PR. Add new leaves here
 * as concrete event-source operations need them.
 * @returns a fresh Babylon-scene object-model tree with the default property surface.
 */
function CreateDefaultBabylonSceneObjectModelTree() {
    return {
        transformNodes: {
            length: {
                type: "number",
                get: (arr) => arr.length,
                getTarget: (arr) => arr,
            },
            __array__: {
                __target__: true,
                name: {
                    type: "string",
                    get: (n) => n.name,
                    set: (v, n) => {
                        n.name = v;
                    },
                    getTarget: (n) => n,
                },
                translation: {
                    type: "Vector3",
                    get: (n) => n.position,
                    set: (v, n) => n.position.copyFrom(v),
                    getTarget: (n) => n,
                },
                rotation: {
                    type: "Quaternion",
                    get: (n) => n.rotationQuaternion ?? Quaternion.RotationYawPitchRoll(n.rotation.y, n.rotation.x, n.rotation.z),
                    set: (v, n) => {
                        if (!n.rotationQuaternion) {
                            n.rotationQuaternion = v.clone();
                        }
                        else {
                            n.rotationQuaternion.copyFrom(v);
                        }
                    },
                    getTarget: (n) => n,
                },
                scale: {
                    type: "Vector3",
                    get: (n) => n.scaling,
                    set: (v, n) => n.scaling.copyFrom(v),
                    getTarget: (n) => n,
                },
                matrix: {
                    type: "Matrix",
                    get: (n) => n.computeWorldMatrix(false),
                    getTarget: (n) => n,
                    isReadOnly: true,
                },
                globalMatrix: {
                    type: "Matrix",
                    get: (n) => n.computeWorldMatrix(true),
                    getTarget: (n) => n,
                    isReadOnly: true,
                },
            },
        },
        meshes: {
            length: {
                type: "number",
                get: (arr) => arr.length,
                getTarget: (arr) => arr,
            },
            __array__: {
                __target__: true,
                name: {
                    type: "string",
                    get: (m) => m.name,
                    set: (v, m) => {
                        m.name = v;
                    },
                    getTarget: (m) => m,
                },
                visible: {
                    type: "boolean",
                    get: (m) => m.isVisible,
                    set: (v, m) => {
                        m.isVisible = v;
                    },
                    getTarget: (m) => m,
                },
            },
        },
        materials: {
            length: {
                type: "number",
                get: (arr) => arr.length,
                getTarget: (arr) => arr,
            },
            __array__: {
                __target__: true,
                name: {
                    type: "string",
                    get: (m) => m.name,
                    set: (v, m) => {
                        m.name = v;
                    },
                    getTarget: (m) => m,
                },
            },
        },
    };
}

/**
 * Re-exports pure implementation and applies runtime side effects.
 * Import flowGraphInteger.pure for tree-shakeable, side-effect-free usage.
 */
RegisterFlowGraphInteger();

/**
 * KHR_interactivity opaque reference representation.
 *
 * The specification gives `event/onStart`, `event/onTick`, and `event/receive` a `ref event`
 * output value socket — a runtime reference to the event instance that is consumed by
 * `event/stopPropagation` and validated via `pointer/get` on
 * `/extensions/KHR_interactivity/events/{}`. `flow/setDelay` likewise produces a delay reference
 * validated via `/extensions/KHR_interactivity/delays/{}`.
 *
 * A `ref` value is represented as a JSON Pointer string (the empty string acting as the canonical
 * "null" reference), so both namespaces are expressed as object-model pointers. These formats are
 * specific to this extension and are therefore owned here rather than by the FlowGraph engine.
 */
/**
 * The JSON Pointer prefix shared by all KHR_interactivity event references.
 */
const EventReferencePrefix = "/extensions/KHR_interactivity/events/";
/**
 * The JSON Pointer prefix shared by all KHR_interactivity delay references.
 */
const DelayReferencePrefix = "/extensions/KHR_interactivity/delays/";
/**
 * Builds the event reference for a FlowGraph event source key.
 *
 * Lifecycle events use a constant key so that all instances of the same operation return the same
 * reference; custom event receivers use their event id so that receivers of the same event compare
 * equal under `ref/eq`.
 * @param key the FlowGraph event source key
 * @returns the event reference string
 */
function GetEventReference(key) {
    return EventReferencePrefix + key;
}
/**
 * Extracts the FlowGraph event source key from an event reference.
 * @param reference the value to decode
 * @returns the event source key, or `undefined` when the value is not an event reference
 */
function GetEventReferenceKey(reference) {
    return IsEventReference(reference) ? reference.substring(EventReferencePrefix.length) : undefined;
}
/**
 * Returns whether the provided value is a KHR_interactivity event reference, i.e. a non-empty
 * string addressing the events object-model namespace.
 * @param value the value to test
 * @returns true if the value was produced by an event operation as a reference
 */
function IsEventReference(value) {
    return typeof value === "string" && value.startsWith(EventReferencePrefix);
}

/**
 * Tracking of the delays that are currently scheduled in a flow graph context.
 *
 * `flow/setDelay` produces a unique integer handle for every delayed activation it schedules.
 * This module records which of those handles are still pending — i.e. scheduled and not yet fired
 * or cancelled — per {@link FlowGraphContext}, so that a host can answer "is this delay handle
 * still valid?" for a reference it was handed earlier.
 */
/**
 * Name of the global context variable holding the set of active delay handles.
 * @internal
 */
const ActiveDelayIndicesKey = "activeDelayIndices";
function GetActiveDelaySet(context) {
    let set = context._getGlobalContextVariable(ActiveDelayIndicesKey, null);
    if (!set) {
        set = new Set();
        context._setGlobalContextVariable(ActiveDelayIndicesKey, set);
    }
    return set;
}
/**
 * Marks the given delay handle as active (scheduled and pending) in the context.
 * Called by `flow/setDelay` when it schedules a new delayed activation.
 * @param context the flow graph context owning the delay.
 * @param index the unique delay handle produced by `flow/setDelay`.
 */
function MarkDelayActive(context, index) {
    GetActiveDelaySet(context).add(index);
}
/**
 * Marks the given delay handle as no longer active. Called when a delay fires, is cancelled via
 * the `cancel` input, or is cancelled by `flow/cancelDelay`.
 * @param context the flow graph context owning the delay.
 * @param index the unique delay handle to clear.
 */
function MarkDelayInactive(context, index) {
    context._getGlobalContextVariable(ActiveDelayIndicesKey, null)?.delete(index);
}
/**
 * Returns whether the given delay handle is currently active, i.e. scheduled and not yet fired or
 * cancelled.
 * @param context the flow graph context to query.
 * @param index the delay handle to test.
 * @returns true if the delay is currently scheduled and has not yet fired or been cancelled.
 */
function IsDelayActive(context, index) {
    return context._getGlobalContextVariable(ActiveDelayIndicesKey, null)?.has(index) ?? false;
}

/**
 * Sentinel target returned by the validity accessors. The KHR_interactivity
 * ref-validity pointers are not backed by a glTF object, so the accessor uses a
 * non-null sentinel to satisfy callers that expect a truthy target.
 */
const RefValidityTarget = { isKhrInteractivityRef: true };
/**
 * Path-to-object converter that resolves the KHR_interactivity ref-validity
 * pointers `pointer/get` can query (KHR_interactivity spec §4.2.3 Event
 * References and §4.2.4 Delay References):
 *
 *  - `/extensions/KHR_interactivity/events/{}` — valid when the input reference
 *    was produced by an event operation (an event reference). Stateless: any
 *    event-reference path that reaches this converter is valid; a null ref is
 *    rejected earlier by the path-template substitution.
 *  - `/extensions/KHR_interactivity/delays/{}` — valid only while the referenced
 *    delay index is in the runtime active-delay set (i.e. the delay is scheduled
 *    and has not yet fired or been cancelled). This requires the runtime
 *    {@link FlowGraphContext}, which is supplied to the accessor `get` as its
 *    payload argument by `FlowGraphJsonPointerParserBlock`.
 *
 * On success `get` returns the input reference value (matching the spec, which
 * sets the `value` output to the input reference); on failure it returns
 * `undefined`, which the `pointer/get` block surfaces as `isValid = false`.
 */
class InteractivityRefPathToObjectConverter {
    /**
     * @param path the (template-substituted) JSON Pointer to resolve
     * @returns an object accessor whose `get` validates the reference
     */
    convert(path) {
        const normalized = path.endsWith("/") ? path.slice(0, -1) : path;
        if (normalized.startsWith(EventReferencePrefix)) {
            const key = normalized.substring(EventReferencePrefix.length);
            return {
                object: RefValidityTarget,
                info: {
                    type: "object",
                    isReadOnly: true,
                    // A non-empty key means a real event reference was supplied (the
                    // template substitution rejects null refs before we get here).
                    get: () => (key.length > 0 ? normalized : undefined),
                    getTarget: () => RefValidityTarget,
                },
            };
        }
        // Delay reference: the substituted segment is the integer delay index. parseInt is too lenient
        // (it would accept "1abc" as 1), so require an all-digits segment before converting.
        const rawIndex = normalized.substring(DelayReferencePrefix.length);
        const index = /^\d+$/.test(rawIndex) ? parseInt(rawIndex, 10) : NaN;
        return {
            object: RefValidityTarget,
            info: {
                type: "object",
                isReadOnly: true,
                get: (_target, _index, payload) => {
                    const context = payload;
                    if (!context || isNaN(index) || index < 0) {
                        return undefined;
                    }
                    return IsDelayActive(context, index) ? new FlowGraphInteger(index) : undefined;
                },
                getTarget: () => RefValidityTarget,
            },
        };
    }
}

/**
 * Path prefix of the KHR_interactivity asset-capability pointers (spec §4.1 Asset Capabilities).
 */
const InteractivityAssetCapabilitiesPrefix = "/extensions/KHR_interactivity/asset/";
/**
 * Path prefix of the KHR_interactivity runtime-limit pointers (spec §4.2 Implementation-Specific Runtime Limits).
 */
const InteractivityLimitsPrefix = "/extensions/KHR_interactivity/limits/";
/**
 * Highest glTF version this loader can present an asset with.
 */
const MaxSupportedGltfVersion = { major: 2, minor: 0 };
/**
 * The spec allows an implementation to report the maximum `int` value for a runtime limit it does not enforce or
 * does not want to disclose. Babylon imposes no hard cap on any of these features.
 */
const UndisclosedRuntimeLimit = 2147483647;
const RuntimeLimits = {
    maxActiveAnimations: UndisclosedRuntimeLimit,
    maxActiveDelays: UndisclosedRuntimeLimit,
    maxActivePropertyInterpolations: UndisclosedRuntimeLimit,
    maxActiveVariableInterpolations: UndisclosedRuntimeLimit,
};
/**
 * Sentinel target returned by the asset-capability accessors. These pointers are virtual and are not backed by a
 * glTF object, so the accessor uses a non-null sentinel to satisfy callers that expect a truthy target.
 */
const AssetCapabilityTarget = { isKhrInteractivityAssetCapability: true };
/**
 * Resolves the glTF version the asset is presented with: the minimum of the version declared in the glTF JSON and
 * the maximum version this implementation supports.
 * @param version the `asset.version` string from the glTF JSON
 * @returns the effective major and minor version components
 */
function GetEffectiveGltfVersion(version) {
    const [rawMajor, rawMinor] = (version ?? "").split(".");
    const major = parseInt(rawMajor, 10);
    const minor = parseInt(rawMinor, 10);
    if (isNaN(major)) {
        return MaxSupportedGltfVersion;
    }
    if (major !== MaxSupportedGltfVersion.major) {
        return major < MaxSupportedGltfVersion.major ? { major, minor: isNaN(minor) ? 0 : minor } : MaxSupportedGltfVersion;
    }
    return { major, minor: Math.min(isNaN(minor) ? 0 : minor, MaxSupportedGltfVersion.minor) };
}
/**
 * Path-to-object converter that resolves the virtual KHR_interactivity pointers describing the capabilities of the
 * asset and of the implementation running it:
 *
 *  - `/extensions/KHR_interactivity/asset/majorVersion` and `.../minorVersion` — the glTF version the asset is
 *    presented with.
 *  - `/extensions/KHR_interactivity/asset/extensions/<EXTENSION_NAME>/enabled` — whether the extension is both
 *    listed in `extensionsUsed` and supported by this loader. Reading an extension that is not used or not
 *    supported resolves successfully and yields `false`, so a behavior graph can branch on extension support.
 *  - `/extensions/KHR_interactivity/limits/<LIMIT_NAME>` — the implementation-specific runtime limits.
 *
 * All of these are read-only.
 */
class InteractivityAssetPathToObjectConverter {
    /**
     * @param _gltf the loaded glTF, used to read the asset version
     * @param _isExtensionEnabled predicate telling whether a glTF extension is both used by the asset and supported
     * by this loader
     */
    constructor(_gltf, _isExtensionEnabled) {
        this._gltf = _gltf;
        this._isExtensionEnabled = _isExtensionEnabled;
    }
    /**
     * @param path the JSON Pointer to resolve
     * @returns an object accessor for the addressed capability
     * @throws if the path does not address a known capability, which `pointer/get` surfaces as `isValid = false`
     */
    convert(path) {
        const normalized = path.endsWith("/") ? path.slice(0, -1) : path;
        if (normalized.startsWith(InteractivityLimitsPrefix)) {
            const limit = RuntimeLimits[normalized.substring(InteractivityLimitsPrefix.length)];
            if (limit === undefined) {
                throw new Error(`Path ${path} is invalid`);
            }
            return this._createAccessor("number", () => limit);
        }
        const capability = normalized.substring(InteractivityAssetCapabilitiesPrefix.length);
        if (capability === "majorVersion" || capability === "minorVersion") {
            return this._createAccessor("number", () => GetEffectiveGltfVersion(this._gltf.asset?.version)[capability === "majorVersion" ? "major" : "minor"]);
        }
        // `extensions/<EXTENSION_NAME>/enabled`. The extension name itself may not contain a slash.
        const segments = capability.split("/");
        if (segments.length === 3 && segments[0] === "extensions" && segments[2] === "enabled") {
            const extensionName = segments[1];
            return this._createAccessor("boolean", () => this._isExtensionEnabled(extensionName));
        }
        throw new Error(`Path ${path} is invalid`);
    }
    _createAccessor(type, get) {
        return {
            object: AssetCapabilityTarget,
            info: {
                type,
                isReadOnly: true,
                get,
                getTarget: () => AssetCapabilityTarget,
            },
        };
    }
}

/**
 * Supplies the KHR_interactivity representation of opaque `ref` values to the FlowGraph engine.
 *
 * The engine itself has no notion of the glTF object model: it asks this resolver how to represent
 * event sources and runtime objects as references, and how to read one back.
 */
class InteractivityHostResolver {
    /**
     * @param key the FlowGraph event source key
     * @returns the KHR_interactivity event reference
     */
    encodeEventReference(key) {
        return GetEventReference(key);
    }
    /**
     * @param reference the value to decode
     * @returns the FlowGraph event source key, or `undefined` when the value is not an event reference
     */
    decodeEventReference(reference) {
        return GetEventReferenceKey(reference);
    }
    /**
     * @param reference the reference to decode
     * @returns the index the reference denotes, or `undefined` when it is not an indexed JSON Pointer
     */
    decodeIndexReference(reference) {
        if (reference.length === 0 || reference[0] !== "/") {
            return undefined;
        }
        const tail = reference.substring(reference.lastIndexOf("/") + 1);
        // RFC 6901 array indices are unsigned decimal integers with no leading zeros, so reject
        // anything else rather than letting `Number` accept "0x2", "1e1" or " 3".
        if (!/^(0|[1-9]\d*)$/.test(tail)) {
            return undefined;
        }
        return parseInt(tail, 10);
    }
    /**
     * Maps a Babylon object loaded from the glTF back to a JSON Pointer addressing it.
     *
     * The glTF loader stamps `_internalMetadata.gltf.pointers` with one entry per JSON Pointer the
     * object can be addressed by; a single-primitive mesh, for example, holds both `/nodes/<i>` and
     * `/meshes/<j>/primitives/<k>`. The hint is the path segment preceding the template parameter
     * being resolved, so a template like `/nodes/{nodeRef}/globalMatrix` picks the `/nodes/<i>`
     * pointer even when another pointer was added to the object first.
     * @param object the Babylon object to address
     * @param hint the expected root segment of the pointer, when known
     * @returns the JSON Pointer for the object, or `undefined` when it is not addressable
     */
    getObjectReference(object, hint) {
        const pointers = object._internalMetadata?.gltf?.pointers;
        if (!Array.isArray(pointers)) {
            return undefined;
        }
        const stringPointers = pointers.filter((pointer) => typeof pointer === "string");
        if (stringPointers.length === 0) {
            return undefined;
        }
        if (hint) {
            const match = stringPointers.find((pointer) => pointer.split("/")[1] === hint);
            if (match) {
                return match;
            }
        }
        return stringPointers[0];
    }
}

const NAME = "KHR_interactivity";
/**
 * Loader extension for KHR_interactivity
 */
let KHR_interactivity$1 = class KHR_interactivity {
    /**
     * @internal
     * @param _loader
     */
    constructor(_loader) {
        this._loader = _loader;
        /**
         * The name of this extension.
         */
        this.name = NAME;
        this.enabled = this._loader.isExtensionUsed(NAME);
        this._gltfPathConverter = GetPathToObjectConverter(this._loader.gltf);
        const scene = _loader.babylonScene;
        if (this._gltfPathConverter) {
            // Build a composite that handles both:
            //   - The Babylon-scene namespace (`/extensions/BABYLON_scene_objects/...`),
            //     used by ref values that point at scene objects not described by the
            //     source glTF (e.g. refs emitted by engine-side event blocks).
            //   - The standard glTF object model (everything else), via the existing
            //     glTF converter as a fallback.
            const initialPrefixes = [];
            if (scene) {
                initialPrefixes.push({
                    prefix: BABYLON_SCENE_OBJECT_MODEL_PREFIX,
                    converter: new BabylonScenePathToObjectConverter(scene, CreateDefaultBabylonSceneObjectModelTree()),
                });
            }
            // KHR_interactivity ref-validity pointers (`/extensions/KHR_interactivity/events/{}`
            // and `/extensions/KHR_interactivity/delays/{}`) are virtual: they validate an opaque
            // event/delay reference rather than addressing a glTF object, so route them to a
            // dedicated converter instead of the glTF fallback.
            const refConverter = new InteractivityRefPathToObjectConverter();
            initialPrefixes.push({ prefix: EventReferencePrefix, converter: refConverter });
            initialPrefixes.push({ prefix: DelayReferencePrefix, converter: refConverter });
            // Asset capabilities and runtime limits (`/extensions/KHR_interactivity/asset/...` and
            // `/extensions/KHR_interactivity/limits/...`) are virtual too: they describe the asset and the
            // implementation running it rather than addressing a glTF object. The set of enabled extensions is
            // resolved eagerly because the loader is released once loading completes, while the behavior graph
            // keeps querying these pointers at runtime.
            const enabledExtensions = new Set((this._loader.gltf.extensionsUsed ?? []).filter((name) => registeredGLTFExtensions.has(name) && this._loader.parent.extensionOptions[name]?.enabled !== false));
            const assetConverter = new InteractivityAssetPathToObjectConverter(this._loader.gltf, (extensionName) => enabledExtensions.has(extensionName));
            initialPrefixes.push({ prefix: InteractivityAssetCapabilitiesPrefix, converter: assetConverter });
            initialPrefixes.push({ prefix: InteractivityLimitsPrefix, converter: assetConverter });
            this._pathConverter = new CompositePathToObjectConverter(initialPrefixes, this._gltfPathConverter);
        }
        // avoid starting animations automatically.
        _loader._skipStartAnimationStep = true;
        // Update object model with new pointers
        if (scene) {
            _AddInteractivityObjectModel(scene);
        }
    }
    dispose() {
        this._loader = null;
        delete this._gltfPathConverter;
        delete this._pathConverter;
    }
    // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/no-misused-promises
    async onReady() {
        if (!this._loader.babylonScene || !this._pathConverter) {
            return;
        }
        const scene = this._loader.babylonScene;
        const interactivityDefinition = this._loader.gltf.extensions?.KHR_interactivity;
        if (!interactivityDefinition) {
            // This can technically throw, but it's not a critical error
            return;
        }
        // The specification requires an invalid behavior graph to be rejected. Parse each graph into its
        // own coordinator so a graph that throws part-way can be disposed without leaving a half-built graph
        // registered — a shared coordinator's start() would otherwise run that partial graph. A scene
        // supports many coordinators, and glTF behavior graphs are independent of one another.
        await Promise.all(interactivityDefinition.graphs.map(async (graph, index) => {
            const coordinator = new FlowGraphCoordinator({ scene, hostResolver: new InteractivityHostResolver() });
            coordinator.dispatchEventsSynchronously = false; // glTF interactivity dispatches events asynchronously
            try {
                const parser = new InteractivityGraphToFlowGraphParser(graph, this._loader.gltf, this._loader.parent.targetFps);
                await ParseFlowGraphAsync(parser.serializeToFlowGraph(), { coordinator, pathConverter: this._pathConverter });
                // Only start graphs that parsed cleanly; keep loading the rest of the asset either way.
                coordinator.start();
            }
            catch (error) {
                Logger.Error(`KHR_interactivity: rejecting behavior graph #${index}: ${error?.message ?? error}`);
                // Dispose the coordinator (and the partially-built graph it holds) so nothing from the
                // rejected graph stays registered or running.
                coordinator.dispose();
            }
        }));
    }
};
/**
 * @internal
 * populates the object model with the interactivity extension
 */
function _AddInteractivityObjectModel(scene) {
    // Note - all of those are read-only, as per the specs!
    // active camera rotation
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/rotation", {
        get: () => {
            if (!scene.activeCamera) {
                return new Quaternion(NaN, NaN, NaN, NaN);
            }
            const quat = Quaternion.FromRotationMatrix(scene.activeCamera.getWorldMatrix()).normalize();
            if (!scene.useRightHandedSystem) {
                quat.w *= -1; // glTF uses right-handed system, while babylon uses left-handed
                quat.x *= -1; // glTF uses right-handed system, while babylon uses left-handed
            }
            return quat;
        },
        type: "Quaternion",
        getTarget: () => scene.activeCamera,
    });
    // activeCamera position
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/position", {
        get: () => {
            if (!scene.activeCamera) {
                return new Vector3(NaN, NaN, NaN);
            }
            const pos = scene.activeCamera.getWorldMatrix().getTranslation(); // not global position
            if (!scene.useRightHandedSystem) {
                pos.x *= -1; // glTF uses right-handed system, while babylon uses left-handed
            }
            return pos;
        },
        type: "Vector3",
        getTarget: () => scene.activeCamera,
    });
    // activeCamera projection properties. Per the spec these read-only values are NaN when there is no
    // active camera, or when the active camera does not use the projection type of the requested pointer
    // (all perspective properties are NaN for an orthographic camera, and vice-versa).
    const getActivePerspectiveValue = (compute) => {
        const camera = scene.activeCamera;
        if (!camera || camera.mode === Constants.ORTHOGRAPHIC_CAMERA) {
            return NaN;
        }
        return compute(camera);
    };
    const getActiveOrthographicValue = (compute) => {
        const camera = scene.activeCamera;
        if (!camera || camera.mode !== Constants.ORTHOGRAPHIC_CAMERA) {
            return NaN;
        }
        return compute(camera);
    };
    // perspective/aspectRatio (width over height)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/perspective/aspectRatio", {
        get: () => getActivePerspectiveValue((camera) => camera.getEngine().getAspectRatio(camera)),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // perspective/yfov (vertical field of view, in radians)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/perspective/yfov", {
        get: () => getActivePerspectiveValue((camera) => {
            // Babylon stores the vertical fov when fovMode is vertical-fixed (the default and what the glTF
            // loader sets). For a horizontal-fixed camera, convert the horizontal fov to vertical.
            if (camera.fovMode === Constants.FOVMODE_VERTICAL_FIXED) {
                return camera.fov;
            }
            const aspectRatio = camera.getEngine().getAspectRatio(camera);
            return aspectRatio ? 2 * Math.atan(Math.tan(camera.fov / 2) / aspectRatio) : camera.fov;
        }),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // perspective/znear (distance to the near clipping plane)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/perspective/znear", {
        get: () => getActivePerspectiveValue((camera) => camera.minZ),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // perspective/zfar (distance to the far clipping plane; Babylon uses maxZ === 0 to mean an infinite far plane)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/perspective/zfar", {
        get: () => getActivePerspectiveValue((camera) => (camera.maxZ === 0 ? Infinity : camera.maxZ)),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // orthographic/xmag (half the orthographic width)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/orthographic/xmag", {
        get: () => getActiveOrthographicValue((camera) => {
            const halfWidth = camera.getEngine().getRenderWidth() / 2;
            return ((camera.orthoRight ?? halfWidth) - (camera.orthoLeft ?? -halfWidth)) / 2;
        }),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // orthographic/ymag (half the orthographic height)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/orthographic/ymag", {
        get: () => getActiveOrthographicValue((camera) => {
            const halfHeight = camera.getEngine().getRenderHeight() / 2;
            return ((camera.orthoTop ?? halfHeight) - (camera.orthoBottom ?? -halfHeight)) / 2;
        }),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // orthographic/znear (distance to the near clipping plane)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/orthographic/znear", {
        get: () => getActiveOrthographicValue((camera) => camera.minZ),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // orthographic/zfar (distance to the far clipping plane)
    AddObjectAccessorToKey("/extensions/KHR_interactivity/?/activeCamera/orthographic/zfar", {
        get: () => getActiveOrthographicValue((camera) => camera.maxZ),
        type: "number",
        getTarget: () => scene.activeCamera,
    });
    // /animations/{} pointers:
    AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/isPlaying", {
        get: (animation) => {
            return animation._babylonAnimationGroup?.isPlaying ?? false;
        },
        type: "boolean",
        getTarget: (animation) => {
            return animation._babylonAnimationGroup;
        },
    });
    AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/minTime", {
        get: (animation) => {
            return (animation._babylonAnimationGroup?.from ?? 0) / 60; // fixed factor for duration-to-frames conversion
        },
        type: "number",
        getTarget: (animation) => {
            return animation._babylonAnimationGroup;
        },
    });
    AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/maxTime", {
        get: (animation) => {
            return (animation._babylonAnimationGroup?.to ?? 0) / 60; // fixed factor for duration-to-frames conversion
        },
        type: "number",
        getTarget: (animation) => {
            return animation._babylonAnimationGroup;
        },
    });
    // playhead
    AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/playhead", {
        get: (animation) => {
            return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion
        },
        type: "number",
        getTarget: (animation) => {
            return animation._babylonAnimationGroup;
        },
    });
    //virtualPlayhead - TODO, do we support this property in our animations? getCurrentFrame  is the only method we have for this.
    AddObjectAccessorToKey("/animations/{}/extensions/KHR_interactivity/virtualPlayhead", {
        get: (animation) => {
            return (animation._babylonAnimationGroup?.getCurrentFrame() ?? 0) / 60; // fixed factor for duration-to-frames conversion
        },
        type: "number",
        getTarget: (animation) => {
            return animation._babylonAnimationGroup;
        },
    });
}
// Register flow graph blocks. Do it here so they are available when the extension is enabled.
let _Registered = false;
/**
 * Registers the KHR_interactivity glTF loader extension.
 * Safe to call multiple times; only the first call has an effect.
 */
// eslint-disable-next-line @typescript-eslint/naming-convention
function RegisterKHR_interactivity() {
    if (_Registered) {
        return;
    }
    _Registered = true;
    addToBlockFactory(NAME, "FlowGraphGLTFDataProvider", async () => {
        return (await import('./flowGraphGLTFDataProvider-D-r5Orir.esm.js')).FlowGraphGLTFDataProvider;
    });
    unregisterGLTFExtension(NAME);
    registerGLTFExtension(NAME, true, (loader) => new KHR_interactivity$1(loader));
}

/**
 * Re-exports the pure implementation and applies the runtime registration side effect.
 * Import "./KHR_interactivity.pure" for tree-shakeable, side-effect-free usage.
 */
RegisterKHR_interactivity();

var KHR_interactivity = /*#__PURE__*/Object.freeze({
    __proto__: null,
    KHR_interactivity: KHR_interactivity$1,
    RegisterKHR_interactivity: RegisterKHR_interactivity,
    _AddInteractivityObjectModel: _AddInteractivityObjectModel
});

export { FlowGraphBlock as F, GetFlowGraphAssetWithType as G, IsDelayActive as I, KHR_interactivity as K, MarkDelayInactive as M, _IsMacPlatform as _, FlowGraphEventBlock as a, FlowGraphAsyncExecutionBlock as b, FlowGraphExecutionBlockWithOutSignal as c, FlowGraphCoordinator as d, _IsDescendantOf as e, FlowGraphExecutionBlock as f, getNumericValue as g, defaultValueSerializationFunction as h, isNumeric as i, _GetClassNameOf as j, _AreSameVectorOrQuaternionClass as k, _AreSameMatrixClass as l, _AreSameIntegerClass as m, MarkDelayActive as n };
//# sourceMappingURL=KHR_interactivity-CnR665Qq.esm.js.map