@dstorage-tech/dstorage-sdk 0.0.6 → 0.0.8

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 (33) hide show
  1. package/dist/KeypairEncryptionAdapter-EOBRCIGP-5PFPNLLA.mjs +8 -0
  2. package/dist/{KeypairEncryptionAdapter-XUKCLRFU-AYDAZ2TZ.mjs → KeypairEncryptionAdapter-EOBRCIGP-FLPJIL26.mjs} +3 -3
  3. package/dist/{MnemonicEncryptionAdapter-RCHUETBP-M7NHGSHO.mjs → MnemonicEncryptionAdapter-S56YG63Q-B3F3TO3T.mjs} +3 -3
  4. package/dist/MnemonicEncryptionAdapter-S56YG63Q-POTD74UN.mjs +8 -0
  5. package/dist/{PasswordEncryptionAdapter-ECHND672-MUTHAMZY.mjs → PasswordEncryptionAdapter-B2UG7MWB-M4C3KJ4S.mjs} +3 -3
  6. package/dist/{PasswordEncryptionAdapter-ECHND672-3DEFO6FP.mjs → PasswordEncryptionAdapter-B2UG7MWB-QCSOIQJA.mjs} +3 -3
  7. package/dist/browser.d.mts +91 -8
  8. package/dist/browser.mjs +171 -67
  9. package/dist/{chunk-HDD6SUU4.mjs → chunk-42O7KEL4.mjs} +30 -11
  10. package/dist/{chunk-X4LEBNK6.mjs → chunk-5F3HZ2RJ.mjs} +30 -11
  11. package/dist/{chunk-6IAQVMSM.mjs → chunk-E73HFT6J.mjs} +5 -5
  12. package/dist/{chunk-UDYXMII2.mjs → chunk-EOPUNIUH.mjs} +5 -5
  13. package/dist/{chunk-O37NHERH.mjs → chunk-L6WCT64V.mjs} +4 -2
  14. package/dist/{chunk-4X5K43JF.mjs → chunk-M3F2LR3B.mjs} +3 -1
  15. package/dist/{chunk-JMPRZK5D.mjs → chunk-O6COTUD4.mjs} +3 -1
  16. package/dist/{chunk-BWLEAAFW.mjs → chunk-PCXBQRCO.mjs} +55 -2056
  17. package/dist/{chunk-OLGVVM5Y.mjs → chunk-QFJ2Y4XE.mjs} +5 -5
  18. package/dist/{chunk-Z6DUAI5B.mjs → chunk-XW4RZG2X.mjs} +5 -5
  19. package/dist/{contract-api-TJ7JPHZA-6FUBJ6DS.mjs → contract-api-NUY4UEKE-ZANT3ICG.mjs} +3 -3
  20. package/dist/{contract-api-AO4RGPLT-ENC72WCG.mjs → contract-api-P6YEDYQ6-2X6BMD64.mjs} +3 -3
  21. package/dist/{dist-INTAFXQ7.mjs → dist-AGNOIJI4.mjs} +3 -1
  22. package/dist/{dist-MOLWJ4Y7.mjs → dist-J4JUJ2J5.mjs} +3 -1
  23. package/dist/errors.d.mts +2 -0
  24. package/dist/errors.d.ts +2 -0
  25. package/dist/errors.js +10 -2054
  26. package/dist/errors.mjs +10 -2054
  27. package/dist/index.d.mts +108 -8
  28. package/dist/index.d.ts +108 -8
  29. package/dist/index.js +306 -2187
  30. package/dist/index.mjs +174 -70
  31. package/package.json +3 -2
  32. package/dist/KeypairEncryptionAdapter-XUKCLRFU-ERPYK5S4.mjs +0 -8
  33. package/dist/MnemonicEncryptionAdapter-RCHUETBP-DZPBGQG6.mjs +0 -8
package/dist/browser.mjs CHANGED
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  ChainErrorCode
3
- } from "./chunk-4X5K43JF.mjs";
3
+ } from "./chunk-O6COTUD4.mjs";
4
4
  import {
5
5
  MnemonicEncryptionAdapter
6
- } from "./chunk-OLGVVM5Y.mjs";
6
+ } from "./chunk-E73HFT6J.mjs";
7
7
  import {
8
8
  KDF_PRESETS,
9
9
  PasswordEncryptionAdapter,
10
10
  generatePqsPassword
11
- } from "./chunk-HDD6SUU4.mjs";
11
+ } from "./chunk-42O7KEL4.mjs";
12
12
  import {
13
13
  KeypairEncryptionAdapter
14
- } from "./chunk-Z6DUAI5B.mjs";
14
+ } from "./chunk-XW4RZG2X.mjs";
15
15
  import {
16
16
  EncryptionErrorCode
17
- } from "./chunk-O37NHERH.mjs";
17
+ } from "./chunk-L6WCT64V.mjs";
18
18
  import {
19
19
  CryptoErrorCode,
20
20
  DStorageError,
@@ -42,11 +42,12 @@ import {
42
42
  mlkemUnwrapDek,
43
43
  mlkemWrapDek,
44
44
  parseKeyEnvelope,
45
+ readResponseTextWithLimit,
45
46
  serializeKeyEnvelope,
46
47
  serializeXChaChaPayload,
47
48
  unwrapKey,
48
49
  wrapKey
49
- } from "./chunk-BWLEAAFW.mjs";
50
+ } from "./chunk-PCXBQRCO.mjs";
50
51
  import "./chunk-5QOKJEH3.mjs";
51
52
 
52
53
  // src/types.ts
@@ -93,6 +94,7 @@ var CoreErrorCode = {
93
94
  STORAGE_ADAPTER_MISMATCH: 10052,
94
95
  RETRIEVE_BY_REF_ID_ENVELOPE_EMPTY: 10053,
95
96
  CONTENT_HASH_MISMATCH: 10054,
97
+ METADATA_PARSE_FAILED: 10055,
96
98
  // listReferences — 10060-10069
97
99
  LIST_REFS_REQUIRES_CHAIN: 10060,
98
100
  LIST_REFS_NOT_SUPPORTED: 10061,
@@ -106,6 +108,7 @@ var CoreErrorCode = {
106
108
  DECRYPT_CHUNK_FAILED: 10093,
107
109
  CHUNK_OVERFLOWS_TOTAL_SIZE: 10094,
108
110
  REASSEMBLY_SIZE_MISMATCH: 10095,
111
+ MANIFEST_PARSE_FAILED: 10096,
109
112
  // init state — 10100-10109
110
113
  NOT_INITIALIZED: 10100,
111
114
  // generic catch-all — 10999
@@ -128,7 +131,7 @@ function isStorePartialError(err) {
128
131
  // package.json
129
132
  var package_default = {
130
133
  name: "@dstorage-tech/dstorage-sdk",
131
- version: "0.0.6",
134
+ version: "0.0.8",
132
135
  description: "Privacy-first decentralized storage SDK with pluggable adapters",
133
136
  license: "MIT",
134
137
  homepage: "https://dstorage.pro",
@@ -204,10 +207,11 @@ var package_default = {
204
207
  "@midnight-ntwrk/midnight-js-protocol": "4.1.1",
205
208
  "@midnight-ntwrk/midnight-js-types": "4.1.1",
206
209
  "@midnight-ntwrk/midnight-js-utils": "4.1.1",
207
- "@midnight-ntwrk/wallet-sdk": "1.1.0",
210
+ "@midnight-ntwrk/wallet-sdk": "1.2.0",
208
211
  "@noble/ciphers": "^2.2.0",
209
212
  "@noble/hashes": "^1.5.0",
210
213
  "@noble/post-quantum": "^0.6.1",
214
+ "@scure/bip39": "^2.0.1",
211
215
  arweave: "^1.15.7"
212
216
  },
213
217
  devDependencies: {
@@ -282,6 +286,9 @@ var DStorage = class {
282
286
  }
283
287
  /** Tear down active state and clear all in-memory key material. */
284
288
  destroy() {
289
+ for (const provider of this._encryptionProviders) {
290
+ provider.destroy?.();
291
+ }
285
292
  this.wallet = null;
286
293
  this._encryptionProviders = [];
287
294
  this._initialized = false;
@@ -381,20 +388,24 @@ var DStorage = class {
381
388
  );
382
389
  }
383
390
  const ownerSeed = generateDek();
384
- const ownerSeedWrappers = await Promise.all(
385
- this._encryptionProviders.map(async (provider) => ({
386
- wrapKeyName: provider.name,
387
- ...await provider.wrapDek(ownerSeed)
388
- }))
389
- );
390
- keyEnvelope = serializeKeyEnvelope(
391
- buildKeyEnvelope(ownerSeedWrappers)
392
- );
393
- ownerSecret = await deriveOwnerSecret(
394
- ownerSeed,
395
- new TextEncoder().encode(storageResult.id),
396
- "dstorage:owner-secret:public:v1"
397
- );
391
+ try {
392
+ const ownerSeedWrappers = await Promise.all(
393
+ this._encryptionProviders.map(async (provider) => ({
394
+ wrapKeyName: provider.name,
395
+ ...await provider.wrapDek(ownerSeed)
396
+ }))
397
+ );
398
+ keyEnvelope = serializeKeyEnvelope(
399
+ buildKeyEnvelope(ownerSeedWrappers)
400
+ );
401
+ ownerSecret = await deriveOwnerSecret(
402
+ ownerSeed,
403
+ new TextEncoder().encode(storageResult.id),
404
+ "dstorage:owner-secret:public:v1"
405
+ );
406
+ } finally {
407
+ ownerSeed.fill(0);
408
+ }
398
409
  }
399
410
  onChainStorageId = storageResult.id;
400
411
  } else {
@@ -467,6 +478,8 @@ var DStorage = class {
467
478
  };
468
479
  } catch {
469
480
  throw new StorePartialError(storeRecovery);
481
+ } finally {
482
+ dek?.fill(0);
470
483
  }
471
484
  }
472
485
  this.logger?.log(
@@ -827,7 +840,10 @@ var DStorage = class {
827
840
  async retrieveByStorageId(storageId, dekScheme, contentHash) {
828
841
  try {
829
842
  this.assertConnected();
830
- const { bytes: rawBytes, tags } = await this.config.storageAdapter.retrieve(storageId);
843
+ const { bytes: rawBytes, tags } = await this.config.storageAdapter.retrieve(
844
+ storageId,
845
+ contentHash !== void 0 ? { expectedContentHash: contentHash } : void 0
846
+ );
831
847
  if (contentHash) {
832
848
  const computed = await computeBlake3Hex(rawBytes);
833
849
  if (computed !== contentHash) {
@@ -968,9 +984,16 @@ var DStorage = class {
968
984
  } catch {
969
985
  }
970
986
  if (manifestBytes !== null) {
971
- const parsed = JSON.parse(
972
- new TextDecoder().decode(manifestBytes)
973
- );
987
+ let parsed;
988
+ try {
989
+ parsed = JSON.parse(new TextDecoder().decode(manifestBytes));
990
+ } catch (err) {
991
+ throw new DStorageError(
992
+ CoreErrorCode.MANIFEST_PARSE_FAILED,
993
+ `[dStorage] Payload authenticated as manifest but is not valid JSON: ${err}`,
994
+ { cause: err }
995
+ );
996
+ }
974
997
  if (!isManifest(parsed)) {
975
998
  throw new DStorageError(
976
999
  CoreErrorCode.MANIFEST_INVALID_STRUCTURE,
@@ -1094,7 +1117,15 @@ var DStorage = class {
1094
1117
  base64urlToBytes(ref.encryptedMetadata),
1095
1118
  METADATA_AAD
1096
1119
  );
1097
- metadata = JSON.parse(new TextDecoder().decode(raw));
1120
+ try {
1121
+ metadata = JSON.parse(new TextDecoder().decode(raw));
1122
+ } catch (err) {
1123
+ throw new DStorageError(
1124
+ CoreErrorCode.METADATA_PARSE_FAILED,
1125
+ `[dStorage] Decrypted metadata is not valid JSON: ${err}`,
1126
+ { cause: err }
1127
+ );
1128
+ }
1098
1129
  }
1099
1130
  return { bytes: contentBytes, tags: contentTags, metadata };
1100
1131
  } catch (err) {
@@ -1439,12 +1470,17 @@ var DStorage = class {
1439
1470
  };
1440
1471
  } catch {
1441
1472
  throw new StorePartialError(storeRecovery);
1473
+ } finally {
1474
+ chunkDek?.fill(0);
1475
+ ownerSeed?.fill(0);
1442
1476
  }
1443
1477
  }
1444
1478
  this.logger?.log(
1445
1479
  "dStorage",
1446
1480
  `Chunked upload complete (storage-only) \u2192 ${totalChunks} chunks + manifest, storageId: ${manifestResult.id}`
1447
1481
  );
1482
+ chunkDek?.fill(0);
1483
+ ownerSeed?.fill(0);
1448
1484
  return {
1449
1485
  storageId: manifestResult.id,
1450
1486
  storageProvider: manifestResult.provider,
@@ -2238,7 +2274,8 @@ var StorageErrorCode = {
2238
2274
  ARWEAVE_BUNDLER_INVALID_AUTH_TOKEN_FORMAT: 15411,
2239
2275
  ARWEAVE_BUNDLER_INVALID_AUTH_TOKEN_KEY: 15412,
2240
2276
  ARWEAVE_BUNDLER_NO_TX_ID_RETURNED: 15413,
2241
- ARWEAVE_BUNDLER_UPLOAD_FAILED: 15414
2277
+ ARWEAVE_BUNDLER_UPLOAD_FAILED: 15414,
2278
+ ARWEAVE_BUNDLER_METADATA_TOO_LARGE: 15415
2242
2279
  };
2243
2280
  var memoryStore = /* @__PURE__ */ new Map();
2244
2281
  function bytesToBase64url2(bytes) {
@@ -2515,7 +2552,7 @@ var ArweaveStorageAdapter = class {
2515
2552
  };
2516
2553
  }
2517
2554
  // ── retrieve ───────────────────────────────────────────────────────────────────
2518
- async retrieve(id) {
2555
+ async retrieve(id, options) {
2519
2556
  if (!/^[A-Za-z0-9_-]{43}$/.test(id)) {
2520
2557
  throw new DStorageError(
2521
2558
  StorageErrorCode.ARWEAVE_INVALID_TX_ID,
@@ -2579,14 +2616,15 @@ Check status at: https://viewblock.io/arweave/tx/${id}`
2579
2616
  );
2580
2617
  }
2581
2618
  if (!this.skipIntegrityCheck) {
2582
- if (!storedHash) {
2619
+ const anchorHash = options?.expectedContentHash;
2620
+ if (!anchorHash && !storedHash) {
2583
2621
  throw new DStorageError(
2584
2622
  StorageErrorCode.ARWEAVE_HASH_TAG_MISSING,
2585
- `[dStorage/arweave] ${CONTENT_HASH_TAG2} tag not found for txId: ${id}.
2623
+ `[dStorage/arweave] Cannot verify content integrity for txId: ${id} \u2014 no expectedContentHash anchor was provided and the ${CONTENT_HASH_TAG2} tag was not found.
2586
2624
  Set skipIntegrityCheck: true to bypass integrity checking for fully trusted gateways.`
2587
2625
  );
2588
2626
  }
2589
- await assertContentHash(id, bytes, storedHash, "arweave");
2627
+ await assertContentHash(id, bytes, anchorHash ?? storedHash, "arweave");
2590
2628
  }
2591
2629
  this.logger?.log("dStorage/arweave", `Retrieved txId: ${id}`);
2592
2630
  return { bytes, tags };
@@ -2710,14 +2748,19 @@ async function getTurboFactory() {
2710
2748
  return TurboFactory;
2711
2749
  }
2712
2750
  var ARWEAVE_GATEWAY_URL = "https://turbo-gateway.com";
2751
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
2752
+ var DEFAULT_MAX_METADATA_RESPONSE_BYTES = 1024 * 1024;
2753
+ var DEFAULT_MAX_RETRIEVE_SIZE_BYTES = 256 * 1024 * 1024;
2713
2754
  var ArweaveBundlerStorageAdapter = class _ArweaveBundlerStorageAdapter {
2714
2755
  constructor(config = {}) {
2715
2756
  this.name = "arweave_bundler";
2716
2757
  this.providerName = "Arweave (Bundler)";
2717
2758
  this.signingServerUrl = config.signingServerUrl ? config.signingServerUrl.replace(/\/$/, "") : null;
2718
2759
  this.gateway = config.gateway ?? ARWEAVE_GATEWAY_URL;
2719
- this.maxRetrieveSizeBytes = config.maxRetrieveSizeBytes ?? 256 * 1024 * 1024;
2760
+ this.maxRetrieveSizeBytes = config.maxRetrieveSizeBytes ?? DEFAULT_MAX_RETRIEVE_SIZE_BYTES;
2720
2761
  this.skipIntegrityCheck = config.skipIntegrityCheck ?? false;
2762
+ this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
2763
+ this.maxMetadataResponseBytes = config.maxMetadataResponseBytes ?? DEFAULT_MAX_METADATA_RESPONSE_BYTES;
2721
2764
  this.logger = config.logger;
2722
2765
  if (config.signingServerUrl) {
2723
2766
  if (!config.authToken?.trim()) {
@@ -2768,8 +2811,13 @@ var ArweaveBundlerStorageAdapter = class _ArweaveBundlerStorageAdapter {
2768
2811
  );
2769
2812
  return { id: txId, provider: this.name, url, uploadedAt: Date.now() };
2770
2813
  }
2814
+ /** Adds an AbortSignal.timeout() to a fetch init object (when requestTimeoutMs > 0). */
2815
+ _withTimeout(init = {}) {
2816
+ const signal = this.requestTimeoutMs > 0 ? AbortSignal.timeout(this.requestTimeoutMs) : void 0;
2817
+ return { ...init, ...signal !== void 0 ? { signal } : {} };
2818
+ }
2771
2819
  // ── retrieve ───────────────────────────────────────────────────────────────────
2772
- async retrieve(id) {
2820
+ async retrieve(id, options) {
2773
2821
  if (!/^[A-Za-z0-9_-]{43}$/.test(id)) {
2774
2822
  throw new DStorageError(
2775
2823
  StorageErrorCode.ARWEAVE_BUNDLER_INVALID_TX_ID,
@@ -2779,29 +2827,37 @@ var ArweaveBundlerStorageAdapter = class _ArweaveBundlerStorageAdapter {
2779
2827
  let storedHash;
2780
2828
  const tags = {};
2781
2829
  if (!this.skipIntegrityCheck) {
2782
- const metaResp = await fetch(`${this.gateway}/tx/${id}`);
2830
+ const metaResp = await fetch(
2831
+ `${this.gateway}/tx/${id}`,
2832
+ this._withTimeout()
2833
+ );
2783
2834
  if (!metaResp.ok) {
2784
2835
  throw new DStorageError(
2785
2836
  StorageErrorCode.ARWEAVE_BUNDLER_TX_METADATA_FAILED,
2786
2837
  `[dStorage/arweave-bundler] Could not fetch transaction metadata for txId: ${id} \u2014 content hash cannot be verified. Set skipIntegrityCheck: true to bypass integrity checking for fully trusted gateways.`
2787
2838
  );
2788
2839
  }
2789
- const tx = await metaResp.json();
2840
+ const metaText = await readResponseTextWithLimit(metaResp, {
2841
+ maxBytes: this.maxMetadataResponseBytes,
2842
+ code: StorageErrorCode.ARWEAVE_BUNDLER_METADATA_TOO_LARGE,
2843
+ component: `[dStorage/arweave-bundler] retrieve(${id}) metadata`
2844
+ });
2845
+ const tx = JSON.parse(metaText);
2790
2846
  for (const tag of tx.tags ?? []) {
2791
2847
  const name = new TextDecoder().decode(_base64urlToBytes(tag.name));
2792
2848
  const value = new TextDecoder().decode(_base64urlToBytes(tag.value));
2793
2849
  tags[name] = value;
2794
2850
  if (name === CONTENT_HASH_TAG2) storedHash = value;
2795
2851
  }
2796
- if (!storedHash) {
2852
+ if (!options?.expectedContentHash && !storedHash) {
2797
2853
  throw new DStorageError(
2798
2854
  StorageErrorCode.ARWEAVE_BUNDLER_HASH_TAG_MISSING,
2799
- `[dStorage/arweave-bundler] ${CONTENT_HASH_TAG2} tag not found for txId: ${id}.
2855
+ `[dStorage/arweave-bundler] Cannot verify content integrity for txId: ${id} \u2014 no expectedContentHash anchor was provided and the ${CONTENT_HASH_TAG2} tag was not found.
2800
2856
  Set skipIntegrityCheck: true to bypass integrity checking for fully trusted gateways.`
2801
2857
  );
2802
2858
  }
2803
2859
  }
2804
- const resp = await fetch(`${this.gateway}/${id}`);
2860
+ const resp = await fetch(`${this.gateway}/${id}`, this._withTimeout());
2805
2861
  if (!resp.ok) {
2806
2862
  throw new DStorageError(
2807
2863
  StorageErrorCode.ARWEAVE_BUNDLER_RETRIEVE_HTTP_ERROR,
@@ -2826,8 +2882,14 @@ Check: https://viewblock.io/arweave/tx/${id}`
2826
2882
  `[dStorage/arweave-bundler] Downloaded ${bytes.byteLength} bytes for txId: ${id}, which exceeds the configured limit of ${this.maxRetrieveSizeBytes} bytes.`
2827
2883
  );
2828
2884
  }
2829
- if (!this.skipIntegrityCheck && storedHash) {
2830
- await assertContentHash(id, bytes, storedHash, "arweave-bundler");
2885
+ const anchorHash = options?.expectedContentHash;
2886
+ if (!this.skipIntegrityCheck && (anchorHash || storedHash)) {
2887
+ await assertContentHash(
2888
+ id,
2889
+ bytes,
2890
+ anchorHash ?? storedHash,
2891
+ "arweave-bundler"
2892
+ );
2831
2893
  }
2832
2894
  this.logger?.log("dStorage/arweave-bundler", `Retrieved txId: ${id}`);
2833
2895
  return { bytes, tags };
@@ -3638,6 +3700,8 @@ var MidnightSimulatorChainAdapter = class {
3638
3700
  }
3639
3701
  };
3640
3702
  var COMPONENT = "[dStorage/http-gateway-chain]";
3703
+ var DEFAULT_REQUEST_TIMEOUT_MS2 = 3e4;
3704
+ var DEFAULT_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
3641
3705
  function generateRefId2() {
3642
3706
  const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
3643
3707
  return `ref_${id}`;
@@ -3653,6 +3717,13 @@ var HttpGatewayChainAdapter = class {
3653
3717
  this.readUrl = `${config.baseUrl}${config.readPath}`;
3654
3718
  this.listUrl = `${config.baseUrl}${config.listPath}`;
3655
3719
  this.deleteUrl = `${config.baseUrl}${config.deletePath}`;
3720
+ this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS2;
3721
+ this.maxResponseBytes = config.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
3722
+ }
3723
+ /** Adds an AbortSignal.timeout() to a fetch init object (when requestTimeoutMs > 0). */
3724
+ _withTimeout(init) {
3725
+ const signal = this.requestTimeoutMs > 0 ? AbortSignal.timeout(this.requestTimeoutMs) : void 0;
3726
+ return { ...init, ...signal !== void 0 ? { signal } : {} };
3656
3727
  }
3657
3728
  async writeReference(ref) {
3658
3729
  const { ownerSecret: _omit, ...payload } = ref;
@@ -3660,11 +3731,17 @@ var HttpGatewayChainAdapter = class {
3660
3731
  const url = `${this.writeUrl}/${encodeURIComponent(refId)}`;
3661
3732
  let res;
3662
3733
  try {
3663
- res = await this.fetchFn(url, {
3664
- method: "POST",
3665
- headers: { "Content-Type": "application/json", ...this.extraHeaders },
3666
- body: JSON.stringify(payload)
3667
- });
3734
+ res = await this.fetchFn(
3735
+ url,
3736
+ this._withTimeout({
3737
+ method: "POST",
3738
+ headers: {
3739
+ "Content-Type": "application/json",
3740
+ ...this.extraHeaders
3741
+ },
3742
+ body: JSON.stringify(payload)
3743
+ })
3744
+ );
3668
3745
  } catch (err) {
3669
3746
  throw new DStorageError(
3670
3747
  ChainErrorCode.GATEWAY_WRITE_FAILED,
@@ -3690,11 +3767,17 @@ var HttpGatewayChainAdapter = class {
3690
3767
  const url = `${this.writeUrl}/${encodeURIComponent(refId)}`;
3691
3768
  let res;
3692
3769
  try {
3693
- res = await this.fetchFn(url, {
3694
- method: "POST",
3695
- headers: { "Content-Type": "application/json", ...this.extraHeaders },
3696
- body: JSON.stringify(update)
3697
- });
3770
+ res = await this.fetchFn(
3771
+ url,
3772
+ this._withTimeout({
3773
+ method: "POST",
3774
+ headers: {
3775
+ "Content-Type": "application/json",
3776
+ ...this.extraHeaders
3777
+ },
3778
+ body: JSON.stringify(update)
3779
+ })
3780
+ );
3698
3781
  } catch (err) {
3699
3782
  throw new DStorageError(
3700
3783
  ChainErrorCode.GATEWAY_UPDATE_FAILED,
@@ -3715,7 +3798,10 @@ var HttpGatewayChainAdapter = class {
3715
3798
  const url = `${this.readUrl}/${encodeURIComponent(refId)}`;
3716
3799
  let res;
3717
3800
  try {
3718
- res = await this.fetchFn(url, { headers: { ...this.extraHeaders } });
3801
+ res = await this.fetchFn(
3802
+ url,
3803
+ this._withTimeout({ headers: { ...this.extraHeaders } })
3804
+ );
3719
3805
  } catch (err) {
3720
3806
  throw new DStorageError(
3721
3807
  ChainErrorCode.GATEWAY_READ_FAILED,
@@ -3730,9 +3816,14 @@ var HttpGatewayChainAdapter = class {
3730
3816
  { httpStatus: res.status }
3731
3817
  );
3732
3818
  }
3819
+ const text = await readResponseTextWithLimit(res, {
3820
+ maxBytes: this.maxResponseBytes,
3821
+ code: ChainErrorCode.GATEWAY_READ_RESPONSE_TOO_LARGE,
3822
+ component: `${COMPONENT} readReference`
3823
+ });
3733
3824
  let data;
3734
3825
  try {
3735
- data = await res.json();
3826
+ data = JSON.parse(text);
3736
3827
  } catch {
3737
3828
  throw new DStorageError(
3738
3829
  ChainErrorCode.GATEWAY_READ_INVALID_JSON,
@@ -3751,9 +3842,10 @@ var HttpGatewayChainAdapter = class {
3751
3842
  async listReferences() {
3752
3843
  let res;
3753
3844
  try {
3754
- res = await this.fetchFn(this.listUrl, {
3755
- headers: { ...this.extraHeaders }
3756
- });
3845
+ res = await this.fetchFn(
3846
+ this.listUrl,
3847
+ this._withTimeout({ headers: { ...this.extraHeaders } })
3848
+ );
3757
3849
  } catch (err) {
3758
3850
  throw new DStorageError(
3759
3851
  ChainErrorCode.GATEWAY_LIST_FAILED,
@@ -3768,9 +3860,14 @@ var HttpGatewayChainAdapter = class {
3768
3860
  { httpStatus: res.status }
3769
3861
  );
3770
3862
  }
3863
+ const text = await readResponseTextWithLimit(res, {
3864
+ maxBytes: this.maxResponseBytes,
3865
+ code: ChainErrorCode.GATEWAY_LIST_RESPONSE_TOO_LARGE,
3866
+ component: `${COMPONENT} listReferences`
3867
+ });
3771
3868
  let data;
3772
3869
  try {
3773
- data = await res.json();
3870
+ data = JSON.parse(text);
3774
3871
  } catch {
3775
3872
  throw new DStorageError(
3776
3873
  ChainErrorCode.GATEWAY_LIST_INVALID_JSON,
@@ -3803,11 +3900,17 @@ var HttpGatewayChainAdapter = class {
3803
3900
  const body = ownerSecret !== void 0 ? { ownerSecret: bytesToBase64url(ownerSecret) } : {};
3804
3901
  let res;
3805
3902
  try {
3806
- res = await this.fetchFn(url, {
3807
- method: "DELETE",
3808
- headers: { "Content-Type": "application/json", ...this.extraHeaders },
3809
- body: JSON.stringify(body)
3810
- });
3903
+ res = await this.fetchFn(
3904
+ url,
3905
+ this._withTimeout({
3906
+ method: "DELETE",
3907
+ headers: {
3908
+ "Content-Type": "application/json",
3909
+ ...this.extraHeaders
3910
+ },
3911
+ body: JSON.stringify(body)
3912
+ })
3913
+ );
3811
3914
  } catch (err) {
3812
3915
  throw new DStorageError(
3813
3916
  ChainErrorCode.GATEWAY_REMOVE_FAILED,
@@ -3854,16 +3957,16 @@ var NETWORKS = {
3854
3957
  networkId: "preprod",
3855
3958
  nodeHttp: "https://rpc.preprod.midnight.network",
3856
3959
  nodeWs: "wss://rpc.preprod.midnight.network",
3857
- indexerHttp: "https://indexer.preprod.midnight.network/api/v3/graphql",
3858
- indexerWs: "wss://indexer.preprod.midnight.network/api/v3/graphql/ws",
3960
+ indexerHttp: "https://indexer.preprod.midnight.network/api/v4/graphql",
3961
+ indexerWs: "wss://indexer.preprod.midnight.network/api/v4/graphql/ws",
3859
3962
  proofServer: "http://127.0.0.1:6300"
3860
3963
  },
3861
3964
  undeployed: {
3862
3965
  networkId: "undeployed",
3863
3966
  nodeHttp: "http://127.0.0.1:9944",
3864
3967
  nodeWs: "ws://127.0.0.1:9944",
3865
- indexerHttp: "http://127.0.0.1:8088/api/v3/graphql",
3866
- indexerWs: "ws://127.0.0.1:8088/api/v3/graphql/ws",
3968
+ indexerHttp: "http://127.0.0.1:8088/api/v4/graphql",
3969
+ indexerWs: "ws://127.0.0.1:8088/api/v4/graphql/ws",
3867
3970
  proofServer: "http://127.0.0.1:6300"
3868
3971
  }
3869
3972
  };
@@ -4273,7 +4376,7 @@ var MidnightChainAdapter = class _MidnightChainAdapter {
4273
4376
  } else {
4274
4377
  providers = await this._buildWalletProviderMode();
4275
4378
  }
4276
- const { DataRegistryAPI } = await import("./contract-api-AO4RGPLT-ENC72WCG.mjs");
4379
+ const { DataRegistryAPI } = await import("./contract-api-P6YEDYQ6-2X6BMD64.mjs");
4277
4380
  if (!this._contractAddress) {
4278
4381
  this.logger?.log("dStorage/midnight", "Deploying DataRegistry contract\u2026");
4279
4382
  this._api = await DataRegistryAPI.deploy(providers, this.logger);
@@ -4812,6 +4915,7 @@ export {
4812
4915
  mlkemUnwrapDek,
4813
4916
  mlkemWrapDek,
4814
4917
  parseKeyEnvelope,
4918
+ readResponseTextWithLimit,
4815
4919
  serializeKeyEnvelope,
4816
4920
  serializeXChaChaPayload,
4817
4921
  unwrapKey,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  EncryptionErrorCode
3
- } from "./chunk-O37NHERH.mjs";
3
+ } from "./chunk-L6WCT64V.mjs";
4
4
  import {
5
5
  DStorageError,
6
6
  deriveKek,
7
7
  unwrapKey,
8
8
  wrapKey
9
- } from "./chunk-BWLEAAFW.mjs";
9
+ } from "./chunk-PCXBQRCO.mjs";
10
10
 
11
- // ../encryption/dist/chunk-6IHXTW2Y.mjs
11
+ // ../encryption/dist/chunk-ENX7VXEV.mjs
12
12
  import { scrypt } from "@noble/hashes/scrypt.js";
13
13
  var KDF_PRESETS = {
14
14
  v1: { N: 131072, r: 8, p: 1 },
@@ -137,8 +137,8 @@ var PasswordEncryptionAdapter = class _PasswordEncryptionAdapter {
137
137
  * @param variant ML-KEM variant. Default: "mlkem768".
138
138
  */
139
139
  async intoKeypairEncryptionAdapter(context, variant) {
140
- const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-XUKCLRFU-ERPYK5S4.mjs");
141
- const { mlkemGenerateKeypairFromSeed } = await import("./dist-MOLWJ4Y7.mjs");
140
+ const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-EOBRCIGP-5PFPNLLA.mjs");
141
+ const { mlkemGenerateKeypairFromSeed } = await import("./dist-J4JUJ2J5.mjs");
142
142
  const kekMaterial = await this.kekMaterialPromise;
143
143
  const mlkemBits = await globalThis.crypto.subtle.deriveBits(
144
144
  {
@@ -170,7 +170,23 @@ function resolveSalt(salt) {
170
170
  }
171
171
  return bytes;
172
172
  }
173
+ var MIN_SCRYPT_N = 32768;
174
+ function assertScryptCost(N) {
175
+ if (!Number.isInteger(N) || N < 2 || (N & N - 1) !== 0) {
176
+ throw new DStorageError(
177
+ EncryptionErrorCode.PASSWORD_SCRYPT_N_NOT_POWER_OF_TWO,
178
+ `[dStorage] PasswordEncryptionAdapter: scrypt N must be a power of two >= 2 (got ${N}).`
179
+ );
180
+ }
181
+ if (N < MIN_SCRYPT_N) {
182
+ throw new DStorageError(
183
+ EncryptionErrorCode.PASSWORD_SCRYPT_N_TOO_LOW,
184
+ `[dStorage] PasswordEncryptionAdapter: scrypt N=${N} is below the minimum cost floor of ${MIN_SCRYPT_N} (OWASP 2024 minimum, 32 MB memory-hard). A lower N makes the KDF cheap enough to brute-force offline. Use a preset ("v1" or "v1-lite") or a custom N >= 32768.`
185
+ );
186
+ }
187
+ }
173
188
  function resolveParams(preset, params) {
189
+ let resolved;
174
190
  if (preset !== void 0) {
175
191
  if (!(preset in KDF_PRESETS)) {
176
192
  throw new DStorageError(
@@ -178,13 +194,16 @@ function resolveParams(preset, params) {
178
194
  `[dStorage] PasswordEncryptionAdapter: unknown preset "${preset}". Valid presets: ${Object.keys(KDF_PRESETS).join(", ")}.`
179
195
  );
180
196
  }
181
- return KDF_PRESETS[preset];
197
+ resolved = KDF_PRESETS[preset];
198
+ } else {
199
+ resolved = {
200
+ N: params?.N ?? KDF_PRESETS["v1"].N,
201
+ r: params?.r ?? KDF_PRESETS["v1"].r,
202
+ p: params?.p ?? KDF_PRESETS["v1"].p
203
+ };
182
204
  }
183
- return {
184
- N: params?.N ?? KDF_PRESETS["v1"].N,
185
- r: params?.r ?? KDF_PRESETS["v1"].r,
186
- p: params?.p ?? KDF_PRESETS["v1"].p
187
- };
205
+ assertScryptCost(resolved.N);
206
+ return resolved;
188
207
  }
189
208
  function estimateEntropyBits(password) {
190
209
  let pool = 0;
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  EncryptionErrorCode
3
- } from "./chunk-O37NHERH.mjs";
3
+ } from "./chunk-L6WCT64V.mjs";
4
4
  import {
5
5
  DStorageError,
6
6
  deriveKek,
7
7
  unwrapKey,
8
8
  wrapKey
9
- } from "./chunk-BWLEAAFW.mjs";
9
+ } from "./chunk-PCXBQRCO.mjs";
10
10
 
11
- // ../encryption/dist/chunk-6IHXTW2Y.mjs
11
+ // ../encryption/dist/chunk-ENX7VXEV.mjs
12
12
  import { scrypt } from "@noble/hashes/scrypt.js";
13
13
  var KDF_PRESETS = {
14
14
  v1: { N: 131072, r: 8, p: 1 },
@@ -137,8 +137,8 @@ var PasswordEncryptionAdapter = class _PasswordEncryptionAdapter {
137
137
  * @param variant ML-KEM variant. Default: "mlkem768".
138
138
  */
139
139
  async intoKeypairEncryptionAdapter(context, variant) {
140
- const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-XUKCLRFU-AYDAZ2TZ.mjs");
141
- const { mlkemGenerateKeypairFromSeed } = await import("./dist-INTAFXQ7.mjs");
140
+ const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-EOBRCIGP-FLPJIL26.mjs");
141
+ const { mlkemGenerateKeypairFromSeed } = await import("./dist-AGNOIJI4.mjs");
142
142
  const kekMaterial = await this.kekMaterialPromise;
143
143
  const mlkemBits = await globalThis.crypto.subtle.deriveBits(
144
144
  {
@@ -170,7 +170,23 @@ function resolveSalt(salt) {
170
170
  }
171
171
  return bytes;
172
172
  }
173
+ var MIN_SCRYPT_N = 32768;
174
+ function assertScryptCost(N) {
175
+ if (!Number.isInteger(N) || N < 2 || (N & N - 1) !== 0) {
176
+ throw new DStorageError(
177
+ EncryptionErrorCode.PASSWORD_SCRYPT_N_NOT_POWER_OF_TWO,
178
+ `[dStorage] PasswordEncryptionAdapter: scrypt N must be a power of two >= 2 (got ${N}).`
179
+ );
180
+ }
181
+ if (N < MIN_SCRYPT_N) {
182
+ throw new DStorageError(
183
+ EncryptionErrorCode.PASSWORD_SCRYPT_N_TOO_LOW,
184
+ `[dStorage] PasswordEncryptionAdapter: scrypt N=${N} is below the minimum cost floor of ${MIN_SCRYPT_N} (OWASP 2024 minimum, 32 MB memory-hard). A lower N makes the KDF cheap enough to brute-force offline. Use a preset ("v1" or "v1-lite") or a custom N >= 32768.`
185
+ );
186
+ }
187
+ }
173
188
  function resolveParams(preset, params) {
189
+ let resolved;
174
190
  if (preset !== void 0) {
175
191
  if (!(preset in KDF_PRESETS)) {
176
192
  throw new DStorageError(
@@ -178,13 +194,16 @@ function resolveParams(preset, params) {
178
194
  `[dStorage] PasswordEncryptionAdapter: unknown preset "${preset}". Valid presets: ${Object.keys(KDF_PRESETS).join(", ")}.`
179
195
  );
180
196
  }
181
- return KDF_PRESETS[preset];
197
+ resolved = KDF_PRESETS[preset];
198
+ } else {
199
+ resolved = {
200
+ N: params?.N ?? KDF_PRESETS["v1"].N,
201
+ r: params?.r ?? KDF_PRESETS["v1"].r,
202
+ p: params?.p ?? KDF_PRESETS["v1"].p
203
+ };
182
204
  }
183
- return {
184
- N: params?.N ?? KDF_PRESETS["v1"].N,
185
- r: params?.r ?? KDF_PRESETS["v1"].r,
186
- p: params?.p ?? KDF_PRESETS["v1"].p
187
- };
205
+ assertScryptCost(resolved.N);
206
+ return resolved;
188
207
  }
189
208
  function estimateEntropyBits(password) {
190
209
  let pool = 0;