@peac/adapter-did 0.12.6

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.
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /**
3
+ * Verification key extraction from DID Documents.
4
+ *
5
+ * Implements DD-202 (DID Verification Method Selection Policy):
6
+ * 1. Prefer methods referenced in authentication/assertionMethod
7
+ * 2. If multiple eligible Ed25519 methods remain, require caller keyId
8
+ * or fail with E_DID_KEY_AMBIGUOUS
9
+ * 3. Only Ed25519 keys extracted; other types silently skipped
10
+ * 4. Iterates ALL methods regardless of match (no early-return oracle)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.extractVerificationKey = extractVerificationKey;
14
+ const multicodec_js_1 = require("./multicodec.js");
15
+ const errors_js_1 = require("./errors.js");
16
+ // ---------------------------------------------------------------------------
17
+ // Implementation
18
+ // ---------------------------------------------------------------------------
19
+ /**
20
+ * Extract an Ed25519 public key from a DID Document.
21
+ *
22
+ * Selection policy (DD-202):
23
+ * 1. Collect all verification methods from the document
24
+ * 2. Filter by keyId if provided
25
+ * 3. Filter by relationship references (authentication/assertionMethod)
26
+ * 4. Try to extract Ed25519 key from each eligible method
27
+ * 5. If exactly one Ed25519 key found: return it
28
+ * 6. If zero found: return null
29
+ * 7. If multiple found without keyId: throw E_DID_KEY_AMBIGUOUS
30
+ *
31
+ * All methods are iterated regardless of match to prevent key-type oracle.
32
+ *
33
+ * @param document - The resolved DID Document
34
+ * @param options - Key selection options
35
+ * @returns 32-byte Ed25519 public key, or null if no suitable key found
36
+ * @throws DIDError with E_DID_KEY_AMBIGUOUS if multiple eligible keys
37
+ */
38
+ function extractVerificationKey(document, options) {
39
+ const methods = document.verificationMethod ?? [];
40
+ const relationship = options?.relationship ?? 'authentication';
41
+ const keyId = options?.keyId;
42
+ // Get relationship-referenced method IDs
43
+ const relationshipRefs = getRelationshipRefs(document, relationship);
44
+ // Collect all eligible Ed25519 keys (iterate ALL methods, no early return)
45
+ const candidates = [];
46
+ for (const method of methods) {
47
+ // Filter by keyId if specified
48
+ if (keyId && method.id !== keyId) {
49
+ continue;
50
+ }
51
+ // Prefer relationship-referenced methods if refs exist
52
+ if (relationshipRefs.length > 0 && !relationshipRefs.includes(method.id)) {
53
+ continue;
54
+ }
55
+ // Try to extract Ed25519 key (silently skip non-Ed25519)
56
+ const key = tryExtractEd25519(method);
57
+ if (key) {
58
+ candidates.push(key);
59
+ }
60
+ }
61
+ // If no relationship-referenced methods found, fall back to all methods
62
+ if (candidates.length === 0 && relationshipRefs.length > 0) {
63
+ for (const method of methods) {
64
+ if (keyId && method.id !== keyId)
65
+ continue;
66
+ const key = tryExtractEd25519(method);
67
+ if (key)
68
+ candidates.push(key);
69
+ }
70
+ }
71
+ if (candidates.length === 0) {
72
+ return null;
73
+ }
74
+ if (candidates.length === 1) {
75
+ return candidates[0];
76
+ }
77
+ // Multiple Ed25519 keys without explicit keyId selection
78
+ throw new errors_js_1.DIDError('E_DID_KEY_AMBIGUOUS', `Found ${candidates.length} eligible Ed25519 keys. Provide keyId to select.`);
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Helpers
82
+ // ---------------------------------------------------------------------------
83
+ /**
84
+ * Get verification method IDs referenced by a relationship.
85
+ */
86
+ function getRelationshipRefs(document, relationship) {
87
+ const refs = document[relationship];
88
+ if (!refs)
89
+ return [];
90
+ return refs
91
+ .map((ref) => (typeof ref === 'string' ? ref : ref.id))
92
+ .filter((id) => typeof id === 'string');
93
+ }
94
+ /**
95
+ * Try to extract an Ed25519 key from a verification method.
96
+ * Returns null for non-Ed25519 methods (no oracle).
97
+ */
98
+ function tryExtractEd25519(method) {
99
+ // Try publicKeyMultibase (Multikey / Ed25519VerificationKey2020)
100
+ if (method.publicKeyMultibase) {
101
+ try {
102
+ return (0, multicodec_js_1.extractEd25519FromMultibase)(method.publicKeyMultibase);
103
+ }
104
+ catch {
105
+ return null;
106
+ }
107
+ }
108
+ // Try publicKeyJwk (JsonWebKey2020, OKP/Ed25519)
109
+ if (method.publicKeyJwk) {
110
+ const jwk = method.publicKeyJwk;
111
+ if (jwk.kty === 'OKP' && jwk.crv === 'Ed25519' && typeof jwk.x === 'string') {
112
+ try {
113
+ // JWK x value is base64url-encoded raw key
114
+ const padded = jwk.x + '='.repeat((4 - (jwk.x.length % 4)) % 4);
115
+ const binary = atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
116
+ const bytes = new Uint8Array(binary.length);
117
+ for (let i = 0; i < binary.length; i++) {
118
+ bytes[i] = binary.charCodeAt(i);
119
+ }
120
+ if (bytes.length === 32)
121
+ return bytes;
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ return null;
128
+ }
129
+ return null;
130
+ }
131
+ //# sourceMappingURL=extract-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-key.js","sourceRoot":"","sources":["../src/extract-key.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AA8CH,wDAsDC;AAjGD,mDAA8D;AAC9D,2CAAuC;AAmBvC,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,sBAAsB,CACpC,QAAqB,EACrB,OAA2B;IAE3B,MAAM,OAAO,GAAG,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAAC;IAClD,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,gBAAgB,CAAC;IAC/D,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;IAE7B,yCAAyC;IACzC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAErE,2EAA2E;IAC3E,MAAM,UAAU,GAAiB,EAAE,CAAC;IAEpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,+BAA+B;QAC/B,IAAI,KAAK,IAAI,MAAM,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YACjC,SAAS;QACX,CAAC;QAED,uDAAuD;QACvD,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YACzE,SAAS;QACX,CAAC;QAED,yDAAyD;QACzD,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,GAAG,EAAE,CAAC;YACR,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,KAAK,IAAI,MAAM,CAAC,EAAE,KAAK,KAAK;gBAAE,SAAS;YAC3C,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,GAAG;gBAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IAED,yDAAyD;IACzD,MAAM,IAAI,oBAAQ,CAChB,qBAAqB,EACrB,SAAS,UAAU,CAAC,MAAM,kDAAkD,CAC7E,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;GAEG;AACH,SAAS,mBAAmB,CAC1B,QAAqB,EACrB,YAAkD;IAElD,MAAM,IAAI,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAErB,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SACtD,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,MAA0B;IACnD,iEAAiE;IACjE,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,IAAA,2CAA2B,EAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC;QAChC,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC5E,IAAI,CAAC;gBACH,2CAA2C;gBAC3C,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAChE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;gBAClE,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAClC,CAAC;gBACD,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE;oBAAE,OAAO,KAAK,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/index.cjs ADDED
@@ -0,0 +1,461 @@
1
+ 'use strict';
2
+
3
+ // src/resolver.ts
4
+ function createCompositeResolver(resolvers) {
5
+ return {
6
+ methods: resolvers.flatMap((r) => [...r.methods]),
7
+ async resolve(did) {
8
+ const method = extractMethod(did);
9
+ if (!method) {
10
+ return {
11
+ didDocument: null,
12
+ didResolutionMetadata: { error: "invalidDid" },
13
+ didDocumentMetadata: {}
14
+ };
15
+ }
16
+ for (const resolver of resolvers) {
17
+ if (resolver.methods.includes(method)) {
18
+ return resolver.resolve(did);
19
+ }
20
+ }
21
+ return {
22
+ didDocument: null,
23
+ didResolutionMetadata: { error: "methodNotSupported" },
24
+ didDocumentMetadata: {}
25
+ };
26
+ }
27
+ };
28
+ }
29
+ function extractMethod(did) {
30
+ if (!did.startsWith("did:")) return null;
31
+ const parts = did.split(":");
32
+ if (parts.length < 3) return null;
33
+ return parts[1];
34
+ }
35
+
36
+ // src/errors.ts
37
+ var DIDError = class extends Error {
38
+ code;
39
+ constructor(code, message) {
40
+ super(message);
41
+ this.name = "DIDError";
42
+ this.code = code;
43
+ }
44
+ };
45
+
46
+ // src/multicodec.ts
47
+ var ED25519_MULTICODEC_PREFIX = new Uint8Array([237, 1]);
48
+ var ED25519_KEY_LENGTH = 32;
49
+ var ED25519_MULTICODEC_LENGTH = ED25519_MULTICODEC_PREFIX.length + ED25519_KEY_LENGTH;
50
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
51
+ var BASE58_MAP = /* @__PURE__ */ new Map();
52
+ for (let i = 0; i < BASE58_ALPHABET.length; i++) {
53
+ BASE58_MAP.set(BASE58_ALPHABET[i], i);
54
+ }
55
+ function base58btcDecode(input) {
56
+ if (input.length === 0) return new Uint8Array(0);
57
+ let zeros = 0;
58
+ while (zeros < input.length && input[zeros] === "1") {
59
+ zeros++;
60
+ }
61
+ const size = Math.ceil(input.length * 733 / 1e3) + 1;
62
+ const b256 = new Uint8Array(size);
63
+ let length = 0;
64
+ for (let i = zeros; i < input.length; i++) {
65
+ const value = BASE58_MAP.get(input[i]);
66
+ if (value === void 0) {
67
+ throw new DIDError("E_DID_DOCUMENT_INVALID", `Invalid base58btc character: ${input[i]}`);
68
+ }
69
+ let carry = value;
70
+ for (let j = 0; j < length || carry > 0; j++) {
71
+ carry += 58 * (b256[j] || 0);
72
+ b256[j] = carry % 256;
73
+ carry = Math.floor(carry / 256);
74
+ if (j >= length) length = j + 1;
75
+ }
76
+ }
77
+ const result = new Uint8Array(zeros + length);
78
+ for (let i = 0; i < length; i++) {
79
+ result[zeros + length - 1 - i] = b256[i];
80
+ }
81
+ return result;
82
+ }
83
+ function base64urlDecode(input) {
84
+ return new Uint8Array(Buffer.from(input, "base64url"));
85
+ }
86
+ function extractEd25519FromMultibase(multibaseValue) {
87
+ if (multibaseValue.length < 2) {
88
+ throw new DIDError("E_DID_DOCUMENT_INVALID", "Multibase value too short");
89
+ }
90
+ const prefix = multibaseValue[0];
91
+ const encoded = multibaseValue.slice(1);
92
+ let decoded;
93
+ if (prefix === "z") {
94
+ decoded = base58btcDecode(encoded);
95
+ } else if (prefix === "u") {
96
+ decoded = base64urlDecode(encoded);
97
+ } else {
98
+ throw new DIDError(
99
+ "E_DID_DOCUMENT_INVALID",
100
+ `Unsupported multibase prefix: '${prefix}'. Expected 'z' (base58btc) or 'u' (base64url).`
101
+ );
102
+ }
103
+ if (decoded.length !== ED25519_MULTICODEC_LENGTH) {
104
+ throw new DIDError(
105
+ "E_DID_KEY_NOT_FOUND",
106
+ `Expected ${ED25519_MULTICODEC_LENGTH} bytes (2 prefix + 32 key), got ${decoded.length}`
107
+ );
108
+ }
109
+ if (decoded[0] !== ED25519_MULTICODEC_PREFIX[0] || decoded[1] !== ED25519_MULTICODEC_PREFIX[1]) {
110
+ throw new DIDError("E_DID_KEY_NOT_FOUND", "Multicodec prefix does not indicate Ed25519");
111
+ }
112
+ return decoded.slice(2);
113
+ }
114
+
115
+ // src/did-key.ts
116
+ var DidKeyResolver = class {
117
+ methods = ["key"];
118
+ async resolve(did) {
119
+ if (!did.startsWith("did:key:")) {
120
+ return {
121
+ didDocument: null,
122
+ didResolutionMetadata: { error: "invalidDid" },
123
+ didDocumentMetadata: {}
124
+ };
125
+ }
126
+ const multibaseValue = did.slice("did:key:".length);
127
+ if (!multibaseValue) {
128
+ return {
129
+ didDocument: null,
130
+ didResolutionMetadata: { error: "invalidDid" },
131
+ didDocumentMetadata: {}
132
+ };
133
+ }
134
+ try {
135
+ extractEd25519FromMultibase(multibaseValue);
136
+ } catch (e) {
137
+ if (e instanceof DIDError) {
138
+ return {
139
+ didDocument: null,
140
+ didResolutionMetadata: { error: e.code },
141
+ didDocumentMetadata: {}
142
+ };
143
+ }
144
+ return {
145
+ didDocument: null,
146
+ didResolutionMetadata: { error: "invalidDid" },
147
+ didDocumentMetadata: {}
148
+ };
149
+ }
150
+ const keyId = `${did}#${multibaseValue}`;
151
+ const verificationMethod = {
152
+ id: keyId,
153
+ type: "Ed25519VerificationKey2020",
154
+ controller: did,
155
+ publicKeyMultibase: multibaseValue
156
+ };
157
+ const document = {
158
+ "@context": [
159
+ "https://www.w3.org/ns/did/v1",
160
+ "https://w3id.org/security/suites/ed25519-2020/v1"
161
+ ],
162
+ id: did,
163
+ verificationMethod: [verificationMethod],
164
+ authentication: [keyId],
165
+ assertionMethod: [keyId]
166
+ };
167
+ return {
168
+ didDocument: document,
169
+ didResolutionMetadata: { contentType: "application/did+json" },
170
+ didDocumentMetadata: {}
171
+ };
172
+ }
173
+ };
174
+
175
+ // src/did-web.ts
176
+ var MAX_DOCUMENT_BYTES = 256 * 1024;
177
+ var DEFAULT_TIMEOUT_MS = 5e3;
178
+ var ACCEPTABLE_CONTENT_TYPES = /* @__PURE__ */ new Set(["application/did+json", "application/json"]);
179
+ var DidWebResolver = class {
180
+ methods = ["web"];
181
+ timeoutMs;
182
+ allowedDomains;
183
+ fetchFn;
184
+ constructor(options) {
185
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
186
+ this.allowedDomains = options.allowedDomains?.map(normalizeHostname);
187
+ this.fetchFn = options.fetchFn;
188
+ }
189
+ async resolve(did) {
190
+ if (!did.startsWith("did:web:")) {
191
+ return errorResult("invalidDid");
192
+ }
193
+ const methodSpecificId = did.slice("did:web:".length);
194
+ if (!methodSpecificId) {
195
+ return errorResult("invalidDid");
196
+ }
197
+ const urlResult = validateAndTransform(methodSpecificId);
198
+ if (!urlResult.ok) {
199
+ return errorResult("invalidDid");
200
+ }
201
+ const url = urlResult.url;
202
+ const hostname = normalizeHostname(urlResult.hostname);
203
+ if (this.allowedDomains && !this.allowedDomains.includes(hostname)) {
204
+ return errorResult("invalidDid");
205
+ }
206
+ let fetchResult;
207
+ try {
208
+ fetchResult = await this.fetchFn(url, {
209
+ timeoutMs: this.timeoutMs,
210
+ maxResponseBytes: MAX_DOCUMENT_BYTES,
211
+ maxRedirects: 0
212
+ });
213
+ } catch {
214
+ return errorResult("notFound");
215
+ }
216
+ if (!fetchResult.ok || !fetchResult.data) {
217
+ return errorResult("notFound");
218
+ }
219
+ if (fetchResult.finalUrl && fetchResult.finalUrl !== url) {
220
+ return errorResult("invalidDid");
221
+ }
222
+ if (fetchResult.contentType) {
223
+ const baseType = fetchResult.contentType.split(";")[0].trim().toLowerCase();
224
+ if (!ACCEPTABLE_CONTENT_TYPES.has(baseType)) {
225
+ return errorResult("invalidDid");
226
+ }
227
+ }
228
+ const raw = fetchResult.data;
229
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
230
+ return errorResult("invalidDid");
231
+ }
232
+ const doc = raw;
233
+ if (typeof doc.id !== "string") {
234
+ return errorResult("invalidDid");
235
+ }
236
+ if (doc.id !== did) {
237
+ return errorResult("invalidDid");
238
+ }
239
+ return {
240
+ didDocument: doc,
241
+ didResolutionMetadata: { contentType: "application/did+json" },
242
+ didDocumentMetadata: {}
243
+ };
244
+ }
245
+ };
246
+ function validateAndTransform(methodSpecificId) {
247
+ const fail = { ok: false, url: "", hostname: "" };
248
+ const parts = methodSpecificId.split(":");
249
+ if (parts.length === 0 || !parts[0]) {
250
+ return fail;
251
+ }
252
+ let authority;
253
+ try {
254
+ authority = decodeURIComponent(parts[0]);
255
+ } catch {
256
+ return fail;
257
+ }
258
+ if (!authority) {
259
+ return fail;
260
+ }
261
+ if (authority.includes("@")) {
262
+ return fail;
263
+ }
264
+ if (authority.includes("?") || authority.includes("#")) {
265
+ return fail;
266
+ }
267
+ if (authority.includes("/")) {
268
+ return fail;
269
+ }
270
+ let hostname;
271
+ try {
272
+ const testUrl = new URL(`https://${authority}`);
273
+ hostname = testUrl.hostname;
274
+ if (testUrl.username || testUrl.password) {
275
+ return fail;
276
+ }
277
+ } catch {
278
+ return fail;
279
+ }
280
+ if (isIPLiteral(hostname)) {
281
+ return fail;
282
+ }
283
+ if (!hostname) {
284
+ return fail;
285
+ }
286
+ if (parts.length > 1) {
287
+ const pathParts = parts.slice(1);
288
+ for (const segment of pathParts) {
289
+ if (!segment) {
290
+ return fail;
291
+ }
292
+ let decoded;
293
+ try {
294
+ decoded = decodeURIComponent(segment);
295
+ } catch {
296
+ return fail;
297
+ }
298
+ if (decoded.includes("/") || decoded.includes("?") || decoded.includes("#")) {
299
+ return fail;
300
+ }
301
+ }
302
+ const decodedSegments = pathParts.map(decodeURIComponent);
303
+ return {
304
+ ok: true,
305
+ url: `https://${authority}/${decodedSegments.join("/")}/did.json`,
306
+ hostname
307
+ };
308
+ }
309
+ return {
310
+ ok: true,
311
+ url: `https://${authority}/.well-known/did.json`,
312
+ hostname
313
+ };
314
+ }
315
+ function isIPLiteral(hostname) {
316
+ if (hostname.startsWith("[")) return true;
317
+ const parts = hostname.split(".");
318
+ if (parts.length === 4 && parts.every((p) => /^\d+$/.test(p))) return true;
319
+ return false;
320
+ }
321
+ function normalizeHostname(host) {
322
+ return host.toLowerCase().replace(/\.$/, "");
323
+ }
324
+ function errorResult(error) {
325
+ return {
326
+ didDocument: null,
327
+ didResolutionMetadata: { error },
328
+ didDocumentMetadata: {}
329
+ };
330
+ }
331
+
332
+ // src/extract-key.ts
333
+ function extractVerificationKey(document, options) {
334
+ const methods = document.verificationMethod ?? [];
335
+ const relationship = options?.relationship ?? "authentication";
336
+ const keyId = options?.keyId;
337
+ const relationshipRefs = getRelationshipRefs(document, relationship);
338
+ const candidates = [];
339
+ for (const method of methods) {
340
+ if (keyId && method.id !== keyId) {
341
+ continue;
342
+ }
343
+ if (relationshipRefs.length > 0 && !relationshipRefs.includes(method.id)) {
344
+ continue;
345
+ }
346
+ const key = tryExtractEd25519(method);
347
+ if (key) {
348
+ candidates.push(key);
349
+ }
350
+ }
351
+ if (candidates.length === 0 && relationshipRefs.length > 0) {
352
+ for (const method of methods) {
353
+ if (keyId && method.id !== keyId) continue;
354
+ const key = tryExtractEd25519(method);
355
+ if (key) candidates.push(key);
356
+ }
357
+ }
358
+ if (candidates.length === 0) {
359
+ return null;
360
+ }
361
+ if (candidates.length === 1) {
362
+ return candidates[0];
363
+ }
364
+ throw new DIDError(
365
+ "E_DID_KEY_AMBIGUOUS",
366
+ `Found ${candidates.length} eligible Ed25519 keys. Provide keyId to select.`
367
+ );
368
+ }
369
+ function getRelationshipRefs(document, relationship) {
370
+ const refs = document[relationship];
371
+ if (!refs) return [];
372
+ return refs.map((ref) => typeof ref === "string" ? ref : ref.id).filter((id) => typeof id === "string");
373
+ }
374
+ function tryExtractEd25519(method) {
375
+ if (method.publicKeyMultibase) {
376
+ try {
377
+ return extractEd25519FromMultibase(method.publicKeyMultibase);
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+ if (method.publicKeyJwk) {
383
+ const jwk = method.publicKeyJwk;
384
+ if (jwk.kty === "OKP" && jwk.crv === "Ed25519" && typeof jwk.x === "string") {
385
+ try {
386
+ const bytes = new Uint8Array(Buffer.from(jwk.x, "base64url"));
387
+ if (bytes.length === 32) return bytes;
388
+ } catch {
389
+ return null;
390
+ }
391
+ }
392
+ return null;
393
+ }
394
+ return null;
395
+ }
396
+
397
+ // src/cache.ts
398
+ var DEFAULT_TTL_MS = 5 * 60 * 1e3;
399
+ var DEFAULT_MAX_ENTRIES = 1e3;
400
+ var CachingResolver = class {
401
+ methods;
402
+ inner;
403
+ ttlMs;
404
+ maxEntries;
405
+ cache = /* @__PURE__ */ new Map();
406
+ constructor(inner, options) {
407
+ this.inner = inner;
408
+ this.methods = inner.methods;
409
+ this.ttlMs = options?.ttlMs ?? DEFAULT_TTL_MS;
410
+ this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES;
411
+ }
412
+ async resolve(did) {
413
+ const now = Date.now();
414
+ const cached = this.cache.get(did);
415
+ if (cached && cached.expiresAt > now) {
416
+ return deepCloneResult(cached.result);
417
+ }
418
+ if (cached) {
419
+ this.cache.delete(did);
420
+ }
421
+ const result = await this.inner.resolve(did);
422
+ if (result.didDocument !== null) {
423
+ if (this.cache.size >= this.maxEntries) {
424
+ const oldestKey = this.cache.keys().next().value;
425
+ if (oldestKey !== void 0) {
426
+ this.cache.delete(oldestKey);
427
+ }
428
+ }
429
+ this.cache.set(did, {
430
+ result: deepCloneResult(result),
431
+ expiresAt: now + this.ttlMs
432
+ });
433
+ }
434
+ return result;
435
+ }
436
+ /** Invalidate a specific cached entry */
437
+ invalidate(did) {
438
+ this.cache.delete(did);
439
+ }
440
+ /** Clear the entire cache */
441
+ clear() {
442
+ this.cache.clear();
443
+ }
444
+ /** Current number of cached entries */
445
+ get size() {
446
+ return this.cache.size;
447
+ }
448
+ };
449
+ function deepCloneResult(result) {
450
+ return structuredClone(result);
451
+ }
452
+
453
+ exports.CachingResolver = CachingResolver;
454
+ exports.DIDError = DIDError;
455
+ exports.DidKeyResolver = DidKeyResolver;
456
+ exports.DidWebResolver = DidWebResolver;
457
+ exports.createCompositeResolver = createCompositeResolver;
458
+ exports.extractEd25519FromMultibase = extractEd25519FromMultibase;
459
+ exports.extractVerificationKey = extractVerificationKey;
460
+ //# sourceMappingURL=index.cjs.map
461
+ //# sourceMappingURL=index.cjs.map