@did-btcr2/method 0.29.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +13 -5
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/browser.js +8174 -7157
  4. package/dist/browser.mjs +8174 -7157
  5. package/dist/cjs/index.js +1845 -455
  6. package/dist/esm/core/aggregation/transport/factory.js +15 -6
  7. package/dist/esm/core/aggregation/transport/factory.js.map +1 -1
  8. package/dist/esm/core/aggregation/transport/http/client.js +350 -0
  9. package/dist/esm/core/aggregation/transport/http/client.js.map +1 -0
  10. package/dist/esm/core/aggregation/transport/http/envelope.js +126 -0
  11. package/dist/esm/core/aggregation/transport/http/envelope.js.map +1 -0
  12. package/dist/esm/core/aggregation/transport/http/errors.js +11 -0
  13. package/dist/esm/core/aggregation/transport/http/errors.js.map +1 -0
  14. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +45 -0
  15. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js.map +1 -0
  16. package/dist/esm/core/aggregation/transport/http/index.js +12 -0
  17. package/dist/esm/core/aggregation/transport/http/index.js.map +1 -0
  18. package/dist/esm/core/aggregation/transport/http/nonce-cache.js +38 -0
  19. package/dist/esm/core/aggregation/transport/http/nonce-cache.js.map +1 -0
  20. package/dist/esm/core/aggregation/transport/http/protocol.js +28 -0
  21. package/dist/esm/core/aggregation/transport/http/protocol.js.map +1 -0
  22. package/dist/esm/core/aggregation/transport/http/rate-limiter.js +45 -0
  23. package/dist/esm/core/aggregation/transport/http/rate-limiter.js.map +1 -0
  24. package/dist/esm/core/aggregation/transport/http/request-auth.js +100 -0
  25. package/dist/esm/core/aggregation/transport/http/request-auth.js.map +1 -0
  26. package/dist/esm/core/aggregation/transport/http/server.js +481 -0
  27. package/dist/esm/core/aggregation/transport/http/server.js.map +1 -0
  28. package/dist/esm/core/aggregation/transport/http/sse-stream.js +110 -0
  29. package/dist/esm/core/aggregation/transport/http/sse-stream.js.map +1 -0
  30. package/dist/esm/core/aggregation/transport/http/sse-writer.js +25 -0
  31. package/dist/esm/core/aggregation/transport/http/sse-writer.js.map +1 -0
  32. package/dist/esm/core/aggregation/transport/index.js +1 -0
  33. package/dist/esm/core/aggregation/transport/index.js.map +1 -1
  34. package/dist/esm/core/beacon/beacon.js +197 -51
  35. package/dist/esm/core/beacon/beacon.js.map +1 -1
  36. package/dist/esm/core/beacon/cas-beacon.js +3 -3
  37. package/dist/esm/core/beacon/cas-beacon.js.map +1 -1
  38. package/dist/esm/core/beacon/singleton-beacon.js +3 -3
  39. package/dist/esm/core/beacon/singleton-beacon.js.map +1 -1
  40. package/dist/esm/core/beacon/smt-beacon.js +3 -3
  41. package/dist/esm/core/beacon/smt-beacon.js.map +1 -1
  42. package/dist/esm/core/updater.js +63 -55
  43. package/dist/esm/core/updater.js.map +1 -1
  44. package/dist/types/core/aggregation/transport/factory.d.ts +22 -7
  45. package/dist/types/core/aggregation/transport/factory.d.ts.map +1 -1
  46. package/dist/types/core/aggregation/transport/http/client.d.ts +48 -0
  47. package/dist/types/core/aggregation/transport/http/client.d.ts.map +1 -0
  48. package/dist/types/core/aggregation/transport/http/envelope.d.ts +64 -0
  49. package/dist/types/core/aggregation/transport/http/envelope.d.ts.map +1 -0
  50. package/dist/types/core/aggregation/transport/http/errors.d.ts +9 -0
  51. package/dist/types/core/aggregation/transport/http/errors.d.ts.map +1 -0
  52. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +32 -0
  53. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts.map +1 -0
  54. package/dist/types/core/aggregation/transport/http/index.d.ts +12 -0
  55. package/dist/types/core/aggregation/transport/http/index.d.ts.map +1 -0
  56. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +26 -0
  57. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts.map +1 -0
  58. package/dist/types/core/aggregation/transport/http/protocol.d.ts +53 -0
  59. package/dist/types/core/aggregation/transport/http/protocol.d.ts.map +1 -0
  60. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +41 -0
  61. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts.map +1 -0
  62. package/dist/types/core/aggregation/transport/http/request-auth.d.ts +50 -0
  63. package/dist/types/core/aggregation/transport/http/request-auth.d.ts.map +1 -0
  64. package/dist/types/core/aggregation/transport/http/server.d.ts +110 -0
  65. package/dist/types/core/aggregation/transport/http/server.d.ts.map +1 -0
  66. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +34 -0
  67. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts.map +1 -0
  68. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +12 -0
  69. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts.map +1 -0
  70. package/dist/types/core/aggregation/transport/index.d.ts +1 -0
  71. package/dist/types/core/aggregation/transport/index.d.ts.map +1 -1
  72. package/dist/types/core/aggregation/transport/transport.d.ts +1 -1
  73. package/dist/types/core/aggregation/transport/transport.d.ts.map +1 -1
  74. package/dist/types/core/beacon/beacon.d.ts +72 -12
  75. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  76. package/dist/types/core/beacon/cas-beacon.d.ts +3 -3
  77. package/dist/types/core/beacon/cas-beacon.d.ts.map +1 -1
  78. package/dist/types/core/beacon/singleton-beacon.d.ts +3 -3
  79. package/dist/types/core/beacon/singleton-beacon.d.ts.map +1 -1
  80. package/dist/types/core/beacon/smt-beacon.d.ts +3 -3
  81. package/dist/types/core/beacon/smt-beacon.d.ts.map +1 -1
  82. package/dist/types/core/updater.d.ts +27 -12
  83. package/dist/types/core/updater.d.ts.map +1 -1
  84. package/package.json +5 -5
  85. package/src/core/aggregation/transport/factory.ts +48 -12
  86. package/src/core/aggregation/transport/http/client.ts +409 -0
  87. package/src/core/aggregation/transport/http/envelope.ts +204 -0
  88. package/src/core/aggregation/transport/http/errors.ts +11 -0
  89. package/src/core/aggregation/transport/http/inbox-buffer.ts +53 -0
  90. package/src/core/aggregation/transport/http/index.ts +11 -0
  91. package/src/core/aggregation/transport/http/nonce-cache.ts +43 -0
  92. package/src/core/aggregation/transport/http/protocol.ts +57 -0
  93. package/src/core/aggregation/transport/http/rate-limiter.ts +75 -0
  94. package/src/core/aggregation/transport/http/request-auth.ts +164 -0
  95. package/src/core/aggregation/transport/http/server.ts +615 -0
  96. package/src/core/aggregation/transport/http/sse-stream.ts +121 -0
  97. package/src/core/aggregation/transport/http/sse-writer.ts +23 -0
  98. package/src/core/aggregation/transport/index.ts +1 -0
  99. package/src/core/aggregation/transport/transport.ts +1 -1
  100. package/src/core/beacon/beacon.ts +255 -64
  101. package/src/core/beacon/cas-beacon.ts +4 -4
  102. package/src/core/beacon/singleton-beacon.ts +4 -4
  103. package/src/core/beacon/smt-beacon.ts +4 -4
  104. package/src/core/updater.ts +113 -67
package/dist/cjs/index.js CHANGED
@@ -62,7 +62,9 @@ __export(index_exports, {
62
62
  CONSOLE_LOGGER: () => CONSOLE_LOGGER,
63
63
  DEFAULT_ADVERT_REPEAT_INTERVAL_MS: () => DEFAULT_ADVERT_REPEAT_INTERVAL_MS,
64
64
  DEFAULT_BROADCAST_LOOKBACK_MS: () => DEFAULT_BROADCAST_LOOKBACK_MS,
65
+ DEFAULT_CLOCK_SKEW_SEC: () => DEFAULT_CLOCK_SKEW_SEC,
65
66
  DEFAULT_MAX_UPDATE_SIZE_BYTES: () => DEFAULT_MAX_UPDATE_SIZE_BYTES,
67
+ DEFAULT_NONCE_LEN_BYTES: () => DEFAULT_NONCE_LEN_BYTES,
66
68
  DEFAULT_NOSTR_RELAYS: () => DEFAULT_NOSTR_RELAYS,
67
69
  DID_REGEX: () => DID_REGEX,
68
70
  DISTRIBUTE_AGGREGATED_DATA: () => DISTRIBUTE_AGGREGATED_DATA,
@@ -73,16 +75,31 @@ __export(index_exports, {
73
75
  DidVerificationMethod: () => DidVerificationMethod,
74
76
  Document: () => Document,
75
77
  GenesisDocument: () => GenesisDocument,
78
+ HTTP_ENVELOPE_VERSION: () => HTTP_ENVELOPE_VERSION,
79
+ HTTP_ROUTE: () => HTTP_ROUTE,
80
+ HttpClientTransport: () => HttpClientTransport,
81
+ HttpServerTransport: () => HttpServerTransport,
82
+ HttpTransportError: () => HttpTransportError,
76
83
  ID_PLACEHOLDER_VALUE: () => ID_PLACEHOLDER_VALUE,
77
84
  Identifier: () => Identifier,
85
+ InMemoryRateLimitStore: () => InMemoryRateLimitStore,
86
+ InboxBuffer: () => InboxBuffer,
78
87
  NONCE_CONTRIBUTION: () => NONCE_CONTRIBUTION,
88
+ NonceCache: () => NonceCache,
79
89
  NostrTransport: () => NostrTransport,
90
+ P2PKH_BEACON_TX_VSIZE: () => P2PKH_BEACON_TX_VSIZE,
91
+ P2TR_BEACON_TX_VSIZE: () => P2TR_BEACON_TX_VSIZE,
92
+ P2WPKH_BEACON_TX_VSIZE: () => P2WPKH_BEACON_TX_VSIZE,
80
93
  ParticipantCohortPhase: () => ParticipantCohortPhase,
94
+ REQUEST_AUTH_SCHEME: () => REQUEST_AUTH_SCHEME,
95
+ RateLimiter: () => RateLimiter,
81
96
  Resolver: () => Resolver,
82
97
  SIGNATURE_AUTHORIZATION: () => SIGNATURE_AUTHORIZATION,
83
98
  SILENT_LOGGER: () => SILENT_LOGGER,
99
+ SINGLETON_BEACON_TX_VSIZE: () => SINGLETON_BEACON_TX_VSIZE,
84
100
  SMTBeacon: () => SMTBeacon,
85
101
  SMTBeaconError: () => SMTBeaconError,
102
+ SSE_EVENT: () => SSE_EVENT,
86
103
  SUBMIT_UPDATE: () => SUBMIT_UPDATE,
87
104
  ServiceCohortPhase: () => ServiceCohortPhase,
88
105
  SigningSessionError: () => SigningSessionError,
@@ -97,6 +114,7 @@ __export(index_exports, {
97
114
  Updater: () => Updater,
98
115
  VALIDATION_ACK: () => VALIDATION_ACK,
99
116
  buildAggregationBeaconTx: () => buildAggregationBeaconTx,
117
+ buildRequestAuth: () => buildRequestAuth,
100
118
  createAggregatedNonceMessage: () => createAggregatedNonceMessage,
101
119
  createAuthorizationRequestMessage: () => createAuthorizationRequestMessage,
102
120
  createCohortAdvertMessage: () => createCohortAdvertMessage,
@@ -108,6 +126,11 @@ __export(index_exports, {
108
126
  createSignatureAuthorizationMessage: () => createSignatureAuthorizationMessage,
109
127
  createSubmitUpdateMessage: () => createSubmitUpdateMessage,
110
128
  createValidationAckMessage: () => createValidationAckMessage,
129
+ defaultReconnectBackoff: () => defaultReconnectBackoff,
130
+ deriveSingletonAddress: () => deriveSingletonAddress,
131
+ detectSingletonScriptKind: () => detectSingletonScriptKind,
132
+ formatSseComment: () => formatSseComment,
133
+ formatSseEvent: () => formatSseEvent,
111
134
  getBeaconStrategy: () => getBeaconStrategy,
112
135
  isAggregatedNonceMessage: () => isAggregatedNonceMessage,
113
136
  isAggregationMessageType: () => isAggregationMessageType,
@@ -124,8 +147,15 @@ __export(index_exports, {
124
147
  isSubmitUpdateMessage: () => isSubmitUpdateMessage,
125
148
  isUpdateMessageType: () => isUpdateMessageType,
126
149
  isValidationAckMessage: () => isValidationAckMessage,
150
+ normalizeForWire: () => normalizeForWire,
127
151
  opReturnScript: () => opReturnScript,
128
- registerBeaconStrategy: () => registerBeaconStrategy
152
+ parseRequestAuth: () => parseRequestAuth,
153
+ parseSseStream: () => parseSseStream,
154
+ registerBeaconStrategy: () => registerBeaconStrategy,
155
+ reviveFromWire: () => reviveFromWire,
156
+ signEnvelope: () => signEnvelope,
157
+ verifyEnvelope: () => verifyEnvelope,
158
+ verifyRequestAuth: () => verifyRequestAuth
129
159
  });
130
160
  module.exports = __toCommonJS(index_exports);
131
161
 
@@ -1751,11 +1781,1392 @@ var TransportAdapterError = class extends import_common6.MethodError {
1751
1781
  };
1752
1782
 
1753
1783
  // src/core/aggregation/transport/factory.ts
1784
+ var import_common10 = require("@did-btcr2/common");
1785
+
1786
+ // src/core/aggregation/transport/http/client.ts
1787
+ var import_keypair2 = require("@did-btcr2/keypair");
1788
+
1789
+ // src/core/identifier.ts
1754
1790
  var import_common7 = require("@did-btcr2/common");
1791
+ var import_keypair = require("@did-btcr2/keypair");
1792
+ var import_base4 = require("@scure/base");
1793
+ var Identifier = class _Identifier {
1794
+ /**
1795
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-encoding | 3.2 did:btcr2 Identifier Encoding}.
1796
+ *
1797
+ * A did:btcr2 DID consists of a did:btcr2 prefix, followed by an id-bech32 value, which is a Bech32m encoding of:
1798
+ * - the specification version;
1799
+ * - the Bitcoin network identifier; and
1800
+ * - either:
1801
+ * - a key-value representing a secp256k1 public key; or
1802
+ * - a hash-value representing the hash of an initiating external DID document.
1803
+ *
1804
+ * @param {KeyBytes | DocumentBytes} genesisBytes The genesis bytes (public key or document bytes).
1805
+ * @param {DidCreateOptions} options The DID creation options.
1806
+ * @returns {string} The new did:btcr2 identifier.
1807
+ */
1808
+ static encode(genesisBytes, options) {
1809
+ const { idType, version = 1, network } = options;
1810
+ if (!(idType in import_common7.IdentifierTypes)) {
1811
+ throw new import_common7.IdentifierError('Expected "idType" to be "KEY" or "EXTERNAL"', import_common7.INVALID_DID, { idType });
1812
+ }
1813
+ if (isNaN(version) || version > 1) {
1814
+ throw new import_common7.IdentifierError('Expected "version" to be 1', import_common7.INVALID_DID, { version });
1815
+ }
1816
+ if (typeof network === "string" && !(network in import_common7.BitcoinNetworkNames)) {
1817
+ throw new import_common7.IdentifierError('Invalid "network" name', import_common7.INVALID_DID, { network });
1818
+ }
1819
+ if (typeof network === "number" && (network < 0 || network > 8)) {
1820
+ throw new import_common7.IdentifierError('Invalid "network" number', import_common7.INVALID_DID, { network });
1821
+ }
1822
+ if (idType === "KEY") {
1823
+ try {
1824
+ new import_keypair.CompressedSecp256k1PublicKey(genesisBytes);
1825
+ } catch {
1826
+ throw new import_common7.IdentifierError(
1827
+ 'Expected "genesisBytes" to be a valid compressed secp256k1 public key',
1828
+ import_common7.INVALID_DID,
1829
+ { genesisBytes }
1830
+ );
1831
+ }
1832
+ }
1833
+ const hrp = idType === "KEY" ? "k" : "x";
1834
+ const nibbles = [];
1835
+ const fCount = Math.floor((version - 1) / 15);
1836
+ for (let i = 0; i < fCount; i++) {
1837
+ nibbles.push(15);
1838
+ }
1839
+ nibbles.push((version - 1) % 15);
1840
+ if (typeof network === "string") {
1841
+ nibbles.push(import_common7.BitcoinNetworkNames[network]);
1842
+ } else if (typeof network === "number") {
1843
+ nibbles.push(network + 11);
1844
+ }
1845
+ if (nibbles.length % 2 !== 0) {
1846
+ nibbles.push(0);
1847
+ }
1848
+ if (fCount !== 0) {
1849
+ for (const index in Array.from({ length: nibbles.length / 2 - 1 })) {
1850
+ throw new import_common7.IdentifierError("Not implemented", "NOT_IMPLEMENTED", { index });
1851
+ }
1852
+ }
1853
+ const dataBytes = new Uint8Array([nibbles[2 * 0] << 4 | nibbles[2 * 0 + 1], ...genesisBytes]);
1854
+ return `did:btcr2:${import_base4.bech32m.encodeFromBytes(hrp, dataBytes)}`;
1855
+ }
1856
+ /**
1857
+ * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-decoding | 3.3 did:btcr2 Identifier Decoding}.
1858
+ * @param {string} identifier The BTCR2 DID to be parsed
1859
+ * @returns {DidComponents} The parsed identifier components. See {@link DidComponents} for details.
1860
+ * @throws {DidError} if an error occurs while parsing the identifier
1861
+ * @throws {DidErrorCode.InvalidDid} if identifier is invalid
1862
+ * @throws {DidErrorCode.MethodNotSupported} if the method is not supported
1863
+ */
1864
+ static decode(identifier) {
1865
+ const components = identifier.split(":");
1866
+ if (components.length !== 3) {
1867
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1868
+ }
1869
+ const [scheme, method, encoded] = components;
1870
+ if (scheme !== "did") {
1871
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1872
+ }
1873
+ if (method !== "btcr2") {
1874
+ throw new import_common7.IdentifierError(`Invalid did method: ${method}`, import_common7.METHOD_NOT_SUPPORTED, { identifier });
1875
+ }
1876
+ if (!encoded) {
1877
+ throw new import_common7.IdentifierError(`Invalid method-specific id: ${identifier}`, import_common7.INVALID_DID, { identifier });
1878
+ }
1879
+ const { prefix: hrp, bytes: dataBytes } = import_base4.bech32m.decodeToBytes(encoded);
1880
+ if (!["x", "k"].includes(hrp)) {
1881
+ throw new import_common7.IdentifierError(`Invalid hrp: ${hrp}`, import_common7.INVALID_DID, { identifier });
1882
+ }
1883
+ if (!dataBytes) {
1884
+ throw new import_common7.IdentifierError(`Failed to decode id: ${encoded}`, import_common7.INVALID_DID, { identifier });
1885
+ }
1886
+ const idType = hrp === "k" ? "KEY" : "EXTERNAL";
1887
+ let version = 1;
1888
+ let byteIndex = 0;
1889
+ let nibblesConsumed = 0;
1890
+ let currentByte = dataBytes[byteIndex];
1891
+ let versionNibble = currentByte >>> 4;
1892
+ while (versionNibble === 15) {
1893
+ version += 15;
1894
+ if (nibblesConsumed % 2 === 0) {
1895
+ versionNibble = currentByte & 15;
1896
+ } else {
1897
+ currentByte = dataBytes[++byteIndex];
1898
+ versionNibble = currentByte >>> 4;
1899
+ }
1900
+ nibblesConsumed += 1;
1901
+ if (version > 1) {
1902
+ throw new import_common7.IdentifierError(`Invalid version: ${version}`, import_common7.INVALID_DID, { identifier });
1903
+ }
1904
+ }
1905
+ version += versionNibble;
1906
+ nibblesConsumed += 1;
1907
+ let networkValue = nibblesConsumed % 2 === 0 ? dataBytes[++byteIndex] >>> 4 : currentByte & 15;
1908
+ nibblesConsumed += 1;
1909
+ let network = import_common7.BitcoinNetworkNames[networkValue];
1910
+ if (!network) {
1911
+ if (networkValue >= 8 && networkValue <= 15) {
1912
+ network = networkValue - 11;
1913
+ } else {
1914
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1915
+ }
1916
+ }
1917
+ if (nibblesConsumed % 2 === 1) {
1918
+ const fillerNibble = currentByte & 15;
1919
+ if (fillerNibble !== 0) {
1920
+ throw new import_common7.IdentifierError(`Invalid did: ${identifier}`, import_common7.INVALID_DID, { identifier });
1921
+ }
1922
+ }
1923
+ const genesisBytes = dataBytes.slice(byteIndex + 1);
1924
+ if (idType === "KEY") {
1925
+ try {
1926
+ new import_keypair.CompressedSecp256k1PublicKey(genesisBytes);
1927
+ } catch {
1928
+ throw new import_common7.IdentifierError(`Invalid genesisBytes: ${genesisBytes}`, import_common7.INVALID_DID, { identifier });
1929
+ }
1930
+ }
1931
+ return { idType, hrp, version, network, genesisBytes };
1932
+ }
1933
+ /**
1934
+ * Generates a new did:btcr2 identifier based on a newly generated key pair.
1935
+ * @returns {string} The new did:btcr2 identifier.
1936
+ */
1937
+ static generate() {
1938
+ const keyPair = import_keypair.SchnorrKeyPair.generate();
1939
+ const did = this.encode(
1940
+ keyPair.publicKey.compressed,
1941
+ {
1942
+ idType: "KEY",
1943
+ version: 1,
1944
+ network: "regtest"
1945
+ }
1946
+ );
1947
+ return { keyPair: keyPair.exportJSON(), did };
1948
+ }
1949
+ /**
1950
+ * Extracts the compressed secp256k1 public key from a KEY-type did:btcr2 identifier.
1951
+ * @param {string} did The did:btcr2 identifier to extract the public key from.
1952
+ * @returns {CompressedSecp256k1PublicKey} The compressed public key.
1953
+ * @throws {IdentifierError} If the DID is EXTERNAL type (genesis bytes are a hash, not a pubkey).
1954
+ */
1955
+ static getPublicKey(did) {
1956
+ const { idType, genesisBytes } = _Identifier.decode(did);
1957
+ if (idType !== "KEY") {
1958
+ throw new import_common7.IdentifierError(
1959
+ `Cannot extract public key from EXTERNAL DID: ${did}. EXTERNAL DIDs encode a document hash, not a public key.`,
1960
+ import_common7.INVALID_DID,
1961
+ { did, idType }
1962
+ );
1963
+ }
1964
+ return new import_keypair.CompressedSecp256k1PublicKey(genesisBytes);
1965
+ }
1966
+ /**
1967
+ * Validates a did:btcr2 identifier.
1968
+ * @param {string} identifier The did:btcr2 identifier to validate.
1969
+ * @returns {boolean} True if the identifier is valid, false otherwise.
1970
+ */
1971
+ static isValid(identifier) {
1972
+ try {
1973
+ this.decode(identifier);
1974
+ return true;
1975
+ } catch {
1976
+ return false;
1977
+ }
1978
+ }
1979
+ };
1980
+
1981
+ // src/core/aggregation/transport/http/envelope.ts
1982
+ var import_common8 = require("@did-btcr2/common");
1983
+ var import_utils4 = require("@noble/hashes/utils");
1984
+
1985
+ // src/core/aggregation/transport/http/errors.ts
1986
+ var HttpTransportError = class extends TransportAdapterError {
1987
+ constructor(message, type = "HttpTransportError", data) {
1988
+ super(message, type, { adapter: "http", ...data ?? {} });
1989
+ }
1990
+ };
1991
+
1992
+ // src/core/aggregation/transport/http/protocol.ts
1993
+ var HTTP_ENVELOPE_VERSION = 1;
1994
+ var HTTP_ROUTE = {
1995
+ ADVERTS: "/v1/adverts",
1996
+ MESSAGES: "/v1/messages",
1997
+ ACTOR_INBOX: "/v1/actors/{did}/inbox",
1998
+ WELL_KNOWN: "/v1/.well-known/aggregation"
1999
+ };
2000
+ var SSE_EVENT = {
2001
+ ADVERT: "advert",
2002
+ MESSAGE: "message",
2003
+ HEARTBEAT: "heartbeat"
2004
+ };
2005
+ var DEFAULT_CLOCK_SKEW_SEC = 60;
2006
+ var DEFAULT_NONCE_LEN_BYTES = 16;
2007
+
2008
+ // src/core/aggregation/transport/http/envelope.ts
2009
+ function signEnvelope(message, sender, opts = {}) {
2010
+ const timestamp = opts.timestamp ?? Math.floor(Date.now() / 1e3);
2011
+ const nonce = opts.nonce ?? (0, import_utils4.bytesToHex)((0, import_utils4.randomBytes)(DEFAULT_NONCE_LEN_BYTES));
2012
+ const messageJson = normalizeForWire(normalizeMessage(message));
2013
+ const unsigned = {
2014
+ v: HTTP_ENVELOPE_VERSION,
2015
+ from: sender.did,
2016
+ ...opts.to !== void 0 ? { to: opts.to } : {},
2017
+ timestamp,
2018
+ nonce,
2019
+ message: messageJson
2020
+ };
2021
+ const digest = (0, import_common8.canonicalHashBytes)(unsigned);
2022
+ const sig = sender.keys.secretKey.sign(digest, { scheme: "schnorr" });
2023
+ return { ...unsigned, sig: (0, import_utils4.bytesToHex)(sig) };
2024
+ }
2025
+ function verifyEnvelope(envelope, senderPk, opts = {}) {
2026
+ if (envelope.v !== HTTP_ENVELOPE_VERSION) {
2027
+ throw new HttpTransportError(
2028
+ `Unsupported envelope version: ${envelope.v}`,
2029
+ "ENVELOPE_VERSION_MISMATCH",
2030
+ { version: envelope.v, expected: HTTP_ENVELOPE_VERSION }
2031
+ );
2032
+ }
2033
+ if (opts.expectedFrom !== void 0 && envelope.from !== opts.expectedFrom) {
2034
+ throw new HttpTransportError(
2035
+ `Envelope from mismatch: expected ${opts.expectedFrom}, got ${envelope.from}`,
2036
+ "ENVELOPE_FROM_MISMATCH",
2037
+ { expected: opts.expectedFrom, got: envelope.from }
2038
+ );
2039
+ }
2040
+ if ("expectedTo" in opts && envelope.to !== opts.expectedTo) {
2041
+ throw new HttpTransportError(
2042
+ `Envelope to mismatch: expected ${opts.expectedTo ?? "<broadcast>"}, got ${envelope.to ?? "<broadcast>"}`,
2043
+ "ENVELOPE_TO_MISMATCH",
2044
+ { expected: opts.expectedTo, got: envelope.to }
2045
+ );
2046
+ }
2047
+ const skewSec = opts.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2048
+ const nowMs = opts.now ? opts.now() : Date.now();
2049
+ const nowSec = Math.floor(nowMs / 1e3);
2050
+ const diff = Math.abs(nowSec - envelope.timestamp);
2051
+ if (diff > skewSec) {
2052
+ throw new HttpTransportError(
2053
+ `Envelope timestamp out of skew: ${diff}s > ${skewSec}s`,
2054
+ "ENVELOPE_TIMESTAMP_SKEW",
2055
+ { diff, skewSec, timestamp: envelope.timestamp, now: nowSec }
2056
+ );
2057
+ }
2058
+ let sigBytes;
2059
+ try {
2060
+ sigBytes = (0, import_utils4.hexToBytes)(envelope.sig);
2061
+ } catch {
2062
+ throw new HttpTransportError(
2063
+ "Envelope signature is not valid hex",
2064
+ "ENVELOPE_SIG_HEX"
2065
+ );
2066
+ }
2067
+ if (sigBytes.length !== 64) {
2068
+ throw new HttpTransportError(
2069
+ `Invalid signature length: ${sigBytes.length} (expected 64)`,
2070
+ "ENVELOPE_SIG_LENGTH",
2071
+ { length: sigBytes.length }
2072
+ );
2073
+ }
2074
+ const { sig: _sig, ...unsigned } = envelope;
2075
+ const digest = (0, import_common8.canonicalHashBytes)(unsigned);
2076
+ const ok = senderPk.verify(sigBytes, digest, { scheme: "schnorr" });
2077
+ if (!ok) {
2078
+ throw new HttpTransportError(
2079
+ "Envelope signature verification failed",
2080
+ "ENVELOPE_SIG_INVALID"
2081
+ );
2082
+ }
2083
+ }
2084
+ function normalizeMessage(message) {
2085
+ const maybeToJSON = message.toJSON;
2086
+ if (typeof maybeToJSON === "function") {
2087
+ return maybeToJSON.call(message);
2088
+ }
2089
+ return message;
2090
+ }
2091
+ function normalizeForWire(value) {
2092
+ if (value instanceof Uint8Array) {
2093
+ return { __bytes: (0, import_utils4.bytesToHex)(value) };
2094
+ }
2095
+ if (Array.isArray(value)) {
2096
+ return value.map((v) => normalizeForWire(v));
2097
+ }
2098
+ if (value && typeof value === "object") {
2099
+ const out = {};
2100
+ for (const [k, v] of Object.entries(value)) {
2101
+ out[k] = normalizeForWire(v);
2102
+ }
2103
+ return out;
2104
+ }
2105
+ return value;
2106
+ }
2107
+ function reviveFromWire(value) {
2108
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2109
+ const rec = value;
2110
+ const keys = Object.keys(rec);
2111
+ if (keys.length === 1 && keys[0] === "__bytes" && typeof rec.__bytes === "string") {
2112
+ return (0, import_utils4.hexToBytes)(rec.__bytes);
2113
+ }
2114
+ const out = {};
2115
+ for (const [k, v] of Object.entries(rec)) out[k] = reviveFromWire(v);
2116
+ return out;
2117
+ }
2118
+ if (Array.isArray(value)) {
2119
+ return value.map((v) => reviveFromWire(v));
2120
+ }
2121
+ return value;
2122
+ }
2123
+
2124
+ // src/core/aggregation/transport/http/request-auth.ts
2125
+ var import_common9 = require("@did-btcr2/common");
2126
+ var import_utils5 = require("@noble/hashes/utils");
2127
+ var REQUEST_AUTH_SCHEME = "BTCR2-Sig";
2128
+ function buildRequestAuth(did, keys, path, opts = {}) {
2129
+ const ts = opts.timestamp ?? Math.floor(Date.now() / 1e3);
2130
+ const nonce = opts.nonce ?? (0, import_utils5.bytesToHex)((0, import_utils5.randomBytes)(DEFAULT_NONCE_LEN_BYTES));
2131
+ const digest = (0, import_common9.canonicalHashBytes)({
2132
+ v: HTTP_ENVELOPE_VERSION,
2133
+ did,
2134
+ ts,
2135
+ nonce,
2136
+ path
2137
+ });
2138
+ const sig = keys.secretKey.sign(digest, { scheme: "schnorr" });
2139
+ return `${REQUEST_AUTH_SCHEME} v=${HTTP_ENVELOPE_VERSION},did=${did},ts=${ts},nonce=${nonce},sig=${(0, import_utils5.bytesToHex)(sig)}`;
2140
+ }
2141
+ function parseRequestAuth(headerValue) {
2142
+ const prefix = `${REQUEST_AUTH_SCHEME} `;
2143
+ if (!headerValue.startsWith(prefix)) {
2144
+ throw new HttpTransportError(
2145
+ `Unexpected auth scheme (want ${REQUEST_AUTH_SCHEME})`,
2146
+ "REQUEST_AUTH_SCHEME"
2147
+ );
2148
+ }
2149
+ const params = {};
2150
+ for (const piece of headerValue.slice(prefix.length).split(",")) {
2151
+ const eq = piece.indexOf("=");
2152
+ if (eq === -1) continue;
2153
+ const key = piece.slice(0, eq).trim();
2154
+ const val = piece.slice(eq + 1).trim();
2155
+ if (key.length > 0) params[key] = val;
2156
+ }
2157
+ const v = Number(params.v);
2158
+ const ts = Number(params.ts);
2159
+ if (!Number.isInteger(v) || !Number.isInteger(ts) || !params.did || !params.nonce || !params.sig) {
2160
+ throw new HttpTransportError(
2161
+ "Malformed auth header (missing or invalid field)",
2162
+ "REQUEST_AUTH_MALFORMED",
2163
+ { received: Object.keys(params) }
2164
+ );
2165
+ }
2166
+ return { v, did: params.did, ts, nonce: params.nonce, sig: params.sig };
2167
+ }
2168
+ function verifyRequestAuth(headerValue, expectedPath, senderPk, opts = {}) {
2169
+ const parsed = parseRequestAuth(headerValue);
2170
+ if (parsed.v !== HTTP_ENVELOPE_VERSION) {
2171
+ throw new HttpTransportError(
2172
+ `Unsupported auth version: ${parsed.v}`,
2173
+ "REQUEST_AUTH_VERSION_MISMATCH",
2174
+ { version: parsed.v, expected: HTTP_ENVELOPE_VERSION }
2175
+ );
2176
+ }
2177
+ const skewSec = opts.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2178
+ const nowMs = opts.now ? opts.now() : Date.now();
2179
+ const nowSec = Math.floor(nowMs / 1e3);
2180
+ const diff = Math.abs(nowSec - parsed.ts);
2181
+ if (diff > skewSec) {
2182
+ throw new HttpTransportError(
2183
+ `Auth timestamp out of skew: ${diff}s > ${skewSec}s`,
2184
+ "REQUEST_AUTH_TIMESTAMP_SKEW",
2185
+ { diff, skewSec }
2186
+ );
2187
+ }
2188
+ let sigBytes;
2189
+ try {
2190
+ sigBytes = (0, import_utils5.hexToBytes)(parsed.sig);
2191
+ } catch {
2192
+ throw new HttpTransportError("Auth signature is not valid hex", "REQUEST_AUTH_SIG_HEX");
2193
+ }
2194
+ if (sigBytes.length !== 64) {
2195
+ throw new HttpTransportError(
2196
+ `Invalid auth signature length: ${sigBytes.length}`,
2197
+ "REQUEST_AUTH_SIG_LENGTH",
2198
+ { length: sigBytes.length }
2199
+ );
2200
+ }
2201
+ const digest = (0, import_common9.canonicalHashBytes)({
2202
+ v: parsed.v,
2203
+ did: parsed.did,
2204
+ ts: parsed.ts,
2205
+ nonce: parsed.nonce,
2206
+ path: expectedPath
2207
+ });
2208
+ const ok = senderPk.verify(sigBytes, digest, { scheme: "schnorr" });
2209
+ if (!ok) {
2210
+ throw new HttpTransportError("Auth signature verification failed", "REQUEST_AUTH_SIG_INVALID");
2211
+ }
2212
+ return parsed;
2213
+ }
2214
+
2215
+ // src/core/aggregation/transport/http/sse-stream.ts
2216
+ async function* parseSseStream(readable) {
2217
+ const decoder = new TextDecoder("utf-8");
2218
+ const reader = readable.getReader();
2219
+ let buffer = "";
2220
+ let pending = {};
2221
+ const dispatchPending = () => {
2222
+ if (pending.data === void 0) {
2223
+ pending = {};
2224
+ return null;
2225
+ }
2226
+ const ev = { data: pending.data };
2227
+ if (pending.event !== void 0) ev.event = pending.event;
2228
+ if (pending.id !== void 0) ev.id = pending.id;
2229
+ if (pending.retry !== void 0) ev.retry = pending.retry;
2230
+ pending = {};
2231
+ return ev;
2232
+ };
2233
+ const processLine = (line) => {
2234
+ if (line.startsWith(":")) return;
2235
+ const colon = line.indexOf(":");
2236
+ const field = colon === -1 ? line : line.slice(0, colon);
2237
+ let value = colon === -1 ? "" : line.slice(colon + 1);
2238
+ if (value.startsWith(" ")) value = value.slice(1);
2239
+ switch (field) {
2240
+ case "data":
2241
+ pending.data = pending.data === void 0 ? value : `${pending.data}
2242
+ ${value}`;
2243
+ break;
2244
+ case "event":
2245
+ pending.event = value;
2246
+ break;
2247
+ case "id":
2248
+ if (!value.includes("\0")) pending.id = value;
2249
+ break;
2250
+ case "retry": {
2251
+ const n = Number(value);
2252
+ if (Number.isInteger(n) && n >= 0) pending.retry = n;
2253
+ break;
2254
+ }
2255
+ }
2256
+ };
2257
+ try {
2258
+ for (; ; ) {
2259
+ const { value, done } = await reader.read();
2260
+ if (done) {
2261
+ buffer += decoder.decode();
2262
+ if (buffer.length > 0) {
2263
+ const line = buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer;
2264
+ if (line.length > 0) processLine(line);
2265
+ buffer = "";
2266
+ }
2267
+ const tail = dispatchPending();
2268
+ if (tail) yield tail;
2269
+ return;
2270
+ }
2271
+ buffer += decoder.decode(value, { stream: true });
2272
+ let lineEnd = buffer.indexOf("\n");
2273
+ while (lineEnd !== -1) {
2274
+ let line = buffer.slice(0, lineEnd);
2275
+ if (line.endsWith("\r")) line = line.slice(0, -1);
2276
+ buffer = buffer.slice(lineEnd + 1);
2277
+ if (line.length === 0) {
2278
+ const ev = dispatchPending();
2279
+ if (ev) yield ev;
2280
+ } else {
2281
+ processLine(line);
2282
+ }
2283
+ lineEnd = buffer.indexOf("\n");
2284
+ }
2285
+ }
2286
+ } finally {
2287
+ try {
2288
+ reader.releaseLock();
2289
+ } catch {
2290
+ }
2291
+ }
2292
+ }
2293
+
2294
+ // src/core/aggregation/transport/http/client.ts
2295
+ function defaultReconnectBackoff(attempt) {
2296
+ const base2 = Math.min(1e3 * 2 ** attempt, 3e4);
2297
+ const jitter = base2 * 0.2 * Math.random();
2298
+ return Math.floor(base2 + jitter);
2299
+ }
2300
+ var HttpClientTransport = class {
2301
+ name = "http";
2302
+ #baseUrl;
2303
+ #fetch;
2304
+ #logger;
2305
+ #backoff;
2306
+ #clockSkewSec;
2307
+ #actors = /* @__PURE__ */ new Map();
2308
+ #peers = /* @__PURE__ */ new Map();
2309
+ #started = false;
2310
+ #broadcastAbort;
2311
+ constructor(config) {
2312
+ const base2 = typeof config.baseUrl === "string" ? new URL(config.baseUrl) : new URL(config.baseUrl.href);
2313
+ if (!base2.pathname.endsWith("/")) base2.pathname += "/";
2314
+ this.#baseUrl = base2;
2315
+ this.#logger = config.logger ?? CONSOLE_LOGGER;
2316
+ this.#backoff = config.reconnectBackoff ?? defaultReconnectBackoff;
2317
+ this.#clockSkewSec = config.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2318
+ const fetchImpl = config.fetchImpl ?? globalThis.fetch;
2319
+ if (typeof fetchImpl !== "function") {
2320
+ throw new HttpTransportError(
2321
+ "No fetch implementation available. Pass config.fetchImpl explicitly.",
2322
+ "NO_FETCH_IMPL"
2323
+ );
2324
+ }
2325
+ this.#fetch = fetchImpl;
2326
+ }
2327
+ start() {
2328
+ if (this.#started) return;
2329
+ this.#started = true;
2330
+ this.#broadcastAbort = new AbortController();
2331
+ this.#runBroadcastLoop(this.#broadcastAbort.signal);
2332
+ for (const [did, entry] of this.#actors) {
2333
+ this.#openInbox(did, entry);
2334
+ }
2335
+ }
2336
+ /**
2337
+ * Tear down all SSE subscriptions and stop reconnect loops. Not part of the
2338
+ * {@link Transport} interface, but needed in tests and whenever a client
2339
+ * wants to cleanly disconnect without unregistering every actor.
2340
+ *
2341
+ * Idempotent. Actors remain registered (re-call {@link start} to resume).
2342
+ */
2343
+ stop() {
2344
+ this.#broadcastAbort?.abort();
2345
+ this.#broadcastAbort = void 0;
2346
+ for (const entry of this.#actors.values()) {
2347
+ entry.inboxAbort?.abort();
2348
+ entry.inboxAbort = void 0;
2349
+ }
2350
+ this.#started = false;
2351
+ }
2352
+ registerActor(did, keys) {
2353
+ const existing = this.#actors.get(did);
2354
+ if (existing?.inboxAbort) existing.inboxAbort.abort();
2355
+ const entry = { keys, handlers: /* @__PURE__ */ new Map() };
2356
+ this.#actors.set(did, entry);
2357
+ if (this.#started) this.#openInbox(did, entry);
2358
+ }
2359
+ unregisterActor(did) {
2360
+ const entry = this.#actors.get(did);
2361
+ if (!entry) return;
2362
+ entry.inboxAbort?.abort();
2363
+ this.#actors.delete(did);
2364
+ this.#peers.delete(did);
2365
+ }
2366
+ getActorPk(did) {
2367
+ return this.#actors.get(did)?.keys.publicKey.compressed;
2368
+ }
2369
+ registerPeer(did, communicationPk) {
2370
+ try {
2371
+ new import_keypair2.CompressedSecp256k1PublicKey(communicationPk);
2372
+ } catch {
2373
+ throw new HttpTransportError(
2374
+ `Invalid communication public key for peer ${did}: expected a 33-byte compressed secp256k1 key.`,
2375
+ "INVALID_PEER_KEY",
2376
+ { did, keyLength: communicationPk.length }
2377
+ );
2378
+ }
2379
+ this.#peers.set(did, communicationPk);
2380
+ }
2381
+ getPeerPk(did) {
2382
+ return this.#peers.get(did);
2383
+ }
2384
+ registerMessageHandler(actorDid, messageType, handler) {
2385
+ const actor = this.#actors.get(actorDid);
2386
+ if (!actor) {
2387
+ throw new HttpTransportError(
2388
+ `Cannot register handler: actor ${actorDid} not registered. Call registerActor() first.`,
2389
+ "UNKNOWN_ACTOR",
2390
+ { did: actorDid }
2391
+ );
2392
+ }
2393
+ actor.handlers.set(messageType, handler);
2394
+ }
2395
+ unregisterMessageHandler(actorDid, messageType) {
2396
+ this.#actors.get(actorDid)?.handlers.delete(messageType);
2397
+ }
2398
+ async sendMessage(message, sender, recipient) {
2399
+ const actor = this.#actors.get(sender);
2400
+ if (!actor) {
2401
+ throw new HttpTransportError(
2402
+ `Unknown sender: ${sender}. Call registerActor() before sending messages.`,
2403
+ "UNKNOWN_SENDER",
2404
+ { did: sender }
2405
+ );
2406
+ }
2407
+ const envelope = signEnvelope(
2408
+ message,
2409
+ { did: sender, keys: actor.keys },
2410
+ { to: recipient }
2411
+ );
2412
+ const url = this.#route(HTTP_ROUTE.MESSAGES);
2413
+ const res = await this.#fetch(url, {
2414
+ method: "POST",
2415
+ headers: { "content-type": "application/json" },
2416
+ body: JSON.stringify(envelope)
2417
+ });
2418
+ if (!res.ok) {
2419
+ const body = await safeText(res);
2420
+ throw new HttpTransportError(
2421
+ `sendMessage failed: HTTP ${res.status}`,
2422
+ "SEND_MESSAGE_HTTP",
2423
+ { status: res.status, body: body.slice(0, 256), messageType: message.type }
2424
+ );
2425
+ }
2426
+ }
2427
+ publishRepeating(message, sender, intervalMs, recipient) {
2428
+ let stopped = false;
2429
+ const attempt = () => {
2430
+ if (stopped) return;
2431
+ this.sendMessage(message, sender, recipient).catch((err) => {
2432
+ this.#logger.debug("publishRepeating send failed:", err);
2433
+ });
2434
+ };
2435
+ attempt();
2436
+ const timer = setInterval(attempt, intervalMs);
2437
+ return () => {
2438
+ if (stopped) return;
2439
+ stopped = true;
2440
+ clearInterval(timer);
2441
+ };
2442
+ }
2443
+ #route(template) {
2444
+ return new URL(template.replace(/^\//, ""), this.#baseUrl);
2445
+ }
2446
+ #openInbox(did, entry) {
2447
+ const abort = new AbortController();
2448
+ entry.inboxAbort = abort;
2449
+ this.#runInboxLoop(did, entry, abort.signal);
2450
+ }
2451
+ async #runBroadcastLoop(signal) {
2452
+ const url = this.#route(HTTP_ROUTE.ADVERTS);
2453
+ let attempt = 0;
2454
+ while (!signal.aborted) {
2455
+ try {
2456
+ const res = await this.#fetch(url, {
2457
+ method: "GET",
2458
+ headers: { accept: "text/event-stream" },
2459
+ signal
2460
+ });
2461
+ if (!res.ok || !res.body) {
2462
+ this.#logger.warn(`Broadcast subscribe failed: HTTP ${res.status}`);
2463
+ await sleep(this.#backoff(attempt++), signal);
2464
+ continue;
2465
+ }
2466
+ attempt = 0;
2467
+ for await (const ev of parseSseStream(res.body)) {
2468
+ if (signal.aborted) return;
2469
+ if (ev.event !== SSE_EVENT.ADVERT) continue;
2470
+ this.#dispatchBroadcast(ev.data);
2471
+ }
2472
+ } catch (err) {
2473
+ if (signal.aborted) return;
2474
+ this.#logger.debug("Broadcast loop error:", err);
2475
+ try {
2476
+ await sleep(this.#backoff(attempt++), signal);
2477
+ } catch {
2478
+ return;
2479
+ }
2480
+ }
2481
+ }
2482
+ }
2483
+ async #runInboxLoop(did, entry, signal) {
2484
+ const url = this.#route(HTTP_ROUTE.ACTOR_INBOX.replace("{did}", encodeURIComponent(did)));
2485
+ let attempt = 0;
2486
+ while (!signal.aborted) {
2487
+ try {
2488
+ const auth = buildRequestAuth(did, entry.keys, url.pathname);
2489
+ const res = await this.#fetch(url, {
2490
+ method: "GET",
2491
+ headers: { accept: "text/event-stream", authorization: auth },
2492
+ signal
2493
+ });
2494
+ if (!res.ok || !res.body) {
2495
+ this.#logger.warn(`Inbox subscribe failed for ${did}: HTTP ${res.status}`);
2496
+ await sleep(this.#backoff(attempt++), signal);
2497
+ continue;
2498
+ }
2499
+ attempt = 0;
2500
+ for await (const ev of parseSseStream(res.body)) {
2501
+ if (signal.aborted) return;
2502
+ if (ev.event !== SSE_EVENT.MESSAGE) continue;
2503
+ await this.#dispatchInbox(ev.data, did, entry);
2504
+ }
2505
+ } catch (err) {
2506
+ if (signal.aborted) return;
2507
+ this.#logger.debug(`Inbox loop error for ${did}:`, err);
2508
+ try {
2509
+ await sleep(this.#backoff(attempt++), signal);
2510
+ } catch {
2511
+ return;
2512
+ }
2513
+ }
2514
+ }
2515
+ }
2516
+ #dispatchBroadcast(dataJson) {
2517
+ const envelope = parseEnvelope(dataJson, this.#logger);
2518
+ if (!envelope) return;
2519
+ const senderPk = this.#resolveSenderPk(envelope.from);
2520
+ if (!senderPk) {
2521
+ this.#logger.debug(`Broadcast from unresolvable DID: ${envelope.from}`);
2522
+ return;
2523
+ }
2524
+ try {
2525
+ verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
2526
+ } catch (err) {
2527
+ this.#logger.debug("Broadcast envelope verification failed:", err);
2528
+ return;
2529
+ }
2530
+ const revived = reviveFromWire(envelope.message);
2531
+ const flat = flattenMessage(revived);
2532
+ const messageType = typeof flat.type === "string" ? flat.type : void 0;
2533
+ if (!messageType) return;
2534
+ for (const actor of this.#actors.values()) {
2535
+ const handler = actor.handlers.get(messageType);
2536
+ if (handler) void Promise.resolve(handler(flat));
2537
+ }
2538
+ }
2539
+ async #dispatchInbox(dataJson, actorDid, entry) {
2540
+ const envelope = parseEnvelope(dataJson, this.#logger);
2541
+ if (!envelope) return;
2542
+ const senderPk = this.#resolveSenderPk(envelope.from);
2543
+ if (!senderPk) {
2544
+ this.#logger.debug(`Inbox message from unresolvable DID: ${envelope.from}`);
2545
+ return;
2546
+ }
2547
+ try {
2548
+ verifyEnvelope(envelope, senderPk, {
2549
+ clockSkewSec: this.#clockSkewSec,
2550
+ expectedTo: actorDid
2551
+ });
2552
+ } catch (err) {
2553
+ this.#logger.debug(`Inbox envelope verification failed for ${actorDid}:`, err);
2554
+ return;
2555
+ }
2556
+ const revived = reviveFromWire(envelope.message);
2557
+ const flat = flattenMessage(revived);
2558
+ const messageType = typeof flat.type === "string" ? flat.type : void 0;
2559
+ if (!messageType) return;
2560
+ const handler = entry.handlers.get(messageType);
2561
+ if (handler) await handler(flat);
2562
+ }
2563
+ #resolveSenderPk(did) {
2564
+ const peerBytes = this.#peers.get(did);
2565
+ if (peerBytes) {
2566
+ try {
2567
+ return new import_keypair2.CompressedSecp256k1PublicKey(peerBytes);
2568
+ } catch {
2569
+ }
2570
+ }
2571
+ try {
2572
+ const components = Identifier.decode(did);
2573
+ if (components.idType === "KEY") {
2574
+ return new import_keypair2.CompressedSecp256k1PublicKey(components.genesisBytes);
2575
+ }
2576
+ } catch {
2577
+ }
2578
+ return void 0;
2579
+ }
2580
+ };
2581
+ function sleep(ms, signal) {
2582
+ if (ms <= 0) return Promise.resolve();
2583
+ return new Promise((resolve, reject) => {
2584
+ const onAbort = () => {
2585
+ clearTimeout(timer);
2586
+ reject(new Error("aborted"));
2587
+ };
2588
+ const timer = setTimeout(() => {
2589
+ signal.removeEventListener("abort", onAbort);
2590
+ resolve();
2591
+ }, ms);
2592
+ if (signal.aborted) onAbort();
2593
+ else signal.addEventListener("abort", onAbort, { once: true });
2594
+ });
2595
+ }
2596
+ async function safeText(res) {
2597
+ try {
2598
+ return await res.text();
2599
+ } catch {
2600
+ return "";
2601
+ }
2602
+ }
2603
+ function parseEnvelope(dataJson, logger) {
2604
+ try {
2605
+ return JSON.parse(dataJson);
2606
+ } catch (err) {
2607
+ logger.debug("SSE event: failed to parse envelope JSON:", err);
2608
+ return void 0;
2609
+ }
2610
+ }
2611
+ function flattenMessage(msg) {
2612
+ if (msg.body && typeof msg.body === "object") {
2613
+ return { ...msg, ...msg.body };
2614
+ }
2615
+ return msg;
2616
+ }
2617
+
2618
+ // src/core/aggregation/transport/http/server.ts
2619
+ var import_keypair3 = require("@did-btcr2/keypair");
2620
+
2621
+ // src/core/aggregation/transport/http/inbox-buffer.ts
2622
+ var InboxBuffer = class {
2623
+ #capacity;
2624
+ #entries = [];
2625
+ #nextId = 1;
2626
+ constructor(capacity = 100) {
2627
+ if (capacity < 1) throw new Error(`InboxBuffer capacity must be >= 1; got ${capacity}`);
2628
+ this.#capacity = capacity;
2629
+ }
2630
+ /** Append an event. Returns the stored record (including its assigned id). */
2631
+ append(event, data) {
2632
+ const stored = { id: String(this.#nextId++), event, data };
2633
+ this.#entries.push(stored);
2634
+ if (this.#entries.length > this.#capacity) this.#entries.shift();
2635
+ return stored;
2636
+ }
2637
+ /**
2638
+ * Return stored events with id strictly greater than `lastEventId`. If
2639
+ * `lastEventId` is unset or unparseable, returns everything currently
2640
+ * retained.
2641
+ */
2642
+ since(lastEventId) {
2643
+ if (!lastEventId) return this.#entries.slice();
2644
+ const boundary = Number(lastEventId);
2645
+ if (!Number.isFinite(boundary)) return this.#entries.slice();
2646
+ return this.#entries.filter((e) => Number(e.id) > boundary);
2647
+ }
2648
+ /** Currently retained event count. */
2649
+ size() {
2650
+ return this.#entries.length;
2651
+ }
2652
+ };
2653
+
2654
+ // src/core/aggregation/transport/http/nonce-cache.ts
2655
+ var NonceCache = class {
2656
+ #maxEntries;
2657
+ #entries = /* @__PURE__ */ new Map();
2658
+ constructor(config = {}) {
2659
+ this.#maxEntries = config.maxEntries ?? 1e4;
2660
+ }
2661
+ /**
2662
+ * Record a nonce. Returns `true` if it was novel (caller should accept the
2663
+ * request) or `false` if it was a replay (caller should reject).
2664
+ */
2665
+ store(did, nonce, timestampSec) {
2666
+ const key = `${did}:${nonce}`;
2667
+ if (this.#entries.has(key)) return false;
2668
+ this.#entries.set(key, timestampSec);
2669
+ if (this.#entries.size > this.#maxEntries) {
2670
+ const oldest = this.#entries.keys().next();
2671
+ if (!oldest.done) this.#entries.delete(oldest.value);
2672
+ }
2673
+ return true;
2674
+ }
2675
+ /** Current cache size. Exposed for observability and tests. */
2676
+ size() {
2677
+ return this.#entries.size;
2678
+ }
2679
+ };
2680
+
2681
+ // src/core/aggregation/transport/http/rate-limiter.ts
2682
+ var InMemoryRateLimitStore = class {
2683
+ #buckets = /* @__PURE__ */ new Map();
2684
+ get(key) {
2685
+ return this.#buckets.get(key);
2686
+ }
2687
+ set(key, state) {
2688
+ this.#buckets.set(key, state);
2689
+ }
2690
+ };
2691
+ var RateLimiter = class {
2692
+ #rps;
2693
+ #burst;
2694
+ #store;
2695
+ constructor(config = {}) {
2696
+ this.#rps = config.rps ?? 10;
2697
+ this.#burst = config.burst ?? 30;
2698
+ this.#store = config.store ?? new InMemoryRateLimitStore();
2699
+ }
2700
+ /** Consume one token for `key`. Returns `true` if accepted, `false` if throttled. */
2701
+ consume(key, nowMs) {
2702
+ const existing = this.#store.get(key);
2703
+ const state = existing ?? { tokens: this.#burst, lastRefillMs: nowMs };
2704
+ if (existing) {
2705
+ const elapsedSec = Math.max(0, (nowMs - existing.lastRefillMs) / 1e3);
2706
+ state.tokens = Math.min(this.#burst, existing.tokens + elapsedSec * this.#rps);
2707
+ state.lastRefillMs = nowMs;
2708
+ }
2709
+ if (state.tokens < 1) {
2710
+ this.#store.set(key, state);
2711
+ return false;
2712
+ }
2713
+ state.tokens -= 1;
2714
+ this.#store.set(key, state);
2715
+ return true;
2716
+ }
2717
+ };
2718
+
2719
+ // src/core/aggregation/transport/http/server.ts
2720
+ var INBOX_PATH_PREFIX = "/v1/actors/";
2721
+ var INBOX_PATH_SUFFIX = "/inbox";
2722
+ var DEFAULT_ADVERT_TTL_MS = 5 * 60 * 1e3;
2723
+ var DEFAULT_HEARTBEAT_MS = 2e4;
2724
+ var HttpServerTransport = class {
2725
+ name = "http";
2726
+ #logger;
2727
+ #cors;
2728
+ #clockSkewSec;
2729
+ #inboxBufferSize;
2730
+ #advertTtlMs;
2731
+ #heartbeatMs;
2732
+ #rateLimiter;
2733
+ #nonceCache;
2734
+ #now;
2735
+ #actors = /* @__PURE__ */ new Map();
2736
+ #peers = /* @__PURE__ */ new Map();
2737
+ #inboxes = /* @__PURE__ */ new Map();
2738
+ #broadcastSubscribers = /* @__PURE__ */ new Set();
2739
+ #currentAdvert;
2740
+ #advertSeq = 0;
2741
+ constructor(config = {}) {
2742
+ this.#logger = config.logger ?? CONSOLE_LOGGER;
2743
+ this.#cors = config.cors ?? { mode: "permissive" };
2744
+ this.#clockSkewSec = config.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
2745
+ this.#inboxBufferSize = config.inboxBufferSize ?? 100;
2746
+ this.#advertTtlMs = config.advertTtlMs ?? DEFAULT_ADVERT_TTL_MS;
2747
+ this.#heartbeatMs = config.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS;
2748
+ this.#rateLimiter = config.rateLimiter ?? new RateLimiter();
2749
+ this.#nonceCache = config.nonceCache ?? new NonceCache();
2750
+ this.#now = config.now ?? (() => Date.now());
2751
+ }
2752
+ // ----------------------------------------------------------------
2753
+ // Transport interface
2754
+ // ----------------------------------------------------------------
2755
+ start() {
2756
+ }
2757
+ /**
2758
+ * Detach the transport: close every open SSE subscription, clear the advert
2759
+ * cache, and drop all actor / peer / inbox state. Intended for shutdown and
2760
+ * for test teardown.
2761
+ */
2762
+ stop() {
2763
+ for (const sub of this.#broadcastSubscribers) this.#closeBroadcastSubscriber(sub);
2764
+ this.#broadcastSubscribers.clear();
2765
+ for (const inbox of this.#inboxes.values()) {
2766
+ for (const sub of inbox.subscribers) this.#closeInboxSubscriber(sub);
2767
+ inbox.subscribers.clear();
2768
+ }
2769
+ this.#inboxes.clear();
2770
+ this.#currentAdvert = void 0;
2771
+ }
2772
+ registerActor(did, keys) {
2773
+ this.#actors.set(did, { keys, handlers: /* @__PURE__ */ new Map() });
2774
+ }
2775
+ unregisterActor(did) {
2776
+ this.#actors.delete(did);
2777
+ this.#peers.delete(did);
2778
+ }
2779
+ getActorPk(did) {
2780
+ return this.#actors.get(did)?.keys.publicKey.compressed;
2781
+ }
2782
+ registerPeer(did, communicationPk) {
2783
+ try {
2784
+ new import_keypair3.CompressedSecp256k1PublicKey(communicationPk);
2785
+ } catch {
2786
+ throw new HttpTransportError(
2787
+ `Invalid peer public key for ${did}`,
2788
+ "INVALID_PEER_KEY",
2789
+ { did, keyLength: communicationPk.length }
2790
+ );
2791
+ }
2792
+ this.#peers.set(did, communicationPk);
2793
+ }
2794
+ getPeerPk(did) {
2795
+ return this.#peers.get(did);
2796
+ }
2797
+ registerMessageHandler(actorDid, messageType, handler) {
2798
+ const actor = this.#actors.get(actorDid);
2799
+ if (!actor) {
2800
+ throw new HttpTransportError(
2801
+ `Cannot register handler: actor ${actorDid} not registered`,
2802
+ "UNKNOWN_ACTOR",
2803
+ { did: actorDid }
2804
+ );
2805
+ }
2806
+ actor.handlers.set(messageType, handler);
2807
+ }
2808
+ unregisterMessageHandler(actorDid, messageType) {
2809
+ this.#actors.get(actorDid)?.handlers.delete(messageType);
2810
+ }
2811
+ async sendMessage(message, sender, recipient) {
2812
+ if (!recipient) {
2813
+ throw new HttpTransportError(
2814
+ "HttpServerTransport.sendMessage requires a recipient. Use publishRepeating for broadcasts.",
2815
+ "MISSING_RECIPIENT",
2816
+ { messageType: message.type }
2817
+ );
2818
+ }
2819
+ const actor = this.#actors.get(sender);
2820
+ if (!actor) {
2821
+ throw new HttpTransportError(
2822
+ `Unknown sender: ${sender}`,
2823
+ "UNKNOWN_SENDER",
2824
+ { did: sender }
2825
+ );
2826
+ }
2827
+ const envelope = signEnvelope(message, { did: sender, keys: actor.keys }, { to: recipient });
2828
+ const dataJson = JSON.stringify(envelope);
2829
+ const inbox = this.#getOrCreateInbox(recipient);
2830
+ const stored = inbox.buffer.append(SSE_EVENT.MESSAGE, dataJson);
2831
+ for (const sub of inbox.subscribers) {
2832
+ this.#safeWrite(sub.stream, stored.event, stored.data, stored.id);
2833
+ }
2834
+ }
2835
+ publishRepeating(message, sender, _intervalMs, _recipient) {
2836
+ const actor = this.#actors.get(sender);
2837
+ if (!actor) {
2838
+ throw new HttpTransportError(`Unknown sender: ${sender}`, "UNKNOWN_SENDER", { did: sender });
2839
+ }
2840
+ const envelope = signEnvelope(message, { did: sender, keys: actor.keys });
2841
+ const dataJson = JSON.stringify(envelope);
2842
+ const id = String(++this.#advertSeq);
2843
+ const expiresAtMs = this.#now() + this.#advertTtlMs;
2844
+ this.#currentAdvert = { dataJson, id, expiresAtMs };
2845
+ for (const sub of this.#broadcastSubscribers) {
2846
+ this.#safeWrite(sub.stream, SSE_EVENT.ADVERT, dataJson, id);
2847
+ }
2848
+ return () => {
2849
+ if (this.#currentAdvert?.id === id) this.#currentAdvert = void 0;
2850
+ };
2851
+ }
2852
+ // ----------------------------------------------------------------
2853
+ // Sans-I/O HTTP surface
2854
+ // ----------------------------------------------------------------
2855
+ /**
2856
+ * Handle a POST / GET request (non-SSE). The caller dispatches SSE paths to
2857
+ * {@link handleSse} instead. Returns a fully formed response; the caller's
2858
+ * adapter turns it into an HTTP write.
2859
+ */
2860
+ async handleRequest(req) {
2861
+ const method = req.method.toUpperCase();
2862
+ if (method === "OPTIONS") return this.#respond(204, "", req);
2863
+ const path = extractPath(req.url);
2864
+ if (method === "GET" && path === HTTP_ROUTE.WELL_KNOWN) {
2865
+ return this.#respondJson(200, this.#wellKnownMetadata(), req);
2866
+ }
2867
+ if (method === "POST" && path === HTTP_ROUTE.MESSAGES) {
2868
+ return await this.#handleMessagesPost(req);
2869
+ }
2870
+ if (method === "POST" && path === HTTP_ROUTE.ADVERTS) {
2871
+ return await this.#handleAdvertsPost(req);
2872
+ }
2873
+ return this.#respondJson(404, { error: "not_found" }, req);
2874
+ }
2875
+ /**
2876
+ * Open an SSE stream for a GET request. The caller is responsible for
2877
+ * flushing writes and propagating the `onClose` callback when the HTTP
2878
+ * connection ends.
2879
+ */
2880
+ handleSse(req, stream) {
2881
+ if (req.method.toUpperCase() !== "GET") {
2882
+ stream.close();
2883
+ return;
2884
+ }
2885
+ const path = extractPath(req.url);
2886
+ if (path === HTTP_ROUTE.ADVERTS) {
2887
+ this.#openBroadcastSubscription(stream);
2888
+ return;
2889
+ }
2890
+ const inboxMatch = matchInboxPath(path);
2891
+ if (inboxMatch) {
2892
+ this.#openInboxSubscription(req, stream, inboxMatch.did, path);
2893
+ return;
2894
+ }
2895
+ stream.close();
2896
+ }
2897
+ // ----------------------------------------------------------------
2898
+ // Request handlers
2899
+ // ----------------------------------------------------------------
2900
+ async #handleMessagesPost(req) {
2901
+ const envelope = parseJsonBody(req.body);
2902
+ if (!envelope) return this.#respondJson(400, { error: "invalid_json" }, req);
2903
+ const senderPk = this.#resolveSenderPk(envelope.from);
2904
+ if (!senderPk) {
2905
+ return this.#respondJson(401, { error: "unknown_sender" }, req);
2906
+ }
2907
+ try {
2908
+ verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
2909
+ } catch (err) {
2910
+ this.#logger.debug("POST /v1/messages: envelope verification failed:", err);
2911
+ return this.#respondJson(401, { error: "invalid_envelope" }, req);
2912
+ }
2913
+ if (!this.#nonceCache.store(envelope.from, envelope.nonce, envelope.timestamp)) {
2914
+ return this.#respondJson(409, { error: "replay" }, req);
2915
+ }
2916
+ if (!this.#rateLimiter.consume(envelope.from, this.#now())) {
2917
+ return this.#respondJson(429, { error: "rate_limited" }, req);
2918
+ }
2919
+ if (!envelope.to) {
2920
+ return this.#respondJson(400, { error: "missing_recipient" }, req);
2921
+ }
2922
+ const actor = this.#actors.get(envelope.to);
2923
+ if (!actor) {
2924
+ return this.#respondJson(404, { error: "unknown_recipient" }, req);
2925
+ }
2926
+ const revived = reviveFromWire(envelope.message);
2927
+ const flat = flattenMessage2(revived);
2928
+ const messageType = typeof flat.type === "string" ? flat.type : void 0;
2929
+ if (!messageType) return this.#respondJson(400, { error: "missing_message_type" }, req);
2930
+ const handler = actor.handlers.get(messageType);
2931
+ if (handler) {
2932
+ try {
2933
+ await handler(flat);
2934
+ } catch (err) {
2935
+ this.#logger.debug(`Handler threw for ${messageType}:`, err);
2936
+ }
2937
+ }
2938
+ return this.#respondJson(202, { ok: true }, req);
2939
+ }
2940
+ async #handleAdvertsPost(req) {
2941
+ const envelope = parseJsonBody(req.body);
2942
+ if (!envelope) return this.#respondJson(400, { error: "invalid_json" }, req);
2943
+ const senderPk = this.#resolveSenderPk(envelope.from);
2944
+ if (!senderPk) return this.#respondJson(401, { error: "unknown_sender" }, req);
2945
+ try {
2946
+ verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
2947
+ } catch {
2948
+ return this.#respondJson(401, { error: "invalid_envelope" }, req);
2949
+ }
2950
+ if (!this.#nonceCache.store(envelope.from, envelope.nonce, envelope.timestamp)) {
2951
+ return this.#respondJson(409, { error: "replay" }, req);
2952
+ }
2953
+ if (!this.#rateLimiter.consume(envelope.from, this.#now())) {
2954
+ return this.#respondJson(429, { error: "rate_limited" }, req);
2955
+ }
2956
+ if (!this.#actors.has(envelope.from)) {
2957
+ return this.#respondJson(403, { error: "not_an_actor" }, req);
2958
+ }
2959
+ const id = String(++this.#advertSeq);
2960
+ this.#currentAdvert = {
2961
+ dataJson: JSON.stringify(envelope),
2962
+ id,
2963
+ expiresAtMs: this.#now() + this.#advertTtlMs
2964
+ };
2965
+ for (const sub of this.#broadcastSubscribers) {
2966
+ this.#safeWrite(sub.stream, SSE_EVENT.ADVERT, this.#currentAdvert.dataJson, id);
2967
+ }
2968
+ return this.#respondJson(202, { ok: true }, req);
2969
+ }
2970
+ #openBroadcastSubscription(stream) {
2971
+ const sub = { stream };
2972
+ this.#broadcastSubscribers.add(sub);
2973
+ stream.onClose(() => {
2974
+ this.#closeBroadcastSubscriber(sub);
2975
+ this.#broadcastSubscribers.delete(sub);
2976
+ });
2977
+ if (this.#currentAdvert && this.#currentAdvert.expiresAtMs > this.#now()) {
2978
+ this.#safeWrite(stream, SSE_EVENT.ADVERT, this.#currentAdvert.dataJson, this.#currentAdvert.id);
2979
+ }
2980
+ if (this.#heartbeatMs > 0) {
2981
+ sub.heartbeatTimer = setInterval(() => {
2982
+ try {
2983
+ stream.writeComment("hb");
2984
+ } catch {
2985
+ }
2986
+ }, this.#heartbeatMs);
2987
+ }
2988
+ }
2989
+ #openInboxSubscription(req, stream, did, path) {
2990
+ const auth = req.headers.authorization;
2991
+ if (!auth) {
2992
+ this.#logger.debug(`Inbox subscribe: missing authorization header for ${did}`);
2993
+ stream.close();
2994
+ return;
2995
+ }
2996
+ const senderPk = this.#resolveSenderPk(did);
2997
+ if (!senderPk) {
2998
+ stream.close();
2999
+ return;
3000
+ }
3001
+ let parsedTs = 0;
3002
+ let parsedNonce = "";
3003
+ try {
3004
+ const parsed = verifyRequestAuth(auth, path, senderPk, {
3005
+ clockSkewSec: this.#clockSkewSec,
3006
+ now: () => this.#now()
3007
+ });
3008
+ if (parsed.did !== did) {
3009
+ stream.close();
3010
+ return;
3011
+ }
3012
+ parsedTs = parsed.ts;
3013
+ parsedNonce = parsed.nonce;
3014
+ } catch (err) {
3015
+ this.#logger.debug(`Inbox subscribe: auth verification failed for ${did}:`, err);
3016
+ stream.close();
3017
+ return;
3018
+ }
3019
+ if (!this.#nonceCache.store(did, parsedNonce, parsedTs)) {
3020
+ stream.close();
3021
+ return;
3022
+ }
3023
+ if (!this.#rateLimiter.consume(did, this.#now())) {
3024
+ stream.close();
3025
+ return;
3026
+ }
3027
+ const inbox = this.#getOrCreateInbox(did);
3028
+ const sub = { stream };
3029
+ inbox.subscribers.add(sub);
3030
+ stream.onClose(() => {
3031
+ this.#closeInboxSubscriber(sub);
3032
+ inbox.subscribers.delete(sub);
3033
+ });
3034
+ const lastEventId = req.headers["last-event-id"];
3035
+ for (const stored of inbox.buffer.since(lastEventId)) {
3036
+ this.#safeWrite(stream, stored.event, stored.data, stored.id);
3037
+ }
3038
+ if (this.#heartbeatMs > 0) {
3039
+ sub.heartbeatTimer = setInterval(() => {
3040
+ try {
3041
+ stream.writeComment("hb");
3042
+ } catch {
3043
+ }
3044
+ }, this.#heartbeatMs);
3045
+ }
3046
+ }
3047
+ // ----------------------------------------------------------------
3048
+ // Internal helpers
3049
+ // ----------------------------------------------------------------
3050
+ #getOrCreateInbox(did) {
3051
+ let inbox = this.#inboxes.get(did);
3052
+ if (!inbox) {
3053
+ inbox = { buffer: new InboxBuffer(this.#inboxBufferSize), subscribers: /* @__PURE__ */ new Set() };
3054
+ this.#inboxes.set(did, inbox);
3055
+ }
3056
+ return inbox;
3057
+ }
3058
+ #resolveSenderPk(did) {
3059
+ const peerBytes = this.#peers.get(did);
3060
+ if (peerBytes) {
3061
+ try {
3062
+ return new import_keypair3.CompressedSecp256k1PublicKey(peerBytes);
3063
+ } catch {
3064
+ }
3065
+ }
3066
+ try {
3067
+ const components = Identifier.decode(did);
3068
+ if (components.idType === "KEY") {
3069
+ return new import_keypair3.CompressedSecp256k1PublicKey(components.genesisBytes);
3070
+ }
3071
+ } catch {
3072
+ }
3073
+ return void 0;
3074
+ }
3075
+ #safeWrite(stream, event, data, id) {
3076
+ try {
3077
+ stream.writeEvent(event, data, id);
3078
+ } catch (err) {
3079
+ this.#logger.debug("SSE writeEvent failed:", err);
3080
+ }
3081
+ }
3082
+ #closeBroadcastSubscriber(sub) {
3083
+ if (sub.heartbeatTimer) clearInterval(sub.heartbeatTimer);
3084
+ try {
3085
+ sub.stream.close();
3086
+ } catch {
3087
+ }
3088
+ }
3089
+ #closeInboxSubscriber(sub) {
3090
+ if (sub.heartbeatTimer) clearInterval(sub.heartbeatTimer);
3091
+ try {
3092
+ sub.stream.close();
3093
+ } catch {
3094
+ }
3095
+ }
3096
+ #respondJson(status, body, req) {
3097
+ return {
3098
+ status,
3099
+ headers: { "content-type": "application/json", ...this.#corsHeaders(req) },
3100
+ body: JSON.stringify(body)
3101
+ };
3102
+ }
3103
+ #respond(status, body, req) {
3104
+ return { status, headers: this.#corsHeaders(req), body };
3105
+ }
3106
+ #corsHeaders(req) {
3107
+ const origin = req.headers.origin;
3108
+ if (!origin) return {};
3109
+ const common = {
3110
+ "access-control-allow-methods": "GET, POST, OPTIONS",
3111
+ "access-control-allow-headers": "authorization, content-type, last-event-id",
3112
+ "access-control-max-age": "86400"
3113
+ };
3114
+ switch (this.#cors.mode) {
3115
+ case "permissive":
3116
+ return { "access-control-allow-origin": "*", ...common };
3117
+ case "allowlist":
3118
+ if (this.#cors.origins.includes(origin)) {
3119
+ return { "access-control-allow-origin": origin, vary: "origin", ...common };
3120
+ }
3121
+ return {};
3122
+ case "same-origin":
3123
+ return {};
3124
+ }
3125
+ }
3126
+ #wellKnownMetadata() {
3127
+ return {
3128
+ envelopeVersion: HTTP_ENVELOPE_VERSION,
3129
+ heartbeatIntervalMs: this.#heartbeatMs,
3130
+ inboxBufferSize: this.#inboxBufferSize,
3131
+ advertTtlMs: this.#advertTtlMs
3132
+ };
3133
+ }
3134
+ };
3135
+ function extractPath(reqUrl) {
3136
+ if (reqUrl.startsWith("http://") || reqUrl.startsWith("https://")) {
3137
+ return new URL(reqUrl).pathname;
3138
+ }
3139
+ const q = reqUrl.indexOf("?");
3140
+ return q === -1 ? reqUrl : reqUrl.slice(0, q);
3141
+ }
3142
+ function matchInboxPath(path) {
3143
+ if (!path.startsWith(INBOX_PATH_PREFIX) || !path.endsWith(INBOX_PATH_SUFFIX)) return void 0;
3144
+ const encodedDid = path.slice(INBOX_PATH_PREFIX.length, path.length - INBOX_PATH_SUFFIX.length);
3145
+ if (!encodedDid) return void 0;
3146
+ try {
3147
+ return { did: decodeURIComponent(encodedDid) };
3148
+ } catch {
3149
+ return void 0;
3150
+ }
3151
+ }
3152
+ function parseJsonBody(body) {
3153
+ if (body === void 0 || body === "") return void 0;
3154
+ try {
3155
+ return JSON.parse(body);
3156
+ } catch {
3157
+ return void 0;
3158
+ }
3159
+ }
3160
+ function flattenMessage2(msg) {
3161
+ if (msg.body && typeof msg.body === "object") {
3162
+ return { ...msg, ...msg.body };
3163
+ }
3164
+ return msg;
3165
+ }
1755
3166
 
1756
3167
  // src/core/aggregation/transport/nostr.ts
1757
- var import_keypair = require("@did-btcr2/keypair");
1758
- var import_utils4 = require("@noble/hashes/utils");
3168
+ var import_keypair4 = require("@did-btcr2/keypair");
3169
+ var import_utils6 = require("@noble/hashes/utils");
1759
3170
  var import_nostr_tools = require("nostr-tools");
1760
3171
  var import_pool = require("nostr-tools/pool");
1761
3172
  var DEFAULT_NOSTR_RELAYS = [
@@ -1852,7 +3263,7 @@ var NostrTransport = class _NostrTransport {
1852
3263
  */
1853
3264
  registerPeer(did, communicationPk) {
1854
3265
  try {
1855
- new import_keypair.CompressedSecp256k1PublicKey(communicationPk);
3266
+ new import_keypair4.CompressedSecp256k1PublicKey(communicationPk);
1856
3267
  } catch {
1857
3268
  throw new TransportAdapterError(
1858
3269
  `Invalid communication public key for peer ${did}: expected a 33-byte compressed secp256k1 key.`,
@@ -1953,14 +3364,14 @@ var NostrTransport = class _NostrTransport {
1953
3364
  }
1954
3365
  const senderKeys = actor.keys;
1955
3366
  const tags = [
1956
- ["p", (0, import_utils4.bytesToHex)(senderKeys.publicKey.x)],
3367
+ ["p", (0, import_utils6.bytesToHex)(senderKeys.publicKey.x)],
1957
3368
  ["t", type]
1958
3369
  ];
1959
3370
  if (to) {
1960
3371
  const recipientPkBytes = this.#peerRegistry.get(to);
1961
3372
  if (recipientPkBytes) {
1962
- const recipientPk = new import_keypair.CompressedSecp256k1PublicKey(recipientPkBytes);
1963
- tags.push(["p", (0, import_utils4.bytesToHex)(recipientPk.x)]);
3373
+ const recipientPk = new import_keypair4.CompressedSecp256k1PublicKey(recipientPkBytes);
3374
+ tags.push(["p", (0, import_utils6.bytesToHex)(recipientPk.x)]);
1964
3375
  }
1965
3376
  }
1966
3377
  if (isKeygenMessageType(type)) {
@@ -1990,10 +3401,10 @@ var NostrTransport = class _NostrTransport {
1990
3401
  { adapter: this.name, did: to }
1991
3402
  );
1992
3403
  }
1993
- const recipientPk = new import_keypair.CompressedSecp256k1PublicKey(recipientPkBytes);
3404
+ const recipientPk = new import_keypair4.CompressedSecp256k1PublicKey(recipientPkBytes);
1994
3405
  const conversationKey = import_nostr_tools.nip44.v2.utils.getConversationKey(
1995
3406
  senderKeys.secretKey.bytes,
1996
- (0, import_utils4.bytesToHex)(recipientPk.x)
3407
+ (0, import_utils6.bytesToHex)(recipientPk.x)
1997
3408
  );
1998
3409
  const content = import_nostr_tools.nip44.v2.encrypt(JSON.stringify(message, _NostrTransport.#jsonReplacer), conversationKey);
1999
3410
  const event = (0, import_nostr_tools.finalizeEvent)({
@@ -2047,7 +3458,7 @@ var NostrTransport = class _NostrTransport {
2047
3458
  */
2048
3459
  #subscribeDirected(did, entry) {
2049
3460
  if (!this.pool) return;
2050
- const pkHex = (0, import_utils4.bytesToHex)(entry.keys.publicKey.x);
3461
+ const pkHex = (0, import_utils6.bytesToHex)(entry.keys.publicKey.x);
2051
3462
  const sub = this.pool.subscribeMany(this.#relays, { kinds: [1, 1059], "#p": [pkHex] }, {
2052
3463
  onclose: (reasons) => this.#logger.debug(`Nostr directed subscription closed for ${did}`, reasons),
2053
3464
  onevent: this.#makeActorEventHandler(did)
@@ -2065,7 +3476,7 @@ var NostrTransport = class _NostrTransport {
2065
3476
  return async (event) => {
2066
3477
  const actor = this.#actors.get(actorDid);
2067
3478
  if (!actor) return;
2068
- if (event.pubkey === (0, import_utils4.bytesToHex)(actor.keys.publicKey.x)) return;
3479
+ if (event.pubkey === (0, import_utils6.bytesToHex)(actor.keys.publicKey.x)) return;
2069
3480
  let message;
2070
3481
  try {
2071
3482
  if (event.kind === 1) {
@@ -2178,7 +3589,7 @@ var NostrTransport = class _NostrTransport {
2178
3589
  */
2179
3590
  static #jsonReplacer(_key, value) {
2180
3591
  if (value instanceof Uint8Array) {
2181
- return { __bytes: (0, import_utils4.bytesToHex)(value) };
3592
+ return { __bytes: (0, import_utils6.bytesToHex)(value) };
2182
3593
  }
2183
3594
  return value;
2184
3595
  }
@@ -2196,7 +3607,7 @@ var NostrTransport = class _NostrTransport {
2196
3607
  */
2197
3608
  static #jsonReviver(_key, value) {
2198
3609
  if (value && typeof value === "object" && "__bytes" in value) {
2199
- return (0, import_utils4.hexToBytes)(value.__bytes);
3610
+ return (0, import_utils6.hexToBytes)(value.__bytes);
2200
3611
  }
2201
3612
  return value;
2202
3613
  }
@@ -2207,9 +3618,19 @@ var TransportFactory = class {
2207
3618
  static establish(config) {
2208
3619
  switch (config.type) {
2209
3620
  case "nostr":
2210
- return new NostrTransport({ relays: config.relays });
3621
+ return new NostrTransport({
3622
+ relays: config.relays,
3623
+ logger: config.logger,
3624
+ broadcastLookbackMs: config.broadcastLookbackMs
3625
+ });
2211
3626
  case "didcomm":
2212
- throw new import_common7.NotImplementedError("DIDComm transport not implemented yet.");
3627
+ throw new import_common10.NotImplementedError("DIDComm transport not implemented yet.");
3628
+ case "http":
3629
+ if (config.role === "client") return new HttpClientTransport(config);
3630
+ if (config.role === "server") return new HttpServerTransport(config);
3631
+ throw new import_common10.NotImplementedError(
3632
+ `HTTP transport role not implemented: ${config.role}`
3633
+ );
2213
3634
  default:
2214
3635
  throw new TransportError(
2215
3636
  `Invalid transport type: ${config.type}`,
@@ -2221,41 +3642,58 @@ var TransportFactory = class {
2221
3642
  };
2222
3643
 
2223
3644
  // src/core/aggregation/transport/didcomm.ts
2224
- var import_common8 = require("@did-btcr2/common");
3645
+ var import_common11 = require("@did-btcr2/common");
2225
3646
  var DidCommTransport = class {
2226
3647
  name = "didcomm";
2227
3648
  start() {
2228
- throw new import_common8.NotImplementedError("DidCommTransport not implemented. Use NostrTransport instead.");
3649
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented. Use NostrTransport instead.");
2229
3650
  }
2230
3651
  registerActor(_did, _keys) {
2231
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3652
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2232
3653
  }
2233
3654
  getActorPk(_did) {
2234
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3655
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2235
3656
  }
2236
3657
  registerPeer(_did, _communicationPk) {
2237
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3658
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2238
3659
  }
2239
3660
  getPeerPk(_did) {
2240
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3661
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2241
3662
  }
2242
3663
  registerMessageHandler(_actorDid, _messageType, _handler) {
2243
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3664
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2244
3665
  }
2245
3666
  unregisterMessageHandler(_actorDid, _messageType) {
2246
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3667
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2247
3668
  }
2248
3669
  unregisterActor(_did) {
2249
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3670
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2250
3671
  }
2251
3672
  async sendMessage(_message, _sender, _recipient) {
2252
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3673
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2253
3674
  }
2254
3675
  publishRepeating(_message, _sender, _intervalMs, _recipient) {
2255
- throw new import_common8.NotImplementedError("DidCommTransport not implemented.");
3676
+ throw new import_common11.NotImplementedError("DidCommTransport not implemented.");
2256
3677
  }
2257
3678
  };
2258
3679
 
3680
+ // src/core/aggregation/transport/http/sse-writer.ts
3681
+ function formatSseEvent(event, data, id) {
3682
+ const lines = [];
3683
+ if (id !== void 0) lines.push(`id: ${id}`);
3684
+ lines.push(`event: ${event}`);
3685
+ for (const part of data.split("\n")) lines.push(`data: ${part}`);
3686
+ lines.push("");
3687
+ lines.push("");
3688
+ return lines.join("\n");
3689
+ }
3690
+ function formatSseComment(comment) {
3691
+ const safe = comment.replace(/\n/g, " ");
3692
+ return `: ${safe}
3693
+
3694
+ `;
3695
+ }
3696
+
2259
3697
  // src/core/aggregation/runner/typed-emitter.ts
2260
3698
  var TypedEventEmitter = class {
2261
3699
  #listeners = /* @__PURE__ */ new Map();
@@ -2931,33 +4369,32 @@ var AggregationParticipantRunner = class _AggregationParticipantRunner extends T
2931
4369
  };
2932
4370
 
2933
4371
  // src/core/beacon/beacon.ts
2934
- var import_secp256k12 = require("@noble/secp256k1");
2935
- var import_utils5 = require("@noble/hashes/utils");
4372
+ var import_utils7 = require("@noble/hashes/utils.js");
2936
4373
  var import_btc_signer4 = require("@scure/btc-signer");
2937
4374
 
2938
4375
  // src/core/beacon/error.ts
2939
- var import_common9 = require("@did-btcr2/common");
2940
- var BeaconError = class extends import_common9.MethodError {
4376
+ var import_common12 = require("@did-btcr2/common");
4377
+ var BeaconError = class extends import_common12.MethodError {
2941
4378
  constructor(message, type = "BeaconError", data) {
2942
4379
  super(message, type, data);
2943
4380
  }
2944
4381
  };
2945
- var SingletonBeaconError = class extends import_common9.MethodError {
4382
+ var SingletonBeaconError = class extends import_common12.MethodError {
2946
4383
  constructor(message, type = "SingletonBeaconError", data) {
2947
4384
  super(message, type, data);
2948
4385
  }
2949
4386
  };
2950
- var AggregateBeaconError = class extends import_common9.MethodError {
4387
+ var AggregateBeaconError = class extends import_common12.MethodError {
2951
4388
  constructor(message, type = "AggregateBeaconError", data) {
2952
4389
  super(message, type, data);
2953
4390
  }
2954
4391
  };
2955
- var CASBeaconError = class extends import_common9.MethodError {
4392
+ var CASBeaconError = class extends import_common12.MethodError {
2956
4393
  constructor(message, type = "CASBeaconError", data) {
2957
4394
  super(message, type, data);
2958
4395
  }
2959
4396
  };
2960
- var SMTBeaconError = class extends import_common9.MethodError {
4397
+ var SMTBeaconError = class extends import_common12.MethodError {
2961
4398
  constructor(message, type = "SMTBeaconError", data) {
2962
4399
  super(message, type, data);
2963
4400
  }
@@ -2985,9 +4422,32 @@ var StaticFeeEstimator = class {
2985
4422
 
2986
4423
  // src/core/beacon/beacon.ts
2987
4424
  var DEFAULT_FEE_ESTIMATOR = new StaticFeeEstimator(5);
2988
- var P2TR_BEACON_TX_VSIZE = 140;
4425
+ var P2TR_BEACON_TX_VSIZE = 160;
4426
+ var P2WPKH_BEACON_TX_VSIZE = 155;
4427
+ var P2PKH_BEACON_TX_VSIZE = 240;
4428
+ var SINGLETON_BEACON_TX_VSIZE = {
4429
+ p2pkh: P2PKH_BEACON_TX_VSIZE,
4430
+ p2wpkh: P2WPKH_BEACON_TX_VSIZE,
4431
+ p2tr: P2TR_BEACON_TX_VSIZE
4432
+ };
4433
+ function detectSingletonScriptKind(bitcoinAddress, network) {
4434
+ const decoded = (0, import_btc_signer4.Address)(network).decode(bitcoinAddress);
4435
+ if (decoded.type === "pkh") return "p2pkh";
4436
+ if (decoded.type === "wpkh") return "p2wpkh";
4437
+ if (decoded.type === "tr") return "p2tr";
4438
+ throw new BeaconError(
4439
+ `Unsupported singleton beacon address type "${decoded.type}". Expected P2PKH, P2WPKH, or P2TR (taproot key-path).`,
4440
+ "UNSUPPORTED_BEACON_ADDRESS_TYPE",
4441
+ { address: bitcoinAddress, kind: decoded.type }
4442
+ );
4443
+ }
4444
+ function deriveSingletonAddress(kind, pubkey, network) {
4445
+ if (kind === "p2pkh") return (0, import_btc_signer4.p2pkh)(pubkey, network).address;
4446
+ if (kind === "p2wpkh") return (0, import_btc_signer4.p2wpkh)(pubkey, network).address;
4447
+ return (0, import_btc_signer4.p2tr)(pubkey.slice(1, 33), void 0, network).address;
4448
+ }
2989
4449
  function opReturnScript(signalBytes) {
2990
- return import_btc_signer4.Script.encode([import_btc_signer4.OP.RETURN, signalBytes]);
4450
+ return import_btc_signer4.Script.encode(["RETURN", signalBytes]);
2991
4451
  }
2992
4452
  async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
2993
4453
  const utxos = await bitcoin.rest.address.getUtxos(bitcoinAddress);
@@ -2995,7 +4455,7 @@ async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
2995
4455
  throw new BeaconError(
2996
4456
  "No UTXOs found, please fund address!",
2997
4457
  "UNFUNDED_BEACON_ADDRESS",
2998
- { bitcoinAddress }
4458
+ { address: bitcoinAddress }
2999
4459
  );
3000
4460
  }
3001
4461
  const utxo = utxos.sort((a, b) => b.status.block_height - a.status.block_height).shift();
@@ -3003,11 +4463,11 @@ async function fetchSpendableUtxo(bitcoinAddress, bitcoin) {
3003
4463
  throw new BeaconError(
3004
4464
  "Beacon bitcoin address unfunded or utxos unconfirmed.",
3005
4465
  "UNFUNDED_BEACON_ADDRESS",
3006
- { bitcoinAddress }
4466
+ { address: bitcoinAddress }
3007
4467
  );
3008
4468
  }
3009
4469
  const prevTxHex = await bitcoin.rest.transaction.getHex(utxo.txid);
3010
- return { utxo, prevTxBytes: (0, import_utils5.hexToBytes)(prevTxHex) };
4470
+ return { utxo, prevTxBytes: (0, import_utils7.hexToBytes)(prevTxHex) };
3011
4471
  }
3012
4472
  async function buildAggregationBeaconTx(opts) {
3013
4473
  const feeEstimator = opts.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
@@ -3019,10 +4479,10 @@ async function buildAggregationBeaconTx(opts) {
3019
4479
  throw new BeaconError(
3020
4480
  `UTXO value (${utxo.value}) insufficient to cover fee (${feeSats}).`,
3021
4481
  "INSUFFICIENT_FUNDS",
3022
- { bitcoinAddress: opts.beaconAddress, utxoValue: utxo.value, fee: feeSats.toString() }
4482
+ { address: opts.beaconAddress, valueSats: utxo.value, feeSats }
3023
4483
  );
3024
4484
  }
3025
- const tx = new import_btc_signer4.Transaction();
4485
+ const tx = new import_btc_signer4.Transaction({ allowUnknownOutputs: true });
3026
4486
  tx.addInput({
3027
4487
  txid: utxo.txid,
3028
4488
  index: utxo.vout,
@@ -3038,9 +4498,45 @@ async function buildAggregationBeaconTx(opts) {
3038
4498
  prevOutValues: [BigInt(utxo.value)],
3039
4499
  beaconAddress: opts.beaconAddress,
3040
4500
  utxo,
3041
- feeSats
4501
+ feeSats,
4502
+ scriptKind: "p2tr"
3042
4503
  };
3043
4504
  }
4505
+ async function signSingletonInput(tx, inputIdx, kind, signer, prevOutScript, amount) {
4506
+ const pubkey = signer.publicKey;
4507
+ if (kind === "p2pkh") {
4508
+ const sighashType = import_btc_signer4.SigHash.ALL;
4509
+ const sighash2 = tx.preimageLegacy(inputIdx, prevOutScript, sighashType);
4510
+ const sig2 = signer.sign(sighash2, "ecdsa");
4511
+ const sigWithType = (0, import_utils7.concatBytes)(sig2, new Uint8Array([sighashType]));
4512
+ tx.updateInput(inputIdx, { partialSig: [[pubkey, sigWithType]] }, true);
4513
+ tx.finalize();
4514
+ return tx.hex;
4515
+ }
4516
+ if (kind === "p2wpkh") {
4517
+ const decoded = import_btc_signer4.OutScript.decode(prevOutScript);
4518
+ if (decoded.type !== "wpkh") {
4519
+ throw new BeaconError(
4520
+ `Expected P2WPKH prev-output script, got "${decoded.type}".`,
4521
+ "PREVOUT_SCRIPT_MISMATCH",
4522
+ { kind, observedScriptType: decoded.type }
4523
+ );
4524
+ }
4525
+ const sighashScript = import_btc_signer4.OutScript.encode({ type: "pkh", hash: decoded.hash });
4526
+ const sighashType = import_btc_signer4.SigHash.ALL;
4527
+ const sighash2 = tx.preimageWitnessV0(inputIdx, sighashScript, sighashType, amount);
4528
+ const sig2 = signer.sign(sighash2, "ecdsa");
4529
+ const sigWithType = (0, import_utils7.concatBytes)(sig2, new Uint8Array([sighashType]));
4530
+ tx.updateInput(inputIdx, { partialSig: [[pubkey, sigWithType]] }, true);
4531
+ tx.finalize();
4532
+ return tx.hex;
4533
+ }
4534
+ const sighash = tx.preimageWitnessV1(inputIdx, [prevOutScript], import_btc_signer4.SigHash.DEFAULT, [amount]);
4535
+ const sig = signer.sign(sighash, "bip341");
4536
+ tx.updateInput(inputIdx, { tapKeySig: sig });
4537
+ tx.finalize();
4538
+ return tx.hex;
4539
+ }
3044
4540
  var Beacon = class {
3045
4541
  /**
3046
4542
  * The Beacon service configuration parsed from the DID Document.
@@ -3050,7 +4546,9 @@ var Beacon = class {
3050
4546
  this.service = service;
3051
4547
  }
3052
4548
  /**
3053
- * Build + sign + broadcast a single-party beacon signal transaction (P2WPKH spend).
4549
+ * Build + sign + broadcast a singleton beacon signal transaction. The beacon
4550
+ * address's script kind (P2PKH / P2WPKH / P2TR) is detected automatically
4551
+ * and the input is constructed and signed accordingly.
3054
4552
  *
3055
4553
  * Composed from the three extracted phases ({@link buildSinglePartyTx},
3056
4554
  * {@link signSinglePartyTx}, {@link broadcastRawTx}) so each piece can be exercised
@@ -3059,13 +4557,13 @@ var Beacon = class {
3059
4557
  * plumbing (UTXO fetch + OP_RETURN output + change output) is shared.
3060
4558
  *
3061
4559
  * @param signalBytes 32-byte payload to embed in OP_RETURN.
3062
- * @param secretKey Secret key used to sign the spending input.
4560
+ * @param signer Signer used to sign the spending input.
3063
4561
  * @param bitcoin Bitcoin network connection.
3064
4562
  * @param options Broadcast options (fee estimator, etc.).
3065
4563
  * @returns The txid of the broadcast transaction.
3066
4564
  * @throws {BeaconError} if the address is unfunded, no UTXO is available, or fee exceeds value.
3067
4565
  */
3068
- async buildSignAndBroadcast(signalBytes, secretKey, bitcoin, options) {
4566
+ async buildSignAndBroadcast(signalBytes, signer, bitcoin, options) {
3069
4567
  const feeEstimator = options?.feeEstimator ?? DEFAULT_FEE_ESTIMATOR;
3070
4568
  const beaconAddress = this.service.serviceEndpoint.replace("bitcoin:", "");
3071
4569
  const { utxo, prevTxBytes } = await fetchSpendableUtxo(beaconAddress, bitcoin);
@@ -3074,69 +4572,97 @@ var Beacon = class {
3074
4572
  beaconAddress,
3075
4573
  utxo,
3076
4574
  prevTxBytes,
3077
- secretKey,
4575
+ signer,
3078
4576
  bitcoin,
3079
4577
  feeEstimator
3080
4578
  });
3081
- const signedHex = this.signSinglePartyTx(plan.tx, secretKey);
4579
+ const signedHex = await this.signSinglePartyTx(plan, signer);
3082
4580
  return this.broadcastRawTx(bitcoin, signedHex);
3083
4581
  }
3084
4582
  /**
3085
- * Build an unsigned P2WPKH single-party beacon tx + probe-sign to determine vsize,
3086
- * then rebuild with the real fee. Returns the tx and prev-output metadata.
4583
+ * Build an unsigned singleton beacon tx ready for {@link signSinglePartyTx}.
3087
4584
  *
3088
- * The secret key is required here (not just in `signSinglePartyTx`) because the
3089
- * two-pass fee estimation requires an actual signature to measure vsize accurately.
4585
+ * Detects the beacon address script kind (P2PKH / P2WPKH / P2TR) and configures
4586
+ * the input accordingly. Validates that the signer's pubkey produces the beacon
4587
+ * address under that script kind — without this check, a misconfigured caller
4588
+ * would burn a real UTXO on a tx that fails at broadcast. Fees are computed from
4589
+ * the per-kind {@link SINGLETON_BEACON_TX_VSIZE} constant, avoiding any probe-sign
4590
+ * round-trip.
3090
4591
  */
3091
4592
  async buildSinglePartyTx(opts) {
3092
- const pubkey = this.#derivePubkey(opts.secretKey);
3093
- const witnessOut = (0, import_btc_signer4.p2wpkh)(pubkey, opts.bitcoin.data);
3094
- const witnessScript = witnessOut.script;
3095
- const build = (feeSats2) => {
3096
- const tx2 = new import_btc_signer4.Transaction();
3097
- tx2.addInput({
3098
- txid: opts.utxo.txid,
3099
- index: opts.utxo.vout,
3100
- nonWitnessUtxo: opts.prevTxBytes,
3101
- witnessUtxo: { amount: BigInt(opts.utxo.value), script: witnessScript }
3102
- });
3103
- tx2.addOutputAddress(
3104
- opts.beaconAddress,
3105
- BigInt(opts.utxo.value) - feeSats2,
3106
- opts.bitcoin.data
4593
+ const network = opts.bitcoin.data;
4594
+ const pubkey = opts.signer.publicKey;
4595
+ const kind = detectSingletonScriptKind(opts.beaconAddress, network);
4596
+ const derivedAddress = deriveSingletonAddress(kind, pubkey, network);
4597
+ if (derivedAddress !== opts.beaconAddress) {
4598
+ throw new BeaconError(
4599
+ `Signer pubkey produces ${kind.toUpperCase()} address "${derivedAddress}", but beacon address is "${opts.beaconAddress}".`,
4600
+ "SIGNER_KEY_MISMATCH",
4601
+ { kind, address: opts.beaconAddress, derivedAddress }
3107
4602
  );
3108
- tx2.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
3109
- return tx2;
3110
- };
3111
- const probe = build(0n);
3112
- probe.signIdx(opts.secretKey, 0);
3113
- probe.finalize();
3114
- const vsize = probe.vsize;
3115
- const feeSats = await opts.feeEstimator.estimateFee(vsize);
3116
- if (BigInt(opts.utxo.value) <= feeSats) {
4603
+ }
4604
+ const feeSats = await opts.feeEstimator.estimateFee(SINGLETON_BEACON_TX_VSIZE[kind]);
4605
+ const amount = BigInt(opts.utxo.value);
4606
+ if (amount <= feeSats) {
3117
4607
  throw new BeaconError(
3118
4608
  `UTXO value (${opts.utxo.value}) insufficient to cover fee (${feeSats}).`,
3119
4609
  "INSUFFICIENT_FUNDS",
3120
- { bitcoinAddress: opts.beaconAddress, utxoValue: opts.utxo.value, fee: feeSats.toString() }
4610
+ { address: opts.beaconAddress, valueSats: opts.utxo.value, feeSats }
3121
4611
  );
3122
4612
  }
3123
- const tx = build(feeSats);
4613
+ const tx = new import_btc_signer4.Transaction({ allowUnknownOutputs: true });
4614
+ let prevOutScript;
4615
+ if (kind === "p2pkh") {
4616
+ prevOutScript = (0, import_btc_signer4.p2pkh)(pubkey, network).script;
4617
+ tx.addInput({
4618
+ txid: opts.utxo.txid,
4619
+ index: opts.utxo.vout,
4620
+ nonWitnessUtxo: opts.prevTxBytes
4621
+ });
4622
+ } else if (kind === "p2wpkh") {
4623
+ prevOutScript = (0, import_btc_signer4.p2wpkh)(pubkey, network).script;
4624
+ tx.addInput({
4625
+ txid: opts.utxo.txid,
4626
+ index: opts.utxo.vout,
4627
+ nonWitnessUtxo: opts.prevTxBytes,
4628
+ witnessUtxo: { amount, script: prevOutScript }
4629
+ });
4630
+ } else {
4631
+ const internalKey = pubkey.slice(1, 33);
4632
+ prevOutScript = (0, import_btc_signer4.p2tr)(internalKey, void 0, network).script;
4633
+ tx.addInput({
4634
+ txid: opts.utxo.txid,
4635
+ index: opts.utxo.vout,
4636
+ nonWitnessUtxo: opts.prevTxBytes,
4637
+ witnessUtxo: { amount, script: prevOutScript },
4638
+ tapInternalKey: internalKey
4639
+ });
4640
+ }
4641
+ tx.addOutputAddress(opts.beaconAddress, amount - feeSats, network);
4642
+ tx.addOutput({ script: opReturnScript(opts.signalBytes), amount: 0n });
3124
4643
  return {
3125
4644
  tx,
3126
- prevOutScripts: [witnessScript],
3127
- prevOutValues: [BigInt(opts.utxo.value)],
4645
+ prevOutScripts: [prevOutScript],
4646
+ prevOutValues: [amount],
3128
4647
  beaconAddress: opts.beaconAddress,
3129
4648
  utxo: opts.utxo,
3130
- feeSats
4649
+ feeSats,
4650
+ scriptKind: kind
3131
4651
  };
3132
4652
  }
3133
4653
  /**
3134
4654
  * Sign + finalize the unsigned single-party tx and return its raw hex.
4655
+ * Dispatches to the correct signing primitive based on `plan.scriptKind`.
3135
4656
  */
3136
- signSinglePartyTx(tx, secretKey) {
3137
- tx.signIdx(secretKey, 0);
3138
- tx.finalize();
3139
- return tx.hex;
4657
+ async signSinglePartyTx(plan, signer) {
4658
+ return signSingletonInput(
4659
+ plan.tx,
4660
+ 0,
4661
+ plan.scriptKind,
4662
+ signer,
4663
+ plan.prevOutScripts[0],
4664
+ plan.prevOutValues[0]
4665
+ );
3140
4666
  }
3141
4667
  /**
3142
4668
  * Broadcast raw transaction hex via the Bitcoin REST endpoint. Returns the txid.
@@ -3144,14 +4670,10 @@ var Beacon = class {
3144
4670
  async broadcastRawTx(bitcoin, rawHex) {
3145
4671
  return bitcoin.rest.transaction.send(rawHex);
3146
4672
  }
3147
- /** Derive the compressed secp256k1 public key from a raw secret key. */
3148
- #derivePubkey(secretKey) {
3149
- return (0, import_secp256k12.getPublicKey)(secretKey, true);
3150
- }
3151
4673
  };
3152
4674
 
3153
4675
  // src/core/beacon/cas-beacon.ts
3154
- var import_common10 = require("@did-btcr2/common");
4676
+ var import_common13 = require("@did-btcr2/common");
3155
4677
  var CASBeacon = class extends Beacon {
3156
4678
  /**
3157
4679
  * Creates an instance of CASBeacon.
@@ -3192,7 +4714,7 @@ var CASBeacon = class extends Beacon {
3192
4714
  if (!updateHashEncoded) {
3193
4715
  continue;
3194
4716
  }
3195
- const updateHash = (0, import_common10.encode)((0, import_common10.decode)(updateHashEncoded, "base64urlnopad"), "hex");
4717
+ const updateHash = (0, import_common13.encode)((0, import_common13.decode)(updateHashEncoded, "base64urlnopad"), "hex");
3196
4718
  const signedUpdate = sidecar.updateMap.get(updateHash);
3197
4719
  if (!signedUpdate) {
3198
4720
  needs.push({
@@ -3215,19 +4737,19 @@ var CASBeacon = class extends Beacon {
3215
4737
  * and broadcast are delegated to {@link Beacon.buildSignAndBroadcast}.
3216
4738
  *
3217
4739
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
3218
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
4740
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
3219
4741
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
3220
4742
  * @param {CASBroadcastOptions} [options] Optional broadcast configuration, including a
3221
4743
  * `casPublish` callback to publish the announcement off-chain and a `feeEstimator`.
3222
4744
  * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
3223
4745
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
3224
4746
  */
3225
- async broadcastSignal(signedUpdate, secretKey, bitcoin, options) {
4747
+ async broadcastSignal(signedUpdate, signer, bitcoin, options) {
3226
4748
  const did = this.service.id.split("#")[0];
3227
- const updateHash = (0, import_common10.canonicalHash)(signedUpdate);
4749
+ const updateHash = (0, import_common13.canonicalHash)(signedUpdate);
3228
4750
  const casAnnouncement = { [did]: updateHash };
3229
- const announcementHash = (0, import_common10.hash)((0, import_common10.canonicalize)(casAnnouncement));
3230
- await this.buildSignAndBroadcast(announcementHash, secretKey, bitcoin, options);
4751
+ const announcementHash = (0, import_common13.hash)((0, import_common13.canonicalize)(casAnnouncement));
4752
+ await this.buildSignAndBroadcast(announcementHash, signer, bitcoin, options);
3231
4753
  if (options?.casPublish) {
3232
4754
  await options.casPublish(casAnnouncement);
3233
4755
  }
@@ -3236,10 +4758,10 @@ var CASBeacon = class extends Beacon {
3236
4758
  };
3237
4759
 
3238
4760
  // src/core/beacon/factory.ts
3239
- var import_common13 = require("@did-btcr2/common");
4761
+ var import_common16 = require("@did-btcr2/common");
3240
4762
 
3241
4763
  // src/core/beacon/singleton-beacon.ts
3242
- var import_common11 = require("@did-btcr2/common");
4764
+ var import_common14 = require("@did-btcr2/common");
3243
4765
  var SingletonBeacon = class extends Beacon {
3244
4766
  /**
3245
4767
  * Creates an instance of SingletonBeacon.
@@ -3280,23 +4802,23 @@ var SingletonBeacon = class extends Beacon {
3280
4802
  * {@link Beacon.buildSignAndBroadcast}.
3281
4803
  *
3282
4804
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
3283
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
4805
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
3284
4806
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
3285
4807
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
3286
4808
  * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
3287
4809
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
3288
4810
  */
3289
- async broadcastSignal(signedUpdate, secretKey, bitcoin, options) {
3290
- const signalBytes = (0, import_common11.hash)((0, import_common11.canonicalize)(signedUpdate));
3291
- await this.buildSignAndBroadcast(signalBytes, secretKey, bitcoin, options);
4811
+ async broadcastSignal(signedUpdate, signer, bitcoin, options) {
4812
+ const signalBytes = (0, import_common14.hash)((0, import_common14.canonicalize)(signedUpdate));
4813
+ await this.buildSignAndBroadcast(signalBytes, signer, bitcoin, options);
3292
4814
  return signedUpdate;
3293
4815
  }
3294
4816
  };
3295
4817
 
3296
4818
  // src/core/beacon/smt-beacon.ts
3297
- var import_common12 = require("@did-btcr2/common");
4819
+ var import_common15 = require("@did-btcr2/common");
3298
4820
  var import_smt3 = require("@did-btcr2/smt");
3299
- var import_utils6 = require("@noble/hashes/utils");
4821
+ var import_utils8 = require("@noble/hashes/utils");
3300
4822
  var SMTBeacon = class extends Beacon {
3301
4823
  /**
3302
4824
  * Creates an instance of SMTBeacon.
@@ -3374,20 +4896,20 @@ var SMTBeacon = class extends Beacon {
3374
4896
  * signing, and broadcast are delegated to {@link Beacon.buildSignAndBroadcast}.
3375
4897
  *
3376
4898
  * @param {SignedBTCR2Update} signedUpdate The signed BTCR2 update to broadcast.
3377
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
4899
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
3378
4900
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
3379
4901
  * @param {BroadcastOptions} [options] Optional broadcast configuration (e.g. fee estimator).
3380
4902
  * @return {Promise<SignedBTCR2Update>} The signed update that was broadcast.
3381
4903
  * @throws {BeaconError} if the bitcoin address is invalid, unfunded, or UTXO cannot cover the fee.
3382
4904
  */
3383
- async broadcastSignal(signedUpdate, secretKey, bitcoin, options) {
4905
+ async broadcastSignal(signedUpdate, signer, bitcoin, options) {
3384
4906
  const did = this.service.id.split("#")[0];
3385
- const canonicalBytes = new TextEncoder().encode((0, import_common12.canonicalize)(signedUpdate));
3386
- const nonce = (0, import_utils6.randomBytes)(32);
4907
+ const canonicalBytes = new TextEncoder().encode((0, import_common15.canonicalize)(signedUpdate));
4908
+ const nonce = (0, import_utils8.randomBytes)(32);
3387
4909
  const tree = new import_smt3.BTCR2MerkleTree();
3388
4910
  tree.addEntries([{ did, nonce, signedUpdate: canonicalBytes }]);
3389
4911
  tree.finalize();
3390
- await this.buildSignAndBroadcast(tree.rootHash, secretKey, bitcoin, options);
4912
+ await this.buildSignAndBroadcast(tree.rootHash, signer, bitcoin, options);
3391
4913
  return signedUpdate;
3392
4914
  }
3393
4915
  };
@@ -3408,18 +4930,18 @@ var BeaconFactory = class {
3408
4930
  case "SMTBeacon":
3409
4931
  return new SMTBeacon(service);
3410
4932
  default:
3411
- throw new import_common13.MethodError("Invalid Beacon Type", "INVALID_BEACON_ERROR", service);
4933
+ throw new import_common16.MethodError("Invalid Beacon Type", "INVALID_BEACON_ERROR", service);
3412
4934
  }
3413
4935
  }
3414
4936
  };
3415
4937
 
3416
4938
  // src/core/beacon/signal-discovery.ts
3417
4939
  var import_bitcoin2 = require("@did-btcr2/bitcoin");
3418
- var import_common16 = require("@did-btcr2/common");
4940
+ var import_common18 = require("@did-btcr2/common");
3419
4941
 
3420
4942
  // src/core/beacon/utils.ts
3421
4943
  var import_bitcoin = require("@did-btcr2/bitcoin");
3422
- var import_common15 = require("@did-btcr2/common");
4944
+ var import_common17 = require("@did-btcr2/common");
3423
4945
  var import_btc_signer5 = require("@scure/btc-signer");
3424
4946
 
3425
4947
  // src/utils/appendix.ts
@@ -4460,198 +5982,6 @@ var Appendix = class _Appendix {
4460
5982
  }
4461
5983
  };
4462
5984
 
4463
- // src/core/identifier.ts
4464
- var import_common14 = require("@did-btcr2/common");
4465
- var import_keypair2 = require("@did-btcr2/keypair");
4466
- var import_base7 = require("@scure/base");
4467
- var Identifier = class _Identifier {
4468
- /**
4469
- * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-encoding | 3.2 did:btcr2 Identifier Encoding}.
4470
- *
4471
- * A did:btcr2 DID consists of a did:btcr2 prefix, followed by an id-bech32 value, which is a Bech32m encoding of:
4472
- * - the specification version;
4473
- * - the Bitcoin network identifier; and
4474
- * - either:
4475
- * - a key-value representing a secp256k1 public key; or
4476
- * - a hash-value representing the hash of an initiating external DID document.
4477
- *
4478
- * @param {KeyBytes | DocumentBytes} genesisBytes The genesis bytes (public key or document bytes).
4479
- * @param {DidCreateOptions} options The DID creation options.
4480
- * @returns {string} The new did:btcr2 identifier.
4481
- */
4482
- static encode(genesisBytes, options) {
4483
- const { idType, version = 1, network } = options;
4484
- if (!(idType in import_common14.IdentifierTypes)) {
4485
- throw new import_common14.IdentifierError('Expected "idType" to be "KEY" or "EXTERNAL"', import_common14.INVALID_DID, { idType });
4486
- }
4487
- if (isNaN(version) || version > 1) {
4488
- throw new import_common14.IdentifierError('Expected "version" to be 1', import_common14.INVALID_DID, { version });
4489
- }
4490
- if (typeof network === "string" && !(network in import_common14.BitcoinNetworkNames)) {
4491
- throw new import_common14.IdentifierError('Invalid "network" name', import_common14.INVALID_DID, { network });
4492
- }
4493
- if (typeof network === "number" && (network < 0 || network > 8)) {
4494
- throw new import_common14.IdentifierError('Invalid "network" number', import_common14.INVALID_DID, { network });
4495
- }
4496
- if (idType === "KEY") {
4497
- try {
4498
- new import_keypair2.CompressedSecp256k1PublicKey(genesisBytes);
4499
- } catch {
4500
- throw new import_common14.IdentifierError(
4501
- 'Expected "genesisBytes" to be a valid compressed secp256k1 public key',
4502
- import_common14.INVALID_DID,
4503
- { genesisBytes }
4504
- );
4505
- }
4506
- }
4507
- const hrp = idType === "KEY" ? "k" : "x";
4508
- const nibbles = [];
4509
- const fCount = Math.floor((version - 1) / 15);
4510
- for (let i = 0; i < fCount; i++) {
4511
- nibbles.push(15);
4512
- }
4513
- nibbles.push((version - 1) % 15);
4514
- if (typeof network === "string") {
4515
- nibbles.push(import_common14.BitcoinNetworkNames[network]);
4516
- } else if (typeof network === "number") {
4517
- nibbles.push(network + 11);
4518
- }
4519
- if (nibbles.length % 2 !== 0) {
4520
- nibbles.push(0);
4521
- }
4522
- if (fCount !== 0) {
4523
- for (const index in Array.from({ length: nibbles.length / 2 - 1 })) {
4524
- throw new import_common14.IdentifierError("Not implemented", "NOT_IMPLEMENTED", { index });
4525
- }
4526
- }
4527
- const dataBytes = new Uint8Array([nibbles[2 * 0] << 4 | nibbles[2 * 0 + 1], ...genesisBytes]);
4528
- return `did:btcr2:${import_base7.bech32m.encodeFromBytes(hrp, dataBytes)}`;
4529
- }
4530
- /**
4531
- * Implements {@link https://dcdpr.github.io/did-btcr2/#didbtcr2-identifier-decoding | 3.3 did:btcr2 Identifier Decoding}.
4532
- * @param {string} identifier The BTCR2 DID to be parsed
4533
- * @returns {DidComponents} The parsed identifier components. See {@link DidComponents} for details.
4534
- * @throws {DidError} if an error occurs while parsing the identifier
4535
- * @throws {DidErrorCode.InvalidDid} if identifier is invalid
4536
- * @throws {DidErrorCode.MethodNotSupported} if the method is not supported
4537
- */
4538
- static decode(identifier) {
4539
- const components = identifier.split(":");
4540
- if (components.length !== 3) {
4541
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4542
- }
4543
- const [scheme, method, encoded] = components;
4544
- if (scheme !== "did") {
4545
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4546
- }
4547
- if (method !== "btcr2") {
4548
- throw new import_common14.IdentifierError(`Invalid did method: ${method}`, import_common14.METHOD_NOT_SUPPORTED, { identifier });
4549
- }
4550
- if (!encoded) {
4551
- throw new import_common14.IdentifierError(`Invalid method-specific id: ${identifier}`, import_common14.INVALID_DID, { identifier });
4552
- }
4553
- const { prefix: hrp, bytes: dataBytes } = import_base7.bech32m.decodeToBytes(encoded);
4554
- if (!["x", "k"].includes(hrp)) {
4555
- throw new import_common14.IdentifierError(`Invalid hrp: ${hrp}`, import_common14.INVALID_DID, { identifier });
4556
- }
4557
- if (!dataBytes) {
4558
- throw new import_common14.IdentifierError(`Failed to decode id: ${encoded}`, import_common14.INVALID_DID, { identifier });
4559
- }
4560
- const idType = hrp === "k" ? "KEY" : "EXTERNAL";
4561
- let version = 1;
4562
- let byteIndex = 0;
4563
- let nibblesConsumed = 0;
4564
- let currentByte = dataBytes[byteIndex];
4565
- let versionNibble = currentByte >>> 4;
4566
- while (versionNibble === 15) {
4567
- version += 15;
4568
- if (nibblesConsumed % 2 === 0) {
4569
- versionNibble = currentByte & 15;
4570
- } else {
4571
- currentByte = dataBytes[++byteIndex];
4572
- versionNibble = currentByte >>> 4;
4573
- }
4574
- nibblesConsumed += 1;
4575
- if (version > 1) {
4576
- throw new import_common14.IdentifierError(`Invalid version: ${version}`, import_common14.INVALID_DID, { identifier });
4577
- }
4578
- }
4579
- version += versionNibble;
4580
- nibblesConsumed += 1;
4581
- let networkValue = nibblesConsumed % 2 === 0 ? dataBytes[++byteIndex] >>> 4 : currentByte & 15;
4582
- nibblesConsumed += 1;
4583
- let network = import_common14.BitcoinNetworkNames[networkValue];
4584
- if (!network) {
4585
- if (networkValue >= 8 && networkValue <= 15) {
4586
- network = networkValue - 11;
4587
- } else {
4588
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4589
- }
4590
- }
4591
- if (nibblesConsumed % 2 === 1) {
4592
- const fillerNibble = currentByte & 15;
4593
- if (fillerNibble !== 0) {
4594
- throw new import_common14.IdentifierError(`Invalid did: ${identifier}`, import_common14.INVALID_DID, { identifier });
4595
- }
4596
- }
4597
- const genesisBytes = dataBytes.slice(byteIndex + 1);
4598
- if (idType === "KEY") {
4599
- try {
4600
- new import_keypair2.CompressedSecp256k1PublicKey(genesisBytes);
4601
- } catch {
4602
- throw new import_common14.IdentifierError(`Invalid genesisBytes: ${genesisBytes}`, import_common14.INVALID_DID, { identifier });
4603
- }
4604
- }
4605
- return { idType, hrp, version, network, genesisBytes };
4606
- }
4607
- /**
4608
- * Generates a new did:btcr2 identifier based on a newly generated key pair.
4609
- * @returns {string} The new did:btcr2 identifier.
4610
- */
4611
- static generate() {
4612
- const keyPair = import_keypair2.SchnorrKeyPair.generate();
4613
- const did = this.encode(
4614
- keyPair.publicKey.compressed,
4615
- {
4616
- idType: "KEY",
4617
- version: 1,
4618
- network: "regtest"
4619
- }
4620
- );
4621
- return { keyPair: keyPair.exportJSON(), did };
4622
- }
4623
- /**
4624
- * Extracts the compressed secp256k1 public key from a KEY-type did:btcr2 identifier.
4625
- * @param {string} did The did:btcr2 identifier to extract the public key from.
4626
- * @returns {CompressedSecp256k1PublicKey} The compressed public key.
4627
- * @throws {IdentifierError} If the DID is EXTERNAL type (genesis bytes are a hash, not a pubkey).
4628
- */
4629
- static getPublicKey(did) {
4630
- const { idType, genesisBytes } = _Identifier.decode(did);
4631
- if (idType !== "KEY") {
4632
- throw new import_common14.IdentifierError(
4633
- `Cannot extract public key from EXTERNAL DID: ${did}. EXTERNAL DIDs encode a document hash, not a public key.`,
4634
- import_common14.INVALID_DID,
4635
- { did, idType }
4636
- );
4637
- }
4638
- return new import_keypair2.CompressedSecp256k1PublicKey(genesisBytes);
4639
- }
4640
- /**
4641
- * Validates a did:btcr2 identifier.
4642
- * @param {string} identifier The did:btcr2 identifier to validate.
4643
- * @returns {boolean} True if the identifier is valid, false otherwise.
4644
- */
4645
- static isValid(identifier) {
4646
- try {
4647
- this.decode(identifier);
4648
- return true;
4649
- } catch {
4650
- return false;
4651
- }
4652
- }
4653
- };
4654
-
4655
5985
  // src/core/beacon/utils.ts
4656
5986
  var BeaconUtils = class {
4657
5987
  /**
@@ -4662,7 +5992,7 @@ var BeaconUtils = class {
4662
5992
  */
4663
5993
  static parseBitcoinAddress(uri) {
4664
5994
  if (!uri.startsWith("bitcoin:")) {
4665
- throw new import_common15.MethodError("Invalid Bitcoin URI format", "BEACON_SERVICE_ERROR", { uri });
5995
+ throw new import_common17.MethodError("Invalid Bitcoin URI format", "BEACON_SERVICE_ERROR", { uri });
4666
5996
  }
4667
5997
  return uri.replace("bitcoin:", "").split("?")[0];
4668
5998
  }
@@ -4743,7 +6073,7 @@ var BeaconUtils = class {
4743
6073
  const p2wpkhAddr = (0, import_btc_signer5.p2wpkh)(publicKey, network).address;
4744
6074
  const p2trAddr = (0, import_btc_signer5.p2tr)(publicKey.slice(1, 33), void 0, network).address;
4745
6075
  if (!p2pkhAddr || !p2wpkhAddr || !p2trAddr) {
4746
- throw new import_common15.DidMethodError("Failed to generate bitcoin addresses");
6076
+ throw new import_common17.DidMethodError("Failed to generate bitcoin addresses");
4747
6077
  }
4748
6078
  return [
4749
6079
  {
@@ -4859,7 +6189,7 @@ var BeaconSignalDiscovery = class {
4859
6189
  }
4860
6190
  const rpc = bitcoin.rpc;
4861
6191
  if (!rpc) {
4862
- throw new import_common16.ResolveError("RPC connection is not available", "RPC_CONNECTION_ERROR", bitcoin);
6192
+ throw new import_common18.ResolveError("RPC connection is not available", "RPC_CONNECTION_ERROR", bitcoin);
4863
6193
  }
4864
6194
  const targetHeight = await rpc.getBlockCount();
4865
6195
  const beaconServicesMap = BeaconUtils.getBeaconServicesMap(beaconServices);
@@ -4930,23 +6260,23 @@ var BeaconSignalDiscovery = class {
4930
6260
 
4931
6261
  // src/core/resolver.ts
4932
6262
  var import_bitcoin4 = require("@did-btcr2/bitcoin");
4933
- var import_common20 = require("@did-btcr2/common");
6263
+ var import_common22 = require("@did-btcr2/common");
4934
6264
  var import_cryptosuite3 = require("@did-btcr2/cryptosuite");
4935
- var import_keypair4 = require("@did-btcr2/keypair");
6265
+ var import_keypair6 = require("@did-btcr2/keypair");
4936
6266
 
4937
6267
  // src/did-btcr2.ts
4938
- var import_common19 = require("@did-btcr2/common");
6268
+ var import_common21 = require("@did-btcr2/common");
4939
6269
  var import_dids2 = require("@web5/dids");
4940
6270
 
4941
6271
  // src/core/updater.ts
4942
- var import_common18 = require("@did-btcr2/common");
6272
+ var import_common20 = require("@did-btcr2/common");
4943
6273
  var import_cryptosuite2 = require("@did-btcr2/cryptosuite");
4944
6274
 
4945
6275
  // src/utils/did-document.ts
4946
6276
  var import_bitcoin3 = require("@did-btcr2/bitcoin");
4947
- var import_common17 = require("@did-btcr2/common");
4948
- var import_keypair3 = require("@did-btcr2/keypair");
4949
- var import_utils8 = require("@web5/dids/utils");
6277
+ var import_common19 = require("@did-btcr2/common");
6278
+ var import_keypair5 = require("@did-btcr2/keypair");
6279
+ var import_utils10 = require("@web5/dids/utils");
4950
6280
  var import_btc_signer6 = require("@scure/btc-signer");
4951
6281
  var BTCR2_DID_DOCUMENT_CONTEXT = [
4952
6282
  "https://www.w3.org/ns/did/v1.1",
@@ -4988,20 +6318,20 @@ var DidDocument = class _DidDocument {
4988
6318
  deactivated;
4989
6319
  constructor(document) {
4990
6320
  if (!document.id) {
4991
- throw new import_common17.DidDocumentError("DID Document must have an id", import_common17.INVALID_DID_DOCUMENT, document);
6321
+ throw new import_common19.DidDocumentError("DID Document must have an id", import_common19.INVALID_DID_DOCUMENT, document);
4992
6322
  }
4993
- const idType = document.id.includes("k1") ? import_common17.IdentifierTypes.KEY : import_common17.IdentifierTypes.EXTERNAL;
6323
+ const idType = document.id.includes("k1") ? import_common19.IdentifierTypes.KEY : import_common19.IdentifierTypes.EXTERNAL;
4994
6324
  const isGenesis = document.id === ID_PLACEHOLDER_VALUE;
4995
6325
  const { id, verificationMethod: vm, service } = document;
4996
6326
  if (!isGenesis) {
4997
6327
  if (!_DidDocument.isValidId(id)) {
4998
- throw new import_common17.DidDocumentError(`Invalid id: ${id}`, import_common17.INVALID_DID_DOCUMENT, document);
6328
+ throw new import_common19.DidDocumentError(`Invalid id: ${id}`, import_common19.INVALID_DID_DOCUMENT, document);
4999
6329
  }
5000
6330
  if (!_DidDocument.isValidVerificationMethods(vm)) {
5001
- throw new import_common17.DidDocumentError("Invalid verificationMethod: " + vm, import_common17.INVALID_DID_DOCUMENT, document);
6331
+ throw new import_common19.DidDocumentError("Invalid verificationMethod: " + vm, import_common19.INVALID_DID_DOCUMENT, document);
5002
6332
  }
5003
6333
  if (!_DidDocument.isValidServices(service)) {
5004
- throw new import_common17.DidDocumentError("Invalid service: " + service, import_common17.INVALID_DID_DOCUMENT, document);
6334
+ throw new import_common19.DidDocumentError("Invalid service: " + service, import_common19.INVALID_DID_DOCUMENT, document);
5005
6335
  }
5006
6336
  }
5007
6337
  this.id = document.id;
@@ -5011,7 +6341,7 @@ var DidDocument = class _DidDocument {
5011
6341
  "https://www.w3.org/ns/did/v1.1",
5012
6342
  "https://btcr2.dev/context/v1"
5013
6343
  ];
5014
- if (idType === import_common17.IdentifierTypes.KEY) {
6344
+ if (idType === import_common19.IdentifierTypes.KEY) {
5015
6345
  const keyRef = `${this.id}#initialKey`;
5016
6346
  this.authentication = document.authentication || [keyRef];
5017
6347
  this.assertionMethod = document.assertionMethod || [keyRef];
@@ -5097,19 +6427,19 @@ var DidDocument = class _DidDocument {
5097
6427
  */
5098
6428
  static isValid(didDocument) {
5099
6429
  if (!this.isValidContext(didDocument?.["@context"])) {
5100
- throw new import_common17.DidDocumentError('Invalid "@context"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6430
+ throw new import_common19.DidDocumentError('Invalid "@context"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5101
6431
  }
5102
6432
  if (!this.isValidId(didDocument?.id)) {
5103
- throw new import_common17.DidDocumentError('Invalid "id"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6433
+ throw new import_common19.DidDocumentError('Invalid "id"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5104
6434
  }
5105
6435
  if (!this.isValidVerificationMethods(didDocument?.verificationMethod)) {
5106
- throw new import_common17.DidDocumentError('Invalid "verificationMethod"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6436
+ throw new import_common19.DidDocumentError('Invalid "verificationMethod"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5107
6437
  }
5108
6438
  if (!this.isValidServices(didDocument?.service)) {
5109
- throw new import_common17.DidDocumentError('Invalid "service"', import_common17.INVALID_DID_DOCUMENT, didDocument);
6439
+ throw new import_common19.DidDocumentError('Invalid "service"', import_common19.INVALID_DID_DOCUMENT, didDocument);
5110
6440
  }
5111
6441
  if (!this.isValidVerificationRelationships(didDocument)) {
5112
- throw new import_common17.DidDocumentError("Invalid verification relationships", import_common17.INVALID_DID_DOCUMENT, didDocument);
6442
+ throw new import_common19.DidDocumentError("Invalid verification relationships", import_common19.INVALID_DID_DOCUMENT, didDocument);
5113
6443
  }
5114
6444
  return true;
5115
6445
  }
@@ -5156,7 +6486,7 @@ var DidDocument = class _DidDocument {
5156
6486
  * @returns {boolean} True if the services are valid.
5157
6487
  */
5158
6488
  static isValidServices(service) {
5159
- return Array.isArray(service) && service.every(import_utils8.isDidService);
6489
+ return Array.isArray(service) && service.every(import_utils10.isDidService);
5160
6490
  }
5161
6491
  /**
5162
6492
  * Validates verification relationships (authentication, assertionMethod, capabilityInvocation, capabilityDelegation).
@@ -5199,16 +6529,16 @@ var DidDocument = class _DidDocument {
5199
6529
  */
5200
6530
  validateGenesis() {
5201
6531
  if (this.id !== ID_PLACEHOLDER_VALUE) {
5202
- throw new import_common17.DidDocumentError("Invalid GenesisDocument ID", import_common17.INVALID_DID_DOCUMENT, this);
6532
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument ID", import_common19.INVALID_DID_DOCUMENT, this);
5203
6533
  }
5204
6534
  if (!this.verificationMethod.every((vm) => vm.id.includes(ID_PLACEHOLDER_VALUE) && vm.controller === ID_PLACEHOLDER_VALUE)) {
5205
- throw new import_common17.DidDocumentError("Invalid GenesisDocument verificationMethod", import_common17.INVALID_DID_DOCUMENT, this);
6535
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument verificationMethod", import_common19.INVALID_DID_DOCUMENT, this);
5206
6536
  }
5207
6537
  if (!this.service.every((svc) => svc.id.includes(ID_PLACEHOLDER_VALUE))) {
5208
- throw new import_common17.DidDocumentError("Invalid GenesisDocument service", import_common17.INVALID_DID_DOCUMENT, this);
6538
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument service", import_common19.INVALID_DID_DOCUMENT, this);
5209
6539
  }
5210
6540
  if (!_DidDocument.isValidVerificationRelationships(this)) {
5211
- throw new import_common17.DidDocumentError("Invalid GenesisDocument assertionMethod", import_common17.INVALID_DID_DOCUMENT, this);
6541
+ throw new import_common19.DidDocumentError("Invalid GenesisDocument assertionMethod", import_common19.INVALID_DID_DOCUMENT, this);
5212
6542
  }
5213
6543
  return true;
5214
6544
  }
@@ -5218,7 +6548,7 @@ var DidDocument = class _DidDocument {
5218
6548
  */
5219
6549
  toIntermediate() {
5220
6550
  if (this.id.includes("k1")) {
5221
- throw new import_common17.DidDocumentError("Cannot convert a key identifier to an intermediate document", import_common17.INVALID_DID_DOCUMENT, this);
6551
+ throw new import_common19.DidDocumentError("Cannot convert a key identifier to an intermediate document", import_common19.INVALID_DID_DOCUMENT, this);
5222
6552
  }
5223
6553
  return new GenesisDocument(this);
5224
6554
  }
@@ -5248,7 +6578,7 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
5248
6578
  * @returns {GenesisDocument} The GenesisDocument representation of the DidDocument.
5249
6579
  */
5250
6580
  static fromDidDocument(didDocument) {
5251
- const intermediateDocument = import_common17.JSONUtils.cloneReplace(didDocument, DID_REGEX, ID_PLACEHOLDER_VALUE);
6581
+ const intermediateDocument = import_common19.JSONUtils.cloneReplace(didDocument, DID_REGEX, ID_PLACEHOLDER_VALUE);
5252
6582
  return new _GenesisDocument(intermediateDocument);
5253
6583
  }
5254
6584
  /**
@@ -5267,7 +6597,7 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
5267
6597
  * @returns {GenesisDocument} A new GenesisDocument with the placeholder ID.
5268
6598
  */
5269
6599
  static fromPublicKey(publicKey, network) {
5270
- const pk = new import_keypair3.CompressedSecp256k1PublicKey(publicKey);
6600
+ const pk = new import_keypair5.CompressedSecp256k1PublicKey(publicKey);
5271
6601
  const id = ID_PLACEHOLDER_VALUE;
5272
6602
  const address = (0, import_btc_signer6.p2pkh)(pk.compressed, (0, import_bitcoin3.getNetwork)(network)).address;
5273
6603
  const services = [{
@@ -5305,20 +6635,18 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
5305
6635
  * @returns {Bytes} The genesis bytes.
5306
6636
  */
5307
6637
  static toGenesisBytes(genesisDocument) {
5308
- return (0, import_common17.hash)((0, import_common17.canonicalize)(genesisDocument));
6638
+ return (0, import_common19.hash)((0, import_common19.canonicalize)(genesisDocument));
5309
6639
  }
5310
6640
  };
5311
6641
 
5312
6642
  // src/core/updater.ts
5313
6643
  var Updater = class _Updater {
5314
- #phase = "Construct" /* Construct */;
6644
+ #state = { phase: "Construct" };
5315
6645
  #sourceDocument;
5316
6646
  #patches;
5317
6647
  #sourceVersionId;
5318
6648
  #verificationMethod;
5319
6649
  #beaconService;
5320
- #unsignedUpdate = null;
5321
- #signedUpdate = null;
5322
6650
  /**
5323
6651
  * @internal — Use {@link DidBtcr2.update} to create instances.
5324
6652
  */
@@ -5352,19 +6680,26 @@ var Updater = class _Updater {
5352
6680
  patch: patches,
5353
6681
  targetHash: "",
5354
6682
  targetVersionId: sourceVersionId + 1,
5355
- sourceHash: (0, import_common18.canonicalHash)(sourceDocument)
6683
+ sourceHash: (0, import_common20.canonicalHash)(sourceDocument)
5356
6684
  };
5357
- const targetDocument = import_common18.JSONPatch.apply(sourceDocument, patches);
6685
+ const targetDocument = import_common20.JSONPatch.apply(sourceDocument, patches);
6686
+ if (targetDocument.id !== sourceDocument.id) {
6687
+ throw new import_common20.UpdateError(
6688
+ `Patches must not change the DID document id (source "${sourceDocument.id}" \u2192 target "${targetDocument.id}").`,
6689
+ import_common20.INVALID_DID_UPDATE,
6690
+ { sourceId: sourceDocument.id, targetId: targetDocument.id }
6691
+ );
6692
+ }
5358
6693
  try {
5359
6694
  DidDocument.isValid(targetDocument);
5360
6695
  } catch (error) {
5361
- throw new import_common18.UpdateError(
6696
+ throw new import_common20.UpdateError(
5362
6697
  "Error validating targetDocument: " + (error instanceof Error ? error.message : String(error)),
5363
- import_common18.INVALID_DID_UPDATE,
6698
+ import_common20.INVALID_DID_UPDATE,
5364
6699
  targetDocument
5365
6700
  );
5366
6701
  }
5367
- unsignedUpdate.targetHash = (0, import_common18.canonicalHash)(targetDocument);
6702
+ unsignedUpdate.targetHash = (0, import_common20.canonicalHash)(targetDocument);
5368
6703
  return unsignedUpdate;
5369
6704
  }
5370
6705
  /**
@@ -5373,13 +6708,28 @@ var Updater = class _Updater {
5373
6708
  * @param {string} did The did-btcr2 identifier to derive the root capability from.
5374
6709
  * @param {UnsignedBTCR2Update} unsignedUpdate The unsigned update to sign.
5375
6710
  * @param {DidVerificationMethod} verificationMethod The verification method for signing.
5376
- * @param {KeyBytes} secretKey The secret key bytes.
6711
+ * @param {Signer} signer Signer that produces the BIP-340 Schnorr signature.
5377
6712
  * @returns {SignedBTCR2Update} The signed update with a Data Integrity proof.
5378
6713
  */
5379
- static sign(did, unsignedUpdate, verificationMethod, secretKey) {
6714
+ static sign(did, unsignedUpdate, verificationMethod, signer) {
6715
+ if (!did.startsWith("did:btcr2:")) {
6716
+ throw new import_common20.UpdateError(
6717
+ `Expected a did:btcr2 identifier for the root capability; got "${did}".`,
6718
+ import_common20.INVALID_DID_UPDATE,
6719
+ { did }
6720
+ );
6721
+ }
5380
6722
  const controller = verificationMethod.controller;
5381
- const id = verificationMethod.id.slice(verificationMethod.id.indexOf("#"));
5382
- const multikey = import_cryptosuite2.SchnorrMultikey.fromSecretKey(id, controller, secretKey);
6723
+ const hashIdx = verificationMethod.id.indexOf("#");
6724
+ if (hashIdx < 0) {
6725
+ throw new import_common20.UpdateError(
6726
+ `Verification method id must contain a fragment (e.g. "${verificationMethod.id}#initialKey"); got "${verificationMethod.id}".`,
6727
+ import_common20.INVALID_DID_UPDATE,
6728
+ { verificationMethodId: verificationMethod.id }
6729
+ );
6730
+ }
6731
+ const id = verificationMethod.id.slice(hashIdx);
6732
+ const multikey = import_cryptosuite2.SchnorrMultikey.fromSigner(id, controller, signer);
5383
6733
  const config = {
5384
6734
  "@context": [
5385
6735
  "https://w3id.org/security/v2",
@@ -5403,24 +6753,20 @@ var Updater = class _Updater {
5403
6753
  *
5404
6754
  * @param {BeaconService} beaconService The beacon service to broadcast through.
5405
6755
  * @param {SignedBTCR2Update} update The signed update to announce.
5406
- * @param {KeyBytes} secretKey The secret key for signing the Bitcoin transaction.
6756
+ * @param {Signer} signer Signer that produces the ECDSA signature for the Bitcoin transaction.
5407
6757
  * @param {BitcoinConnection} bitcoin The Bitcoin network connection.
5408
6758
  * @returns {Promise<SignedBTCR2Update>} The signed update that was broadcast.
5409
6759
  */
5410
- static async announce(beaconService, update, secretKey, bitcoin) {
6760
+ static async announce(beaconService, update, signer, bitcoin) {
5411
6761
  const beacon = BeaconFactory.establish(beaconService);
5412
- return beacon.broadcastSignal(update, secretKey, bitcoin);
6762
+ return beacon.broadcastSignal(update, signer, bitcoin);
5413
6763
  }
5414
- // ─── Private instance wrappers ─────────────────────────────────────────────
6764
+ // Private instance wrappers
5415
6765
  // Delegate to the public statics with bound instance fields for cleaner
5416
6766
  // advance/provide code.
5417
6767
  #construct() {
5418
6768
  return _Updater.construct(this.#sourceDocument, this.#patches, this.#sourceVersionId);
5419
6769
  }
5420
- #sign(secretKey) {
5421
- return _Updater.sign(this.#sourceDocument.id, this.#unsignedUpdate, this.#verificationMethod, secretKey);
5422
- }
5423
- // ─── State machine ─────────────────────────────────────────────────────────
5424
6770
  /**
5425
6771
  * Advance the state machine. Returns either:
5426
6772
  * - `{ status: 'action-required', needs }` — caller must provide data via {@link provide}
@@ -5428,30 +6774,30 @@ var Updater = class _Updater {
5428
6774
  */
5429
6775
  advance() {
5430
6776
  while (true) {
5431
- switch (this.#phase) {
6777
+ switch (this.#state.phase) {
5432
6778
  // Phase: Construct
5433
6779
  // Build the unsigned update from source doc + patches. Pure, synchronous.
5434
- case "Construct" /* Construct */: {
5435
- this.#unsignedUpdate = this.#construct();
5436
- this.#phase = "Sign" /* Sign */;
6780
+ case "Construct": {
6781
+ const unsignedUpdate = this.#construct();
6782
+ this.#state = { phase: "Sign", unsignedUpdate };
5437
6783
  continue;
5438
6784
  }
5439
6785
  // Phase: Sign
5440
6786
  // Emit NeedSigningKey — the caller supplies the secret key (or a KMS signature).
5441
- case "Sign" /* Sign */: {
6787
+ case "Sign": {
5442
6788
  return {
5443
6789
  status: "action-required",
5444
6790
  needs: [{
5445
6791
  kind: "NeedSigningKey",
5446
6792
  verificationMethodId: this.#verificationMethod.id,
5447
- unsignedUpdate: this.#unsignedUpdate
6793
+ unsignedUpdate: this.#state.unsignedUpdate
5448
6794
  }]
5449
6795
  };
5450
6796
  }
5451
6797
  // Phase: Fund
5452
6798
  // Emit NeedFunding with the beacon address. The caller checks UTXOs,
5453
6799
  // funds the address if needed, and provides to continue.
5454
- case "Fund" /* Fund */: {
6800
+ case "Fund": {
5455
6801
  const beaconAddress = this.#beaconService.serviceEndpoint.replace("bitcoin:", "");
5456
6802
  return {
5457
6803
  status: "action-required",
@@ -5465,21 +6811,21 @@ var Updater = class _Updater {
5465
6811
  // Phase: Broadcast
5466
6812
  // Emit NeedBroadcast with the signed update + beacon service. The caller performs
5467
6813
  // the actual on-chain announcement (or hands off to the aggregation protocol).
5468
- case "Broadcast" /* Broadcast */: {
6814
+ case "Broadcast": {
5469
6815
  return {
5470
6816
  status: "action-required",
5471
6817
  needs: [{
5472
6818
  kind: "NeedBroadcast",
5473
6819
  beaconService: this.#beaconService,
5474
- signedUpdate: this.#signedUpdate
6820
+ signedUpdate: this.#state.signedUpdate
5475
6821
  }]
5476
6822
  };
5477
6823
  }
5478
6824
  // Phase: Complete
5479
- case "Complete" /* Complete */: {
6825
+ case "Complete": {
5480
6826
  return {
5481
6827
  status: "complete",
5482
- result: { signedUpdate: this.#signedUpdate }
6828
+ result: { signedUpdate: this.#state.signedUpdate }
5483
6829
  };
5484
6830
  }
5485
6831
  }
@@ -5488,49 +6834,63 @@ var Updater = class _Updater {
5488
6834
  provide(need, data) {
5489
6835
  switch (need.kind) {
5490
6836
  case "NeedSigningKey": {
5491
- if (this.#phase !== "Sign" /* Sign */) {
5492
- throw new import_common18.UpdateError(
5493
- `Cannot provide NeedSigningKey: updater phase is ${this.#phase}, expected Sign.`,
5494
- import_common18.INVALID_DID_UPDATE,
5495
- { phase: this.#phase }
6837
+ if (this.#state.phase !== "Sign") {
6838
+ throw new import_common20.UpdateError(
6839
+ `Cannot provide NeedSigningKey: updater phase is ${this.#state.phase}, expected Sign.`,
6840
+ import_common20.INVALID_DID_UPDATE,
6841
+ { phase: this.#state.phase }
5496
6842
  );
5497
6843
  }
5498
6844
  if (!data) {
5499
- throw new import_common18.UpdateError(
5500
- "NeedSigningKey requires secret key bytes.",
5501
- import_common18.INVALID_DID_UPDATE
5502
- );
5503
- }
5504
- if (!this.#unsignedUpdate) {
5505
- throw new import_common18.UpdateError(
5506
- "Internal error: unsigned update missing in Sign phase.",
5507
- import_common18.INVALID_DID_UPDATE
6845
+ throw new import_common20.UpdateError(
6846
+ "NeedSigningKey requires a Signer.",
6847
+ import_common20.INVALID_DID_UPDATE
5508
6848
  );
5509
6849
  }
5510
- this.#signedUpdate = this.#sign(data);
5511
- this.#phase = "Fund" /* Fund */;
6850
+ const unsignedUpdate = this.#state.unsignedUpdate;
6851
+ const signedUpdate = _Updater.sign(
6852
+ this.#sourceDocument.id,
6853
+ unsignedUpdate,
6854
+ this.#verificationMethod,
6855
+ data
6856
+ );
6857
+ this.#state = { phase: "Fund", unsignedUpdate, signedUpdate };
5512
6858
  break;
5513
6859
  }
5514
6860
  case "NeedFunding": {
5515
- if (this.#phase !== "Fund" /* Fund */) {
5516
- throw new import_common18.UpdateError(
5517
- `Cannot provide NeedFunding: updater phase is ${this.#phase}, expected Fund.`,
5518
- import_common18.INVALID_DID_UPDATE,
5519
- { phase: this.#phase }
6861
+ if (this.#state.phase !== "Fund") {
6862
+ throw new import_common20.UpdateError(
6863
+ `Cannot provide NeedFunding: updater phase is ${this.#state.phase}, expected Fund.`,
6864
+ import_common20.INVALID_DID_UPDATE,
6865
+ { phase: this.#state.phase }
5520
6866
  );
5521
6867
  }
5522
- this.#phase = "Broadcast" /* Broadcast */;
6868
+ if (data !== void 0) {
6869
+ const proof = data;
6870
+ if (typeof proof.utxoCount !== "number" || !Number.isFinite(proof.utxoCount) || proof.utxoCount < 1) {
6871
+ throw new import_common20.UpdateError(
6872
+ `NeedFunding proof must have utxoCount >= 1; got ${String(proof.utxoCount)}.`,
6873
+ import_common20.INVALID_DID_UPDATE,
6874
+ { utxoCount: proof.utxoCount }
6875
+ );
6876
+ }
6877
+ }
6878
+ this.#state = {
6879
+ phase: "Broadcast",
6880
+ unsignedUpdate: this.#state.unsignedUpdate,
6881
+ signedUpdate: this.#state.signedUpdate
6882
+ };
5523
6883
  break;
5524
6884
  }
5525
6885
  case "NeedBroadcast": {
5526
- if (this.#phase !== "Broadcast" /* Broadcast */) {
5527
- throw new import_common18.UpdateError(
5528
- `Cannot provide NeedBroadcast: updater phase is ${this.#phase}, expected Broadcast.`,
5529
- import_common18.INVALID_DID_UPDATE,
5530
- { phase: this.#phase }
6886
+ if (this.#state.phase !== "Broadcast") {
6887
+ throw new import_common20.UpdateError(
6888
+ `Cannot provide NeedBroadcast: updater phase is ${this.#state.phase}, expected Broadcast.`,
6889
+ import_common20.INVALID_DID_UPDATE,
6890
+ { phase: this.#state.phase }
5531
6891
  );
5532
6892
  }
5533
- this.#phase = "Complete" /* Complete */;
6893
+ this.#state = { phase: "Complete", signedUpdate: this.#state.signedUpdate };
5534
6894
  break;
5535
6895
  }
5536
6896
  }
@@ -5562,9 +6922,9 @@ var DidBtcr2 = class {
5562
6922
  static create(genesisBytes, options) {
5563
6923
  const { idType, version = 1, network = "bitcoin" } = options || {};
5564
6924
  if (!idType) {
5565
- throw new import_common19.MethodError(
6925
+ throw new import_common21.MethodError(
5566
6926
  "idType is required for creating a did:btcr2 identifier",
5567
- import_common19.INVALID_DID_DOCUMENT,
6927
+ import_common21.INVALID_DID_DOCUMENT,
5568
6928
  options
5569
6929
  );
5570
6930
  }
@@ -5594,7 +6954,7 @@ var DidBtcr2 = class {
5594
6954
  static resolve(did, resolutionOptions = {}) {
5595
6955
  const didComponents = Identifier.decode(did);
5596
6956
  const sidecarData = Resolver.sidecarData(resolutionOptions.sidecar);
5597
- const currentDocument = didComponents.hrp === import_common19.IdentifierHrp.k ? Resolver.deterministic(didComponents) : null;
6957
+ const currentDocument = didComponents.hrp === import_common21.IdentifierHrp.k ? Resolver.deterministic(didComponents) : null;
5598
6958
  return new Resolver(didComponents, sidecarData, currentDocument, {
5599
6959
  versionId: resolutionOptions.versionId,
5600
6960
  versionTime: resolutionOptions.versionTime,
@@ -5632,39 +6992,39 @@ var DidBtcr2 = class {
5632
6992
  beaconId
5633
6993
  }) {
5634
6994
  if (!sourceDocument.capabilityInvocation?.some((vr) => vr === verificationMethodId)) {
5635
- throw new import_common19.UpdateError(
6995
+ throw new import_common21.UpdateError(
5636
6996
  "Invalid verificationMethodId: not authorized for capabilityInvocation",
5637
- import_common19.INVALID_DID_DOCUMENT,
6997
+ import_common21.INVALID_DID_DOCUMENT,
5638
6998
  sourceDocument
5639
6999
  );
5640
7000
  }
5641
7001
  const verificationMethod = this.getSigningMethod(sourceDocument, verificationMethodId);
5642
7002
  if (!verificationMethod) {
5643
- throw new import_common19.UpdateError(
7003
+ throw new import_common21.UpdateError(
5644
7004
  "Invalid verificationMethod: not found in source document",
5645
- import_common19.INVALID_DID_DOCUMENT,
7005
+ import_common21.INVALID_DID_DOCUMENT,
5646
7006
  { sourceDocument, verificationMethodId }
5647
7007
  );
5648
7008
  }
5649
7009
  if (verificationMethod.type !== "Multikey") {
5650
- throw new import_common19.UpdateError(
7010
+ throw new import_common21.UpdateError(
5651
7011
  'Invalid verificationMethod: verificationMethod.type must be "Multikey"',
5652
- import_common19.INVALID_DID_DOCUMENT,
7012
+ import_common21.INVALID_DID_DOCUMENT,
5653
7013
  verificationMethod
5654
7014
  );
5655
7015
  }
5656
7016
  if (verificationMethod.publicKeyMultibase?.slice(0, 4) !== "zQ3s") {
5657
- throw new import_common19.UpdateError(
7017
+ throw new import_common21.UpdateError(
5658
7018
  'Invalid verificationMethodId: publicKeyMultibase prefix must start with "zQ3s"',
5659
- import_common19.INVALID_DID_DOCUMENT,
7019
+ import_common21.INVALID_DID_DOCUMENT,
5660
7020
  verificationMethod
5661
7021
  );
5662
7022
  }
5663
7023
  const beaconService = sourceDocument.service.filter((service) => service.id === beaconId).filter((service) => !!service).shift();
5664
7024
  if (!beaconService) {
5665
- throw new import_common19.UpdateError(
7025
+ throw new import_common21.UpdateError(
5666
7026
  "No beacon service found for provided beaconId",
5667
- import_common19.INVALID_DID_UPDATE,
7027
+ import_common21.INVALID_DID_UPDATE,
5668
7028
  { sourceDocument, beaconId }
5669
7029
  );
5670
7030
  }
@@ -5690,7 +7050,7 @@ var DidBtcr2 = class {
5690
7050
  methodId ??= "#initialKey";
5691
7051
  const parsedDid = import_dids2.Did.parse(didDocument.id);
5692
7052
  if (parsedDid && parsedDid.method !== this.methodName) {
5693
- throw new import_common19.MethodError(`Method not supported: ${parsedDid.method}`, import_common19.METHOD_NOT_SUPPORTED, { identifier: didDocument.id });
7053
+ throw new import_common21.MethodError(`Method not supported: ${parsedDid.method}`, import_common21.METHOD_NOT_SUPPORTED, { identifier: didDocument.id });
5694
7054
  }
5695
7055
  const verificationMethod = didDocument.verificationMethod?.find(
5696
7056
  (vm) => Appendix.extractDidFragment(vm.id) === (Appendix.extractDidFragment(methodId) ?? Appendix.extractDidFragment(didDocument.assertionMethod?.[0]))
@@ -5706,7 +7066,7 @@ var DidBtcr2 = class {
5706
7066
  };
5707
7067
 
5708
7068
  // src/core/resolver.ts
5709
- var import_utils10 = require("@noble/curves/utils.js");
7069
+ var import_utils12 = require("@noble/curves/utils.js");
5710
7070
  var Resolver = class _Resolver {
5711
7071
  // --- Immutable inputs ---
5712
7072
  #didComponents;
@@ -5746,7 +7106,7 @@ var Resolver = class _Resolver {
5746
7106
  static deterministic(didComponents) {
5747
7107
  const genesisBytes = didComponents.genesisBytes;
5748
7108
  const did = Identifier.encode(genesisBytes, didComponents);
5749
- const { multibase } = new import_keypair4.CompressedSecp256k1PublicKey(genesisBytes);
7109
+ const { multibase } = new import_keypair6.CompressedSecp256k1PublicKey(genesisBytes);
5750
7110
  const service = BeaconUtils.generateBeaconServices({
5751
7111
  id: did,
5752
7112
  publicKey: genesisBytes,
@@ -5772,14 +7132,14 @@ var Resolver = class _Resolver {
5772
7132
  * @throws {ResolveError} InvalidDidDocument if not conformant to DID Core v1.1
5773
7133
  */
5774
7134
  static external(didComponents, genesisDocument) {
5775
- const genesisDocumentHash = (0, import_common20.canonicalHashBytes)(genesisDocument);
5776
- if (!(0, import_utils10.equalBytes)(didComponents.genesisBytes, genesisDocumentHash)) {
5777
- throw new import_common20.ResolveError(
7135
+ const genesisDocumentHash = (0, import_common22.canonicalHashBytes)(genesisDocument);
7136
+ if (!(0, import_utils12.equalBytes)(didComponents.genesisBytes, genesisDocumentHash)) {
7137
+ throw new import_common22.ResolveError(
5778
7138
  `Initial document mismatch: genesisBytes !== genesisDocumentHash`,
5779
- import_common20.INVALID_DID_DOCUMENT,
7139
+ import_common22.INVALID_DID_DOCUMENT,
5780
7140
  {
5781
- genesisBytes: (0, import_common20.encode)(didComponents.genesisBytes, "hex"),
5782
- genesisDocumentHash: (0, import_common20.encode)(genesisDocumentHash, "hex")
7141
+ genesisBytes: (0, import_common22.encode)(didComponents.genesisBytes, "hex"),
7142
+ genesisDocumentHash: (0, import_common22.encode)(genesisDocumentHash, "hex")
5783
7143
  }
5784
7144
  );
5785
7145
  }
@@ -5798,12 +7158,12 @@ var Resolver = class _Resolver {
5798
7158
  const updateMap = /* @__PURE__ */ new Map();
5799
7159
  if (sidecar.updates?.length)
5800
7160
  for (const update of sidecar.updates) {
5801
- updateMap.set((0, import_common20.canonicalHash)(update, { encoding: "hex" }), update);
7161
+ updateMap.set((0, import_common22.canonicalHash)(update, { encoding: "hex" }), update);
5802
7162
  }
5803
7163
  const casMap = /* @__PURE__ */ new Map();
5804
7164
  if (sidecar.casUpdates?.length)
5805
7165
  for (const update of sidecar.casUpdates) {
5806
- casMap.set((0, import_common20.canonicalHash)(update, { encoding: "hex" }), update);
7166
+ casMap.set((0, import_common22.canonicalHash)(update, { encoding: "hex" }), update);
5807
7167
  }
5808
7168
  const smtMap = /* @__PURE__ */ new Map();
5809
7169
  if (sidecar.smtProofs?.length)
@@ -5836,12 +7196,12 @@ var Resolver = class _Resolver {
5836
7196
  }
5837
7197
  };
5838
7198
  for (const [update, block] of updates) {
5839
- const currentDocumentHash = (0, import_common20.canonicalHashBytes)(response.didDocument);
5840
- const blocktime = import_common20.DateUtils.blocktimeToTimestamp(block.time);
5841
- response.metadata.updated = import_common20.DateUtils.toISOStringNonFractional(blocktime);
7199
+ const currentDocumentHash = (0, import_common22.canonicalHashBytes)(response.didDocument);
7200
+ const blocktime = import_common22.DateUtils.blocktimeToTimestamp(block.time);
7201
+ response.metadata.updated = import_common22.DateUtils.toISOStringNonFractional(blocktime);
5842
7202
  response.metadata.confirmations = block.confirmations;
5843
7203
  if (versionTime) {
5844
- if (blocktime > import_common20.DateUtils.dateStringToTimestamp(versionTime)) {
7204
+ if (blocktime > import_common22.DateUtils.dateStringToTimestamp(versionTime)) {
5845
7205
  return response;
5846
7206
  }
5847
7207
  }
@@ -5849,22 +7209,22 @@ var Resolver = class _Resolver {
5849
7209
  updateHashHistory.push(currentDocumentHash);
5850
7210
  this.confirmDuplicate(update, updateHashHistory);
5851
7211
  } else if (update.targetVersionId === currentVersionId + 1) {
5852
- const sourceHashBytes = (0, import_common20.decode)(update.sourceHash, "base64urlnopad");
5853
- if (!(0, import_utils10.equalBytes)(sourceHashBytes, currentDocumentHash)) {
5854
- throw new import_common20.ResolveError(
7212
+ const sourceHashBytes = (0, import_common22.decode)(update.sourceHash, "base64urlnopad");
7213
+ if (!(0, import_utils12.equalBytes)(sourceHashBytes, currentDocumentHash)) {
7214
+ throw new import_common22.ResolveError(
5855
7215
  `Hash mismatch: update.sourceHash !== currentDocumentHash`,
5856
- import_common20.INVALID_DID_UPDATE,
7216
+ import_common22.INVALID_DID_UPDATE,
5857
7217
  {
5858
7218
  sourceHash: update.sourceHash,
5859
- currentDocumentHash: (0, import_common20.encode)(currentDocumentHash, "hex")
7219
+ currentDocumentHash: (0, import_common22.encode)(currentDocumentHash, "hex")
5860
7220
  }
5861
7221
  );
5862
7222
  }
5863
7223
  response.didDocument = this.applyUpdate(response.didDocument, update);
5864
- const unsignedUpdate = import_common20.JSONUtils.deleteKeys(update, ["proof"]);
5865
- updateHashHistory.push((0, import_common20.canonicalHashBytes)(unsignedUpdate));
7224
+ const unsignedUpdate = import_common22.JSONUtils.deleteKeys(update, ["proof"]);
7225
+ updateHashHistory.push((0, import_common22.canonicalHashBytes)(unsignedUpdate));
5866
7226
  } else if (update.targetVersionId > currentVersionId + 1) {
5867
- throw new import_common20.ResolveError(
7227
+ throw new import_common22.ResolveError(
5868
7228
  `Version Id Mismatch: targetVersionId cannot be > currentVersionId + 1`,
5869
7229
  "LATE_PUBLISHING_ERROR",
5870
7230
  {
@@ -5895,15 +7255,15 @@ var Resolver = class _Resolver {
5895
7255
  */
5896
7256
  static confirmDuplicate(update, updateHashHistory) {
5897
7257
  const { proof: _, ...unsignedUpdate } = update;
5898
- const unsignedUpdateHash = (0, import_common20.canonicalHashBytes)(unsignedUpdate);
7258
+ const unsignedUpdateHash = (0, import_common22.canonicalHashBytes)(unsignedUpdate);
5899
7259
  const historicalUpdateHash = updateHashHistory[update.targetVersionId - 2];
5900
- if (!(0, import_utils10.equalBytes)(historicalUpdateHash, unsignedUpdateHash)) {
5901
- throw new import_common20.ResolveError(
7260
+ if (!(0, import_utils12.equalBytes)(historicalUpdateHash, unsignedUpdateHash)) {
7261
+ throw new import_common22.ResolveError(
5902
7262
  `Invalid duplicate: unsigned update hash does not match historical hash`,
5903
- import_common20.LATE_PUBLISHING_ERROR,
7263
+ import_common22.LATE_PUBLISHING_ERROR,
5904
7264
  {
5905
- unsignedUpdateHash: (0, import_common20.encode)(unsignedUpdateHash, "hex"),
5906
- historicalHash: (0, import_common20.encode)(historicalUpdateHash, "hex")
7265
+ unsignedUpdateHash: (0, import_common22.encode)(unsignedUpdateHash, "hex"),
7266
+ historicalHash: (0, import_common22.encode)(historicalUpdateHash, "hex")
5907
7267
  }
5908
7268
  );
5909
7269
  }
@@ -5918,42 +7278,42 @@ var Resolver = class _Resolver {
5918
7278
  static applyUpdate(currentDocument, update) {
5919
7279
  const capabilityId = update.proof?.capability;
5920
7280
  if (!capabilityId) {
5921
- throw new import_common20.ResolveError("No root capability found in update", import_common20.INVALID_DID_UPDATE, update);
7281
+ throw new import_common22.ResolveError("No root capability found in update", import_common22.INVALID_DID_UPDATE, update);
5922
7282
  }
5923
7283
  const rootCapability = Appendix.dereferenceZcapId(capabilityId);
5924
7284
  const { invocationTarget, controller: rootController } = rootCapability;
5925
7285
  if (![invocationTarget, rootController].every((id) => id === currentDocument.id)) {
5926
- throw new import_common20.ResolveError(
7286
+ throw new import_common22.ResolveError(
5927
7287
  "Invalid root capability",
5928
- import_common20.INVALID_DID_UPDATE,
7288
+ import_common22.INVALID_DID_UPDATE,
5929
7289
  { rootCapability, currentDocument }
5930
7290
  );
5931
7291
  }
5932
7292
  const verificationMethodId = update.proof?.verificationMethod;
5933
7293
  if (!verificationMethodId) {
5934
- throw new import_common20.ResolveError("No verificationMethod found in update", import_common20.INVALID_DID_UPDATE, update);
7294
+ throw new import_common22.ResolveError("No verificationMethod found in update", import_common22.INVALID_DID_UPDATE, update);
5935
7295
  }
5936
7296
  const vm = DidBtcr2.getSigningMethod(currentDocument, verificationMethodId);
5937
7297
  const multikey = import_cryptosuite3.SchnorrMultikey.fromVerificationMethod(vm);
5938
7298
  const cryptosuite = new import_cryptosuite3.BIP340Cryptosuite(multikey);
5939
- const canonicalUpdate = (0, import_common20.canonicalize)(update);
7299
+ const canonicalUpdate = (0, import_common22.canonicalize)(update);
5940
7300
  const diProof = new import_cryptosuite3.BIP340DataIntegrityProof(cryptosuite);
5941
7301
  const verificationResult = diProof.verifyProof(canonicalUpdate, "capabilityInvocation");
5942
7302
  if (!verificationResult.verified) {
5943
- throw new import_common20.ResolveError(
7303
+ throw new import_common22.ResolveError(
5944
7304
  "Invalid update: proof not verified",
5945
- import_common20.INVALID_DID_UPDATE,
7305
+ import_common22.INVALID_DID_UPDATE,
5946
7306
  verificationResult
5947
7307
  );
5948
7308
  }
5949
- const updatedDocument = import_common20.JSONPatch.apply(currentDocument, update.patch);
7309
+ const updatedDocument = import_common22.JSONPatch.apply(currentDocument, update.patch);
5950
7310
  DidDocument.validate(updatedDocument);
5951
- const currentDocumentHash = (0, import_common20.canonicalHashBytes)(updatedDocument);
5952
- const updateTargetHash = (0, import_common20.decode)(update.targetHash);
5953
- if (!(0, import_utils10.equalBytes)(updateTargetHash, currentDocumentHash)) {
5954
- throw new import_common20.ResolveError(
7311
+ const currentDocumentHash = (0, import_common22.canonicalHashBytes)(updatedDocument);
7312
+ const updateTargetHash = (0, import_common22.decode)(update.targetHash);
7313
+ if (!(0, import_utils12.equalBytes)(updateTargetHash, currentDocumentHash)) {
7314
+ throw new import_common22.ResolveError(
5955
7315
  `Invalid update: update.targetHash !== currentDocumentHash`,
5956
- import_common20.INVALID_DID_UPDATE,
7316
+ import_common22.INVALID_DID_UPDATE,
5957
7317
  { updateTargetHash, currentDocumentHash }
5958
7318
  );
5959
7319
  }
@@ -5981,7 +7341,7 @@ var Resolver = class _Resolver {
5981
7341
  this.#phase = "BeaconDiscovery" /* BeaconDiscovery */;
5982
7342
  continue;
5983
7343
  }
5984
- const genesisHash = (0, import_common20.encode)(this.#didComponents.genesisBytes, "hex");
7344
+ const genesisHash = (0, import_common22.encode)(this.#didComponents.genesisBytes, "hex");
5985
7345
  return {
5986
7346
  status: "action-required",
5987
7347
  needs: [{ kind: "NeedGenesisDocument", genesisHash }]
@@ -6085,21 +7445,21 @@ var Resolver = class _Resolver {
6085
7445
  }
6086
7446
  case "NeedCASAnnouncement": {
6087
7447
  const announcement = data;
6088
- this.#sidecarData.casMap.set((0, import_common20.canonicalHash)(announcement, { encoding: "hex" }), announcement);
7448
+ this.#sidecarData.casMap.set((0, import_common22.canonicalHash)(announcement, { encoding: "hex" }), announcement);
6089
7449
  break;
6090
7450
  }
6091
7451
  case "NeedSignedUpdate": {
6092
7452
  const update = data;
6093
- this.#sidecarData.updateMap.set((0, import_common20.canonicalHash)(update, { encoding: "hex" }), update);
7453
+ this.#sidecarData.updateMap.set((0, import_common22.canonicalHash)(update, { encoding: "hex" }), update);
6094
7454
  break;
6095
7455
  }
6096
7456
  case "NeedSMTProof": {
6097
7457
  const smtNeed = need;
6098
7458
  const proof = data;
6099
7459
  if (proof.id !== smtNeed.smtRootHash) {
6100
- throw new import_common20.ResolveError(
7460
+ throw new import_common22.ResolveError(
6101
7461
  `SMT proof root hash mismatch: expected ${smtNeed.smtRootHash}, got ${proof.id}`,
6102
- import_common20.INVALID_DID_UPDATE,
7462
+ import_common22.INVALID_DID_UPDATE,
6103
7463
  { expected: smtNeed.smtRootHash, actual: proof.id }
6104
7464
  );
6105
7465
  }
@@ -6111,12 +7471,12 @@ var Resolver = class _Resolver {
6111
7471
  };
6112
7472
 
6113
7473
  // src/utils/did-document-builder.ts
6114
- var import_common21 = require("@did-btcr2/common");
7474
+ var import_common23 = require("@did-btcr2/common");
6115
7475
  var DidDocumentBuilder = class {
6116
7476
  document = {};
6117
7477
  constructor(initialDocument) {
6118
7478
  if (!initialDocument.id) {
6119
- throw new import_common21.DidDocumentError('Missing required "id" property', import_common21.INVALID_DID_DOCUMENT, initialDocument);
7479
+ throw new import_common23.DidDocumentError('Missing required "id" property', import_common23.INVALID_DID_DOCUMENT, initialDocument);
6120
7480
  }
6121
7481
  this.document.id = initialDocument.id;
6122
7482
  this.document.verificationMethod = initialDocument.verificationMethod ?? [];
@@ -6198,7 +7558,9 @@ var DidDocumentBuilder = class {
6198
7558
  CONSOLE_LOGGER,
6199
7559
  DEFAULT_ADVERT_REPEAT_INTERVAL_MS,
6200
7560
  DEFAULT_BROADCAST_LOOKBACK_MS,
7561
+ DEFAULT_CLOCK_SKEW_SEC,
6201
7562
  DEFAULT_MAX_UPDATE_SIZE_BYTES,
7563
+ DEFAULT_NONCE_LEN_BYTES,
6202
7564
  DEFAULT_NOSTR_RELAYS,
6203
7565
  DID_REGEX,
6204
7566
  DISTRIBUTE_AGGREGATED_DATA,
@@ -6209,16 +7571,31 @@ var DidDocumentBuilder = class {
6209
7571
  DidVerificationMethod,
6210
7572
  Document,
6211
7573
  GenesisDocument,
7574
+ HTTP_ENVELOPE_VERSION,
7575
+ HTTP_ROUTE,
7576
+ HttpClientTransport,
7577
+ HttpServerTransport,
7578
+ HttpTransportError,
6212
7579
  ID_PLACEHOLDER_VALUE,
6213
7580
  Identifier,
7581
+ InMemoryRateLimitStore,
7582
+ InboxBuffer,
6214
7583
  NONCE_CONTRIBUTION,
7584
+ NonceCache,
6215
7585
  NostrTransport,
7586
+ P2PKH_BEACON_TX_VSIZE,
7587
+ P2TR_BEACON_TX_VSIZE,
7588
+ P2WPKH_BEACON_TX_VSIZE,
6216
7589
  ParticipantCohortPhase,
7590
+ REQUEST_AUTH_SCHEME,
7591
+ RateLimiter,
6217
7592
  Resolver,
6218
7593
  SIGNATURE_AUTHORIZATION,
6219
7594
  SILENT_LOGGER,
7595
+ SINGLETON_BEACON_TX_VSIZE,
6220
7596
  SMTBeacon,
6221
7597
  SMTBeaconError,
7598
+ SSE_EVENT,
6222
7599
  SUBMIT_UPDATE,
6223
7600
  ServiceCohortPhase,
6224
7601
  SigningSessionError,
@@ -6233,6 +7610,7 @@ var DidDocumentBuilder = class {
6233
7610
  Updater,
6234
7611
  VALIDATION_ACK,
6235
7612
  buildAggregationBeaconTx,
7613
+ buildRequestAuth,
6236
7614
  createAggregatedNonceMessage,
6237
7615
  createAuthorizationRequestMessage,
6238
7616
  createCohortAdvertMessage,
@@ -6244,6 +7622,11 @@ var DidDocumentBuilder = class {
6244
7622
  createSignatureAuthorizationMessage,
6245
7623
  createSubmitUpdateMessage,
6246
7624
  createValidationAckMessage,
7625
+ defaultReconnectBackoff,
7626
+ deriveSingletonAddress,
7627
+ detectSingletonScriptKind,
7628
+ formatSseComment,
7629
+ formatSseEvent,
6247
7630
  getBeaconStrategy,
6248
7631
  isAggregatedNonceMessage,
6249
7632
  isAggregationMessageType,
@@ -6260,6 +7643,13 @@ var DidDocumentBuilder = class {
6260
7643
  isSubmitUpdateMessage,
6261
7644
  isUpdateMessageType,
6262
7645
  isValidationAckMessage,
7646
+ normalizeForWire,
6263
7647
  opReturnScript,
6264
- registerBeaconStrategy
7648
+ parseRequestAuth,
7649
+ parseSseStream,
7650
+ registerBeaconStrategy,
7651
+ reviveFromWire,
7652
+ signEnvelope,
7653
+ verifyEnvelope,
7654
+ verifyRequestAuth
6265
7655
  });