@logux/server 0.13.1 → 0.15.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.
@@ -19,11 +19,19 @@ import type {
19
19
  IncomingMessage,
20
20
  ServerResponse
21
21
  } from 'node:http'
22
- import type { LogFn } from 'pino'
22
+ import type { WebSocket } from 'ws'
23
23
 
24
- import type { ChannelContext, Context } from '../context/index.js'
24
+ import type {
25
+ ChannelContext,
26
+ ConnectContext,
27
+ Context
28
+ } from '../context/index.js'
25
29
  import type { ServerClient } from '../server-client/index.js'
26
30
 
31
+ interface LogFn {
32
+ (...objs: unknown[]): void
33
+ }
34
+
27
35
  interface TypeOptions {
28
36
  /**
29
37
  * Name of the queue that will be used to process actions
@@ -40,6 +48,23 @@ interface ChannelOptions {
40
48
  queue?: string
41
49
  }
42
50
 
51
+ interface ConnectLoader<Headers extends object = unknown> {
52
+ (
53
+ ctx: ConnectContext<Headers>,
54
+ lastSynced: number
55
+ ):
56
+ | [Action, ServerMeta][]
57
+ | Promise<
58
+ [
59
+ Action,
60
+ Partial<Pick<ServerMeta, 'added' | 'subprotocol'>> &
61
+ Pick<ServerMeta, 'id' | 'time'>
62
+ ][]
63
+ >
64
+ }
65
+
66
+ type ServerNodeConstructor = new (...args: unknown[]) => ServerNode
67
+
43
68
  export interface ServerMeta extends Meta {
44
69
  /**
45
70
  * All nodes subscribed to channel will receive the action.
@@ -98,12 +123,6 @@ export interface ServerMeta extends Meta {
98
123
  }
99
124
 
100
125
  export interface BaseServerOptions {
101
- /**
102
- * URL to PHP, Ruby on Rails, or other backend to process actions and
103
- * authentication.
104
- */
105
- backend?: string
106
-
107
126
  /**
108
127
  * SSL certificate or path to it. Path could be relative from server
109
128
  * root. It is required in production mode, because WSS is highly
@@ -119,17 +138,7 @@ export interface BaseServerOptions {
119
138
  cleanFromLog?: RegExp
120
139
 
121
140
  /**
122
- * CIDR masks for IP address, where control requests could came from.
123
- */
124
- controlMask?: string
125
-
126
- /**
127
- * Secret to control the server.
128
- */
129
- controlSecret?: string
130
-
131
- /**
132
- * Disable health check endpoint, control HTTP API, {@link Server#http}.
141
+ * Disable health check endpoint, {@link Server#http}.
133
142
  *
134
143
  * The server will process only WebSocket connection and ignore all other
135
144
  * HTTP request (so they can be processed by other HTTP server).
@@ -144,8 +153,8 @@ export interface BaseServerOptions {
144
153
  env?: 'development' | 'production'
145
154
 
146
155
  /**
147
- * URL of main JS file in the root dir. Shortcut to set `root` in ES modules
148
- * without `fileURLToPath`.
156
+ * URL of main JS file in the root dir for the cases where you can’t use
157
+ * `import.meta.dirname`.
149
158
  *
150
159
  * ```
151
160
  * fileUrl: import.meta.url
@@ -169,6 +178,16 @@ export interface BaseServerOptions {
169
178
  */
170
179
  key?: { pem: string } | string
171
180
 
181
+ /**
182
+ * The version requirements for client subprotocol version.
183
+ */
184
+ minSubprotocol?: number
185
+
186
+ /**
187
+ * Replace class for ServerNode.
188
+ */
189
+ Node?: ServerNodeConstructor
190
+
172
191
  /**
173
192
  * Process ID, to display in logs.
174
193
  */
@@ -184,7 +203,7 @@ export interface BaseServerOptions {
184
203
  * Port to bind server. It will create HTTP server manually to connect
185
204
  * WebSocket server to it. Default is `31337`.
186
205
  */
187
- port?: number
206
+ port?: number | string
188
207
 
189
208
  /**
190
209
  * URL to Redis for Logux Server Pro scaling.
@@ -196,7 +215,7 @@ export interface BaseServerOptions {
196
215
  * Default is `process.cwd()`.
197
216
  *
198
217
  * ```js
199
- * root: __dirname
218
+ * root: import.meta.dirname
200
219
  * ```
201
220
  */
202
221
  root?: string
@@ -215,14 +234,17 @@ export interface BaseServerOptions {
215
234
  store?: LogStore
216
235
 
217
236
  /**
218
- * Server current application subprotocol version in SemVer format.
237
+ * Server current application subprotocol version.
219
238
  */
220
- subprotocol?: string
239
+ subprotocol?: number
221
240
 
222
241
  /**
223
- * npm’s version requirements for client subprotocol version.
242
+ * How many actions could be sent in a single message. `100` by default.
243
+ *
244
+ * Actions from a single `Server#log.add()` call (or from `ctx.sendBack()`
245
+ * with an array) are sent together and will be split by this limit.
224
246
  */
225
- supports?: string
247
+ syncBatch?: number
226
248
 
227
249
  /**
228
250
  * Test time to test server.
@@ -236,8 +258,11 @@ export interface BaseServerOptions {
236
258
  timeout?: number
237
259
  }
238
260
 
239
- export interface AuthenticatorOptions<Headers extends object> {
240
- client: ServerClient
261
+ export interface AuthenticatorOptions<
262
+ Headers extends object,
263
+ ClientData extends object
264
+ > {
265
+ client: ServerClient<ClientData>
241
266
  cookie: Record<string, string>
242
267
  headers: Headers
243
268
  token: string
@@ -245,9 +270,8 @@ export interface AuthenticatorOptions<Headers extends object> {
245
270
  }
246
271
 
247
272
  export type SendBackActions =
248
- | [Action, Partial<Meta>][]
273
+ | (Action | [Action, Partial<Meta>])[]
249
274
  | Action
250
- | Action[]
251
275
  | void
252
276
 
253
277
  /**
@@ -258,8 +282,11 @@ export type SendBackActions =
258
282
  * @param client Client object.
259
283
  * @returns `true` if credentials was correct
260
284
  */
261
- interface ServerAuthenticator<Headers extends object> {
262
- (user: AuthenticatorOptions<Headers>): boolean | Promise<boolean>
285
+ interface ServerAuthenticator<
286
+ Headers extends object,
287
+ ClientData extends object
288
+ > {
289
+ (user: AuthenticatorOptions<Headers, ClientData>): boolean | Promise<boolean>
263
290
  }
264
291
 
265
292
  /**
@@ -352,7 +379,7 @@ interface ActionFinally<
352
379
  */
353
380
  interface ChannelFilter<Headers extends object> {
354
381
  (
355
- ctx: Context<{}, Headers>,
382
+ ctx: Context<unknown, Headers>,
356
383
  action: Readonly<Action>,
357
384
  meta: Readonly<ServerMeta>
358
385
  ): boolean | Promise<boolean>
@@ -466,10 +493,7 @@ type ActionCallbacks<
466
493
  TypeAction extends Action,
467
494
  Data extends object,
468
495
  Headers extends object
469
- > = {
470
- finally?: ActionFinally<TypeAction, Data, Headers>
471
- resend?: Resender<TypeAction, Data, Headers>
472
- } & (
496
+ > = (
473
497
  | {
474
498
  access: Authorizer<TypeAction, Data, Headers>
475
499
  process?: Processor<TypeAction, Data, Headers>
@@ -477,18 +501,17 @@ type ActionCallbacks<
477
501
  | {
478
502
  accessAndProcess: Processor<TypeAction, Data, Headers>
479
503
  }
480
- )
504
+ ) & {
505
+ finally?: ActionFinally<TypeAction, Data, Headers>
506
+ resend?: Resender<TypeAction, Data, Headers>
507
+ }
481
508
 
482
509
  type ChannelCallbacks<
483
510
  SubscribeAction extends Action,
484
511
  Data extends object,
485
512
  ChannelParams extends object | string[],
486
513
  Headers extends object
487
- > = {
488
- filter?: FilterCreator<SubscribeAction, Data, ChannelParams, Headers>
489
- finally?: ChannelFinally<SubscribeAction, Data, ChannelParams, Headers>
490
- unsubscribe?: ChannelUnsubscribe<Data, ChannelParams, Headers>
491
- } & (
514
+ > = (
492
515
  | {
493
516
  access: ChannelAuthorizer<SubscribeAction, Data, ChannelParams, Headers>
494
517
  load?: ChannelLoader<SubscribeAction, Data, ChannelParams, Headers>
@@ -501,7 +524,11 @@ type ChannelCallbacks<
501
524
  Headers
502
525
  >
503
526
  }
504
- )
527
+ ) & {
528
+ filter?: FilterCreator<SubscribeAction, Data, ChannelParams, Headers>
529
+ finally?: ChannelFinally<SubscribeAction, Data, ChannelParams, Headers>
530
+ unsubscribe?: ChannelUnsubscribe<Data, ChannelParams, Headers>
531
+ }
505
532
 
506
533
  interface ActionReporter {
507
534
  action: Readonly<Action>
@@ -551,20 +578,17 @@ interface ReportersArguments {
551
578
  nodeId?: string
552
579
  }
553
580
  listen: {
554
- backend: string
555
581
  cert: boolean
556
- controlMask: string
557
- controlSecret: string
558
582
  environment: 'development' | 'production'
559
583
  host: string
560
584
  loguxServer: string
585
+ minSubprotocol: number
561
586
  nodeId: string
562
587
  notes: object
563
588
  port: string
564
589
  redis: string
565
590
  server: boolean
566
- subprotocol: string
567
- supports: string
591
+ subprotocol: number
568
592
  }
569
593
  processed: {
570
594
  actionId: ID
@@ -607,29 +631,13 @@ export type Resend =
607
631
  | string[]
608
632
 
609
633
  export interface Logger {
634
+ debug(details: object, message: string): void
610
635
  error(details: object, message: string): void
611
636
  fatal(details: object, message: string): void
612
637
  info(details: object, message: string): void
613
638
  warn(details: object, message: string): void
614
639
  }
615
640
 
616
- interface Response {
617
- body: string
618
- header?: {
619
- [name: string]: string
620
- }
621
- }
622
-
623
- interface GetProcessor {
624
- request(request: object): Promise<Response> | Response
625
- safe?: boolean
626
- }
627
-
628
- interface PostProcessor {
629
- command(command: object, request: object): Promise<void>
630
- isValid(command: object): boolean
631
- }
632
-
633
641
  /**
634
642
  * Return `false` if `cb()` got response error with 403.
635
643
  *
@@ -651,8 +659,9 @@ export function wasNot403(cb: () => Promise<void>): Promise<boolean>
651
659
  * Base server class to extend.
652
660
  */
653
661
  export class BaseServer<
654
- Headers extends object = {},
655
- ServerLog extends Log = Log<ServerMeta>
662
+ Headers extends object = unknown,
663
+ ServerLog extends Log = Log<ServerMeta>,
664
+ ClientData extends object = unknown
656
665
  > {
657
666
  /**
658
667
  * Connected client by client ID.
@@ -660,7 +669,7 @@ export class BaseServer<
660
669
  * Do not rely on this data, when you have multiple Logux servers.
661
670
  * Each server will have a different list.
662
671
  */
663
- clientIds: Map<string, ServerClient>
672
+ clientIds: Map<string, ServerClient<ClientData>>
664
673
 
665
674
  /**
666
675
  * Connected clients.
@@ -671,14 +680,7 @@ export class BaseServer<
671
680
  * }
672
681
  * ```
673
682
  */
674
- connected: Map<string, ServerClient>
675
-
676
- /**
677
- * Add callback to internal HTTP server.
678
- */
679
- controls: {
680
- [path: string]: GetProcessor | PostProcessor
681
- }
683
+ connected: Map<string, ServerClient<ClientData>>
682
684
 
683
685
  /**
684
686
  * Production or development mode.
@@ -735,7 +737,7 @@ export class BaseServer<
735
737
  * Do not rely on this data, when you have multiple Logux servers.
736
738
  * Each server will have a different list.
737
739
  */
738
- nodeIds: Map<string, ServerClient>
740
+ nodeIds: Map<string, ServerClient<ClientData>>
739
741
 
740
742
  /**
741
743
  * Server options.
@@ -755,7 +757,7 @@ export class BaseServer<
755
757
  subscribers: {
756
758
  [channel: string]: {
757
759
  [nodeId: string]: {
758
- filters: Record<string, ChannelFilter<{}> | true>
760
+ filters: Record<string, ChannelFilter<unknown> | true>
759
761
  unsubscribe?: (action: LoguxUnsubscribeAction, meta: ServerMeta) => void
760
762
  }
761
763
  }
@@ -767,7 +769,7 @@ export class BaseServer<
767
769
  * Do not rely on this data, when you have multiple Logux servers.
768
770
  * Each server will have a different list.
769
771
  */
770
- userIds: Map<string, ServerClient[]>
772
+ userIds: Map<string, ServerClient<ClientData>[]>
771
773
 
772
774
  /**
773
775
  * @param opts Server options.
@@ -800,7 +802,7 @@ export class BaseServer<
800
802
  *
801
803
  * @param authenticator The authentication callback.
802
804
  */
803
- auth(authenticator: ServerAuthenticator<Headers>): void
805
+ auth(authenticator: ServerAuthenticator<Headers, ClientData>): void
804
806
 
805
807
  /**
806
808
  * Define the channel.
@@ -827,15 +829,14 @@ export class BaseServer<
827
829
  * @param options Additional options
828
830
  */
829
831
  channel<
830
- ChannelParams extends object = {},
831
- Data extends object = {},
832
+ ChannelParams extends object = unknown,
833
+ Data extends object = unknown,
832
834
  SubscribeAction extends LoguxSubscribeAction = LoguxSubscribeAction
833
835
  >(
834
836
  pattern: string,
835
837
  callbacks: ChannelCallbacks<SubscribeAction, Data, ChannelParams, Headers>,
836
838
  options?: ChannelOptions
837
839
  ): void
838
-
839
840
  /**
840
841
  * @param pattern Regular expression for channel name.
841
842
  * @param callbacks Callback during subscription process.
@@ -843,7 +844,7 @@ export class BaseServer<
843
844
  */
844
845
  channel<
845
846
  ChannelParams extends string[] = string[],
846
- Data extends object = {},
847
+ Data extends object = unknown,
847
848
  SubscribeAction extends LoguxSubscribeAction = LoguxSubscribeAction
848
849
  >(
849
850
  pattern: RegExp,
@@ -877,25 +878,68 @@ export class BaseServer<
877
878
  */
878
879
  destroy(): Promise<void>
879
880
 
881
+ /**
882
+ * Wait until the client will confirm all actions, which were sent to it.
883
+ *
884
+ * Use it to send a long history without loading it all into the memory:
885
+ * the client’s speed will limit how fast you read the database.
886
+ *
887
+ * ```js
888
+ * while (await server.drain(clientId)) {
889
+ * let page = await cursor.next(100)
890
+ * if (!page.length) break
891
+ * server.log.add(page.map(i => [i.action, { clients: [clientId] }]))
892
+ * }
893
+ * ```
894
+ *
895
+ * @param clientId Client ID.
896
+ * @returns Promise with `false` if the client was disconnected.
897
+ */
898
+ drain(clientId: string): Promise<boolean>
899
+
900
+ /**
901
+ * Handle WebSocket connection explicitly
902
+ *
903
+ * This is a low-level method allowing to integrate Logux server with an existing server
904
+ *
905
+ * ```js
906
+ * fastify.get('/', { websocket: true }, (socket, req) => {
907
+ * loguxServer.handleClient(socket, req)
908
+ * })
909
+ * ```
910
+ */
911
+ handleClient(ws: WebSocket, req: IncomingMessage): void
912
+
880
913
  /**
881
914
  * Add non-WebSocket HTTP request processor.
882
915
  *
883
916
  * ```js
884
- * server.http((req, res) => {
885
- * if (req.url === '/auth') {
886
- * let token = signIn(req)
887
- * if (token) {
888
- * res.setHeader('Set-Cookie', `token=${token}; Secure; HttpOnly`)
889
- * res.end()
890
- * } else {
891
- * res.statusCode = 400
892
- * res.end('Wrong user or password')
893
- * }
917
+ * server.http('GET', '/auth', (req, res) => {
918
+ * let token = signIn(req)
919
+ * if (token) {
920
+ * res.setHeader('Set-Cookie', `token=${token}; Secure; HttpOnly`)
921
+ * res.end()
922
+ * } else {
923
+ * res.statusCode = 400
924
+ * res.end('Wrong user or password')
894
925
  * }
895
926
  * })
896
927
  * ```
897
928
  */
898
- http(listener: (req: IncomingMessage, res: ServerResponse) => void): void
929
+ http(
930
+ method: string,
931
+ url: string,
932
+ listener: (
933
+ req: IncomingMessage,
934
+ res: ServerResponse
935
+ ) => Promise<void> | void
936
+ ): void
937
+ http(
938
+ listener: (
939
+ req: IncomingMessage,
940
+ res: ServerResponse
941
+ ) => boolean | Promise<boolean>
942
+ ): void
899
943
 
900
944
  /**
901
945
  * Start WebSocket server and listen for clients.
@@ -909,7 +953,6 @@ export class BaseServer<
909
953
  * @param listener Event listener.
910
954
  */
911
955
  on(event: 'subscriptionCancelled', listener: () => void): Unsubscribe
912
-
913
956
  /**
914
957
  * @param event The event name.
915
958
  * @param listener Subscription listener.
@@ -918,38 +961,34 @@ export class BaseServer<
918
961
  event: 'subscribing',
919
962
  listener: (action: LoguxSubscribeAction, meta: Readonly<ServerMeta>) => void
920
963
  ): Unsubscribe
921
-
922
964
  /**
923
965
  * @param event The event name.
924
966
  * @param listener Processing listener.
925
967
  */
926
968
  on(
927
- event: 'backendGranted' | 'backendProcessed' | 'processed',
969
+ event: 'processed',
928
970
  listener: (
929
971
  action: Action,
930
972
  meta: Readonly<ServerMeta>,
931
973
  latencyMilliseconds: number
932
974
  ) => void
933
975
  ): Unsubscribe
934
-
935
976
  /**
936
977
  * @param event The event name.
937
978
  * @param listener Action listener.
938
979
  */
939
980
  on(
940
- event: 'add' | 'backendSent' | 'clean',
981
+ event: 'add' | 'clean',
941
982
  listener: (action: Action, meta: Readonly<ServerMeta>) => void
942
983
  ): Unsubscribe
943
-
944
984
  /**
945
985
  * @param event The event name.
946
986
  * @param listener Client listener.
947
987
  */
948
988
  on(
949
989
  event: 'connected' | 'disconnected',
950
- listener: (client: ServerClient) => void
990
+ listener: (client: ServerClient<ClientData>) => void
951
991
  ): Unsubscribe
952
-
953
992
  /**
954
993
  * Subscribe for synchronization events. It implements nanoevents API.
955
994
  * Supported events:
@@ -985,7 +1024,6 @@ export class BaseServer<
985
1024
  event: 'clientError' | 'fatal',
986
1025
  listener: (err: Error) => void
987
1026
  ): Unsubscribe
988
-
989
1027
  /**
990
1028
  * @param event The event name.
991
1029
  * @param listener Error listener.
@@ -994,16 +1032,17 @@ export class BaseServer<
994
1032
  event: 'error',
995
1033
  listener: (err: Error, action: Action, meta: Readonly<ServerMeta>) => void
996
1034
  ): Unsubscribe
997
-
998
1035
  /**
999
1036
  * @param event The event name.
1000
1037
  * @param listener Client listener.
1001
1038
  */
1002
1039
  on(
1003
1040
  event: 'authenticated' | 'unauthenticated',
1004
- listener: (client: ServerClient, latencyMilliseconds: number) => void
1041
+ listener: (
1042
+ client: ServerClient<ClientData>,
1043
+ latencyMilliseconds: number
1044
+ ) => void
1005
1045
  ): Unsubscribe
1006
-
1007
1046
  /**
1008
1047
  * @param event The event name.
1009
1048
  * @param listener Action listener.
@@ -1012,7 +1051,6 @@ export class BaseServer<
1012
1051
  event: 'preadd',
1013
1052
  listener: (action: Action, meta: ServerMeta) => void
1014
1053
  ): Unsubscribe
1015
-
1016
1054
  /**
1017
1055
  * @param event The event name.
1018
1056
  * @param listener Subscription listener.
@@ -1025,7 +1063,6 @@ export class BaseServer<
1025
1063
  latencyMilliseconds: number
1026
1064
  ) => void
1027
1065
  ): Unsubscribe
1028
-
1029
1066
  /**
1030
1067
  * @param event The event name.
1031
1068
  * @param listener Subscription listener.
@@ -1038,7 +1075,6 @@ export class BaseServer<
1038
1075
  clientNodeId: string
1039
1076
  ) => void
1040
1077
  ): Unsubscribe
1041
-
1042
1078
  /**
1043
1079
  * @param event The event name.
1044
1080
  * @param listener Report listener.
@@ -1064,7 +1100,7 @@ export class BaseServer<
1064
1100
  *
1065
1101
  * @param callbacks Callback during subscription process.
1066
1102
  */
1067
- otherChannel<Data extends object = {}>(
1103
+ otherChannel<Data extends object = unknown>(
1068
1104
  callbacks: ChannelCallbacks<LoguxSubscribeAction, Data, [string], Headers>
1069
1105
  ): void
1070
1106
 
@@ -1094,7 +1130,7 @@ export class BaseServer<
1094
1130
  *
1095
1131
  * @param callbacks Callbacks for actions with this type.
1096
1132
  */
1097
- otherType<Data extends object = {}>(
1133
+ otherType<Data extends object = unknown>(
1098
1134
  callbacks: ActionCallbacks<Action, Data, Headers>
1099
1135
  ): void
1100
1136
 
@@ -1106,8 +1142,8 @@ export class BaseServer<
1106
1142
  * @param meta Action’s meta.
1107
1143
  * @returns Promise until new action will be resend to clients and processed.
1108
1144
  */
1109
- process(
1110
- action: AnyAction,
1145
+ process<TypeAction extends Action = AnyAction>(
1146
+ action: TypeAction,
1111
1147
  meta?: Partial<ServerMeta>
1112
1148
  ): Promise<Readonly<ServerMeta>>
1113
1149
 
@@ -1131,6 +1167,27 @@ export class BaseServer<
1131
1167
  */
1132
1168
  sendAction(action: Action, meta: ServerMeta): Promise<void> | void
1133
1169
 
1170
+ /**
1171
+ * Change a way how server loads actions history for the client.
1172
+ *
1173
+ * ```js
1174
+ * server.sendOnConnect(async (ctx, lastSynced) => {
1175
+ * return db.loadActions({ user: ctx.userId, after: lastSynced })
1176
+ * })
1177
+ * ```
1178
+ *
1179
+ * Actions should be returned from the newest one to the oldest one.
1180
+ * The list will be split into messages by the `syncBatch` option, but it
1181
+ * is loaded into the memory as a whole. For a big history send it by
1182
+ * pages with `Context#sendBack()` and `Context#drain()` instead.
1183
+ *
1184
+ * Set `meta.added` to let the client ask only for newer actions after
1185
+ * the reconnect: the biggest `added` will be sent as the sync position.
1186
+ *
1187
+ * @param loader Callback which loads list of actions and meta.
1188
+ */
1189
+ sendOnConnect(loader: ConnectLoader<Headers>): void
1190
+
1134
1191
  /**
1135
1192
  * Send `logux/subscribed` if client was not already subscribed.
1136
1193
  *
@@ -1148,12 +1205,11 @@ export class BaseServer<
1148
1205
  * @param callbacks Callbacks for action created by creator.
1149
1206
  * @param options Additional options
1150
1207
  */
1151
- type<Creator extends AbstractActionCreator, Data extends object = {}>(
1208
+ type<Creator extends AbstractActionCreator, Data extends object = unknown>(
1152
1209
  actionCreator: Creator,
1153
1210
  callbacks: ActionCallbacks<ReturnType<Creator>, Data, Headers>,
1154
1211
  options?: TypeOptions
1155
1212
  ): void
1156
-
1157
1213
  /**
1158
1214
  * Define action type’s callbacks.
1159
1215
  *
@@ -1177,7 +1233,7 @@ export class BaseServer<
1177
1233
  * @param callbacks Callbacks for actions with this type.
1178
1234
  * @param options Additional options
1179
1235
  */
1180
- type<TypeAction extends Action = AnyAction, Data extends object = {}>(
1236
+ type<TypeAction extends Action = AnyAction, Data extends object = unknown>(
1181
1237
  name: RegExp | TypeAction['type'],
1182
1238
  callbacks: ActionCallbacks<TypeAction, Data, Headers>,
1183
1239
  options?: TypeOptions