@did-btcr2/method 0.38.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 (240) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser.js +47859 -34821
  3. package/dist/browser.mjs +47858 -34820
  4. package/dist/cjs/index.js +405 -6065
  5. package/dist/esm/core/beacon/fee-estimator.js +6 -29
  6. package/dist/esm/core/beacon/fee-estimator.js.map +1 -1
  7. package/dist/esm/core/did-sender-resolver.js +26 -0
  8. package/dist/esm/core/did-sender-resolver.js.map +1 -0
  9. package/dist/esm/index.js +3 -17
  10. package/dist/esm/index.js.map +1 -1
  11. package/dist/types/core/beacon/fee-estimator.d.ts +4 -39
  12. package/dist/types/core/beacon/fee-estimator.d.ts.map +1 -1
  13. package/dist/types/core/did-sender-resolver.d.ts +14 -0
  14. package/dist/types/core/did-sender-resolver.d.ts.map +1 -0
  15. package/dist/types/index.d.ts +2 -16
  16. package/dist/types/index.d.ts.map +1 -1
  17. package/package.json +5 -4
  18. package/src/core/beacon/fee-estimator.ts +8 -51
  19. package/src/core/did-sender-resolver.ts +25 -0
  20. package/src/index.ts +3 -17
  21. package/dist/esm/core/aggregation/beacon-strategy.js +0 -76
  22. package/dist/esm/core/aggregation/beacon-strategy.js.map +0 -1
  23. package/dist/esm/core/aggregation/cohort.js +0 -316
  24. package/dist/esm/core/aggregation/cohort.js.map +0 -1
  25. package/dist/esm/core/aggregation/conditions.js +0 -102
  26. package/dist/esm/core/aggregation/conditions.js.map +0 -1
  27. package/dist/esm/core/aggregation/errors.js +0 -22
  28. package/dist/esm/core/aggregation/errors.js.map +0 -1
  29. package/dist/esm/core/aggregation/fallback-spend.js +0 -132
  30. package/dist/esm/core/aggregation/fallback-spend.js.map +0 -1
  31. package/dist/esm/core/aggregation/logger.js +0 -15
  32. package/dist/esm/core/aggregation/logger.js.map +0 -1
  33. package/dist/esm/core/aggregation/messages/base.js +0 -36
  34. package/dist/esm/core/aggregation/messages/base.js.map +0 -1
  35. package/dist/esm/core/aggregation/messages/bodies.js +0 -149
  36. package/dist/esm/core/aggregation/messages/bodies.js.map +0 -1
  37. package/dist/esm/core/aggregation/messages/constants.js +0 -37
  38. package/dist/esm/core/aggregation/messages/constants.js.map +0 -1
  39. package/dist/esm/core/aggregation/messages/factories.js +0 -139
  40. package/dist/esm/core/aggregation/messages/factories.js.map +0 -1
  41. package/dist/esm/core/aggregation/messages/guards.js +0 -40
  42. package/dist/esm/core/aggregation/messages/guards.js.map +0 -1
  43. package/dist/esm/core/aggregation/messages/index.js +0 -6
  44. package/dist/esm/core/aggregation/messages/index.js.map +0 -1
  45. package/dist/esm/core/aggregation/participant.js +0 -588
  46. package/dist/esm/core/aggregation/participant.js.map +0 -1
  47. package/dist/esm/core/aggregation/phases.js +0 -45
  48. package/dist/esm/core/aggregation/phases.js.map +0 -1
  49. package/dist/esm/core/aggregation/recovery-policy.js +0 -156
  50. package/dist/esm/core/aggregation/recovery-policy.js.map +0 -1
  51. package/dist/esm/core/aggregation/recovery-spend.js +0 -106
  52. package/dist/esm/core/aggregation/recovery-spend.js.map +0 -1
  53. package/dist/esm/core/aggregation/runner/aggregation-runner.js +0 -75
  54. package/dist/esm/core/aggregation/runner/aggregation-runner.js.map +0 -1
  55. package/dist/esm/core/aggregation/runner/events.js +0 -2
  56. package/dist/esm/core/aggregation/runner/events.js.map +0 -1
  57. package/dist/esm/core/aggregation/runner/index.js +0 -6
  58. package/dist/esm/core/aggregation/runner/index.js.map +0 -1
  59. package/dist/esm/core/aggregation/runner/participant-runner.js +0 -407
  60. package/dist/esm/core/aggregation/runner/participant-runner.js.map +0 -1
  61. package/dist/esm/core/aggregation/runner/service-runner.js +0 -699
  62. package/dist/esm/core/aggregation/runner/service-runner.js.map +0 -1
  63. package/dist/esm/core/aggregation/runner/typed-emitter.js +0 -80
  64. package/dist/esm/core/aggregation/runner/typed-emitter.js.map +0 -1
  65. package/dist/esm/core/aggregation/service.js +0 -774
  66. package/dist/esm/core/aggregation/service.js.map +0 -1
  67. package/dist/esm/core/aggregation/signer.js +0 -33
  68. package/dist/esm/core/aggregation/signer.js.map +0 -1
  69. package/dist/esm/core/aggregation/signing-session.js +0 -195
  70. package/dist/esm/core/aggregation/signing-session.js.map +0 -1
  71. package/dist/esm/core/aggregation/transport/didcomm.js +0 -41
  72. package/dist/esm/core/aggregation/transport/didcomm.js.map +0 -1
  73. package/dist/esm/core/aggregation/transport/error.js +0 -12
  74. package/dist/esm/core/aggregation/transport/error.js.map +0 -1
  75. package/dist/esm/core/aggregation/transport/factory.js +0 -29
  76. package/dist/esm/core/aggregation/transport/factory.js.map +0 -1
  77. package/dist/esm/core/aggregation/transport/http/client.js +0 -350
  78. package/dist/esm/core/aggregation/transport/http/client.js.map +0 -1
  79. package/dist/esm/core/aggregation/transport/http/envelope.js +0 -126
  80. package/dist/esm/core/aggregation/transport/http/envelope.js.map +0 -1
  81. package/dist/esm/core/aggregation/transport/http/errors.js +0 -11
  82. package/dist/esm/core/aggregation/transport/http/errors.js.map +0 -1
  83. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js +0 -45
  84. package/dist/esm/core/aggregation/transport/http/inbox-buffer.js.map +0 -1
  85. package/dist/esm/core/aggregation/transport/http/index.js +0 -12
  86. package/dist/esm/core/aggregation/transport/http/index.js.map +0 -1
  87. package/dist/esm/core/aggregation/transport/http/nonce-cache.js +0 -38
  88. package/dist/esm/core/aggregation/transport/http/nonce-cache.js.map +0 -1
  89. package/dist/esm/core/aggregation/transport/http/protocol.js +0 -28
  90. package/dist/esm/core/aggregation/transport/http/protocol.js.map +0 -1
  91. package/dist/esm/core/aggregation/transport/http/rate-limiter.js +0 -45
  92. package/dist/esm/core/aggregation/transport/http/rate-limiter.js.map +0 -1
  93. package/dist/esm/core/aggregation/transport/http/request-auth.js +0 -100
  94. package/dist/esm/core/aggregation/transport/http/request-auth.js.map +0 -1
  95. package/dist/esm/core/aggregation/transport/http/server.js +0 -481
  96. package/dist/esm/core/aggregation/transport/http/server.js.map +0 -1
  97. package/dist/esm/core/aggregation/transport/http/sse-stream.js +0 -110
  98. package/dist/esm/core/aggregation/transport/http/sse-stream.js.map +0 -1
  99. package/dist/esm/core/aggregation/transport/http/sse-writer.js +0 -25
  100. package/dist/esm/core/aggregation/transport/http/sse-writer.js.map +0 -1
  101. package/dist/esm/core/aggregation/transport/in-memory.js +0 -149
  102. package/dist/esm/core/aggregation/transport/in-memory.js.map +0 -1
  103. package/dist/esm/core/aggregation/transport/index.js +0 -8
  104. package/dist/esm/core/aggregation/transport/index.js.map +0 -1
  105. package/dist/esm/core/aggregation/transport/nostr.js +0 -491
  106. package/dist/esm/core/aggregation/transport/nostr.js.map +0 -1
  107. package/dist/esm/core/aggregation/transport/transport.js +0 -2
  108. package/dist/esm/core/aggregation/transport/transport.js.map +0 -1
  109. package/dist/types/core/aggregation/beacon-strategy.d.ts +0 -56
  110. package/dist/types/core/aggregation/beacon-strategy.d.ts.map +0 -1
  111. package/dist/types/core/aggregation/cohort.d.ts +0 -182
  112. package/dist/types/core/aggregation/cohort.d.ts.map +0 -1
  113. package/dist/types/core/aggregation/conditions.d.ts +0 -86
  114. package/dist/types/core/aggregation/conditions.d.ts.map +0 -1
  115. package/dist/types/core/aggregation/errors.d.ts +0 -14
  116. package/dist/types/core/aggregation/errors.d.ts.map +0 -1
  117. package/dist/types/core/aggregation/fallback-spend.d.ts +0 -94
  118. package/dist/types/core/aggregation/fallback-spend.d.ts.map +0 -1
  119. package/dist/types/core/aggregation/logger.d.ts +0 -22
  120. package/dist/types/core/aggregation/logger.d.ts.map +0 -1
  121. package/dist/types/core/aggregation/messages/base.d.ts +0 -58
  122. package/dist/types/core/aggregation/messages/base.d.ts.map +0 -1
  123. package/dist/types/core/aggregation/messages/bodies.d.ts +0 -177
  124. package/dist/types/core/aggregation/messages/bodies.d.ts.map +0 -1
  125. package/dist/types/core/aggregation/messages/constants.d.ts +0 -31
  126. package/dist/types/core/aggregation/messages/constants.d.ts.map +0 -1
  127. package/dist/types/core/aggregation/messages/factories.d.ts +0 -217
  128. package/dist/types/core/aggregation/messages/factories.d.ts.map +0 -1
  129. package/dist/types/core/aggregation/messages/guards.d.ts +0 -11
  130. package/dist/types/core/aggregation/messages/guards.d.ts.map +0 -1
  131. package/dist/types/core/aggregation/messages/index.d.ts +0 -6
  132. package/dist/types/core/aggregation/messages/index.d.ts.map +0 -1
  133. package/dist/types/core/aggregation/participant.d.ts +0 -162
  134. package/dist/types/core/aggregation/participant.d.ts.map +0 -1
  135. package/dist/types/core/aggregation/phases.d.ts +0 -55
  136. package/dist/types/core/aggregation/phases.d.ts.map +0 -1
  137. package/dist/types/core/aggregation/recovery-policy.d.ts +0 -132
  138. package/dist/types/core/aggregation/recovery-policy.d.ts.map +0 -1
  139. package/dist/types/core/aggregation/recovery-spend.d.ts +0 -94
  140. package/dist/types/core/aggregation/recovery-spend.d.ts.map +0 -1
  141. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts +0 -72
  142. package/dist/types/core/aggregation/runner/aggregation-runner.d.ts.map +0 -1
  143. package/dist/types/core/aggregation/runner/events.d.ts +0 -147
  144. package/dist/types/core/aggregation/runner/events.d.ts.map +0 -1
  145. package/dist/types/core/aggregation/runner/index.d.ts +0 -6
  146. package/dist/types/core/aggregation/runner/index.d.ts.map +0 -1
  147. package/dist/types/core/aggregation/runner/participant-runner.d.ts +0 -122
  148. package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +0 -1
  149. package/dist/types/core/aggregation/runner/service-runner.d.ts +0 -228
  150. package/dist/types/core/aggregation/runner/service-runner.d.ts.map +0 -1
  151. package/dist/types/core/aggregation/runner/typed-emitter.d.ts +0 -41
  152. package/dist/types/core/aggregation/runner/typed-emitter.d.ts.map +0 -1
  153. package/dist/types/core/aggregation/service.d.ts +0 -177
  154. package/dist/types/core/aggregation/service.d.ts.map +0 -1
  155. package/dist/types/core/aggregation/signer.d.ts +0 -50
  156. package/dist/types/core/aggregation/signer.d.ts.map +0 -1
  157. package/dist/types/core/aggregation/signing-session.d.ts +0 -81
  158. package/dist/types/core/aggregation/signing-session.d.ts.map +0 -1
  159. package/dist/types/core/aggregation/transport/didcomm.d.ts +0 -23
  160. package/dist/types/core/aggregation/transport/didcomm.d.ts.map +0 -1
  161. package/dist/types/core/aggregation/transport/error.d.ts +0 -8
  162. package/dist/types/core/aggregation/transport/error.d.ts.map +0 -1
  163. package/dist/types/core/aggregation/transport/factory.d.ts +0 -28
  164. package/dist/types/core/aggregation/transport/factory.d.ts.map +0 -1
  165. package/dist/types/core/aggregation/transport/http/client.d.ts +0 -48
  166. package/dist/types/core/aggregation/transport/http/client.d.ts.map +0 -1
  167. package/dist/types/core/aggregation/transport/http/envelope.d.ts +0 -64
  168. package/dist/types/core/aggregation/transport/http/envelope.d.ts.map +0 -1
  169. package/dist/types/core/aggregation/transport/http/errors.d.ts +0 -9
  170. package/dist/types/core/aggregation/transport/http/errors.d.ts.map +0 -1
  171. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts +0 -32
  172. package/dist/types/core/aggregation/transport/http/inbox-buffer.d.ts.map +0 -1
  173. package/dist/types/core/aggregation/transport/http/index.d.ts +0 -12
  174. package/dist/types/core/aggregation/transport/http/index.d.ts.map +0 -1
  175. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts +0 -26
  176. package/dist/types/core/aggregation/transport/http/nonce-cache.d.ts.map +0 -1
  177. package/dist/types/core/aggregation/transport/http/protocol.d.ts +0 -53
  178. package/dist/types/core/aggregation/transport/http/protocol.d.ts.map +0 -1
  179. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts +0 -41
  180. package/dist/types/core/aggregation/transport/http/rate-limiter.d.ts.map +0 -1
  181. package/dist/types/core/aggregation/transport/http/request-auth.d.ts +0 -50
  182. package/dist/types/core/aggregation/transport/http/request-auth.d.ts.map +0 -1
  183. package/dist/types/core/aggregation/transport/http/server.d.ts +0 -110
  184. package/dist/types/core/aggregation/transport/http/server.d.ts.map +0 -1
  185. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts +0 -34
  186. package/dist/types/core/aggregation/transport/http/sse-stream.d.ts.map +0 -1
  187. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts +0 -12
  188. package/dist/types/core/aggregation/transport/http/sse-writer.d.ts.map +0 -1
  189. package/dist/types/core/aggregation/transport/in-memory.d.ts +0 -64
  190. package/dist/types/core/aggregation/transport/in-memory.d.ts.map +0 -1
  191. package/dist/types/core/aggregation/transport/index.d.ts +0 -8
  192. package/dist/types/core/aggregation/transport/index.d.ts.map +0 -1
  193. package/dist/types/core/aggregation/transport/nostr.d.ts +0 -153
  194. package/dist/types/core/aggregation/transport/nostr.d.ts.map +0 -1
  195. package/dist/types/core/aggregation/transport/transport.d.ts +0 -62
  196. package/dist/types/core/aggregation/transport/transport.d.ts.map +0 -1
  197. package/src/core/aggregation/beacon-strategy.ts +0 -142
  198. package/src/core/aggregation/cohort.ts +0 -415
  199. package/src/core/aggregation/conditions.ts +0 -172
  200. package/src/core/aggregation/errors.ts +0 -25
  201. package/src/core/aggregation/fallback-spend.ts +0 -223
  202. package/src/core/aggregation/logger.ts +0 -33
  203. package/src/core/aggregation/messages/base.ts +0 -80
  204. package/src/core/aggregation/messages/bodies.ts +0 -328
  205. package/src/core/aggregation/messages/constants.ts +0 -40
  206. package/src/core/aggregation/messages/factories.ts +0 -297
  207. package/src/core/aggregation/messages/guards.ts +0 -61
  208. package/src/core/aggregation/messages/index.ts +0 -5
  209. package/src/core/aggregation/participant.ts +0 -779
  210. package/src/core/aggregation/phases.ts +0 -91
  211. package/src/core/aggregation/recovery-policy.ts +0 -237
  212. package/src/core/aggregation/recovery-spend.ts +0 -192
  213. package/src/core/aggregation/runner/aggregation-runner.ts +0 -125
  214. package/src/core/aggregation/runner/events.ts +0 -129
  215. package/src/core/aggregation/runner/index.ts +0 -5
  216. package/src/core/aggregation/runner/participant-runner.ts +0 -488
  217. package/src/core/aggregation/runner/service-runner.ts +0 -888
  218. package/src/core/aggregation/runner/typed-emitter.ts +0 -87
  219. package/src/core/aggregation/service.ts +0 -1000
  220. package/src/core/aggregation/signer.ts +0 -67
  221. package/src/core/aggregation/signing-session.ts +0 -290
  222. package/src/core/aggregation/transport/didcomm.ts +0 -59
  223. package/src/core/aggregation/transport/error.ts +0 -13
  224. package/src/core/aggregation/transport/factory.ts +0 -65
  225. package/src/core/aggregation/transport/http/client.ts +0 -409
  226. package/src/core/aggregation/transport/http/envelope.ts +0 -204
  227. package/src/core/aggregation/transport/http/errors.ts +0 -11
  228. package/src/core/aggregation/transport/http/inbox-buffer.ts +0 -53
  229. package/src/core/aggregation/transport/http/index.ts +0 -11
  230. package/src/core/aggregation/transport/http/nonce-cache.ts +0 -43
  231. package/src/core/aggregation/transport/http/protocol.ts +0 -57
  232. package/src/core/aggregation/transport/http/rate-limiter.ts +0 -75
  233. package/src/core/aggregation/transport/http/request-auth.ts +0 -164
  234. package/src/core/aggregation/transport/http/server.ts +0 -615
  235. package/src/core/aggregation/transport/http/sse-stream.ts +0 -121
  236. package/src/core/aggregation/transport/http/sse-writer.ts +0 -23
  237. package/src/core/aggregation/transport/in-memory.ts +0 -178
  238. package/src/core/aggregation/transport/index.ts +0 -7
  239. package/src/core/aggregation/transport/nostr.ts +0 -576
  240. 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