@mstuercke/api-utils 6.0.0 → 6.0.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 (35) hide show
  1. package/package.json +3 -5
  2. package/src/__mocks__/ignoreConsoleErrors.ts +10 -12
  3. package/src/rest/client/fetchRestApi.ts +33 -31
  4. package/src/rest/server/RestApi.ts +66 -57
  5. package/src/rest/server/buildRestApiRouteHandler.ts +18 -11
  6. package/src/rest/server/index.ts +2 -2
  7. package/src/rest/types/RestApiRequest.ts +10 -10
  8. package/src/rest/types/RestApiRequestHandler.ts +13 -13
  9. package/src/rest/types/RestApiRequestHandlerWithAuthentication.ts +7 -9
  10. package/src/rest/types/RestApiResponse.ts +3 -3
  11. package/src/rest/types/RestApiRoute.ts +13 -15
  12. package/src/rest/types/RestApiRouteHandler.ts +20 -13
  13. package/src/rest/types/RestApiRouteWithAuthentication.ts +7 -9
  14. package/src/rest/types/common/RestRequestHeadersWithAuthorization.ts +10 -8
  15. package/src/rest/types/common/index.ts +8 -8
  16. package/src/rest/types/index.ts +8 -8
  17. package/src/rest/types/internal/InternalRestApiRequest.ts +14 -12
  18. package/src/types/OmitNever.ts +1 -1
  19. package/src/ws/client/useWsApi.ts +68 -57
  20. package/src/ws/server/WsApi.ts +34 -36
  21. package/src/ws/server/buildWsApiRouteHandler.ts +15 -8
  22. package/src/ws/server/closeConnection.ts +7 -10
  23. package/src/ws/server/getApiGatewayManagementApiClient.ts +7 -8
  24. package/src/ws/server/getConnection.ts +5 -6
  25. package/src/ws/server/index.ts +8 -8
  26. package/src/ws/server/sendResponse.ts +13 -15
  27. package/src/ws/server/trpc/TrpcSubscriptionRequest.ts +10 -8
  28. package/src/ws/server/trpc/TrpcSubscriptionResponse.ts +2 -6
  29. package/src/ws/server/trpc/awsLambdaTrpcSubscriptionRequestHandler.ts +65 -48
  30. package/src/ws/server/trpc/isObservable.ts +3 -6
  31. package/src/ws/types/WsApiRequestHandler.ts +14 -13
  32. package/src/ws/types/WsApiRoute.ts +6 -8
  33. package/src/ws/types/WsApiRouteHandler.ts +7 -9
  34. package/src/ws/types/common/WsApiRequest.ts +2 -2
  35. package/src/ws/types/index.ts +5 -5
@@ -1,66 +1,71 @@
1
- import {PostToConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi';
2
- import {AnyTRPCRouter, callTRPCProcedure, inferRouterContext} from '@trpc/server';
3
- import {APIGatewayProxyEvent, APIGatewayProxyResult, Context} from 'aws-lambda';
4
- import {getApiGatewayManagementApiClient} from '../getApiGatewayManagementApiClient';
5
- import {getConnection} from '../getConnection';
6
- import {TrpcSubscriptionRequest} from './TrpcSubscriptionRequest';
7
- import {TrpcSubscriptionResponse} from './TrpcSubscriptionResponse';
8
- import {isObservable} from './isObservable';
1
+ import {GetConnectionCommand, GoneException, PostToConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi'
2
+ import {type AnyTRPCRouter, callTRPCProcedure, type inferRouterContext} from '@trpc/server'
3
+ import type {APIGatewayProxyEvent, APIGatewayProxyResult, Context} from 'aws-lambda'
4
+ import {getApiGatewayManagementApiClient} from '../getApiGatewayManagementApiClient'
5
+ import {getConnection} from '../getConnection'
6
+ import type {TrpcSubscriptionRequest} from './TrpcSubscriptionRequest'
7
+ import type {TrpcSubscriptionResponse} from './TrpcSubscriptionResponse'
8
+ import {isObservable} from './isObservable'
9
9
 
10
10
  export const awsLambdaTrpcSubscriptionRequestHandler = <Router extends AnyTRPCRouter>(opts: {
11
11
  router: Router
12
12
  createContext?: (event: APIGatewayProxyEvent) => inferRouterContext<Router> | Promise<inferRouterContext<Router>>
13
13
  }) => {
14
- const {router, createContext} = opts;
14
+ const {router, createContext} = opts
15
15
 
16
16
  return async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
17
- context.callbackWaitsForEmptyEventLoop = false;
18
- const connection = getConnection(event);
17
+ context.callbackWaitsForEmptyEventLoop = false
18
+ const connection = getConnection(event)
19
19
 
20
- if (!event.body || event.body === '[]')
21
- return {statusCode: 200, body: '{}'};
20
+ if (!event.body || event.body === '[]') return {statusCode: 200, body: '{}'}
22
21
 
23
- const request = (JSON.parse(event.body) as TrpcSubscriptionRequest);
22
+ const request = JSON.parse(event.body) as TrpcSubscriptionRequest
24
23
 
25
24
  const sendMessage = async (response: TrpcSubscriptionResponse) => {
26
25
  await getApiGatewayManagementApiClient(connection.endpoint).send(
27
- new PostToConnectionCommand({
28
- ConnectionId: connection.connectionId,
29
- Data: JSON.stringify(response),
30
- }),
31
- );
32
- };
26
+ new PostToConnectionCommand({
27
+ ConnectionId: connection.connectionId,
28
+ Data: JSON.stringify(response),
29
+ }),
30
+ )
31
+ }
32
+ const isConnected = async (): Promise<boolean> => {
33
+ return getApiGatewayManagementApiClient(connection.endpoint)
34
+ .send(new GetConnectionCommand({ConnectionId: connection.connectionId}))
35
+ .then(() => true)
36
+ .catch(() => false)
37
+ }
33
38
  const stopConnection = async (): Promise<void> => {
34
39
  await sendMessage({
35
40
  id: request.id,
36
41
  result: {type: 'stopped'},
37
- });
38
- };
42
+ })
43
+ }
39
44
 
40
45
  try {
41
- const messageQueue: TrpcSubscriptionResponse[] = [];
46
+ const messageQueue: TrpcSubscriptionResponse[] = []
42
47
 
43
48
  if (request.method === 'subscription.stop') {
44
- await stopConnection();
49
+ await stopConnection()
45
50
  }
46
51
 
47
52
  if (request.method === 'subscription') {
48
53
  messageQueue.push({
49
54
  id: request.id,
50
55
  result: {type: 'started'},
51
- });
56
+ })
52
57
 
53
- const procedure = (await callTRPCProcedure({
58
+ const procedure = await callTRPCProcedure({
54
59
  procedures: router._def.procedures,
55
60
  path: request.params.path,
56
61
  type: 'subscription',
57
- ctx: await createContext?.(event) || {},
62
+ ctx: (await createContext?.(event)) || {},
58
63
  getRawInput: async () => request.params.input,
59
- }));
64
+ })
60
65
 
61
66
  if (!isObservable(procedure)) {
62
- console.error(`observable`, procedure);
63
- throw `Procedure is not an observable!`;
67
+ console.error(`observable`, procedure)
68
+ throw `Procedure is not an observable!`
64
69
  }
65
70
 
66
71
  procedure.subscribe({
@@ -71,44 +76,56 @@ export const awsLambdaTrpcSubscriptionRequestHandler = <Router extends AnyTRPCRo
71
76
  type: 'data',
72
77
  data: data,
73
78
  },
74
- });
79
+ })
75
80
  },
76
81
  error: (err) => {
77
- console.error(err);
82
+ console.error(err)
78
83
  messageQueue.push({
79
84
  id: request.id,
80
85
  result: {type: 'stopped'},
81
- });
86
+ })
82
87
  },
83
88
  complete: () => {
84
89
  messageQueue.push({
85
90
  id: request.id,
86
91
  result: {type: 'stopped'},
87
- });
92
+ })
88
93
  },
89
- });
94
+ })
90
95
 
91
- let stopped = false;
96
+ const pingIntervalMs = 5_000
97
+ let lastPing = Date.now()
98
+ let stopped = false
92
99
  while (!stopped) {
93
- if (messageQueue.length === 0)
94
- await new Promise(resolve => setTimeout(resolve, 10));
95
- const message = messageQueue.shift();
96
- if (!message)
97
- continue;
100
+ if (messageQueue.length === 0) await new Promise((resolve) => setTimeout(resolve, 10))
101
+ const message = messageQueue.shift()
102
+ if (!message) continue
98
103
 
99
- await sendMessage(message);
104
+ await sendMessage(message)
100
105
 
101
- stopped = message.result.type === 'stopped';
106
+ stopped = message.result.type === 'stopped'
107
+
108
+ if (Date.now() > lastPing + pingIntervalMs) {
109
+ if (!(await isConnected())) {
110
+ console.info('Connection closed by client')
111
+ stopped = true
112
+ }
113
+ lastPing = Date.now()
114
+ }
102
115
  }
103
116
  }
104
117
  } catch (err) {
105
- console.error(err);
106
- await stopConnection();
118
+ if (err instanceof GoneException) {
119
+ console.info('Connection closed by client')
120
+ } else {
121
+ console.error(err)
122
+ await stopConnection()
123
+ }
107
124
  }
108
125
 
109
126
  return {
110
127
  statusCode: 200,
111
128
  body: '{}',
112
- };
113
- };
114
- };
129
+ }
130
+ }
131
+ }
@@ -1,8 +1,5 @@
1
- import type {Observable} from '@trpc/server/observable';
1
+ import type {Observable} from '@trpc/server/observable'
2
2
 
3
3
  export const isObservable = (value: unknown): value is Observable<unknown, unknown> => {
4
- return !!value
5
- && typeof value === 'object'
6
- && `subscribe` in value
7
- && `pipe` in value;
8
- };
4
+ return !!value && typeof value === 'object' && `subscribe` in value && `pipe` in value
5
+ }
@@ -1,15 +1,16 @@
1
- import {OmitNever} from '../../types/OmitNever';
2
- import {WsApiConnection} from './WsApiConnection';
3
- import {WsApiAction, WsApiRequest, WsApiResponse} from './common';
1
+ import type {OmitNever} from '../../types/OmitNever'
2
+ import type {WsApiConnection} from './WsApiConnection'
3
+ import type {WsApiAction, WsApiRequest, WsApiResponse} from './common'
4
4
 
5
5
  export type WsApiRequestHandler<
6
- Action extends WsApiAction,
7
- Request extends WsApiRequest<Action>,
8
- Response extends WsApiResponse,
9
- > = (request: Request & { action: Action }, responseActions: OmitNever<{
10
- connection: WsApiConnection
11
- sendResponse: [Response] extends [never] ? never : (response: Response) => Promise<void>
12
- closeConnection: () => Promise<void>
13
- }>) => Promise<void>;
14
-
15
-
6
+ Action extends WsApiAction,
7
+ Request extends WsApiRequest<Action>,
8
+ Response extends WsApiResponse,
9
+ > = (
10
+ request: Request & {action: Action},
11
+ responseActions: OmitNever<{
12
+ connection: WsApiConnection
13
+ sendResponse: [Response] extends [never] ? never : (response: Response) => Promise<void>
14
+ closeConnection: () => Promise<void>
15
+ }>,
16
+ ) => Promise<void>
@@ -1,9 +1,9 @@
1
- import {WsApiAction, WsApiRequest, WsApiResponse} from './common';
1
+ import type {WsApiAction, WsApiRequest, WsApiResponse} from './common'
2
2
 
3
3
  export type WsApiRoute<
4
- Action extends WsApiAction,
5
- Request extends WsApiRequest<Action>,
6
- Response extends WsApiResponse,
4
+ Action extends WsApiAction,
5
+ Request extends WsApiRequest<Action>,
6
+ Response extends WsApiResponse,
7
7
  > = {
8
8
  action: Action
9
9
 
@@ -11,7 +11,5 @@ export type WsApiRoute<
11
11
  __types?: {
12
12
  Request: Request
13
13
  Response: Response
14
- };
15
- };
16
-
17
-
14
+ }
15
+ }
@@ -1,14 +1,12 @@
1
- import {WsApiRequestHandler} from './WsApiRequestHandler';
2
- import {WsApiRoute} from './WsApiRoute';
3
- import {WsApiAction, WsApiRequest, WsApiResponse} from './common';
1
+ import type {WsApiRequestHandler} from './WsApiRequestHandler'
2
+ import type {WsApiRoute} from './WsApiRoute'
3
+ import type {WsApiAction, WsApiRequest, WsApiResponse} from './common'
4
4
 
5
5
  export type WsApiRouteHandler<
6
- Action extends WsApiAction,
7
- Request extends WsApiRequest<Action>,
8
- Response extends WsApiResponse,
6
+ Action extends WsApiAction,
7
+ Request extends WsApiRequest<Action>,
8
+ Response extends WsApiResponse,
9
9
  > = {
10
- route: WsApiRoute<Action, Request, Response>,
10
+ route: WsApiRoute<Action, Request, Response>
11
11
  requestHandler: WsApiRequestHandler<Action, Request, Response>
12
12
  }
13
-
14
-
@@ -1,3 +1,3 @@
1
- import {WsApiAction} from './WsApiAction';
1
+ import type {WsApiAction} from './WsApiAction'
2
2
 
3
- export type WsApiRequest<Action extends WsApiAction> = { action: Action }
3
+ export type WsApiRequest<Action extends WsApiAction> = {action: Action}
@@ -1,5 +1,5 @@
1
- export * from './common';
2
- export * from './WsApiConnection';
3
- export * from './WsApiRequestHandler';
4
- export * from './WsApiRoute';
5
- export * from './WsApiRouteHandler';
1
+ export * from './common'
2
+ export * from './WsApiConnection'
3
+ export * from './WsApiRequestHandler'
4
+ export * from './WsApiRoute'
5
+ export * from './WsApiRouteHandler'