@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.
- package/package.json +3 -5
- package/src/__mocks__/ignoreConsoleErrors.ts +10 -12
- package/src/rest/client/fetchRestApi.ts +33 -31
- package/src/rest/server/RestApi.ts +66 -57
- package/src/rest/server/buildRestApiRouteHandler.ts +18 -11
- package/src/rest/server/index.ts +2 -2
- package/src/rest/types/RestApiRequest.ts +10 -10
- package/src/rest/types/RestApiRequestHandler.ts +13 -13
- package/src/rest/types/RestApiRequestHandlerWithAuthentication.ts +7 -9
- package/src/rest/types/RestApiResponse.ts +3 -3
- package/src/rest/types/RestApiRoute.ts +13 -15
- package/src/rest/types/RestApiRouteHandler.ts +20 -13
- package/src/rest/types/RestApiRouteWithAuthentication.ts +7 -9
- package/src/rest/types/common/RestRequestHeadersWithAuthorization.ts +10 -8
- package/src/rest/types/common/index.ts +8 -8
- package/src/rest/types/index.ts +8 -8
- package/src/rest/types/internal/InternalRestApiRequest.ts +14 -12
- package/src/types/OmitNever.ts +1 -1
- package/src/ws/client/useWsApi.ts +68 -57
- package/src/ws/server/WsApi.ts +34 -36
- package/src/ws/server/buildWsApiRouteHandler.ts +15 -8
- package/src/ws/server/closeConnection.ts +7 -10
- package/src/ws/server/getApiGatewayManagementApiClient.ts +7 -8
- package/src/ws/server/getConnection.ts +5 -6
- package/src/ws/server/index.ts +8 -8
- package/src/ws/server/sendResponse.ts +13 -15
- package/src/ws/server/trpc/TrpcSubscriptionRequest.ts +10 -8
- package/src/ws/server/trpc/TrpcSubscriptionResponse.ts +2 -6
- package/src/ws/server/trpc/awsLambdaTrpcSubscriptionRequestHandler.ts +65 -48
- package/src/ws/server/trpc/isObservable.ts +3 -6
- package/src/ws/types/WsApiRequestHandler.ts +14 -13
- package/src/ws/types/WsApiRoute.ts +6 -8
- package/src/ws/types/WsApiRouteHandler.ts +7 -9
- package/src/ws/types/common/WsApiRequest.ts +2 -2
- 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 =
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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 =
|
|
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
|
-
|
|
96
|
+
const pingIntervalMs = 5_000
|
|
97
|
+
let lastPing = Date.now()
|
|
98
|
+
let stopped = false
|
|
92
99
|
while (!stopped) {
|
|
93
|
-
if (messageQueue.length === 0)
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
> = (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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> = {
|
|
3
|
+
export type WsApiRequest<Action extends WsApiAction> = {action: Action}
|
package/src/ws/types/index.ts
CHANGED
|
@@ -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'
|