import { getRamCost } from "./RamCostGenerator"; import type { WorkerScript } from "./WorkerScript"; import { helpers } from "./NetscriptHelpers"; import { ScriptArg } from "./ScriptArg"; import { NSFull } from "src/NetscriptFunctions"; import { cloneDeep } from "lodash"; /** Generic type for an enums object */ type Enums = Record>; /** Permissive type for the documented API functions */ type APIFn = (...args: any[]) => void; /** Type for the actual wrapped function given to the player */ type WrappedFn = (...args: unknown[]) => unknown; /** Type for internal, unwrapped ctx function that produces an APIFunction */ type InternalFn = (ctx: NetscriptContext) => ((...args: unknown[]) => ReturnType) & F; type Key = keyof API & string; export type ExternalAPI = { [key in keyof API]: API[key] extends Enums ? Enums : key extends "args" ? ScriptArg[] // "args" required to be ScriptArg[] : API[key] extends APIFn ? WrappedFn : ExternalAPI; }; export type InternalAPI = { [key in keyof API]: API[key] extends Enums ? API[key] & Enums : key extends "args" ? ScriptArg[] : API[key] extends APIFn ? InternalFn : InternalAPI; }; /** Any of the possible values on a internal API layer */ type InternalValues = Enums | ScriptArg[] | InternalFn | InternalAPI; export type NetscriptContext = { workerScript: WorkerScript; function: string; functionPath: string; }; export function wrapAPI(ws: WorkerScript, internalAPI: InternalAPI, args: ScriptArg[]): ExternalAPI { function wrapAPILayer(eLayer: ExternalAPI, iLayer: InternalAPI, tree: string[]): ExternalAPI { for (const [key, value] of Object.entries(iLayer) as [Key, InternalValues][]) { if (key === "enums") { (eLayer[key] as Enums) = cloneDeep(value as Enums); } else if (key === "args") continue; // Args are added in wrapAPI function and should only exist at top level else if (typeof value === "function") { wrapFunction(eLayer, value as InternalFn, tree, key); } else if (typeof value === "object") { wrapAPILayer((eLayer[key] = {} as ExternalAPI[Key]), value, [...tree, key as string]); } else { console.warn(`Unexpected data while wrapping API.`, "tree:", tree, "key:", key, "value:", value); throw new Error("Error while wrapping netscript API. See console."); } } return eLayer; } function wrapFunction(eLayer: ExternalAPI, func: InternalFn, tree: string[], key: Key) { const arrayPath = [...tree, key]; const functionPath = arrayPath.join("."); const ctx = { workerScript: ws, function: key, functionPath }; function wrappedFunction(...args: unknown[]): unknown { helpers.checkEnvFlags(ctx); helpers.updateDynamicRam(ctx, getRamCost(...tree, key)); return func(ctx)(...args); } (eLayer[key] as WrappedFn) = wrappedFunction; } const wrappedAPI = wrapAPILayer({ args } as ExternalAPI, internalAPI, []); return wrappedAPI; }