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
@@ -10,6 +10,23 @@ import {
10
10
  } from "../invoke/index.js";
11
11
  import { toUint8Array } from "../runtime/bufferLike.js";
12
12
 
13
+ const WASMEDGE_HOST_MAGIC = Uint8Array.from([0x4f, 0x52, 0x50, 0x57]); // ORPW
14
+ const textEncoder = new TextEncoder();
15
+ const textDecoder = new TextDecoder();
16
+ const HOST_CONTROL_OPCODE = Object.freeze({
17
+ INSTALL_MODULE: 16,
18
+ LIST_MODULES: 17,
19
+ UNLOAD_MODULE: 18,
20
+ INVOKE_MODULE: 19,
21
+ APPEND_ROW: 20,
22
+ LIST_ROWS: 21,
23
+ RESOLVE_ROW: 22,
24
+ ALLOCATE_REGION: 23,
25
+ DESCRIBE_REGION: 24,
26
+ RESOLVE_RECORD: 25,
27
+ QUERY_ROWS: 26,
28
+ });
29
+
13
30
  function formatProcessFailure(message, stderrChunks = [], cause = null) {
14
31
  const stderrText = Buffer.concat(stderrChunks).toString("utf8").trim();
15
32
  const details = stderrText ? `${message}\n${stderrText}` : message;
@@ -49,6 +66,25 @@ export function buildWasmEdgeSpawnEnv(baseEnv = process.env) {
49
66
  }
50
67
 
51
68
  export function resolveWasmEdgePluginLaunchPlan(options = {}) {
69
+ const hostProfile = String(options.hostProfile ?? "").trim().toLowerCase();
70
+ if (hostProfile === "runtime-host") {
71
+ if (
72
+ typeof options.wasmEdgeRunnerBinary !== "string" ||
73
+ options.wasmEdgeRunnerBinary.trim().length === 0
74
+ ) {
75
+ throw new Error(
76
+ "resolveWasmEdgePluginLaunchPlan requires wasmEdgeRunnerBinary for runtime-host mode.",
77
+ );
78
+ }
79
+ return {
80
+ command: options.wasmEdgeRunnerBinary,
81
+ args: ["--serve-runtime-host"],
82
+ env: buildWasmEdgeSpawnEnv(options.env),
83
+ wasmPath: null,
84
+ hostProfile: "runtime-host",
85
+ };
86
+ }
87
+
52
88
  const wasmPath =
53
89
  typeof options.wasmPath === "string" && options.wasmPath.trim().length > 0
54
90
  ? path.resolve(options.wasmPath)
@@ -83,7 +119,7 @@ export function resolveWasmEdgePluginLaunchPlan(options = {}) {
83
119
  };
84
120
  }
85
121
 
86
- export async function createPluginInvokeProcessClient(options = {}) {
122
+ async function createLengthPrefixedProcessClient(options = {}) {
87
123
  const launchPlan = normalizeLaunchPlan(options);
88
124
  if (
89
125
  typeof launchPlan.command !== "string" ||
@@ -202,15 +238,7 @@ export async function createPluginInvokeProcessClient(options = {}) {
202
238
 
203
239
  return {
204
240
  launchPlan,
205
-
206
- async invoke(request = {}) {
207
- const requestBytes = encodePluginInvokeRequest(request);
208
- const responseBytes = await invokeRaw(requestBytes);
209
- return decodePluginInvokeResponse(responseBytes);
210
- },
211
-
212
241
  invokeRaw,
213
-
214
242
  async destroy() {
215
243
  expectedShutdown = true;
216
244
  if (!closed) {
@@ -224,3 +252,216 @@ export async function createPluginInvokeProcessClient(options = {}) {
224
252
  },
225
253
  };
226
254
  }
255
+
256
+ function encodeHostControl(opcode, payload = new Uint8Array()) {
257
+ const message = new Uint8Array(5 + payload.length);
258
+ message.set(WASMEDGE_HOST_MAGIC, 0);
259
+ message[4] = opcode;
260
+ message.set(payload, 5);
261
+ return message;
262
+ }
263
+
264
+ function encodeJsonPayload(value) {
265
+ return textEncoder.encode(JSON.stringify(value));
266
+ }
267
+
268
+ function decodeJsonPayload(bytes) {
269
+ if (!bytes || bytes.length === 0) {
270
+ return null;
271
+ }
272
+ return JSON.parse(textDecoder.decode(bytes));
273
+ }
274
+
275
+ function encodeU32(value) {
276
+ const bytes = new Uint8Array(4);
277
+ new DataView(bytes.buffer).setUint32(0, value >>> 0, true);
278
+ return bytes;
279
+ }
280
+
281
+ function decodeU32(bytes) {
282
+ return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(
283
+ 0,
284
+ true,
285
+ );
286
+ }
287
+
288
+ function encodeModuleInvokePayload(moduleId, requestBytes) {
289
+ if (typeof moduleId !== "string" || moduleId.trim().length === 0) {
290
+ throw new TypeError("moduleId must be a non-empty string");
291
+ }
292
+ const moduleIdBytes = textEncoder.encode(moduleId.trim());
293
+ const requestPayload = toUint8Array(requestBytes);
294
+ if (!requestPayload) {
295
+ throw new TypeError("module invoke request bytes are required");
296
+ }
297
+ const payload = new Uint8Array(4 + moduleIdBytes.length + requestPayload.length);
298
+ payload.set(encodeU32(moduleIdBytes.length), 0);
299
+ payload.set(moduleIdBytes, 4);
300
+ payload.set(requestPayload, 4 + moduleIdBytes.length);
301
+ return payload;
302
+ }
303
+
304
+ function serializeRegionOptions(options = {}) {
305
+ return {
306
+ ...options,
307
+ initialRecords: Array.isArray(options.initialRecords)
308
+ ? options.initialRecords.map((record) =>
309
+ record === null || record === undefined
310
+ ? null
311
+ : Array.from(
312
+ toUint8Array(record) ??
313
+ (() => {
314
+ throw new TypeError(
315
+ "runtime region initialRecords must be byte-oriented",
316
+ );
317
+ })(),
318
+ ),
319
+ )
320
+ : [],
321
+ };
322
+ }
323
+
324
+ function normalizeRecordResponse(record) {
325
+ if (!record) {
326
+ return null;
327
+ }
328
+ return {
329
+ ...record,
330
+ bytes: Uint8Array.from(record.bytes ?? []),
331
+ };
332
+ }
333
+
334
+ async function invokeJsonHostControl(rawClient, opcode, payload) {
335
+ const responseBytes = await rawClient.invokeRaw(
336
+ encodeHostControl(opcode, encodeJsonPayload(payload)),
337
+ );
338
+ return decodeJsonPayload(responseBytes);
339
+ }
340
+
341
+ function attachRuntimeHostControls(client, rawClient, options = {}) {
342
+ const encodeRequest = options.encodeRequest ?? ((request) => request);
343
+ const decodeResponse = options.decodeResponse ?? ((response) => response);
344
+
345
+ return Object.assign(client, {
346
+ installModule(definition = {}) {
347
+ return invokeJsonHostControl(
348
+ rawClient,
349
+ HOST_CONTROL_OPCODE.INSTALL_MODULE,
350
+ definition,
351
+ );
352
+ },
353
+ listModules() {
354
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.LIST_MODULES, {});
355
+ },
356
+ unloadModule(moduleId) {
357
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.UNLOAD_MODULE, {
358
+ moduleId,
359
+ });
360
+ },
361
+ async invokeModule(moduleId, request = {}) {
362
+ const responseBytes = await rawClient.invokeRaw(
363
+ encodeHostControl(
364
+ HOST_CONTROL_OPCODE.INVOKE_MODULE,
365
+ encodeModuleInvokePayload(moduleId, encodeRequest(request)),
366
+ ),
367
+ );
368
+ return decodeResponse(responseBytes);
369
+ },
370
+ appendRow(options = {}) {
371
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.APPEND_ROW, options);
372
+ },
373
+ listRows(schemaFileId = null) {
374
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.LIST_ROWS, {
375
+ schemaFileId,
376
+ });
377
+ },
378
+ resolveRow(handle) {
379
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.RESOLVE_ROW, handle);
380
+ },
381
+ queryRows(sql) {
382
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.QUERY_ROWS, {
383
+ sql,
384
+ });
385
+ },
386
+ allocateRegion(options = {}) {
387
+ return invokeJsonHostControl(
388
+ rawClient,
389
+ HOST_CONTROL_OPCODE.ALLOCATE_REGION,
390
+ serializeRegionOptions(options),
391
+ );
392
+ },
393
+ describeRegion(regionId) {
394
+ return invokeJsonHostControl(rawClient, HOST_CONTROL_OPCODE.DESCRIBE_REGION, {
395
+ regionId,
396
+ });
397
+ },
398
+ async resolveRecord(query = {}) {
399
+ const record = await invokeJsonHostControl(
400
+ rawClient,
401
+ HOST_CONTROL_OPCODE.RESOLVE_RECORD,
402
+ query,
403
+ );
404
+ return normalizeRecordResponse(record);
405
+ },
406
+ });
407
+ }
408
+
409
+ export async function createWasmEdgeStreamProcessClient(options = {}) {
410
+ const rawClient = await createLengthPrefixedProcessClient(options);
411
+
412
+ const client = {
413
+ launchPlan: rawClient.launchPlan,
414
+
415
+ invokeRaw(requestBytes) {
416
+ return rawClient.invokeRaw(requestBytes);
417
+ },
418
+
419
+ async invoke(request = {}) {
420
+ const requestBytes = encodePluginInvokeRequest(request);
421
+ const responseBytes = await rawClient.invokeRaw(requestBytes);
422
+ return decodePluginInvokeResponse(responseBytes);
423
+ },
424
+
425
+ destroy() {
426
+ return rawClient.destroy();
427
+ },
428
+ };
429
+
430
+ return attachRuntimeHostControls(client, rawClient, {
431
+ encodeRequest(request) {
432
+ return encodePluginInvokeRequest(request);
433
+ },
434
+ decodeResponse(responseBytes) {
435
+ return decodePluginInvokeResponse(responseBytes);
436
+ },
437
+ });
438
+ }
439
+
440
+ export async function createPluginInvokeProcessClient(options = {}) {
441
+ const rawClient = await createLengthPrefixedProcessClient(options);
442
+
443
+ const client = {
444
+ launchPlan: rawClient.launchPlan,
445
+
446
+ async invoke(request = {}) {
447
+ const requestBytes = encodePluginInvokeRequest(request);
448
+ const responseBytes = await rawClient.invokeRaw(requestBytes);
449
+ return decodePluginInvokeResponse(responseBytes);
450
+ },
451
+
452
+ invokeRaw: rawClient.invokeRaw,
453
+
454
+ destroy() {
455
+ return rawClient.destroy();
456
+ },
457
+ };
458
+
459
+ return attachRuntimeHostControls(client, rawClient, {
460
+ encodeRequest(request) {
461
+ return encodePluginInvokeRequest(request);
462
+ },
463
+ decodeResponse(responseBytes) {
464
+ return decodePluginInvokeResponse(responseBytes);
465
+ },
466
+ });
467
+ }
@@ -0,0 +1,175 @@
1
+ import * as flatbuffers from "flatbuffers/mjs/flatbuffers.js";
2
+
3
+ import { DrainPolicy } from "../generated/orbpro/manifest/drain-policy.js";
4
+ import { TypedArenaBuffer } from "../generated/orbpro/stream/typed-arena-buffer.js";
5
+
6
+ function toByteBuffer(data) {
7
+ if (data instanceof flatbuffers.ByteBuffer) {
8
+ return data;
9
+ }
10
+ return new flatbuffers.ByteBuffer(data);
11
+ }
12
+
13
+ class StreamInvokeRequest {
14
+ constructor() {
15
+ this.bb = null;
16
+ this.bb_pos = 0;
17
+ }
18
+
19
+ __init(i, bb) {
20
+ this.bb_pos = i;
21
+ this.bb = bb;
22
+ return this;
23
+ }
24
+
25
+ static getRootAsStreamInvokeRequest(bb, obj) {
26
+ return (obj || new StreamInvokeRequest()).__init(
27
+ bb.readInt32(bb.position()) + bb.position(),
28
+ bb,
29
+ );
30
+ }
31
+
32
+ static startStreamInvokeRequest(builder) {
33
+ builder.startObject(4);
34
+ }
35
+
36
+ static addMethodId(builder, methodIdOffset) {
37
+ builder.addFieldOffset(0, methodIdOffset, 0);
38
+ }
39
+
40
+ static addInputs(builder, inputsOffset) {
41
+ builder.addFieldOffset(1, inputsOffset, 0);
42
+ }
43
+
44
+ static createInputsVector(builder, data) {
45
+ builder.startVector(4, data.length, 4);
46
+ for (let i = data.length - 1; i >= 0; i -= 1) {
47
+ builder.addOffset(data[i]);
48
+ }
49
+ return builder.endVector();
50
+ }
51
+
52
+ static addOutputStreamCap(builder, outputStreamCap) {
53
+ builder.addFieldInt32(2, outputStreamCap, 0);
54
+ }
55
+
56
+ static addDrainPolicy(builder, drainPolicy) {
57
+ builder.addFieldInt8(3, drainPolicy, DrainPolicy.DRAIN_UNTIL_YIELD);
58
+ }
59
+
60
+ static endStreamInvokeRequest(builder) {
61
+ const offset = builder.endObject();
62
+ builder.requiredField(offset, 4);
63
+ return offset;
64
+ }
65
+ }
66
+
67
+ class StreamInvokeRequestT {
68
+ constructor(
69
+ methodId = null,
70
+ inputs = [],
71
+ outputStreamCap = 0,
72
+ drainPolicy = DrainPolicy.DRAIN_UNTIL_YIELD,
73
+ ) {
74
+ this.methodId = methodId;
75
+ this.inputs = inputs;
76
+ this.outputStreamCap = outputStreamCap;
77
+ this.drainPolicy = drainPolicy;
78
+ }
79
+
80
+ pack(builder) {
81
+ const methodId =
82
+ this.methodId !== null ? builder.createString(this.methodId) : 0;
83
+ const inputs = StreamInvokeRequest.createInputsVector(
84
+ builder,
85
+ builder.createObjectOffsetList(this.inputs),
86
+ );
87
+ StreamInvokeRequest.startStreamInvokeRequest(builder);
88
+ StreamInvokeRequest.addMethodId(builder, methodId);
89
+ StreamInvokeRequest.addInputs(builder, inputs);
90
+ StreamInvokeRequest.addOutputStreamCap(builder, this.outputStreamCap);
91
+ StreamInvokeRequest.addDrainPolicy(builder, this.drainPolicy);
92
+ return StreamInvokeRequest.endStreamInvokeRequest(builder);
93
+ }
94
+ }
95
+
96
+ class StreamInvokeResponse {
97
+ constructor() {
98
+ this.bb = null;
99
+ this.bb_pos = 0;
100
+ }
101
+
102
+ __init(i, bb) {
103
+ this.bb_pos = i;
104
+ this.bb = bb;
105
+ return this;
106
+ }
107
+
108
+ static getRootAsStreamInvokeResponse(bb, obj) {
109
+ return (obj || new StreamInvokeResponse()).__init(
110
+ bb.readInt32(bb.position()) + bb.position(),
111
+ bb,
112
+ );
113
+ }
114
+
115
+ outputs(index, obj) {
116
+ const offset = this.bb.__offset(this.bb_pos, 4);
117
+ return offset
118
+ ? (obj || new TypedArenaBuffer()).__init(
119
+ this.bb.__indirect(this.bb.__vector(this.bb_pos + offset) + index * 4),
120
+ this.bb,
121
+ )
122
+ : null;
123
+ }
124
+
125
+ outputsLength() {
126
+ const offset = this.bb.__offset(this.bb_pos, 4);
127
+ return offset ? this.bb.__vector_len(this.bb_pos + offset) : 0;
128
+ }
129
+
130
+ backlogRemaining() {
131
+ const offset = this.bb.__offset(this.bb_pos, 6);
132
+ return offset ? this.bb.readUint32(this.bb_pos + offset) : 0;
133
+ }
134
+
135
+ yielded() {
136
+ const offset = this.bb.__offset(this.bb_pos, 8);
137
+ return offset ? !!this.bb.readInt8(this.bb_pos + offset) : false;
138
+ }
139
+
140
+ errorCode() {
141
+ const offset = this.bb.__offset(this.bb_pos, 10);
142
+ return offset ? this.bb.readInt32(this.bb_pos + offset) : 0;
143
+ }
144
+
145
+ errorMessage(optionalEncoding) {
146
+ const offset = this.bb.__offset(this.bb_pos, 12);
147
+ return offset ? this.bb.__string(this.bb_pos + offset, optionalEncoding) : null;
148
+ }
149
+
150
+ unpack() {
151
+ return {
152
+ outputs: this.bb.createObjList(this.outputs.bind(this), this.outputsLength()),
153
+ backlogRemaining: this.backlogRemaining(),
154
+ yielded: this.yielded(),
155
+ errorCode: this.errorCode(),
156
+ errorMessage: this.errorMessage(),
157
+ };
158
+ }
159
+ }
160
+
161
+ export function encodeStreamInvokeRequest(request) {
162
+ const normalized =
163
+ request instanceof StreamInvokeRequestT
164
+ ? request
165
+ : Object.assign(new StreamInvokeRequestT(), request);
166
+ const builder = new flatbuffers.Builder(1024);
167
+ builder.finish(normalized.pack(builder));
168
+ return builder.asUint8Array();
169
+ }
170
+
171
+ export function decodeStreamInvokeResponse(data) {
172
+ return StreamInvokeResponse.getRootAsStreamInvokeResponse(
173
+ toByteBuffer(data),
174
+ ).unpack();
175
+ }
@@ -1,6 +1,12 @@
1
- import * as flatbuffers from "flatbuffers";
1
+ import * as flatbuffers from "flatbuffers/mjs/flatbuffers.js";
2
2
 
3
- import { ENC, ENCT, KDF, KeyExchange, SymmetricAlgo } from "spacedatastandards.org/lib/js/ENC/main.js";
3
+ import {
4
+ ENC,
5
+ ENCT,
6
+ KDF,
7
+ KeyExchange,
8
+ SymmetricAlgo,
9
+ } from "spacedatastandards.org/lib/js/ENC/main.js";
4
10
  import { PNM, PNMT } from "spacedatastandards.org/lib/js/PNM/main.js";
5
11
  import { REC, RECT } from "spacedatastandards.org/lib/js/REC/REC.js";
6
12
  import { Record, RecordT } from "spacedatastandards.org/lib/js/REC/Record.js";
@@ -75,6 +81,16 @@ function readUint16LE(buffer, offset, label) {
75
81
  return buffer[offset] | (buffer[offset + 1] << 8);
76
82
  }
77
83
 
84
+ function getRecordValueType(recordTable) {
85
+ if (typeof recordTable.valueType === "function") {
86
+ return recordTable.valueType();
87
+ }
88
+ if (typeof recordTable.value_type === "function") {
89
+ return recordTable.value_type();
90
+ }
91
+ throw new TypeError("REC record table does not expose a value type accessor.");
92
+ }
93
+
78
94
  function readUint32LE(buffer, offset, label) {
79
95
  assertBounds(buffer, offset, 4, label);
80
96
  return (
@@ -609,10 +625,7 @@ export function decodePublicationRecordCollection(bytes) {
609
625
  8,
610
626
  `REC trailer record ${index} standard`,
611
627
  );
612
- const recordType =
613
- typeof recordTable.valueType === "function"
614
- ? recordTable.valueType()
615
- : recordTable.value_type();
628
+ const recordType = getRecordValueType(recordTable);
616
629
  const standard =
617
630
  normalizeStringField(recordTable.standard()) ??
618
631
  STANDARD_BY_RECORD_TYPE[recordType] ??