space-data-module-sdk 0.5.15 → 0.5.18

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 (37) hide show
  1. package/README.md +24 -0
  2. package/package.json +8 -4
  3. package/schemas/HostStorageAbi.fbs +53 -0
  4. package/src/bundle/codec.js +1 -1
  5. package/src/compliance/pluginCompliance.js +1 -1
  6. package/src/generated/orbpro/invoke/plugin-invoke-request.js +1 -1
  7. package/src/generated/orbpro/invoke/plugin-invoke-response.js +1 -1
  8. package/src/generated/orbpro/manifest/accepted-type-set.js +1 -1
  9. package/src/generated/orbpro/manifest/build-artifact.js +1 -1
  10. package/src/generated/orbpro/manifest/host-capability.js +1 -1
  11. package/src/generated/orbpro/manifest/method-manifest.js +1 -1
  12. package/src/generated/orbpro/manifest/plugin-manifest.js +1 -1
  13. package/src/generated/orbpro/manifest/port-manifest.js +1 -1
  14. package/src/generated/orbpro/manifest/protocol-spec.js +1 -1
  15. package/src/generated/orbpro/manifest/timer-spec.js +1 -1
  16. package/src/generated/orbpro/module/canonicalization-rule.js +1 -1
  17. package/src/generated/orbpro/module/module-bundle-entry.js +1 -1
  18. package/src/generated/orbpro/module/module-bundle.js +1 -1
  19. package/src/generated/orbpro/stream/flat-buffer-type-ref.js +1 -1
  20. package/src/generated/orbpro/stream/typed-arena-buffer.js +1 -1
  21. package/src/index.d.ts +139 -0
  22. package/src/index.js +1 -0
  23. package/src/invoke/codec.js +1 -1
  24. package/src/manifest/codec.js +1 -1
  25. package/src/manifest/typeRefs.js +49 -12
  26. package/src/runtime-host/flatsqlRuntimeStore.js +217 -0
  27. package/src/runtime-host/index.js +21 -0
  28. package/src/runtime-host/moduleRegistry.js +92 -0
  29. package/src/runtime-host/runtimeRegionStore.js +245 -0
  30. package/src/testing/buildWasmEdgeRunner.js +41 -2
  31. package/src/testing/index.d.ts +123 -2
  32. package/src/testing/index.js +1 -0
  33. package/src/testing/moduleHarness.js +102 -1
  34. package/src/testing/native/wasmedge_emscripten_pthread_runner.c +2319 -209
  35. package/src/testing/processInvoke.js +250 -9
  36. package/src/testing/streamInvokeCodec.js +175 -0
  37. package/src/transport/records.js +19 -6
@@ -0,0 +1,217 @@
1
+ import { DirectAccessor, FlatSQLDatabase } from "flatsql";
2
+
3
+ const RUNTIME_ROW_TABLE = "RuntimeHostRow";
4
+ const RUNTIME_ROW_SCHEMA = `
5
+ table RuntimeHostRow {
6
+ schemaFileId: string;
7
+ rowId: ulong;
8
+ payloadJson: string;
9
+ }
10
+ `;
11
+
12
+ const encoder = new TextEncoder();
13
+ const decoder = new TextDecoder();
14
+
15
+ function clonePayload(payload) {
16
+ if (payload === null || payload === undefined) {
17
+ return payload ?? null;
18
+ }
19
+ if (typeof structuredClone === "function") {
20
+ return structuredClone(payload);
21
+ }
22
+ if (ArrayBuffer.isView(payload)) {
23
+ return payload.slice(0);
24
+ }
25
+ if (payload instanceof ArrayBuffer) {
26
+ return payload.slice(0);
27
+ }
28
+ if (typeof payload === "object") {
29
+ return JSON.parse(JSON.stringify(payload));
30
+ }
31
+ return payload;
32
+ }
33
+
34
+ function normalizeSchemaFileId(value) {
35
+ if (typeof value !== "string" || value.trim().length === 0) {
36
+ throw new TypeError("schemaFileId must be a non-empty string");
37
+ }
38
+ return value.trim();
39
+ }
40
+
41
+ function normalizeRowHandle(handle) {
42
+ if (!handle || typeof handle !== "object") {
43
+ throw new TypeError("row handle is required");
44
+ }
45
+ const schemaFileId = normalizeSchemaFileId(handle.schemaFileId);
46
+ const rowId = Number(handle.rowId);
47
+ if (!Number.isInteger(rowId) || rowId <= 0) {
48
+ throw new TypeError("rowId must be a positive integer");
49
+ }
50
+ return { schemaFileId, rowId };
51
+ }
52
+
53
+ function escapeSqlStringLiteral(value) {
54
+ return String(value).replaceAll("'", "''");
55
+ }
56
+
57
+ function serializePayload(payload) {
58
+ if (payload === null || payload === undefined) {
59
+ return {
60
+ kind: "json",
61
+ value: null,
62
+ };
63
+ }
64
+ if (ArrayBuffer.isView(payload)) {
65
+ return {
66
+ kind: "bytes",
67
+ bytes: Array.from(
68
+ new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength),
69
+ ),
70
+ };
71
+ }
72
+ if (payload instanceof ArrayBuffer) {
73
+ return {
74
+ kind: "bytes",
75
+ bytes: Array.from(new Uint8Array(payload)),
76
+ };
77
+ }
78
+ return {
79
+ kind: "json",
80
+ value: clonePayload(payload),
81
+ };
82
+ }
83
+
84
+ function deserializePayload(payloadJson) {
85
+ const payload = JSON.parse(String(payloadJson ?? "null"));
86
+ if (payload?.kind === "bytes") {
87
+ return Uint8Array.from(payload.bytes ?? []);
88
+ }
89
+ if (payload?.kind === "json") {
90
+ return clonePayload(payload.value);
91
+ }
92
+ return clonePayload(payload);
93
+ }
94
+
95
+ function createRuntimeRowAccessor() {
96
+ const accessor = new DirectAccessor();
97
+ accessor.registerAccessor(RUNTIME_ROW_TABLE, (data, path) => {
98
+ const row = JSON.parse(decoder.decode(data));
99
+ let current = row;
100
+ for (const segment of Array.isArray(path) ? path : []) {
101
+ if (current === null || current === undefined) {
102
+ return null;
103
+ }
104
+ current = current[segment];
105
+ }
106
+ return current ?? null;
107
+ });
108
+ accessor.registerBuilder(RUNTIME_ROW_TABLE, (fields) =>
109
+ encoder.encode(
110
+ JSON.stringify({
111
+ schemaFileId: normalizeSchemaFileId(fields.schemaFileId),
112
+ rowId: Number(fields.rowId),
113
+ payloadJson: String(fields.payloadJson ?? "null"),
114
+ }),
115
+ ),
116
+ );
117
+ return accessor;
118
+ }
119
+
120
+ function cloneQueryResult(result) {
121
+ return {
122
+ columns: Array.from(result?.columns ?? []),
123
+ rows: (result?.rows ?? []).map((row) =>
124
+ Array.isArray(row) ? row.map((value) => clonePayload(value)) : row,
125
+ ),
126
+ rowCount: Number(result?.rowCount ?? 0),
127
+ };
128
+ }
129
+
130
+ function rowViewFromQueryRow(row) {
131
+ return {
132
+ handle: {
133
+ schemaFileId: String(row[0]),
134
+ rowId: Number(row[1]),
135
+ },
136
+ payload: deserializePayload(row[2]),
137
+ };
138
+ }
139
+
140
+ export function createFlatSqlRuntimeStore(options = {}) {
141
+ const accessor = options.accessor ?? createRuntimeRowAccessor();
142
+ const database =
143
+ options.database ??
144
+ FlatSQLDatabase.fromSchema(
145
+ RUNTIME_ROW_SCHEMA,
146
+ accessor,
147
+ options.databaseName ?? "runtime-host",
148
+ );
149
+ const nextRowIdBySchema = new Map();
150
+ const existingRows = database.query(
151
+ `SELECT schemaFileId, rowId FROM ${RUNTIME_ROW_TABLE} ORDER BY schemaFileId, rowId`,
152
+ );
153
+ for (const row of existingRows.rows ?? []) {
154
+ const schemaFileId = normalizeSchemaFileId(row[0]);
155
+ const rowId = Number(row[1]);
156
+ if (Number.isInteger(rowId) && rowId > 0) {
157
+ nextRowIdBySchema.set(
158
+ schemaFileId,
159
+ Math.max(nextRowIdBySchema.get(schemaFileId) ?? 0, rowId),
160
+ );
161
+ }
162
+ }
163
+
164
+ function appendRow({ schemaFileId, payload = null }) {
165
+ const normalizedSchemaFileId = normalizeSchemaFileId(schemaFileId);
166
+ const nextRowId = (nextRowIdBySchema.get(normalizedSchemaFileId) ?? 0) + 1;
167
+ nextRowIdBySchema.set(normalizedSchemaFileId, nextRowId);
168
+ database.insert(RUNTIME_ROW_TABLE, {
169
+ schemaFileId: normalizedSchemaFileId,
170
+ rowId: nextRowId,
171
+ payloadJson: JSON.stringify(serializePayload(payload)),
172
+ });
173
+ return {
174
+ schemaFileId: normalizedSchemaFileId,
175
+ rowId: nextRowId,
176
+ };
177
+ }
178
+
179
+ function resolveRow(handle) {
180
+ const normalizedHandle = normalizeRowHandle(handle);
181
+ return (
182
+ listRows(normalizedHandle.schemaFileId).find(
183
+ (row) => row.handle.rowId === normalizedHandle.rowId,
184
+ ) ?? null
185
+ );
186
+ }
187
+
188
+ function listRows(schemaFileId = null) {
189
+ const normalizedSchemaFileId =
190
+ schemaFileId === null || schemaFileId === undefined
191
+ ? null
192
+ : normalizeSchemaFileId(schemaFileId);
193
+ const result =
194
+ normalizedSchemaFileId === null
195
+ ? database.query(
196
+ `SELECT schemaFileId, rowId, payloadJson FROM ${RUNTIME_ROW_TABLE} ORDER BY schemaFileId, rowId`,
197
+ )
198
+ : database.query(
199
+ `SELECT schemaFileId, rowId, payloadJson FROM ${RUNTIME_ROW_TABLE} WHERE schemaFileId = '${escapeSqlStringLiteral(normalizedSchemaFileId)}' ORDER BY rowId`,
200
+ );
201
+ return (result.rows ?? []).map(rowViewFromQueryRow);
202
+ }
203
+
204
+ function query(sql) {
205
+ if (typeof sql !== "string" || sql.trim().length === 0) {
206
+ throw new TypeError("sql must be a non-empty string");
207
+ }
208
+ return cloneQueryResult(database.query(sql));
209
+ }
210
+
211
+ return {
212
+ appendRow,
213
+ listRows,
214
+ query,
215
+ resolveRow,
216
+ };
217
+ }
@@ -0,0 +1,21 @@
1
+ import { createFlatSqlRuntimeStore } from "./flatsqlRuntimeStore.js";
2
+ import { createRuntimeRegionStore } from "./runtimeRegionStore.js";
3
+ import { createModuleRegistry } from "./moduleRegistry.js";
4
+
5
+ export function createRuntimeHost(options = {}) {
6
+ const rows = options.rows ?? createFlatSqlRuntimeStore();
7
+ const regions = options.regions ?? createRuntimeRegionStore();
8
+ const moduleRegistry = options.moduleRegistry ?? createModuleRegistry();
9
+
10
+ return {
11
+ rows,
12
+ regions,
13
+ moduleRegistry,
14
+ };
15
+ }
16
+
17
+ export {
18
+ createFlatSqlRuntimeStore,
19
+ createModuleRegistry,
20
+ createRuntimeRegionStore,
21
+ };
@@ -0,0 +1,92 @@
1
+ function normalizeModuleId(value) {
2
+ if (typeof value !== "string" || value.trim().length === 0) {
3
+ throw new TypeError("moduleId must be a non-empty string");
4
+ }
5
+ return value.trim();
6
+ }
7
+
8
+ function cloneMetadata(value) {
9
+ if (value === null || value === undefined) {
10
+ return value ?? null;
11
+ }
12
+ if (typeof structuredClone === "function") {
13
+ return structuredClone(value);
14
+ }
15
+ return JSON.parse(JSON.stringify(value));
16
+ }
17
+
18
+ function toPublicModuleRecord(moduleRecord) {
19
+ return {
20
+ moduleId: moduleRecord.moduleId,
21
+ metadata: cloneMetadata(moduleRecord.metadata),
22
+ methodIds: Object.keys(moduleRecord.methods),
23
+ };
24
+ }
25
+
26
+ export function createModuleRegistry() {
27
+ const modules = new Map();
28
+
29
+ function installModule(definition) {
30
+ if (!definition || typeof definition !== "object") {
31
+ throw new TypeError("module definition is required");
32
+ }
33
+ const moduleId = normalizeModuleId(definition.moduleId);
34
+ const moduleRecord = {
35
+ moduleId,
36
+ methods: {
37
+ ...(definition.methods ?? {}),
38
+ },
39
+ metadata: cloneMetadata(definition.metadata),
40
+ };
41
+ modules.set(moduleId, moduleRecord);
42
+ return toPublicModuleRecord(moduleRecord);
43
+ }
44
+
45
+ function loadModule(moduleId) {
46
+ const moduleRecord = modules.get(normalizeModuleId(moduleId));
47
+ if (!moduleRecord) {
48
+ return null;
49
+ }
50
+ return {
51
+ moduleId: moduleRecord.moduleId,
52
+ methods: {
53
+ ...moduleRecord.methods,
54
+ },
55
+ metadata: cloneMetadata(moduleRecord.metadata),
56
+ };
57
+ }
58
+
59
+ function unloadModule(moduleId) {
60
+ return modules.delete(normalizeModuleId(moduleId));
61
+ }
62
+
63
+ function listModules() {
64
+ return Array.from(modules.values(), toPublicModuleRecord);
65
+ }
66
+
67
+ async function invokeModule(moduleId, methodId, ...args) {
68
+ const moduleRecord = modules.get(normalizeModuleId(moduleId));
69
+ if (!moduleRecord) {
70
+ throw new Error(`Unknown module: ${moduleId}`);
71
+ }
72
+ const method = moduleRecord.methods?.[methodId];
73
+ if (typeof method !== "function") {
74
+ throw new Error(`Unknown module method: ${moduleId}.${methodId}`);
75
+ }
76
+ return method.call(
77
+ {
78
+ moduleId: moduleRecord.moduleId,
79
+ metadata: cloneMetadata(moduleRecord.metadata),
80
+ },
81
+ ...args,
82
+ );
83
+ }
84
+
85
+ return {
86
+ installModule,
87
+ invokeModule,
88
+ listModules,
89
+ loadModule,
90
+ unloadModule,
91
+ };
92
+ }
@@ -0,0 +1,245 @@
1
+ function cloneBytes(bytes) {
2
+ return new Uint8Array(bytes);
3
+ }
4
+
5
+ function normalizePositiveInteger(value, label) {
6
+ const normalized = Number(value);
7
+ if (!Number.isInteger(normalized) || normalized < 0) {
8
+ throw new TypeError(`${label} must be a non-negative integer`);
9
+ }
10
+ return normalized;
11
+ }
12
+
13
+ function normalizeStrictlyPositiveInteger(value, label) {
14
+ const normalized = Number(value);
15
+ if (!Number.isInteger(normalized) || normalized <= 0) {
16
+ throw new TypeError(`${label} must be a positive integer`);
17
+ }
18
+ return normalized;
19
+ }
20
+
21
+ function normalizeRequiredString(value, label) {
22
+ if (typeof value !== "string" || value.trim().length === 0) {
23
+ throw new TypeError(`${label} must be a non-empty string`);
24
+ }
25
+ return value.trim();
26
+ }
27
+
28
+ function normalizeOptionalFunction(value, label) {
29
+ if (value === undefined) {
30
+ return undefined;
31
+ }
32
+ if (typeof value !== "function") {
33
+ throw new TypeError(`${label} must be a function`);
34
+ }
35
+ return value;
36
+ }
37
+
38
+ function toRecordBytes(value, recordByteLength) {
39
+ const bytes = new Uint8Array(recordByteLength);
40
+ if (value === null || value === undefined) {
41
+ return bytes;
42
+ }
43
+ if (ArrayBuffer.isView(value)) {
44
+ const view = value;
45
+ if (view.byteLength > recordByteLength) {
46
+ throw new RangeError("record bytes exceed recordByteLength");
47
+ }
48
+ bytes.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
49
+ return bytes;
50
+ }
51
+ if (value instanceof ArrayBuffer) {
52
+ if (value.byteLength > recordByteLength) {
53
+ throw new RangeError("record bytes exceed recordByteLength");
54
+ }
55
+ bytes.set(new Uint8Array(value));
56
+ return bytes;
57
+ }
58
+ throw new TypeError("runtime region records must be byte-oriented");
59
+ }
60
+
61
+ export function createRuntimeRegionStore() {
62
+ let nextRegionId = 1;
63
+ const regions = new Map();
64
+
65
+ function getRegionRecordCount(region) {
66
+ if (typeof region.getRecordCount === "function") {
67
+ return normalizePositiveInteger(
68
+ region.getRecordCount(region.regionId),
69
+ "recordCount",
70
+ );
71
+ }
72
+ return region.records.length;
73
+ }
74
+
75
+ function describeRegion(regionId) {
76
+ const normalizedRegionId = normalizePositiveInteger(regionId, "regionId");
77
+ const region = regions.get(normalizedRegionId);
78
+ if (!region) {
79
+ return null;
80
+ }
81
+ return {
82
+ regionId: region.regionId,
83
+ layoutId: region.layoutId,
84
+ recordByteLength: region.recordByteLength,
85
+ alignment: region.alignment,
86
+ recordCount: getRegionRecordCount(region),
87
+ };
88
+ }
89
+
90
+ function allocateRegion({
91
+ layoutId,
92
+ recordByteLength,
93
+ alignment = 1,
94
+ initialRecords = [],
95
+ }) {
96
+ const region = {
97
+ regionId: nextRegionId++,
98
+ kind: "owned",
99
+ layoutId: normalizeRequiredString(layoutId, "layoutId"),
100
+ recordByteLength: normalizeStrictlyPositiveInteger(
101
+ recordByteLength,
102
+ "recordByteLength",
103
+ ),
104
+ alignment: normalizeStrictlyPositiveInteger(alignment, "alignment"),
105
+ records: [],
106
+ };
107
+ region.records = Array.from(initialRecords, (record) =>
108
+ toRecordBytes(record, region.recordByteLength),
109
+ );
110
+ regions.set(region.regionId, region);
111
+ return describeRegion(region.regionId);
112
+ }
113
+
114
+ function registerExternalRegion({
115
+ layoutId,
116
+ recordByteLength,
117
+ alignment = 1,
118
+ recordCount = 0,
119
+ getRecordCount,
120
+ resolveRecordView,
121
+ }) {
122
+ const region = {
123
+ regionId: nextRegionId++,
124
+ kind: "external",
125
+ layoutId: normalizeRequiredString(layoutId, "layoutId"),
126
+ recordByteLength: normalizeStrictlyPositiveInteger(
127
+ recordByteLength,
128
+ "recordByteLength",
129
+ ),
130
+ alignment: normalizeStrictlyPositiveInteger(alignment, "alignment"),
131
+ records: [],
132
+ getRecordCount: normalizeOptionalFunction(getRecordCount, "getRecordCount"),
133
+ resolveRecordView: normalizeOptionalFunction(
134
+ resolveRecordView,
135
+ "resolveRecordView",
136
+ ),
137
+ };
138
+ if (region.getRecordCount === undefined) {
139
+ region.records.length = normalizePositiveInteger(recordCount, "recordCount");
140
+ }
141
+ regions.set(region.regionId, region);
142
+ return describeRegion(region.regionId);
143
+ }
144
+
145
+ function setRegionRecordCount(regionId, recordCount) {
146
+ const normalizedRegionId = normalizePositiveInteger(regionId, "regionId");
147
+ const region = regions.get(normalizedRegionId);
148
+ if (!region) {
149
+ return null;
150
+ }
151
+ if (typeof region.getRecordCount === "function") {
152
+ throw new Error("Cannot set recordCount for externally counted regions");
153
+ }
154
+ const normalizedRecordCount = normalizePositiveInteger(
155
+ recordCount,
156
+ "recordCount",
157
+ );
158
+ if (normalizedRecordCount < region.records.length) {
159
+ region.records.length = normalizedRecordCount;
160
+ } else {
161
+ while (region.records.length < normalizedRecordCount) {
162
+ region.records.push(new Uint8Array(region.recordByteLength));
163
+ }
164
+ }
165
+ return describeRegion(normalizedRegionId);
166
+ }
167
+
168
+ function resolveRecord({ regionId, recordIndex }) {
169
+ const normalizedRegionId = normalizePositiveInteger(regionId, "regionId");
170
+ const normalizedRecordIndex = normalizePositiveInteger(
171
+ recordIndex,
172
+ "recordIndex",
173
+ );
174
+ const region = regions.get(normalizedRegionId);
175
+ if (!region) {
176
+ return null;
177
+ }
178
+ if (normalizedRecordIndex >= getRegionRecordCount(region)) {
179
+ return null;
180
+ }
181
+ if (region.kind === "external") {
182
+ return null;
183
+ }
184
+ return {
185
+ regionId: region.regionId,
186
+ recordIndex: normalizedRecordIndex,
187
+ layoutId: region.layoutId,
188
+ recordByteLength: region.recordByteLength,
189
+ alignment: region.alignment,
190
+ byteLength: region.recordByteLength,
191
+ bytes: cloneBytes(region.records[normalizedRecordIndex]),
192
+ };
193
+ }
194
+
195
+ function resolveRecordView({ regionId, recordIndex }) {
196
+ const normalizedRegionId = normalizePositiveInteger(regionId, "regionId");
197
+ const normalizedRecordIndex = normalizePositiveInteger(
198
+ recordIndex,
199
+ "recordIndex",
200
+ );
201
+ const region = regions.get(normalizedRegionId);
202
+ if (!region) {
203
+ return null;
204
+ }
205
+ if (normalizedRecordIndex >= getRegionRecordCount(region)) {
206
+ return null;
207
+ }
208
+ if (region.kind !== "external") {
209
+ return resolveRecord({
210
+ regionId: normalizedRegionId,
211
+ recordIndex: normalizedRecordIndex,
212
+ });
213
+ }
214
+ if (typeof region.resolveRecordView !== "function") {
215
+ return null;
216
+ }
217
+ const view = region.resolveRecordView({
218
+ regionId: normalizedRegionId,
219
+ recordIndex: normalizedRecordIndex,
220
+ layoutId: region.layoutId,
221
+ recordByteLength: region.recordByteLength,
222
+ alignment: region.alignment,
223
+ });
224
+ if (view === null || view === undefined) {
225
+ return null;
226
+ }
227
+ return {
228
+ regionId: normalizedRegionId,
229
+ recordIndex: normalizedRecordIndex,
230
+ layoutId: region.layoutId,
231
+ recordByteLength: region.recordByteLength,
232
+ alignment: region.alignment,
233
+ ...view,
234
+ };
235
+ }
236
+
237
+ return {
238
+ allocateRegion,
239
+ describeRegion,
240
+ registerExternalRegion,
241
+ resolveRecord,
242
+ resolveRecordView,
243
+ setRegionRecordCount,
244
+ };
245
+ }
@@ -1,5 +1,6 @@
1
1
  import { accessSync, existsSync } from "node:fs";
2
2
  import { execFile } from "node:child_process";
3
+ import os from "node:os";
3
4
  import path from "node:path";
4
5
  import process from "node:process";
5
6
  import { promisify } from "node:util";
@@ -63,12 +64,50 @@ export function resolveWasmEdgeRunnerSourcePath() {
63
64
  );
64
65
  }
65
66
 
67
+ function resolveDefaultWasmEdgeInstall() {
68
+ const home = os.homedir();
69
+ const candidates = [
70
+ {
71
+ includeDir: path.join(home, ".wasmedge", "include"),
72
+ libDir: path.join(home, ".wasmedge", "lib"),
73
+ },
74
+ {
75
+ includeDir: "/opt/homebrew/include",
76
+ libDir: "/opt/homebrew/opt/wasmedge/lib",
77
+ },
78
+ {
79
+ includeDir: "/usr/local/include",
80
+ libDir: "/usr/local/lib",
81
+ },
82
+ ];
83
+ for (const candidate of candidates) {
84
+ const header = path.join(
85
+ candidate.includeDir,
86
+ "wasmedge",
87
+ "enum_configure.h",
88
+ );
89
+ const library = path.join(
90
+ candidate.libDir,
91
+ resolveWasmEdgeSharedLibraryFilename(),
92
+ );
93
+ if (existsSync(header) && existsSync(library)) {
94
+ return candidate;
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+
66
100
  export function resolveWasmEdgeRunnerBuildPlan(options = {}) {
101
+ const detectedInstall = resolveDefaultWasmEdgeInstall();
67
102
  const requestedIncludeDir = normalizeResolvedPath(
68
- options.wasmedgeIncludeDir ?? process.env.WASMEDGE_INCLUDE_DIR,
103
+ options.wasmedgeIncludeDir ??
104
+ process.env.WASMEDGE_INCLUDE_DIR ??
105
+ detectedInstall?.includeDir,
69
106
  );
70
107
  const wasmedgeLibDir = normalizeResolvedPath(
71
- options.wasmedgeLibDir ?? process.env.WASMEDGE_LIB_DIR,
108
+ options.wasmedgeLibDir ??
109
+ process.env.WASMEDGE_LIB_DIR ??
110
+ detectedInstall?.libDir,
72
111
  );
73
112
  const outputPath = normalizeResolvedPath(
74
113
  options.outputPath ?? options.output,