@wasm-oj/server 0.2.3 → 0.2.5
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.
- package/crates/runtime-core/src/deterministic.rs +27 -1
- package/crates/runtime-core/src/interactive.rs +365 -110
- package/crates/runtime-core/src/meter.rs +736 -66
- package/crates/runtime-core/src/run/mod.rs +2 -0
- package/crates/runtime-core/src/run/native.rs +5 -2
- package/crates/runtime-core/src/run/web.rs +39 -13
- package/crates/runtime-core/src/run/web_interactive.rs +375 -0
- package/crates/runtime-core/src/web.rs +52 -6
- package/dist/chunks/process-keepalive-BaXpMTap.js +191 -0
- package/dist/index.js +107 -18
- package/dist/server-build-stage.mjs +11 -94
- package/dist/server-runner-stage.mjs +73 -4
- package/package.json +3 -3
- package/dist/chunks/process-keepalive-BWLUz55N.js +0 -16
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import "./toolchains-uz3XbZGI.js";
|
|
2
|
+
import { assertLanguageIdentifier } from "@wasm-oj/contracts";
|
|
3
|
+
//#region src/core/project-files.ts
|
|
4
|
+
var PROJECT_SOURCE_LIMITS = Object.freeze({
|
|
5
|
+
files: 256,
|
|
6
|
+
bytesPerFile: 4194304,
|
|
7
|
+
totalBytes: 16777216
|
|
8
|
+
});
|
|
9
|
+
var UTF8_ENCODER = new TextEncoder();
|
|
10
|
+
/** Locale-independent ordering used anywhere file order can affect build output. */
|
|
11
|
+
function compareCanonicalPaths(left, right) {
|
|
12
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
13
|
+
}
|
|
14
|
+
function assertSafeRelativePath(path, label = "Project path") {
|
|
15
|
+
if (typeof path !== "string" || !path || path !== path.trim() || path.length > 4096) throw new Error(`${label} must be a non-empty, trimmed string of at most 4096 characters.`);
|
|
16
|
+
if (path.startsWith("/") || path.includes("\\") || path.includes("\0") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`${label} '${path}' must be a normalized relative path that cannot escape the project.`);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validates a project file set and returns a fresh, locale-independently sorted
|
|
20
|
+
* array. Build hosts must use this order for filesystem creation and argv.
|
|
21
|
+
*/
|
|
22
|
+
function canonicalProjectFiles(files) {
|
|
23
|
+
if (!Array.isArray(files) || files.length === 0) throw new Error("A project must contain at least one source file.");
|
|
24
|
+
if (files.length > PROJECT_SOURCE_LIMITS.files) throw new Error(`A project cannot contain more than ${PROJECT_SOURCE_LIMITS.files} source files.`);
|
|
25
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26
|
+
let totalBytes = 0;
|
|
27
|
+
return files.map((file, index) => {
|
|
28
|
+
if (!file || typeof file !== "object") throw new Error(`Project file ${index} is invalid.`);
|
|
29
|
+
assertSafeRelativePath(file.path, `Project file ${index} path`);
|
|
30
|
+
assertLanguageIdentifier(file.language);
|
|
31
|
+
if (typeof file.content !== "string") throw new Error(`Project file '${file.path}' content must be a string.`);
|
|
32
|
+
if (file.content.length > PROJECT_SOURCE_LIMITS.bytesPerFile) throw new Error(`Project file '${file.path}' exceeds the ${PROJECT_SOURCE_LIMITS.bytesPerFile} byte source limit.`);
|
|
33
|
+
const contentBytes = UTF8_ENCODER.encode(file.content).byteLength;
|
|
34
|
+
if (contentBytes > PROJECT_SOURCE_LIMITS.bytesPerFile) throw new Error(`Project file '${file.path}' exceeds the ${PROJECT_SOURCE_LIMITS.bytesPerFile} byte source limit.`);
|
|
35
|
+
totalBytes += contentBytes;
|
|
36
|
+
if (totalBytes > PROJECT_SOURCE_LIMITS.totalBytes) throw new Error(`Project sources exceed the ${PROJECT_SOURCE_LIMITS.totalBytes} byte total limit.`);
|
|
37
|
+
if (seen.has(file.path)) throw new Error(`Duplicate project path '${file.path}'.`);
|
|
38
|
+
seen.add(file.path);
|
|
39
|
+
return {
|
|
40
|
+
path: file.path,
|
|
41
|
+
language: file.language,
|
|
42
|
+
content: file.content
|
|
43
|
+
};
|
|
44
|
+
}).sort((left, right) => compareCanonicalPaths(left.path, right.path));
|
|
45
|
+
}
|
|
46
|
+
function canonicalFileEntries(files) {
|
|
47
|
+
if (!files || typeof files !== "object" || Array.isArray(files)) throw new Error("Runtime bundle files must be a record.");
|
|
48
|
+
return Object.entries(files).map(([path, contents]) => {
|
|
49
|
+
assertSafeRelativePath(path, "Runtime bundle path");
|
|
50
|
+
return [path, contents];
|
|
51
|
+
}).sort(([left], [right]) => compareCanonicalPaths(left, right));
|
|
52
|
+
}
|
|
53
|
+
function canonicalFileRecord(files) {
|
|
54
|
+
return Object.fromEntries(canonicalFileEntries(files));
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/core/dependencies.ts
|
|
58
|
+
var MIB = 1048576;
|
|
59
|
+
/** Contract-level admission limits shared by dependency hosts and compilers. */
|
|
60
|
+
var DEPENDENCY_RESOLUTION_LIMITS = Object.freeze({
|
|
61
|
+
requirements: 128,
|
|
62
|
+
sourceFiles: 128,
|
|
63
|
+
sourceTextBytes: 8 * MIB,
|
|
64
|
+
hosts: 32,
|
|
65
|
+
roots: 512,
|
|
66
|
+
packages: 512,
|
|
67
|
+
referencesPerPackage: 512,
|
|
68
|
+
concurrency: 16,
|
|
69
|
+
metadataBytes: 8 * MIB,
|
|
70
|
+
packageBytes: 256 * MIB,
|
|
71
|
+
totalDownloadBytes: 512 * MIB,
|
|
72
|
+
archiveFiles: 16384,
|
|
73
|
+
unpackedBytes: 512 * MIB
|
|
74
|
+
});
|
|
75
|
+
Object.freeze({
|
|
76
|
+
packages: DEPENDENCY_RESOLUTION_LIMITS.packages,
|
|
77
|
+
filesPerPackage: DEPENDENCY_RESOLUTION_LIMITS.archiveFiles,
|
|
78
|
+
bytesPerFile: 64 * MIB,
|
|
79
|
+
totalBytes: DEPENDENCY_RESOLUTION_LIMITS.unpackedBytes
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Read stdout until `parse` recognizes one complete self-delimiting message.
|
|
83
|
+
*
|
|
84
|
+
* `@wasmer/sdk` 0.10 `Instance.wait()` joins stdout EOF, stderr EOF and the exit code, and its
|
|
85
|
+
* thread-pool teardown can drop either EOF after all output arrived, so `wait()` never settles.
|
|
86
|
+
* Completion therefore comes from the message itself. Either EOF means the guest exited, so the
|
|
87
|
+
* other stream then gets `idleGraceMs` without new bytes before the read stops. If both EOFs are
|
|
88
|
+
* lost before the message completes, the caller's own deadline still bounds the read.
|
|
89
|
+
*/
|
|
90
|
+
async function readProcessMessage(child, parse, options = {}) {
|
|
91
|
+
const idleGraceMs = options.idleGraceMs ?? 2e3;
|
|
92
|
+
await child.stdin?.close().catch(() => void 0);
|
|
93
|
+
const stdoutReader = child.stdout.getReader();
|
|
94
|
+
const stderrReader = child.stderr.getReader();
|
|
95
|
+
const nextStdout = () => stdoutReader.read().then(({ done, value }) => ({
|
|
96
|
+
source: "stdout",
|
|
97
|
+
chunk: done ? void 0 : value
|
|
98
|
+
}));
|
|
99
|
+
const nextStderr = () => stderrReader.read().then(({ done, value }) => ({
|
|
100
|
+
source: "stderr",
|
|
101
|
+
chunk: done ? void 0 : value
|
|
102
|
+
})).catch(() => ({ source: "stderr" }));
|
|
103
|
+
const stdout = byteBuffer();
|
|
104
|
+
const stderr = byteBuffer();
|
|
105
|
+
let stdoutRead = nextStdout();
|
|
106
|
+
let stderrRead = nextStderr();
|
|
107
|
+
let message;
|
|
108
|
+
try {
|
|
109
|
+
while (true) {
|
|
110
|
+
const complete = message !== void 0;
|
|
111
|
+
const pending = (complete ? [options.collectStderr ? stderrRead : void 0] : [stdoutRead, stderrRead]).filter((read) => read !== void 0);
|
|
112
|
+
if (pending.length === 0) break;
|
|
113
|
+
let timer;
|
|
114
|
+
if (complete || pending.length === 1) pending.push(new Promise((resolve) => {
|
|
115
|
+
timer = setTimeout(() => resolve({ source: "idle" }), idleGraceMs);
|
|
116
|
+
}));
|
|
117
|
+
const event = await Promise.race(pending).finally(() => clearTimeout(timer));
|
|
118
|
+
if (event.source === "idle") break;
|
|
119
|
+
if (event.source === "stdout") {
|
|
120
|
+
if (!event.chunk) {
|
|
121
|
+
stdoutRead = void 0;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
stdout.append(event.chunk);
|
|
125
|
+
message = parse(stdout.bytes());
|
|
126
|
+
stdoutRead = message === void 0 ? nextStdout() : void 0;
|
|
127
|
+
} else if (!event.chunk) stderrRead = void 0;
|
|
128
|
+
else {
|
|
129
|
+
stderr.append(event.chunk);
|
|
130
|
+
stderrRead = nextStderr();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} finally {
|
|
134
|
+
await Promise.allSettled([stdoutReader.cancel(), stderrReader.cancel()]);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
message,
|
|
138
|
+
stderr: new TextDecoder().decode(stderr.bytes())
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
var JSON_WHITESPACE = /* @__PURE__ */ new Set([
|
|
142
|
+
9,
|
|
143
|
+
10,
|
|
144
|
+
13,
|
|
145
|
+
32
|
|
146
|
+
]);
|
|
147
|
+
/** Parse stdout once it holds one complete JSON object, optionally followed by whitespace. */
|
|
148
|
+
function completeJsonObject(stdout) {
|
|
149
|
+
let end = stdout.byteLength;
|
|
150
|
+
while (end > 0 && JSON_WHITESPACE.has(stdout[end - 1])) end -= 1;
|
|
151
|
+
if (end === 0 || stdout[end - 1] !== 125) return void 0;
|
|
152
|
+
try {
|
|
153
|
+
return JSON.parse(new TextDecoder().decode(stdout.subarray(0, end)));
|
|
154
|
+
} catch {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** An append-only byte buffer that grows geometrically, so appending chunks stays linear. */
|
|
159
|
+
function byteBuffer() {
|
|
160
|
+
let buffer = /* @__PURE__ */ new Uint8Array(0);
|
|
161
|
+
let length = 0;
|
|
162
|
+
return {
|
|
163
|
+
append(chunk) {
|
|
164
|
+
if (length + chunk.byteLength > buffer.byteLength) {
|
|
165
|
+
const grown = new Uint8Array(Math.max(length + chunk.byteLength, buffer.byteLength * 2));
|
|
166
|
+
grown.set(buffer.subarray(0, length));
|
|
167
|
+
buffer = grown;
|
|
168
|
+
}
|
|
169
|
+
buffer.set(chunk, length);
|
|
170
|
+
length += chunk.byteLength;
|
|
171
|
+
},
|
|
172
|
+
bytes: () => buffer.subarray(0, length)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/server/process-keepalive.mjs
|
|
177
|
+
/**
|
|
178
|
+
* Keep Node alive while a promise is backed only by a foreign async runtime.
|
|
179
|
+
* Top-level await does not itself create a referenced libuv handle, so Node can
|
|
180
|
+
* otherwise exit with code 13 while a Wasmer SDK operation is still pending.
|
|
181
|
+
*/
|
|
182
|
+
async function withProcessKeepalive(promise) {
|
|
183
|
+
const keepalive = setInterval(() => void 0, 6e4);
|
|
184
|
+
try {
|
|
185
|
+
return await promise;
|
|
186
|
+
} finally {
|
|
187
|
+
clearInterval(keepalive);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
export { canonicalFileRecord as a, assertSafeRelativePath as i, completeJsonObject as n, canonicalProjectFiles as o, readProcessMessage as r, withProcessKeepalive as t };
|
package/dist/index.js
CHANGED
|
@@ -775,8 +775,8 @@ Object.freeze({
|
|
|
775
775
|
});
|
|
776
776
|
new TextEncoder();
|
|
777
777
|
Object.freeze({
|
|
778
|
-
runtimeCoreWasmSha256: "
|
|
779
|
-
runtimeSourceRootSha256: "
|
|
778
|
+
runtimeCoreWasmSha256: "077aa936a3382a242eee7222fc17369769cb5ad265303d730ffc45ee192367e9",
|
|
779
|
+
runtimeSourceRootSha256: "c65b3bbcad41359f86b8f1d1315af8f079b44f0e9084ace0d6d50c0ec418415b",
|
|
780
780
|
wasmerNativeVersion: "7.2.1",
|
|
781
781
|
wasmerSdkVersion: "0.10.0",
|
|
782
782
|
wasmerSdkWasmSha256: "49a6646209f5ab5e7c737eac33407d87d9a9959ac83e5ecaaab9261b2323589e",
|
|
@@ -787,7 +787,7 @@ Object.freeze({
|
|
|
787
787
|
* Release verification independently checks the component bytes before this
|
|
788
788
|
* identity is admitted into a calibrated release.
|
|
789
789
|
*/
|
|
790
|
-
var WASM_OJ_RUNTIME_IDENTITY_SHA256 = "
|
|
790
|
+
var WASM_OJ_RUNTIME_IDENTITY_SHA256 = "47684bee57aafd163b18a787fa16eed33cab3ec3c0005c3c46bb4f1803ffc835";
|
|
791
791
|
//#endregion
|
|
792
792
|
//#region src/core/cost-profile.ts
|
|
793
793
|
function coordinates(language, target, optimization) {
|
|
@@ -1858,6 +1858,100 @@ function failedBuild(project, stdout, stderr, code, source, providedDiagnostics)
|
|
|
1858
1858
|
cacheHit: false
|
|
1859
1859
|
};
|
|
1860
1860
|
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Read stdout until `parse` recognizes one complete self-delimiting message.
|
|
1863
|
+
*
|
|
1864
|
+
* `@wasmer/sdk` 0.10 `Instance.wait()` joins stdout EOF, stderr EOF and the exit code, and its
|
|
1865
|
+
* thread-pool teardown can drop either EOF after all output arrived, so `wait()` never settles.
|
|
1866
|
+
* Completion therefore comes from the message itself. Either EOF means the guest exited, so the
|
|
1867
|
+
* other stream then gets `idleGraceMs` without new bytes before the read stops. If both EOFs are
|
|
1868
|
+
* lost before the message completes, the caller's own deadline still bounds the read.
|
|
1869
|
+
*/
|
|
1870
|
+
async function readProcessMessage(child, parse, options = {}) {
|
|
1871
|
+
const idleGraceMs = options.idleGraceMs ?? 2e3;
|
|
1872
|
+
await child.stdin?.close().catch(() => void 0);
|
|
1873
|
+
const stdoutReader = child.stdout.getReader();
|
|
1874
|
+
const stderrReader = child.stderr.getReader();
|
|
1875
|
+
const nextStdout = () => stdoutReader.read().then(({ done, value }) => ({
|
|
1876
|
+
source: "stdout",
|
|
1877
|
+
chunk: done ? void 0 : value
|
|
1878
|
+
}));
|
|
1879
|
+
const nextStderr = () => stderrReader.read().then(({ done, value }) => ({
|
|
1880
|
+
source: "stderr",
|
|
1881
|
+
chunk: done ? void 0 : value
|
|
1882
|
+
})).catch(() => ({ source: "stderr" }));
|
|
1883
|
+
const stdout = byteBuffer();
|
|
1884
|
+
const stderr = byteBuffer();
|
|
1885
|
+
let stdoutRead = nextStdout();
|
|
1886
|
+
let stderrRead = nextStderr();
|
|
1887
|
+
let message;
|
|
1888
|
+
try {
|
|
1889
|
+
while (true) {
|
|
1890
|
+
const complete = message !== void 0;
|
|
1891
|
+
const pending = (complete ? [options.collectStderr ? stderrRead : void 0] : [stdoutRead, stderrRead]).filter((read) => read !== void 0);
|
|
1892
|
+
if (pending.length === 0) break;
|
|
1893
|
+
let timer;
|
|
1894
|
+
if (complete || pending.length === 1) pending.push(new Promise((resolve) => {
|
|
1895
|
+
timer = setTimeout(() => resolve({ source: "idle" }), idleGraceMs);
|
|
1896
|
+
}));
|
|
1897
|
+
const event = await Promise.race(pending).finally(() => clearTimeout(timer));
|
|
1898
|
+
if (event.source === "idle") break;
|
|
1899
|
+
if (event.source === "stdout") {
|
|
1900
|
+
if (!event.chunk) {
|
|
1901
|
+
stdoutRead = void 0;
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
stdout.append(event.chunk);
|
|
1905
|
+
message = parse(stdout.bytes());
|
|
1906
|
+
stdoutRead = message === void 0 ? nextStdout() : void 0;
|
|
1907
|
+
} else if (!event.chunk) stderrRead = void 0;
|
|
1908
|
+
else {
|
|
1909
|
+
stderr.append(event.chunk);
|
|
1910
|
+
stderrRead = nextStderr();
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
} finally {
|
|
1914
|
+
await Promise.allSettled([stdoutReader.cancel(), stderrReader.cancel()]);
|
|
1915
|
+
}
|
|
1916
|
+
return {
|
|
1917
|
+
message,
|
|
1918
|
+
stderr: new TextDecoder().decode(stderr.bytes())
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
var JSON_WHITESPACE = /* @__PURE__ */ new Set([
|
|
1922
|
+
9,
|
|
1923
|
+
10,
|
|
1924
|
+
13,
|
|
1925
|
+
32
|
|
1926
|
+
]);
|
|
1927
|
+
/** Parse stdout once it holds one complete JSON object, optionally followed by whitespace. */
|
|
1928
|
+
function completeJsonObject(stdout) {
|
|
1929
|
+
let end = stdout.byteLength;
|
|
1930
|
+
while (end > 0 && JSON_WHITESPACE.has(stdout[end - 1])) end -= 1;
|
|
1931
|
+
if (end === 0 || stdout[end - 1] !== 125) return void 0;
|
|
1932
|
+
try {
|
|
1933
|
+
return JSON.parse(new TextDecoder().decode(stdout.subarray(0, end)));
|
|
1934
|
+
} catch {
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
/** An append-only byte buffer that grows geometrically, so appending chunks stays linear. */
|
|
1939
|
+
function byteBuffer() {
|
|
1940
|
+
let buffer = /* @__PURE__ */ new Uint8Array(0);
|
|
1941
|
+
let length = 0;
|
|
1942
|
+
return {
|
|
1943
|
+
append(chunk) {
|
|
1944
|
+
if (length + chunk.byteLength > buffer.byteLength) {
|
|
1945
|
+
const grown = new Uint8Array(Math.max(length + chunk.byteLength, buffer.byteLength * 2));
|
|
1946
|
+
grown.set(buffer.subarray(0, length));
|
|
1947
|
+
buffer = grown;
|
|
1948
|
+
}
|
|
1949
|
+
buffer.set(chunk, length);
|
|
1950
|
+
length += chunk.byteLength;
|
|
1951
|
+
},
|
|
1952
|
+
bytes: () => buffer.subarray(0, length)
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1861
1955
|
new TextEncoder();
|
|
1862
1956
|
Object.freeze({
|
|
1863
1957
|
python: "python",
|
|
@@ -2244,7 +2338,7 @@ async function transpileScriptProject(project, requestId) {
|
|
|
2244
2338
|
if (!entrypoint) throw new Error("The TypeScript/WASI compiler has no executable entrypoint.");
|
|
2245
2339
|
const outputPaths = emittedFiles.map((file) => emittedScriptPath(file.path));
|
|
2246
2340
|
const declarationPath = "/project/.wasm-oj/quickjs.d.ts";
|
|
2247
|
-
const
|
|
2341
|
+
const instance = await entrypoint.run({ stdin: JSON.stringify({
|
|
2248
2342
|
files: {
|
|
2249
2343
|
...Object.fromEntries(project.files.map((file) => [`/project/${file.path}`, file.content])),
|
|
2250
2344
|
...Object.fromEntries(Object.entries(dependencyFiles).filter(([, contents]) => typeof contents === "string").map(([path, contents]) => [`/project/${path}`, contents])),
|
|
@@ -2256,13 +2350,9 @@ async function transpileScriptProject(project, requestId) {
|
|
|
2256
2350
|
...Object.entries(dependencyFiles).filter(([path, contents]) => path.endsWith(".d.ts") && typeof contents === "string").map(([path]) => `/project/${path}`)
|
|
2257
2351
|
],
|
|
2258
2352
|
outputs: outputPaths.map((path) => `/project/build/${path}`)
|
|
2259
|
-
}) })
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
response = JSON.parse(output.stdout);
|
|
2263
|
-
} catch {
|
|
2264
|
-
response = void 0;
|
|
2265
|
-
}
|
|
2353
|
+
}) });
|
|
2354
|
+
const output = await readProcessMessage(instance, completeJsonObject, { collectStderr: true }).finally(() => instance.free());
|
|
2355
|
+
const response = output.message;
|
|
2266
2356
|
const files = {};
|
|
2267
2357
|
if (response) for (const outputPath of outputPaths) {
|
|
2268
2358
|
const contents = response.files[`/project/build/${outputPath}`];
|
|
@@ -2271,7 +2361,7 @@ async function transpileScriptProject(project, requestId) {
|
|
|
2271
2361
|
Object.assign(files, dependencyFiles);
|
|
2272
2362
|
return {
|
|
2273
2363
|
files,
|
|
2274
|
-
output,
|
|
2364
|
+
stderr: output.stderr,
|
|
2275
2365
|
response
|
|
2276
2366
|
};
|
|
2277
2367
|
}
|
|
@@ -2304,10 +2394,10 @@ async function buildScript(project, cacheKey, requestId) {
|
|
|
2304
2394
|
progress(requestId, "compiling", "Compiling TypeScript with TypeScript/WASI", .5);
|
|
2305
2395
|
const transpiled = await transpileScriptProject(project, requestId);
|
|
2306
2396
|
files = transpiled.files;
|
|
2307
|
-
stderr = transpiled.
|
|
2397
|
+
stderr = transpiled.stderr;
|
|
2308
2398
|
diagnostics = parseTypeScriptDiagnostics(transpiled.response?.diagnostics ?? "");
|
|
2309
2399
|
const emittedOutputsPresent = emittedSourceFiles(project).every((file) => Object.hasOwn(files, emittedScriptPath(file.path)));
|
|
2310
|
-
if (!transpiled.
|
|
2400
|
+
if (!transpiled.response || transpiled.response.status !== 0 || !emittedOutputsPresent || diagnostics.some((diagnostic) => diagnostic.severity === "error")) return {
|
|
2311
2401
|
success: false,
|
|
2312
2402
|
diagnostics: ensureFailureDiagnostic(diagnostics, {
|
|
2313
2403
|
file: project.config.entry,
|
|
@@ -3287,7 +3377,6 @@ var ServerRunner = class {
|
|
|
3287
3377
|
this.assertArtifactToolchain(contestantArtifact);
|
|
3288
3378
|
this.assertArtifactToolchain(interactorArtifact);
|
|
3289
3379
|
if (this.activeOperation || this.cacheClearActive) throw new Error("ServerRunner accepts one active operation at a time.");
|
|
3290
|
-
if (interactorArtifact.kind !== "wasm") throw new Error("Interactive judge artifacts must be standalone Wasm modules.");
|
|
3291
3380
|
const operation = createServerRunOperation(this.generation);
|
|
3292
3381
|
this.activeOperation = operation;
|
|
3293
3382
|
this.inFlightRuns.add(operation);
|
|
@@ -3610,7 +3699,7 @@ var ServerRunner = class {
|
|
|
3610
3699
|
const cleanup = (ignoreLateChildError) => {
|
|
3611
3700
|
child.off("close", onClose);
|
|
3612
3701
|
child.off("error", onChildError);
|
|
3613
|
-
child.stdin.off("error", onStdinError);
|
|
3702
|
+
child.stdin.off("error", onStdinError).on("error", () => void 0);
|
|
3614
3703
|
child.stdout.off("data", onStdout);
|
|
3615
3704
|
child.stderr.off("data", onStderr);
|
|
3616
3705
|
this.activePreparationStages.delete(active);
|
|
@@ -3749,7 +3838,7 @@ var ServerRunner = class {
|
|
|
3749
3838
|
clearTimeout(timer);
|
|
3750
3839
|
child.off("close", onClose);
|
|
3751
3840
|
child.off("error", onChildError);
|
|
3752
|
-
child.stdin.off("error", onStdinError);
|
|
3841
|
+
child.stdin.off("error", onStdinError).on("error", () => void 0);
|
|
3753
3842
|
child.stdout.off("data", onStdout);
|
|
3754
3843
|
child.stderr.off("data", onStderr);
|
|
3755
3844
|
this.activeNativeRuns.delete(active);
|
|
@@ -3847,7 +3936,7 @@ var ServerRunner = class {
|
|
|
3847
3936
|
clearTimeout(timer);
|
|
3848
3937
|
child.off("close", onClose);
|
|
3849
3938
|
child.off("error", onChildError);
|
|
3850
|
-
child.stdin.off("error", onStdinError);
|
|
3939
|
+
child.stdin.off("error", onStdinError).on("error", () => void 0);
|
|
3851
3940
|
child.stdout.off("data", onStdout);
|
|
3852
3941
|
child.stderr.off("data", onStderr);
|
|
3853
3942
|
this.activeNativeRuns.delete(active);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { A as QUICKJS_PACKAGE, B as toolchainPackageIdentities, C as JAVA_COMPILE_CLASSLIB_SHA256, E as JAVA_VERSION, I as RUST_VERSION, L as TYPESCRIPT_ASSET_PATH, O as PYTHON_PACKAGE, R as TYPESCRIPT_VERSION, S as JAVA_COMPILE_CLASSLIB_ASSET_PATH, T as JAVA_RUNTIME_CLASSLIB_SHA256, a as CLANG_PACKAGE_ASSET_PATH, b as JAVA_COMPILER_PACKAGE, i as CLANG_LIBCXX_PCH_MANIFEST_SHA256, n as CLANG_CC1_PINS_SHA256, o as CLANG_PACKAGE_SHA256, r as CLANG_LIBCXX_PCH_MANIFEST_ASSET_PATH, s as CLANG_VERSION, t as CLANG_CC1_PINS_ASSET_PATH, v as GO_VERSION, w as JAVA_RUNTIME_CLASSLIB_ASSET_PATH, x as JAVA_COMPILER_SHA256, y as JAVA_COMPILER_ASSET_PATH, z as toolchainContentIdentity } from "./chunks/toolchains-uz3XbZGI.js";
|
|
2
|
+
import { a as canonicalFileRecord, i as assertSafeRelativePath, n as completeJsonObject, o as canonicalProjectFiles, r as readProcessMessage, t as withProcessKeepalive } from "./chunks/process-keepalive-BaXpMTap.js";
|
|
2
3
|
import { f as MountedOutputStabilityObserver, n as RUST_TOOLCHAIN, t as RUST_COMPILE_TIMEOUT_MS } from "./chunks/rust-toolchain-CK5h2WT8.js";
|
|
3
4
|
import { _ as pythonDependencyFiles, g as npmDependencyFiles, h as goDependencyInput, i as GO_TOOLCHAIN, m as cppDependencyInput, n as GO_COMPILE_TIMEOUT_MS, p as assertProjectDependencyEcosystem, v as rustDependencyInput } from "./chunks/go-toolchain-DRQSILaC.js";
|
|
4
|
-
import { t as withProcessKeepalive } from "./chunks/process-keepalive-BWLUz55N.js";
|
|
5
5
|
import { constants, writeFileSync } from "node:fs";
|
|
6
6
|
import { deserialize, serialize } from "node:v8";
|
|
7
7
|
import { access, lstat, mkdtemp, open, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -15,60 +15,6 @@ import { assertCompilerCacheKey, assertValidProject, toolchainAssetSource, toolc
|
|
|
15
15
|
import { WASM_OJ_CONTRACT_VERSION, WASM_OJ_SCHEMAS, assertLanguageIdentifier, isBuiltinLanguage } from "@wasm-oj/contracts";
|
|
16
16
|
import { Directory as Directory$1, Wasmer as Wasmer$1 } from "@wasmer/sdk";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
|
-
//#region src/core/project-files.ts
|
|
19
|
-
var PROJECT_SOURCE_LIMITS = Object.freeze({
|
|
20
|
-
files: 256,
|
|
21
|
-
bytesPerFile: 4194304,
|
|
22
|
-
totalBytes: 16777216
|
|
23
|
-
});
|
|
24
|
-
var UTF8_ENCODER = new TextEncoder();
|
|
25
|
-
/** Locale-independent ordering used anywhere file order can affect build output. */
|
|
26
|
-
function compareCanonicalPaths(left, right) {
|
|
27
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
28
|
-
}
|
|
29
|
-
function assertSafeRelativePath(path, label = "Project path") {
|
|
30
|
-
if (typeof path !== "string" || !path || path !== path.trim() || path.length > 4096) throw new Error(`${label} must be a non-empty, trimmed string of at most 4096 characters.`);
|
|
31
|
-
if (path.startsWith("/") || path.includes("\\") || path.includes("\0") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) throw new Error(`${label} '${path}' must be a normalized relative path that cannot escape the project.`);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Validates a project file set and returns a fresh, locale-independently sorted
|
|
35
|
-
* array. Build hosts must use this order for filesystem creation and argv.
|
|
36
|
-
*/
|
|
37
|
-
function canonicalProjectFiles(files) {
|
|
38
|
-
if (!Array.isArray(files) || files.length === 0) throw new Error("A project must contain at least one source file.");
|
|
39
|
-
if (files.length > PROJECT_SOURCE_LIMITS.files) throw new Error(`A project cannot contain more than ${PROJECT_SOURCE_LIMITS.files} source files.`);
|
|
40
|
-
const seen = /* @__PURE__ */ new Set();
|
|
41
|
-
let totalBytes = 0;
|
|
42
|
-
return files.map((file, index) => {
|
|
43
|
-
if (!file || typeof file !== "object") throw new Error(`Project file ${index} is invalid.`);
|
|
44
|
-
assertSafeRelativePath(file.path, `Project file ${index} path`);
|
|
45
|
-
assertLanguageIdentifier(file.language);
|
|
46
|
-
if (typeof file.content !== "string") throw new Error(`Project file '${file.path}' content must be a string.`);
|
|
47
|
-
if (file.content.length > PROJECT_SOURCE_LIMITS.bytesPerFile) throw new Error(`Project file '${file.path}' exceeds the ${PROJECT_SOURCE_LIMITS.bytesPerFile} byte source limit.`);
|
|
48
|
-
const contentBytes = UTF8_ENCODER.encode(file.content).byteLength;
|
|
49
|
-
if (contentBytes > PROJECT_SOURCE_LIMITS.bytesPerFile) throw new Error(`Project file '${file.path}' exceeds the ${PROJECT_SOURCE_LIMITS.bytesPerFile} byte source limit.`);
|
|
50
|
-
totalBytes += contentBytes;
|
|
51
|
-
if (totalBytes > PROJECT_SOURCE_LIMITS.totalBytes) throw new Error(`Project sources exceed the ${PROJECT_SOURCE_LIMITS.totalBytes} byte total limit.`);
|
|
52
|
-
if (seen.has(file.path)) throw new Error(`Duplicate project path '${file.path}'.`);
|
|
53
|
-
seen.add(file.path);
|
|
54
|
-
return {
|
|
55
|
-
path: file.path,
|
|
56
|
-
language: file.language,
|
|
57
|
-
content: file.content
|
|
58
|
-
};
|
|
59
|
-
}).sort((left, right) => compareCanonicalPaths(left.path, right.path));
|
|
60
|
-
}
|
|
61
|
-
function canonicalFileEntries(files) {
|
|
62
|
-
if (!files || typeof files !== "object" || Array.isArray(files)) throw new Error("Runtime bundle files must be a record.");
|
|
63
|
-
return Object.entries(files).map(([path, contents]) => {
|
|
64
|
-
assertSafeRelativePath(path, "Runtime bundle path");
|
|
65
|
-
return [path, contents];
|
|
66
|
-
}).sort(([left], [right]) => compareCanonicalPaths(left, right));
|
|
67
|
-
}
|
|
68
|
-
function canonicalFileRecord(files) {
|
|
69
|
-
return Object.fromEntries(canonicalFileEntries(files));
|
|
70
|
-
}
|
|
71
|
-
//#endregion
|
|
72
18
|
//#region src/core/sha256.ts
|
|
73
19
|
async function sha256Hex(value) {
|
|
74
20
|
const source = typeof value === "string" ? new TextEncoder().encode(value) : value;
|
|
@@ -78,31 +24,6 @@ async function sha256Hex(value) {
|
|
|
78
24
|
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
79
25
|
}
|
|
80
26
|
//#endregion
|
|
81
|
-
//#region src/core/dependencies.ts
|
|
82
|
-
var MIB = 1048576;
|
|
83
|
-
/** Contract-level admission limits shared by dependency hosts and compilers. */
|
|
84
|
-
var DEPENDENCY_RESOLUTION_LIMITS = Object.freeze({
|
|
85
|
-
requirements: 128,
|
|
86
|
-
sourceFiles: 128,
|
|
87
|
-
sourceTextBytes: 8 * MIB,
|
|
88
|
-
hosts: 32,
|
|
89
|
-
roots: 512,
|
|
90
|
-
packages: 512,
|
|
91
|
-
referencesPerPackage: 512,
|
|
92
|
-
concurrency: 16,
|
|
93
|
-
metadataBytes: 8 * MIB,
|
|
94
|
-
packageBytes: 256 * MIB,
|
|
95
|
-
totalDownloadBytes: 512 * MIB,
|
|
96
|
-
archiveFiles: 16384,
|
|
97
|
-
unpackedBytes: 512 * MIB
|
|
98
|
-
});
|
|
99
|
-
Object.freeze({
|
|
100
|
-
packages: DEPENDENCY_RESOLUTION_LIMITS.packages,
|
|
101
|
-
filesPerPackage: DEPENDENCY_RESOLUTION_LIMITS.archiveFiles,
|
|
102
|
-
bytesPerFile: 64 * MIB,
|
|
103
|
-
totalBytes: DEPENDENCY_RESOLUTION_LIMITS.unpackedBytes
|
|
104
|
-
});
|
|
105
|
-
//#endregion
|
|
106
27
|
//#region src/compiler/clang-pins.ts
|
|
107
28
|
var decoder$3 = new TextDecoder();
|
|
108
29
|
async function decodeClangPins(bytes) {
|
|
@@ -561,8 +482,8 @@ Object.freeze({
|
|
|
561
482
|
});
|
|
562
483
|
new TextEncoder();
|
|
563
484
|
Object.freeze({
|
|
564
|
-
runtimeCoreWasmSha256: "
|
|
565
|
-
runtimeSourceRootSha256: "
|
|
485
|
+
runtimeCoreWasmSha256: "077aa936a3382a242eee7222fc17369769cb5ad265303d730ffc45ee192367e9",
|
|
486
|
+
runtimeSourceRootSha256: "c65b3bbcad41359f86b8f1d1315af8f079b44f0e9084ace0d6d50c0ec418415b",
|
|
566
487
|
wasmerNativeVersion: "7.2.1",
|
|
567
488
|
wasmerSdkVersion: "0.10.0",
|
|
568
489
|
wasmerSdkWasmSha256: "49a6646209f5ab5e7c737eac33407d87d9a9959ac83e5ecaaab9261b2323589e",
|
|
@@ -573,7 +494,7 @@ Object.freeze({
|
|
|
573
494
|
* Release verification independently checks the component bytes before this
|
|
574
495
|
* identity is admitted into a calibrated release.
|
|
575
496
|
*/
|
|
576
|
-
var WASM_OJ_RUNTIME_IDENTITY_SHA256 = "
|
|
497
|
+
var WASM_OJ_RUNTIME_IDENTITY_SHA256 = "47684bee57aafd163b18a787fa16eed33cab3ec3c0005c3c46bb4f1803ffc835";
|
|
577
498
|
//#endregion
|
|
578
499
|
//#region src/core/cost-profile.ts
|
|
579
500
|
function coordinates(language, target, optimization) {
|
|
@@ -1753,7 +1674,7 @@ async function transpileScriptProject(project, requestId) {
|
|
|
1753
1674
|
if (!entrypoint) throw new Error("The TypeScript/WASI compiler has no executable entrypoint.");
|
|
1754
1675
|
const outputPaths = emittedFiles.map((file) => emittedScriptPath(file.path));
|
|
1755
1676
|
const declarationPath = "/project/.wasm-oj/quickjs.d.ts";
|
|
1756
|
-
const
|
|
1677
|
+
const instance = await entrypoint.run({ stdin: JSON.stringify({
|
|
1757
1678
|
files: {
|
|
1758
1679
|
...Object.fromEntries(project.files.map((file) => [`/project/${file.path}`, file.content])),
|
|
1759
1680
|
...Object.fromEntries(Object.entries(dependencyFiles).filter(([, contents]) => typeof contents === "string").map(([path, contents]) => [`/project/${path}`, contents])),
|
|
@@ -1765,13 +1686,9 @@ async function transpileScriptProject(project, requestId) {
|
|
|
1765
1686
|
...Object.entries(dependencyFiles).filter(([path, contents]) => path.endsWith(".d.ts") && typeof contents === "string").map(([path]) => `/project/${path}`)
|
|
1766
1687
|
],
|
|
1767
1688
|
outputs: outputPaths.map((path) => `/project/build/${path}`)
|
|
1768
|
-
}) })
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
response = JSON.parse(output.stdout);
|
|
1772
|
-
} catch {
|
|
1773
|
-
response = void 0;
|
|
1774
|
-
}
|
|
1689
|
+
}) });
|
|
1690
|
+
const output = await readProcessMessage(instance, completeJsonObject, { collectStderr: true }).finally(() => instance.free());
|
|
1691
|
+
const response = output.message;
|
|
1775
1692
|
const files = {};
|
|
1776
1693
|
if (response) for (const outputPath of outputPaths) {
|
|
1777
1694
|
const contents = response.files[`/project/build/${outputPath}`];
|
|
@@ -1780,7 +1697,7 @@ async function transpileScriptProject(project, requestId) {
|
|
|
1780
1697
|
Object.assign(files, dependencyFiles);
|
|
1781
1698
|
return {
|
|
1782
1699
|
files,
|
|
1783
|
-
output,
|
|
1700
|
+
stderr: output.stderr,
|
|
1784
1701
|
response
|
|
1785
1702
|
};
|
|
1786
1703
|
}
|
|
@@ -1813,10 +1730,10 @@ async function buildScript(project, cacheKey, requestId) {
|
|
|
1813
1730
|
progress(requestId, "compiling", "Compiling TypeScript with TypeScript/WASI", .5);
|
|
1814
1731
|
const transpiled = await transpileScriptProject(project, requestId);
|
|
1815
1732
|
files = transpiled.files;
|
|
1816
|
-
stderr = transpiled.
|
|
1733
|
+
stderr = transpiled.stderr;
|
|
1817
1734
|
diagnostics = parseTypeScriptDiagnostics(transpiled.response?.diagnostics ?? "");
|
|
1818
1735
|
const emittedOutputsPresent = emittedSourceFiles(project).every((file) => Object.hasOwn(files, emittedScriptPath(file.path)));
|
|
1819
|
-
if (!transpiled.
|
|
1736
|
+
if (!transpiled.response || transpiled.response.status !== 0 || !emittedOutputsPresent || diagnostics.some((diagnostic) => diagnostic.severity === "error")) return {
|
|
1820
1737
|
success: false,
|
|
1821
1738
|
diagnostics: ensureFailureDiagnostic(diagnostics, {
|
|
1822
1739
|
file: project.config.entry,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as PYTHON_COMPRESSED_PACKAGE_SHA256, O as PYTHON_PACKAGE, k as PYTHON_PACKAGE_SHA256 } from "./chunks/toolchains-uz3XbZGI.js";
|
|
2
|
-
import { t as withProcessKeepalive } from "./chunks/process-keepalive-
|
|
2
|
+
import { r as readProcessMessage, t as withProcessKeepalive } from "./chunks/process-keepalive-BaXpMTap.js";
|
|
3
3
|
import { writeFileSync } from "node:fs";
|
|
4
4
|
import { serialize } from "node:v8";
|
|
5
5
|
import { readFile } from "node:fs/promises";
|
|
@@ -7,6 +7,71 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { gunzipSync } from "node:zlib";
|
|
9
9
|
import { Runtime, Wasmer, init } from "@wasmer/sdk/node";
|
|
10
|
+
import { WASM_OJ_STORAGE } from "@wasm-oj/contracts";
|
|
11
|
+
//#region src/runner/runtime-files.ts
|
|
12
|
+
var MAGIC = new TextEncoder().encode("WOJFS002");
|
|
13
|
+
var HEADER_BYTES = 12;
|
|
14
|
+
`${WASM_OJ_STORAGE.runtimeFilesCache}`;
|
|
15
|
+
String.raw`
|
|
16
|
+
import io
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
import zipfile
|
|
20
|
+
|
|
21
|
+
source_root = "/usr/local/lib/python3.14"
|
|
22
|
+
guest_path = "/cpython/lib/python314.zip"
|
|
23
|
+
output = sys.stdout.buffer
|
|
24
|
+
output.write(b"WOJFS002")
|
|
25
|
+
|
|
26
|
+
archive_buffer = io.BytesIO()
|
|
27
|
+
with zipfile.ZipFile(archive_buffer, "w", compression=zipfile.ZIP_STORED) as archive:
|
|
28
|
+
for root, directories, files in os.walk(source_root):
|
|
29
|
+
directories[:] = sorted(
|
|
30
|
+
name for name in directories
|
|
31
|
+
if name != "__pycache__" and not os.path.islink(os.path.join(root, name))
|
|
32
|
+
)
|
|
33
|
+
for name in sorted(files):
|
|
34
|
+
source_path = os.path.join(root, name)
|
|
35
|
+
if os.path.islink(source_path) or name.endswith((".pyc", ".pyo")):
|
|
36
|
+
continue
|
|
37
|
+
archive_path = os.path.relpath(source_path, source_root).replace(os.sep, "/")
|
|
38
|
+
info = zipfile.ZipInfo(archive_path, date_time=(1980, 1, 1, 0, 0, 0))
|
|
39
|
+
info.compress_type = zipfile.ZIP_STORED
|
|
40
|
+
info.external_attr = 0o100644 << 16
|
|
41
|
+
with open(source_path, "rb") as source:
|
|
42
|
+
archive.writestr(info, source.read())
|
|
43
|
+
|
|
44
|
+
encoded_path = guest_path.encode("utf-8")
|
|
45
|
+
archive_data = archive_buffer.getvalue()
|
|
46
|
+
output.write(len(encoded_path).to_bytes(4, "little"))
|
|
47
|
+
output.write(len(archive_data).to_bytes(8, "little"))
|
|
48
|
+
output.write(encoded_path)
|
|
49
|
+
output.write(archive_data)
|
|
50
|
+
|
|
51
|
+
output.write((0).to_bytes(4, "little"))
|
|
52
|
+
output.write((0).to_bytes(8, "little"))
|
|
53
|
+
`;
|
|
54
|
+
/**
|
|
55
|
+
* Read an export until stdout holds a complete archive and return exactly its framed bytes.
|
|
56
|
+
* `@wasmer/sdk` `Instance.wait()` can hang after all output arrived; see readProcessMessage.
|
|
57
|
+
*/
|
|
58
|
+
async function readRuntimeFilesExport(exporter, idleGraceMs) {
|
|
59
|
+
const { message, stderr } = await readProcessMessage(exporter, completeArchive, { idleGraceMs });
|
|
60
|
+
if (message === void 0) throw new Error(`The runtime file export ended before its archive was complete: ${stderr}`);
|
|
61
|
+
return message;
|
|
62
|
+
}
|
|
63
|
+
function completeArchive(bytes) {
|
|
64
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
65
|
+
let offset = MAGIC.byteLength;
|
|
66
|
+
while (offset + HEADER_BYTES <= bytes.byteLength) {
|
|
67
|
+
const pathLength = view.getUint32(offset, true);
|
|
68
|
+
const dataLength = Number(view.getBigUint64(offset + 4, true));
|
|
69
|
+
offset += HEADER_BYTES;
|
|
70
|
+
if (pathLength === 0 && dataLength === 0) return bytes.slice(0, offset);
|
|
71
|
+
offset += pathLength + dataLength;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
10
75
|
//#region src/server/server-runner-stage.mjs
|
|
11
76
|
var MAX_INPUT_BYTES = 1048576;
|
|
12
77
|
var MAX_RESULT_BYTES = 268435456;
|
|
@@ -43,9 +108,13 @@ try {
|
|
|
43
108
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
44
109
|
}
|
|
45
110
|
}));
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
111
|
+
try {
|
|
112
|
+
bytes = await withProcessKeepalive(readRuntimeFilesExport(instance));
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new Error(`Unable to export runtime files from ${input.request.packageSpecifier}: ${boundedText(errorText(error))}`);
|
|
115
|
+
} finally {
|
|
116
|
+
instance.free();
|
|
117
|
+
}
|
|
49
118
|
}
|
|
50
119
|
if (bytes.byteLength > MAX_RESULT_BYTES) throw new Error(`Runner stage result is ${bytes.byteLength} bytes; the limit is ${MAX_RESULT_BYTES} bytes.`);
|
|
51
120
|
response = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wasm-oj/server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Node.js compiler and runner host for the WASM-OJ SDK.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "JacobLinCool",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"node": ">=24.18.0 <25"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@wasm-oj/contracts": "0.2.
|
|
62
|
-
"@wasm-oj/core": "0.2.
|
|
61
|
+
"@wasm-oj/contracts": "0.2.5",
|
|
62
|
+
"@wasm-oj/core": "0.2.5",
|
|
63
63
|
"@wasmer/sdk": "0.10.0",
|
|
64
64
|
"fflate": "0.8.3"
|
|
65
65
|
},
|