import "./go.js";
import { fromBinary as bufFromBinary, toBinary } from "@bufbuild/protobuf";
import { AnySchema, anyIs } from "@bufbuild/protobuf/wkt";
import { CheckResourcesResponseSchema, ConfigSchema, ErrorSchema, PlanResourcesResponseSchema, } from "@cerbos/api/cerbos/cloud/epdp/v2/epdp_pb";
import { AuxDataSchema } from "@cerbos/api/cerbos/engine/v1/engine_pb";
import { CheckResourcesRequestSchema, PlanResourcesRequestSchema, } from "@cerbos/api/cerbos/request/v1/request_pb";
import { Code } from "@cerbos/api/google/rpc/code_pb";
import { ServerError } from "./error.js";
export class Server {
    instance;
    exited;
    static async from(instantiate, config) {
        const go = new Go();
        const instantiated = await instantiate(go.importObject);
        const instance = (instantiated instanceof WebAssembly.Instance
            ? instantiated
            : instantiated.instance);
        return new Server(go, instance, config);
    }
    constructor(go, instance, config) {
        this.instance = instance;
        go.run(instance).then(() => {
            this.exited = new ServerError(Code.INTERNAL, "embedded server has exited");
        }, (error) => {
            this.exited = new ServerError(Code.INTERNAL, error instanceof Error ? error.message : "embedded server has failed", { cause: error });
        });
        if (config) {
            this.configure(config);
        }
    }
    loadRuleTable(ruleTable) {
        const pointer = this.write(ruleTable);
        try {
            const result = this.exports.loadRuleTable(pointer);
            if (result !== 0n) {
                this.decode(ErrorSchema, result);
            }
        }
        finally {
            this.free(pointer);
        }
    }
    checkResources(request, auxData) {
        const requestPointer = this.encode(CheckResourcesRequestSchema, request);
        try {
            const auxDataPointer = this.encode(AuxDataSchema, auxData);
            try {
                return this.decode(CheckResourcesResponseSchema, this.exports.checkResources(requestPointer, auxDataPointer));
            }
            finally {
                this.free(auxDataPointer);
            }
        }
        finally {
            this.free(requestPointer);
        }
    }
    planResources(request, auxData) {
        const requestPointer = this.encode(PlanResourcesRequestSchema, request);
        try {
            const auxDataPointer = this.encode(AuxDataSchema, auxData);
            try {
                return this.decode(PlanResourcesResponseSchema, this.exports.planResources(requestPointer, auxDataPointer));
            }
            finally {
                this.free(auxDataPointer);
            }
        }
        finally {
            this.free(requestPointer);
        }
    }
    configure(config) {
        const pointer = this.encode(ConfigSchema, config);
        try {
            const result = this.exports.configure(pointer);
            if (result !== 0n) {
                this.decode(ErrorSchema, result);
            }
        }
        finally {
            this.free(pointer);
        }
    }
    decode(schema, pointer) {
        try {
            const message = fromBinary(AnySchema, this.slice(pointer));
            if (anyIs(message, ErrorSchema)) {
                throw ServerError.from(fromBinary(ErrorSchema, message.value));
            }
            if (!anyIs(message, schema)) {
                throw new ServerError(Code.INTERNAL, `unexpected response type "${message.typeUrl}"`);
            }
            return fromBinary(schema, message.value);
        }
        finally {
            this.free(pointer);
        }
    }
    encode(schema, message) {
        if (!message) {
            return 0n;
        }
        return this.write(toBinary(schema, message));
    }
    write(data) {
        const pointer = this.exports.alloc(data.length);
        this.slice(pointer).set(data);
        return pointer;
    }
    slice(pointer) {
        return new Uint8Array(this.exports.mem.buffer, Number(pointer >> 32n), Number(pointer & 0xffffffffn));
    }
    get exports() {
        if (this.exited) {
            throw this.exited;
        }
        return this.instance.exports;
    }
    free(pointer) {
        if (!this.exited && pointer !== 0n) {
            this.exports.free(pointer);
        }
    }
}
function fromBinary(schema, bytes) {
    return bufFromBinary(schema, bytes, { readUnknownFields: false });
}
//# sourceMappingURL=server.js.map