space-data-module-sdk 0.5.14 → 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 +91 -1
  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 +0 -130
  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 +157 -1
  22. package/src/index.js +1 -0
  23. package/src/invoke/codec.js +10 -8
  24. package/src/manifest/codec.js +1 -1
  25. package/src/runtime-host/flatsqlRuntimeStore.js +217 -0
  26. package/src/runtime-host/index.js +21 -0
  27. package/src/runtime-host/moduleRegistry.js +92 -0
  28. package/src/runtime-host/runtimeRegionStore.js +245 -0
  29. package/src/testing/buildWasmEdgeRunner.js +253 -0
  30. package/src/testing/index.d.ts +311 -0
  31. package/src/testing/index.js +21 -0
  32. package/src/testing/moduleHarness.js +163 -0
  33. package/src/testing/native/wasmedge_emscripten_pthread_runner.c +3111 -0
  34. package/src/testing/processInvoke.js +467 -0
  35. package/src/testing/publicationProtectionDemo.js +271 -0
  36. package/src/testing/streamInvokeCodec.js +175 -0
  37. package/src/transport/records.js +56 -55
package/src/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export type ProtocolRoleName = "handle" | "dial" | "both";
11
11
 
12
12
  export interface PayloadTypeRef {
13
13
  schemaName?: string;
14
- fileIdentifier?: string | null;
14
+ fileIdentifier?: string;
15
15
  schemaHash?: string | number[] | Uint8Array;
16
16
  acceptsAnyFlatbuffer?: boolean;
17
17
  wireFormat?: PayloadWireFormat;
@@ -258,6 +258,144 @@ export function loadComplianceConfig(
258
258
  rootDirectory: string,
259
259
  ): Promise<{ path: string; config: Record<string, unknown> } | null>;
260
260
  export function getWasmExportNames(wasmBytes: Uint8Array): string[];
261
+
262
+ // --- Runtime Host ---
263
+
264
+ export interface RowHandle {
265
+ schemaFileId: string;
266
+ rowId: number;
267
+ }
268
+
269
+ export interface RuntimeRowView {
270
+ handle: RowHandle;
271
+ payload: unknown;
272
+ }
273
+
274
+ export interface RuntimeRowQueryResult {
275
+ columns: string[];
276
+ rows: unknown[][];
277
+ rowCount: number;
278
+ }
279
+
280
+ export interface FlatSqlRuntimeStore {
281
+ appendRow(options: { schemaFileId: string; payload?: unknown }): RowHandle;
282
+ listRows(schemaFileId?: string | null): RuntimeRowView[];
283
+ query(sql: string): RuntimeRowQueryResult;
284
+ resolveRow(handle: RowHandle): RuntimeRowView | null;
285
+ }
286
+
287
+ export interface RuntimeRegionDescriptor {
288
+ regionId: number;
289
+ layoutId: string;
290
+ recordByteLength: number;
291
+ alignment: number;
292
+ recordCount: number;
293
+ }
294
+
295
+ export interface RuntimeRegionRecord {
296
+ regionId: number;
297
+ recordIndex: number;
298
+ layoutId: string;
299
+ recordByteLength: number;
300
+ alignment: number;
301
+ byteLength: number;
302
+ bytes: Uint8Array;
303
+ }
304
+
305
+ export interface RuntimeRegionExternalRecordView {
306
+ regionId: number;
307
+ recordIndex: number;
308
+ layoutId: string;
309
+ recordByteLength: number;
310
+ alignment: number;
311
+ byteOffset?: number;
312
+ buffer?: ArrayBufferLike;
313
+ elementType?: string;
314
+ elementCount?: number;
315
+ strideElements?: number;
316
+ [key: string]: unknown;
317
+ }
318
+
319
+ export interface RuntimeRegionRecordViewRequest {
320
+ regionId: number;
321
+ recordIndex: number;
322
+ }
323
+
324
+ export interface RuntimeRegionStore {
325
+ allocateRegion(options: {
326
+ layoutId: string;
327
+ recordByteLength: number;
328
+ alignment?: number;
329
+ initialRecords?: Array<Uint8Array | ArrayBuffer | ArrayBufferView | null | undefined>;
330
+ }): RuntimeRegionDescriptor;
331
+ registerExternalRegion(options: {
332
+ layoutId: string;
333
+ recordByteLength: number;
334
+ alignment?: number;
335
+ recordCount?: number;
336
+ getRecordCount?: (regionId: number) => number;
337
+ resolveRecordView?: (query: {
338
+ regionId: number;
339
+ recordIndex: number;
340
+ layoutId: string;
341
+ recordByteLength: number;
342
+ alignment: number;
343
+ }) =>
344
+ | Omit<
345
+ RuntimeRegionExternalRecordView,
346
+ "regionId" | "recordIndex" | "layoutId" | "recordByteLength" | "alignment"
347
+ >
348
+ | null
349
+ | undefined;
350
+ }): RuntimeRegionDescriptor;
351
+ setRegionRecordCount(regionId: number, recordCount: number): RuntimeRegionDescriptor | null;
352
+ describeRegion(regionId: number): RuntimeRegionDescriptor | null;
353
+ resolveRecord(options: RuntimeRegionRecordViewRequest): RuntimeRegionRecord | null;
354
+ resolveRecordView(
355
+ options: RuntimeRegionRecordViewRequest,
356
+ ): RuntimeRegionRecord | RuntimeRegionExternalRecordView | null;
357
+ }
358
+
359
+ export interface InstalledRuntimeModule {
360
+ moduleId: string;
361
+ metadata: unknown;
362
+ methodIds: string[];
363
+ }
364
+
365
+ export interface RuntimeModuleRegistry {
366
+ installModule(definition: {
367
+ moduleId: string;
368
+ methods?: Record<string, (...args: unknown[]) => unknown>;
369
+ metadata?: unknown;
370
+ }): InstalledRuntimeModule;
371
+ invokeModule(
372
+ moduleId: string,
373
+ methodId: string,
374
+ ...args: unknown[]
375
+ ): Promise<unknown>;
376
+ listModules(): InstalledRuntimeModule[];
377
+ loadModule(moduleId: string): {
378
+ moduleId: string;
379
+ methods: Record<string, (...args: unknown[]) => unknown>;
380
+ metadata: unknown;
381
+ } | null;
382
+ unloadModule(moduleId: string): boolean;
383
+ }
384
+
385
+ export interface RuntimeHost {
386
+ rows: FlatSqlRuntimeStore;
387
+ regions: RuntimeRegionStore;
388
+ moduleRegistry: RuntimeModuleRegistry;
389
+ }
390
+
391
+ export function createFlatSqlRuntimeStore(): FlatSqlRuntimeStore;
392
+ export function createRuntimeRegionStore(): RuntimeRegionStore;
393
+ export function createModuleRegistry(): RuntimeModuleRegistry;
394
+ export function createRuntimeHost(options?: {
395
+ rows?: FlatSqlRuntimeStore;
396
+ regions?: RuntimeRegionStore;
397
+ moduleRegistry?: RuntimeModuleRegistry;
398
+ }): RuntimeHost;
261
399
  export function getWasmExportNamesFromFile(wasmPath: string): Promise<string[]>;
262
400
 
263
401
  // --- Auth ---
@@ -662,12 +800,30 @@ export type {
662
800
  HarnessInvokeScenario,
663
801
  HarnessRawScenario,
664
802
  ManifestHarnessPlan,
803
+ ModuleHarness,
804
+ ModuleHarnessRuntimeDescriptor,
805
+ PublicationProtectionDemoAlignedType,
806
+ PublicationProtectionDemoSummary,
807
+ PluginInvokeProcessClient,
808
+ PluginInvokeProcessLaunchPlan,
809
+ WasmEdgeRunnerBuildPlan,
665
810
  } from "./testing/index.js";
666
811
 
667
812
  export {
813
+ buildWasmEdgeEmscriptenPthreadRunner,
814
+ buildWasmEdgeSpawnEnv,
815
+ createPublicationProtectionDemoManifest,
816
+ createPublicationProtectionDemoSummary,
817
+ createModuleHarness,
818
+ createPluginInvokeProcessClient,
819
+ createWasmEdgeStreamProcessClient,
668
820
  describeCapabilityRuntimeSurface,
669
821
  generateManifestHarnessPlan,
670
822
  materializeHarnessScenario,
823
+ resolveModuleHarnessLaunchPlan,
824
+ resolveWasmEdgeRunnerBuildPlan,
825
+ resolveWasmEdgeRunnerSourcePath,
826
+ resolveWasmEdgePluginLaunchPlan,
671
827
  serializeHarnessPlan,
672
828
  } from "./testing/index.js";
673
829
 
package/src/index.js CHANGED
@@ -7,6 +7,7 @@ export * from "./bundle/index.js";
7
7
  export * from "./capabilities.js";
8
8
  export * from "./standards/index.js";
9
9
  export * from "./host/index.js";
10
+ export * from "./runtime-host/index.js";
10
11
  export * from "./invoke/index.js";
11
12
  export * from "./testing/index.js";
12
13
  export * from "./deployment/index.js";
@@ -1,4 +1,4 @@
1
- import * as flatbuffers from "flatbuffers";
1
+ import * as flatbuffers from "flatbuffers/mjs/flatbuffers.js";
2
2
 
3
3
  import {
4
4
  PluginInvokeRequest,
@@ -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 { TypedArenaBuffer, TypedArenaBufferT } from "../generated/orbpro/stream/typed-arena-buffer.js";
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 arena = [];
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: Uint8Array.from(arena),
167
+ arena,
166
168
  };
167
169
  }
168
170
 
@@ -1,4 +1,4 @@
1
- import * as flatbuffers from "flatbuffers";
1
+ import * as flatbuffers from "flatbuffers/mjs/flatbuffers.js";
2
2
 
3
3
  import {
4
4
  CapabilityKind,
@@ -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
+ }