@dignetwork/dig-sdk 0.1.0 → 0.2.1

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 (40) hide show
  1. package/README.md +159 -11
  2. package/dist/adapters.cjs +56 -15
  3. package/dist/adapters.cjs.map +1 -1
  4. package/dist/adapters.d.cts +2 -2
  5. package/dist/adapters.d.ts +2 -2
  6. package/dist/adapters.js +56 -15
  7. package/dist/adapters.js.map +1 -1
  8. package/dist/{dev-shim-Bgw8X2E9.d.cts → dev-shim-DfKRA1ok.d.cts} +13 -3
  9. package/dist/{dev-shim-Bgw8X2E9.d.ts → dev-shim-DfKRA1ok.d.ts} +13 -3
  10. package/dist/{dig-client-entry-BIzuZ7Rs.d.cts → dig-client-entry-Bc95rd_t.d.cts} +120 -7
  11. package/dist/{dig-client-entry-BIzuZ7Rs.d.ts → dig-client-entry-Bc95rd_t.d.ts} +120 -7
  12. package/dist/dig-client.cjs +163 -43
  13. package/dist/dig-client.cjs.map +1 -1
  14. package/dist/dig-client.d.cts +1 -1
  15. package/dist/dig-client.d.ts +1 -1
  16. package/dist/dig-client.js +163 -43
  17. package/dist/dig-client.js.map +1 -1
  18. package/dist/index.cjs +495 -56
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +313 -3
  21. package/dist/index.d.ts +313 -3
  22. package/dist/index.js +489 -57
  23. package/dist/index.js.map +1 -1
  24. package/dist/next.cjs +58 -15
  25. package/dist/next.cjs.map +1 -1
  26. package/dist/next.d.cts +1 -1
  27. package/dist/next.d.ts +1 -1
  28. package/dist/next.js +58 -15
  29. package/dist/next.js.map +1 -1
  30. package/dist/vite.cjs +58 -15
  31. package/dist/vite.cjs.map +1 -1
  32. package/dist/vite.d.cts +1 -1
  33. package/dist/vite.d.ts +1 -1
  34. package/dist/vite.js +58 -15
  35. package/dist/vite.js.map +1 -1
  36. package/package.json +5 -6
  37. package/vendor/PROVENANCE.md +0 -102
  38. package/vendor/dig_client.d.ts +0 -150
  39. package/vendor/dig_client.mjs +0 -805
  40. package/vendor/dig_client_bg.wasm +0 -0
package/dist/index.cjs CHANGED
@@ -20,6 +20,75 @@ var SIGN_METHODS = [
20
20
  ];
21
21
  var DEFAULT_CHAIN = "chia:mainnet";
22
22
 
23
+ // src/errors.ts
24
+ var DIG_SDK_ERROR_CODES = Object.freeze({
25
+ // ---- provider / connect (provider/chia-provider.ts, provider/*) ----
26
+ /** WalletConnect was requested/needed but no `walletConnect` options were supplied. */
27
+ WC_OPTIONS_REQUIRED: "WC_OPTIONS_REQUIRED",
28
+ /** `mode: "injected"` (or the injected leg of `auto`) found no usable `window.chia`. */
29
+ NO_INJECTED_WALLET: "NO_INJECTED_WALLET",
30
+ /** The optional `@walletconnect/sign-client` peer dependency is not installed/usable. */
31
+ WC_DEPENDENCY_MISSING: "WC_DEPENDENCY_MISSING",
32
+ /** The active wallet session/transport does not grant the requested method. */
33
+ METHOD_NOT_SUPPORTED: "METHOD_NOT_SUPPORTED",
34
+ /** A wallet RPC timed out (e.g. Sage did not respond within the per-request timeout). */
35
+ WALLET_TIMEOUT: "WALLET_TIMEOUT",
36
+ /** The wallet returned no public keys / no key to sign with. */
37
+ WALLET_NO_KEYS: "WALLET_NO_KEYS",
38
+ // ---- read-crypto / RPC (dig-client.ts, loader.ts) ----
39
+ /** A content read needs a confirmed on-chain root and none was supplied/derivable. */
40
+ ROOT_REQUIRED: "ROOT_REQUIRED",
41
+ /** The resource did not decrypt+authenticate under this URN (wrong key/salt, or a decoy). */
42
+ DECRYPT_FAILED: "DECRYPT_FAILED",
43
+ /** The dig RPC could not be reached (network/transport failure). */
44
+ RPC_TRANSPORT: "RPC_TRANSPORT",
45
+ /** The dig RPC responded with an HTTP error or a JSON-RPC `error` object. */
46
+ RPC_ERROR: "RPC_ERROR",
47
+ /** The dig RPC returned a malformed / inconsistent payload (e.g. chunk-length mismatch). */
48
+ RPC_MALFORMED_RESPONSE: "RPC_MALFORMED_RESPONSE",
49
+ /** The read-crypto wasm failed its SRI integrity check — fail closed. */
50
+ WASM_INTEGRITY: "WASM_INTEGRITY",
51
+ /** The read-crypto wasm could not be loaded (fetch/resolve failure). */
52
+ WASM_LOAD_FAILED: "WASM_LOAD_FAILED",
53
+ // ---- paywall / spends (paywall.ts) ----
54
+ /** The canonical chip35 wasm builder for this operation is unavailable (never hand-rolled). */
55
+ SPEND_BUILDER_UNAVAILABLE: "SPEND_BUILDER_UNAVAILABLE",
56
+ /** No secure random source was available to generate a payment nonce. */
57
+ NO_SECURE_RANDOM: "NO_SECURE_RANDOM",
58
+ // ---- deploy / adapters (adapters/run.ts, adapters/deploy.ts) ----
59
+ /** The `digstore` binary could not be spawned (not installed / not on PATH). */
60
+ DIGSTORE_NOT_FOUND: "DIGSTORE_NOT_FOUND",
61
+ /** `digstore deploy` exited non-zero. */
62
+ DEPLOY_FAILED: "DEPLOY_FAILED",
63
+ /** `digstore deploy --json` output could not be parsed into a capsule result. */
64
+ DEPLOY_OUTPUT_UNPARSEABLE: "DEPLOY_OUTPUT_UNPARSEABLE",
65
+ // ---- argument validation (shared) ----
66
+ /** An argument was malformed (e.g. a non-hex string, a bad URN, mutually-exclusive options). */
67
+ INVALID_ARGUMENT: "INVALID_ARGUMENT"
68
+ });
69
+ var DIG_SDK_ERROR_BRAND = "__dignetwork_dig_sdk_error__";
70
+ var DigSdkError = class _DigSdkError extends Error {
71
+ constructor(code, message, context = {}, options = {}) {
72
+ super(message);
73
+ this.name = "DigSdkError";
74
+ this.code = code;
75
+ this.context = context;
76
+ if (options.cause !== void 0) {
77
+ this.cause = options.cause;
78
+ }
79
+ Object.defineProperty(this, DIG_SDK_ERROR_BRAND, { value: true, enumerable: false });
80
+ Object.setPrototypeOf(this, _DigSdkError.prototype);
81
+ }
82
+ /** A JSON-friendly view of the error: `{ code, message, context }`. */
83
+ toJSON() {
84
+ return { code: this.code, message: this.message, context: this.context };
85
+ }
86
+ };
87
+ function isDigSdkError(e, code) {
88
+ const branded = e instanceof DigSdkError || typeof e === "object" && e !== null && e[DIG_SDK_ERROR_BRAND] === true;
89
+ return branded && (code === void 0 || e.code === code);
90
+ }
91
+
23
92
  // src/provider/injected.ts
24
93
  function getInjectedProvider() {
25
94
  const g = globalThis;
@@ -52,7 +121,11 @@ var InjectedTransport = class {
52
121
  }
53
122
  async request(method, params) {
54
123
  if (!this.supports(method)) {
55
- throw new Error(`The DIG Browser wallet does not support "${method}".`);
124
+ throw new DigSdkError(
125
+ "METHOD_NOT_SUPPORTED",
126
+ `The DIG Browser wallet does not support "${method}".`,
127
+ { method }
128
+ );
56
129
  }
57
130
  return this.provider.request({ method, params });
58
131
  }
@@ -136,8 +209,10 @@ var WalletConnectTransport = class _WalletConnectTransport {
136
209
  }
137
210
  async request(method, params) {
138
211
  if (!this.supports(method)) {
139
- throw new Error(
140
- `Your wallet session does not grant "${method}". Disconnect and reconnect your wallet to refresh the session (and make sure Sage is up to date).`
212
+ throw new DigSdkError(
213
+ "METHOD_NOT_SUPPORTED",
214
+ `Your wallet session does not grant "${method}". Disconnect and reconnect your wallet to refresh the session (and make sure Sage is up to date).`,
215
+ { method }
141
216
  );
142
217
  }
143
218
  const MAX_ATTEMPTS = 3;
@@ -160,7 +235,13 @@ var WalletConnectTransport = class _WalletConnectTransport {
160
235
  let t;
161
236
  const timeout = new Promise((_, rej) => {
162
237
  t = setTimeout(
163
- () => rej(new Error("Sage did not respond \u2014 open the Sage app and try again.")),
238
+ () => rej(
239
+ new DigSdkError(
240
+ "WALLET_TIMEOUT",
241
+ "Sage did not respond \u2014 open the Sage app and try again.",
242
+ { method, timeoutMs: this.requestTimeoutMs }
243
+ )
244
+ ),
164
245
  this.requestTimeoutMs
165
246
  );
166
247
  });
@@ -190,9 +271,12 @@ async function loadSignClient() {
190
271
  throw new Error("unexpected @walletconnect/sign-client shape");
191
272
  }
192
273
  return SignClient;
193
- } catch {
194
- throw new Error(
195
- "WalletConnect fallback requires the optional peer dependency '@walletconnect/sign-client'. Install it (npm i @walletconnect/sign-client) to use the WalletConnect\u2192Sage transport."
274
+ } catch (e) {
275
+ throw new DigSdkError(
276
+ "WC_DEPENDENCY_MISSING",
277
+ "WalletConnect fallback requires the optional peer dependency '@walletconnect/sign-client'. Install it (npm i @walletconnect/sign-client) to use the WalletConnect\u2192Sage transport.",
278
+ {},
279
+ { cause: e }
196
280
  );
197
281
  }
198
282
  }
@@ -229,7 +313,8 @@ async function signMessage(request, supports, message, address) {
229
313
  }
230
314
  const keys = await getPublicKeys(request);
231
315
  const publicKey = keys[0];
232
- if (!publicKey) throw new Error("Wallet returned no keys to sign with.");
316
+ if (!publicKey)
317
+ throw new DigSdkError("WALLET_NO_KEYS", "Wallet returned no keys to sign with.");
233
318
  const sig = normalizeSig(await request("chip0002_signMessage", { message, publicKey }));
234
319
  return { publicKey: sig.publicKey ?? with0x(publicKey), signature: sig.signature };
235
320
  }
@@ -241,7 +326,11 @@ async function signCoinSpends(request, coinSpends) {
241
326
  }
242
327
  async function takeOffer(request, supports, offer, fee = 0) {
243
328
  if (!supports("chia_takeOffer")) {
244
- throw new Error("Your wallet session does not support taking offers. Reconnect your wallet.");
329
+ throw new DigSdkError(
330
+ "METHOD_NOT_SUPPORTED",
331
+ "Your wallet session does not support taking offers. Reconnect your wallet.",
332
+ { method: "chia_takeOffer" }
333
+ );
245
334
  }
246
335
  return request("chia_takeOffer", { offer, fee });
247
336
  }
@@ -325,8 +414,10 @@ var ChiaProvider = class _ChiaProvider {
325
414
  };
326
415
  const tryWalletConnect = async () => {
327
416
  if (!options.walletConnect) {
328
- throw new Error(
329
- "WalletConnect options are required to connect via WalletConnect (pass { walletConnect: { projectId, metadata, onUri } })."
417
+ throw new DigSdkError(
418
+ "WC_OPTIONS_REQUIRED",
419
+ "WalletConnect options are required to connect via WalletConnect (pass { walletConnect: { projectId, metadata, onUri } }).",
420
+ { mode }
330
421
  );
331
422
  }
332
423
  const transport = await WalletConnectTransport.connect({
@@ -338,8 +429,10 @@ var ChiaProvider = class _ChiaProvider {
338
429
  if (mode === "injected") {
339
430
  const injected = await tryInjected();
340
431
  if (!injected) {
341
- throw new Error(
342
- "No injected DIG wallet found. Open this page in the DIG Browser, or use mode 'auto'."
432
+ throw new DigSdkError(
433
+ "NO_INJECTED_WALLET",
434
+ "No injected DIG wallet found. Open this page in the DIG Browser, or use mode 'auto'.",
435
+ { mode, acceptAnyInjected: !!options.acceptAnyInjected }
343
436
  );
344
437
  }
345
438
  return injected;
@@ -414,8 +507,202 @@ function connectWallet(options) {
414
507
  return ChiaProvider.connect(options);
415
508
  }
416
509
 
510
+ // src/paywall.ts
511
+ function strip0x2(hex) {
512
+ return hex.replace(/^0x/i, "").toLowerCase();
513
+ }
514
+ function hexToBytes(hex) {
515
+ const h = strip0x2(hex);
516
+ if (h.length % 2 !== 0 || /[^0-9a-f]/.test(h)) {
517
+ throw new DigSdkError("INVALID_ARGUMENT", `invalid hex string: ${hex}`, { value: hex });
518
+ }
519
+ const out = new Uint8Array(h.length / 2);
520
+ for (let i = 0; i < out.length; i++) out[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
521
+ return out;
522
+ }
523
+ function bytesToHex(bytes) {
524
+ let s = "";
525
+ for (const b of bytes) s += b.toString(16).padStart(2, "0");
526
+ return s;
527
+ }
528
+ async function randomNonce() {
529
+ const out = new Uint8Array(32);
530
+ const webcrypto = globalThis.crypto;
531
+ if (webcrypto?.getRandomValues) {
532
+ webcrypto.getRandomValues(out);
533
+ return out;
534
+ }
535
+ const nodeCrypto = await import('crypto');
536
+ if (!nodeCrypto.webcrypto?.getRandomValues) {
537
+ throw new DigSdkError(
538
+ "NO_SECURE_RANDOM",
539
+ "No secure random source available to generate a payment nonce."
540
+ );
541
+ }
542
+ nodeCrypto.webcrypto.getRandomValues(out);
543
+ return out;
544
+ }
545
+ var Paywall = class {
546
+ constructor(provider, options = {}) {
547
+ this.provider = provider;
548
+ this.injectedSpends = options.spends;
549
+ }
550
+ /**
551
+ * Resolve the canonical monetization spends (injected, or lazily imported + `init()`ed). Memoized.
552
+ * This is the ONLY source of coin-spend construction in the Paywall — there is no fallback that
553
+ * hand-rolls a spend.
554
+ */
555
+ spends() {
556
+ if (this.injectedSpends) {
557
+ if (typeof this.injectedSpends.init === "function") this.injectedSpends.init();
558
+ return Promise.resolve(this.injectedSpends);
559
+ }
560
+ if (!this.spendsReady) {
561
+ this.spendsReady = (async () => {
562
+ const mod = await import(
563
+ /* @vite-ignore */
564
+ '@dignetwork/chip35-dl-coin-wasm'
565
+ );
566
+ if (typeof mod.init === "function") mod.init();
567
+ return mod;
568
+ })().catch((e) => {
569
+ this.spendsReady = void 0;
570
+ throw e;
571
+ });
572
+ }
573
+ return this.spendsReady;
574
+ }
575
+ /** The wallet's first synthetic public key, as bytes (the buyer key the spends are built against). */
576
+ async buyerKey() {
577
+ const keys = await this.provider.getPublicKeys();
578
+ const key = keys?.[0];
579
+ if (!key)
580
+ throw new DigSdkError(
581
+ "WALLET_NO_KEYS",
582
+ "Wallet returned no public keys to build the payment against."
583
+ );
584
+ return hexToBytes(key);
585
+ }
586
+ /** Resolve the 32-byte unlock nonce: explicit hex, else derived from `memo`, else random. */
587
+ async resolveNonce(spends, args) {
588
+ if (args.nonce) return hexToBytes(args.nonce);
589
+ if (args.memo) {
590
+ if (typeof spends.paymentNonce !== "function") {
591
+ throw new DigSdkError(
592
+ "SPEND_BUILDER_UNAVAILABLE",
593
+ "Cannot derive a nonce from `memo`: the chip35 wasm `paymentNonce` is unavailable.",
594
+ { builder: "paymentNonce" }
595
+ );
596
+ }
597
+ return spends.paymentNonce(new TextEncoder().encode(args.memo));
598
+ }
599
+ return randomNonce();
600
+ }
601
+ /**
602
+ * Charge the buyer (the connected wallet) `amount` to pay the dapp `owner`, then push the
603
+ * wasm-built coin spends to the wallet for signing. Returns the wallet signature + the receipt.
604
+ * Builds via the canonical wasm `buildPayment` (XCH) or `buildCatPayment` (CAT) — never hand-rolled.
605
+ */
606
+ async requestPayment(args) {
607
+ const spends = await this.spends();
608
+ const ownerPh = hexToBytes(args.owner);
609
+ const amount = BigInt(args.amount);
610
+ const nonce = await this.resolveNonce(spends, args);
611
+ const buyerKey = await this.buyerKey();
612
+ let built;
613
+ if (args.assetId) {
614
+ if (typeof spends.buildCatPayment !== "function") {
615
+ throw new DigSdkError(
616
+ "SPEND_BUILDER_UNAVAILABLE",
617
+ "chip35 wasm `buildCatPayment` is unavailable \u2014 cannot build a CAT payment (the Paywall never hand-rolls spends).",
618
+ { builder: "buildCatPayment" }
619
+ );
620
+ }
621
+ const cats = await this.provider.getCatCoins(strip0x2(args.assetId), args.coinLimit);
622
+ built = spends.buildCatPayment(buyerKey, cats, ownerPh, amount, nonce);
623
+ } else {
624
+ if (typeof spends.buildPayment !== "function") {
625
+ throw new DigSdkError(
626
+ "SPEND_BUILDER_UNAVAILABLE",
627
+ "chip35 wasm `buildPayment` is unavailable \u2014 cannot build an XCH payment (the Paywall never hand-rolls spends).",
628
+ { builder: "buildPayment" }
629
+ );
630
+ }
631
+ const coins = await this.provider.getXchCoins(args.coinLimit);
632
+ built = spends.buildPayment(buyerKey, coins, ownerPh, amount, nonce, BigInt(args.fee ?? 0));
633
+ }
634
+ const signature = await this.provider.signCoinSpends(built.coinSpends);
635
+ return {
636
+ signature,
637
+ receipt: built.receipt,
638
+ coinSpends: built.coinSpends,
639
+ nonce: bytesToHex(nonce)
640
+ };
641
+ }
642
+ /**
643
+ * Verify an observed on-chain payment unlocks the paywall (pay-to-unlock). Delegates to the wasm
644
+ * `verifyPaymentReceipt`; returns its `{ ok, error? }` verdict. `ok:true` grants access.
645
+ */
646
+ async verifyReceipt(args) {
647
+ const spends = await this.spends();
648
+ if (typeof spends.verifyPaymentReceipt !== "function") {
649
+ throw new DigSdkError(
650
+ "SPEND_BUILDER_UNAVAILABLE",
651
+ "chip35 wasm `verifyPaymentReceipt` is unavailable.",
652
+ { builder: "verifyPaymentReceipt" }
653
+ );
654
+ }
655
+ const asset = args.asset ?? { xch: true };
656
+ const requiredAsset = "assetId" in asset && asset.assetId ? { assetId: hexToBytes(asset.assetId) } : { xch: true };
657
+ const requireNonce = args.nonce ? hexToBytes(args.nonce) : null;
658
+ return spends.verifyPaymentReceipt(
659
+ args.observed,
660
+ hexToBytes(args.owner),
661
+ BigInt(args.minAmount),
662
+ requiredAsset,
663
+ requireNonce
664
+ );
665
+ }
666
+ /**
667
+ * Prove the `owner` holds access: gate on a specific NFT (`nft` launcher id) via the wasm
668
+ * `proveNftOwnership`, or on collection/creator membership (`collection` DID) via
669
+ * `proveCollectionMembership`. Returns `{ ok, proof?, error? }`. The caller still confirms the
670
+ * proof's coin is unspent on-chain for liveness.
671
+ */
672
+ async proveAccess(args) {
673
+ if (args.nft && args.collection) {
674
+ throw new DigSdkError(
675
+ "INVALID_ARGUMENT",
676
+ "proveAccess: pass either `nft` or `collection`, not both.",
677
+ { value: "nft+collection" }
678
+ );
679
+ }
680
+ const spends = await this.spends();
681
+ const owner = hexToBytes(args.owner);
682
+ if (args.collection) {
683
+ if (typeof spends.proveCollectionMembership !== "function") {
684
+ throw new DigSdkError(
685
+ "SPEND_BUILDER_UNAVAILABLE",
686
+ "chip35 wasm `proveCollectionMembership` is unavailable.",
687
+ { builder: "proveCollectionMembership" }
688
+ );
689
+ }
690
+ return spends.proveCollectionMembership(args.parentSpend, owner, hexToBytes(args.collection));
691
+ }
692
+ if (typeof spends.proveNftOwnership !== "function") {
693
+ throw new DigSdkError(
694
+ "SPEND_BUILDER_UNAVAILABLE",
695
+ "chip35 wasm `proveNftOwnership` is unavailable.",
696
+ { builder: "proveNftOwnership" }
697
+ );
698
+ }
699
+ const requiredNft = args.nft ? hexToBytes(args.nft) : null;
700
+ return spends.proveNftOwnership(args.parentSpend, owner, requiredNft);
701
+ }
702
+ };
703
+
417
704
  // src/loader.ts
418
- var DIG_CLIENT_WASM_SHA256 = "ff486be806f908a2a90780e499a04dbd34e10e3b97be0470cb9ee841a1e49e77";
705
+ var DIG_CLIENT_WASM_SHA256 = "8c983561eabca34778abf698ca7c2fba36117f87282ca649079599ef7d1b1156";
419
706
  var _config = {};
420
707
  var _ready = null;
421
708
  function configureWasm(config) {
@@ -439,49 +726,62 @@ async function sha256Hex(bytes) {
439
726
  function assertIntegrity(hex) {
440
727
  if (_config.skipIntegrity) return;
441
728
  if (hex !== DIG_CLIENT_WASM_SHA256) {
442
- throw new Error(
443
- `dig-client wasm integrity check failed \u2014 refusing to run unverified crypto (expected ${DIG_CLIENT_WASM_SHA256}, got ${hex}).`
729
+ throw new DigSdkError(
730
+ "WASM_INTEGRITY",
731
+ `dig-client wasm integrity check failed \u2014 refusing to run unverified crypto (expected ${DIG_CLIENT_WASM_SHA256}, got ${hex}).`,
732
+ { expected: DIG_CLIENT_WASM_SHA256, actual: hex }
444
733
  );
445
734
  }
446
735
  }
447
736
  async function loadNode() {
737
+ const { createRequire } = await import('module');
448
738
  const { readFile } = await import('fs/promises');
449
- const { fileURLToPath, pathToFileURL } = await import('url');
450
- const path = await import('path');
451
- const here = path.dirname(fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
452
- const vendorDir = path.resolve(here, "..", "vendor");
453
- const wasmPath = path.join(vendorDir, "dig_client_bg.wasm");
454
- const gluePath = path.join(vendorDir, "dig_client.mjs");
739
+ const require2 = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
740
+ const wasmPath = require2.resolve("@dignetwork/dig-client/dig_client_bg.wasm");
455
741
  const buf = await readFile(wasmPath);
456
742
  const bytes = new Uint8Array(buf.byteLength);
457
743
  bytes.set(buf);
458
- return { glueHref: pathToFileURL(gluePath).href, bytes };
744
+ assertIntegrity(await sha256Hex(bytes));
745
+ const mod = await import('@dignetwork/dig-client/node');
746
+ return mod;
459
747
  }
460
748
  async function loadBrowser() {
461
- const glueHref = _config.glueUrl ?? new URL("../vendor/dig_client.mjs", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).href;
462
- let bytes;
463
- if (_config.wasmBytes) {
464
- bytes = _config.wasmBytes;
749
+ const glueHref = _config.glueUrl ?? "@dignetwork/dig-client/web";
750
+ const mod = await import(
751
+ /* @vite-ignore */
752
+ glueHref
753
+ );
754
+ if (typeof mod.default !== "function") {
755
+ throw new DigSdkError(
756
+ "WASM_LOAD_FAILED",
757
+ "dig-client web build exposed no init function (unexpected module shape)",
758
+ {}
759
+ );
760
+ }
761
+ if (_config.wasmBytes || _config.wasmUrl) {
762
+ let bytes;
763
+ if (_config.wasmBytes) {
764
+ bytes = _config.wasmBytes;
765
+ } else {
766
+ const wasmUrl = _config.wasmUrl;
767
+ const res = await fetch(wasmUrl);
768
+ if (!res.ok)
769
+ throw new DigSdkError("WASM_LOAD_FAILED", `dig-client wasm fetch failed (${res.status})`, {
770
+ httpStatus: res.status,
771
+ wasmUrl
772
+ });
773
+ bytes = await res.arrayBuffer();
774
+ }
775
+ assertIntegrity(await sha256Hex(bytes));
776
+ await mod.default({ module_or_path: bytes });
465
777
  } else {
466
- const wasmUrl = _config.wasmUrl ?? new URL("../vendor/dig_client_bg.wasm", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).href;
467
- const res = await fetch(wasmUrl);
468
- if (!res.ok) throw new Error(`dig-client wasm fetch failed (${res.status})`);
469
- bytes = await res.arrayBuffer();
778
+ await mod.default();
470
779
  }
471
- return { glueHref, bytes };
780
+ return mod;
472
781
  }
473
782
  function loadDigClientWasm() {
474
783
  if (_ready) return _ready;
475
- _ready = (async () => {
476
- const { glueHref, bytes } = isNode ? await loadNode() : await loadBrowser();
477
- assertIntegrity(await sha256Hex(bytes));
478
- const mod = await import(
479
- /* @vite-ignore */
480
- glueHref
481
- );
482
- await mod.default({ module_or_path: bytes });
483
- return mod;
484
- })().catch((e) => {
784
+ _ready = (isNode ? loadNode() : loadBrowser()).catch((e) => {
485
785
  _ready = null;
486
786
  throw e;
487
787
  });
@@ -494,8 +794,10 @@ function parseUrn(raw) {
494
794
  const s = String(raw ?? "").trim();
495
795
  const m = URN_RE.exec(s);
496
796
  if (!m) {
497
- throw new Error(
498
- "Not a valid dig URN (expected urn:dig:chia:<store-id>[:<root>]/<path>[?salt=<hex>])."
797
+ throw new DigSdkError(
798
+ "INVALID_ARGUMENT",
799
+ "Not a valid dig URN (expected urn:dig:chia:<store-id>[:<root>]/<path>[?salt=<hex>]).",
800
+ { value: s, expected: "urn:dig:chia:<store-id>[:<root>]/<path>[?salt=<hex>]" }
499
801
  );
500
802
  }
501
803
  return {
@@ -587,8 +889,10 @@ var DigClient = class {
587
889
  const effSalt = input.salt ?? parsed.salt ?? null;
588
890
  const effRoot = input.root ?? parsed.root ?? null;
589
891
  if (!effRoot) {
590
- throw new Error(
591
- "a confirmed on-chain root is required to read content (pass { root } or use a root-pinned URN)"
892
+ throw new DigSdkError(
893
+ "ROOT_REQUIRED",
894
+ "a confirmed on-chain root is required to read content (pass { root } or use a root-pinned URN)",
895
+ { urn: input.urn }
592
896
  );
593
897
  }
594
898
  return this.readResource(
@@ -600,8 +904,10 @@ var DigClient = class {
600
904
  async readText(input, opts = {}) {
601
905
  const r = await this.read(input, opts);
602
906
  if (!r.decrypted) {
603
- throw new Error(
604
- "resource did not decrypt under this URN \u2014 wrong store/key/salt, or a decoy response"
907
+ throw new DigSdkError(
908
+ "DECRYPT_FAILED",
909
+ "resource did not decrypt under this URN \u2014 wrong store/key/salt, or a decoy response",
910
+ { urn: input.urn }
605
911
  );
606
912
  }
607
913
  return new TextDecoder().decode(r.bytes);
@@ -650,6 +956,62 @@ var DigClient = class {
650
956
  };
651
957
  }
652
958
  }
959
+ /**
960
+ * Read a collection's public, owner-independent facts (creator DID, item count, uniform royalty)
961
+ * from the dig RPC (`dig.getCollection`). The collection's item set is its NFT launcher ids — the
962
+ * authoritative, owner-independent anchor the mint produced (a DID-attributed NFT is hinted to its
963
+ * OWNER at mint, not to the creator DID, so launcher ids — not the DID — are the read key). Pass
964
+ * the optional `did` to have it echoed back and recorded as the declared creator.
965
+ *
966
+ * No wallet, no read-crypto wasm — a plain JSON-RPC read. Throws a coded {@link DigSdkError} on a
967
+ * transport/RPC failure (never a "not found": an empty/partly-confirmed set just resolves to fewer
968
+ * items).
969
+ *
970
+ * @example
971
+ * const meta = await dig.getCollection({ launcherIds: ["ab…", "cd…"], did: "ef…" });
972
+ * console.log(meta.resolved_count, meta.royalty_basis_points);
973
+ */
974
+ async getCollection(input, opts = {}) {
975
+ const rpc = opts.rpc ?? this.rpc;
976
+ const params = { launcher_ids: input.launcherIds };
977
+ if (input.did) params.did = input.did;
978
+ const r = await this.rpcCall(rpc, "dig.getCollection", params);
979
+ if (!r)
980
+ throw new DigSdkError(
981
+ "RPC_MALFORMED_RESPONSE",
982
+ "The content network returned no collection facts for this request.",
983
+ { rpcMethod: "dig.getCollection" }
984
+ );
985
+ return r;
986
+ }
987
+ /**
988
+ * Read a deterministic, paginated page of a collection's items (`dig.listCollectionItems`), each
989
+ * resolved to its CURRENT on-chain state — current owner, royalty, and CHIP-0007 metadata — by the
990
+ * RPC walking the singleton lineage forward to the live tip (so the owner is never the stale
991
+ * mint-time owner). Items come back in the input launcher-id order. `limit` is clamped to the
992
+ * server cap (200); `offset` defaults to 0.
993
+ *
994
+ * Throws a coded {@link DigSdkError} on a transport/RPC failure.
995
+ *
996
+ * @example
997
+ * let page = await dig.listCollectionItems({ launcherIds, limit: 50 });
998
+ * for (const item of page.items) console.log(item.launcher_id, item.owner_puzzle_hash);
999
+ * // page.next_offset is null on the last page.
1000
+ */
1001
+ async listCollectionItems(input, opts = {}) {
1002
+ const rpc = opts.rpc ?? this.rpc;
1003
+ const params = { launcher_ids: input.launcherIds };
1004
+ if (input.offset !== void 0) params.offset = input.offset;
1005
+ if (input.limit !== void 0) params.limit = input.limit;
1006
+ const r = await this.rpcCall(rpc, "dig.listCollectionItems", params);
1007
+ if (!r)
1008
+ throw new DigSdkError(
1009
+ "RPC_MALFORMED_RESPONSE",
1010
+ "The content network returned no collection items for this request.",
1011
+ { rpcMethod: "dig.listCollectionItems" }
1012
+ );
1013
+ return r;
1014
+ }
653
1015
  // Stream the FULL ciphertext for a resource from the RPC by retrieval key, reassembling 3-MiB
654
1016
  // chunks. A null result is a TRANSPORT failure, never a presence judgment.
655
1017
  async fetchCiphertext(storeId, rk, root, rpc) {
@@ -666,7 +1028,12 @@ var DigClient = class {
666
1028
  offset,
667
1029
  length: RPC_CHUNK_BYTES
668
1030
  });
669
- if (!r) throw new Error("The content network returned no data for this request.");
1031
+ if (!r)
1032
+ throw new DigSdkError(
1033
+ "RPC_MALFORMED_RESPONSE",
1034
+ "The content network returned no data for this request.",
1035
+ { rpcMethod: "dig.getContent" }
1036
+ );
670
1037
  if (total === null) {
671
1038
  total = r.total_length >>> 0;
672
1039
  buf = new Uint8Array(total);
@@ -683,7 +1050,8 @@ var DigClient = class {
683
1050
  }
684
1051
  return { ciphertext: buf ?? new Uint8Array(0), proof, chunkLens };
685
1052
  }
686
- // One JSON-RPC 2.0 call. Throws on transport failure or a JSON-RPC error; returns `result`.
1053
+ // One JSON-RPC 2.0 call. Throws a coded DigSdkError on transport failure (RPC_TRANSPORT) or a
1054
+ // JSON-RPC/HTTP error (RPC_ERROR, carrying rpcMethod/httpStatus/rpcCode context); returns `result`.
687
1055
  async rpcCall(rpc, method, params) {
688
1056
  let res;
689
1057
  try {
@@ -692,12 +1060,26 @@ var DigClient = class {
692
1060
  headers: { "content-type": "application/json" },
693
1061
  body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
694
1062
  });
695
- } catch {
696
- throw new Error("Could not reach the content network. Check your connection and try again.");
1063
+ } catch (e) {
1064
+ throw new DigSdkError(
1065
+ "RPC_TRANSPORT",
1066
+ "Could not reach the content network. Check your connection and try again.",
1067
+ { rpcMethod: method },
1068
+ { cause: e }
1069
+ );
697
1070
  }
698
- if (!res.ok) throw new Error(`dig RPC ${method} failed (${res.status})`);
1071
+ if (!res.ok)
1072
+ throw new DigSdkError("RPC_ERROR", `dig RPC ${method} failed (${res.status})`, {
1073
+ rpcMethod: method,
1074
+ httpStatus: res.status
1075
+ });
699
1076
  const json = await res.json();
700
- if (json && json.error) throw new Error(`dig RPC ${method}: ${json.error.message ?? "error"}`);
1077
+ if (json && json.error)
1078
+ throw new DigSdkError(
1079
+ "RPC_ERROR",
1080
+ `dig RPC ${method}: ${json.error.message ?? "error"}`,
1081
+ { rpcMethod: method, rpcCode: json.error.code }
1082
+ );
701
1083
  return json ? json.result ?? null : null;
702
1084
  }
703
1085
  };
@@ -705,7 +1087,11 @@ function decryptResourceChunks(wasm, keyHex, ciphertext, chunkLens) {
705
1087
  const lens = chunkLens && chunkLens.length ? chunkLens : [ciphertext.length];
706
1088
  const total = lens.reduce((a, n) => a + n, 0);
707
1089
  if (total !== ciphertext.length) {
708
- throw new Error("served ciphertext length does not match chunk lengths");
1090
+ throw new DigSdkError(
1091
+ "RPC_MALFORMED_RESPONSE",
1092
+ "served ciphertext length does not match chunk lengths",
1093
+ { rpcMethod: "dig.getContent", expected: String(total), actual: String(ciphertext.length) }
1094
+ );
709
1095
  }
710
1096
  if (lens.length === 1) return wasm.decryptChunk(keyHex, ciphertext);
711
1097
  const parts = [];
@@ -724,25 +1110,78 @@ function decryptResourceChunks(wasm, keyHex, ciphertext, chunkLens) {
724
1110
  }
725
1111
  function undefinedFetch() {
726
1112
  return (() => {
727
- throw new Error(
1113
+ throw new DigSdkError(
1114
+ "INVALID_ARGUMENT",
728
1115
  "No global fetch available. Pass { fetch } to DigClient (Node < 18 needs a fetch polyfill)."
729
1116
  );
730
1117
  });
731
1118
  }
732
1119
 
1120
+ // src/capabilities.ts
1121
+ var SDK_VERSION = "0.2.1" ;
1122
+ var MODULES = Object.freeze([
1123
+ {
1124
+ name: "ChiaProvider",
1125
+ summary: "Unified Chia wallet surface \u2014 prefers the injected DIG Browser wallet, falls back to WalletConnect\u2192Sage.",
1126
+ entry: "@dignetwork/dig-sdk"
1127
+ },
1128
+ {
1129
+ name: "DigClient",
1130
+ summary: "Read-crypto: derive URN keys, verify inclusion against an on-chain root, and decrypt content from the dig RPC.",
1131
+ entry: "@dignetwork/dig-sdk"
1132
+ },
1133
+ {
1134
+ name: "Paywall",
1135
+ summary: "Pay-to-unlock helper composing ChiaProvider with the canonical chip35 monetization spends (payment / receipt / NFT+collection gating).",
1136
+ entry: "@dignetwork/dig-sdk"
1137
+ },
1138
+ {
1139
+ name: "spend",
1140
+ summary: "The canonical CHIP-0035 spend builder (@dignetwork/chip35-dl-coin-wasm), re-exported verbatim.",
1141
+ entry: "@dignetwork/dig-sdk/spend"
1142
+ },
1143
+ {
1144
+ name: "adapters",
1145
+ summary: "Framework deploy glue (Vite + Next): dev window.chia shim and `digstore deploy` runner.",
1146
+ entry: "@dignetwork/dig-sdk/adapters"
1147
+ }
1148
+ ]);
1149
+ function capabilities() {
1150
+ return {
1151
+ name: "@dignetwork/dig-sdk",
1152
+ version: SDK_VERSION,
1153
+ modules: MODULES,
1154
+ walletMethods: [...WALLET_METHODS],
1155
+ signMethods: [...SIGN_METHODS],
1156
+ transports: ["injected", "walletconnect"],
1157
+ chains: [DEFAULT_CHAIN],
1158
+ defaultRpc: DEFAULT_RPC,
1159
+ readCryptoWasmSha256: DIG_CLIENT_WASM_SHA256,
1160
+ errorCodes: Object.values(DIG_SDK_ERROR_CODES)
1161
+ };
1162
+ }
1163
+ var describe = capabilities;
1164
+
733
1165
  exports.ChiaProvider = ChiaProvider;
734
1166
  exports.DEFAULT_CHAIN = DEFAULT_CHAIN;
735
1167
  exports.DEFAULT_RPC = DEFAULT_RPC;
736
1168
  exports.DIG_CLIENT_WASM_SHA256 = DIG_CLIENT_WASM_SHA256;
1169
+ exports.DIG_SDK_ERROR_CODES = DIG_SDK_ERROR_CODES;
737
1170
  exports.DigClient = DigClient;
1171
+ exports.DigSdkError = DigSdkError;
738
1172
  exports.INJECTED_TOPIC = INJECTED_TOPIC;
739
1173
  exports.InjectedTransport = InjectedTransport;
1174
+ exports.Paywall = Paywall;
1175
+ exports.SDK_VERSION = SDK_VERSION;
740
1176
  exports.SIGN_METHODS = SIGN_METHODS;
741
1177
  exports.WALLET_METHODS = WALLET_METHODS;
742
1178
  exports.WalletConnectTransport = WalletConnectTransport;
1179
+ exports.capabilities = capabilities;
743
1180
  exports.configureWasm = configureWasm;
744
1181
  exports.connectWallet = connectWallet;
1182
+ exports.describe = describe;
745
1183
  exports.getInjectedProvider = getInjectedProvider;
1184
+ exports.isDigSdkError = isDigSdkError;
746
1185
  exports.isInjectedAvailable = isInjectedAvailable;
747
1186
  exports.isTransientPublishError = isTransientPublishError;
748
1187
  exports.isUrn = isUrn;