space-data-module-sdk 0.2.5 → 0.2.7

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/src/index.js CHANGED
@@ -7,6 +7,8 @@ export * from "./bundle/index.js";
7
7
  export * from "./standards/index.js";
8
8
  export * from "./host/index.js";
9
9
  export * from "./invoke/index.js";
10
+ export * from "./testing/index.js";
11
+ export * from "./deployment/index.js";
10
12
  export {
11
13
  DefaultInvokeExports,
12
14
  DefaultManifestExports,
@@ -14,5 +16,7 @@ export {
14
16
  ExternalInterfaceDirection,
15
17
  ExternalInterfaceKind,
16
18
  InvokeSurface,
19
+ ProtocolRole,
20
+ ProtocolTransportKind,
17
21
  RuntimeTarget,
18
22
  } from "./runtime/constants.js";
@@ -6,6 +6,13 @@ export {
6
6
  } from "../generated/orbpro/stream/flat-buffer-type-ref.js";
7
7
  export { decodePluginManifest, encodePluginManifest } from "./codec.js";
8
8
  export { toEmbeddedPluginManifest } from "./normalize.js";
9
+ export {
10
+ clonePayloadTypeRef,
11
+ getPayloadTypeWireFormat,
12
+ normalizePayloadWireFormatName,
13
+ payloadTypeRefsMatch,
14
+ selectPreferredPayloadTypeRef,
15
+ } from "./typeRefs.js";
9
16
  export {
10
17
  generateEmbeddedManifestSource,
11
18
  writeEmbeddedManifestArtifacts,
@@ -13,6 +13,7 @@ import {
13
13
  TimerSpecT,
14
14
  } from "../generated/orbpro/manifest.js";
15
15
  import { FlatBufferTypeRefT } from "../generated/orbpro/stream/flat-buffer-type-ref.js";
16
+ import { ProtocolRole, ProtocolTransportKind } from "../runtime/constants.js";
16
17
 
17
18
  const pluginFamilyByName = Object.freeze({
18
19
  sensor: PluginFamily.SENSOR,
@@ -81,6 +82,31 @@ const invokeSurfaceByName = Object.freeze({
81
82
  command: InvokeSurface.COMMAND,
82
83
  });
83
84
 
85
+ const protocolTransportKindByName = Object.freeze({
86
+ libp2p: ProtocolTransportKind.LIBP2P,
87
+ http: ProtocolTransportKind.HTTP,
88
+ ws: ProtocolTransportKind.WS,
89
+ websocket: ProtocolTransportKind.WS,
90
+ "wasi-pipe": ProtocolTransportKind.WASI_PIPE,
91
+ wasi_pipe: ProtocolTransportKind.WASI_PIPE,
92
+ pipe: ProtocolTransportKind.WASI_PIPE,
93
+ });
94
+
95
+ const protocolRoleByName = Object.freeze({
96
+ handle: ProtocolRole.HANDLE,
97
+ handler: ProtocolRole.HANDLE,
98
+ dial: ProtocolRole.DIAL,
99
+ both: ProtocolRole.BOTH,
100
+ });
101
+
102
+ function normalizeOptionalString(value) {
103
+ if (value === undefined || value === null) {
104
+ return null;
105
+ }
106
+ const normalized = String(value).trim();
107
+ return normalized.length > 0 ? normalized : null;
108
+ }
109
+
84
110
  function normalizeSchemaHash(value) {
85
111
  if (!value) {
86
112
  return [];
@@ -177,6 +203,41 @@ function normalizeInvokeSurfaces(value) {
177
203
  return normalized;
178
204
  }
179
205
 
206
+ function normalizeBoolean(value, fallback = false) {
207
+ if (value === undefined || value === null) {
208
+ return fallback;
209
+ }
210
+ return value === true;
211
+ }
212
+
213
+ function normalizeProtocolTransportKind(value) {
214
+ if (value === undefined || value === null) {
215
+ return null;
216
+ }
217
+ const normalized = String(value)
218
+ .trim()
219
+ .toLowerCase()
220
+ .replace(/_/g, "-");
221
+ if (normalized.length === 0) {
222
+ return null;
223
+ }
224
+ return protocolTransportKindByName[normalized] ?? normalized;
225
+ }
226
+
227
+ function normalizeProtocolRole(value) {
228
+ if (value === undefined || value === null) {
229
+ return null;
230
+ }
231
+ const normalized = String(value)
232
+ .trim()
233
+ .toLowerCase()
234
+ .replace(/_/g, "-");
235
+ if (normalized.length === 0) {
236
+ return null;
237
+ }
238
+ return protocolRoleByName[normalized] ?? normalized;
239
+ }
240
+
180
241
  function toFlatBufferTypeRefT(value = {}) {
181
242
  if (value instanceof FlatBufferTypeRefT) {
182
243
  return value;
@@ -274,11 +335,20 @@ function toProtocolSpecT(value = {}) {
274
335
  return value;
275
336
  }
276
337
  return new ProtocolSpecT(
277
- value.protocolId ?? null,
278
- value.methodId ?? null,
279
- value.inputPortId ?? null,
280
- value.outputPortId ?? null,
281
- value.description ?? null,
338
+ normalizeOptionalString(value.protocolId),
339
+ normalizeOptionalString(value.methodId),
340
+ normalizeOptionalString(value.inputPortId),
341
+ normalizeOptionalString(value.outputPortId),
342
+ normalizeOptionalString(value.description),
343
+ normalizeOptionalString(value.wireId),
344
+ normalizeProtocolTransportKind(value.transportKind),
345
+ normalizeProtocolRole(value.role),
346
+ normalizeOptionalString(value.specUri),
347
+ normalizeBoolean(value.autoInstall, true),
348
+ normalizeBoolean(value.advertise, false),
349
+ normalizeOptionalString(value.discoveryKey),
350
+ normalizeUnsignedInteger(value.defaultPort),
351
+ normalizeBoolean(value.requireSecureTransport, false),
282
352
  );
283
353
  }
284
354
 
@@ -0,0 +1,143 @@
1
+ function cloneSchemaHash(value) {
2
+ if (value instanceof Uint8Array) {
3
+ return new Uint8Array(value);
4
+ }
5
+ if (Array.isArray(value)) {
6
+ return [...value];
7
+ }
8
+ return value ?? undefined;
9
+ }
10
+
11
+ export function clonePayloadTypeRef(value = null) {
12
+ if (!value || typeof value !== "object") {
13
+ return { acceptsAnyFlatbuffer: true };
14
+ }
15
+ return {
16
+ schemaName: value.schemaName ?? value.schema_name ?? undefined,
17
+ fileIdentifier: value.fileIdentifier ?? value.file_identifier ?? undefined,
18
+ schemaHash: cloneSchemaHash(value.schemaHash ?? value.schema_hash),
19
+ acceptsAnyFlatbuffer: Boolean(
20
+ value.acceptsAnyFlatbuffer ?? value.accepts_any_flatbuffer ?? false,
21
+ ),
22
+ wireFormat: value.wireFormat ?? value.wire_format ?? undefined,
23
+ rootTypeName: value.rootTypeName ?? value.root_type_name ?? undefined,
24
+ fixedStringLength:
25
+ value.fixedStringLength ?? value.fixed_string_length ?? undefined,
26
+ byteLength: value.byteLength ?? value.byte_length ?? undefined,
27
+ requiredAlignment:
28
+ value.requiredAlignment ?? value.required_alignment ?? undefined,
29
+ };
30
+ }
31
+
32
+ export function normalizePayloadWireFormatName(value) {
33
+ if (value === undefined || value === null || value === "") {
34
+ return null;
35
+ }
36
+ const normalized = String(value).trim().toLowerCase().replace(/_/g, "-");
37
+ if (normalized === "aligned-binary") {
38
+ return "aligned-binary";
39
+ }
40
+ if (normalized === "flatbuffer") {
41
+ return "flatbuffer";
42
+ }
43
+ return null;
44
+ }
45
+
46
+ export function getPayloadTypeWireFormat(typeRef = {}) {
47
+ return normalizePayloadWireFormatName(typeRef.wireFormat) ?? "flatbuffer";
48
+ }
49
+
50
+ function schemaHashMatches(expected, actual) {
51
+ if (!Array.isArray(expected) && !(expected instanceof Uint8Array)) {
52
+ return true;
53
+ }
54
+ const expectedArray = Array.from(expected);
55
+ if (expectedArray.length === 0) {
56
+ return true;
57
+ }
58
+ const actualArray =
59
+ Array.isArray(actual) || actual instanceof Uint8Array
60
+ ? Array.from(actual)
61
+ : null;
62
+ if (!actualArray || actualArray.length !== expectedArray.length) {
63
+ return false;
64
+ }
65
+ for (let index = 0; index < expectedArray.length; index += 1) {
66
+ if (expectedArray[index] !== actualArray[index]) {
67
+ return false;
68
+ }
69
+ }
70
+ return true;
71
+ }
72
+
73
+ function optionalScalarMatches(expected, actual) {
74
+ return expected === undefined || expected === null || expected === actual;
75
+ }
76
+
77
+ export function payloadTypeRefsMatch(expectedTypeRef = {}, actualTypeRef = {}) {
78
+ const expected = clonePayloadTypeRef(expectedTypeRef);
79
+ const actual = clonePayloadTypeRef(actualTypeRef);
80
+ const expectedWireFormat = getPayloadTypeWireFormat(expected);
81
+ const actualWireFormat = getPayloadTypeWireFormat(actual);
82
+
83
+ if (expected.acceptsAnyFlatbuffer === true) {
84
+ return actualWireFormat === "flatbuffer";
85
+ }
86
+ if (expectedWireFormat !== actualWireFormat) {
87
+ return false;
88
+ }
89
+ if (expected.schemaName && expected.schemaName !== actual.schemaName) {
90
+ return false;
91
+ }
92
+ if (
93
+ expected.fileIdentifier &&
94
+ expected.fileIdentifier !== actual.fileIdentifier
95
+ ) {
96
+ return false;
97
+ }
98
+ if (!schemaHashMatches(expected.schemaHash, actual.schemaHash)) {
99
+ return false;
100
+ }
101
+ if (expectedWireFormat === "aligned-binary") {
102
+ if (!optionalScalarMatches(expected.rootTypeName, actual.rootTypeName)) {
103
+ return false;
104
+ }
105
+ if (!optionalScalarMatches(expected.fixedStringLength, actual.fixedStringLength)) {
106
+ return false;
107
+ }
108
+ if (!optionalScalarMatches(expected.byteLength, actual.byteLength)) {
109
+ return false;
110
+ }
111
+ if (
112
+ !optionalScalarMatches(expected.requiredAlignment, actual.requiredAlignment)
113
+ ) {
114
+ return false;
115
+ }
116
+ }
117
+ return true;
118
+ }
119
+
120
+ export function selectPreferredPayloadTypeRef(port = {}, options = {}) {
121
+ const preferredWireFormat = normalizePayloadWireFormatName(
122
+ options.preferredWireFormat,
123
+ );
124
+ let fallback = null;
125
+ for (const typeSet of Array.isArray(port.acceptedTypeSets) ? port.acceptedTypeSets : []) {
126
+ const allowedTypes = Array.isArray(typeSet.allowedTypes)
127
+ ? typeSet.allowedTypes
128
+ : [];
129
+ for (const allowedType of allowedTypes) {
130
+ const candidate = clonePayloadTypeRef(allowedType);
131
+ if (fallback === null) {
132
+ fallback = candidate;
133
+ }
134
+ if (
135
+ preferredWireFormat !== null &&
136
+ getPayloadTypeWireFormat(candidate) === preferredWireFormat
137
+ ) {
138
+ return candidate;
139
+ }
140
+ }
141
+ }
142
+ return fallback ?? clonePayloadTypeRef(null);
143
+ }
@@ -48,6 +48,19 @@ export const InvokeSurface = Object.freeze({
48
48
  COMMAND: "command",
49
49
  });
50
50
 
51
+ export const ProtocolTransportKind = Object.freeze({
52
+ LIBP2P: "libp2p",
53
+ HTTP: "http",
54
+ WS: "ws",
55
+ WASI_PIPE: "wasi-pipe",
56
+ });
57
+
58
+ export const ProtocolRole = Object.freeze({
59
+ HANDLE: "handle",
60
+ DIAL: "dial",
61
+ BOTH: "both",
62
+ });
63
+
51
64
  export const DefaultManifestExports = Object.freeze({
52
65
  pluginBytesSymbol: "plugin_get_manifest_flatbuffer",
53
66
  pluginSizeSymbol: "plugin_get_manifest_flatbuffer_size",
@@ -0,0 +1,15 @@
1
+ export {
2
+ DefaultInvokeExports,
3
+ DefaultManifestExports,
4
+ DrainPolicy,
5
+ ExternalInterfaceDirection,
6
+ ExternalInterfaceKind,
7
+ InvokeSurface,
8
+ ProtocolRole,
9
+ ProtocolTransportKind,
10
+ RuntimeTarget,
11
+ } from "../index.js";
12
+
13
+ export function isArrayBufferLike(value: unknown): boolean;
14
+ export function hasByteAddressableBuffer(value: unknown): boolean;
15
+ export function toUint8Array(value: unknown): Uint8Array | null;
@@ -0,0 +1,2 @@
1
+ export * from "./bufferLike.js";
2
+ export * from "./constants.js";
@@ -0,0 +1,84 @@
1
+ import type {
2
+ InvokeSurface,
3
+ PayloadTypeRef,
4
+ PayloadWireFormat,
5
+ PluginManifest,
6
+ } from "../index.js";
7
+
8
+ export interface HarnessInputFrame {
9
+ portId?: string | null;
10
+ typeRef?: PayloadTypeRef | null;
11
+ payload?: Uint8Array | ArrayBuffer | ArrayBufferView | string | null;
12
+ }
13
+
14
+ export interface HarnessInvokeScenario {
15
+ id: string;
16
+ kind: "invoke";
17
+ surface: InvokeSurface;
18
+ methodId: string;
19
+ displayName?: string | null;
20
+ inputs?: HarnessInputFrame[];
21
+ requiredPortIds?: string[];
22
+ expectedStatusCode?: number;
23
+ notes?: string[];
24
+ }
25
+
26
+ export interface HarnessRawScenario {
27
+ id: string;
28
+ kind: string;
29
+ stdinBytes?: Uint8Array | ArrayBuffer | ArrayBufferView | string | null;
30
+ notes?: string[];
31
+ }
32
+
33
+ export interface CapabilityRuntimeSurface {
34
+ capability: string;
35
+ wasi: boolean;
36
+ syncHostcall: boolean;
37
+ nodeHostApi: boolean;
38
+ notes: string[];
39
+ }
40
+
41
+ export interface ManifestHarnessPlan {
42
+ moduleKind: "module" | "flow";
43
+ pluginId: string | null;
44
+ name: string | null;
45
+ version: string | null;
46
+ invokeSurfaces: InvokeSurface[];
47
+ methods: Array<{
48
+ methodId: string | null;
49
+ displayName: string | null;
50
+ inputPorts: number;
51
+ outputPorts: number;
52
+ }>;
53
+ capabilities: CapabilityRuntimeSurface[];
54
+ generatedCases: HarnessInvokeScenario[];
55
+ scenarios: Array<HarnessInvokeScenario | HarnessRawScenario>;
56
+ }
57
+
58
+ export function describeCapabilityRuntimeSurface(
59
+ capability: string,
60
+ ): CapabilityRuntimeSurface;
61
+
62
+ export function generateManifestHarnessPlan(options: {
63
+ manifest: PluginManifest;
64
+ includeOptionalInputs?: boolean;
65
+ expectedStatusCode?: number;
66
+ preferredWireFormat?: PayloadWireFormat;
67
+ payloadForPort?: (context: {
68
+ methodId: string | null;
69
+ portId: string | null;
70
+ port: unknown;
71
+ required: boolean;
72
+ typeRef: PayloadTypeRef;
73
+ }) => Uint8Array | ArrayBuffer | ArrayBufferView | string | null | undefined;
74
+ scenarios?: Array<HarnessInvokeScenario | HarnessRawScenario>;
75
+ }): ManifestHarnessPlan;
76
+
77
+ export function materializeHarnessScenario(
78
+ scenario: HarnessInvokeScenario | HarnessRawScenario,
79
+ ): (HarnessInvokeScenario | HarnessRawScenario) & {
80
+ stdinBytes?: Uint8Array;
81
+ requestBytes?: Uint8Array;
82
+ };
83
+
84
+ export function serializeHarnessPlan(plan: ManifestHarnessPlan): unknown;