@nexart/codemode-sdk 1.8.3 → 1.9.0

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 (150) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +121 -2
  3. package/dist/cjs/browser.cjs +3077 -0
  4. package/dist/cjs/browser.js +3042 -0
  5. package/dist/cjs/core.cjs +1998 -0
  6. package/dist/cjs/core.js +1966 -0
  7. package/dist/cjs/node.cjs +3245 -0
  8. package/dist/cjs/node.js +3208 -0
  9. package/dist/esm/browser.cjs +3077 -0
  10. package/dist/esm/browser.js +3042 -0
  11. package/dist/esm/core.cjs +1998 -0
  12. package/dist/esm/core.js +1966 -0
  13. package/dist/esm/node.cjs +3245 -0
  14. package/dist/esm/node.js +3208 -0
  15. package/dist/types/sdk/codemode/attestation.d.ts +24 -0
  16. package/dist/types/sdk/codemode/attestation.d.ts.map +1 -0
  17. package/dist/types/sdk/codemode/builder-manifest.d.ts.map +1 -0
  18. package/dist/types/sdk/codemode/canonicalJson.d.ts +16 -0
  19. package/dist/types/sdk/codemode/canonicalJson.d.ts.map +1 -0
  20. package/dist/{sdk → types/sdk}/codemode/core-index.d.ts +5 -1
  21. package/dist/types/sdk/codemode/core-index.d.ts.map +1 -0
  22. package/dist/types/sdk/codemode/engine.d.ts.map +1 -0
  23. package/dist/{sdk → types/sdk}/codemode/entry/browser.d.ts +1 -1
  24. package/dist/types/sdk/codemode/entry/browser.d.ts.map +1 -0
  25. package/dist/types/sdk/codemode/entry/node.d.ts.map +1 -0
  26. package/dist/types/sdk/codemode/execute.d.ts.map +1 -0
  27. package/dist/types/sdk/codemode/execution-sandbox.d.ts.map +1 -0
  28. package/dist/types/sdk/codemode/loop-engine.d.ts.map +1 -0
  29. package/dist/types/sdk/codemode/nodeReceipt.d.ts +65 -0
  30. package/dist/types/sdk/codemode/nodeReceipt.d.ts.map +1 -0
  31. package/dist/types/sdk/codemode/p5-runtime.d.ts.map +1 -0
  32. package/dist/{sdk → types/sdk}/codemode/runtime.d.ts +1 -1
  33. package/dist/types/sdk/codemode/runtime.d.ts.map +1 -0
  34. package/dist/types/sdk/codemode/snapshot.d.ts +72 -0
  35. package/dist/types/sdk/codemode/snapshot.d.ts.map +1 -0
  36. package/dist/types/sdk/codemode/sound-bridge.d.ts.map +1 -0
  37. package/dist/types/sdk/codemode/soundart-engine.d.ts.map +1 -0
  38. package/dist/types/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +1 -0
  39. package/dist/types/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +1 -0
  40. package/dist/types/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +1 -0
  41. package/dist/types/sdk/codemode/soundart-sketches/index.d.ts.map +1 -0
  42. package/dist/types/sdk/codemode/soundart-sketches/isoflow.d.ts.map +1 -0
  43. package/dist/types/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +1 -0
  44. package/dist/types/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +1 -0
  45. package/dist/types/sdk/codemode/soundart-sketches/orb.d.ts.map +1 -0
  46. package/dist/types/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +1 -0
  47. package/dist/types/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +1 -0
  48. package/dist/types/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +1 -0
  49. package/dist/types/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +1 -0
  50. package/dist/types/sdk/codemode/soundart-sketches/rings.d.ts.map +1 -0
  51. package/dist/types/sdk/codemode/soundart-sketches/squares.d.ts.map +1 -0
  52. package/dist/types/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +1 -0
  53. package/dist/types/sdk/codemode/static-engine.d.ts.map +1 -0
  54. package/dist/{sdk → types/sdk}/codemode/types.d.ts +96 -0
  55. package/dist/types/sdk/codemode/types.d.ts.map +1 -0
  56. package/dist/{sdk → types/sdk}/codemode/version.d.ts +2 -2
  57. package/dist/types/sdk/codemode/version.d.ts.map +1 -0
  58. package/dist/types/shared/soundSnapshot.d.ts.map +1 -0
  59. package/examples/sketch-minimal.js +27 -0
  60. package/examples/sketch-vars.js +59 -0
  61. package/examples/sketch.js +24 -0
  62. package/package.json +29 -23
  63. package/dist/sdk/codemode/builder-manifest.d.ts.map +0 -1
  64. package/dist/sdk/codemode/builder-manifest.js +0 -97
  65. package/dist/sdk/codemode/core-index.d.ts.map +0 -1
  66. package/dist/sdk/codemode/core-index.js +0 -28
  67. package/dist/sdk/codemode/engine.d.ts.map +0 -1
  68. package/dist/sdk/codemode/engine.js +0 -67
  69. package/dist/sdk/codemode/entry/browser.d.ts.map +0 -1
  70. package/dist/sdk/codemode/entry/browser.js +0 -69
  71. package/dist/sdk/codemode/entry/node.d.ts.map +0 -1
  72. package/dist/sdk/codemode/entry/node.js +0 -35
  73. package/dist/sdk/codemode/execute.d.ts.map +0 -1
  74. package/dist/sdk/codemode/execute.js +0 -283
  75. package/dist/sdk/codemode/execution-sandbox.d.ts.map +0 -1
  76. package/dist/sdk/codemode/execution-sandbox.js +0 -207
  77. package/dist/sdk/codemode/loop-engine.d.ts.map +0 -1
  78. package/dist/sdk/codemode/loop-engine.js +0 -229
  79. package/dist/sdk/codemode/p5-runtime.d.ts.map +0 -1
  80. package/dist/sdk/codemode/p5-runtime.js +0 -1033
  81. package/dist/sdk/codemode/runtime.d.ts.map +0 -1
  82. package/dist/sdk/codemode/runtime.js +0 -220
  83. package/dist/sdk/codemode/sound-bridge.d.ts.map +0 -1
  84. package/dist/sdk/codemode/sound-bridge.js +0 -128
  85. package/dist/sdk/codemode/soundart-engine.d.ts.map +0 -1
  86. package/dist/sdk/codemode/soundart-engine.js +0 -173
  87. package/dist/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +0 -1
  88. package/dist/sdk/codemode/soundart-sketches/chladniBloom.js +0 -53
  89. package/dist/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +0 -1
  90. package/dist/sdk/codemode/soundart-sketches/dualVortex.js +0 -67
  91. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +0 -1
  92. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.js +0 -89
  93. package/dist/sdk/codemode/soundart-sketches/index.d.ts.map +0 -1
  94. package/dist/sdk/codemode/soundart-sketches/index.js +0 -72
  95. package/dist/sdk/codemode/soundart-sketches/isoflow.d.ts.map +0 -1
  96. package/dist/sdk/codemode/soundart-sketches/isoflow.js +0 -60
  97. package/dist/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +0 -1
  98. package/dist/sdk/codemode/soundart-sketches/loomWeave.js +0 -59
  99. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +0 -1
  100. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.js +0 -53
  101. package/dist/sdk/codemode/soundart-sketches/orb.d.ts.map +0 -1
  102. package/dist/sdk/codemode/soundart-sketches/orb.js +0 -50
  103. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +0 -1
  104. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.js +0 -72
  105. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +0 -1
  106. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.js +0 -51
  107. package/dist/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +0 -1
  108. package/dist/sdk/codemode/soundart-sketches/radialBurst.js +0 -60
  109. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +0 -1
  110. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.js +0 -89
  111. package/dist/sdk/codemode/soundart-sketches/rings.d.ts.map +0 -1
  112. package/dist/sdk/codemode/soundart-sketches/rings.js +0 -89
  113. package/dist/sdk/codemode/soundart-sketches/squares.d.ts.map +0 -1
  114. package/dist/sdk/codemode/soundart-sketches/squares.js +0 -52
  115. package/dist/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +0 -1
  116. package/dist/sdk/codemode/soundart-sketches/waveStripes.js +0 -44
  117. package/dist/sdk/codemode/static-engine.d.ts.map +0 -1
  118. package/dist/sdk/codemode/static-engine.js +0 -157
  119. package/dist/sdk/codemode/types.d.ts.map +0 -1
  120. package/dist/sdk/codemode/types.js +0 -34
  121. package/dist/sdk/codemode/version.d.ts.map +0 -1
  122. package/dist/sdk/codemode/version.js +0 -17
  123. package/dist/shared/soundSnapshot.d.ts.map +0 -1
  124. package/dist/shared/soundSnapshot.js +0 -128
  125. /package/dist/{sdk → types/sdk}/codemode/builder-manifest.d.ts +0 -0
  126. /package/dist/{sdk → types/sdk}/codemode/engine.d.ts +0 -0
  127. /package/dist/{sdk → types/sdk}/codemode/entry/node.d.ts +0 -0
  128. /package/dist/{sdk → types/sdk}/codemode/execute.d.ts +0 -0
  129. /package/dist/{sdk → types/sdk}/codemode/execution-sandbox.d.ts +0 -0
  130. /package/dist/{sdk → types/sdk}/codemode/loop-engine.d.ts +0 -0
  131. /package/dist/{sdk → types/sdk}/codemode/p5-runtime.d.ts +0 -0
  132. /package/dist/{sdk → types/sdk}/codemode/sound-bridge.d.ts +0 -0
  133. /package/dist/{sdk → types/sdk}/codemode/soundart-engine.d.ts +0 -0
  134. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/chladniBloom.d.ts +0 -0
  135. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/dualVortex.d.ts +0 -0
  136. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/geometryIllusion.d.ts +0 -0
  137. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/index.d.ts +0 -0
  138. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/isoflow.d.ts +0 -0
  139. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/loomWeave.d.ts +0 -0
  140. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/noiseTerraces.d.ts +0 -0
  141. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/orb.d.ts +0 -0
  142. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/pixelGlyphs.d.ts +0 -0
  143. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/prismFlowFields.d.ts +0 -0
  144. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/radialBurst.d.ts +0 -0
  145. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/resonantSoundBodies.d.ts +0 -0
  146. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/rings.d.ts +0 -0
  147. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/squares.d.ts +0 -0
  148. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/waveStripes.d.ts +0 -0
  149. /package/dist/{sdk → types/sdk}/codemode/static-engine.d.ts +0 -0
  150. /package/dist/{shared → types/shared}/soundSnapshot.d.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,95 @@ All notable changes to @nexart/codemode-sdk will be documented in this file.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.9.0] — 2026-02-25
8
+
9
+ ### Added — Attestation Parity (Reason Codes + Signed Receipt Verification)
10
+
11
+ **Feature Release — Additive only. No existing APIs changed.**
12
+
13
+ Brings the Code Mode SDK attestation layer to parity with `@nexart/ai-execution` v0.5.0.
14
+
15
+ #### New Types (`types.ts`)
16
+ - `CodeVerifyCode` — stable string-valued reason code enum (12 codes)
17
+ - `CodeVerificationResult` — `{ ok, code, details? }` returned by all verification functions
18
+ - `NodeReceiptVerifyResult` — alias of `CodeVerificationResult` for receipt-specific call sites
19
+ - `NodeKeysDocument` — shape of `/.well-known/nexart-node.json`
20
+ - `SignedAttestationReceipt` — payload signed by the attestation node
21
+ - `CodeAttestationReceipt` — normalised receipt extracted from any bundle layout
22
+
23
+ #### New Module — `nodeReceipt.ts`
24
+ - `verifyNodeReceiptSignature({ receipt, signatureB64Url, key })` — offline Ed25519 verification (browser + Node, @noble/ed25519)
25
+ - `fetchNodeKeys(nodeUrl)` — fetch `NodeKeysDocument` from attestation node
26
+ - `selectNodeKey(doc, kid?)` — select key by explicit kid → activeKid → first
27
+ - `verifyBundleAttestation(bundle, { nodeUrl, kid? })` — full offline attestation check with certificateHash cross-check (receipt-swap prevention), all three bundle layouts supported
28
+
29
+ #### New Module — `attestation.ts`
30
+ - `getAttestationReceipt(bundle)` — normalised receipt or null; recognises Layout A (top-level), Layout B (nested envelope), Layout C (legacy flat fields)
31
+ - `hasAttestation(bundle)` — boolean shorthand
32
+
33
+ #### New Module — `canonicalJson.ts`
34
+ - `toCanonicalJson(value)` — deterministic sorted-key JSON serialisation used for signing/verifying receipt bytes
35
+
36
+ #### New Dependencies
37
+ - `@noble/ed25519` ^3.0.0 (browser-compatible Ed25519) — declared in `dependencies` (installed per-package, no hoisting required)
38
+ - `@noble/hashes` ^1.7.0 (sha512 for noble ed25519) — same
39
+ - `@types/node` ^20.0.0 (dev) — added to `devDependencies` so `Buffer`/`process`/`require` resolve correctly under TypeScript strict mode; prevents "works only when hoisted" failures
40
+
41
+ #### TypeScript Config
42
+ - `"types": ["node"]` added to both `tsconfig.json` and `tsconfig.types.json` `compilerOptions`
43
+ - `nodeReceipt.ts`, `attestation.ts`, `canonicalJson.ts` added explicitly to both `files` arrays
44
+
45
+ #### Tests
46
+ - `tests/attestation.test.ts` — 25 tests covering all code paths, error cases, and all three bundle layouts; `npm run test:attestation`
47
+
48
+ #### Fixtures
49
+ - `fixtures/attestation/keys-v1.json`
50
+ - `fixtures/attestation/receipt-v1.json`
51
+ - `fixtures/attestation/receipt-v1.sig`
52
+ - `fixtures/attestation/receipt-v1.pub`
53
+
54
+ #### Docs
55
+ - README: new Attestation & Verification section with reason codes, `verifyBundleAttestation` usage, node keys endpoint, and receipt extraction helpers
56
+
57
+ #### Unchanged
58
+ - No protocol changes (v1.2.0)
59
+ - No changes to existing hashing or canonicalisation semantics
60
+ - All existing `executeCodeMode`, `createRuntime`, `validateSnapshot` APIs unchanged
61
+ - All existing smoke tests and execution boundary tests pass
62
+
63
+ ---
64
+
65
+ ## [1.8.4] — 2026-01-25
66
+
67
+ ### Fixed — Node ESM Compatibility
68
+
69
+ **Patch Release — Packaging Fix Only**
70
+
71
+ This release fixes Node.js ESM compatibility by switching to dual build output (ESM + CJS) via tsup.
72
+
73
+ #### Breaking Change (Internal Only)
74
+ - Dist structure changed from `dist/entry/*.js` to `dist/esm/*.js` and `dist/cjs/*.js`
75
+ - Types now in `dist/types/`
76
+ - Exports map updated to support both `import` and `require`
77
+
78
+ #### Fixes
79
+ - Node ESM: Imports now resolve correctly (no more missing .js extension errors)
80
+ - Node CJS: `require('@nexart/codemode-sdk')` now works
81
+ - Bundlers: Continue to work via exports map
82
+
83
+ #### Build Changes
84
+ - Added tsup for dual ESM/CJS bundling
85
+ - Types emitted separately via tsc
86
+ - New smoke tests: `scripts/smoke-node.mjs` and `scripts/smoke-node.cjs`
87
+
88
+ #### Unchanged
89
+ - No protocol changes (v1.2.0)
90
+ - No runtime API changes
91
+ - No behavior changes
92
+ - Full backward compatibility with v1.8.3 API
93
+
94
+ ---
95
+
7
96
  ## [1.8.3] — 2026-01-25
8
97
 
9
98
  ### Changed — CLI v0.2 Remote Renderer + Version Bump
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @nexart/codemode-sdk
2
2
 
3
- **Version: 1.8.3 (Protocol v1.2.0)**
3
+ **Version: 1.9.0 (Protocol v1.2.0)**
4
4
 
5
5
  A deterministic execution runtime for reproducible, verifiable computation.
6
6
 
@@ -33,6 +33,43 @@ A deterministic execution runtime for reproducible, verifiable computation.
33
33
 
34
34
  ---
35
35
 
36
+ ## Quickstart: CLI Examples
37
+
38
+ The SDK includes ready-to-run example sketches. Use the `@nexart/cli` to render them via the canonical renderer.
39
+
40
+ ### Setup
41
+
42
+ ```bash
43
+ # Set up authentication for remote rendering
44
+ export NEXART_RENDERER_ENDPOINT=https://nexart-canonical-renderer-production.up.railway.app
45
+ export NEXART_API_KEY=nx_live_your_key_here
46
+ ```
47
+
48
+ ### Run Examples
49
+
50
+ ```bash
51
+ # Run the main example sketch
52
+ npx @nexart/cli run ./examples/sketch.js --seed 12345 --include-code --out ./out.png
53
+
54
+ # Verify the output is deterministic
55
+ npx @nexart/cli verify ./out.snapshot.json
56
+ # Output: [nexart] Result: PASS
57
+ ```
58
+
59
+ ### Example Sketches
60
+
61
+ | File | Description |
62
+ |------|-------------|
63
+ | `examples/sketch.js` | Main example — VAR controls + random palette, protocol-safe |
64
+ | `examples/sketch-minimal.js` | Simple shapes, no randomness — identical every run |
65
+ | `examples/sketch-vars.js` | Uses VAR + random() — demonstrates determinism |
66
+
67
+ ### Canonical Size
68
+
69
+ The canonical renderer enforces a fixed canvas size of **1950x2400**. Do not pass custom `--width` or `--height` to the canonical endpoint — the size is enforced server-side for consistent, verifiable output.
70
+
71
+ ---
72
+
36
73
  ## What This SDK Does
37
74
 
38
75
  This SDK provides a **deterministic runtime layer** for executing code that must produce identical output given identical inputs — across environments, over time, and under verification.
@@ -236,7 +273,7 @@ These are examples — the SDK is designed for any system requiring deterministi
236
273
  |----------|-------|
237
274
  | Protocol Version | v1.2.0 |
238
275
  | Status | **STABLE** |
239
- | SDK Version | 1.8.3 |
276
+ | SDK Version | 1.8.4 |
240
277
 
241
278
  **Core protocol surface is frozen. Breaking changes require v2.0.0.**
242
279
 
@@ -434,6 +471,88 @@ The following are rejected with `[Code Mode Protocol Error]`:
434
471
 
435
472
  ---
436
473
 
474
+ ## Attestation & Verification (v1.9.0)
475
+
476
+ ### Verification Reason Codes
477
+
478
+ Every verification function returns a `CodeVerificationResult` with a stable, machine-readable `code`:
479
+
480
+ ```typescript
481
+ import { CodeVerifyCode } from '@nexart/codemode-sdk';
482
+
483
+ // Stable string codes — safe to switch on, persist to storage, or compare:
484
+ // CodeVerifyCode.OK
485
+ // CodeVerifyCode.CERTIFICATE_HASH_MISMATCH
486
+ // CodeVerifyCode.SNAPSHOT_HASH_MISMATCH
487
+ // CodeVerifyCode.RENDER_HASH_MISMATCH
488
+ // CodeVerifyCode.INVALID_SHA256_FORMAT
489
+ // CodeVerifyCode.CANONICALIZATION_ERROR
490
+ // CodeVerifyCode.SCHEMA_ERROR
491
+ // CodeVerifyCode.NODE_RECEIPT_MISSING
492
+ // CodeVerifyCode.NODE_RECEIPT_KEY_NOT_FOUND
493
+ // CodeVerifyCode.NODE_RECEIPT_INVALID_SIGNATURE
494
+ // CodeVerifyCode.NODE_RECEIPT_KEY_FORMAT_UNSUPPORTED
495
+ // CodeVerifyCode.UNKNOWN_ERROR
496
+ ```
497
+
498
+ ### Signed Node Receipt Verification (offline)
499
+
500
+ Verify that a Code Mode snapshot bundle was attested by an authorised NexArt attestation node — entirely offline, browser and Node.js compatible:
501
+
502
+ ```typescript
503
+ import { verifyBundleAttestation } from '@nexart/codemode-sdk';
504
+
505
+ const res = await verifyBundleAttestation(bundle, { nodeUrl: 'https://node.nexart.dev' });
506
+ console.log(res.ok, res.code);
507
+ // true "OK"
508
+ // false "CERTIFICATE_HASH_MISMATCH" (receipt doesn't match bundle)
509
+ // false "NODE_RECEIPT_INVALID_SIGNATURE" (tampered receipt)
510
+ ```
511
+
512
+ `verifyBundleAttestation` performs these steps automatically:
513
+ 1. Extracts the signed receipt and signature from the bundle (top-level or nested layout).
514
+ 2. Cross-checks `receipt.certificateHash === bundle.certificateHash` to prevent receipt-swapping attacks.
515
+ 3. Fetches the node's public keys from `/.well-known/nexart-node.json`.
516
+ 4. Selects the appropriate key (by `kid`, `activeKid`, or first available).
517
+ 5. Verifies the Ed25519 signature over the canonical JSON bytes of the receipt.
518
+
519
+ ### Node Keys Endpoint
520
+
521
+ Public keys are discovered automatically from:
522
+
523
+ ```
524
+ GET {nodeUrl}/.well-known/nexart-node.json
525
+ ```
526
+
527
+ You can also fetch or inspect keys directly:
528
+
529
+ ```typescript
530
+ import { fetchNodeKeys, selectNodeKey } from '@nexart/codemode-sdk';
531
+
532
+ const doc = await fetchNodeKeys('https://node.nexart.dev');
533
+ const { key } = selectNodeKey(doc, 'kid-optional');
534
+ ```
535
+
536
+ ### Receipt Extraction Helpers
537
+
538
+ Inspect bundles without performing full verification:
539
+
540
+ ```typescript
541
+ import { getAttestationReceipt, hasAttestation } from '@nexart/codemode-sdk';
542
+
543
+ if (hasAttestation(bundle)) {
544
+ const receipt = getAttestationReceipt(bundle);
545
+ console.log(receipt?.attestationId, receipt?.nodeId);
546
+ }
547
+ ```
548
+
549
+ Both helpers recognise:
550
+ - **Layout A** — `bundle.receipt` + `bundle.signature` (signed envelope)
551
+ - **Layout B** — `bundle.attestation.receipt` + `bundle.attestation.signature` (nested envelope)
552
+ - **Layout C** — legacy flat fields (`bundle.attestationId`, `bundle.nodeRuntimeHash`, …)
553
+
554
+ ---
555
+
437
556
  ## Examples
438
557
 
439
558
  ```bash