@wasm-oj/server 0.2.0

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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -0
  3. package/THIRD_PARTY_NOTICES.md +302 -0
  4. package/crates/runtime-core/Cargo.lock +5099 -0
  5. package/crates/runtime-core/Cargo.toml +66 -0
  6. package/crates/runtime-core/README.md +47 -0
  7. package/crates/runtime-core/src/bin/wasm-oj-compiler.rs +418 -0
  8. package/crates/runtime-core/src/bin/wasm-oj-runner.rs +294 -0
  9. package/crates/runtime-core/src/capabilities.rs +118 -0
  10. package/crates/runtime-core/src/compiler.rs +658 -0
  11. package/crates/runtime-core/src/contract.rs +5 -0
  12. package/crates/runtime-core/src/deterministic.rs +1051 -0
  13. package/crates/runtime-core/src/error.rs +58 -0
  14. package/crates/runtime-core/src/filesystem.rs +547 -0
  15. package/crates/runtime-core/src/filesystem_quota.rs +167 -0
  16. package/crates/runtime-core/src/go_compiler_session.rs +297 -0
  17. package/crates/runtime-core/src/interactive.rs +1019 -0
  18. package/crates/runtime-core/src/judge_package.rs +1539 -0
  19. package/crates/runtime-core/src/lib.rs +98 -0
  20. package/crates/runtime-core/src/memory.rs +84 -0
  21. package/crates/runtime-core/src/meter.rs +549 -0
  22. package/crates/runtime-core/src/module_imports.rs +149 -0
  23. package/crates/runtime-core/src/module_policy.rs +714 -0
  24. package/crates/runtime-core/src/output.rs +204 -0
  25. package/crates/runtime-core/src/run/mod.rs +208 -0
  26. package/crates/runtime-core/src/run/native.rs +260 -0
  27. package/crates/runtime-core/src/run/web.rs +229 -0
  28. package/crates/runtime-core/src/run/web_runtime.rs +109 -0
  29. package/crates/runtime-core/src/types.rs +268 -0
  30. package/crates/runtime-core/src/web.rs +83 -0
  31. package/dist/chunks/go-toolchain-Dbt-lp2L.js +426 -0
  32. package/dist/chunks/java-toolchain-DajoRCHu.js +44 -0
  33. package/dist/chunks/python-toolchain-Dx834o2A.js +4 -0
  34. package/dist/chunks/rust-toolchain-CJ3sMxPE.js +252 -0
  35. package/dist/chunks/toolchains-C6KuA1yM.js +224 -0
  36. package/dist/go-stage.mjs +193 -0
  37. package/dist/index.d.ts +221 -0
  38. package/dist/index.js +4393 -0
  39. package/dist/java-stage.mjs +111 -0
  40. package/dist/python-stage.mjs +90 -0
  41. package/dist/rustc-stage.mjs +301 -0
  42. package/dist/server-build-stage.mjs +2564 -0
  43. package/dist/server-runner-stage.mjs +155 -0
  44. package/licenses/fflate-MIT.txt +21 -0
  45. package/licenses/runtime-core-dependencies.html +6253 -0
  46. package/licenses/runtime-core-dependencies.json +3041 -0
  47. package/licenses/wasmer-sdk-MIT.txt +21 -0
  48. package/licenses/wasmer-sdk-dependencies.html +6901 -0
  49. package/licenses/wasmer-sdk-dependencies.json +3013 -0
  50. package/package.json +70 -0
  51. package/rust-toolchain.toml +5 -0
  52. package/testdata/wojjdg02-v2-text.hex +1 -0
  53. package/vendor/shared-buffer/Cargo.toml +22 -0
  54. package/vendor/shared-buffer/LICENSE_APACHE.md +176 -0
  55. package/vendor/shared-buffer/LICENSE_MIT.md +25 -0
  56. package/vendor/shared-buffer/README.md +34 -0
  57. package/vendor/shared-buffer/src/lib.rs +58 -0
  58. package/vendor/shared-buffer/src/mmap.rs +250 -0
  59. package/vendor/shared-buffer/src/owned.rs +389 -0
  60. package/vendor/virtual-fs/Cargo.toml +181 -0
  61. package/vendor/virtual-fs/LICENSE +25 -0
  62. package/vendor/virtual-fs/src/arc_box_file.rs +142 -0
  63. package/vendor/virtual-fs/src/arc_file.rs +182 -0
  64. package/vendor/virtual-fs/src/arc_fs.rs +68 -0
  65. package/vendor/virtual-fs/src/buffer_file.rs +103 -0
  66. package/vendor/virtual-fs/src/builder.rs +232 -0
  67. package/vendor/virtual-fs/src/combine_file.rs +101 -0
  68. package/vendor/virtual-fs/src/cow_file.rs +345 -0
  69. package/vendor/virtual-fs/src/dual_write_file.rs +113 -0
  70. package/vendor/virtual-fs/src/empty_fs.rs +81 -0
  71. package/vendor/virtual-fs/src/filesystems.rs +108 -0
  72. package/vendor/virtual-fs/src/host_fs.rs +1390 -0
  73. package/vendor/virtual-fs/src/lib.rs +782 -0
  74. package/vendor/virtual-fs/src/limiter.rs +252 -0
  75. package/vendor/virtual-fs/src/mem_fs/file.rs +1799 -0
  76. package/vendor/virtual-fs/src/mem_fs/file_opener.rs +941 -0
  77. package/vendor/virtual-fs/src/mem_fs/filesystem.rs +2134 -0
  78. package/vendor/virtual-fs/src/mem_fs/mod.rs +245 -0
  79. package/vendor/virtual-fs/src/mem_fs/offloaded_file.rs +474 -0
  80. package/vendor/virtual-fs/src/mem_fs/stdio.rs +318 -0
  81. package/vendor/virtual-fs/src/mount_fs.rs +2225 -0
  82. package/vendor/virtual-fs/src/null_file.rs +87 -0
  83. package/vendor/virtual-fs/src/ops.rs +364 -0
  84. package/vendor/virtual-fs/src/overlay_fs.rs +2216 -0
  85. package/vendor/virtual-fs/src/passthru_fs.rs +119 -0
  86. package/vendor/virtual-fs/src/pipe.rs +603 -0
  87. package/vendor/virtual-fs/src/random_file.rs +88 -0
  88. package/vendor/virtual-fs/src/special_file.rs +108 -0
  89. package/vendor/virtual-fs/src/static_file.rs +133 -0
  90. package/vendor/virtual-fs/src/static_fs.rs +460 -0
  91. package/vendor/virtual-fs/src/tmp_fs.rs +95 -0
  92. package/vendor/virtual-fs/src/trace_fs.rs +258 -0
  93. package/vendor/virtual-fs/src/webc_volume_fs.rs +829 -0
  94. package/vendor/virtual-fs/src/zero_file.rs +90 -0
@@ -0,0 +1,229 @@
1
+ use super::web_runtime::runtime_with_engine;
2
+ use crate::capabilities::attach_capability_denials;
3
+ use crate::deterministic::{VirtualClock, attach_deterministic_imports};
4
+ use crate::filesystem::{read_files_bounded, runtime_project_files};
5
+ use crate::meter::{CostPoints, METER_MODEL, instrument_wasm, meter_state, remaining_points};
6
+ use crate::module_imports::attach_imported_memory;
7
+ use crate::module_policy::{DEFERRED_START_EXPORT, defer_start_section, enforce_memory_limit};
8
+ use crate::output::{OutputBudget, OutputCapture};
9
+ use crate::{ExecutionMetrics, ExecutionTermination, RunError, RunRequest, RunResult};
10
+ use std::io::Write;
11
+ use std::sync::{Arc, Mutex};
12
+ use wasmer::{Instance, Memory, Module, Store};
13
+ use wasmer_wasix::{
14
+ Pipe, WasiEnv, WasiError, WasiModuleInstanceHandles, WasiModuleTreeHandles, wasmer_wasix_types,
15
+ };
16
+
17
+ pub fn run(request: RunRequest) -> Result<RunResult, RunError> {
18
+ let limited = enforce_memory_limit(&request.wasm, request.resources.memory_limit_bytes)
19
+ .map_err(RunError::Compile)?;
20
+ let metered = instrument_wasm(&limited, request.resources.instruction_budget)
21
+ .map_err(RunError::Compile)?;
22
+ let executable = defer_start_section(&metered.wasm).map_err(RunError::Compile)?;
23
+ let mut store = Store::default();
24
+ let module = Module::new(&store, &executable.wasm).map_err(|error| {
25
+ RunError::Compile(format!("failed to compile instrumented module: {error}"))
26
+ })?;
27
+ let runtime = runtime_with_engine(store.engine().clone());
28
+
29
+ let (mut stdin_writer, stdin_reader) = Pipe::channel();
30
+ stdin_writer
31
+ .write_all(&request.stdin)
32
+ .map_err(|error| RunError::Io(error.to_string()))?;
33
+ drop(stdin_writer);
34
+ let output_limit = usize::try_from(request.resources.output_limit_bytes)
35
+ .map_err(|_| RunError::InvalidRequest("output limit exceeds host range".to_string()))?;
36
+ let output_budget = OutputBudget::new(output_limit);
37
+ let (stdout_capture, stdout_file) = OutputCapture::new(output_budget.clone(), 1);
38
+ let (stderr_capture, stderr_file) = OutputCapture::new(output_budget, 2);
39
+
40
+ let project_filesystem = runtime_project_files(
41
+ &request.files,
42
+ &request.output_paths,
43
+ &request.determinism,
44
+ &request.resources,
45
+ )?;
46
+ let filesystem = project_filesystem.filesystem();
47
+ let mut builder = WasiEnv::builder("app")
48
+ .runtime(runtime)
49
+ .args(request.args.clone())
50
+ .envs(request.env.clone())
51
+ .stdin(Box::new(stdin_reader))
52
+ .stdout(Box::new(stdout_file))
53
+ .stderr(Box::new(stderr_file))
54
+ .fs(filesystem.clone());
55
+ builder
56
+ .add_preopen_build(|directory| directory.directory("/").read(true).write(true).create(true))
57
+ .map_err(|error| {
58
+ RunError::InvalidRequest(format!("failed to preopen guest filesystem: {error}"))
59
+ })?;
60
+ if let Some(cwd) = &request.cwd {
61
+ builder.set_current_dir(cwd);
62
+ }
63
+ let mut sandbox = builder.finalize(&mut store).map_err(|error| {
64
+ RunError::Compile(format!("failed to finalize WASI environment: {error}"))
65
+ })?;
66
+ let mut imports = sandbox
67
+ .import_object_for_all_wasi_versions(&mut store, &module)
68
+ .map_err(|error| RunError::Compile(format!("failed to create WASI imports: {error}")))?;
69
+ let memory_slot: Arc<Mutex<Option<Memory>>> = Arc::new(Mutex::new(None));
70
+ let clock = VirtualClock::new(
71
+ &request.determinism,
72
+ request.resources.logical_time_limit_ms,
73
+ );
74
+ attach_deterministic_imports(
75
+ &mut store,
76
+ &mut imports,
77
+ memory_slot.clone(),
78
+ &request.determinism,
79
+ clock.clone(),
80
+ request.startup_entropy_bytes,
81
+ );
82
+ attach_capability_denials(&mut store, &module, &mut imports).map_err(RunError::Compile)?;
83
+ let imported_memory =
84
+ attach_imported_memory(&mut store, &module, &mut imports).map_err(RunError::Compile)?;
85
+ let instance = Instance::new(&mut store, &module, &imports)
86
+ .map_err(|error| RunError::Compile(format!("failed to instantiate module: {error}")))?;
87
+ let meter = meter_state(&mut store, &instance).map_err(RunError::Runtime)?;
88
+ let guest_memory = instance
89
+ .exports
90
+ .get_memory("memory")
91
+ .cloned()
92
+ .ok()
93
+ .or(imported_memory)
94
+ .ok_or_else(|| RunError::Compile("module has no guest linear memory".to_string()))?;
95
+ *memory_slot
96
+ .lock()
97
+ .map_err(|error| RunError::Runtime(error.to_string()))? = Some(guest_memory.clone());
98
+ let handles = WasiModuleTreeHandles::Static(WasiModuleInstanceHandles::new(
99
+ guest_memory.clone(),
100
+ &store,
101
+ instance.clone(),
102
+ None,
103
+ ));
104
+ sandbox
105
+ .initialize_handles_and_layout(&mut store, instance.clone(), handles, None, true)
106
+ .map_err(|error| {
107
+ RunError::Compile(format!("failed to initialize WASI instance: {error}"))
108
+ })?;
109
+ let start = instance
110
+ .exports
111
+ .get_function("_start")
112
+ .map_err(|error| RunError::Compile(format!("module has no _start function: {error}")))?;
113
+ let execution = if executable.has_deferred_start {
114
+ let initializer = instance
115
+ .exports
116
+ .get_function(DEFERRED_START_EXPORT)
117
+ .map_err(|error| {
118
+ RunError::Runtime(format!("deferred start function is unavailable: {error}"))
119
+ })?;
120
+ match initializer.call(&mut store, &[]) {
121
+ Ok(_) => start.call(&mut store, &[]),
122
+ Err(error) => Err(error),
123
+ }
124
+ } else {
125
+ start.call(&mut store, &[])
126
+ };
127
+
128
+ let stdout = stdout_capture.bytes();
129
+ let stderr = stderr_capture.bytes();
130
+ let mut output_exceeded = stdout_capture.exceeded() || stderr_capture.exceeded();
131
+ let remaining = remaining_points(&mut store, &meter).map_err(RunError::Runtime)?;
132
+ let logical_time_exceeded = clock.limit_exceeded()?;
133
+
134
+ let mut code = 0;
135
+ let mut termination = ExecutionTermination::Exited;
136
+ let mut trap_message = None;
137
+ if let Err(error) = execution {
138
+ if let Some(wasi_error) = crate::wasi_error(&error) {
139
+ match wasi_error {
140
+ WasiError::Exit(exit) => {
141
+ let errno: wasmer_wasix_types::wasi::Errno = (*exit).into();
142
+ if errno != wasmer_wasix_types::wasi::Errno::Success {
143
+ code = errno as i32;
144
+ }
145
+ }
146
+ WasiError::UnknownWasiVersion => {
147
+ return Err(RunError::WasiUnsupported(
148
+ "unknown WASI version".to_string(),
149
+ ));
150
+ }
151
+ WasiError::ThreadExit => {
152
+ return Err(RunError::WasiUnsupported("thread exit".to_string()));
153
+ }
154
+ WasiError::DeepSleep(_) => {
155
+ return Err(RunError::WasiUnsupported("deep sleep".to_string()));
156
+ }
157
+ WasiError::DlSymbolResolutionFailed(symbol) => {
158
+ return Err(RunError::WasiUnsupported(format!(
159
+ "unresolved symbol {symbol}"
160
+ )));
161
+ }
162
+ }
163
+ } else {
164
+ trap_message = Some(super::canonical_trap_message(&error.to_string()));
165
+ termination = ExecutionTermination::Trap;
166
+ code = 1;
167
+ }
168
+ }
169
+
170
+ sandbox.on_exit(
171
+ &mut store,
172
+ Some(wasmer_wasix_types::wasi::Errno::Success.into()),
173
+ );
174
+ let captured_bytes = stdout.len().saturating_add(stderr.len());
175
+ let remaining_output = output_limit.saturating_sub(captured_bytes);
176
+ let (files, file_output_exceeded) =
177
+ read_files_bounded(&filesystem, &request.output_paths, remaining_output)?;
178
+ output_exceeded |= file_output_exceeded;
179
+
180
+ if project_filesystem.quota_exceeded() {
181
+ code = 137;
182
+ termination = ExecutionTermination::FilesystemLimit;
183
+ } else if output_exceeded {
184
+ code = 137;
185
+ termination = ExecutionTermination::OutputLimit;
186
+ } else if logical_time_exceeded {
187
+ code = 137;
188
+ termination = ExecutionTermination::LogicalTimeLimit;
189
+ } else if matches!(remaining, CostPoints::Exhausted) {
190
+ code = 137;
191
+ termination = ExecutionTermination::InstructionLimit;
192
+ }
193
+ let memory_bytes = u64::from(guest_memory.size(&store).0) * 65_536;
194
+ if memory_bytes > request.resources.memory_limit_bytes {
195
+ code = 137;
196
+ termination = ExecutionTermination::MemoryLimit;
197
+ }
198
+ if termination != ExecutionTermination::Trap {
199
+ trap_message = None;
200
+ }
201
+ let cost = match remaining {
202
+ CostPoints::Remaining(points) => {
203
+ request.resources.instruction_budget.saturating_sub(points)
204
+ }
205
+ CostPoints::Exhausted => request.resources.instruction_budget,
206
+ };
207
+ let filesystem_metrics = project_filesystem.metrics();
208
+ Ok(RunResult {
209
+ code,
210
+ metrics: ExecutionMetrics {
211
+ cost,
212
+ cost_model: METER_MODEL.to_string(),
213
+ operations: metered.operations,
214
+ memory_bytes,
215
+ logical_time_ns: clock.elapsed_ns()?,
216
+ filesystem_bytes: filesystem_metrics.bytes,
217
+ filesystem_entries: filesystem_metrics.entries,
218
+ stdout_bytes: stdout.len() as u64,
219
+ stderr_bytes: stderr.len() as u64,
220
+ },
221
+ stdout,
222
+ stderr,
223
+ files,
224
+ termination,
225
+ trap_message,
226
+ determinism: request.determinism,
227
+ resources: request.resources,
228
+ })
229
+ }
@@ -0,0 +1,109 @@
1
+ use std::future::Future;
2
+ use std::pin::Pin;
3
+ use std::sync::Arc;
4
+ use std::time::Duration;
5
+ use wasm_bindgen_futures::spawn_local;
6
+ use wasmer::Engine;
7
+ use wasmer_wasix::runtime::task_manager::{
8
+ SpawnMemoryTypeOrStore, SpawnType, TaskWasm, TaskWasmRunProperties, VirtualTaskManager,
9
+ };
10
+ use wasmer_wasix::{PluggableRuntime, Runtime, WasiFunctionEnv, WasiThreadError};
11
+
12
+ #[derive(Debug, Default)]
13
+ pub struct WebTaskManager;
14
+
15
+ impl VirtualTaskManager for WebTaskManager {
16
+ fn sleep_now(&self, _duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> {
17
+ Box::pin(async {})
18
+ }
19
+
20
+ fn task_shared(
21
+ &self,
22
+ task: Box<
23
+ dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> + Send + 'static,
24
+ >,
25
+ ) -> Result<(), WasiThreadError> {
26
+ spawn_local(async move { task().await });
27
+ Ok(())
28
+ }
29
+
30
+ fn task_wasm(&self, task: TaskWasm) -> Result<(), WasiThreadError> {
31
+ let TaskWasm {
32
+ callbacks,
33
+ env,
34
+ module,
35
+ globals,
36
+ spawn_type,
37
+ update_layout,
38
+ call_initialize,
39
+ } = task;
40
+ let (memory, instance_group) = match spawn_type {
41
+ SpawnType::CreateMemory => (SpawnMemoryTypeOrStore::New, None),
42
+ SpawnType::NewLinkerInstanceGroup(group) => (SpawnMemoryTypeOrStore::New, Some(group)),
43
+ SpawnType::CreateMemoryOfType(ty) => (SpawnMemoryTypeOrStore::Type(ty), None),
44
+ SpawnType::AttachMemory(shared) => {
45
+ let mut store = env.runtime().new_store();
46
+ let memory = shared.attach(&mut store);
47
+ (SpawnMemoryTypeOrStore::StoreAndMemory(store, memory), None)
48
+ }
49
+ };
50
+ let (mut ctx, mut store) = WasiFunctionEnv::new_with_store(
51
+ module,
52
+ env,
53
+ globals,
54
+ memory,
55
+ update_layout,
56
+ call_initialize,
57
+ instance_group,
58
+ )?;
59
+ if let Some(trigger) = callbacks.trigger {
60
+ let run = callbacks.run;
61
+ let recycle = callbacks.recycle;
62
+ let pre_run = callbacks.pre_run;
63
+ spawn_local(async move {
64
+ let trigger_result = trigger().await;
65
+ if let Some(pre_run) = pre_run {
66
+ pre_run(&mut ctx, &mut store).await;
67
+ }
68
+ run(TaskWasmRunProperties {
69
+ ctx,
70
+ store,
71
+ trigger_result: Some(trigger_result),
72
+ recycle,
73
+ });
74
+ });
75
+ } else {
76
+ spawn_local(async move {
77
+ if let Some(pre_run) = callbacks.pre_run {
78
+ pre_run(&mut ctx, &mut store).await;
79
+ }
80
+ (callbacks.run)(TaskWasmRunProperties {
81
+ ctx,
82
+ store,
83
+ trigger_result: None,
84
+ recycle: callbacks.recycle,
85
+ });
86
+ });
87
+ }
88
+ Ok(())
89
+ }
90
+
91
+ fn task_dedicated(
92
+ &self,
93
+ task: Box<dyn FnOnce() + Send + 'static>,
94
+ ) -> Result<(), WasiThreadError> {
95
+ task();
96
+ Ok(())
97
+ }
98
+
99
+ fn thread_parallelism(&self) -> Result<usize, WasiThreadError> {
100
+ Ok(1)
101
+ }
102
+ }
103
+
104
+ pub fn runtime_with_engine(engine: Engine) -> Arc<dyn Runtime + Send + Sync> {
105
+ let tasks: Arc<dyn VirtualTaskManager> = Arc::new(WebTaskManager);
106
+ let mut runtime = PluggableRuntime::new(tasks);
107
+ runtime.set_engine(engine);
108
+ Arc::new(runtime)
109
+ }
@@ -0,0 +1,268 @@
1
+ use serde::{Deserialize, Serialize};
2
+ use std::collections::BTreeMap;
3
+
4
+ use crate::RunErrorCode;
5
+
6
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
7
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
8
+ pub struct DeterminismConfig {
9
+ pub random_seed: u64,
10
+ pub realtime_epoch_ms: u64,
11
+ pub clock_step_ns: u64,
12
+ }
13
+
14
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
15
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
16
+ pub struct ResourcePolicy {
17
+ pub instruction_budget: u64,
18
+ pub logical_time_limit_ms: u64,
19
+ pub memory_limit_bytes: u64,
20
+ pub output_limit_bytes: u64,
21
+ pub filesystem_write_limit_bytes: u64,
22
+ pub filesystem_entry_limit: u64,
23
+ }
24
+
25
+ #[derive(Clone, Debug, Deserialize, Serialize)]
26
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
27
+ pub struct RunRequest {
28
+ #[serde(with = "serde_bytes")]
29
+ pub wasm: Vec<u8>,
30
+ #[serde(default)]
31
+ pub args: Vec<String>,
32
+ #[serde(default)]
33
+ pub env: BTreeMap<String, String>,
34
+ #[serde(default, with = "serde_bytes")]
35
+ pub stdin: Vec<u8>,
36
+ #[serde(default)]
37
+ pub files: BTreeMap<String, serde_bytes::ByteBuf>,
38
+ #[serde(default)]
39
+ pub output_paths: Vec<String>,
40
+ pub cwd: Option<String>,
41
+ /// Fixed host entropy consumed before the caller-seeded stream.
42
+ pub startup_entropy_bytes: u64,
43
+ pub determinism: DeterminismConfig,
44
+ pub resources: ResourcePolicy,
45
+ }
46
+
47
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
48
+ #[serde(rename_all = "kebab-case")]
49
+ pub enum ExecutionTermination {
50
+ Exited,
51
+ InstructionLimit,
52
+ LogicalTimeLimit,
53
+ MemoryLimit,
54
+ OutputLimit,
55
+ FilesystemLimit,
56
+ Trap,
57
+ }
58
+
59
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
60
+ #[serde(rename_all = "camelCase")]
61
+ pub struct ExecutionMetrics {
62
+ pub cost: u64,
63
+ pub cost_model: String,
64
+ pub operations: BTreeMap<String, u64>,
65
+ pub memory_bytes: u64,
66
+ pub logical_time_ns: u64,
67
+ pub filesystem_bytes: u64,
68
+ pub filesystem_entries: u64,
69
+ pub stdout_bytes: u64,
70
+ pub stderr_bytes: u64,
71
+ }
72
+
73
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
74
+ #[serde(rename_all = "camelCase")]
75
+ pub struct RunResult {
76
+ pub code: i32,
77
+ #[serde(with = "serde_bytes")]
78
+ pub stdout: Vec<u8>,
79
+ #[serde(with = "serde_bytes")]
80
+ pub stderr: Vec<u8>,
81
+ pub files: BTreeMap<String, serde_bytes::ByteBuf>,
82
+ pub termination: ExecutionTermination,
83
+ pub trap_message: Option<String>,
84
+ pub metrics: ExecutionMetrics,
85
+ pub determinism: DeterminismConfig,
86
+ pub resources: ResourcePolicy,
87
+ }
88
+
89
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
90
+ #[serde(rename_all = "camelCase")]
91
+ pub struct RunFailure {
92
+ pub code: RunErrorCode,
93
+ pub message: String,
94
+ }
95
+
96
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
97
+ #[serde(rename_all = "camelCase")]
98
+ pub struct RunResponse {
99
+ pub ok: bool,
100
+ pub result: Option<RunResult>,
101
+ pub error: Option<RunFailure>,
102
+ }
103
+
104
+ #[derive(Clone, Debug, Deserialize, Serialize)]
105
+ #[serde(rename_all = "camelCase")]
106
+ pub struct CompilerToolchainConfig {
107
+ #[serde(with = "serde_bytes")]
108
+ pub package: Vec<u8>,
109
+ pub memory_limit_bytes: u64,
110
+ }
111
+
112
+ #[derive(Clone, Debug, Deserialize, Serialize)]
113
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
114
+ pub struct CompileRequest {
115
+ pub command: String,
116
+ #[serde(default)]
117
+ pub args: Vec<String>,
118
+ #[serde(default)]
119
+ pub env: BTreeMap<String, String>,
120
+ #[serde(default, with = "serde_bytes")]
121
+ pub stdin: Vec<u8>,
122
+ #[serde(default)]
123
+ pub files: BTreeMap<String, serde_bytes::ByteBuf>,
124
+ pub cwd: Option<String>,
125
+ #[serde(default)]
126
+ pub output_paths: Vec<String>,
127
+ pub output_limit_bytes: u64,
128
+ }
129
+
130
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
131
+ #[serde(rename_all = "camelCase")]
132
+ pub struct CompileResult {
133
+ pub code: i32,
134
+ #[serde(with = "serde_bytes")]
135
+ pub stdout: Vec<u8>,
136
+ #[serde(with = "serde_bytes")]
137
+ pub stderr: Vec<u8>,
138
+ pub output_files: BTreeMap<String, serde_bytes::ByteBuf>,
139
+ pub termination: ExecutionTermination,
140
+ }
141
+
142
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
143
+ #[serde(rename_all = "camelCase")]
144
+ pub struct CompileResponse {
145
+ pub ok: bool,
146
+ pub result: Option<CompileResult>,
147
+ pub error: Option<RunFailure>,
148
+ }
149
+
150
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
151
+ #[serde(rename_all = "camelCase")]
152
+ pub struct CompilePipelineResult {
153
+ pub stages: Vec<CompileResult>,
154
+ }
155
+
156
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
157
+ #[serde(rename_all = "camelCase")]
158
+ pub struct CompilePipelineResponse {
159
+ pub ok: bool,
160
+ pub result: Option<CompilePipelineResult>,
161
+ pub error: Option<RunFailure>,
162
+ }
163
+
164
+ /// One-time immutable inputs for a browser Go compiler session.
165
+ ///
166
+ /// `digest` is the SHA-256 of the already verified Go toolchain manifest. The
167
+ /// manifest binds the WebC package and standard-library archive digests.
168
+ #[derive(Clone, Debug, Deserialize, Serialize)]
169
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
170
+ pub struct GoCompilerSessionConfig {
171
+ pub digest: String,
172
+ pub toolchain: CompilerToolchainConfig,
173
+ pub standard_library_files: BTreeMap<String, serde_bytes::ByteBuf>,
174
+ }
175
+
176
+ /// A monotonic update to the mutable `/work` source snapshot.
177
+ #[derive(Clone, Debug, Deserialize, Serialize)]
178
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
179
+ pub struct GoCompilerSourceDelta {
180
+ pub generation: u32,
181
+ #[serde(default)]
182
+ pub upsert_files: BTreeMap<String, serde_bytes::ByteBuf>,
183
+ #[serde(default)]
184
+ pub remove_paths: Vec<String>,
185
+ }
186
+
187
+ /// A compile request against an existing digest-bound browser Go session.
188
+ #[derive(Clone, Debug, Deserialize, Serialize)]
189
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
190
+ pub struct GoCompilerSessionRequest {
191
+ pub digest: String,
192
+ pub source_delta: GoCompilerSourceDelta,
193
+ pub stages: Vec<CompileRequest>,
194
+ }
195
+
196
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
197
+ #[serde(rename_all = "camelCase")]
198
+ pub struct GoCompilerSessionResponse {
199
+ pub digest: String,
200
+ pub generation: u32,
201
+ pub response: CompilePipelineResponse,
202
+ }
203
+
204
+ #[derive(Clone, Debug, Deserialize, Serialize)]
205
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
206
+ pub struct InteractiveProgram {
207
+ #[serde(with = "serde_bytes")]
208
+ pub wasm: Vec<u8>,
209
+ #[serde(default)]
210
+ pub args: Vec<String>,
211
+ #[serde(default)]
212
+ pub env: BTreeMap<String, String>,
213
+ #[serde(default)]
214
+ pub files: BTreeMap<String, serde_bytes::ByteBuf>,
215
+ pub cwd: Option<String>,
216
+ pub startup_entropy_bytes: u64,
217
+ pub resources: ResourcePolicy,
218
+ }
219
+
220
+ #[derive(Clone, Debug, Deserialize, Serialize)]
221
+ #[serde(deny_unknown_fields, rename_all = "camelCase")]
222
+ pub struct InteractiveRequest {
223
+ pub contestant: InteractiveProgram,
224
+ pub interactor: InteractiveProgram,
225
+ pub determinism: DeterminismConfig,
226
+ }
227
+
228
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
229
+ #[serde(rename_all = "camelCase")]
230
+ pub struct InteractiveMetrics {
231
+ pub cost: u64,
232
+ pub operations: BTreeMap<String, u64>,
233
+ pub logical_time_ns: u64,
234
+ pub filesystem_bytes: u64,
235
+ pub filesystem_entries: u64,
236
+ pub protocol_bytes: u64,
237
+ pub stderr_bytes: u64,
238
+ }
239
+
240
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
241
+ #[serde(rename_all = "camelCase")]
242
+ pub struct InteractiveProcessResult {
243
+ pub code: i32,
244
+ #[serde(with = "serde_bytes")]
245
+ pub stderr: Vec<u8>,
246
+ pub termination: ExecutionTermination,
247
+ pub metrics: InteractiveMetrics,
248
+ }
249
+
250
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
251
+ #[serde(rename_all = "camelCase")]
252
+ pub struct InteractiveResult {
253
+ pub contestant: InteractiveProcessResult,
254
+ pub interactor: InteractiveProcessResult,
255
+ #[serde(with = "serde_bytes")]
256
+ pub contestant_to_interactor: Vec<u8>,
257
+ #[serde(with = "serde_bytes")]
258
+ pub interactor_to_contestant: Vec<u8>,
259
+ pub determinism: DeterminismConfig,
260
+ }
261
+
262
+ #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
263
+ #[serde(rename_all = "camelCase")]
264
+ pub struct InteractiveResponse {
265
+ pub ok: bool,
266
+ pub result: Option<InteractiveResult>,
267
+ pub error: Option<RunFailure>,
268
+ }
@@ -0,0 +1,83 @@
1
+ use crate::{
2
+ GoCompilerSession as CoreGoCompilerSession, GoCompilerSessionConfig, GoCompilerSessionRequest,
3
+ InteractiveRequest, RunRequest, interactive_response, run_response,
4
+ };
5
+ use serde::Serialize;
6
+ use wasm_bindgen::prelude::*;
7
+
8
+ #[wasm_bindgen]
9
+ pub fn run_wasm_oj(request: JsValue) -> Result<JsValue, JsValue> {
10
+ console_error_panic_hook::set_once();
11
+ let request: RunRequest = serde_wasm_bindgen::from_value(request)
12
+ .map_err(|error| JsValue::from_str(&format!("invalid run request: {error}")))?;
13
+ let response = run_response(request);
14
+ response
15
+ .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
16
+ .map_err(|error| JsValue::from_str(&format!("failed to serialize run response: {error}")))
17
+ }
18
+
19
+ /// Process-local browser Go compiler session. Construction transfers and
20
+ /// hydrates the immutable package/stdlib exactly once; later calls carry only
21
+ /// monotonic source deltas and pipeline requests.
22
+ #[wasm_bindgen(js_name = GoCompilerSession)]
23
+ pub struct WebGoCompilerSession {
24
+ inner: CoreGoCompilerSession,
25
+ }
26
+
27
+ #[wasm_bindgen(js_class = GoCompilerSession)]
28
+ impl WebGoCompilerSession {
29
+ #[wasm_bindgen(constructor)]
30
+ pub fn new(config: JsValue) -> Result<WebGoCompilerSession, JsValue> {
31
+ console_error_panic_hook::set_once();
32
+ let config: GoCompilerSessionConfig = serde_wasm_bindgen::from_value(config)
33
+ .map_err(|error| JsValue::from_str(&format!("invalid Go compiler session: {error}")))?;
34
+ let inner = CoreGoCompilerSession::new(config)
35
+ .map_err(|error| JsValue::from_str(&error.to_string()))?;
36
+ Ok(Self { inner })
37
+ }
38
+
39
+ #[wasm_bindgen(getter)]
40
+ pub fn digest(&self) -> String {
41
+ self.inner.digest().to_string()
42
+ }
43
+
44
+ #[wasm_bindgen(getter)]
45
+ pub fn generation(&self) -> Result<u32, JsValue> {
46
+ self.inner
47
+ .generation()
48
+ .map_err(|error| JsValue::from_str(&error.to_string()))
49
+ }
50
+
51
+ #[wasm_bindgen(js_name = compilePipeline)]
52
+ pub async fn compile_pipeline(&self, request: JsValue) -> Result<JsValue, JsValue> {
53
+ let request: GoCompilerSessionRequest = serde_wasm_bindgen::from_value(request)
54
+ .map_err(|error| JsValue::from_str(&format!("invalid Go compiler request: {error}")))?;
55
+ let response = self
56
+ .inner
57
+ .compile_pipeline_response(request)
58
+ .await
59
+ .map_err(|error| JsValue::from_str(&error.to_string()))?;
60
+ response
61
+ .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
62
+ .map_err(|error| {
63
+ JsValue::from_str(&format!(
64
+ "failed to serialize Go compiler response: {error}"
65
+ ))
66
+ })
67
+ }
68
+ }
69
+
70
+ #[wasm_bindgen]
71
+ pub async fn interact_wasm_oj(request: JsValue) -> Result<JsValue, JsValue> {
72
+ console_error_panic_hook::set_once();
73
+ let request: InteractiveRequest = serde_wasm_bindgen::from_value(request)
74
+ .map_err(|error| JsValue::from_str(&format!("invalid interactive request: {error}")))?;
75
+ let response = interactive_response(request).await;
76
+ response
77
+ .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
78
+ .map_err(|error| {
79
+ JsValue::from_str(&format!(
80
+ "failed to serialize interactive response: {error}"
81
+ ))
82
+ })
83
+ }