@did-btcr2/method 0.37.0 → 0.39.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 (245) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser.js +47868 -34771
  3. package/dist/browser.mjs +47867 -34770
  4. package/dist/cjs/index.js +462 -6060
  5. package/dist/esm/core/beacon/beacon.js +101 -10
  6. package/dist/esm/core/beacon/beacon.js.map +1 -1
  7. package/dist/esm/core/beacon/fee-estimator.js +12 -27
  8. package/dist/esm/core/beacon/fee-estimator.js.map +1 -1
  9. package/dist/esm/core/did-sender-resolver.js +26 -0
  10. package/dist/esm/core/did-sender-resolver.js.map +1 -0
  11. package/dist/esm/index.js +3 -17
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/types/core/beacon/beacon.d.ts +59 -3
  14. package/dist/types/core/beacon/beacon.d.ts.map +1 -1
  15. package/dist/types/core/beacon/fee-estimator.d.ts +10 -37
  16. package/dist/types/core/beacon/fee-estimator.d.ts.map +1 -1
  17. package/dist/types/core/did-sender-resolver.d.ts +14 -0
  18. package/dist/types/core/did-sender-resolver.d.ts.map +1 -0
  19. package/dist/types/index.d.ts +2 -16
  20. package/dist/types/index.d.ts.map +1 -1
  21. package/package.json +6 -5
  22. package/src/core/beacon/beacon.ts +131 -12
  23. package/src/core/beacon/fee-estimator.ts +15 -49
  24. package/src/core/did-sender-resolver.ts +25 -0
  25. package/src/index.ts +3 -17
  26. package/dist/esm/core/aggregation/beacon-strategy.js +0 -76
  27. package/dist/esm/core/aggregation/beacon-strategy.js.map +0 -1
  28. package/dist/esm/core/aggregation/cohort.js +0 -316
  29. package/dist/esm/core/aggregation/cohort.js.map +0 -1
  30. package/dist/esm/core/aggregation/conditions.js +0 -102
  31. package/dist/esm/core/aggregation/conditions.js.map +0 -1
  32. package/dist/esm/core/aggregation/errors.js +0 -22
  33. package/dist/esm/core/aggregation/errors.js.map +0 -1
  34. package/dist/esm/core/aggregation/fallback-spend.js +0 -132
  35. package/dist/esm/core/aggregation/fallback-spend.js.map +0 -1
  36. package/dist/esm/core/aggregation/logger.js +0 -15
  37. package/dist/esm/core/aggregation/logger.js.map +0 -1
  38. package/dist/esm/core/aggregation/messages/base.js +0 -36
  39. package/dist/esm/core/aggregation/messages/base.js.map +0 -1
  40. package/dist/esm/core/aggregation/messages/bodies.js +0 -149
  41. package/dist/esm/core/aggregation/messages/bodies.js.map +0 -1
  42. package/dist/esm/core/aggregation/messages/constants.js +0 -37
  43. package/dist/esm/core/aggregation/messages/constants.js.map +0 -1
  44. package/dist/esm/core/aggregation/messages/factories.js +0 -139
  45. package/dist/esm/core/aggregation/messages/factories.js.map +0 -1
  46. package/dist/esm/core/aggregation/messages/guards.js +0 -40
  47. package/dist/esm/core/aggregation/messages/guards.js.map +0 -1
  48. package/dist/esm/core/aggregation/messages/index.js +0 -6
  49. package/dist/esm/core/aggregation/messages/index.js.map +0 -1
  50. package/dist/esm/core/aggregation/participant.js +0 -588
  51. package/dist/esm/core/aggregation/participant.js.map +0 -1
  52. package/dist/esm/core/aggregation/phases.js +0 -45
  53. package/dist/esm/core/aggregation/phases.js.map +0 -1
  54. package/dist/esm/core/aggregation/recovery-policy.js +0 -156
  55. package/dist/esm/core/aggregation/recovery-policy.js.map +0 -1
  56. package/dist/esm/core/aggregation/recovery-spend.js +0 -106
  57. package/dist/esm/core/aggregation/recovery-spend.js.map +0 -1
  58. package/dist/esm/core/aggregation/runner/aggregation-runner.js +0 -74
  59. package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +0 -1
  60. package/dist/esm/core/aggregation/runner/events.js +0 -2
  61. package/dist/esm/core/aggregation/runner/events.js.map +0 -1
  62. package/dist/esm/core/aggregation/runner/index.js +0 -6
  63. package/dist/esm/core/aggregation/runner/index.js.map +0 -1
  64. package/dist/esm/core/aggregation/runner/participant-runner.js +0 -407
  65. package/dist/esm/core/aggregation/runner/participant-runner.js.map +0 -1
  66. package/dist/esm/core/aggregation/runner/service-runner.js +0 -694
  67. package/dist/esm/core/aggregation/runner/service-runner.js.map +0 -1
  68. package/dist/esm/core/aggregation/runner/typed-emitter.js +0 -80
  69. package/dist/esm/core/aggregation/runner/typed-emitter.js.map +0 -1
  70. package/dist/esm/core/aggregation/service.js +0 -774
  71. package/dist/esm/core/aggregation/service.js.map +0 -1
  72. package/dist/esm/core/aggregation/signer.js +0 -33
  73. package/dist/esm/core/aggregation/signer.js.map +0 -1
  74. package/dist/esm/core/aggregation/signing-session.js +0 -195
  75. package/dist/esm/core/aggregation/signing-session.js.map +0 -1
  76. package/dist/esm/core/aggregation/transport/didcomm.js +0 -41
  77. package/dist/esm/core/aggregation/transport/didcomm.js.map +0 -1
  78. package/dist/esm/core/aggregation/transport/error.js +0 -12
  79. package/dist/esm/core/aggregation/transport/error.js.map +0 -1
  80. package/dist/esm/core/aggregation/transport/factory.js +0 -29
  81. package/dist/esm/core/aggregation/transport/factory.js.map +0 -1
  82. package/dist/esm/core/aggregation/transport/http/client.js +0 -350
  83. package/dist/esm/core/aggregation/transport/http/client.js.map +0 -1
  84. package/dist/esm/core/aggregation/transport/http/envelope.js +0 -126
  85. package/dist/esm/core/aggregation/transport/http/envelope.js.map +0 -1
  86. package/dist/esm/core/aggregation/transport/http/errors.js +0 -11
  87. package/dist/esm/core/aggregation/transport/http/errors.js.map +0 -1
  88. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +0 -45
  89. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js.map +0 -1
  90. package/dist/esm/core/aggregation/transport/http/index.js +0 -12
  91. package/dist/esm/core/aggregation/transport/http/index.js.map +0 -1
  92. package/dist/esm/core/aggregation/transport/http/nonce-cache.js +0 -38
  93. package/dist/esm/core/aggregation/transport/http/nonce-cache.js.map +0 -1
  94. package/dist/esm/core/aggregation/transport/http/protocol.js +0 -28
  95. package/dist/esm/core/aggregation/transport/http/protocol.js.map +0 -1
  96. package/dist/esm/core/aggregation/transport/http/rate-limiter.js +0 -45
  97. package/dist/esm/core/aggregation/transport/http/rate-limiter.js.map +0 -1
  98. package/dist/esm/core/aggregation/transport/http/request-auth.js +0 -100
  99. package/dist/esm/core/aggregation/transport/http/request-auth.js.map +0 -1
  100. package/dist/esm/core/aggregation/transport/http/server.js +0 -481
  101. package/dist/esm/core/aggregation/transport/http/server.js.map +0 -1
  102. package/dist/esm/core/aggregation/transport/http/sse-stream.js +0 -110
  103. package/dist/esm/core/aggregation/transport/http/sse-stream.js.map +0 -1
  104. package/dist/esm/core/aggregation/transport/http/sse-writer.js +0 -25
  105. package/dist/esm/core/aggregation/transport/http/sse-writer.js.map +0 -1
  106. package/dist/esm/core/aggregation/transport/in-memory.js +0 -149
  107. package/dist/esm/core/aggregation/transport/in-memory.js.map +0 -1
  108. package/dist/esm/core/aggregation/transport/index.js +0 -8
  109. package/dist/esm/core/aggregation/transport/index.js.map +0 -1
  110. package/dist/esm/core/aggregation/transport/nostr.js +0 -491
  111. package/dist/esm/core/aggregation/transport/nostr.js.map +0 -1
  112. package/dist/esm/core/aggregation/transport/transport.js +0 -2
  113. package/dist/esm/core/aggregation/transport/transport.js.map +0 -1
  114. package/dist/types/core/aggregation/beacon-strategy.d.ts +0 -56
  115. package/dist/types/core/aggregation/beacon-strategy.d.ts.map +0 -1
  116. package/dist/types/core/aggregation/cohort.d.ts +0 -182
  117. package/dist/types/core/aggregation/cohort.d.ts.map +0 -1
  118. package/dist/types/core/aggregation/conditions.d.ts +0 -86
  119. package/dist/types/core/aggregation/conditions.d.ts.map +0 -1
  120. package/dist/types/core/aggregation/errors.d.ts +0 -14
  121. package/dist/types/core/aggregation/errors.d.ts.map +0 -1
  122. package/dist/types/core/aggregation/fallback-spend.d.ts +0 -94
  123. package/dist/types/core/aggregation/fallback-spend.d.ts.map +0 -1
  124. package/dist/types/core/aggregation/logger.d.ts +0 -22
  125. package/dist/types/core/aggregation/logger.d.ts.map +0 -1
  126. package/dist/types/core/aggregation/messages/base.d.ts +0 -58
  127. package/dist/types/core/aggregation/messages/base.d.ts.map +0 -1
  128. package/dist/types/core/aggregation/messages/bodies.d.ts +0 -177
  129. package/dist/types/core/aggregation/messages/bodies.d.ts.map +0 -1
  130. package/dist/types/core/aggregation/messages/constants.d.ts +0 -31
  131. package/dist/types/core/aggregation/messages/constants.d.ts.map +0 -1
  132. package/dist/types/core/aggregation/messages/factories.d.ts +0 -217
  133. package/dist/types/core/aggregation/messages/factories.d.ts.map +0 -1
  134. package/dist/types/core/aggregation/messages/guards.d.ts +0 -11
  135. package/dist/types/core/aggregation/messages/guards.d.ts.map +0 -1
  136. package/dist/types/core/aggregation/messages/index.d.ts +0 -6
  137. package/dist/types/core/aggregation/messages/index.d.ts.map +0 -1
  138. package/dist/types/core/aggregation/participant.d.ts +0 -162
  139. package/dist/types/core/aggregation/participant.d.ts.map +0 -1
  140. package/dist/types/core/aggregation/phases.d.ts +0 -55
  141. package/dist/types/core/aggregation/phases.d.ts.map +0 -1
  142. package/dist/types/core/aggregation/recovery-policy.d.ts +0 -132
  143. package/dist/types/core/aggregation/recovery-policy.d.ts.map +0 -1
  144. package/dist/types/core/aggregation/recovery-spend.d.ts +0 -94
  145. package/dist/types/core/aggregation/recovery-spend.d.ts.map +0 -1
  146. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts +0 -66
  147. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +0 -1
  148. package/dist/types/core/aggregation/runner/events.d.ts +0 -147
  149. package/dist/types/core/aggregation/runner/events.d.ts.map +0 -1
  150. package/dist/types/core/aggregation/runner/index.d.ts +0 -6
  151. package/dist/types/core/aggregation/runner/index.d.ts.map +0 -1
  152. package/dist/types/core/aggregation/runner/participant-runner.d.ts +0 -122
  153. package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +0 -1
  154. package/dist/types/core/aggregation/runner/service-runner.d.ts +0 -211
  155. package/dist/types/core/aggregation/runner/service-runner.d.ts.map +0 -1
  156. package/dist/types/core/aggregation/runner/typed-emitter.d.ts +0 -41
  157. package/dist/types/core/aggregation/runner/typed-emitter.d.ts.map +0 -1
  158. package/dist/types/core/aggregation/service.d.ts +0 -177
  159. package/dist/types/core/aggregation/service.d.ts.map +0 -1
  160. package/dist/types/core/aggregation/signer.d.ts +0 -50
  161. package/dist/types/core/aggregation/signer.d.ts.map +0 -1
  162. package/dist/types/core/aggregation/signing-session.d.ts +0 -81
  163. package/dist/types/core/aggregation/signing-session.d.ts.map +0 -1
  164. package/dist/types/core/aggregation/transport/didcomm.d.ts +0 -23
  165. package/dist/types/core/aggregation/transport/didcomm.d.ts.map +0 -1
  166. package/dist/types/core/aggregation/transport/error.d.ts +0 -8
  167. package/dist/types/core/aggregation/transport/error.d.ts.map +0 -1
  168. package/dist/types/core/aggregation/transport/factory.d.ts +0 -28
  169. package/dist/types/core/aggregation/transport/factory.d.ts.map +0 -1
  170. package/dist/types/core/aggregation/transport/http/client.d.ts +0 -48
  171. package/dist/types/core/aggregation/transport/http/client.d.ts.map +0 -1
  172. package/dist/types/core/aggregation/transport/http/envelope.d.ts +0 -64
  173. package/dist/types/core/aggregation/transport/http/envelope.d.ts.map +0 -1
  174. package/dist/types/core/aggregation/transport/http/errors.d.ts +0 -9
  175. package/dist/types/core/aggregation/transport/http/errors.d.ts.map +0 -1
  176. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +0 -32
  177. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts.map +0 -1
  178. package/dist/types/core/aggregation/transport/http/index.d.ts +0 -12
  179. package/dist/types/core/aggregation/transport/http/index.d.ts.map +0 -1
  180. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +0 -26
  181. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts.map +0 -1
  182. package/dist/types/core/aggregation/transport/http/protocol.d.ts +0 -53
  183. package/dist/types/core/aggregation/transport/http/protocol.d.ts.map +0 -1
  184. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +0 -41
  185. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts.map +0 -1
  186. package/dist/types/core/aggregation/transport/http/request-auth.d.ts +0 -50
  187. package/dist/types/core/aggregation/transport/http/request-auth.d.ts.map +0 -1
  188. package/dist/types/core/aggregation/transport/http/server.d.ts +0 -110
  189. package/dist/types/core/aggregation/transport/http/server.d.ts.map +0 -1
  190. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +0 -34
  191. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts.map +0 -1
  192. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +0 -12
  193. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts.map +0 -1
  194. package/dist/types/core/aggregation/transport/in-memory.d.ts +0 -64
  195. package/dist/types/core/aggregation/transport/in-memory.d.ts.map +0 -1
  196. package/dist/types/core/aggregation/transport/index.d.ts +0 -8
  197. package/dist/types/core/aggregation/transport/index.d.ts.map +0 -1
  198. package/dist/types/core/aggregation/transport/nostr.d.ts +0 -153
  199. package/dist/types/core/aggregation/transport/nostr.d.ts.map +0 -1
  200. package/dist/types/core/aggregation/transport/transport.d.ts +0 -62
  201. package/dist/types/core/aggregation/transport/transport.d.ts.map +0 -1
  202. package/src/core/aggregation/beacon-strategy.ts +0 -142
  203. package/src/core/aggregation/cohort.ts +0 -415
  204. package/src/core/aggregation/conditions.ts +0 -172
  205. package/src/core/aggregation/errors.ts +0 -25
  206. package/src/core/aggregation/fallback-spend.ts +0 -223
  207. package/src/core/aggregation/logger.ts +0 -33
  208. package/src/core/aggregation/messages/base.ts +0 -80
  209. package/src/core/aggregation/messages/bodies.ts +0 -328
  210. package/src/core/aggregation/messages/constants.ts +0 -40
  211. package/src/core/aggregation/messages/factories.ts +0 -297
  212. package/src/core/aggregation/messages/guards.ts +0 -61
  213. package/src/core/aggregation/messages/index.ts +0 -5
  214. package/src/core/aggregation/participant.ts +0 -779
  215. package/src/core/aggregation/phases.ts +0 -91
  216. package/src/core/aggregation/recovery-policy.ts +0 -237
  217. package/src/core/aggregation/recovery-spend.ts +0 -192
  218. package/src/core/aggregation/runner/aggregation-runner.ts +0 -118
  219. package/src/core/aggregation/runner/events.ts +0 -129
  220. package/src/core/aggregation/runner/index.ts +0 -5
  221. package/src/core/aggregation/runner/participant-runner.ts +0 -488
  222. package/src/core/aggregation/runner/service-runner.ts +0 -866
  223. package/src/core/aggregation/runner/typed-emitter.ts +0 -87
  224. package/src/core/aggregation/service.ts +0 -1000
  225. package/src/core/aggregation/signer.ts +0 -67
  226. package/src/core/aggregation/signing-session.ts +0 -290
  227. package/src/core/aggregation/transport/didcomm.ts +0 -59
  228. package/src/core/aggregation/transport/error.ts +0 -13
  229. package/src/core/aggregation/transport/factory.ts +0 -65
  230. package/src/core/aggregation/transport/http/client.ts +0 -409
  231. package/src/core/aggregation/transport/http/envelope.ts +0 -204
  232. package/src/core/aggregation/transport/http/errors.ts +0 -11
  233. package/src/core/aggregation/transport/http/inbox-buffer.ts +0 -53
  234. package/src/core/aggregation/transport/http/index.ts +0 -11
  235. package/src/core/aggregation/transport/http/nonce-cache.ts +0 -43
  236. package/src/core/aggregation/transport/http/protocol.ts +0 -57
  237. package/src/core/aggregation/transport/http/rate-limiter.ts +0 -75
  238. package/src/core/aggregation/transport/http/request-auth.ts +0 -164
  239. package/src/core/aggregation/transport/http/server.ts +0 -615
  240. package/src/core/aggregation/transport/http/sse-stream.ts +0 -121
  241. package/src/core/aggregation/transport/http/sse-writer.ts +0 -23
  242. package/src/core/aggregation/transport/in-memory.ts +0 -178
  243. package/src/core/aggregation/transport/index.ts +0 -7
  244. package/src/core/aggregation/transport/nostr.ts +0 -576
  245. package/src/core/aggregation/transport/transport.ts +0 -79
@@ -1,28 +0,0 @@
1
- /**
2
- * On-the-wire version of the HTTP transport envelope. Separate from
3
- * {@link AGGREGATION_WIRE_VERSION} (which versions the aggregation protocol
4
- * payloads) so transport-layer changes don't force a protocol bump.
5
- */
6
- export const HTTP_ENVELOPE_VERSION = 1;
7
- /**
8
- * HTTP routes served by {@link HttpServerTransport}. The `{did}` placeholder
9
- * in {@link HTTP_ROUTE.ACTOR_INBOX} is substituted with a URL-safe DID at
10
- * request time.
11
- */
12
- export const HTTP_ROUTE = {
13
- ADVERTS: '/v1/adverts',
14
- MESSAGES: '/v1/messages',
15
- ACTOR_INBOX: '/v1/actors/{did}/inbox',
16
- WELL_KNOWN: '/v1/.well-known/aggregation',
17
- };
18
- /** Server-Sent Events event names used by the broadcast + inbox streams. */
19
- export const SSE_EVENT = {
20
- ADVERT: 'advert',
21
- MESSAGE: 'message',
22
- HEARTBEAT: 'heartbeat',
23
- };
24
- /** Default clock-skew tolerance for envelope timestamps (seconds). */
25
- export const DEFAULT_CLOCK_SKEW_SEC = 60;
26
- /** Default length of the per-envelope anti-replay nonce (bytes). */
27
- export const DEFAULT_NONCE_LEN_BYTES = 16;
28
- //# sourceMappingURL=protocol.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../../../src/core/aggregation/transport/http/protocol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO,EAAO,aAAa;IAC3B,QAAQ,EAAM,cAAc;IAC5B,WAAW,EAAG,wBAAwB;IACtC,UAAU,EAAI,6BAA6B;CACnC,CAAC;AAEX,4EAA4E;AAC5E,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAM,QAAQ;IACpB,OAAO,EAAK,SAAS;IACrB,SAAS,EAAG,WAAW;CACf,CAAC;AAEX,sEAAsE;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,oEAAoE;AACpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC"}
@@ -1,45 +0,0 @@
1
- export class InMemoryRateLimitStore {
2
- #buckets = new Map();
3
- get(key) {
4
- return this.#buckets.get(key);
5
- }
6
- set(key, state) {
7
- this.#buckets.set(key, state);
8
- }
9
- }
10
- /**
11
- * Token-bucket rate limiter keyed by an opaque string (typically a verified
12
- * sender DID). Tokens refill linearly at `rps` up to `burst`. Each `consume`
13
- * call atomically debits one token or returns `false` to reject.
14
- *
15
- * The limiter is synchronous and deterministic given `nowMs` - tests can
16
- * drive it with a fixed clock to exercise exact boundaries.
17
- */
18
- export class RateLimiter {
19
- #rps;
20
- #burst;
21
- #store;
22
- constructor(config = {}) {
23
- this.#rps = config.rps ?? 10;
24
- this.#burst = config.burst ?? 30;
25
- this.#store = config.store ?? new InMemoryRateLimitStore();
26
- }
27
- /** Consume one token for `key`. Returns `true` if accepted, `false` if throttled. */
28
- consume(key, nowMs) {
29
- const existing = this.#store.get(key);
30
- const state = existing ?? { tokens: this.#burst, lastRefillMs: nowMs };
31
- if (existing) {
32
- const elapsedSec = Math.max(0, (nowMs - existing.lastRefillMs) / 1000);
33
- state.tokens = Math.min(this.#burst, existing.tokens + elapsedSec * this.#rps);
34
- state.lastRefillMs = nowMs;
35
- }
36
- if (state.tokens < 1) {
37
- this.#store.set(key, state);
38
- return false;
39
- }
40
- state.tokens -= 1;
41
- this.#store.set(key, state);
42
- return true;
43
- }
44
- }
45
- //# sourceMappingURL=rate-limiter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../../../../src/core/aggregation/transport/http/rate-limiter.ts"],"names":[],"mappings":"AAeA,MAAM,OAAO,sBAAsB;IACxB,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEnD,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAkB;QACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;CACF;AAWD;;;;;;;GAOG;AACH,MAAM,OAAO,WAAW;IACb,IAAI,CAAS;IACb,MAAM,CAAS;IACf,MAAM,CAAiB;IAEhC,YAAY,SAA4B,EAAE;QACxC,IAAI,CAAC,IAAI,GAAK,MAAM,CAAC,GAAG,IAAM,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,sBAAsB,EAAE,CAAC;IAC7D,CAAC;IAED,qFAAqF;IACrF,OAAO,CAAC,GAAW,EAAE,KAAa;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,KAAK,GAAgB,QAAQ,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAEpF,IAAG,QAAQ,EAAE,CAAC;YACZ,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC;YACvE,KAAK,CAAC,MAAM,GAAS,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;YACrF,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;QAC7B,CAAC;QAED,IAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -1,100 +0,0 @@
1
- import { canonicalHashBytes } from '@did-btcr2/common';
2
- import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils';
3
- import { HttpTransportError } from './errors.js';
4
- import { DEFAULT_CLOCK_SKEW_SEC, DEFAULT_NONCE_LEN_BYTES, HTTP_ENVELOPE_VERSION } from './protocol.js';
5
- /**
6
- * `Authorization`-header scheme used to authenticate SSE subscription
7
- * requests. The header value takes the form
8
- * `BTCR2-Sig v=<n>,did=<did>,ts=<unix>,nonce=<hex>,sig=<hex>`.
9
- *
10
- * Used only for GET endpoints (SSE inbox subscribe). POST endpoints carry a
11
- * full {@link SignedEnvelope} in the request body instead.
12
- */
13
- export const REQUEST_AUTH_SCHEME = 'BTCR2-Sig';
14
- /**
15
- * Build an `Authorization` header value proving the caller controls the
16
- * private key for `did`. Covers a specific request path so the signature
17
- * can't be replayed against a different endpoint.
18
- */
19
- export function buildRequestAuth(did, keys, path, opts = {}) {
20
- const ts = opts.timestamp ?? Math.floor(Date.now() / 1000);
21
- const nonce = opts.nonce ?? bytesToHex(randomBytes(DEFAULT_NONCE_LEN_BYTES));
22
- const digest = canonicalHashBytes({
23
- v: HTTP_ENVELOPE_VERSION,
24
- did,
25
- ts,
26
- nonce,
27
- path,
28
- });
29
- const sig = keys.secretKey.sign(digest, { scheme: 'schnorr' });
30
- return `${REQUEST_AUTH_SCHEME} v=${HTTP_ENVELOPE_VERSION},did=${did},ts=${ts},nonce=${nonce},sig=${bytesToHex(sig)}`;
31
- }
32
- /**
33
- * Parse a `BTCR2-Sig` auth header value into its structured fields. Does NOT
34
- * verify the signature: call {@link verifyRequestAuth} for that.
35
- */
36
- export function parseRequestAuth(headerValue) {
37
- const prefix = `${REQUEST_AUTH_SCHEME} `;
38
- if (!headerValue.startsWith(prefix)) {
39
- throw new HttpTransportError(`Unexpected auth scheme (want ${REQUEST_AUTH_SCHEME})`, 'REQUEST_AUTH_SCHEME');
40
- }
41
- const params = {};
42
- for (const piece of headerValue.slice(prefix.length).split(',')) {
43
- const eq = piece.indexOf('=');
44
- if (eq === -1)
45
- continue;
46
- const key = piece.slice(0, eq).trim();
47
- const val = piece.slice(eq + 1).trim();
48
- if (key.length > 0)
49
- params[key] = val;
50
- }
51
- const v = Number(params.v);
52
- const ts = Number(params.ts);
53
- if (!Number.isInteger(v) || !Number.isInteger(ts) || !params.did || !params.nonce || !params.sig) {
54
- throw new HttpTransportError('Malformed auth header (missing or invalid field)', 'REQUEST_AUTH_MALFORMED', { received: Object.keys(params) });
55
- }
56
- return { v, did: params.did, ts, nonce: params.nonce, sig: params.sig };
57
- }
58
- /**
59
- * Parse + verify an auth header. Throws {@link HttpTransportError} on any
60
- * failure; returns the parsed fields on success.
61
- *
62
- * `expectedPath` must match the path the signature commits to. `senderPk`
63
- * must correspond to the DID the signer claims.
64
- */
65
- export function verifyRequestAuth(headerValue, expectedPath, senderPk, opts = {}) {
66
- const parsed = parseRequestAuth(headerValue);
67
- if (parsed.v !== HTTP_ENVELOPE_VERSION) {
68
- throw new HttpTransportError(`Unsupported auth version: ${parsed.v}`, 'REQUEST_AUTH_VERSION_MISMATCH', { version: parsed.v, expected: HTTP_ENVELOPE_VERSION });
69
- }
70
- const skewSec = opts.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
71
- const nowMs = opts.now ? opts.now() : Date.now();
72
- const nowSec = Math.floor(nowMs / 1000);
73
- const diff = Math.abs(nowSec - parsed.ts);
74
- if (diff > skewSec) {
75
- throw new HttpTransportError(`Auth timestamp out of skew: ${diff}s > ${skewSec}s`, 'REQUEST_AUTH_TIMESTAMP_SKEW', { diff, skewSec });
76
- }
77
- let sigBytes;
78
- try {
79
- sigBytes = hexToBytes(parsed.sig);
80
- }
81
- catch {
82
- throw new HttpTransportError('Auth signature is not valid hex', 'REQUEST_AUTH_SIG_HEX');
83
- }
84
- if (sigBytes.length !== 64) {
85
- throw new HttpTransportError(`Invalid auth signature length: ${sigBytes.length}`, 'REQUEST_AUTH_SIG_LENGTH', { length: sigBytes.length });
86
- }
87
- const digest = canonicalHashBytes({
88
- v: parsed.v,
89
- did: parsed.did,
90
- ts: parsed.ts,
91
- nonce: parsed.nonce,
92
- path: expectedPath,
93
- });
94
- const ok = senderPk.verify(sigBytes, digest, { scheme: 'schnorr' });
95
- if (!ok) {
96
- throw new HttpTransportError('Auth signature verification failed', 'REQUEST_AUTH_SIG_INVALID');
97
- }
98
- return parsed;
99
- }
100
- //# sourceMappingURL=request-auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"request-auth.js","sourceRoot":"","sources":["../../../../../../src/core/aggregation/transport/http/request-auth.ts"],"names":[],"mappings":"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"}
@@ -1,481 +0,0 @@
1
- import { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
2
- import { Identifier } from '../../../identifier.js';
3
- import { CONSOLE_LOGGER } from '../../logger.js';
4
- import { reviveFromWire, signEnvelope, verifyEnvelope } from './envelope.js';
5
- import { HttpTransportError } from './errors.js';
6
- import { InboxBuffer } from './inbox-buffer.js';
7
- import { NonceCache } from './nonce-cache.js';
8
- import { DEFAULT_CLOCK_SKEW_SEC, HTTP_ENVELOPE_VERSION, HTTP_ROUTE, SSE_EVENT, } from './protocol.js';
9
- import { RateLimiter } from './rate-limiter.js';
10
- import { verifyRequestAuth } from './request-auth.js';
11
- const INBOX_PATH_PREFIX = '/v1/actors/';
12
- const INBOX_PATH_SUFFIX = '/inbox';
13
- const DEFAULT_ADVERT_TTL_MS = 5 * 60 * 1000;
14
- const DEFAULT_HEARTBEAT_MS = 20_000;
15
- /**
16
- * Server-side HTTP transport. Sans-I/O - the caller mounts
17
- * {@link handleRequest} and {@link handleSse} under their HTTP framework of
18
- * choice; the transport owns only in-memory state (actors, inboxes, advert
19
- * cache, replay / rate-limit policies).
20
- *
21
- * Implements the generic {@link Transport} interface so the aggregation
22
- * runners can drive it exactly the same way they drive {@link NostrTransport}
23
- * or {@link HttpClientTransport}.
24
- */
25
- export class HttpServerTransport {
26
- name = 'http';
27
- #logger;
28
- #cors;
29
- #clockSkewSec;
30
- #inboxBufferSize;
31
- #advertTtlMs;
32
- #heartbeatMs;
33
- #rateLimiter;
34
- #nonceCache;
35
- #now;
36
- #actors = new Map();
37
- #peers = new Map();
38
- #inboxes = new Map();
39
- #broadcastSubscribers = new Set();
40
- #currentAdvert;
41
- #advertSeq = 0;
42
- constructor(config = {}) {
43
- this.#logger = config.logger ?? CONSOLE_LOGGER;
44
- this.#cors = config.cors ?? { mode: 'permissive' };
45
- this.#clockSkewSec = config.clockSkewSec ?? DEFAULT_CLOCK_SKEW_SEC;
46
- this.#inboxBufferSize = config.inboxBufferSize ?? 100;
47
- this.#advertTtlMs = config.advertTtlMs ?? DEFAULT_ADVERT_TTL_MS;
48
- this.#heartbeatMs = config.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS;
49
- this.#rateLimiter = config.rateLimiter ?? new RateLimiter();
50
- this.#nonceCache = config.nonceCache ?? new NonceCache();
51
- this.#now = config.now ?? (() => Date.now());
52
- }
53
- // ----------------------------------------------------------------
54
- // Transport interface
55
- // ----------------------------------------------------------------
56
- start() {
57
- // No-op: server-side transport has no persistent outbound connections.
58
- // SSE subscribers connect on demand via handleSse().
59
- }
60
- /**
61
- * Detach the transport: close every open SSE subscription, clear the advert
62
- * cache, and drop all actor / peer / inbox state. Intended for shutdown and
63
- * for test teardown.
64
- */
65
- stop() {
66
- for (const sub of this.#broadcastSubscribers)
67
- this.#closeBroadcastSubscriber(sub);
68
- this.#broadcastSubscribers.clear();
69
- for (const inbox of this.#inboxes.values()) {
70
- for (const sub of inbox.subscribers)
71
- this.#closeInboxSubscriber(sub);
72
- inbox.subscribers.clear();
73
- }
74
- this.#inboxes.clear();
75
- this.#currentAdvert = undefined;
76
- }
77
- registerActor(did, keys) {
78
- this.#actors.set(did, { keys, handlers: new Map() });
79
- }
80
- unregisterActor(did) {
81
- this.#actors.delete(did);
82
- this.#peers.delete(did);
83
- }
84
- getActorPk(did) {
85
- return this.#actors.get(did)?.keys.publicKey.compressed;
86
- }
87
- registerPeer(did, communicationPk) {
88
- try {
89
- new CompressedSecp256k1PublicKey(communicationPk);
90
- }
91
- catch {
92
- throw new HttpTransportError(`Invalid peer public key for ${did}`, 'INVALID_PEER_KEY', { did, keyLength: communicationPk.length });
93
- }
94
- this.#peers.set(did, communicationPk);
95
- }
96
- getPeerPk(did) {
97
- return this.#peers.get(did);
98
- }
99
- registerMessageHandler(actorDid, messageType, handler) {
100
- const actor = this.#actors.get(actorDid);
101
- if (!actor) {
102
- throw new HttpTransportError(`Cannot register handler: actor ${actorDid} not registered`, 'UNKNOWN_ACTOR', { did: actorDid });
103
- }
104
- actor.handlers.set(messageType, handler);
105
- }
106
- unregisterMessageHandler(actorDid, messageType) {
107
- this.#actors.get(actorDid)?.handlers.delete(messageType);
108
- }
109
- async sendMessage(message, sender, recipient) {
110
- if (!recipient) {
111
- throw new HttpTransportError('HttpServerTransport.sendMessage requires a recipient. Use publishRepeating for broadcasts.', 'MISSING_RECIPIENT', { messageType: message.type });
112
- }
113
- const actor = this.#actors.get(sender);
114
- if (!actor) {
115
- throw new HttpTransportError(`Unknown sender: ${sender}`, 'UNKNOWN_SENDER', { did: sender });
116
- }
117
- const envelope = signEnvelope(message, { did: sender, keys: actor.keys }, { to: recipient });
118
- const dataJson = JSON.stringify(envelope);
119
- const inbox = this.#getOrCreateInbox(recipient);
120
- const stored = inbox.buffer.append(SSE_EVENT.MESSAGE, dataJson);
121
- for (const sub of inbox.subscribers) {
122
- this.#safeWrite(sub.stream, stored.event, stored.data, stored.id);
123
- }
124
- }
125
- publishRepeating(message, sender, _intervalMs, _recipient) {
126
- const actor = this.#actors.get(sender);
127
- if (!actor) {
128
- throw new HttpTransportError(`Unknown sender: ${sender}`, 'UNKNOWN_SENDER', { did: sender });
129
- }
130
- const envelope = signEnvelope(message, { did: sender, keys: actor.keys });
131
- const dataJson = JSON.stringify(envelope);
132
- const id = String(++this.#advertSeq);
133
- const expiresAtMs = this.#now() + this.#advertTtlMs;
134
- this.#currentAdvert = { dataJson, id, expiresAtMs };
135
- for (const sub of this.#broadcastSubscribers) {
136
- this.#safeWrite(sub.stream, SSE_EVENT.ADVERT, dataJson, id);
137
- }
138
- return () => {
139
- if (this.#currentAdvert?.id === id)
140
- this.#currentAdvert = undefined;
141
- };
142
- }
143
- // ----------------------------------------------------------------
144
- // Sans-I/O HTTP surface
145
- // ----------------------------------------------------------------
146
- /**
147
- * Handle a POST / GET request (non-SSE). The caller dispatches SSE paths to
148
- * {@link handleSse} instead. Returns a fully formed response; the caller's
149
- * adapter turns it into an HTTP write.
150
- */
151
- async handleRequest(req) {
152
- const method = req.method.toUpperCase();
153
- if (method === 'OPTIONS')
154
- return this.#respond(204, '', req);
155
- const path = extractPath(req.url);
156
- if (method === 'GET' && path === HTTP_ROUTE.WELL_KNOWN) {
157
- return this.#respondJson(200, this.#wellKnownMetadata(), req);
158
- }
159
- if (method === 'POST' && path === HTTP_ROUTE.MESSAGES) {
160
- return await this.#handleMessagesPost(req);
161
- }
162
- if (method === 'POST' && path === HTTP_ROUTE.ADVERTS) {
163
- return await this.#handleAdvertsPost(req);
164
- }
165
- return this.#respondJson(404, { error: 'not_found' }, req);
166
- }
167
- /**
168
- * Open an SSE stream for a GET request. The caller is responsible for
169
- * flushing writes and propagating the `onClose` callback when the HTTP
170
- * connection ends.
171
- */
172
- handleSse(req, stream) {
173
- if (req.method.toUpperCase() !== 'GET') {
174
- stream.close();
175
- return;
176
- }
177
- const path = extractPath(req.url);
178
- if (path === HTTP_ROUTE.ADVERTS) {
179
- this.#openBroadcastSubscription(stream);
180
- return;
181
- }
182
- const inboxMatch = matchInboxPath(path);
183
- if (inboxMatch) {
184
- this.#openInboxSubscription(req, stream, inboxMatch.did, path);
185
- return;
186
- }
187
- stream.close();
188
- }
189
- // ----------------------------------------------------------------
190
- // Request handlers
191
- // ----------------------------------------------------------------
192
- async #handleMessagesPost(req) {
193
- const envelope = parseJsonBody(req.body);
194
- if (!envelope)
195
- return this.#respondJson(400, { error: 'invalid_json' }, req);
196
- const senderPk = this.#resolveSenderPk(envelope.from);
197
- if (!senderPk) {
198
- return this.#respondJson(401, { error: 'unknown_sender' }, req);
199
- }
200
- try {
201
- verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
202
- }
203
- catch (err) {
204
- this.#logger.debug('POST /v1/messages: envelope verification failed:', err);
205
- return this.#respondJson(401, { error: 'invalid_envelope' }, req);
206
- }
207
- if (!this.#nonceCache.store(envelope.from, envelope.nonce, envelope.timestamp)) {
208
- return this.#respondJson(409, { error: 'replay' }, req);
209
- }
210
- if (!this.#rateLimiter.consume(envelope.from, this.#now())) {
211
- return this.#respondJson(429, { error: 'rate_limited' }, req);
212
- }
213
- if (!envelope.to) {
214
- return this.#respondJson(400, { error: 'missing_recipient' }, req);
215
- }
216
- const actor = this.#actors.get(envelope.to);
217
- if (!actor) {
218
- return this.#respondJson(404, { error: 'unknown_recipient' }, req);
219
- }
220
- const revived = reviveFromWire(envelope.message);
221
- const flat = flattenMessage(revived);
222
- const messageType = typeof flat.type === 'string' ? flat.type : undefined;
223
- if (!messageType)
224
- return this.#respondJson(400, { error: 'missing_message_type' }, req);
225
- const handler = actor.handlers.get(messageType);
226
- if (handler) {
227
- try {
228
- await handler(flat);
229
- }
230
- catch (err) {
231
- this.#logger.debug(`Handler threw for ${messageType}:`, err);
232
- }
233
- }
234
- return this.#respondJson(202, { ok: true }, req);
235
- }
236
- async #handleAdvertsPost(req) {
237
- const envelope = parseJsonBody(req.body);
238
- if (!envelope)
239
- return this.#respondJson(400, { error: 'invalid_json' }, req);
240
- const senderPk = this.#resolveSenderPk(envelope.from);
241
- if (!senderPk)
242
- return this.#respondJson(401, { error: 'unknown_sender' }, req);
243
- try {
244
- verifyEnvelope(envelope, senderPk, { clockSkewSec: this.#clockSkewSec });
245
- }
246
- catch {
247
- return this.#respondJson(401, { error: 'invalid_envelope' }, req);
248
- }
249
- if (!this.#nonceCache.store(envelope.from, envelope.nonce, envelope.timestamp)) {
250
- return this.#respondJson(409, { error: 'replay' }, req);
251
- }
252
- if (!this.#rateLimiter.consume(envelope.from, this.#now())) {
253
- return this.#respondJson(429, { error: 'rate_limited' }, req);
254
- }
255
- // Only registered actors can publish adverts on this server.
256
- if (!this.#actors.has(envelope.from)) {
257
- return this.#respondJson(403, { error: 'not_an_actor' }, req);
258
- }
259
- const id = String(++this.#advertSeq);
260
- this.#currentAdvert = {
261
- dataJson: JSON.stringify(envelope),
262
- id,
263
- expiresAtMs: this.#now() + this.#advertTtlMs,
264
- };
265
- for (const sub of this.#broadcastSubscribers) {
266
- this.#safeWrite(sub.stream, SSE_EVENT.ADVERT, this.#currentAdvert.dataJson, id);
267
- }
268
- return this.#respondJson(202, { ok: true }, req);
269
- }
270
- #openBroadcastSubscription(stream) {
271
- const sub = { stream };
272
- this.#broadcastSubscribers.add(sub);
273
- stream.onClose(() => {
274
- this.#closeBroadcastSubscriber(sub);
275
- this.#broadcastSubscribers.delete(sub);
276
- });
277
- // Replay current advert if still within TTL.
278
- if (this.#currentAdvert && this.#currentAdvert.expiresAtMs > this.#now()) {
279
- this.#safeWrite(stream, SSE_EVENT.ADVERT, this.#currentAdvert.dataJson, this.#currentAdvert.id);
280
- }
281
- if (this.#heartbeatMs > 0) {
282
- sub.heartbeatTimer = setInterval(() => {
283
- try {
284
- stream.writeComment('hb');
285
- }
286
- catch { /* caller-owned failure */ }
287
- }, this.#heartbeatMs);
288
- }
289
- }
290
- #openInboxSubscription(req, stream, did, path) {
291
- const auth = req.headers.authorization;
292
- if (!auth) {
293
- this.#logger.debug(`Inbox subscribe: missing authorization header for ${did}`);
294
- stream.close();
295
- return;
296
- }
297
- const senderPk = this.#resolveSenderPk(did);
298
- if (!senderPk) {
299
- stream.close();
300
- return;
301
- }
302
- let parsedTs = 0;
303
- let parsedNonce = '';
304
- try {
305
- const parsed = verifyRequestAuth(auth, path, senderPk, {
306
- clockSkewSec: this.#clockSkewSec,
307
- now: () => this.#now(),
308
- });
309
- if (parsed.did !== did) {
310
- stream.close();
311
- return;
312
- }
313
- parsedTs = parsed.ts;
314
- parsedNonce = parsed.nonce;
315
- }
316
- catch (err) {
317
- this.#logger.debug(`Inbox subscribe: auth verification failed for ${did}:`, err);
318
- stream.close();
319
- return;
320
- }
321
- if (!this.#nonceCache.store(did, parsedNonce, parsedTs)) {
322
- stream.close();
323
- return;
324
- }
325
- if (!this.#rateLimiter.consume(did, this.#now())) {
326
- stream.close();
327
- return;
328
- }
329
- const inbox = this.#getOrCreateInbox(did);
330
- const sub = { stream };
331
- inbox.subscribers.add(sub);
332
- stream.onClose(() => {
333
- this.#closeInboxSubscriber(sub);
334
- inbox.subscribers.delete(sub);
335
- });
336
- // Replay buffered events since the client's Last-Event-ID, if any.
337
- const lastEventId = req.headers['last-event-id'];
338
- for (const stored of inbox.buffer.since(lastEventId)) {
339
- this.#safeWrite(stream, stored.event, stored.data, stored.id);
340
- }
341
- if (this.#heartbeatMs > 0) {
342
- sub.heartbeatTimer = setInterval(() => {
343
- try {
344
- stream.writeComment('hb');
345
- }
346
- catch { /* caller-owned failure */ }
347
- }, this.#heartbeatMs);
348
- }
349
- }
350
- // ----------------------------------------------------------------
351
- // Internal helpers
352
- // ----------------------------------------------------------------
353
- #getOrCreateInbox(did) {
354
- let inbox = this.#inboxes.get(did);
355
- if (!inbox) {
356
- inbox = { buffer: new InboxBuffer(this.#inboxBufferSize), subscribers: new Set() };
357
- this.#inboxes.set(did, inbox);
358
- }
359
- return inbox;
360
- }
361
- #resolveSenderPk(did) {
362
- const peerBytes = this.#peers.get(did);
363
- if (peerBytes) {
364
- try {
365
- return new CompressedSecp256k1PublicKey(peerBytes);
366
- }
367
- catch { /* fall through */ }
368
- }
369
- try {
370
- const components = Identifier.decode(did);
371
- if (components.idType === 'KEY') {
372
- return new CompressedSecp256k1PublicKey(components.genesisBytes);
373
- }
374
- }
375
- catch { /* not decodable */ }
376
- return undefined;
377
- }
378
- #safeWrite(stream, event, data, id) {
379
- try {
380
- stream.writeEvent(event, data, id);
381
- }
382
- catch (err) {
383
- this.#logger.debug('SSE writeEvent failed:', err);
384
- }
385
- }
386
- #closeBroadcastSubscriber(sub) {
387
- if (sub.heartbeatTimer)
388
- clearInterval(sub.heartbeatTimer);
389
- try {
390
- sub.stream.close();
391
- }
392
- catch { /* already closed */ }
393
- }
394
- #closeInboxSubscriber(sub) {
395
- if (sub.heartbeatTimer)
396
- clearInterval(sub.heartbeatTimer);
397
- try {
398
- sub.stream.close();
399
- }
400
- catch { /* already closed */ }
401
- }
402
- #respondJson(status, body, req) {
403
- return {
404
- status,
405
- headers: { 'content-type': 'application/json', ...this.#corsHeaders(req) },
406
- body: JSON.stringify(body),
407
- };
408
- }
409
- #respond(status, body, req) {
410
- return { status, headers: this.#corsHeaders(req), body };
411
- }
412
- #corsHeaders(req) {
413
- const origin = req.headers.origin;
414
- if (!origin)
415
- return {};
416
- const common = {
417
- 'access-control-allow-methods': 'GET, POST, OPTIONS',
418
- 'access-control-allow-headers': 'authorization, content-type, last-event-id',
419
- 'access-control-max-age': '86400',
420
- };
421
- switch (this.#cors.mode) {
422
- case 'permissive':
423
- return { 'access-control-allow-origin': '*', ...common };
424
- case 'allowlist':
425
- if (this.#cors.origins.includes(origin)) {
426
- return { 'access-control-allow-origin': origin, vary: 'origin', ...common };
427
- }
428
- return {};
429
- case 'same-origin':
430
- return {};
431
- }
432
- }
433
- #wellKnownMetadata() {
434
- return {
435
- envelopeVersion: HTTP_ENVELOPE_VERSION,
436
- heartbeatIntervalMs: this.#heartbeatMs,
437
- inboxBufferSize: this.#inboxBufferSize,
438
- advertTtlMs: this.#advertTtlMs,
439
- };
440
- }
441
- }
442
- // ----------------------------------------------------------------
443
- // Module-local pure helpers
444
- // ----------------------------------------------------------------
445
- function extractPath(reqUrl) {
446
- if (reqUrl.startsWith('http://') || reqUrl.startsWith('https://')) {
447
- return new URL(reqUrl).pathname;
448
- }
449
- const q = reqUrl.indexOf('?');
450
- return q === -1 ? reqUrl : reqUrl.slice(0, q);
451
- }
452
- function matchInboxPath(path) {
453
- if (!path.startsWith(INBOX_PATH_PREFIX) || !path.endsWith(INBOX_PATH_SUFFIX))
454
- return undefined;
455
- const encodedDid = path.slice(INBOX_PATH_PREFIX.length, path.length - INBOX_PATH_SUFFIX.length);
456
- if (!encodedDid)
457
- return undefined;
458
- try {
459
- return { did: decodeURIComponent(encodedDid) };
460
- }
461
- catch {
462
- return undefined;
463
- }
464
- }
465
- function parseJsonBody(body) {
466
- if (body === undefined || body === '')
467
- return undefined;
468
- try {
469
- return JSON.parse(body);
470
- }
471
- catch {
472
- return undefined;
473
- }
474
- }
475
- function flattenMessage(msg) {
476
- if (msg.body && typeof msg.body === 'object') {
477
- return { ...msg, ...msg.body };
478
- }
479
- return msg;
480
- }
481
- //# sourceMappingURL=server.js.map