@mstuercke/api-utils 7.0.2 → 7.1.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.
- package/package.json +9 -17
- package/src/__mocks__/ignoreConsoleErrors.ts +0 -2
- package/src/ws/server/WsApi.ts +3 -3
- package/src/ws/server/buildWsApiRouteHandler.ts +1 -1
- package/src/ws/server/closeConnection.ts +2 -2
- package/src/ws/server/getConnection.ts +1 -1
- package/src/ws/server/index.ts +8 -8
- package/src/ws/server/sendResponse.ts +2 -2
- package/src/ws/server/trpc/awsLambdaTrpcSubscriptionRequestHandler.ts +5 -5
- package/src/ws/types/WsApiRequestHandler.ts +3 -3
- package/src/ws/types/WsApiRoute.ts +1 -1
- package/src/ws/types/WsApiRouteHandler.ts +3 -3
- package/src/ws/types/common/WsApiRequest.ts +1 -1
- package/src/ws/types/common/index.ts +3 -3
- package/src/ws/types/index.ts +5 -5
- package/src/rest/client/fetchRestApi.ts +0 -49
- package/src/rest/client/index.ts +0 -1
- package/src/rest/server/RestApi.ts +0 -153
- package/src/rest/server/buildRestApiRouteHandler.ts +0 -33
- package/src/rest/server/index.ts +0 -2
- package/src/rest/types/RestApiRequest.ts +0 -14
- package/src/rest/types/RestApiRequestHandler.ts +0 -21
- package/src/rest/types/RestApiRequestHandlerWithAuthentication.ts +0 -10
- package/src/rest/types/RestApiResponse.ts +0 -5
- package/src/rest/types/RestApiRoute.ts +0 -32
- package/src/rest/types/RestApiRouteHandler.ts +0 -29
- package/src/rest/types/RestApiRouteWithAuthentication.ts +0 -10
- package/src/rest/types/common/BearerToken.ts +0 -1
- package/src/rest/types/common/RestPathParams.ts +0 -1
- package/src/rest/types/common/RestQueryParams.ts +0 -1
- package/src/rest/types/common/RestRequestBody.ts +0 -1
- package/src/rest/types/common/RestRequestHeaders.ts +0 -1
- package/src/rest/types/common/RestRequestHeadersWithAuthorization.ts +0 -13
- package/src/rest/types/common/RestRequireAuthentication.ts +0 -1
- package/src/rest/types/common/RestResponseBody.ts +0 -1
- package/src/rest/types/common/index.ts +0 -8
- package/src/rest/types/index.ts +0 -8
- package/src/rest/types/internal/InternalRestApiRequest.ts +0 -21
- package/src/rest/types/internal/index.ts +0 -2
- package/src/ws/client/index.ts +0 -1
- package/src/ws/client/useWsApi.ts +0 -99
package/package.json
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mstuercke/api-utils",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "src/index.ts",
|
|
6
5
|
"files": [
|
|
7
6
|
"src/**/!(*.test|*.fixture).ts",
|
|
8
7
|
"README.md"
|
|
9
8
|
],
|
|
10
9
|
"exports": {
|
|
11
10
|
"./src": null,
|
|
12
|
-
"./rest/types": "./src/rest/types/index.ts",
|
|
13
|
-
"./rest/client": "./src/rest/client/index.ts",
|
|
14
|
-
"./rest/server": "./src/rest/server/index.ts",
|
|
15
11
|
"./ws/types": "./src/ws/types/index.ts",
|
|
16
|
-
"./ws/client": "./src/ws/client/index.ts",
|
|
17
12
|
"./ws/server": "./src/ws/server/index.ts"
|
|
18
13
|
},
|
|
19
14
|
"typesVersions": {
|
|
@@ -39,31 +34,28 @@
|
|
|
39
34
|
}
|
|
40
35
|
},
|
|
41
36
|
"scripts": {
|
|
42
|
-
"test:unit": "
|
|
37
|
+
"test:unit": "vitest run --coverage",
|
|
43
38
|
"typeCheck": "tsc",
|
|
44
|
-
"
|
|
45
|
-
"bump:minor": "npm version minor --git-tag-version false",
|
|
46
|
-
"bump:patch": "npm version patch --git-tag-version false",
|
|
47
|
-
"release": "bun run scripts/release.ts"
|
|
39
|
+
"release": "node --no-warnings --loader ts-node/esm scripts/release.ts"
|
|
48
40
|
},
|
|
49
41
|
"dependencies": {
|
|
50
42
|
"path-to-regexp": "^8.0.0"
|
|
51
43
|
},
|
|
52
44
|
"peerDependencies": {
|
|
53
45
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.438.0",
|
|
54
|
-
"@trpc/server": "^11.
|
|
46
|
+
"@trpc/server": "^11.1.2",
|
|
55
47
|
"@types/aws-lambda": "8.10.147"
|
|
56
48
|
},
|
|
57
49
|
"devDependencies": {
|
|
58
|
-
"@
|
|
59
|
-
"@
|
|
50
|
+
"@aws-sdk/client-apigatewaymanagementapi": "^3.438.0",
|
|
51
|
+
"@mstuercke/node-utils": "0.0.3",
|
|
52
|
+
"@trpc/server": "^11.1.2",
|
|
60
53
|
"@types/aws-lambda": "8.10.147",
|
|
61
|
-
"@types/bun": "1.2.2",
|
|
62
54
|
"@types/path-to-regexp": "1.7.0",
|
|
63
|
-
"@types/react": "19.0.
|
|
55
|
+
"@types/react": "19.0.10",
|
|
64
56
|
"aws-sdk-client-mock": "4.1.0",
|
|
65
57
|
"typescript": "5.7.3",
|
|
66
|
-
"zod": "3.24.
|
|
58
|
+
"zod": "3.24.2"
|
|
67
59
|
},
|
|
68
60
|
"publishConfig": {
|
|
69
61
|
"registry": "https://registry.npmjs.org/"
|
package/src/ws/server/WsApi.ts
CHANGED
|
@@ -6,9 +6,9 @@ import type {
|
|
|
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/index.ts'
|
|
10
|
+
import {closeConnection} from './closeConnection.ts'
|
|
11
|
+
import {sendResponse} from './sendResponse.ts'
|
|
12
12
|
|
|
13
13
|
export class WsApi {
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {DeleteConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi'
|
|
2
|
-
import type {WsApiConnection} from '../types'
|
|
3
|
-
import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient'
|
|
2
|
+
import type {WsApiConnection} from '../types/index.ts'
|
|
3
|
+
import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient.ts'
|
|
4
4
|
|
|
5
5
|
export const closeConnection = async ({endpoint, connectionId}: WsApiConnection) => {
|
|
6
6
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {APIGatewayProxyEvent} from 'aws-lambda'
|
|
2
|
-
import type {WsApiConnection} from '../types'
|
|
2
|
+
import type {WsApiConnection} from '../types/index.ts'
|
|
3
3
|
|
|
4
4
|
export const getConnection = (event: APIGatewayProxyEvent): WsApiConnection => {
|
|
5
5
|
if (!event.requestContext.connectionId) throw 'no connectionId'
|
package/src/ws/server/index.ts
CHANGED
|
@@ -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.ts'
|
|
2
|
+
export * from './trpc/TrpcSubscriptionResponse.ts'
|
|
3
|
+
export * from './trpc/awsLambdaTrpcSubscriptionRequestHandler.ts'
|
|
4
|
+
export * from './buildWsApiRouteHandler.ts'
|
|
5
|
+
export * from './closeConnection.ts'
|
|
6
|
+
export * from './getConnection.ts'
|
|
7
|
+
export * from './sendResponse.ts'
|
|
8
|
+
export * from './WsApi.ts'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {PostToConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi'
|
|
2
|
-
import type {WsApiConnection, WsApiResponse} from '../types'
|
|
3
|
-
import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient'
|
|
2
|
+
import type {WsApiConnection, WsApiResponse} from '../types/index.ts'
|
|
3
|
+
import {getApiGatewayManagementApiClient} from './getApiGatewayManagementApiClient.ts'
|
|
4
4
|
|
|
5
5
|
export const sendResponse = async <Response extends WsApiResponse>(
|
|
6
6
|
{endpoint, connectionId}: WsApiConnection,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {GetConnectionCommand, GoneException, PostToConnectionCommand} from '@aws-sdk/client-apigatewaymanagementapi'
|
|
2
2
|
import {type AnyTRPCRouter, callTRPCProcedure, type inferRouterContext} from '@trpc/server'
|
|
3
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'
|
|
4
|
+
import {getApiGatewayManagementApiClient} from '../getApiGatewayManagementApiClient.ts'
|
|
5
|
+
import {getConnection} from '../getConnection.ts'
|
|
6
|
+
import type {TrpcSubscriptionRequest} from './TrpcSubscriptionRequest.ts'
|
|
7
|
+
import type {TrpcSubscriptionResponse} from './TrpcSubscriptionResponse.ts'
|
|
8
8
|
|
|
9
9
|
export const awsLambdaTrpcSubscriptionRequestHandler = <Router extends AnyTRPCRouter>(opts: {
|
|
10
10
|
router: Router
|
|
@@ -57,7 +57,7 @@ export const awsLambdaTrpcSubscriptionRequestHandler = <Router extends AnyTRPCRo
|
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
const procedure: AsyncGenerator = await callTRPCProcedure({
|
|
60
|
-
|
|
60
|
+
router,
|
|
61
61
|
path: request.params.path,
|
|
62
62
|
type: 'subscription',
|
|
63
63
|
ctx: (await createContext?.(event)) || {},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {OmitNever} from '../../types/OmitNever'
|
|
2
|
-
import type {WsApiConnection} from './WsApiConnection'
|
|
3
|
-
import type {WsApiAction, WsApiRequest, WsApiResponse} from './common'
|
|
1
|
+
import type {OmitNever} from '../../types/OmitNever.ts'
|
|
2
|
+
import type {WsApiConnection} from './WsApiConnection.ts'
|
|
3
|
+
import type {WsApiAction, WsApiRequest, WsApiResponse} from './common/index.ts'
|
|
4
4
|
|
|
5
5
|
export type WsApiRequestHandler<
|
|
6
6
|
Action extends WsApiAction,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {WsApiRequestHandler} from './WsApiRequestHandler'
|
|
2
|
-
import type {WsApiRoute} from './WsApiRoute'
|
|
3
|
-
import type {WsApiAction, WsApiRequest, WsApiResponse} from './common'
|
|
1
|
+
import type {WsApiRequestHandler} from './WsApiRequestHandler.ts'
|
|
2
|
+
import type {WsApiRoute} from './WsApiRoute.ts'
|
|
3
|
+
import type {WsApiAction, WsApiRequest, WsApiResponse} from './common/index.ts'
|
|
4
4
|
|
|
5
5
|
export type WsApiRouteHandler<
|
|
6
6
|
Action extends WsApiAction,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './WsApiRequest'
|
|
2
|
-
export * from './WsApiResponse'
|
|
3
|
-
export * from './WsApiAction'
|
|
1
|
+
export * from './WsApiRequest.ts'
|
|
2
|
+
export * from './WsApiResponse.ts'
|
|
3
|
+
export * from './WsApiAction.ts'
|
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/index.ts'
|
|
2
|
+
export * from './WsApiConnection.ts'
|
|
3
|
+
export * from './WsApiRequestHandler.ts'
|
|
4
|
+
export * from './WsApiRoute.ts'
|
|
5
|
+
export * from './WsApiRouteHandler.ts'
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
RestApiRequest,
|
|
3
|
-
RestApiRoute,
|
|
4
|
-
RestPathParams,
|
|
5
|
-
RestQueryParams,
|
|
6
|
-
RestRequestBody,
|
|
7
|
-
RestRequestHeaders,
|
|
8
|
-
RestRequestHeadersWithAuthorization,
|
|
9
|
-
RestRequireAuthentication,
|
|
10
|
-
RestResponseBody,
|
|
11
|
-
} from '../types'
|
|
12
|
-
import type {OmitNever} from '../types/internal'
|
|
13
|
-
|
|
14
|
-
export async function fetchRestApi<
|
|
15
|
-
PathParams extends RestPathParams,
|
|
16
|
-
QueryParams extends RestQueryParams,
|
|
17
|
-
RequestHeaders extends RestRequestHeaders,
|
|
18
|
-
RequestBody extends RestRequestBody,
|
|
19
|
-
ResponseBody extends RestResponseBody,
|
|
20
|
-
RequireAuthentication extends RestRequireAuthentication,
|
|
21
|
-
>(
|
|
22
|
-
restApiUrl: string,
|
|
23
|
-
route: RestApiRoute<PathParams, QueryParams, RequestHeaders, RequestBody, ResponseBody, RequireAuthentication>,
|
|
24
|
-
request: OmitNever<
|
|
25
|
-
RestApiRequest<
|
|
26
|
-
PathParams,
|
|
27
|
-
QueryParams,
|
|
28
|
-
RestRequestHeadersWithAuthorization<RequestHeaders, RequireAuthentication>,
|
|
29
|
-
RequestBody
|
|
30
|
-
>
|
|
31
|
-
>,
|
|
32
|
-
): Promise<ResponseBody> {
|
|
33
|
-
const pathParams = 'pathParams' in request ? (request.pathParams as PathParams) : (undefined as never)
|
|
34
|
-
const queryParams = 'queryParams' in request ? (request.queryParams as QueryParams) : (undefined as never)
|
|
35
|
-
const headers = 'headers' in request ? (request.headers as RequestHeaders) : (undefined as never)
|
|
36
|
-
const body = 'body' in request ? (request.body as RequestBody) : (undefined as never)
|
|
37
|
-
|
|
38
|
-
const url = `${restApiUrl}${route.buildUrl(pathParams, queryParams)}`
|
|
39
|
-
const response = await fetch(url, {
|
|
40
|
-
method: route.method,
|
|
41
|
-
headers: headers,
|
|
42
|
-
body: body ? JSON.stringify(body) : null,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
if (!response.ok && response.status !== 404) throw `Request ${route.method} ${url} failed`
|
|
46
|
-
|
|
47
|
-
const text = (await response.text()).trim()
|
|
48
|
-
return response.ok && response.status !== 204 ? (text ? JSON.parse(text) : null) : null
|
|
49
|
-
}
|
package/src/rest/client/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './fetchRestApi'
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import type {APIGatewayProxyEvent, APIGatewayProxyResult} from 'aws-lambda'
|
|
2
|
-
import {URL} from 'node:url'
|
|
3
|
-
import {match} from 'path-to-regexp'
|
|
4
|
-
import type {
|
|
5
|
-
RestApiRouteHandler,
|
|
6
|
-
RestPathParams,
|
|
7
|
-
RestQueryParams,
|
|
8
|
-
RestRequestBody,
|
|
9
|
-
RestRequestHeaders,
|
|
10
|
-
RestRequireAuthentication,
|
|
11
|
-
RestResponseBody,
|
|
12
|
-
} from '../types'
|
|
13
|
-
|
|
14
|
-
const DEFAULT_RESPONSE_HEADERS = {
|
|
15
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
16
|
-
'Access-Control-Allow-Origin': '*',
|
|
17
|
-
'Access-Control-Allow-Methods': '*',
|
|
18
|
-
'Access-Control-Allow-Headers': '*',
|
|
19
|
-
} as const
|
|
20
|
-
|
|
21
|
-
export type AnyRestApiRouteHandler = RestApiRouteHandler<
|
|
22
|
-
RestPathParams,
|
|
23
|
-
RestQueryParams,
|
|
24
|
-
RestRequestHeaders,
|
|
25
|
-
RestRequestBody,
|
|
26
|
-
RestResponseBody,
|
|
27
|
-
RestRequireAuthentication
|
|
28
|
-
>
|
|
29
|
-
|
|
30
|
-
type MatchingRouteHandler = {
|
|
31
|
-
routeHandler: AnyRestApiRouteHandler
|
|
32
|
-
pathParams: RestPathParams
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class RestApi {
|
|
36
|
-
private routeHandlers: AnyRestApiRouteHandler[] = []
|
|
37
|
-
|
|
38
|
-
addRouteHandler<
|
|
39
|
-
PathParams extends RestPathParams,
|
|
40
|
-
QueryParams extends RestQueryParams,
|
|
41
|
-
RequestHeaders extends RestRequestHeaders,
|
|
42
|
-
RequestBody extends RestRequestBody,
|
|
43
|
-
ResponseBody extends RestResponseBody,
|
|
44
|
-
RequireAuthentication extends RestRequireAuthentication,
|
|
45
|
-
>(
|
|
46
|
-
routeHandler: RestApiRouteHandler<
|
|
47
|
-
PathParams,
|
|
48
|
-
QueryParams,
|
|
49
|
-
RequestHeaders,
|
|
50
|
-
RequestBody,
|
|
51
|
-
ResponseBody,
|
|
52
|
-
RequireAuthentication
|
|
53
|
-
>,
|
|
54
|
-
) {
|
|
55
|
-
this.routeHandlers = [...this.routeHandlers, routeHandler as unknown as AnyRestApiRouteHandler]
|
|
56
|
-
return this
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async handleRequest(
|
|
60
|
-
proxyEvent: APIGatewayProxyEvent,
|
|
61
|
-
onError?: (error: unknown) => Promise<void>,
|
|
62
|
-
): Promise<APIGatewayProxyResult> {
|
|
63
|
-
try {
|
|
64
|
-
const url = new URL(`${proxyEvent.headers?.['origin'] || 'https://no-origin'}${proxyEvent.path}`)
|
|
65
|
-
const routeHandler = this.getMatchingRouteHandler(url, proxyEvent.httpMethod)
|
|
66
|
-
if (!routeHandler && proxyEvent.httpMethod === 'OPTIONS') {
|
|
67
|
-
const allowedHttpMethods = this.getMatchingRouteHandlers(url).map(({routeHandler}) => routeHandler.route.method)
|
|
68
|
-
return {
|
|
69
|
-
statusCode: 200,
|
|
70
|
-
body: '',
|
|
71
|
-
headers: {
|
|
72
|
-
...DEFAULT_RESPONSE_HEADERS,
|
|
73
|
-
'Access-Control-Allow-Methods': allowedHttpMethods.join(','),
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (!routeHandler)
|
|
79
|
-
return {
|
|
80
|
-
statusCode: 405,
|
|
81
|
-
headers: DEFAULT_RESPONSE_HEADERS,
|
|
82
|
-
body: `Method ${proxyEvent.httpMethod} not allowed on ${proxyEvent.path}`,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const {
|
|
86
|
-
routeHandler: {requestHandler, route},
|
|
87
|
-
pathParams,
|
|
88
|
-
} = routeHandler
|
|
89
|
-
const userId = proxyEvent.requestContext.authorizer?.principalId
|
|
90
|
-
if (route.requireAuthentication && !userId) return {statusCode: 401, body: ''}
|
|
91
|
-
|
|
92
|
-
const responseBody = await requestHandler({
|
|
93
|
-
pathParams,
|
|
94
|
-
queryParams: this.extractQueryParams(proxyEvent),
|
|
95
|
-
headers: proxyEvent.headers as Record<string, string>,
|
|
96
|
-
body: proxyEvent.body && JSON.parse(proxyEvent.body),
|
|
97
|
-
userId,
|
|
98
|
-
})
|
|
99
|
-
return {
|
|
100
|
-
statusCode: responseBody.status,
|
|
101
|
-
body: responseBody.body ? JSON.stringify(responseBody.body) : '',
|
|
102
|
-
headers: {
|
|
103
|
-
...DEFAULT_RESPONSE_HEADERS,
|
|
104
|
-
...(responseBody.headers || {}),
|
|
105
|
-
},
|
|
106
|
-
}
|
|
107
|
-
} catch (error) {
|
|
108
|
-
if (onError) await onError(error)
|
|
109
|
-
|
|
110
|
-
console.error(error)
|
|
111
|
-
return {
|
|
112
|
-
statusCode: 500,
|
|
113
|
-
headers: DEFAULT_RESPONSE_HEADERS,
|
|
114
|
-
body: JSON.stringify({error}),
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
private getMatchingRouteHandlers(url: URL): MatchingRouteHandler[] {
|
|
120
|
-
return this.routeHandlers.reduce((matchingRouteHandlers, routeHandler) => {
|
|
121
|
-
const pathParams = this.extractPathParams(routeHandler, url)
|
|
122
|
-
const matchingRouteHandler: MatchingRouteHandler = {
|
|
123
|
-
routeHandler,
|
|
124
|
-
pathParams,
|
|
125
|
-
}
|
|
126
|
-
return pathParams ? [...matchingRouteHandlers, matchingRouteHandler] : matchingRouteHandlers
|
|
127
|
-
}, [] as MatchingRouteHandler[])
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private getMatchingRouteHandler(url: URL, httpMethod: string): MatchingRouteHandler | undefined {
|
|
131
|
-
return this.getMatchingRouteHandlers(url).find(({routeHandler}) => routeHandler.route.method === httpMethod)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private extractPathParams(routeHandler: AnyRestApiRouteHandler, url: URL): object | never {
|
|
135
|
-
const matcher = match(routeHandler.route.path, {decode: decodeURIComponent})
|
|
136
|
-
return (matcher(url.pathname) || null)?.params as object | never
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private extractQueryParams(proxyEvent: APIGatewayProxyEvent): object {
|
|
140
|
-
const keys = new Set([
|
|
141
|
-
...Object.keys(proxyEvent.queryStringParameters || {}),
|
|
142
|
-
...Object.keys(proxyEvent.multiValueQueryStringParameters || {}),
|
|
143
|
-
])
|
|
144
|
-
|
|
145
|
-
const queryParams: {[key: string]: string | string[] | undefined} = {}
|
|
146
|
-
for (const key of keys.values()) {
|
|
147
|
-
const singleValue = proxyEvent.queryStringParameters?.[key]
|
|
148
|
-
const multiValues = proxyEvent.multiValueQueryStringParameters?.[key] || []
|
|
149
|
-
queryParams[key] = multiValues.length > 1 ? multiValues : singleValue
|
|
150
|
-
}
|
|
151
|
-
return queryParams
|
|
152
|
-
}
|
|
153
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
RestApiRequestHandler,
|
|
3
|
-
RestApiRoute,
|
|
4
|
-
RestApiRouteHandler,
|
|
5
|
-
RestPathParams,
|
|
6
|
-
RestQueryParams,
|
|
7
|
-
RestRequestBody,
|
|
8
|
-
RestRequestHeaders,
|
|
9
|
-
RestRequireAuthentication,
|
|
10
|
-
RestResponseBody,
|
|
11
|
-
} from '../types'
|
|
12
|
-
|
|
13
|
-
export const buildRestApiRouteHandler = <
|
|
14
|
-
PathParams extends RestPathParams = never,
|
|
15
|
-
QueryParams extends RestQueryParams = never,
|
|
16
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
17
|
-
RequestBody extends RestRequestBody = never,
|
|
18
|
-
ResponseBody extends RestResponseBody = never,
|
|
19
|
-
RequireAuthentication extends RestRequireAuthentication = false,
|
|
20
|
-
>(
|
|
21
|
-
route: RestApiRoute<PathParams, QueryParams, RequestHeaders, RequestBody, ResponseBody, RequireAuthentication>,
|
|
22
|
-
requestHandler: RestApiRequestHandler<
|
|
23
|
-
PathParams,
|
|
24
|
-
QueryParams,
|
|
25
|
-
RequestHeaders,
|
|
26
|
-
RequestBody,
|
|
27
|
-
ResponseBody,
|
|
28
|
-
RequireAuthentication
|
|
29
|
-
>,
|
|
30
|
-
): RestApiRouteHandler<PathParams, QueryParams, RequestHeaders, RequestBody, ResponseBody, RequireAuthentication> => ({
|
|
31
|
-
route,
|
|
32
|
-
requestHandler,
|
|
33
|
-
})
|
package/src/rest/server/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type {RestPathParams, RestQueryParams, RestRequestBody, RestRequestHeaders} from './common'
|
|
2
|
-
import type {OmitNever} from './internal'
|
|
3
|
-
|
|
4
|
-
export type RestApiRequest<
|
|
5
|
-
PathParams extends RestPathParams = never,
|
|
6
|
-
QueryParams extends RestQueryParams = never,
|
|
7
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
8
|
-
RequestBody extends RestRequestBody = never,
|
|
9
|
-
> = OmitNever<{
|
|
10
|
-
pathParams: PathParams
|
|
11
|
-
queryParams: QueryParams
|
|
12
|
-
headers: RequestHeaders
|
|
13
|
-
body: RequestBody
|
|
14
|
-
}>
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type {RestApiResponse} from './RestApiResponse'
|
|
2
|
-
import type {
|
|
3
|
-
RestPathParams,
|
|
4
|
-
RestQueryParams,
|
|
5
|
-
RestRequestBody,
|
|
6
|
-
RestRequestHeaders,
|
|
7
|
-
RestRequireAuthentication,
|
|
8
|
-
RestResponseBody,
|
|
9
|
-
} from './common'
|
|
10
|
-
import type {InternalRestApiRequest} from './internal'
|
|
11
|
-
|
|
12
|
-
export type RestApiRequestHandler<
|
|
13
|
-
PathParams extends RestPathParams = never,
|
|
14
|
-
QueryParams extends RestQueryParams = never,
|
|
15
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
16
|
-
RequestBody extends RestRequestBody = never,
|
|
17
|
-
ResponseBody extends RestResponseBody = never,
|
|
18
|
-
RequireAuthentication extends RestRequireAuthentication = false,
|
|
19
|
-
> = (
|
|
20
|
-
request: InternalRestApiRequest<PathParams, QueryParams, RequestHeaders, RequestBody, RequireAuthentication>,
|
|
21
|
-
) => Promise<RestApiResponse<ResponseBody>>
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type {RestApiRequestHandler} from './RestApiRequestHandler'
|
|
2
|
-
import type {RestPathParams, RestQueryParams, RestRequestBody, RestRequestHeaders, RestResponseBody} from './common'
|
|
3
|
-
|
|
4
|
-
export type RestApiRequestHandlerWithAuthentication<
|
|
5
|
-
PathParams extends RestPathParams = never,
|
|
6
|
-
QueryParams extends RestQueryParams = never,
|
|
7
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
8
|
-
RequestBody extends RestRequestBody = never,
|
|
9
|
-
ResponseBody extends RestResponseBody = never,
|
|
10
|
-
> = RestApiRequestHandler<PathParams, QueryParams, RequestHeaders, RequestBody, ResponseBody, true>
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
RestPathParams,
|
|
3
|
-
RestQueryParams,
|
|
4
|
-
RestRequestBody,
|
|
5
|
-
RestRequestHeaders,
|
|
6
|
-
RestRequireAuthentication,
|
|
7
|
-
RestResponseBody,
|
|
8
|
-
} from './common'
|
|
9
|
-
|
|
10
|
-
export type RestHttpMethod = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'PUT' | 'OPTIONS'
|
|
11
|
-
export type RestRoutePath = `/${string}`
|
|
12
|
-
|
|
13
|
-
export type RestApiRoute<
|
|
14
|
-
PathParams extends RestPathParams = never,
|
|
15
|
-
QueryParams extends RestQueryParams = never,
|
|
16
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
17
|
-
RequestBody extends RestRequestBody = never,
|
|
18
|
-
ResponseBody extends RestResponseBody = never,
|
|
19
|
-
RequireAuthentication extends RestRequireAuthentication = false,
|
|
20
|
-
> = {
|
|
21
|
-
method: RestHttpMethod
|
|
22
|
-
path: RestRoutePath
|
|
23
|
-
buildUrl: (pathParams: PathParams, queryParams: QueryParams) => RestRoutePath
|
|
24
|
-
requireAuthentication: RequireAuthentication
|
|
25
|
-
|
|
26
|
-
// this is a workaround to force optional generics to be typesafe
|
|
27
|
-
__types?: {
|
|
28
|
-
RequestHeaders: RequestHeaders
|
|
29
|
-
RequestBody: RequestBody
|
|
30
|
-
ResponseBody: ResponseBody
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type {RestApiRequestHandler} from './RestApiRequestHandler'
|
|
2
|
-
import type {RestApiRoute} from './RestApiRoute'
|
|
3
|
-
import type {
|
|
4
|
-
RestPathParams,
|
|
5
|
-
RestQueryParams,
|
|
6
|
-
RestRequestBody,
|
|
7
|
-
RestRequestHeaders,
|
|
8
|
-
RestRequireAuthentication,
|
|
9
|
-
RestResponseBody,
|
|
10
|
-
} from './common'
|
|
11
|
-
|
|
12
|
-
export interface RestApiRouteHandler<
|
|
13
|
-
PathParams extends RestPathParams = never,
|
|
14
|
-
QueryParams extends RestQueryParams = never,
|
|
15
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
16
|
-
RequestBody extends RestRequestBody = never,
|
|
17
|
-
ResponseBody extends RestResponseBody = never,
|
|
18
|
-
RequireAuthentication extends RestRequireAuthentication = false,
|
|
19
|
-
> {
|
|
20
|
-
route: RestApiRoute<PathParams, QueryParams, RequestHeaders, RequestBody, ResponseBody, RequireAuthentication>
|
|
21
|
-
requestHandler: RestApiRequestHandler<
|
|
22
|
-
PathParams,
|
|
23
|
-
QueryParams,
|
|
24
|
-
RequestHeaders,
|
|
25
|
-
RequestBody,
|
|
26
|
-
ResponseBody,
|
|
27
|
-
RequireAuthentication
|
|
28
|
-
>
|
|
29
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type {RestApiRoute} from './RestApiRoute'
|
|
2
|
-
import type {RestPathParams, RestQueryParams, RestRequestBody, RestRequestHeaders, RestResponseBody} from './common'
|
|
3
|
-
|
|
4
|
-
export type RestApiRouteWithAuthentication<
|
|
5
|
-
PathParams extends RestPathParams = never,
|
|
6
|
-
QueryParams extends RestQueryParams = never,
|
|
7
|
-
RequestHeaders extends RestRequestHeaders = never,
|
|
8
|
-
RequestBody extends RestRequestBody = never,
|
|
9
|
-
ResponseBody extends RestResponseBody = never,
|
|
10
|
-
> = RestApiRoute<PathParams, QueryParams, RequestHeaders, RequestBody, ResponseBody, true>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type BearerToken = `Bearer ${string}`
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type RestPathParams = object | never
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type RestQueryParams = object | never
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type RestRequestBody = object | never
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type RestRequestHeaders = Record<string, string> | never
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type {BearerToken} from './BearerToken'
|
|
2
|
-
import type {RestRequestHeaders} from './RestRequestHeaders'
|
|
3
|
-
import type {RestRequireAuthentication} from './RestRequireAuthentication'
|
|
4
|
-
|
|
5
|
-
export type AuthorizationHeader = {Authorization: BearerToken}
|
|
6
|
-
export type RestRequestHeadersWithAuthorization<
|
|
7
|
-
RequestHeaders extends RestRequestHeaders,
|
|
8
|
-
RequireAuthentication extends RestRequireAuthentication,
|
|
9
|
-
> = RequireAuthentication extends true
|
|
10
|
-
? [RequestHeaders] extends [never]
|
|
11
|
-
? AuthorizationHeader
|
|
12
|
-
: RequestHeaders & AuthorizationHeader
|
|
13
|
-
: RequestHeaders
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type RestRequireAuthentication = boolean
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type RestResponseBody = object | object[] | null | never
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export * from './BearerToken'
|
|
2
|
-
export * from './RestRequestHeadersWithAuthorization'
|
|
3
|
-
export * from './RestPathParams'
|
|
4
|
-
export * from './RestQueryParams'
|
|
5
|
-
export * from './RestRequestBody'
|
|
6
|
-
export * from './RestRequestHeaders'
|
|
7
|
-
export * from './RestRequireAuthentication'
|
|
8
|
-
export * from './RestResponseBody'
|
package/src/rest/types/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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,21 +0,0 @@
|
|
|
1
|
-
import type {OmitNever} from '../../../types/OmitNever'
|
|
2
|
-
import type {RestApiRequest} from '../RestApiRequest'
|
|
3
|
-
import type {
|
|
4
|
-
RestPathParams,
|
|
5
|
-
RestQueryParams,
|
|
6
|
-
RestRequestBody,
|
|
7
|
-
RestRequestHeaders,
|
|
8
|
-
RestRequireAuthentication,
|
|
9
|
-
} from '../common'
|
|
10
|
-
|
|
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,
|
|
17
|
-
> = OmitNever<
|
|
18
|
-
RestApiRequest<PathParams, QueryParams, RequestHeaders, RequestBody> & {
|
|
19
|
-
userId: RequireAuthentication extends true ? string : never
|
|
20
|
-
}
|
|
21
|
-
>
|
package/src/ws/client/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './useWsApi'
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import {useCallback, useMemo, useRef, useState} from 'react'
|
|
2
|
-
import type {WsApiAction, WsApiRequest, WsApiResponse, WsApiRoute} from '../types'
|
|
3
|
-
|
|
4
|
-
type WsApiConnection<Action extends WsApiAction, Request extends WsApiRequest<Action>> = {
|
|
5
|
-
disconnect: () => void
|
|
6
|
-
sendRequest: (request: Request) => void
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type WsApiOptions<
|
|
10
|
-
Action extends WsApiAction,
|
|
11
|
-
Request extends WsApiRequest<Action>,
|
|
12
|
-
Response extends WsApiResponse,
|
|
13
|
-
> = {
|
|
14
|
-
onConnected?: (connection: WsApiConnection<Action, Request>) => void
|
|
15
|
-
onResponse?: (response: Response, connection: WsApiConnection<Action, Request>) => void
|
|
16
|
-
onError?: (error: unknown, connection: WsApiConnection<Action, Request>) => void
|
|
17
|
-
onDisconnected?: () => void
|
|
18
|
-
}
|
|
19
|
-
export const useWsApi = <
|
|
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>(undefined)
|
|
30
|
-
|
|
31
|
-
const sendRequest = useCallback(
|
|
32
|
-
(request: Omit<Request, 'action'>) => {
|
|
33
|
-
if (!route) throw 'Not route'
|
|
34
|
-
if (!webSocket.current) throw 'Not connected'
|
|
35
|
-
|
|
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(), [])
|
|
46
|
-
const connection: WsApiConnection<Action, Request> = useMemo(() => {
|
|
47
|
-
return {
|
|
48
|
-
disconnect,
|
|
49
|
-
sendRequest,
|
|
50
|
-
}
|
|
51
|
-
}, [disconnect, sendRequest])
|
|
52
|
-
|
|
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'
|
|
61
|
-
|
|
62
|
-
webSocket.current = new WebSocket(wsApiUrl)
|
|
63
|
-
|
|
64
|
-
webSocket.current.onopen = () => {
|
|
65
|
-
setConnected(true)
|
|
66
|
-
setError(false)
|
|
67
|
-
options?.onConnected?.(connection)
|
|
68
|
-
}
|
|
69
|
-
|
|
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
|
-
}
|
|
75
|
-
|
|
76
|
-
webSocket.current.onerror = (error) => {
|
|
77
|
-
setError(false)
|
|
78
|
-
options?.onError?.(error, connection)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
webSocket.current.onclose = () => {
|
|
82
|
-
setConnected(false)
|
|
83
|
-
options?.onDisconnected?.()
|
|
84
|
-
webSocket.current = undefined
|
|
85
|
-
resolve()
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
},
|
|
89
|
-
[wsApiUrl, connection],
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
connect,
|
|
94
|
-
connected,
|
|
95
|
-
connection,
|
|
96
|
-
error,
|
|
97
|
-
disconnect,
|
|
98
|
-
}
|
|
99
|
-
}
|