@mstuercke/api-utils 6.0.0 → 6.0.1

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 +49 -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,8 +1,8 @@
1
- export * from './common';
2
- export * from './RestApiRequest';
3
- export * from './RestApiRequestHandler';
4
- export * from './RestApiRequestHandlerWithAuthentication';
5
- export * from './RestApiResponse';
6
- export * from './RestApiRoute';
7
- export * from './RestApiRouteHandler';
8
- export * from './RestApiRouteWithAuthentication';
1
+ export * from './common'
2
+ export * from './RestApiRequest'
3
+ export * from './RestApiRequestHandler'
4
+ export * from './RestApiRequestHandlerWithAuthentication'
5
+ export * from './RestApiResponse'
6
+ export * from './RestApiRoute'
7
+ export * from './RestApiRouteHandler'
8
+ export * from './RestApiRouteWithAuthentication'
@@ -1,19 +1,21 @@
1
- import {OmitNever} from '../../../types/OmitNever';
2
- import {RestApiRequest} from '../RestApiRequest';
3
- import {
1
+ import type {OmitNever} from '../../../types/OmitNever'
2
+ import type {RestApiRequest} from '../RestApiRequest'
3
+ import type {
4
4
  RestPathParams,
5
5
  RestQueryParams,
6
6
  RestRequestBody,
7
7
  RestRequestHeaders,
8
8
  RestRequireAuthentication,
9
- } from '../common';
9
+ } from '../common'
10
10
 
11
11
  export type InternalRestApiRequest<
12
- PathParams extends RestPathParams = never,
13
- QueryParams extends RestQueryParams = never,
14
- RequestHeaders extends RestRequestHeaders = never,
15
- RequestBody extends RestRequestBody = never,
16
- RequireAuthentication extends RestRequireAuthentication = false> = OmitNever<
17
- RestApiRequest<PathParams, QueryParams, RequestHeaders, RequestBody>
18
- & { userId: RequireAuthentication extends true ? string : never }>
19
-
12
+ PathParams extends RestPathParams = never,
13
+ QueryParams extends RestQueryParams = never,
14
+ RequestHeaders extends RestRequestHeaders = never,
15
+ RequestBody extends RestRequestBody = never,
16
+ RequireAuthentication extends RestRequireAuthentication = false,
17
+ > = OmitNever<
18
+ RestApiRequest<PathParams, QueryParams, RequestHeaders, RequestBody> & {
19
+ userId: RequireAuthentication extends true ? string : never
20
+ }
21
+ >
@@ -1 +1 @@
1
- export type OmitNever<T> = { [P in keyof T as T[P] extends never ? never : P]: T[P] };
1
+ export type OmitNever<T> = {[P in keyof T as T[P] extends never ? never : P]: T[P]}
@@ -1,82 +1,93 @@
1
- import {useCallback, useMemo, useRef, useState} from 'react';
2
- import {WsApiAction, WsApiRequest, WsApiResponse, WsApiRoute} from '../types';
1
+ import {useCallback, useMemo, useRef, useState} from 'react'
2
+ import type {WsApiAction, WsApiRequest, WsApiResponse, WsApiRoute} from '../types'
3
3
 
4
- type WsApiConnection<
5
- Action extends WsApiAction,
6
- Request extends WsApiRequest<Action>,
7
- > = {
4
+ type WsApiConnection<Action extends WsApiAction, Request extends WsApiRequest<Action>> = {
8
5
  disconnect: () => void
9
6
  sendRequest: (request: Request) => void
10
7
  }
11
8
 
12
9
  export type WsApiOptions<
13
- Action extends WsApiAction,
14
- Request extends WsApiRequest<Action>,
15
- Response extends WsApiResponse> = {
10
+ Action extends WsApiAction,
11
+ Request extends WsApiRequest<Action>,
12
+ Response extends WsApiResponse,
13
+ > = {
16
14
  onConnected?: (connection: WsApiConnection<Action, Request>) => void
17
15
  onResponse?: (response: Response, connection: WsApiConnection<Action, Request>) => void
18
16
  onError?: (error: unknown, connection: WsApiConnection<Action, Request>) => void
19
17
  onDisconnected?: () => void
20
18
  }
21
19
  export const useWsApi = <
22
- Action extends WsApiAction,
23
- Request extends WsApiRequest<Action>,
24
- Response extends WsApiResponse
25
- >(wsApiUrl: string, route: WsApiRoute<Action, Request, Response> | undefined) => {
26
- const [error, setError] = useState<boolean>(false);
27
- const [connected, setConnected] = useState<boolean>(false);
28
- const webSocket = useRef<WebSocket>();
20
+ Action extends WsApiAction,
21
+ Request extends WsApiRequest<Action>,
22
+ Response extends WsApiResponse,
23
+ >(
24
+ wsApiUrl: string,
25
+ route: WsApiRoute<Action, Request, Response> | undefined,
26
+ ) => {
27
+ const [error, setError] = useState<boolean>(false)
28
+ const [connected, setConnected] = useState<boolean>(false)
29
+ const webSocket = useRef<WebSocket>()
29
30
 
30
- const sendRequest = useCallback((request: Omit<Request, 'action'>) => {
31
- if (!route) throw 'Not route';
32
- if (!webSocket.current) throw 'Not connected';
31
+ const sendRequest = useCallback(
32
+ (request: Omit<Request, 'action'>) => {
33
+ if (!route) throw 'Not route'
34
+ if (!webSocket.current) throw 'Not connected'
33
35
 
34
- webSocket.current.send(JSON.stringify({
35
- ...request,
36
- action: route.action,
37
- }));
38
- }, [route]);
39
- const disconnect = useCallback(() => webSocket.current?.close(), [webSocket]);
36
+ webSocket.current.send(
37
+ JSON.stringify({
38
+ ...request,
39
+ action: route.action,
40
+ }),
41
+ )
42
+ },
43
+ [route],
44
+ )
45
+ const disconnect = useCallback(() => webSocket.current?.close(), [])
40
46
  const connection: WsApiConnection<Action, Request> = useMemo(() => {
41
47
  return {
42
48
  disconnect,
43
49
  sendRequest,
44
- };
45
- }, [disconnect, sendRequest]);
50
+ }
51
+ }, [disconnect, sendRequest])
46
52
 
47
- const connect = useCallback(async (options: WsApiOptions<Action, Request, Response>) => {
48
- return new Promise<void>((resolve) => {
49
- if (webSocket.current && (webSocket.current.readyState === WebSocket.CONNECTING || webSocket.current.readyState === WebSocket.OPEN))
50
- throw 'Already connected';
53
+ const connect = useCallback(
54
+ async (options: WsApiOptions<Action, Request, Response>) => {
55
+ return new Promise<void>((resolve) => {
56
+ if (
57
+ webSocket.current &&
58
+ (webSocket.current.readyState === WebSocket.CONNECTING || webSocket.current.readyState === WebSocket.OPEN)
59
+ )
60
+ throw 'Already connected'
51
61
 
52
- webSocket.current = new WebSocket(wsApiUrl);
62
+ webSocket.current = new WebSocket(wsApiUrl)
53
63
 
54
- webSocket.current.onopen = () => {
55
- setConnected(true);
56
- setError(false);
57
- options?.onConnected?.(connection);
58
- };
64
+ webSocket.current.onopen = () => {
65
+ setConnected(true)
66
+ setError(false)
67
+ options?.onConnected?.(connection)
68
+ }
59
69
 
60
- webSocket.current.onmessage = (ev) => {
61
- if (!ev.data || ev.data === '{}')
62
- return;
63
- setError(false);
64
- options?.onResponse?.(JSON.parse(ev.data as string), connection);
65
- };
70
+ webSocket.current.onmessage = (ev) => {
71
+ if (!ev.data || ev.data === '{}') return
72
+ setError(false)
73
+ options?.onResponse?.(JSON.parse(ev.data as string), connection)
74
+ }
66
75
 
67
- webSocket.current.onerror = (error) => {
68
- setError(false);
69
- options?.onError?.(error, connection);
70
- };
76
+ webSocket.current.onerror = (error) => {
77
+ setError(false)
78
+ options?.onError?.(error, connection)
79
+ }
71
80
 
72
- webSocket.current.onclose = () => {
73
- setConnected(false);
74
- options?.onDisconnected?.();
75
- webSocket.current = undefined;
76
- resolve();
77
- };
78
- });
79
- }, [wsApiUrl, connection]);
81
+ webSocket.current.onclose = () => {
82
+ setConnected(false)
83
+ options?.onDisconnected?.()
84
+ webSocket.current = undefined
85
+ resolve()
86
+ }
87
+ })
88
+ },
89
+ [wsApiUrl, connection],
90
+ )
80
91
 
81
92
  return {
82
93
  connect,
@@ -84,5 +95,5 @@ export const useWsApi = <
84
95
  connection,
85
96
  error,
86
97
  disconnect,
87
- };
88
- };
98
+ }
99
+ }
@@ -1,75 +1,73 @@
1
- import {APIGatewayProxyEvent, APIGatewayProxyResult} from 'aws-lambda';
2
- import {
1
+ import type {APIGatewayProxyEvent, APIGatewayProxyResult} from 'aws-lambda'
2
+ import type {
3
3
  WsApiAction,
4
4
  WsApiConnection,
5
5
  WsApiRequest,
6
6
  WsApiRequestHandler,
7
7
  WsApiResponse,
8
8
  WsApiRouteHandler,
9
- } from '../types';
10
- import {closeConnection} from './closeConnection';
11
- import {sendResponse} from './sendResponse';
9
+ } from '../types'
10
+ import {closeConnection} from './closeConnection'
11
+ import {sendResponse} from './sendResponse'
12
12
 
13
13
  export class WsApi {
14
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
- private readonly routeHandlers: { [action: WsApiAction]: WsApiRequestHandler<any, any, any> } = {
16
- '$connect': () => Promise.resolve(),
17
- '$disconnect': () => Promise.resolve(),
18
- };
15
+ private readonly routeHandlers: {[action: WsApiAction]: WsApiRequestHandler<any, any, any>} = {
16
+ $connect: () => Promise.resolve(),
17
+ $disconnect: () => Promise.resolve(),
18
+ }
19
19
 
20
- addRouteHandler<
21
- Action extends WsApiAction,
22
- Request extends WsApiRequest<Action>,
23
- Response extends WsApiResponse
24
- >(routeHandler: WsApiRouteHandler<Action, Request, Response>) {
25
- this.routeHandlers[routeHandler.route.action] = routeHandler.requestHandler;
26
- return this;
20
+ addRouteHandler<Action extends WsApiAction, Request extends WsApiRequest<Action>, Response extends WsApiResponse>(
21
+ routeHandler: WsApiRouteHandler<Action, Request, Response>,
22
+ ) {
23
+ this.routeHandlers[routeHandler.route.action] = routeHandler.requestHandler
24
+ return this
27
25
  }
28
26
 
29
27
  async handleRequest(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
30
- const {connectionId, domainName, stage, eventType} = event.requestContext;
28
+ const {connectionId, domainName, stage, eventType} = event.requestContext
31
29
 
32
- if (!eventType) throw 'No eventType';
33
- if (!connectionId) throw 'No connectionId';
34
- if (!domainName) throw 'No domainName';
30
+ if (!eventType) throw 'No eventType'
31
+ if (!connectionId) throw 'No connectionId'
32
+ if (!domainName) throw 'No domainName'
35
33
 
36
- let request: WsApiRequest<WsApiAction>;
34
+ let request: WsApiRequest<WsApiAction>
37
35
  switch (eventType) {
38
36
  case 'CONNECT':
39
- request = {action: '$connect'};
40
- break;
37
+ request = {action: '$connect'}
38
+ break
41
39
 
42
40
  case 'DISCONNECT':
43
- request = {action: '$disconnect'};
44
- break;
41
+ request = {action: '$disconnect'}
42
+ break
45
43
 
46
44
  case 'MESSAGE':
47
- if (!event.body) throw 'No body';
48
- request = JSON.parse(event.body) as WsApiRequest<WsApiAction>;
49
- break;
45
+ if (!event.body) throw 'No body'
46
+ request = JSON.parse(event.body) as WsApiRequest<WsApiAction>
47
+ break
50
48
 
51
49
  default:
52
- throw `Unknown eventType ${eventType}`;
50
+ throw `Unknown eventType ${eventType}`
53
51
  }
54
52
 
55
- const endpoint = `https://${domainName}/${stage}`;
56
- const connection: WsApiConnection = {endpoint, connectionId};
53
+ const endpoint = `https://${domainName}/${stage}`
54
+ const connection: WsApiConnection = {endpoint, connectionId}
57
55
 
58
- const requestHandler = this.routeHandlers[request.action];
56
+ const requestHandler = this.routeHandlers[request.action]
59
57
  if (!requestHandler) {
60
- await closeConnection(connection);
61
- throw `No requestHandler found for action ${request.action}`;
58
+ await closeConnection(connection)
59
+ throw `No requestHandler found for action ${request.action}`
62
60
  }
63
61
 
64
62
  await requestHandler(request, {
65
63
  connection,
66
64
  sendResponse: async (response) => await sendResponse(connection, response),
67
65
  closeConnection: async () => await closeConnection(connection),
68
- });
66
+ })
69
67
 
70
68
  return {
71
69
  statusCode: 200,
72
70
  body: '',
73
- };
71
+ }
74
72
  }
75
73
  }
@@ -1,15 +1,22 @@
1
- import {WsApiAction, WsApiRequest, WsApiRequestHandler, WsApiResponse, WsApiRoute, WsApiRouteHandler} from '../types';
1
+ import type {
2
+ WsApiAction,
3
+ WsApiRequest,
4
+ WsApiRequestHandler,
5
+ WsApiResponse,
6
+ WsApiRoute,
7
+ WsApiRouteHandler,
8
+ } from '../types'
2
9
 
3
10
  export const buildWsApiRouteHandler = <
4
- Action extends WsApiAction,
5
- Request extends WsApiRequest<Action>,
6
- Response extends WsApiResponse
11
+ Action extends WsApiAction,
12
+ Request extends WsApiRequest<Action>,
13
+ Response extends WsApiResponse,
7
14
  >(
8
- route: WsApiRoute<Action, Request, Response>,
9
- requestHandler: WsApiRequestHandler<Action, Request, Response>,
15
+ route: WsApiRoute<Action, Request, Response>,
16
+ requestHandler: WsApiRequestHandler<Action, Request, Response>,
10
17
  ): WsApiRouteHandler<Action, Request, Response> => {
11
18
  return {
12
19
  route,
13
20
  requestHandler,
14
- };
15
- };
21
+ }
22
+ }
@@ -1,16 +1,13 @@
1
- import {DeleteConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi';
2
- import {WsApiConnection} from '../types';
3
- import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient';
1
+ import {DeleteConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi'
2
+ import type {WsApiConnection} from '../types'
3
+ import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient'
4
4
 
5
5
  export const closeConnection = async ({endpoint, connectionId}: WsApiConnection) => {
6
6
  try {
7
- await getApiGatewayManagementApiClient(endpoint).send(
8
- new DeleteConnectionCommand({ConnectionId: connectionId}),
9
- );
7
+ await getApiGatewayManagementApiClient(endpoint).send(new DeleteConnectionCommand({ConnectionId: connectionId}))
10
8
  } catch (err) {
11
- if (err && typeof err === 'object' && 'name' in err && err?.name === 'GoneException')
12
- return;
9
+ if (err && typeof err === 'object' && 'name' in err && err?.name === 'GoneException') return
13
10
 
14
- throw err;
11
+ throw err
15
12
  }
16
- };
13
+ }
@@ -1,11 +1,10 @@
1
- import {ApiGatewayManagementApiClient} from '@aws-sdk/client-apigatewaymanagementapi';
1
+ import {ApiGatewayManagementApiClient} from '@aws-sdk/client-apigatewaymanagementapi'
2
2
 
3
- const clients: { [endpoint: string]: ApiGatewayManagementApiClient } = {};
3
+ const clients: {[endpoint: string]: ApiGatewayManagementApiClient} = {}
4
4
  export const getApiGatewayManagementApiClient = (endpoint: string): ApiGatewayManagementApiClient => {
5
- if (clients[endpoint])
6
- return clients[endpoint];
5
+ if (clients[endpoint]) return clients[endpoint]
7
6
 
8
- const client = new ApiGatewayManagementApiClient({endpoint});
9
- clients[endpoint] = client;
10
- return client;
11
- };
7
+ const client = new ApiGatewayManagementApiClient({endpoint})
8
+ clients[endpoint] = client
9
+ return client
10
+ }
@@ -1,12 +1,11 @@
1
- import {APIGatewayProxyEvent} from 'aws-lambda';
2
- import {WsApiConnection} from '../types';
1
+ import type {APIGatewayProxyEvent} from 'aws-lambda'
2
+ import type {WsApiConnection} from '../types'
3
3
 
4
4
  export const getConnection = (event: APIGatewayProxyEvent): WsApiConnection => {
5
- if (!event.requestContext.connectionId)
6
- throw 'no connectionId';
5
+ if (!event.requestContext.connectionId) throw 'no connectionId'
7
6
 
8
7
  return {
9
8
  endpoint: `https://${event.requestContext.domainName}/${event.requestContext.stage}`,
10
9
  connectionId: event.requestContext.connectionId,
11
- };
12
- };
10
+ }
11
+ }
@@ -1,8 +1,8 @@
1
- export * from './trpc/TrpcSubscriptionRequest';
2
- export * from './trpc/TrpcSubscriptionResponse';
3
- export * from './trpc/awsLambdaTrpcSubscriptionRequestHandler';
4
- export * from './buildWsApiRouteHandler';
5
- export * from './closeConnection';
6
- export * from './getConnection';
7
- export * from './sendResponse';
8
- export * from './WsApi';
1
+ export * from './trpc/TrpcSubscriptionRequest'
2
+ export * from './trpc/TrpcSubscriptionResponse'
3
+ export * from './trpc/awsLambdaTrpcSubscriptionRequestHandler'
4
+ export * from './buildWsApiRouteHandler'
5
+ export * from './closeConnection'
6
+ export * from './getConnection'
7
+ export * from './sendResponse'
8
+ export * from './WsApi'
@@ -1,17 +1,15 @@
1
- import {PostToConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi';
2
- import {WsApiConnection, WsApiResponse} from '../types';
3
- import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient';
1
+ import {PostToConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi'
2
+ import type {WsApiConnection, WsApiResponse} from '../types'
3
+ import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient'
4
4
 
5
- export const sendResponse = async <
6
- Response extends WsApiResponse
7
- >({
8
- endpoint,
9
- connectionId,
10
- }: WsApiConnection, response: Response): Promise<void> => {
5
+ export const sendResponse = async <Response extends WsApiResponse>(
6
+ {endpoint, connectionId}: WsApiConnection,
7
+ response: Response,
8
+ ): Promise<void> => {
11
9
  await getApiGatewayManagementApiClient(endpoint).send(
12
- new PostToConnectionCommand({
13
- ConnectionId: connectionId,
14
- Data: JSON.stringify(response),
15
- }),
16
- );
17
- };
10
+ new PostToConnectionCommand({
11
+ ConnectionId: connectionId,
12
+ Data: JSON.stringify(response),
13
+ }),
14
+ )
15
+ }
@@ -1,8 +1,10 @@
1
- export type TrpcSubscriptionRequest<Input = unknown> = {
2
- id: number | string
3
- method: 'subscription'
4
- params: { path: string, input?: Input }
5
- } | {
6
- id: number | string
7
- method: 'subscription.stop'
8
- }
1
+ export type TrpcSubscriptionRequest<Input = unknown> =
2
+ | {
3
+ id: number | string
4
+ method: 'subscription'
5
+ params: {path: string; input?: Input}
6
+ }
7
+ | {
8
+ id: number | string
9
+ method: 'subscription.stop'
10
+ }
@@ -1,8 +1,4 @@
1
1
  export type TrpcSubscriptionResponse<Data = unknown> = {
2
- id: number | string;
3
- result: (
4
- | { type: 'data', data: Data }
5
- | { type: 'started' }
6
- | { type: 'stopped' }
7
- )
2
+ id: number | string
3
+ result: {type: 'data'; data: Data} | {type: 'started'} | {type: 'stopped'}
8
4
  }