const ops = Deno.core.ops;
function host(kind, payload) {
return ops.op_apiplant_host(kind, JSON.stringify(payload ?? null));
}
function hostJson(kind, payload) {
const reply = JSON.parse(host(kind, payload));
if (reply && typeof reply === "object" && !Array.isArray(reply) && "error" in reply) {
throw new Error(reply.error);
}
return reply;
}
class BadRequest extends Error {
constructor(message) {
super(message);
this.name = "BadRequest";
this.request = true;
}
}
const ctx = Object.freeze({
query(sql, params = []) {
return hostJson("query", { sql, params });
},
config() {
return JSON.parse(host("config", null) || "{}");
},
principalId() {
return host("principal_id", null);
},
hook() {
const raw = host("hook", null);
return raw ? JSON.parse(raw) : null;
},
sendEmail(message) {
return hostJson("send_email", message);
},
cache(request) {
return hostJson("cache", request);
},
payments(request) {
return hostJson("payments", request);
},
chat(request) {
const body = typeof request === "string"
? { messages: [{ role: "user", content: request }] }
: request;
return hostJson("ai", body);
},
publish(topic, message) {
return hostJson("publish", { op: "publish", topic, message: message ?? {} });
},
emit(chunk) {
return hostJson("emit", String(chunk)).delivered === true;
},
log: Object.freeze({
trace: (m) => host("log", { level: "trace", message: String(m) }),
debug: (m) => host("log", { level: "debug", message: String(m) }),
info: (m) => host("log", { level: "info", message: String(m) }),
warn: (m) => host("log", { level: "warn", message: String(m) }),
error: (m) => host("log", { level: "error", message: String(m) }),
}),
BadRequest,
});
globalThis.BadRequest = BadRequest;
globalThis.__apiplantInternals = Object.freeze({ host, hostJson, ctx, BadRequest });
globalThis.console = Object.freeze({
log: ctx.log.info,
info: ctx.log.info,
debug: ctx.log.debug,
warn: ctx.log.warn,
error: ctx.log.error,
trace: ctx.log.trace,
});
globalThis.setTimeout = (callback, delay = 0, ...args) =>
Deno.core.createSystemTimer(() => callback(...args), delay, true);
globalThis.setInterval = (callback, delay = 0, ...args) =>
Deno.core.createSystemInterval(() => callback(...args), delay, true);
globalThis.clearTimeout = (id) => {
if (id !== undefined) Deno.core.cancelTimer(id);
};
globalThis.clearInterval = globalThis.clearTimeout;
globalThis.__apiplantModule = null;
function declared() {
const module = globalThis.__apiplantModule;
const bundle = module?.default;
if (bundle && typeof bundle === "object" && bundle.__apiplant) {
return { manifest: bundle.manifest, handlers: bundle.handlers };
}
return { manifest: module?.manifest, handlers: module };
}
globalThis.__apiplantManifest = () => {
const { manifest } = declared();
if (manifest === undefined || manifest === null) return null;
return JSON.stringify(Array.isArray(manifest) ? manifest : [manifest]);
};
globalThis.__apiplantInvoke = async (name, inputJson) => {
const fn = declared().handlers?.[name];
if (typeof fn !== "function") {
return JSON.stringify({
error: `this module exports no function named \`${name}\``,
request: false,
});
}
let input;
try {
input = inputJson === "" ? null : JSON.parse(inputJson);
} catch (e) {
return JSON.stringify({ error: `request body is not valid JSON: ${e}`, request: true });
}
try {
const output = await fn(input, ctx);
return JSON.stringify({ ok: output === undefined ? null : output });
} catch (e) {
const request =
e instanceof BadRequest ||
e?.request === true ||
(typeof e?.status === "number" && e.status >= 400 && e.status < 500);
const error = e instanceof Error ? `${e.message}` : String(e);
return JSON.stringify({ error: error || "function threw", request });
}
};