@wasm-oj/server 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,13 +65,39 @@ package. The generated dependency report covers every package in the pinned
65
65
  normal Cargo dependency graph selected for `wasm32-unknown-unknown`; its compact
66
66
  inventory binds all 332 package identities and the exact HTML report digest.
67
67
 
68
+ ### QuickJS guest Node standard I/O libraries
69
+
70
+ The generated guest prelude in `src/runtime/quickjs/stdlib.generated.ts` bundles
71
+ the following exact npm releases. `scripts/build-quickjs-stdlib.mjs` builds this
72
+ prelude from the locked packages and the WASM-OJ standard I/O adapters.
73
+
74
+ | Component | Source release | License |
75
+ | --- | --- | --- |
76
+ | buffer 6.0.3 | <https://github.com/feross/buffer/tree/v6.0.3> | MIT |
77
+ | readable-stream 4.7.0 | <https://github.com/nodejs/readable-stream/tree/v4.7.0> | MIT |
78
+ | events 3.3.0 | <https://github.com/Gozala/events/tree/v3.3.0> | MIT |
79
+ | abort-controller 3.0.0 | <https://github.com/mysticatea/abort-controller/tree/v3.0.0> | MIT |
80
+ | event-target-shim 5.0.1 | <https://github.com/mysticatea/event-target-shim/tree/v5.0.1> | MIT |
81
+ | base64-js 1.5.1 | <https://github.com/beatgammit/base64-js/tree/v1.5.1> | MIT |
82
+ | ieee754 1.2.1 | <https://github.com/feross/ieee754/tree/v1.2.1> | BSD-3-Clause |
83
+ | string_decoder 1.3.0 | <https://github.com/nodejs/string_decoder/tree/v1.3.0> | MIT |
84
+ | safe-buffer 5.2.1 | <https://github.com/feross/safe-buffer/tree/v5.2.1> | MIT |
85
+
86
+ The complete upstream license texts, including the Node-derived stream and
87
+ decoder notices, are reproduced in `licenses/quickjs-node-stdlib-LICENSES.txt`
88
+ (SHA-256 `38e42dcb48e476e4e836552c814c911eb5464d80b56b970d87aa07b88c7b28f8`).
89
+ `licenses/components.json` binds each release to the generated bundle digest;
90
+ `pnpm-lock.yaml` pins all package integrity values. The Buffer declarations are
91
+ also copied from the same buffer 6.0.3 release. These guest libraries implement
92
+ the supported byte buffers and standard streams; they do not include Node.js.
93
+
68
94
  ## Distributed toolchain assets
69
95
 
70
96
  ### TypeScript-Go
71
97
 
72
98
  - Distributed asset: `typescript-7.0.2.wasm.gz.bin`
73
- - Compressed SHA-256: `06e58ce887d95d1895055699b8dc96a1cde7d1f2baa48de40f9b790e3271dc16`
74
- - Expanded Wasm SHA-256: `27c931087b3b0cc48804f3749b07eb710538c4ab175969db8a8ff680e6ec4fde`
99
+ - Compressed SHA-256: `29c6ee0e46151e2644049ae96162b1b1d46dcb13310e2a928114bb9030c3ea92`
100
+ - Expanded Wasm SHA-256: `01cea8c18841b73e0601d31859be1dcd323e61d83e7cfb15523dd051fc78c8da`
75
101
  - Source revision: `microsoft/typescript-go@2bd066d87f5bafd315be9f40889d0a60b9e58e0b`
76
102
  - Source: <https://github.com/microsoft/typescript-go/tree/2bd066d87f5bafd315be9f40889d0a60b9e58e0b>
77
103
  - Build runtime: Go 1.26.3 standard library (`GOOS=wasip1`, `GOARCH=wasm`)
@@ -101,29 +127,29 @@ byte-identical to `go/LICENSE` in the exact Go 1.26.5 source distribution.
101
127
 
102
128
  ### Java compiler toolchain
103
129
 
104
- - Distributed assets: `java-teavm-0.13.1.wasi.compiler.webc.gz.bin`,
130
+ - Distributed assets: `java-teavm-0.13.1.compiler.wasm`,
105
131
  `java-teavm-0.13.1.compile-classlib.bin`, and
106
132
  `java-teavm-0.13.1.runtime-classlib.bin`
107
133
  - TeaVM Java compiler source: <https://github.com/konsoletyper/teavm-javac/tree/7e4a44cf521694a4e326e33850dd8aec165eb5c9>
108
134
  - TeaVM core source: `konsoletyper/teavm@b3a245b7d9034ff35cdfab2def057a3d4f256efb`
109
- plus the pinned WASI compiler and math-intrinsic patch
110
- - Compiler package compressed SHA-256:
111
- `129f1f51d591e58954f88787d36396b856a9a68ba3ae9c9d14f20bd67c2c7722`
112
- - Compiler package expanded SHA-256:
113
- `f8f86761cf31062565187e4a66f73b6903f257fe84c0ce70ea1cd28441b6c2e9`
114
- - Licenses: TeaVM Apache-2.0; OpenJDK class library GPL-2.0 with Classpath
115
- Exception
116
- - License material: `licenses/Apache-2.0.txt` and
117
- `licenses/openjdk-21-GPL-2.0-with-Classpath-exception.txt`
118
-
119
- The compiler emits standalone `wasip1` modules. The class-library archives are
120
- mounted only during compilation and are not included in submitted artifacts.
135
+ with source patches and build provenance in `tools/java-client/`
136
+ - Compiler SHA-256: `8c37bce1c6fceeaffeffe93e10834309b67c8b6fb58c6f8a24547afe8be67f5d`
137
+ - Android Scanner and Spliterators source: `platform/libcore@de876a01b29230b877c9f408348c38a90ee724a5`
138
+ - Licenses: TeaVM Apache-2.0; OpenJDK and Android class libraries GPL-2.0
139
+ with Classpath exception
140
+ - License material: `licenses/Apache-2.0.txt`,
141
+ `licenses/openjdk-21-GPL-2.0-with-Classpath-exception.txt`, and
142
+ `licenses/android-libcore-GPL-2.0-with-Classpath-exception.txt`
143
+
144
+ The WasmGC compiler uses statically packaged JavaScript bindings and emits
145
+ standalone `wasip1` student modules. TeaVM links reachable class-library code
146
+ into those modules; the full class-library archives are compiler inputs.
121
147
 
122
148
  ### QuickJS-ng
123
149
 
124
150
  - Distributed asset: `quickjs-0.15.1.wasm.gz.bin`
125
- - Compressed SHA-256: `8c7f0588210490e7d77f198fc91f72c1b94787ab4c359c4786ca59a363c4f5e8`
126
- - Expanded Wasm SHA-256: `956bf2b3700690e1817034eb8e063cfd9781c66b4bffa244ef4f9445656ccfa1`
151
+ - Compressed SHA-256: `dc6e02e8610269e61b341331661515616451cec929fd534fa96d1c4f47fbcc9a`
152
+ - Expanded Wasm SHA-256: `a098db08592626781f6ad6e63f1b8a36da6d03b85b530dc94c441a348426b20b`
127
153
  - Source revision: `quickjs-ng/quickjs@fd0a0210b7be00957751871e7e01b8291268fc29`
128
154
  - Source: <https://github.com/quickjs-ng/quickjs/tree/fd0a0210b7be00957751871e7e01b8291268fc29>
129
155
  - Build SDK: [WASI SDK 24.0 release archive for arm64 macOS](https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-24/wasi-sdk-24.0-arm64-macos.tar.gz),
@@ -240,25 +266,25 @@ source trees, so the inventory reuses the exact digest-bound `b9ef79d7` copies;
240
266
  the revision-specific root and musl-fts notices remain separate. The generated
241
267
  WebC and pinned command manifests do not relicense those payloads.
242
268
 
243
- ### CPython 3.14.6 for WASI P1
269
+ ### CPython 3.14.7 for WASI P1
244
270
 
245
- - Distributed assets: `python-3.14.6-wasip1.webc.gz.bin` and its provenance
246
- manifest `python-3.14.6-wasip1.manifest.json`
247
- - Official source archive: <https://www.python.org/ftp/python/3.14.6/Python-3.14.6.tar.xz>,
248
- SHA-256 `143b1dddefaec3bd2e21e3b839b34a2b7fb9842272883c576420d605e9f30c63`
249
- - Official SPDX document: <https://www.python.org/ftp/python/3.14.6/Python-3.14.6.tar.xz.spdx.json>,
250
- SHA-256 `1f5d394856783fa77e1f1db280f84eabf693bffc1fb06a747f7116de9f99f3bd`
271
+ - Distributed assets: `python-3.14.7-wasip1.webc.gz.bin` and its provenance
272
+ manifest `python-3.14.7-wasip1.manifest.json`
273
+ - Official source archive: <https://www.python.org/ftp/python/3.14.7/Python-3.14.7.tar.xz>,
274
+ SHA-256 `3b48dac8fb59f62eaa67ac83c1eb12bda1b7a08406dd286e252c11a66be27f81`
275
+ - Official SPDX document: <https://www.python.org/ftp/python/3.14.7/Python-3.14.7.tar.xz.spdx.json>,
276
+ SHA-256 `87f55ca6c59fe159fa8c47ba2d7d8bec39cc649b1d3f47c667f34025a2ca9a68`
251
277
  - Expanded WebC SHA-256:
252
- `454ffc53936aa13a0d7f4afbb5bd50ada339c8ebd04bbf27ea19e4104cf43207`
278
+ `a42c98f5f00582638c8e445440c506bbf94d5e55b3415d1f8d82d11c56cd4b56`
253
279
  - Distributed gzip SHA-256:
254
- `218cd20ac4abb443e0700816010a615a345a43eae623a0232da2227135a6c7a6`
280
+ `10027f0e32c77dfa0ce1c413b6cb27307d6744fe5e18e6da8cea738bd020260c`
255
281
  - Provenance manifest SHA-256:
256
- `054eccad04a7cee7ba1661062142ef0d639976850981eab8fc785f48eb26129e`
282
+ `53a24b258363a8494af164121111a34b49c85fa0e054627fe131feba6f359cd5`
257
283
  - Deterministically exported `WOJFS002` runtime archive SHA-256:
258
- `44d894f91487f20c2bb04fe496a9343db37d8720fb706472c2b4a7f3300db039`
284
+ `c1acac884af1c86833db5b65f8cc0a2304fce74dea0a05d9b40bde0a4e3e7c0e`
259
285
  - CPython license: Python Software Foundation License Version 2 and the
260
286
  historical notices reproduced with it
261
- - License text: `licenses/cpython-3.14.6-PSF-2.0.txt`
287
+ - License text: `licenses/cpython-3.14.7-PSF-2.0.txt`
262
288
  - Bundled third-party source: Expat 2.8.1 (MIT), HACL* revision
263
289
  `8ba599b2f6c9701b3dc961db895b0856a2210f76` (MIT), and libmpdec 2.5.1
264
290
  (BSD-2-Clause); their exact notices are
@@ -270,7 +296,8 @@ WebC and pinned command manifests do not relicense those payloads.
270
296
  `26a1d6601d727a96f4301d0d8647b5a42760ae0c`, and wasi-libc revision
271
297
  `b9ef79d7dbd47c6c5bafdae760823467c2f60b70`.
272
298
 
273
- WASM-OJ compiles this package from the pinned official sources, disables
299
+ WASM-OJ compiles this package from the pinned official sources with a 4 MiB C
300
+ stack and 16 MiB initial linear memory, retaining `--stack-first`, disables
274
301
  `_socket`, removes test/development-only standard-library roots, canonicalizes
275
302
  build-only sysconfig paths, and packages the complete source SPDX document and
276
303
  applicable CPython, third-party, compiler-rt, WASI SDK, and wasi-libc notices
@@ -1,4 +1,7 @@
1
- use wasmer::{AsStoreMut, ExternType, Function, Imports, Module, RuntimeError};
1
+ use wasmer::{
2
+ AsStoreMut, ExternType, Function, FunctionEnv, FunctionEnvMut, FunctionType, Imports, Instance,
3
+ Module, RuntimeError, Type,
4
+ };
2
5
 
3
6
  /// Returns whether an import exposes host state that WASM-OJ deliberately keeps
4
7
  /// outside the deterministic judge contract.
@@ -63,14 +66,77 @@ pub(crate) fn attach_capability_denials(
63
66
  for (namespace, name, function_type) in denied {
64
67
  let capability = format!("{namespace}.{name}");
65
68
  let error_message = format!("WASM-OJ denied nondeterministic capability {capability}");
66
- let denial = Function::new(store, function_type, move |_| {
67
- Err(RuntimeError::new(error_message.clone()))
68
- });
69
+ let denial = capability_trap(store, &function_type, error_message)?;
69
70
  imports.define(&namespace, &name, denial);
70
71
  }
71
72
  Ok(())
72
73
  }
73
74
 
75
+ fn deny_capability(env: FunctionEnvMut<String>) -> Result<(), RuntimeError> {
76
+ Err(RuntimeError::new(env.data().clone()))
77
+ }
78
+
79
+ fn capability_trap(
80
+ store: &mut impl AsStoreMut,
81
+ signature: &FunctionType,
82
+ message: String,
83
+ ) -> Result<Function, String> {
84
+ use wasm_encoder::{
85
+ CodeSection, EntityType, ExportKind, ExportSection, FunctionSection, ImportSection,
86
+ Instruction, TypeSection, ValType,
87
+ };
88
+
89
+ let value_type = |ty: &Type| match ty {
90
+ Type::I32 => ValType::I32,
91
+ Type::I64 => ValType::I64,
92
+ Type::F32 => ValType::F32,
93
+ Type::F64 => ValType::F64,
94
+ Type::V128 => ValType::V128,
95
+ Type::ExternRef => ValType::EXTERNREF,
96
+ Type::FuncRef => ValType::FUNCREF,
97
+ Type::ExceptionRef => ValType::EXNREF,
98
+ };
99
+ let mut types = TypeSection::new();
100
+ types.ty().function([], []);
101
+ types.ty().function(
102
+ signature.params().iter().map(value_type),
103
+ signature.results().iter().map(value_type),
104
+ );
105
+ let mut imports = ImportSection::new();
106
+ imports.import("host", "deny", EntityType::Function(0));
107
+ let mut functions = FunctionSection::new();
108
+ functions.function(1);
109
+ let mut exports = ExportSection::new();
110
+ exports.export("deny", ExportKind::Func, 1);
111
+ let mut body = wasm_encoder::Function::new([]);
112
+ body.instruction(&Instruction::Call(0));
113
+ body.instruction(&Instruction::Unreachable);
114
+ body.instruction(&Instruction::End);
115
+ let mut code = CodeSection::new();
116
+ code.function(&body);
117
+ let mut wasm = wasm_encoder::Module::new();
118
+ wasm.section(&types)
119
+ .section(&imports)
120
+ .section(&functions)
121
+ .section(&exports)
122
+ .section(&code);
123
+
124
+ // Wasmer's dynamic host functions evaluate JavaScript under the web backend.
125
+ // A Wasm stub preserves any declared signature and calls a CSP-safe typed trap.
126
+ let module =
127
+ Module::new(&store.as_store_ref(), wasm.finish()).map_err(|error| error.to_string())?;
128
+ let env = FunctionEnv::new(&mut *store, message);
129
+ let trap = Function::new_typed_with_env(&mut *store, &env, deny_capability);
130
+ let mut imports = Imports::new();
131
+ imports.define("host", "deny", trap);
132
+ let instance = Instance::new(store, &module, &imports).map_err(|error| error.to_string())?;
133
+ instance
134
+ .exports
135
+ .get_function("deny")
136
+ .cloned()
137
+ .map_err(|error| error.to_string())
138
+ }
139
+
74
140
  #[cfg(test)]
75
141
  mod tests {
76
142
  use super::is_denied_capability;
@@ -46,7 +46,11 @@ pub use types::{
46
46
  };
47
47
 
48
48
  pub fn run_response(request: RunRequest) -> RunResponse {
49
- match run(request) {
49
+ run_response_from_result(run(request))
50
+ }
51
+
52
+ pub(crate) fn run_response_from_result(result: Result<RunResult, RunError>) -> RunResponse {
53
+ match result {
50
54
  Ok(result) => RunResponse {
51
55
  ok: true,
52
56
  result: Some(result),
@@ -22,7 +22,16 @@ pub fn run(request: RunRequest) -> Result<RunResult, RunError> {
22
22
  #[cfg(not(target_arch = "wasm32"))]
23
23
  return native::run(request);
24
24
  #[cfg(target_arch = "wasm32")]
25
- return web::run(request);
25
+ return web::run(request, |_| Ok(()));
26
+ }
27
+
28
+ #[cfg(target_arch = "wasm32")]
29
+ pub(crate) fn run_observed(
30
+ request: RunRequest,
31
+ on_execution: impl FnMut(bool) -> Result<(), RunError>,
32
+ ) -> Result<RunResult, RunError> {
33
+ validate(&request)?;
34
+ web::run(request, on_execution)
26
35
  }
27
36
 
28
37
  fn validate(request: &RunRequest) -> Result<(), RunError> {
@@ -14,7 +14,10 @@ use wasmer_wasix::{
14
14
  Pipe, WasiEnv, WasiError, WasiModuleInstanceHandles, WasiModuleTreeHandles, wasmer_wasix_types,
15
15
  };
16
16
 
17
- pub fn run(request: RunRequest) -> Result<RunResult, RunError> {
17
+ pub fn run(
18
+ request: RunRequest,
19
+ mut on_execution: impl FnMut(bool) -> Result<(), RunError>,
20
+ ) -> Result<RunResult, RunError> {
18
21
  let limited = enforce_memory_limit(&request.wasm, request.resources.memory_limit_bytes)
19
22
  .map_err(RunError::Compile)?;
20
23
  let metered = instrument_wasm(&limited, request.resources.instruction_budget)
@@ -110,6 +113,7 @@ pub fn run(request: RunRequest) -> Result<RunResult, RunError> {
110
113
  .exports
111
114
  .get_function("_start")
112
115
  .map_err(|error| RunError::Compile(format!("module has no _start function: {error}")))?;
116
+ on_execution(true)?;
113
117
  let execution = if executable.has_deferred_start {
114
118
  let initializer = instance
115
119
  .exports
@@ -125,6 +129,8 @@ pub fn run(request: RunRequest) -> Result<RunResult, RunError> {
125
129
  start.call(&mut store, &[])
126
130
  };
127
131
 
132
+ on_execution(false)?;
133
+
128
134
  let stdout = stdout_capture.bytes();
129
135
  let stderr = stderr_capture.bytes();
130
136
  let mut output_exceeded = stdout_capture.exceeded() || stderr_capture.exceeded();
@@ -1,5 +1,7 @@
1
1
  use std::sync::{Arc, Mutex};
2
- use wasmer::{AsStoreMut, Extern, Function, FunctionEnv, Imports, Memory, RuntimeError, Value};
2
+ use wasmer::{
3
+ AsStoreMut, Extern, Function, FunctionEnv, FunctionEnvMut, Imports, Memory, RuntimeError, Value,
4
+ };
3
5
  use wasmer_wasix::wasmer_wasix_types::wasi::Filetype;
4
6
 
5
7
  struct StdioEnv {
@@ -17,7 +19,6 @@ pub(crate) fn attach_redirected_stdio(
17
19
  else {
18
20
  continue;
19
21
  };
20
- let signature = original.ty(store);
21
22
  let env = FunctionEnv::new(
22
23
  store,
23
24
  StdioEnv {
@@ -25,32 +26,123 @@ pub(crate) fn attach_redirected_stdio(
25
26
  memory: memory.clone(),
26
27
  },
27
28
  );
28
- imports.define(
29
- namespace,
30
- "fd_fdstat_get",
31
- Function::new_with_env(store, &env, signature, |mut env, arguments| {
32
- let result = env.data().original.clone().call(&mut env, arguments)?;
33
- if matches!(arguments.first(), Some(Value::I32(0..=2)))
34
- && matches!(result.as_ref(), [Value::I32(0)])
35
- {
36
- let offset = match arguments[1] {
37
- Value::I32(offset) => u64::from(offset as u32),
38
- Value::I64(offset) => offset as u64,
39
- _ => return Err(RuntimeError::new("Invalid fdstat pointer type")),
40
- };
41
- let memory = env.data().memory.lock().unwrap();
42
- let memory = memory
43
- .as_ref()
44
- .ok_or_else(|| RuntimeError::new("Missing WASI memory"))?;
45
- // Wasmer hardcodes stdio as CharacterDevice even when backed by pipes.
46
- // WASI has no FIFO filetype; only replace the first fdstat field.
47
- memory
48
- .view(&env)
49
- .write(offset, &[Filetype::Unknown as u8])
50
- .map_err(|error| RuntimeError::new(error.to_string()))?;
29
+ let function = if namespace == "wasix_64v1" {
30
+ Function::new_typed_with_env(store, &env, redirected_fdstat_64)
31
+ } else {
32
+ Function::new_typed_with_env(store, &env, redirected_fdstat_32)
33
+ };
34
+ imports.define(namespace, "fd_fdstat_get", function);
35
+ }
36
+ }
37
+
38
+ fn redirected_fdstat_32(
39
+ env: FunctionEnvMut<StdioEnv>,
40
+ fd: i32,
41
+ offset: i32,
42
+ ) -> Result<i32, RuntimeError> {
43
+ redirected_fdstat(env, fd, Value::I32(offset), u64::from(offset as u32))
44
+ }
45
+
46
+ fn redirected_fdstat_64(
47
+ env: FunctionEnvMut<StdioEnv>,
48
+ fd: i32,
49
+ offset: i64,
50
+ ) -> Result<i32, RuntimeError> {
51
+ redirected_fdstat(env, fd, Value::I64(offset), offset as u64)
52
+ }
53
+
54
+ fn redirected_fdstat(
55
+ mut env: FunctionEnvMut<StdioEnv>,
56
+ fd: i32,
57
+ pointer: Value,
58
+ offset: u64,
59
+ ) -> Result<i32, RuntimeError> {
60
+ let original = env.data().original.clone();
61
+ let result = original.call(&mut env, &[Value::I32(fd), pointer])?;
62
+ let [Value::I32(errno)] = result.as_ref() else {
63
+ return Err(RuntimeError::new("WASI fdstat returned an invalid result"));
64
+ };
65
+ if (0..=2).contains(&fd) && *errno == 0 {
66
+ let memory = env.data().memory.lock().unwrap();
67
+ let memory = memory
68
+ .as_ref()
69
+ .ok_or_else(|| RuntimeError::new("Missing WASI memory"))?;
70
+ // Wasmer hardcodes stdio as CharacterDevice even when backed by pipes.
71
+ // WASI has no FIFO filetype; only replace the first fdstat field.
72
+ memory
73
+ .view(&env)
74
+ .write(offset, &[Filetype::Unknown as u8])
75
+ .map_err(|error| RuntimeError::new(error.to_string()))?;
76
+ }
77
+ Ok(*errno)
78
+ }
79
+
80
+ #[cfg(test)]
81
+ mod tests {
82
+ use super::*;
83
+ use wasmer::{MemoryType, Store};
84
+
85
+ fn original_32(env: FunctionEnvMut<Memory>, fd: i32, offset: i32) -> i32 {
86
+ original(env, fd, u64::from(offset as u32))
87
+ }
88
+
89
+ fn original_64(env: FunctionEnvMut<Memory>, fd: i32, offset: i64) -> i32 {
90
+ original(env, fd, offset as u64)
91
+ }
92
+
93
+ fn original(env: FunctionEnvMut<Memory>, fd: i32, offset: u64) -> i32 {
94
+ if fd == 999 {
95
+ return 8;
96
+ }
97
+ let mut stat = [0x5a; 24];
98
+ stat[0] = Filetype::CharacterDevice as u8;
99
+ env.data().view(&env).write(offset, &stat).unwrap();
100
+ 0
101
+ }
102
+
103
+ #[test]
104
+ fn redirects_only_successful_stdio_filetype_and_preserves_flags_and_rights() {
105
+ for namespace in ["wasi_snapshot_preview1", "wasix_32v1", "wasix_64v1"] {
106
+ let mut store = Store::default();
107
+ let memory = Memory::new(&mut store, MemoryType::new(1, None, false)).unwrap();
108
+ let env = FunctionEnv::new(&mut store, memory.clone());
109
+ let original = if namespace == "wasix_64v1" {
110
+ Function::new_typed_with_env(&mut store, &env, original_64)
111
+ } else {
112
+ Function::new_typed_with_env(&mut store, &env, original_32)
113
+ };
114
+ let mut imports = Imports::new();
115
+ imports.define(namespace, "fd_fdstat_get", original);
116
+ attach_redirected_stdio(
117
+ &mut store,
118
+ &mut imports,
119
+ &Arc::new(Mutex::new(Some(memory.clone()))),
120
+ );
121
+ let Some(Extern::Function(stat)) = imports.get_export(namespace, "fd_fdstat_get")
122
+ else {
123
+ panic!("missing fdstat wrapper");
124
+ };
125
+ for fd in [0, 1, 2, 3, 999] {
126
+ memory.view(&store).write(32, &[0x5a; 24]).unwrap();
127
+ let pointer = if namespace == "wasix_64v1" {
128
+ Value::I64(32)
129
+ } else {
130
+ Value::I32(32)
131
+ };
132
+ let result = stat.call(&mut store, &[Value::I32(fd), pointer]).unwrap();
133
+ assert!(
134
+ matches!(result.as_ref(), [Value::I32(errno)] if *errno == if fd == 999 { 8 } else { 0 })
135
+ );
136
+ let mut actual = [0; 24];
137
+ memory.view(&store).read(32, &mut actual).unwrap();
138
+ let mut expected = [0x5a; 24];
139
+ if fd <= 2 {
140
+ expected[0] = Filetype::Unknown as u8;
141
+ } else if fd == 3 {
142
+ expected[0] = Filetype::CharacterDevice as u8;
51
143
  }
52
- Ok(result.into_vec())
53
- }),
54
- );
144
+ assert_eq!(actual, expected, "{namespace} fd {fd}");
145
+ }
146
+ }
55
147
  }
56
148
  }
@@ -1,16 +1,21 @@
1
1
  use crate::{
2
2
  GoCompilerSession as CoreGoCompilerSession, GoCompilerSessionConfig, GoCompilerSessionRequest,
3
- InteractiveRequest, RunRequest, interactive_response, run_response,
3
+ InteractiveRequest, RunError, RunRequest, interactive_response, run_response_from_result,
4
4
  };
5
5
  use serde::Serialize;
6
6
  use wasm_bindgen::prelude::*;
7
7
 
8
8
  #[wasm_bindgen]
9
- pub fn run_wasm_oj(request: JsValue) -> Result<JsValue, JsValue> {
9
+ pub fn run_wasm_oj(request: JsValue, on_execution: js_sys::Function) -> Result<JsValue, JsValue> {
10
10
  console_error_panic_hook::set_once();
11
11
  let request: RunRequest = serde_wasm_bindgen::from_value(request)
12
12
  .map_err(|error| JsValue::from_str(&format!("invalid run request: {error}")))?;
13
- let response = run_response(request);
13
+ let response = run_response_from_result(crate::run::run_observed(request, |running| {
14
+ on_execution
15
+ .call1(&JsValue::UNDEFINED, &JsValue::from_bool(running))
16
+ .map(|_| ())
17
+ .map_err(|error| RunError::Runtime(format!("execution observer failed: {error:?}")))
18
+ }));
14
19
  response
15
20
  .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
16
21
  .map_err(|error| JsValue::from_str(&format!("failed to serialize run response: {error}")))
@@ -1,4 +1,4 @@
1
- import { _ as GO_STANDARD_LIBRARY_SHA256, c as GO_COMPILER_SHA256, d as GO_LINKER_SHA256, f as GO_PACKAGE_ASSET_PATH, g as GO_STANDARD_LIBRARY_ASSET_PATH, h as GO_PACKAGE_SHA256, l as GO_COMPRESSED_PACKAGE_SHA256, m as GO_PACKAGE_MANIFEST_SHA256, p as GO_PACKAGE_MANIFEST_ASSET_PATH, u as GO_COMPRESSED_STANDARD_LIBRARY_SHA256, v as GO_VERSION } from "./toolchains-C6KuA1yM.js";
1
+ import { _ as GO_STANDARD_LIBRARY_SHA256, c as GO_COMPILER_SHA256, d as GO_LINKER_SHA256, f as GO_PACKAGE_ASSET_PATH, g as GO_STANDARD_LIBRARY_ASSET_PATH, h as GO_PACKAGE_SHA256, l as GO_COMPRESSED_PACKAGE_SHA256, m as GO_PACKAGE_MANIFEST_SHA256, p as GO_PACKAGE_MANIFEST_ASSET_PATH, u as GO_COMPRESSED_STANDARD_LIBRARY_SHA256, v as GO_VERSION } from "./toolchains-uz3XbZGI.js";
2
2
  import { WASM_OJ_SCHEMAS } from "@wasm-oj/contracts";
3
3
  //#region src/compiler/dependency-input.ts
4
4
  var ECOSYSTEM_BY_LANGUAGE = Object.freeze({
@@ -22,21 +22,9 @@ function projectDependencyPackages(project, ecosystem) {
22
22
  return project.dependencies?.packages.filter((item) => item.package.ecosystem === ecosystem) ?? [];
23
23
  }
24
24
  function pythonDependencyFiles(project) {
25
- const sourceFiles = [];
26
- const artifactFiles = {};
27
- for (const item of projectDependencyPackages(project, "pypi")) for (const [path, bytes] of Object.entries(item.files)) {
28
- const installedPath = `site-packages/${path}`;
29
- artifactFiles[installedPath] = bytes.slice();
30
- if (path.endsWith(".py")) sourceFiles.push({
31
- path: installedPath,
32
- language: "python",
33
- content: decodeDependencyText(bytes, item.package.id, path)
34
- });
35
- }
36
- return {
37
- sourceFiles,
38
- artifactFiles
39
- };
25
+ const files = {};
26
+ for (const item of projectDependencyPackages(project, "pypi")) for (const [path, bytes] of Object.entries(item.files)) files[`site-packages/${path}`] = bytes.slice();
27
+ return files;
40
28
  }
41
29
  function npmDependencyFiles(project) {
42
30
  const files = {};
@@ -0,0 +1,16 @@
1
+ //#region src/server/process-keepalive.mjs
2
+ /**
3
+ * Keep Node alive while a promise is backed only by a foreign async runtime.
4
+ * Top-level await does not itself create a referenced libuv handle, so Node can
5
+ * otherwise exit with code 13 while a Wasmer SDK operation is still pending.
6
+ */
7
+ async function withProcessKeepalive(promise) {
8
+ const keepalive = setInterval(() => void 0, 6e4);
9
+ try {
10
+ return await promise;
11
+ } finally {
12
+ clearInterval(keepalive);
13
+ }
14
+ }
15
+ //#endregion
16
+ export { withProcessKeepalive as t };
@@ -1,4 +1,4 @@
1
- import { F as RUST_PACKAGE_MANIFEST_ASSET_PATH, I as RUST_PACKAGE_MANIFEST_SHA256, L as RUST_PACKAGE_SHA256, N as RUST_COMPRESSED_PACKAGE_SHA256, P as RUST_PACKAGE_ASSET_PATH, R as RUST_VERSION } from "./toolchains-C6KuA1yM.js";
1
+ import { F as RUST_PACKAGE_SHA256, I as RUST_VERSION, M as RUST_PACKAGE_ASSET_PATH, N as RUST_PACKAGE_MANIFEST_ASSET_PATH, P as RUST_PACKAGE_MANIFEST_SHA256, j as RUST_COMPRESSED_PACKAGE_SHA256 } from "./toolchains-uz3XbZGI.js";
2
2
  /**
3
3
  * Accept a mounted output only after byte-identical snapshots have persisted for
4
4
  * the complete stability interval. This prevents a valid WebAssembly prefix
@@ -12,37 +12,37 @@ var CLANG_COMPRESSED_PACKAGE_SHA256 = "7f10d90b8e52b270f04874641a1d0bf9e94e85b4f
12
12
  /** SHA-256 after browser-side gzip decompression. */
13
13
  var CLANG_PACKAGE_SHA256 = "21ded33b9c6d4e1aaad5528c940bdaf6c3e84be77ea8f522f018ca7289a2a224";
14
14
  /** SHA-256 of the pinned cc1/wasm-ld argv manifest; regenerated by scripts/pin-clang-cc1-argv.mjs. */
15
- var CLANG_CC1_PINS_SHA256 = "66c4604dccd3f89d8e1472bf4432367d7396cce4a01279b1a1db445f229dba72";
15
+ var CLANG_CC1_PINS_SHA256 = "895eee17c63b1112c84adcc4ddbcb38e0c18e53bff15f50a8689b9b82d87b1ff";
16
16
  var CLANG_MANIFEST_SHA256 = "6382dcdfb6a2da49032a0e08da3b1fb490eb24432be85c3c12e3e871a5065273";
17
17
  /** Updated atomically with the generated toolchain-admitted PCH assets. */
18
- var CLANG_LIBCXX_PCH_MANIFEST_SHA256 = "d126c99e951a7302d4ea2b66da4ed64d3d74e9d319d562518867c8d8c97a06b8";
18
+ var CLANG_LIBCXX_PCH_MANIFEST_SHA256 = "9bbe41d77c786f1c36169369c909268530b05726e1581683f6edd9a2983eebd0";
19
19
  var CLANG_LIBCXX_PCH = Object.freeze({
20
20
  "cpp-debug": Object.freeze({
21
21
  path: CLANG_LIBCXX_PCH_DEBUG_ASSET_PATH,
22
- sha256: "05a4db721448ba33e65977cb4ac366908ea215ab377e3b6d09f1fd224d515b68",
23
- compressedSha256: "a4152027d248412eca8aec3e7e23f6f7c81f95170cae9fd385bcf02e57e91fc9"
22
+ sha256: "c420c4643c48826643e75585d5bfdc281f11883d4bda28b283e3c706f3046b3a",
23
+ compressedSha256: "a3811351bae3289caf096f2d4bb6d2ee5db84b97e05514e87190d36b65c500cf"
24
24
  }),
25
25
  "cpp-release": Object.freeze({
26
26
  path: CLANG_LIBCXX_PCH_RELEASE_ASSET_PATH,
27
- sha256: "aec9e778ac527c120ba369b9b0e142cf8206d11365801208847ba0e1c39daa1a",
28
- compressedSha256: "18f4ca8ab8ca7888db572ba34146fc1acb213a7e7305000ea6285188f52f99f4"
27
+ sha256: "c4b2132cc6d698e97dd1386431fdb54faaaf38b35b2528c7bc854f860d1f64b1",
28
+ compressedSha256: "ef004fc05ec50af697a37f7c9c814bf62e9676d0de162d6ac35305533939f93f"
29
29
  })
30
30
  });
31
- var PYTHON_VERSION = "3.14.6";
32
- var PYTHON_PACKAGE_SHA256 = "454ffc53936aa13a0d7f4afbb5bd50ada339c8ebd04bbf27ea19e4104cf43207";
31
+ var PYTHON_VERSION = "3.14.7";
32
+ var PYTHON_PACKAGE_SHA256 = "a42c98f5f00582638c8e445440c506bbf94d5e55b3415d1f8d82d11c56cd4b56";
33
33
  var PYTHON_PACKAGE = `${`wasm-oj/cpython@${PYTHON_VERSION}`}#sha256:${PYTHON_PACKAGE_SHA256}`;
34
34
  var PYTHON_PACKAGE_ASSET_PATH = `/toolchains/python-${PYTHON_VERSION}-wasip1.webc.gz.bin`;
35
35
  var PYTHON_PACKAGE_MANIFEST_ASSET_PATH = `/toolchains/python-${PYTHON_VERSION}-wasip1.manifest.json`;
36
- var PYTHON_COMPRESSED_PACKAGE_SHA256 = "218cd20ac4abb443e0700816010a615a345a43eae623a0232da2227135a6c7a6";
37
- var PYTHON_PACKAGE_MANIFEST_SHA256 = "054eccad04a7cee7ba1661062142ef0d639976850981eab8fc785f48eb26129e";
38
- var PYTHON_RUNTIME_FILES_ARCHIVE_SHA256 = "44d894f91487f20c2bb04fe496a9343db37d8720fb706472c2b4a7f3300db039";
36
+ var PYTHON_COMPRESSED_PACKAGE_SHA256 = "10027f0e32c77dfa0ce1c413b6cb27307d6744fe5e18e6da8cea738bd020260c";
37
+ var PYTHON_PACKAGE_MANIFEST_SHA256 = "53a24b258363a8494af164121111a34b49c85fa0e054627fe131feba6f359cd5";
38
+ var PYTHON_RUNTIME_FILES_ARCHIVE_SHA256 = "c1acac884af1c86833db5b65f8cc0a2304fce74dea0a05d9b40bde0a4e3e7c0e";
39
39
  var QUICKJS_VERSION = "0.15.1";
40
40
  var QUICKJS_PACKAGE = `wasm-oj/quickjs-ng@${QUICKJS_VERSION}`;
41
41
  var QUICKJS_ASSET_PATH = `/toolchains/quickjs-${QUICKJS_VERSION}.wasm.gz.bin`;
42
- var QUICKJS_ASSET_SHA256 = "8c7f0588210490e7d77f198fc91f72c1b94787ab4c359c4786ca59a363c4f5e8";
42
+ var QUICKJS_ASSET_SHA256 = "dc6e02e8610269e61b341331661515616451cec929fd534fa96d1c4f47fbcc9a";
43
43
  var TYPESCRIPT_VERSION = "7.0.2";
44
44
  var TYPESCRIPT_ASSET_PATH = `/toolchains/typescript-${TYPESCRIPT_VERSION}.wasm.gz.bin`;
45
- var TYPESCRIPT_ASSET_SHA256 = "06e58ce887d95d1895055699b8dc96a1cde7d1f2baa48de40f9b790e3271dc16";
45
+ var TYPESCRIPT_ASSET_SHA256 = "29c6ee0e46151e2644049ae96162b1b1d46dcb13310e2a928114bb9030c3ea92";
46
46
  var RUST_VERSION = "1.91.1-dev";
47
47
  var RUST_PACKAGE_ASSET_PATH = `/toolchains/rust-${RUST_VERSION}.webc.gz.bin`;
48
48
  var RUST_PACKAGE_MANIFEST_ASSET_PATH = `/toolchains/rust-${RUST_VERSION}.manifest.json`;
@@ -61,16 +61,14 @@ var GO_COMPRESSED_STANDARD_LIBRARY_SHA256 = "aeffc384fdc624544f174ba5fc3c2239571
61
61
  var GO_STANDARD_LIBRARY_SHA256 = "e1ec64b08efd02b35b7ffdaf3970875aaae98325fa4795eafca94ebae2d0d192";
62
62
  var GO_COMPILER_SHA256 = "9e557f5b86961fd604217d7521461c5d2b7322e383fa30dead5669b77db12201";
63
63
  var GO_LINKER_SHA256 = "2eefca10af935a307ab7946447146bd30e0ea0fb5460be54dc1c55844d155580";
64
- var JAVA_VERSION = "teavm-0.13.1-wasi";
64
+ var JAVA_VERSION = "teavm-0.13.1-wasmgc-wasip1";
65
65
  var JAVA_COMPILER_PACKAGE = `wasm-oj/java-teavm@${JAVA_VERSION}`;
66
- var JAVA_COMPILER_ASSET_PATH = "/toolchains/java-teavm-0.13.1.wasi.compiler.webc.gz.bin";
66
+ var JAVA_COMPILER_ASSET_PATH = "/toolchains/java-teavm-0.13.1.compiler.wasm";
67
67
  var JAVA_COMPILE_CLASSLIB_ASSET_PATH = "/toolchains/java-teavm-0.13.1.compile-classlib.bin";
68
68
  var JAVA_RUNTIME_CLASSLIB_ASSET_PATH = "/toolchains/java-teavm-0.13.1.runtime-classlib.bin";
69
- var JAVA_COMPILER_COMPRESSED_PACKAGE_SHA256 = "129f1f51d591e58954f88787d36396b856a9a68ba3ae9c9d14f20bd67c2c7722";
70
- var JAVA_COMPILER_PACKAGE_SHA256 = "f8f86761cf31062565187e4a66f73b6903f257fe84c0ce70ea1cd28441b6c2e9";
71
- var JAVA_COMPILER_SHA256 = "33a0d662395256f10a5d02ea305fc7b18007b2b8fe8996859017b084d3d19735";
72
- var JAVA_COMPILE_CLASSLIB_SHA256 = "acfe3fb09e5f2c0c7c8dc2339c66fcdadc1f8e1bf1c74be446926175ef770868";
73
- var JAVA_RUNTIME_CLASSLIB_SHA256 = "21a9394586e416af2fca4eb0ed08521cbc8924e1d1afaa07863a59a3cfae54ab";
69
+ var JAVA_COMPILER_SHA256 = "8c37bce1c6fceeaffeffe93e10834309b67c8b6fb58c6f8a24547afe8be67f5d";
70
+ var JAVA_COMPILE_CLASSLIB_SHA256 = "738a82b7d7e7ff8d98b07bb076785da7035847a166afb8446fd150c4efce6103";
71
+ var JAVA_RUNTIME_CLASSLIB_SHA256 = "0ed234b66cdde19cb29e6c2865a3636d792d3cf72f102e73a7b3fdede80b79e6";
74
72
  Object.freeze({
75
73
  [CLANG_PACKAGE_ASSET_PATH]: CLANG_COMPRESSED_PACKAGE_SHA256,
76
74
  [CLANG_CC1_PINS_ASSET_PATH]: CLANG_CC1_PINS_SHA256,
@@ -87,7 +85,7 @@ Object.freeze({
87
85
  [GO_PACKAGE_ASSET_PATH]: GO_COMPRESSED_PACKAGE_SHA256,
88
86
  [GO_PACKAGE_MANIFEST_ASSET_PATH]: GO_PACKAGE_MANIFEST_SHA256,
89
87
  [GO_STANDARD_LIBRARY_ASSET_PATH]: GO_COMPRESSED_STANDARD_LIBRARY_SHA256,
90
- [JAVA_COMPILER_ASSET_PATH]: JAVA_COMPILER_COMPRESSED_PACKAGE_SHA256,
88
+ [JAVA_COMPILER_ASSET_PATH]: JAVA_COMPILER_SHA256,
91
89
  [JAVA_COMPILE_CLASSLIB_ASSET_PATH]: JAVA_COMPILE_CLASSLIB_SHA256,
92
90
  [JAVA_RUNTIME_CLASSLIB_ASSET_PATH]: JAVA_RUNTIME_CLASSLIB_SHA256
93
91
  });
@@ -130,8 +128,6 @@ var TOOLCHAIN_CONTENT_SHA256 = Object.freeze({
130
128
  /** Exact executable/compiler content used by cache keys and cost calibration. */
131
129
  function toolchainContentIdentity(language) {
132
130
  if (language === "java") return [
133
- JAVA_COMPILER_COMPRESSED_PACKAGE_SHA256,
134
- JAVA_COMPILER_PACKAGE_SHA256,
135
131
  JAVA_COMPILER_SHA256,
136
132
  JAVA_COMPILE_CLASSLIB_SHA256,
137
133
  JAVA_RUNTIME_CLASSLIB_SHA256
@@ -182,7 +178,7 @@ var TOOLCHAINS = Object.freeze({
182
178
  runtimePackage: PYTHON_PACKAGE,
183
179
  targets: ["wasip1"],
184
180
  version: PYTHON_VERSION,
185
- note: "Byte-compiled project bundled with the source-built CPython wasm32-wasip1 interpreter and standard library."
181
+ note: "Python source files executed by the source-built CPython wasm32-wasip1 interpreter and standard library."
186
182
  }),
187
183
  javascript: freezeToolchain({
188
184
  language: "javascript",
@@ -221,4 +217,4 @@ function toolchainPackageIdentities(language) {
221
217
  return [.../* @__PURE__ */ new Set([...toolchain.compilerPackages, ...toolchain.runtimePackage ? [toolchain.runtimePackage] : []])];
222
218
  }
223
219
  //#endregion
224
- export { PYTHON_PACKAGE_ASSET_PATH as A, TYPESCRIPT_VERSION as B, JAVA_COMPILE_CLASSLIB_ASSET_PATH as C, JAVA_VERSION as D, JAVA_RUNTIME_CLASSLIB_SHA256 as E, RUST_PACKAGE_MANIFEST_ASSET_PATH as F, toolchainPackageIdentities as H, RUST_PACKAGE_MANIFEST_SHA256 as I, RUST_PACKAGE_SHA256 as L, QUICKJS_PACKAGE as M, RUST_COMPRESSED_PACKAGE_SHA256 as N, PYTHON_COMPRESSED_PACKAGE_SHA256 as O, RUST_PACKAGE_ASSET_PATH as P, RUST_VERSION as R, JAVA_COMPILER_PACKAGE_SHA256 as S, JAVA_RUNTIME_CLASSLIB_ASSET_PATH as T, toolchainContentIdentity as V, GO_STANDARD_LIBRARY_SHA256 as _, CLANG_PACKAGE_ASSET_PATH as a, JAVA_COMPILER_COMPRESSED_PACKAGE_SHA256 as b, GO_COMPILER_SHA256 as c, GO_LINKER_SHA256 as d, GO_PACKAGE_ASSET_PATH as f, GO_STANDARD_LIBRARY_ASSET_PATH as g, GO_PACKAGE_SHA256 as h, CLANG_LIBCXX_PCH_MANIFEST_SHA256 as i, PYTHON_PACKAGE_SHA256 as j, PYTHON_PACKAGE as k, GO_COMPRESSED_PACKAGE_SHA256 as l, GO_PACKAGE_MANIFEST_SHA256 as m, CLANG_CC1_PINS_SHA256 as n, CLANG_PACKAGE_SHA256 as o, GO_PACKAGE_MANIFEST_ASSET_PATH as p, CLANG_LIBCXX_PCH_MANIFEST_ASSET_PATH as r, CLANG_VERSION as s, CLANG_CC1_PINS_ASSET_PATH as t, GO_COMPRESSED_STANDARD_LIBRARY_SHA256 as u, GO_VERSION as v, JAVA_COMPILE_CLASSLIB_SHA256 as w, JAVA_COMPILER_PACKAGE as x, JAVA_COMPILER_ASSET_PATH as y, TYPESCRIPT_ASSET_PATH as z };
220
+ export { QUICKJS_PACKAGE as A, toolchainPackageIdentities as B, JAVA_COMPILE_CLASSLIB_SHA256 as C, PYTHON_COMPRESSED_PACKAGE_SHA256 as D, JAVA_VERSION as E, RUST_PACKAGE_SHA256 as F, RUST_VERSION as I, TYPESCRIPT_ASSET_PATH as L, RUST_PACKAGE_ASSET_PATH as M, RUST_PACKAGE_MANIFEST_ASSET_PATH as N, PYTHON_PACKAGE as O, RUST_PACKAGE_MANIFEST_SHA256 as P, TYPESCRIPT_VERSION as R, JAVA_COMPILE_CLASSLIB_ASSET_PATH as S, JAVA_RUNTIME_CLASSLIB_SHA256 as T, GO_STANDARD_LIBRARY_SHA256 as _, CLANG_PACKAGE_ASSET_PATH as a, JAVA_COMPILER_PACKAGE as b, GO_COMPILER_SHA256 as c, GO_LINKER_SHA256 as d, GO_PACKAGE_ASSET_PATH as f, GO_STANDARD_LIBRARY_ASSET_PATH as g, GO_PACKAGE_SHA256 as h, CLANG_LIBCXX_PCH_MANIFEST_SHA256 as i, RUST_COMPRESSED_PACKAGE_SHA256 as j, PYTHON_PACKAGE_SHA256 as k, GO_COMPRESSED_PACKAGE_SHA256 as l, GO_PACKAGE_MANIFEST_SHA256 as m, CLANG_CC1_PINS_SHA256 as n, CLANG_PACKAGE_SHA256 as o, GO_PACKAGE_MANIFEST_ASSET_PATH as p, CLANG_LIBCXX_PCH_MANIFEST_ASSET_PATH as r, CLANG_VERSION as s, CLANG_CC1_PINS_ASSET_PATH as t, GO_COMPRESSED_STANDARD_LIBRARY_SHA256 as u, GO_VERSION as v, JAVA_RUNTIME_CLASSLIB_ASSET_PATH as w, JAVA_COMPILER_SHA256 as x, JAVA_COMPILER_ASSET_PATH as y, toolchainContentIdentity as z };