var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _SandboxedRuntimeLite_instances, _SandboxedRuntimeLite_runtime, _SandboxedRuntimeLite_vm, _SandboxedRuntimeLite_importObjectPtr, _SandboxedRuntimeLite_actor, _SandboxedRuntimeLite_aborted, _SandboxedRuntimeLite_console, _SandboxedRuntimeLite_modules, _SandboxedRuntimeLite_pluginCallId, _SandboxedRuntimeLite_pluginReturnHandlers, _SandboxedRuntimeLite_localMetadata, _SandboxedRuntimeLite_dependencyMetadata, _SandboxedRuntimeLite_importObject, _SandboxedRuntimeLite_evalGlobal, _SandboxedRuntimeLite_eval, _SandboxedRuntimeLite_initConsole, _SandboxedRuntimeLite_initImporter, _SandboxedRuntimeLite_initCircuitBreaker, _SandboxedRuntimeLite_initPluginProxy, _SandboxedRuntimeLite_initRequire, _SandboxedRuntimeLite_initFetch, _SandboxedRuntimeLite_logErr;
import { CircuitBreak, isCircuitBreaker } from '@devvit/shared-types/CircuitBreaker.js';
import releaseSync from '@jitl/quickjs-singlefile-browser-release-sync';
import { newQuickJSWASMModuleFromVariant, Scope } from 'quickjs-emscripten-core';
import corejs from '../../dist/assets/corejs.js?raw';
import intlPolyfill from '../../dist/assets/intl-polyfill.js?raw';
import protos from '../../dist/assets/protos.js?raw';
import { QueueConsole } from '../console/QueueConsole.js';
import { ActorResponse } from '../types/Commands.js';
import { inspect } from '../utils/inspect.js';
import { getDependencyMetadata } from '../utils/metadata.js';
import { box } from './RuntimeLite.js';
const wasmModule = newQuickJSWASMModuleFromVariant(releaseSync);
export class SandboxedRuntimeLite {
    /**
     * @arg modules Environment dependencies available via `require(id)` to
     *              bundles loaded. Unlike UnsandboxedRuntimeLite, values must be
     *              stringified JavaScript. @devvit/protos is provided by default.
     */
    constructor(modules = {}) {
        _SandboxedRuntimeLite_instances.add(this);
        _SandboxedRuntimeLite_runtime.set(this, void 0);
        _SandboxedRuntimeLite_vm.set(this, void 0);
        _SandboxedRuntimeLite_importObjectPtr.set(this, void 0);
        _SandboxedRuntimeLite_actor.set(this, void 0);
        _SandboxedRuntimeLite_aborted.set(this, false);
        _SandboxedRuntimeLite_console.set(this, box.console);
        _SandboxedRuntimeLite_modules.set(this, void 0);
        _SandboxedRuntimeLite_pluginCallId.set(this, 0);
        _SandboxedRuntimeLite_pluginReturnHandlers.set(this, {});
        _SandboxedRuntimeLite_localMetadata.set(this, {});
        _SandboxedRuntimeLite_dependencyMetadata.set(this, {});
        _SandboxedRuntimeLite_importObject.set(this, (vm, obj) => {
            return vm
                .newString(typeof obj === 'string' ? obj : JSON.stringify(obj))
                .consume((inputPtr) => vm.unwrapResult(vm.callFunction(__classPrivateFieldGet(this, _SandboxedRuntimeLite_importObjectPtr, "f"), vm.null, inputPtr)));
        });
        _SandboxedRuntimeLite_initConsole.set(this, () => {
            const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
            const methods = Object.keys({
                debug: true,
                error: true,
                info: true,
                log: true,
                trace: true,
                warn: true,
            });
            vm.newObject().consume((consolePtr) => {
                for (const method of methods) {
                    vm.newFunction(method, (...args) => {
                        __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f")[method](...args.map(vm.dump));
                    }).consume((fnPtr) => vm.setProp(consolePtr, method, fnPtr));
                }
                vm.setProp(vm.global, 'console', consolePtr);
            });
        });
        _SandboxedRuntimeLite_initImporter.set(this, () => {
            __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, 'function importObject(objStr) { return JSON.parse(objStr); }');
            __classPrivateFieldSet(this, _SandboxedRuntimeLite_importObjectPtr, __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").getProp(__classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").global, 'importObject'), "f");
        });
        _SandboxedRuntimeLite_initCircuitBreaker.set(this, () => {
            const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
            vm.newFunction('abort', (...args) => {
                box.log('[sandboxed runtime] circuit break: ', args[0] ? vm.dump(args[0]) : 'unknown');
                __classPrivateFieldSet(this, _SandboxedRuntimeLite_aborted, true, "f");
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f").logs = [];
            }).consume((fnPtr) => vm.setProp(vm.global, 'abort', fnPtr));
            // See WorkerBox.clearCircuitBreak.
            vm.newFunction('clearCircuitBreak', () => {
                __classPrivateFieldSet(this, _SandboxedRuntimeLite_aborted, false, "f");
                box.log('[sandboxed runtime] circuit break cleared');
            }).consume((fnPtr) => vm.setProp(vm.global, 'clearCircuitBreak', fnPtr));
            // language=JavaScript
            __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, `
        const CircuitBreak = ${CircuitBreak.toString()};

        function circuitBreak(method) {
          abort(method);
          throw CircuitBreak(method);
        }
      `);
        });
        _SandboxedRuntimeLite_initPluginProxy.set(this, () => {
            const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
            vm.newFunction('callPlugin', (...args) => {
                var _a;
                const [service, method, req, meta] = args.map(vm.dump);
                const id = __classPrivateFieldSet(this, _SandboxedRuntimeLite_pluginCallId, (_a = __classPrivateFieldGet(this, _SandboxedRuntimeLite_pluginCallId, "f"), ++_a), "f");
                const promise = vm.newPromise();
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_pluginReturnHandlers, "f")[id] = promise;
                box.postMessage([ActorResponse.PLUGIN_CALL, id, service, method, req, meta]);
                return promise.handle;
            }).consume((fnPtr) => vm.setProp(vm.global, 'callPlugin', fnPtr));
        });
        /**
         * Route `require()` calls to `import`s for pre-packaged dependencies like
         * @devvit/protos. Devvit apps are CJS for compute-go compatibility and to
         * force them to use require() (the unsandboxed runtime can't override
         * import).
         */
        _SandboxedRuntimeLite_initRequire.set(this, () => {
            __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, `globalThis.__imports__ = {};`, 
            // language=javascript
            `function require(module) {
        for(const prefix of Object.keys(globalThis.__imports__)) {
          if (module.startsWith(prefix)) {
            return globalThis.__imports__[prefix];
          }
        }
      }`);
            Object.keys(__classPrivateFieldGet(this, _SandboxedRuntimeLite_modules, "f")).forEach((name) => {
                const importName = name.replaceAll(/[@/._-]/g, '_');
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, `import * as __IMPORT__${importName} from '${name}'; globalThis.__imports__['${name}'] = __IMPORT__${importName};`);
            });
        });
        _SandboxedRuntimeLite_initFetch.set(this, () => {
            __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, "function fetch() { circuitBreak('fetch'); }");
        });
        __classPrivateFieldSet(this, _SandboxedRuntimeLite_modules, {
            '@devvit/protos': protos,
            ...modules,
        }, "f");
    }
    enableAppLogging() {
        __classPrivateFieldSet(this, _SandboxedRuntimeLite_console, new QueueConsole(), "f");
    }
    getCallState() {
        return { logs: __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f").logs ?? [] };
    }
    async init() {
        box.log('[sandboxed runtime] init');
        __classPrivateFieldSet(this, _SandboxedRuntimeLite_runtime, (await wasmModule).newRuntime({
            memoryLimitBytes: 1024 * 1024 * 1024, // 1GB temp limit
            maxStackSizeBytes: 0,
        }), "f");
        __classPrivateFieldSet(this, _SandboxedRuntimeLite_vm, __classPrivateFieldGet(this, _SandboxedRuntimeLite_runtime, "f").newContext(), "f");
        const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
        // to-do: pull in https://github.com/stephenh/ts-proto/issues/732 to fork
        // and remove this line.
        vm.setProp(vm.global, 'self', vm.global);
        vm.runtime.setModuleLoader((module) => {
            if (module in __classPrivateFieldGet(this, _SandboxedRuntimeLite_modules, "f")) {
                return __classPrivateFieldGet(this, _SandboxedRuntimeLite_modules, "f")[module];
            }
            if (module === 'core-js') {
                return corejs;
            }
            if (module === 'intl-polyfill') {
                return intlPolyfill;
            }
            return '';
        });
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_initConsole, "f").call(this);
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, 'import "core-js";');
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_eval).call(this, 'import "intl-polyfill";'); // Warning: long operation
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_initRequire, "f").call(this); // Warning: long operation
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_initImporter, "f").call(this);
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_initCircuitBreaker, "f").call(this);
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_initPluginProxy, "f").call(this);
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_initFetch, "f").call(this);
    }
    setPluginList(plugins) {
        const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_evalGlobal).call(this, vm, `const plugins = ${JSON.stringify(plugins)};`);
    }
    setLocalMetadata(meta) {
        __classPrivateFieldSet(this, _SandboxedRuntimeLite_localMetadata, meta, "f");
    }
    loadBundle(bundle) {
        __classPrivateFieldSet(this, _SandboxedRuntimeLite_dependencyMetadata, getDependencyMetadata(bundle), "f");
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f").logs = [];
        const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
        // to-do: type.
        // language=JavaScript
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_evalGlobal).call(this, vm, 'const module = {};', bundle.code, `const config = {
        assets: ${JSON.stringify(bundle.assets)},
        webviewAssets: ${JSON.stringify(bundle.webviewAssets)},
        use: (def) => {
          if (plugins.includes(def.fullName)) {
            const proxy = {};
            for(const method of Object.values(def.methods)) {
              proxy[method.name] = async (req, metadata) => {
                const result = await callPlugin(def.fullName, method.name, req, metadata);
                return JSON.parse(result);
              }
            }
            return proxy;
          } else {
            const circuitBreaker = {};
            for (const method of Object.values(def.methods)) {
              circuitBreaker[method.name] = () => {
                circuitBreak(method.name);
              };
            }
            return circuitBreaker;
          }
        },
        provides: () => {}
      };`, 
        /**
         * @deprecated This is used by older apps but is no longer needed with newer versions of public-api
         *             Apps using public-api 0.10.22 or newer no longer depend on this global.
         */
        `globalThis.devvit = { config: { assets: config.assets } };`);
        try {
            __classPrivateFieldSet(this, _SandboxedRuntimeLite_actor, vm.unwrapResult(vm.evalCode(`
          if (!module.exports?.default) throw Error('bundle missing default export')
          new module.exports.default(config);
      `)), "f");
        }
        catch (err) {
            __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_logErr).call(this, err);
            throw err;
        }
    }
    async call(method, args, meta) {
        return Scope.withScopeAsync(async (scope) => {
            const vm = scope.manage(__classPrivateFieldGet(this, _SandboxedRuntimeLite_runtime, "f").newContext());
            const actor = __classPrivateFieldGet(this, _SandboxedRuntimeLite_actor, "f");
            __classPrivateFieldSet(this, _SandboxedRuntimeLite_aborted, false, "f");
            __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f").logs = [];
            if (!actor)
                return {
                    error: new Error('No actor loaded'),
                    success: false,
                    value: undefined,
                };
            const funcPtr = vm.getProp(actor, method);
            const argsPtr = __classPrivateFieldGet(this, _SandboxedRuntimeLite_importObject, "f").call(this, vm, args);
            // The order of metadata is intentional here:
            // - Use the local environment metadata first
            // - Allow per-call overrides with call metadata
            // - But do not allow `devvit-dependencies` to be overwritten
            const callMetadata = { ...__classPrivateFieldGet(this, _SandboxedRuntimeLite_localMetadata, "f"), ...meta, ...__classPrivateFieldGet(this, _SandboxedRuntimeLite_dependencyMetadata, "f") };
            const metaPtr = __classPrivateFieldGet(this, _SandboxedRuntimeLite_importObject, "f").call(this, vm, callMetadata);
            const result = vm.callFunction(funcPtr, actor, argsPtr, metaPtr);
            let resultPromisePtr;
            try {
                resultPromisePtr = vm.unwrapResult(result);
            }
            catch (err) {
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_logErr).call(this, err);
                return { error: err, success: false, value: undefined };
            }
            let resolved;
            try {
                // This quirky statement awaits the promise and executes pending jobs
                // without an extra local:
                // 1. Enqueue a promise with resolvePromise().
                // 2. Invoke executePendingJobs() directly (the await is on the outer
                //    promise).
                // 3. executePendingJobs() is synchronous so the result is automatically
                //    promisfied but we don't care as we don't use it, only the result
                //    of resolvePromise().
                [resolved] = await Promise.all([
                    vm.resolvePromise(resultPromisePtr),
                    vm.runtime.executePendingJobs(),
                ]);
            }
            catch (err) {
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_logErr).call(this, err);
                return { error: err, success: false, value: undefined };
            }
            let msgPtr;
            try {
                msgPtr = vm.unwrapResult(resolved);
            }
            catch (err) {
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_instances, "m", _SandboxedRuntimeLite_logErr).call(this, err);
                return { error: err, success: false, value: undefined };
            }
            const response = vm.dump(msgPtr);
            if (__classPrivateFieldGet(this, _SandboxedRuntimeLite_aborted, "f")) {
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f").logs = [];
                // Do not use the response. The circuit break was uncleared which may
                // indicate that the break Error was caught unintentionally.
                return { error: CircuitBreak(method), success: false, value: undefined };
            }
            return { error: undefined, success: true, value: response };
        });
    }
    callBinary() {
        throw new Error('binary calls are unsupported');
    }
    onPluginResponse(id, result) {
        const handler = __classPrivateFieldGet(this, _SandboxedRuntimeLite_pluginReturnHandlers, "f")[id];
        if (handler && handler.alive) {
            if (result.success) {
                const value = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").newString(JSON.stringify(result.value));
                handler.resolve(value);
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").runtime.executePendingJobs();
                value.dispose();
            }
            else {
                const error = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").newError(result.error.message);
                handler.reject(error);
                __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").runtime.executePendingJobs();
                error.dispose();
            }
        }
        handler.dispose();
        delete __classPrivateFieldGet(this, _SandboxedRuntimeLite_pluginReturnHandlers, "f")[id];
    }
    quit() {
        box.log('[sandboxed runtime] quitting');
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_actor, "f")?.dispose();
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_importObjectPtr, "f").dispose();
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f").dispose();
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_runtime, "f").dispose();
        globalThis.close();
    }
}
_SandboxedRuntimeLite_runtime = new WeakMap(), _SandboxedRuntimeLite_vm = new WeakMap(), _SandboxedRuntimeLite_importObjectPtr = new WeakMap(), _SandboxedRuntimeLite_actor = new WeakMap(), _SandboxedRuntimeLite_aborted = new WeakMap(), _SandboxedRuntimeLite_console = new WeakMap(), _SandboxedRuntimeLite_modules = new WeakMap(), _SandboxedRuntimeLite_pluginCallId = new WeakMap(), _SandboxedRuntimeLite_pluginReturnHandlers = new WeakMap(), _SandboxedRuntimeLite_localMetadata = new WeakMap(), _SandboxedRuntimeLite_dependencyMetadata = new WeakMap(), _SandboxedRuntimeLite_importObject = new WeakMap(), _SandboxedRuntimeLite_initConsole = new WeakMap(), _SandboxedRuntimeLite_initImporter = new WeakMap(), _SandboxedRuntimeLite_initCircuitBreaker = new WeakMap(), _SandboxedRuntimeLite_initPluginProxy = new WeakMap(), _SandboxedRuntimeLite_initRequire = new WeakMap(), _SandboxedRuntimeLite_initFetch = new WeakMap(), _SandboxedRuntimeLite_instances = new WeakSet(), _SandboxedRuntimeLite_evalGlobal = function _SandboxedRuntimeLite_evalGlobal(vm, ...args) {
    for (const code of args) {
        vm.unwrapResult(vm.evalCode(code, undefined, { type: 'global' })).dispose();
    }
}, _SandboxedRuntimeLite_eval = function _SandboxedRuntimeLite_eval(...args) {
    const vm = __classPrivateFieldGet(this, _SandboxedRuntimeLite_vm, "f");
    for (const code of args) {
        vm.unwrapResult(vm.evalCode(code)).dispose();
    }
}, _SandboxedRuntimeLite_logErr = function _SandboxedRuntimeLite_logErr(err) {
    var _a;
    // Don't log circuit breaks. The runtime operates optimistically. When
    // it fails, all side-effects are rolled back. Logs should not be reported.
    if (!isCircuitBreaker(err)) {
        (_a = __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f")).logs ?? (_a.logs = []);
        __classPrivateFieldGet(this, _SandboxedRuntimeLite_console, "f").logs.push({ method: 'error', args: [inspect(err)] });
    }
};
