@tanstack/pacer 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +7 -7
  2. package/dist/async-batcher.cjs.map +1 -1
  3. package/dist/async-batcher.js.map +1 -1
  4. package/dist/async-debouncer.cjs.map +1 -1
  5. package/dist/async-debouncer.js.map +1 -1
  6. package/dist/async-queuer.cjs.map +1 -1
  7. package/dist/async-queuer.js.map +1 -1
  8. package/dist/async-rate-limiter.cjs.map +1 -1
  9. package/dist/async-rate-limiter.js.map +1 -1
  10. package/dist/async-retryer.cjs.map +1 -1
  11. package/dist/async-retryer.js.map +1 -1
  12. package/dist/async-throttler.cjs.map +1 -1
  13. package/dist/async-throttler.js.map +1 -1
  14. package/dist/batcher.cjs.map +1 -1
  15. package/dist/batcher.js.map +1 -1
  16. package/dist/debouncer.cjs.map +1 -1
  17. package/dist/debouncer.js.map +1 -1
  18. package/dist/event-client.cjs +38 -3
  19. package/dist/event-client.cjs.map +1 -1
  20. package/dist/event-client.d.cts +53 -36
  21. package/dist/event-client.d.ts +53 -36
  22. package/dist/event-client.js +37 -4
  23. package/dist/event-client.js.map +1 -1
  24. package/dist/index.cjs +2 -0
  25. package/dist/index.d.cts +2 -2
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +2 -2
  28. package/dist/queuer.cjs.map +1 -1
  29. package/dist/queuer.js.map +1 -1
  30. package/dist/rate-limiter.cjs.map +1 -1
  31. package/dist/rate-limiter.js.map +1 -1
  32. package/dist/throttler.cjs.map +1 -1
  33. package/dist/throttler.js.map +1 -1
  34. package/package.json +3 -3
  35. package/src/async-batcher.ts +6 -2
  36. package/src/async-debouncer.ts +6 -2
  37. package/src/async-queuer.ts +6 -2
  38. package/src/async-rate-limiter.ts +6 -2
  39. package/src/async-retryer.ts +6 -2
  40. package/src/async-throttler.ts +6 -2
  41. package/src/batcher.ts +6 -2
  42. package/src/debouncer.ts +4 -2
  43. package/src/event-client.ts +109 -48
  44. package/src/index.ts +10 -2
  45. package/src/queuer.ts +4 -2
  46. package/src/rate-limiter.ts +4 -2
  47. package/src/throttler.ts +5 -3
@@ -1,68 +1,129 @@
1
1
  import { EventClient } from '@tanstack/devtools-event-client'
2
- import type { AsyncBatcher } from './async-batcher'
3
- import type { AsyncDebouncer } from './async-debouncer'
4
- import type { AsyncQueuer } from './async-queuer'
5
- import type { AsyncRateLimiter } from './async-rate-limiter'
6
- import type { AsyncRetryer } from './async-retryer'
7
- import type { AsyncThrottler } from './async-throttler'
8
- import type { Debouncer } from './debouncer'
9
- import type { Batcher } from './batcher'
10
- import type { Queuer } from './queuer'
11
- import type { RateLimiter } from './rate-limiter'
12
- import type { Throttler } from './throttler'
13
2
 
3
+ /**
4
+ * Payload on the devtools event bus must be JSON-serializable: `ClientEventBus`
5
+ * always stringifies events for `emitToServer` and `BroadcastChannel`, so live
6
+ * util instances (with reactive `store` graphs) cannot be sent as-is.
7
+ *
8
+ * Live instances are kept in {@link pacerDevtoolsInstancesByKey} for the panel.
9
+ */
10
+ export interface PacerDevtoolsWirePayload {
11
+ key: string
12
+ store: { state: unknown }
13
+ options: unknown
14
+ }
15
+
16
+ const pacerDevtoolsInstancesByKey = new Map<string, unknown>()
17
+
18
+ export function registerPacerDevtoolsInstance(
19
+ key: string,
20
+ instance: unknown,
21
+ ): void {
22
+ pacerDevtoolsInstancesByKey.set(key, instance)
23
+ }
24
+
25
+ export function getPacerDevtoolsInstance(key: string): unknown {
26
+ return pacerDevtoolsInstancesByKey.get(key)
27
+ }
28
+
29
+ function cloneJsonSafe(value: unknown): unknown {
30
+ if (value === undefined) {
31
+ return undefined
32
+ }
33
+ try {
34
+ return JSON.parse(JSON.stringify(value)) as unknown
35
+ } catch {
36
+ return null
37
+ }
38
+ }
39
+
40
+ function readStoreSnapshot(store: {
41
+ get?: () => unknown
42
+ state?: unknown
43
+ }): unknown {
44
+ if (typeof store.get === 'function') {
45
+ return store.get()
46
+ }
47
+ return store.state
48
+ }
49
+
50
+ function toPacerDevtoolsWirePayload(instance: {
51
+ key: string
52
+ store: { get?: () => unknown; state?: unknown }
53
+ options: unknown
54
+ }): PacerDevtoolsWirePayload {
55
+ return {
56
+ key: instance.key,
57
+ store: { state: cloneJsonSafe(readStoreSnapshot(instance.store)) },
58
+ options: cloneJsonSafe(instance.options),
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Suffix-only keys: {@link EventClient} prepends `pluginId:` (`pacer:`) at runtime
64
+ * for `emit` / `on`. Wire `type` values are `pacer:${key}`.
65
+ */
14
66
  export interface PacerEventMap {
15
- 'pacer:d-AsyncBatcher': AsyncBatcher<any>
16
- 'pacer:d-AsyncDebouncer': AsyncDebouncer<any>
17
- 'pacer:d-AsyncQueuer': AsyncQueuer<any>
18
- 'pacer:d-AsyncRateLimiter': AsyncRateLimiter<any>
19
- 'pacer:d-AsyncRetryer': AsyncRetryer<any>
20
- 'pacer:d-AsyncThrottler': AsyncThrottler<any>
21
- 'pacer:d-Batcher': Batcher<any>
22
- 'pacer:d-Debouncer': Debouncer<any>
23
- 'pacer:d-Queuer': Queuer<any>
24
- 'pacer:d-RateLimiter': RateLimiter<any>
25
- 'pacer:d-Throttler': Throttler<any>
26
- 'pacer:AsyncBatcher': AsyncBatcher<any>
27
- 'pacer:AsyncDebouncer': AsyncDebouncer<any>
28
- 'pacer:AsyncQueuer': AsyncQueuer<any>
29
- 'pacer:AsyncRateLimiter': AsyncRateLimiter<any>
30
- 'pacer:AsyncRetryer': AsyncRetryer<any>
31
- 'pacer:AsyncThrottler': AsyncThrottler<any>
32
- 'pacer:Batcher': Batcher<any>
33
- 'pacer:Debouncer': Debouncer<any>
34
- 'pacer:Queuer': Queuer<any>
35
- 'pacer:RateLimiter': RateLimiter<any>
36
- 'pacer:Throttler': Throttler<any>
67
+ 'd-AsyncBatcher': PacerDevtoolsWirePayload
68
+ 'd-AsyncDebouncer': PacerDevtoolsWirePayload
69
+ 'd-AsyncQueuer': PacerDevtoolsWirePayload
70
+ 'd-AsyncRateLimiter': PacerDevtoolsWirePayload
71
+ 'd-AsyncRetryer': PacerDevtoolsWirePayload
72
+ 'd-AsyncThrottler': PacerDevtoolsWirePayload
73
+ 'd-Batcher': PacerDevtoolsWirePayload
74
+ 'd-Debouncer': PacerDevtoolsWirePayload
75
+ 'd-Queuer': PacerDevtoolsWirePayload
76
+ 'd-RateLimiter': PacerDevtoolsWirePayload
77
+ 'd-Throttler': PacerDevtoolsWirePayload
78
+ AsyncBatcher: PacerDevtoolsWirePayload
79
+ AsyncDebouncer: PacerDevtoolsWirePayload
80
+ AsyncQueuer: PacerDevtoolsWirePayload
81
+ AsyncRateLimiter: PacerDevtoolsWirePayload
82
+ AsyncRetryer: PacerDevtoolsWirePayload
83
+ AsyncThrottler: PacerDevtoolsWirePayload
84
+ Batcher: PacerDevtoolsWirePayload
85
+ Debouncer: PacerDevtoolsWirePayload
86
+ Queuer: PacerDevtoolsWirePayload
87
+ RateLimiter: PacerDevtoolsWirePayload
88
+ Throttler: PacerDevtoolsWirePayload
37
89
  }
38
90
 
39
- export type PacerEventName = keyof PacerEventMap extends `pacer:${infer T}`
40
- ? T
41
- : never
91
+ export type PacerEventName = keyof PacerEventMap
92
+
93
+ type PacerUtilEmitSource = {
94
+ key?: string
95
+ store: { get?: () => unknown; state?: unknown }
96
+ options: unknown
97
+ }
42
98
 
43
99
  class PacerEventClient extends EventClient<PacerEventMap> {
44
100
  constructor(props?: { debug: boolean }) {
45
101
  super({
46
102
  pluginId: 'pacer',
47
103
  debug: props?.debug,
104
+ /**
105
+ * TanStack Devtools starts `ClientEventBus` inside an async dynamic import
106
+ * after the first commit. Pacer utils often emit during the first render
107
+ * (constructor → store sync). `EventClient` only retries `tanstack-connect`
108
+ * `#maxRetries` times at `reconnectEveryMs` before it gives up and drops
109
+ * all queued events permanently. Default 300ms × 5 ≈ 1.5s is often too
110
+ * short; Form uses 1000ms here for the same reason.
111
+ */
112
+ reconnectEveryMs: 1000,
48
113
  })
49
114
  }
50
115
  }
51
116
 
52
- export const emitChange = <
53
- TSuffix extends Extract<
54
- keyof PacerEventMap,
55
- `${string}:${string}`
56
- > extends `${string}:${infer S}`
57
- ? S
58
- : never,
59
- >(
60
- event: TSuffix,
61
- payload: PacerEventMap[`pacer:${TSuffix}`],
117
+ export const emitChange = <TEvent extends keyof PacerEventMap>(
118
+ event: TEvent,
119
+ instance: PacerUtilEmitSource,
62
120
  ) => {
63
- if (payload.key) {
64
- pacerEventClient.emit(event, payload)
121
+ const key = instance.key
122
+ if (!key) {
123
+ return
65
124
  }
125
+ registerPacerDevtoolsInstance(key, instance)
126
+ pacerEventClient.emit(event, toPacerDevtoolsWirePayload({ ...instance, key }))
66
127
  }
67
128
 
68
129
  export const pacerEventClient = new PacerEventClient()
package/src/index.ts CHANGED
@@ -12,5 +12,13 @@ export * from './throttler'
12
12
  export * from './types'
13
13
  export * from './utils'
14
14
 
15
- export { pacerEventClient } from './event-client'
16
- export type { PacerEventMap, PacerEventName } from './event-client'
15
+ export {
16
+ emitChange,
17
+ getPacerDevtoolsInstance,
18
+ pacerEventClient,
19
+ } from './event-client'
20
+ export type {
21
+ PacerDevtoolsWirePayload,
22
+ PacerEventMap,
23
+ PacerEventName,
24
+ } from './event-client'
package/src/queuer.ts CHANGED
@@ -305,8 +305,10 @@ export class Queuer<TValue> {
305
305
  if (this.key) {
306
306
  pacerEventClient.on('d-Queuer', (event) => {
307
307
  if (event.payload.key !== this.key) return
308
- this.#setState(event.payload.store.state)
309
- this.setOptions(event.payload.options)
308
+ this.#setState(
309
+ event.payload.store.state as Partial<QueuerState<TValue>>,
310
+ )
311
+ this.setOptions(event.payload.options as Partial<QueuerOptions<TValue>>)
310
312
  })
311
313
  }
312
314
  }
@@ -177,8 +177,10 @@ export class RateLimiter<TFn extends AnyFunction> {
177
177
  if (this.key) {
178
178
  pacerEventClient.on('d-RateLimiter', (event) => {
179
179
  if (event.payload.key !== this.key) return
180
- this.#setState(event.payload.store.state)
181
- this.setOptions(event.payload.options)
180
+ this.#setState(event.payload.store.state as Partial<RateLimiterState>)
181
+ this.setOptions(
182
+ event.payload.options as Partial<RateLimiterOptions<TFn>>,
183
+ )
182
184
  })
183
185
  }
184
186
  }
package/src/throttler.ts CHANGED
@@ -149,7 +149,7 @@ const defaultOptions: Omit<
149
149
  */
150
150
  export class Throttler<TFn extends AnyFunction> {
151
151
  readonly store: Store<Readonly<ThrottlerState<TFn>>> = new Store(
152
- getDefaultThrottlerState(),
152
+ getDefaultThrottlerState<TFn>(),
153
153
  )
154
154
  key: string | undefined
155
155
  options: ThrottlerOptions<TFn>
@@ -169,8 +169,10 @@ export class Throttler<TFn extends AnyFunction> {
169
169
  if (this.key) {
170
170
  pacerEventClient.on('d-Throttler', (event) => {
171
171
  if (event.payload.key !== this.key) return
172
- this.#setState(event.payload.store.state as ThrottlerState<TFn>)
173
- this.setOptions(event.payload.options)
172
+ this.#setState(
173
+ event.payload.store.state as Partial<ThrottlerState<TFn>>,
174
+ )
175
+ this.setOptions(event.payload.options as Partial<ThrottlerOptions<TFn>>)
174
176
  })
175
177
  }
176
178
  }