"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GetServiceManifest = void 0;
exports.getServiceManifest = getServiceManifest;
const AbortedError_1 = require("../../../base/lib/util/AbortedError");
const Drop_1 = require("../../../base/lib/util/Drop");
const deepEqual_1 = require("../../../base/lib/util/deepEqual");
class GetServiceManifest {
    constructor(effects, packageId, map, eq) {
        this.effects = effects;
        this.packageId = packageId;
        this.map = map;
        this.eq = eq;
    }
    /**
     * Returns the manifest of a service. Reruns the context from which it has been called if the underlying value changes
     */
    async const() {
        let abort = new AbortController();
        const watch = this.watch(abort.signal);
        const res = await watch.next();
        if (this.effects.constRetry) {
            watch
                .next()
                .then(() => {
                abort.abort();
                this.effects.constRetry && this.effects.constRetry();
            })
                .catch();
        }
        return res.value;
    }
    /**
     * Returns the manifest of a service. Does nothing if it changes
     */
    async once() {
        const manifest = await this.effects.getServiceManifest({
            packageId: this.packageId,
        });
        return this.map(manifest);
    }
    async *watchGen(abort) {
        let prev = null;
        const resolveCell = { resolve: () => { } };
        this.effects.onLeaveContext(() => {
            resolveCell.resolve();
        });
        abort?.addEventListener('abort', () => resolveCell.resolve());
        while (this.effects.isInContext && !abort?.aborted) {
            let callback = () => { };
            const waitForNext = new Promise((resolve) => {
                callback = resolve;
                resolveCell.resolve = resolve;
            });
            const next = this.map(await this.effects.getServiceManifest({
                packageId: this.packageId,
                callback: () => callback(),
            }));
            if (!prev || !this.eq(prev.value, next)) {
                prev = { value: next };
                yield next;
            }
            await waitForNext;
        }
        return new Promise((_, rej) => rej(new AbortedError_1.AbortedError()));
    }
    /**
     * Watches the manifest of a service. Returns an async iterator that yields whenever the value changes
     */
    watch(abort) {
        const ctrl = new AbortController();
        abort?.addEventListener('abort', () => ctrl.abort());
        return Drop_1.DropGenerator.of(this.watchGen(ctrl.signal), () => ctrl.abort());
    }
    /**
     * Watches the manifest of a service. Takes a custom callback function to run whenever it changes
     */
    onChange(callback) {
        ;
        (async () => {
            const ctrl = new AbortController();
            for await (const value of this.watch(ctrl.signal)) {
                try {
                    const res = await callback(value);
                    if (res.cancel) {
                        ctrl.abort();
                        break;
                    }
                }
                catch (e) {
                    console.error('callback function threw an error @ GetServiceManifest.onChange', e);
                }
            }
        })()
            .catch((e) => callback(null, e))
            .catch((e) => console.error('callback function threw an error @ GetServiceManifest.onChange', e));
    }
    /**
     * Watches the manifest of a service. Returns when the predicate is true
     */
    waitFor(pred) {
        const ctrl = new AbortController();
        return Drop_1.DropPromise.of(Promise.resolve().then(async () => {
            for await (const next of this.watchGen(ctrl.signal)) {
                if (pred(next)) {
                    return next;
                }
            }
            throw new Error('context left before predicate passed');
        }), () => ctrl.abort());
    }
}
exports.GetServiceManifest = GetServiceManifest;
function getServiceManifest(effects, packageId, map, eq) {
    return new GetServiceManifest(effects, packageId, map ?? ((a) => a), eq ?? ((a, b) => (0, deepEqual_1.deepEqual)(a, b)));
}
//# sourceMappingURL=GetServiceManifest.js.map