Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
import {
SAND_PERM_CODES,
SandboxPermissionError,
} from "@suaveplan/api-sandbox-permissions";
import type {
SerializedRequest,
SerializedResponse,
} from "@suaveplan/api-types/sandbox-bridge";
import {
SAND_WASM_BRIDGE_CODES,
SandboxWasmBridgeError,
} from "../error-codes/error-codes.js";
/**
* Shape the bridge factory threads through its `RequestContext` slot.
* Defined locally to avoid leaking the host's `RequestContext<T>`
* generic into this utility — both variants need the same five fields
* and nothing more.
*/
export interface RequestContextLike {
readonly request: Request;
readonly principal: { readonly id: string } | null;
readonly tenant: { readonly id: string } | null;
readonly capabilities: Readonly<Record<string, unknown>>;
readonly requestId: string;
}
/**
* Marshal a `RequestContextLike` into the wire `SerializedRequest`
* shape both variants exchange with their guest plugins.
*/
export async function marshalContextRequest(
ctx: RequestContextLike
): Promise<SerializedRequest> {
const request = ctx.request;
const headers: Array<readonly [string, string]> = [];
request.headers.forEach((value, name) => {
headers.push([name.toLowerCase(), value] as const);
});
let bodyBase64 = "";
if (request.body !== null) {
const buffer = await request.arrayBuffer();
bodyBase64 = bytesToBase64UrlNoPad(new Uint8Array(buffer));
}
return {
url: request.url,
method: request.method.toUpperCase(),
headers,
bodyBase64,
principalId: (ctx.principal === null
? null
: ctx.principal.id) as SerializedRequest["principalId"],
tenantId: (ctx.tenant === null
? null
: ctx.tenant.id) as SerializedRequest["tenantId"],
capabilities: Object.keys(ctx.capabilities),
requestId: ctx.requestId,
timestamp: Date.now(),
};
}
/**
* Convert the wire `SerializedResponse` into a host `Response`. When
* the wire response carries an `errorCode`, surface the matching
* sandbox error (preserving the canonical `SAND_PERM.DENIED` mapping).
*/
export function buildHostResponse(
wire: SerializedResponse,
pluginName: string
): Response {
if (wire.errorCode !== undefined) {
if (wire.errorCode === SAND_PERM_CODES.DENIED) {
throw new SandboxPermissionError(
SAND_PERM_CODES.DENIED,
wire.errorMessage ?? "capability denied",
{ context: { plugin: pluginName } }
);
}
throw new SandboxWasmBridgeError(
SAND_WASM_BRIDGE_CODES.CAPABILITY_DENIED,
wire.errorCode,
{
context: {
plugin: pluginName,
sandboxErrorCode: wire.errorCode,
sandboxMessage: wire.errorMessage ?? "",
},
}
);
}
const headers = new Headers();
for (const [name, value] of wire.headers) headers.append(name, value);
const body = base64UrlNoPadToBytes(wire.bodyBase64);
return new Response(
body.length > 0 ? (body as unknown as BodyInit) : null,
{
status: wire.status,
headers,
}
);
}
function bytesToBase64UrlNoPad(bytes: Uint8Array): string {
let binary = "";
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i] as number);
}
return btoa(binary)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
function base64UrlNoPadToBytes(value: string): Uint8Array {
if (value.length === 0) return new Uint8Array(0);
const padded = value
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(value.length + ((4 - (value.length % 4)) % 4), "=");
const binary = atob(padded);
const out = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
out[i] = binary.charCodeAt(i);
}
return out;
}