@interfere/react 0.1.0-alpha.6 → 0.2.0-alpha.1

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 (310) hide show
  1. package/README.md +90 -0
  2. package/dist/error-boundary.d.mts +27 -0
  3. package/dist/error-boundary.d.mts.map +1 -0
  4. package/dist/error-boundary.mjs +42 -0
  5. package/dist/error-boundary.mjs.map +1 -0
  6. package/dist/internal/client.d.mts +13 -0
  7. package/dist/internal/client.d.mts.map +1 -0
  8. package/dist/internal/client.mjs +80 -0
  9. package/dist/internal/client.mjs.map +1 -0
  10. package/dist/internal/config.d.mts +9 -0
  11. package/dist/internal/config.d.mts.map +1 -0
  12. package/dist/internal/config.mjs +27 -0
  13. package/dist/internal/config.mjs.map +1 -0
  14. package/dist/internal/context.d.mts +6 -0
  15. package/dist/internal/context.d.mts.map +1 -0
  16. package/dist/internal/context.mjs +32 -0
  17. package/dist/internal/context.mjs.map +1 -0
  18. package/dist/internal/envelope.d.mts +14 -0
  19. package/dist/internal/envelope.d.mts.map +1 -0
  20. package/dist/internal/envelope.mjs +20 -0
  21. package/dist/internal/envelope.mjs.map +1 -0
  22. package/dist/internal/errors.d.mts +4 -0
  23. package/dist/internal/errors.d.mts.map +1 -0
  24. package/dist/internal/errors.mjs +4 -0
  25. package/dist/internal/errors.mjs.map +1 -0
  26. package/dist/internal/sw.d.mts +4 -0
  27. package/dist/internal/sw.d.mts.map +1 -0
  28. package/dist/internal/sw.mjs +10 -0
  29. package/dist/internal/sw.mjs.map +1 -0
  30. package/dist/plugins/errors.d.mts +6 -0
  31. package/dist/plugins/errors.d.mts.map +1 -0
  32. package/dist/plugins/errors.mjs +59 -0
  33. package/dist/plugins/errors.mjs.map +1 -0
  34. package/dist/plugins/lib/loader.d.mts +9 -0
  35. package/dist/plugins/lib/loader.d.mts.map +1 -0
  36. package/dist/plugins/lib/loader.mjs +47 -0
  37. package/dist/plugins/lib/loader.mjs.map +1 -0
  38. package/dist/plugins/lib/types.d.mts +14 -0
  39. package/dist/plugins/lib/types.d.mts.map +1 -0
  40. package/dist/plugins/lib/types.mjs +1 -0
  41. package/dist/plugins/pages.d.mts +6 -0
  42. package/dist/plugins/pages.d.mts.map +1 -0
  43. package/dist/plugins/pages.mjs +102 -0
  44. package/dist/plugins/pages.mjs.map +1 -0
  45. package/dist/plugins/rage-clicks.d.mts +6 -0
  46. package/dist/plugins/rage-clicks.d.mts.map +1 -0
  47. package/dist/plugins/rage-clicks.mjs +53 -0
  48. package/dist/plugins/rage-clicks.mjs.map +1 -0
  49. package/dist/plugins/replay.d.mts +6 -0
  50. package/dist/plugins/replay.d.mts.map +1 -0
  51. package/dist/plugins/replay.mjs +62 -0
  52. package/dist/plugins/replay.mjs.map +1 -0
  53. package/dist/provider.d.mts +17 -11
  54. package/dist/provider.d.mts.map +1 -1
  55. package/dist/provider.mjs +18 -17
  56. package/dist/provider.mjs.map +1 -1
  57. package/dist/tracking/api.d.mts +22 -0
  58. package/dist/tracking/api.d.mts.map +1 -0
  59. package/dist/tracking/api.mjs +88 -0
  60. package/dist/tracking/api.mjs.map +1 -0
  61. package/dist/tracking/session.d.mts +19 -0
  62. package/dist/tracking/session.d.mts.map +1 -0
  63. package/dist/tracking/session.mjs +92 -0
  64. package/dist/tracking/session.mjs.map +1 -0
  65. package/dist/tracking/visitor.d.mts +6 -0
  66. package/dist/tracking/visitor.d.mts.map +1 -0
  67. package/dist/tracking/visitor.mjs +35 -0
  68. package/dist/tracking/visitor.mjs.map +1 -0
  69. package/dist/transport/http.d.mts +15 -0
  70. package/dist/transport/http.d.mts.map +1 -0
  71. package/dist/transport/http.mjs +56 -0
  72. package/dist/transport/http.mjs.map +1 -0
  73. package/dist/transport/queue.d.mts +25 -0
  74. package/dist/transport/queue.d.mts.map +1 -0
  75. package/dist/transport/queue.mjs +60 -0
  76. package/dist/transport/queue.mjs.map +1 -0
  77. package/dist/util/log.d.mts +13 -0
  78. package/dist/util/log.d.mts.map +1 -0
  79. package/dist/util/log.mjs +37 -0
  80. package/dist/util/log.mjs.map +1 -0
  81. package/package.json +38 -64
  82. package/dist/client.d.mts +0 -8
  83. package/dist/client.d.mts.map +0 -1
  84. package/dist/client.mjs +0 -14
  85. package/dist/client.mjs.map +0 -1
  86. package/dist/core/events/define-event.d.mts +0 -12
  87. package/dist/core/events/define-event.d.mts.map +0 -1
  88. package/dist/core/events/define-event.mjs +0 -39
  89. package/dist/core/events/define-event.mjs.map +0 -1
  90. package/dist/core/events/event-registry.d.mts +0 -23
  91. package/dist/core/events/event-registry.d.mts.map +0 -1
  92. package/dist/core/events/event-registry.mjs +0 -32
  93. package/dist/core/events/event-registry.mjs.map +0 -1
  94. package/dist/core/events/plugin-event-types.d.mts +0 -130
  95. package/dist/core/events/plugin-event-types.d.mts.map +0 -1
  96. package/dist/core/events/plugin-event-types.mjs +0 -25
  97. package/dist/core/events/plugin-event-types.mjs.map +0 -1
  98. package/dist/core/plugins/define-plugin.d.mts +0 -43
  99. package/dist/core/plugins/define-plugin.d.mts.map +0 -1
  100. package/dist/core/plugins/define-plugin.mjs +0 -23
  101. package/dist/core/plugins/define-plugin.mjs.map +0 -1
  102. package/dist/core/plugins/dom-utils.d.mts +0 -9
  103. package/dist/core/plugins/dom-utils.d.mts.map +0 -1
  104. package/dist/core/plugins/dom-utils.mjs +0 -25
  105. package/dist/core/plugins/dom-utils.mjs.map +0 -1
  106. package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.d.mts +0 -18
  107. package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.d.mts.map +0 -1
  108. package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.mjs +0 -17
  109. package/dist/core/plugins/impl/ai-summary/ai-summary-plugin-api.mjs.map +0 -1
  110. package/dist/core/plugins/impl/ai-summary/plugin.d.mts +0 -6
  111. package/dist/core/plugins/impl/ai-summary/plugin.d.mts.map +0 -1
  112. package/dist/core/plugins/impl/ai-summary/plugin.mjs +0 -151
  113. package/dist/core/plugins/impl/ai-summary/plugin.mjs.map +0 -1
  114. package/dist/core/plugins/impl/errors/errors-plugin-api.d.mts +0 -17
  115. package/dist/core/plugins/impl/errors/errors-plugin-api.d.mts.map +0 -1
  116. package/dist/core/plugins/impl/errors/errors-plugin-api.mjs +0 -58
  117. package/dist/core/plugins/impl/errors/errors-plugin-api.mjs.map +0 -1
  118. package/dist/core/plugins/impl/errors/logic.d.mts +0 -15
  119. package/dist/core/plugins/impl/errors/logic.d.mts.map +0 -1
  120. package/dist/core/plugins/impl/errors/logic.mjs +0 -48
  121. package/dist/core/plugins/impl/errors/logic.mjs.map +0 -1
  122. package/dist/core/plugins/impl/errors/patches.d.mts +0 -13
  123. package/dist/core/plugins/impl/errors/patches.d.mts.map +0 -1
  124. package/dist/core/plugins/impl/errors/patches.mjs +0 -43
  125. package/dist/core/plugins/impl/errors/patches.mjs.map +0 -1
  126. package/dist/core/plugins/impl/errors/plugin.d.mts +0 -9
  127. package/dist/core/plugins/impl/errors/plugin.d.mts.map +0 -1
  128. package/dist/core/plugins/impl/errors/plugin.mjs +0 -91
  129. package/dist/core/plugins/impl/errors/plugin.mjs.map +0 -1
  130. package/dist/core/plugins/impl/page-events/page-events-plugin-api.d.mts +0 -19
  131. package/dist/core/plugins/impl/page-events/page-events-plugin-api.d.mts.map +0 -1
  132. package/dist/core/plugins/impl/page-events/page-events-plugin-api.mjs +0 -38
  133. package/dist/core/plugins/impl/page-events/page-events-plugin-api.mjs.map +0 -1
  134. package/dist/core/plugins/impl/page-events/plugin.d.mts +0 -6
  135. package/dist/core/plugins/impl/page-events/plugin.d.mts.map +0 -1
  136. package/dist/core/plugins/impl/page-events/plugin.mjs +0 -95
  137. package/dist/core/plugins/impl/page-events/plugin.mjs.map +0 -1
  138. package/dist/core/plugins/impl/rage-click/plugin.d.mts +0 -6
  139. package/dist/core/plugins/impl/rage-click/plugin.d.mts.map +0 -1
  140. package/dist/core/plugins/impl/rage-click/plugin.mjs +0 -38
  141. package/dist/core/plugins/impl/rage-click/plugin.mjs.map +0 -1
  142. package/dist/core/plugins/impl/rage-click/rage-click.layer.d.mts +0 -9
  143. package/dist/core/plugins/impl/rage-click/rage-click.layer.d.mts.map +0 -1
  144. package/dist/core/plugins/impl/rage-click/rage-click.layer.mjs +0 -35
  145. package/dist/core/plugins/impl/rage-click/rage-click.layer.mjs.map +0 -1
  146. package/dist/core/plugins/impl/rage-click/rage-click.service.d.mts +0 -16
  147. package/dist/core/plugins/impl/rage-click/rage-click.service.d.mts.map +0 -1
  148. package/dist/core/plugins/impl/rage-click/rage-click.service.mjs +0 -7
  149. package/dist/core/plugins/impl/rage-click/rage-click.service.mjs.map +0 -1
  150. package/dist/core/plugins/impl/rage-click/rage-click.test-layer.d.mts +0 -16
  151. package/dist/core/plugins/impl/rage-click/rage-click.test-layer.d.mts.map +0 -1
  152. package/dist/core/plugins/impl/rage-click/rage-click.test-layer.mjs +0 -18
  153. package/dist/core/plugins/impl/rage-click/rage-click.test-layer.mjs.map +0 -1
  154. package/dist/core/plugins/impl/replay/plugin.d.mts +0 -9
  155. package/dist/core/plugins/impl/replay/plugin.d.mts.map +0 -1
  156. package/dist/core/plugins/impl/replay/plugin.mjs +0 -83
  157. package/dist/core/plugins/impl/replay/plugin.mjs.map +0 -1
  158. package/dist/core/plugins/impl/replay/replay-plugin-api.d.mts +0 -18
  159. package/dist/core/plugins/impl/replay/replay-plugin-api.d.mts.map +0 -1
  160. package/dist/core/plugins/impl/replay/replay-plugin-api.mjs +0 -50
  161. package/dist/core/plugins/impl/replay/replay-plugin-api.mjs.map +0 -1
  162. package/dist/core/plugins/impl/server-tracing/plugin.d.mts +0 -13
  163. package/dist/core/plugins/impl/server-tracing/plugin.d.mts.map +0 -1
  164. package/dist/core/plugins/impl/server-tracing/plugin.mjs +0 -75
  165. package/dist/core/plugins/impl/server-tracing/plugin.mjs.map +0 -1
  166. package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.d.mts +0 -16
  167. package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.d.mts.map +0 -1
  168. package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.mjs +0 -17
  169. package/dist/core/plugins/impl/server-tracing/server-tracing-plugin-api.mjs.map +0 -1
  170. package/dist/core/plugins/impl/server-tracing/tracing-logic.d.mts +0 -29
  171. package/dist/core/plugins/impl/server-tracing/tracing-logic.d.mts.map +0 -1
  172. package/dist/core/plugins/impl/server-tracing/tracing-logic.mjs +0 -55
  173. package/dist/core/plugins/impl/server-tracing/tracing-logic.mjs.map +0 -1
  174. package/dist/core/plugins/plugin-loader.d.mts +0 -75
  175. package/dist/core/plugins/plugin-loader.d.mts.map +0 -1
  176. package/dist/core/plugins/plugin-loader.mjs +0 -79
  177. package/dist/core/plugins/plugin-loader.mjs.map +0 -1
  178. package/dist/core/plugins/services/event-capture.d.mts +0 -12
  179. package/dist/core/plugins/services/event-capture.d.mts.map +0 -1
  180. package/dist/core/plugins/services/event-capture.layer.d.mts +0 -11
  181. package/dist/core/plugins/services/event-capture.layer.d.mts.map +0 -1
  182. package/dist/core/plugins/services/event-capture.layer.mjs +0 -79
  183. package/dist/core/plugins/services/event-capture.layer.mjs.map +0 -1
  184. package/dist/core/plugins/services/event-capture.mjs +0 -7
  185. package/dist/core/plugins/services/event-capture.mjs.map +0 -1
  186. package/dist/core/plugins/services/event-capture.test-layer.d.mts +0 -17
  187. package/dist/core/plugins/services/event-capture.test-layer.d.mts.map +0 -1
  188. package/dist/core/plugins/services/event-capture.test-layer.mjs +0 -21
  189. package/dist/core/plugins/services/event-capture.test-layer.mjs.map +0 -1
  190. package/dist/core/plugins/services/plugin-config.d.mts +0 -11
  191. package/dist/core/plugins/services/plugin-config.d.mts.map +0 -1
  192. package/dist/core/plugins/services/plugin-config.mjs +0 -7
  193. package/dist/core/plugins/services/plugin-config.mjs.map +0 -1
  194. package/dist/core/plugins/services/plugin-config.test-layer.d.mts +0 -60
  195. package/dist/core/plugins/services/plugin-config.test-layer.d.mts.map +0 -1
  196. package/dist/core/plugins/services/plugin-config.test-layer.mjs +0 -8
  197. package/dist/core/plugins/services/plugin-config.test-layer.mjs.map +0 -1
  198. package/dist/core/plugins/services/plugin-logger.d.mts +0 -14
  199. package/dist/core/plugins/services/plugin-logger.d.mts.map +0 -1
  200. package/dist/core/plugins/services/plugin-logger.mjs +0 -7
  201. package/dist/core/plugins/services/plugin-logger.mjs.map +0 -1
  202. package/dist/core/plugins/services/plugin-logger.test-layer.d.mts +0 -18
  203. package/dist/core/plugins/services/plugin-logger.test-layer.d.mts.map +0 -1
  204. package/dist/core/plugins/services/plugin-logger.test-layer.mjs +0 -28
  205. package/dist/core/plugins/services/plugin-logger.test-layer.mjs.map +0 -1
  206. package/dist/core/plugins/services/plugin-runtime.d.mts +0 -10
  207. package/dist/core/plugins/services/plugin-runtime.d.mts.map +0 -1
  208. package/dist/core/plugins/services/plugin-runtime.mjs +0 -7
  209. package/dist/core/plugins/services/plugin-runtime.mjs.map +0 -1
  210. package/dist/core/plugins/services/plugin-runtime.test-layer.d.mts +0 -16
  211. package/dist/core/plugins/services/plugin-runtime.test-layer.d.mts.map +0 -1
  212. package/dist/core/plugins/services/plugin-runtime.test-layer.mjs +0 -21
  213. package/dist/core/plugins/services/plugin-runtime.test-layer.mjs.map +0 -1
  214. package/dist/core/plugins/services/session-info.d.mts +0 -11
  215. package/dist/core/plugins/services/session-info.d.mts.map +0 -1
  216. package/dist/core/plugins/services/session-info.mjs +0 -7
  217. package/dist/core/plugins/services/session-info.mjs.map +0 -1
  218. package/dist/core/plugins/services/session-info.test-layer.d.mts +0 -18
  219. package/dist/core/plugins/services/session-info.test-layer.d.mts.map +0 -1
  220. package/dist/core/plugins/services/session-info.test-layer.mjs +0 -20
  221. package/dist/core/plugins/services/session-info.test-layer.mjs.map +0 -1
  222. package/dist/core/runtime/config.d.mts +0 -14
  223. package/dist/core/runtime/config.d.mts.map +0 -1
  224. package/dist/core/runtime/config.mjs +0 -33
  225. package/dist/core/runtime/config.mjs.map +0 -1
  226. package/dist/core/runtime/context.d.mts +0 -50
  227. package/dist/core/runtime/context.d.mts.map +0 -1
  228. package/dist/core/runtime/context.mjs +0 -46
  229. package/dist/core/runtime/context.mjs.map +0 -1
  230. package/dist/core/runtime/ingest-target.d.mts +0 -10
  231. package/dist/core/runtime/ingest-target.d.mts.map +0 -1
  232. package/dist/core/runtime/ingest-target.mjs +0 -15
  233. package/dist/core/runtime/ingest-target.mjs.map +0 -1
  234. package/dist/core/runtime/native-fetch.d.mts +0 -32
  235. package/dist/core/runtime/native-fetch.d.mts.map +0 -1
  236. package/dist/core/runtime/native-fetch.mjs +0 -49
  237. package/dist/core/runtime/native-fetch.mjs.map +0 -1
  238. package/dist/core/schemas.d.mts +0 -26
  239. package/dist/core/schemas.d.mts.map +0 -1
  240. package/dist/core/schemas.mjs +0 -1
  241. package/dist/effect/errors.d.mts +0 -22
  242. package/dist/effect/errors.d.mts.map +0 -1
  243. package/dist/effect/errors.mjs +0 -17
  244. package/dist/effect/errors.mjs.map +0 -1
  245. package/dist/effect/layers/circuit-breaker.layer.d.mts +0 -9
  246. package/dist/effect/layers/circuit-breaker.layer.d.mts.map +0 -1
  247. package/dist/effect/layers/circuit-breaker.layer.mjs +0 -9
  248. package/dist/effect/layers/circuit-breaker.layer.mjs.map +0 -1
  249. package/dist/effect/layers/circuit-breaker.layer.test-layer.d.mts +0 -18
  250. package/dist/effect/layers/circuit-breaker.layer.test-layer.d.mts.map +0 -1
  251. package/dist/effect/layers/circuit-breaker.layer.test-layer.mjs +0 -43
  252. package/dist/effect/layers/circuit-breaker.layer.test-layer.mjs.map +0 -1
  253. package/dist/effect/layers/config.layer.d.mts +0 -13
  254. package/dist/effect/layers/config.layer.d.mts.map +0 -1
  255. package/dist/effect/layers/config.layer.mjs +0 -21
  256. package/dist/effect/layers/config.layer.mjs.map +0 -1
  257. package/dist/effect/layers/context.layer.d.mts +0 -12
  258. package/dist/effect/layers/context.layer.d.mts.map +0 -1
  259. package/dist/effect/layers/context.layer.mjs +0 -14
  260. package/dist/effect/layers/context.layer.mjs.map +0 -1
  261. package/dist/effect/layers/http.layer.d.mts +0 -21
  262. package/dist/effect/layers/http.layer.d.mts.map +0 -1
  263. package/dist/effect/layers/http.layer.mjs +0 -118
  264. package/dist/effect/layers/http.layer.mjs.map +0 -1
  265. package/dist/effect/layers/queue.layer.d.mts +0 -31
  266. package/dist/effect/layers/queue.layer.d.mts.map +0 -1
  267. package/dist/effect/layers/queue.layer.mjs +0 -257
  268. package/dist/effect/layers/queue.layer.mjs.map +0 -1
  269. package/dist/effect/layers/queue.layer.test-layer.d.mts +0 -19
  270. package/dist/effect/layers/queue.layer.test-layer.d.mts.map +0 -1
  271. package/dist/effect/layers/queue.layer.test-layer.mjs +0 -44
  272. package/dist/effect/layers/queue.layer.test-layer.mjs.map +0 -1
  273. package/dist/effect/layers/session.layer.d.mts +0 -34
  274. package/dist/effect/layers/session.layer.d.mts.map +0 -1
  275. package/dist/effect/layers/session.layer.mjs +0 -127
  276. package/dist/effect/layers/session.layer.mjs.map +0 -1
  277. package/dist/effect/layers/storage.layer.d.mts +0 -50
  278. package/dist/effect/layers/storage.layer.d.mts.map +0 -1
  279. package/dist/effect/layers/storage.layer.mjs +0 -180
  280. package/dist/effect/layers/storage.layer.mjs.map +0 -1
  281. package/dist/effect/layers/test-utils.d.mts +0 -19
  282. package/dist/effect/layers/test-utils.d.mts.map +0 -1
  283. package/dist/effect/layers/test-utils.mjs +0 -32
  284. package/dist/effect/layers/test-utils.mjs.map +0 -1
  285. package/dist/effect/runtime-services.d.mts +0 -23
  286. package/dist/effect/runtime-services.d.mts.map +0 -1
  287. package/dist/effect/runtime-services.mjs +0 -79
  288. package/dist/effect/runtime-services.mjs.map +0 -1
  289. package/dist/effect/tags.d.mts +0 -58
  290. package/dist/effect/tags.d.mts.map +0 -1
  291. package/dist/effect/tags.mjs +0 -7
  292. package/dist/effect/tags.mjs.map +0 -1
  293. package/dist/hooks/use-runtime-and-plugins.d.mts +0 -7
  294. package/dist/hooks/use-runtime-and-plugins.d.mts.map +0 -1
  295. package/dist/hooks/use-runtime-and-plugins.mjs +0 -121
  296. package/dist/hooks/use-runtime-and-plugins.mjs.map +0 -1
  297. package/dist/hooks/use-session.d.mts +0 -40
  298. package/dist/hooks/use-session.d.mts.map +0 -1
  299. package/dist/hooks/use-session.mjs +0 -96
  300. package/dist/hooks/use-session.mjs.map +0 -1
  301. package/dist/package.mjs +0 -103
  302. package/dist/package.mjs.map +0 -1
  303. package/dist/server/auth.d.mts +0 -15
  304. package/dist/server/auth.d.mts.map +0 -1
  305. package/dist/server/auth.mjs +0 -45
  306. package/dist/server/auth.mjs.map +0 -1
  307. package/dist/server/capture.d.mts +0 -34
  308. package/dist/server/capture.d.mts.map +0 -1
  309. package/dist/server/capture.mjs +0 -172
  310. package/dist/server/capture.mjs.map +0 -1
@@ -1,21 +0,0 @@
1
- import { HttpError } from "../errors.mjs";
2
- import { SdkConfig } from "./config.layer.mjs";
3
- import { Context, Effect, Layer } from "effect";
4
- import { Envelope } from "@interfere/types/sdk/envelope";
5
-
6
- //#region src/effect/layers/http.layer.d.ts
7
- interface HttpService {
8
- readonly postEnvelopes: (envelopes: Envelope[]) => Effect.Effect<void, HttpError>;
9
- }
10
- declare const HttpServiceTag_base: Context.TagClass<HttpServiceTag, "@interfere/react/Http", HttpService>;
11
- declare class HttpServiceTag extends HttpServiceTag_base {}
12
- /**
13
- * Creates an HTTP service layer for posting envelopes
14
- */
15
- interface HttpDeps {
16
- readonly fetch?: typeof globalThis.fetch;
17
- readonly timeoutMs?: number;
18
- }
19
- declare const HttpServiceLive: (config: SdkConfig, deps?: HttpDeps) => Layer.Layer<HttpServiceTag, never, never>;
20
- //#endregion
21
- export { HttpService, HttpServiceLive, HttpServiceTag };
@@ -1 +0,0 @@
1
- {"version":3,"file":"http.layer.d.mts","names":[],"sources":["../../../src/effect/layers/http.layer.ts"],"mappings":";;;;;;UAaiB,WAAA;EAAA,SACN,aAAA,GACP,SAAA,EAAW,QAAA,OACR,MAAA,CAAO,MAAA,OAAa,SAAA;AAAA;AAAA,cAC1B,mBAAA;cAEY,cAAA,SAAuB,mBAAA;;;;UAoD1B,QAAA;EAAA,SACC,KAAA,UAAe,UAAA,CAAW,KAAA;EAAA,SAC1B,SAAA;AAAA;AAAA,cAGE,eAAA,GAAmB,MAAA,EAAQ,SAAA,EAAW,IAAA,GAAO,QAAA,KAAQ,KAAA,CAAA,KAAA,CAAA,cAAA"}
@@ -1,118 +0,0 @@
1
- import { HttpError } from "../errors.mjs";
2
- import { deriveIngestTarget } from "../../core/runtime/ingest-target.mjs";
3
- import { nativeFetch } from "../../core/runtime/native-fetch.mjs";
4
- import { Context, Effect, Layer, Schema } from "effect";
5
- import { DEFAULT_TIMEOUT_MS, withTimeoutFail } from "@interfere/effect-utils/retry";
6
-
7
- //#region src/effect/layers/http.layer.ts
8
- var HttpServiceTag = class extends Context.Tag("@interfere/react/Http")() {};
9
- /**
10
- * Generates a W3C traceparent header
11
- */
12
- const generateTraceparent = () => {
13
- return `00-${randomHex(16)}-${randomHex(8)}-01`;
14
- };
15
- function randomHex(bytes) {
16
- const RANDOM_MAX = 256;
17
- const HEX_BYTE_WIDTH = 2;
18
- const buf = new Uint8Array(bytes);
19
- if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") crypto.getRandomValues(buf);
20
- else for (let i = 0; i < bytes; i += 1) buf[i] = Math.floor(Math.random() * RANDOM_MAX);
21
- const HEX_BASE = 16;
22
- return Array.from(buf).map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
23
- }
24
- /**
25
- * Keepalive limits per Fetch spec (https://fetch.spec.whatwg.org/#http-network-or-cache-fetch):
26
- * - If sum of body + inflight keepalive bytes > 64KB, returns network error
27
- * - We use 60KB threshold to leave room for headers/overhead
28
- * - Also limit concurrent keepalive requests to 15 (browser implementation detail)
29
- */
30
- const KEEPALIVE_BYTE_LIMIT = 6e4;
31
- const KEEPALIVE_REQUEST_LIMIT = 15;
32
- const HttpServiceLive = (config, deps) => {
33
- let pendingBodySize = 0;
34
- let pendingCount = 0;
35
- return Layer.succeed(HttpServiceTag, { postEnvelopes: (envelopes) => {
36
- const spanUrl = (() => {
37
- try {
38
- return deriveIngestTarget(config).url;
39
- } catch {
40
- return "<config-error>";
41
- }
42
- })();
43
- return Effect.gen(function* () {
44
- const target = yield* Effect.try({
45
- try: () => deriveIngestTarget(config),
46
- catch: (err) => new HttpError({
47
- status: 0,
48
- message: String(err instanceof Error ? err.message : err),
49
- url: "<config-error>"
50
- })
51
- });
52
- yield* Effect.logDebug("Sending batch to ingest").pipe(Effect.annotateLogs({
53
- url: target.url,
54
- envelopeCount: envelopes.length
55
- }));
56
- const headers = new Headers(target.headers);
57
- const url = target.url;
58
- headers.set("traceparent", generateTraceparent());
59
- const TIMEOUT_MS = deps?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
60
- const doFetch = deps?.fetch ?? nativeFetch;
61
- const HTTP_METHOD_POST = "POST";
62
- const body = yield* Schema.encode(Schema.parseJson())(envelopes).pipe(Effect.mapError((parseError) => new HttpError({
63
- status: 0,
64
- message: `JSON encoding failed: ${String(parseError)}`,
65
- url
66
- })));
67
- const requestSize = body.length;
68
- pendingBodySize += requestSize;
69
- pendingCount += 1;
70
- const useKeepalive = pendingBodySize <= KEEPALIVE_BYTE_LIMIT && pendingCount <= KEEPALIVE_REQUEST_LIMIT;
71
- const response = yield* withTimeoutFail(Effect.tryPromise({
72
- try: async () => {
73
- try {
74
- return await doFetch(url, {
75
- method: HTTP_METHOD_POST,
76
- headers,
77
- body,
78
- keepalive: useKeepalive
79
- });
80
- } finally {
81
- pendingBodySize -= requestSize;
82
- pendingCount -= 1;
83
- }
84
- },
85
- catch: (error) => new HttpError({
86
- status: 0,
87
- message: `Network error: ${String(error)}`,
88
- url
89
- })
90
- }), TIMEOUT_MS, () => new HttpError({
91
- status: 0,
92
- message: "Network timeout",
93
- url
94
- }));
95
- if (!response.ok) {
96
- const body = yield* Effect.promise(() => response.text()).pipe(Effect.orElse(() => Effect.succeed("")));
97
- if (response.status === 422) return yield* new HttpError({
98
- status: response.status,
99
- message: "Invalid envelopes (schema mismatch)",
100
- url,
101
- body
102
- });
103
- return yield* new HttpError({
104
- status: response.status,
105
- message: `Ingest failed: ${response.status} ${response.statusText}`,
106
- url,
107
- body
108
- });
109
- }
110
- }).pipe(Effect.withSpan("http.postEnvelopes", { attributes: {
111
- envelopeCount: envelopes.length,
112
- url: spanUrl
113
- } }));
114
- } });
115
- };
116
-
117
- //#endregion
118
- export { HttpServiceLive, HttpServiceTag };
@@ -1 +0,0 @@
1
- {"version":3,"file":"http.layer.mjs","names":[],"sources":["../../../src/effect/layers/http.layer.ts"],"sourcesContent":["import {\n DEFAULT_TIMEOUT_MS,\n withTimeoutFail,\n} from \"@interfere/effect-utils/retry\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport { Context, Effect, Layer, Schema } from \"effect\";\n\nimport { deriveIngestTarget } from \"../../core/runtime/ingest-target.js\";\nimport { nativeFetch } from \"../../core/runtime/native-fetch.js\";\nimport { HttpError } from \"../errors.js\";\nimport type { SdkConfig } from \"./config.layer.js\";\n\nexport interface HttpService {\n readonly postEnvelopes: (\n envelopes: Envelope[]\n ) => Effect.Effect<void, HttpError>;\n}\n\nexport class HttpServiceTag extends Context.Tag(\"@interfere/react/Http\")<\n HttpServiceTag,\n HttpService\n>() {}\n\n/**\n * Generates a W3C traceparent header\n */\nconst generateTraceparent = (): string => {\n const version = \"00\";\n const TRACE_ID_BYTES = 16;\n const SPAN_ID_BYTES = 8;\n const traceId = randomHex(TRACE_ID_BYTES);\n const spanId = randomHex(SPAN_ID_BYTES);\n const flags = \"01\";\n return `${version}-${traceId}-${spanId}-${flags}`;\n};\n\nfunction randomHex(bytes: number): string {\n const RANDOM_MAX = 256;\n const HEX_BYTE_WIDTH = 2;\n const buf = new Uint8Array(bytes);\n\n if (\n typeof crypto !== \"undefined\" &&\n typeof crypto.getRandomValues === \"function\"\n ) {\n crypto.getRandomValues(buf);\n } else {\n for (let i = 0; i < bytes; i += 1) {\n buf[i] = Math.floor(Math.random() * RANDOM_MAX);\n }\n }\n\n const HEX_BASE = 16;\n return Array.from(buf)\n .map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, \"0\"))\n .join(\"\");\n}\n\n/**\n * Keepalive limits per Fetch spec (https://fetch.spec.whatwg.org/#http-network-or-cache-fetch):\n * - If sum of body + inflight keepalive bytes > 64KB, returns network error\n * - We use 60KB threshold to leave room for headers/overhead\n * - Also limit concurrent keepalive requests to 15 (browser implementation detail)\n */\nconst KEEPALIVE_BYTE_LIMIT = 60_000;\nconst KEEPALIVE_REQUEST_LIMIT = 15; // Arbitrary limit\n\n/**\n * Creates an HTTP service layer for posting envelopes\n */\ninterface HttpDeps {\n readonly fetch?: typeof globalThis.fetch;\n readonly timeoutMs?: number;\n}\n\nexport const HttpServiceLive = (config: SdkConfig, deps?: HttpDeps) => {\n // Track pending keepalive request state across all concurrent requests.\n // These are closure variables that persist for the lifetime of the layer.\n let pendingBodySize = 0;\n let pendingCount = 0;\n\n return Layer.succeed(HttpServiceTag, {\n postEnvelopes: (envelopes: Envelope[]) => {\n const spanUrl = (() => {\n try {\n return deriveIngestTarget(config).url;\n } catch {\n return \"<config-error>\";\n }\n })();\n\n return Effect.gen(function* () {\n const target = yield* Effect.try({\n try: () => deriveIngestTarget(config),\n catch: (err) =>\n new HttpError({\n status: 0,\n message: String(err instanceof Error ? err.message : err),\n url: \"<config-error>\",\n }),\n });\n\n yield* Effect.logDebug(\"Sending batch to ingest\").pipe(\n Effect.annotateLogs({\n url: target.url,\n envelopeCount: envelopes.length,\n })\n );\n\n const headers = new Headers(target.headers);\n const url = target.url;\n\n // Add traceparent header\n headers.set(\"traceparent\", generateTraceparent());\n\n // Send the request with timeout\n const TIMEOUT_MS = deps?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n // Use native fetch to avoid infinite loops when errors plugin patches window.fetch\n // If SDK's own request fails with patched fetch, it would be captured as an error,\n // triggering another send attempt, which could fail and loop indefinitely.\n const doFetch = deps?.fetch ?? nativeFetch;\n const HTTP_METHOD_POST = \"POST\" as const;\n\n const body = yield* Schema.encode(Schema.parseJson())(envelopes).pipe(\n Effect.mapError(\n (parseError) =>\n new HttpError({\n status: 0,\n message: `JSON encoding failed: ${String(parseError)}`,\n url,\n })\n )\n );\n const requestSize = body.length;\n\n // Track this request's size for keepalive limit calculation\n pendingBodySize += requestSize;\n pendingCount += 1;\n\n // Keepalive allows requests to complete even when navigating away, but has limits:\n // - Per Fetch spec, if sum of inflight keepalive bytes > 64KB, it fails with network error\n // - The limit is aggregate across ALL concurrent keepalive requests, not per-request\n // - Caused \"Failed to fetch\" (status 0) errors in production when batches were large\n // - We only enable keepalive when under both size and count limits\n // See: https://fetch.spec.whatwg.org/#http-network-or-cache-fetch (step 8.10.5)\n const useKeepalive =\n pendingBodySize <= KEEPALIVE_BYTE_LIMIT &&\n pendingCount <= KEEPALIVE_REQUEST_LIMIT;\n\n const response = yield* withTimeoutFail(\n Effect.tryPromise({\n try: async () => {\n try {\n return await doFetch(url, {\n method: HTTP_METHOD_POST,\n headers,\n body,\n keepalive: useKeepalive,\n });\n } finally {\n // Always decrement counters when request completes (success or failure)\n pendingBodySize -= requestSize;\n pendingCount -= 1;\n }\n },\n catch: (error) =>\n new HttpError({\n status: 0,\n message: `Network error: ${String(error)}`,\n url,\n }),\n }),\n TIMEOUT_MS,\n () =>\n new HttpError({\n status: 0,\n message: \"Network timeout\",\n url,\n })\n );\n\n if (!response.ok) {\n const body = yield* Effect.promise(() => response.text()).pipe(\n Effect.orElse(() => Effect.succeed(\"\"))\n );\n\n // If the server tells us the envelopes are malformed, treat this as a\n // permanent failure so the queue layer can drop them instead of retrying.\n if (response.status === 422) {\n return yield* new HttpError({\n status: response.status,\n message: \"Invalid envelopes (schema mismatch)\",\n url,\n body,\n });\n }\n\n return yield* new HttpError({\n status: response.status,\n message: `Ingest failed: ${response.status} ${response.statusText}`,\n url,\n body,\n });\n }\n }).pipe(\n Effect.withSpan(\"http.postEnvelopes\", {\n attributes: {\n envelopeCount: envelopes.length,\n url: spanUrl,\n },\n })\n );\n },\n });\n};\n"],"mappings":"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"}
@@ -1,31 +0,0 @@
1
- import { ConfigTag } from "../tags.mjs";
2
- import { CircuitBreakerTag } from "./circuit-breaker.layer.mjs";
3
- import { HttpServiceTag } from "./http.layer.mjs";
4
- import { Context, Effect, Layer, Scope } from "effect";
5
- import { CircuitBreakerState } from "@interfere/effect-utils/circuit-breaker";
6
- import { Envelope } from "@interfere/types/sdk/envelope";
7
-
8
- //#region src/effect/layers/queue.layer.d.ts
9
- interface QueueMetrics {
10
- readonly circuitBreakerState: CircuitBreakerState;
11
- readonly failureCount: number;
12
- readonly queueDepth: number;
13
- readonly successCount: number;
14
- }
15
- interface QueueService {
16
- readonly add: (envelope: Envelope) => Effect.Effect<void>;
17
- readonly boundedFlush: (timeoutMs: number) => Effect.Effect<void>;
18
- readonly flush: () => Effect.Effect<void>;
19
- readonly getMetrics: () => Effect.Effect<QueueMetrics>;
20
- readonly setReady: (ready: boolean) => Effect.Effect<void>;
21
- readonly size: () => Effect.Effect<number>;
22
- readonly startBatchProcessor: () => Effect.Effect<void, never, Scope.Scope>;
23
- }
24
- declare const QueueServiceTag_base: Context.TagClass<QueueServiceTag, "@interfere/react/Queue", QueueService>;
25
- declare class QueueServiceTag extends QueueServiceTag_base {}
26
- /**
27
- * Creates the queue service layer
28
- */
29
- declare const QueueServiceLive: Layer.Layer<QueueServiceTag, never, CircuitBreakerTag | HttpServiceTag | ConfigTag>;
30
- //#endregion
31
- export { QueueMetrics, QueueService, QueueServiceLive, QueueServiceTag };
@@ -1 +0,0 @@
1
- {"version":3,"file":"queue.layer.d.mts","names":[],"sources":["../../../src/effect/layers/queue.layer.ts"],"mappings":";;;;;;;;UA8CiB,YAAA;EAAA,SACN,mBAAA,EAAqB,mBAAA;EAAA,SACrB,YAAA;EAAA,SACA,UAAA;EAAA,SACA,YAAA;AAAA;AAAA,UAGM,YAAA;EAAA,SACN,GAAA,GAAM,QAAA,EAAU,QAAA,KAAa,MAAA,CAAO,MAAA;EAAA,SACpC,YAAA,GAAe,SAAA,aAAsB,MAAA,CAAO,MAAA;EAAA,SAC5C,KAAA,QAAa,MAAA,CAAO,MAAA;EAAA,SACpB,UAAA,QAAkB,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA,SAChC,QAAA,GAAW,KAAA,cAAmB,MAAA,CAAO,MAAA;EAAA,SACrC,IAAA,QAAY,MAAA,CAAO,MAAA;EAAA,SACnB,mBAAA,QAA2B,MAAA,CAAO,MAAA,cAAoB,KAAA,CAAM,KAAA;AAAA;AAAA,cACtE,oBAAA;cAEY,eAAA,SAAwB,oBAAA;;;;cA0BxB,gBAAA,EAAgB,KAAA,CAAA,KAAA,CAAA,eAAA,SAAA,iBAAA,GAAA,cAAA,GAAA,SAAA"}
@@ -1,257 +0,0 @@
1
- import { ConfigTag } from "../tags.mjs";
2
- import { CircuitBreakerTag } from "./circuit-breaker.layer.mjs";
3
- import { HttpServiceTag } from "./http.layer.mjs";
4
- import { createStorageAdapter } from "./storage.layer.mjs";
5
- import { Chunk, Clock, Context, Duration, Effect, Layer, Queue, Ref, Runtime, Schedule } from "effect";
6
- import { InterfereLogger } from "@interfere/effect-utils/observability";
7
- import { CircuitBreakerOpen, DEFAULT_CIRCUIT_BREAKER_CONFIG } from "@interfere/effect-utils/circuit-breaker";
8
- import { DEFAULT_RETRY_INITIAL_MS, DEFAULT_RETRY_MAX_MS, DEFAULT_RETRY_RECURS, createBackoffSchedule } from "@interfere/effect-utils/retry";
9
-
10
- //#region src/effect/layers/queue.layer.ts
11
- const MAX_RETRY_ATTEMPTS = 3;
12
- const DEFER_SEND_DELAY_MS = 250;
13
- const BREAKER_RETRY_BASE_MS = 5e3;
14
- const BREAKER_RETRY_MAX_MS = 3e4;
15
- const BREAKER_JITTER_FACTOR = .3;
16
- const BREAKER_COALESCE_REMAINING_MS = 250;
17
- const TEST_OFFLINE_REQUEUE_LIMIT = 3;
18
- const OFFLINE_RETRY_BASE_MS = DEFAULT_RETRY_INITIAL_MS;
19
- const OFFLINE_RETRY_MAX_MS = DEFAULT_RETRY_MAX_MS;
20
- const OFFLINE_JITTER_FACTOR = .3;
21
- var QueueServiceTag = class extends Context.Tag("@interfere/react/Queue")() {};
22
- /**
23
- * Creates the retry schedule for failed batches
24
- */
25
- const createRetrySchedule = () => createBackoffSchedule(DEFAULT_RETRY_INITIAL_MS, DEFAULT_RETRY_MAX_MS, MAX_RETRY_ATTEMPTS ?? DEFAULT_RETRY_RECURS);
26
- /**
27
- * Checks if the browser is online
28
- */
29
- const isOnline = () => Effect.sync(() => typeof navigator !== "undefined" ? navigator.onLine : true);
30
- /**
31
- * Creates the queue service layer
32
- */
33
- const QueueServiceLive = Layer.scoped(QueueServiceTag, Effect.gen(function* () {
34
- const config = yield* ConfigTag;
35
- const http = yield* HttpServiceTag;
36
- const storage = createStorageAdapter({ offline: config.offline });
37
- const capacity = storage.capacity();
38
- const circuitBreaker = yield* CircuitBreakerTag;
39
- const metrics = yield* Ref.make({
40
- queueDepth: 0,
41
- successCount: 0,
42
- failureCount: 0,
43
- circuitBreakerState: "closed"
44
- });
45
- const envelopeQueue = yield* Queue.sliding(capacity);
46
- const readyRef = yield* Ref.make(false);
47
- const breakerRetryCount = yield* Ref.make(0);
48
- const runtime = yield* Effect.runtime();
49
- yield* Effect.gen(function* () {
50
- const initial = yield* storage.load();
51
- const filtered = initial.filter((e) => typeof e.uuid === "string" && e.uuid.length > 0);
52
- if (filtered.length !== initial.length) {
53
- yield* storage.save(filtered).pipe(Effect.ignore);
54
- yield* Effect.logWarning("Purged persisted envelopes missing UUID").pipe(Effect.annotateLogs({
55
- before: initial.length,
56
- after: filtered.length,
57
- removed: initial.length - filtered.length
58
- }));
59
- }
60
- if (filtered.length > 0) {
61
- const seed = filtered.length > capacity ? filtered.slice(filtered.length - capacity) : filtered;
62
- yield* Queue.offerAll(envelopeQueue, Chunk.fromIterable(seed));
63
- if (seed.length !== filtered.length) yield* storage.save(seed).pipe(Effect.ignore);
64
- yield* Effect.logDebug("Loaded persisted envelopes").pipe(Effect.annotateLogs({
65
- count: seed.length,
66
- truncated: seed.length !== filtered.length
67
- }));
68
- }
69
- });
70
- const requeueWithLog = Effect.fn("queue.requeueWithLog")(function* (batch, message, extra) {
71
- yield* Queue.offerAll(envelopeQueue, batch);
72
- yield* Effect.logWarning(message).pipe(Effect.annotateLogs({
73
- batchSize: Chunk.size(batch),
74
- ...extra ?? {}
75
- }));
76
- });
77
- const performHttpSend = (envelopes, ids) => http.postEnvelopes(envelopes).pipe(Effect.tap(() => storage.removeByIds(ids).pipe(Effect.ignore)), Effect.tap(() => Ref.update(metrics, (m) => ({
78
- ...m,
79
- successCount: m.successCount + envelopes.length
80
- }))), Effect.tap(() => Effect.logDebug("Batch sent successfully").pipe(Effect.annotateLogs({ batchSize: envelopes.length }))), Effect.catchTag("HttpError", (error) => Effect.gen(function* () {
81
- if (error.status === 422) {
82
- yield* storage.removeByIds(ids).pipe(Effect.ignore);
83
- yield* Ref.update(metrics, (m) => ({
84
- ...m,
85
- failureCount: m.failureCount + envelopes.length
86
- }));
87
- yield* Effect.logWarning("Dropping invalid envelopes after 422 response").pipe(Effect.annotateLogs({
88
- batchSize: envelopes.length,
89
- status: error.status,
90
- url: error.url,
91
- body: error.body
92
- }));
93
- return;
94
- }
95
- yield* Effect.all([Effect.logWarning("Failed to send batch").pipe(Effect.annotateLogs({
96
- batchSize: envelopes.length,
97
- status: error.status,
98
- url: error.url,
99
- body: error.body,
100
- error: String(error)
101
- })), Ref.update(metrics, (m) => ({
102
- ...m,
103
- failureCount: m.failureCount + 1
104
- }))]);
105
- return yield* error;
106
- }).pipe(Effect.withSpan("queue.handleHttpError"))), Effect.retry(createRetrySchedule()));
107
- const sendWithBreaker = (envelopes, ids, batch) => circuitBreaker.protect(performHttpSend(envelopes, ids)).pipe(Effect.catchIf((error) => error instanceof CircuitBreakerOpen, () => Effect.gen(function* () {
108
- const retryCount = yield* Ref.getAndUpdate(breakerRetryCount, (n) => n + 1);
109
- const backoffMs = Math.min(BREAKER_RETRY_BASE_MS * 2 ** retryCount, BREAKER_RETRY_MAX_MS);
110
- const jitter = Math.random() * BREAKER_JITTER_FACTOR * backoffMs;
111
- const sleepMs = Math.floor(backoffMs + jitter);
112
- const data = yield* circuitBreaker.breakerData.get;
113
- const now = yield* Clock.currentTimeMillis;
114
- const since = data.lastFailureTime ? now - data.lastFailureTime : 0;
115
- yield* requeueWithLog(batch, "Circuit breaker prevented batch send - requeueing", {
116
- remainingMs: Math.max(0, DEFAULT_CIRCUIT_BREAKER_CONFIG.resetMs - since),
117
- retryCount,
118
- nextRetryMs: sleepMs
119
- });
120
- yield* Effect.sleep(Duration.millis(sleepMs));
121
- }).pipe(Effect.withSpan("queue.handleCircuitBreakerOpen"))), Effect.tap(() => Ref.set(breakerRetryCount, 0)), Effect.catchAll((error) => Effect.gen(function* () {
122
- if (config.offline?.enabled) {
123
- yield* requeueWithLog(batch, "Failed to send batch after all retries - requeueing for later", { error: String(error) });
124
- return;
125
- }
126
- const lostCount = Chunk.size(batch);
127
- yield* storage.removeByIds(ids).pipe(Effect.ignore);
128
- yield* Ref.update(metrics, (m) => ({
129
- ...m,
130
- failureCount: m.failureCount + lostCount
131
- }));
132
- yield* Effect.logError("Envelopes lost due to network error (offline storage disabled)").pipe(Effect.annotateLogs({
133
- lostCount,
134
- error: String(error),
135
- hint: "Enable offline storage to persist failed envelopes"
136
- }));
137
- }).pipe(Effect.withSpan("queue.handleSendFailure"))), Effect.tap(() => Effect.gen(function* () {
138
- const state = yield* circuitBreaker.getState;
139
- yield* Ref.update(metrics, (m) => ({
140
- ...m,
141
- circuitBreakerState: state
142
- }));
143
- }).pipe(Effect.withSpan("queue.updateBreakerMetrics"))), Effect.withSpan("queue.sendBatch", { attributes: { batchSize: envelopes.length } }));
144
- const offlineAttemptsRef = yield* Ref.make(0);
145
- const handleOffline = Effect.fn("queue.handleOffline")(function* (batch) {
146
- const attempts = yield* Ref.getAndUpdate(offlineAttemptsRef, (n) => n + 1);
147
- if (typeof process !== "undefined" && process.env?.VITEST && attempts >= TEST_OFFLINE_REQUEUE_LIMIT) {
148
- yield* Effect.logWarning(`Offline - dropping batch after ${TEST_OFFLINE_REQUEUE_LIMIT} attempts (test mode)`);
149
- return;
150
- }
151
- yield* requeueWithLog(batch, "Offline - requeueing batch");
152
- const base = OFFLINE_RETRY_BASE_MS;
153
- const capped = Math.min(base * 2 ** attempts, OFFLINE_RETRY_MAX_MS);
154
- const jitter = Math.random() * OFFLINE_JITTER_FACTOR * capped;
155
- const sleepMs = Math.floor(capped + jitter);
156
- yield* Effect.sleep(Duration.millis(sleepMs));
157
- });
158
- const sendBatch = Effect.fn("queue.sendBatch")(function* (batch) {
159
- if (Chunk.isEmpty(batch)) return;
160
- if (!(yield* isOnline())) {
161
- yield* handleOffline(batch);
162
- return;
163
- }
164
- const envelopes = Chunk.toArray(batch);
165
- yield* sendWithBreaker(envelopes, envelopes.map((e) => e.uuid).filter((v) => typeof v === "string"), batch);
166
- });
167
- const flush = Effect.fn("queue.flush")(function* () {
168
- yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
169
- const items = yield* Queue.takeAll(envelopeQueue);
170
- yield* Ref.update(metrics, (m) => ({
171
- ...m,
172
- queueDepth: 0
173
- }));
174
- if (Chunk.isEmpty(items)) return;
175
- yield* Effect.onInterrupt(restore(sendBatch(items)), () => requeueWithLog(items, "Flush interrupted - requeueing drained batch", { reason: "interrupt" }));
176
- }));
177
- });
178
- const processBatch = Effect.gen(function* () {
179
- if (!(yield* Ref.get(readyRef))) {
180
- yield* Effect.sleep(Duration.millis(DEFER_SEND_DELAY_MS));
181
- return;
182
- }
183
- const breakerState = yield* circuitBreaker.getState;
184
- if (breakerState === "open") {
185
- const data = yield* circuitBreaker.breakerData.get;
186
- const now = yield* Clock.currentTimeMillis;
187
- const since = data.lastFailureTime ? now - data.lastFailureTime : 0;
188
- const remainingMs = Math.max(0, DEFAULT_CIRCUIT_BREAKER_CONFIG.resetMs - since);
189
- yield* Ref.update(metrics, (m) => ({
190
- ...m,
191
- circuitBreakerState: breakerState
192
- }));
193
- if (remainingMs > BREAKER_COALESCE_REMAINING_MS) {
194
- yield* Effect.logDebug("Circuit breaker open - coalescing batches until cooldown is small").pipe(Effect.annotateLogs({ remainingMs }));
195
- yield* Effect.sleep(Duration.millis(remainingMs - BREAKER_COALESCE_REMAINING_MS));
196
- return;
197
- }
198
- }
199
- const batchSize = config.batch.size;
200
- const batchMs = config.batch.ms;
201
- const pending = yield* Queue.takeBetween(envelopeQueue, 1, batchSize).pipe(Effect.timeout(Duration.millis(batchMs)), Effect.catchTag("TimeoutException", () => Queue.takeAll(envelopeQueue).pipe(Effect.map((items) => Chunk.isEmpty(items) ? Chunk.empty() : items))));
202
- if (Chunk.isNonEmpty(pending)) {
203
- yield* Effect.logDebug("Processing batch").pipe(Effect.annotateLogs({ batchSize: Chunk.size(pending) }));
204
- yield* sendBatch(pending);
205
- }
206
- });
207
- const startBatchProcessor = () => processBatch.pipe(Effect.catchAllCause((cause) => Effect.logError("Batch processor error", { cause: String(cause) })), Effect.repeat(Schedule.forever), Effect.provide(InterfereLogger.layer()), Effect.forkScoped, Effect.as(Effect.void));
208
- const add = Effect.fn("queue.add")(function* (envelope) {
209
- const size = yield* Queue.size(envelopeQueue);
210
- if (size >= capacity) {
211
- const toDrop = Math.max(1, size - capacity + 1);
212
- yield* storage.dropOldest(toDrop).pipe(Effect.ignore);
213
- }
214
- yield* storage.append([envelope]).pipe(Effect.ignore);
215
- yield* Queue.offer(envelopeQueue, envelope);
216
- yield* Effect.logDebug(`[${envelope.type}] Envelope '${envelope.uuid}' added to queue`).pipe(Effect.annotateLogs({
217
- envelopeId: envelope.uuid,
218
- queueSize: size + 1
219
- }));
220
- });
221
- const size = () => Queue.size(envelopeQueue).pipe(Effect.map((n) => Math.max(0, n)));
222
- const boundedFlush = (timeoutMs) => Effect.gen(function* () {
223
- const fiber = yield* Effect.fork(flush());
224
- yield* Effect.race(fiber.await.pipe(Effect.asVoid), Effect.sleep(Duration.millis(timeoutMs)));
225
- }).pipe(Effect.asVoid);
226
- const setReady = (ready) => Ref.set(readyRef, ready).pipe(Effect.zipRight(ready ? Effect.logDebug("Queue ready - enabling batch sends") : Effect.logDebug("Queue not ready - deferring batch sends")));
227
- const getMetrics = () => Effect.gen(function* () {
228
- const m = yield* Ref.get(metrics);
229
- const depth = yield* Queue.size(envelopeQueue);
230
- return {
231
- ...m,
232
- queueDepth: Math.max(0, depth)
233
- };
234
- });
235
- if (config.offline?.enabled && typeof window !== "undefined") {
236
- const onOnline = () => {
237
- Runtime.runPromise(runtime)(flush().pipe(Effect.tap(() => Effect.logDebug("Flushed queue after coming back online")))).catch(() => void 0);
238
- };
239
- window.addEventListener("online", onOnline);
240
- yield* Effect.addFinalizer(() => Effect.sync(() => {
241
- window.removeEventListener("online", onOnline);
242
- }));
243
- yield* Effect.logDebug("Registered 'online' event listener for automatic flush");
244
- }
245
- return {
246
- add,
247
- size,
248
- flush,
249
- boundedFlush,
250
- startBatchProcessor,
251
- setReady,
252
- getMetrics
253
- };
254
- }));
255
-
256
- //#endregion
257
- export { QueueServiceLive, QueueServiceTag };
@@ -1 +0,0 @@
1
- {"version":3,"file":"queue.layer.mjs","names":[],"sources":["../../../src/effect/layers/queue.layer.ts"],"sourcesContent":["import {\n CircuitBreakerOpen,\n type CircuitBreakerState,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n} from \"@interfere/effect-utils/circuit-breaker\";\nimport { InterfereLogger } from \"@interfere/effect-utils/observability\";\nimport {\n createBackoffSchedule,\n DEFAULT_RETRY_INITIAL_MS,\n DEFAULT_RETRY_MAX_MS,\n DEFAULT_RETRY_RECURS,\n} from \"@interfere/effect-utils/retry\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport {\n Chunk,\n Clock,\n Context,\n Duration,\n Effect,\n Layer,\n Queue,\n Ref,\n Runtime,\n Schedule,\n type Scope,\n} from \"effect\";\n\nimport type { HttpError } from \"../errors.js\";\nimport { ConfigTag } from \"../tags.js\";\nimport { CircuitBreakerTag } from \"./circuit-breaker.layer.js\";\nimport { HttpServiceTag } from \"./http.layer.js\";\nimport { createStorageAdapter, type StorageAdapter } from \"./storage.layer.js\";\n\n// Configuration constants\nconst MAX_RETRY_ATTEMPTS = 3; // Short-term retries for transient failures\nconst DEFER_SEND_DELAY_MS = 250; // Delay before sending when not ready\nconst BREAKER_RETRY_BASE_MS = 5000; // Base retry delay when breaker is open\nconst BREAKER_RETRY_MAX_MS = 30_000; // Max retry delay when breaker is open\nconst BREAKER_JITTER_FACTOR = 0.3; // 30% jitter for exponential backoff\nconst BREAKER_COALESCE_REMAINING_MS = 250; // Skip sending while breaker has more than this remaining\nconst TEST_OFFLINE_REQUEUE_LIMIT = 3; // Test-only cap to avoid infinite loops when offline\nconst OFFLINE_RETRY_BASE_MS = DEFAULT_RETRY_INITIAL_MS; // Base offline backoff\nconst OFFLINE_RETRY_MAX_MS = DEFAULT_RETRY_MAX_MS; // Cap offline backoff\nconst OFFLINE_JITTER_FACTOR = 0.3; // 30% jitter for offline backoff\n\nexport interface QueueMetrics {\n readonly circuitBreakerState: CircuitBreakerState;\n readonly failureCount: number;\n readonly queueDepth: number;\n readonly successCount: number;\n}\n\nexport interface QueueService {\n readonly add: (envelope: Envelope) => Effect.Effect<void>;\n readonly boundedFlush: (timeoutMs: number) => Effect.Effect<void>;\n readonly flush: () => Effect.Effect<void>;\n readonly getMetrics: () => Effect.Effect<QueueMetrics>;\n readonly setReady: (ready: boolean) => Effect.Effect<void>;\n readonly size: () => Effect.Effect<number>;\n readonly startBatchProcessor: () => Effect.Effect<void, never, Scope.Scope>;\n}\n\nexport class QueueServiceTag extends Context.Tag(\"@interfere/react/Queue\")<\n QueueServiceTag,\n QueueService\n>() {}\n\n/**\n * Creates the retry schedule for failed batches\n */\nconst createRetrySchedule = () =>\n createBackoffSchedule(\n DEFAULT_RETRY_INITIAL_MS,\n DEFAULT_RETRY_MAX_MS,\n MAX_RETRY_ATTEMPTS ?? DEFAULT_RETRY_RECURS\n );\n\n/**\n * Checks if the browser is online\n */\nconst isOnline = () =>\n Effect.sync(() =>\n typeof navigator !== \"undefined\" ? navigator.onLine : true\n );\n\n/**\n * Creates the queue service layer\n */\nexport const QueueServiceLive = Layer.scoped(\n QueueServiceTag,\n Effect.gen(function* () {\n const config = yield* ConfigTag;\n const http = yield* HttpServiceTag;\n\n // Create storage based on offline config\n // When offline.enabled is false (default), uses in-memory storage\n // When offline.enabled is true, uses IndexedDB or localStorage for persistence\n const storage: StorageAdapter = createStorageAdapter({\n offline: config.offline,\n });\n const capacity = storage.capacity();\n\n // Inject circuit breaker from context\n const circuitBreaker = yield* CircuitBreakerTag;\n\n const metrics = yield* Ref.make<QueueMetrics>({\n queueDepth: 0,\n successCount: 0,\n failureCount: 0,\n circuitBreakerState: \"closed\",\n });\n\n const envelopeQueue = yield* Queue.sliding<Envelope>(capacity);\n const readyRef = yield* Ref.make<boolean>(false);\n const breakerRetryCount = yield* Ref.make<number>(0);\n const runtime = yield* Effect.runtime();\n\n yield* Effect.gen(function* () {\n const initial = yield* storage.load();\n\n // Filter out any entries missing required identifiers\n const filtered = initial.filter(\n (e) => typeof e.uuid === \"string\" && e.uuid.length > 0\n );\n\n if (filtered.length !== initial.length) {\n // Persist the cleaned set best-effort\n yield* storage.save(filtered).pipe(Effect.ignore);\n yield* Effect.logWarning(\n \"Purged persisted envelopes missing UUID\"\n ).pipe(\n Effect.annotateLogs({\n before: initial.length,\n after: filtered.length,\n removed: initial.length - filtered.length,\n })\n );\n }\n\n if (filtered.length > 0) {\n const seed =\n filtered.length > capacity\n ? filtered.slice(filtered.length - capacity)\n : filtered;\n\n yield* Queue.offerAll(envelopeQueue, Chunk.fromIterable(seed));\n\n // Ensure storage mirrors in-memory when trimming\n if (seed.length !== filtered.length) {\n yield* storage.save(seed).pipe(Effect.ignore);\n }\n\n yield* Effect.logDebug(\"Loaded persisted envelopes\").pipe(\n Effect.annotateLogs({\n count: seed.length,\n truncated: seed.length !== filtered.length,\n })\n );\n }\n });\n\n const requeueWithLog = Effect.fn(\"queue.requeueWithLog\")(function* (\n batch: Chunk.Chunk<Envelope>,\n message: string,\n extra?: Record<string, unknown>\n ) {\n yield* Queue.offerAll(envelopeQueue, batch);\n yield* Effect.logWarning(message).pipe(\n Effect.annotateLogs({\n batchSize: Chunk.size(batch),\n ...(extra ?? {}),\n })\n );\n });\n\n const performHttpSend = (envelopes: Envelope[], ids: string[]) =>\n http.postEnvelopes(envelopes).pipe(\n Effect.tap(() => storage.removeByIds(ids).pipe(Effect.ignore)),\n Effect.tap(() =>\n Ref.update(metrics, (m) => ({\n ...m,\n successCount: m.successCount + envelopes.length,\n }))\n ),\n Effect.tap(() =>\n Effect.logDebug(\"Batch sent successfully\").pipe(\n Effect.annotateLogs({\n batchSize: envelopes.length,\n })\n )\n ),\n Effect.catchTag(\"HttpError\", (error: HttpError) =>\n Effect.gen(function* () {\n // If the server says the envelopes are invalid (schema mismatch),\n // drop this batch permanently instead of retrying.\n if (error.status === 422) {\n yield* storage.removeByIds(ids).pipe(Effect.ignore);\n yield* Ref.update(metrics, (m) => ({\n ...m,\n failureCount: m.failureCount + envelopes.length,\n }));\n yield* Effect.logWarning(\n \"Dropping invalid envelopes after 422 response\"\n ).pipe(\n Effect.annotateLogs({\n batchSize: envelopes.length,\n status: error.status,\n url: error.url,\n body: error.body,\n })\n );\n return;\n }\n\n // For all other HTTP errors, log and retry as before\n yield* Effect.all([\n Effect.logWarning(\"Failed to send batch\").pipe(\n Effect.annotateLogs({\n batchSize: envelopes.length,\n status: error.status,\n url: error.url,\n body: error.body,\n error: String(error),\n })\n ),\n Ref.update(metrics, (m) => ({\n ...m,\n failureCount: m.failureCount + 1,\n })),\n ]);\n\n // Re‑fail so the retry logic still kicks in\n return yield* error;\n }).pipe(Effect.withSpan(\"queue.handleHttpError\"))\n ),\n Effect.retry(createRetrySchedule())\n );\n\n const sendWithBreaker = (\n envelopes: Envelope[],\n ids: string[],\n batch: Chunk.Chunk<Envelope>\n ) =>\n circuitBreaker.protect(performHttpSend(envelopes, ids)).pipe(\n Effect.catchIf(\n (error): error is CircuitBreakerOpen =>\n error instanceof CircuitBreakerOpen,\n () =>\n Effect.gen(function* () {\n const retryCount = yield* Ref.getAndUpdate(\n breakerRetryCount,\n (n) => n + 1\n );\n\n // Calculate exponential backoff with jitter\n const backoffMs = Math.min(\n BREAKER_RETRY_BASE_MS * 2 ** retryCount,\n BREAKER_RETRY_MAX_MS\n );\n const jitter = Math.random() * BREAKER_JITTER_FACTOR * backoffMs;\n const sleepMs = Math.floor(backoffMs + jitter);\n\n // Get remaining time until breaker might reset\n const data = yield* circuitBreaker.breakerData.get;\n const now = yield* Clock.currentTimeMillis;\n const since = data.lastFailureTime\n ? now - data.lastFailureTime\n : 0;\n const remainingMs = Math.max(\n 0,\n DEFAULT_CIRCUIT_BREAKER_CONFIG.resetMs - since\n );\n\n yield* requeueWithLog(\n batch,\n \"Circuit breaker prevented batch send - requeueing\",\n {\n remainingMs,\n retryCount,\n nextRetryMs: sleepMs,\n }\n );\n\n yield* Effect.sleep(Duration.millis(sleepMs));\n }).pipe(Effect.withSpan(\"queue.handleCircuitBreakerOpen\"))\n ),\n Effect.tap(() =>\n // Reset retry count on successful send or after final failure\n Ref.set(breakerRetryCount, 0)\n ),\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n // When offline storage is enabled, requeue for later retry\n // (envelopes are persisted to IndexedDB)\n if (config.offline?.enabled) {\n yield* requeueWithLog(\n batch,\n \"Failed to send batch after all retries - requeueing for later\",\n { error: String(error) }\n );\n return;\n }\n\n // When offline storage is disabled (default), envelopes are lost\n // since in-memory storage doesn't survive page refresh\n const lostCount = Chunk.size(batch);\n yield* storage.removeByIds(ids).pipe(Effect.ignore);\n yield* Ref.update(metrics, (m) => ({\n ...m,\n failureCount: m.failureCount + lostCount,\n }));\n yield* Effect.logError(\n \"Envelopes lost due to network error (offline storage disabled)\"\n ).pipe(\n Effect.annotateLogs({\n lostCount,\n error: String(error),\n hint: \"Enable offline storage to persist failed envelopes\",\n })\n );\n }).pipe(Effect.withSpan(\"queue.handleSendFailure\"))\n ),\n // Always update circuit breaker state in metrics\n Effect.tap(() =>\n Effect.gen(function* () {\n const state = yield* circuitBreaker.getState;\n yield* Ref.update(metrics, (m) => ({\n ...m,\n circuitBreakerState: state,\n }));\n }).pipe(Effect.withSpan(\"queue.updateBreakerMetrics\"))\n ),\n Effect.withSpan(\"queue.sendBatch\", {\n attributes: { batchSize: envelopes.length },\n })\n );\n\n // Track offline requeue attempts in test environments\n const offlineAttemptsRef = yield* Ref.make(0);\n\n const handleOffline = Effect.fn(\"queue.handleOffline\")(function* (\n batch: Chunk.Chunk<Envelope>\n ) {\n // Increment attempts and compute backoff\n const attempts = yield* Ref.getAndUpdate(\n offlineAttemptsRef,\n (n) => n + 1\n );\n if (\n typeof process !== \"undefined\" &&\n process.env?.VITEST &&\n attempts >= TEST_OFFLINE_REQUEUE_LIMIT\n ) {\n yield* Effect.logWarning(\n `Offline - dropping batch after ${TEST_OFFLINE_REQUEUE_LIMIT} attempts (test mode)`\n );\n return;\n }\n\n yield* requeueWithLog(batch, \"Offline - requeueing batch\");\n\n const base = OFFLINE_RETRY_BASE_MS;\n\n const capped = Math.min(base * 2 ** attempts, OFFLINE_RETRY_MAX_MS);\n const jitter = Math.random() * OFFLINE_JITTER_FACTOR * capped;\n const sleepMs = Math.floor(capped + jitter);\n\n yield* Effect.sleep(Duration.millis(sleepMs));\n });\n\n const sendBatch = Effect.fn(\"queue.sendBatch\")(function* (\n batch: Chunk.Chunk<Envelope>\n ) {\n if (Chunk.isEmpty(batch)) {\n return;\n }\n\n const online = yield* isOnline();\n if (!online) {\n yield* handleOffline(batch);\n return;\n }\n\n const envelopes = Chunk.toArray(batch);\n const ids = envelopes\n .map((e) => e.uuid)\n .filter((v): v is string => typeof v === \"string\");\n\n yield* sendWithBreaker(envelopes, ids, batch);\n });\n\n const flush = Effect.fn(\"queue.flush\")(function* () {\n yield* Effect.uninterruptibleMask((restore) =>\n Effect.gen(function* () {\n const items = yield* Queue.takeAll(envelopeQueue);\n yield* Ref.update(metrics, (m) => ({\n ...m,\n queueDepth: 0,\n }));\n\n if (Chunk.isEmpty(items)) {\n return;\n }\n\n // If shutdown/timeout interrupts a drained flush, requeue to avoid drops.\n // We prefer at-least-once delivery and rely on envelope UUIDs for dedupe.\n yield* Effect.onInterrupt(restore(sendBatch(items)), () =>\n requeueWithLog(\n items,\n \"Flush interrupted - requeueing drained batch\",\n {\n reason: \"interrupt\",\n }\n )\n );\n })\n );\n });\n\n const processBatch = Effect.gen(function* () {\n const isReady = yield* Ref.get(readyRef);\n\n if (!isReady) {\n yield* Effect.sleep(Duration.millis(DEFER_SEND_DELAY_MS));\n\n return;\n }\n\n // If the circuit breaker is open, coalesce batches by skipping sends\n // until the remaining cooldown is small. This prevents a retry storm.\n const breakerState = yield* circuitBreaker.getState;\n if (breakerState === \"open\") {\n const data = yield* circuitBreaker.breakerData.get;\n const now = yield* Clock.currentTimeMillis;\n const since = data.lastFailureTime ? now - data.lastFailureTime : 0;\n const remainingMs = Math.max(\n 0,\n DEFAULT_CIRCUIT_BREAKER_CONFIG.resetMs - since\n );\n\n // Reflect breaker state in metrics\n yield* Ref.update(metrics, (m) => ({\n ...m,\n circuitBreakerState: breakerState,\n }));\n\n if (remainingMs > BREAKER_COALESCE_REMAINING_MS) {\n yield* Effect.logDebug(\n \"Circuit breaker open - coalescing batches until cooldown is small\"\n ).pipe(Effect.annotateLogs({ remainingMs }));\n // Sleep until near the reset time to allow one consolidated send attempt\n yield* Effect.sleep(\n Duration.millis(remainingMs - BREAKER_COALESCE_REMAINING_MS)\n );\n return;\n }\n }\n\n const batchSize = config.batch.size;\n const batchMs = config.batch.ms;\n\n const pending = yield* Queue.takeBetween(\n envelopeQueue,\n 1,\n batchSize\n ).pipe(\n Effect.timeout(Duration.millis(batchMs)),\n Effect.catchTag(\"TimeoutException\", () =>\n Queue.takeAll(envelopeQueue).pipe(\n Effect.map((items) =>\n Chunk.isEmpty(items) ? Chunk.empty<Envelope>() : items\n )\n )\n )\n );\n\n if (Chunk.isNonEmpty(pending)) {\n yield* Effect.logDebug(\"Processing batch\").pipe(\n Effect.annotateLogs({ batchSize: Chunk.size(pending) })\n );\n yield* sendBatch(pending);\n }\n });\n\n const startBatchProcessor = () =>\n processBatch.pipe(\n Effect.catchAllCause((cause) =>\n Effect.logError(\"Batch processor error\", { cause: String(cause) })\n ),\n Effect.repeat(Schedule.forever),\n // Ensure the forked fiber uses the same logger configuration\n Effect.provide(InterfereLogger.layer()),\n Effect.forkScoped,\n Effect.as(Effect.void)\n );\n\n const add = Effect.fn(\"queue.add\")(function* (envelope: Envelope) {\n const size = yield* Queue.size(envelopeQueue);\n\n if (size >= capacity) {\n const toDrop = Math.max(1, size - capacity + 1);\n\n yield* storage.dropOldest(toDrop).pipe(Effect.ignore);\n }\n\n yield* storage.append([envelope]).pipe(Effect.ignore);\n yield* Queue.offer(envelopeQueue, envelope);\n yield* Effect.logDebug(\n `[${envelope.type}] Envelope '${envelope.uuid}' added to queue`\n ).pipe(\n Effect.annotateLogs({\n envelopeId: envelope.uuid,\n queueSize: size + 1,\n })\n );\n });\n\n const size = () =>\n Queue.size(envelopeQueue).pipe(Effect.map((n) => Math.max(0, n)));\n\n const boundedFlush = (timeoutMs: number) =>\n Effect.gen(function* () {\n // Only bound how long we wait; don't cancel the flush on timeout.\n const fiber = yield* Effect.fork(flush());\n yield* Effect.race(\n fiber.await.pipe(Effect.asVoid),\n Effect.sleep(Duration.millis(timeoutMs))\n );\n }).pipe(Effect.asVoid);\n\n const setReady = (ready: boolean) =>\n Ref.set(readyRef, ready).pipe(\n Effect.zipRight(\n ready\n ? Effect.logDebug(\"Queue ready - enabling batch sends\")\n : Effect.logDebug(\"Queue not ready - deferring batch sends\")\n )\n );\n\n const getMetrics = () =>\n Effect.gen(function* () {\n const m = yield* Ref.get(metrics);\n const depth = yield* Queue.size(envelopeQueue);\n return { ...m, queueDepth: Math.max(0, depth) };\n });\n\n // When offline storage is enabled, listen for 'online' events to flush\n // stored envelopes when connectivity is restored\n if (config.offline?.enabled && typeof window !== \"undefined\") {\n const onOnline = () => {\n // Fire-and-forget flush when coming back online\n Runtime.runPromise(runtime)(\n flush().pipe(\n Effect.tap(() =>\n Effect.logDebug(\"Flushed queue after coming back online\")\n )\n )\n ).catch(() => undefined);\n };\n\n window.addEventListener(\"online\", onOnline);\n\n // Clean up the event listener when the layer scope is closed\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n window.removeEventListener(\"online\", onOnline);\n })\n );\n\n yield* Effect.logDebug(\n \"Registered 'online' event listener for automatic flush\"\n );\n }\n\n return {\n add,\n size,\n flush,\n boundedFlush,\n startBatchProcessor,\n setReady,\n getMetrics,\n } satisfies QueueService;\n })\n);\n"],"mappings":"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"}
@@ -1,19 +0,0 @@
1
- import { QueueServiceTag } from "./queue.layer.mjs";
2
- import { Effect, Layer, Ref } from "effect";
3
- import { Envelope } from "@interfere/types/sdk/envelope";
4
-
5
- //#region src/effect/layers/queue.layer.test-layer.d.ts
6
- interface TestState {
7
- readonly envelopes: readonly Envelope[];
8
- readonly flushCount: number;
9
- readonly ready: boolean;
10
- }
11
- declare const TestState: {
12
- readonly make: () => Effect.Effect<Ref.Ref<TestState>, never, never>;
13
- };
14
- declare const makeQueueServiceTestLayer: () => Effect.Effect<{
15
- stateRef: Ref.Ref<TestState>;
16
- layer: Layer.Layer<QueueServiceTag, never, never>;
17
- }, never, never>;
18
- //#endregion
19
- export { makeQueueServiceTestLayer };
@@ -1 +0,0 @@
1
- {"version":3,"file":"queue.layer.test-layer.d.mts","names":[],"sources":["../../../src/effect/layers/queue.layer.test-layer.ts"],"mappings":";;;;;UAOU,SAAA;EAAA,SACC,SAAA,WAAoB,QAAA;EAAA,SACpB,UAAA;EAAA,SACA,KAAA;AAAA;AAAA,cAGL,SAAA;EAAA;;cASO,yBAAA,QAAyB,MAAA,CAAA,MAAA"}
@@ -1,44 +0,0 @@
1
- import { QueueServiceTag } from "./queue.layer.mjs";
2
- import { Effect, Layer, Ref } from "effect";
3
-
4
- //#region src/effect/layers/queue.layer.test-layer.ts
5
- const TestState = { make: () => Ref.make({
6
- envelopes: [],
7
- flushCount: 0,
8
- ready: true
9
- }) };
10
- const makeQueueServiceTestLayer = Effect.fn("makeQueueServiceTestLayer")(function* () {
11
- const stateRef = yield* TestState.make();
12
- return {
13
- stateRef,
14
- layer: Layer.succeed(QueueServiceTag, {
15
- add: (envelope) => Ref.update(stateRef, (s) => ({
16
- ...s,
17
- envelopes: [...s.envelopes, envelope]
18
- })),
19
- size: () => Ref.get(stateRef).pipe(Effect.map((s) => s.envelopes.length)),
20
- flush: () => Ref.update(stateRef, (s) => ({
21
- ...s,
22
- flushCount: s.flushCount + 1
23
- })),
24
- boundedFlush: () => Ref.update(stateRef, (s) => ({
25
- ...s,
26
- flushCount: s.flushCount + 1
27
- })),
28
- startBatchProcessor: () => Effect.void,
29
- setReady: (ready) => Ref.update(stateRef, (s) => ({
30
- ...s,
31
- ready
32
- })),
33
- getMetrics: () => Ref.get(stateRef).pipe(Effect.map((s) => ({
34
- queueDepth: s.envelopes.length,
35
- successCount: 0,
36
- failureCount: 0,
37
- circuitBreakerState: "closed"
38
- })))
39
- })
40
- };
41
- });
42
-
43
- //#endregion
44
- export { makeQueueServiceTestLayer };
@@ -1 +0,0 @@
1
- {"version":3,"file":"queue.layer.test-layer.mjs","names":[],"sources":["../../../src/effect/layers/queue.layer.test-layer.ts"],"sourcesContent":["import type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport { Effect, Layer, Ref } from \"effect\";\n\nimport type { QueueMetrics, QueueService } from \"./queue.layer.js\";\nimport { QueueServiceTag } from \"./queue.layer.js\";\n\ninterface TestState {\n readonly envelopes: readonly Envelope[];\n readonly flushCount: number;\n readonly ready: boolean;\n}\n\nconst TestState = {\n make: () =>\n Ref.make<TestState>({\n envelopes: [],\n flushCount: 0,\n ready: true,\n }),\n} as const;\n\nexport const makeQueueServiceTestLayer = Effect.fn(\"makeQueueServiceTestLayer\")(\n function* () {\n const stateRef = yield* TestState.make();\n\n const service: QueueService = {\n add: (envelope) =>\n Ref.update(stateRef, (s) => ({\n ...s,\n envelopes: [...s.envelopes, envelope],\n })),\n\n size: () => Ref.get(stateRef).pipe(Effect.map((s) => s.envelopes.length)),\n\n flush: () =>\n Ref.update(stateRef, (s) => ({\n ...s,\n flushCount: s.flushCount + 1,\n })),\n\n boundedFlush: () =>\n Ref.update(stateRef, (s) => ({\n ...s,\n flushCount: s.flushCount + 1,\n })),\n\n startBatchProcessor: () => Effect.void,\n\n setReady: (ready) =>\n Ref.update(stateRef, (s) => ({\n ...s,\n ready,\n })),\n\n getMetrics: () =>\n Ref.get(stateRef).pipe(\n Effect.map(\n (s): QueueMetrics => ({\n queueDepth: s.envelopes.length,\n successCount: 0,\n failureCount: 0,\n circuitBreakerState: \"closed\",\n })\n )\n ),\n };\n\n return {\n stateRef,\n layer: Layer.succeed(QueueServiceTag, service),\n };\n }\n);\n"],"mappings":";;;;AAaA,MAAM,YAAY,EAChB,YACE,IAAI,KAAgB;CAClB,WAAW,EAAE;CACb,YAAY;CACZ,OAAO;CACR,CAAC,EACL;AAED,MAAa,4BAA4B,OAAO,GAAG,4BAA4B,CAC7E,aAAa;CACX,MAAM,WAAW,OAAO,UAAU,MAAM;AA4CxC,QAAO;EACL;EACA,OAAO,MAAM,QAAQ,iBA5CO;GAC5B,MAAM,aACJ,IAAI,OAAO,WAAW,OAAO;IAC3B,GAAG;IACH,WAAW,CAAC,GAAG,EAAE,WAAW,SAAS;IACtC,EAAE;GAEL,YAAY,IAAI,IAAI,SAAS,CAAC,KAAK,OAAO,KAAK,MAAM,EAAE,UAAU,OAAO,CAAC;GAEzE,aACE,IAAI,OAAO,WAAW,OAAO;IAC3B,GAAG;IACH,YAAY,EAAE,aAAa;IAC5B,EAAE;GAEL,oBACE,IAAI,OAAO,WAAW,OAAO;IAC3B,GAAG;IACH,YAAY,EAAE,aAAa;IAC5B,EAAE;GAEL,2BAA2B,OAAO;GAElC,WAAW,UACT,IAAI,OAAO,WAAW,OAAO;IAC3B,GAAG;IACH;IACD,EAAE;GAEL,kBACE,IAAI,IAAI,SAAS,CAAC,KAChB,OAAO,KACJ,OAAqB;IACpB,YAAY,EAAE,UAAU;IACxB,cAAc;IACd,cAAc;IACd,qBAAqB;IACtB,EACF,CACF;GACJ,CAI+C;EAC/C;EAEJ"}
@@ -1,34 +0,0 @@
1
- import { Context, Effect, Layer, Option } from "effect";
2
- import * as effect_Types0 from "effect/Types";
3
- import * as effect_Cause0 from "effect/Cause";
4
-
5
- //#region src/effect/layers/session.layer.d.ts
6
- declare const SessionTimeoutError_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
7
- readonly _tag: "SessionTimeoutError";
8
- } & Readonly<A>;
9
- declare class SessionTimeoutError extends SessionTimeoutError_base<{
10
- readonly message: string;
11
- }> {}
12
- interface SessionService {
13
- readonly ensureWindowId: () => Effect.Effect<string | null>;
14
- readonly getSessionDebug: () => Effect.Effect<{
15
- sessionId: string | null;
16
- sessionStartTimestamp: number | null;
17
- lastActivityTimestamp: number | null;
18
- sessionTimeoutMs: number;
19
- sessionAge: number | undefined;
20
- timeSinceLastActivity: number | undefined;
21
- }>;
22
- readonly getSessionId: () => Effect.Effect<Option.Option<string>>;
23
- readonly resetSession: () => Effect.Effect<void>;
24
- readonly updateActivity: () => Effect.Effect<void>;
25
- readonly whenSessionReady: (options?: {
26
- timeout?: number;
27
- signal?: AbortSignal;
28
- }) => Effect.Effect<string, SessionTimeoutError>;
29
- }
30
- declare const SessionServiceTag_base: Context.TagClass<SessionServiceTag, "@interfere/react/Session", SessionService>;
31
- declare class SessionServiceTag extends SessionServiceTag_base {}
32
- declare const SessionServiceLive: Layer.Layer<SessionServiceTag, never, never>;
33
- //#endregion
34
- export { SessionService, SessionServiceLive, SessionServiceTag, SessionTimeoutError };
@@ -1 +0,0 @@
1
- {"version":3,"file":"session.layer.d.mts","names":[],"sources":["../../../src/effect/layers/session.layer.ts"],"mappings":";;;;;cAAwE,wBAAA;;;cAK3D,mBAAA,SAA4B,wBAAA;EAAA,SAG9B,OAAA;AAAA;AAAA,UA8CM,cAAA;EAAA,SACN,cAAA,QAAsB,MAAA,CAAO,MAAA;EAAA,SAC7B,eAAA,QAAuB,MAAA,CAAO,MAAA;IACrC,SAAA;IACA,qBAAA;IACA,qBAAA;IACA,gBAAA;IACA,UAAA;IACA,qBAAA;EAAA;EAAA,SAEO,YAAA,QAAoB,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,MAAA;EAAA,SACzC,YAAA,QAAoB,MAAA,CAAO,MAAA;EAAA,SAC3B,cAAA,QAAsB,MAAA,CAAO,MAAA;EAAA,SAC7B,gBAAA,GAAmB,OAAA;IAC1B,OAAA;IACA,MAAA,GAAS,WAAA;EAAA,MACL,MAAA,CAAO,MAAA,SAAe,mBAAA;AAAA;AAAA,cAC7B,sBAAA;cAEY,iBAAA,SAA0B,sBAAA;AAAA,cA2C1B,kBAAA,EAAkB,KAAA,CAAA,KAAA,CAAA,iBAAA"}