space-data-module-sdk 0.5.8 → 0.5.10
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/README.md +86 -47
- package/package.json +1 -1
- package/src/index.d.ts +13 -0
- package/src/invoke/codec.js +9 -7
- package/src/testing/buildWasmEdgeRunner.js +214 -0
- package/src/testing/index.d.ts +109 -0
- package/src/testing/index.js +16 -0
- package/src/testing/moduleHarness.js +62 -0
- package/src/testing/native/wasmedge_emscripten_pthread_runner.c +1001 -0
- package/src/testing/processInvoke.js +226 -0
- package/src/transport/records.js +37 -52
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Space Data Module SDK
|
|
2
2
|
|
|
3
|
-
`space-data-module-sdk` defines the canonical module
|
|
4
|
-
|
|
5
|
-
vocabulary,
|
|
6
|
-
used to move modules between hosts.
|
|
3
|
+
`space-data-module-sdk` defines the canonical Space Data module architecture for
|
|
4
|
+
WebAssembly artifacts: an embedded FlatBuffer manifest, canonical ABI exports,
|
|
5
|
+
a stable capability vocabulary, single-file packaging, and the signing and
|
|
6
|
+
transport records used to move modules between hosts.
|
|
7
7
|
|
|
8
8
|
This repository is the source of truth for module-level concerns:
|
|
9
9
|
|
|
@@ -11,17 +11,34 @@ This repository is the source of truth for module-level concerns:
|
|
|
11
11
|
- embedded manifest exports inside `.wasm` modules
|
|
12
12
|
- standards-aware compliance and capability validation
|
|
13
13
|
- module compilation and protection
|
|
14
|
-
- the `sds.bundle` single-file
|
|
14
|
+
- the `sds.bundle` single-file container
|
|
15
15
|
- deployment authorization plus SDS publication records (`REC`, `PNM`, `ENC`)
|
|
16
16
|
- the first canonical module hostcall/import ABI surface
|
|
17
|
+
- shared module-level testing/runtime harnesses, including the WasmEdge
|
|
18
|
+
process runner used by package-level validation suites
|
|
17
19
|
|
|
18
20
|
<p align="center">
|
|
19
21
|
<img src="docs/architecture.svg" alt="Module architecture overview" width="820" />
|
|
20
22
|
</p>
|
|
21
23
|
|
|
22
|
-
##
|
|
24
|
+
## Shared Harness Ownership
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
This repo owns the generic module-side runtime harnesses used across the stack:
|
|
27
|
+
|
|
28
|
+
- `createModuleHarness(...)` for process and WasmEdge-backed module invocation
|
|
29
|
+
- `resolveModuleHarnessLaunchPlan(...)` for portable launch planning
|
|
30
|
+
- `buildWasmEdgeEmscriptenPthreadRunner(...)` plus the shared native runner
|
|
31
|
+
source and runner-level pthread smoke test
|
|
32
|
+
|
|
33
|
+
Flow-specific standalone harnessing lives in `sdn-flow`, which layers flow
|
|
34
|
+
enqueue/drain behavior on top of its compiled standalone runtime host. Package-
|
|
35
|
+
specific validation suites, including conjunction replay/V&V, are expected to
|
|
36
|
+
sit on top of these shared harnesses instead of defining their own runtime
|
|
37
|
+
process model.
|
|
38
|
+
|
|
39
|
+
## Module Artifact Model
|
|
40
|
+
|
|
41
|
+
A compliant module built with this SDK is always a valid `.wasm` artifact with:
|
|
25
42
|
|
|
26
43
|
- an embedded `PluginManifest.fbs`
|
|
27
44
|
- exported manifest accessors:
|
|
@@ -37,10 +54,15 @@ A module built with this SDK is a `.wasm` artifact with:
|
|
|
37
54
|
- resolved deployment plans and input bindings
|
|
38
55
|
- deployment authorization
|
|
39
56
|
- auxiliary FlatBuffer or raw payloads
|
|
40
|
-
- optional appended SDS
|
|
57
|
+
- optional appended SDS FlatBuffer publication records in a trailing `REC`
|
|
58
|
+
container carrying:
|
|
41
59
|
- `PNM` digital-signature/publication metadata
|
|
42
60
|
- `ENC` encrypted-delivery metadata
|
|
43
|
-
|
|
61
|
+
|
|
62
|
+
Single-file bundling does not append arbitrary bytes to the end of the wasm
|
|
63
|
+
module. The one-file module container is `sds.bundle`, stored as a standard
|
|
64
|
+
wasm custom section. Appended bytes are reserved for SDS publication records
|
|
65
|
+
such as `PNM` and `ENC`, wrapped in a trailing `REC` FlatBuffer collection.
|
|
44
66
|
|
|
45
67
|
The module contract stays the same whether the artifact is loaded directly,
|
|
46
68
|
wrapped in a deployment envelope, or shipped as one bundled `.wasm` file.
|
|
@@ -65,7 +87,7 @@ same request bytes from guest memory and returns response bytes in guest
|
|
|
65
87
|
memory.
|
|
66
88
|
|
|
67
89
|
For simple single-input / single-output methods, command mode also supports a
|
|
68
|
-
|
|
90
|
+
raw shortcut:
|
|
69
91
|
|
|
70
92
|
```bash
|
|
71
93
|
wasmedge module.wasm --method echo < input.fb > output.fb
|
|
@@ -101,8 +123,8 @@ anywhere it can:
|
|
|
101
123
|
- read FlatBuffers
|
|
102
124
|
- honor the module capability and host ABI contract
|
|
103
125
|
|
|
104
|
-
That
|
|
105
|
-
|
|
126
|
+
That architecture is intended to stay portable across the common WebAssembly
|
|
127
|
+
and FlatBuffer host environments:
|
|
106
128
|
|
|
107
129
|
- browser
|
|
108
130
|
- Node.js
|
|
@@ -126,26 +148,11 @@ This repo currently includes:
|
|
|
126
148
|
- a reference Node host and sync `sdn_host` bridge for the first hostcall
|
|
127
149
|
surface
|
|
128
150
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
This repo now exposes a manifest-driven harness generator from
|
|
132
|
-
`space-data-module-sdk/testing` and two complementary integration suites:
|
|
133
|
-
|
|
134
|
-
- `npm run test:runtime-matrix`
|
|
135
|
-
- cross-language runtime smoke across the same WASM in Node.js, Go, Python,
|
|
136
|
-
Rust, Java, C#, and Swift
|
|
137
|
-
- covers method calling, aligned-binary envelope metadata preservation,
|
|
138
|
-
stdin/stdout/stderr, args, env, preopened filesystem access, and basic WASI
|
|
139
|
-
clock/time smoke
|
|
140
|
-
- `npm run test:host-surfaces`
|
|
141
|
-
- authoritative Node-host coverage for HTTP, TCP, UDP, TLS, WebSocket, MQTT,
|
|
142
|
-
process execution, timers, filesystem, and the sync `sdn_host` ABI
|
|
151
|
+
## Runtime Targets
|
|
143
152
|
|
|
144
|
-
|
|
145
|
-
documented in
|
|
146
|
-
[`docs/testing-harness.md`](./docs/testing-harness.md).
|
|
153
|
+
Manifests can declare coarse runtime targets in `manifest.runtimeTargets`.
|
|
147
154
|
|
|
148
|
-
If a manifest declares `runtimeTargets: ["wasi"]`, this SDK
|
|
155
|
+
If a manifest declares `runtimeTargets: ["wasi"]`, this SDK treats that as
|
|
149
156
|
"standalone WASI, no host wrapper required." In practice that currently means:
|
|
150
157
|
|
|
151
158
|
- the artifact must declare the `command` invoke surface
|
|
@@ -153,15 +160,14 @@ If a manifest declares `runtimeTargets: ["wasi"]`, this SDK now treats that as
|
|
|
153
160
|
`logging`, `clock`, `random`, `filesystem`, `pipe`
|
|
154
161
|
- hosted protocols may only use `wasi-pipe` transport
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
[`docs/node-red-default-node-parity.md`](./docs/node-red-default-node-parity.md).
|
|
163
|
+
If a manifest declares `runtimeTargets: ["wasmedge"]`, this SDK treats that as
|
|
164
|
+
the preferred server-side target when the guest needs network-oriented runtime
|
|
165
|
+
features such as sockets or TLS. Plain `wasi` remains the strict portability
|
|
166
|
+
baseline; `wasmedge` is the practical higher-capability target.
|
|
161
167
|
|
|
162
168
|
## WasmEdge Pthreads
|
|
163
169
|
|
|
164
|
-
`space-data-module-sdk` is the source of truth for module thread-model
|
|
170
|
+
`space-data-module-sdk` is also the source of truth for module thread-model
|
|
165
171
|
selection.
|
|
166
172
|
|
|
167
173
|
- `compileModuleFromSource({ threadModel })` accepts an explicit thread model.
|
|
@@ -174,10 +180,39 @@ toolchain. They require a real system Emscripten installation on `PATH`, and
|
|
|
174
180
|
the compiler result plus guest-link bundle metadata preserve the selected
|
|
175
181
|
`threadModel`.
|
|
176
182
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
As emitted by current Emscripten, these pthread artifacts still import
|
|
184
|
+
Emscripten `env.*` host functions plus imported shared memory. That means a
|
|
185
|
+
bare `wasmedge` CLI invocation is not yet the direct execution path for them;
|
|
186
|
+
they currently require a WasmEdge-side host shim that satisfies the Emscripten
|
|
187
|
+
pthread contract.
|
|
188
|
+
|
|
189
|
+
If a runtime cannot interoperate with the guest pthread contract directly,
|
|
190
|
+
document that as a wrapper requirement instead of changing the guest artifact
|
|
191
|
+
semantics.
|
|
192
|
+
|
|
193
|
+
## Testing
|
|
194
|
+
|
|
195
|
+
This repo now exposes a manifest-driven harness generator from
|
|
196
|
+
`space-data-module-sdk/testing` and two complementary integration suites:
|
|
197
|
+
|
|
198
|
+
- shared process-level helpers for command-surface runtimes:
|
|
199
|
+
- `createPluginInvokeProcessClient(...)`
|
|
200
|
+
- `resolveWasmEdgePluginLaunchPlan(...)`
|
|
201
|
+
- `buildWasmEdgeEmscriptenPthreadRunner(...)`
|
|
202
|
+
|
|
203
|
+
- `npm run test:runtime-matrix`
|
|
204
|
+
- cross-language runtime smoke across the same WASM in Node.js, Go, Python,
|
|
205
|
+
Rust, Java, C#, and Swift
|
|
206
|
+
- covers method calling, aligned-binary envelope metadata preservation,
|
|
207
|
+
stdin/stdout/stderr, args, env, preopened filesystem access, and basic WASI
|
|
208
|
+
clock/time smoke
|
|
209
|
+
- `npm run test:host-surfaces`
|
|
210
|
+
- authoritative Node-host coverage for HTTP, TCP, UDP, TLS, WebSocket, MQTT,
|
|
211
|
+
process execution, timers, filesystem, and the sync `sdn_host` ABI
|
|
212
|
+
|
|
213
|
+
The detailed edge cases and the current WASI-vs-host portability boundary are
|
|
214
|
+
documented in
|
|
215
|
+
[`docs/testing-harness.md`](./docs/testing-harness.md).
|
|
181
216
|
|
|
182
217
|
## Install
|
|
183
218
|
|
|
@@ -196,6 +231,8 @@ import {
|
|
|
196
231
|
validateManifestWithStandards,
|
|
197
232
|
} from "space-data-module-sdk";
|
|
198
233
|
|
|
234
|
+
manifest.runtimeTargets = ["wasmedge"];
|
|
235
|
+
|
|
199
236
|
const manifestBytes = encodePluginManifest(manifest);
|
|
200
237
|
const validation = await validateManifestWithStandards(manifest);
|
|
201
238
|
if (!validation.ok) {
|
|
@@ -265,10 +302,11 @@ The full contract split is documented in
|
|
|
265
302
|
|
|
266
303
|
`sds.bundle` keeps module delivery to one file without changing WebAssembly
|
|
267
304
|
loadability for the runtime payload itself. The SDK writes the bundle as a
|
|
268
|
-
standard custom section inside the wasm module
|
|
269
|
-
|
|
270
|
-
wasm bytes
|
|
271
|
-
|
|
305
|
+
standard custom section inside the wasm module. When the artifact is signed or
|
|
306
|
+
encrypted for publication, SDS appends FlatBuffer publication records after the
|
|
307
|
+
wasm bytes in a trailing `REC` container. Loaders must scan from the end,
|
|
308
|
+
resolve `PNM` / `ENC`, strip or decrypt as needed, and only then hand the
|
|
309
|
+
remaining raw wasm bytes to a runtime such as WasmEdge.
|
|
272
310
|
|
|
273
311
|
The reference path lives in
|
|
274
312
|
[`examples/single-file-bundle`](./examples/single-file-bundle):
|
|
@@ -285,7 +323,7 @@ for resolved protocol installations and producer input bindings.
|
|
|
285
323
|
|
|
286
324
|
## Module Publication
|
|
287
325
|
|
|
288
|
-
Packages that publish
|
|
326
|
+
Packages that publish Space Data modules use the canonical `sdn-module`
|
|
289
327
|
publication descriptor. That descriptor covers:
|
|
290
328
|
|
|
291
329
|
- standalone module packages
|
|
@@ -345,7 +383,7 @@ includes:
|
|
|
345
383
|
|
|
346
384
|
Manifests can also declare coarse runtime targets for planning and compliance:
|
|
347
385
|
|
|
348
|
-
`node` `browser` `wasi` `server` `desktop` `edge`
|
|
386
|
+
`node` `browser` `wasi` `wasmedge` `server` `desktop` `edge`
|
|
349
387
|
|
|
350
388
|
## Environment Notes
|
|
351
389
|
|
|
@@ -401,7 +439,8 @@ npm run check:compliance
|
|
|
401
439
|
```
|
|
402
440
|
|
|
403
441
|
Node.js `>=20` is required. The compiler uses `sdn-emception` and `flatc-wasm`
|
|
404
|
-
by default.
|
|
442
|
+
by default for the embedded toolchain path. WasmEdge pthread builds require a
|
|
443
|
+
system Emscripten toolchain on `PATH`.
|
|
405
444
|
|
|
406
445
|
If another repo needs the same compiler runtime, the package also exposes a
|
|
407
446
|
shared emception session at `space-data-module-sdk/compiler/emception` with
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -662,12 +662,25 @@ export type {
|
|
|
662
662
|
HarnessInvokeScenario,
|
|
663
663
|
HarnessRawScenario,
|
|
664
664
|
ManifestHarnessPlan,
|
|
665
|
+
ModuleHarness,
|
|
666
|
+
ModuleHarnessRuntimeDescriptor,
|
|
667
|
+
PluginInvokeProcessClient,
|
|
668
|
+
PluginInvokeProcessLaunchPlan,
|
|
669
|
+
WasmEdgeRunnerBuildPlan,
|
|
665
670
|
} from "./testing/index.js";
|
|
666
671
|
|
|
667
672
|
export {
|
|
673
|
+
buildWasmEdgeEmscriptenPthreadRunner,
|
|
674
|
+
buildWasmEdgeSpawnEnv,
|
|
675
|
+
createModuleHarness,
|
|
676
|
+
createPluginInvokeProcessClient,
|
|
668
677
|
describeCapabilityRuntimeSurface,
|
|
669
678
|
generateManifestHarnessPlan,
|
|
670
679
|
materializeHarnessScenario,
|
|
680
|
+
resolveModuleHarnessLaunchPlan,
|
|
681
|
+
resolveWasmEdgeRunnerBuildPlan,
|
|
682
|
+
resolveWasmEdgeRunnerSourcePath,
|
|
683
|
+
resolveWasmEdgePluginLaunchPlan,
|
|
671
684
|
serializeHarnessPlan,
|
|
672
685
|
} from "./testing/index.js";
|
|
673
686
|
|
package/src/invoke/codec.js
CHANGED
|
@@ -12,7 +12,7 @@ import { InvokeSurface } from "../generated/orbpro/manifest/invoke-surface.js";
|
|
|
12
12
|
import { BufferMutability } from "../generated/orbpro/stream/buffer-mutability.js";
|
|
13
13
|
import { BufferOwnership } from "../generated/orbpro/stream/buffer-ownership.js";
|
|
14
14
|
import { FlatBufferTypeRefT } from "../generated/orbpro/stream/flat-buffer-type-ref.js";
|
|
15
|
-
import {
|
|
15
|
+
import { TypedArenaBufferT } from "../generated/orbpro/stream/typed-arena-buffer.js";
|
|
16
16
|
import { toUint8Array } from "../runtime/bufferLike.js";
|
|
17
17
|
|
|
18
18
|
function toByteBuffer(data) {
|
|
@@ -149,20 +149,22 @@ function normalizeArenaFrame(frame = {}, offset) {
|
|
|
149
149
|
|
|
150
150
|
function packArenaFrames(frames = []) {
|
|
151
151
|
const packedFrames = [];
|
|
152
|
-
const
|
|
152
|
+
const normalizedFrames = [];
|
|
153
153
|
let offset = 0;
|
|
154
154
|
for (const frame of frames) {
|
|
155
155
|
const normalized = normalizeArenaFrame(frame, offset);
|
|
156
|
-
for (let index = 0; index < normalized.padding; index += 1) {
|
|
157
|
-
arena.push(0);
|
|
158
|
-
}
|
|
159
|
-
arena.push(...normalized.payload);
|
|
160
156
|
offset = normalized.buffer.offset + normalized.buffer.size;
|
|
161
157
|
packedFrames.push(normalized.buffer);
|
|
158
|
+
normalizedFrames.push(normalized);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const arena = new Uint8Array(offset);
|
|
162
|
+
for (const normalized of normalizedFrames) {
|
|
163
|
+
arena.set(normalized.payload, normalized.buffer.offset);
|
|
162
164
|
}
|
|
163
165
|
return {
|
|
164
166
|
frames: packedFrames,
|
|
165
|
-
arena
|
|
167
|
+
arena,
|
|
166
168
|
};
|
|
167
169
|
}
|
|
168
170
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { accessSync, existsSync } from "node:fs";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
function normalizeResolvedPath(value) {
|
|
13
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return path.resolve(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ensureReadableSync(targetPath, label) {
|
|
20
|
+
try {
|
|
21
|
+
accessSync(targetPath);
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error(`${label} not found: ${targetPath}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveWasmEdgeSharedLibraryFilename() {
|
|
28
|
+
if (process.platform === "darwin") {
|
|
29
|
+
return "libwasmedge.0.dylib";
|
|
30
|
+
}
|
|
31
|
+
if (process.platform === "win32") {
|
|
32
|
+
return "wasmedge.dll";
|
|
33
|
+
}
|
|
34
|
+
return "libwasmedge.so.0";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveGeneratedIncludeDir(requestedIncludeDir) {
|
|
38
|
+
const directHeaderPath = path.join(
|
|
39
|
+
requestedIncludeDir,
|
|
40
|
+
"wasmedge",
|
|
41
|
+
"enum_configure.h",
|
|
42
|
+
);
|
|
43
|
+
if (existsSync(directHeaderPath)) {
|
|
44
|
+
return requestedIncludeDir;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const repoRoot = path.resolve(requestedIncludeDir, "..", "..");
|
|
48
|
+
const generatedDir = path.join(repoRoot, "build", "include", "api");
|
|
49
|
+
if (existsSync(path.join(generatedDir, "wasmedge", "enum_configure.h"))) {
|
|
50
|
+
return generatedDir;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error(
|
|
54
|
+
`WasmEdge include directory does not contain wasmedge/enum_configure.h: ${requestedIncludeDir}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function resolveWasmEdgeRunnerSourcePath() {
|
|
59
|
+
return path.resolve(
|
|
60
|
+
__dirname,
|
|
61
|
+
"native",
|
|
62
|
+
"wasmedge_emscripten_pthread_runner.c",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolveWasmEdgeRunnerBuildPlan(options = {}) {
|
|
67
|
+
const requestedIncludeDir = normalizeResolvedPath(
|
|
68
|
+
options.wasmedgeIncludeDir ?? process.env.WASMEDGE_INCLUDE_DIR,
|
|
69
|
+
);
|
|
70
|
+
const wasmedgeLibDir = normalizeResolvedPath(
|
|
71
|
+
options.wasmedgeLibDir ?? process.env.WASMEDGE_LIB_DIR,
|
|
72
|
+
);
|
|
73
|
+
const outputPath = normalizeResolvedPath(
|
|
74
|
+
options.outputPath ?? options.output,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!requestedIncludeDir) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
"Missing WasmEdge include directory. Set wasmedgeIncludeDir, --wasmedge-include-dir, or WASMEDGE_INCLUDE_DIR.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (!wasmedgeLibDir) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"Missing WasmEdge library directory. Set wasmedgeLibDir, --wasmedge-lib-dir, or WASMEDGE_LIB_DIR.",
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (!outputPath) {
|
|
88
|
+
throw new Error("Missing runner outputPath.");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const runnerSourcePath = resolveWasmEdgeRunnerSourcePath();
|
|
92
|
+
const wasmedgeSharedLibraryPath = path.join(
|
|
93
|
+
wasmedgeLibDir,
|
|
94
|
+
resolveWasmEdgeSharedLibraryFilename(),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
runnerSourcePath,
|
|
99
|
+
requestedIncludeDir,
|
|
100
|
+
wasmedgeIncludeDir: requestedIncludeDir,
|
|
101
|
+
wasmedgeLibDir,
|
|
102
|
+
wasmedgeSharedLibraryPath,
|
|
103
|
+
outputPath,
|
|
104
|
+
compilerCommand:
|
|
105
|
+
process.platform === "darwin" ? "xcrun" : process.env.CC ?? "cc",
|
|
106
|
+
compilerArgs:
|
|
107
|
+
process.platform === "darwin"
|
|
108
|
+
? [
|
|
109
|
+
"clang",
|
|
110
|
+
runnerSourcePath,
|
|
111
|
+
"-std=c11",
|
|
112
|
+
"-O2",
|
|
113
|
+
"-pthread",
|
|
114
|
+
"-Wall",
|
|
115
|
+
"-Wextra",
|
|
116
|
+
"-Werror",
|
|
117
|
+
"-Wno-unused-parameter",
|
|
118
|
+
"-Wno-error=visibility",
|
|
119
|
+
`-I${requestedIncludeDir}`,
|
|
120
|
+
`-L${wasmedgeLibDir}`,
|
|
121
|
+
"-lwasmedge",
|
|
122
|
+
`-Wl,-rpath,${wasmedgeLibDir}`,
|
|
123
|
+
"-o",
|
|
124
|
+
outputPath,
|
|
125
|
+
]
|
|
126
|
+
: [
|
|
127
|
+
runnerSourcePath,
|
|
128
|
+
"-std=c11",
|
|
129
|
+
"-O2",
|
|
130
|
+
"-pthread",
|
|
131
|
+
"-Wall",
|
|
132
|
+
"-Wextra",
|
|
133
|
+
"-Werror",
|
|
134
|
+
"-Wno-unused-parameter",
|
|
135
|
+
"-Wno-error=visibility",
|
|
136
|
+
`-I${requestedIncludeDir}`,
|
|
137
|
+
`-L${wasmedgeLibDir}`,
|
|
138
|
+
"-lwasmedge",
|
|
139
|
+
`-Wl,-rpath,${wasmedgeLibDir}`,
|
|
140
|
+
"-o",
|
|
141
|
+
outputPath,
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function buildWasmEdgeEmscriptenPthreadRunner(options = {}) {
|
|
147
|
+
const basePlan = resolveWasmEdgeRunnerBuildPlan(options);
|
|
148
|
+
const wasmedgeIncludeDir = resolveGeneratedIncludeDir(
|
|
149
|
+
basePlan.requestedIncludeDir,
|
|
150
|
+
);
|
|
151
|
+
const compilerArgs =
|
|
152
|
+
process.platform === "darwin"
|
|
153
|
+
? [
|
|
154
|
+
"clang",
|
|
155
|
+
basePlan.runnerSourcePath,
|
|
156
|
+
"-std=c11",
|
|
157
|
+
"-O2",
|
|
158
|
+
"-pthread",
|
|
159
|
+
"-Wall",
|
|
160
|
+
"-Wextra",
|
|
161
|
+
"-Werror",
|
|
162
|
+
"-Wno-unused-parameter",
|
|
163
|
+
"-Wno-error=visibility",
|
|
164
|
+
`-I${wasmedgeIncludeDir}`,
|
|
165
|
+
`-L${basePlan.wasmedgeLibDir}`,
|
|
166
|
+
"-lwasmedge",
|
|
167
|
+
`-Wl,-rpath,${basePlan.wasmedgeLibDir}`,
|
|
168
|
+
"-o",
|
|
169
|
+
basePlan.outputPath,
|
|
170
|
+
]
|
|
171
|
+
: [
|
|
172
|
+
basePlan.runnerSourcePath,
|
|
173
|
+
"-std=c11",
|
|
174
|
+
"-O2",
|
|
175
|
+
"-pthread",
|
|
176
|
+
"-Wall",
|
|
177
|
+
"-Wextra",
|
|
178
|
+
"-Werror",
|
|
179
|
+
"-Wno-unused-parameter",
|
|
180
|
+
"-Wno-error=visibility",
|
|
181
|
+
`-I${wasmedgeIncludeDir}`,
|
|
182
|
+
`-L${basePlan.wasmedgeLibDir}`,
|
|
183
|
+
"-lwasmedge",
|
|
184
|
+
`-Wl,-rpath,${basePlan.wasmedgeLibDir}`,
|
|
185
|
+
"-o",
|
|
186
|
+
basePlan.outputPath,
|
|
187
|
+
];
|
|
188
|
+
const plan = {
|
|
189
|
+
...basePlan,
|
|
190
|
+
wasmedgeIncludeDir,
|
|
191
|
+
compilerArgs,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
ensureReadableSync(plan.runnerSourcePath, "Runner source");
|
|
195
|
+
ensureReadableSync(plan.wasmedgeIncludeDir, "WasmEdge include directory");
|
|
196
|
+
ensureReadableSync(plan.wasmedgeLibDir, "WasmEdge library directory");
|
|
197
|
+
ensureReadableSync(
|
|
198
|
+
plan.wasmedgeSharedLibraryPath,
|
|
199
|
+
"WasmEdge shared library",
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await execFileAsync(plan.compilerCommand, plan.compilerArgs, {
|
|
203
|
+
cwd: options.cwd ?? process.cwd(),
|
|
204
|
+
});
|
|
205
|
+
if (process.platform === "darwin") {
|
|
206
|
+
await execFileAsync("install_name_tool", [
|
|
207
|
+
"-change",
|
|
208
|
+
"@rpath/libwasmedge.0.dylib",
|
|
209
|
+
plan.wasmedgeSharedLibraryPath,
|
|
210
|
+
plan.outputPath,
|
|
211
|
+
]);
|
|
212
|
+
}
|
|
213
|
+
return plan.outputPath;
|
|
214
|
+
}
|
package/src/testing/index.d.ts
CHANGED
|
@@ -57,6 +57,69 @@ export interface ManifestHarnessPlan {
|
|
|
57
57
|
scenarios: Array<HarnessInvokeScenario | HarnessRawScenario>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export interface PluginInvokeProcessLaunchPlan {
|
|
61
|
+
command: string;
|
|
62
|
+
args: string[];
|
|
63
|
+
env?: Record<string, string | undefined>;
|
|
64
|
+
cwd?: string;
|
|
65
|
+
wasmPath?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface PluginInvokeProcessClient {
|
|
69
|
+
launchPlan: PluginInvokeProcessLaunchPlan;
|
|
70
|
+
invokeRaw(requestBytes: Uint8Array | ArrayBuffer | ArrayBufferView): Promise<Uint8Array>;
|
|
71
|
+
invoke(request: {
|
|
72
|
+
methodId?: string | null;
|
|
73
|
+
inputs?: HarnessInputFrame[];
|
|
74
|
+
}): Promise<{
|
|
75
|
+
statusCode: number;
|
|
76
|
+
errorCode?: string | null;
|
|
77
|
+
errorMessage?: string | null;
|
|
78
|
+
outputs: HarnessInputFrame[];
|
|
79
|
+
}>;
|
|
80
|
+
destroy(): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ModuleHarnessRuntimeDescriptor {
|
|
84
|
+
kind?: "process" | "wasmedge";
|
|
85
|
+
launchPlan?: PluginInvokeProcessLaunchPlan;
|
|
86
|
+
command?: string;
|
|
87
|
+
args?: string[];
|
|
88
|
+
env?: Record<string, string | undefined>;
|
|
89
|
+
cwd?: string;
|
|
90
|
+
wasmPath?: string;
|
|
91
|
+
wasmEdgeBinary?: string;
|
|
92
|
+
wasmEdgeRunnerBinary?: string;
|
|
93
|
+
enableThreads?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ModuleHarness {
|
|
97
|
+
runtime: ModuleHarnessRuntimeDescriptor & { kind: "process" | "wasmedge" };
|
|
98
|
+
launchPlan: PluginInvokeProcessLaunchPlan;
|
|
99
|
+
invokeRaw(requestBytes: Uint8Array | ArrayBuffer | ArrayBufferView): Promise<Uint8Array>;
|
|
100
|
+
invoke(request: {
|
|
101
|
+
methodId?: string | null;
|
|
102
|
+
inputs?: HarnessInputFrame[];
|
|
103
|
+
}): Promise<{
|
|
104
|
+
statusCode: number;
|
|
105
|
+
errorCode?: string | null;
|
|
106
|
+
errorMessage?: string | null;
|
|
107
|
+
outputs: HarnessInputFrame[];
|
|
108
|
+
}>;
|
|
109
|
+
destroy(): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface WasmEdgeRunnerBuildPlan {
|
|
113
|
+
runnerSourcePath: string;
|
|
114
|
+
requestedIncludeDir: string;
|
|
115
|
+
wasmedgeIncludeDir: string;
|
|
116
|
+
wasmedgeLibDir: string;
|
|
117
|
+
wasmedgeSharedLibraryPath: string;
|
|
118
|
+
outputPath: string;
|
|
119
|
+
compilerCommand: string;
|
|
120
|
+
compilerArgs: string[];
|
|
121
|
+
}
|
|
122
|
+
|
|
60
123
|
export function describeCapabilityRuntimeSurface(
|
|
61
124
|
capability: string,
|
|
62
125
|
): CapabilityRuntimeSurface;
|
|
@@ -84,3 +147,49 @@ export function materializeHarnessScenario(
|
|
|
84
147
|
};
|
|
85
148
|
|
|
86
149
|
export function serializeHarnessPlan(plan: ManifestHarnessPlan): unknown;
|
|
150
|
+
|
|
151
|
+
export function buildWasmEdgeSpawnEnv(
|
|
152
|
+
baseEnv?: Record<string, string | undefined>,
|
|
153
|
+
): Record<string, string | undefined>;
|
|
154
|
+
|
|
155
|
+
export function resolveWasmEdgePluginLaunchPlan(options: {
|
|
156
|
+
wasmPath: string;
|
|
157
|
+
wasmEdgeBinary?: string;
|
|
158
|
+
wasmEdgeRunnerBinary?: string;
|
|
159
|
+
enableThreads?: boolean;
|
|
160
|
+
invokeArgs?: string[];
|
|
161
|
+
env?: Record<string, string | undefined>;
|
|
162
|
+
}): PluginInvokeProcessLaunchPlan;
|
|
163
|
+
|
|
164
|
+
export function createPluginInvokeProcessClient(options: {
|
|
165
|
+
launchPlan?: PluginInvokeProcessLaunchPlan;
|
|
166
|
+
command?: string;
|
|
167
|
+
args?: string[];
|
|
168
|
+
env?: Record<string, string | undefined>;
|
|
169
|
+
cwd?: string;
|
|
170
|
+
}): Promise<PluginInvokeProcessClient>;
|
|
171
|
+
|
|
172
|
+
export function resolveModuleHarnessLaunchPlan(options: {
|
|
173
|
+
runtime?: ModuleHarnessRuntimeDescriptor;
|
|
174
|
+
} | ModuleHarnessRuntimeDescriptor): PluginInvokeProcessLaunchPlan;
|
|
175
|
+
|
|
176
|
+
export function createModuleHarness(options: {
|
|
177
|
+
runtime?: ModuleHarnessRuntimeDescriptor;
|
|
178
|
+
} | ModuleHarnessRuntimeDescriptor): Promise<ModuleHarness>;
|
|
179
|
+
|
|
180
|
+
export function resolveWasmEdgeRunnerSourcePath(): string;
|
|
181
|
+
|
|
182
|
+
export function resolveWasmEdgeRunnerBuildPlan(options: {
|
|
183
|
+
outputPath: string;
|
|
184
|
+
wasmedgeIncludeDir?: string;
|
|
185
|
+
wasmedgeLibDir?: string;
|
|
186
|
+
output?: string;
|
|
187
|
+
}): WasmEdgeRunnerBuildPlan;
|
|
188
|
+
|
|
189
|
+
export function buildWasmEdgeEmscriptenPthreadRunner(options: {
|
|
190
|
+
outputPath: string;
|
|
191
|
+
wasmedgeIncludeDir?: string;
|
|
192
|
+
wasmedgeLibDir?: string;
|
|
193
|
+
output?: string;
|
|
194
|
+
cwd?: string;
|
|
195
|
+
}): Promise<string>;
|
package/src/testing/index.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
|
|
1
3
|
import { encodePluginInvokeRequest } from "../invoke/codec.js";
|
|
2
4
|
import { normalizeInvokeSurfaces } from "../invoke/index.js";
|
|
3
5
|
import { selectPreferredPayloadTypeRef } from "../manifest/typeRefs.js";
|
|
6
|
+
export {
|
|
7
|
+
buildWasmEdgeSpawnEnv,
|
|
8
|
+
createPluginInvokeProcessClient,
|
|
9
|
+
resolveWasmEdgePluginLaunchPlan,
|
|
10
|
+
} from "./processInvoke.js";
|
|
11
|
+
export {
|
|
12
|
+
buildWasmEdgeEmscriptenPthreadRunner,
|
|
13
|
+
resolveWasmEdgeRunnerBuildPlan,
|
|
14
|
+
resolveWasmEdgeRunnerSourcePath,
|
|
15
|
+
} from "./buildWasmEdgeRunner.js";
|
|
16
|
+
export {
|
|
17
|
+
createModuleHarness,
|
|
18
|
+
resolveModuleHarnessLaunchPlan,
|
|
19
|
+
} from "./moduleHarness.js";
|
|
4
20
|
|
|
5
21
|
const CapabilitySurfaceMatrix = Object.freeze({
|
|
6
22
|
logging: Object.freeze({
|