Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion native/v8-runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ fn read_v8_version(lock_path: &Path) -> String {

fn find_v8_icu_data(v8_version: &str) -> PathBuf {
let registry_src = cargo_home().join("registry").join("src");
// Order matters: prefer full ICU data (flutter_desktop) over the
// stripped-down common variant which only supports English and causes
// "Internal error. Icu error." for non-English locales.
let candidates = [
Path::new("third_party/icu/common/icudtl.dat"),
Path::new("third_party/icu/flutter_desktop/icudtl.dat"),
Path::new("third_party/icu/common/icudtl.dat"),
Path::new("third_party/icu/chromecast_video/icudtl.dat"),
];

Expand Down
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.5",
"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.5",
"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.5",
"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.5",
"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.5",
"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.5",
"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.5",
"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.5",
"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.5",
"type": "module",
"license": "Apache-2.0",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/secure-exec/tests/projects/uuid-pass/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"private": true,
"type": "module",
"dependencies": {
"uuid": "11.1.0"
"uuid": "14.0.0"
}
}
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.5",
"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.5",
"type": "module",
"license": "Apache-2.0",
"main": "./dist/index.js",
Expand Down
Loading