@did-btcr2/api 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.js +149910 -127351
  3. package/dist/browser.mjs +149186 -126627
  4. package/dist/cjs/api.js +210 -150
  5. package/dist/cjs/api.js.map +1 -1
  6. package/dist/cjs/bitcoin.js +110 -0
  7. package/dist/cjs/bitcoin.js.map +1 -0
  8. package/dist/cjs/cas.js +90 -0
  9. package/dist/cjs/cas.js.map +1 -0
  10. package/dist/cjs/crypto.js +425 -0
  11. package/dist/cjs/crypto.js.map +1 -0
  12. package/dist/cjs/did.js +70 -0
  13. package/dist/cjs/did.js.map +1 -0
  14. package/dist/cjs/helpers.js +28 -0
  15. package/dist/cjs/helpers.js.map +1 -0
  16. package/dist/cjs/index.js +12 -0
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/kms.js +73 -0
  19. package/dist/cjs/kms.js.map +1 -0
  20. package/dist/cjs/method.js +262 -0
  21. package/dist/cjs/method.js.map +1 -0
  22. package/dist/cjs/types.js +2 -0
  23. package/dist/cjs/types.js.map +1 -0
  24. package/dist/esm/api.js +210 -150
  25. package/dist/esm/api.js.map +1 -1
  26. package/dist/esm/bitcoin.js +110 -0
  27. package/dist/esm/bitcoin.js.map +1 -0
  28. package/dist/esm/cas.js +90 -0
  29. package/dist/esm/cas.js.map +1 -0
  30. package/dist/esm/crypto.js +425 -0
  31. package/dist/esm/crypto.js.map +1 -0
  32. package/dist/esm/did.js +70 -0
  33. package/dist/esm/did.js.map +1 -0
  34. package/dist/esm/helpers.js +28 -0
  35. package/dist/esm/helpers.js.map +1 -0
  36. package/dist/esm/index.js +12 -0
  37. package/dist/esm/index.js.map +1 -1
  38. package/dist/esm/kms.js +73 -0
  39. package/dist/esm/kms.js.map +1 -0
  40. package/dist/esm/method.js +262 -0
  41. package/dist/esm/method.js.map +1 -0
  42. package/dist/esm/types.js +2 -0
  43. package/dist/esm/types.js.map +1 -0
  44. package/dist/types/api.d.ts +107 -97
  45. package/dist/types/api.d.ts.map +1 -1
  46. package/dist/types/bitcoin.d.ts +64 -0
  47. package/dist/types/bitcoin.d.ts.map +1 -0
  48. package/dist/types/cas.d.ts +70 -0
  49. package/dist/types/cas.d.ts.map +1 -0
  50. package/dist/types/crypto.d.ts +310 -0
  51. package/dist/types/crypto.d.ts.map +1 -0
  52. package/dist/types/did.d.ts +51 -0
  53. package/dist/types/did.d.ts.map +1 -0
  54. package/dist/types/helpers.d.ts +10 -0
  55. package/dist/types/helpers.d.ts.map +1 -0
  56. package/dist/types/index.d.ts +15 -0
  57. package/dist/types/index.d.ts.map +1 -1
  58. package/dist/types/kms.d.ts +49 -0
  59. package/dist/types/kms.d.ts.map +1 -0
  60. package/dist/types/method.d.ts +117 -0
  61. package/dist/types/method.d.ts.map +1 -0
  62. package/dist/types/types.d.ts +128 -0
  63. package/dist/types/types.d.ts.map +1 -0
  64. package/package.json +7 -6
  65. package/src/api.ts +246 -262
  66. package/src/bitcoin.ts +129 -0
  67. package/src/cas.ts +121 -0
  68. package/src/crypto.ts +525 -0
  69. package/src/did.ts +75 -0
  70. package/src/helpers.ts +35 -0
  71. package/src/index.ts +37 -1
  72. package/src/kms.ts +95 -0
  73. package/src/method.ts +331 -0
  74. package/src/types.ts +122 -0
package/src/api.ts CHANGED
@@ -1,313 +1,297 @@
1
- import {
2
- BitcoinCoreRpcClient,
3
- BitcoinNetworkConnection,
4
- BitcoinRestClient,
5
- BlockV3,
6
- RawTransactionV2,
7
- RestClientConfigParams,
8
- RpcClientConfig
9
- } from '@did-btcr2/bitcoin';
10
- import type {
11
- Bytes,
12
- CryptosuiteName,
13
- DocumentBytes,
14
- Entropy,
15
- HashBytes,
16
- Hex,
17
- HexString,
18
- JSONObject,
19
- KeyBytes,
20
- PatchOperation,
21
- ProofBytes,
22
- SchnorrKeyPairObject,
23
- SignatureBytes
24
- } from '@did-btcr2/common';
25
- import { DEFAULT_BLOCK_CONFIRMATIONS, DEFAULT_REST_CONFIG, DEFAULT_RPC_CONFIG, IdentifierTypes, NotImplementedError } from '@did-btcr2/common';
26
- import type { MultikeyObject, SignedBTCR2Update } from '@did-btcr2/cryptosuite';
27
- import { SchnorrMultikey } from '@did-btcr2/cryptosuite';
28
- import { SchnorrKeyPair, Secp256k1SecretKey } from '@did-btcr2/keypair';
29
- import type { Btcr2DidDocument, DidCreateOptions, ResolutionOptions, SidecarData } from '@did-btcr2/method';
30
- import { DidBtcr2, DidDocument, DidDocumentBuilder, Identifier } from '@did-btcr2/method';
31
- import type { DidResolutionResult, DidService, DidVerificationMethod } from '@web5/dids';
32
-
33
- export { DidDocument, DidDocumentBuilder, Identifier, IdentifierTypes };
34
- export type {
35
- BlockV3,
36
- Bytes,
37
- CryptosuiteName,
38
- DidResolutionResult,
39
- DidService,
40
- DidVerificationMethod,
41
- DocumentBytes,
42
- HashBytes,
43
- Hex,
44
- JSONObject,
45
- KeyBytes,
46
- MultikeyObject,
47
- PatchOperation,
48
- ProofBytes,
49
- RawTransactionV2,
50
- RestClientConfigParams,
51
- RpcClientConfig,
52
- SchnorrKeyPairObject,
53
- SignatureBytes
54
- };
55
-
56
- /* =========================
57
- * Configuration Interfaces
58
- * ========================= */
59
-
60
- export type NetworkName = 'mainnet' | 'testnet4' | 'signet' | 'regtest';
61
-
62
- export type BitcoinApiConfig = {
63
- /** Shortcut to compute base URLs and params via @did-btcr2/bitcoin getNetwork */
64
- network?: NetworkName;
65
- /** Override REST client settings */
66
- rest?: RestClientConfigParams;
67
- /** Override RPC client settings */
68
- rpc?: RpcClientConfig;
69
- /** Default number of confirmations to consider "final" */
70
- defaultConfirmations?: number;
71
- };
72
-
73
- export type ApiConfig = {
74
- bitcoin?: BitcoinApiConfig;
75
- };
76
-
77
- /* =========================
78
- * Sub-facade: KeyPair
79
- * ========================= */
1
+ import type { NetworkName } from '@did-btcr2/bitcoin';
2
+ import type { DocumentBytes, KeyBytes, PatchOperation } from '@did-btcr2/common';
3
+ import { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
4
+ import { SchnorrKeyPair } from '@did-btcr2/keypair';
5
+ import { KeyIdentifier } from '@did-btcr2/kms';
6
+ import type { Btcr2DidDocument, DidCreateOptions, ResolutionOptions } from '@did-btcr2/method';
7
+ import type { DidResolutionResult } from '@web5/dids';
8
+ import { BitcoinApi } from './bitcoin.js';
9
+ import { CasApi, type CasConfig } from './cas.js';
10
+ import { CryptoApi } from './crypto.js';
11
+ import { DidApi } from './did.js';
12
+ import { assertString, NOOP_LOGGER } from './helpers.js';
13
+ import { KeyManagerApi } from './kms.js';
14
+ import { DidMethodApi } from './method.js';
15
+ import type { ApiConfig, BitcoinApiConfig, Logger, ResolutionResult } from './types.js';
16
+
17
+ /**
18
+ * Main DidBtcr2Api facade — the primary entry point for the SDK.
19
+ *
20
+ * Exposes sub-facades for Bitcoin, DID Method, KeyPair, Crypto, and
21
+ * KeyManager operations. Created via the {@link createApi} factory.
22
+ * @public
23
+ */
24
+ export class DidBtcr2Api {
25
+ /** Cryptographic operations (keypair, multikey, cryptosuite, proof). */
26
+ readonly crypto: CryptoApi;
27
+ /** DID identifier operations (encode, decode, generate, parse). */
28
+ readonly did: DidApi;
29
+ /** Key management operations. */
30
+ readonly kms: KeyManagerApi;
80
31
 
81
- export class KeyPairApi {
82
- /** Generate a new Schnorr keypair (secp256k1). */
83
- static generate(): SchnorrKeyPair {
84
- return new SchnorrKeyPair();
85
- }
32
+ #btcConfig?: BitcoinApiConfig;
33
+ #btc?: BitcoinApi;
34
+ #casConfig?: CasConfig;
35
+ #cas?: CasApi;
36
+ #btcr2?: DidMethodApi;
37
+ #log: Logger;
38
+ #disposed = false;
86
39
 
87
- /** Import from secret key bytes or bigint. */
88
- static fromSecret(ent: Entropy): SchnorrKeyPair {
89
- const sk = new Secp256k1SecretKey(ent);
90
- return new SchnorrKeyPair({ secretKey: sk });
40
+ constructor(config?: ApiConfig) {
41
+ this.#btcConfig = config?.btc;
42
+ this.#casConfig = config?.cas;
43
+ this.#log = config?.logger ?? NOOP_LOGGER;
44
+ this.kms = new KeyManagerApi(config?.kms);
45
+ this.did = new DidApi();
46
+ this.crypto = new CryptoApi();
91
47
  }
92
- }
93
48
 
94
- export class MultikeyApi {
95
49
  /**
96
- * Create a Schnorr Multikey wrapper (includes verificationMethod, sign/verify).
97
- * If secret is present, the multikey can sign.
50
+ * Bitcoin API sub-facade (lazily initialized).
51
+ * Only available when `btc` config was provided to the constructor.
52
+ * @throws {Error} If the instance has been disposed or no Bitcoin config was provided.
98
53
  */
99
- static create(params: {
100
- id: string;
101
- controller: string;
102
- keys: SchnorrKeyPair
103
- }): SchnorrMultikey {
104
- return new SchnorrMultikey(params);
105
- }
106
-
107
- /** Produce a DID Verification Method JSON from a multikey. */
108
- static toVerificationMethod(mk: SchnorrMultikey): DidVerificationMethod {
109
- return mk.toVerificationMethod();
110
- }
111
-
112
- /** Sign bytes via the multikey (requires secret). */
113
- static async sign(mk: SchnorrMultikey, data: Bytes): Promise<SignatureBytes> {
114
- return mk.sign(data);
115
- }
116
-
117
- /** Verify signature via multikey. */
118
- static async verify(mk: SchnorrMultikey, data: Bytes, signature: SignatureBytes): Promise<boolean> {
119
- return mk.verify(data, signature);
120
- }
121
- }
122
-
123
- /* =========================
124
- * Sub-facade: Crypto
125
- * ========================= */
126
-
127
- export class CryptoApi {
128
- public static keyPairApi = new KeyPairApi();
129
- public static multikeyApi = new MultikeyApi();
130
- }
131
-
132
- /* =========================
133
- * Sub-facade: Bitcoin
134
- * ========================= */
135
-
136
- export class BitcoinApi {
137
- readonly rest: BitcoinRestClient;
138
- readonly rpc: BitcoinCoreRpcClient;
139
- readonly defaultConfirmations: number;
140
-
141
- constructor(cfg?: BitcoinApiConfig) {
142
- const restCfg = {
143
- host : cfg?.rest?.host ?? DEFAULT_REST_CONFIG.host,
144
- ...cfg?.rest
145
- };
146
-
147
- const rpcCfg = {
148
- ...DEFAULT_RPC_CONFIG,
149
- ...cfg?.rpc
150
- };
151
-
152
- this.rest = new BitcoinRestClient(restCfg);
153
- this.rpc = new BitcoinCoreRpcClient(rpcCfg);
154
- this.defaultConfirmations = cfg?.defaultConfirmations ?? DEFAULT_BLOCK_CONFIRMATIONS;
54
+ get btc(): BitcoinApi {
55
+ this.#assertNotDisposed();
56
+ if (!this.#btc) {
57
+ if (!this.#btcConfig) {
58
+ throw new Error(
59
+ 'Bitcoin not configured. Pass a btc config to createApi(), e.g.: '
60
+ + 'createApi({ btc: { network: \'regtest\' } })'
61
+ );
62
+ }
63
+ this.#btc = new BitcoinApi(this.#btcConfig);
64
+ }
65
+ return this.#btc;
155
66
  }
156
67
 
157
- /** Fetch a transaction by txid via REST. */
158
- async getTransaction(txid: string) {
159
- return await this.rest.transaction.get(txid);
68
+ /**
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.
72
+ */
73
+ get cas(): CasApi {
74
+ this.#assertNotDisposed();
75
+ 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);
83
+ }
84
+ return this.#cas;
160
85
  }
161
86
 
162
- /** Broadcast a raw tx (hex) via REST. */
163
- async send(rawTxHex: string) {
164
- return await this.rest.transaction.send(rawTxHex);
87
+ /**
88
+ * DID Method API sub-facade (lazily initialized with bitcoin + CAS wiring).
89
+ * @throws {Error} If the instance has been disposed.
90
+ */
91
+ get btcr2(): DidMethodApi {
92
+ this.#assertNotDisposed();
93
+ if (!this.#btcr2) {
94
+ this.#btcr2 = new DidMethodApi(
95
+ this.#btcConfig ? this.btc : undefined,
96
+ this.#casConfig ? this.cas : undefined,
97
+ this.#log
98
+ );
99
+ }
100
+ return this.#btcr2;
165
101
  }
166
102
 
167
- /** Get UTXOs for an address via REST. */
168
- async getUtxos(address: string) {
169
- return await this.rest.address.getUtxos(address);
103
+ /**
104
+ * Whether this API instance has been disposed.
105
+ */
106
+ get disposed(): boolean {
107
+ return this.#disposed;
170
108
  }
171
109
 
172
- /** Get a block by hash or height via REST. */
173
- async getBlock(params: { hash?: string; height?: number }) {
174
- return await this.rest.block.get({ blockhash: params.hash, height: params.height });
110
+ /**
111
+ * Create a DID using either deterministic (KEY) or external (EXTERNAL) mode.
112
+ * @param type The creation mode.
113
+ * @param genesisBytes Public key bytes (deterministic) or document bytes (external).
114
+ * @param options Creation options (idType is set for you).
115
+ * @returns The created DID identifier string.
116
+ */
117
+ createDid(
118
+ type: 'deterministic' | 'external',
119
+ genesisBytes: KeyBytes | DocumentBytes,
120
+ options?: Omit<DidCreateOptions, 'idType'>
121
+ ): string {
122
+ this.#assertNotDisposed();
123
+ return type === 'deterministic'
124
+ ? this.btcr2.createDeterministic(genesisBytes as KeyBytes, options)
125
+ : this.btcr2.createExternal(genesisBytes as DocumentBytes, options);
175
126
  }
176
- }
177
-
178
- /* =========================
179
- * Sub-facade: KeyManager
180
- * ========================= */
181
-
182
- // export class KeyManagerApi {
183
- // readonly impl: IMethodKeyManager;
184
-
185
- // constructor(params?: ApiKeyManagerConfig) {
186
- // this.impl = new MethodKeyManager(params);
187
- // }
188
127
 
189
- // setActive(keyUri: string) {
190
- // this.impl.activeKeyUri = keyUri;
191
- // }
192
-
193
- // export(keyUri: string) {
194
- // return this.impl.export(keyUri);
195
- // }
196
-
197
- // import(mk: SchnorrMultikey, opts?: { importKey?: boolean; active?: boolean }) {
198
- // return this.impl.import(mk, opts);
199
- // }
200
-
201
- // sign(keyUri: string, hash: HashBytes): Promise<SignatureBytes> {
202
- // return this.impl.sign(keyUri, hash);
203
- // }
204
- // }
205
-
206
- /* =========================
207
- * Sub-facade: DID / CRUD
208
- * ========================= */
209
-
210
- export class DidApi {
211
128
  /**
212
- * Create a deterministic DID from a public key (bytes).
129
+ * Generate a new DID, create the keypair, and import it into the KMS.
130
+ * @param options Optional settings.
131
+ * @param options.setActive Whether to set the imported key as active in the KMS (default `true`).
132
+ * @param options.network Network for the generated DID (default `'regtest'`).
133
+ * @returns The generated DID string and KMS key identifier.
213
134
  */
214
- async createDeterministic({ genesisBytes, options }: {
215
- genesisBytes: KeyBytes;
216
- options: DidCreateOptions;
217
- }) {
218
- return DidBtcr2.create(genesisBytes, options);
135
+ generateDid(options?: { setActive?: boolean; network?: NetworkName }): { did: string; keyId: KeyIdentifier } {
136
+ this.#assertNotDisposed();
137
+ const { keyPair, did } = this.did.generate(options?.network);
138
+ const kp = SchnorrKeyPair.fromJSON(keyPair);
139
+ const keyId = this.kms.import(kp, { setActive: options?.setActive ?? true });
140
+ return { did, keyId };
219
141
  }
220
142
 
221
143
  /**
222
- * Create from an intermediate DID document (external genesis).
144
+ * Resolve a DID, automatically injecting the configured Bitcoin connection.
145
+ * @param did The DID to resolve.
146
+ * @param options Optional resolution options.
147
+ * @returns The resolution result.
223
148
  */
224
- async createExternal({ genesisBytes, options }: {
225
- genesisBytes: DocumentBytes;
226
- options: DidCreateOptions;
227
- }) {
228
- return DidBtcr2.create(genesisBytes, options);
149
+ async resolveDid(did: string, options?: ResolutionOptions): Promise<DidResolutionResult> {
150
+ this.#assertNotDisposed();
151
+ return await this.btcr2.resolve(did, options);
229
152
  }
230
153
 
231
154
  /**
232
- * Resolve DID document from DID (did:btcr2:...).
155
+ * Resolve a DID and return a discriminated result instead of throwing.
156
+ * Useful when resolution failure is an expected outcome (e.g. checking
157
+ * whether a DID exists before creating it).
158
+ * @param did The DID to resolve.
159
+ * @param options Optional resolution options.
160
+ * @returns A {@link ResolutionResult} with `ok: true` on success or
161
+ * `ok: false` with error details on failure.
233
162
  */
234
- async resolve(did: string, options: ResolutionOptions): Promise<DidResolutionResult> {
235
- return await DidBtcr2.resolve(did, options);
163
+ async tryResolveDid(did: string, options?: ResolutionOptions): Promise<ResolutionResult> {
164
+ this.#assertNotDisposed();
165
+ assertString(did, 'did');
166
+ try {
167
+ const raw = await this.btcr2.resolve(did, options);
168
+ if (raw.didDocument) {
169
+ return {
170
+ ok : true,
171
+ document : raw.didDocument as Btcr2DidDocument,
172
+ metadata : raw.didDocumentMetadata,
173
+ raw,
174
+ };
175
+ }
176
+ return {
177
+ ok : false,
178
+ error : raw.didResolutionMetadata?.error ?? 'unknown',
179
+ errorMessage : raw.didResolutionMetadata?.errorMessage as string | undefined,
180
+ raw,
181
+ };
182
+ } catch (err: any) {
183
+ return {
184
+ ok : false,
185
+ error : 'internalError',
186
+ errorMessage : err.message,
187
+ raw : {
188
+ didDocument : null,
189
+ didDocumentMetadata : {},
190
+ didResolutionMetadata : { error: 'internalError', errorMessage: err.message },
191
+ } as unknown as DidResolutionResult,
192
+ };
193
+ }
236
194
  }
237
195
 
238
196
  /**
239
- * Update a DID Document using a JSON Patch, signed as capabilityInvocation.
240
- * You provide the prior DID Document (to pick VM), a JSON Patch, and a signer multikey.
241
- * This delegates to MethodUpdate (which follows the cryptosuite rules internally).
197
+ * Update a DID document: resolve the current state, apply patches, sign, and announce.
198
+ * Automatically injects the configured Bitcoin connection.
199
+ *
200
+ * If `sourceDocument` and `sourceVersionId` are both provided, resolution
201
+ * is skipped. Otherwise the DID is resolved first to obtain them.
202
+ * @param params The update parameters.
203
+ * @returns The signed update.
242
204
  */
243
- async update({
244
- sourceDocument,
205
+ async updateDid({
206
+ did,
245
207
  patches,
246
- sourceVersionId,
247
208
  verificationMethodId,
248
209
  beaconId,
249
- signingMaterial,
250
- bitcoin,
210
+ sourceDocument,
211
+ sourceVersionId,
251
212
  }: {
252
- sourceDocument: Btcr2DidDocument;
213
+ did: string;
253
214
  patches: PatchOperation[];
254
- sourceVersionId: number;
255
215
  verificationMethodId: string;
256
216
  beaconId: string;
257
- signingMaterial?: KeyBytes | HexString;
258
- bitcoin?: BitcoinNetworkConnection;
217
+ sourceDocument?: Btcr2DidDocument;
218
+ sourceVersionId?: number;
259
219
  }): Promise<SignedBTCR2Update> {
260
- // The Update class exposes the algorithm that creates a DID Update Payload and proof;
261
- // keep this wrapper narrow so testing can mock MethodUpdate directly.
262
- return await DidBtcr2.update({
263
- sourceDocument,
220
+ this.#assertNotDisposed();
221
+ assertString(did, 'did');
222
+
223
+ let doc = sourceDocument;
224
+ let versionId = sourceVersionId;
225
+
226
+ if (!doc || versionId === undefined) {
227
+ const resolution = await this.resolveDid(did);
228
+ if (!resolution.didDocument) {
229
+ const meta = resolution.didResolutionMetadata;
230
+ const detail = meta?.error ? `: ${meta.error}` : '.';
231
+ const extra = meta?.errorMessage ? ` ${meta.errorMessage}` : '';
232
+ throw new Error(
233
+ `Failed to resolve DID ${did} for update${detail}${extra}`,
234
+ { cause: meta }
235
+ );
236
+ }
237
+ doc = doc ?? resolution.didDocument as Btcr2DidDocument;
238
+
239
+ if (versionId === undefined) {
240
+ const rawVersionId = resolution.didDocumentMetadata?.versionId;
241
+ if (rawVersionId === undefined || rawVersionId === null) {
242
+ throw new Error(
243
+ `Resolution of DID ${did} succeeded but returned no versionId in metadata. `
244
+ + 'Provide sourceVersionId explicitly.'
245
+ );
246
+ }
247
+ const parsed = Number(rawVersionId);
248
+ if (!Number.isFinite(parsed)) {
249
+ throw new Error(
250
+ `Resolution of DID ${did} returned a non-numeric versionId: ${String(rawVersionId)}.`
251
+ );
252
+ }
253
+ versionId = parsed;
254
+ }
255
+ }
256
+
257
+ return await this.btcr2.update({
258
+ sourceDocument : doc,
264
259
  patches,
265
- sourceVersionId,
260
+ sourceVersionId : versionId,
266
261
  verificationMethodId,
267
262
  beaconId,
268
- signingMaterial,
269
- bitcoin,
270
263
  });
271
264
  }
272
265
 
273
- /** Deactivate convenience: applies the standard `deactivated: true` patch. */
274
- async deactivate(): Promise<SidecarData> {
275
- // This class is a stub in method right now; expose a narrow wrapper for future expansion.
276
- // return DidBtcr2.deactivate({ identifier, patch }); // No-op holder; implement when core adds behavior.
277
- throw new NotImplementedError(
278
- 'DidApi.deactivate is not implemented yet.',
279
- {
280
- type : 'DID_API_METHOD_NOT_IMPLEMENTED',
281
- name : 'NOT_IMPLEMENTED_ERROR'
282
- }
283
- );
266
+ /**
267
+ * Release internal references. After disposal, accessing `btc`, `btcr2`,
268
+ * or calling top-level methods will throw.
269
+ *
270
+ * Note: the underlying {@link BitcoinConnection} does not hold persistent
271
+ * connections, so this is primarily a guard against accidental reuse.
272
+ */
273
+ dispose(): void {
274
+ this.#btc = undefined;
275
+ this.#cas = undefined;
276
+ this.#btcr2 = undefined;
277
+ this.#btcConfig = undefined;
278
+ this.#casConfig = undefined;
279
+ this.#disposed = true;
284
280
  }
285
- }
286
-
287
- /* =========================
288
- * Root facade
289
- * ========================= */
290
281
 
291
- export class DidBtcr2Api {
292
- readonly bitcoin: BitcoinApi;
293
- readonly did: DidApi;
294
- readonly keys: KeyPairApi;
295
- readonly crypto: CryptoApi;
296
- // readonly keyManager: KeyManagerApi;
297
-
298
- constructor(config?: ApiConfig) {
299
- this.bitcoin = new BitcoinApi(config?.bitcoin);
300
- this.did = new DidApi();
301
- this.keys = new KeyPairApi();
302
- this.crypto = new CryptoApi();
303
- // this.keyManager = new KeyManagerApi(config?.keyManager);
282
+ #assertNotDisposed(): void {
283
+ if (this.#disposed) {
284
+ throw new Error('This DidBtcr2Api instance has been disposed and can no longer be used.');
285
+ }
304
286
  }
305
287
  }
306
288
 
307
- /* =========================
308
- * Factory
309
- * ========================= */
310
-
311
- export function createApi(config?: ApiConfig) {
289
+ /**
290
+ * Create a new {@link DidBtcr2Api} instance with the given configuration.
291
+ * @param config Optional configuration for the API.
292
+ * @returns The created DidBtcr2Api instance.
293
+ * @public
294
+ */
295
+ export function createApi(config?: ApiConfig): DidBtcr2Api {
312
296
  return new DidBtcr2Api(config);
313
297
  }