'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var util = require('@firebase/util');
var logger$1 = require('@firebase/logger');
var app = require('@firebase/app');
var component = require('@firebase/component');

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const Code = {
    OTHER: 'other',
    ALREADY_INITIALIZED: 'already-initialized',
    NOT_INITIALIZED: 'not-initialized',
    NOT_SUPPORTED: 'not-supported',
    INVALID_ARGUMENT: 'invalid-argument',
    PARTIAL_ERROR: 'partial-error',
    UNAUTHORIZED: 'unauthorized'
};
/** An error returned by a DataConnect operation. */
class DataConnectError extends util.FirebaseError {
    constructor(code, message) {
        super(code, message);
        /** @internal */
        this.name = 'DataConnectError';
        // Ensure the instanceof operator works as expected on subclasses of Error.
        // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types
        // and https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
        Object.setPrototypeOf(this, DataConnectError.prototype);
    }
    /** @internal */
    toString() {
        return `${this.name}[code=${this.code}]: ${this.message}`;
    }
}
/** An error returned by a DataConnect operation. */
class DataConnectOperationError extends DataConnectError {
    /** @hideconstructor */
    constructor(message, response) {
        super(Code.PARTIAL_ERROR, message);
        /** @internal */
        this.name = 'DataConnectOperationError';
        this.response = response;
    }
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/** The semver (www.semver.org) version of the SDK. */
let SDK_VERSION = '';
/**
 * SDK_VERSION should be set before any database instance is created
 * @internal
 */
function setSDKVersion(version) {
    SDK_VERSION = version;
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const logger = new logger$1.Logger('@firebase/data-connect');
function setLogLevel(logLevel) {
    logger.setLogLevel(logLevel);
}
function logDebug(msg) {
    logger.debug(`DataConnect (${SDK_VERSION}): ${msg}`);
}
function logError(msg) {
    logger.error(`DataConnect (${SDK_VERSION}): ${msg}`);
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const CallerSdkTypeEnum = {
    Base: 'Base', // Core JS SDK
    Generated: 'Generated', // Generated JS SDK
    TanstackReactCore: 'TanstackReactCore', // Tanstack non-generated React SDK
    GeneratedReact: 'GeneratedReact', // Tanstack non-generated Angular SDK
    TanstackAngularCore: 'TanstackAngularCore', // Tanstack non-generated Angular SDK
    GeneratedAngular: 'GeneratedAngular' // Generated Angular SDK
};

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
let connectFetch = globalThis.fetch;
function initializeFetch(fetchImpl) {
    connectFetch = fetchImpl;
}
function getGoogApiClientValue(_isUsingGen, _callerSdkType) {
    let str = 'gl-js/ fire/' + SDK_VERSION;
    if (_callerSdkType !== CallerSdkTypeEnum.Base &&
        _callerSdkType !== CallerSdkTypeEnum.Generated) {
        str += ' js/' + _callerSdkType.toLowerCase();
    }
    else if (_isUsingGen || _callerSdkType === CallerSdkTypeEnum.Generated) {
        str += ' js/gen';
    }
    return str;
}
async function dcFetch(url, body, { signal }, appId, accessToken, appCheckToken, _isUsingGen, _callerSdkType, _isUsingEmulator) {
    if (!connectFetch) {
        throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
    }
    const headers = {
        'Content-Type': 'application/json',
        'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen, _callerSdkType)
    };
    if (accessToken) {
        headers['X-Firebase-Auth-Token'] = accessToken;
    }
    if (appId) {
        headers['x-firebase-gmpid'] = appId;
    }
    if (appCheckToken) {
        headers['X-Firebase-AppCheck'] = appCheckToken;
    }
    const bodyStr = JSON.stringify(body);
    const fetchOptions = {
        body: bodyStr,
        method: 'POST',
        headers,
        signal
    };
    if (util.isCloudWorkstation(url) && _isUsingEmulator) {
        fetchOptions.credentials = 'include';
    }
    let response;
    try {
        response = await connectFetch(url, fetchOptions);
    }
    catch (err) {
        throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
    }
    let jsonResponse;
    try {
        jsonResponse = await response.json();
    }
    catch (e) {
        throw new DataConnectError(Code.OTHER, JSON.stringify(e));
    }
    const message = getErrorMessage(jsonResponse);
    if (response.status >= 400) {
        logError('Error while performing request: ' + JSON.stringify(jsonResponse));
        if (response.status === 401) {
            throw new DataConnectError(Code.UNAUTHORIZED, message);
        }
        throw new DataConnectError(Code.OTHER, message);
    }
    if (jsonResponse.errors && jsonResponse.errors.length) {
        const stringified = JSON.stringify(jsonResponse.errors);
        const failureResponse = {
            errors: jsonResponse.errors,
            data: jsonResponse.data
        };
        throw new DataConnectOperationError('DataConnect error while performing request: ' + stringified, failureResponse);
    }
    if (!jsonResponse.extensions) {
        jsonResponse.extensions = {
            dataConnect: []
        };
    }
    return jsonResponse;
}
function getErrorMessage(obj) {
    if ('message' in obj && obj.message) {
        return obj.message;
    }
    return JSON.stringify(obj);
}

const name = "@firebase/data-connect";
const version = "0.4.0";

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class EntityDataObject {
    getServerValue(key) {
        return this.serverValues[key];
    }
    constructor(globalID) {
        this.globalID = globalID;
        this.serverValues = {};
        this.referencedFrom = new Set();
    }
    getServerValues() {
        return this.serverValues;
    }
    toJSON() {
        return {
            globalID: this.globalID,
            map: this.serverValues,
            referencedFrom: Array.from(this.referencedFrom)
        };
    }
    static fromJSON(json) {
        const edo = new EntityDataObject(json.globalID);
        edo.serverValues = json.map;
        edo.referencedFrom = new Set(json.referencedFrom);
        return edo;
    }
    updateServerValue(key, value, requestedFrom) {
        this.serverValues[key] = value;
        this.referencedFrom.add(requestedFrom);
        return Array.from(this.referencedFrom);
    }
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class InMemoryCacheProvider {
    constructor(_keyId) {
        this._keyId = _keyId;
        this.edos = new Map();
        this.resultTrees = new Map();
    }
    async setResultTree(queryId, rt) {
        this.resultTrees.set(queryId, rt);
    }
    async getResultTree(queryId) {
        return this.resultTrees.get(queryId);
    }
    async updateEntityData(entityData) {
        this.edos.set(entityData.globalID, entityData);
    }
    async getEntityData(globalId) {
        if (!this.edos.has(globalId)) {
            this.edos.set(globalId, new EntityDataObject(globalId));
        }
        // Because of the above, we can guarantee that there will be an EDO at the globalId.
        return this.edos.get(globalId);
    }
    close() {
        // No-op
        return Promise.resolve();
    }
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const GLOBAL_ID_KEY = '_id';
const OBJECT_LISTS_KEY = '_objectLists';
const REFERENCES_KEY = '_references';
const SCALARS_KEY = '_scalars';
const ENTITY_DATA_KEYS_KEY = '_entity_data_keys';
class EntityNode {
    constructor() {
        this.scalars = {};
        this.references = {};
        this.objectLists = {};
        this.entityDataKeys = new Set();
    }
    async loadData(queryId, values, entityIds, acc, cacheProvider) {
        if (values === undefined) {
            return;
        }
        if (typeof values !== 'object' || Array.isArray(values)) {
            throw new DataConnectError(Code.INVALID_ARGUMENT, 'EntityNode initialized with non-object value');
        }
        if (values === null) {
            return;
        }
        if (typeof values === 'object' &&
            entityIds &&
            entityIds[GLOBAL_ID_KEY] &&
            typeof entityIds[GLOBAL_ID_KEY] === 'string') {
            this.globalId = entityIds[GLOBAL_ID_KEY];
            this.entityData = await cacheProvider.getEntityData(this.globalId);
        }
        for (const key in values) {
            if (values.hasOwnProperty(key)) {
                if (typeof values[key] === 'object') {
                    if (Array.isArray(values[key])) {
                        const ids = entityIds && entityIds[key];
                        const objArray = [];
                        const scalarArray = [];
                        for (const [index, value] of values[key].entries()) {
                            if (typeof value === 'object') {
                                if (Array.isArray(value)) ;
                                else {
                                    const entityNode = new EntityNode();
                                    await entityNode.loadData(queryId, value, ids && ids[index], acc, cacheProvider);
                                    objArray.push(entityNode);
                                }
                            }
                            else {
                                scalarArray.push(value);
                            }
                        }
                        if (scalarArray.length > 0 && objArray.length > 0) {
                            this.scalars[key] = values[key];
                        }
                        else if (scalarArray.length > 0) {
                            if (this.entityData) {
                                const impactedRefs = this.entityData.updateServerValue(key, scalarArray, queryId);
                                this.entityDataKeys.add(key);
                                acc.add(impactedRefs);
                            }
                            else {
                                this.scalars[key] = scalarArray;
                            }
                        }
                        else if (objArray.length > 0) {
                            this.objectLists[key] = objArray;
                        }
                        else {
                            this.scalars[key] = [];
                        }
                    }
                    else {
                        if (values[key] === null) {
                            this.scalars[key] = null;
                            continue;
                        }
                        const entityNode = new EntityNode();
                        // TODO: Load Data might need to be pushed into ResultTreeProcessor instead.
                        await entityNode.loadData(queryId, values[key], entityIds && entityIds[key], acc, cacheProvider);
                        this.references[key] = entityNode;
                    }
                }
                else {
                    if (this.entityData) {
                        const impactedRefs = this.entityData.updateServerValue(key, values[key], queryId);
                        this.entityDataKeys.add(key);
                        acc.add(impactedRefs);
                    }
                    else {
                        this.scalars[key] = values[key];
                    }
                }
            }
        }
        if (this.entityData) {
            await cacheProvider.updateEntityData(this.entityData);
        }
    }
    toJSON(mode) {
        const resultObject = {};
        if (mode === EncodingMode.hydrated) {
            if (this.entityData) {
                for (const key of this.entityDataKeys) {
                    resultObject[key] = this.entityData.getServerValue(key);
                }
            }
            if (this.scalars) {
                Object.assign(resultObject, this.scalars);
            }
            if (this.references) {
                for (const key in this.references) {
                    if (this.references.hasOwnProperty(key)) {
                        resultObject[key] = this.references[key].toJSON(mode);
                    }
                }
            }
            if (this.objectLists) {
                for (const key in this.objectLists) {
                    if (this.objectLists.hasOwnProperty(key)) {
                        resultObject[key] = this.objectLists[key].map(obj => obj.toJSON(mode));
                    }
                }
            }
            return resultObject;
        }
        else {
            // Get JSON representation of dehydrated list
            if (this.entityData) {
                resultObject[GLOBAL_ID_KEY] = this.entityData.globalID;
            }
            resultObject[ENTITY_DATA_KEYS_KEY] = Array.from(this.entityDataKeys);
            if (this.scalars) {
                resultObject[SCALARS_KEY] = this.scalars;
            }
            if (this.references) {
                const references = {};
                for (const key in this.references) {
                    if (this.references.hasOwnProperty(key)) {
                        references[key] = this.references[key].toJSON(mode);
                    }
                }
                resultObject[REFERENCES_KEY] = references;
            }
            if (this.objectLists) {
                const objectLists = {};
                for (const key in this.objectLists) {
                    if (this.objectLists.hasOwnProperty(key)) {
                        objectLists[key] = this.objectLists[key].map(obj => obj.toJSON(mode));
                    }
                }
                resultObject[OBJECT_LISTS_KEY] = objectLists;
            }
        }
        return resultObject;
    }
    static fromJson(obj) {
        const sdo = new EntityNode();
        if (obj.backingData) {
            sdo.entityData = EntityDataObject.fromJSON(obj.backingData);
        }
        sdo.globalId = obj.globalID;
        sdo.scalars = obj.scalars;
        if (obj.references) {
            const references = {};
            for (const key in obj.references) {
                if (obj.references.hasOwnProperty(key)) {
                    references[key] = EntityNode.fromJson(obj.references[key]);
                }
            }
            sdo.references = references;
        }
        if (obj.objectLists) {
            const objectLists = {};
            for (const key in obj.objectLists) {
                if (obj.objectLists.hasOwnProperty(key)) {
                    objectLists[key] = obj.objectLists[key].map(obj => EntityNode.fromJson(obj));
                }
            }
            sdo.objectLists = objectLists;
        }
        return sdo;
    }
}
// Helpful for storing in persistent cache, which is not available yet.
var EncodingMode;
(function (EncodingMode) {
    EncodingMode[EncodingMode["hydrated"] = 0] = "hydrated";
    EncodingMode[EncodingMode["dehydrated"] = 1] = "dehydrated";
})(EncodingMode || (EncodingMode = {}));

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class ResultTree {
    /**
     * Create a {@link ResultTree} from a dehydrated JSON object.
     * @param value The dehydrated JSON object.
     * @returns The {@link ResultTree}.
     */
    static fromJson(value) {
        return new ResultTree(EntityNode.fromJson(value.rootStub), value.maxAge, value.cachedAt, value.lastAccessed);
    }
    constructor(rootStub, maxAge = 0, cachedAt, _lastAccessed) {
        this.rootStub = rootStub;
        this.maxAge = maxAge;
        this.cachedAt = cachedAt;
        this._lastAccessed = _lastAccessed;
    }
    isStale() {
        return (Date.now() - new Date(this.cachedAt.getTime()).getTime() >
            this.maxAge * 1000);
    }
    updateMaxAge(maxAgeInSeconds) {
        this.maxAge = maxAgeInSeconds;
    }
    updateAccessed() {
        this._lastAccessed = new Date();
    }
    get lastAccessed() {
        return this._lastAccessed;
    }
    getRootStub() {
        return this.rootStub;
    }
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class ImpactedQueryRefsAccumulator {
    constructor(queryId) {
        this.queryId = queryId;
        this.impacted = new Set();
    }
    add(impacted) {
        impacted
            .filter(ref => ref !== this.queryId)
            .forEach(ref => this.impacted.add(ref));
    }
    consumeEvents() {
        const events = Array.from(this.impacted);
        this.impacted.clear();
        return events;
    }
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class ResultTreeProcessor {
    /**
     * Hydrate the EntityNode into a JSON object so that it can be returned to the user.
     * @param rootStubObject
     * @returns {string}
     */
    hydrateResults(rootStubObject) {
        return rootStubObject.toJSON(EncodingMode.hydrated);
    }
    /**
     * Dehydrate results so that they can be stored in the cache.
     * @param json
     * @param entityIds
     * @param cacheProvider
     * @param queryId
     * @returns {Promise<DehydratedResults>}
     */
    async dehydrateResults(json, entityIds, cacheProvider, queryId) {
        const acc = new ImpactedQueryRefsAccumulator(queryId);
        const entityNode = new EntityNode();
        await entityNode.loadData(queryId, json, entityIds, acc, cacheProvider);
        return {
            entityNode,
            impacted: acc.consumeEvents()
        };
    }
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class DataConnectCache {
    constructor(authProvider, projectId, connectorConfig, host, cacheSettings) {
        this.authProvider = authProvider;
        this.projectId = projectId;
        this.connectorConfig = connectorConfig;
        this.host = host;
        this.cacheSettings = cacheSettings;
        this.cacheProvider = null;
        this.uid = null;
        this.authProvider.addTokenChangeListener(async (_) => {
            const newUid = this.authProvider.getAuth().getUid();
            // We should only close if the token changes and so does the new UID
            if (this.uid !== newUid) {
                this.cacheProvider?.close();
                this.uid = newUid;
                const identifier = await this.getIdentifier(this.uid);
                this.cacheProvider = this.initializeNewProviders(identifier);
            }
        });
    }
    async initialize() {
        if (!this.cacheProvider) {
            const identifier = await this.getIdentifier(this.uid);
            this.cacheProvider = this.initializeNewProviders(identifier);
        }
    }
    async getIdentifier(uid) {
        const identifier = `${'memory' // TODO: replace this with indexeddb when persistence is available.
        }-${this.projectId}-${this.connectorConfig.service}-${this.connectorConfig.connector}-${this.connectorConfig.location}-${uid}-${this.host}`;
        const sha256 = await util.generateSHA256Hash(identifier);
        return sha256;
    }
    initializeNewProviders(identifier) {
        return this.cacheSettings.cacheProvider.initialize(identifier);
    }
    async containsResultTree(queryId) {
        await this.initialize();
        const resultTree = await this.cacheProvider.getResultTree(queryId);
        return resultTree !== undefined;
    }
    async getResultTree(queryId) {
        await this.initialize();
        return this.cacheProvider.getResultTree(queryId);
    }
    async getResultJSON(queryId) {
        await this.initialize();
        const processor = new ResultTreeProcessor();
        const cacheProvider = this.cacheProvider;
        const resultTree = await cacheProvider.getResultTree(queryId);
        if (!resultTree) {
            throw new DataConnectError(Code.INVALID_ARGUMENT, `${queryId} not found in cache. Call "update()" first.`);
        }
        return processor.hydrateResults(resultTree.getRootStub());
    }
    async update(queryId, serverValues, entityIds) {
        await this.initialize();
        const processor = new ResultTreeProcessor();
        const cacheProvider = this.cacheProvider;
        const { entityNode: stubDataObject, impacted } = await processor.dehydrateResults(serverValues, entityIds, cacheProvider, queryId);
        const now = new Date();
        await cacheProvider.setResultTree(queryId, new ResultTree(stubDataObject, serverValues.maxAge || this.cacheSettings.maxAgeSeconds, now, now));
        return impacted;
    }
}
class MemoryStub {
    constructor() {
        this.type = 'MEMORY';
    }
    /**
     * @internal
     */
    initialize(cacheId) {
        return new InMemoryCacheProvider(cacheId);
    }
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @internal
 * Abstraction around AppCheck's token fetching capabilities.
 */
class AppCheckTokenProvider {
    constructor(app$1, appCheckProvider) {
        this.appCheckProvider = appCheckProvider;
        if (app._isFirebaseServerApp(app$1) && app$1.settings.appCheckToken) {
            this.serverAppAppCheckToken = app$1.settings.appCheckToken;
        }
        this.appCheck = appCheckProvider?.getImmediate({ optional: true });
        if (!this.appCheck) {
            void appCheckProvider
                ?.get()
                .then(appCheck => (this.appCheck = appCheck))
                .catch();
        }
    }
    getToken() {
        if (this.serverAppAppCheckToken) {
            return Promise.resolve({ token: this.serverAppAppCheckToken });
        }
        if (!this.appCheck) {
            return new Promise((resolve, reject) => {
                // Support delayed initialization of FirebaseAppCheck. This allows our
                // customers to initialize the RTDB SDK before initializing Firebase
                // AppCheck and ensures that all requests are authenticated if a token
                // becomes available before the timoeout below expires.
                setTimeout(() => {
                    if (this.appCheck) {
                        this.getToken().then(resolve, reject);
                    }
                    else {
                        resolve(null);
                    }
                }, 0);
            });
        }
        return this.appCheck.getToken();
    }
    addTokenChangeListener(listener) {
        void this.appCheckProvider
            ?.get()
            .then(appCheck => appCheck.addTokenListener(listener));
    }
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// @internal
class FirebaseAuthProvider {
    constructor(_appName, _options, _authProvider) {
        this._appName = _appName;
        this._options = _options;
        this._authProvider = _authProvider;
        this._auth = _authProvider.getImmediate({ optional: true });
        if (!this._auth) {
            _authProvider.onInit(auth => (this._auth = auth));
        }
    }
    getAuth() {
        return this._auth;
    }
    getToken(forceRefresh) {
        if (!this._auth) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if (this._auth) {
                        this.getToken(forceRefresh).then(resolve, reject);
                    }
                    else {
                        resolve(null);
                    }
                }, 0);
            });
        }
        return this._auth.getToken(forceRefresh).catch(error => {
            if (error && error.code === 'auth/token-not-initialized') {
                logDebug('Got auth/token-not-initialized error.  Treating as null token.');
                return null;
            }
            else {
                logError('Error received when attempting to retrieve token: ' +
                    JSON.stringify(error));
                return Promise.reject(error);
            }
        });
    }
    addTokenChangeListener(listener) {
        this._auth?.addAuthTokenListener(listener);
    }
    removeTokenChangeListener(listener) {
        this._authProvider
            .get()
            .then(auth => auth.removeAuthTokenListener(listener))
            .catch(err => logError(err));
    }
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const QUERY_STR = 'query';
const MUTATION_STR = 'mutation';
const SOURCE_SERVER = 'SERVER';
const SOURCE_CACHE = 'CACHE';

/**
 * @license
 * Copyright 2026 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function parseEntityIds(result) {
    // Iterate through extensions.dataConnect
    const dataConnectExtensions = result.extensions?.dataConnect;
    const dataCopy = Object.assign(result);
    if (!dataConnectExtensions) {
        return dataCopy;
    }
    const ret = {};
    for (const extension of dataConnectExtensions) {
        const { path } = extension;
        populatePath(path, ret, extension);
    }
    return ret;
}
// mutates the object to update the path
function populatePath(path, toUpdate, extension) {
    let curObj = toUpdate;
    for (const slice of path) {
        if (typeof curObj[slice] !== 'object') {
            curObj[slice] = {};
        }
        curObj = curObj[slice];
    }
    if ('entityId' in extension && extension.entityId) {
        curObj['_id'] = extension.entityId;
    }
    else if ('entityIds' in extension) {
        const entityArr = extension.entityIds;
        for (let i = 0; i < entityArr.length; i++) {
            const entityId = entityArr[i];
            if (typeof curObj[i] === 'undefined') {
                curObj[i] = {};
            }
            curObj[i]._id = entityId;
        }
    }
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
let encoderImpl;
let decoderImpl;
function setEncoder(encoder) {
    encoderImpl = encoder;
}
function setDecoder(decoder) {
    decoderImpl = decoder;
}
function sortKeysForObj(o) {
    return Object.keys(o)
        .sort()
        .reduce((accumulator, currentKey) => {
        accumulator[currentKey] = o[currentKey];
        return accumulator;
    }, {});
}
setEncoder((o) => JSON.stringify(sortKeysForObj(o)));
setDecoder(s => sortKeysForObj(JSON.parse(s)));

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function getRefSerializer(queryRef, data, source, fetchTime) {
    return function toJSON() {
        return {
            data,
            refInfo: {
                name: queryRef.name,
                variables: queryRef.variables,
                connectorConfig: {
                    projectId: queryRef.dataConnect.app.options.projectId,
                    ...queryRef.dataConnect.getSettings()
                }
            },
            fetchTime,
            source
        };
    };
}
class QueryManager {
    async preferCacheResults(queryRef, allowStale = false) {
        let cacheResult;
        try {
            cacheResult = await this.fetchCacheResults(queryRef, allowStale);
        }
        catch (e) {
            // Ignore the error and try to fetch from the server.
        }
        if (cacheResult) {
            return cacheResult;
        }
        return this.fetchServerResults(queryRef);
    }
    constructor(transport, dc, cache) {
        this.transport = transport;
        this.dc = dc;
        this.cache = cache;
        this.callbacks = new Map();
        this.subscriptionCache = new Map();
        this.queue = [];
    }
    async waitForQueuedWrites() {
        for (const promise of this.queue) {
            await promise;
        }
        this.queue = [];
    }
    updateSSR(updatedData) {
        this.queue.push(this.updateCache(updatedData).then(async (result) => this.publishCacheResultsToSubscribers(result, updatedData.fetchTime)));
    }
    async updateCache(result, extensions) {
        await this.waitForQueuedWrites();
        if (this.cache) {
            const entityIds = parseEntityIds(result);
            const updatedMaxAge = getMaxAgeFromExtensions(extensions);
            if (updatedMaxAge !== undefined) {
                this.cache.cacheSettings.maxAgeSeconds = updatedMaxAge;
            }
            return this.cache.update(encoderImpl({
                name: result.ref.name,
                variables: result.ref.variables,
                refType: QUERY_STR
            }), result.data, entityIds);
        }
        else {
            const key = encoderImpl({
                name: result.ref.name,
                variables: result.ref.variables,
                refType: QUERY_STR
            });
            this.subscriptionCache.set(key, result);
            return [key];
        }
    }
    addSubscription(queryRef, onResultCallback, onCompleteCallback, onErrorCallback, initialCache) {
        const key = encoderImpl({
            name: queryRef.name,
            variables: queryRef.variables,
            refType: QUERY_STR
        });
        const unsubscribe = () => {
            if (this.callbacks.has(key)) {
                const callbackList = this.callbacks.get(key);
                this.callbacks.set(key, callbackList.filter(callback => callback !== subscription));
                onCompleteCallback?.();
            }
        };
        const subscription = {
            userCallback: onResultCallback,
            errCallback: onErrorCallback,
            unsubscribe
        };
        if (initialCache) {
            this.updateSSR(initialCache);
        }
        const promise = this.preferCacheResults(queryRef, /*allowStale=*/ true);
        // We want to ignore the error and let subscriptions handle it
        promise.then(undefined, err => { });
        if (!this.callbacks.has(key)) {
            this.callbacks.set(key, []);
        }
        this.callbacks
            .get(key)
            .push(subscription);
        return unsubscribe;
    }
    async fetchServerResults(queryRef) {
        await this.waitForQueuedWrites();
        const key = encoderImpl({
            name: queryRef.name,
            variables: queryRef.variables,
            refType: QUERY_STR
        });
        try {
            const result = await this.transport.invokeQuery(queryRef.name, queryRef.variables);
            const fetchTime = Date.now().toString();
            const originalExtensions = result.extensions;
            const queryResult = {
                ...result,
                ref: queryRef,
                source: SOURCE_SERVER,
                fetchTime,
                data: result.data,
                extensions: getDataConnectExtensionsWithoutMaxAge(originalExtensions),
                toJSON: getRefSerializer(queryRef, result.data, SOURCE_SERVER, fetchTime)
            };
            let updatedKeys = [];
            updatedKeys = await this.updateCache(queryResult, originalExtensions?.dataConnect);
            this.publishDataToSubscribers(key, queryResult);
            if (this.cache) {
                await this.publishCacheResultsToSubscribers(updatedKeys, fetchTime);
            }
            else {
                this.subscriptionCache.set(key, queryResult);
            }
            return queryResult;
        }
        catch (e) {
            this.publishErrorToSubscribers(key, e);
            throw e;
        }
    }
    async fetchCacheResults(queryRef, allowStale = false) {
        await this.waitForQueuedWrites();
        let result;
        if (!this.cache) {
            result = await this.getFromSubscriberCache(queryRef);
        }
        else {
            result = await this.getFromResultTreeCache(queryRef, allowStale);
        }
        if (!result) {
            throw new DataConnectError(Code.OTHER, 'No cache entry found for query: ' + queryRef.name);
        }
        const fetchTime = Date.now().toString();
        const queryResult = {
            ...result,
            ref: queryRef,
            source: SOURCE_CACHE,
            fetchTime,
            data: result.data,
            extensions: result.extensions,
            toJSON: getRefSerializer(queryRef, result.data, SOURCE_CACHE, fetchTime)
        };
        if (this.cache) {
            const key = encoderImpl({
                name: queryRef.name,
                variables: queryRef.variables,
                refType: QUERY_STR
            });
            await this.publishCacheResultsToSubscribers([key], fetchTime);
        }
        else {
            const key = encoderImpl({
                name: queryRef.name,
                variables: queryRef.variables,
                refType: QUERY_STR
            });
            this.subscriptionCache.set(key, queryResult);
            this.publishDataToSubscribers(key, queryResult);
        }
        return queryResult;
    }
    publishErrorToSubscribers(key, err) {
        this.callbacks.get(key)?.forEach(subscription => {
            if (subscription.errCallback) {
                subscription.errCallback(err);
            }
        });
    }
    async getFromResultTreeCache(queryRef, allowStale = false) {
        const key = encoderImpl({
            name: queryRef.name,
            variables: queryRef.variables,
            refType: QUERY_STR
        });
        if (!this.cache || !(await this.cache.containsResultTree(key))) {
            return null;
        }
        const cacheResult = (await this.cache.getResultJSON(key));
        const resultTree = await this.cache.getResultTree(key);
        if (!allowStale && resultTree.isStale()) {
            return null;
        }
        const result = {
            source: SOURCE_CACHE,
            ref: queryRef,
            data: cacheResult,
            toJSON: getRefSerializer(queryRef, cacheResult, SOURCE_CACHE, resultTree.cachedAt.toString()),
            fetchTime: resultTree.cachedAt.toString()
        };
        (await this.cache.getResultTree(key)).updateAccessed();
        return result;
    }
    async getFromSubscriberCache(queryRef) {
        const key = encoderImpl({
            name: queryRef.name,
            variables: queryRef.variables,
            refType: QUERY_STR
        });
        if (!this.subscriptionCache.has(key)) {
            return;
        }
        const result = this.subscriptionCache.get(key);
        result.source = SOURCE_CACHE;
        result.toJSON = getRefSerializer(result.ref, result.data, SOURCE_CACHE, result.fetchTime);
        return result;
    }
    publishDataToSubscribers(key, queryResult) {
        if (!this.callbacks.has(key)) {
            return;
        }
        const subscribers = this.callbacks.get(key);
        subscribers.forEach(callback => {
            callback.userCallback(queryResult);
        });
    }
    async publishCacheResultsToSubscribers(impactedQueries, fetchTime) {
        if (!this.cache) {
            return;
        }
        for (const query of impactedQueries) {
            const callbacks = this.callbacks.get(query);
            if (!callbacks) {
                continue;
            }
            const newJson = (await this.cache.getResultTree(query))
                .getRootStub()
                .toJSON(EncodingMode.hydrated);
            const { name, variables } = decoderImpl(query);
            const queryRef = {
                dataConnect: this.dc,
                refType: QUERY_STR,
                name,
                variables
            };
            this.publishDataToSubscribers(query, {
                data: newJson,
                fetchTime,
                ref: queryRef,
                source: SOURCE_CACHE,
                toJSON: getRefSerializer(queryRef, newJson, SOURCE_CACHE, fetchTime)
            });
        }
    }
    enableEmulator(host, port) {
        this.transport.useEmulator(host, port);
    }
}
function getMaxAgeFromExtensions(extensions) {
    if (!extensions) {
        return;
    }
    for (const extension of extensions) {
        if ('maxAge' in extension &&
            extension.maxAge !== undefined &&
            extension.maxAge !== null) {
            if (extension.maxAge.endsWith('s')) {
                return Number(extension.maxAge.substring(0, extension.maxAge.length - 1));
            }
        }
    }
}
function getDataConnectExtensionsWithoutMaxAge(extensions) {
    return {
        dataConnect: extensions.dataConnect?.filter(extension => 'entityId' in extension || 'entityIds' in extension)
    };
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const PROD_HOST = 'firebasedataconnect.googleapis.com';
function urlBuilder(projectConfig, transportOptions) {
    const { connector, location, projectId: project, service } = projectConfig;
    const { host, sslEnabled, port } = transportOptions;
    const protocol = sslEnabled ? 'https' : 'http';
    const realHost = host || PROD_HOST;
    let baseUrl = `${protocol}://${realHost}`;
    if (typeof port === 'number') {
        baseUrl += `:${port}`;
    }
    else if (typeof port !== 'undefined') {
        logError('Port type is of an invalid type');
        throw new DataConnectError(Code.INVALID_ARGUMENT, 'Incorrect type for port passed in!');
    }
    return `${baseUrl}/v1/projects/${project}/locations/${location}/services/${service}/connectors/${connector}`;
}
function addToken(url, apiKey) {
    if (!apiKey) {
        return url;
    }
    const newUrl = new URL(url);
    newUrl.searchParams.append('key', apiKey);
    return newUrl.toString();
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
class RESTTransport {
    constructor(options, apiKey, appId, authProvider, appCheckProvider, transportOptions, _isUsingGen = false, _callerSdkType = CallerSdkTypeEnum.Base) {
        this.apiKey = apiKey;
        this.appId = appId;
        this.authProvider = authProvider;
        this.appCheckProvider = appCheckProvider;
        this._isUsingGen = _isUsingGen;
        this._callerSdkType = _callerSdkType;
        this._host = '';
        this._location = 'l';
        this._connectorName = '';
        this._secure = true;
        this._project = 'p';
        this._accessToken = null;
        this._appCheckToken = null;
        this._lastToken = null;
        this._isUsingEmulator = false;
        // TODO(mtewani): Update U to include shape of body defined in line 13.
        this.invokeQuery = (queryName, body) => {
            const abortController = new AbortController();
            // TODO(mtewani): Update to proper value
            const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
                name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
                operationName: queryName,
                variables: body
            }, abortController, this.appId, this._accessToken, this._appCheckToken, this._isUsingGen, this._callerSdkType, this._isUsingEmulator));
            return withAuth;
        };
        this.invokeMutation = (mutationName, body) => {
            const abortController = new AbortController();
            const taskResult = this.withRetry(() => {
                return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
                    name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
                    operationName: mutationName,
                    variables: body
                }, abortController, this.appId, this._accessToken, this._appCheckToken, this._isUsingGen, this._callerSdkType, this._isUsingEmulator);
            });
            return taskResult;
        };
        if (transportOptions) {
            if (typeof transportOptions.port === 'number') {
                this._port = transportOptions.port;
            }
            if (typeof transportOptions.sslEnabled !== 'undefined') {
                this._secure = transportOptions.sslEnabled;
            }
            this._host = transportOptions.host;
        }
        const { location, projectId: project, connector, service } = options;
        if (location) {
            this._location = location;
        }
        if (project) {
            this._project = project;
        }
        this._serviceName = service;
        if (!connector) {
            throw new DataConnectError(Code.INVALID_ARGUMENT, 'Connector Name required!');
        }
        this._connectorName = connector;
        this.authProvider?.addTokenChangeListener(token => {
            logDebug(`New Token Available: ${token}`);
            this._accessToken = token;
        });
        this.appCheckProvider?.addTokenChangeListener(result => {
            const { token } = result;
            logDebug(`New App Check Token Available: ${token}`);
            this._appCheckToken = token;
        });
    }
    get endpointUrl() {
        return urlBuilder({
            connector: this._connectorName,
            location: this._location,
            projectId: this._project,
            service: this._serviceName
        }, { host: this._host, sslEnabled: this._secure, port: this._port });
    }
    useEmulator(host, port, isSecure) {
        this._host = host;
        this._isUsingEmulator = true;
        if (typeof port === 'number') {
            this._port = port;
        }
        if (typeof isSecure !== 'undefined') {
            this._secure = isSecure;
        }
    }
    onTokenChanged(newToken) {
        this._accessToken = newToken;
    }
    async getWithAuth(forceToken = false) {
        let starterPromise = new Promise(resolve => resolve(this._accessToken));
        if (this.appCheckProvider) {
            const appCheckToken = await this.appCheckProvider.getToken();
            if (appCheckToken) {
                this._appCheckToken = appCheckToken.token;
            }
        }
        if (this.authProvider) {
            starterPromise = this.authProvider
                .getToken(/*forceToken=*/ forceToken)
                .then(data => {
                if (!data) {
                    return null;
                }
                this._accessToken = data.accessToken;
                return this._accessToken;
            });
        }
        else {
            starterPromise = new Promise(resolve => resolve(''));
        }
        return starterPromise;
    }
    _setLastToken(lastToken) {
        this._lastToken = lastToken;
    }
    withRetry(promiseFactory, retry = false) {
        let isNewToken = false;
        return this.getWithAuth(retry)
            .then(res => {
            isNewToken = this._lastToken !== res;
            this._lastToken = res;
            return res;
        })
            .then(promiseFactory)
            .catch(err => {
            // Only retry if the result is unauthorized and the last token isn't the same as the new one.
            if ('code' in err &&
                err.code === Code.UNAUTHORIZED &&
                !retry &&
                isNewToken) {
                logDebug('Retrying due to unauthorized');
                return this.withRetry(promiseFactory, true);
            }
            throw err;
        });
    }
    _setCallerSdkType(callerSdkType) {
        this._callerSdkType = callerSdkType;
    }
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 *
 * @param dcInstance Data Connect instance
 * @param mutationName name of mutation
 * @param variables variables to send with mutation
 * @returns `MutationRef`
 */
function mutationRef(dcInstance, mutationName, variables) {
    dcInstance.setInitialized();
    const ref = {
        dataConnect: dcInstance,
        name: mutationName,
        refType: MUTATION_STR,
        variables: variables
    };
    return ref;
}
/**
 * @internal
 */
class MutationManager {
    constructor(_transport) {
        this._transport = _transport;
        this._inflight = [];
    }
    executeMutation(mutationRef) {
        const result = this._transport.invokeMutation(mutationRef.name, mutationRef.variables);
        const withRefPromise = result.then(res => {
            const obj = {
                ...res, // Double check that the result is result.data, not just result
                source: SOURCE_SERVER,
                ref: mutationRef,
                fetchTime: Date.now().toLocaleString()
            };
            return obj;
        });
        this._inflight.push(result);
        const removePromise = () => (this._inflight = this._inflight.filter(promise => promise !== result));
        result.then(removePromise, removePromise);
        return withRefPromise;
    }
}
/**
 * Execute Mutation
 * @param mutationRef mutation to execute
 * @returns `MutationRef`
 */
function executeMutation(mutationRef) {
    return mutationRef.dataConnect._mutationManager.executeMutation(mutationRef);
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR = 'FIREBASE_DATA_CONNECT_EMULATOR_HOST';
/**
 *
 * @param fullHost
 * @returns TransportOptions
 * @internal
 */
function parseOptions(fullHost) {
    const [protocol, hostName] = fullHost.split('://');
    const isSecure = protocol === 'https';
    const [host, portAsString] = hostName.split(':');
    const port = Number(portAsString);
    return { host, port, sslEnabled: isSecure };
}
/**
 * Class representing Firebase Data Connect
 */
class DataConnect {
    // @internal
    constructor(app, 
    // TODO(mtewani): Replace with _dataConnectOptions in the future
    dataConnectOptions, _authProvider, _appCheckProvider) {
        this.app = app;
        this.dataConnectOptions = dataConnectOptions;
        this._authProvider = _authProvider;
        this._appCheckProvider = _appCheckProvider;
        this.isEmulator = false;
        this._initialized = false;
        this._isUsingGeneratedSdk = false;
        this._callerSdkType = CallerSdkTypeEnum.Base;
        if (typeof process !== 'undefined' && process.env) {
            const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
            if (host) {
                logDebug('Found custom host. Using emulator');
                this.isEmulator = true;
                this._transportOptions = parseOptions(host);
            }
        }
    }
    /**
     * @internal
     */
    getCache() {
        return this.cache;
    }
    // @internal
    _useGeneratedSdk() {
        if (!this._isUsingGeneratedSdk) {
            this._isUsingGeneratedSdk = true;
        }
    }
    _setCallerSdkType(callerSdkType) {
        this._callerSdkType = callerSdkType;
        if (this._initialized) {
            this._transport._setCallerSdkType(callerSdkType);
        }
    }
    _delete() {
        app._removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
        return Promise.resolve();
    }
    // @internal
    getSettings() {
        const copy = JSON.parse(JSON.stringify(this.dataConnectOptions));
        delete copy.projectId;
        return copy;
    }
    /**
     * @internal
     */
    setCacheSettings(cacheSettings) {
        this._cacheSettings = cacheSettings;
    }
    // @internal
    setInitialized() {
        if (this._initialized) {
            return;
        }
        if (this._transportClass === undefined) {
            logDebug('transportClass not provided. Defaulting to RESTTransport.');
            this._transportClass = RESTTransport;
        }
        this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
        const connectorConfig = {
            connector: this.dataConnectOptions.connector,
            service: this.dataConnectOptions.service,
            location: this.dataConnectOptions.location
        };
        if (this._cacheSettings) {
            this.cache = new DataConnectCache(this._authTokenProvider, this.app.options.projectId, connectorConfig, this._transportOptions?.host || PROD_HOST, this._cacheSettings);
        }
        if (this._appCheckProvider) {
            this._appCheckTokenProvider = new AppCheckTokenProvider(this.app, this._appCheckProvider);
        }
        this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this.app.options.appId, this._authTokenProvider, this._appCheckTokenProvider, undefined, this._isUsingGeneratedSdk, this._callerSdkType);
        if (this._transportOptions) {
            this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
        }
        this._queryManager = new QueryManager(this._transport, this, this.cache);
        this._mutationManager = new MutationManager(this._transport);
        this._initialized = true;
    }
    // @internal
    enableEmulator(transportOptions) {
        if (this._transportOptions &&
            this._initialized &&
            !areTransportOptionsEqual(this._transportOptions, transportOptions)) {
            logError('enableEmulator called after initialization');
            throw new DataConnectError(Code.ALREADY_INITIALIZED, 'DataConnect instance already initialized!');
        }
        this._transportOptions = transportOptions;
        this.isEmulator = true;
    }
}
/**
 * @internal
 * @param transportOptions1
 * @param transportOptions2
 * @returns
 */
function areTransportOptionsEqual(transportOptions1, transportOptions2) {
    return (transportOptions1.host === transportOptions2.host &&
        transportOptions1.port === transportOptions2.port &&
        transportOptions1.sslEnabled === transportOptions2.sslEnabled);
}
/**
 * Connect to the DataConnect Emulator
 * @param dc Data Connect instance
 * @param host host of emulator server
 * @param port port of emulator server
 * @param sslEnabled use https
 */
function connectDataConnectEmulator(dc, host, port, sslEnabled = false) {
    // Workaround to get cookies in Firebase Studio
    if (util.isCloudWorkstation(host)) {
        void util.pingServer(`https://${host}${port ? `:${port}` : ''}`);
        util.updateEmulatorBanner('Data Connect', true);
    }
    dc.enableEmulator({ host, port, sslEnabled });
}
function getDataConnect(appOrConnectorConfig, settingsOrConnectorConfig, settings) {
    let app$1;
    let connectorConfig;
    let realSettings;
    if ('location' in appOrConnectorConfig) {
        connectorConfig = appOrConnectorConfig;
        app$1 = app.getApp();
        realSettings = settingsOrConnectorConfig;
    }
    else {
        app$1 = appOrConnectorConfig;
        connectorConfig = settingsOrConnectorConfig;
        realSettings = settings;
    }
    if (!app$1 || Object.keys(app$1).length === 0) {
        app$1 = app.getApp();
    }
    // Options to store in Firebase Component Provider.
    const serializedOptions = {
        ...connectorConfig,
        projectId: app$1.options.projectId
    };
    // We should sort the keys before initialization.
    const sortedSerialized = Object.fromEntries(Object.entries(serializedOptions).sort());
    const provider = app._getProvider(app$1, 'data-connect');
    const identifier = JSON.stringify(sortedSerialized);
    if (provider.isInitialized(identifier)) {
        const dcInstance = provider.getImmediate({ identifier });
        const options = provider.getOptions(identifier);
        const optionsValid = Object.keys(options).length > 0;
        if (optionsValid) {
            logDebug('Re-using cached instance');
            return dcInstance;
        }
    }
    validateDCOptions(connectorConfig);
    logDebug('Creating new DataConnect instance');
    // Initialize with options.
    const dataConnect = provider.initialize({
        instanceIdentifier: identifier,
        options: Object.fromEntries(Object.entries({
            ...sortedSerialized
        }).sort())
    });
    if (realSettings?.cacheSettings) {
        dataConnect.setCacheSettings(realSettings.cacheSettings);
    }
    return dataConnect;
}
/**
 *
 * @param dcOptions
 * @returns {void}
 * @internal
 */
function validateDCOptions(dcOptions) {
    const fields = ['connector', 'location', 'service'];
    if (!dcOptions) {
        throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
    }
    fields.forEach(field => {
        if (dcOptions[field] === null ||
            dcOptions[field] === undefined) {
            throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
        }
    });
    return true;
}
/**
 * Delete DataConnect instance
 * @param dataConnect DataConnect instance
 * @returns
 */
function terminate(dataConnect) {
    return dataConnect._delete();
    // TODO(mtewani): Stop pending tasks
}
const StorageType = {
    MEMORY: 'MEMORY'
};
function makeMemoryCacheProvider() {
    return new MemoryStub();
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function registerDataConnect(variant) {
    setSDKVersion(app.SDK_VERSION);
    app._registerComponent(new component.Component('data-connect', (container, { instanceIdentifier: connectorConfigStr, options }) => {
        const app = container.getProvider('app').getImmediate();
        const authProvider = container.getProvider('auth-internal');
        const appCheckProvider = container.getProvider('app-check-internal');
        let newOpts = options;
        if (connectorConfigStr) {
            newOpts = {
                ...JSON.parse(connectorConfigStr),
                ...newOpts
            };
        }
        if (!app.options.projectId) {
            throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
        }
        return new DataConnect(app, { ...newOpts, projectId: app.options.projectId }, authProvider, appCheckProvider);
    }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
    app.registerVersion(name, version, variant);
    // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
    app.registerVersion(name, version, 'cjs2020');
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const QueryFetchPolicy = {
    PREFER_CACHE: 'PREFER_CACHE',
    CACHE_ONLY: 'CACHE_ONLY',
    SERVER_ONLY: 'SERVER_ONLY'
};

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Execute Query
 * @param queryRef query to execute.
 * @returns `QueryPromise`
 */
function executeQuery(queryRef, options) {
    if (queryRef.refType !== QUERY_STR) {
        return Promise.reject(new DataConnectError(Code.INVALID_ARGUMENT, `ExecuteQuery can only execute query operations`));
    }
    const queryManager = queryRef.dataConnect._queryManager;
    const fetchPolicy = options?.fetchPolicy ?? QueryFetchPolicy.PREFER_CACHE;
    switch (fetchPolicy) {
        case QueryFetchPolicy.SERVER_ONLY:
            return queryManager.fetchServerResults(queryRef);
        case QueryFetchPolicy.CACHE_ONLY:
            return queryManager.fetchCacheResults(queryRef, true);
        case QueryFetchPolicy.PREFER_CACHE:
            return queryManager.preferCacheResults(queryRef, false);
        default:
            throw new DataConnectError(Code.INVALID_ARGUMENT, `Invalid fetch policy: ${fetchPolicy}`);
    }
}
/**
 * Execute Query
 * @param dcInstance Data Connect instance to use.
 * @param queryName Query to execute
 * @param variables Variables to execute with
 * @param initialCache initial cache to use for client hydration
 * @returns `QueryRef`
 */
function queryRef(dcInstance, queryName, variables, initialCache) {
    dcInstance.setInitialized();
    if (initialCache !== undefined) {
        dcInstance._queryManager.updateSSR(initialCache);
    }
    return {
        dataConnect: dcInstance,
        refType: QUERY_STR,
        name: queryName,
        variables: variables
    };
}
/**
 * Converts serialized ref to query ref
 * @param serializedRef ref to convert to `QueryRef`
 * @returns `QueryRef`
 */
function toQueryRef(serializedRef) {
    const { refInfo: { name, variables, connectorConfig } } = serializedRef;
    return queryRef(getDataConnect(connectorConfig), name, variables);
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
 * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
 * @param connectorConfig
 * @param dcOrVars
 * @param vars
 * @param validateVars
 * @returns {DataConnect} and {Variables} instance
 * @internal
 */
function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
    let dcInstance;
    let realVars;
    if (dcOrVars && 'enableEmulator' in dcOrVars) {
        dcInstance = dcOrVars;
        realVars = vars;
    }
    else {
        dcInstance = getDataConnect(connectorConfig);
        realVars = dcOrVars;
    }
    if (!dcInstance || (!realVars && validateVars)) {
        throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
    }
    return { dc: dcInstance, vars: realVars };
}

/**
 * @license
 * Copyright 2025 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Subscribe to a `QueryRef`
 * @param queryRefOrSerializedResult query ref or serialized result.
 * @param observerOrOnNext observer object or next function.
 * @param onError Callback to call when error gets thrown.
 * @param onComplete Called when subscription completes.
 * @returns `SubscriptionOptions`
 */
function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComplete) {
    let ref;
    let initialCache;
    if ('refInfo' in queryRefOrSerializedResult) {
        const serializedRef = queryRefOrSerializedResult;
        const { data, source, fetchTime } = serializedRef;
        ref = toQueryRef(serializedRef);
        initialCache = {
            data,
            source,
            fetchTime,
            ref,
            toJSON: getRefSerializer(ref, data, source, fetchTime)
        };
    }
    else {
        ref = queryRefOrSerializedResult;
    }
    let onResult = undefined;
    if (typeof observerOrOnNext === 'function') {
        onResult = observerOrOnNext;
    }
    else {
        onResult = observerOrOnNext.onNext;
        onError = observerOrOnNext.onErr;
        onComplete = observerOrOnNext.onComplete;
    }
    if (!onResult) {
        throw new DataConnectError(Code.INVALID_ARGUMENT, 'Must provide onNext');
    }
    return ref.dataConnect._queryManager.addSubscription(ref, onResult, onComplete, onError, initialCache);
}

/**
 * @license
 * Copyright 2024 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
initializeFetch(fetch);
registerDataConnect('node');

exports.CallerSdkTypeEnum = CallerSdkTypeEnum;
exports.Code = Code;
exports.DataConnect = DataConnect;
exports.DataConnectError = DataConnectError;
exports.DataConnectOperationError = DataConnectOperationError;
exports.MUTATION_STR = MUTATION_STR;
exports.MutationManager = MutationManager;
exports.QUERY_STR = QUERY_STR;
exports.QueryFetchPolicy = QueryFetchPolicy;
exports.SOURCE_CACHE = SOURCE_CACHE;
exports.SOURCE_SERVER = SOURCE_SERVER;
exports.StorageType = StorageType;
exports.areTransportOptionsEqual = areTransportOptionsEqual;
exports.connectDataConnectEmulator = connectDataConnectEmulator;
exports.executeMutation = executeMutation;
exports.executeQuery = executeQuery;
exports.getDataConnect = getDataConnect;
exports.makeMemoryCacheProvider = makeMemoryCacheProvider;
exports.mutationRef = mutationRef;
exports.parseOptions = parseOptions;
exports.queryRef = queryRef;
exports.setLogLevel = setLogLevel;
exports.subscribe = subscribe;
exports.terminate = terminate;
exports.toQueryRef = toQueryRef;
exports.validateArgs = validateArgs;
exports.validateDCOptions = validateDCOptions;
//# sourceMappingURL=index.node.cjs.js.map
