Skip to content
Open
2 changes: 1 addition & 1 deletion native/v8-runtime/npm/darwin-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-v8-darwin-arm64",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"license": "Apache-2.0",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
Expand Down
2 changes: 1 addition & 1 deletion native/v8-runtime/npm/darwin-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-v8-darwin-x64",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"license": "Apache-2.0",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
Expand Down
2 changes: 1 addition & 1 deletion native/v8-runtime/npm/linux-arm64-gnu/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-v8-linux-arm64-gnu",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"license": "Apache-2.0",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
Expand Down
2 changes: 1 addition & 1 deletion native/v8-runtime/npm/linux-x64-gnu/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-v8-linux-x64-gnu",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"license": "Apache-2.0",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
Expand Down
2 changes: 1 addition & 1 deletion native/v8-runtime/npm/win32-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-v8-win32-x64",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"license": "Apache-2.0",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firestart-secure-exec-monorepo",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"private": true,
"license": "Apache-2.0",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-core",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"type": "module",
"license": "Apache-2.0",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/nodejs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-nodejs",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"type": "module",
"license": "Apache-2.0",
"main": "./dist/index.js",
Expand Down
14 changes: 5 additions & 9 deletions packages/nodejs/src/bridge-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4145,16 +4145,12 @@ async function maybeDecompressHttpBody(
}

function shouldEncodeHttpBodyAsBinary(
urlString: string,
headers: http.IncomingHttpHeaders,
_urlString: string,
_headers: http.IncomingHttpHeaders,
): boolean {
const contentType = headers["content-type"] || "";
const headerValue = Array.isArray(contentType) ? contentType.join(", ") : contentType;
return (
headerValue.includes("octet-stream") ||
headerValue.includes("gzip") ||
urlString.endsWith(".tgz")
);
// Always encode response bodies as base64 across the bridge to prevent
// binary corruption. The isolate decodes via Buffer.from(body, "base64").
return true;
}

/**
Expand Down
114 changes: 92 additions & 22 deletions packages/nodejs/src/bridge/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,17 @@ function encodeFetchBody(
body: string,
bodyEncoding: string | null,
): Uint8Array {
if (bodyEncoding === "base64" && typeof Buffer !== "undefined") {
return new Uint8Array(Buffer.from(body, "base64"));
if (bodyEncoding === "base64") {
if (typeof Buffer !== "undefined") {
return new Uint8Array(Buffer.from(body, "base64"));
}
// Fallback base64 decode via atob
const binary = atob(body);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
if (typeof TextEncoder !== "undefined") {
return new TextEncoder().encode(body);
Expand Down Expand Up @@ -504,16 +513,22 @@ export async function fetch(input: string | URL | Request, options: FetchOptions
await sendBytes(bytes);
}
} else {
// Node.js Readable stream
// Node.js Readable stream — pause/resume for backpressure
await new Promise<void>((resolve, reject) => {
const stream = rawBody as any;
let pending: Promise<void> = Promise.resolve();
stream.on("data", (data: any) => {
const bytes = typeof data === "string"
? new TextEncoder().encode(data)
: data instanceof Uint8Array ? data : new Uint8Array(data);
sendBytes(bytes);
stream.pause();
pending = pending.then(() => sendBytes(bytes)).then(() => {
stream.resume();
});
});
stream.on("end", () => {
pending.then(() => resolve(), reject);
});
stream.on("end", () => resolve());
stream.on("error", (err: any) => reject(err));
});
}
Expand Down Expand Up @@ -635,16 +650,27 @@ export async function fetch(input: string | URL | Request, options: FetchOptions
},

async text(): Promise<string> {
if (bodyEncoding === "base64" && typeof Buffer !== "undefined") {
return Buffer.from(responseBody, "base64").toString("utf8");
if (bodyEncoding === "base64") {
if (typeof Buffer !== "undefined") {
return Buffer.from(responseBody, "base64").toString("utf8");
}
const binary = atob(responseBody);
return new TextDecoder().decode(Uint8Array.from(binary, c => c.charCodeAt(0)));
}
return responseBody;
},
async json(): Promise<unknown> {
const textBody =
bodyEncoding === "base64" && typeof Buffer !== "undefined"
? Buffer.from(responseBody, "base64").toString("utf8")
: responseBody;
let textBody: string;
if (bodyEncoding === "base64") {
if (typeof Buffer !== "undefined") {
textBody = Buffer.from(responseBody, "base64").toString("utf8");
} else {
const binary = atob(responseBody);
textBody = new TextDecoder().decode(Uint8Array.from(binary, c => c.charCodeAt(0)));
}
} else {
textBody = responseBody;
}
return JSON.parse(textBody || "{}");
},
async arrayBuffer(): Promise<ArrayBuffer> {
Expand Down Expand Up @@ -767,7 +793,7 @@ export class Request {

// Response class
export class Response {
private _body: string | null;
private _body: string | Uint8Array | null;
status: number;
statusText: string;
headers: Headers;
Expand All @@ -776,8 +802,19 @@ export class Response {
url: string;
redirected: boolean;

constructor(body?: string | null, init: { status?: number; statusText?: string; headers?: Record<string, string> } = {}) {
this._body = body || null;
constructor(body?: string | ReadableStream | ArrayBuffer | Uint8Array | null, init: { status?: number; statusText?: string; headers?: Record<string, string> } = {}) {
if (body === null || body === undefined) {
this._body = null;
} else if (typeof body === "string") {
this._body = body;
} else if (body instanceof Uint8Array) {
this._body = body;
} else if (body instanceof ArrayBuffer) {
this._body = new Uint8Array(body);
} else {
// ReadableStream — store reference, will be consumed lazily
this._body = body as any;
}
this.status = init.status || 200;
this.statusText = init.statusText || "OK";
this.headers = new Headers(init.headers);
Expand All @@ -787,34 +824,67 @@ export class Response {
this.redirected = false;
}

private async _bytes(): Promise<Uint8Array> {
const body = this._body;
if (body === null) return new Uint8Array(0);
if (body instanceof Uint8Array) return body;
if (typeof body === "string") return new TextEncoder().encode(body);
// ReadableStream
const reader = (body as any).getReader();
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value instanceof Uint8Array ? value : new Uint8Array(value));
}
const total = chunks.reduce((s: number, c: Uint8Array) => s + c.length, 0);
const result = new Uint8Array(total);
let off = 0;
for (const c of chunks) { result.set(c, off); off += c.length; }
// Cache for subsequent calls
(this as any)._body = result;
return result;
}

async text(): Promise<string> {
return String(this._body || "");
const bytes = await this._bytes();
return new TextDecoder().decode(bytes);
}

async json(): Promise<unknown> {
return JSON.parse(this._body || "{}");
return JSON.parse(await this.text());
}

async arrayBuffer(): Promise<ArrayBuffer> {
const bytes = await this._bytes();
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
}

async blob(): Promise<Blob> {
const bytes = await this._bytes();
return new Blob([bytes as any], { type: this.headers.get("content-type") || "" });
}

get body(): { getReader(): { read(): Promise<{ done: boolean; value?: Uint8Array }> } } | null {
const bodyStr = this._body;
if (bodyStr === null) return null;
const self = this;
if (this._body === null) return null;
return {
getReader() {
let consumed = false;
return {
async read() {
if (consumed) return { done: true };
if (consumed) return { done: true as const };
consumed = true;
const encoder = new TextEncoder();
return { done: false, value: encoder.encode(bodyStr) };
const bytes = await self._bytes();
return { done: false as const, value: bytes };
},
};
},
};
}

clone(): Response {
return new Response(this._body, { status: this.status, statusText: this.statusText });
return new Response(this._body as any, { status: this.status, statusText: this.statusText });
}

static error(): Response {
Expand Down
2 changes: 1 addition & 1 deletion packages/secure-exec/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"type": "module",
"license": "Apache-2.0",
"main": "./dist/index.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"private": true,
"type": "module",
"dependencies": {
"hono": "4.7.5"
"hono": "4.12.18"
}
}
2 changes: 1 addition & 1 deletion packages/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@secure-exec/typescript",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"private": true,
"type": "module",
"license": "Apache-2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/v8/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firestartorg/secure-exec-v8",
"version": "0.2.2",
"version": "0.2.3-rc.4",
"type": "module",
"license": "Apache-2.0",
"main": "./dist/index.js",
Expand Down
Loading