wip: Construct API

This commit is contained in:
ForestOfLight
2026-05-29 23:27:11 +02:00
Unverified
parent f750710732
commit 2b90020e37
25 changed files with 454 additions and 29 deletions
+92
View File
@@ -0,0 +1,92 @@
import { IPC, PROTO } from "./MCBE-IPC/ipc";
import { APICallerError } from "./Errors/APICallerError";
import { APIErrorEnum } from "./Errors/APIErrorEnum";
import { APIServerError } from "./Errors/APIServerError";
import { APIVersionMismatchError } from "./Errors/APIVersionMismatchError";
import { ReturnModelShell } from "./APIModels";
export class AddonAPI {
#name;
#version;
constructor(name, version) {
this.#name = name;
this.#version = version;
}
get name() {
return this.#name;
}
get version() {
return this.#version;
}
get endpointBase() {
return this.#name + ':';
}
setupController(apiController) {
for (const [endpoint, features] of Object.entries(apiController.endpoints)) {
const {callback, parameterModel, returnModel} = features;
const boundCallback = callback.bind(apiController);
this.#setupEndpoint(endpoint, boundCallback, parameterModel, returnModel);
}
}
#setupEndpoint(endpoint, callback, parameterModel, returnDataModel) {
const returnPacketModel = this.#resolveReturnModel(returnDataModel);
const endpointPath = this.endpointBase + endpoint;
IPC.handle(endpointPath, parameterModel, returnPacketModel, (callPacket) => {
const apiVersion = callPacket.apiVersion;
const parameters = Object.values(callPacket.parameterMap);
return this.#handleCallback(apiVersion, callback, parameters);
});
}
#handleCallback(apiVersion, callback, parameters) {
try {
this.#assertVersionsMatch(apiVersion);
const returnValue = callback(...parameters);
return this.#bundleReturnPacket({ code: APIErrorEnum.Success }, returnValue);
} catch(error) {
if (error instanceof APICallerError)
const errorPacket = this.#resolveErrorPacket(error);
return this.#bundleReturnPacket(errorPacket);
console.error(error);
const apiError = new APIServerError(error);
const errorPacket = this.#resolveErrorPacket(apiError);
return this.#bundleReturnPacket(errorPacket);
}
}
#assertVersionsMatch(versionToCheck) {
if (versionToCheck !== this.version) {
const apiVersionMismatchError = new APIVersionMismatchError(this.version, versionToCheck);
throw new APICallerError(apiVersionMismatchError);
}
}
#resolveReturnModel(returnDataModel) {
let returnModel = { ...ReturnModelShell };
returnModel.data = returnDataModel;
returnModel = PROTO.Object(returnModel);
return returnModel;
}
#bundleReturnPacket(errorPacket, returnValue = void 0) {
return {
apiVersion: this.version,
data: returnValue,
error: errorPacket
};
}
#resolveErrorPacket(error) {
return {
code: error.errorCode,
name: error.thrownError.name,
message: error.thrownError.message
};
}
}
@@ -0,0 +1,13 @@
export class APIController {
#endpoints;
constructor(endpoints) {
if (this.constructor === APIController)
throw new Error("Cannot instantiate abstract class 'APIController'");
this.#endpoints = endpoints;
}
get endpoints() {
return this.#endpoints;
}
}
@@ -0,0 +1,20 @@
import { PROTO } from "./MCBE-IPC/ipc";
export const VoidModel = PROTO.Void;
export const ErrorModel = PROTO.Optional(PROTO.Object({
code: PROTO.Int8,
name: PROTO.Optional(PROTO.String),
message: PROTO.Optional(PROTO.String)
}));
export const ReturnModelShell = {
apiVersion: PROTO.String,
data: void 0,
error: ErrorModel
};
export const CallModelShell = {
apiVersion: PROTO.String,
parameterMap: void 0
};
@@ -0,0 +1,7 @@
import { AddonAPI } from "./API";
import { APIController } from "./APIController";
import { VoidModel } from "./APIModels";
import { APICallerError } from "./Errors/APICallerError";
import { PROTO } from "./MCBE-IPC/ipc";
export { AddonAPI, APIController, VoidModel, APICallerError, PROTO };
@@ -0,0 +1,11 @@
import { APIErrorEnum } from "./APIErrorEnum";
export class APICallerError extends Error {
constructor(error) {
super(error.message);
this.errorName = error.name;
this.errorMessage = error.message;
this.errorCode = APIErrorEnum.Caller;
this.name = "APICallerError";
}
}
@@ -0,0 +1,6 @@
export const APIErrorEnum = Object.freeze({
Unknown: 0,
Success: 1,
Caller: 2,
Server: 3
});
@@ -0,0 +1,10 @@
import { APIErrorEnum } from "./APIErrorEnum";
export class APIServerError extends Error {
constructor(error) {
super(error.message);
this.thrownError = error;
this.errorCode = APIErrorEnum.Server;
this.name = "APIServerError";
}
}
@@ -0,0 +1,6 @@
export class APIVersionMismatchError extends Error {
constructor(serverApiVersion, callerApiVersion) {
super(`API version numbers do not match (${callerApiVersion} != ${serverApiVersion}). Please use API version ${serverApiVersion}.`);
this.name = 'APIVersionMismatchError';
}
}