@emeryld/rrroutes-client 2.2.6 → 2.2.8

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.
@@ -102,12 +102,16 @@ type ParamsOf<L extends AnyLeaf> = InferParams<L>;
102
102
  type QueryOf<L extends AnyLeaf> = InferQuery<L>;
103
103
  type BodyOf<L extends AnyLeaf> = InferBody<L>;
104
104
  type OnReceive<L extends AnyLeaf> = (data: OutputOf<L>) => void;
105
+ type OnReceiveRegistrar<L extends AnyLeaf> = (listener: OnReceive<L>) => () => void;
106
+ type UseEndpointResult<L extends AnyLeaf, Result> = Result & {
107
+ onReceive: OnReceiveRegistrar<L>;
108
+ };
105
109
  /** Variadic args consumed by the direct fetch helper: optional args + body. */
106
- export type MutationFetchArgs<L extends AnyLeaf> = [...ArgsTuple<L>, BodyOf<L>];
110
+ export type MutationFetchArgs<L extends AnyLeaf> = Prettify<[...ArgsTuple<L>, BodyOf<L>]>;
107
111
  /** Signature for the fetch helper returned by mutation builds. */
108
112
  export type MutationFetcher<L extends AnyLeaf> = (...args: MutationFetchArgs<L>) => Promise<OutputOf<L>>;
109
113
  /** Fetch args for GET endpoints (optionally include a body when a schema exists). */
110
- type QueryFetchArgs<L extends AnyLeaf> = BodyOf<L> extends never ? ArgsTuple<L> : ArgsTuple<L> | [...ArgsTuple<L>, BodyOf<L>];
114
+ type QueryFetchArgs<L extends AnyLeaf> = BodyOf<L> extends never ? ArgsTuple<L> : Prettify<ArgsTuple<L> | [...ArgsTuple<L>, BodyOf<L>]>;
111
115
  /** Optional metadata provided when building a helper (used for debug filtering). */
112
116
  export type BuildMeta<Names extends string = string> = {
113
117
  /**
@@ -117,11 +121,11 @@ export type BuildMeta<Names extends string = string> = {
117
121
  name?: Names;
118
122
  };
119
123
  /** Object shape the user passes to hooks and helpers (params/query are optional). */
120
- export type ArgsFor<L extends AnyLeaf> = (ParamsOf<L> extends never ? {} : {
124
+ export type ArgsFor<L extends AnyLeaf> = Prettify<(ParamsOf<L> extends never ? {} : {
121
125
  params: ParamsOf<L>;
122
126
  }) & (QueryOf<L> extends never ? {} : {
123
127
  query: QueryOf<L>;
124
- });
128
+ })>;
125
129
  /** Variadic tuple representation that omits the argument entirely when not needed. */
126
130
  export type ArgsTuple<L extends AnyLeaf> = keyof ArgsFor<L> extends never ? [] : [args: ArgsFor<L>];
127
131
  /** Cache data shape for setData(...) */
@@ -130,6 +134,7 @@ type ArgsFromTuple<L extends AnyLeaf, T extends ArgsTuple<L>> = T extends [infer
130
134
  type ParamsFromArgs<L extends AnyLeaf, T extends ArgsTuple<L>> = ArgsFromTuple<L, T> extends {
131
135
  params: infer P;
132
136
  } ? P : undefined;
137
+ type SetDataArgs<L extends AnyLeaf> = Prettify<[updater: Updater<DataShape<L>>, ...ArgsTuple<L>]>;
133
138
  type QueryFromArgs<L extends AnyLeaf, T extends ArgsTuple<L>> = ArgsFromTuple<L, T> extends {
134
139
  query: infer Q;
135
140
  } ? Q : undefined;
@@ -161,13 +166,10 @@ export type MutationBuildOptionsFor<L extends AnyLeaf> = Omit<UseMutationOptions
161
166
  };
162
167
  /** Build options narrowed to the method/feed shape of the leaf. */
163
168
  export type BuildOptionsFor<L extends AnyLeaf> = L['method'] extends 'get' ? L['cfg']['feed'] extends true ? InfiniteBuildOptionsFor<L> : QueryBuildOptionsFor<L> : MutationBuildOptionsFor<L>;
164
- type UseEndpointOptionsFor<L extends AnyLeaf> = {
165
- onReceive?: OnReceive<L>;
166
- };
167
- export type UseEndpointArgs<L extends AnyLeaf, O> = keyof ArgsFor<L> extends never ? [options?: O] : [args: ArgsFor<L>, options?: O];
168
- export type QueryUseEndpointOptionsFor<L extends AnyLeaf> = UseEndpointOptionsFor<L>;
169
- export type InfiniteUseEndpointOptionsFor<L extends AnyLeaf> = UseEndpointOptionsFor<L>;
170
- export type MutationUseEndpointOptionsFor<L extends AnyLeaf> = UseEndpointOptionsFor<L>;
169
+ export type UseEndpointArgs<L extends AnyLeaf> = ArgsTuple<L>;
170
+ export type QueryUseEndpointResultFor<L extends AnyLeaf> = UseEndpointResult<L, UseQueryResult<OutputOf<L>, unknown>>;
171
+ export type InfiniteUseEndpointResultFor<L extends AnyLeaf> = UseEndpointResult<L, UseInfiniteQueryResult<InfiniteData<OutputOf<L>>, unknown>>;
172
+ export type MutationUseEndpointResultFor<L extends AnyLeaf> = UseEndpointResult<L, UseMutationResult<OutputOf<L>, unknown, BodyOf<L>, unknown>>;
171
173
  /** Shared capabilities exposed by every built endpoint helper. */
172
174
  export type BuiltCommon<L extends AnyLeaf> = {
173
175
  /**
@@ -186,7 +188,7 @@ export type BuiltCommon<L extends AnyLeaf> = {
186
188
  * @param updater New value or function applied to existing cache data.
187
189
  * @param rest Optional params/query tuple for the leaf.
188
190
  */
189
- setData: (...args: [updater: Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => DataShape<L> | undefined;
191
+ setData: (...args: SetDataArgs<L>) => DataShape<L> | undefined;
190
192
  };
191
193
  /** Hook+helpers for a standard GET endpoint. */
192
194
  export type BuiltQuery<L extends AnyLeaf> = BuiltCommon<L> & {
@@ -194,7 +196,7 @@ export type BuiltQuery<L extends AnyLeaf> = BuiltCommon<L> & {
194
196
  * React hook bound to the GET leaf.
195
197
  * @param args Optional params/query tuple for the leaf.
196
198
  */
197
- useEndpoint: (...args: UseEndpointArgs<L, QueryUseEndpointOptionsFor<L>>) => UseQueryResult<OutputOf<L>, unknown>;
199
+ useEndpoint: (...args: UseEndpointArgs<L>) => QueryUseEndpointResultFor<L>;
198
200
  /**
199
201
  * Direct fetch helper mirroring the query hook without touching the cache.
200
202
  * @param args Optional params/query tuple for the leaf.
@@ -207,7 +209,7 @@ export type BuiltInfinite<L extends AnyLeaf> = BuiltCommon<L> & {
207
209
  * React hook bound to an infinite GET leaf.
208
210
  * @param args Optional params/query tuple for the leaf.
209
211
  */
210
- useEndpoint: (...args: UseEndpointArgs<L, InfiniteUseEndpointOptionsFor<L>>) => UseInfiniteQueryResult<InfiniteData<OutputOf<L>>, unknown>;
212
+ useEndpoint: (...args: UseEndpointArgs<L>) => InfiniteUseEndpointResultFor<L>;
211
213
  /**
212
214
  * Direct fetch helper for a single page of the feed (pass cursor in query args).
213
215
  * @param args Optional params/query tuple for the leaf.
@@ -220,7 +222,7 @@ export type BuiltMutation<L extends AnyLeaf> = BuiltCommon<L> & {
220
222
  * React hook bound to a mutation leaf.
221
223
  * @param args Optional params/query tuple for the leaf.
222
224
  */
223
- useEndpoint: (...args: UseEndpointArgs<L, MutationUseEndpointOptionsFor<L>>) => UseMutationResult<OutputOf<L>, unknown, BodyOf<L>, unknown>;
225
+ useEndpoint: (...args: UseEndpointArgs<L>) => MutationUseEndpointResultFor<L>;
224
226
  /**
225
227
  * Direct fetch helper that bypasses React Query, useful for server actions or scripts.
226
228
  * Pass params/query first (if required), followed by the body payload.
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { Socket } from 'socket.io-client';
3
3
  import { SocketClient, SocketClientOptions, ClientCtx, ServerEnvelope } from './socket.client.index';
4
- import { EventMap, SocketConnectionConfig, Payload, SocketSchemaOutput } from '@emeryld/rrroutes-contract';
4
+ import { EventMap, SocketConnectionConfigOutput, Payload, SocketSchemaOutput } from '@emeryld/rrroutes-contract';
5
5
  /** === Provider-side debug === */
6
6
  type HookDebugReason = 'init' | 'change';
7
7
  type HookDebugValue = string | number | boolean | null | undefined;
@@ -33,13 +33,13 @@ export type SocketProviderDebugOptions = {
33
33
  [P in SocketProviderDebugEvent['type']]?: boolean;
34
34
  };
35
35
  /** === Types for runtime socket injection === */
36
- type BaseOptions<T extends EventMap, C extends SocketConnectionConfig> = Omit<SocketClientOptions<T, C>, 'socket'>;
36
+ type BaseOptions<T extends EventMap, C extends SocketConnectionConfigOutput> = Omit<SocketClientOptions<T, C>, 'socket'>;
37
37
  type ProviderRuntimeSocket = {
38
38
  socket: Socket | null;
39
39
  } | {
40
40
  getSocket: () => Socket | Promise<Socket>;
41
41
  };
42
- export declare function buildSocketProvider<T extends EventMap, C extends SocketConnectionConfig>(args: {
42
+ export declare function buildSocketProvider<T extends EventMap, C extends SocketConnectionConfigOutput>(args: {
43
43
  events: T;
44
44
  options: Omit<BaseOptions<T, C>, 'debug'> & {
45
45
  debug: BaseOptions<T, C>['debug'] & SocketProviderDebugOptions;
@@ -52,9 +52,9 @@ export declare function buildSocketProvider<T extends EventMap, C extends Socket
52
52
  useSocketClient: () => SocketClient<T, C>;
53
53
  useSocketConnection: <K extends keyof T & string>(p: Parameters<typeof useSocketConnection<T, K, C>>[0]) => void;
54
54
  };
55
- export declare function useSocketClient<T extends EventMap, C extends SocketConnectionConfig>(): SocketClient<T, C>;
55
+ export declare function useSocketClient<T extends EventMap, C extends SocketConnectionConfigOutput>(): SocketClient<T, C>;
56
56
  type Rooms = string[] | string | undefined;
57
- export type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & string, C extends SocketConnectionConfig> = {
57
+ export type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & string, C extends SocketConnectionConfigOutput> = {
58
58
  event: K;
59
59
  rooms?: Rooms;
60
60
  onMessage: (payload: Payload<T, K>, meta: {
@@ -67,5 +67,5 @@ export type UseSocketConnectionArgs<T extends EventMap, K extends keyof T & stri
67
67
  joinMeta: SocketSchemaOutput<C['joinMetaMessage']>;
68
68
  leaveMeta: SocketSchemaOutput<C['leaveMetaMessage']>;
69
69
  };
70
- export declare function useSocketConnection<T extends EventMap, K extends keyof T & string, C extends SocketConnectionConfig>(args: UseSocketConnectionArgs<T, K, C>): void;
70
+ export declare function useSocketConnection<T extends EventMap, K extends keyof T & string, C extends SocketConnectionConfigOutput>(args: UseSocketConnectionArgs<T, K, C>): void;
71
71
  export {};
@@ -1,4 +1,4 @@
1
- import type { EventMap, SocketConnectionConfig, Payload, SocketSchemaOutput } from '@emeryld/rrroutes-contract';
1
+ import type { EventMap, SocketConnectionConfigOutput, Payload, SocketSchemaOutput } from '@emeryld/rrroutes-contract';
2
2
  import type { MaybeSocket, SysEventMap } from './socket.client.sys';
3
3
  import { SocketClientDebugEvent } from './socket.client.debug';
4
4
  export type ServerEnvelope<T extends EventMap, K extends keyof T & string> = {
@@ -43,7 +43,7 @@ export type HeartbeatClientOptions = {
43
43
  /** Give up waiting for pong after this many ms. Default 7_500. */
44
44
  timeoutMs?: number;
45
45
  };
46
- export type SocketClientOptions<T extends EventMap = EventMap, C extends SocketConnectionConfig = SocketConnectionConfig> = {
46
+ export type SocketClientOptions<T extends EventMap = EventMap, C extends SocketConnectionConfigOutput = SocketConnectionConfigOutput> = {
47
47
  /** Inject an existing socket.io-client Socket. Can be null while bootstrapping. */
48
48
  socket: MaybeSocket;
49
49
  config: C;
@@ -52,7 +52,7 @@ export type SocketClientOptions<T extends EventMap = EventMap, C extends SocketC
52
52
  heartbeat?: HeartbeatClientOptions;
53
53
  sys: SysEventMap<T, C>;
54
54
  };
55
- export declare class SocketClient<T extends EventMap, C extends SocketConnectionConfig = SocketConnectionConfig> {
55
+ export declare class SocketClient<T extends EventMap, C extends SocketConnectionConfigOutput = SocketConnectionConfigOutput> {
56
56
  readonly socket: MaybeSocket;
57
57
  private readonly events;
58
58
  private readonly environment;
@@ -116,4 +116,3 @@ export declare class SocketClient<T extends EventMap, C extends SocketConnection
116
116
  }
117
117
  export * from './socket.client.context';
118
118
  export * from './socket.client.sys';
119
- export * from './socketedRoute/socket.client.helper';
@@ -1,9 +1,9 @@
1
1
  import type { Socket } from 'socket.io-client';
2
2
  import { z } from 'zod';
3
- import type { EventMap, SocketConnectionConfig, SocketSchemaOutput } from '@emeryld/rrroutes-contract';
3
+ import type { EventMap, SocketConnectionConfigOutput, SocketSchemaOutput } from '@emeryld/rrroutes-contract';
4
4
  import { SocketClient } from './socket.client.index';
5
5
  export type MaybeSocket = Socket | null;
6
- export type SysEventMap<T extends EventMap, C extends SocketConnectionConfig = SocketConnectionConfig> = {
6
+ export type SysEventMap<T extends EventMap, C extends SocketConnectionConfigOutput = SocketConnectionConfigOutput> = {
7
7
  'sys:connect': (args: {
8
8
  socket: Socket;
9
9
  client: SocketClient<T, C>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emeryld/rrroutes-client",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@jest/globals": "^30.2.0",
33
- "@types/react": "^18.3.26",
33
+ "@types/react": "^18.3.27",
34
34
  "@types/react-native": "^0.73.0"
35
35
  },
36
36
  "scripts": {
@@ -1,121 +0,0 @@
1
- import type { AnyLeaf, InferOutput, EventMap as ContractEventMap, SocketSchemaOutput, Payload, SocketConnectionConfigOutput } from '@emeryld/rrroutes-contract';
2
- import type { BuildMeta, BuildOptionsFor, ArgsFor, DataShape, InfiniteUseEndpointOptionsFor, QueryUseEndpointOptionsFor, RouterBuilder } from '../../routesV3.client.types';
3
- import type { ClientCtx, ServerEnvelope, SocketClient } from '../socket.client.index';
4
- /** Narrow to GET leaves. */
5
- type GetLeaf = AnyLeaf & {
6
- method: 'get';
7
- };
8
- type PageOf<L extends GetLeaf> = InferOutput<L>;
9
- type SocketedRouteDebugEventBase = {
10
- name?: string;
11
- };
12
- export type SocketedRouteDebugEvent<Ev extends string = string> = (SocketedRouteDebugEventBase & {
13
- type: 'build';
14
- routeKey: string;
15
- socketEvent: Ev;
16
- options?: unknown;
17
- }) | (SocketedRouteDebugEventBase & {
18
- type: 'subscribe';
19
- phase: 'start' | 'stop';
20
- routeKey: string;
21
- socketEvent: Ev;
22
- }) | (SocketedRouteDebugEventBase & {
23
- type: 'receive';
24
- routeKey: string;
25
- socketEvent: Ev;
26
- payload?: unknown;
27
- envelope?: {
28
- eventName: Ev;
29
- sentAt?: string | Date;
30
- sentTo?: string[];
31
- metadata?: Record<string, unknown>;
32
- };
33
- }) | (SocketedRouteDebugEventBase & {
34
- type: 'onReceive';
35
- routeKey: string;
36
- socketEvent: Ev;
37
- page?: unknown;
38
- rooms?: string[];
39
- }) | (SocketedRouteDebugEventBase & {
40
- type: 'rooms';
41
- phase: 'derive' | 'join' | 'leave';
42
- routeKey: string;
43
- socketEvent: Ev;
44
- rooms: string[];
45
- prev?: string[];
46
- meta?: unknown;
47
- reason?: string;
48
- source?: 'hydrate' | 'onReceive';
49
- }) | (SocketedRouteDebugEventBase & {
50
- type: 'setData';
51
- routeKey: string;
52
- socketEvent: Ev;
53
- args?: unknown;
54
- }) | (SocketedRouteDebugEventBase & {
55
- type: 'render';
56
- render: number;
57
- routeKey: string;
58
- socketEvent: Ev;
59
- changed?: string[];
60
- rooms?: string[];
61
- }) | (SocketedRouteDebugEventBase & {
62
- type: 'effect';
63
- effect: 'rooms' | 'subscribe';
64
- phase: 'run' | 'cleanup';
65
- changedDeps?: string[];
66
- rooms?: string[];
67
- });
68
- export type SocketedRouteDebugOptions<Ev extends string = string> = {
69
- verbose?: boolean;
70
- only?: string[];
71
- logger?: (e: SocketedRouteDebugEvent<Ev>) => void;
72
- } & {
73
- [P in SocketedRouteDebugEvent['type']]?: boolean;
74
- };
75
- type SocketedHandlers<L extends GetLeaf, E extends ContractEventMap, K extends keyof E & string, C extends SocketConnectionConfigOutput> = {
76
- /**
77
- * Derive the full set of rooms to join based on a newly received payload plus
78
- * the current active room list. Return the complete desired list (not a delta).
79
- */
80
- onReceiveRooms?: (page: PageOf<L>, activeRooms: string[]) => string[];
81
- /**
82
- * Update the cached route data when a socket message arrives.
83
- * Return the next cache value (or undefined to keep it unchanged).
84
- */
85
- handleMessage: (prev: DataShape<L> | undefined, payload: Payload<E, K>, meta: {
86
- envelope: ServerEnvelope<E, K>;
87
- ctx: ClientCtx;
88
- }) => DataShape<L> | undefined;
89
- /** Meta payloads for join/leave messages. */
90
- joinMeta: SocketSchemaOutput<C['joinMetaMessage']>;
91
- leaveMeta: SocketSchemaOutput<C['leaveMetaMessage']>;
92
- };
93
- type GetRoutesOf<R extends Record<PropertyKey, AnyLeaf>> = {
94
- [K in keyof R as R[K] extends GetLeaf ? K : never]: R[K] extends GetLeaf ? R[K] : never;
95
- };
96
- type OptionsForGet<L extends GetLeaf> = L['cfg']['feed'] extends true ? InfiniteUseEndpointOptionsFor<L> : QueryUseEndpointOptionsFor<L>;
97
- export type SocketedRouteHook<Routes extends Record<PropertyKey, GetLeaf>, Names extends string, Events extends ContractEventMap, C extends SocketConnectionConfigOutput> = <K extends keyof Routes & string, Ev extends keyof Events & string>(params: {
98
- routeKey: K;
99
- socketEvent: Ev;
100
- handlers: SocketedHandlers<Routes[K], Events, Ev, C>;
101
- buildOptions?: BuildOptionsFor<Routes[K]>;
102
- buildMeta?: BuildMeta<Names>;
103
- debug?: SocketedRouteDebugOptions<Ev>;
104
- useOptions?: {
105
- args?: ArgsFor<Routes[K]>;
106
- options?: OptionsForGet<Routes[K]>;
107
- };
108
- }) => {
109
- data: DataShape<Routes[K]> | undefined;
110
- rooms: string[];
111
- };
112
- /**
113
- * Build a hook that wires a GET route (feed or standard) to a socket event:
114
- * - Keeps React Query cache in sync when socket messages arrive.
115
- * - Derives and joins rooms from the route’s responses.
116
- */
117
- export declare function createSocketedRouteHook<Routes extends Record<PropertyKey, AnyLeaf>, Names extends string, Events extends ContractEventMap, C extends SocketConnectionConfigOutput>(args: {
118
- buildRoute: RouterBuilder<Routes, Names>;
119
- useClient?: () => SocketClient<Events, C>;
120
- }): SocketedRouteHook<GetRoutesOf<Routes>, Names, Events, C>;
121
- export {};