@awiki/anp-typescript-sdk 0.2.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/index.js ADDED
@@ -0,0 +1,4868 @@
1
+ import bs58 from 'bs58';
2
+ import { randomBytes, verify, ECDH, sign, createHash, randomUUID, createPublicKey, createPrivateKey, createECDH, generateKeyPairSync } from 'crypto';
3
+ import { x25519, ed25519 } from '@noble/curves/ed25519';
4
+ import { isIP } from 'net';
5
+ import canonicalize from 'canonicalize';
6
+ import { readFile, mkdir, writeFile, rename, chmod, unlink } from 'fs/promises';
7
+ import { jwtVerify, SignJWT, importPKCS8, importSPKI } from 'jose';
8
+ import { dirname } from 'path';
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+
16
+ // src/errors/index.ts
17
+ var ANPError = class extends Error {
18
+ constructor(message, code, cause) {
19
+ super(message, { cause });
20
+ this.code = code;
21
+ this.name = "ANPError";
22
+ }
23
+ };
24
+ var CryptoError = class extends ANPError {
25
+ constructor(message, cause) {
26
+ super(message, "CRYPTO_ERROR", cause);
27
+ this.name = "CryptoError";
28
+ }
29
+ };
30
+ var AuthenticationError = class extends ANPError {
31
+ constructor(message, cause) {
32
+ super(message, "AUTHENTICATION_ERROR", cause);
33
+ this.name = "AuthenticationError";
34
+ }
35
+ };
36
+ var ProofError = class extends ANPError {
37
+ constructor(message, cause) {
38
+ super(message, "PROOF_ERROR", cause);
39
+ this.name = "ProofError";
40
+ }
41
+ };
42
+ var NetworkError = class extends ANPError {
43
+ constructor(message, statusCode, cause) {
44
+ super(message, "NETWORK_ERROR", cause);
45
+ this.statusCode = statusCode;
46
+ this.name = "NetworkError";
47
+ }
48
+ };
49
+ var WnsError = class extends ANPError {
50
+ constructor(message, code = "WNS_ERROR", cause) {
51
+ super(message, code, cause);
52
+ this.name = "WnsError";
53
+ }
54
+ };
55
+ var HandleValidationError = class extends WnsError {
56
+ constructor(message) {
57
+ super(message, "HANDLE_VALIDATION_ERROR");
58
+ this.name = "HandleValidationError";
59
+ }
60
+ };
61
+ var HandleResolutionError = class extends WnsError {
62
+ constructor(message, statusCode, cause) {
63
+ super(message, "HANDLE_RESOLUTION_ERROR", cause);
64
+ this.statusCode = statusCode;
65
+ this.name = "HandleResolutionError";
66
+ }
67
+ };
68
+ var HandleNotFoundError = class extends HandleResolutionError {
69
+ constructor(message) {
70
+ super(message, 404);
71
+ this.name = "HandleNotFoundError";
72
+ }
73
+ };
74
+ var HandleGoneError = class extends HandleResolutionError {
75
+ constructor(message) {
76
+ super(message, 410);
77
+ this.name = "HandleGoneError";
78
+ }
79
+ };
80
+ var HandleMovedError = class extends HandleResolutionError {
81
+ constructor(message, redirectUrl = "") {
82
+ super(message, 301);
83
+ this.redirectUrl = redirectUrl;
84
+ this.name = "HandleMovedError";
85
+ }
86
+ };
87
+ var HandleBindingError = class extends WnsError {
88
+ constructor(message) {
89
+ super(message, "HANDLE_BINDING_ERROR");
90
+ this.name = "HandleBindingError";
91
+ }
92
+ };
93
+ var WbaUriParseError = class extends WnsError {
94
+ constructor(message) {
95
+ super(message, "WBA_URI_PARSE_ERROR");
96
+ this.name = "WbaUriParseError";
97
+ }
98
+ };
99
+
100
+ // src/authentication/types.ts
101
+ var DidProfile = /* @__PURE__ */ ((DidProfile2) => {
102
+ DidProfile2["E1"] = "e1";
103
+ DidProfile2["K1"] = "k1";
104
+ DidProfile2["PlainLegacy"] = "plain_legacy";
105
+ return DidProfile2;
106
+ })(DidProfile || {});
107
+ var AuthMode = /* @__PURE__ */ ((AuthMode2) => {
108
+ AuthMode2["HttpSignatures"] = "http_signatures";
109
+ AuthMode2["LegacyDidWba"] = "legacy_didwba";
110
+ AuthMode2["Auto"] = "auto";
111
+ return AuthMode2;
112
+ })(AuthMode || {});
113
+
114
+ // src/internal/base64.ts
115
+ function encodeBase64Url(value) {
116
+ return Buffer.from(value).toString("base64url");
117
+ }
118
+ function decodeBase64Url(value) {
119
+ return new Uint8Array(Buffer.from(value, "base64url"));
120
+ }
121
+ function encodeBase64(value) {
122
+ return Buffer.from(value).toString("base64");
123
+ }
124
+ function decodeBase64(value) {
125
+ return new Uint8Array(Buffer.from(value, "base64"));
126
+ }
127
+
128
+ // src/internal/pem.ts
129
+ var PEM_LINE_LENGTH = 64;
130
+ function encodePem(label, bytes) {
131
+ const encoded = encodeBase64(bytes);
132
+ const lines = [];
133
+ for (let index = 0; index < encoded.length; index += PEM_LINE_LENGTH) {
134
+ lines.push(encoded.slice(index, index + PEM_LINE_LENGTH));
135
+ }
136
+ return `-----BEGIN ${label}-----
137
+ ${lines.join("\n")}
138
+ -----END ${label}-----
139
+ `;
140
+ }
141
+ function decodePem(input) {
142
+ const lines = input.trim().split(/\r?\n/);
143
+ if (lines.length < 3) {
144
+ throw new Error("Invalid PEM structure");
145
+ }
146
+ const beginLine = lines[0];
147
+ const endLine = lines.at(-1);
148
+ if (!beginLine.startsWith("-----BEGIN ") || !beginLine.endsWith("-----")) {
149
+ throw new Error("Invalid PEM structure");
150
+ }
151
+ const label = beginLine.slice("-----BEGIN ".length, -"-----".length);
152
+ if (endLine !== `-----END ${label}-----`) {
153
+ throw new Error("Invalid PEM structure");
154
+ }
155
+ const body = lines.slice(1, -1).join("");
156
+ return {
157
+ label,
158
+ bytes: decodeBase64(body)
159
+ };
160
+ }
161
+
162
+ // src/internal/keys.ts
163
+ var PRIVATE_LABELS = {
164
+ secp256k1: "ANP SECP256K1 PRIVATE KEY",
165
+ secp256r1: "ANP SECP256R1 PRIVATE KEY",
166
+ ed25519: "ANP ED25519 PRIVATE KEY",
167
+ x25519: "ANP X25519 PRIVATE KEY"
168
+ };
169
+ var PUBLIC_LABELS = {
170
+ secp256k1: "ANP SECP256K1 PUBLIC KEY",
171
+ secp256r1: "ANP SECP256R1 PUBLIC KEY",
172
+ ed25519: "ANP ED25519 PUBLIC KEY",
173
+ x25519: "ANP X25519 PUBLIC KEY"
174
+ };
175
+ var EC_CURVES = {
176
+ secp256k1: "secp256k1",
177
+ secp256r1: "prime256v1"
178
+ };
179
+ function sha256(value) {
180
+ return new Uint8Array(createHash("sha256").update(value).digest());
181
+ }
182
+ function normalizePrivateKeyMaterial(input) {
183
+ return typeof input === "string" ? privateKeyFromPem(input) : input;
184
+ }
185
+ function normalizePublicKeyMaterial(input) {
186
+ return typeof input === "string" ? publicKeyFromPem(input) : input;
187
+ }
188
+ function generatePrivateKeyMaterial(type) {
189
+ switch (type) {
190
+ case "secp256k1": {
191
+ const { privateKey } = generateKeyPairSync("ec", { namedCurve: "secp256k1" });
192
+ const jwk = privateKey.export({ format: "jwk" });
193
+ return { type, bytes: requireBase64UrlBytes(jwk.d, "Missing secp256k1 private key") };
194
+ }
195
+ case "secp256r1": {
196
+ const { privateKey } = generateKeyPairSync("ec", { namedCurve: "prime256v1" });
197
+ const jwk = privateKey.export({ format: "jwk" });
198
+ return { type, bytes: requireBase64UrlBytes(jwk.d, "Missing secp256r1 private key") };
199
+ }
200
+ case "ed25519": {
201
+ const { privateKey } = generateKeyPairSync("ed25519");
202
+ const jwk = privateKey.export({ format: "jwk" });
203
+ return { type, bytes: requireBase64UrlBytes(jwk.d, "Missing ed25519 private key") };
204
+ }
205
+ case "x25519": {
206
+ const { privateKey } = generateKeyPairSync("x25519");
207
+ const jwk = privateKey.export({ format: "jwk" });
208
+ return { type, bytes: requireBase64UrlBytes(jwk.d, "Missing x25519 private key") };
209
+ }
210
+ default:
211
+ throw new CryptoError(`Unsupported key type: ${String(type)}`);
212
+ }
213
+ }
214
+ function derivePublicKey(privateKey) {
215
+ switch (privateKey.type) {
216
+ case "secp256k1":
217
+ case "secp256r1": {
218
+ const ecdh = createECDH(EC_CURVES[privateKey.type]);
219
+ ecdh.setPrivateKey(Buffer.from(privateKey.bytes));
220
+ return {
221
+ type: privateKey.type,
222
+ bytes: new Uint8Array(ecdh.getPublicKey(void 0, "compressed"))
223
+ };
224
+ }
225
+ case "ed25519":
226
+ return { type: "ed25519", bytes: ed25519.getPublicKey(privateKey.bytes) };
227
+ case "x25519":
228
+ return { type: "x25519", bytes: x25519.getPublicKey(privateKey.bytes) };
229
+ default:
230
+ throw new CryptoError(`Unsupported key type: ${String(privateKey.type)}`);
231
+ }
232
+ }
233
+ function generateKeyPairPem(type) {
234
+ const privateKey = generatePrivateKeyMaterial(type);
235
+ const publicKey = derivePublicKey(privateKey);
236
+ return {
237
+ privateKey,
238
+ publicKey,
239
+ pair: {
240
+ privateKeyPem: privateKeyToPem(privateKey),
241
+ publicKeyPem: publicKeyToPem(publicKey)
242
+ }
243
+ };
244
+ }
245
+ function privateKeyToPem(privateKey) {
246
+ return encodePem(PRIVATE_LABELS[privateKey.type], privateKey.bytes);
247
+ }
248
+ function publicKeyToPem(publicKey) {
249
+ return encodePem(PUBLIC_LABELS[publicKey.type], publicKey.bytes);
250
+ }
251
+ function privateKeyFromPem(input) {
252
+ const decoded = decodePem(input);
253
+ return { type: keyTypeFromLabel(decoded.label, true), bytes: decoded.bytes };
254
+ }
255
+ function publicKeyFromPem(input) {
256
+ const decoded = decodePem(input);
257
+ return { type: keyTypeFromLabel(decoded.label, false), bytes: decoded.bytes };
258
+ }
259
+ function signMessage(privateKey, message) {
260
+ const keyObject = toPrivateKeyObject(privateKey);
261
+ switch (privateKey.type) {
262
+ case "secp256k1":
263
+ case "secp256r1":
264
+ return new Uint8Array(
265
+ sign("sha256", Buffer.from(message), {
266
+ key: keyObject,
267
+ dsaEncoding: "ieee-p1363"
268
+ })
269
+ );
270
+ case "ed25519":
271
+ return new Uint8Array(sign(null, Buffer.from(message), keyObject));
272
+ case "x25519":
273
+ throw new CryptoError("X25519 keys cannot be used for signing");
274
+ default:
275
+ throw new CryptoError(`Unsupported key type: ${String(privateKey.type)}`);
276
+ }
277
+ }
278
+ function verifyMessage(publicKey, message, signature) {
279
+ const keyObject = toPublicKeyObject(publicKey);
280
+ switch (publicKey.type) {
281
+ case "secp256k1":
282
+ case "secp256r1":
283
+ return verify(
284
+ "sha256",
285
+ Buffer.from(message),
286
+ { key: keyObject, dsaEncoding: "ieee-p1363" },
287
+ Buffer.from(signature)
288
+ );
289
+ case "ed25519":
290
+ return verify(null, Buffer.from(message), keyObject, Buffer.from(signature));
291
+ case "x25519":
292
+ throw new CryptoError("X25519 keys cannot be used for signature verification");
293
+ default:
294
+ throw new CryptoError(`Unsupported key type: ${String(publicKey.type)}`);
295
+ }
296
+ }
297
+ function publicKeyToJwk(publicKey) {
298
+ switch (publicKey.type) {
299
+ case "secp256k1":
300
+ case "secp256r1": {
301
+ const uncompressed = new Uint8Array(
302
+ ECDH.convertKey(
303
+ Buffer.from(publicKey.bytes),
304
+ EC_CURVES[publicKey.type],
305
+ void 0,
306
+ void 0,
307
+ "uncompressed"
308
+ )
309
+ );
310
+ if (uncompressed.length !== 65 || uncompressed[0] !== 4) {
311
+ throw new AuthenticationError("Invalid EC public key");
312
+ }
313
+ return {
314
+ kty: "EC",
315
+ crv: publicKey.type === "secp256k1" ? "secp256k1" : "P-256",
316
+ x: encodeBase64Url(uncompressed.slice(1, 33)),
317
+ y: encodeBase64Url(uncompressed.slice(33, 65))
318
+ };
319
+ }
320
+ case "ed25519":
321
+ return { kty: "OKP", crv: "Ed25519", x: encodeBase64Url(publicKey.bytes) };
322
+ case "x25519":
323
+ return { kty: "OKP", crv: "X25519", x: encodeBase64Url(publicKey.bytes) };
324
+ default:
325
+ throw new AuthenticationError("Unsupported public key type");
326
+ }
327
+ }
328
+ function computeJwkThumbprint(jwk) {
329
+ const ordered = Object.keys(jwk).sort().reduce((result, key) => {
330
+ const value = jwk[key];
331
+ if (typeof value === "string") {
332
+ result[key] = value;
333
+ }
334
+ return result;
335
+ }, {});
336
+ return encodeBase64Url(sha256(new TextEncoder().encode(JSON.stringify(ordered))));
337
+ }
338
+ function ed25519PublicKeyToMultibase(publicKey) {
339
+ return `z${bs58.encode(Buffer.concat([Buffer.from([237, 1]), Buffer.from(publicKey)]))}`;
340
+ }
341
+ function x25519PublicKeyToMultibase(publicKey) {
342
+ return `z${bs58.encode(Buffer.concat([Buffer.from([236, 1]), Buffer.from(publicKey)]))}`;
343
+ }
344
+ function parseEd25519Multibase(value) {
345
+ const bytes = bs58.decode(stripMultibasePrefix(value));
346
+ const normalized = bytes.length === 34 && bytes[0] === 237 && bytes[1] === 1 ? bytes.slice(2) : bytes;
347
+ if (normalized.length !== 32) {
348
+ throw new AuthenticationError("Invalid Ed25519 multibase value");
349
+ }
350
+ return { type: "ed25519", bytes: new Uint8Array(normalized) };
351
+ }
352
+ function parseX25519Multibase(value) {
353
+ const bytes = bs58.decode(stripMultibasePrefix(value));
354
+ const normalized = bytes.length === 34 && bytes[0] === 236 && bytes[1] === 1 ? bytes.slice(2) : bytes;
355
+ if (normalized.length !== 32) {
356
+ throw new AuthenticationError("Invalid X25519 multibase value");
357
+ }
358
+ return { type: "x25519", bytes: new Uint8Array(normalized) };
359
+ }
360
+ function publicKeyFromJwk(jwk) {
361
+ if (jwk.kty === "EC" && jwk.x && jwk.y) {
362
+ const curve = jwk.crv;
363
+ if (curve !== "secp256k1" && curve !== "P-256") {
364
+ throw new AuthenticationError(`Unsupported EC curve: ${curve}`);
365
+ }
366
+ const keySize = 32;
367
+ const uncompressed = Buffer.concat([
368
+ Buffer.from([4]),
369
+ leftPadCoordinate(decodeBase64Url(jwk.x), keySize),
370
+ leftPadCoordinate(decodeBase64Url(jwk.y), keySize)
371
+ ]);
372
+ const bytes = new Uint8Array(
373
+ ECDH.convertKey(
374
+ uncompressed,
375
+ curve === "secp256k1" ? "secp256k1" : "prime256v1",
376
+ void 0,
377
+ void 0,
378
+ "compressed"
379
+ )
380
+ );
381
+ return { type: curve === "secp256k1" ? "secp256k1" : "secp256r1", bytes };
382
+ }
383
+ if (jwk.kty === "OKP" && jwk.x) {
384
+ if (jwk.crv === "Ed25519") {
385
+ return { type: "ed25519", bytes: decodeBase64Url(jwk.x) };
386
+ }
387
+ if (jwk.crv === "X25519") {
388
+ return { type: "x25519", bytes: decodeBase64Url(jwk.x) };
389
+ }
390
+ }
391
+ throw new AuthenticationError("Unsupported JWK key material");
392
+ }
393
+ function leftPadCoordinate(value, size) {
394
+ if (value.length > size) {
395
+ throw new AuthenticationError("Invalid EC public key coordinate length");
396
+ }
397
+ if (value.length === size) {
398
+ return Buffer.from(value);
399
+ }
400
+ return Buffer.concat([Buffer.alloc(size - value.length), Buffer.from(value)]);
401
+ }
402
+ function toPrivateKeyObject(privateKey) {
403
+ switch (privateKey.type) {
404
+ case "secp256k1":
405
+ case "secp256r1": {
406
+ const ecdh = createECDH(EC_CURVES[privateKey.type]);
407
+ ecdh.setPrivateKey(Buffer.from(privateKey.bytes));
408
+ const publicKey = new Uint8Array(ecdh.getPublicKey(void 0, "uncompressed"));
409
+ return createPrivateKey({
410
+ key: {
411
+ kty: "EC",
412
+ crv: privateKey.type === "secp256k1" ? "secp256k1" : "P-256",
413
+ d: encodeBase64Url(privateKey.bytes),
414
+ x: encodeBase64Url(publicKey.slice(1, 33)),
415
+ y: encodeBase64Url(publicKey.slice(33, 65))
416
+ },
417
+ format: "jwk"
418
+ });
419
+ }
420
+ case "ed25519": {
421
+ const publicKey = ed25519.getPublicKey(privateKey.bytes);
422
+ return createPrivateKey({
423
+ key: {
424
+ kty: "OKP",
425
+ crv: "Ed25519",
426
+ d: encodeBase64Url(privateKey.bytes),
427
+ x: encodeBase64Url(publicKey)
428
+ },
429
+ format: "jwk"
430
+ });
431
+ }
432
+ case "x25519": {
433
+ const publicKey = x25519.getPublicKey(privateKey.bytes);
434
+ return createPrivateKey({
435
+ key: {
436
+ kty: "OKP",
437
+ crv: "X25519",
438
+ d: encodeBase64Url(privateKey.bytes),
439
+ x: encodeBase64Url(publicKey)
440
+ },
441
+ format: "jwk"
442
+ });
443
+ }
444
+ default:
445
+ throw new CryptoError(`Unsupported key type: ${String(privateKey.type)}`);
446
+ }
447
+ }
448
+ function toPublicKeyObject(publicKey) {
449
+ return createPublicKey({
450
+ key: publicKeyToJwk(publicKey),
451
+ format: "jwk"
452
+ });
453
+ }
454
+ function keyTypeFromLabel(label, isPrivate) {
455
+ const source = isPrivate ? PRIVATE_LABELS : PUBLIC_LABELS;
456
+ for (const [type, candidate] of Object.entries(source)) {
457
+ if (candidate === label) {
458
+ return type;
459
+ }
460
+ }
461
+ throw new AuthenticationError(`Unsupported PEM label: ${label}`);
462
+ }
463
+ function requireBase64UrlBytes(value, message) {
464
+ if (!value) {
465
+ throw new AuthenticationError(message);
466
+ }
467
+ return decodeBase64Url(value);
468
+ }
469
+ function stripMultibasePrefix(value) {
470
+ return value.startsWith("z") ? value.slice(1) : value;
471
+ }
472
+
473
+ // src/authentication/verification-methods.ts
474
+ var VerificationMethod = class {
475
+ constructor(id, methodType, publicKey) {
476
+ this.id = id;
477
+ this.methodType = methodType;
478
+ this.publicKey = publicKey;
479
+ }
480
+ verifySignature(content, signature) {
481
+ return verifyMessage(this.publicKey, content, decodeBase64Url(signature));
482
+ }
483
+ encodeSignature(signatureBytes) {
484
+ if (this.publicKey.type === "x25519") {
485
+ throw new AuthenticationError("X25519 cannot encode signatures");
486
+ }
487
+ return encodeBase64Url(signatureBytes);
488
+ }
489
+ };
490
+ function createVerificationMethod(method) {
491
+ if (!method.type) {
492
+ throw new AuthenticationError("Missing verification method type");
493
+ }
494
+ return new VerificationMethod(method.id ?? "", method.type, extractPublicKey(method));
495
+ }
496
+ function extractPublicKey(method) {
497
+ switch (method.type) {
498
+ case "EcdsaSecp256k1VerificationKey2019":
499
+ return extractEcPublicKey(method, "secp256k1");
500
+ case "EcdsaSecp256r1VerificationKey2019":
501
+ return extractEcPublicKey(method, "P-256");
502
+ case "Ed25519VerificationKey2018":
503
+ case "Ed25519VerificationKey2020":
504
+ case "Multikey":
505
+ return extractEd25519PublicKey(method);
506
+ case "X25519KeyAgreementKey2019":
507
+ return extractX25519PublicKey(method);
508
+ case "JsonWebKey2020":
509
+ if (!method.publicKeyJwk) {
510
+ throw new AuthenticationError("Missing key material");
511
+ }
512
+ return publicKeyFromJwk(method.publicKeyJwk);
513
+ default:
514
+ throw new AuthenticationError(`Unsupported verification method type: ${method.type}`);
515
+ }
516
+ }
517
+ function extractEcPublicKey(method, expectedCurve) {
518
+ if (method.publicKeyJwk) {
519
+ const publicKey = publicKeyFromJwk(method.publicKeyJwk);
520
+ const actualCurve = publicKey.type === "secp256k1" ? "secp256k1" : "P-256";
521
+ if (actualCurve !== expectedCurve) {
522
+ throw new AuthenticationError("Invalid JWK parameters");
523
+ }
524
+ return publicKey;
525
+ }
526
+ if (method.publicKeyMultibase) {
527
+ return {
528
+ type: expectedCurve === "secp256k1" ? "secp256k1" : "secp256r1",
529
+ bytes: new Uint8Array(bs58.decode(stripMultibasePrefix2(method.publicKeyMultibase)))
530
+ };
531
+ }
532
+ throw new AuthenticationError("Missing key material");
533
+ }
534
+ function extractEd25519PublicKey(method) {
535
+ if (method.publicKeyJwk) {
536
+ return publicKeyFromJwk(method.publicKeyJwk);
537
+ }
538
+ if (method.publicKeyMultibase) {
539
+ return parseEd25519Multibase(method.publicKeyMultibase);
540
+ }
541
+ if (method.publicKeyBase58) {
542
+ const bytes = bs58.decode(method.publicKeyBase58);
543
+ if (bytes.length !== 32) {
544
+ throw new AuthenticationError("Invalid Ed25519 publicKeyBase58");
545
+ }
546
+ return {
547
+ type: "ed25519",
548
+ bytes: new Uint8Array(bytes)
549
+ };
550
+ }
551
+ throw new AuthenticationError("Missing key material");
552
+ }
553
+ function extractX25519PublicKey(method) {
554
+ if (!method.publicKeyMultibase) {
555
+ throw new AuthenticationError("Missing key material");
556
+ }
557
+ return parseX25519Multibase(method.publicKeyMultibase);
558
+ }
559
+ function stripMultibasePrefix2(value) {
560
+ return value.startsWith("z") ? value.slice(1) : value;
561
+ }
562
+ function canonicalizeJson(value) {
563
+ const output = canonicalize(value);
564
+ if (output === void 0) {
565
+ throw new Error("Failed to canonicalize JSON value");
566
+ }
567
+ return new TextEncoder().encode(output);
568
+ }
569
+ function cloneJson(value) {
570
+ return structuredClone(value);
571
+ }
572
+
573
+ // src/proof/proof.ts
574
+ var PROOF_TYPE_SECP256K1 = "EcdsaSecp256k1Signature2019";
575
+ var PROOF_TYPE_ED25519 = "Ed25519Signature2020";
576
+ var PROOF_TYPE_DATA_INTEGRITY = "DataIntegrityProof";
577
+ var CRYPTOSUITE_EDDSA_JCS_2022 = "eddsa-jcs-2022";
578
+ var CRYPTOSUITE_DIDWBA_SECP256K1_2025 = "didwba-jcs-ecdsa-secp256k1-2025";
579
+ function generateW3cProof(document, privateKeyInput, verificationMethod, options = {}) {
580
+ const privateKey = normalizePrivateKeyMaterial(privateKeyInput);
581
+ const proofType = options.proofType ?? inferProofType(privateKey.type);
582
+ validateProofCompatibility(privateKey.type, proofType, options.cryptosuite);
583
+ const proof2 = {
584
+ type: proofType,
585
+ created: options.created ?? (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z"),
586
+ verificationMethod,
587
+ proofPurpose: options.proofPurpose ?? "assertionMethod",
588
+ proofValue: ""
589
+ };
590
+ if (proofType === PROOF_TYPE_DATA_INTEGRITY) {
591
+ proof2.cryptosuite = options.cryptosuite ?? inferCryptosuite(privateKey.type);
592
+ }
593
+ if (options.domain) {
594
+ proof2.domain = options.domain;
595
+ }
596
+ if (options.challenge) {
597
+ proof2.challenge = options.challenge;
598
+ }
599
+ const signingDocument = cloneJson(document);
600
+ delete signingDocument.proof;
601
+ const signingInput = computeSigningInput(signingDocument, omitProofValue(proof2));
602
+ proof2.proofValue = encodeBase64Url(signMessage(privateKey, signingInput));
603
+ return {
604
+ ...cloneJson(document),
605
+ proof: proof2
606
+ };
607
+ }
608
+ function verifyW3cProof(document, publicKeyInput, options = {}) {
609
+ try {
610
+ verifyW3cProofDetailed(document, publicKeyInput, options);
611
+ return true;
612
+ } catch {
613
+ return false;
614
+ }
615
+ }
616
+ function verifyW3cProofDetailed(document, publicKeyInput, options = {}) {
617
+ const publicKey = normalizePublicKeyMaterial(publicKeyInput);
618
+ const proof2 = document.proof;
619
+ if (!proof2 || typeof proof2 !== "object") {
620
+ throw new ProofError("Missing proof object");
621
+ }
622
+ const proofObject = proof2;
623
+ const proofType = requireStringField(proofObject, "type");
624
+ const proofValue = requireStringField(proofObject, "proofValue");
625
+ const proofPurpose = requireStringField(proofObject, "proofPurpose");
626
+ requireStringField(proofObject, "verificationMethod");
627
+ requireStringField(proofObject, "created");
628
+ validatePublicKeyCompatibility(publicKey.type, proofType, proofObject.cryptosuite);
629
+ if (options.expectedPurpose && options.expectedPurpose !== proofPurpose) {
630
+ throw new ProofError("Verification failed: proofPurpose mismatch");
631
+ }
632
+ if (options.expectedDomain && proofObject.domain !== options.expectedDomain) {
633
+ throw new ProofError("Verification failed: domain mismatch");
634
+ }
635
+ if (options.expectedChallenge && proofObject.challenge !== options.expectedChallenge) {
636
+ throw new ProofError("Verification failed: challenge mismatch");
637
+ }
638
+ const signingDocument = cloneJson(document);
639
+ delete signingDocument.proof;
640
+ const signingInput = computeSigningInput(signingDocument, omitProofValue(proofObject));
641
+ const signature = decodeBase64Url(proofValue);
642
+ if (!verifyMessage(publicKey, signingInput, signature)) {
643
+ throw new ProofError("Verification failed");
644
+ }
645
+ }
646
+ function inferProofType(keyType) {
647
+ switch (keyType) {
648
+ case "secp256k1":
649
+ return PROOF_TYPE_SECP256K1;
650
+ case "ed25519":
651
+ return PROOF_TYPE_ED25519;
652
+ default:
653
+ return PROOF_TYPE_DATA_INTEGRITY;
654
+ }
655
+ }
656
+ function inferCryptosuite(keyType) {
657
+ switch (keyType) {
658
+ case "ed25519":
659
+ return CRYPTOSUITE_EDDSA_JCS_2022;
660
+ case "secp256k1":
661
+ return CRYPTOSUITE_DIDWBA_SECP256K1_2025;
662
+ default:
663
+ throw new ProofError(`Unsupported cryptosuite for key type: ${keyType}`);
664
+ }
665
+ }
666
+ function validateProofCompatibility(keyType, proofType, cryptosuite) {
667
+ if (proofType === PROOF_TYPE_SECP256K1 && keyType !== "secp256k1") {
668
+ throw new ProofError("Key type mismatch for secp256k1 proof generation");
669
+ }
670
+ if (proofType === PROOF_TYPE_ED25519 && keyType !== "ed25519") {
671
+ throw new ProofError("Key type mismatch for Ed25519 proof generation");
672
+ }
673
+ if (proofType === PROOF_TYPE_DATA_INTEGRITY && cryptosuite) {
674
+ validateCryptosuite(keyType, cryptosuite);
675
+ }
676
+ }
677
+ function validatePublicKeyCompatibility(keyType, proofType, cryptosuite) {
678
+ if (proofType === PROOF_TYPE_SECP256K1 && keyType !== "secp256k1") {
679
+ throw new ProofError("Invalid public key for proof verification");
680
+ }
681
+ if (proofType === PROOF_TYPE_ED25519 && keyType !== "ed25519") {
682
+ throw new ProofError("Invalid public key for proof verification");
683
+ }
684
+ if (proofType === PROOF_TYPE_DATA_INTEGRITY && cryptosuite) {
685
+ validateCryptosuite(keyType, cryptosuite);
686
+ }
687
+ }
688
+ function validateCryptosuite(keyType, cryptosuite) {
689
+ if (cryptosuite === CRYPTOSUITE_EDDSA_JCS_2022 && keyType !== "ed25519") {
690
+ throw new ProofError("Unsupported cryptosuite for non-Ed25519 key");
691
+ }
692
+ if (cryptosuite === CRYPTOSUITE_DIDWBA_SECP256K1_2025 && keyType !== "secp256k1") {
693
+ throw new ProofError("Unsupported cryptosuite for non-secp256k1 key");
694
+ }
695
+ if (cryptosuite !== CRYPTOSUITE_EDDSA_JCS_2022 && cryptosuite !== CRYPTOSUITE_DIDWBA_SECP256K1_2025) {
696
+ throw new ProofError(`Unsupported cryptosuite: ${cryptosuite}`);
697
+ }
698
+ }
699
+ function computeSigningInput(document, proofOptions) {
700
+ const documentHash = sha256(canonicalizeJson(document));
701
+ const proofHash = sha256(canonicalizeJson(proofOptions));
702
+ const combined = new Uint8Array(documentHash.length + proofHash.length);
703
+ combined.set(proofHash, 0);
704
+ combined.set(documentHash, proofHash.length);
705
+ return combined;
706
+ }
707
+ function omitProofValue(proof2) {
708
+ const clone = { ...proof2 };
709
+ delete clone.proofValue;
710
+ return clone;
711
+ }
712
+ function requireStringField(proof2, key) {
713
+ const value = proof2[key];
714
+ if (typeof value !== "string" || value.length === 0) {
715
+ throw new ProofError(`Missing proof field: ${String(key)}`);
716
+ }
717
+ return value;
718
+ }
719
+
720
+ // src/authentication/did-wba.ts
721
+ var VM_KEY_AUTH = "key-1";
722
+ var VM_KEY_E2EE_SIGNING = "key-2";
723
+ var VM_KEY_E2EE_AGREEMENT = "key-3";
724
+ var ANP_MESSAGE_SERVICE_TYPE = "ANPMessageService";
725
+ async function resolveDidWbaDocument(did, verifyProof = true, options = {}) {
726
+ if (!did.startsWith("did:wba:")) {
727
+ throw new AuthenticationError("Invalid DID format");
728
+ }
729
+ void options.verifySsl;
730
+ const url = buildDidResolutionUrl(did, options.baseUrlOverride);
731
+ const timeoutMs = Math.round((options.timeoutSeconds ?? 10) * 1e3);
732
+ const response = await fetch(url, {
733
+ headers: {
734
+ Accept: "application/json",
735
+ ...options.headers ?? {}
736
+ },
737
+ signal: AbortSignal.timeout(timeoutMs)
738
+ }).catch((error) => {
739
+ throw new NetworkError("Network failure during DID resolution", void 0, error);
740
+ });
741
+ if (!response.ok) {
742
+ throw new NetworkError("Network failure during DID resolution", response.status);
743
+ }
744
+ const document = await response.json();
745
+ if (document.id !== did) {
746
+ throw new AuthenticationError("Invalid DID document");
747
+ }
748
+ if (!validateDidDocumentBinding(document, verifyProof)) {
749
+ throw new AuthenticationError("DID binding verification failed");
750
+ }
751
+ if (verifyProof && document.proof) {
752
+ const verificationMethodId = document.proof.verificationMethod;
753
+ const method = findVerificationMethod(document, verificationMethodId);
754
+ if (!method) {
755
+ throw new AuthenticationError("Verification method not found");
756
+ }
757
+ const publicKey = extractPublicKey(method);
758
+ if (!verifyW3cProof(document, publicKey)) {
759
+ throw new AuthenticationError("Verification failed");
760
+ }
761
+ }
762
+ return document;
763
+ }
764
+ function buildAnpMessageService(didOrServiceRef, serviceEndpoint, options = {}) {
765
+ const fragment = options.fragment ?? "message";
766
+ const serviceId = didOrServiceRef.startsWith("#") || didOrServiceRef.startsWith("did:") ? didOrServiceRef.startsWith("#") ? didOrServiceRef : `${didOrServiceRef}#${fragment}` : `${didOrServiceRef}#${fragment}`;
767
+ const service = {
768
+ id: serviceId,
769
+ type: ANP_MESSAGE_SERVICE_TYPE,
770
+ serviceEndpoint
771
+ };
772
+ if (options.serviceDid) {
773
+ service.serviceDid = options.serviceDid;
774
+ }
775
+ if (options.profiles?.length) {
776
+ service.profiles = [...options.profiles];
777
+ }
778
+ if (options.securityProfiles?.length) {
779
+ service.securityProfiles = [...options.securityProfiles];
780
+ }
781
+ if (options.accepts?.length) {
782
+ service.accepts = [...options.accepts];
783
+ }
784
+ if (options.priority !== void 0) {
785
+ service.priority = options.priority;
786
+ }
787
+ if (options.authSchemes?.length) {
788
+ service.authSchemes = [...options.authSchemes];
789
+ }
790
+ return service;
791
+ }
792
+ function buildAgentMessageService(didOrServiceRef, serviceEndpoint, options = {}) {
793
+ return buildAnpMessageService(didOrServiceRef, serviceEndpoint, {
794
+ profiles: options.profiles ?? ["anp.core.binding.v1", "anp.direct.base.v1", "anp.direct.e2ee.v1"],
795
+ securityProfiles: options.securityProfiles ?? ["transport-protected", "direct-e2ee"],
796
+ ...options
797
+ });
798
+ }
799
+ function buildGroupMessageService(didOrServiceRef, serviceEndpoint, options = {}) {
800
+ return buildAnpMessageService(didOrServiceRef, serviceEndpoint, {
801
+ profiles: options.profiles ?? ["anp.core.binding.v1", "anp.group.base.v1", "anp.group.e2ee.v1"],
802
+ securityProfiles: options.securityProfiles ?? ["transport-protected", "group-e2ee"],
803
+ ...options
804
+ });
805
+ }
806
+ function createDidWbaDocument(hostname, options = {}) {
807
+ if (!hostname.trim()) {
808
+ throw new AuthenticationError("Hostname cannot be empty");
809
+ }
810
+ if (isIP(hostname) !== 0) {
811
+ throw new AuthenticationError("Hostname cannot be an IP address");
812
+ }
813
+ const didProfile = options.didProfile ?? "e1" /* E1 */;
814
+ const didBase = buildDidBase(hostname, options.port);
815
+ const pathSegments = [...options.pathSegments ?? []];
816
+ const contexts = ["https://www.w3.org/ns/did/v1"];
817
+ const verificationMethods = [];
818
+ const authentication2 = [];
819
+ const assertionMethod = [];
820
+ const keyAgreement = [];
821
+ const keys = {};
822
+ const authKey = generateKeyPairPem(
823
+ didProfile === "e1" /* E1 */ ? "ed25519" : "secp256k1"
824
+ );
825
+ const authPublicKey = authKey.publicKey;
826
+ let did = didBase;
827
+ if (didProfile === "e1" /* E1 */ && pathSegments.length > 0) {
828
+ pathSegments.push(`e1_${computeMultikeyFingerprint(authPublicKey)}`);
829
+ }
830
+ if (didProfile === "k1" /* K1 */ && pathSegments.length > 0) {
831
+ pathSegments.push(`k1_${computeJwkFingerprint(authPublicKey)}`);
832
+ }
833
+ did = joinDid(didBase, pathSegments);
834
+ const authVerificationMethodId = `${did}#${VM_KEY_AUTH}`;
835
+ const authVerificationMethod = buildAuthVerificationMethod(did, didProfile, authPublicKey, contexts);
836
+ verificationMethods.push(authVerificationMethod);
837
+ authentication2.push(authVerificationMethodId);
838
+ if (didProfile === "e1" /* E1 */ || didProfile === "k1" /* K1 */) {
839
+ assertionMethod.push(authVerificationMethodId);
840
+ }
841
+ keys[VM_KEY_AUTH] = authKey.pair;
842
+ if (options.enableE2ee !== false) {
843
+ contexts.push("https://w3id.org/security/suites/x25519-2019/v1");
844
+ const signingKey = generateKeyPairPem("secp256r1");
845
+ const agreementKey = generateKeyPairPem("x25519");
846
+ verificationMethods.push({
847
+ id: `${did}#${VM_KEY_E2EE_SIGNING}`,
848
+ type: "EcdsaSecp256r1VerificationKey2019",
849
+ controller: did,
850
+ publicKeyJwk: publicKeyToJwk(signingKey.publicKey)
851
+ });
852
+ verificationMethods.push({
853
+ id: `${did}#${VM_KEY_E2EE_AGREEMENT}`,
854
+ type: "X25519KeyAgreementKey2019",
855
+ controller: did,
856
+ publicKeyMultibase: x25519PublicKeyToMultibase(agreementKey.publicKey.bytes)
857
+ });
858
+ keyAgreement.push(`${did}#${VM_KEY_E2EE_AGREEMENT}`);
859
+ keys[VM_KEY_E2EE_SIGNING] = signingKey.pair;
860
+ keys[VM_KEY_E2EE_AGREEMENT] = agreementKey.pair;
861
+ }
862
+ const document = {
863
+ "@context": contexts,
864
+ id: did,
865
+ verificationMethod: verificationMethods,
866
+ authentication: authentication2
867
+ };
868
+ if (assertionMethod.length > 0) {
869
+ document.assertionMethod = assertionMethod;
870
+ }
871
+ if (keyAgreement.length > 0) {
872
+ document.keyAgreement = keyAgreement;
873
+ }
874
+ const services = buildServiceEntries(did, options.agentDescriptionUrl, options.services);
875
+ if (services.length > 0) {
876
+ document.service = services;
877
+ }
878
+ const proofOptions = {
879
+ proofPurpose: options.proofPurpose ?? "assertionMethod",
880
+ proofType: didProfile === "plain_legacy" /* PlainLegacy */ ? PROOF_TYPE_SECP256K1 : PROOF_TYPE_DATA_INTEGRITY,
881
+ cryptosuite: didProfile === "e1" /* E1 */ ? CRYPTOSUITE_EDDSA_JCS_2022 : didProfile === "k1" /* K1 */ ? CRYPTOSUITE_DIDWBA_SECP256K1_2025 : void 0,
882
+ created: options.created,
883
+ domain: options.domain,
884
+ challenge: options.challenge
885
+ };
886
+ const signedDocument = generateW3cProof(
887
+ document,
888
+ authKey.privateKey,
889
+ options.verificationMethod ?? authVerificationMethodId,
890
+ proofOptions
891
+ );
892
+ return {
893
+ didDocument: signedDocument,
894
+ keys
895
+ };
896
+ }
897
+ function createDidWbaDocumentWithKeyBinding(hostname, options = {}) {
898
+ return createDidWbaDocument(hostname, {
899
+ ...options,
900
+ pathSegments: options.pathSegments?.length ? options.pathSegments : ["user"],
901
+ didProfile: "k1" /* K1 */
902
+ });
903
+ }
904
+ function computeJwkFingerprint(publicKeyInput) {
905
+ const publicKey = normalizePublicKeyMaterial(publicKeyInput);
906
+ if (publicKey.type !== "secp256k1") {
907
+ throw new AuthenticationError("Invalid DID document");
908
+ }
909
+ return computeJwkThumbprint(publicKeyToJwk(publicKey));
910
+ }
911
+ function computeMultikeyFingerprint(publicKeyInput) {
912
+ const publicKey = normalizePublicKeyMaterial(publicKeyInput);
913
+ if (publicKey.type !== "ed25519") {
914
+ throw new AuthenticationError("Invalid DID document");
915
+ }
916
+ return computeJwkThumbprint(publicKeyToJwk(publicKey));
917
+ }
918
+ function verifyDidKeyBinding(did, bindingMaterial) {
919
+ const lastSegment = did.split(":").at(-1) ?? "";
920
+ const publicKey = toPublicKeyMaterial(bindingMaterial);
921
+ if (lastSegment.startsWith("k1_")) {
922
+ return publicKey.type === "secp256k1" && computeJwkFingerprint(publicKey) === lastSegment.slice(3);
923
+ }
924
+ if (lastSegment.startsWith("e1_")) {
925
+ return publicKey.type === "ed25519" && computeMultikeyFingerprint(publicKey) === lastSegment.slice(3);
926
+ }
927
+ return true;
928
+ }
929
+ function validateDidDocumentBinding(didDocument, verifyProof = true) {
930
+ const lastSegment = didDocument.id.split(":").at(-1) ?? "";
931
+ if (lastSegment.startsWith("e1_")) {
932
+ return validateE1Binding(didDocument, lastSegment.slice(3));
933
+ }
934
+ if (lastSegment.startsWith("k1_")) {
935
+ if (verifyProof) {
936
+ return validateK1Binding(didDocument, lastSegment.slice(3));
937
+ }
938
+ return didDocument.verificationMethod.some(
939
+ (method) => isAuthenticationAuthorized(didDocument, method.id) && verifyDidKeyBinding(didDocument.id, method)
940
+ );
941
+ }
942
+ return true;
943
+ }
944
+ function generateAuthHeader(didDocument, serviceDomain, privateKeyInput, version = "1.1", options = {}) {
945
+ const payload = generateAuthPayload(
946
+ didDocument,
947
+ serviceDomain,
948
+ privateKeyInput,
949
+ version,
950
+ options
951
+ );
952
+ return `DIDWba v="${payload.version}", did="${payload.did}", nonce="${payload.nonce}", timestamp="${payload.timestamp}", verification_method="${payload.verificationMethod}", signature="${payload.signature}"`;
953
+ }
954
+ function generateAuthJson(didDocument, serviceDomain, privateKeyInput, version = "1.1", options = {}) {
955
+ const payload = generateAuthPayload(
956
+ didDocument,
957
+ serviceDomain,
958
+ privateKeyInput,
959
+ version,
960
+ options
961
+ );
962
+ return JSON.stringify({
963
+ v: payload.version,
964
+ did: payload.did,
965
+ nonce: payload.nonce,
966
+ timestamp: payload.timestamp,
967
+ verification_method: payload.verificationMethod,
968
+ signature: payload.signature
969
+ });
970
+ }
971
+ function extractAuthHeaderParts(authHeader) {
972
+ if (!authHeader.trimStart().startsWith("DIDWba")) {
973
+ throw new AuthenticationError("Authentication header must start with DIDWba");
974
+ }
975
+ const versionMatch = authHeader.match(/\bv="([^"]+)"/i);
976
+ return {
977
+ did: requiredHeaderField(authHeader, "did"),
978
+ nonce: requiredHeaderField(authHeader, "nonce"),
979
+ timestamp: requiredHeaderField(authHeader, "timestamp"),
980
+ verificationMethod: requiredHeaderField(authHeader, "verification_method"),
981
+ signature: requiredHeaderField(authHeader, "signature"),
982
+ version: versionMatch?.[1] ?? "1.1"
983
+ };
984
+ }
985
+ function verifyAuthHeaderSignature(authHeader, didDocument, serviceDomain) {
986
+ verifyAuthPayload(extractAuthHeaderParts(authHeader), didDocument, serviceDomain);
987
+ return true;
988
+ }
989
+ function verifyAuthJsonSignature(authJson, didDocument, serviceDomain) {
990
+ const value = JSON.parse(authJson);
991
+ verifyAuthPayload(
992
+ {
993
+ did: String(value.did ?? ""),
994
+ nonce: String(value.nonce ?? ""),
995
+ timestamp: String(value.timestamp ?? ""),
996
+ verificationMethod: String(value.verification_method ?? ""),
997
+ signature: String(value.signature ?? ""),
998
+ version: String(value.v ?? "1.1")
999
+ },
1000
+ didDocument,
1001
+ serviceDomain
1002
+ );
1003
+ return true;
1004
+ }
1005
+ function findVerificationMethod(didDocument, verificationMethodId) {
1006
+ const directMethod = didDocument.verificationMethod.find(
1007
+ (method) => method.id === verificationMethodId
1008
+ );
1009
+ if (directMethod) {
1010
+ return directMethod;
1011
+ }
1012
+ for (const entry of didDocument.authentication) {
1013
+ if (typeof entry !== "string" && entry.id === verificationMethodId) {
1014
+ return entry;
1015
+ }
1016
+ }
1017
+ for (const entry of didDocument.assertionMethod ?? []) {
1018
+ if (typeof entry !== "string" && entry.id === verificationMethodId) {
1019
+ return entry;
1020
+ }
1021
+ }
1022
+ return void 0;
1023
+ }
1024
+ function isAuthenticationAuthorized(didDocument, verificationMethodId) {
1025
+ return isVerificationMethodAuthorized(didDocument.authentication, verificationMethodId);
1026
+ }
1027
+ function isAssertionMethodAuthorized(didDocument, verificationMethodId) {
1028
+ return isVerificationMethodAuthorized(didDocument.assertionMethod ?? [], verificationMethodId);
1029
+ }
1030
+ function validateE1Binding(didDocument, expectedFingerprint) {
1031
+ const proof2 = didDocument.proof;
1032
+ if (!proof2) {
1033
+ return false;
1034
+ }
1035
+ if (proof2.type !== PROOF_TYPE_DATA_INTEGRITY || proof2.cryptosuite !== CRYPTOSUITE_EDDSA_JCS_2022) {
1036
+ return false;
1037
+ }
1038
+ if (!isAssertionMethodAuthorized(didDocument, proof2.verificationMethod)) {
1039
+ return false;
1040
+ }
1041
+ const method = findVerificationMethod(didDocument, proof2.verificationMethod);
1042
+ if (!method) {
1043
+ return false;
1044
+ }
1045
+ const publicKey = extractPublicKey(method);
1046
+ return publicKey.type === "ed25519" && verifyW3cProof(didDocument, publicKey, { expectedPurpose: "assertionMethod" }) && computeMultikeyFingerprint(publicKey) === expectedFingerprint;
1047
+ }
1048
+ function validateK1Binding(didDocument, expectedFingerprint) {
1049
+ const proof2 = didDocument.proof;
1050
+ if (!proof2) {
1051
+ return false;
1052
+ }
1053
+ if (!isAssertionMethodAuthorized(didDocument, proof2.verificationMethod)) {
1054
+ return false;
1055
+ }
1056
+ const method = findVerificationMethod(didDocument, proof2.verificationMethod);
1057
+ if (!method) {
1058
+ return false;
1059
+ }
1060
+ const publicKey = extractPublicKey(method);
1061
+ return publicKey.type === "secp256k1" && verifyW3cProof(didDocument, publicKey, { expectedPurpose: "assertionMethod" }) && computeJwkFingerprint(publicKey) === expectedFingerprint;
1062
+ }
1063
+ function generateAuthPayload(didDocument, serviceDomain, privateKeyInput, version, options = {}) {
1064
+ const did = didDocument.id;
1065
+ const [method, fragment] = selectAuthenticationMethod(didDocument);
1066
+ const nonce = options.nonce ?? encodeBase64Url(crypto.getRandomValues(new Uint8Array(16)));
1067
+ const timestamp = options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
1068
+ const payload = {
1069
+ nonce,
1070
+ timestamp,
1071
+ [domainFieldForVersion(version)]: serviceDomain,
1072
+ did
1073
+ };
1074
+ const contentHash = sha256(canonicalizeJson(payload));
1075
+ const signatureBytes = signMessage(normalizePrivateKeyMaterial(privateKeyInput), contentHash);
1076
+ const verifier = createVerificationMethod(method);
1077
+ return {
1078
+ did,
1079
+ nonce,
1080
+ timestamp,
1081
+ verificationMethod: fragment,
1082
+ signature: verifier.encodeSignature(signatureBytes),
1083
+ version
1084
+ };
1085
+ }
1086
+ function verifyAuthPayload(parsed, didDocument, serviceDomain) {
1087
+ if (didDocument.id.toLowerCase() !== parsed.did.toLowerCase()) {
1088
+ throw new AuthenticationError("Verification failed");
1089
+ }
1090
+ const payload = {
1091
+ nonce: parsed.nonce,
1092
+ timestamp: parsed.timestamp,
1093
+ [domainFieldForVersion(parsed.version)]: serviceDomain,
1094
+ did: parsed.did
1095
+ };
1096
+ const contentHash = sha256(canonicalizeJson(payload));
1097
+ const verificationMethodId = `${parsed.did}#${parsed.verificationMethod}`;
1098
+ const method = findVerificationMethod(didDocument, verificationMethodId);
1099
+ if (!method) {
1100
+ throw new AuthenticationError("Verification method not found");
1101
+ }
1102
+ const verifier = createVerificationMethod(method);
1103
+ if (!verifier.verifySignature(contentHash, parsed.signature)) {
1104
+ throw new AuthenticationError("Verification failed");
1105
+ }
1106
+ }
1107
+ function selectAuthenticationMethod(didDocument) {
1108
+ const first = didDocument.authentication[0];
1109
+ if (!first) {
1110
+ throw new AuthenticationError("Invalid DID document");
1111
+ }
1112
+ if (typeof first === "string") {
1113
+ const method = findVerificationMethod(didDocument, first);
1114
+ if (!method) {
1115
+ throw new AuthenticationError("Verification method not found");
1116
+ }
1117
+ return [method, first.split("#").at(-1) ?? ""];
1118
+ }
1119
+ return [first, first.id.split("#").at(-1) ?? ""];
1120
+ }
1121
+ function buildAuthVerificationMethod(did, didProfile, authPublicKey, contexts) {
1122
+ if (didProfile === "e1" /* E1 */) {
1123
+ contexts.push("https://w3id.org/security/data-integrity/v2");
1124
+ contexts.push("https://w3id.org/security/multikey/v1");
1125
+ return {
1126
+ id: `${did}#${VM_KEY_AUTH}`,
1127
+ type: "Multikey",
1128
+ controller: did,
1129
+ publicKeyMultibase: ed25519PublicKeyToMultibase(authPublicKey.bytes)
1130
+ };
1131
+ }
1132
+ contexts.push("https://w3id.org/security/suites/jws-2020/v1");
1133
+ contexts.push("https://w3id.org/security/suites/secp256k1-2019/v1");
1134
+ if (didProfile === "k1" /* K1 */) {
1135
+ contexts.push("https://w3id.org/security/data-integrity/v2");
1136
+ }
1137
+ return {
1138
+ id: `${did}#${VM_KEY_AUTH}`,
1139
+ type: "EcdsaSecp256k1VerificationKey2019",
1140
+ controller: did,
1141
+ publicKeyJwk: publicKeyToJwk(authPublicKey)
1142
+ };
1143
+ }
1144
+ function buildServiceEntries(did, agentDescriptionUrl, services) {
1145
+ const output = [];
1146
+ if (agentDescriptionUrl) {
1147
+ output.push({
1148
+ id: `${did}#ad`,
1149
+ type: "AgentDescription",
1150
+ serviceEndpoint: agentDescriptionUrl
1151
+ });
1152
+ }
1153
+ for (const service of services ?? []) {
1154
+ const copy = cloneJson(service);
1155
+ if (typeof copy.id === "string" && copy.id.startsWith("#")) {
1156
+ copy.id = `${did}${copy.id}`;
1157
+ }
1158
+ output.push(copy);
1159
+ }
1160
+ return output;
1161
+ }
1162
+ function buildDidBase(hostname, port) {
1163
+ return port === void 0 ? `did:wba:${hostname}` : `did:wba:${hostname}%3A${port}`;
1164
+ }
1165
+ function joinDid(base, pathSegments) {
1166
+ return pathSegments.length === 0 ? base : `${base}:${pathSegments.join(":")}`;
1167
+ }
1168
+ function buildDidResolutionUrl(did, baseUrlOverride) {
1169
+ const parts = did.split(":");
1170
+ if (parts.length < 3) {
1171
+ throw new AuthenticationError("Invalid DID format");
1172
+ }
1173
+ const domain = decodeURIComponent(parts[2]);
1174
+ const pathSegments = parts.slice(3);
1175
+ const baseUrl = (baseUrlOverride ?? `https://${domain}`).replace(/\/$/, "");
1176
+ if (pathSegments.length === 0) {
1177
+ return `${baseUrl}/.well-known/did.json`;
1178
+ }
1179
+ return `${baseUrl}/${pathSegments.join("/")}/did.json`;
1180
+ }
1181
+ function domainFieldForVersion(version) {
1182
+ return Number.parseFloat(version) >= 1.1 ? "aud" : "service";
1183
+ }
1184
+ function requiredHeaderField(authHeader, field) {
1185
+ const match = authHeader.match(new RegExp(`${field}="([^"]+)"`, "i"));
1186
+ if (!match?.[1]) {
1187
+ throw new AuthenticationError(`Missing field in authorization header: ${field}`);
1188
+ }
1189
+ return match[1];
1190
+ }
1191
+ function toPublicKeyMaterial(bindingMaterial) {
1192
+ if (typeof bindingMaterial === "string") {
1193
+ return normalizePublicKeyMaterial(bindingMaterial);
1194
+ }
1195
+ if ("bytes" in bindingMaterial) {
1196
+ return bindingMaterial;
1197
+ }
1198
+ return extractPublicKey(bindingMaterial);
1199
+ }
1200
+ function isVerificationMethodAuthorized(entries, verificationMethodId) {
1201
+ return entries.some(
1202
+ (entry) => typeof entry === "string" ? entry === verificationMethodId : entry.id === verificationMethodId
1203
+ );
1204
+ }
1205
+
1206
+ // src/authentication/did-resolver.ts
1207
+ async function resolveDidDocument(did, verifyProof = true, options = {}) {
1208
+ if (did.startsWith("did:wba:")) {
1209
+ return resolveDidWbaDocument(did, verifyProof, options);
1210
+ }
1211
+ if (!did.startsWith("did:web:")) {
1212
+ throw new AuthenticationError("Unsupported DID method");
1213
+ }
1214
+ void options.verifySsl;
1215
+ const url = buildDidResolutionUrl2(did, options.baseUrlOverride);
1216
+ const timeoutMs = Math.round((options.timeoutSeconds ?? 10) * 1e3);
1217
+ const response = await fetch(url, {
1218
+ headers: {
1219
+ Accept: "application/json",
1220
+ ...options.headers ?? {}
1221
+ },
1222
+ signal: AbortSignal.timeout(timeoutMs)
1223
+ }).catch((error) => {
1224
+ throw new NetworkError("Network failure during DID resolution", void 0, error);
1225
+ });
1226
+ if (!response.ok) {
1227
+ throw new NetworkError("Network failure during DID resolution", response.status);
1228
+ }
1229
+ const document = await response.json();
1230
+ if (document.id !== did) {
1231
+ throw new AuthenticationError("Invalid DID document");
1232
+ }
1233
+ if (verifyProof && document.proof) {
1234
+ const verificationMethodId = document.proof.verificationMethod;
1235
+ const method = findVerificationMethod(document, verificationMethodId);
1236
+ if (!method) {
1237
+ throw new AuthenticationError("Verification method not found");
1238
+ }
1239
+ const publicKey = extractPublicKey(method);
1240
+ if (!verifyW3cProof(document, publicKey)) {
1241
+ throw new AuthenticationError("Verification failed");
1242
+ }
1243
+ }
1244
+ return document;
1245
+ }
1246
+ function buildDidResolutionUrl2(did, baseUrlOverride) {
1247
+ const parts = did.split(":");
1248
+ if (parts.length < 3) {
1249
+ throw new AuthenticationError("Invalid DID format");
1250
+ }
1251
+ const domain = decodeURIComponent(parts[2]);
1252
+ const pathSegments = parts.slice(3).map((segment) => decodeURIComponent(segment));
1253
+ const baseUrl = (baseUrlOverride ?? `https://${domain}`).replace(/\/$/, "");
1254
+ if (pathSegments.length === 0) {
1255
+ return `${baseUrl}/.well-known/did.json`;
1256
+ }
1257
+ return `${baseUrl}/${pathSegments.join("/")}/did.json`;
1258
+ }
1259
+ function buildContentDigest(body) {
1260
+ const digest = sha256(toBytes(body));
1261
+ return `sha-256=:${encodeBase64(digest)}:`;
1262
+ }
1263
+ function verifyContentDigest(body, contentDigest) {
1264
+ return buildContentDigest(body) === contentDigest.trim();
1265
+ }
1266
+ function generateHttpSignatureHeaders(didDocument, requestUrl, requestMethod, privateKeyInput, headers = {}, body, options = {}) {
1267
+ const keyid = options.keyid ?? selectDefaultKeyid(didDocument);
1268
+ const coveredComponents = options.coveredComponents ?? ["@method", "@target-uri", "@authority"];
1269
+ const headersToSign = { ...headers };
1270
+ const bodyBytes = body ? toBytes(body) : new Uint8Array(0);
1271
+ const covered = [...coveredComponents];
1272
+ if (bodyBytes.length > 0) {
1273
+ headersToSign["Content-Digest"] ??= buildContentDigest(bodyBytes);
1274
+ headersToSign["Content-Length"] ??= String(bodyBytes.length);
1275
+ if (!covered.some((component) => component.toLowerCase() === "content-digest")) {
1276
+ covered.push("content-digest");
1277
+ }
1278
+ }
1279
+ const created = options.created ?? Math.floor(Date.now() / 1e3);
1280
+ const expires = options.expires ?? created + 300;
1281
+ const nonce = options.nonce ?? encodeBase64Url(randomBytes(16));
1282
+ const signatureBase = buildSignatureBase(
1283
+ covered,
1284
+ requestMethod,
1285
+ requestUrl,
1286
+ headersToSign,
1287
+ created,
1288
+ expires,
1289
+ nonce,
1290
+ keyid
1291
+ );
1292
+ const signature = signMessage(
1293
+ normalizePrivateKeyMaterial(privateKeyInput),
1294
+ new TextEncoder().encode(signatureBase)
1295
+ );
1296
+ const result = {
1297
+ "Signature-Input": `sig1=${serializeSignatureParams(covered, created, expires, nonce, keyid)}`,
1298
+ Signature: `sig1=:${Buffer.from(signature).toString("base64")}:`
1299
+ };
1300
+ if (headersToSign["Content-Digest"]) {
1301
+ result["Content-Digest"] = headersToSign["Content-Digest"];
1302
+ }
1303
+ return result;
1304
+ }
1305
+ function extractSignatureMetadata(headers) {
1306
+ const signatureInput = getHeaderCaseInsensitive(headers, "Signature-Input");
1307
+ const signatureHeader = getHeaderCaseInsensitive(headers, "Signature");
1308
+ if (!signatureInput || !signatureHeader) {
1309
+ throw new AuthenticationError("Missing Signature-Input or Signature header");
1310
+ }
1311
+ const [labelInput, components, params] = parseSignatureInput(signatureInput);
1312
+ const [labelSignature] = parseSignatureHeader(signatureHeader);
1313
+ if (labelInput !== labelSignature) {
1314
+ throw new AuthenticationError("Invalid signature input");
1315
+ }
1316
+ const keyid = params.keyid;
1317
+ const created = Number(params.created);
1318
+ if (!keyid || Number.isNaN(created)) {
1319
+ throw new AuthenticationError("Invalid signature input");
1320
+ }
1321
+ return {
1322
+ label: labelInput,
1323
+ components,
1324
+ keyid,
1325
+ nonce: params.nonce,
1326
+ created,
1327
+ expires: params.expires ? Number(params.expires) : void 0
1328
+ };
1329
+ }
1330
+ function verifyHttpMessageSignature(didDocument, requestMethod, requestUrl, headers, body) {
1331
+ const signatureInput = getHeaderCaseInsensitive(headers, "Signature-Input");
1332
+ const signatureHeader = getHeaderCaseInsensitive(headers, "Signature");
1333
+ if (!signatureInput || !signatureHeader) {
1334
+ throw new AuthenticationError("Missing Signature-Input or Signature header");
1335
+ }
1336
+ const [labelInput, components, params] = parseSignatureInput(signatureInput);
1337
+ const [labelSignature, signatureBytes] = parseSignatureHeader(signatureHeader);
1338
+ if (labelInput !== labelSignature) {
1339
+ throw new AuthenticationError("Invalid signature input");
1340
+ }
1341
+ const keyid = params.keyid;
1342
+ const created = Number(params.created);
1343
+ if (!keyid || Number.isNaN(created)) {
1344
+ throw new AuthenticationError("Invalid signature input");
1345
+ }
1346
+ const bodyBytes = body ? toBytes(body) : new Uint8Array(0);
1347
+ if (bodyBytes.length > 0 || components.some((component) => component.toLowerCase() === "content-digest")) {
1348
+ const contentDigest = getHeaderCaseInsensitive(headers, "Content-Digest");
1349
+ if (!contentDigest) {
1350
+ throw new AuthenticationError("Missing Content-Digest header");
1351
+ }
1352
+ if (!verifyContentDigest(bodyBytes, contentDigest)) {
1353
+ throw new AuthenticationError("Content-Digest verification failed");
1354
+ }
1355
+ }
1356
+ const method = findVerificationMethod(didDocument, keyid);
1357
+ if (!method) {
1358
+ throw new AuthenticationError("Verification method not found");
1359
+ }
1360
+ const publicKey = extractPublicKey(method);
1361
+ const signatureBase = buildSignatureBase(
1362
+ components,
1363
+ requestMethod,
1364
+ requestUrl,
1365
+ headers,
1366
+ created,
1367
+ params.expires ? Number(params.expires) : void 0,
1368
+ params.nonce,
1369
+ keyid
1370
+ );
1371
+ if (!verifyMessage(publicKey, new TextEncoder().encode(signatureBase), signatureBytes)) {
1372
+ throw new AuthenticationError("Signature verification failed");
1373
+ }
1374
+ return {
1375
+ label: labelInput,
1376
+ components,
1377
+ keyid,
1378
+ nonce: params.nonce,
1379
+ created,
1380
+ expires: params.expires ? Number(params.expires) : void 0
1381
+ };
1382
+ }
1383
+ function buildSignatureBase(components, method, url, headers, created, expires, nonce, keyid) {
1384
+ const lines = components.map(
1385
+ (component) => `"${component}": ${componentValue(component, method, url, headers)}`
1386
+ );
1387
+ lines.push(
1388
+ `"@signature-params": ${serializeSignatureParams(components, created, expires, nonce, keyid)}`
1389
+ );
1390
+ return lines.join("\n");
1391
+ }
1392
+ function componentValue(component, method, url, headers) {
1393
+ switch (component) {
1394
+ case "@method":
1395
+ return method.toUpperCase();
1396
+ case "@target-uri":
1397
+ return url;
1398
+ case "@authority": {
1399
+ const parsed = new URL(url);
1400
+ return parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname;
1401
+ }
1402
+ default: {
1403
+ const value = getHeaderCaseInsensitive(headers, component);
1404
+ if (!value) {
1405
+ throw new AuthenticationError("Invalid signature input");
1406
+ }
1407
+ return value;
1408
+ }
1409
+ }
1410
+ }
1411
+ function serializeSignatureParams(components, created, expires, nonce, keyid) {
1412
+ const quotedComponents = components.map((component) => `"${component}"`).join(" ");
1413
+ const parts = [`created=${created}`];
1414
+ if (expires !== void 0) {
1415
+ parts.push(`expires=${expires}`);
1416
+ }
1417
+ if (nonce) {
1418
+ parts.push(`nonce="${nonce}"`);
1419
+ }
1420
+ parts.push(`keyid="${keyid}"`);
1421
+ return `(${quotedComponents});${parts.join(";")}`;
1422
+ }
1423
+ function parseSignatureInput(value) {
1424
+ const separator = value.indexOf("=");
1425
+ if (separator < 0) {
1426
+ throw new AuthenticationError("Invalid signature input");
1427
+ }
1428
+ const label = value.slice(0, separator);
1429
+ const remainder = value.slice(separator + 1);
1430
+ const openIndex = remainder.indexOf("(");
1431
+ const closeIndex = remainder.indexOf(")");
1432
+ if (openIndex < 0 || closeIndex < 0 || closeIndex <= openIndex) {
1433
+ throw new AuthenticationError("Invalid signature input");
1434
+ }
1435
+ const components = remainder.slice(openIndex + 1, closeIndex).split(/\s+/).map((component) => component.replaceAll('"', "")).filter(Boolean);
1436
+ if (components.length === 0) {
1437
+ throw new AuthenticationError("Invalid signature input");
1438
+ }
1439
+ const params = remainder.slice(closeIndex + 1).replace(/^;/, "").split(";").filter(Boolean).reduce((result, part) => {
1440
+ const [name, rawValue] = part.split("=", 2);
1441
+ if (!name || rawValue === void 0) {
1442
+ throw new AuthenticationError("Invalid signature input");
1443
+ }
1444
+ result[name] = rawValue.replace(/^"|"$/g, "");
1445
+ return result;
1446
+ }, {});
1447
+ return [label, components, params];
1448
+ }
1449
+ function parseSignatureHeader(value) {
1450
+ const separator = value.indexOf("=");
1451
+ if (separator < 0) {
1452
+ throw new AuthenticationError("Invalid signature header format");
1453
+ }
1454
+ const label = value.slice(0, separator);
1455
+ const raw = value.slice(separator + 1);
1456
+ if (!raw.startsWith(":") || !raw.endsWith(":")) {
1457
+ throw new AuthenticationError("Invalid signature header format");
1458
+ }
1459
+ return [label, new Uint8Array(Buffer.from(raw.slice(1, -1), "base64"))];
1460
+ }
1461
+ function selectDefaultKeyid(didDocument) {
1462
+ const first = didDocument.authentication[0];
1463
+ if (typeof first === "string") {
1464
+ return first;
1465
+ }
1466
+ if (first?.id) {
1467
+ return first.id;
1468
+ }
1469
+ throw new AuthenticationError("Verification method not found");
1470
+ }
1471
+ function getHeaderCaseInsensitive(headers, name) {
1472
+ const target = name.toLowerCase();
1473
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === target)?.[1];
1474
+ }
1475
+ function toBytes(value) {
1476
+ return typeof value === "string" ? new TextEncoder().encode(value) : value;
1477
+ }
1478
+ var DIDWbaAuthHeader = class {
1479
+ constructor(didDocumentPath, privateKeyPath, authMode = "http_signatures" /* HttpSignatures */) {
1480
+ this.didDocumentPath = didDocumentPath;
1481
+ this.privateKeyPath = privateKeyPath;
1482
+ this.authMode = authMode;
1483
+ }
1484
+ didDocumentCache;
1485
+ tokens = /* @__PURE__ */ new Map();
1486
+ async getAuthHeaders(serverUrl, forceNew = false, method = "GET", headers, body) {
1487
+ const domain = extractDomain(serverUrl);
1488
+ if (!forceNew) {
1489
+ const token = this.tokens.get(domain);
1490
+ if (token) {
1491
+ return { Authorization: `Bearer ${token}` };
1492
+ }
1493
+ }
1494
+ const [didDocument, privateKeyPem] = await Promise.all([
1495
+ this.loadDidDocument(),
1496
+ readFile(this.privateKeyPath, "utf8")
1497
+ ]);
1498
+ if (this.authMode === "legacy_didwba" /* LegacyDidWba */) {
1499
+ return {
1500
+ Authorization: generateAuthHeader(didDocument, domain, privateKeyPem, "1.1")
1501
+ };
1502
+ }
1503
+ return generateHttpSignatureHeaders(
1504
+ didDocument,
1505
+ serverUrl,
1506
+ method,
1507
+ privateKeyPem,
1508
+ headers,
1509
+ body
1510
+ );
1511
+ }
1512
+ async getAuthHeader(serverUrl, forceNew = false, method = "GET", headers, body) {
1513
+ return this.getAuthHeaders(serverUrl, forceNew, method, headers, body);
1514
+ }
1515
+ updateToken(serverUrl, headers) {
1516
+ const domain = extractDomain(serverUrl);
1517
+ const authenticationInfo = getHeaderCaseInsensitive2(headers, "Authentication-Info");
1518
+ if (authenticationInfo) {
1519
+ const parsed = parseAuthenticationInfo(authenticationInfo);
1520
+ const accessToken = parsed.access_token;
1521
+ const tokenType = parsed.token_type ?? "Bearer";
1522
+ if (accessToken && tokenType.toLowerCase() === "bearer") {
1523
+ this.tokens.set(domain, accessToken);
1524
+ return accessToken;
1525
+ }
1526
+ }
1527
+ const authorization = getHeaderCaseInsensitive2(headers, "Authorization");
1528
+ if (authorization?.startsWith("Bearer ")) {
1529
+ const token = authorization.slice(7);
1530
+ this.tokens.set(domain, token);
1531
+ return token;
1532
+ }
1533
+ return void 0;
1534
+ }
1535
+ clearToken(serverUrl) {
1536
+ this.tokens.delete(extractDomain(serverUrl));
1537
+ }
1538
+ clearAllTokens() {
1539
+ this.tokens.clear();
1540
+ }
1541
+ shouldRetryAfter401(responseHeaders) {
1542
+ const wwwAuthenticate = getHeaderCaseInsensitive2(responseHeaders, "WWW-Authenticate");
1543
+ if (!wwwAuthenticate) {
1544
+ return false;
1545
+ }
1546
+ const challenge = parseWwwAuthenticate(wwwAuthenticate);
1547
+ if (challenge.nonce) {
1548
+ return true;
1549
+ }
1550
+ return !["invalid_did", "invalid_verification_method", "forbidden_did"].includes(
1551
+ challenge.error ?? ""
1552
+ );
1553
+ }
1554
+ async getChallengeAuthHeaders(serverUrl, responseHeaders, method = "GET", headers, body) {
1555
+ const wwwAuthenticate = getHeaderCaseInsensitive2(responseHeaders, "WWW-Authenticate");
1556
+ const acceptSignature = getHeaderCaseInsensitive2(responseHeaders, "Accept-Signature");
1557
+ const challenge = wwwAuthenticate ? parseWwwAuthenticate(wwwAuthenticate) : {};
1558
+ const coveredComponents = normalizeCoveredComponents(
1559
+ acceptSignature ? parseAcceptSignature(acceptSignature) : void 0,
1560
+ headers,
1561
+ body
1562
+ );
1563
+ const [didDocument, privateKeyPem] = await Promise.all([
1564
+ this.loadDidDocument(),
1565
+ readFile(this.privateKeyPath, "utf8")
1566
+ ]);
1567
+ if (this.authMode === "legacy_didwba" /* LegacyDidWba */) {
1568
+ return {
1569
+ Authorization: generateAuthHeader(
1570
+ didDocument,
1571
+ extractDomain(serverUrl),
1572
+ privateKeyPem,
1573
+ "1.1",
1574
+ {
1575
+ nonce: challenge.nonce
1576
+ }
1577
+ )
1578
+ };
1579
+ }
1580
+ return generateHttpSignatureHeaders(
1581
+ didDocument,
1582
+ serverUrl,
1583
+ method,
1584
+ privateKeyPem,
1585
+ headers,
1586
+ body,
1587
+ {
1588
+ nonce: challenge.nonce,
1589
+ coveredComponents
1590
+ }
1591
+ );
1592
+ }
1593
+ async getChallengeAuthHeader(serverUrl, responseHeaders, method = "GET", headers, body) {
1594
+ return this.getChallengeAuthHeaders(serverUrl, responseHeaders, method, headers, body);
1595
+ }
1596
+ async loadDidDocument() {
1597
+ if (!this.didDocumentCache) {
1598
+ this.didDocumentCache = JSON.parse(
1599
+ await readFile(this.didDocumentPath, "utf8")
1600
+ );
1601
+ }
1602
+ return this.didDocumentCache;
1603
+ }
1604
+ };
1605
+ function extractDomain(serverUrl) {
1606
+ try {
1607
+ return new URL(serverUrl).hostname;
1608
+ } catch {
1609
+ return serverUrl;
1610
+ }
1611
+ }
1612
+ function getHeaderCaseInsensitive2(headers, name) {
1613
+ const target = name.toLowerCase();
1614
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === target)?.[1];
1615
+ }
1616
+ function parseAuthenticationInfo(value) {
1617
+ return value.split(",").map((item) => item.trim()).filter(Boolean).reduce((result, item) => {
1618
+ const [key, rawValue] = item.split("=", 2);
1619
+ if (key && rawValue) {
1620
+ result[key.trim()] = rawValue.trim().replace(/^"|"$/g, "");
1621
+ }
1622
+ return result;
1623
+ }, {});
1624
+ }
1625
+ function parseWwwAuthenticate(value) {
1626
+ const normalized = value.replace(/^DIDWba\s+/i, "").trim();
1627
+ const matches = [...normalized.matchAll(/([\w-]+)=("[^"]*"|[^,]+)/g)];
1628
+ return matches.reduce((result, match) => {
1629
+ result[match[1]] = match[2].trim().replace(/^"|"$/g, "");
1630
+ return result;
1631
+ }, {});
1632
+ }
1633
+ function parseAcceptSignature(value) {
1634
+ return [...value.matchAll(/"([^"]+)"/g)].map((match) => match[1]);
1635
+ }
1636
+ function normalizeCoveredComponents(coveredComponents, headers, body) {
1637
+ if (!coveredComponents) {
1638
+ return void 0;
1639
+ }
1640
+ const normalizedHeaders = Object.fromEntries(
1641
+ Object.entries(headers ?? {}).map(([key, value]) => [key.toLowerCase(), value])
1642
+ );
1643
+ const bodyPresent = body !== void 0 && !(typeof body === "string" && body.length === 0 || body instanceof Uint8Array && body.byteLength === 0);
1644
+ return coveredComponents.filter((component) => {
1645
+ const normalized = component.toLowerCase();
1646
+ if (normalized === "content-digest" && !bodyPresent) {
1647
+ return false;
1648
+ }
1649
+ if (normalized === "content-length" && !bodyPresent && !("content-length" in normalizedHeaders)) {
1650
+ return false;
1651
+ }
1652
+ if (normalized === "content-type" && !("content-type" in normalizedHeaders)) {
1653
+ return false;
1654
+ }
1655
+ if (!normalized.startsWith("@") && normalized !== "content-length" && normalized !== "content-digest" && !(normalized in normalizedHeaders)) {
1656
+ return false;
1657
+ }
1658
+ return true;
1659
+ });
1660
+ }
1661
+ var DidWbaVerifierError = class extends Error {
1662
+ constructor(message, statusCode = 400, headers = {}) {
1663
+ super(message);
1664
+ this.statusCode = statusCode;
1665
+ this.headers = headers;
1666
+ this.name = "DidWbaVerifierError";
1667
+ }
1668
+ };
1669
+ var DEFAULT_CONFIG = {
1670
+ jwtAlgorithm: "RS256",
1671
+ accessTokenExpireMinutes: 60,
1672
+ nonceExpirationMinutes: 6,
1673
+ timestampExpirationMinutes: 5,
1674
+ allowHttpSignatures: true,
1675
+ allowLegacyDidwba: true,
1676
+ emitAuthenticationInfoHeader: true,
1677
+ emitLegacyAuthorizationHeader: true,
1678
+ requireNonceForHttpSignatures: true
1679
+ };
1680
+ var DidWbaVerifier = class {
1681
+ config;
1682
+ usedNonces = /* @__PURE__ */ new Map();
1683
+ constructor(config = {}) {
1684
+ this.config = {
1685
+ ...DEFAULT_CONFIG,
1686
+ ...config
1687
+ };
1688
+ }
1689
+ async verifyRequest(method, url, headers, body, domain) {
1690
+ return this.verifyRequestWithOptionalDidDocument(method, url, headers, body, domain);
1691
+ }
1692
+ async verifyRequestWithDidDocument(method, url, headers, didDocument, body, domain) {
1693
+ return this.verifyRequestWithOptionalDidDocument(
1694
+ method,
1695
+ url,
1696
+ headers,
1697
+ body,
1698
+ domain,
1699
+ didDocument
1700
+ );
1701
+ }
1702
+ async verifyRequestWithOptionalDidDocument(method, url, headers, body, domain, didDocument) {
1703
+ const requestDomain = domain ?? extractDomainFromUrl(url);
1704
+ this.validateAllowedDomain(requestDomain);
1705
+ const authHeader = getHeaderCaseInsensitive3(headers, "Authorization");
1706
+ if (authHeader?.startsWith("Bearer ")) {
1707
+ return this.handleBearerAuth(authHeader);
1708
+ }
1709
+ if (getHeaderCaseInsensitive3(headers, "Signature-Input") || getHeaderCaseInsensitive3(headers, "Signature")) {
1710
+ if (!this.config.allowHttpSignatures) {
1711
+ throw this.challengeError(
1712
+ "HTTP Message Signatures authentication is disabled",
1713
+ 401,
1714
+ requestDomain,
1715
+ "invalid_request"
1716
+ );
1717
+ }
1718
+ return this.handleHttpSignatureAuth(method, url, headers, body, requestDomain, didDocument);
1719
+ }
1720
+ if (authHeader) {
1721
+ if (!this.config.allowLegacyDidwba) {
1722
+ throw this.challengeError(
1723
+ "Legacy DIDWba authentication is disabled",
1724
+ 401,
1725
+ requestDomain,
1726
+ "invalid_request"
1727
+ );
1728
+ }
1729
+ return this.handleLegacyDidAuth(authHeader, requestDomain, didDocument);
1730
+ }
1731
+ throw this.challengeError(
1732
+ "Missing authentication headers",
1733
+ 401,
1734
+ requestDomain,
1735
+ "invalid_request"
1736
+ );
1737
+ }
1738
+ async handleHttpSignatureAuth(method, url, headers, body, domain, providedDidDocument) {
1739
+ let metadata;
1740
+ try {
1741
+ metadata = extractSignatureMetadata(headers);
1742
+ } catch (error) {
1743
+ throw this.challengeError(
1744
+ `Invalid signature metadata: ${error.message}`,
1745
+ 401,
1746
+ domain,
1747
+ "invalid_request"
1748
+ );
1749
+ }
1750
+ if (!metadata.keyid.includes("#")) {
1751
+ throw this.challengeError(
1752
+ "Invalid Signature-Input keyid",
1753
+ 401,
1754
+ domain,
1755
+ "invalid_verification_method"
1756
+ );
1757
+ }
1758
+ const did = metadata.keyid.split("#", 1)[0];
1759
+ const didDocument = providedDidDocument ?? await this.resolveDidDocument(did, domain);
1760
+ if (!validateDidDocumentBinding(didDocument, true)) {
1761
+ throw this.challengeError("DID binding verification failed", 401, domain, "invalid_did");
1762
+ }
1763
+ if (!isAuthenticationAuthorized(didDocument, metadata.keyid)) {
1764
+ throw new DidWbaVerifierError(
1765
+ "Verification method is not authorized for authentication",
1766
+ 403
1767
+ );
1768
+ }
1769
+ let verification;
1770
+ try {
1771
+ verification = verifyHttpMessageSignature(
1772
+ didDocument,
1773
+ method,
1774
+ url,
1775
+ headers,
1776
+ body ?? void 0
1777
+ );
1778
+ } catch (error) {
1779
+ throw this.challengeError(error.message, 401, domain, "invalid_signature");
1780
+ }
1781
+ if (!this.verifyHttpSignatureTimeWindow(verification.created, verification.expires)) {
1782
+ throw this.challengeError(
1783
+ "HTTP signature timestamp is expired or invalid",
1784
+ 401,
1785
+ domain,
1786
+ "invalid_timestamp"
1787
+ );
1788
+ }
1789
+ if (this.config.requireNonceForHttpSignatures && !verification.nonce) {
1790
+ throw this.challengeError("HTTP signature nonce is required", 401, domain, "invalid_nonce");
1791
+ }
1792
+ if (verification.nonce && !await this.isValidServerNonce(did, verification.nonce)) {
1793
+ throw this.challengeError("Invalid or expired nonce", 401, domain, "invalid_nonce");
1794
+ }
1795
+ const accessToken = await this.createAccessToken(did);
1796
+ return this.buildSuccessResult(did, "http_signatures", accessToken);
1797
+ }
1798
+ async handleLegacyDidAuth(authorization, domain, providedDidDocument) {
1799
+ let parsed;
1800
+ try {
1801
+ parsed = extractAuthHeaderParts(authorization);
1802
+ } catch (error) {
1803
+ throw this.challengeError(
1804
+ `Invalid authorization header format: ${error.message}`,
1805
+ 401,
1806
+ domain,
1807
+ "invalid_request"
1808
+ );
1809
+ }
1810
+ if (!this.verifyLegacyTimestamp(parsed.timestamp)) {
1811
+ throw this.challengeError("Timestamp expired or invalid", 401, domain, "invalid_timestamp");
1812
+ }
1813
+ if (!await this.isValidServerNonce(parsed.did, parsed.nonce)) {
1814
+ throw this.challengeError("Invalid or expired nonce", 401, domain, "invalid_nonce");
1815
+ }
1816
+ const didDocument = providedDidDocument ?? await this.resolveDidDocument(parsed.did, domain);
1817
+ if (!validateDidDocumentBinding(didDocument, true)) {
1818
+ throw this.challengeError("DID binding verification failed", 401, domain, "invalid_did");
1819
+ }
1820
+ const keyid = `${parsed.did}#${parsed.verificationMethod}`;
1821
+ if (!isAuthenticationAuthorized(didDocument, keyid)) {
1822
+ throw new DidWbaVerifierError(
1823
+ "Verification method is not authorized for authentication",
1824
+ 403
1825
+ );
1826
+ }
1827
+ try {
1828
+ verifyAuthHeaderSignature(authorization, didDocument, domain);
1829
+ } catch (error) {
1830
+ throw this.challengeError(
1831
+ `Error verifying signature: ${error.message}`,
1832
+ 401,
1833
+ domain,
1834
+ "invalid_signature"
1835
+ );
1836
+ }
1837
+ const accessToken = await this.createAccessToken(parsed.did);
1838
+ return this.buildSuccessResult(parsed.did, "legacy_didwba", accessToken);
1839
+ }
1840
+ async handleBearerAuth(authorization) {
1841
+ if (!this.config.jwtPublicKey) {
1842
+ throw new DidWbaVerifierError("Internal server error during token verification", 500);
1843
+ }
1844
+ const token = authorization.startsWith("Bearer ") ? authorization.slice(7) : authorization;
1845
+ const verifyKey = await importVerifyKey(
1846
+ this.config.jwtPublicKey,
1847
+ this.config.jwtAlgorithm ?? "RS256"
1848
+ );
1849
+ const result = await jwtVerify(token, verifyKey, {
1850
+ algorithms: [this.config.jwtAlgorithm ?? "RS256"]
1851
+ }).catch(() => {
1852
+ throw new DidWbaVerifierError("Invalid token", 401);
1853
+ });
1854
+ const did = result.payload.sub;
1855
+ if (!did || !did.startsWith("did:wba:")) {
1856
+ throw new DidWbaVerifierError("Invalid DID format", 401);
1857
+ }
1858
+ return {
1859
+ did,
1860
+ authScheme: "bearer",
1861
+ responseHeaders: {},
1862
+ accessToken: token,
1863
+ tokenType: "bearer"
1864
+ };
1865
+ }
1866
+ async createAccessToken(did) {
1867
+ if (!this.config.jwtPrivateKey) {
1868
+ return void 0;
1869
+ }
1870
+ const algorithm = this.config.jwtAlgorithm ?? "RS256";
1871
+ const signingKey = await importSigningKey(this.config.jwtPrivateKey, algorithm);
1872
+ return new SignJWT({}).setProtectedHeader({ alg: algorithm }).setSubject(did).setIssuedAt().setExpirationTime(`${this.config.accessTokenExpireMinutes ?? 60}m`).sign(signingKey);
1873
+ }
1874
+ buildSuccessResult(did, authScheme, accessToken) {
1875
+ const responseHeaders = {};
1876
+ if (accessToken) {
1877
+ const expiresInSeconds = (this.config.accessTokenExpireMinutes ?? 60) * 60;
1878
+ if (this.config.emitAuthenticationInfoHeader !== false) {
1879
+ responseHeaders["Authentication-Info"] = `access_token="${accessToken}", token_type="Bearer", expires_in=${expiresInSeconds}`;
1880
+ }
1881
+ if (this.config.emitLegacyAuthorizationHeader !== false) {
1882
+ responseHeaders.Authorization = `Bearer ${accessToken}`;
1883
+ }
1884
+ }
1885
+ return {
1886
+ did,
1887
+ authScheme,
1888
+ responseHeaders,
1889
+ accessToken,
1890
+ tokenType: accessToken ? "bearer" : void 0
1891
+ };
1892
+ }
1893
+ verifyLegacyTimestamp(timestamp) {
1894
+ const requestTime = new Date(timestamp);
1895
+ if (Number.isNaN(requestTime.getTime())) {
1896
+ return false;
1897
+ }
1898
+ const now = Date.now();
1899
+ if (requestTime.getTime() - now > 6e4) {
1900
+ return false;
1901
+ }
1902
+ return now - requestTime.getTime() <= (this.config.timestampExpirationMinutes ?? 5) * 6e4;
1903
+ }
1904
+ verifyHttpSignatureTimeWindow(created, expires) {
1905
+ const now = Math.floor(Date.now() / 1e3);
1906
+ if (created > now + 60) {
1907
+ return false;
1908
+ }
1909
+ if (now - created > (this.config.timestampExpirationMinutes ?? 5) * 60) {
1910
+ return false;
1911
+ }
1912
+ return expires === void 0 || expires >= now;
1913
+ }
1914
+ async isValidServerNonce(did, nonce) {
1915
+ if (this.config.externalNonceValidator) {
1916
+ return Boolean(await this.config.externalNonceValidator(did, nonce));
1917
+ }
1918
+ const expirationMs = (this.config.nonceExpirationMinutes ?? 6) * 6e4;
1919
+ const now = Date.now();
1920
+ for (const [key, createdAt] of this.usedNonces.entries()) {
1921
+ if (now - createdAt.getTime() > expirationMs) {
1922
+ this.usedNonces.delete(key);
1923
+ }
1924
+ }
1925
+ const cacheKey = `${did}:${nonce}`;
1926
+ if (this.usedNonces.has(cacheKey)) {
1927
+ return false;
1928
+ }
1929
+ this.usedNonces.set(cacheKey, new Date(now));
1930
+ return true;
1931
+ }
1932
+ async resolveDidDocument(did, domain) {
1933
+ try {
1934
+ const resolved = this.config.didResolver ? await this.config.didResolver(did) : await resolveDidWbaDocument(did, false, this.config.didResolutionOptions);
1935
+ if (!resolved) {
1936
+ throw this.challengeError("Failed to resolve DID document", 401, domain, "invalid_did");
1937
+ }
1938
+ return resolved;
1939
+ } catch (error) {
1940
+ if (error instanceof DidWbaVerifierError) {
1941
+ throw error;
1942
+ }
1943
+ throw this.challengeError(
1944
+ `Failed to resolve DID document: ${error.message}`,
1945
+ 401,
1946
+ domain,
1947
+ "invalid_did"
1948
+ );
1949
+ }
1950
+ }
1951
+ validateAllowedDomain(domain) {
1952
+ if (this.config.allowedDomains && !this.config.allowedDomains.includes(domain)) {
1953
+ throw new DidWbaVerifierError("Domain is not allowed", 403);
1954
+ }
1955
+ }
1956
+ challengeError(message, statusCode, domain, error) {
1957
+ const headers = {
1958
+ "WWW-Authenticate": `DIDWba realm="${domain}", error="${error}", error_description="${message}"`
1959
+ };
1960
+ if (this.config.allowHttpSignatures !== false) {
1961
+ headers["Accept-Signature"] = 'sig1=("@method" "@target-uri" "@authority" "content-digest");created;expires;nonce;keyid';
1962
+ }
1963
+ return new DidWbaVerifierError(message, statusCode, headers);
1964
+ }
1965
+ };
1966
+ function getHeaderCaseInsensitive3(headers, name) {
1967
+ const target = name.toLowerCase();
1968
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === target)?.[1];
1969
+ }
1970
+ function extractDomainFromUrl(url) {
1971
+ return new URL(url).hostname;
1972
+ }
1973
+ async function importSigningKey(key, algorithm) {
1974
+ if (algorithm.startsWith("HS")) {
1975
+ return new TextEncoder().encode(key);
1976
+ }
1977
+ return importPKCS8(key, algorithm);
1978
+ }
1979
+ async function importVerifyKey(key, algorithm) {
1980
+ if (algorithm.startsWith("HS")) {
1981
+ return new TextEncoder().encode(key);
1982
+ }
1983
+ return importSPKI(key, algorithm);
1984
+ }
1985
+
1986
+ // src/authentication/federation.ts
1987
+ async function verifyFederatedHttpRequest(senderDid, requestMethod, requestUrl, headers, body, options = {}) {
1988
+ const senderDidDocument = options.senderDidDocument ?? await resolveDidDocument(
1989
+ senderDid,
1990
+ options.verifySenderDidProof ?? false,
1991
+ options.didResolutionOptions ?? {}
1992
+ );
1993
+ if (senderDidDocument.id !== senderDid) {
1994
+ throw new AuthenticationError("Sender DID document ID mismatch");
1995
+ }
1996
+ const service = selectAnpMessageService(senderDidDocument, options.serviceId, options.serviceEndpoint);
1997
+ if (!service.serviceDid) {
1998
+ throw new AuthenticationError("Selected ANPMessageService is missing serviceDid");
1999
+ }
2000
+ const signatureMetadata = extractSignatureMetadata(headers);
2001
+ const keyidDid = signatureMetadata.keyid.split("#", 1)[0];
2002
+ if (keyidDid !== service.serviceDid) {
2003
+ throw new AuthenticationError("Signature keyid DID does not match serviceDid");
2004
+ }
2005
+ const serviceDidDocument = options.serviceDidDocument ?? await resolveDidDocument(
2006
+ service.serviceDid,
2007
+ options.verifyServiceDidProof ?? false,
2008
+ options.didResolutionOptions ?? {}
2009
+ );
2010
+ if (serviceDidDocument.id !== service.serviceDid) {
2011
+ throw new AuthenticationError("serviceDid document ID mismatch");
2012
+ }
2013
+ if (!isAuthenticationAuthorized(serviceDidDocument, signatureMetadata.keyid)) {
2014
+ throw new AuthenticationError("Verification method is not authorized for authentication");
2015
+ }
2016
+ const verifiedMetadata = verifyHttpMessageSignature(
2017
+ serviceDidDocument,
2018
+ requestMethod,
2019
+ requestUrl,
2020
+ headers,
2021
+ body
2022
+ );
2023
+ return {
2024
+ senderDid,
2025
+ serviceDid: service.serviceDid,
2026
+ serviceId: service.id,
2027
+ signatureMetadata: verifiedMetadata
2028
+ };
2029
+ }
2030
+ function selectAnpMessageService(didDocument, serviceId, serviceEndpoint) {
2031
+ const candidates = (didDocument.service ?? []).filter(
2032
+ (service) => service.type === ANP_MESSAGE_SERVICE_TYPE
2033
+ );
2034
+ if (candidates.length === 0) {
2035
+ throw new AuthenticationError("No ANPMessageService found in DID document");
2036
+ }
2037
+ if (serviceId) {
2038
+ const matched = candidates.find((service) => service.id === serviceId);
2039
+ if (!matched) {
2040
+ throw new AuthenticationError(`ANPMessageService not found for serviceId=${serviceId}`);
2041
+ }
2042
+ return matched;
2043
+ }
2044
+ if (serviceEndpoint) {
2045
+ const matched = candidates.find((service) => service.serviceEndpoint === serviceEndpoint);
2046
+ if (!matched) {
2047
+ throw new AuthenticationError("ANPMessageService not found for serviceEndpoint");
2048
+ }
2049
+ return matched;
2050
+ }
2051
+ if (candidates.length === 1) {
2052
+ return candidates[0];
2053
+ }
2054
+ throw new AuthenticationError(
2055
+ "Multiple ANPMessageService entries found; serviceId or serviceEndpoint is required"
2056
+ );
2057
+ }
2058
+
2059
+ // src/authentication/index.ts
2060
+ var didDocuments = {
2061
+ ANP_MESSAGE_SERVICE_TYPE,
2062
+ buildAnpMessageService,
2063
+ buildAgentMessageService,
2064
+ buildGroupMessageService,
2065
+ create: createDidWbaDocument,
2066
+ createWithKeyBinding: createDidWbaDocumentWithKeyBinding,
2067
+ resolve: resolveDidDocument,
2068
+ validateBinding: validateDidDocumentBinding,
2069
+ verifyKeyBinding: verifyDidKeyBinding
2070
+ };
2071
+ var legacyAuth = {
2072
+ createHeader: generateAuthHeader,
2073
+ createPayload: generateAuthJson,
2074
+ parseHeader: extractAuthHeaderParts,
2075
+ verifyHeader: verifyAuthHeaderSignature,
2076
+ verifyPayload: verifyAuthJsonSignature
2077
+ };
2078
+ var httpSignatures = {
2079
+ buildContentDigest,
2080
+ verifyContentDigest,
2081
+ createHeaders: generateHttpSignatureHeaders,
2082
+ verifyMessage: verifyHttpMessageSignature,
2083
+ parseMetadata: extractSignatureMetadata
2084
+ };
2085
+ var authentication = {
2086
+ didDocuments,
2087
+ legacyAuth,
2088
+ httpSignatures,
2089
+ federation: {
2090
+ verifyRequest: verifyFederatedHttpRequest
2091
+ },
2092
+ DidAuthHeaders: DIDWbaAuthHeader,
2093
+ RequestVerifier: DidWbaVerifier
2094
+ };
2095
+
2096
+ // src/proof/im.ts
2097
+ var im_exports = {};
2098
+ __export(im_exports, {
2099
+ IM_PROOF_DEFAULT_COMPONENTS: () => IM_PROOF_DEFAULT_COMPONENTS,
2100
+ IM_PROOF_RELATION_ASSERTION_METHOD: () => IM_PROOF_RELATION_ASSERTION_METHOD,
2101
+ IM_PROOF_RELATION_AUTHENTICATION: () => IM_PROOF_RELATION_AUTHENTICATION,
2102
+ buildImContentDigest: () => buildImContentDigest,
2103
+ buildImSignatureInput: () => buildImSignatureInput,
2104
+ decodeImSignature: () => decodeImSignature,
2105
+ encodeImSignature: () => encodeImSignature,
2106
+ generateImProof: () => generateImProof,
2107
+ parseImSignatureInput: () => parseImSignatureInput,
2108
+ verifyImContentDigest: () => verifyImContentDigest,
2109
+ verifyImProof: () => verifyImProof
2110
+ });
2111
+ var IM_PROOF_DEFAULT_COMPONENTS = ["@method", "@target-uri", "content-digest"];
2112
+ var IM_PROOF_RELATION_AUTHENTICATION = "authentication";
2113
+ var IM_PROOF_RELATION_ASSERTION_METHOD = "assertionMethod";
2114
+ function buildImContentDigest(payload) {
2115
+ return buildContentDigest(payload);
2116
+ }
2117
+ function verifyImContentDigest(payload, contentDigest) {
2118
+ return buildImContentDigest(payload) === contentDigest.trim();
2119
+ }
2120
+ function buildImSignatureInput(keyid, options = {}) {
2121
+ const label = options.label ?? "sig1";
2122
+ const components = options.components ?? [...IM_PROOF_DEFAULT_COMPONENTS];
2123
+ if (components.length === 0) {
2124
+ throw new ProofError("signatureInput must include covered components");
2125
+ }
2126
+ const created = options.created ?? Math.floor(Date.now() / 1e3);
2127
+ const nonce = options.nonce ?? encodeBase64Url(randomBytes(16));
2128
+ const quotedComponents = components.map((component) => `"${component}"`).join(" ");
2129
+ const params = [`created=${created}`];
2130
+ if (options.expires !== void 0) {
2131
+ params.push(`expires=${options.expires}`);
2132
+ }
2133
+ params.push(`nonce="${nonce}"`);
2134
+ params.push(`keyid="${keyid}"`);
2135
+ return `${label}=(${quotedComponents});${params.join(";")}`;
2136
+ }
2137
+ function parseImSignatureInput(value) {
2138
+ const separator = value.indexOf("=");
2139
+ if (separator < 0) {
2140
+ throw new ProofError("invalid proof.signatureInput format");
2141
+ }
2142
+ const label = value.slice(0, separator).trim();
2143
+ const remainder = value.slice(separator + 1).trim();
2144
+ const openIndex = remainder.indexOf("(");
2145
+ const closeIndex = remainder.indexOf(")");
2146
+ if (openIndex < 0 || closeIndex < 0 || closeIndex <= openIndex) {
2147
+ throw new ProofError("invalid proof.signatureInput format");
2148
+ }
2149
+ const components = remainder.slice(openIndex + 1, closeIndex).split(/\s+/).map((component) => component.replaceAll('"', "")).filter(Boolean);
2150
+ if (components.length === 0) {
2151
+ throw new ProofError("proof.signatureInput must include covered components");
2152
+ }
2153
+ const params = {};
2154
+ const paramsRaw = remainder.slice(closeIndex + 1).replace(/^;/, "");
2155
+ for (const part of paramsRaw.split(";")) {
2156
+ const trimmed = part.trim();
2157
+ if (!trimmed) {
2158
+ continue;
2159
+ }
2160
+ const [name, rawValue] = trimmed.split("=", 2);
2161
+ if (!name || rawValue === void 0) {
2162
+ throw new ProofError("invalid proof.signatureInput format");
2163
+ }
2164
+ params[name.trim()] = rawValue.trim().replace(/^"|"$/g, "");
2165
+ }
2166
+ if (!params.keyid) {
2167
+ throw new ProofError("proof.signatureInput must include keyid");
2168
+ }
2169
+ return {
2170
+ label,
2171
+ components,
2172
+ signatureParams: remainder,
2173
+ keyid: params.keyid,
2174
+ nonce: params.nonce,
2175
+ created: params.created ? Number(params.created) : void 0,
2176
+ expires: params.expires ? Number(params.expires) : void 0
2177
+ };
2178
+ }
2179
+ function encodeImSignature(signatureBytes, label = "sig1") {
2180
+ return `${label}=:${encodeBase64(signatureBytes)}:`;
2181
+ }
2182
+ function decodeImSignature(signature) {
2183
+ const trimmed = signature.trim();
2184
+ const labeled = trimmed.match(/^\s*([a-zA-Z0-9_-]+)=:(.+):\s*$/);
2185
+ const unlabeled = trimmed.match(/^\s*:(.+):\s*$/);
2186
+ const label = labeled?.[1];
2187
+ const encoded = labeled?.[2] ?? unlabeled?.[1];
2188
+ if (!encoded) {
2189
+ throw new ProofError("invalid proof.signature encoding");
2190
+ }
2191
+ try {
2192
+ return { label, signatureBytes: decodeBase64(encoded) };
2193
+ } catch {
2194
+ try {
2195
+ return { label, signatureBytes: decodeBase64Url(encoded) };
2196
+ } catch {
2197
+ throw new ProofError("invalid proof.signature encoding");
2198
+ }
2199
+ }
2200
+ }
2201
+ function generateImProof(payload, signatureBase, privateKeyInput, keyid, options = {}) {
2202
+ const payloadBytes = toBytes2(payload);
2203
+ const signatureInput = buildImSignatureInput(keyid, options);
2204
+ const privateKey = normalizePrivateKeyMaterial(privateKeyInput);
2205
+ const signatureBytes = signMessage(privateKey, toBytes2(signatureBase));
2206
+ return {
2207
+ contentDigest: buildImContentDigest(payloadBytes),
2208
+ signatureInput,
2209
+ signature: encodeImSignature(signatureBytes, options.label ?? "sig1")
2210
+ };
2211
+ }
2212
+ function verifyImProof(proof2, payload, signatureBase, verificationTarget, expectedSignerDid) {
2213
+ if (!verifyImContentDigest(payload, proof2.contentDigest)) {
2214
+ throw new ProofError("proof contentDigest does not match request payload");
2215
+ }
2216
+ const parsed = parseImSignatureInput(proof2.signatureInput);
2217
+ if (expectedSignerDid && !keyidBelongsToExpectedDid(parsed.keyid, expectedSignerDid)) {
2218
+ throw new ProofError("proof keyid must belong to expected signer DID");
2219
+ }
2220
+ if (verificationTarget.didDocument) {
2221
+ const verificationRelationship = verificationTarget.verificationRelationship ?? IM_PROOF_RELATION_AUTHENTICATION;
2222
+ if (!isVerificationMethodAuthorized2(
2223
+ verificationTarget.didDocument,
2224
+ parsed.keyid,
2225
+ verificationRelationship
2226
+ )) {
2227
+ throw new ProofError(`verification method is not authorized for ${verificationRelationship}`);
2228
+ }
2229
+ }
2230
+ const verificationMethod = verificationTarget.verificationMethod ?? resolveVerificationMethod(verificationTarget.didDocument, parsed.keyid);
2231
+ const publicKey = extractPublicKey(verificationMethod);
2232
+ const { signatureBytes } = decodeImSignature(proof2.signature);
2233
+ if (!verifyMessage(publicKey, toBytes2(signatureBase), signatureBytes)) {
2234
+ throw new ProofError("signature verification failed");
2235
+ }
2236
+ return {
2237
+ parsedSignatureInput: parsed,
2238
+ verificationMethod
2239
+ };
2240
+ }
2241
+ function resolveVerificationMethod(didDocument, verificationMethodId) {
2242
+ if (!didDocument) {
2243
+ throw new ProofError("didDocument or verificationMethod is required");
2244
+ }
2245
+ const method = findVerificationMethod(didDocument, verificationMethodId);
2246
+ if (!method) {
2247
+ throw new ProofError("verification method not found in DID document");
2248
+ }
2249
+ return method;
2250
+ }
2251
+ function toBytes2(value) {
2252
+ return typeof value === "string" ? new TextEncoder().encode(value) : value;
2253
+ }
2254
+ function keyidBelongsToExpectedDid(keyid, expectedSignerDid) {
2255
+ return keyid.split("#", 1)[0] === expectedSignerDid;
2256
+ }
2257
+ function isVerificationMethodAuthorized2(didDocument, verificationMethodId, verificationRelationship) {
2258
+ if (verificationRelationship === IM_PROOF_RELATION_AUTHENTICATION) {
2259
+ return isAuthenticationAuthorized(didDocument, verificationMethodId);
2260
+ }
2261
+ if (verificationRelationship === IM_PROOF_RELATION_ASSERTION_METHOD) {
2262
+ return isAssertionMethodAuthorized(didDocument, verificationMethodId);
2263
+ }
2264
+ throw new ProofError(`unsupported verification relationship: ${verificationRelationship}`);
2265
+ }
2266
+
2267
+ // src/proof/index.ts
2268
+ var proof = {
2269
+ create: generateW3cProof,
2270
+ verify: verifyW3cProof,
2271
+ verifyDetailed: verifyW3cProofDetailed,
2272
+ im: im_exports
2273
+ };
2274
+
2275
+ // src/wns/types.ts
2276
+ var ANP_HANDLE_SERVICE_TYPE = "ANPHandleService";
2277
+ var HandleStatus = /* @__PURE__ */ ((HandleStatus2) => {
2278
+ HandleStatus2["Active"] = "active";
2279
+ HandleStatus2["Suspended"] = "suspended";
2280
+ HandleStatus2["Revoked"] = "revoked";
2281
+ return HandleStatus2;
2282
+ })(HandleStatus || {});
2283
+ var SubjectType = /* @__PURE__ */ ((SubjectType2) => {
2284
+ SubjectType2["Person"] = "person";
2285
+ SubjectType2["Agent"] = "agent";
2286
+ SubjectType2["Group"] = "group";
2287
+ SubjectType2["Organization"] = "organization";
2288
+ SubjectType2["Service"] = "service";
2289
+ SubjectType2["Application"] = "application";
2290
+ SubjectType2["Unknown"] = "unknown";
2291
+ return SubjectType2;
2292
+ })(SubjectType || {});
2293
+
2294
+ // src/wns/validator.ts
2295
+ var DOMAIN_LABEL_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
2296
+ function validateLocalPart(localPart) {
2297
+ const normalized = localPart.toLowerCase();
2298
+ if (!normalized || normalized.length > 63) {
2299
+ return false;
2300
+ }
2301
+ if (normalized.startsWith("-") || normalized.endsWith("-") || normalized.includes("--")) {
2302
+ return false;
2303
+ }
2304
+ return /^[a-z0-9-]+$/.test(normalized);
2305
+ }
2306
+ function validateHandle(handle) {
2307
+ const normalized = handle.trim().toLowerCase();
2308
+ if (!normalized) {
2309
+ throw new HandleValidationError("Handle must not be empty");
2310
+ }
2311
+ const dotIndex = normalized.indexOf(".");
2312
+ if (dotIndex < 0) {
2313
+ throw new HandleValidationError(`Handle must contain at least one dot: '${handle}'`);
2314
+ }
2315
+ const localPart = normalized.slice(0, dotIndex);
2316
+ const domain = normalized.slice(dotIndex + 1);
2317
+ if (!localPart) {
2318
+ throw new HandleValidationError(`Handle local-part is empty: '${handle}'`);
2319
+ }
2320
+ if (!domain) {
2321
+ throw new HandleValidationError(`Handle domain is empty: '${handle}'`);
2322
+ }
2323
+ if (!validateLocalPart(localPart)) {
2324
+ throw new HandleValidationError(
2325
+ `Invalid local-part '${localPart}': must be 1-63 chars of a-z, 0-9, hyphen; must start/end with alnum; no consecutive hyphens`
2326
+ );
2327
+ }
2328
+ if (!isValidDomain(domain)) {
2329
+ throw new HandleValidationError(`Invalid domain '${domain}'`);
2330
+ }
2331
+ return [localPart, domain];
2332
+ }
2333
+ function normalizeHandle(handle) {
2334
+ const [localPart, domain] = validateHandle(handle);
2335
+ return `${localPart}.${domain}`;
2336
+ }
2337
+ function parseWbaUri(uri) {
2338
+ if (!uri.startsWith("wba://")) {
2339
+ throw new WbaUriParseError(`URI must start with 'wba://': '${uri}'`);
2340
+ }
2341
+ const handlePart = uri.slice("wba://".length);
2342
+ if (!handlePart) {
2343
+ throw new WbaUriParseError(`URI contains no handle after 'wba://': '${uri}'`);
2344
+ }
2345
+ try {
2346
+ const [localPart, domain] = validateHandle(handlePart);
2347
+ return {
2348
+ localPart,
2349
+ domain,
2350
+ handle: `${localPart}.${domain}`,
2351
+ originalUri: uri
2352
+ };
2353
+ } catch (error) {
2354
+ throw new WbaUriParseError(`Invalid handle in URI '${uri}': ${error.message}`);
2355
+ }
2356
+ }
2357
+ function buildResolutionUrl(localPart, domain) {
2358
+ return `https://${domain}/.well-known/handle/${localPart}`;
2359
+ }
2360
+ function buildWbaUri(localPart, domain) {
2361
+ return `wba://${localPart}.${domain}`;
2362
+ }
2363
+ function isValidDomain(domain) {
2364
+ const labels = domain.split(".");
2365
+ return labels.length >= 2 && labels.every((label) => DOMAIN_LABEL_RE.test(label));
2366
+ }
2367
+
2368
+ // src/wns/generation.ts
2369
+ function canonicalizeBindingGeneration(value) {
2370
+ if (typeof value !== "string" || !/^[1-9][0-9]*$/.test(value)) {
2371
+ throw new TypeError("binding_generation must be a canonical positive decimal string");
2372
+ }
2373
+ return value;
2374
+ }
2375
+ function compareBindingGenerations(current, previous) {
2376
+ const left = canonicalizeBindingGeneration(current);
2377
+ const right = canonicalizeBindingGeneration(previous);
2378
+ if (left.length !== right.length) return left.length < right.length ? -1 : 1;
2379
+ return left === right ? 0 : left < right ? -1 : 1;
2380
+ }
2381
+
2382
+ // src/wns/resolver.ts
2383
+ async function resolveHandle(handle, options = {}) {
2384
+ const bareHandle = stripWbaScheme(handle);
2385
+ const [localPart, domain] = validateHandle(bareHandle);
2386
+ const normalized = `${localPart}.${domain}`;
2387
+ const baseUrl = options.baseUrlOverride?.replace(/\/$/, "");
2388
+ const url = baseUrl ? `${baseUrl}/.well-known/handle/${localPart}` : buildResolutionUrl(localPart, domain);
2389
+ void options.verifySsl;
2390
+ const timeoutMs = Math.round((options.timeoutSeconds ?? 10) * 1e3);
2391
+ const response = await fetch(url, {
2392
+ headers: { Accept: "application/json" },
2393
+ redirect: "manual",
2394
+ signal: AbortSignal.timeout(timeoutMs)
2395
+ }).catch((error) => {
2396
+ throw new HandleResolutionError(
2397
+ `Network error resolving handle '${normalized}': ${error.message}`,
2398
+ 502,
2399
+ error
2400
+ );
2401
+ });
2402
+ if (response.status === 301) {
2403
+ throw new HandleMovedError(
2404
+ `Handle '${normalized}' has been migrated`,
2405
+ response.headers.get("Location") ?? ""
2406
+ );
2407
+ }
2408
+ if (response.status === 404) {
2409
+ throw new HandleNotFoundError(`Handle '${normalized}' does not exist`);
2410
+ }
2411
+ if (response.status === 410) {
2412
+ throw new HandleGoneError(`Handle '${normalized}' has been permanently revoked`);
2413
+ }
2414
+ if (!response.ok) {
2415
+ throw new HandleResolutionError(
2416
+ `Unexpected status ${response.status} resolving '${normalized}'`,
2417
+ 502
2418
+ );
2419
+ }
2420
+ const payload = await response.json();
2421
+ const document = {
2422
+ handle: String(payload.handle ?? ""),
2423
+ did: String(payload.did ?? ""),
2424
+ status: normalizeStatus(String(payload.status ?? "")),
2425
+ binding_generation: canonicalizeBindingGeneration(payload.binding_generation),
2426
+ updated: payload.updated ? String(payload.updated) : void 0,
2427
+ versionId: payload.versionId ? String(payload.versionId) : void 0,
2428
+ ttl: typeof payload.ttl === "number" ? payload.ttl : void 0,
2429
+ profile: normalizeProfile(payload.profile)
2430
+ };
2431
+ if (document.handle.toLowerCase() !== normalized) {
2432
+ throw new HandleResolutionError(
2433
+ `Handle mismatch: requested '${normalized}', got '${document.handle}'`,
2434
+ 502
2435
+ );
2436
+ }
2437
+ dropInvalidProfileProjection(document);
2438
+ return document;
2439
+ }
2440
+ async function resolveHandleFromUri(wbaUri, options = {}) {
2441
+ const parsed = parseWbaUri(wbaUri);
2442
+ return resolveHandle(parsed.handle, options);
2443
+ }
2444
+ function stripWbaScheme(handleOrUri) {
2445
+ return handleOrUri.startsWith("wba://") ? handleOrUri.slice("wba://".length) : handleOrUri;
2446
+ }
2447
+ function normalizeStatus(value) {
2448
+ switch (value.toLowerCase()) {
2449
+ case "active" /* Active */:
2450
+ return "active" /* Active */;
2451
+ case "suspended" /* Suspended */:
2452
+ return "suspended" /* Suspended */;
2453
+ case "revoked" /* Revoked */:
2454
+ return "revoked" /* Revoked */;
2455
+ default:
2456
+ throw new HandleResolutionError(`Unexpected handle status '${value}'`, 502);
2457
+ }
2458
+ }
2459
+ function normalizeProfile(value) {
2460
+ if (!isRecord(value)) {
2461
+ return void 0;
2462
+ }
2463
+ return {
2464
+ type: value.type ? String(value.type) : void 0,
2465
+ subject_did: String(value.subject_did ?? ""),
2466
+ subject_type: normalizeSubjectType(value.subject_type),
2467
+ handle: value.handle ? String(value.handle) : void 0,
2468
+ display_name: value.display_name ? String(value.display_name) : void 0,
2469
+ description: value.description ? String(value.description) : void 0,
2470
+ avatar_uri: value.avatar_uri ? String(value.avatar_uri) : void 0,
2471
+ profile_uri: value.profile_uri ? String(value.profile_uri) : void 0,
2472
+ discoverability: value.discoverability ? String(value.discoverability) : void 0,
2473
+ labels: isRecord(value.labels) ? value.labels : void 0,
2474
+ updated: value.updated ? String(value.updated) : void 0,
2475
+ versionId: value.versionId ? String(value.versionId) : void 0,
2476
+ ttl: typeof value.ttl === "number" ? value.ttl : void 0,
2477
+ proof: isRecord(value.proof) ? value.proof : void 0
2478
+ };
2479
+ }
2480
+ function dropInvalidProfileProjection(document) {
2481
+ if (!document.profile) {
2482
+ return;
2483
+ }
2484
+ if (document.profile.subject_did !== document.did) {
2485
+ document.profile = void 0;
2486
+ return;
2487
+ }
2488
+ if (document.profile.handle && document.profile.handle !== document.handle) {
2489
+ document.profile = void 0;
2490
+ }
2491
+ }
2492
+ function normalizeSubjectType(value) {
2493
+ if (!value) {
2494
+ return "unknown" /* Unknown */;
2495
+ }
2496
+ const normalized = String(value).toLowerCase();
2497
+ return Object.values(SubjectType).includes(normalized) ? normalized : "unknown" /* Unknown */;
2498
+ }
2499
+ function isRecord(value) {
2500
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2501
+ }
2502
+
2503
+ // src/wns/binding.ts
2504
+ async function verifyHandleBinding(handle, options = {}) {
2505
+ const bareHandle = handle.startsWith("wba://") ? handle.slice("wba://".length) : handle;
2506
+ let localPart;
2507
+ let domain;
2508
+ try {
2509
+ [localPart, domain] = validateHandle(bareHandle);
2510
+ } catch (error) {
2511
+ return {
2512
+ isValid: false,
2513
+ handle: bareHandle,
2514
+ did: "",
2515
+ forwardVerified: false,
2516
+ reverseVerified: false,
2517
+ errorMessage: error.message
2518
+ };
2519
+ }
2520
+ const normalizedHandle = `${localPart}.${domain}`;
2521
+ try {
2522
+ const resolution = await resolveHandle(normalizedHandle, options.resolutionOptions);
2523
+ if (resolution.status !== "active" /* Active */) {
2524
+ return {
2525
+ isValid: false,
2526
+ handle: normalizedHandle,
2527
+ did: resolution.did,
2528
+ forwardVerified: false,
2529
+ reverseVerified: false,
2530
+ errorMessage: `Handle status is '${resolution.status}', expected 'active'`
2531
+ };
2532
+ }
2533
+ if (!resolution.did.startsWith("did:wba:")) {
2534
+ return {
2535
+ isValid: false,
2536
+ handle: normalizedHandle,
2537
+ did: resolution.did,
2538
+ forwardVerified: true,
2539
+ reverseVerified: false,
2540
+ errorMessage: "DID does not use did:wba method"
2541
+ };
2542
+ }
2543
+ const didDomain = resolution.did.split(":")[2] ?? "";
2544
+ if (didDomain.toLowerCase() !== domain) {
2545
+ return {
2546
+ isValid: false,
2547
+ handle: normalizedHandle,
2548
+ did: resolution.did,
2549
+ forwardVerified: true,
2550
+ reverseVerified: false,
2551
+ errorMessage: `Domain mismatch: handle domain '${domain}' != DID domain '${didDomain}'`
2552
+ };
2553
+ }
2554
+ const didDocument = options.didDocument ?? await resolveDidWbaDocument(resolution.did, false, options.didResolutionOptions);
2555
+ const handleServices = extractHandleServiceFromDidDocument(didDocument);
2556
+ const reverseVerified = handleServices.some(
2557
+ (service) => matchesHandleServiceDomain(service.serviceEndpoint, domain)
2558
+ );
2559
+ if (!reverseVerified) {
2560
+ return {
2561
+ isValid: false,
2562
+ handle: normalizedHandle,
2563
+ did: resolution.did,
2564
+ forwardVerified: true,
2565
+ reverseVerified: false,
2566
+ errorMessage: `DID Document does not contain an ${ANP_HANDLE_SERVICE_TYPE} entry whose HTTPS domain matches '${domain}'`
2567
+ };
2568
+ }
2569
+ return {
2570
+ isValid: true,
2571
+ handle: normalizedHandle,
2572
+ did: resolution.did,
2573
+ bindingGeneration: resolution.binding_generation,
2574
+ forwardVerified: true,
2575
+ reverseVerified: true
2576
+ };
2577
+ } catch (error) {
2578
+ if (error instanceof HandleBindingError) {
2579
+ throw error;
2580
+ }
2581
+ return {
2582
+ isValid: false,
2583
+ handle: normalizedHandle,
2584
+ did: "",
2585
+ forwardVerified: false,
2586
+ reverseVerified: false,
2587
+ errorMessage: error.message
2588
+ };
2589
+ }
2590
+ }
2591
+ function buildHandleServiceEntry(did, localPart, domain) {
2592
+ return {
2593
+ id: `${did}#handle`,
2594
+ type: ANP_HANDLE_SERVICE_TYPE,
2595
+ serviceEndpoint: buildResolutionUrl(localPart, domain)
2596
+ };
2597
+ }
2598
+ function extractHandleServiceFromDidDocument(didDocument) {
2599
+ return (didDocument.service ?? []).filter((service) => service.type === ANP_HANDLE_SERVICE_TYPE).map((service) => ({
2600
+ id: String(service.id),
2601
+ type: String(service.type),
2602
+ serviceEndpoint: String(service.serviceEndpoint)
2603
+ }));
2604
+ }
2605
+ function matchesHandleServiceDomain(serviceEndpoint, expectedDomain) {
2606
+ try {
2607
+ const parsed = new URL(serviceEndpoint);
2608
+ return parsed.protocol === "https:" && parsed.hostname.toLowerCase() === expectedDomain.toLowerCase();
2609
+ } catch {
2610
+ return false;
2611
+ }
2612
+ }
2613
+
2614
+ // src/wns/index.ts
2615
+ var wns = {
2616
+ validateLocalPart,
2617
+ validateHandle,
2618
+ normalizeHandle,
2619
+ parseUri: parseWbaUri,
2620
+ buildResolutionUrl,
2621
+ buildUri: buildWbaUri,
2622
+ resolveHandle,
2623
+ resolveUri: resolveHandleFromUri,
2624
+ verifyBinding: verifyHandleBinding,
2625
+ createHandleServiceEntry: buildHandleServiceEntry,
2626
+ extractHandleServices: extractHandleServiceFromDidDocument,
2627
+ canonicalizeBindingGeneration,
2628
+ compareBindingGenerations
2629
+ };
2630
+
2631
+ // src/im/errors.ts
2632
+ var AwikiImError = class extends Error {
2633
+ /**
2634
+ * Create a normalized AWiki IM error.
2635
+ *
2636
+ * @param code Stable category for callers.
2637
+ * @param message Public diagnostic message.
2638
+ * @param options Optional transport status and underlying cause.
2639
+ */
2640
+ constructor(code, message, status, options) {
2641
+ super(message, options?.cause === void 0 ? void 0 : { cause: options.cause });
2642
+ this.code = code;
2643
+ this.status = status;
2644
+ this.name = "AwikiImError";
2645
+ }
2646
+ };
2647
+ function normalizeAwikiImError(error) {
2648
+ if (error instanceof AwikiImError) {
2649
+ return error;
2650
+ }
2651
+ if (error instanceof TypeError) {
2652
+ return new AwikiImError("network", "AWiki service is unavailable", void 0, {
2653
+ cause: error
2654
+ });
2655
+ }
2656
+ return new AwikiImError("remote", "AWiki operation failed", void 0, { cause: error });
2657
+ }
2658
+ function awikiImRemoteError(input) {
2659
+ const code = classifyRemoteError(input);
2660
+ return new AwikiImError(code, publicMessage(code), input.status);
2661
+ }
2662
+ function classifyRemoteError(input) {
2663
+ const serviceCode = input.serviceCode?.toLowerCase() ?? "";
2664
+ const message = input.message?.toLowerCase() ?? "";
2665
+ const combined = `${serviceCode} ${message}`;
2666
+ if (input.rpcCode === 1003) {
2667
+ return "invalid-request";
2668
+ }
2669
+ if (input.rpcCode === 1403 || serviceCode === "anp.forbidden") {
2670
+ return "forbidden";
2671
+ }
2672
+ if (input.rpcCode === 1404 || serviceCode === "anp.target_not_found" || serviceCode === "target_not_found") {
2673
+ return "not-found";
2674
+ }
2675
+ if (input.rpcCode === 1409 || serviceCode === "anp.idempotency_conflict" || serviceCode === "idempotency_conflict") {
2676
+ return "conflict";
2677
+ }
2678
+ if (combined.includes("otp_rate_limited") || input.status === 429 || input.rpcCode === -32005) {
2679
+ return "rate-limited";
2680
+ }
2681
+ if (combined.includes("invalid_otp") || combined.includes("otp_invalid")) {
2682
+ return "invalid-otp";
2683
+ }
2684
+ if (combined.includes("otp_expired") || combined.includes("challenge_expired")) {
2685
+ return "challenge-expired";
2686
+ }
2687
+ if (combined.includes("handle_unavailable") || combined.includes("handle_exists") || combined.includes("handle already")) {
2688
+ return "handle-unavailable";
2689
+ }
2690
+ if (combined.includes("already_registered") || combined.includes("did already")) {
2691
+ return "already-registered";
2692
+ }
2693
+ if (input.status === 404 || input.rpcCode === -32002) {
2694
+ return "not-found";
2695
+ }
2696
+ if (input.status === 401 || input.status === 403 || input.rpcCode === -32001) {
2697
+ return "forbidden";
2698
+ }
2699
+ if (input.status === 409 || input.rpcCode === -32003) {
2700
+ return "conflict";
2701
+ }
2702
+ if (input.rpcCode === -32600 || input.rpcCode === -32602 || input.rpcCode === -32004) {
2703
+ return combined.includes("otp") ? "invalid-otp" : "invalid-request";
2704
+ }
2705
+ if (input.status !== void 0 && input.status >= 400 && input.status < 500) {
2706
+ return "invalid-request";
2707
+ }
2708
+ return "remote";
2709
+ }
2710
+ function publicMessage(code) {
2711
+ switch (code) {
2712
+ case "not-registered":
2713
+ return "AWiki identity is not registered";
2714
+ case "already-registered":
2715
+ return "AWiki identity is already registered";
2716
+ case "invalid-request":
2717
+ return "AWiki rejected the request";
2718
+ case "invalid-otp":
2719
+ return "AWiki verification code is invalid";
2720
+ case "challenge-expired":
2721
+ return "AWiki registration challenge has expired";
2722
+ case "handle-unavailable":
2723
+ return "AWiki handle is unavailable";
2724
+ case "not-found":
2725
+ return "AWiki resource was not found";
2726
+ case "forbidden":
2727
+ return "AWiki operation is not permitted";
2728
+ case "conflict":
2729
+ return "AWiki operation conflicts with existing state";
2730
+ case "rate-limited":
2731
+ return "AWiki request was rate limited";
2732
+ case "network":
2733
+ return "AWiki service is unavailable";
2734
+ case "remote":
2735
+ return "AWiki service returned an error";
2736
+ }
2737
+ }
2738
+
2739
+ // src/im/internal.ts
2740
+ var STATE_VERSION = 2;
2741
+ var DEFAULT_PAGE_LIMIT = 50;
2742
+ var MAX_PAGE_LIMIT = 100;
2743
+ function emptyState() {
2744
+ return {
2745
+ version: STATE_VERSION,
2746
+ conversations: {},
2747
+ attachments: {},
2748
+ sendOperations: {}
2749
+ };
2750
+ }
2751
+ function conversationKey(id) {
2752
+ return id;
2753
+ }
2754
+ var HANDLE_RPC_PATH = "/user-service/v1/handle/rpc";
2755
+ var DID_AUTH_RPC_PATH = "/user-service/v1/did-auth/rpc";
2756
+ var MESSAGE_RPC_PATH = "/im/rpc";
2757
+ var MAX_RPC_RESPONSE_BYTES = 1024 * 1024;
2758
+ var MAX_DID_DOCUMENT_BYTES = 512 * 1024;
2759
+ var REQUEST_TIMEOUT_MS = 3e4;
2760
+ var AwikiImTransport = class {
2761
+ constructor(fetchImpl, options) {
2762
+ this.fetchImpl = fetchImpl;
2763
+ this.options = options;
2764
+ this.allowedAttachmentOrigins = new Set(
2765
+ options.allowedAttachmentOrigins.map(
2766
+ (value) => normalizeAllowedOrigin(value, options.allowInsecureLoopback)
2767
+ )
2768
+ );
2769
+ }
2770
+ controllers = /* @__PURE__ */ new Set();
2771
+ allowedAttachmentOrigins;
2772
+ disposed = false;
2773
+ /** Execute an unsigned or bearer-authenticated JSON-RPC request. */
2774
+ async rpc(baseUrl, path, method, params, accessToken) {
2775
+ const url = joinServiceUrl(baseUrl, path, this.options.allowInsecureLoopback);
2776
+ const request = encodeJsonRpc(method, params);
2777
+ const body = request.body;
2778
+ const headers = { "Content-Type": "application/json" };
2779
+ if (accessToken) {
2780
+ headers.Authorization = `Bearer ${accessToken}`;
2781
+ }
2782
+ return this.executeRpc(url, body, headers, request.id);
2783
+ }
2784
+ /** Execute a DID HTTP-Signature-authenticated JSON-RPC request. */
2785
+ async signedRpc(baseUrl, path, method, params, authentication2) {
2786
+ const url = joinServiceUrl(baseUrl, path, this.options.allowInsecureLoopback);
2787
+ const request = encodeJsonRpc(method, params);
2788
+ const body = request.body;
2789
+ const baseHeaders = { "Content-Type": "application/json" };
2790
+ let headers = {
2791
+ ...baseHeaders,
2792
+ ...generateHttpSignatureHeaders(
2793
+ authentication2.didDocument,
2794
+ url,
2795
+ "POST",
2796
+ authentication2.signingPrivateKeyPem,
2797
+ baseHeaders,
2798
+ body,
2799
+ { keyid: authentication2.signingKeyId }
2800
+ )
2801
+ };
2802
+ const result = await this.withResponse(
2803
+ url,
2804
+ {
2805
+ method: "POST",
2806
+ headers,
2807
+ body,
2808
+ redirect: "error"
2809
+ },
2810
+ async (response) => {
2811
+ if (response.status !== 401) {
2812
+ return {
2813
+ kind: "result",
2814
+ value: await decodeRpcResponse(response, request.id)
2815
+ };
2816
+ }
2817
+ const nonce = parseAuthenticationParameter(
2818
+ response.headers.get("www-authenticate") ?? "",
2819
+ "nonce"
2820
+ );
2821
+ await response.body?.cancel();
2822
+ return { kind: "challenge", nonce };
2823
+ }
2824
+ );
2825
+ if (result.kind === "challenge" && result.nonce) {
2826
+ headers = {
2827
+ ...baseHeaders,
2828
+ ...generateHttpSignatureHeaders(
2829
+ authentication2.didDocument,
2830
+ url,
2831
+ "POST",
2832
+ authentication2.signingPrivateKeyPem,
2833
+ baseHeaders,
2834
+ body,
2835
+ { keyid: authentication2.signingKeyId, nonce: result.nonce }
2836
+ )
2837
+ };
2838
+ return this.withResponse(
2839
+ url,
2840
+ {
2841
+ method: "POST",
2842
+ headers,
2843
+ body,
2844
+ redirect: "error"
2845
+ },
2846
+ (response) => decodeRpcResponse(response, request.id)
2847
+ );
2848
+ }
2849
+ if (result.kind === "challenge") {
2850
+ throw awikiImRemoteError({ status: 401 });
2851
+ }
2852
+ return result.value;
2853
+ }
2854
+ /** Upload one object without following redirects. */
2855
+ async putBytes(url, headers, bytes) {
2856
+ this.validateAttachmentUrl(url);
2857
+ await this.withResponse(
2858
+ url,
2859
+ {
2860
+ method: "PUT",
2861
+ headers: { ...headers },
2862
+ body: Buffer.from(bytes),
2863
+ redirect: "error"
2864
+ },
2865
+ async (response) => {
2866
+ if (!response.ok) {
2867
+ await response.body?.cancel();
2868
+ throw awikiImRemoteError({ status: response.status });
2869
+ }
2870
+ await response.body?.cancel();
2871
+ }
2872
+ );
2873
+ }
2874
+ /** Read one public JSON document without following redirects. */
2875
+ async getJson(url) {
2876
+ this.validateAttachmentUrl(url);
2877
+ return this.withResponse(
2878
+ url,
2879
+ {
2880
+ method: "GET",
2881
+ headers: { Accept: "application/json" },
2882
+ redirect: "error"
2883
+ },
2884
+ async (response) => {
2885
+ if (!response.ok) {
2886
+ await response.body?.cancel();
2887
+ throw awikiImRemoteError({ status: response.status });
2888
+ }
2889
+ const value = parseJson(await readCappedBody(response, MAX_DID_DOCUMENT_BYTES));
2890
+ if (!isRecord2(value)) {
2891
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
2892
+ }
2893
+ return value;
2894
+ }
2895
+ );
2896
+ }
2897
+ /** Download one object without following redirects. */
2898
+ async getBytes(url, bearerToken, expectedSize) {
2899
+ if (!Number.isSafeInteger(expectedSize) || expectedSize < 0 || expectedSize > this.options.attachmentMaxBytes) {
2900
+ throw new AwikiImError("invalid-request", "AWiki attachment size is invalid");
2901
+ }
2902
+ this.validateAttachmentUrl(url);
2903
+ return this.withResponse(
2904
+ url,
2905
+ {
2906
+ method: "GET",
2907
+ headers: { Authorization: `Bearer ${bearerToken}` },
2908
+ redirect: "error"
2909
+ },
2910
+ async (response) => {
2911
+ if (!response.ok) {
2912
+ await response.body?.cancel();
2913
+ throw awikiImRemoteError({ status: response.status });
2914
+ }
2915
+ const declaredLength = contentLength(response.headers);
2916
+ if (declaredLength !== void 0 && declaredLength !== expectedSize) {
2917
+ await response.body?.cancel();
2918
+ throw new AwikiImError("remote", "AWiki attachment verification failed");
2919
+ }
2920
+ return readCappedBody(response, expectedSize);
2921
+ }
2922
+ );
2923
+ }
2924
+ /** Validate an untrusted DID, service, upload, or object URL against the operator allowlist. */
2925
+ validateAttachmentUrl(value) {
2926
+ const url = validateServiceBaseUrl(value, this.options.allowInsecureLoopback);
2927
+ if (!this.allowedAttachmentOrigins.has(url.origin)) {
2928
+ throw new AwikiImError("forbidden", "AWiki attachment origin is not permitted");
2929
+ }
2930
+ return url;
2931
+ }
2932
+ /** Abort every owned request and reject future work. */
2933
+ dispose() {
2934
+ this.disposed = true;
2935
+ for (const controller of this.controllers) {
2936
+ controller.abort();
2937
+ }
2938
+ this.controllers.clear();
2939
+ }
2940
+ async executeRpc(url, body, headers, requestId) {
2941
+ return this.withResponse(
2942
+ url,
2943
+ {
2944
+ method: "POST",
2945
+ headers,
2946
+ body,
2947
+ redirect: "error"
2948
+ },
2949
+ (response) => decodeRpcResponse(response, requestId)
2950
+ );
2951
+ }
2952
+ async withResponse(input, init, consume) {
2953
+ if (this.disposed) {
2954
+ throw new AwikiImError("remote", "AWiki IM client has been disposed");
2955
+ }
2956
+ const controller = new AbortController();
2957
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
2958
+ this.controllers.add(controller);
2959
+ try {
2960
+ const response = await this.fetchImpl(input, { ...init, signal: controller.signal });
2961
+ return await consume(response);
2962
+ } catch (error) {
2963
+ if (this.disposed) {
2964
+ throw new AwikiImError("remote", "AWiki IM client has been disposed");
2965
+ }
2966
+ throw normalizeAwikiImError(error);
2967
+ } finally {
2968
+ clearTimeout(timeout);
2969
+ this.controllers.delete(controller);
2970
+ }
2971
+ }
2972
+ };
2973
+ function buildOriginAuthentication(input) {
2974
+ const target = input.meta.target;
2975
+ if (!isRecord2(target)) {
2976
+ throw new AwikiImError("invalid-request", "AWiki message target is invalid");
2977
+ }
2978
+ const kind = requiredString(target.kind, "target kind");
2979
+ const did = requiredString(target.did, "target DID");
2980
+ if (kind !== "agent" && kind !== "group" && kind !== "service") {
2981
+ throw new AwikiImError("invalid-request", "AWiki message target is invalid");
2982
+ }
2983
+ const canonicalRequest = canonicalizeJson({
2984
+ method: input.method,
2985
+ meta: input.meta,
2986
+ body: input.body
2987
+ });
2988
+ const signatureInput = buildImSignatureInput(input.signingKeyId, {
2989
+ label: "sig1",
2990
+ components: ["@method", "@target-uri", "content-digest"]
2991
+ });
2992
+ const contentDigest = buildImContentDigest(canonicalRequest);
2993
+ const targetUri = `anp://${kind}/${encodeRfc3986(did)}`;
2994
+ const signatureParams = signatureInput.slice(signatureInput.indexOf("=") + 1);
2995
+ const signatureBase = [
2996
+ `"@method": ${input.method}`,
2997
+ `"@target-uri": ${targetUri}`,
2998
+ `"content-digest": ${contentDigest}`,
2999
+ `"@signature-params": ${signatureParams}`
3000
+ ].join("\n");
3001
+ const signature = signMessage(
3002
+ normalizePrivateKeyMaterial(input.signingPrivateKeyPem),
3003
+ new TextEncoder().encode(signatureBase)
3004
+ );
3005
+ return {
3006
+ scheme: "anp-rfc9421-origin-proof-v1",
3007
+ origin_proof: {
3008
+ contentDigest,
3009
+ signatureInput,
3010
+ signature: encodeImSignature(signature, "sig1")
3011
+ }
3012
+ };
3013
+ }
3014
+ function joinServiceUrl(baseUrl, path, allowInsecureLoopback = false) {
3015
+ const base = validateServiceBaseUrl(baseUrl, allowInsecureLoopback);
3016
+ return new URL(`/${path.replace(/^\/+/, "")}`, base).toString();
3017
+ }
3018
+ function validateServiceBaseUrl(value, allowInsecureLoopback = false) {
3019
+ let url;
3020
+ try {
3021
+ url = new URL(value);
3022
+ } catch (error) {
3023
+ throw new AwikiImError("invalid-request", "AWiki service URL is invalid", void 0, {
3024
+ cause: error
3025
+ });
3026
+ }
3027
+ if (url.username || url.password || url.protocol !== "https:" && !(allowInsecureLoopback && url.protocol === "http:" && isLoopback(url.hostname))) {
3028
+ throw new AwikiImError("invalid-request", "AWiki service URL is invalid");
3029
+ }
3030
+ return url;
3031
+ }
3032
+ function operationId(prefix) {
3033
+ return `${prefix}-${randomUUID()}`;
3034
+ }
3035
+ function randomChallenge() {
3036
+ return randomBytes(16).toString("hex");
3037
+ }
3038
+ function encodeJsonRpc(method, params) {
3039
+ const id = randomUUID();
3040
+ return { id, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }) };
3041
+ }
3042
+ async function decodeRpcResponse(response, expectedId) {
3043
+ let decoded;
3044
+ try {
3045
+ decoded = parseJson(await readCappedBody(response, MAX_RPC_RESPONSE_BYTES));
3046
+ } catch (error) {
3047
+ if (!response.ok) {
3048
+ throw awikiImRemoteError({ status: response.status });
3049
+ }
3050
+ throw new AwikiImError(
3051
+ "remote",
3052
+ "AWiki service returned an invalid response",
3053
+ response.status,
3054
+ {
3055
+ cause: error
3056
+ }
3057
+ );
3058
+ }
3059
+ if (!isRecord2(decoded)) {
3060
+ throw new AwikiImError("remote", "AWiki service returned an invalid response", response.status);
3061
+ }
3062
+ const hasResult = Object.hasOwn(decoded, "result") && decoded.result !== null;
3063
+ const hasError = Object.hasOwn(decoded, "error") && decoded.error !== null;
3064
+ if (decoded.jsonrpc !== "2.0" || decoded.id !== expectedId || hasResult === hasError) {
3065
+ throw new AwikiImError(
3066
+ "remote",
3067
+ "AWiki JSON-RPC response envelope is invalid",
3068
+ response.status
3069
+ );
3070
+ }
3071
+ if (hasError && !isRecord2(decoded.error)) {
3072
+ throw new AwikiImError(
3073
+ "remote",
3074
+ "AWiki JSON-RPC response envelope is invalid",
3075
+ response.status
3076
+ );
3077
+ }
3078
+ const rpcError = hasError ? decoded.error : void 0;
3079
+ if (!response.ok || rpcError !== void 0) {
3080
+ throw awikiImRemoteError({
3081
+ status: response.status,
3082
+ rpcCode: numberValue(rpcError?.code),
3083
+ serviceCode: serviceErrorCode(rpcError?.data),
3084
+ message: stringValue(rpcError?.message)
3085
+ });
3086
+ }
3087
+ if (!isRecord2(decoded.result)) {
3088
+ throw new AwikiImError("remote", "AWiki service returned an invalid response", response.status);
3089
+ }
3090
+ return {
3091
+ value: decoded.result,
3092
+ accessToken: responseAccessToken(response.headers) ?? stringValue(decoded.result.access_token)
3093
+ };
3094
+ }
3095
+ function responseAccessToken(headers) {
3096
+ const authenticationInfo = headers.get("authentication-info") ?? "";
3097
+ return parseAuthenticationParameter(authenticationInfo, "access_token");
3098
+ }
3099
+ function parseAuthenticationParameter(value, key) {
3100
+ const match = value.match(new RegExp(`(?:^|[,\\s])${key}=(?:"([^"]+)"|([^,\\s]+))`, "i"));
3101
+ return (match?.[1] ?? match?.[2])?.trim() || void 0;
3102
+ }
3103
+ function serviceErrorCode(data) {
3104
+ if (!isRecord2(data)) {
3105
+ return void 0;
3106
+ }
3107
+ for (const key of ["awiki_code", "anp_code", "code"]) {
3108
+ const value = stringValue(data[key]);
3109
+ if (value) {
3110
+ return value;
3111
+ }
3112
+ }
3113
+ return void 0;
3114
+ }
3115
+ function numberValue(value) {
3116
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3117
+ }
3118
+ function stringValue(value) {
3119
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
3120
+ }
3121
+ function requiredString(value, label) {
3122
+ const result = stringValue(value);
3123
+ if (!result) {
3124
+ throw new AwikiImError("invalid-request", `AWiki ${label} is required`);
3125
+ }
3126
+ return result;
3127
+ }
3128
+ function normalizeAllowedOrigin(value, allowInsecureLoopback) {
3129
+ const url = validateServiceBaseUrl(value, allowInsecureLoopback);
3130
+ if (url.pathname !== "/" || url.search || url.hash) {
3131
+ throw new AwikiImError("invalid-request", "AWiki attachment origin is invalid");
3132
+ }
3133
+ return url.origin;
3134
+ }
3135
+ function isLoopback(hostname) {
3136
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
3137
+ }
3138
+ async function readCappedBody(response, maximumBytes) {
3139
+ const declaredLength = contentLength(response.headers);
3140
+ if (declaredLength !== void 0 && declaredLength > maximumBytes) {
3141
+ await response.body?.cancel();
3142
+ throw new AwikiImError("remote", "AWiki response exceeds the permitted size");
3143
+ }
3144
+ if (!response.body) {
3145
+ return new Uint8Array();
3146
+ }
3147
+ const reader = response.body.getReader();
3148
+ const chunks = [];
3149
+ let total = 0;
3150
+ try {
3151
+ let part = await reader.read();
3152
+ while (!part.done) {
3153
+ total += part.value.byteLength;
3154
+ if (total > maximumBytes) {
3155
+ await reader.cancel();
3156
+ throw new AwikiImError("remote", "AWiki response exceeds the permitted size");
3157
+ }
3158
+ chunks.push(part.value);
3159
+ part = await reader.read();
3160
+ }
3161
+ } finally {
3162
+ reader.releaseLock();
3163
+ }
3164
+ const output = new Uint8Array(total);
3165
+ let offset = 0;
3166
+ for (const chunk of chunks) {
3167
+ output.set(chunk, offset);
3168
+ offset += chunk.byteLength;
3169
+ }
3170
+ return output;
3171
+ }
3172
+ function contentLength(headers) {
3173
+ const raw = headers.get("content-length");
3174
+ if (raw === null) {
3175
+ return void 0;
3176
+ }
3177
+ const value = Number(raw);
3178
+ if (!Number.isSafeInteger(value) || value < 0) {
3179
+ throw new AwikiImError("remote", "AWiki response has an invalid Content-Length");
3180
+ }
3181
+ return value;
3182
+ }
3183
+ function parseJson(bytes) {
3184
+ try {
3185
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
3186
+ } catch (error) {
3187
+ throw new AwikiImError("remote", "AWiki service returned an invalid response", void 0, {
3188
+ cause: error
3189
+ });
3190
+ }
3191
+ }
3192
+ function encodeRfc3986(value) {
3193
+ return encodeURIComponent(value).replace(
3194
+ /[!'()*]/g,
3195
+ (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
3196
+ );
3197
+ }
3198
+ function isRecord2(value) {
3199
+ return !!value && typeof value === "object" && !Array.isArray(value);
3200
+ }
3201
+
3202
+ // src/im/messaging.ts
3203
+ var MAX_REFRESH_PAGES = 1e3;
3204
+ var AwikiMessagingRuntime = class {
3205
+ constructor(options) {
3206
+ this.options = options;
3207
+ }
3208
+ sendTail = Promise.resolve();
3209
+ /** Refresh conversation records and return one local page. */
3210
+ async listConversations(request = {}) {
3211
+ this.options.identity.requireSecrets();
3212
+ const limit = pageLimit(request.limit);
3213
+ await this.refreshConversations();
3214
+ const conversations = Object.values(this.options.store.snapshot().conversations).map((record) => record.conversation).sort(compareConversationRecency);
3215
+ const offset = decodeOffsetCursor(request.cursor);
3216
+ const items = conversations.slice(offset, offset + limit);
3217
+ const nextOffset = offset + items.length;
3218
+ return {
3219
+ items,
3220
+ hasMore: nextOffset < conversations.length,
3221
+ ...nextOffset < conversations.length ? { nextCursor: encodeOffsetCursor(nextOffset) } : {}
3222
+ };
3223
+ }
3224
+ /** Read and normalize one direct/group history page using the service's offset support. */
3225
+ async getHistory(request) {
3226
+ const record = this.options.store.snapshot().conversations[conversationKey(request.conversationId)];
3227
+ if (!record) {
3228
+ throw new AwikiImError("not-found", "AWiki conversation was not found");
3229
+ }
3230
+ const identity = this.options.identity.requireSecrets();
3231
+ const limit = pageLimit(request.limit);
3232
+ const kind = record.conversation.kind;
3233
+ const skip = decodeHistoryCursor(request.cursor, kind, request.conversationId);
3234
+ const result = kind === "direct" ? await this.authenticatedRpc("direct.get_history", {
3235
+ meta: localMeta(identity.public.did, "anp.direct.local.v1"),
3236
+ body: compactRecord({
3237
+ user_did: identity.public.did,
3238
+ peer_did: requiredConversationValue(record.peerDid),
3239
+ limit,
3240
+ skip: skip || void 0
3241
+ })
3242
+ }) : await this.authenticatedRpc("group.list_messages", {
3243
+ meta: groupLocalMeta(
3244
+ identity.public.did,
3245
+ requiredConversationValue(record.groupDid)
3246
+ ),
3247
+ body: compactRecord({
3248
+ group_did: requiredConversationValue(record.groupDid),
3249
+ limit,
3250
+ skip: skip || void 0
3251
+ })
3252
+ });
3253
+ const wires = arrayValue(result.messages);
3254
+ validateHistoryWires(wires, record, identity.public.did);
3255
+ const mapped = wires.map(
3256
+ (wire) => isRecord3(wire) ? this.mapWireMessage(wire, record.conversation, identity.public.did) : null
3257
+ ).filter((message) => message !== null);
3258
+ await this.persistMappedMessages(mapped);
3259
+ const items = mapped.map((entry) => entry.message).sort(compareMessageTime);
3260
+ const consumed = wires.length;
3261
+ if (result.has_more === true && consumed === 0) {
3262
+ throw new AwikiImError("remote", "AWiki history pagination did not advance");
3263
+ }
3264
+ const hasMore = result.has_more === true;
3265
+ return {
3266
+ items,
3267
+ hasMore,
3268
+ ...hasMore ? { nextCursor: encodeHistoryCursor(kind, request.conversationId, skip + consumed) } : {}
3269
+ };
3270
+ }
3271
+ /** Resolve and send one idempotent text message. */
3272
+ async sendText(request) {
3273
+ return this.exclusiveSend(async () => {
3274
+ const text = request.text.trim();
3275
+ if (!text) {
3276
+ throw new AwikiImError("invalid-request", "AWiki message text is required");
3277
+ }
3278
+ const key = sendOperationKey(request.idempotencyKey);
3279
+ const fingerprint = sendFingerprint({ kind: "text", target: request.target, text });
3280
+ const existing = this.options.store.snapshot().sendOperations[key];
3281
+ if (existing) {
3282
+ assertOperationFingerprint(existing.kind, existing.fingerprint, "text", fingerprint);
3283
+ if (existing.kind !== "text") {
3284
+ throw new AwikiImError("conflict", "AWiki idempotency key is already in use");
3285
+ }
3286
+ if (existing.stage === "completed" && existing.result) {
3287
+ return structuredClone(existing.result);
3288
+ }
3289
+ return this.resumeTextSend(key, existing);
3290
+ }
3291
+ const target = await this.resolveTarget(request.target);
3292
+ const stable = stableIdentifiers(request.idempotencyKey);
3293
+ const operation = {
3294
+ kind: "text",
3295
+ fingerprint,
3296
+ target,
3297
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3298
+ operationId: stable.operationId,
3299
+ messageId: stable.messageId,
3300
+ text,
3301
+ stage: "prepared"
3302
+ };
3303
+ await this.options.store.mutate((state) => {
3304
+ if (state.sendOperations[key]) {
3305
+ throw new AwikiImError("conflict", "AWiki idempotency key is already in use");
3306
+ }
3307
+ state.sendOperations[key] = operation;
3308
+ });
3309
+ return this.resumeTextSend(key, operation);
3310
+ });
3311
+ }
3312
+ /** Serialize persistent send state machines across text and attachment operations. */
3313
+ async exclusiveSend(operation) {
3314
+ let release = () => void 0;
3315
+ const previous = this.sendTail;
3316
+ this.sendTail = new Promise((resolve) => {
3317
+ release = resolve;
3318
+ });
3319
+ await previous;
3320
+ try {
3321
+ return await operation();
3322
+ } finally {
3323
+ release();
3324
+ }
3325
+ }
3326
+ /** Resolve a direct Handle/DID or an existing group conversation. */
3327
+ async resolveTarget(target) {
3328
+ if (target.kind === "direct") {
3329
+ const peer = target.peer.trim();
3330
+ if (!peer) {
3331
+ throw new AwikiImError("invalid-request", "AWiki direct target is required");
3332
+ }
3333
+ const resolved = peer.startsWith("did:") ? { did: peer } : await this.resolveHandle(peer.replace(/^wba:\/\//, ""));
3334
+ const conversationId = directConversationId(resolved.did);
3335
+ await this.upsertConversation({
3336
+ conversation: {
3337
+ kind: "direct",
3338
+ id: conversationId,
3339
+ peerDid: resolved.did,
3340
+ ...resolved.handle ? { peerHandle: resolved.handle } : {},
3341
+ title: resolved.handle ?? resolved.did
3342
+ },
3343
+ peerDid: resolved.did
3344
+ });
3345
+ return {
3346
+ kind: "direct",
3347
+ did: resolved.did,
3348
+ ...resolved.handle ? { handle: resolved.handle } : {},
3349
+ conversationId
3350
+ };
3351
+ }
3352
+ const group = target.group.trim();
3353
+ if (!group) {
3354
+ throw new AwikiImError("invalid-request", "AWiki group target is required");
3355
+ }
3356
+ let record = this.groupConversation(group);
3357
+ if (!record) {
3358
+ await this.refreshGroups();
3359
+ record = this.groupConversation(group);
3360
+ }
3361
+ if (!record?.groupDid) {
3362
+ throw new AwikiImError("not-found", "AWiki group conversation was not found");
3363
+ }
3364
+ return {
3365
+ kind: "group",
3366
+ did: record.groupDid,
3367
+ conversationId: record.conversation.id
3368
+ };
3369
+ }
3370
+ /** Submit one already-encoded Direct/Group message and persist its projection. */
3371
+ async sendPayload(target, contentType, body, wire, content, attachmentReference) {
3372
+ const identity = this.options.identity.requireSecrets();
3373
+ const method = target.kind === "direct" ? "direct.send" : "group.send";
3374
+ const meta = {
3375
+ profile: target.kind === "direct" ? "anp.direct.base.v1" : "anp.group.base.v1",
3376
+ security_profile: "transport-protected",
3377
+ sender_did: identity.public.did,
3378
+ target: { kind: target.kind === "direct" ? "agent" : "group", did: target.did },
3379
+ operation_id: wire.operationId,
3380
+ message_id: wire.messageId,
3381
+ created_at: wire.createdAt,
3382
+ content_type: contentType
3383
+ };
3384
+ const auth = buildOriginAuthentication({
3385
+ method,
3386
+ meta,
3387
+ body,
3388
+ signingPrivateKeyPem: identity.signingPrivateKeyPem,
3389
+ signingKeyId: identity.signingKeyId
3390
+ });
3391
+ const result = await this.authenticatedRpc(method, { meta, auth, body });
3392
+ validateSendResult(result, wire, target);
3393
+ const messageId = wire.messageId;
3394
+ const sentAt = timestampValue(result.accepted_at) ?? Date.now();
3395
+ const message = {
3396
+ id: messageId,
3397
+ conversationId: target.conversationId,
3398
+ conversationKind: target.kind,
3399
+ senderDid: identity.public.did,
3400
+ senderHandle: identity.public.handle,
3401
+ sentAt,
3402
+ outgoing: true,
3403
+ content
3404
+ };
3405
+ const conversation = this.options.store.snapshot().conversations[conversationKey(target.conversationId)];
3406
+ await this.options.store.mutate((state) => {
3407
+ if (conversation) {
3408
+ state.conversations[conversationKey(target.conversationId)] = {
3409
+ ...conversation,
3410
+ conversation: { ...conversation.conversation, lastMessageAt: sentAt }
3411
+ };
3412
+ }
3413
+ if (attachmentReference) {
3414
+ const persistedReference = {
3415
+ ...attachmentReference,
3416
+ messageId
3417
+ };
3418
+ state.attachments[attachmentReferenceKey(persistedReference)] = persistedReference;
3419
+ }
3420
+ });
3421
+ return message;
3422
+ }
3423
+ async resumeTextSend(key, operation) {
3424
+ const message = await this.sendPayload(
3425
+ operation.target,
3426
+ "text/plain",
3427
+ { text: operation.text },
3428
+ {
3429
+ operationId: operation.operationId,
3430
+ messageId: operation.messageId,
3431
+ createdAt: operation.createdAt
3432
+ },
3433
+ { kind: "text", text: operation.text }
3434
+ );
3435
+ await this.options.store.mutate((state) => {
3436
+ const current = state.sendOperations[key];
3437
+ if (!current || current.kind !== "text" || current.fingerprint !== operation.fingerprint) {
3438
+ throw new AwikiImError("conflict", "AWiki send operation state changed");
3439
+ }
3440
+ state.sendOperations[key] = { ...current, stage: "completed", result: message };
3441
+ });
3442
+ return message;
3443
+ }
3444
+ /** Execute a Message Service RPC, refreshing the bearer once on 401/403. */
3445
+ async authenticatedRpc(method, params) {
3446
+ let identity = this.options.identity.requireSecrets();
3447
+ try {
3448
+ const result = await this.options.transport.rpc(
3449
+ this.options.messageServiceUrl,
3450
+ MESSAGE_RPC_PATH,
3451
+ method,
3452
+ params,
3453
+ identity.accessToken
3454
+ );
3455
+ await this.persistReturnedToken(result.accessToken);
3456
+ return result.value;
3457
+ } catch (error) {
3458
+ const normalized = normalizeAwikiImError(error);
3459
+ if (normalized.code !== "forbidden") {
3460
+ throw normalized;
3461
+ }
3462
+ const accessToken = await this.options.identity.refreshAccessToken();
3463
+ identity = this.options.identity.requireSecrets();
3464
+ const result = await this.options.transport.rpc(
3465
+ this.options.messageServiceUrl,
3466
+ MESSAGE_RPC_PATH,
3467
+ method,
3468
+ params,
3469
+ accessToken || identity.accessToken
3470
+ );
3471
+ await this.persistReturnedToken(result.accessToken);
3472
+ return result.value;
3473
+ }
3474
+ }
3475
+ async refreshConversations() {
3476
+ await this.refreshGroups();
3477
+ const identity = this.options.identity.requireSecrets();
3478
+ let skip = 0;
3479
+ for (let page = 0; page < MAX_REFRESH_PAGES; page += 1) {
3480
+ const result = await this.authenticatedRpc("inbox.get", {
3481
+ meta: localMeta(identity.public.did, "anp.inbox.local.v1"),
3482
+ body: compactRecord({
3483
+ user_did: identity.public.did,
3484
+ limit: MAX_PAGE_LIMIT,
3485
+ skip: skip || void 0
3486
+ })
3487
+ });
3488
+ const wires = arrayValue(result.messages);
3489
+ const mapped = wires.map(
3490
+ (wire) => isRecord3(wire) ? this.mapWireMessage(wire, void 0, identity.public.did) : null
3491
+ ).filter((message) => message !== null);
3492
+ await this.persistMappedMessages(mapped);
3493
+ if (result.has_more !== true) {
3494
+ return;
3495
+ }
3496
+ if (wires.length === 0) {
3497
+ throw new AwikiImError("remote", "AWiki inbox pagination did not advance");
3498
+ }
3499
+ skip += wires.length;
3500
+ }
3501
+ throw new AwikiImError("remote", "AWiki inbox pagination exceeded the safety limit");
3502
+ }
3503
+ async refreshGroups() {
3504
+ const identity = this.options.identity.requireSecrets();
3505
+ let cursor;
3506
+ for (let page = 0; page < MAX_REFRESH_PAGES; page += 1) {
3507
+ const result = await this.authenticatedRpc("group.list", {
3508
+ meta: groupLocalMeta(identity.public.did),
3509
+ body: compactRecord({ limit: MAX_PAGE_LIMIT, cursor })
3510
+ });
3511
+ const groups = arrayValue(result.groups).map(groupConversationFromWire).filter((group) => group !== null);
3512
+ if (groups.length > 0) {
3513
+ await this.options.store.mutate((state) => {
3514
+ for (const group of groups) {
3515
+ state.conversations[conversationKey(group.conversation.id)] = mergeConversation(
3516
+ state.conversations[conversationKey(group.conversation.id)],
3517
+ group
3518
+ );
3519
+ }
3520
+ });
3521
+ }
3522
+ const nextCursor = cursorValue(result.next_cursor);
3523
+ if (result.has_more === true && !nextCursor) {
3524
+ throw new AwikiImError("remote", "AWiki group pagination omitted its cursor");
3525
+ }
3526
+ if (!nextCursor) {
3527
+ return;
3528
+ }
3529
+ if (nextCursor === cursor) {
3530
+ throw new AwikiImError("remote", "AWiki group pagination did not advance");
3531
+ }
3532
+ cursor = nextCursor;
3533
+ }
3534
+ throw new AwikiImError("remote", "AWiki group pagination exceeded the safety limit");
3535
+ }
3536
+ async resolveHandle(peer) {
3537
+ const result = await this.options.transport.rpc(
3538
+ this.options.userServiceUrl,
3539
+ HANDLE_RPC_PATH,
3540
+ "lookup",
3541
+ { handle: peer }
3542
+ );
3543
+ const did = requiredWireString(result.value.did, "resolved DID");
3544
+ const handle = stringValue2(result.value.full_handle) ?? stringValue2(result.value.handle);
3545
+ return { did, ...handle ? { handle } : {} };
3546
+ }
3547
+ mapWireMessage(wire, fallbackConversation, ownerDid) {
3548
+ const messageId = stringValue2(wire.message_id) ?? stringValue2(wire.id) ?? stringValue2(wire.client_msg_id);
3549
+ const senderDid = stringValue2(wire.sender_did);
3550
+ if (!messageId || !senderDid) {
3551
+ return null;
3552
+ }
3553
+ const receiverDid = stringValue2(wire.receiver_did);
3554
+ const groupDid = stringValue2(wire.group_did) ?? (fallbackConversation?.kind === "group" ? fallbackConversation.groupDid : void 0);
3555
+ const kind = groupDid ? "group" : "direct";
3556
+ const peerDid = kind === "direct" ? senderDid !== ownerDid ? senderDid : receiverDid : void 0;
3557
+ if (kind === "direct" && !peerDid) {
3558
+ return null;
3559
+ }
3560
+ const conversationId = fallbackConversation?.id ?? (kind === "group" ? groupConversationId(groupDid) : directConversationId(peerDid));
3561
+ const sentAt = timestampValue(wire.sent_at) ?? timestampValue(wire.accepted_at) ?? timestampValue(wire.created_at) ?? 0;
3562
+ const contentType = stringValue2(wire.content_type) ?? "text/plain";
3563
+ const attachment = parseAttachmentMessage(
3564
+ wire.content,
3565
+ contentType,
3566
+ this.options.attachmentMaxBytes
3567
+ );
3568
+ const content = attachment ? {
3569
+ kind: "attachment",
3570
+ attachment: attachment.attachment,
3571
+ ...attachment.caption ? { caption: attachment.caption } : {}
3572
+ } : { kind: "text", text: textContent(wire) };
3573
+ const peerHandle = stringValue2(wire.peer_full_handle) ?? (senderDid !== ownerDid ? stringValue2(wire.sender_handle) : void 0);
3574
+ const conversation = kind === "group" ? {
3575
+ kind: "group",
3576
+ id: conversationId,
3577
+ groupDid,
3578
+ title: stringValue2(wire.group_name) ?? (fallbackConversation?.kind === "group" ? fallbackConversation.title : groupDid),
3579
+ ...sentAt ? { lastMessageAt: sentAt } : {}
3580
+ } : {
3581
+ kind: "direct",
3582
+ id: conversationId,
3583
+ peerDid,
3584
+ ...peerHandle ? { peerHandle } : {},
3585
+ title: peerHandle ?? (fallbackConversation?.kind === "direct" ? fallbackConversation.title : peerDid),
3586
+ ...sentAt ? { lastMessageAt: sentAt } : {}
3587
+ };
3588
+ const message = {
3589
+ id: messageId,
3590
+ conversationId,
3591
+ conversationKind: kind,
3592
+ senderDid,
3593
+ ...stringValue2(wire.sender_handle) ? { senderHandle: stringValue2(wire.sender_handle) } : {},
3594
+ sentAt,
3595
+ outgoing: senderDid === ownerDid,
3596
+ content
3597
+ };
3598
+ const attachmentReference = attachment ? {
3599
+ attachment: attachment.attachment,
3600
+ objectUri: attachment.objectUri,
3601
+ senderDid,
3602
+ messageId,
3603
+ ...kind === "group" ? { groupDid } : { messageTargetDid: receiverDid ?? peerDid },
3604
+ messageServiceDid: this.options.identity.requireSecrets().messageServiceDid
3605
+ } : void 0;
3606
+ return {
3607
+ message,
3608
+ conversation: {
3609
+ conversation,
3610
+ ...peerDid ? { peerDid } : {},
3611
+ ...groupDid ? { groupDid } : {}
3612
+ },
3613
+ attachmentReference
3614
+ };
3615
+ }
3616
+ async persistMappedMessages(mapped) {
3617
+ if (mapped.length === 0) {
3618
+ return;
3619
+ }
3620
+ await this.options.store.mutate((state) => {
3621
+ for (const entry of mapped) {
3622
+ const key = conversationKey(entry.conversation.conversation.id);
3623
+ state.conversations[key] = mergeConversation(state.conversations[key], entry.conversation);
3624
+ if (entry.attachmentReference) {
3625
+ state.attachments[attachmentReferenceKey(entry.attachmentReference)] = entry.attachmentReference;
3626
+ }
3627
+ }
3628
+ });
3629
+ }
3630
+ async upsertConversation(record) {
3631
+ await this.options.store.mutate((state) => {
3632
+ const key = conversationKey(record.conversation.id);
3633
+ state.conversations[key] = mergeConversation(state.conversations[key], record);
3634
+ });
3635
+ }
3636
+ groupConversation(value) {
3637
+ return Object.values(this.options.store.snapshot().conversations).find(
3638
+ (record) => record.conversation.kind === "group" && (record.conversation.id === value || record.groupDid === value)
3639
+ );
3640
+ }
3641
+ async persistReturnedToken(token) {
3642
+ if (!token || token === this.options.store.snapshot().identity?.accessToken) {
3643
+ return;
3644
+ }
3645
+ await this.options.store.mutate((state) => {
3646
+ if (state.identity) {
3647
+ state.identity = { ...state.identity, accessToken: token };
3648
+ }
3649
+ });
3650
+ }
3651
+ };
3652
+ function validateHistoryWires(wires, conversation, ownerDid) {
3653
+ for (const wire of wires) {
3654
+ if (!isRecord3(wire)) {
3655
+ throw new AwikiImError("remote", "AWiki history response contains an invalid message");
3656
+ }
3657
+ if (conversation.conversation.kind === "group") {
3658
+ if (stringValue2(wire.group_did) !== conversation.groupDid) {
3659
+ throw new AwikiImError("remote", "AWiki history message does not belong to the group");
3660
+ }
3661
+ continue;
3662
+ }
3663
+ const senderDid = stringValue2(wire.sender_did);
3664
+ const receiverDid = stringValue2(wire.receiver_did);
3665
+ const peerDid = conversation.peerDid;
3666
+ if (!peerDid || !senderDid || !receiverDid || !(senderDid === ownerDid && receiverDid === peerDid || senderDid === peerDid && receiverDid === ownerDid)) {
3667
+ throw new AwikiImError("remote", "AWiki history message does not belong to the direct peer");
3668
+ }
3669
+ }
3670
+ }
3671
+ function validateSendResult(result, wire, target) {
3672
+ if (result.accepted !== true || result.operation_id !== wire.operationId || result.message_id !== wire.messageId || (target.kind === "direct" ? result.target_did !== target.did : result.group_did !== target.did)) {
3673
+ throw new AwikiImError("remote", "AWiki send acknowledgement is invalid");
3674
+ }
3675
+ }
3676
+ function localMeta(senderDid, profile) {
3677
+ return {
3678
+ profile,
3679
+ security_profile: "transport-protected",
3680
+ sender_did: senderDid,
3681
+ operation_id: operationId("op"),
3682
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
3683
+ };
3684
+ }
3685
+ function groupLocalMeta(senderDid, groupDid) {
3686
+ return {
3687
+ profile: "anp.group.local.v1",
3688
+ security_profile: "transport-protected",
3689
+ sender_did: senderDid,
3690
+ ...groupDid ? { target: { kind: "group", did: groupDid } } : {}
3691
+ };
3692
+ }
3693
+ function groupConversationFromWire(value) {
3694
+ if (!isRecord3(value)) {
3695
+ return null;
3696
+ }
3697
+ const groupDid = stringValue2(value.group_did) ?? stringValue2(value.did) ?? stringValue2(value.id);
3698
+ if (!groupDid) {
3699
+ return null;
3700
+ }
3701
+ const profile = isRecord3(value.profile) ? value.profile : void 0;
3702
+ const title = stringValue2(value.title) ?? stringValue2(value.name) ?? stringValue2(value.display_name) ?? stringValue2(profile?.display_name) ?? groupDid;
3703
+ const lastMessageAt = timestampValue(value.last_message_at);
3704
+ return {
3705
+ conversation: {
3706
+ kind: "group",
3707
+ id: groupConversationId(groupDid),
3708
+ groupDid,
3709
+ title,
3710
+ ...lastMessageAt ? { lastMessageAt } : {}
3711
+ },
3712
+ groupDid
3713
+ };
3714
+ }
3715
+ function parseAttachmentMessage(rawContent, contentType, maximumBytes) {
3716
+ if (contentType !== "application/anp-attachment-manifest+json") {
3717
+ return null;
3718
+ }
3719
+ const decoded = typeof rawContent === "string" ? parseJsonRecord(rawContent) : isRecord3(rawContent) ? rawContent : void 0;
3720
+ if (!decoded) {
3721
+ throw new AwikiImError("remote", "AWiki attachment manifest is invalid");
3722
+ }
3723
+ const attachments = arrayValue(decoded.attachments);
3724
+ if (attachments.length !== 1 || !isRecord3(attachments[0])) {
3725
+ throw new AwikiImError("remote", "AWiki attachment manifest is invalid");
3726
+ }
3727
+ const selected = attachments[0];
3728
+ const digest = isRecord3(selected.digest) ? selected.digest : void 0;
3729
+ const access = isRecord3(selected.access_info) ? selected.access_info : void 0;
3730
+ const encryption = isRecord3(selected.encryption_info) ? selected.encryption_info : void 0;
3731
+ const id = stringValue2(selected.attachment_id);
3732
+ const mimeType = stringValue2(selected.mime_type);
3733
+ const size = integerValue(selected.size);
3734
+ const digestB64u = stringValue2(digest?.value_b64u);
3735
+ const objectUri = stringValue2(access?.object_uri);
3736
+ if (!id || !mimeType || size === void 0 || size > maximumBytes || !digestB64u || !objectUri || digest?.alg !== "sha-256" || encryption?.mode !== "none" || decoded.primary_attachment_id !== void 0 && decoded.primary_attachment_id !== id) {
3737
+ throw new AwikiImError("remote", "AWiki attachment manifest is invalid");
3738
+ }
3739
+ return {
3740
+ attachment: {
3741
+ id,
3742
+ fileName: stringValue2(selected.filename) ?? id,
3743
+ mimeType,
3744
+ size,
3745
+ sha256: Buffer.from(digestB64u, "base64url").toString("hex")
3746
+ },
3747
+ objectUri,
3748
+ ...stringValue2(decoded.caption) ? { caption: stringValue2(decoded.caption) } : {}
3749
+ };
3750
+ }
3751
+ function textContent(wire) {
3752
+ const content = wire.content;
3753
+ if (typeof content === "string") {
3754
+ return content;
3755
+ }
3756
+ if (isRecord3(content) && typeof content.text === "string") {
3757
+ return content.text;
3758
+ }
3759
+ if (isRecord3(wire.body) && typeof wire.body.text === "string") {
3760
+ return wire.body.text;
3761
+ }
3762
+ return "";
3763
+ }
3764
+ function stableIdentifiers(idempotencyKey) {
3765
+ const key = idempotencyKey.trim();
3766
+ if (!key || key.length > 256) {
3767
+ throw new AwikiImError("invalid-request", "AWiki idempotency key is invalid");
3768
+ }
3769
+ const digest = createHash("sha256").update(key).digest("hex").slice(0, 32);
3770
+ return { operationId: `op-${digest}`, messageId: `msg-${digest}` };
3771
+ }
3772
+ function sendOperationKey(idempotencyKey) {
3773
+ const key = idempotencyKey.trim();
3774
+ if (!key || key.length > 256) {
3775
+ throw new AwikiImError("invalid-request", "AWiki idempotency key is invalid");
3776
+ }
3777
+ return createHash("sha256").update(`send-operation:${key}`).digest("hex");
3778
+ }
3779
+ function sendFingerprint(value) {
3780
+ return createHash("sha256").update(canonicalizeJson(value)).digest("hex");
3781
+ }
3782
+ function assertOperationFingerprint(actualKind, actualFingerprint, expectedKind, expectedFingerprint) {
3783
+ if (actualKind !== expectedKind || actualFingerprint !== expectedFingerprint) {
3784
+ throw new AwikiImError("conflict", "AWiki idempotency key is already in use");
3785
+ }
3786
+ }
3787
+ function attachmentReferenceKey(reference) {
3788
+ return [reference.senderDid, reference.messageId, reference.attachment.id].map((value) => Buffer.from(value).toString("base64url")).join(".");
3789
+ }
3790
+ function directConversationId(peerDid) {
3791
+ return `direct:${Buffer.from(peerDid).toString("base64url")}`;
3792
+ }
3793
+ function groupConversationId(groupDid) {
3794
+ return `group:${Buffer.from(groupDid).toString("base64url")}`;
3795
+ }
3796
+ function mergeConversation(current, next) {
3797
+ if (!current) {
3798
+ return next;
3799
+ }
3800
+ const currentTime = current.conversation.lastMessageAt ?? 0;
3801
+ const nextTime = next.conversation.lastMessageAt ?? 0;
3802
+ return {
3803
+ ...current,
3804
+ ...next,
3805
+ conversation: {
3806
+ ...current.conversation,
3807
+ ...next.conversation,
3808
+ ...Math.max(currentTime, nextTime) > 0 ? { lastMessageAt: Math.max(currentTime, nextTime) } : {}
3809
+ }
3810
+ };
3811
+ }
3812
+ function compareConversationRecency(left, right) {
3813
+ return (right.lastMessageAt ?? 0) - (left.lastMessageAt ?? 0) || left.title.localeCompare(right.title);
3814
+ }
3815
+ function compareMessageTime(left, right) {
3816
+ return left.sentAt - right.sentAt || left.id.localeCompare(right.id);
3817
+ }
3818
+ function pageLimit(value) {
3819
+ if (value === void 0) {
3820
+ return DEFAULT_PAGE_LIMIT;
3821
+ }
3822
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_PAGE_LIMIT) {
3823
+ throw new AwikiImError("invalid-request", "AWiki page limit is invalid");
3824
+ }
3825
+ return value;
3826
+ }
3827
+ function encodeOffsetCursor(offset) {
3828
+ return Buffer.from(JSON.stringify({ v: 1, offset })).toString("base64url");
3829
+ }
3830
+ function decodeOffsetCursor(cursor) {
3831
+ if (!cursor) {
3832
+ return 0;
3833
+ }
3834
+ try {
3835
+ const decoded = JSON.parse(
3836
+ Buffer.from(cursor, "base64url").toString("utf8")
3837
+ );
3838
+ if (isRecord3(decoded) && decoded.v === 1 && typeof decoded.offset === "number" && Number.isSafeInteger(decoded.offset) && decoded.offset >= 0) {
3839
+ return decoded.offset;
3840
+ }
3841
+ } catch {
3842
+ }
3843
+ throw new AwikiImError("invalid-request", "AWiki page cursor is invalid");
3844
+ }
3845
+ function encodeHistoryCursor(kind, conversationId, skip) {
3846
+ return Buffer.from(
3847
+ JSON.stringify({ v: 1, kind, conversationId, skip }),
3848
+ "utf8"
3849
+ ).toString("base64url");
3850
+ }
3851
+ function decodeHistoryCursor(cursor, expectedKind, expectedConversationId) {
3852
+ if (!cursor) {
3853
+ return 0;
3854
+ }
3855
+ try {
3856
+ const decoded = JSON.parse(
3857
+ Buffer.from(cursor, "base64url").toString("utf8")
3858
+ );
3859
+ if (isRecord3(decoded) && decoded.v === 1 && decoded.kind === expectedKind && decoded.conversationId === expectedConversationId && typeof decoded.skip === "number" && Number.isSafeInteger(decoded.skip) && decoded.skip >= 0) {
3860
+ return decoded.skip;
3861
+ }
3862
+ } catch {
3863
+ }
3864
+ throw new AwikiImError("invalid-request", "AWiki history cursor is invalid");
3865
+ }
3866
+ function compactRecord(record) {
3867
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== void 0));
3868
+ }
3869
+ function requiredConversationValue(value) {
3870
+ if (!value) {
3871
+ throw new AwikiImError("not-found", "AWiki conversation was not found");
3872
+ }
3873
+ return value;
3874
+ }
3875
+ function requiredWireString(value, label) {
3876
+ const result = stringValue2(value);
3877
+ if (!result) {
3878
+ throw new AwikiImError("remote", `AWiki response is missing ${label}`);
3879
+ }
3880
+ return result;
3881
+ }
3882
+ function cursorValue(value) {
3883
+ if (typeof value === "number" && Number.isFinite(value)) {
3884
+ return String(value);
3885
+ }
3886
+ return stringValue2(value);
3887
+ }
3888
+ function timestampValue(value) {
3889
+ if (typeof value === "number" && Number.isFinite(value)) {
3890
+ return value > 1e10 ? value : value * 1e3;
3891
+ }
3892
+ if (typeof value === "string" && value.trim()) {
3893
+ const numeric = Number(value);
3894
+ if (Number.isFinite(numeric)) {
3895
+ return numeric > 1e10 ? numeric : numeric * 1e3;
3896
+ }
3897
+ const parsed = Date.parse(value);
3898
+ return Number.isNaN(parsed) ? void 0 : parsed;
3899
+ }
3900
+ return void 0;
3901
+ }
3902
+ function integerValue(value) {
3903
+ const numeric = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
3904
+ return Number.isSafeInteger(numeric) && numeric >= 0 ? numeric : void 0;
3905
+ }
3906
+ function stringValue2(value) {
3907
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
3908
+ }
3909
+ function arrayValue(value) {
3910
+ return Array.isArray(value) ? value : [];
3911
+ }
3912
+ function parseJsonRecord(value) {
3913
+ try {
3914
+ const decoded = JSON.parse(value);
3915
+ return isRecord3(decoded) ? decoded : void 0;
3916
+ } catch {
3917
+ return void 0;
3918
+ }
3919
+ }
3920
+ function isRecord3(value) {
3921
+ return !!value && typeof value === "object" && !Array.isArray(value);
3922
+ }
3923
+
3924
+ // src/im/attachments.ts
3925
+ var ATTACHMENT_PROFILE = "anp.attachment.v1";
3926
+ var ATTACHMENT_CONTENT_TYPE = "application/anp-attachment-manifest+json";
3927
+ var AwikiAttachmentRuntime = class {
3928
+ constructor(options) {
3929
+ this.options = options;
3930
+ }
3931
+ /** Upload, commit, and send one attachment manifest. */
3932
+ async sendAttachment(request) {
3933
+ return this.options.messaging.exclusiveSend(async () => {
3934
+ validateUpload(request.attachment, this.options.attachmentMaxBytes);
3935
+ const prepared = prepareAttachment(request.attachment.bytes);
3936
+ const caption = request.caption?.trim() || void 0;
3937
+ const fingerprint = sendFingerprint({
3938
+ kind: "attachment",
3939
+ target: request.target,
3940
+ fileName: request.attachment.fileName,
3941
+ mimeType: request.attachment.mimeType,
3942
+ size: request.attachment.bytes.byteLength,
3943
+ digest: prepared.digestHex,
3944
+ caption
3945
+ });
3946
+ const key = sendOperationKey(request.idempotencyKey);
3947
+ const existing = this.options.store.snapshot().sendOperations[key];
3948
+ if (existing) {
3949
+ assertOperationFingerprint(existing.kind, existing.fingerprint, "attachment", fingerprint);
3950
+ if (existing.kind !== "attachment") {
3951
+ throw new AwikiImError("conflict", "AWiki idempotency key is already in use");
3952
+ }
3953
+ if (existing.stage === "completed" && existing.result) {
3954
+ return structuredClone(existing.result);
3955
+ }
3956
+ return this.resumeAttachmentSend(key, existing, request.attachment.bytes);
3957
+ }
3958
+ const target = await this.options.messaging.resolveTarget(request.target);
3959
+ const identifiers = attachmentIdentifiers(request.idempotencyKey);
3960
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3961
+ const operation = {
3962
+ kind: "attachment",
3963
+ fingerprint,
3964
+ target,
3965
+ attachment: {
3966
+ id: identifiers.attachmentId,
3967
+ fileName: request.attachment.fileName,
3968
+ mimeType: request.attachment.mimeType,
3969
+ size: request.attachment.bytes.byteLength,
3970
+ sha256: prepared.digestHex
3971
+ },
3972
+ digestB64u: prepared.digestB64u,
3973
+ ...caption ? { caption } : {},
3974
+ createOperationId: identifiers.createOperationId,
3975
+ commitOperationId: identifiers.commitOperationId,
3976
+ messageOperationId: identifiers.messageOperationId,
3977
+ messageId: identifiers.messageId,
3978
+ createCreatedAt: now,
3979
+ commitCreatedAt: now,
3980
+ messageCreatedAt: now,
3981
+ stage: "prepared"
3982
+ };
3983
+ await this.options.store.mutate((state) => {
3984
+ if (state.sendOperations[key]) {
3985
+ throw new AwikiImError("conflict", "AWiki idempotency key is already in use");
3986
+ }
3987
+ state.sendOperations[key] = operation;
3988
+ });
3989
+ return this.resumeAttachmentSend(key, operation, request.attachment.bytes);
3990
+ });
3991
+ }
3992
+ async resumeAttachmentSend(key, initial, bytes) {
3993
+ const identity = this.options.identity.requireSecrets();
3994
+ let operation = initial;
3995
+ if (operation.stage === "prepared") {
3996
+ const slotResult = await this.options.messaging.authenticatedRpc("attachment.create_slot", {
3997
+ meta: attachmentMeta(
3998
+ identity.public.did,
3999
+ identity.messageServiceDid,
4000
+ operation.createOperationId,
4001
+ operation.createCreatedAt
4002
+ ),
4003
+ body: {
4004
+ attachment_id: operation.attachment.id,
4005
+ expected_size: String(operation.attachment.size),
4006
+ expected_digest: { alg: "sha-256", value_b64u: operation.digestB64u },
4007
+ mime_type: operation.attachment.mimeType,
4008
+ filename: operation.attachment.fileName,
4009
+ intended_message_security_profile: "transport-protected",
4010
+ intended_target: {
4011
+ kind: operation.target.kind === "direct" ? "agent" : "group",
4012
+ did: operation.target.did
4013
+ },
4014
+ object_encryption_mode: "none"
4015
+ }
4016
+ });
4017
+ const slot2 = parseSlot(slotResult, operation.attachment.id);
4018
+ this.options.transport.validateAttachmentUrl(slot2.uploadUri);
4019
+ this.options.transport.validateAttachmentUrl(slot2.objectUri);
4020
+ operation = await this.advanceOperation(key, operation, { stage: "slot-created", slot: slot2 });
4021
+ }
4022
+ const slot = requiredSlot(operation);
4023
+ if (operation.stage === "slot-created") {
4024
+ await this.options.transport.putBytes(slot.uploadUri, slot.uploadHeaders, bytes);
4025
+ operation = await this.advanceOperation(key, operation, { stage: "uploaded" });
4026
+ }
4027
+ if (operation.stage === "uploaded") {
4028
+ const commit = await this.options.messaging.authenticatedRpc("attachment.commit_object", {
4029
+ meta: attachmentMeta(
4030
+ identity.public.did,
4031
+ identity.messageServiceDid,
4032
+ operation.commitOperationId,
4033
+ operation.commitCreatedAt
4034
+ ),
4035
+ body: {
4036
+ attachment_id: operation.attachment.id,
4037
+ slot_id: slot.slotId,
4038
+ commit_token: slot.commitToken,
4039
+ size: String(operation.attachment.size),
4040
+ digest: { alg: "sha-256", value_b64u: operation.digestB64u },
4041
+ object_encryption_mode: "none"
4042
+ }
4043
+ });
4044
+ if (commit.committed !== true || commit.attachment_id !== operation.attachment.id || commit.object_uri !== slot.objectUri) {
4045
+ throw new AwikiImError("remote", "AWiki attachment commit acknowledgement is invalid");
4046
+ }
4047
+ operation = await this.advanceOperation(key, operation, { stage: "committed" });
4048
+ }
4049
+ if (operation.stage !== "committed") {
4050
+ if (operation.stage === "completed" && operation.result) return operation.result;
4051
+ throw new AwikiImError("remote", "AWiki attachment send state is invalid");
4052
+ }
4053
+ const manifest = {
4054
+ attachments: [
4055
+ {
4056
+ attachment_id: operation.attachment.id,
4057
+ filename: operation.attachment.fileName,
4058
+ mime_type: operation.attachment.mimeType,
4059
+ size: String(operation.attachment.size),
4060
+ digest: { alg: "sha-256", value_b64u: operation.digestB64u },
4061
+ access_info: { object_uri: slot.objectUri },
4062
+ encryption_info: { mode: "none" }
4063
+ }
4064
+ ],
4065
+ ...operation.caption ? { caption: operation.caption } : {},
4066
+ primary_attachment_id: operation.attachment.id
4067
+ };
4068
+ const reference = {
4069
+ attachment: operation.attachment,
4070
+ objectUri: slot.objectUri,
4071
+ senderDid: identity.public.did,
4072
+ ...operation.target.kind === "group" ? { groupDid: operation.target.did } : { messageTargetDid: operation.target.did },
4073
+ messageServiceDid: identity.messageServiceDid
4074
+ };
4075
+ const message = await this.options.messaging.sendPayload(
4076
+ operation.target,
4077
+ ATTACHMENT_CONTENT_TYPE,
4078
+ { payload: manifest },
4079
+ {
4080
+ operationId: operation.messageOperationId,
4081
+ messageId: operation.messageId,
4082
+ createdAt: operation.messageCreatedAt
4083
+ },
4084
+ {
4085
+ kind: "attachment",
4086
+ attachment: operation.attachment,
4087
+ ...operation.caption ? { caption: operation.caption } : {}
4088
+ },
4089
+ reference
4090
+ );
4091
+ await this.advanceOperation(key, operation, { stage: "completed", result: message });
4092
+ return message;
4093
+ }
4094
+ async advanceOperation(key, expected, patch) {
4095
+ let updated;
4096
+ await this.options.store.mutate((state) => {
4097
+ const current = state.sendOperations[key];
4098
+ if (!current || current.kind !== "attachment" || current.fingerprint !== expected.fingerprint || current.stage !== expected.stage) {
4099
+ throw new AwikiImError("conflict", "AWiki attachment send operation state changed");
4100
+ }
4101
+ updated = { ...current, ...patch };
4102
+ state.sendOperations[key] = updated;
4103
+ });
4104
+ if (!updated) {
4105
+ throw new AwikiImError("remote", "AWiki attachment send state was not persisted");
4106
+ }
4107
+ return updated;
4108
+ }
4109
+ /** Issue a ticket, download the object, and verify exact size and SHA-256. */
4110
+ async downloadAttachment(request) {
4111
+ const identity = this.options.identity.requireSecrets();
4112
+ const references = Object.values(this.options.store.snapshot().attachments).filter(
4113
+ (candidate) => candidate.attachment.id === request.attachmentId && candidate.messageId === request.messageId
4114
+ );
4115
+ if (references.length === 0) {
4116
+ throw new AwikiImError("not-found", "AWiki attachment was not found");
4117
+ }
4118
+ if (references.length > 1) {
4119
+ throw new AwikiImError("conflict", "AWiki attachment message reference is ambiguous");
4120
+ }
4121
+ const reference = references[0];
4122
+ if (!reference) {
4123
+ throw new AwikiImError("not-found", "AWiki attachment was not found");
4124
+ }
4125
+ this.options.transport.validateAttachmentUrl(reference.objectUri);
4126
+ const operation = operationId("op");
4127
+ const messageServiceDid = await this.resolveAttachmentServiceDid(reference.senderDid);
4128
+ const ticket = await this.options.messaging.authenticatedRpc("attachment.get_download_ticket", {
4129
+ meta: attachmentMeta(
4130
+ identity.public.did,
4131
+ messageServiceDid,
4132
+ operation,
4133
+ (/* @__PURE__ */ new Date()).toISOString()
4134
+ ),
4135
+ body: {
4136
+ attachment_id: reference.attachment.id,
4137
+ object_uri: reference.objectUri,
4138
+ requester_did: identity.public.did,
4139
+ message_security_profile: "transport-protected",
4140
+ message_id: reference.messageId,
4141
+ one_time: true,
4142
+ ...reference.groupDid ? { group_did: reference.groupDid } : { message_target_did: requiredDirectTarget(reference) }
4143
+ }
4144
+ });
4145
+ validateTicketBinding(ticket.ticket_binding, reference, identity.public.did);
4146
+ const ticketValue = requiredString2(ticket.download_ticket_b64u, "download ticket");
4147
+ const bytes = await this.options.transport.getBytes(
4148
+ reference.objectUri,
4149
+ ticketValue,
4150
+ reference.attachment.size
4151
+ );
4152
+ const digestHex = createHash("sha256").update(bytes).digest("hex");
4153
+ if (bytes.byteLength !== reference.attachment.size || digestHex !== reference.attachment.sha256) {
4154
+ throw new AwikiImError("remote", "AWiki attachment verification failed");
4155
+ }
4156
+ return { attachment: reference.attachment, bytes };
4157
+ }
4158
+ async resolveAttachmentServiceDid(senderDid) {
4159
+ const identity = this.options.identity.requireSecrets();
4160
+ const document = senderDid === identity.public.did ? identity.didDocument : await this.options.transport.getJson(didResolutionUrl(senderDid));
4161
+ if (document.id !== senderDid) {
4162
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4163
+ }
4164
+ if (!isValidAttachmentDidDocument(document)) {
4165
+ throw new AwikiImError("remote", "AWiki attachment sender DID document is invalid");
4166
+ }
4167
+ const matching = arrayValue2(document.service).filter(isRecord4).filter(
4168
+ (service) => service.type === "ANPMessageService" && arrayValue2(service.profiles).includes(ATTACHMENT_PROFILE) && arrayValue2(service.securityProfiles ?? service.security_profiles).includes(
4169
+ "transport-protected"
4170
+ ) && typeof service.serviceDid === "string" && service.serviceDid.trim() && typeof service.serviceEndpoint === "string" && service.serviceEndpoint.trim()
4171
+ ).map((service, index) => ({
4172
+ service,
4173
+ index,
4174
+ priority: priorityValue(service.priority)
4175
+ })).sort((left, right) => {
4176
+ if (left.priority !== void 0 && right.priority !== void 0) {
4177
+ return left.priority - right.priority || left.index - right.index;
4178
+ }
4179
+ if (left.priority !== void 0) return -1;
4180
+ if (right.priority !== void 0) return 1;
4181
+ return left.index - right.index;
4182
+ });
4183
+ const selected = matching[0]?.service;
4184
+ if (!selected) {
4185
+ throw new AwikiImError("remote", "AWiki attachment service was not found");
4186
+ }
4187
+ this.options.transport.validateAttachmentUrl(
4188
+ requiredString2(selected.serviceEndpoint, "attachment service endpoint")
4189
+ );
4190
+ return requiredString2(selected.serviceDid, "attachment service DID");
4191
+ }
4192
+ };
4193
+ function requiredSlot(operation) {
4194
+ if (!operation.slot) {
4195
+ throw new AwikiImError("remote", "AWiki attachment slot state is missing");
4196
+ }
4197
+ return operation.slot;
4198
+ }
4199
+ function parseSlot(result, expectedAttachmentId) {
4200
+ const attachmentId = requiredString2(result.attachment_id, "attachment ID");
4201
+ if (attachmentId !== expectedAttachmentId) {
4202
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4203
+ }
4204
+ return {
4205
+ attachmentId,
4206
+ slotId: requiredString2(result.slot_id, "attachment slot ID"),
4207
+ uploadUri: requiredString2(result.upload_uri, "attachment upload URI"),
4208
+ uploadHeaders: stringRecord(result.upload_headers, "attachment upload headers"),
4209
+ objectUri: requiredString2(result.object_uri, "attachment object URI"),
4210
+ commitToken: requiredString2(result.commit_token, "attachment commit token")
4211
+ };
4212
+ }
4213
+ function attachmentMeta(senderDid, serviceDid, operation, createdAt) {
4214
+ return {
4215
+ profile: ATTACHMENT_PROFILE,
4216
+ security_profile: "transport-protected",
4217
+ sender_did: senderDid,
4218
+ target: { kind: "service", did: serviceDid },
4219
+ operation_id: operation,
4220
+ created_at: createdAt
4221
+ };
4222
+ }
4223
+ function attachmentIdentifiers(idempotencyKey) {
4224
+ const key = idempotencyKey.trim();
4225
+ if (!key || key.length > 256) {
4226
+ throw new AwikiImError("invalid-request", "AWiki idempotency key is invalid");
4227
+ }
4228
+ return {
4229
+ attachmentId: `att-${digestPrefix(`attachment:${key}`)}`,
4230
+ createOperationId: `op-${digestPrefix(`create:${key}`)}`,
4231
+ commitOperationId: `op-${digestPrefix(`commit:${key}`)}`,
4232
+ messageOperationId: `op-${digestPrefix(`message:${key}`)}`,
4233
+ messageId: `msg-${digestPrefix(`message:${key}`)}`
4234
+ };
4235
+ }
4236
+ function digestPrefix(value) {
4237
+ return createHash("sha256").update(value).digest("hex").slice(0, 32);
4238
+ }
4239
+ function prepareAttachment(bytes) {
4240
+ const digest = createHash("sha256").update(bytes).digest();
4241
+ return { digestHex: digest.toString("hex"), digestB64u: digest.toString("base64url") };
4242
+ }
4243
+ function validateUpload(upload, maximumBytes) {
4244
+ if (!upload.fileName.trim() || upload.fileName.includes("/") || upload.fileName.includes("\\")) {
4245
+ throw new AwikiImError("invalid-request", "AWiki attachment file name is invalid");
4246
+ }
4247
+ if (!/^[\w.+-]+\/[\w.+-]+$/.test(upload.mimeType.trim())) {
4248
+ throw new AwikiImError("invalid-request", "AWiki attachment MIME type is invalid");
4249
+ }
4250
+ if (!(upload.bytes instanceof Uint8Array)) {
4251
+ throw new AwikiImError("invalid-request", "AWiki attachment bytes are invalid");
4252
+ }
4253
+ if (upload.bytes.byteLength > maximumBytes) {
4254
+ throw new AwikiImError("invalid-request", "AWiki attachment exceeds the configured limit");
4255
+ }
4256
+ }
4257
+ function validateTicketBinding(value, reference, requesterDid) {
4258
+ if (!isRecord4(value)) {
4259
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4260
+ }
4261
+ const expected = {
4262
+ attachment_id: reference.attachment.id,
4263
+ object_uri: reference.objectUri,
4264
+ requester_did: requesterDid,
4265
+ message_id: reference.messageId,
4266
+ message_security_profile: "transport-protected"
4267
+ };
4268
+ if (Object.entries(expected).some(([key, expectedValue]) => value[key] !== expectedValue) || (reference.groupDid ? value.group_did !== reference.groupDid : value.message_target_did !== reference.messageTargetDid)) {
4269
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4270
+ }
4271
+ }
4272
+ function requiredDirectTarget(reference) {
4273
+ if (!reference.messageTargetDid) {
4274
+ throw new AwikiImError("not-found", "AWiki attachment message context was not found");
4275
+ }
4276
+ return reference.messageTargetDid;
4277
+ }
4278
+ function didResolutionUrl(did) {
4279
+ if (!did.startsWith("did:wba:")) {
4280
+ throw new AwikiImError("remote", "AWiki attachment sender DID is invalid");
4281
+ }
4282
+ const parts = did.split(":");
4283
+ const authority = parts[2];
4284
+ if (!authority) {
4285
+ throw new AwikiImError("remote", "AWiki attachment sender DID is invalid");
4286
+ }
4287
+ const host = decodeURIComponent(authority);
4288
+ const path = parts.slice(3).map((segment) => encodeURIComponent(segment)).join("/");
4289
+ return path ? `https://${host}/${path}/did.json` : `https://${host}/.well-known/did.json`;
4290
+ }
4291
+ function requiredString2(value, label) {
4292
+ if (typeof value !== "string" || !value.trim()) {
4293
+ throw new AwikiImError("remote", `AWiki response is missing ${label}`);
4294
+ }
4295
+ return value.trim();
4296
+ }
4297
+ function stringRecord(value, label) {
4298
+ if (!isRecord4(value)) {
4299
+ throw new AwikiImError("remote", `AWiki response is missing ${label}`);
4300
+ }
4301
+ const output = {};
4302
+ for (const [key, headerValue] of Object.entries(value)) {
4303
+ if (typeof headerValue !== "string" || !key.trim()) {
4304
+ throw new AwikiImError("remote", `AWiki response is missing ${label}`);
4305
+ }
4306
+ output[key] = headerValue;
4307
+ }
4308
+ return output;
4309
+ }
4310
+ function isRecord4(value) {
4311
+ return !!value && typeof value === "object" && !Array.isArray(value);
4312
+ }
4313
+ function arrayValue2(value) {
4314
+ return Array.isArray(value) ? value : [];
4315
+ }
4316
+ function priorityValue(value) {
4317
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
4318
+ return Number.isFinite(parsed) ? Math.trunc(parsed) : void 0;
4319
+ }
4320
+ function isValidAttachmentDidDocument(document) {
4321
+ try {
4322
+ return validateDidDocumentBinding(document, true);
4323
+ } catch {
4324
+ return false;
4325
+ }
4326
+ }
4327
+
4328
+ // src/im/identity.ts
4329
+ var AwikiIdentityRuntime = class {
4330
+ constructor(options) {
4331
+ this.options = options;
4332
+ }
4333
+ registrationTail = Promise.resolve();
4334
+ /** Return the public identity projection. */
4335
+ getIdentity() {
4336
+ return this.options.store.snapshot().identity?.public ?? null;
4337
+ }
4338
+ /** Reject persisted identity material that belongs to a different configured deployment. */
4339
+ validateConfiguredIdentity() {
4340
+ const snapshot = this.options.store.snapshot();
4341
+ const material = snapshot.identity ?? snapshot.pendingRegistration;
4342
+ if (material && material.messageServiceDid !== this.options.messageServiceDid) {
4343
+ throw new AwikiImError("conflict", "AWiki state belongs to a different Message Service");
4344
+ }
4345
+ const handle = snapshot.identity?.public.handle ?? snapshot.pendingRegistration?.handle;
4346
+ if (handle && !handle.endsWith(`.${this.options.userServiceDomain}`)) {
4347
+ throw new AwikiImError("conflict", "AWiki state belongs to a different User Service domain");
4348
+ }
4349
+ }
4350
+ /** Send the phone-only registration OTP supported by the frozen MVP API. */
4351
+ async sendRegistrationOtp(request) {
4352
+ if (this.getIdentity()) {
4353
+ throw new AwikiImError("already-registered", "AWiki identity is already registered");
4354
+ }
4355
+ const phone = normalizePhone(request.phone);
4356
+ const handle = normalizeRegistrationHandle(request.handle, this.options.userServiceDomain);
4357
+ const result = await this.options.transport.rpc(
4358
+ this.options.userServiceUrl,
4359
+ HANDLE_RPC_PATH,
4360
+ "send_otp",
4361
+ { phone }
4362
+ );
4363
+ const retryAfterSeconds = requiredWireNumber(
4364
+ result.value.retry_after_seconds,
4365
+ "retry_after_seconds"
4366
+ );
4367
+ const retryAt = requiredWireString2(result.value.retry_at, "retry_at");
4368
+ const registrationOtp = {
4369
+ handle: handle.full,
4370
+ phone,
4371
+ retryAt
4372
+ };
4373
+ await this.options.store.mutate((state) => {
4374
+ state.registrationOtp = registrationOtp;
4375
+ });
4376
+ return { retryAfterSeconds, retryAt };
4377
+ }
4378
+ /** Register and persist the one deployment identity. */
4379
+ async registerIdentity(request) {
4380
+ return this.exclusiveRegistration(async () => {
4381
+ if (this.getIdentity()) {
4382
+ throw new AwikiImError("already-registered", "AWiki identity is already registered");
4383
+ }
4384
+ const registrationOtp = this.options.store.snapshot().registrationOtp;
4385
+ const phone = normalizePhone(request.phone);
4386
+ const otp = normalizeOtp(request.otp);
4387
+ const handle = normalizeRegistrationHandle(request.handle, this.options.userServiceDomain);
4388
+ if (!registrationOtp || registrationOtp.phone !== phone || registrationOtp.handle !== handle.full) {
4389
+ throw new AwikiImError("invalid-request", "AWiki registration OTP target does not match");
4390
+ }
4391
+ let pending = this.options.store.snapshot().pendingRegistration;
4392
+ if (pending) {
4393
+ if (pending.handle !== handle.full || pending.phone !== phone) {
4394
+ throw new AwikiImError("conflict", "AWiki registration is already pending");
4395
+ }
4396
+ pending = refreshPendingProof(pending, handle.domain);
4397
+ } else {
4398
+ pending = createPendingRegistration(
4399
+ phone,
4400
+ handle,
4401
+ this.options.messageServicePublicUrl,
4402
+ this.options.messageServiceDid
4403
+ );
4404
+ }
4405
+ await this.options.store.mutate((state) => {
4406
+ state.pendingRegistration = pending;
4407
+ });
4408
+ try {
4409
+ const result = await this.options.transport.rpc(
4410
+ this.options.userServiceUrl,
4411
+ DID_AUTH_RPC_PATH,
4412
+ "register",
4413
+ {
4414
+ did_document: pending.didDocument,
4415
+ handle: handle.local,
4416
+ phone,
4417
+ otp_code: otp
4418
+ }
4419
+ );
4420
+ return this.commitRegistration(pending, result.value, result.accessToken);
4421
+ } catch (error) {
4422
+ const normalized = normalizeAwikiImError(error);
4423
+ if (normalized.code === "conflict") {
4424
+ const reconciled = await this.tryReconcilePending(pending);
4425
+ if (reconciled) {
4426
+ return reconciled;
4427
+ }
4428
+ }
4429
+ throw normalized;
4430
+ }
4431
+ });
4432
+ }
4433
+ /** Require secret identity material for a message operation. */
4434
+ requireSecrets() {
4435
+ const identity = this.options.store.snapshot().identity;
4436
+ if (!identity) {
4437
+ throw new AwikiImError("not-registered", "AWiki identity is not registered");
4438
+ }
4439
+ return identity;
4440
+ }
4441
+ /** Refresh an expired bearer by authenticating the persisted DID. */
4442
+ async refreshAccessToken() {
4443
+ const identity = this.requireSecrets();
4444
+ const result = await this.options.transport.signedRpc(
4445
+ this.options.userServiceUrl,
4446
+ DID_AUTH_RPC_PATH,
4447
+ "get_me",
4448
+ {},
4449
+ {
4450
+ didDocument: identity.didDocument,
4451
+ signingPrivateKeyPem: identity.signingPrivateKeyPem,
4452
+ signingKeyId: identity.signingKeyId
4453
+ }
4454
+ );
4455
+ const token = result.accessToken ?? requiredWireString2(result.value.access_token, "access token");
4456
+ await this.options.store.mutate((state) => {
4457
+ if (state.identity) {
4458
+ state.identity = { ...state.identity, accessToken: token };
4459
+ }
4460
+ });
4461
+ return token;
4462
+ }
4463
+ async commitRegistration(pending, result, headerToken) {
4464
+ const state = requiredWireString2(result.state, "registration state");
4465
+ if (state === "join_required") {
4466
+ throw new AwikiImError("already-registered", "AWiki identity is already registered");
4467
+ }
4468
+ if (state !== "registered") {
4469
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4470
+ }
4471
+ const did = requiredWireString2(result.did, "registered DID");
4472
+ if (did !== pending.didDocument.id) {
4473
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4474
+ }
4475
+ const fullHandle = stringValue3(result.full_handle) ?? pending.handle;
4476
+ if (fullHandle !== pending.handle) {
4477
+ throw new AwikiImError("remote", "AWiki service returned an invalid response");
4478
+ }
4479
+ const accessToken = headerToken ?? requiredWireString2(result.access_token, "access token");
4480
+ return this.persistRegisteredIdentity(pending, did, fullHandle, accessToken);
4481
+ }
4482
+ async tryReconcilePending(pending) {
4483
+ try {
4484
+ const result = await this.options.transport.signedRpc(
4485
+ this.options.userServiceUrl,
4486
+ DID_AUTH_RPC_PATH,
4487
+ "get_me",
4488
+ {},
4489
+ {
4490
+ didDocument: pending.didDocument,
4491
+ signingPrivateKeyPem: pending.signingPrivateKeyPem,
4492
+ signingKeyId: pending.signingKeyId
4493
+ }
4494
+ );
4495
+ const did = requiredWireString2(result.value.did, "registered DID");
4496
+ if (did !== pending.didDocument.id) {
4497
+ return null;
4498
+ }
4499
+ const token = result.accessToken ?? requiredWireString2(result.value.access_token, "access token");
4500
+ return this.persistRegisteredIdentity(pending, did, pending.handle, token);
4501
+ } catch {
4502
+ return null;
4503
+ }
4504
+ }
4505
+ async persistRegisteredIdentity(pending, did, handle, accessToken) {
4506
+ const publicIdentity = {
4507
+ did,
4508
+ handle,
4509
+ registeredAt: Date.now()
4510
+ };
4511
+ await this.options.store.mutate((state) => {
4512
+ state.identity = {
4513
+ public: publicIdentity,
4514
+ didDocument: pending.didDocument,
4515
+ rootPrivateKeyPem: pending.rootPrivateKeyPem,
4516
+ signingPrivateKeyPem: pending.signingPrivateKeyPem,
4517
+ signingKeyId: pending.signingKeyId,
4518
+ accessToken,
4519
+ messageServiceDid: pending.messageServiceDid
4520
+ };
4521
+ delete state.registrationOtp;
4522
+ delete state.pendingRegistration;
4523
+ });
4524
+ return publicIdentity;
4525
+ }
4526
+ async exclusiveRegistration(operation) {
4527
+ let release = () => void 0;
4528
+ const previous = this.registrationTail;
4529
+ this.registrationTail = new Promise((resolve) => {
4530
+ release = resolve;
4531
+ });
4532
+ await previous;
4533
+ try {
4534
+ return await operation();
4535
+ } finally {
4536
+ release();
4537
+ }
4538
+ }
4539
+ };
4540
+ function createPendingRegistration(phone, handle, messageServicePublicUrl, messageServiceDid) {
4541
+ const messageEndpoint = new URL("/anp-im/rpc", messageServicePublicUrl).toString();
4542
+ const services = [
4543
+ buildAnpMessageService("#message", messageEndpoint, {
4544
+ serviceDid: messageServiceDid,
4545
+ profiles: [
4546
+ "anp.core.binding.v1",
4547
+ "anp.direct.base.v1",
4548
+ "anp.group.base.v1",
4549
+ "anp.attachment.v1"
4550
+ ],
4551
+ securityProfiles: ["transport-protected"]
4552
+ }),
4553
+ {
4554
+ id: "#handle",
4555
+ type: "ANPHandleService",
4556
+ serviceEndpoint: `https://${handle.domain}/.well-known/handle/${handle.local}`
4557
+ }
4558
+ ];
4559
+ const bundle = createDidWbaDocument(handle.domain, {
4560
+ pathSegments: [handle.local],
4561
+ services,
4562
+ domain: handle.domain,
4563
+ challenge: randomChallenge(),
4564
+ didProfile: "e1" /* E1 */,
4565
+ enableE2ee: false
4566
+ });
4567
+ const signingKey = bundle.keys["key-1"];
4568
+ if (!signingKey) {
4569
+ throw new AwikiImError("remote", "AWiki identity generation failed");
4570
+ }
4571
+ return {
4572
+ handle: handle.full,
4573
+ phone,
4574
+ didDocument: bundle.didDocument,
4575
+ rootPrivateKeyPem: signingKey.privateKeyPem,
4576
+ signingPrivateKeyPem: signingKey.privateKeyPem,
4577
+ signingKeyId: `${bundle.didDocument.id}#key-1`,
4578
+ messageServiceDid
4579
+ };
4580
+ }
4581
+ function refreshPendingProof(pending, domain) {
4582
+ const unsigned = structuredClone(pending.didDocument);
4583
+ delete unsigned.proof;
4584
+ const didDocument = generateW3cProof(
4585
+ unsigned,
4586
+ pending.rootPrivateKeyPem,
4587
+ `${unsigned.id}#key-1`,
4588
+ {
4589
+ proofPurpose: "assertionMethod",
4590
+ proofType: PROOF_TYPE_DATA_INTEGRITY,
4591
+ cryptosuite: CRYPTOSUITE_EDDSA_JCS_2022,
4592
+ domain,
4593
+ challenge: randomChallenge()
4594
+ }
4595
+ );
4596
+ return { ...pending, didDocument };
4597
+ }
4598
+ function normalizeRegistrationHandle(input, userServiceDomain) {
4599
+ const value = input.trim().toLowerCase().replace(/^wba:\/\//, "");
4600
+ const configuredDomain = userServiceDomain.toLowerCase();
4601
+ const dot = value.indexOf(".");
4602
+ const local = dot < 0 ? value : value.slice(0, dot);
4603
+ const domain = dot < 0 ? configuredDomain : value.slice(dot + 1);
4604
+ if (!validateLocalPart(local) || !domain.includes(".") || domain !== configuredDomain) {
4605
+ throw new AwikiImError("invalid-request", "AWiki handle is invalid");
4606
+ }
4607
+ return { local, domain, full: `${local}.${domain}` };
4608
+ }
4609
+ function normalizePhone(value) {
4610
+ const phone = value.trim().replace(/[\s()-]/g, "");
4611
+ if (!/^\+?[0-9]{6,20}$/.test(phone)) {
4612
+ throw new AwikiImError("invalid-request", "AWiki phone number is invalid");
4613
+ }
4614
+ return phone;
4615
+ }
4616
+ function normalizeOtp(value) {
4617
+ const otp = value.trim();
4618
+ if (!/^[0-9]{4,12}$/.test(otp)) {
4619
+ throw new AwikiImError("invalid-otp", "AWiki verification code is invalid");
4620
+ }
4621
+ return otp;
4622
+ }
4623
+ function requiredWireString2(value, label) {
4624
+ const result = stringValue3(value);
4625
+ if (!result) {
4626
+ throw new AwikiImError("remote", `AWiki response is missing ${label}`);
4627
+ }
4628
+ return result;
4629
+ }
4630
+ function requiredWireNumber(value, label) {
4631
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
4632
+ throw new AwikiImError("remote", `AWiki response is missing ${label}`);
4633
+ }
4634
+ return value;
4635
+ }
4636
+ function stringValue3(value) {
4637
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
4638
+ }
4639
+ var AwikiImStateStore = class {
4640
+ constructor(path) {
4641
+ this.path = path;
4642
+ }
4643
+ state = emptyState();
4644
+ mutationTail = Promise.resolve();
4645
+ /** Load and minimally validate state from disk. */
4646
+ async load() {
4647
+ if (!this.path.trim()) {
4648
+ throw new AwikiImError("invalid-request", "AWiki statePath is required");
4649
+ }
4650
+ try {
4651
+ const decoded = JSON.parse(await readFile(this.path, "utf8"));
4652
+ if (!isPersistedState(decoded)) {
4653
+ throw new AwikiImError("invalid-request", "AWiki identity state is invalid");
4654
+ }
4655
+ this.state = decoded;
4656
+ } catch (error) {
4657
+ if (isMissingFile(error)) {
4658
+ this.state = emptyState();
4659
+ return;
4660
+ }
4661
+ if (error instanceof AwikiImError) {
4662
+ throw error;
4663
+ }
4664
+ throw new AwikiImError("invalid-request", "AWiki identity state cannot be read", void 0, {
4665
+ cause: error
4666
+ });
4667
+ }
4668
+ }
4669
+ /** Return the current in-memory state. Callers must not mutate it directly. */
4670
+ snapshot() {
4671
+ return this.state;
4672
+ }
4673
+ /** Serialize one mutation and persist its complete result atomically. */
4674
+ async mutate(mutator) {
4675
+ const operation = this.mutationTail.then(async () => {
4676
+ const next = structuredClone(this.state);
4677
+ mutator(next);
4678
+ await this.persist(next);
4679
+ this.state = next;
4680
+ });
4681
+ this.mutationTail = operation.catch(() => void 0);
4682
+ await operation;
4683
+ }
4684
+ async persist(state) {
4685
+ const parent = dirname(this.path);
4686
+ await mkdir(parent, { recursive: true, mode: 448 });
4687
+ const temporaryPath = `${this.path}.${process.pid}.${randomUUID()}.tmp`;
4688
+ try {
4689
+ await writeFile(temporaryPath, `${JSON.stringify(state)}
4690
+ `, {
4691
+ encoding: "utf8",
4692
+ mode: 384,
4693
+ flag: "wx"
4694
+ });
4695
+ await rename(temporaryPath, this.path);
4696
+ await chmod(this.path, 384);
4697
+ } catch (error) {
4698
+ await unlink(temporaryPath).catch((unlinkError) => {
4699
+ if (!isMissingFile(unlinkError)) {
4700
+ throw unlinkError;
4701
+ }
4702
+ });
4703
+ throw new AwikiImError("remote", "AWiki identity state cannot be persisted", void 0, {
4704
+ cause: error
4705
+ });
4706
+ }
4707
+ }
4708
+ };
4709
+ function isPersistedState(value) {
4710
+ if (!value || typeof value !== "object") {
4711
+ return false;
4712
+ }
4713
+ const record = value;
4714
+ return record.version === STATE_VERSION && isRecord5(record.conversations) && isRecord5(record.attachments) && isRecord5(record.sendOperations);
4715
+ }
4716
+ function isRecord5(value) {
4717
+ return !!value && typeof value === "object" && !Array.isArray(value);
4718
+ }
4719
+ function isMissingFile(error) {
4720
+ return !!error && typeof error === "object" && "code" in error && error.code === "ENOENT";
4721
+ }
4722
+
4723
+ // src/im/client.ts
4724
+ function createAwikiImClient(options) {
4725
+ return new DefaultAwikiImClient(options);
4726
+ }
4727
+ var DefaultAwikiImClient = class {
4728
+ store;
4729
+ transport;
4730
+ identity;
4731
+ messaging;
4732
+ attachments;
4733
+ ready;
4734
+ inFlight = /* @__PURE__ */ new Set();
4735
+ disposal;
4736
+ disposed = false;
4737
+ constructor(options) {
4738
+ const allowInsecureLoopback = options.allowInsecureLoopbackForTesting === true;
4739
+ validateServiceBaseUrl(options.userServiceUrl, allowInsecureLoopback);
4740
+ validateServiceBaseUrl(options.messageServiceUrl, allowInsecureLoopback);
4741
+ validateServiceBaseUrl(options.messageServicePublicUrl, allowInsecureLoopback);
4742
+ if (!isDomainName(options.userServiceDomain)) {
4743
+ throw new AwikiImError("invalid-request", "AWiki userServiceDomain is invalid");
4744
+ }
4745
+ if (!isBareDomainDidWba(options.messageServiceDid)) {
4746
+ throw new AwikiImError("invalid-request", "AWiki messageServiceDid is invalid");
4747
+ }
4748
+ if (!Array.isArray(options.allowedAttachmentOrigins) || options.allowedAttachmentOrigins.length === 0) {
4749
+ throw new AwikiImError("invalid-request", "AWiki allowedAttachmentOrigins is required");
4750
+ }
4751
+ if (!Number.isSafeInteger(options.attachmentMaxBytes) || options.attachmentMaxBytes < 1) {
4752
+ throw new AwikiImError("invalid-request", "AWiki attachmentMaxBytes is invalid");
4753
+ }
4754
+ if (!options.statePath.trim()) {
4755
+ throw new AwikiImError("invalid-request", "AWiki statePath is required");
4756
+ }
4757
+ const fetchImpl = options.fetch ?? globalThis.fetch;
4758
+ if (typeof fetchImpl !== "function") {
4759
+ throw new AwikiImError("invalid-request", "AWiki fetch implementation is required");
4760
+ }
4761
+ this.store = new AwikiImStateStore(options.statePath);
4762
+ this.transport = new AwikiImTransport(fetchImpl, {
4763
+ allowedAttachmentOrigins: options.allowedAttachmentOrigins,
4764
+ allowInsecureLoopback,
4765
+ attachmentMaxBytes: options.attachmentMaxBytes
4766
+ });
4767
+ this.identity = new AwikiIdentityRuntime({
4768
+ userServiceUrl: options.userServiceUrl,
4769
+ userServiceDomain: options.userServiceDomain.toLowerCase(),
4770
+ messageServicePublicUrl: options.messageServicePublicUrl,
4771
+ messageServiceDid: options.messageServiceDid,
4772
+ transport: this.transport,
4773
+ store: this.store
4774
+ });
4775
+ this.messaging = new AwikiMessagingRuntime({
4776
+ userServiceUrl: options.userServiceUrl,
4777
+ messageServiceUrl: options.messageServiceUrl,
4778
+ transport: this.transport,
4779
+ store: this.store,
4780
+ identity: this.identity,
4781
+ attachmentMaxBytes: options.attachmentMaxBytes
4782
+ });
4783
+ this.attachments = new AwikiAttachmentRuntime({
4784
+ transport: this.transport,
4785
+ store: this.store,
4786
+ identity: this.identity,
4787
+ messaging: this.messaging,
4788
+ attachmentMaxBytes: options.attachmentMaxBytes
4789
+ });
4790
+ this.ready = this.store.load().then(() => this.identity.validateConfiguredIdentity());
4791
+ }
4792
+ async getIdentity() {
4793
+ return this.run(() => Promise.resolve(structuredClone(this.identity.getIdentity())));
4794
+ }
4795
+ async sendRegistrationOtp(request) {
4796
+ return this.run(() => this.identity.sendRegistrationOtp(request));
4797
+ }
4798
+ async registerIdentity(request) {
4799
+ return this.run(() => this.identity.registerIdentity(request));
4800
+ }
4801
+ async listConversations(request) {
4802
+ return this.run(() => this.messaging.listConversations(request));
4803
+ }
4804
+ async getHistory(request) {
4805
+ return this.run(() => this.messaging.getHistory(request));
4806
+ }
4807
+ async sendText(request) {
4808
+ return this.run(() => this.messaging.sendText(request));
4809
+ }
4810
+ async sendAttachment(request) {
4811
+ return this.run(() => this.attachments.sendAttachment(request));
4812
+ }
4813
+ async downloadAttachment(request) {
4814
+ return this.run(() => this.attachments.downloadAttachment(request));
4815
+ }
4816
+ async dispose() {
4817
+ this.disposal ??= this.disposeOnce();
4818
+ return this.disposal;
4819
+ }
4820
+ run(operation) {
4821
+ if (this.disposed) {
4822
+ return Promise.reject(new AwikiImError("remote", "AWiki IM client has been disposed"));
4823
+ }
4824
+ const pending = (async () => {
4825
+ try {
4826
+ await this.ready;
4827
+ return await operation();
4828
+ } catch (error) {
4829
+ throw normalizeAwikiImError(error);
4830
+ }
4831
+ })();
4832
+ this.inFlight.add(pending);
4833
+ void pending.then(
4834
+ () => this.inFlight.delete(pending),
4835
+ () => this.inFlight.delete(pending)
4836
+ );
4837
+ return pending;
4838
+ }
4839
+ async disposeOnce() {
4840
+ this.disposed = true;
4841
+ this.transport.dispose();
4842
+ const ready = this.ready.catch((error) => {
4843
+ throw normalizeAwikiImError(error);
4844
+ });
4845
+ await Promise.allSettled([...this.inFlight]);
4846
+ await ready;
4847
+ }
4848
+ };
4849
+ function isDomainName(value) {
4850
+ const domain = value.trim().toLowerCase();
4851
+ if (!domain || domain.length > 253 || domain.includes("/") || domain.includes(":")) {
4852
+ return false;
4853
+ }
4854
+ try {
4855
+ return new URL(`https://${domain}`).hostname === domain;
4856
+ } catch {
4857
+ return false;
4858
+ }
4859
+ }
4860
+ function isBareDomainDidWba(value) {
4861
+ const prefix = "did:wba:";
4862
+ const normalized = value.trim().toLowerCase();
4863
+ return normalized.startsWith(prefix) && isDomainName(normalized.slice(prefix.length));
4864
+ }
4865
+
4866
+ export { ANPError, ANP_HANDLE_SERVICE_TYPE, ANP_MESSAGE_SERVICE_TYPE, AuthMode, AuthenticationError, AwikiImError, CRYPTOSUITE_DIDWBA_SECP256K1_2025, CRYPTOSUITE_EDDSA_JCS_2022, CryptoError, DIDWbaAuthHeader, DIDWbaAuthHeader as DidAuthHeaders, DidProfile, DidWbaVerifier, DidWbaVerifierError, HandleBindingError, HandleGoneError, HandleMovedError, HandleNotFoundError, HandleResolutionError, HandleStatus, HandleValidationError, IM_PROOF_DEFAULT_COMPONENTS, IM_PROOF_RELATION_ASSERTION_METHOD, IM_PROOF_RELATION_AUTHENTICATION, NetworkError, PROOF_TYPE_DATA_INTEGRITY, PROOF_TYPE_ED25519, PROOF_TYPE_SECP256K1, ProofError, DidWbaVerifier as RequestVerifier, DidWbaVerifierError as RequestVerifierError, SubjectType, VM_KEY_AUTH, VM_KEY_E2EE_AGREEMENT, VM_KEY_E2EE_SIGNING, VerificationMethod, WbaUriParseError, WnsError, authentication, buildAgentMessageService, buildAnpMessageService, buildContentDigest, buildGroupMessageService, buildHandleServiceEntry, buildImContentDigest, buildImSignatureInput, buildResolutionUrl, buildWbaUri as buildUri, buildWbaUri, canonicalizeBindingGeneration, compareBindingGenerations, computeJwkFingerprint, computeMultikeyFingerprint, createAwikiImClient, createDidWbaDocument as createDidDocument, createDidWbaDocumentWithKeyBinding as createDidDocumentWithKeyBinding, createDidWbaDocument, createDidWbaDocumentWithKeyBinding, buildHandleServiceEntry as createHandleServiceEntry, generateAuthHeader as createLegacyAuthHeader, generateAuthJson as createLegacyAuthPayload, generateW3cProof as createProof, generateHttpSignatureHeaders as createSignatureHeaders, createVerificationMethod, decodeImSignature, didDocuments, encodeImSignature, extractAuthHeaderParts, extractHandleServiceFromDidDocument, extractHandleServiceFromDidDocument as extractHandleServices, extractPublicKey, extractSignatureMetadata, findVerificationMethod, generateAuthHeader, generateAuthJson, generateHttpSignatureHeaders, generateImProof, generateW3cProof, httpSignatures, isAssertionMethodAuthorized, isAuthenticationAuthorized, legacyAuth, normalizeHandle, parseImSignatureInput, extractAuthHeaderParts as parseLegacyAuthHeader, extractSignatureMetadata as parseSignatureMetadata, parseWbaUri as parseUri, parseWbaUri, proof, resolveDidDocument, resolveDidWbaDocument, resolveHandle, resolveHandleFromUri, resolveHandleFromUri as resolveUri, validateDidDocumentBinding as validateDidBinding, validateDidDocumentBinding, validateHandle, validateLocalPart, verifyAuthHeaderSignature, verifyAuthJsonSignature, verifyHandleBinding as verifyBinding, verifyContentDigest, verifyDidKeyBinding as verifyDidBinding, verifyDidKeyBinding, verifyFederatedHttpRequest, verifyHandleBinding, verifyHttpMessageSignature, verifyImContentDigest, verifyImProof, verifyAuthHeaderSignature as verifyLegacyAuthHeader, verifyAuthJsonSignature as verifyLegacyAuthPayload, verifyW3cProof as verifyProof, verifyW3cProofDetailed as verifyProofDetailed, verifyHttpMessageSignature as verifySignatureHeaders, verifyW3cProof, verifyW3cProofDetailed, wns };
4867
+ //# sourceMappingURL=index.js.map
4868
+ //# sourceMappingURL=index.js.map