"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.cursorToJsonObject = exports.singleJsonCursor = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../../core/index.js");
const index_js_2 = require("../../feature-libraries/index.js");
const leafDomain_js_1 = require("../leafDomain.js");
const jsonDomainSchema_js_1 = require("./jsonDomainSchema.js");
const internal_2 = require("@fluidframework/runtime-utils/internal");
const adapter = {
    value: (node) => node !== null && typeof node === "object"
        ? undefined // arrays and objects have no defined value
        : node, // null, boolean, numbers, and strings are their own values
    type: (node) => {
        const type = typeof node;
        switch (type) {
            case "number":
                return leafDomain_js_1.leaf.number.name;
            case "string":
                return leafDomain_js_1.leaf.string.name;
            case "boolean":
                return leafDomain_js_1.leaf.boolean.name;
            default:
                if (node === null) {
                    return leafDomain_js_1.leaf.null.name;
                }
                else if (Array.isArray(node)) {
                    return jsonDomainSchema_js_1.jsonArray.name;
                }
                else {
                    return jsonDomainSchema_js_1.jsonObject.name;
                }
        }
    },
    keysFromNode: (node) => {
        switch (typeof node) {
            case "object":
                if (node === null) {
                    return [];
                }
                else if (Array.isArray(node)) {
                    return node.length === 0 ? [] : [index_js_1.EmptyKey];
                }
                else {
                    return Object.keys(node);
                }
            default:
                return [];
        }
    },
    getFieldFromNode: (node, key) => {
        // Object.prototype.hasOwnProperty can return true for strings (ex: with key "0"), so we have to filter them out.
        // Rather than just special casing strings, we can handle them with an early return for all primitives.
        if (typeof node !== "object") {
            return [];
        }
        if (node === null) {
            return [];
        }
        if (Array.isArray(node)) {
            return key === index_js_1.EmptyKey ? node : [];
        }
        if (Object.prototype.hasOwnProperty.call(node, key)) {
            const field = node[key];
            (0, internal_1.assert)(field !== undefined, 0x41e /* explicit undefined fields should not be preserved in JSON */);
            return [field];
        }
        return [];
    },
};
/**
 * Used to read a Jsonable tree for testing and benchmarking.
 *
 * @returns an {@link ITreeCursorSynchronous} for a single {@link JsonCompatible}.
 */
function singleJsonCursor(root) {
    return (0, index_js_2.stackTreeNodeCursor)(adapter, root);
}
exports.singleJsonCursor = singleJsonCursor;
/**
 * Extract a JS object tree from the contents of the given ITreeCursor.
 * Assumes that ITreeCursor contains only unaugmented JsonTypes.
 */
function cursorToJsonObject(reader) {
    const type = reader.type;
    switch (type) {
        case leafDomain_js_1.leaf.number.name:
        case leafDomain_js_1.leaf.boolean.name:
        case leafDomain_js_1.leaf.string.name:
            (0, internal_1.assert)(reader.value !== undefined, 0x84f /* out of schema: missing value */);
            (0, internal_1.assert)(!(0, internal_2.isFluidHandle)(reader.value), 0x850 /* out of schema: unexpected FluidHandle */);
            return reader.value;
        case jsonDomainSchema_js_1.jsonArray.name: {
            reader.enterField(index_js_1.EmptyKey);
            const result = (0, index_js_1.mapCursorField)(reader, cursorToJsonObject);
            reader.exitField();
            return result;
        }
        case jsonDomainSchema_js_1.jsonObject.name: {
            const result = {};
            (0, index_js_1.mapCursorFields)(reader, (cursor) => {
                const key = cursor.getFieldKey();
                (0, internal_1.assert)(cursor.firstNode(), 0x420 /* expected non-empty field */);
                // like `result[key] = cursorToJsonObject(reader);` except safe when keyString == "__proto__".
                Object.defineProperty(result, key, {
                    enumerable: true,
                    configurable: true,
                    writable: true,
                    value: cursorToJsonObject(reader),
                });
                (0, internal_1.assert)(!cursor.nextNode(), 0x421 /* expected exactly one node */);
            });
            return result;
        }
        default: {
            (0, internal_1.assert)(type === leafDomain_js_1.leaf.null.name, 0x422 /* unexpected type */);
            return null;
        }
    }
}
exports.cursorToJsonObject = cursorToJsonObject;
//# sourceMappingURL=jsonCursor.js.map