Skip to content

Commit cce0b17

Browse files
committed
fix(wasm): export error constants from rust to ensure consistency with js polyfill
1 parent a6380a5 commit cce0b17

File tree

6 files changed

+136
-35
lines changed

6 files changed

+136
-35
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ serde_path_to_error = "0.1.16"
4444
simd-json = "0.17.0"
4545
thiserror = "1.0"
4646
tokio = { version = "1.47.1" }
47-
tokio-fs-ext = "0.7.4"
47+
tokio-fs-ext = "0.7.5"
4848
toml = "0.8"
4949
tracing = "0.1.41"
5050
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "fmt"] }

crates/utoo-wasm/src/errors.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,55 @@
1+
use std::io;
12
use wasm_bindgen::prelude::*;
23

4+
// Helper macro to define constants and their JS getters (exported as functions)
5+
macro_rules! define_error_consts {
6+
($($name:ident, $fn_name:ident = $val:expr),* $(,)?) => {
7+
$(
8+
// Internal Rust constant
9+
pub const $name: &str = $val;
10+
11+
// Exported JS function returning the string
12+
#[wasm_bindgen(js_name = $name)]
13+
pub fn $fn_name() -> String {
14+
$name.to_string()
15+
}
16+
)*
17+
};
18+
}
19+
20+
define_error_consts! {
21+
ERR_NOT_FOUND, err_not_found = "NotFoundError",
22+
ERR_KEY_ALREADY_EXISTS, err_key_already_exists = "NoModificationAllowedError",
23+
ERR_NOT_ALLOWED, err_not_allowed = "NotAllowedError",
24+
ERR_NO_MODIFICATION_ALLOWED, err_no_modification_allowed = "NoModificationAllowedError",
25+
ERR_TYPE_MISMATCH, err_type_mismatch = "TypeMismatchError",
26+
ERR_QUOTA_EXCEEDED, err_quota_exceeded = "QuotaExceededError",
27+
ERR_INVALID_STATE, err_invalid_state = "InvalidStateError",
28+
ERR_ABORT, err_abort = "AbortError",
29+
}
30+
331
pub fn to_js_error<E>(e: E) -> JsError
432
where
533
E: Into<anyhow::Error>,
634
{
7-
JsError::new(&format!("{:#?}", e.into()))
35+
let err = e.into();
36+
37+
if let Some(io_err) = err.downcast_ref::<io::Error>() {
38+
let prefix = match io_err.kind() {
39+
io::ErrorKind::NotFound => ERR_NOT_FOUND,
40+
io::ErrorKind::PermissionDenied => ERR_NOT_ALLOWED,
41+
io::ErrorKind::WouldBlock => ERR_NO_MODIFICATION_ALLOWED,
42+
io::ErrorKind::InvalidData => ERR_TYPE_MISMATCH,
43+
io::ErrorKind::StorageFull => ERR_QUOTA_EXCEEDED,
44+
io::ErrorKind::InvalidInput => ERR_INVALID_STATE,
45+
io::ErrorKind::Interrupted => ERR_ABORT,
46+
_ => "",
47+
};
48+
49+
if !prefix.is_empty() {
50+
return JsError::new(&format!("{}: {}", prefix, io_err));
51+
}
52+
}
53+
54+
JsError::new(&format!("{:#?}", err))
855
}

next.js

packages/utoo-web/src/utoo/index.d.ts

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ export class DirEntry {
2525
type: DirEntryType;
2626
}
2727

28+
export function ERR_ABORT(): string;
29+
30+
export function ERR_INVALID_STATE(): string;
31+
32+
export function ERR_KEY_ALREADY_EXISTS(): string;
33+
34+
export function ERR_NOT_ALLOWED(): string;
35+
36+
export function ERR_NOT_FOUND(): string;
37+
38+
export function ERR_NO_MODIFICATION_ALLOWED(): string;
39+
40+
export function ERR_QUOTA_EXCEEDED(): string;
41+
42+
export function ERR_TYPE_MISMATCH(): string;
43+
2844
export class Fs {
2945
private constructor();
3046
free(): void;
@@ -178,19 +194,8 @@ export function workerCreated(worker_id: number): void;
178194
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
179195

180196
export interface InitOutput {
181-
readonly __wbg_project_free: (a: number, b: number) => void;
182-
readonly project_build: () => any;
183-
readonly project_cwd: () => [number, number];
184-
readonly project_deps: (a: number, b: number, c: number) => any;
185-
readonly project_entrypointsSubscribe: (a: any) => any;
186-
readonly project_gzip: (a: any) => any;
187-
readonly project_hmrEvents: (a: number, b: number, c: any) => any;
188-
readonly project_init: (a: number, b: number) => void;
189-
readonly project_install: (a: number, b: number, c: number) => any;
190-
readonly project_setCwd: (a: number, b: number) => void;
191-
readonly project_sigMd5: (a: any) => any;
192-
readonly project_updateInfoSubscribe: (a: number, b: any) => void;
193-
readonly project_writeAllToDisk: (a: any) => void;
197+
readonly initLogFilter: (a: number, b: number) => void;
198+
readonly init_pack: () => void;
194199
readonly __wbg_direntry_free: (a: number, b: number) => void;
195200
readonly __wbg_fs_free: (a: number, b: number) => void;
196201
readonly __wbg_get_direntry_name: (a: number) => [number, number];
@@ -222,13 +227,32 @@ export interface InitOutput {
222227
readonly fs_write: (a: number, b: number, c: any) => any;
223228
readonly fs_writeString: (a: number, b: number, c: number, d: number) => any;
224229
readonly fs_writeSync: (a: number, b: number, c: any) => [number, number];
225-
readonly getWasmMemory: () => any;
226-
readonly getWasmModule: () => any;
227-
readonly initLogFilter: (a: number, b: number) => void;
228-
readonly init_pack: () => void;
229230
readonly __wbg_roottask_free: (a: number, b: number) => void;
230231
readonly registerWorkerScheduler: (a: any, b: any) => void;
231232
readonly workerCreated: (a: number) => void;
233+
readonly __wbg_project_free: (a: number, b: number) => void;
234+
readonly project_build: () => any;
235+
readonly project_cwd: () => [number, number];
236+
readonly project_deps: (a: number, b: number, c: number) => any;
237+
readonly project_entrypointsSubscribe: (a: any) => any;
238+
readonly project_gzip: (a: any) => any;
239+
readonly project_hmrEvents: (a: number, b: number, c: any) => any;
240+
readonly project_init: (a: number, b: number) => void;
241+
readonly project_install: (a: number, b: number, c: number) => any;
242+
readonly project_setCwd: (a: number, b: number) => void;
243+
readonly project_sigMd5: (a: any) => any;
244+
readonly project_updateInfoSubscribe: (a: number, b: any) => void;
245+
readonly project_writeAllToDisk: (a: any) => void;
246+
readonly getWasmMemory: () => any;
247+
readonly getWasmModule: () => any;
248+
readonly ERR_ABORT: () => [number, number];
249+
readonly ERR_INVALID_STATE: () => [number, number];
250+
readonly ERR_KEY_ALREADY_EXISTS: () => [number, number];
251+
readonly ERR_NOT_ALLOWED: () => [number, number];
252+
readonly ERR_NOT_FOUND: () => [number, number];
253+
readonly ERR_NO_MODIFICATION_ALLOWED: () => [number, number];
254+
readonly ERR_QUOTA_EXCEEDED: () => [number, number];
255+
readonly ERR_TYPE_MISMATCH: () => [number, number];
232256
readonly rust_mi_get_default_heap: () => number;
233257
readonly rust_mi_get_thread_id: () => number;
234258
readonly rust_mi_set_default_heap: (a: number) => void;
@@ -254,16 +278,16 @@ export interface InitOutput {
254278
readonly __wbg_createsyncaccesshandleoptions_free: (a: number, b: number) => void;
255279
readonly wasm_thread_entry_point: (a: number) => void;
256280
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_core_97b1fb32719f2dc8___ops__function__FnMut_____Output_______: (a: number, b: number) => void;
281+
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_core_97b1fb32719f2dc8___ops__function__Fn__js_sys_8afca1662fec7f75___Array____Output_______: (a: number, b: number) => void;
257282
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_core_97b1fb32719f2dc8___ops__function__FnMut__wasm_bindgen_4243f1f505d3e2fc___JsValue____Output_______: (a: number, b: number) => void;
258283
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_core_97b1fb32719f2dc8___ops__function__FnMut__web_sys_95f4c484d1daab41___features__gen_MessageEvent__MessageEvent____Output_______: (a: number, b: number) => void;
259284
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_core_97b1fb32719f2dc8___ops__function__FnMut_____Output________1_: (a: number, b: number) => void;
260285
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_for__a__core_97b1fb32719f2dc8___ops__function__FnMut____a_web_sys_95f4c484d1daab41___features__gen_MessageEvent__MessageEvent____Output_______: (a: number, b: number) => void;
261-
readonly wasm_bindgen_4243f1f505d3e2fc___closure__destroy___dyn_core_97b1fb32719f2dc8___ops__function__Fn__js_sys_8afca1662fec7f75___Array____Output_______: (a: number, b: number) => void;
262286
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke___js_sys_8afca1662fec7f75___Function__js_sys_8afca1662fec7f75___Function_____: (a: number, b: number, c: any, d: any) => void;
287+
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke___js_sys_8afca1662fec7f75___Array_____: (a: number, b: number, c: any) => void;
263288
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke___wasm_bindgen_4243f1f505d3e2fc___JsValue_____: (a: number, b: number, c: any) => void;
264289
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke___web_sys_95f4c484d1daab41___features__gen_MessageEvent__MessageEvent_____: (a: number, b: number, c: any) => void;
265290
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures________invoke___web_sys_95f4c484d1daab41___features__gen_MessageEvent__MessageEvent_____: (a: number, b: number, c: any) => void;
266-
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke___js_sys_8afca1662fec7f75___Array_____: (a: number, b: number, c: any) => void;
267291
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke______: (a: number, b: number) => void;
268292
readonly wasm_bindgen_4243f1f505d3e2fc___convert__closures_____invoke_______1_: (a: number, b: number) => void;
269293
readonly memory: WebAssembly.Memory;

packages/utoo-web/src/webpackLoaders/polyfills/fsPolyfill/utils.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
import path from "path";
2+
import {
3+
ERR_ABORT,
4+
ERR_INVALID_STATE,
5+
ERR_NO_MODIFICATION_ALLOWED,
6+
ERR_NOT_ALLOWED,
7+
ERR_NOT_FOUND,
8+
ERR_QUOTA_EXCEEDED,
9+
ERR_TYPE_MISMATCH,
10+
} from "../../../utoo";
211
import { workerData } from "../workerThreadsPolyfill";
312

413
export function resolvePath(p: string): string {
@@ -17,11 +26,11 @@ export function getFs() {
1726
}
1827

1928
export function translateError(error: any, path: string, syscall: string) {
20-
const message = (error.message || String(error)).toLowerCase();
29+
const message = error.message || String(error);
2130

2231
// 1. NotFound (ENOENT)
2332
// Mapping "NotFoundError" from tokio-fs-ext
24-
if (message.includes("notfounderror") || message.includes("notfound")) {
33+
if (message.includes(ERR_NOT_FOUND())) {
2534
const e = new Error(
2635
`ENOENT: no such file or directory, ${syscall} '${path}'`,
2736
);
@@ -34,7 +43,7 @@ export function translateError(error: any, path: string, syscall: string) {
3443

3544
// 2. Directory error (Mapped to EISDIR)
3645
// Mapping "TypeMismatchError" or "type mismatch" from tokio-fs-ext
37-
if (message.includes("typemismatcherror") || message.includes("type mismatch")) {
46+
if (message.includes(ERR_TYPE_MISMATCH())) {
3847
const e = new Error(
3948
`EISDIR: illegal operation on a directory, ${syscall} '${path}'`,
4049
);
@@ -47,10 +56,7 @@ export function translateError(error: any, path: string, syscall: string) {
4756

4857
// 3. Locking/Concurrency (Mapped to EAGAIN/EBUSY)
4958
// Mapping "NoModificationAllowedError" from tokio-fs-ext
50-
if (
51-
message.includes("nomodificationallowederror") ||
52-
message.includes("wouldblock")
53-
) {
59+
if (message.includes(ERR_NO_MODIFICATION_ALLOWED())) {
5460
const e = new Error(
5561
`EAGAIN: resource temporarily unavailable, ${syscall} '${path}'`,
5662
);
@@ -62,8 +68,8 @@ export function translateError(error: any, path: string, syscall: string) {
6268
}
6369

6470
// 4. Permission Denied (EACCES)
65-
// Mapping "NotAllowedError" or "SecurityError" from tokio-fs-ext
66-
if (message.includes("notallowederror") || message.includes("securityerror")) {
71+
// Mapping "NotAllowedError" from tokio-fs-ext
72+
if (message.includes(ERR_NOT_ALLOWED())) {
6773
const e = new Error(`EACCES: permission denied, ${syscall} '${path}'`);
6874
(e as any).errno = -13;
6975
(e as any).code = "EACCES";
@@ -74,14 +80,38 @@ export function translateError(error: any, path: string, syscall: string) {
7480

7581
// 5. Storage Full (ENOSPC)
7682
// Mapping "QuotaExceededError" from tokio-fs-ext
77-
if (message.includes("quotaexceedederror")) {
78-
const e = new Error(`ENOSPC: no space left on device, ${syscall} '${path}'`);
83+
if (message.includes(ERR_QUOTA_EXCEEDED())) {
84+
const e = new Error(
85+
`ENOSPC: no space left on device, ${syscall} '${path}'`,
86+
);
7987
(e as any).errno = -28;
8088
(e as any).code = "ENOSPC";
8189
(e as any).syscall = syscall;
8290
(e as any).path = path;
8391
return e;
8492
}
8593

94+
// 6. Invalid Argument (EINVAL)
95+
// Mapping "InvalidStateError" from tokio-fs-ext
96+
if (message.includes(ERR_INVALID_STATE())) {
97+
const e = new Error(`EINVAL: invalid argument, ${syscall} '${path}'`);
98+
(e as any).errno = -22;
99+
(e as any).code = "EINVAL";
100+
(e as any).syscall = syscall;
101+
(e as any).path = path;
102+
return e;
103+
}
104+
105+
// 7. Interrupted (EINTR)
106+
// Mapping "AbortError" from tokio-fs-ext
107+
if (message.includes(ERR_ABORT())) {
108+
const e = new Error(`EINTR: interrupted system call, ${syscall} '${path}'`);
109+
(e as any).errno = -4;
110+
(e as any).code = "EINTR";
111+
(e as any).syscall = syscall;
112+
(e as any).path = path;
113+
return e;
114+
}
115+
86116
return error;
87117
}

0 commit comments

Comments
 (0)