@fozy-labs/rx-toolkit 0.5.4 → 0.6.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 (303) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +22 -13
  3. package/dist/common/devtools/reduxDevtools.js +17 -2
  4. package/dist/common/react/index.d.ts +1 -0
  5. package/dist/common/react/index.js +1 -0
  6. package/dist/common/react/useIsomorphicLayoutEffect.d.ts +17 -0
  7. package/dist/common/react/useIsomorphicLayoutEffect.js +17 -0
  8. package/dist/index.d.ts +1 -2
  9. package/dist/index.js +1 -2
  10. package/dist/query/api/createApi.d.ts +4 -0
  11. package/dist/query/api/createApi.js +9 -0
  12. package/dist/query/api/index.d.ts +1 -0
  13. package/dist/query/api/index.js +1 -0
  14. package/dist/query/constants.d.ts +12 -0
  15. package/dist/query/constants.js +15 -0
  16. package/dist/query/core/api/Api.d.ts +20 -0
  17. package/dist/query/core/api/Api.js +129 -0
  18. package/dist/query/core/api/constants.d.ts +2 -0
  19. package/dist/query/core/api/constants.js +3 -0
  20. package/dist/query/core/api/index.d.ts +3 -0
  21. package/dist/query/core/api/index.js +3 -0
  22. package/dist/query/core/api/mergeHooks.d.ts +2 -0
  23. package/dist/query/core/api/mergeHooks.js +26 -0
  24. package/dist/query/core/api/normalizeLinks.d.ts +2 -0
  25. package/dist/query/core/api/normalizeLinks.js +11 -0
  26. package/dist/query/core/cache/CacheEntry.d.ts +21 -0
  27. package/dist/query/core/cache/CacheEntry.js +54 -0
  28. package/dist/query/core/cache/CacheMap.d.ts +19 -0
  29. package/dist/query/core/cache/CacheMap.js +32 -0
  30. package/dist/query/core/cache/QueryCacheEntry.d.ts +21 -0
  31. package/dist/query/core/cache/QueryCacheEntry.js +136 -0
  32. package/dist/query/core/cache/index.d.ts +3 -0
  33. package/dist/query/core/cache/index.js +3 -0
  34. package/dist/query/core/command/Command.d.ts +67 -0
  35. package/dist/query/core/command/Command.js +253 -0
  36. package/dist/query/core/command/CommandAgent.d.ts +17 -0
  37. package/dist/query/core/command/CommandAgent.js +67 -0
  38. package/dist/query/core/command/LinkManager.d.ts +24 -0
  39. package/dist/query/core/command/LinkManager.js +71 -0
  40. package/dist/query/core/command/index.d.ts +2 -0
  41. package/dist/query/core/command/index.js +2 -0
  42. package/dist/query/core/errors/CacheEntryRemovedError.d.ts +7 -0
  43. package/dist/query/core/errors/CacheEntryRemovedError.js +9 -0
  44. package/dist/query/core/errors/MachineStateError.d.ts +8 -0
  45. package/dist/query/core/errors/MachineStateError.js +10 -0
  46. package/dist/query/core/errors/MachineTransitionError.d.ts +7 -0
  47. package/dist/query/core/errors/MachineTransitionError.js +9 -0
  48. package/dist/query/core/errors/index.d.ts +3 -0
  49. package/dist/query/core/errors/index.js +3 -0
  50. package/dist/query/core/index.d.ts +8 -0
  51. package/dist/query/core/index.js +8 -0
  52. package/dist/query/core/machine/Machine.d.ts +21 -0
  53. package/dist/query/core/machine/Machine.js +42 -0
  54. package/dist/query/core/machine/MachineBase.d.ts +31 -0
  55. package/dist/query/core/machine/MachineBase.js +176 -0
  56. package/dist/query/core/machine/MachineError.d.ts +10 -0
  57. package/dist/query/core/machine/MachineError.js +19 -0
  58. package/dist/query/core/machine/MachinePending.d.ts +13 -0
  59. package/dist/query/core/machine/MachinePending.js +32 -0
  60. package/dist/query/core/machine/MachineRefreshError.d.ts +12 -0
  61. package/dist/query/core/machine/MachineRefreshError.js +23 -0
  62. package/dist/query/core/machine/MachineRefreshing.d.ts +15 -0
  63. package/dist/query/core/machine/MachineRefreshing.js +43 -0
  64. package/dist/query/core/machine/MachineSuccess.d.ts +12 -0
  65. package/dist/query/core/machine/MachineSuccess.js +23 -0
  66. package/dist/query/core/machine/MachineWithData.d.ts +25 -0
  67. package/dist/query/core/machine/MachineWithData.js +73 -0
  68. package/dist/query/core/machine/index.d.ts +9 -0
  69. package/dist/query/core/machine/index.js +9 -0
  70. package/dist/query/core/machine/machine-helpers.d.ts +9 -0
  71. package/dist/query/core/machine/machine-helpers.js +80 -0
  72. package/dist/query/core/patcher/Patcher.d.ts +30 -0
  73. package/dist/query/core/patcher/Patcher.js +122 -0
  74. package/dist/query/core/patcher/index.d.ts +1 -0
  75. package/dist/query/core/patcher/index.js +1 -0
  76. package/dist/query/core/resource/Resource.d.ts +105 -0
  77. package/dist/query/core/resource/Resource.js +340 -0
  78. package/dist/query/core/resource/ResourceAgent.d.ts +37 -0
  79. package/dist/query/core/resource/ResourceAgent.js +179 -0
  80. package/dist/query/core/resource/index.d.ts +2 -0
  81. package/dist/query/core/resource/index.js +2 -0
  82. package/dist/query/core/snapshoter/Snapshoter.d.ts +22 -0
  83. package/dist/query/core/snapshoter/Snapshoter.js +78 -0
  84. package/dist/query/core/snapshoter/index.d.ts +2 -0
  85. package/dist/query/core/snapshoter/index.js +1 -0
  86. package/dist/query/core/syncer/Syncer.d.ts +32 -0
  87. package/dist/query/core/syncer/Syncer.js +85 -0
  88. package/dist/query/core/syncer/index.d.ts +2 -0
  89. package/dist/query/core/syncer/index.js +1 -0
  90. package/dist/query/index.d.ts +5 -10
  91. package/dist/query/index.js +5 -13
  92. package/dist/query/lib/broadcastSyncDriver.d.ts +5 -0
  93. package/dist/query/lib/broadcastSyncDriver.js +46 -0
  94. package/dist/query/lib/index.d.ts +3 -0
  95. package/dist/query/lib/index.js +3 -0
  96. package/dist/{query-v2 → query}/lib/stableStringify.js +5 -3
  97. package/dist/query/lib/toKeyed.d.ts +11 -0
  98. package/dist/query/lib/toKeyed.js +18 -0
  99. package/dist/query/react/ReactHooksPlugin.d.ts +31 -0
  100. package/dist/query/react/ReactHooksPlugin.js +21 -0
  101. package/dist/query/react/index.d.ts +3 -0
  102. package/dist/query/react/index.js +3 -0
  103. package/dist/query/react/useCommand.d.ts +2 -0
  104. package/dist/query/react/useCommand.js +14 -0
  105. package/dist/query/react/useResource.d.ts +2 -0
  106. package/dist/query/react/useResource.js +16 -0
  107. package/dist/query/types/api.d.ts +39 -0
  108. package/dist/query/types/cache.d.ts +51 -0
  109. package/dist/query/types/command.d.ts +52 -0
  110. package/dist/query/types/common.d.ts +65 -0
  111. package/dist/query/types/index.d.ts +8 -4
  112. package/dist/query/types/index.js +8 -5
  113. package/dist/query/types/plugin-hkt.d.ts +64 -0
  114. package/dist/query/types/resource.d.ts +49 -0
  115. package/dist/query/types/snapshot.d.ts +27 -0
  116. package/dist/query/types/snapshot.js +2 -0
  117. package/dist/query/types/state.d.ts +24 -0
  118. package/dist/signals/base/ComputeCache.js +1 -1
  119. package/dist/signals/base/Devtools.js +2 -6
  120. package/dist/signals/signals/Computed.d.ts +1 -0
  121. package/dist/signals/signals/Computed.js +5 -1
  122. package/dist/signals/signals/Effect.d.ts +0 -4
  123. package/dist/signals/signals/Effect.js +0 -6
  124. package/dist/signals/signals/LocalState.d.ts +1 -10
  125. package/dist/signals/signals/LocalState.js +0 -12
  126. package/dist/signals/signals/Signal.d.ts +2 -8
  127. package/dist/signals/signals/Signal.js +1 -10
  128. package/dist/signals/signals/State.d.ts +2 -1
  129. package/dist/signals/signals/State.js +9 -0
  130. package/dist/signals/types/SignalOptions.d.ts +0 -2
  131. package/dist/signals/types/normalizeSignalOptions.js +0 -3
  132. package/dist/signals/types/signals.types.d.ts +3 -1
  133. package/docs/CHANGELOG.md +54 -2
  134. package/docs/contributing/release/README.md +2 -8
  135. package/docs/migrations/0.6.0.md +224 -0
  136. package/docs/query/README.md +52 -562
  137. package/docs/query/api/README.md +59 -0
  138. package/docs/query/api/_CacheEntry.md +39 -0
  139. package/docs/query/api/_CacheMap.md +30 -0
  140. package/docs/query/api/_QueryCacheEntry.md +81 -0
  141. package/docs/query/api/command-agent.md +60 -0
  142. package/docs/query/api/command.md +76 -0
  143. package/docs/query/api/resource-agent.md +68 -0
  144. package/docs/query/api/resource.md +77 -0
  145. package/docs/query/concepts/agent.md +70 -0
  146. package/docs/query/concepts/architecture.md +139 -0
  147. package/docs/query/concepts/cache.md +81 -0
  148. package/docs/query/concepts/dataflows.md +473 -0
  149. package/docs/query/concepts/keyed.md +42 -0
  150. package/docs/query/concepts/machine.md +106 -0
  151. package/docs/query/concepts/patching.md +85 -0
  152. package/docs/query/usage/broadcast.md +188 -0
  153. package/docs/query/usage/command.md +203 -0
  154. package/docs/query/usage/lifecycle.md +114 -0
  155. package/docs/query/usage/links.md +125 -0
  156. package/docs/query/usage/plugins.md +96 -0
  157. package/docs/query/usage/resource.md +206 -0
  158. package/docs/query/usage/snapshot.md +80 -0
  159. package/docs/usage/react/README.md +45 -91
  160. package/package.json +5 -7
  161. package/dist/query/SKIP_TOKEN.d.ts +0 -1
  162. package/dist/query/SKIP_TOKEN.js +0 -1
  163. package/dist/query/api/createCommand.d.ts +0 -21
  164. package/dist/query/api/createCommand.js +0 -20
  165. package/dist/query/api/createOperation.d.ts +0 -5
  166. package/dist/query/api/createOperation.js +0 -6
  167. package/dist/query/api/createResource.d.ts +0 -3
  168. package/dist/query/api/createResource.js +0 -2
  169. package/dist/query/api/createResourceDuplicator.d.ts +0 -4
  170. package/dist/query/api/createResourceDuplicator.js +0 -2
  171. package/dist/query/api/resetAllQueriesCache.d.ts +0 -1
  172. package/dist/query/api/resetAllQueriesCache.js +0 -4
  173. package/dist/query/core/Command/Command.d.ts +0 -35
  174. package/dist/query/core/Command/Command.js +0 -210
  175. package/dist/query/core/Command/CommandAgent.d.ts +0 -19
  176. package/dist/query/core/Command/CommandAgent.js +0 -54
  177. package/dist/query/core/Command/index.d.ts +0 -2
  178. package/dist/query/core/Command/index.js +0 -2
  179. package/dist/query/core/Operation/Operation.d.ts +0 -8
  180. package/dist/query/core/Operation/Operation.js +0 -4
  181. package/dist/query/core/Operation/OperationAgent.d.ts +0 -4
  182. package/dist/query/core/Operation/OperationAgent.js +0 -4
  183. package/dist/query/core/QueriesCache.d.ts +0 -9
  184. package/dist/query/core/QueriesCache.js +0 -28
  185. package/dist/query/core/QueriesLifetimeHooks.d.ts +0 -22
  186. package/dist/query/core/QueriesLifetimeHooks.js +0 -86
  187. package/dist/query/core/ResetAllQueriesSignal.d.ts +0 -6
  188. package/dist/query/core/ResetAllQueriesSignal.js +0 -11
  189. package/dist/query/core/Resource/Resource.d.ts +0 -51
  190. package/dist/query/core/Resource/Resource.js +0 -232
  191. package/dist/query/core/Resource/ResourceAgent.d.ts +0 -35
  192. package/dist/query/core/Resource/ResourceAgent.js +0 -110
  193. package/dist/query/core/Resource/ResourceDuplicator.d.ts +0 -73
  194. package/dist/query/core/Resource/ResourceDuplicator.js +0 -227
  195. package/dist/query/core/Resource/ResourceDuplicatorAgent.d.ts +0 -35
  196. package/dist/query/core/Resource/ResourceDuplicatorAgent.js +0 -110
  197. package/dist/query/core/Resource/ResourceRef.d.ts +0 -16
  198. package/dist/query/core/Resource/ResourceRef.js +0 -136
  199. package/dist/query/lib/IndirectMap.d.ts +0 -19
  200. package/dist/query/lib/IndirectMap.js +0 -88
  201. package/dist/query/lib/ReactiveCache.d.ts +0 -62
  202. package/dist/query/lib/ReactiveCache.js +0 -80
  203. package/dist/query/react/useCommandAgent.d.ts +0 -24
  204. package/dist/query/react/useCommandAgent.js +0 -39
  205. package/dist/query/react/useOperationAgent.d.ts +0 -6
  206. package/dist/query/react/useOperationAgent.js +0 -6
  207. package/dist/query/react/useResourceAgent.d.ts +0 -6
  208. package/dist/query/react/useResourceAgent.js +0 -31
  209. package/dist/query/react/useResourceRef.d.ts +0 -5
  210. package/dist/query/react/useResourceRef.js +0 -13
  211. package/dist/query/types/Command.types.d.ts +0 -154
  212. package/dist/query/types/Command.types.js +0 -1
  213. package/dist/query/types/Operation.types.d.ts +0 -13
  214. package/dist/query/types/Operation.types.js +0 -1
  215. package/dist/query/types/Resource.types.d.ts +0 -129
  216. package/dist/query/types/Resource.types.js +0 -1
  217. package/dist/query/types/shared.types.d.ts +0 -26
  218. package/dist/query/types/shared.types.js +0 -1
  219. package/dist/query-v2/api/createApi.d.ts +0 -10
  220. package/dist/query-v2/api/createApi.js +0 -83
  221. package/dist/query-v2/core/common/CacheEntry.d.ts +0 -29
  222. package/dist/query-v2/core/common/CacheEntry.js +0 -71
  223. package/dist/query-v2/core/common/CacheMap.d.ts +0 -38
  224. package/dist/query-v2/core/common/CacheMap.js +0 -127
  225. package/dist/query-v2/core/common/LifecycleHooks.d.ts +0 -22
  226. package/dist/query-v2/core/common/LifecycleHooks.js +0 -104
  227. package/dist/query-v2/core/common/index.d.ts +0 -3
  228. package/dist/query-v2/core/common/index.js +0 -3
  229. package/dist/query-v2/core/index.d.ts +0 -3
  230. package/dist/query-v2/core/index.js +0 -3
  231. package/dist/query-v2/core/machines/Machine.d.ts +0 -14
  232. package/dist/query-v2/core/machines/Machine.js +0 -33
  233. package/dist/query-v2/core/machines/MachineError.d.ts +0 -11
  234. package/dist/query-v2/core/machines/MachineError.js +0 -26
  235. package/dist/query-v2/core/machines/MachineIdle.d.ts +0 -8
  236. package/dist/query-v2/core/machines/MachineIdle.js +0 -19
  237. package/dist/query-v2/core/machines/MachinePending.d.ts +0 -12
  238. package/dist/query-v2/core/machines/MachinePending.js +0 -29
  239. package/dist/query-v2/core/machines/MachineRefreshing.d.ts +0 -14
  240. package/dist/query-v2/core/machines/MachineRefreshing.js +0 -46
  241. package/dist/query-v2/core/machines/MachineSuccess.d.ts +0 -16
  242. package/dist/query-v2/core/machines/MachineSuccess.js +0 -42
  243. package/dist/query-v2/core/machines/MachineWithData.d.ts +0 -18
  244. package/dist/query-v2/core/machines/MachineWithData.js +0 -40
  245. package/dist/query-v2/core/machines/Patcher.d.ts +0 -20
  246. package/dist/query-v2/core/machines/Patcher.js +0 -104
  247. package/dist/query-v2/core/machines/index.d.ts +0 -8
  248. package/dist/query-v2/core/machines/index.js +0 -8
  249. package/dist/query-v2/core/resource/ResourceV2.d.ts +0 -120
  250. package/dist/query-v2/core/resource/ResourceV2.js +0 -464
  251. package/dist/query-v2/core/resource/ResourceV2Agent.d.ts +0 -26
  252. package/dist/query-v2/core/resource/ResourceV2Agent.js +0 -132
  253. package/dist/query-v2/core/resource/index.d.ts +0 -2
  254. package/dist/query-v2/core/resource/index.js +0 -2
  255. package/dist/query-v2/index.d.ts +0 -11
  256. package/dist/query-v2/index.js +0 -17
  257. package/dist/query-v2/lib/NO_VALUE.d.ts +0 -2
  258. package/dist/query-v2/lib/NO_VALUE.js +0 -1
  259. package/dist/query-v2/lib/SKIP_TOKEN.d.ts +0 -2
  260. package/dist/query-v2/lib/SKIP_TOKEN.js +0 -1
  261. package/dist/query-v2/lib/index.d.ts +0 -4
  262. package/dist/query-v2/lib/index.js +0 -3
  263. package/dist/query-v2/plugins/ReactHooksPlugin.d.ts +0 -25
  264. package/dist/query-v2/plugins/ReactHooksPlugin.js +0 -19
  265. package/dist/query-v2/plugins/types.d.ts +0 -1
  266. package/dist/query-v2/react/__tests__/helpers.d.ts +0 -12
  267. package/dist/query-v2/react/__tests__/helpers.js +0 -33
  268. package/dist/query-v2/react/index.d.ts +0 -2
  269. package/dist/query-v2/react/index.js +0 -2
  270. package/dist/query-v2/react/useResourceV2Agent.d.ts +0 -12
  271. package/dist/query-v2/react/useResourceV2Agent.js +0 -36
  272. package/dist/query-v2/react/useResourceV2Ref.d.ts +0 -12
  273. package/dist/query-v2/react/useResourceV2Ref.js +0 -57
  274. package/dist/query-v2/snapshot/Snapshot.d.ts +0 -13
  275. package/dist/query-v2/snapshot/Snapshot.js +0 -76
  276. package/dist/query-v2/types/agent.types.d.ts +0 -54
  277. package/dist/query-v2/types/api.types.d.ts +0 -22
  278. package/dist/query-v2/types/cache.types.d.ts +0 -37
  279. package/dist/query-v2/types/index.d.ts +0 -9
  280. package/dist/query-v2/types/index.js +0 -9
  281. package/dist/query-v2/types/lifecycle.types.d.ts +0 -25
  282. package/dist/query-v2/types/machine.types.d.ts +0 -67
  283. package/dist/query-v2/types/plugin.types.d.ts +0 -38
  284. package/dist/query-v2/types/resource.types.d.ts +0 -35
  285. package/dist/query-v2/types/resource.types.js +0 -1
  286. package/dist/query-v2/types/shared.types.d.ts +0 -20
  287. package/dist/query-v2/types/shared.types.js +0 -1
  288. package/dist/query-v2/types/snapshot.types.d.ts +0 -21
  289. package/dist/query-v2/types/snapshot.types.js +0 -1
  290. package/docs/contributing/query-v2/README.md +0 -379
  291. package/docs/migrations/query-v2.md +0 -171
  292. package/docs/query-v2/README.md +0 -280
  293. package/docs/query-v2/api-reference.md +0 -235
  294. package/docs/query-v2/optimistic-updates.md +0 -148
  295. package/docs/query-v2/ssr.md +0 -130
  296. /package/dist/{query-v2 → query}/lib/stableStringify.d.ts +0 -0
  297. /package/dist/{query-v2/plugins/types.js → query/types/api.js} +0 -0
  298. /package/dist/{query-v2/types/agent.types.js → query/types/cache.js} +0 -0
  299. /package/dist/{query-v2/types/api.types.js → query/types/command.js} +0 -0
  300. /package/dist/{query-v2/types/cache.types.js → query/types/common.js} +0 -0
  301. /package/dist/{query-v2/types/lifecycle.types.js → query/types/plugin-hkt.js} +0 -0
  302. /package/dist/{query-v2/types/machine.types.js → query/types/resource.js} +0 -0
  303. /package/dist/{query-v2/types/plugin.types.js → query/types/state.js} +0 -0
@@ -1,464 +0,0 @@
1
- import { shallowEqual } from "../../../common/utils/shallowEqual";
2
- import { SKIP } from "../../../query-v2/lib/SKIP_TOKEN";
3
- import { stableStringify } from "../../../query-v2/lib/stableStringify";
4
- import { Batcher } from "../../../signals";
5
- import { CacheEntry } from "../common/CacheEntry";
6
- import { CacheMap } from "../common/CacheMap";
7
- import { LifecycleHooks } from "../common/LifecycleHooks";
8
- import { MachineIdle } from "../machines/MachineIdle";
9
- import { MachineRefreshing } from "../machines/MachineRefreshing";
10
- import { MachineSuccess } from "../machines/MachineSuccess";
11
- import { MachineWithData } from "../machines/MachineWithData";
12
- import { ResourceV2Agent } from "./ResourceV2Agent";
13
- /**
14
- * Cache-backed resource manager.
15
- * Orchestrates queries, caching, lifecycle hooks, GC, and optimistic patches for a single resource type.
16
- */
17
- export class ResourceV2 {
18
- _cache;
19
- _queryFn;
20
- _lifecycleHooks;
21
- _serializeArgs;
22
- _compareArg;
23
- _keyStrategy;
24
- _cacheLifetime;
25
- _beforeDevtoolsPush;
26
- _key;
27
- _keyPrefix;
28
- /** In-flight queries keyed by serialized args — for dedup + abort */
29
- _inFlight = new Map();
30
- /** Cache lifetime timers keyed by serialized args */
31
- _gcTimers = new Map();
32
- /** Refresh error listeners (used by Agent for refreshError tracking) */
33
- _refreshErrorListeners = new Set();
34
- constructor(config) {
35
- this._queryFn = config.queryFn;
36
- this._serializeArgs = config.serializeArgs ?? stableStringify;
37
- this._compareArg = config.compareArg ?? shallowEqual;
38
- this._keyStrategy = config.keyStrategy ?? "serialize";
39
- this._cacheLifetime = config.cacheLifetime ?? 60_000;
40
- this._beforeDevtoolsPush = config.beforeDevtoolsPush;
41
- this._key = config.key;
42
- this._keyPrefix = config.keyPrefix;
43
- this._cache = CacheMap.create({
44
- keyStrategy: this._keyStrategy,
45
- serializeArgs: this._serializeArgs,
46
- compareArg: this._compareArg,
47
- doCacheArgs: config.doCacheArgs ?? false,
48
- });
49
- this._lifecycleHooks = new LifecycleHooks({
50
- onCacheEntryAdded: config.onCacheEntryAdded,
51
- onQueryStarted: config.onQueryStarted,
52
- serializeArgs: this._serializeArgs,
53
- });
54
- }
55
- /**
56
- * Create an agent that tracks a single cache entry with reactive state (SWR).
57
- *
58
- * @returns A new agent bound to this resource.
59
- */
60
- createAgent() {
61
- return new ResourceV2Agent(this);
62
- }
63
- /**
64
- * Execute a query for the given args, returning the cache entry.
65
- * Deduplicates concurrent calls for the same args unless `doForce` is set.
66
- *
67
- * @param args - Query arguments (used as cache key).
68
- * @param doForce - If true, bypass dedup and re-execute the query.
69
- * @returns The cache entry after query initiation.
70
- */
71
- async query(args, doForce) {
72
- // SKIP check
73
- if (args === SKIP) {
74
- throw new Error("SKIP_TOKEN is not valid for direct query()");
75
- }
76
- const key = this._serializeArgs(args);
77
- // Cache hit — return existing entry (unless force)
78
- const existing = this._cache.get(args);
79
- if (existing && !doForce) {
80
- const machine = existing.peek();
81
- // If already success or pending or refreshing, return the entry
82
- if (machine.state.status !== "idle") {
83
- return existing;
84
- }
85
- }
86
- // Query dedup — if already in-flight for same args, return same promise
87
- if (!doForce) {
88
- const flight = this._inFlight.get(key);
89
- if (flight) {
90
- return flight.promise;
91
- }
92
- }
93
- // Cancel existing in-flight for same args (ADR-4 Layer 1)
94
- const existingFlight = this._inFlight.get(key);
95
- if (existingFlight) {
96
- existingFlight.abortController.abort();
97
- this._inFlight.delete(key);
98
- }
99
- // Cancel any pending GC timer for these args
100
- this._cancelGcTimer(key);
101
- const abortController = new AbortController();
102
- const isNewEntry = !existing;
103
- // Create or reuse entry inside Batcher for atomic updates
104
- let cacheEntry;
105
- // Set up entry and pending state
106
- Batcher.run(() => {
107
- if (isNewEntry || doForce) {
108
- if (existing && doForce) {
109
- cacheEntry = existing;
110
- // Transition to refreshing or pending based on current state
111
- const current = cacheEntry.peek();
112
- if (current instanceof MachineSuccess) {
113
- const refreshing = current.invalidate();
114
- cacheEntry.set(refreshing);
115
- }
116
- else if (current instanceof MachineRefreshing) {
117
- // Already refreshing — it'll be re-queried
118
- }
119
- else {
120
- const idle = MachineIdle.create();
121
- const pending = idle.start(args);
122
- cacheEntry.set(pending);
123
- }
124
- }
125
- else {
126
- // New entry
127
- const idle = MachineIdle.create();
128
- const pending = idle.start(args);
129
- cacheEntry = new CacheEntry(pending, this._buildCacheEntryOptions(args));
130
- this._cache.set(args, cacheEntry);
131
- // Fire onCacheEntryAdded for new entries
132
- this._lifecycleHooks.fireCacheEntryAdded(args, () => cacheEntry.peek());
133
- }
134
- }
135
- else {
136
- cacheEntry = existing;
137
- const current = cacheEntry.peek();
138
- const pending = current.start(args);
139
- cacheEntry.set(pending);
140
- }
141
- // Fire onQueryStarted
142
- this._lifecycleHooks.fireQueryStarted(args, () => cacheEntry);
143
- });
144
- // Execute queryFn
145
- const promise = this._executeQuery(args, key, cacheEntry, abortController);
146
- this._inFlight.set(key, { promise: promise, abortController });
147
- return promise;
148
- }
149
- /**
150
- * Reactive query — read machine state as a signal dependency.
151
- * Initiates the query if not already cached.
152
- *
153
- * @param args - Query arguments.
154
- * @returns Current machine state (registers a reactive subscription).
155
- */
156
- query$(args, doForce) {
157
- if (args === SKIP) {
158
- return MachineIdle.create().state;
159
- }
160
- // Trigger query (fire-and-forget) and read entry signal reactively
161
- const entryResult = this._cache.get(args);
162
- if (!entryResult) {
163
- // Initiate query
164
- this.query(args, doForce);
165
- // Return idle while it's being initiated
166
- const newEntry = this._cache.get(args);
167
- if (newEntry) {
168
- return newEntry.machine$().state;
169
- }
170
- return MachineIdle.create().state;
171
- }
172
- if (doForce) {
173
- this.query(args, true);
174
- }
175
- // Reactive read — registers signal dependency
176
- return entryResult.machine$().state;
177
- }
178
- /**
179
- * Get the cache entry for the given args without initiating a query (unless `doInitiate` is set).
180
- *
181
- * @param args - Query arguments.
182
- * @returns The cache entry, or `null` if not cached.
183
- */
184
- entry(args, doInitiate) {
185
- const existing = this._cache.get(args);
186
- if (existing) {
187
- return existing;
188
- }
189
- if (doInitiate) {
190
- // Fire-and-forget query initiation
191
- this.query(args);
192
- return this._cache.get(args) ?? null;
193
- }
194
- return null;
195
- }
196
- entry$(args, doInitiate) {
197
- if (args === SKIP) {
198
- return MachineIdle.create().state;
199
- }
200
- const existing = this._cache.get(args);
201
- if (!existing && doInitiate) {
202
- this.query(args);
203
- const created = this._cache.get(args);
204
- if (created) {
205
- return created.machine$().state;
206
- }
207
- }
208
- if (existing) {
209
- return existing.machine$().state;
210
- }
211
- return MachineIdle.create().state;
212
- }
213
- invalidate(args) {
214
- const existing = this._cache.get(args);
215
- if (!existing)
216
- return;
217
- const machine = existing.peek();
218
- // Only invalidate from success state
219
- if (!(machine instanceof MachineSuccess))
220
- return;
221
- const key = this._serializeArgs(args);
222
- // Abort any existing in-flight for these args (ADR-4 Layer 1)
223
- const flight = this._inFlight.get(key);
224
- if (flight) {
225
- flight.abortController.abort();
226
- this._inFlight.delete(key);
227
- }
228
- const abortController = new AbortController();
229
- Batcher.run(() => {
230
- const refreshing = machine.invalidate();
231
- existing.set(refreshing);
232
- this._lifecycleHooks.fireQueryStarted(args, () => existing);
233
- });
234
- const promise = this._executeRefresh(args, key, existing, abortController);
235
- this._inFlight.set(key, { promise, abortController });
236
- }
237
- compareArgs(a, b) {
238
- if (this._keyStrategy === "compare") {
239
- return this._compareArg(a, b);
240
- }
241
- return this._serializeArgs(a) === this._serializeArgs(b);
242
- }
243
- /** Public key getter for API registry */
244
- get key() {
245
- return this._key;
246
- }
247
- /** Public keyStrategy getter for snapshot validation */
248
- get keyStrategy() {
249
- return this._keyStrategy;
250
- }
251
- /** Serialize args to string key */
252
- getSerializedKey(args) {
253
- return this._serializeArgs(args);
254
- }
255
- /** Iterate cache entries — for snapshot */
256
- cacheEntries() {
257
- return this._cache.entries();
258
- }
259
- /** Hydrate a cache entry from snapshot data */
260
- hydrateEntry(args, machine) {
261
- const existing = this._cache.get(args);
262
- if (existing)
263
- return; // Don't overwrite existing entries
264
- const entry = new CacheEntry(machine, this._buildCacheEntryOptions(args));
265
- this._cache.set(args, entry);
266
- // Fire lifecycle for hydrated entries
267
- this._lifecycleHooks.fireCacheEntryAdded(args, () => entry.peek());
268
- if (machine.state.status === "success") {
269
- this._lifecycleHooks.resolveCacheDataLoaded(args, machine.state.data);
270
- }
271
- }
272
- /** Check if cache entry exists for given args */
273
- hasEntry(args) {
274
- return this._cache.has(args);
275
- }
276
- /** Pre-populate cache with data */
277
- populateEntry(args, data) {
278
- const existing = this._cache.get(args);
279
- if (existing) {
280
- const success = MachineSuccess.create(data, args);
281
- existing.set(success);
282
- return;
283
- }
284
- const success = MachineSuccess.create(data, args);
285
- const entry = new CacheEntry(success, this._buildCacheEntryOptions(args));
286
- this._cache.set(args, entry);
287
- }
288
- /** Create an optimistic patch on a cache entry */
289
- createEntryPatch(args, patchFn) {
290
- const entry = this._cache.get(args);
291
- if (!entry)
292
- return null;
293
- const machine = entry.peek();
294
- if (!(machine instanceof MachineWithData))
295
- return null;
296
- const { machine: patchedMachine, patch } = machine.createPatch(patchFn);
297
- entry.set(patchedMachine);
298
- return {
299
- commit: () => {
300
- const current = entry.peek();
301
- if (current instanceof MachineWithData) {
302
- const finished = current.finishPatch("commit", patch);
303
- entry.set(finished);
304
- }
305
- },
306
- abort: () => {
307
- const current = entry.peek();
308
- if (current instanceof MachineWithData) {
309
- const finished = current.finishPatch("abort", patch);
310
- entry.set(finished);
311
- }
312
- },
313
- };
314
- }
315
- /** Lock a cache entry — prevent GC eviction. Returns unlock function. */
316
- lockEntry(args) {
317
- this.cancelGc(args);
318
- return {
319
- unlock: () => {
320
- // Re-schedule GC (will be cancelled again if still subscribed)
321
- this.scheduleGc(args);
322
- },
323
- };
324
- }
325
- /** Subscribe to refresh error events (used by Agent) */
326
- onRefreshError(listener) {
327
- this._refreshErrorListeners.add(listener);
328
- return () => {
329
- this._refreshErrorListeners.delete(listener);
330
- };
331
- }
332
- /** Reset the entire cache — aborts in-flight requests, clears GC timers, completes all entries. */
333
- resetCache() {
334
- // Abort all in-flight requests
335
- for (const [, flight] of this._inFlight) {
336
- flight.abortController.abort();
337
- }
338
- this._inFlight.clear();
339
- // Clear all GC timers
340
- for (const [, timer] of this._gcTimers) {
341
- clearTimeout(timer);
342
- }
343
- this._gcTimers.clear();
344
- // Complete all cache entries
345
- for (const entry of this._cache.values()) {
346
- entry.complete();
347
- }
348
- // Clear lifecycle hooks
349
- this._lifecycleHooks.clearAll();
350
- // Clear the cache map
351
- this._cache.clear();
352
- }
353
- /**
354
- * Schedule GC for a cache entry after cacheLifetime.
355
- * Called when subscriber count drops to 0.
356
- */
357
- scheduleGc(args) {
358
- const key = this._serializeArgs(args);
359
- this._cancelGcTimer(key);
360
- this._gcTimers.set(key, setTimeout(() => {
361
- this._gcTimers.delete(key);
362
- this._evictEntry(args, key);
363
- }, this._cacheLifetime));
364
- }
365
- /**
366
- * Cancel pending GC for a cache entry.
367
- * Called when a new subscriber appears.
368
- */
369
- cancelGc(args) {
370
- const key = this._serializeArgs(args);
371
- this._cancelGcTimer(key);
372
- }
373
- // --- Private helpers ---
374
- _cancelGcTimer(key) {
375
- const timer = this._gcTimers.get(key);
376
- if (timer != null) {
377
- clearTimeout(timer);
378
- this._gcTimers.delete(key);
379
- }
380
- }
381
- _evictEntry(args, key) {
382
- const entry = this._cache.get(args);
383
- if (!entry)
384
- return;
385
- // Abort in-flight if any
386
- const flight = this._inFlight.get(key);
387
- if (flight) {
388
- flight.abortController.abort();
389
- this._inFlight.delete(key);
390
- }
391
- // Fire lifecycle removal
392
- this._lifecycleHooks.fireCacheEntryRemoved(args);
393
- // Complete the entry (ADR-4 Layer 3)
394
- entry.complete();
395
- // Remove from cache
396
- this._cache.delete(args);
397
- }
398
- async _executeQuery(args, key, cacheEntry, abortController) {
399
- try {
400
- const data = await this._queryFn(args, {
401
- abortSignal: abortController.signal,
402
- });
403
- // If aborted after await, don't process result
404
- if (abortController.signal.aborted) {
405
- return cacheEntry;
406
- }
407
- Batcher.run(() => {
408
- const current = cacheEntry.peek();
409
- if (current.state.status === "pending") {
410
- const success = current.successHappened(data);
411
- cacheEntry.set(success);
412
- }
413
- else if (current.state.status === "refreshing") {
414
- const success = current.successHappened(data);
415
- cacheEntry.set(success);
416
- }
417
- });
418
- this._lifecycleHooks.resolveCacheDataLoaded(args, data);
419
- this._lifecycleHooks.resolveQueryFulfilled(data);
420
- }
421
- catch (error) {
422
- if (abortController.signal.aborted) {
423
- this._lifecycleHooks.rejectQueryFulfilled(error);
424
- return cacheEntry;
425
- }
426
- Batcher.run(() => {
427
- const current = cacheEntry.peek();
428
- if (current.state.status === "pending") {
429
- const errorMachine = current.errorHappened(error);
430
- cacheEntry.set(errorMachine);
431
- }
432
- else if (current.state.status === "refreshing") {
433
- // ADR-2: Preserve stale data
434
- const success = current.errorHappened(error);
435
- cacheEntry.set(success);
436
- // Notify refresh error listeners (used by Agent)
437
- for (const listener of this._refreshErrorListeners) {
438
- listener(args, error);
439
- }
440
- }
441
- });
442
- this._lifecycleHooks.rejectQueryFulfilled(error);
443
- }
444
- finally {
445
- this._inFlight.delete(key);
446
- }
447
- return cacheEntry;
448
- }
449
- async _executeRefresh(args, key, cacheEntry, abortController) {
450
- return this._executeQuery(args, key, cacheEntry, abortController);
451
- }
452
- _buildCacheEntryOptions(args) {
453
- const parts = [];
454
- if (this._keyPrefix)
455
- parts.push(this._keyPrefix);
456
- if (this._key)
457
- parts.push(this._key);
458
- parts.push(this._serializeArgs(args));
459
- return {
460
- keyParts: parts.length > 0 ? parts : undefined,
461
- beforeDevtoolsPush: this._beforeDevtoolsPush,
462
- };
463
- }
464
- }
@@ -1,26 +0,0 @@
1
- import { type SKIP_TOKEN } from "../../../query-v2/lib/SKIP_TOKEN";
2
- import type { IResourceV2Agent, IResourceV2AgentState } from "../../../query-v2/types/agent.types";
3
- import type { ComputeFn } from "../../../signals";
4
- import type { ResourceV2 } from "./ResourceV2";
5
- /**
6
- * Agent that tracks a single cache entry with reactive state, designed for React hook consumption.
7
- * Provides SWR (stale-while-revalidate) semantics by keeping previous data while new data loads.
8
- */
9
- export declare class ResourceV2Agent<TArgs, TData, TError = Error> implements IResourceV2Agent<TArgs, TData, TError> {
10
- private readonly _resource;
11
- private readonly _tracking$;
12
- private readonly _refreshError$;
13
- private readonly _state$;
14
- private readonly _unsubRefreshError;
15
- private _currentArgs;
16
- constructor(resource: ResourceV2<TArgs, TData, TError>);
17
- /** Computed reactive state signal — projects CacheEntry machine state into a flat agent state object. */
18
- get state$(): ComputeFn<IResourceV2AgentState<TArgs, TData, TError>>;
19
- /**
20
- * Start (or re-start) the agent with new args. Skips if args are unchanged.
21
- *
22
- * @param args - Query arguments, or `SKIP_TOKEN` to do nothing.
23
- */
24
- start(args: TArgs | SKIP_TOKEN): Promise<void>;
25
- compareArgs(a: TArgs, b: TArgs): boolean;
26
- }
@@ -1,132 +0,0 @@
1
- import { SKIP } from "../../../query-v2/lib/SKIP_TOKEN";
2
- import { Signal } from "../../../signals";
3
- /**
4
- * Agent that tracks a single cache entry with reactive state, designed for React hook consumption.
5
- * Provides SWR (stale-while-revalidate) semantics by keeping previous data while new data loads.
6
- */
7
- export class ResourceV2Agent {
8
- _resource;
9
- _tracking$;
10
- _refreshError$;
11
- _state$;
12
- _unsubRefreshError;
13
- _currentArgs = null;
14
- constructor(resource) {
15
- this._resource = resource;
16
- // Agent signals are internal derived state for React hooks — only CacheEntry signals belong in devtools
17
- this._tracking$ = Signal.state({ previous: null, current: null }, { isDisabled: true });
18
- // Agent signal — excluded from devtools; only CacheEntry signals represent canonical cache state
19
- this._refreshError$ = Signal.state(null, { isDisabled: true });
20
- // Subscribe to refresh errors from the resource
21
- this._unsubRefreshError = this._resource.onRefreshError((args, error) => {
22
- if (this._currentArgs !== null && this._resource.compareArgs(this._currentArgs, args)) {
23
- this._refreshError$.set(error);
24
- }
25
- });
26
- // Agent signal — excluded from devtools; only CacheEntry signals represent canonical cache state
27
- this._state$ = Signal.compute(() => {
28
- const { previous, current } = this._tracking$();
29
- if (!current) {
30
- return {
31
- status: "idle",
32
- data: null,
33
- error: null,
34
- args: null,
35
- isLoading: false,
36
- isInitialLoading: false,
37
- isRefreshing: false,
38
- isSuccess: false,
39
- isError: false,
40
- refreshError: this._refreshError$(),
41
- };
42
- }
43
- // Read machine$ reactively — this is the key reactive subscription
44
- const machine = current.machine$();
45
- const machineState = machine.state;
46
- const status = machineState.status;
47
- // Determine previous data for SWR
48
- let previousData = null;
49
- if (previous) {
50
- const prevMachine = previous.machine$();
51
- const prevState = prevMachine.state;
52
- if (prevState.data != null) {
53
- previousData = prevState.data;
54
- }
55
- }
56
- const currentData = machineState.data;
57
- const isLoading = status === "pending" || status === "refreshing";
58
- const isRefreshing = status === "refreshing";
59
- // SWR: use previous data if current is loading and has no data yet
60
- const data = currentData ?? (isLoading ? previousData : null);
61
- const hasPreviousData = previousData !== null;
62
- const isInitialLoading = isLoading && !hasPreviousData && currentData === null;
63
- const isSuccess = data !== null;
64
- const isError = status === "error";
65
- const error = isError ? machineState.error : null;
66
- return {
67
- status,
68
- data,
69
- error,
70
- args: machineState.args ?? null,
71
- isLoading,
72
- isInitialLoading,
73
- isRefreshing,
74
- isSuccess,
75
- isError,
76
- refreshError: this._refreshError$(),
77
- };
78
- }, { isDisabled: true });
79
- }
80
- /** Computed reactive state signal — projects CacheEntry machine state into a flat agent state object. */
81
- get state$() {
82
- return this._state$;
83
- }
84
- /**
85
- * Start (or re-start) the agent with new args. Skips if args are unchanged.
86
- *
87
- * @param args - Query arguments, or `SKIP_TOKEN` to do nothing.
88
- */
89
- async start(args) {
90
- // SKIP: no-op
91
- if (args === SKIP) {
92
- return;
93
- }
94
- const typedArgs = args;
95
- // Same args check — skip if unchanged
96
- if (this._currentArgs !== null && this._resource.compareArgs(this._currentArgs, typedArgs)) {
97
- return;
98
- }
99
- this._currentArgs = typedArgs;
100
- // Query the resource — get a cache entry
101
- const entryPromise = this._resource.query(typedArgs);
102
- // Get the entry synchronously from the resource
103
- const entry = this._resource.entry(typedArgs);
104
- // Swap previous/current
105
- const oldTracking = this._tracking$.peek();
106
- this._tracking$.set({
107
- previous: oldTracking.current,
108
- current: entry,
109
- });
110
- // Wait for the query to resolve and then clear previous
111
- try {
112
- await entryPromise;
113
- // Clear refreshError on successful query completion
114
- this._refreshError$.set(null);
115
- }
116
- catch {
117
- // query() shouldn't reject, but handle gracefully
118
- }
119
- // Clear previous after current resolves (success or error)
120
- // Only if current hasn't changed (latest-wins)
121
- const currentTracking = this._tracking$.peek();
122
- if (currentTracking.current === entry) {
123
- this._tracking$.set({
124
- previous: null,
125
- current: entry,
126
- });
127
- }
128
- }
129
- compareArgs(a, b) {
130
- return this._resource.compareArgs(a, b);
131
- }
132
- }
@@ -1,2 +0,0 @@
1
- export { ResourceV2, type ResourceV2Config } from "./ResourceV2";
2
- export { ResourceV2Agent } from "./ResourceV2Agent";
@@ -1,2 +0,0 @@
1
- export { ResourceV2 } from "./ResourceV2";
2
- export { ResourceV2Agent } from "./ResourceV2Agent";
@@ -1,11 +0,0 @@
1
- export { SKIP, type SKIP_TOKEN } from "./lib/SKIP_TOKEN";
2
- export { NO_VALUE } from "./lib/NO_VALUE";
3
- export type { NO_VALUE as NO_VALUE_TYPE } from "./lib/NO_VALUE";
4
- export { stableStringify } from "./lib/stableStringify";
5
- export { Machine, type TMachineInstance, MachineIdle, MachinePending, MachineSuccess, MachineError, MachineRefreshing, MachineWithData, Patcher, CacheEntry, type CacheEntryOptions, CacheMap, type TCacheMapInstance, LifecycleHooks, ResourceV2, type ResourceV2Config, ResourceV2Agent, } from "./core";
6
- export { createApi } from "./api/createApi";
7
- export { ReactHooksPlugin } from "./plugins/ReactHooksPlugin";
8
- export type { IReactHooksPluginContributions } from "./plugins/ReactHooksPlugin";
9
- export { useResourceV2Agent, useResourceV2Ref } from "./react";
10
- export { getSnapshot, hydrateSnapshot, CURRENT_SNAPSHOT_VERSION } from "./snapshot/Snapshot";
11
- export * from "./types";
@@ -1,17 +0,0 @@
1
- // Sentinel tokens
2
- export { SKIP } from "./lib/SKIP_TOKEN";
3
- export { NO_VALUE } from "./lib/NO_VALUE";
4
- // Utilities
5
- export { stableStringify } from "./lib/stableStringify";
6
- // Core — Machine classes
7
- export { Machine, MachineIdle, MachinePending, MachineSuccess, MachineError, MachineRefreshing, MachineWithData, Patcher, CacheEntry, CacheMap, LifecycleHooks, ResourceV2, ResourceV2Agent, } from "./core";
8
- // API factory
9
- export { createApi } from "./api/createApi";
10
- // Plugins
11
- export { ReactHooksPlugin } from "./plugins/ReactHooksPlugin";
12
- // React hooks (standalone)
13
- export { useResourceV2Agent, useResourceV2Ref } from "./react";
14
- // Snapshot
15
- export { getSnapshot, hydrateSnapshot, CURRENT_SNAPSHOT_VERSION } from "./snapshot/Snapshot";
16
- // Types
17
- export * from "./types";
@@ -1,2 +0,0 @@
1
- export declare const NO_VALUE: unique symbol;
2
- export type NO_VALUE = typeof NO_VALUE;
@@ -1 +0,0 @@
1
- export const NO_VALUE = Symbol("NO_VALUE");
@@ -1,2 +0,0 @@
1
- export declare const SKIP: unique symbol;
2
- export type SKIP_TOKEN = typeof SKIP;
@@ -1 +0,0 @@
1
- export const SKIP = Symbol("SKIP");
@@ -1,4 +0,0 @@
1
- export { SKIP, type SKIP_TOKEN } from "./SKIP_TOKEN";
2
- export { NO_VALUE } from "./NO_VALUE";
3
- export type { NO_VALUE as NO_VALUE_TYPE } from "./NO_VALUE";
4
- export { stableStringify } from "./stableStringify";
@@ -1,3 +0,0 @@
1
- export { SKIP } from "./SKIP_TOKEN";
2
- export { NO_VALUE } from "./NO_VALUE";
3
- export { stableStringify } from "./stableStringify";