space-data-module-sdk 0.3.1 → 0.5.3

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 CHANGED
@@ -12,7 +12,7 @@ This repository is the source of truth for module-level concerns:
12
12
  - standards-aware compliance and capability validation
13
13
  - module compilation and protection
14
14
  - the `sds.bundle` single-file custom section
15
- - deployment authorization and encrypted transport envelopes
15
+ - deployment authorization plus SDS publication records (`REC`, `PNM`, `ENC`)
16
16
  - the first canonical module hostcall/import ABI surface
17
17
 
18
18
  <p align="center">
@@ -36,9 +36,11 @@ A module built with this SDK is a `.wasm` artifact with:
36
36
  - manifest bytes
37
37
  - resolved deployment plans and input bindings
38
38
  - deployment authorization
39
- - detached signatures
40
- - encrypted transport envelopes
41
39
  - auxiliary FlatBuffer or raw payloads
40
+ - optional appended SDS `REC` publication trailers carrying:
41
+ - `PNM` digital-signature/publication metadata
42
+ - `ENC` encrypted-delivery metadata
43
+ - auxiliary FlatBuffer or raw payloads
42
44
 
43
45
  The module contract stays the same whether the artifact is loaded directly,
44
46
  wrapped in a deployment envelope, or shipped as one bundled `.wasm` file.
@@ -240,8 +242,11 @@ The full contract split is documented in
240
242
  ## Single-File Bundles
241
243
 
242
244
  `sds.bundle` keeps module delivery to one file without changing WebAssembly
243
- loadability. The SDK appends a standard custom section, not raw trailer bytes,
244
- so the bundled artifact still compiles as a normal `.wasm` module.
245
+ loadability for the runtime payload itself. The SDK writes the bundle as a
246
+ standard custom section inside the wasm module and, when the artifact is
247
+ signed or encrypted for publication, appends an SDS `REC` trailer after the
248
+ wasm bytes. Loaders must scan and strip that trailer before handing bytes to a
249
+ runtime such as WasmEdge.
245
250
 
246
251
  The reference path lives in
247
252
  [`examples/single-file-bundle`](./examples/single-file-bundle):
@@ -263,8 +268,9 @@ publication descriptor. That descriptor covers:
263
268
 
264
269
  - standalone module packages
265
270
  - attached module artifacts shipped inside another language library
266
- - discovery of bundled wasm, sidecar signatures, and encrypted transport
267
- FlatBuffers
271
+ - discovery of bundled wasm
272
+ - appended `REC` trailers carrying `PNM` and optional `ENC`
273
+ - sidecar `PNM` / `ENC` FlatBuffers when a package does not embed the trailer
268
274
 
269
275
  The full standard is in
270
276
  [`docs/module-publication-standard.md`](./docs/module-publication-standard.md),
@@ -280,8 +286,10 @@ For npm packages, the simplest form is:
280
286
  }
281
287
  ```
282
288
 
283
- When signature or transport metadata is published in the same file, it belongs
284
- inside `sds.bundle`, not as raw bytes after the end of the wasm binary.
289
+ When publication metadata is published in the same file, it belongs in an
290
+ appended SDS `REC` trailer. Loaders scan from the end of the protected blob,
291
+ resolve `PNM` / `ENC`, strip or decrypt as needed, and only then instantiate
292
+ the remaining raw wasm bytes.
285
293
 
286
294
  ## Host ABI
287
295
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "space-data-module-sdk",
3
- "version": "0.3.1",
3
+ "version": "0.5.3",
4
4
  "description": "Module SDK for building, validating, signing, and deploying WebAssembly modules on the Space Data Network.",
5
5
  "type": "module",
6
6
  "types": "./src/index.d.ts",
@@ -12,8 +12,16 @@
12
12
  "space-data-module": "bin/space-data-module.js"
13
13
  },
14
14
  "exports": {
15
- ".": "./src/index.js",
16
- "./manifest": "./src/manifest/index.js",
15
+ ".": {
16
+ "browser": "./src/browser.js",
17
+ "default": "./src/index.js"
18
+ },
19
+ "./manifest": {
20
+ "browser": "./src/manifest/browser.js",
21
+ "default": "./src/manifest/index.js"
22
+ },
23
+ "./embedded-manifest": "./src/embeddedManifest.js",
24
+ "./capabilities": "./src/capabilities.js",
17
25
  "./compliance": "./src/compliance/index.js",
18
26
  "./auth": "./src/auth/index.js",
19
27
  "./transport": "./src/transport/index.js",
@@ -46,7 +54,7 @@
46
54
  "flatbuffers": "^25.9.23",
47
55
  "hd-wallet-wasm": "1.6.0",
48
56
  "sdn-emception": "1.0.0",
49
- "spacedatastandards.org": "23.3.3-0.3.4"
57
+ "spacedatastandards.org": "git+https://github.com/DigitalArsenal/spacedatastandards.org.git#09af351a1"
50
58
  },
51
59
  "devDependencies": {
52
60
  "express": "^4.21.2",
package/src/browser.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./manifest/browser.js";
2
+ export * from "./auth/index.js";
3
+ export * from "./transport/index.js";
4
+ export * from "./bundle/index.js";
5
+ export * from "./capabilities.js";
6
+ export * from "./deployment/index.js";
7
+ export * from "./invoke/index.js";
8
+ export * from "./runtime/index.js";
@@ -9,3 +9,8 @@ export const SDS_DEPLOYMENT_SECTION_NAME = "sds.deployment";
9
9
  export const SDS_DEPLOYMENT_ENTRY_ID = "deployment-plan";
10
10
  export const SDS_DEPLOYMENT_MEDIA_TYPE =
11
11
  "application/vnd.space-data.module.deployment+json";
12
+ export const SDS_GUEST_LINK_OBJECT_ENTRY_ID = "guest-link-object";
13
+ export const SDS_GUEST_LINK_METADATA_ENTRY_ID = "guest-link-metadata";
14
+ export const SDS_GUEST_LINK_SECTION_NAME = "sds.guest-link";
15
+ export const SDS_GUEST_LINK_MEDIA_TYPE =
16
+ "application/vnd.space-data.module.guest-link+json";
@@ -20,6 +20,7 @@ import {
20
20
  moduleBundleEncodingToName,
21
21
  moduleBundleRoleToName,
22
22
  } from "./codec.js";
23
+ import { extractPublicationRecordCollection } from "../transport/records.js";
23
24
 
24
25
  const textDecoder = new TextDecoder();
25
26
  const textEncoder = new TextEncoder();
@@ -86,7 +87,11 @@ export function decodeUnsignedLeb128(bytes, offset = 0) {
86
87
  }
87
88
 
88
89
  export function parseWasmModuleSections(bytes) {
89
- const wasmBytes = normalizeBytes(bytes, "wasm bytes");
90
+ const protectedArtifact = extractPublicationRecordCollection(bytes);
91
+ const wasmBytes = normalizeBytes(
92
+ protectedArtifact?.payloadBytes ?? bytes,
93
+ "wasm bytes",
94
+ );
90
95
  if (wasmBytes.length < 8) {
91
96
  throw new Error("WASM module is truncated.");
92
97
  }
@@ -345,10 +350,12 @@ export async function computeCanonicalModuleHash(
345
350
  bytes,
346
351
  options = {},
347
352
  ) {
353
+ const protectedArtifact = extractPublicationRecordCollection(bytes);
354
+ const candidateBytes = protectedArtifact?.payloadBytes ?? bytes;
348
355
  const prefix = String(
349
356
  options.customSectionPrefix ?? SDS_CUSTOM_SECTION_PREFIX,
350
357
  );
351
- const canonicalWasmBytes = stripWasmCustomSections(bytes, (section) =>
358
+ const canonicalWasmBytes = stripWasmCustomSections(candidateBytes, (section) =>
352
359
  section.name.startsWith(prefix),
353
360
  );
354
361
  const hashBytes = await sha256Bytes(canonicalWasmBytes);
@@ -360,7 +367,11 @@ export async function computeCanonicalModuleHash(
360
367
  }
361
368
 
362
369
  export async function createSingleFileBundle(options = {}) {
363
- const wasmBytes = normalizeBytes(options.wasmBytes, "wasmBytes");
370
+ const protectedArtifact = extractPublicationRecordCollection(options.wasmBytes);
371
+ const wasmBytes = normalizeBytes(
372
+ protectedArtifact?.payloadBytes ?? options.wasmBytes,
373
+ "wasmBytes",
374
+ );
364
375
  const manifestBytes =
365
376
  options.manifestBytes !== undefined
366
377
  ? normalizeBytes(options.manifestBytes, "manifestBytes")
@@ -422,7 +433,11 @@ export async function createSingleFileBundle(options = {}) {
422
433
  }
423
434
 
424
435
  export async function parseSingleFileBundle(bytes, options = {}) {
425
- const wasmBytes = normalizeBytes(bytes, "wasm bytes");
436
+ const protectedArtifact = extractPublicationRecordCollection(bytes);
437
+ const wasmBytes = normalizeBytes(
438
+ protectedArtifact?.payloadBytes ?? bytes,
439
+ "wasm bytes",
440
+ );
426
441
  const customSections = listWasmCustomSections(wasmBytes);
427
442
  const bundleSectionName = String(
428
443
  options.bundleSectionName ?? SDS_BUNDLE_SECTION_NAME,
@@ -462,6 +477,8 @@ export async function parseSingleFileBundle(bytes, options = {}) {
462
477
  ) ?? null;
463
478
  return {
464
479
  wasmBytes,
480
+ protectedArtifactBytes: protectedArtifact?.protectedBytes ?? null,
481
+ publicationRecords: protectedArtifact ?? null,
465
482
  bundleBytes,
466
483
  bundle,
467
484
  entries: parsedEntries,
@@ -0,0 +1,46 @@
1
+ export const RecommendedCapabilityIds = Object.freeze([
2
+ "clock",
3
+ "random",
4
+ "logging",
5
+ "timers",
6
+ "schedule_cron",
7
+ "http",
8
+ "tls",
9
+ "websocket",
10
+ "mqtt",
11
+ "tcp",
12
+ "udp",
13
+ "network",
14
+ "filesystem",
15
+ "pipe",
16
+ "pubsub",
17
+ "protocol_handle",
18
+ "protocol_dial",
19
+ "database",
20
+ "storage_adapter",
21
+ "storage_query",
22
+ "storage_write",
23
+ "context_read",
24
+ "context_write",
25
+ "process_exec",
26
+ "crypto_hash",
27
+ "crypto_sign",
28
+ "crypto_verify",
29
+ "crypto_encrypt",
30
+ "crypto_decrypt",
31
+ "crypto_key_agreement",
32
+ "crypto_kdf",
33
+ "wallet_sign",
34
+ "ipfs",
35
+ "scene_access",
36
+ "entity_access",
37
+ "render_hooks",
38
+ ]);
39
+
40
+ export const StandaloneWasiCapabilityIds = Object.freeze([
41
+ "logging",
42
+ "clock",
43
+ "random",
44
+ "filesystem",
45
+ "pipe",
46
+ ]);
@@ -26,10 +26,25 @@ import { runWithEmceptionLock } from "./emceptionNode.js";
26
26
  import { encodePluginManifest, toEmbeddedPluginManifest } from "../manifest/index.js";
27
27
  import { DefaultInvokeExports, InvokeSurface } from "../runtime/constants.js";
28
28
  import {
29
- encryptJsonForRecipient,
29
+ appendPublicationRecordCollection,
30
+ createEncryptedEnvelopePayload,
31
+ createPublicationNotice,
32
+ encodePublicationRecordCollection,
33
+ encryptBytesForRecipient,
34
+ extractPublicationRecordCollection,
30
35
  generateX25519Keypair,
31
36
  } from "../transport/index.js";
32
37
  import { createSingleFileBundle } from "../bundle/index.js";
38
+ import {
39
+ SDS_GUEST_LINK_MEDIA_TYPE,
40
+ SDS_GUEST_LINK_METADATA_ENTRY_ID,
41
+ SDS_GUEST_LINK_OBJECT_ENTRY_ID,
42
+ SDS_GUEST_LINK_SECTION_NAME,
43
+ } from "../bundle/constants.js";
44
+ import {
45
+ decodeUnsignedLeb128,
46
+ parseWasmModuleSections,
47
+ } from "../bundle/wasm.js";
33
48
  import {
34
49
  base64ToBytes,
35
50
  bytesToBase64,
@@ -76,6 +91,201 @@ function buildCompilerArgs(exportedSymbols, options = {}) {
76
91
  return args;
77
92
  }
78
93
 
94
+ function guestLinkSymbolPrefix(pluginId) {
95
+ const normalized = String(pluginId ?? "module");
96
+ const ascii = Array.from(new TextEncoder().encode(normalized))
97
+ .map((byte) => byte.toString(16).padStart(2, "0"))
98
+ .join("")
99
+ .slice(0, 24);
100
+ return `sdm_guest_${ascii}_`;
101
+ }
102
+
103
+ function parseStrongDefinedIdentifiers(nmOutput = "") {
104
+ const identifiers = new Set();
105
+ for (const line of String(nmOutput).split(/\r?\n/)) {
106
+ const match = line.match(/^\S+\s+([A-Za-z])\s+(.+)$/);
107
+ if (!match) {
108
+ continue;
109
+ }
110
+ const symbolType = match[1];
111
+ if (!"TDBCGRSV".includes(symbolType)) {
112
+ continue;
113
+ }
114
+ const demangled = match[2].trim();
115
+ const baseName = demangled
116
+ .replace(/\(.*$/, "")
117
+ .split("::")
118
+ .at(-1)
119
+ ?.trim();
120
+ if (!C_IDENTIFIER.test(baseName ?? "")) {
121
+ continue;
122
+ }
123
+ identifiers.add(baseName);
124
+ }
125
+ return Array.from(identifiers).sort();
126
+ }
127
+
128
+ const WASM_SYM_BINDING_WEAK = 0x01;
129
+ const WASM_SYM_BINDING_LOCAL = 0x02;
130
+ const WASM_SYM_UNDEFINED = 0x10;
131
+ const WASM_SYM_EXPLICIT_NAME = 0x40;
132
+ const WASM_SYMBOL_KIND_FUNCTION = 0;
133
+ const WASM_SYMBOL_KIND_DATA = 1;
134
+ const WASM_SYMBOL_KIND_GLOBAL = 2;
135
+ const WASM_SYMBOL_KIND_SECTION = 3;
136
+ const WASM_SYMBOL_KIND_EVENT = 4;
137
+ const WASM_SYMBOL_KIND_TABLE = 5;
138
+ const LINKING_SYMBOL_TABLE_SUBSECTION_ID = 8;
139
+ const textDecoder = new TextDecoder();
140
+
141
+ function decodeWasmName(bytes, offset) {
142
+ const lengthInfo = decodeUnsignedLeb128(bytes, offset);
143
+ const nameStart = lengthInfo.nextOffset;
144
+ const nameEnd = nameStart + lengthInfo.value;
145
+ if (nameEnd > bytes.length) {
146
+ throw new Error("WASM name extends past end of symbol payload.");
147
+ }
148
+ return {
149
+ value: textDecoder.decode(bytes.subarray(nameStart, nameEnd)),
150
+ nextOffset: nameEnd,
151
+ };
152
+ }
153
+
154
+ function parseDefinedLinkSymbolsFromObjectBytes(objectBytes) {
155
+ const parsed = parseWasmModuleSections(objectBytes);
156
+ const linkingSection = parsed.sections.find(
157
+ (section) => section.id === 0 && section.name === "linking",
158
+ );
159
+ if (!linkingSection) {
160
+ throw new Error("Guest-link object is missing a linking custom section.");
161
+ }
162
+ const payload = linkingSection.dataBytes;
163
+ let offset = decodeUnsignedLeb128(payload, 0).nextOffset;
164
+ const identifiers = new Set();
165
+ while (offset < payload.length) {
166
+ const subsectionId = payload[offset++];
167
+ const sizeInfo = decodeUnsignedLeb128(payload, offset);
168
+ offset = sizeInfo.nextOffset;
169
+ const subsectionEnd = offset + sizeInfo.value;
170
+ if (subsectionEnd > payload.length) {
171
+ throw new Error("Linking subsection extends past end of payload.");
172
+ }
173
+ if (subsectionId !== LINKING_SYMBOL_TABLE_SUBSECTION_ID) {
174
+ offset = subsectionEnd;
175
+ continue;
176
+ }
177
+ let cursor = offset;
178
+ const countInfo = decodeUnsignedLeb128(payload, cursor);
179
+ cursor = countInfo.nextOffset;
180
+ for (let symbolIndex = 0; symbolIndex < countInfo.value; symbolIndex += 1) {
181
+ const kind = payload[cursor++];
182
+ const flagsInfo = decodeUnsignedLeb128(payload, cursor);
183
+ const flags = flagsInfo.value;
184
+ cursor = flagsInfo.nextOffset;
185
+ let name = "";
186
+ if (
187
+ kind === WASM_SYMBOL_KIND_FUNCTION ||
188
+ kind === WASM_SYMBOL_KIND_GLOBAL ||
189
+ kind === WASM_SYMBOL_KIND_EVENT ||
190
+ kind === WASM_SYMBOL_KIND_TABLE
191
+ ) {
192
+ cursor = decodeUnsignedLeb128(payload, cursor).nextOffset;
193
+ if (
194
+ (flags & WASM_SYM_UNDEFINED) === 0 ||
195
+ (flags & WASM_SYM_EXPLICIT_NAME) !== 0
196
+ ) {
197
+ const nameInfo = decodeWasmName(payload, cursor);
198
+ name = nameInfo.value;
199
+ cursor = nameInfo.nextOffset;
200
+ }
201
+ } else if (kind === WASM_SYMBOL_KIND_DATA) {
202
+ const nameInfo = decodeWasmName(payload, cursor);
203
+ name = nameInfo.value;
204
+ cursor = nameInfo.nextOffset;
205
+ if ((flags & WASM_SYM_UNDEFINED) === 0) {
206
+ cursor = decodeUnsignedLeb128(payload, cursor).nextOffset;
207
+ cursor = decodeUnsignedLeb128(payload, cursor).nextOffset;
208
+ cursor = decodeUnsignedLeb128(payload, cursor).nextOffset;
209
+ }
210
+ } else if (kind === WASM_SYMBOL_KIND_SECTION) {
211
+ cursor = decodeUnsignedLeb128(payload, cursor).nextOffset;
212
+ } else {
213
+ throw new Error(`Unsupported WASM linking symbol kind: ${kind}`);
214
+ }
215
+ if ((flags & WASM_SYM_UNDEFINED) !== 0) {
216
+ continue;
217
+ }
218
+ if ((flags & WASM_SYM_BINDING_LOCAL) !== 0) {
219
+ continue;
220
+ }
221
+ if ((flags & WASM_SYM_BINDING_WEAK) !== 0) {
222
+ continue;
223
+ }
224
+ if (!C_IDENTIFIER.test(name)) {
225
+ continue;
226
+ }
227
+ identifiers.add(name);
228
+ }
229
+ return Array.from(identifiers).sort();
230
+ }
231
+ throw new Error("Guest-link object is missing a symbol table subsection.");
232
+ }
233
+
234
+ function deriveGuestLinkRenameArgs({
235
+ objectBytes,
236
+ pluginId,
237
+ methodIds = [],
238
+ } = {}) {
239
+ const identifiers = parseDefinedLinkSymbolsFromObjectBytes(objectBytes);
240
+ const prefix = guestLinkSymbolPrefix(pluginId);
241
+ const renamedIdentifiers = Array.from(
242
+ new Set([...identifiers, ...methodIds.filter((value) => C_IDENTIFIER.test(value))]),
243
+ ).sort();
244
+ return {
245
+ prefix,
246
+ identifiers: renamedIdentifiers,
247
+ renameArgs: renamedIdentifiers.map(
248
+ (identifier) => `-D${identifier}=${prefix}${identifier}`,
249
+ ),
250
+ methodSymbols: Object.fromEntries(
251
+ methodIds.map((methodId) => [methodId, `${prefix}${methodId}`]),
252
+ ),
253
+ };
254
+ }
255
+
256
+ function createGuestLinkBundleEntries(guestLink) {
257
+ if (!guestLink?.objectBytes || guestLink.objectBytes.length === 0) {
258
+ return [];
259
+ }
260
+ return [
261
+ {
262
+ entryId: SDS_GUEST_LINK_OBJECT_ENTRY_ID,
263
+ role: "auxiliary",
264
+ sectionName: SDS_GUEST_LINK_SECTION_NAME,
265
+ payloadEncoding: "raw-bytes",
266
+ mediaType: "application/wasm",
267
+ payload: guestLink.objectBytes,
268
+ description: "Prefixed guest-link wasm object for monolithic flow linking.",
269
+ },
270
+ {
271
+ entryId: SDS_GUEST_LINK_METADATA_ENTRY_ID,
272
+ role: "auxiliary",
273
+ sectionName: SDS_GUEST_LINK_SECTION_NAME,
274
+ payloadEncoding: "json-utf8",
275
+ mediaType: SDS_GUEST_LINK_MEDIA_TYPE,
276
+ payload: {
277
+ version: 1,
278
+ format: "wasm-object",
279
+ symbolPrefix: guestLink.symbolPrefix,
280
+ methodSymbols: guestLink.methodSymbols,
281
+ methodIds: Object.keys(guestLink.methodSymbols ?? {}),
282
+ language: guestLink.language,
283
+ },
284
+ description: "Guest-link metadata for monolithic flow linking.",
285
+ },
286
+ ];
287
+ }
288
+
79
289
  async function getInvokeCppSupportFiles() {
80
290
  const [runtimeHeaders, schemaHeaders] = await Promise.all([
81
291
  getFlatbuffersCppRuntimeHeaders(),
@@ -114,6 +324,8 @@ function removeEmceptionDirectory(emception, directoryPath) {
114
324
 
115
325
  async function compileWithEmception(options = {}) {
116
326
  const {
327
+ manifest,
328
+ language,
117
329
  sourceCompilerCommand,
118
330
  sourceExtension,
119
331
  sourceCode,
@@ -140,6 +352,7 @@ async function compileWithEmception(options = {}) {
140
352
  const invokeHeaderPath = path.posix.join(workDir, "space_data_module_invoke.h");
141
353
  const invokeSourcePath = path.posix.join(workDir, "plugin-invoke-bridge.cpp");
142
354
  const sourceObjectPath = path.posix.join(workDir, "module.o");
355
+ const linkObjectPath = path.posix.join(workDir, "module-link.o");
143
356
  const manifestObjectPath = path.posix.join(workDir, "plugin-manifest-exports.o");
144
357
  const invokeObjectPath = path.posix.join(workDir, "plugin-invoke-bridge.o");
145
358
  const wasmOutputPath = path.posix.join(workDir, "module.wasm");
@@ -165,6 +378,43 @@ async function compileWithEmception(options = {}) {
165
378
  "-o",
166
379
  sourceObjectPath,
167
380
  ],
381
+ ];
382
+
383
+ for (const command of commands) {
384
+ const result = emception.run(command.join(" "));
385
+ if (result.returncode !== 0) {
386
+ throw new Error(
387
+ `Compilation failed with ${command[0]} (emception): ${result.stderr || result.stdout}`,
388
+ );
389
+ }
390
+ }
391
+
392
+ const sourceObjectBytes = new Uint8Array(emception.readFile(sourceObjectPath));
393
+ const guestLink = deriveGuestLinkRenameArgs({
394
+ objectBytes: sourceObjectBytes,
395
+ pluginId: manifest?.pluginId,
396
+ methodIds: Array.isArray(manifest?.methods)
397
+ ? manifest.methods.map((method) => String(method?.methodId ?? ""))
398
+ : [],
399
+ });
400
+
401
+ const linkCompileCommand = [
402
+ sourceCompilerCommand,
403
+ "-c",
404
+ sourcePath,
405
+ `-I${workDir}`,
406
+ ...guestLink.renameArgs,
407
+ "-o",
408
+ linkObjectPath,
409
+ ];
410
+ const linkCompileResult = emception.run(linkCompileCommand.join(" "));
411
+ if (linkCompileResult.returncode !== 0) {
412
+ throw new Error(
413
+ `Compilation failed with ${linkCompileCommand[0]} (emception): ${linkCompileResult.stderr || linkCompileResult.stdout}`,
414
+ );
415
+ }
416
+
417
+ const remainingCommands = [
168
418
  [
169
419
  "em++",
170
420
  "-c",
@@ -195,8 +445,7 @@ async function compileWithEmception(options = {}) {
195
445
  wasmOutputPath,
196
446
  ],
197
447
  ];
198
-
199
- for (const command of commands) {
448
+ for (const command of remainingCommands) {
200
449
  const result = emception.run(command.join(" "));
201
450
  if (result.returncode !== 0) {
202
451
  throw new Error(
@@ -206,8 +455,20 @@ async function compileWithEmception(options = {}) {
206
455
  }
207
456
 
208
457
  const wasmBytes = new Uint8Array(emception.readFile(wasmOutputPath));
458
+ const linkObjectBytes = new Uint8Array(emception.readFile(linkObjectPath));
209
459
  await writeFile(resolvedOutputPath, wasmBytes);
210
- return { wasmBytes, outputPath: resolvedOutputPath, tempDir };
460
+ return {
461
+ wasmBytes,
462
+ outputPath: resolvedOutputPath,
463
+ tempDir,
464
+ guestLink: {
465
+ format: "wasm-object",
466
+ language,
467
+ symbolPrefix: guestLink.prefix,
468
+ methodSymbols: guestLink.methodSymbols,
469
+ objectBytes: linkObjectBytes,
470
+ },
471
+ };
211
472
  } finally {
212
473
  try {
213
474
  removeEmceptionDirectory(emception, workDir);
@@ -275,6 +536,8 @@ export async function compileModuleFromSource(options = {}) {
275
536
  noEntry: includeCommandMain !== true,
276
537
  };
277
538
  const result = await compileWithEmception({
539
+ manifest,
540
+ language: compiler.language,
278
541
  sourceCompilerCommand: compiler.command,
279
542
  sourceExtension: compiler.extension,
280
543
  sourceCode,
@@ -301,6 +564,7 @@ export async function compileModuleFromSource(options = {}) {
301
564
  outputPath: resolvedOutputPath,
302
565
  tempDir,
303
566
  wasmBytes,
567
+ guestLink: result.guestLink,
304
568
  manifestWarnings: warnings,
305
569
  report,
306
570
  };
@@ -383,35 +647,100 @@ export async function protectModuleArtifact(options = {}) {
383
647
  authorization: signedAuthorization,
384
648
  };
385
649
 
386
- let encryptedEnvelope = null;
387
- if (options.recipientPublicKeyHex) {
388
- encryptedEnvelope = await encryptJsonForRecipient({
389
- payload,
390
- recipientPublicKey: hexToBytes(options.recipientPublicKeyHex),
391
- context: "space-data-module-sdk/package",
392
- });
393
- }
394
-
395
650
  let singleFileBundle = null;
396
651
  if (options.singleFileBundle === true) {
652
+ const additionalEntries = [
653
+ ...(Array.isArray(options.bundleEntries) ? options.bundleEntries : []),
654
+ ...createGuestLinkBundleEntries(options.guestLink),
655
+ ];
397
656
  singleFileBundle = await createSingleFileBundle({
398
657
  wasmBytes,
399
658
  manifest,
400
659
  authorization: signedAuthorization,
401
- transportEnvelope: encryptedEnvelope,
402
- entries: options.bundleEntries,
660
+ entries: additionalEntries,
403
661
  });
404
662
  }
405
663
 
664
+ const bundleBytes = singleFileBundle?.wasmBytes ?? wasmBytes;
665
+ let publicationNotice = null;
666
+ let publicationRecordsBytes = null;
667
+ let protectedArtifactBytes = null;
668
+ let encryptedEnvelope = null;
669
+
670
+ if (options.recipientPublicKeyHex) {
671
+ const encryptedBase = await encryptBytesForRecipient({
672
+ plaintext: bundleBytes,
673
+ recipientPublicKey: hexToBytes(options.recipientPublicKeyHex),
674
+ context: "space-data-module-sdk/package",
675
+ rootType: "WASM",
676
+ });
677
+ const encryptedBaseBytes = base64ToBytes(encryptedBase.protectedBlobBase64);
678
+ const parsedEncryptedBase = extractPublicationRecordCollection(encryptedBaseBytes);
679
+ publicationNotice = await createPublicationNotice({
680
+ payloadBytes: parsedEncryptedBase.payloadBytes,
681
+ artifactId,
682
+ programId,
683
+ fileName: `${artifactId}.wasm`,
684
+ fileId: programId,
685
+ signer,
686
+ });
687
+ publicationRecordsBytes = encodePublicationRecordCollection({
688
+ enc: parsedEncryptedBase.enc,
689
+ pnm: publicationNotice,
690
+ });
691
+ protectedArtifactBytes = appendPublicationRecordCollection(
692
+ parsedEncryptedBase.payloadBytes,
693
+ publicationRecordsBytes,
694
+ );
695
+ encryptedEnvelope = createEncryptedEnvelopePayload({
696
+ protectedBlobBytes: protectedArtifactBytes,
697
+ parsedProtectedBlob: {
698
+ payloadBytes: parsedEncryptedBase.payloadBytes,
699
+ recordCollectionBytes: publicationRecordsBytes,
700
+ enc: parsedEncryptedBase.enc,
701
+ pnm: publicationNotice,
702
+ },
703
+ enc: parsedEncryptedBase.enc,
704
+ context: parsedEncryptedBase.enc?.context,
705
+ });
706
+ } else {
707
+ publicationNotice = await createPublicationNotice({
708
+ payloadBytes: bundleBytes,
709
+ artifactId,
710
+ programId,
711
+ fileName: `${artifactId}.wasm`,
712
+ fileId: programId,
713
+ signer,
714
+ });
715
+ publicationRecordsBytes = encodePublicationRecordCollection({
716
+ pnm: publicationNotice,
717
+ });
718
+ protectedArtifactBytes = appendPublicationRecordCollection(
719
+ bundleBytes,
720
+ publicationRecordsBytes,
721
+ );
722
+ }
723
+
724
+ if (singleFileBundle) {
725
+ singleFileBundle = {
726
+ ...singleFileBundle,
727
+ wasmBytes: protectedArtifactBytes,
728
+ };
729
+ }
730
+
406
731
  return {
407
732
  mnemonic: identity.mnemonic,
408
733
  signingPublicKeyHex: bytesToHex(identity.signingKey.publicKey),
409
734
  signingPath: identity.signingKey.path,
410
735
  payload,
411
- encrypted: Boolean(encryptedEnvelope),
736
+ publicationNotice,
737
+ publicationRecordsBytes,
738
+ protectedArtifactBytes,
739
+ protectedArtifactBase64: bytesToBase64(protectedArtifactBytes),
740
+ encrypted: Boolean(options.recipientPublicKeyHex),
412
741
  encryptedEnvelope,
413
742
  singleFileBundle,
414
- bundledWasmBytes: singleFileBundle?.wasmBytes ?? null,
743
+ bundledWasmBytes: singleFileBundle?.wasmBytes ?? protectedArtifactBytes,
415
744
  };
416
745
  }
417
746