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,
});
const ext = (specifier) => Deno.core.loadExtScript(specifier);
import { fetch, Headers, Request, Response } from "ext:apiplant_js/fetch.js";
const encoding = ext("ext:deno_web/08_text_encoding.js");
const base64 = ext("ext:deno_web/05_base64.js");
const url = ext("ext:deno_web/00_url.js");
const urlPattern = ext("ext:deno_web/01_urlpattern.js");
const timers = ext("ext:deno_web/02_timers.js");
const perf = ext("ext:deno_web/15_performance.js");
const clone = ext("ext:deno_web/02_structured_clone.js");
const file = ext("ext:deno_web/09_file.js");
const fileReader = ext("ext:deno_web/10_filereader.js");
const streams = ext("ext:deno_web/06_streams.js");
const compression = ext("ext:deno_web/14_compression.js");
const domException = ext("ext:deno_web/01_dom_exception.js");
const abort = ext("ext:deno_web/03_abort_signal.js");
const event = ext("ext:deno_web/02_event.js");
const { TextEncoder, TextDecoder, TextEncoderStream, TextDecoderStream } = encoding;
const { atob, btoa } = base64;
const { URL, URLSearchParams } = url;
const { URLPattern } = urlPattern;
const { setTimeout, clearTimeout, setInterval, clearInterval } = timers;
const { performance } = perf;
const { structuredClone } = clone;
const { Blob, File } = file;
const { FileReader } = fileReader;
const { ReadableStream, WritableStream, TransformStream, ReadableStreamDefaultReader,
ByteLengthQueuingStrategy, CountQueuingStrategy } = streams;
const { CompressionStream, DecompressionStream } = compression;
const { DOMException } = domException;
const { AbortController, AbortSignal } = abort;
const { Event, EventTarget, CustomEvent } = event;
Object.defineProperties(globalThis, Object.getOwnPropertyDescriptors({
TextEncoder, TextDecoder, TextEncoderStream, TextDecoderStream, atob, btoa,
URL, URLSearchParams, URLPattern,
setTimeout, clearTimeout, setInterval, clearInterval, performance,
structuredClone, Blob, File, FileReader,
ReadableStream, WritableStream, TransformStream, ReadableStreamDefaultReader,
ByteLengthQueuingStrategy, CountQueuingStrategy,
CompressionStream, DecompressionStream,
Event, EventTarget, CustomEvent, AbortController, AbortSignal, DOMException,
fetch, Headers, Request, Response,
}));
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 });
}
};