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

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,127 +0,0 @@
1
- import { Context, Data, Duration, Effect, Layer, Option } from "effect";
2
- import { v7 } from "uuid";
3
- import { nanoid } from "nanoid";
4
-
5
- //#region src/effect/layers/session.layer.ts
6
- var SessionTimeoutError = class extends Data.TaggedError("SessionTimeoutError") {};
7
- const STORAGE_KEYS = {
8
- sessionId: "__interfere_session_id__",
9
- sessionStart: "__interfere_session_start__",
10
- lastActivity: "__interfere_last_activity__",
11
- windowId: "__interfere_window_id__"
12
- };
13
- const SESSION_TIMEOUT_MS = 18e5;
14
- const DEFAULT_TIMEOUT_MS = 3e4;
15
- const WINDOW_ID_LENGTH = 10;
16
- const PROBE_KEY = "__interfere_probe__";
17
- const PROBE_VALUE = "1";
18
- let sessionId = null;
19
- let windowId = null;
20
- let sessionStartTimestamp = null;
21
- let lastActivityTimestamp = null;
22
- function safeNow() {
23
- return Date.now();
24
- }
25
- function canUseStorage() {
26
- try {
27
- if (typeof window === "undefined" || !window.localStorage) return false;
28
- window.localStorage.setItem(PROBE_KEY, PROBE_VALUE);
29
- window.localStorage.removeItem(PROBE_KEY);
30
- return true;
31
- } catch {
32
- return false;
33
- }
34
- }
35
- function newSessionId() {
36
- return v7();
37
- }
38
- var SessionServiceTag = class extends Context.Tag("@interfere/react/Session")() {};
39
- const getSessionIdImpl = () => Effect.sync(() => {
40
- if (typeof window === "undefined") return Option.none();
41
- if (sessionId) return Option.some(sessionId);
42
- const now = safeNow();
43
- const storage = canUseStorage() ? window.localStorage : null;
44
- const stored = storage?.getItem(STORAGE_KEYS.sessionId) ?? null;
45
- const start = Number(storage?.getItem(STORAGE_KEYS.sessionStart) ?? "0");
46
- const last = Number(storage?.getItem(STORAGE_KEYS.lastActivity) ?? "0");
47
- const expired = last > 0 && now - last > SESSION_TIMEOUT_MS;
48
- if (!stored || expired) {
49
- sessionId = newSessionId();
50
- sessionStartTimestamp = now;
51
- lastActivityTimestamp = now;
52
- storage?.setItem(STORAGE_KEYS.sessionId, sessionId);
53
- storage?.setItem(STORAGE_KEYS.sessionStart, String(sessionStartTimestamp));
54
- storage?.setItem(STORAGE_KEYS.lastActivity, String(lastActivityTimestamp));
55
- return Option.some(sessionId);
56
- }
57
- sessionId = stored;
58
- sessionStartTimestamp = start || now;
59
- lastActivityTimestamp = last || now;
60
- return Option.some(sessionId);
61
- });
62
- const SessionServiceLive = Layer.succeed(SessionServiceTag, {
63
- ensureWindowId: () => Effect.sync(() => {
64
- if (typeof window === "undefined") return null;
65
- if (windowId) return windowId;
66
- const storage = canUseStorage() ? window.localStorage : null;
67
- windowId = storage?.getItem(STORAGE_KEYS.windowId) ?? null ?? `win_${nanoid(WINDOW_ID_LENGTH)}`;
68
- storage?.setItem(STORAGE_KEYS.windowId, windowId);
69
- return windowId;
70
- }),
71
- getSessionId: getSessionIdImpl,
72
- updateActivity: () => Effect.sync(() => {
73
- if (typeof window === "undefined") return;
74
- const storage = canUseStorage() ? window.localStorage : null;
75
- lastActivityTimestamp = safeNow();
76
- storage?.setItem(STORAGE_KEYS.lastActivity, String(lastActivityTimestamp));
77
- }),
78
- resetSession: () => Effect.sync(() => {
79
- if (typeof window === "undefined") return;
80
- const storage = canUseStorage() ? window.localStorage : null;
81
- sessionId = newSessionId();
82
- sessionStartTimestamp = safeNow();
83
- lastActivityTimestamp = sessionStartTimestamp;
84
- storage?.setItem(STORAGE_KEYS.sessionId, sessionId);
85
- storage?.setItem(STORAGE_KEYS.sessionStart, String(sessionStartTimestamp));
86
- storage?.setItem(STORAGE_KEYS.lastActivity, String(lastActivityTimestamp));
87
- }),
88
- whenSessionReady: Effect.fn("whenSessionReady")(function* (options = {}) {
89
- const POLL_INTERVAL_MS = 250;
90
- const MAX_TIMEOUT = options.timeout ?? DEFAULT_TIMEOUT_MS;
91
- const abortEffect = options.signal ? Effect.async((resume) => {
92
- const signal = options.signal;
93
- if (!signal) return;
94
- if (signal.aborted) {
95
- resume(Effect.die("Aborted"));
96
- return;
97
- }
98
- const handler = () => resume(Effect.die("Aborted"));
99
- signal.addEventListener("abort", handler, { once: true });
100
- return Effect.sync(() => {
101
- signal.removeEventListener("abort", handler);
102
- });
103
- }) : Effect.never;
104
- const pollOnce = Effect.suspend(() => Effect.gen(function* () {
105
- const sidOpt = yield* getSessionIdImpl();
106
- if (Option.isSome(sidOpt)) return sidOpt.value;
107
- yield* Effect.sleep(Duration.millis(POLL_INTERVAL_MS));
108
- return yield* pollOnce;
109
- }).pipe(Effect.withSpan("session.whenSessionReady.pollOnce")));
110
- const poll = pollOnce.pipe(Effect.timeoutFail({
111
- onTimeout: () => new SessionTimeoutError({ message: "Session ID not available within timeout" }),
112
- duration: Duration.millis(MAX_TIMEOUT)
113
- }));
114
- return yield* Effect.race(poll, abortEffect);
115
- }),
116
- getSessionDebug: () => Effect.sync(() => ({
117
- sessionId,
118
- sessionStartTimestamp,
119
- lastActivityTimestamp,
120
- sessionTimeoutMs: SESSION_TIMEOUT_MS,
121
- sessionAge: sessionStartTimestamp ? safeNow() - sessionStartTimestamp : void 0,
122
- timeSinceLastActivity: lastActivityTimestamp ? safeNow() - lastActivityTimestamp : void 0
123
- }))
124
- });
125
-
126
- //#endregion
127
- export { SessionServiceLive, SessionServiceTag, SessionTimeoutError };
@@ -1 +0,0 @@
1
- {"version":3,"file":"session.layer.mjs","names":["uuidv7"],"sources":["../../../src/effect/layers/session.layer.ts"],"sourcesContent":["import { Context, Data, Duration, Effect, Layer, Option } from \"effect\";\nimport { nanoid } from \"nanoid\";\nimport { v7 as uuidv7 } from \"uuid\";\n\n// Tagged error for session timeout\nexport class SessionTimeoutError extends Data.TaggedError(\n \"SessionTimeoutError\"\n)<{\n readonly message: string;\n}> {}\n\n// Constants\nconst STORAGE_KEYS = {\n sessionId: \"__interfere_session_id__\",\n sessionStart: \"__interfere_session_start__\",\n lastActivity: \"__interfere_last_activity__\",\n windowId: \"__interfere_window_id__\",\n} as const;\n\nconst SESSION_TIMEOUT_MS = 1_800_000; // 30 minutes\nconst DEFAULT_TIMEOUT_MS = 30_000; // 30 seconds\nconst WINDOW_ID_LENGTH = 10;\nconst PROBE_KEY = \"__interfere_probe__\";\nconst PROBE_VALUE = \"1\";\n\n// In-memory state\nlet sessionId: string | null = null;\nlet windowId: string | null = null;\nlet sessionStartTimestamp: number | null = null;\nlet lastActivityTimestamp: number | null = null;\n\n// Helpers\nfunction safeNow() {\n return Date.now();\n}\n\nfunction canUseStorage() {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) {\n return false;\n }\n window.localStorage.setItem(PROBE_KEY, PROBE_VALUE);\n window.localStorage.removeItem(PROBE_KEY);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction newSessionId(): string {\n return uuidv7();\n}\n\n// Service type\nexport interface SessionService {\n readonly ensureWindowId: () => Effect.Effect<string | null>;\n readonly getSessionDebug: () => Effect.Effect<{\n sessionId: string | null;\n sessionStartTimestamp: number | null;\n lastActivityTimestamp: number | null;\n sessionTimeoutMs: number;\n sessionAge: number | undefined;\n timeSinceLastActivity: number | undefined;\n }>;\n readonly getSessionId: () => Effect.Effect<Option.Option<string>>;\n readonly resetSession: () => Effect.Effect<void>;\n readonly updateActivity: () => Effect.Effect<void>;\n readonly whenSessionReady: (options?: {\n timeout?: number;\n signal?: AbortSignal;\n }) => Effect.Effect<string, SessionTimeoutError>;\n}\n\nexport class SessionServiceTag extends Context.Tag(\"@interfere/react/Session\")<\n SessionServiceTag,\n SessionService\n>() {}\n\n// Internal implementation functions\nconst getSessionIdImpl = () =>\n Effect.sync(() => {\n if (typeof window === \"undefined\") {\n return Option.none();\n }\n if (sessionId) {\n return Option.some(sessionId);\n }\n const now = safeNow();\n const storage = canUseStorage() ? window.localStorage : null;\n const stored = storage?.getItem(STORAGE_KEYS.sessionId) ?? null;\n const start = Number(storage?.getItem(STORAGE_KEYS.sessionStart) ?? \"0\");\n const last = Number(storage?.getItem(STORAGE_KEYS.lastActivity) ?? \"0\");\n const expired = last > 0 && now - last > SESSION_TIMEOUT_MS;\n\n if (!stored || expired) {\n sessionId = newSessionId();\n sessionStartTimestamp = now;\n lastActivityTimestamp = now;\n storage?.setItem(STORAGE_KEYS.sessionId, sessionId);\n storage?.setItem(\n STORAGE_KEYS.sessionStart,\n String(sessionStartTimestamp)\n );\n storage?.setItem(\n STORAGE_KEYS.lastActivity,\n String(lastActivityTimestamp)\n );\n return Option.some(sessionId);\n }\n\n sessionId = stored;\n sessionStartTimestamp = start || now;\n lastActivityTimestamp = last || now;\n return Option.some(sessionId);\n });\n\nexport const SessionServiceLive = Layer.succeed(SessionServiceTag, {\n ensureWindowId: () =>\n Effect.sync(() => {\n if (typeof window === \"undefined\") {\n return null;\n }\n if (windowId) {\n return windowId;\n }\n const storage = canUseStorage() ? window.localStorage : null;\n const existing = storage?.getItem(STORAGE_KEYS.windowId) ?? null;\n windowId = existing ?? `win_${nanoid(WINDOW_ID_LENGTH)}`;\n storage?.setItem(STORAGE_KEYS.windowId, windowId);\n return windowId;\n }),\n\n getSessionId: getSessionIdImpl,\n\n updateActivity: () =>\n Effect.sync(() => {\n if (typeof window === \"undefined\") {\n return;\n }\n const storage = canUseStorage() ? window.localStorage : null;\n lastActivityTimestamp = safeNow();\n storage?.setItem(\n STORAGE_KEYS.lastActivity,\n String(lastActivityTimestamp)\n );\n }),\n\n resetSession: () =>\n Effect.sync(() => {\n if (typeof window === \"undefined\") {\n return;\n }\n const storage = canUseStorage() ? window.localStorage : null;\n sessionId = newSessionId();\n sessionStartTimestamp = safeNow();\n lastActivityTimestamp = sessionStartTimestamp;\n storage?.setItem(STORAGE_KEYS.sessionId, sessionId);\n storage?.setItem(\n STORAGE_KEYS.sessionStart,\n String(sessionStartTimestamp)\n );\n storage?.setItem(\n STORAGE_KEYS.lastActivity,\n String(lastActivityTimestamp)\n );\n }),\n\n whenSessionReady: Effect.fn(\"whenSessionReady\")(function* (options = {}) {\n const POLL_INTERVAL_MS = 250;\n const MAX_TIMEOUT = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\n const abortEffect = options.signal\n ? Effect.async<never>((resume) => {\n const signal = options.signal;\n if (!signal) {\n return;\n }\n\n if (signal.aborted) {\n resume(Effect.die(\"Aborted\"));\n return;\n }\n\n const handler = () => resume(Effect.die(\"Aborted\"));\n signal.addEventListener(\"abort\", handler, { once: true });\n return Effect.sync(() => {\n signal.removeEventListener(\"abort\", handler);\n });\n })\n : Effect.never;\n\n const pollOnce: Effect.Effect<string> = Effect.suspend(() =>\n Effect.gen(function* () {\n const sidOpt = yield* getSessionIdImpl();\n if (Option.isSome(sidOpt)) {\n return sidOpt.value;\n }\n yield* Effect.sleep(Duration.millis(POLL_INTERVAL_MS));\n return yield* pollOnce;\n }).pipe(Effect.withSpan(\"session.whenSessionReady.pollOnce\"))\n );\n\n const poll = pollOnce.pipe(\n Effect.timeoutFail({\n onTimeout: () =>\n new SessionTimeoutError({\n message: \"Session ID not available within timeout\",\n }),\n duration: Duration.millis(MAX_TIMEOUT),\n })\n );\n\n return yield* Effect.race(poll, abortEffect);\n }),\n\n getSessionDebug: () =>\n Effect.sync(() => ({\n sessionId,\n sessionStartTimestamp,\n lastActivityTimestamp,\n sessionTimeoutMs: SESSION_TIMEOUT_MS,\n sessionAge: sessionStartTimestamp\n ? safeNow() - sessionStartTimestamp\n : undefined,\n timeSinceLastActivity: lastActivityTimestamp\n ? safeNow() - lastActivityTimestamp\n : undefined,\n })),\n} satisfies SessionService);\n"],"mappings":"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"}
@@ -1,50 +0,0 @@
1
- import { Effect } from "effect";
2
- import { OfflineStorage } from "@interfere/types/sdk/config";
3
- import { Envelope } from "@interfere/types/sdk/envelope";
4
-
5
- //#region src/effect/layers/storage.layer.d.ts
6
- interface StorageAdapter {
7
- append: (envelopes: Envelope[]) => Effect.Effect<void>;
8
- capacity: () => number;
9
- clear: () => Effect.Effect<void>;
10
- dropOldest: (n: number) => Effect.Effect<void>;
11
- load: () => Effect.Effect<Envelope[]>;
12
- removeByIds: (ids: string[]) => Effect.Effect<void>;
13
- save: (envelopes: Envelope[]) => Effect.Effect<void>;
14
- }
15
- interface StorageAdapterOptions {
16
- /**
17
- * Offline storage configuration.
18
- * When enabled, uses IndexedDB for persistence.
19
- * When disabled (default), uses in-memory storage that's lost on page refresh.
20
- */
21
- offline?: OfflineStorage;
22
- }
23
- declare function createIndexedDBAdapter(dbName: string, storeName: string, maxQueueSize?: number): StorageAdapter;
24
- declare function createMemoryStorageAdapter(maxQueueSize?: number): StorageAdapter;
25
- /**
26
- * Factory to choose appropriate storage based on offline config.
27
- *
28
- * When offline storage is disabled (default), uses in-memory storage that is
29
- * lost on page refresh. This is the default behavior for performance and privacy.
30
- *
31
- * When offline storage is enabled, uses IndexedDB to persist envelopes across
32
- * page refreshes and retry when connectivity returns. Falls back to in-memory
33
- * if IndexedDB is unavailable.
34
- *
35
- * @param options - Storage adapter options including offline configuration
36
- * @returns The appropriate storage adapter
37
- *
38
- * @example
39
- * // Default: in-memory storage (no persistence)
40
- * const adapter = createStorageAdapter();
41
- *
42
- * @example
43
- * // Enable offline storage for better reliability
44
- * const adapter = createStorageAdapter({
45
- * offline: { enabled: true, maxQueueSize: 100 }
46
- * });
47
- */
48
- declare function createStorageAdapter(options?: StorageAdapterOptions): StorageAdapter;
49
- //#endregion
50
- export { StorageAdapter, StorageAdapterOptions, createIndexedDBAdapter, createMemoryStorageAdapter, createStorageAdapter };
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage.layer.d.mts","names":[],"sources":["../../../src/effect/layers/storage.layer.ts"],"mappings":";;;;;UAQiB,cAAA;EACf,MAAA,GAAS,SAAA,EAAW,QAAA,OAAe,MAAA,CAAO,MAAA;EAC1C,QAAA;EACA,KAAA,QAAa,MAAA,CAAO,MAAA;EACpB,UAAA,GAAa,CAAA,aAAc,MAAA,CAAO,MAAA;EAClC,IAAA,QAAY,MAAA,CAAO,MAAA,CAAO,QAAA;EAC1B,WAAA,GAAc,GAAA,eAAkB,MAAA,CAAO,MAAA;EACvC,IAAA,GAAO,SAAA,EAAW,QAAA,OAAe,MAAA,CAAO,MAAA;AAAA;AAAA,UAGzB,qBAAA;EALH;;;;;EAWZ,OAAA,GAAU,cAAA;AAAA;AAAA,iBAII,sBAAA,CACd,MAAA,UACA,SAAA,UACA,YAAA,YACC,cAAA;AAAA,iBA6Ma,0BAAA,CAA2B,YAAA,YAAqB,cAAA;;;;;;;;;;;;;;;;;;;;;;;;iBA4DhD,oBAAA,CACd,OAAA,GAAS,qBAAA,GACR,cAAA"}
@@ -1,180 +0,0 @@
1
- import { name, version } from "../../package.mjs";
2
- import { Effect } from "effect";
3
-
4
- //#region src/effect/layers/storage.layer.ts
5
- function createIndexedDBAdapter(dbName, storeName, maxQueueSize = 1e3) {
6
- const capacity = maxQueueSize;
7
- const DB_VERSION = 1;
8
- const openDB = () => Effect.promise(() => new Promise((resolve, reject) => {
9
- if (typeof indexedDB === "undefined") {
10
- reject(/* @__PURE__ */ new Error("IndexedDB not available"));
11
- return;
12
- }
13
- const request = indexedDB.open(dbName, DB_VERSION);
14
- request.onerror = () => reject(request.error);
15
- request.onsuccess = () => resolve(request.result);
16
- request.onupgradeneeded = (event) => {
17
- const db = event.target.result;
18
- if (!db.objectStoreNames.contains(storeName)) db.createObjectStore(storeName, { keyPath: "uuid" }).createIndex("timestamp", "client_ts", { unique: false });
19
- };
20
- }));
21
- const withStore = Effect.fn("storage.withStore")(function* (mode, operation) {
22
- const db = yield* openDB();
23
- try {
24
- const request = operation(db.transaction([storeName], mode).objectStore(storeName));
25
- return yield* Effect.promise(() => new Promise((resolve, reject) => {
26
- request.onsuccess = () => resolve(request.result);
27
- request.onerror = () => reject(request.error);
28
- }));
29
- } finally {
30
- db.close();
31
- }
32
- });
33
- return {
34
- load: () => {
35
- if (typeof indexedDB === "undefined") return Effect.succeed([]);
36
- const eff = withStore("readonly", (store) => store.getAll()).pipe(Effect.map((results) => {
37
- const envelopes = results;
38
- envelopes.sort((a, b) => {
39
- return (a.clientTs || 0) - (b.clientTs || 0);
40
- });
41
- return envelopes.length > capacity ? envelopes.slice(envelopes.length - capacity) : envelopes;
42
- }));
43
- return Effect.sandbox(eff).pipe(Effect.catchAll(() => Effect.succeed([])));
44
- },
45
- save: Effect.fn("storage.save")(function* (envelopes) {
46
- const db = yield* openDB();
47
- try {
48
- const tx = db.transaction([storeName], "readwrite");
49
- const store = tx.objectStore(storeName);
50
- yield* Effect.promise(() => new Promise((resolve, reject) => {
51
- const clearReq = store.clear();
52
- clearReq.onsuccess = () => resolve();
53
- clearReq.onerror = () => reject(clearReq.error);
54
- }));
55
- for (const envelope of envelopes) store.add(envelope);
56
- yield* Effect.promise(() => new Promise((resolve, reject) => {
57
- tx.oncomplete = () => resolve();
58
- tx.onerror = () => reject(tx.error);
59
- }));
60
- } finally {
61
- db.close();
62
- }
63
- }),
64
- append: Effect.fn("storage.append")(function* (envelopes) {
65
- const next = [...yield* withStore("readonly", (store) => store.getAll()), ...envelopes];
66
- const trimmed = next.length > capacity ? next.slice(next.length - capacity) : next;
67
- yield* Effect.gen(function* () {
68
- const db = yield* openDB();
69
- try {
70
- const tx = db.transaction([storeName], "readwrite");
71
- const store = tx.objectStore(storeName);
72
- store.clear();
73
- for (const env of trimmed) store.add(env);
74
- yield* Effect.promise(() => new Promise((resolve, reject) => {
75
- tx.oncomplete = () => resolve();
76
- tx.onerror = () => reject(tx.error);
77
- }));
78
- } finally {
79
- db.close();
80
- }
81
- });
82
- }),
83
- removeByIds: Effect.fn("storage.removeByIds")(function* (ids) {
84
- if (ids.length === 0) return;
85
- const db = yield* openDB();
86
- try {
87
- const tx = db.transaction([storeName], "readwrite");
88
- const store = tx.objectStore(storeName);
89
- for (const id of ids) store.delete(id);
90
- yield* Effect.promise(() => new Promise((resolve, reject) => {
91
- tx.oncomplete = () => resolve();
92
- tx.onerror = () => reject(tx.error);
93
- }));
94
- } finally {
95
- db.close();
96
- }
97
- }),
98
- dropOldest: Effect.fn("storage.dropOldest")(function* (n) {
99
- const sorted = (yield* withStore("readonly", (store) => store.getAll())).sort((a, b) => {
100
- return (a.clientTs || 0) - (b.clientTs || 0);
101
- });
102
- const keep = n >= sorted.length ? [] : sorted.slice(n);
103
- const db = yield* openDB();
104
- try {
105
- const tx = db.transaction([storeName], "readwrite");
106
- const store = tx.objectStore(storeName);
107
- store.clear();
108
- for (const env of keep) store.add(env);
109
- yield* Effect.promise(() => new Promise((resolve, reject) => {
110
- tx.oncomplete = () => resolve();
111
- tx.onerror = () => reject(tx.error);
112
- }));
113
- } finally {
114
- db.close();
115
- }
116
- }),
117
- clear: () => withStore("readwrite", (store) => store.clear()).pipe(Effect.as(void 0)),
118
- capacity: () => capacity
119
- };
120
- }
121
- function createMemoryStorageAdapter(maxQueueSize = 500) {
122
- const capacity = maxQueueSize;
123
- let buffer = [];
124
- return {
125
- load: () => Effect.succeed(buffer),
126
- save: (envelopes) => Effect.sync(() => {
127
- buffer = envelopes;
128
- }),
129
- append: (envelopes) => Effect.sync(() => {
130
- const next = [...buffer, ...envelopes];
131
- buffer = next.length > capacity ? next.slice(next.length - capacity) : next;
132
- }),
133
- removeByIds: (ids) => Effect.sync(() => {
134
- if (ids.length === 0) return;
135
- const set = new Set(ids);
136
- buffer = buffer.filter((e) => e.uuid ? !set.has(e.uuid) : true);
137
- }),
138
- dropOldest: (n) => Effect.sync(() => {
139
- buffer = n >= buffer.length ? [] : buffer.slice(n);
140
- }),
141
- clear: () => Effect.sync(() => {
142
- buffer = [];
143
- }),
144
- capacity: () => capacity
145
- };
146
- }
147
- /**
148
- * Factory to choose appropriate storage based on offline config.
149
- *
150
- * When offline storage is disabled (default), uses in-memory storage that is
151
- * lost on page refresh. This is the default behavior for performance and privacy.
152
- *
153
- * When offline storage is enabled, uses IndexedDB to persist envelopes across
154
- * page refreshes and retry when connectivity returns. Falls back to in-memory
155
- * if IndexedDB is unavailable.
156
- *
157
- * @param options - Storage adapter options including offline configuration
158
- * @returns The appropriate storage adapter
159
- *
160
- * @example
161
- * // Default: in-memory storage (no persistence)
162
- * const adapter = createStorageAdapter();
163
- *
164
- * @example
165
- * // Enable offline storage for better reliability
166
- * const adapter = createStorageAdapter({
167
- * offline: { enabled: true, maxQueueSize: 100 }
168
- * });
169
- */
170
- function createStorageAdapter(options = {}) {
171
- const { offline } = options;
172
- const maxQueueSize = offline?.maxQueueSize ?? 100;
173
- if (!offline?.enabled) return createMemoryStorageAdapter(maxQueueSize);
174
- const namespace = `${name}:${version}`;
175
- if (typeof indexedDB !== "undefined") return createIndexedDBAdapter(namespace, "envelopes", maxQueueSize);
176
- return createMemoryStorageAdapter(maxQueueSize);
177
- }
178
-
179
- //#endregion
180
- export { createIndexedDBAdapter, createMemoryStorageAdapter, createStorageAdapter };
@@ -1 +0,0 @@
1
- {"version":3,"file":"storage.layer.mjs","names":["packageJson.name","packageJson.version"],"sources":["../../../src/effect/layers/storage.layer.ts"],"sourcesContent":["import type { OfflineStorage } from \"@interfere/types/sdk/config\";\nimport type { Envelope } from \"@interfere/types/sdk/envelope\";\n\nimport { Effect } from \"effect\";\n\n// Import SDK version for storage namespacing\nimport packageJson from \"../../../package.json\" with { type: \"json\" };\n\nexport interface StorageAdapter {\n append: (envelopes: Envelope[]) => Effect.Effect<void>;\n capacity: () => number;\n clear: () => Effect.Effect<void>;\n dropOldest: (n: number) => Effect.Effect<void>;\n load: () => Effect.Effect<Envelope[]>;\n removeByIds: (ids: string[]) => Effect.Effect<void>;\n save: (envelopes: Envelope[]) => Effect.Effect<void>;\n}\n\nexport interface StorageAdapterOptions {\n /**\n * Offline storage configuration.\n * When enabled, uses IndexedDB for persistence.\n * When disabled (default), uses in-memory storage that's lost on page refresh.\n */\n offline?: OfflineStorage;\n}\n\n// IndexedDB adapter for persistent offline storage\nexport function createIndexedDBAdapter(\n dbName: string,\n storeName: string,\n maxQueueSize = 1000\n): StorageAdapter {\n const capacity = maxQueueSize;\n const DB_VERSION = 1;\n\n const openDB = () =>\n Effect.promise(\n () =>\n new Promise<IDBDatabase>((resolve, reject) => {\n if (typeof indexedDB === \"undefined\") {\n reject(new Error(\"IndexedDB not available\"));\n return;\n }\n\n const request = indexedDB.open(dbName, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(storeName)) {\n const store = db.createObjectStore(storeName, {\n keyPath: \"uuid\",\n });\n store.createIndex(\"timestamp\", \"client_ts\", { unique: false });\n }\n };\n })\n );\n\n const withStore = Effect.fn(\"storage.withStore\")(function* <T>(\n mode: IDBTransactionMode,\n operation: (store: IDBObjectStore) => IDBRequest<T>\n ) {\n const db = yield* openDB();\n try {\n const tx = db.transaction([storeName], mode);\n const store = tx.objectStore(storeName);\n const request = operation(store);\n\n const result = yield* Effect.promise(\n () =>\n new Promise<T>((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n })\n );\n\n return result;\n } finally {\n db.close();\n }\n });\n\n return {\n load: () => {\n if (typeof indexedDB === \"undefined\") {\n return Effect.succeed([] as Envelope[]);\n }\n\n const eff = withStore(\"readonly\", (store) => store.getAll()).pipe(\n Effect.map((results) => {\n const envelopes = results as Envelope[];\n // Sort by timestamp and enforce capacity\n envelopes.sort((a, b) => {\n const aTime = a.clientTs || 0;\n const bTime = b.clientTs || 0;\n\n return aTime - bTime;\n });\n\n return envelopes.length > capacity\n ? envelopes.slice(envelopes.length - capacity)\n : envelopes;\n })\n );\n return Effect.sandbox(eff).pipe(\n Effect.catchAll(() => Effect.succeed([] as Envelope[]))\n );\n },\n\n save: Effect.fn(\"storage.save\")(function* (envelopes) {\n const db = yield* openDB();\n try {\n const tx = db.transaction([storeName], \"readwrite\");\n const store = tx.objectStore(storeName);\n\n // Clear existing\n yield* Effect.promise(\n () =>\n new Promise<void>((resolve, reject) => {\n const clearReq = store.clear();\n clearReq.onsuccess = () => resolve();\n clearReq.onerror = () => reject(clearReq.error);\n })\n );\n\n for (const envelope of envelopes) {\n store.add(envelope);\n }\n\n yield* Effect.promise(\n () =>\n new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n })\n );\n } finally {\n db.close();\n }\n }),\n\n append: Effect.fn(\"storage.append\")(function* (envelopes) {\n const current = (yield* withStore(\"readonly\", (store) =>\n store.getAll()\n )) as Envelope[];\n const next = [...current, ...envelopes];\n const trimmed =\n next.length > capacity ? next.slice(next.length - capacity) : next;\n // Reuse save logic to write trimmed set\n yield* Effect.gen(function* () {\n const db = yield* openDB();\n try {\n const tx = db.transaction([storeName], \"readwrite\");\n const store = tx.objectStore(storeName);\n store.clear();\n for (const env of trimmed) {\n store.add(env);\n }\n yield* Effect.promise(\n () =>\n new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n })\n );\n } finally {\n db.close();\n }\n });\n }),\n\n removeByIds: Effect.fn(\"storage.removeByIds\")(function* (ids) {\n if (ids.length === 0) {\n return;\n }\n const db = yield* openDB();\n try {\n const tx = db.transaction([storeName], \"readwrite\");\n const store = tx.objectStore(storeName);\n for (const id of ids) {\n store.delete(id);\n }\n yield* Effect.promise(\n () =>\n new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n })\n );\n } finally {\n db.close();\n }\n }),\n\n dropOldest: Effect.fn(\"storage.dropOldest\")(function* (n) {\n const all = yield* withStore(\"readonly\", (store) => store.getAll());\n const sorted = (all as Envelope[]).sort((a, b) => {\n const aTime = a.clientTs || 0;\n const bTime = b.clientTs || 0;\n\n return aTime - bTime;\n });\n const keep = n >= sorted.length ? [] : sorted.slice(n);\n\n const db = yield* openDB();\n try {\n const tx = db.transaction([storeName], \"readwrite\");\n const store = tx.objectStore(storeName);\n store.clear();\n for (const env of keep) {\n store.add(env);\n }\n yield* Effect.promise(\n () =>\n new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n })\n );\n } finally {\n db.close();\n }\n }),\n\n clear: () =>\n withStore(\"readwrite\", (store) => store.clear()).pipe(\n Effect.as(undefined)\n ),\n capacity: () => capacity,\n };\n}\n\n// In-memory adapter - default when offline storage is disabled\nexport function createMemoryStorageAdapter(maxQueueSize = 500): StorageAdapter {\n const capacity = maxQueueSize;\n\n let buffer: Envelope[] = [];\n\n return {\n load: () => Effect.succeed(buffer),\n save: (envelopes) =>\n Effect.sync(() => {\n buffer = envelopes;\n }),\n append: (envelopes) =>\n Effect.sync(() => {\n const next = [...buffer, ...envelopes];\n buffer =\n next.length > capacity ? next.slice(next.length - capacity) : next;\n }),\n removeByIds: (ids) =>\n Effect.sync(() => {\n if (ids.length === 0) {\n return;\n }\n const set = new Set(ids);\n buffer = buffer.filter((e) => (e.uuid ? !set.has(e.uuid) : true));\n }),\n dropOldest: (n) =>\n Effect.sync(() => {\n buffer = n >= buffer.length ? [] : buffer.slice(n);\n }),\n clear: () =>\n Effect.sync(() => {\n buffer = [];\n }),\n capacity: () => capacity,\n };\n}\n\n/**\n * Factory to choose appropriate storage based on offline config.\n *\n * When offline storage is disabled (default), uses in-memory storage that is\n * lost on page refresh. This is the default behavior for performance and privacy.\n *\n * When offline storage is enabled, uses IndexedDB to persist envelopes across\n * page refreshes and retry when connectivity returns. Falls back to in-memory\n * if IndexedDB is unavailable.\n *\n * @param options - Storage adapter options including offline configuration\n * @returns The appropriate storage adapter\n *\n * @example\n * // Default: in-memory storage (no persistence)\n * const adapter = createStorageAdapter();\n *\n * @example\n * // Enable offline storage for better reliability\n * const adapter = createStorageAdapter({\n * offline: { enabled: true, maxQueueSize: 100 }\n * });\n */\nexport function createStorageAdapter(\n options: StorageAdapterOptions = {}\n): StorageAdapter {\n const { offline } = options;\n const maxQueueSize = offline?.maxQueueSize ?? 100;\n\n // Default: in-memory storage when offline is not enabled\n if (!offline?.enabled) {\n return createMemoryStorageAdapter(maxQueueSize);\n }\n\n // Offline enabled: use IndexedDB for persistence\n const sdkName = packageJson.name;\n const sdkVersion = packageJson.version;\n const namespace = `${sdkName}:${sdkVersion}`;\n\n if (typeof indexedDB !== \"undefined\") {\n return createIndexedDBAdapter(namespace, \"envelopes\", maxQueueSize);\n }\n\n // Fallback: in-memory (offline enabled but IndexedDB not available)\n return createMemoryStorageAdapter(maxQueueSize);\n}\n"],"mappings":"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"}
@@ -1,19 +0,0 @@
1
- import { Envelope } from "@interfere/types/sdk/envelope";
2
-
3
- //#region src/effect/layers/test-utils.d.ts
4
- /**
5
- * Envelope header fields that can be overridden in test utilities.
6
- * Excludes discriminant fields (type, payload) to preserve union type safety.
7
- */
8
- type EnvelopeHeaderOverrides = Partial<Pick<Envelope, "uuid" | "v" | "runtime" | "environment" | "buildId" | "releaseId" | "clientTs" | "sessionId" | "sessionSource">>;
9
- /**
10
- * Creates a test envelope with sensible defaults for ui_event type.
11
- * Only header fields can be overridden to maintain discriminated union type safety.
12
- */
13
- declare function makeEnvelope(overrides?: EnvelopeHeaderOverrides): Envelope<"ui_event">;
14
- /**
15
- * Creates a batch of test envelopes with sequential UUIDs and timestamps.
16
- */
17
- declare function makeEnvelopeBatch(count: number, prefix?: string): Envelope<"ui_event">[];
18
- //#endregion
19
- export { makeEnvelope, makeEnvelopeBatch };
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-utils.d.mts","names":[],"sources":["../../../src/effect/layers/test-utils.ts"],"mappings":";;;;;AAA8D;;KAMzD,uBAAA,GAA0B,OAAA,CAC7B,IAAA,CACE,QAAA;;;;;iBAiBY,YAAA,CACd,SAAA,GAAY,uBAAA,GACX,QAAA;;;;iBAmBa,iBAAA,CACd,KAAA,UACA,MAAA,YACC,QAAA"}
@@ -1,32 +0,0 @@
1
- //#region src/effect/layers/test-utils.ts
2
- /**
3
- * Creates a test envelope with sensible defaults for ui_event type.
4
- * Only header fields can be overridden to maintain discriminated union type safety.
5
- */
6
- function makeEnvelope(overrides) {
7
- return {
8
- v: 0,
9
- runtime: "browser",
10
- environment: "production",
11
- clientTs: Date.now(),
12
- sessionId: null,
13
- type: "ui_event",
14
- payload: { event: {} },
15
- buildId: "test-build",
16
- releaseId: "test-release",
17
- uuid: crypto.randomUUID(),
18
- ...overrides
19
- };
20
- }
21
- /**
22
- * Creates a batch of test envelopes with sequential UUIDs and timestamps.
23
- */
24
- function makeEnvelopeBatch(count, prefix = "batch") {
25
- return Array.from({ length: count }, (_, i) => makeEnvelope({
26
- uuid: `${prefix}-${i}`,
27
- clientTs: Date.now() + i * 100
28
- }));
29
- }
30
-
31
- //#endregion
32
- export { makeEnvelope, makeEnvelopeBatch };
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-utils.mjs","names":[],"sources":["../../../src/effect/layers/test-utils.ts"],"sourcesContent":["import type { Envelope } from \"@interfere/types/sdk/envelope\";\n\n/**\n * Envelope header fields that can be overridden in test utilities.\n * Excludes discriminant fields (type, payload) to preserve union type safety.\n */\ntype EnvelopeHeaderOverrides = Partial<\n Pick<\n Envelope,\n | \"uuid\"\n | \"v\"\n | \"runtime\"\n | \"environment\"\n | \"buildId\"\n | \"releaseId\"\n | \"clientTs\"\n | \"sessionId\"\n | \"sessionSource\"\n >\n>;\n\n/**\n * Creates a test envelope with sensible defaults for ui_event type.\n * Only header fields can be overridden to maintain discriminated union type safety.\n */\nexport function makeEnvelope(\n overrides?: EnvelopeHeaderOverrides\n): Envelope<\"ui_event\"> {\n return {\n v: 0,\n runtime: \"browser\",\n environment: \"production\",\n clientTs: Date.now(),\n sessionId: null,\n type: \"ui_event\",\n payload: { event: {} },\n buildId: \"test-build\",\n releaseId: \"test-release\",\n uuid: crypto.randomUUID(),\n ...overrides,\n };\n}\n\n/**\n * Creates a batch of test envelopes with sequential UUIDs and timestamps.\n */\nexport function makeEnvelopeBatch(\n count: number,\n prefix = \"batch\"\n): Envelope<\"ui_event\">[] {\n return Array.from({ length: count }, (_, i) =>\n makeEnvelope({\n uuid: `${prefix}-${i}`,\n clientTs: Date.now() + i * 100,\n })\n );\n}\n"],"mappings":";;;;;AAyBA,SAAgB,aACd,WACsB;AACtB,QAAO;EACL,GAAG;EACH,SAAS;EACT,aAAa;EACb,UAAU,KAAK,KAAK;EACpB,WAAW;EACX,MAAM;EACN,SAAS,EAAE,OAAO,EAAE,EAAE;EACtB,SAAS;EACT,WAAW;EACX,MAAM,OAAO,YAAY;EACzB,GAAG;EACJ;;;;;AAMH,SAAgB,kBACd,OACA,SAAS,SACe;AACxB,QAAO,MAAM,KAAK,EAAE,QAAQ,OAAO,GAAG,GAAG,MACvC,aAAa;EACX,MAAM,GAAG,OAAO,GAAG;EACnB,UAAU,KAAK,KAAK,GAAG,IAAI;EAC5B,CAAC,CACH"}
@@ -1,23 +0,0 @@
1
- import { EventCapture } from "../core/plugins/services/event-capture.mjs";
2
- import { ConfigTag } from "./tags.mjs";
3
- import { HttpServiceTag } from "./layers/http.layer.mjs";
4
- import { QueueServiceTag } from "./layers/queue.layer.mjs";
5
- import { ContextServiceTag } from "./layers/context.layer.mjs";
6
- import { SessionServiceTag } from "./layers/session.layer.mjs";
7
- import { Effect, Layer, Runtime, Scope } from "effect";
8
- import { Config } from "@interfere/types/sdk/config";
9
- import { Layer as Layer$1 } from "effect/Layer";
10
-
11
- //#region src/effect/runtime-services.d.ts
12
- declare const createSDKLayer: (config: Config) => Layer.Layer<EventCapture | QueueServiceTag | HttpServiceTag | ConfigTag | ContextServiceTag | SessionServiceTag, never, never>;
13
- type LayerSuccess<T> = T extends Layer$1<infer ROut, unknown, unknown> ? ROut : never;
14
- type SDKEnv = LayerSuccess<ReturnType<typeof createSDKLayer>>;
15
- declare function setSDKRuntime(scope: Scope.CloseableScope, runtime: Runtime.Runtime<SDKEnv>): void;
16
- declare function initRuntimeServices(input: unknown): void;
17
- declare function runWithSDK<A, E>(effect: Effect.Effect<A, E, SDKEnv>): Promise<A>;
18
- declare function forkWithSDK<A, E, R>(effect: Effect.Effect<A, E, R>): void;
19
- declare function isSDKInitialized(): boolean;
20
- declare function closeSDK(): Promise<void>;
21
- declare function flushQueue(timeoutMs: number): Promise<void>;
22
- //#endregion
23
- export { closeSDK, flushQueue, forkWithSDK, initRuntimeServices, isSDKInitialized, runWithSDK, setSDKRuntime };
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-services.d.mts","names":[],"sources":["../../src/effect/runtime-services.ts"],"mappings":";;;;;;;;;;;cAmBM,cAAA,GAAkB,MAAA,EAAQ,MAAA,KAAM,KAAA,CAAA,KAAA,CAAA,YAAA,GAAA,eAAA,GAAA,cAAA,GAAA,SAAA,GAAA,iBAAA,GAAA,iBAAA;AAAA,KA4BjC,YAAA,MACH,CAAA,SAAU,OAAA,iCAA0C,IAAA;AAAA,KAEjD,MAAA,GAAS,YAAA,CAAa,UAAA,QAAkB,cAAA;AAAA,iBAM7B,aAAA,CACd,KAAA,EAAO,KAAA,CAAM,cAAA,EACb,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,MAAA;AAAA,iBAMX,mBAAA,CAAoB,KAAA;AAAA,iBAmDpB,UAAA,MAAA,CACd,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,EAAG,MAAA,IAC3B,OAAA,CAAQ,CAAA;AAAA,iBAQK,WAAA,SAAA,CAAqB,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA,iBAoBjD,gBAAA,CAAA;AAAA,iBAIM,QAAA,CAAA,GAAY,OAAA;AAAA,iBAWZ,UAAA,CAAW,SAAA,WAAoB,OAAA"}
@@ -1,79 +0,0 @@
1
- import { ContextServiceLive } from "./layers/context.layer.mjs";
2
- import { ConfigTag } from "./tags.mjs";
3
- import { CircuitBreakerLive } from "./layers/circuit-breaker.layer.mjs";
4
- import { HttpServiceLive } from "./layers/http.layer.mjs";
5
- import { QueueServiceLive, QueueServiceTag } from "./layers/queue.layer.mjs";
6
- import { SessionServiceLive } from "./layers/session.layer.mjs";
7
- import { EventCaptureLive } from "../core/plugins/services/event-capture.layer.mjs";
8
- import { getRuntime } from "../core/runtime/config.mjs";
9
- import { Effect, Exit, Layer, Runtime, Scope } from "effect";
10
- import { InterfereLogger, withInterfereLogger } from "@interfere/effect-utils/observability";
11
- import { configSchema } from "@interfere/types/sdk/config";
12
-
13
- //#region src/effect/runtime-services.ts
14
- const createSDKLayer = (config) => {
15
- const configWithRuntime = {
16
- ...config,
17
- runtime: getRuntime()
18
- };
19
- const configLayer = Layer.succeed(ConfigTag, config);
20
- const contextLayer = ContextServiceLive;
21
- const httpLayer = HttpServiceLive(configWithRuntime);
22
- const sessionLayer = SessionServiceLive;
23
- const queueLayer = QueueServiceLive.pipe(Layer.provide(configLayer), Layer.provide(httpLayer), Layer.provide(CircuitBreakerLive));
24
- const baseLayer = Layer.mergeAll(configLayer, contextLayer, httpLayer, sessionLayer, queueLayer);
25
- return EventCaptureLive.pipe(Layer.provideMerge(baseLayer));
26
- };
27
- let sdkRuntime = null;
28
- let sdkScope = null;
29
- const shouldEmitConsoleLogs = () => !InterfereLogger.isTestEnvironment();
30
- function setSDKRuntime(scope, runtime) {
31
- sdkScope = scope;
32
- sdkRuntime = runtime;
33
- }
34
- function initRuntimeServices(input) {
35
- if (sdkRuntime) return;
36
- const config = configSchema.parse(input);
37
- if (shouldEmitConsoleLogs()) console.debug("[Interfere SDK] Initializing with config:", config);
38
- (async () => {
39
- const scope = await Effect.runPromise(Scope.make());
40
- const runtime = await Effect.runPromise(Layer.toRuntime(createSDKLayer(config)).pipe(Effect.provideService(Scope.Scope, scope)));
41
- setSDKRuntime(scope, runtime);
42
- if (shouldEmitConsoleLogs()) console.debug("[Interfere SDK] Runtime initialized successfully");
43
- Runtime.runPromise(runtime)(withInterfereLogger("react", Effect.gen(function* () {
44
- yield* (yield* QueueServiceTag).startBatchProcessor().pipe(Effect.provideService(Scope.Scope, scope));
45
- }))).catch((error) => {
46
- Runtime.runPromise(runtime)(withInterfereLogger("react", Effect.logError("Failed to start batch processor", { error: String(error) })));
47
- });
48
- })();
49
- }
50
- function runWithSDK(effect) {
51
- if (!sdkRuntime) throw new Error("SDK not initialized. Call initRuntimeServices first.");
52
- return Runtime.runPromise(sdkRuntime)(withInterfereLogger("react", effect));
53
- }
54
- function forkWithSDK(effect) {
55
- if (!sdkRuntime) {
56
- if (shouldEmitConsoleLogs()) console.warn("[Interfere SDK] Attempted to fork effect before SDK initialized");
57
- return;
58
- }
59
- Runtime.runFork(sdkRuntime)(withInterfereLogger("react", effect).pipe(Effect.catchAllCause((cause) => Effect.logError("Forked effect failed", { cause: String(cause) }))));
60
- }
61
- function isSDKInitialized() {
62
- return sdkRuntime !== null;
63
- }
64
- async function closeSDK() {
65
- if (sdkScope) {
66
- await Effect.runPromise(withInterfereLogger("react", Scope.close(sdkScope, Exit.void)));
67
- sdkScope = null;
68
- sdkRuntime = null;
69
- }
70
- }
71
- async function flushQueue(timeoutMs) {
72
- if (!sdkRuntime) return;
73
- await Runtime.runPromise(sdkRuntime)(Effect.gen(function* () {
74
- yield* (yield* QueueServiceTag).boundedFlush(timeoutMs);
75
- }));
76
- }
77
-
78
- //#endregion
79
- export { closeSDK, flushQueue, forkWithSDK, initRuntimeServices, isSDKInitialized, runWithSDK, setSDKRuntime };
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-services.mjs","names":[],"sources":["../../src/effect/runtime-services.ts"],"sourcesContent":["import {\n InterfereLogger,\n withInterfereLogger,\n} from \"@interfere/effect-utils/observability\";\nimport type { Config } from \"@interfere/types/sdk/config\";\nimport { configSchema } from \"@interfere/types/sdk/config\";\n\nimport { Effect, Exit, Layer, Runtime, Scope } from \"effect\";\nimport type { Layer as LayerType } from \"effect/Layer\";\n\nimport { EventCaptureLive } from \"../core/plugins/services/event-capture.layer.js\";\nimport { getRuntime } from \"../core/runtime/config.js\";\nimport { CircuitBreakerLive } from \"./layers/circuit-breaker.layer.js\";\nimport { ContextServiceLive } from \"./layers/context.layer.js\";\nimport { HttpServiceLive } from \"./layers/http.layer.js\";\nimport { QueueServiceLive, QueueServiceTag } from \"./layers/queue.layer.js\";\nimport { SessionServiceLive } from \"./layers/session.layer.js\";\nimport { ConfigTag } from \"./tags.js\";\n\nconst createSDKLayer = (config: Config) => {\n const configWithRuntime = {\n ...config,\n runtime: getRuntime(),\n };\n\n const configLayer = Layer.succeed(ConfigTag, config);\n const contextLayer = ContextServiceLive;\n const httpLayer = HttpServiceLive(configWithRuntime);\n const sessionLayer = SessionServiceLive;\n\n const queueLayer = QueueServiceLive.pipe(\n Layer.provide(configLayer),\n Layer.provide(httpLayer),\n Layer.provide(CircuitBreakerLive)\n );\n\n const baseLayer = Layer.mergeAll(\n configLayer,\n contextLayer,\n httpLayer,\n sessionLayer,\n queueLayer\n );\n\n return EventCaptureLive.pipe(Layer.provideMerge(baseLayer));\n};\n\ntype LayerSuccess<T> =\n T extends LayerType<infer ROut, unknown, unknown> ? ROut : never;\n\ntype SDKEnv = LayerSuccess<ReturnType<typeof createSDKLayer>>;\n\nlet sdkRuntime: Runtime.Runtime<SDKEnv> | null = null;\nlet sdkScope: Scope.CloseableScope | null = null;\nconst shouldEmitConsoleLogs = () => !InterfereLogger.isTestEnvironment();\n\nexport function setSDKRuntime(\n scope: Scope.CloseableScope,\n runtime: Runtime.Runtime<SDKEnv>\n): void {\n sdkScope = scope;\n sdkRuntime = runtime;\n}\n\nexport function initRuntimeServices(input: unknown): void {\n if (sdkRuntime) {\n return;\n }\n\n const config = configSchema.parse(input);\n if (shouldEmitConsoleLogs()) {\n console.debug(\"[Interfere SDK] Initializing with config:\", config);\n }\n\n // Build the runtime asynchronously to avoid synchronous fiber resolution\n // biome-ignore lint/complexity/noVoid: fire-and-forget initialization\n void (async () => {\n const scope = await Effect.runPromise(Scope.make());\n\n const runtime = await Effect.runPromise(\n Layer.toRuntime(createSDKLayer(config)).pipe(\n Effect.provideService(Scope.Scope, scope)\n )\n );\n\n setSDKRuntime(scope, runtime);\n\n if (shouldEmitConsoleLogs()) {\n console.debug(\"[Interfere SDK] Runtime initialized successfully\");\n }\n\n Runtime.runPromise(runtime)(\n withInterfereLogger(\n \"react\",\n Effect.gen(function* () {\n const queue = yield* QueueServiceTag;\n\n yield* queue\n .startBatchProcessor()\n .pipe(Effect.provideService(Scope.Scope, scope));\n })\n )\n ).catch((error) => {\n Runtime.runPromise(runtime)(\n withInterfereLogger(\n \"react\",\n Effect.logError(\"Failed to start batch processor\", {\n error: String(error),\n })\n )\n );\n });\n })();\n}\n\nexport function runWithSDK<A, E>(\n effect: Effect.Effect<A, E, SDKEnv>\n): Promise<A> {\n if (!sdkRuntime) {\n throw new Error(\"SDK not initialized. Call initRuntimeServices first.\");\n }\n\n return Runtime.runPromise(sdkRuntime)(withInterfereLogger(\"react\", effect));\n}\n\nexport function forkWithSDK<A, E, R>(effect: Effect.Effect<A, E, R>): void {\n if (!sdkRuntime) {\n if (shouldEmitConsoleLogs()) {\n console.warn(\n \"[Interfere SDK] Attempted to fork effect before SDK initialized\"\n );\n }\n\n return;\n }\n\n Runtime.runFork(sdkRuntime)(\n withInterfereLogger(\"react\", effect as Effect.Effect<A, E, SDKEnv>).pipe(\n Effect.catchAllCause((cause) =>\n Effect.logError(\"Forked effect failed\", { cause: String(cause) })\n )\n )\n );\n}\n\nexport function isSDKInitialized(): boolean {\n return sdkRuntime !== null;\n}\n\nexport async function closeSDK(): Promise<void> {\n if (sdkScope) {\n await Effect.runPromise(\n withInterfereLogger(\"react\", Scope.close(sdkScope, Exit.void))\n );\n\n sdkScope = null;\n sdkRuntime = null;\n }\n}\n\nexport async function flushQueue(timeoutMs: number): Promise<void> {\n if (!sdkRuntime) {\n return;\n }\n await Runtime.runPromise(sdkRuntime)(\n Effect.gen(function* () {\n const queue = yield* QueueServiceTag;\n yield* queue.boundedFlush(timeoutMs);\n })\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAmBA,MAAM,kBAAkB,WAAmB;CACzC,MAAM,oBAAoB;EACxB,GAAG;EACH,SAAS,YAAY;EACtB;CAED,MAAM,cAAc,MAAM,QAAQ,WAAW,OAAO;CACpD,MAAM,eAAe;CACrB,MAAM,YAAY,gBAAgB,kBAAkB;CACpD,MAAM,eAAe;CAErB,MAAM,aAAa,iBAAiB,KAClC,MAAM,QAAQ,YAAY,EAC1B,MAAM,QAAQ,UAAU,EACxB,MAAM,QAAQ,mBAAmB,CAClC;CAED,MAAM,YAAY,MAAM,SACtB,aACA,cACA,WACA,cACA,WACD;AAED,QAAO,iBAAiB,KAAK,MAAM,aAAa,UAAU,CAAC;;AAQ7D,IAAI,aAA6C;AACjD,IAAI,WAAwC;AAC5C,MAAM,8BAA8B,CAAC,gBAAgB,mBAAmB;AAExE,SAAgB,cACd,OACA,SACM;AACN,YAAW;AACX,cAAa;;AAGf,SAAgB,oBAAoB,OAAsB;AACxD,KAAI,WACF;CAGF,MAAM,SAAS,aAAa,MAAM,MAAM;AACxC,KAAI,uBAAuB,CACzB,SAAQ,MAAM,6CAA6C,OAAO;AAKpE,EAAM,YAAY;EAChB,MAAM,QAAQ,MAAM,OAAO,WAAW,MAAM,MAAM,CAAC;EAEnD,MAAM,UAAU,MAAM,OAAO,WAC3B,MAAM,UAAU,eAAe,OAAO,CAAC,CAAC,KACtC,OAAO,eAAe,MAAM,OAAO,MAAM,CAC1C,CACF;AAED,gBAAc,OAAO,QAAQ;AAE7B,MAAI,uBAAuB,CACzB,SAAQ,MAAM,mDAAmD;AAGnE,UAAQ,WAAW,QAAQ,CACzB,oBACE,SACA,OAAO,IAAI,aAAa;AAGtB,WAFc,OAAO,iBAGlB,qBAAqB,CACrB,KAAK,OAAO,eAAe,MAAM,OAAO,MAAM,CAAC;IAClD,CACH,CACF,CAAC,OAAO,UAAU;AACjB,WAAQ,WAAW,QAAQ,CACzB,oBACE,SACA,OAAO,SAAS,mCAAmC,EACjD,OAAO,OAAO,MAAM,EACrB,CAAC,CACH,CACF;IACD;KACA;;AAGN,SAAgB,WACd,QACY;AACZ,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,uDAAuD;AAGzE,QAAO,QAAQ,WAAW,WAAW,CAAC,oBAAoB,SAAS,OAAO,CAAC;;AAG7E,SAAgB,YAAqB,QAAsC;AACzE,KAAI,CAAC,YAAY;AACf,MAAI,uBAAuB,CACzB,SAAQ,KACN,kEACD;AAGH;;AAGF,SAAQ,QAAQ,WAAW,CACzB,oBAAoB,SAAS,OAAsC,CAAC,KAClE,OAAO,eAAe,UACpB,OAAO,SAAS,wBAAwB,EAAE,OAAO,OAAO,MAAM,EAAE,CAAC,CAClE,CACF,CACF;;AAGH,SAAgB,mBAA4B;AAC1C,QAAO,eAAe;;AAGxB,eAAsB,WAA0B;AAC9C,KAAI,UAAU;AACZ,QAAM,OAAO,WACX,oBAAoB,SAAS,MAAM,MAAM,UAAU,KAAK,KAAK,CAAC,CAC/D;AAED,aAAW;AACX,eAAa;;;AAIjB,eAAsB,WAAW,WAAkC;AACjE,KAAI,CAAC,WACH;AAEF,OAAM,QAAQ,WAAW,WAAW,CAClC,OAAO,IAAI,aAAa;AAEtB,UADc,OAAO,iBACR,aAAa,UAAU;GACpC,CACH"}