@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/.tsbuildinfo +1 -1
- package/dist/browser.js +65 -17
- package/dist/browser.mjs +65 -17
- package/dist/cjs/index.js +68 -12
- package/dist/esm/api.js +8 -9
- package/dist/esm/api.js.map +1 -1
- package/dist/esm/cas.js +61 -4
- package/dist/esm/cas.js.map +1 -1
- package/dist/types/api.d.ts +5 -2
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/cas.d.ts +34 -1
- package/dist/types/cas.d.ts.map +1 -1
- package/dist/types/types.d.ts +0 -7
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api.ts +8 -11
- package/src/cas.ts +84 -5
- package/src/types.ts +0 -7
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
|
|
62
|
-
+ 'Example: createApi({ cas: {
|
|
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
|
package/dist/esm/cas.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cas.js","sourceRoot":"","sources":["../../src/cas.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE7F,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"cas.js","sourceRoot":"","sources":["../../src/cas.ts"],"names":[],"mappings":"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"}
|
package/dist/types/api.d.ts
CHANGED
|
@@ -35,8 +35,11 @@ export declare class DidBtcr2Api {
|
|
|
35
35
|
get btc(): BitcoinApi;
|
|
36
36
|
/**
|
|
37
37
|
* CAS API sub-facade (lazily initialized).
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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
|
/**
|
package/dist/types/api.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/types/cas.d.ts
CHANGED
|
@@ -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
|
|
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.
|
package/dist/types/cas.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
*
|
|
71
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
93
|
-
+ 'Example: createApi({ cas: {
|
|
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;
|