"use strict";
// Copyright 2016-2018, Pulumi Corporation.
//
// 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.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const resource_1 = require("./resource");
const utils = __importStar(require("./utils"));
/* eslint-disable no-shadow, @typescript-eslint/no-shadow */
/**
 * Output helps encode the relationship between Resources in a Pulumi application. Specifically an
 * Output holds onto a piece of Data and the Resource it was generated from. An Output value can
 * then be provided when constructing new Resources, allowing that new Resource to know both the
 * value as well as the Resource the value came from.  This allows for a precise 'Resource
 * dependency graph' to be created, which properly tracks the relationship between resources.
 */
class OutputImpl {
    /** @internal */
    constructor(resources, promise, isKnown, isSecret, allResources) {
        /**
         * A private field to help with RTTI that works in SxS scenarios.
         *
         * This is internal instead of being truly private, to support mixins and our serialization model.
         * @internal
         */
        // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
        this.__pulumiOutput = true;
        // Always create a copy so that no one accidentally modifies our Resource list.
        const resourcesCopy = copyResources(resources);
        // Create a copy of the async resources.  Populate this with the sync-resources if that's
        // all we have.  That way this is always ensured to be a superset of the list of sync resources.
        allResources = allResources || Promise.resolve([]);
        const allResourcesCopy = allResources.then((r) => utils.union(copyResources(r), resourcesCopy));
        // We are only known if we are not explicitly unknown and the resolved value of the output
        // contains no distinguished unknown values.
        isKnown = Promise.all([isKnown, promise]).then(([known, val]) => known && !containsUnknowns(val));
        const lifted = Promise.all([allResourcesCopy, promise, isKnown, isSecret]).then(([liftedResources, value, liftedIsKnown, liftedIsSecret]) => liftInnerOutput(liftedResources, value, liftedIsKnown, liftedIsSecret));
        this.resources = () => resourcesCopy;
        this.allResources = () => lifted.then((l) => l.allResources);
        this.isKnown = lifted.then((l) => l.isKnown);
        this.isSecret = lifted.then((l) => l.isSecret);
        this.promise = (withUnknowns) => OutputImpl.getPromisedValue(lifted.then((l) => l.value), withUnknowns);
        this.toString = () => {
            let message = `Calling [toString] on an [Output<T>] is not supported.

To get the value of an Output<T> as an Output<string> consider either:
1: o.apply(v => \`prefix\${v}suffix\`)
2: pulumi.interpolate \`prefix\${v}suffix\`

See https://www.pulumi.com/docs/concepts/inputs-outputs for more details.`;
            if (utils.errorOutputString) {
                throw new Error(message);
            }
            message += `\nThis function may throw in a future version of @pulumi/pulumi.`;
            return message;
        };
        this.toJSON = () => {
            let message = `Calling [toJSON] on an [Output<T>] is not supported.

To get the value of an Output as a JSON value or JSON string consider either:
    1: o.apply(v => v.toJSON())
    2: o.apply(v => JSON.stringify(v))

See https://www.pulumi.com/docs/concepts/inputs-outputs for more details.`;
            if (utils.errorOutputString) {
                throw new Error(message);
            }
            message += `\nThis function may throw in a future version of @pulumi/pulumi.`;
            return message;
        };
        return new Proxy(this, {
            get: (obj, prop) => {
                // Recreate the prototype walk to ensure we find any actual members defined directly
                // on `Output<T>`.
                for (let o = obj; o; o = Object.getPrototypeOf(o)) {
                    if (o.hasOwnProperty(prop)) {
                        return o[prop];
                    }
                }
                // Always explicitly fail on a member called 'then'.  It is used by other systems to
                // determine if this is a Promise, and we do not want to indicate that that's what
                // we are.
                if (prop === "then") {
                    return undefined;
                }
                // Do not lift members that start with __.  Technically, if all libraries were
                // using this version of pulumi/pulumi we would not need this.  However, this is
                // so that downstream consumers can use this version of pulumi/pulumi while also
                // passing these new Outputs to older versions of pulumi/pulumi.  The reason this
                // can be a problem is that older versions do an RTTI check that simply asks questions
                // like:
                //
                //      Is there a member on this object called '__pulumiResource'
                //
                // If we automatically lift such a member (even if it eventually points to 'undefined'),
                // then those RTTI checks will succeed.
                //
                // Note: this should be safe to not lift as, in general, properties with this prefix
                // are not at all common (and in general are used to represent private things anyway
                // that likely should not be exposed).
                //
                // Similarly, do not respond to the 'doNotCapture' member name.  It serves a similar
                // RTTI purpose.
                if (typeof prop === "string") {
                    if (prop.startsWith("__") || prop === "doNotCapture" || prop === "deploymentOnlyModule") {
                        return undefined;
                    }
                }
                // Fail out if we are being accessed using a symbol.  Many APIs will access with a
                // well known symbol (like 'Symbol.toPrimitive') to check for the presence of something.
                // They will only check for the existence of that member, and we don't want to make it
                // appear that have those.
                //
                // Another way of putting this is that we only forward 'string/number' members to our
                // underlying value.
                if (typeof prop === "symbol") {
                    return undefined;
                }
                // Else for *any other* property lookup, succeed the lookup and return a lifted
                // `apply` on the underlying `Output`.
                return obj.apply((ob) => {
                    if (ob === undefined || ob === null) {
                        return undefined;
                    }
                    else if (isUnknown(ob)) {
                        // If the value of this output is unknown, the result of the access should also be unknown.
                        // This is conceptually consistent, and also prevents us from returning a "known undefined"
                        // value from the `ob[prop]` expression below.
                        return exports.unknown;
                    }
                    return ob[prop];
                }, /*runWithUnknowns:*/ true);
            },
        });
    }
    static create(val) {
        return output(val);
    }
    /**
     * Returns true if the given object is an instance of Output<T>.  This is designed to work even when
     * multiple copies of the Pulumi SDK have been loaded into the same process.
     */
    static isInstance(obj) {
        return utils.isInstance(obj, "__pulumiOutput");
    }
    /** @internal */
    static getPromisedValue(promise, withUnknowns) {
        return __awaiter(this, void 0, void 0, function* () {
            // If the caller did not explicitly ask to see unknown values and val contains unknowns, return undefined. This
            // preserves compatibility with earlier versions of the Pulumi SDK.
            const val = yield promise;
            if (!withUnknowns && containsUnknowns(val)) {
                return undefined;
            }
            return val;
        });
    }
    get() {
        throw new Error(`Cannot call '.get' during update or preview.
To manipulate the value of this Output, use '.apply' instead.`);
    }
    // runWithUnknowns requests that `func` is run even if `isKnown` resolves to `false`. This is used to allow
    // callers to process fully- or partially-unknown values and return a known result. the output proxy takes
    // advantage of this to allow proxied property accesses to return known values even if other properties of
    // the containing object are unknown.
    apply(func, runWithUnknowns) {
        // we're inside the modern `output` code, so it's safe to call `.allResources!` here.
        const applied = Promise.all([
            this.allResources(),
            this.promise(/*withUnknowns*/ true),
            this.isKnown,
            this.isSecret,
        ]).then(([allResources, value, isKnown, isSecret]) => applyHelperAsync(allResources, value, isKnown, isSecret, func, !!runWithUnknowns));
        const result = new OutputImpl(this.resources(), applied.then((a) => a.value), applied.then((a) => a.isKnown), applied.then((a) => a.isSecret), applied.then((a) => a.allResources));
        return result;
    }
}
/** @internal */
function getAllResources(op) {
    return op.allResources instanceof Function ? op.allResources() : Promise.resolve(op.resources());
}
exports.getAllResources = getAllResources;
function copyResources(resources) {
    const copy = Array.isArray(resources)
        ? new Set(resources)
        : resources instanceof Set
            ? new Set(resources)
            : new Set([resources]);
    return copy;
}
function liftInnerOutput(allResources, value, isKnown, isSecret) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!exports.Output.isInstance(value)) {
            // 'value' itself wasn't an output, no need to transform any of the data we got.
            return { allResources, value, isKnown, isSecret };
        }
        // 'value' was an Output.  So we unwrap that to get the inner value/isKnown/isSecret/resources
        // returned by that Output and merge with the state passed in to get the state of the final Output.
        // Note: we intentionally await all the promises of the inner output. This way we properly
        // propagate any rejections of any of these promises through the outer output as well.
        const innerValue = yield value.promise(/*withUnknowns*/ true);
        const innerIsKnown = yield value.isKnown;
        const innerIsSecret = yield (value.isSecret || Promise.resolve(false));
        // If we're working with a new-style output, grab all its resources and merge into ours.
        // Otherwise, if this is an old-style output, just grab the resources it was known to have
        // at construction time.
        const innerResources = yield getAllResources(value);
        const totalResources = utils.union(allResources, innerResources);
        return {
            allResources: totalResources,
            value: innerValue,
            isKnown: innerIsKnown,
            isSecret: isSecret || innerIsSecret,
        };
    });
}
/* eslint-disable max-len */
function applyHelperAsync(allResources, value, isKnown, isSecret, func, runWithUnknowns) {
    return __awaiter(this, void 0, void 0, function* () {
        // Only perform the apply if the engine was able to give us an actual value
        // for this Output.
        const doApply = isKnown || runWithUnknowns;
        if (!doApply) {
            // We didn't actually run the function, our new Output is definitely **not** known.
            return {
                allResources,
                value: undefined,
                isKnown: false,
                isSecret,
            };
        }
        // If we are running with unknown values and the value is explicitly unknown but does not actually
        // contain any unknown values, collapse its value to the unknown value. This ensures that callbacks
        // that expect to see unknowns during preview in outputs that are not known will always do so.
        if (!isKnown && runWithUnknowns && !containsUnknowns(value)) {
            value = exports.unknown;
        }
        const transformed = yield func(value);
        // We successfully ran the inner function. Our new Output should be considered known.  We
        // preserve secretness from our original Output to the new one we're creating.
        return liftInnerOutput(allResources, transformed, /*isKnown*/ true, isSecret);
    });
}
/**
 * Returns a promise denoting if the output is a secret or not. This is not the same as just calling `.isSecret`
 * because in cases where the output does not have a `isSecret` property and it is a Proxy, we need to ignore
 * the isSecret member that the proxy reports back.
 *
 * This calls the public implementation so that we only make any calculations in a single place.
 *
 * @internal
 */
function isSecretOutput(o) {
    return isSecret(o);
}
exports.isSecretOutput = isSecretOutput;
/**
 * Helper function for `output`.  This function trivially recurses through an object, copying it,
 * while also lifting any inner Outputs (with all their respective state) to a top-level Output at
 * the end.  If there are no inner outputs, this will not affect the data (except by producing a new
 * copy of it).
 *
 * Importantly:
 *
 *  1. Resources encountered while recursing are not touched.  This helps ensure they stay Resources
 *     (with an appropriate prototype chain).
 *  2. Primitive values (string, number, etc.) are returned as is.
 *  3. Arrays and Record are recursed into.  An Array<...> that contains any Outputs wil become an
 *     Output<Array<Unwrapped>>.  A Record<string, ...> that contains any Output values will be an
 *     Output<Record<string, Unwrap<...>>.  In both cases of recursion, the outer Output's
 *     known/secret/resources will be computed from the nested Outputs.
 */
function outputRec(val) {
    if (val === null || typeof val !== "object") {
        // strings, numbers, booleans, functions, symbols, undefineds, nulls are all returned as
        // themselves.  They are always 'known' (i.e. we can safely 'apply' off of them even during
        // preview).
        return val;
    }
    else if (resource_1.Resource.isInstance(val)) {
        // Don't unwrap Resources, there are existing codepaths that return Resources through
        // Outputs and we want to preserve them as is when flattening.
        return val;
    }
    else if (isUnknown(val)) {
        return val;
    }
    else if (val instanceof Promise) {
        // Recurse into the value the Promise points to.  This may end up producing a
        // Promise<Output>. Wrap this in another Output as the final result.  This Output's
        // construction will be able to merge the inner Output's data with its own.  See
        // liftInnerOutput for more details.
        return createSimpleOutput(val.then((v) => outputRec(v)));
    }
    else if (exports.Output.isInstance(val)) {
        // We create a new output here from the raw pieces of the original output in order to
        // accommodate outputs from downlevel SxS SDKs.  This ensures that within this package it is
        // safe to assume the implementation of any Output returned by the `output` function.
        //
        // This includes:
        // 1. that first-class unknowns are properly represented in the system: if this was a
        //    downlevel output where val.isKnown resolves to false, this guarantees that the
        //    returned output's promise resolves to unknown.
        // 2. That the `isSecret` property is available.
        // 3. That the `.allResources` is available.
        const allResources = getAllResources(val);
        const newOutput = new OutputImpl(val.resources(), val.promise(/*withUnknowns*/ true), val.isKnown, val.isSecret, allResources);
        return newOutput.apply(outputRec, /*runWithUnknowns*/ true);
    }
    else if (val instanceof Array) {
        const allValues = [];
        let hasOutputs = false;
        for (const v of val) {
            const ev = outputRec(v);
            allValues.push(ev);
            if (exports.Output.isInstance(ev)) {
                hasOutputs = true;
            }
        }
        // If we didn't encounter any nested Outputs, we don't need to do anything.  We can just
        // return this value as is.
        if (!hasOutputs) {
            // Note: we intentionally return 'allValues' here and not 'val'.  This ensures we get a
            // copy.  This has been behavior we've had since the beginning and there may be subtle
            // logic out there that depends on this that we would not want ot break.
            return allValues;
        }
        // Otherwise, combine the data from all the outputs/non-outputs to one final output.
        const promisedArray = Promise.all(allValues.map((v) => getAwaitableValue(v)));
        const [syncResources, isKnown, isSecret, allResources] = getResourcesAndDetails(allValues);
        return new exports.Output(syncResources, promisedArray, isKnown, isSecret, allResources);
    }
    else {
        const promisedValues = [];
        let hasOutputs = false;
        for (const k of Object.keys(val)) {
            const ev = outputRec(val[k]);
            promisedValues.push({ key: k, value: ev });
            if (exports.Output.isInstance(ev)) {
                hasOutputs = true;
            }
        }
        if (!hasOutputs) {
            // Note: we intentionally return a new value here and not 'val'.  This ensures we get a
            // copy.  This has been behavior we've had since the beginning and there may be subtle
            // logic out there that depends on this that we would not want ot break.
            return promisedValues.reduce((o, kvp) => {
                o[kvp.key] = kvp.value;
                return o;
            }, {});
        }
        const promisedObject = getPromisedObject(promisedValues);
        const [syncResources, isKnown, isSecret, allResources] = getResourcesAndDetails(promisedValues.map((kvp) => kvp.value));
        return new exports.Output(syncResources, promisedObject, isKnown, isSecret, allResources);
    }
}
function output(val) {
    const ov = outputRec(val);
    return exports.Output.isInstance(ov) ? ov : createSimpleOutput(ov);
}
exports.output = output;
function secret(val) {
    const o = output(val);
    // we called `output` right above this, so it's safe to call `.allResources` on the result.
    return new exports.Output(o.resources(), o.promise(/*withUnknowns*/ true), o.isKnown, Promise.resolve(true), o.allResources());
}
exports.secret = secret;
/**
 * [unsecret] behaves the same as [output] except the returned output takes the existing output and unwraps the secret
 */
function unsecret(val) {
    return new exports.Output(val.resources(), val.promise(/*withUnknowns*/ true), val.isKnown, Promise.resolve(false), val.allResources());
}
exports.unsecret = unsecret;
/**
 * [isSecret] returns `true` if and only if the provided [Output] is a secret.
 */
function isSecret(val) {
    return exports.Output.isInstance(val.isSecret) ? Promise.resolve(false) : val.isSecret;
}
exports.isSecret = isSecret;
function createSimpleOutput(val) {
    return new exports.Output(new Set(), val instanceof Promise ? val : Promise.resolve(val), 
    /*isKnown*/ Promise.resolve(true), 
    /*isSecret */ Promise.resolve(false), Promise.resolve(new Set()));
}
function all(val) {
    // Our recursive `output` helper already does exactly what `all` needs to do in terms of the
    // implementation. Why have both `output` and `all` then?  Currently, to the best of our
    // abilities, we haven't been able to make a single signature for both that can unify tuples and
    // arrays for TypeScript.  So `all` is much better when dealing with a tuple of heterogenous
    // values, while `output` is good for everything else.
    //
    // Specifically ``all` can take an `[Output<string>, Output<number>]` and produce an
    // `Output<[string, number]>` However, `output` for that same type will produce an
    // `Output<(string|number)[]>` which is definitely suboptimal.
    return output(val);
}
exports.all = all;
function getAwaitableValue(v) {
    if (exports.Output.isInstance(v)) {
        return v.promise(/* withUnknowns */ true);
    }
    else {
        return v;
    }
}
function getPromisedObject(keysAndOutputs) {
    return __awaiter(this, void 0, void 0, function* () {
        const result = {};
        for (const kvp of keysAndOutputs) {
            result[kvp.key] = yield getAwaitableValue(kvp.value);
        }
        return result;
    });
}
function getResourcesAndDetails(allValues) {
    const syncResources = new Set();
    const allOutputs = [];
    for (const v of allValues) {
        if (exports.Output.isInstance(v)) {
            allOutputs.push(v);
            for (const res of v.resources()) {
                syncResources.add(res);
            }
        }
    }
    // All the outputs were generated in `function all` using `output(v)`.  So it's safe
    // to call `.allResources!` here.
    const allResources = Promise.all(allOutputs.map((o) => o.allResources())).then((arr) => {
        const result = new Set();
        for (const set of arr) {
            for (const res of set) {
                result.add(res);
            }
        }
        return result;
    });
    // A merged output is known if all of its inputs are known.
    const isKnown = Promise.all(allOutputs.map((o) => o.isKnown)).then((ps) => ps.every((b) => b));
    // A merged output is secret if any of its inputs are secret.
    const isSecret = Promise.all(allOutputs.map((o) => isSecretOutput(o))).then((ps) => ps.some((b) => b));
    return [syncResources, isKnown, isSecret, allResources];
}
/**
 * Unknown represents a value that is unknown. These values correspond to unknown property values received from the
 * Pulumi engine as part of the result of a resource registration (see runtime/rpc.ts). User code is not typically
 * exposed to these values: any Output<> that contains an Unknown will itself be unknown, so any user callbacks
 * passed to `apply` will not be run. Internal callers of `apply` can request that they are run even with unknown
 * values; the output proxy takes advantage of this to allow proxied property accesses to return known values even
 * if other properties of the containing object are unknown.
 */
class Unknown {
    constructor() {
        /**
         * A private field to help with RTTI that works in SxS scenarios.
         *
         * This is internal instead of being truly private, to support mixins and our serialization model.
         * @internal
         */
        // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle,id-blacklist,id-match
        this.__pulumiUnknown = true;
    }
    /**
     * Returns true if the given object is an instance of Unknown. This is designed to work even when
     * multiple copies of the Pulumi SDK have been loaded into the same process.
     */
    static isInstance(obj) {
        return utils.isInstance(obj, "__pulumiUnknown");
    }
}
/**
 * unknown is the singleton unknown value.
 * @internal
 */
exports.unknown = new Unknown();
/**
 * isUnknown returns true if the given value is unknown.
 */
function isUnknown(val) {
    return Unknown.isInstance(val);
}
exports.isUnknown = isUnknown;
/**
 * containsUnknowns returns true if the given value is or contains unknown values.
 */
function containsUnknowns(value) {
    return impl(value, new Set());
    function impl(val, seen) {
        if (val === null || typeof val !== "object") {
            return false;
        }
        else if (isUnknown(val)) {
            return true;
        }
        else if (seen.has(val)) {
            return false;
        }
        seen.add(val);
        if (val instanceof Array) {
            return val.some((e) => impl(e, seen));
        }
        else {
            return Object.keys(val).some((k) => impl(val[k], seen));
        }
    }
}
exports.containsUnknowns = containsUnknowns;
// eslint-disable-next-line @typescript-eslint/naming-convention,@typescript-eslint/no-redeclare,no-underscore-dangle,id-blacklist,id-match
exports.Output = OutputImpl;
/**
 * [concat] takes a sequence of [Inputs], stringifies each, and concatenates all values into one
 * final string.  Individual inputs can be any sort of [Input] value.  i.e. they can be [Promise]s,
 * [Output]s, or just plain JavaScript values.  This can be used like so:
 *
 * ```ts
 *      // 'server' and 'loadBalancer' are both resources that expose [Output] properties.
 *      let val: Output<string> = pulumi.concat("http://", server.hostname, ":", loadBalancer.port);
 * ```
 *
 */
function concat(...params) {
    return output(params).apply((array) => array.join(""));
}
exports.concat = concat;
/**
 * [interpolate] is similar to [concat] but is designed to be used as a tagged template expression.
 * i.e.:
 *
 * ```ts
 *      // 'server' and 'loadBalancer' are both resources that expose [Output] properties.
 *      let val: Output<string> = pulumi.interpolate `http://${server.hostname}:${loadBalancer.port}`
 * ```
 *
 * As with [concat] the 'placeholders' between `${}` can be any Inputs.  i.e. they can be
 * [Promise]s, [Output]s, or just plain JavaScript values.
 */
function interpolate(literals, ...placeholders) {
    return output(placeholders).apply((unwrapped) => {
        let result = "";
        // interleave the literals with the placeholders
        for (let i = 0; i < unwrapped.length; i++) {
            result += literals[i];
            result += unwrapped[i];
        }
        // add the last literal
        result += literals[literals.length - 1];
        return result;
    });
}
exports.interpolate = interpolate;
/**
 * [jsonStringify] Uses JSON.stringify to serialize the given Input value into a JSON string.
 */
function jsonStringify(obj, replacer, space) {
    return output(obj).apply((o) => {
        return JSON.stringify(o, replacer, space);
    });
}
exports.jsonStringify = jsonStringify;
/**
 * [jsonParse] Uses JSON.parse to deserialize the given Input JSON string into a value.
 */
function jsonParse(text, reviver) {
    return output(text).apply((t) => {
        return JSON.parse(t, reviver);
    });
}
exports.jsonParse = jsonParse;
//# sourceMappingURL=output.js.map