import { action, call, once, race, resource, useScope } from "effection";
import { getframe } from "effection";
export const _driver = Symbol.for("revolution.driver");
export function useServer(options) {
    return resource(function* (provide) {
        let scope = yield* useScope();
        let server;
        let handler = (request) => scope.run(function* () {
            let response = yield* race([
                aborted(request),
                options.handler(request),
            ]);
            //@ts-expect-error it's either there or it's not.
            const driver = response[_driver];
            if (driver) {
                scope.run(function* () {
                    let frame = yield* getframe();
                    Object.assign(frame.context, driver.context);
                    try {
                        yield* race([aborted(request), driver.operation()]);
                    }
                    catch (error) {
                        // See https://github.com/denoland/deno/issues/10829
                        if (error !== "resource closed") {
                            throw error;
                        }
                    }
                });
            }
            return response;
        });
        let info = yield* action(function* (resolve) {
            server = Deno.serve({
                handler,
                port: options.port,
                onListen(info) {
                    resolve(info);
                },
            });
        });
        try {
            yield* provide(info);
        }
        finally {
            yield* call(() => server.shutdown());
        }
    });
}
// this satisfies the async/await API which must return an Repsonse
// because async functions cannot be discarded.
function* aborted(request) {
    if (request.signal.aborted) {
        return responseAborted();
    }
    else {
        yield* once(request.signal, "abort");
        return responseAborted();
    }
}
export function* drive(response, operation) {
    let { context } = yield* getframe();
    return Object.create(response, {
        [_driver]: {
            enumerable: false,
            value: { context, operation },
        },
    });
}
function responseAborted() {
    return new Response("aborted");
}
//# sourceMappingURL=server.js.map