@did-btcr2/api 0.4.0 → 0.5.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.
package/dist/esm/cas.js CHANGED
@@ -3,6 +3,8 @@ import { CID } from 'multiformats/cid';
3
3
  import * as raw from 'multiformats/codecs/raw';
4
4
  import { create as createDigest } from 'multiformats/hashes/digest';
5
5
  import { sha256 } from 'multiformats/hashes/sha2';
6
+ /** Default IPFS HTTP gateway used for CAS reads when no CAS config is provided. */
7
+ export const DEFAULT_CAS_GATEWAY = 'https://ipfs.io';
6
8
  /**
7
9
  * Default {@link CasExecutor} backed by IPFS via Helia.
8
10
  *
@@ -35,6 +37,45 @@ export class IpfsCasExecutor {
35
37
  .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
36
38
  }
37
39
  }
40
+ /**
41
+ * Read-only {@link CasExecutor} backed by an IPFS HTTP gateway.
42
+ *
43
+ * Converts the base64url SHA-256 hash to a CIDv1 (raw codec) and fetches
44
+ * the raw block via the
45
+ * {@link https://specs.ipfs.tech/http-gateways/trustless-gateway/ | Trustless Gateway}
46
+ * protocol.
47
+ *
48
+ * Publishing is not supported — use {@link IpfsCasExecutor} with a Helia
49
+ * instance for writes.
50
+ * @public
51
+ */
52
+ export class HttpGatewayCasExecutor {
53
+ #gatewayUrl;
54
+ constructor(gatewayUrl) {
55
+ this.#gatewayUrl = gatewayUrl.replace(/\/+$/, '');
56
+ }
57
+ async retrieve(hash) {
58
+ const hashBytes = decodeHash(hash, 'base64urlnopad');
59
+ const cid = CID.create(1, raw.code, createDigest(sha256.code, hashBytes));
60
+ try {
61
+ const res = await fetch(`${this.#gatewayUrl}/ipfs/${cid.toString()}?format=raw`, {
62
+ headers: { Accept: 'application/vnd.ipld.raw' },
63
+ });
64
+ if (!res.ok)
65
+ return null;
66
+ return new Uint8Array(await res.arrayBuffer());
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ async publish() {
73
+ throw new Error('HttpGatewayCasExecutor is read-only. '
74
+ + 'Publishing requires a full IPFS node (use IpfsCasExecutor with Helia).');
75
+ }
76
+ }
77
+ /** Default timeout (ms) for CAS operations. */
78
+ export const DEFAULT_CAS_TIMEOUT_MS = 30_000;
38
79
  /**
39
80
  * Content-Addressed Storage API sub-facade.
40
81
  *
@@ -50,6 +91,7 @@ export class IpfsCasExecutor {
50
91
  */
51
92
  export class CasApi {
52
93
  #executor;
94
+ #timeoutMs;
53
95
  constructor(config) {
54
96
  if (config.executor) {
55
97
  this.#executor = config.executor;
@@ -57,10 +99,14 @@ export class CasApi {
57
99
  else if (config.helia) {
58
100
  this.#executor = new IpfsCasExecutor(config.helia);
59
101
  }
102
+ else if (config.gateway) {
103
+ this.#executor = new HttpGatewayCasExecutor(config.gateway);
104
+ }
60
105
  else {
61
- throw new Error('CAS configuration requires either an executor or a Helia instance. '
62
- + 'Example: createApi({ cas: { helia: await createHelia() } })');
106
+ throw new Error('CAS configuration requires an executor, Helia instance, or gateway URL. '
107
+ + 'Example: createApi({ cas: { gateway: \'https://ipfs.io\' } })');
63
108
  }
109
+ this.#timeoutMs = config.timeoutMs ?? DEFAULT_CAS_TIMEOUT_MS;
64
110
  }
65
111
  /**
66
112
  * Retrieve a JSON object from the CAS by its SHA-256 hash bytes.
@@ -69,7 +115,7 @@ export class CasApi {
69
115
  */
70
116
  async retrieve(hashBytes) {
71
117
  const hash = encodeHash(hashBytes, 'base64urlnopad');
72
- const bytes = await this.#executor.retrieve(hash);
118
+ const bytes = await this.#withTimeout(this.#executor.retrieve(hash));
73
119
  if (!bytes)
74
120
  return null;
75
121
  return JSON.parse(new TextDecoder().decode(bytes));
@@ -83,7 +129,18 @@ export class CasApi {
83
129
  */
84
130
  async publish(object) {
85
131
  const bytes = new TextEncoder().encode(canonicalize(object));
86
- return await this.#executor.publish(bytes);
132
+ return await this.#withTimeout(this.#executor.publish(bytes));
133
+ }
134
+ /**
135
+ * Wraps a promise with a timeout. If `#timeoutMs` is 0, no timeout is applied.
136
+ */
137
+ #withTimeout(promise) {
138
+ if (!this.#timeoutMs)
139
+ return promise;
140
+ return new Promise((resolve, reject) => {
141
+ const timer = setTimeout(() => reject(new Error(`CAS operation timed out after ${this.#timeoutMs}ms`)), this.#timeoutMs);
142
+ promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
143
+ });
87
144
  }
88
145
  }
89
146
  //# sourceMappingURL=cas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cas.js","sourceRoot":"","sources":["../../src/cas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cas.js","sourceRoot":"","sources":["../../src/cas.ts"],"names":[],"mappings":"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"}
@@ -35,8 +35,11 @@ export declare class DidBtcr2Api {
35
35
  get btc(): BitcoinApi;
36
36
  /**
37
37
  * CAS API sub-facade (lazily initialized).
38
- * Only available when `cas` config was provided to the constructor.
39
- * @throws {Error} If the instance has been disposed or no CAS config was provided.
38
+ *
39
+ * When no `cas` config was provided to the constructor, defaults to a
40
+ * read-only {@link HttpGatewayCasExecutor} backed by the public IPFS
41
+ * gateway (`https://ipfs.io`). Override via `createApi({ cas: { ... } })`.
42
+ * @throws {Error} If the instance has been disposed.
40
43
  */
41
44
  get cas(): CasApi;
42
45
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAkB,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAA4B,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExF;;;;;;GAMG;AACH,qBAAa,WAAW;;IACtB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;gBAUhB,MAAM,CAAC,EAAE,SAAS;IAS9B;;;;OAIG;IACH,IAAI,GAAG,IAAI,UAAU,CAYpB;IAED;;;;OAIG;IACH,IAAI,GAAG,IAAI,MAAM,CAYhB;IAED;;;OAGG;IACH,IAAI,KAAK,IAAI,YAAY,CAUxB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;;;OAMG;IACH,SAAS,CACP,IAAI,EAAE,eAAe,GAAG,UAAU,EAClC,YAAY,EAAE,QAAQ,GAAG,aAAa,EACtC,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GACzC,MAAM;IAOT;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE;IAQ5G;;;;;OAKG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAKxF;;;;;;;;OAQG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiCxF;;;;;;;;OAQG;IACG,SAAS,CAAC,EACd,GAAG,EACH,OAAO,EACP,oBAAoB,EACpB,QAAQ,EACR,cAAc,EACd,eAAe,GAChB,EAAE;QACD,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,cAAc,EAAE,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA+C9B;;;;;;OAMG;IACH,OAAO,IAAI,IAAI;CAchB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,CAEzD"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAuC,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAA4B,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExF;;;;;;GAMG;AACH,qBAAa,WAAW;;IACtB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;gBAUhB,MAAM,CAAC,EAAE,SAAS;IAS9B;;;;OAIG;IACH,IAAI,GAAG,IAAI,UAAU,CAYpB;IAED;;;;;;;OAOG;IACH,IAAI,GAAG,IAAI,MAAM,CAMhB;IAED;;;OAGG;IACH,IAAI,KAAK,IAAI,YAAY,CAUxB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;;;OAMG;IACH,SAAS,CACP,IAAI,EAAE,eAAe,GAAG,UAAU,EAClC,YAAY,EAAE,QAAQ,GAAG,aAAa,EACtC,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GACzC,MAAM;IAOT;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE;IAQ5G;;;;;OAKG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAKxF;;;;;;;;OAQG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiCxF;;;;;;;;OAQG;IACG,SAAS,CAAC,EACd,GAAG,EACH,OAAO,EACP,oBAAoB,EACpB,QAAQ,EACR,cAAc,EACd,eAAe,GAChB,EAAE;QACD,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,cAAc,EAAE,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA+C9B;;;;;;OAMG;IACH,OAAO,IAAI,IAAI;CAchB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,CAEzD"}
@@ -1,5 +1,7 @@
1
1
  import type { HashBytes } from '@did-btcr2/common';
2
2
  import type { Helia } from 'helia';
3
+ /** Default IPFS HTTP gateway used for CAS reads when no CAS config is provided. */
4
+ export declare const DEFAULT_CAS_GATEWAY = "https://ipfs.io";
3
5
  /**
4
6
  * Executor interface for content-addressed storage.
5
7
  *
@@ -27,15 +29,46 @@ export declare class IpfsCasExecutor implements CasExecutor {
27
29
  retrieve(hash: string): Promise<Uint8Array | null>;
28
30
  publish(data: Uint8Array): Promise<string>;
29
31
  }
32
+ /**
33
+ * Read-only {@link CasExecutor} backed by an IPFS HTTP gateway.
34
+ *
35
+ * Converts the base64url SHA-256 hash to a CIDv1 (raw codec) and fetches
36
+ * the raw block via the
37
+ * {@link https://specs.ipfs.tech/http-gateways/trustless-gateway/ | Trustless Gateway}
38
+ * protocol.
39
+ *
40
+ * Publishing is not supported — use {@link IpfsCasExecutor} with a Helia
41
+ * instance for writes.
42
+ * @public
43
+ */
44
+ export declare class HttpGatewayCasExecutor implements CasExecutor {
45
+ #private;
46
+ constructor(gatewayUrl: string);
47
+ retrieve(hash: string): Promise<Uint8Array | null>;
48
+ publish(): Promise<string>;
49
+ }
50
+ /** Default timeout (ms) for CAS operations. */
51
+ export declare const DEFAULT_CAS_TIMEOUT_MS = 30000;
30
52
  /**
31
53
  * Configuration for the CAS (Content-Addressed Storage) driver.
54
+ *
55
+ * Provide exactly one of `executor`, `helia`, or `gateway`.
56
+ * Priority if multiple are set: `executor` > `helia` > `gateway`.
32
57
  * @public
33
58
  */
34
59
  export type CasConfig = {
35
- /** Custom executor implementation (overrides the default IPFS executor). */
60
+ /** Custom executor implementation (overrides all other options). */
36
61
  executor?: CasExecutor;
37
62
  /** Pre-existing Helia instance for the default IPFS executor. */
38
63
  helia?: Helia;
64
+ /** IPFS HTTP gateway URL for read-only CAS access (e.g. `'https://ipfs.io'`). */
65
+ gateway?: string;
66
+ /**
67
+ * Timeout in milliseconds for CAS operations. Prevents indefinite hangs
68
+ * when a Helia DHT lookup or gateway request stalls. Default: 30 000 ms.
69
+ * Set to `0` to disable.
70
+ */
71
+ timeoutMs?: number;
39
72
  };
40
73
  /**
41
74
  * Content-Addressed Storage API sub-facade.
@@ -1 +1 @@
1
- {"version":3,"file":"cas.d.ts","sourceRoot":"","sources":["../../src/cas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAMnC;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,+DAA+D;IAC/D,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,qBAAa,eAAgB,YAAW,WAAW;;gBAGrC,KAAK,EAAE,KAAK;IAIlB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAUlD,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;CAQjD;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,iEAAiE;IACjE,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,qBAAa,MAAM;;gBAGL,MAAM,EAAE,SAAS;IAa7B;;;;OAIG;IACG,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO5D;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAI/C"}
1
+ {"version":3,"file":"cas.d.ts","sourceRoot":"","sources":["../../src/cas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAMnC,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,+DAA+D;IAC/D,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,qBAAa,eAAgB,YAAW,WAAW;;gBAGrC,KAAK,EAAE,KAAK;IAIlB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAUlD,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;CAQjD;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,sBAAuB,YAAW,WAAW;;gBAG5C,UAAU,EAAE,MAAM;IAIxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAclD,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;CAMjC;AAED,+CAA+C;AAC/C,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,iEAAiE;IACjE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,qBAAa,MAAM;;gBAIL,MAAM,EAAE,SAAS;IAgB7B;;;;OAIG;IACG,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO5D;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAqB/C"}
@@ -6,7 +6,6 @@ import type { CasConfig } from './cas.js';
6
6
  /**
7
7
  * Pluggable logger interface. All methods are optional-call; the default
8
8
  * implementation is a silent no-op.
9
- * @public
10
9
  */
11
10
  export type Logger = {
12
11
  debug(message: string, ...args: unknown[]): void;
@@ -21,7 +20,6 @@ export type Logger = {
21
20
  * than a union. This local alias provides compile-time safety at the API
22
21
  * facade level. Upstream runtime validation in `Identifier.encode()` still
23
22
  * catches invalid values.
24
- * @public
25
23
  */
26
24
  export type IdType = 'KEY' | 'EXTERNAL';
27
25
  /**
@@ -35,14 +33,12 @@ export type IdType = 'KEY' | 'EXTERNAL';
35
33
  * api.resolveDid(did); // OK
36
34
  * api.btc.getTransaction(did); // Type error — DidString is not TxId
37
35
  * ```
38
- * @public
39
36
  */
40
37
  export type DidString = string & {
41
38
  readonly __brand: 'DidString';
42
39
  };
43
40
  /**
44
41
  * A branded string representing a Bitcoin transaction ID (64-char hex).
45
- * @public
46
42
  */
47
43
  export type TxId = string & {
48
44
  readonly __brand: 'TxId';
@@ -61,7 +57,6 @@ export type TxId = string & {
61
57
  * console.log(result.error, result.errorMessage);
62
58
  * }
63
59
  * ```
64
- * @public
65
60
  */
66
61
  export type ResolutionResult = {
67
62
  ok: true;
@@ -91,7 +86,6 @@ export type ResolutionResult = {
91
86
  * // Use regtest with custom RPC credentials, default REST
92
87
  * { network: 'regtest', rpc: { host: 'http://mynode:18443', username: 'u', password: 'p' } }
93
88
  * ```
94
- * @public
95
89
  */
96
90
  export type BitcoinApiConfig = {
97
91
  /** Bitcoin network name (e.g., 'regtest', 'testnet4', 'bitcoin'). */
@@ -116,7 +110,6 @@ export type BitcoinApiConfig = {
116
110
  };
117
111
  /**
118
112
  * Top-level API configuration options.
119
- * @public
120
113
  */
121
114
  export type ApiConfig = {
122
115
  btc?: BitcoinApiConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnE;;;GAGG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAC,qBAAqB,CAAC,CAAC;IAAC,GAAG,EAAE,mBAAmB,CAAA;CAAE,GACzH;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAElF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qEAAqE;IACrE,OAAO,EAAE,WAAW,CAAC;IACrB,gEAAgE;IAChE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3B,+DAA+D;IAC/D,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAC,qBAAqB,CAAC,CAAC;IAAC,GAAG,EAAE,mBAAmB,CAAA;CAAE,GACzH;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAElF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qEAAqE;IACrE,OAAO,EAAE,WAAW,CAAC;IACrB,gEAAgE;IAChE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3B,+DAA+D;IAC/D,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@did-btcr2/api",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "SDK for accessing the did:btcr2 method functionality.",
6
6
  "main": "./dist/cjs/index.js",
@@ -84,9 +84,9 @@
84
84
  "multiformats": "^13.3.1",
85
85
  "nostr-tools": "^2.15.0",
86
86
  "tiny-secp256k1": "^2.2.3",
87
- "@did-btcr2/common": "^9.0.0",
88
87
  "@did-btcr2/bitcoin": "^0.5.3",
89
88
  "@did-btcr2/cryptosuite": "^6.0.6",
89
+ "@did-btcr2/common": "^9.0.0",
90
90
  "@did-btcr2/keypair": "^0.11.4",
91
91
  "@did-btcr2/kms": "^0.4.2",
92
92
  "@did-btcr2/method": "^0.26.0"
package/src/api.ts CHANGED
@@ -6,7 +6,7 @@ import type { KeyIdentifier } from '@did-btcr2/kms';
6
6
  import type { Btcr2DidDocument, DidCreateOptions, ResolutionOptions } from '@did-btcr2/method';
7
7
  import type { DidResolutionResult } from '@web5/dids';
8
8
  import { BitcoinApi } from './bitcoin.js';
9
- import { CasApi, type CasConfig } from './cas.js';
9
+ import { CasApi, DEFAULT_CAS_GATEWAY, type CasConfig } from './cas.js';
10
10
  import { CryptoApi } from './crypto.js';
11
11
  import { DidApi } from './did.js';
12
12
  import { assertString, NOOP_LOGGER } from './helpers.js';
@@ -67,19 +67,16 @@ export class DidBtcr2Api {
67
67
 
68
68
  /**
69
69
  * CAS API sub-facade (lazily initialized).
70
- * Only available when `cas` config was provided to the constructor.
71
- * @throws {Error} If the instance has been disposed or no CAS config was provided.
70
+ *
71
+ * When no `cas` config was provided to the constructor, defaults to a
72
+ * read-only {@link HttpGatewayCasExecutor} backed by the public IPFS
73
+ * gateway (`https://ipfs.io`). Override via `createApi({ cas: { ... } })`.
74
+ * @throws {Error} If the instance has been disposed.
72
75
  */
73
76
  get cas(): CasApi {
74
77
  this.#assertNotDisposed();
75
78
  if (!this.#cas) {
76
- if (!this.#casConfig) {
77
- throw new Error(
78
- 'CAS not configured. Pass a cas config to createApi(), e.g.: '
79
- + 'createApi({ cas: { helia: await createHelia() } })'
80
- );
81
- }
82
- this.#cas = new CasApi(this.#casConfig);
79
+ this.#cas = new CasApi(this.#casConfig ?? { gateway: DEFAULT_CAS_GATEWAY });
83
80
  }
84
81
  return this.#cas;
85
82
  }
@@ -93,7 +90,7 @@ export class DidBtcr2Api {
93
90
  if (!this.#btcr2) {
94
91
  this.#btcr2 = new DidMethodApi(
95
92
  this.#btcConfig ? this.btc : undefined,
96
- this.#casConfig ? this.cas : undefined,
93
+ this.cas,
97
94
  this.#log
98
95
  );
99
96
  }
package/src/cas.ts CHANGED
@@ -6,6 +6,9 @@ import * as raw from 'multiformats/codecs/raw';
6
6
  import { create as createDigest } from 'multiformats/hashes/digest';
7
7
  import { sha256 } from 'multiformats/hashes/sha2';
8
8
 
9
+ /** Default IPFS HTTP gateway used for CAS reads when no CAS config is provided. */
10
+ export const DEFAULT_CAS_GATEWAY = 'https://ipfs.io';
11
+
9
12
  /**
10
13
  * Executor interface for content-addressed storage.
11
14
  *
@@ -55,15 +58,70 @@ export class IpfsCasExecutor implements CasExecutor {
55
58
  }
56
59
  }
57
60
 
61
+ /**
62
+ * Read-only {@link CasExecutor} backed by an IPFS HTTP gateway.
63
+ *
64
+ * Converts the base64url SHA-256 hash to a CIDv1 (raw codec) and fetches
65
+ * the raw block via the
66
+ * {@link https://specs.ipfs.tech/http-gateways/trustless-gateway/ | Trustless Gateway}
67
+ * protocol.
68
+ *
69
+ * Publishing is not supported — use {@link IpfsCasExecutor} with a Helia
70
+ * instance for writes.
71
+ * @public
72
+ */
73
+ export class HttpGatewayCasExecutor implements CasExecutor {
74
+ readonly #gatewayUrl: string;
75
+
76
+ constructor(gatewayUrl: string) {
77
+ this.#gatewayUrl = gatewayUrl.replace(/\/+$/, '');
78
+ }
79
+
80
+ async retrieve(hash: string): Promise<Uint8Array | null> {
81
+ const hashBytes = decodeHash(hash, 'base64urlnopad');
82
+ const cid = CID.create(1, raw.code, createDigest(sha256.code, hashBytes));
83
+ try {
84
+ const res = await fetch(`${this.#gatewayUrl}/ipfs/${cid.toString()}?format=raw`, {
85
+ headers : { Accept: 'application/vnd.ipld.raw' },
86
+ });
87
+ if (!res.ok) return null;
88
+ return new Uint8Array(await res.arrayBuffer());
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+
94
+ async publish(): Promise<string> {
95
+ throw new Error(
96
+ 'HttpGatewayCasExecutor is read-only. '
97
+ + 'Publishing requires a full IPFS node (use IpfsCasExecutor with Helia).'
98
+ );
99
+ }
100
+ }
101
+
102
+ /** Default timeout (ms) for CAS operations. */
103
+ export const DEFAULT_CAS_TIMEOUT_MS = 30_000;
104
+
58
105
  /**
59
106
  * Configuration for the CAS (Content-Addressed Storage) driver.
107
+ *
108
+ * Provide exactly one of `executor`, `helia`, or `gateway`.
109
+ * Priority if multiple are set: `executor` > `helia` > `gateway`.
60
110
  * @public
61
111
  */
62
112
  export type CasConfig = {
63
- /** Custom executor implementation (overrides the default IPFS executor). */
113
+ /** Custom executor implementation (overrides all other options). */
64
114
  executor?: CasExecutor;
65
115
  /** Pre-existing Helia instance for the default IPFS executor. */
66
116
  helia?: Helia;
117
+ /** IPFS HTTP gateway URL for read-only CAS access (e.g. `'https://ipfs.io'`). */
118
+ gateway?: string;
119
+ /**
120
+ * Timeout in milliseconds for CAS operations. Prevents indefinite hangs
121
+ * when a Helia DHT lookup or gateway request stalls. Default: 30 000 ms.
122
+ * Set to `0` to disable.
123
+ */
124
+ timeoutMs?: number;
67
125
  };
68
126
 
69
127
  /**
@@ -81,18 +139,22 @@ export type CasConfig = {
81
139
  */
82
140
  export class CasApi {
83
141
  readonly #executor: CasExecutor;
142
+ readonly #timeoutMs: number;
84
143
 
85
144
  constructor(config: CasConfig) {
86
145
  if (config.executor) {
87
146
  this.#executor = config.executor;
88
147
  } else if (config.helia) {
89
148
  this.#executor = new IpfsCasExecutor(config.helia);
149
+ } else if (config.gateway) {
150
+ this.#executor = new HttpGatewayCasExecutor(config.gateway);
90
151
  } else {
91
152
  throw new Error(
92
- 'CAS configuration requires either an executor or a Helia instance. '
93
- + 'Example: createApi({ cas: { helia: await createHelia() } })'
153
+ 'CAS configuration requires an executor, Helia instance, or gateway URL. '
154
+ + 'Example: createApi({ cas: { gateway: \'https://ipfs.io\' } })'
94
155
  );
95
156
  }
157
+ this.#timeoutMs = config.timeoutMs ?? DEFAULT_CAS_TIMEOUT_MS;
96
158
  }
97
159
 
98
160
  /**
@@ -102,7 +164,7 @@ export class CasApi {
102
164
  */
103
165
  async retrieve(hashBytes: HashBytes): Promise<object | null> {
104
166
  const hash = encodeHash(hashBytes, 'base64urlnopad');
105
- const bytes = await this.#executor.retrieve(hash);
167
+ const bytes = await this.#withTimeout(this.#executor.retrieve(hash));
106
168
  if (!bytes) return null;
107
169
  return JSON.parse(new TextDecoder().decode(bytes)) as object;
108
170
  }
@@ -116,6 +178,23 @@ export class CasApi {
116
178
  */
117
179
  async publish(object: object): Promise<string> {
118
180
  const bytes = new TextEncoder().encode(canonicalize(object as Record<string, any>));
119
- return await this.#executor.publish(bytes);
181
+ return await this.#withTimeout(this.#executor.publish(bytes));
182
+ }
183
+
184
+ /**
185
+ * Wraps a promise with a timeout. If `#timeoutMs` is 0, no timeout is applied.
186
+ */
187
+ #withTimeout<T>(promise: Promise<T>): Promise<T> {
188
+ if (!this.#timeoutMs) return promise;
189
+ return new Promise<T>((resolve, reject) => {
190
+ const timer = setTimeout(
191
+ () => reject(new Error(`CAS operation timed out after ${this.#timeoutMs}ms`)),
192
+ this.#timeoutMs
193
+ );
194
+ promise.then(
195
+ (val) => { clearTimeout(timer); resolve(val); },
196
+ (err) => { clearTimeout(timer); reject(err); },
197
+ );
198
+ });
120
199
  }
121
200
  }
package/src/types.ts CHANGED
@@ -7,7 +7,6 @@ import type { CasConfig } from './cas.js';
7
7
  /**
8
8
  * Pluggable logger interface. All methods are optional-call; the default
9
9
  * implementation is a silent no-op.
10
- * @public
11
10
  */
12
11
  export type Logger = {
13
12
  debug(message: string, ...args: unknown[]): void;
@@ -23,7 +22,6 @@ export type Logger = {
23
22
  * than a union. This local alias provides compile-time safety at the API
24
23
  * facade level. Upstream runtime validation in `Identifier.encode()` still
25
24
  * catches invalid values.
26
- * @public
27
25
  */
28
26
  export type IdType = 'KEY' | 'EXTERNAL';
29
27
 
@@ -38,13 +36,11 @@ export type IdType = 'KEY' | 'EXTERNAL';
38
36
  * api.resolveDid(did); // OK
39
37
  * api.btc.getTransaction(did); // Type error — DidString is not TxId
40
38
  * ```
41
- * @public
42
39
  */
43
40
  export type DidString = string & { readonly __brand: 'DidString' };
44
41
 
45
42
  /**
46
43
  * A branded string representing a Bitcoin transaction ID (64-char hex).
47
- * @public
48
44
  */
49
45
  export type TxId = string & { readonly __brand: 'TxId' };
50
46
 
@@ -62,7 +58,6 @@ export type TxId = string & { readonly __brand: 'TxId' };
62
58
  * console.log(result.error, result.errorMessage);
63
59
  * }
64
60
  * ```
65
- * @public
66
61
  */
67
62
  export type ResolutionResult =
68
63
  | { ok: true; document: Btcr2DidDocument; metadata: DidResolutionResult['didDocumentMetadata']; raw: DidResolutionResult }
@@ -85,7 +80,6 @@ export type ResolutionResult =
85
80
  * // Use regtest with custom RPC credentials, default REST
86
81
  * { network: 'regtest', rpc: { host: 'http://mynode:18443', username: 'u', password: 'p' } }
87
82
  * ```
88
- * @public
89
83
  */
90
84
  export type BitcoinApiConfig = {
91
85
  /** Bitcoin network name (e.g., 'regtest', 'testnet4', 'bitcoin'). */
@@ -111,7 +105,6 @@ export type BitcoinApiConfig = {
111
105
 
112
106
  /**
113
107
  * Top-level API configuration options.
114
- * @public
115
108
  */
116
109
  export type ApiConfig = {
117
110
  btc?: BitcoinApiConfig;