Current section
Files
Jump to
Current section
Files
src/deno.ffi.mjs
import { Result$Ok, Result$Error } from "./gleam.mjs";
import { Option$None } from "../gleam_stdlib/gleam/option.mjs";
import { adapt } from "./adapt.ffi.mjs";
import {
// error
FileError$IsDir,
FileError$NoEntry,
FileError$NoAccess,
FileError$UnknownFileError,
//
File$File,
started,
} from "./smol.mjs";
import { upgrade } from "./smol.ffi.mjs";
import {
Builder$Builder$handler,
Builder$Builder$port,
Builder$Builder$interface,
Builder$Builder$on_error,
Builder$Builder$after_start,
} from "./smol/internal/builder.mjs";
export function start(builder) {
const handler = adapt(Builder$Builder$handler(builder));
return new Promise((resolve) => {
const server = Deno.serve({
port: Builder$Builder$port(builder),
hostname: Builder$Builder$interface(builder),
async handler(req) {
const response = await handler(req);
if (response.body[upgrade]) {
const runtime = response.body[upgrade];
const { socket, response: wsResponse } = Deno.upgradeWebSocket(req);
runtime.send = (message) => socket.send(message);
runtime.close = () => socket.close();
socket.addEventListener("open", runtime.onOpen);
socket.addEventListener("message", (event) =>
runtime.onMessage(event.data),
);
socket.addEventListener("close", runtime.onClose);
return wsResponse;
} else {
return response;
}
},
onListen({ port, hostname }) {
let disposed = false;
const stop = async () => {
if (disposed) {
return;
}
disposed = true;
await server.shutdown();
};
// TODO: doesn't seem like Deno lets us bind to SIGINT?
Builder$Builder$after_start(builder)(port, hostname);
resolve(started(port, hostname, stop));
},
onError(error) {
Builder$Builder$on_error(builder)(error.stack, error);
return new Response("Internal Server Error", { status: 500 });
},
});
});
}
export async function from_file(path, offset, limit) {
try {
const file = await Deno.open(path);
const fileInfo = await file.stat();
if (fileInfo.isDirectory) {
return Result$Error(FileError$IsDir());
}
await file.seek(offset, Deno.SeekMode.Start);
const stream =
limit > 0
? file.readable.pipeThrough(
new TransformStream({
transform(chunk, controller) {
if (chunk.byteLength > limit) {
chunk = chunk.slice(0, limit);
controller.enqueue(chunk);
controller.terminate();
} else {
controller.enqueue(chunk);
limit -= chunk.byteLength;
}
},
}),
)
: file.readable;
return Result$Ok(File$File(stream, fileInfo.size, Option$None()));
} catch (e) {
if (e.code === "ENOENT") {
return Result$Error(FileError$NoEntry());
} else if (e.code === "EACCES") {
return Result$Error(FileError$NoAccess());
} else {
return Result$Error(FileError$UnknownFileError());
}
}
}