ts-ag 0.0.1-dev.2 → 1.0.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/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -0
- package/dist/cognito/client.d.ts +6 -0
- package/dist/cognito/client.d.ts.map +1 -0
- package/dist/cognito/client.js +10 -0
- package/dist/cognito/errors.d.ts +95 -0
- package/dist/cognito/errors.d.ts.map +1 -0
- package/dist/cognito/errors.js +128 -0
- package/dist/cognito/index.d.ts +5 -0
- package/dist/cognito/index.d.ts.map +1 -0
- package/dist/cognito/index.js +4 -0
- package/dist/cognito/password.d.ts +53 -0
- package/dist/cognito/password.d.ts.map +1 -0
- package/dist/cognito/password.js +161 -0
- package/dist/cognito/user.d.ts +14 -0
- package/dist/cognito/user.d.ts.map +1 -0
- package/dist/cognito/user.js +34 -0
- package/dist/dynamo/errors.d.ts +9 -0
- package/dist/dynamo/errors.d.ts.map +1 -0
- package/dist/dynamo/errors.js +7 -0
- package/dist/dynamo/index.d.ts +2 -0
- package/dist/dynamo/index.d.ts.map +1 -0
- package/dist/dynamo/index.js +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/lambda/authentication.d.ts +2 -0
- package/dist/lambda/authentication.d.ts.map +1 -0
- package/dist/lambda/authentication.js +1 -0
- package/dist/lambda/browser.d.ts +2 -0
- package/dist/lambda/browser.d.ts.map +1 -1
- package/dist/lambda/browser.js +2 -0
- package/dist/lambda/client-types.d.ts +19 -19
- package/dist/lambda/client-types.d.ts.map +1 -1
- package/dist/lambda/client-types.js +9 -1
- package/dist/lambda/client.d.ts.map +1 -1
- package/dist/lambda/client.js +7 -2
- package/dist/lambda/errors.d.ts +19 -19
- package/dist/lambda/errors.d.ts.map +1 -1
- package/dist/lambda/errors.js +12 -12
- package/dist/lambda/handlerUtils.d.ts +8 -3
- package/dist/lambda/handlerUtils.d.ts.map +1 -1
- package/dist/lambda/handlerUtils.js +3 -2
- package/dist/lambda/index.d.ts +1 -0
- package/dist/lambda/index.d.ts.map +1 -1
- package/dist/lambda/index.js +2 -0
- package/dist/lambda/response.d.ts +48 -95
- package/dist/lambda/response.d.ts.map +1 -1
- package/dist/lambda/response.js +42 -18
- package/dist/lambda/server/authentication.d.ts +10 -0
- package/dist/lambda/server/authentication.d.ts.map +1 -0
- package/dist/lambda/server/authentication.js +24 -0
- package/dist/s3/client.d.ts +6 -0
- package/dist/s3/client.d.ts.map +1 -0
- package/dist/s3/client.js +7 -0
- package/dist/s3/errors.d.ts +7 -0
- package/dist/s3/errors.d.ts.map +1 -0
- package/dist/s3/errors.js +6 -0
- package/dist/s3/index.d.ts +5 -0
- package/dist/s3/index.d.ts.map +1 -0
- package/dist/s3/index.js +4 -0
- package/dist/s3/object.d.ts +17 -0
- package/dist/s3/object.d.ts.map +1 -0
- package/dist/s3/object.js +32 -0
- package/dist/s3/signedUrl.d.ts +3 -0
- package/dist/s3/signedUrl.d.ts.map +1 -0
- package/dist/s3/signedUrl.js +3 -0
- package/dist/scripts/clean.js +30 -59
- package/dist/scripts/ts-alias.d.ts +3 -0
- package/dist/scripts/ts-alias.d.ts.map +1 -0
- package/dist/scripts/ts-alias.js +167 -0
- package/dist/ses/errors.d.ts +4 -0
- package/dist/ses/errors.d.ts.map +1 -0
- package/dist/ses/errors.js +3 -0
- package/dist/ses/index.d.ts +2 -0
- package/dist/ses/index.d.ts.map +1 -0
- package/dist/ses/index.js +1 -0
- package/dist/ts-alias.js +0 -0
- package/dist/types/deep.d.ts +24 -0
- package/dist/types/deep.d.ts.map +1 -0
- package/dist/types/deep.js +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/safe.d.ts +2 -0
- package/dist/types/safe.d.ts.map +1 -0
- package/dist/types/safe.js +1 -0
- package/dist/utils/fs.d.ts +2 -1
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +3 -1
- package/package.json +31 -22
- package/src/browser.ts +3 -1
- package/src/cognito/client.ts +11 -0
- package/src/cognito/errors.ts +175 -0
- package/src/cognito/index.ts +5 -0
- package/src/cognito/password.ts +233 -0
- package/src/cognito/user.ts +46 -0
- package/src/dynamo/errors.ts +11 -0
- package/src/dynamo/index.ts +1 -0
- package/src/index.ts +8 -0
- package/src/lambda/browser.ts +2 -0
- package/src/lambda/client-types.ts +55 -29
- package/src/lambda/client.ts +7 -2
- package/src/lambda/errors.ts +25 -25
- package/src/lambda/handlerUtils.ts +30 -25
- package/src/lambda/index.ts +3 -0
- package/src/lambda/response.ts +90 -26
- package/src/lambda/server/authentication.ts +32 -0
- package/src/s3/client.ts +8 -0
- package/src/s3/errors.ts +6 -0
- package/src/s3/index.ts +6 -0
- package/src/s3/object.ts +37 -0
- package/src/s3/signedUrl.ts +4 -0
- package/src/scripts/clean.ts +34 -68
- package/src/ses/errors.ts +3 -0
- package/src/ses/index.ts +1 -0
- package/src/types/deep.ts +43 -0
- package/src/types/index.ts +2 -0
- package/src/types/safe.ts +1 -0
- package/src/utils/fs.ts +4 -2
- /package/src/{ts-alias.ts → scripts/ts-alias.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ErrorBody, SuccessCode, ErrorCode } from
|
|
1
|
+
import type { ErrorBody, SuccessCode, ErrorCode } from "./handlerUtils.js";
|
|
2
2
|
|
|
3
3
|
// ----------------- Helpers ----------------------
|
|
4
4
|
// Used to easily extract types from ApiEndpoints types
|
|
@@ -9,10 +9,11 @@ import type { ErrorBody, SuccessCode, ErrorCode } from './handlerUtils.js';
|
|
|
9
9
|
* @template P - Path string literal type (e.g. 'payments/account')
|
|
10
10
|
* @template M - HTTP method string literal type (e.g. 'GET', 'POST')
|
|
11
11
|
*/
|
|
12
|
-
export type ApiInput<
|
|
13
|
-
E,
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export type ApiInput<
|
|
13
|
+
E extends ApiEndpoints,
|
|
14
|
+
P extends E["path"],
|
|
15
|
+
M extends E["method"]
|
|
16
|
+
> = Extract<E, { path: P; method: M }>["requestInput"];
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Extracts the requestOutput type from an API endpoint definition
|
|
@@ -20,10 +21,11 @@ export type ApiInput<E extends ApiEndpoints, P extends E['path'], M extends E['m
|
|
|
20
21
|
* @template P - Path string literal type (e.g. 'payments/account')
|
|
21
22
|
* @template M - HTTP method string literal type (e.g. 'GET', 'POST')
|
|
22
23
|
*/
|
|
23
|
-
export type ApiOutput<
|
|
24
|
-
E,
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
export type ApiOutput<
|
|
25
|
+
E extends ApiEndpoints,
|
|
26
|
+
P extends E["path"],
|
|
27
|
+
M extends E["method"]
|
|
28
|
+
> = Extract<E, { path: P; method: M }>["requestOutput"];
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Extracts the response type from an API endpoint definition
|
|
@@ -31,10 +33,11 @@ export type ApiOutput<E extends ApiEndpoints, P extends E['path'], M extends E['
|
|
|
31
33
|
* @template P - Path string literal type (e.g. 'payments/account')
|
|
32
34
|
* @template M - HTTP method string literal type (e.g. 'GET', 'POST')
|
|
33
35
|
*/
|
|
34
|
-
export type ApiResponse<
|
|
35
|
-
E,
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
export type ApiResponse<
|
|
37
|
+
E extends ApiEndpoints,
|
|
38
|
+
P extends E["path"],
|
|
39
|
+
M extends E["method"]
|
|
40
|
+
> = Extract<E, { path: P; method: M }>["response"];
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
43
|
* Extracts the sucessful body type from an API endpoint definition
|
|
@@ -42,10 +45,14 @@ export type ApiResponse<E extends ApiEndpoints, P extends E['path'], M extends E
|
|
|
42
45
|
* @template P - Path string literal type (e.g. 'payments/account')
|
|
43
46
|
* @template M - HTTP method string literal type (e.g. 'GET', 'POST')
|
|
44
47
|
*/
|
|
45
|
-
export type ApiSuccessBody<
|
|
46
|
-
|
|
48
|
+
export type ApiSuccessBody<
|
|
49
|
+
E extends ApiEndpoints,
|
|
50
|
+
P extends E["path"],
|
|
51
|
+
M extends E["method"]
|
|
52
|
+
> = Extract<
|
|
53
|
+
Extract<E, { path: P; method: M }>["response"],
|
|
47
54
|
{ status: SuccessCode }
|
|
48
|
-
>[
|
|
55
|
+
>["json"] extends () => Promise<infer R>
|
|
49
56
|
? R
|
|
50
57
|
: unknown;
|
|
51
58
|
|
|
@@ -55,40 +62,59 @@ export type ApiSuccessBody<E extends ApiEndpoints, P extends E['path'], M extend
|
|
|
55
62
|
* @template P - Path string literal type (e.g. 'payments/account')
|
|
56
63
|
* @template M - HTTP method string literal type (e.g. 'GET', 'POST')
|
|
57
64
|
*/
|
|
58
|
-
export type ApiErrorBody<
|
|
59
|
-
|
|
65
|
+
export type ApiErrorBody<
|
|
66
|
+
E extends ApiEndpoints,
|
|
67
|
+
P extends E["path"],
|
|
68
|
+
M extends E["method"]
|
|
69
|
+
> = Extract<
|
|
70
|
+
Extract<E, { path: P; method: M }>["response"],
|
|
60
71
|
{ status: ErrorCode }
|
|
61
|
-
>[
|
|
72
|
+
>["json"] extends () => Promise<infer R>
|
|
62
73
|
? R
|
|
63
74
|
: unknown;
|
|
64
75
|
|
|
65
76
|
/**
|
|
66
77
|
* Converts a RawApiGatewayHandler response type to a fetch like response type.
|
|
67
78
|
*/
|
|
68
|
-
type ConvertToFetch<T> = T extends {
|
|
79
|
+
type ConvertToFetch<T> = T extends {
|
|
80
|
+
statusCode: number;
|
|
81
|
+
body: object;
|
|
82
|
+
headers: object;
|
|
83
|
+
}
|
|
69
84
|
? {
|
|
70
|
-
ok: T[
|
|
71
|
-
json: () => Promise<T[
|
|
72
|
-
status: T[
|
|
85
|
+
ok: T["statusCode"] extends SuccessCode ? true : false;
|
|
86
|
+
json: () => Promise<T["body"]>;
|
|
87
|
+
status: T["statusCode"];
|
|
73
88
|
}
|
|
74
89
|
: T;
|
|
75
90
|
|
|
76
|
-
export type CleanResponse = Omit<Response,
|
|
77
|
-
export type FetchResponse<T extends (...args: any) => any> = ConvertToFetch<
|
|
91
|
+
export type CleanResponse = Omit<Response, "status" | "ok" | "json">;
|
|
92
|
+
export type FetchResponse<T extends (...args: any) => any> = ConvertToFetch<
|
|
93
|
+
Awaited<ReturnType<T>>
|
|
94
|
+
> &
|
|
95
|
+
CleanResponse;
|
|
78
96
|
|
|
79
97
|
// ------------------------ Proper types ------------------
|
|
80
98
|
// This is used by createApiRequest and createFormFunction
|
|
81
99
|
|
|
82
|
-
export const HTTPMethods = [
|
|
100
|
+
export const HTTPMethods = [
|
|
101
|
+
"GET",
|
|
102
|
+
"POST",
|
|
103
|
+
"PUT",
|
|
104
|
+
"DELETE",
|
|
105
|
+
"PATCH",
|
|
106
|
+
"OPTIONS",
|
|
107
|
+
"HEAD",
|
|
108
|
+
] as const;
|
|
83
109
|
export type HTTPMethod = (typeof HTTPMethods)[number];
|
|
84
110
|
|
|
85
111
|
export type ApiEndpoints = {
|
|
86
112
|
path: string;
|
|
87
113
|
method: HTTPMethod;
|
|
88
|
-
requestInput: Record<string, any
|
|
89
|
-
requestOutput: object;
|
|
114
|
+
requestInput: Record<string, any> | null;
|
|
115
|
+
requestOutput: object | null;
|
|
90
116
|
response: FetchResponse<
|
|
91
|
-
// This means we get better types
|
|
117
|
+
// This means we get better types
|
|
92
118
|
() => Promise<
|
|
93
119
|
| {
|
|
94
120
|
headers: object;
|
package/src/lambda/client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { deserialize } from './deserializer.js';
|
|
1
|
+
// import { deserialize } from './deserializer.js';
|
|
2
|
+
import { parse } from 'devalue';
|
|
2
3
|
import * as v from 'valibot';
|
|
3
4
|
import type { ApiEndpoints, ApiInput, ApiResponse } from './client-types.js';
|
|
4
5
|
|
|
@@ -50,8 +51,12 @@ async function _apiRequest<T = Response>(
|
|
|
50
51
|
credentials: 'include'
|
|
51
52
|
});
|
|
52
53
|
|
|
54
|
+
let retrieved: Promise<string> | false = false;
|
|
53
55
|
response.json = async () => {
|
|
54
|
-
|
|
56
|
+
if (retrieved === false) {
|
|
57
|
+
retrieved = response.text();
|
|
58
|
+
}
|
|
59
|
+
return await parse(await retrieved);
|
|
55
60
|
};
|
|
56
61
|
return response as unknown as T;
|
|
57
62
|
}
|
package/src/lambda/errors.ts
CHANGED
|
@@ -7,52 +7,52 @@
|
|
|
7
7
|
* Im not sure it this is optimal behaviour and if not we will migrate to only using the errorResponse function
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export const
|
|
11
|
-
type: '
|
|
10
|
+
export const error_lambda_badRequest = (message: string, fieldName?: string, fieldValue?: string) => ({
|
|
11
|
+
type: 'lambda_badRequest' as const,
|
|
12
12
|
message,
|
|
13
13
|
fieldName,
|
|
14
14
|
fieldValue
|
|
15
15
|
});
|
|
16
|
-
export const
|
|
17
|
-
type: '
|
|
16
|
+
export const error_lambda_unauthorized = (message: string) => ({
|
|
17
|
+
type: 'lambda_unauthorized' as const,
|
|
18
18
|
message
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
export const
|
|
22
|
-
type: '
|
|
21
|
+
export const error_lambda_forbidden = (message: string) => ({
|
|
22
|
+
type: 'lambda_forbidden' as const,
|
|
23
23
|
message
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
export const
|
|
27
|
-
type: '
|
|
26
|
+
export const error_lambda_notFound = (message: string, fieldName?: string, fieldValue?: string) => ({
|
|
27
|
+
type: 'lambda_notFound' as const,
|
|
28
28
|
message,
|
|
29
29
|
fieldName,
|
|
30
30
|
fieldValue
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
export const
|
|
34
|
-
type: '
|
|
33
|
+
export const error_lambda_conflict = (message: string, fieldName?: string, fieldValue?: string) => ({
|
|
34
|
+
type: 'lambda_conflict' as const,
|
|
35
35
|
message,
|
|
36
36
|
fieldName,
|
|
37
37
|
fieldValue
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
export const
|
|
41
|
-
type: '
|
|
40
|
+
export const error_lambda_internal = (message: string) => ({
|
|
41
|
+
type: 'lambda_internal' as const,
|
|
42
42
|
message
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
export type
|
|
46
|
-
export type
|
|
47
|
-
export type
|
|
48
|
-
export type
|
|
49
|
-
export type
|
|
50
|
-
export type
|
|
45
|
+
export type type_error_lambda_badRequest = ReturnType<typeof error_lambda_badRequest>;
|
|
46
|
+
export type type_error_lambda_unauthorized = ReturnType<typeof error_lambda_unauthorized>;
|
|
47
|
+
export type type_error_lambda_forbidden = ReturnType<typeof error_lambda_forbidden>;
|
|
48
|
+
export type type_error_lambda_notFound = ReturnType<typeof error_lambda_notFound>;
|
|
49
|
+
export type type_error_lambda_conflict = ReturnType<typeof error_lambda_conflict>;
|
|
50
|
+
export type type_error_lambda_internal = ReturnType<typeof error_lambda_internal>;
|
|
51
51
|
|
|
52
|
-
export type
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
52
|
+
export type type_error_lambda =
|
|
53
|
+
| type_error_lambda_badRequest
|
|
54
|
+
| type_error_lambda_unauthorized
|
|
55
|
+
| type_error_lambda_forbidden
|
|
56
|
+
| type_error_lambda_notFound
|
|
57
|
+
| type_error_lambda_conflict
|
|
58
|
+
| type_error_lambda_internal;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { APIGatewayProxyResultV2, Context } from 'aws-lambda';
|
|
2
|
-
import { stringify } from './serializer.js';
|
|
2
|
+
// import { stringify } from './serializer.js';
|
|
3
|
+
import { stringify } from 'devalue';
|
|
3
4
|
|
|
4
5
|
export type SuccessCode = 200 | 201 | 204;
|
|
5
6
|
export type ErrorCode = 400 | 401 | 403 | 404 | 409 | 500;
|
|
@@ -12,32 +13,36 @@ export type ErrorBody = {
|
|
|
12
13
|
};
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The error response for the lambda functions to return
|
|
18
|
+
*/
|
|
19
|
+
export type ErrorRawProxyResultV2 = {
|
|
20
|
+
statusCode: ErrorCode;
|
|
21
|
+
headers?:
|
|
22
|
+
| {
|
|
23
|
+
[header: string]: boolean | number | string;
|
|
24
|
+
}
|
|
25
|
+
| undefined;
|
|
26
|
+
body?: ErrorBody;
|
|
27
|
+
isBase64Encoded?: boolean | undefined;
|
|
28
|
+
cookies?: string[] | undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type OkRawProxyResultV2 = {
|
|
32
|
+
statusCode: SuccessCode;
|
|
33
|
+
headers?:
|
|
34
|
+
| {
|
|
35
|
+
[header: string]: boolean | number | string;
|
|
36
|
+
}
|
|
37
|
+
| undefined;
|
|
38
|
+
body?: object | undefined;
|
|
39
|
+
isBase64Encoded?: boolean | undefined;
|
|
40
|
+
cookies?: string[] | undefined;
|
|
41
|
+
};
|
|
15
42
|
/**
|
|
16
43
|
* A type for the raw proxy result - just using an object not a stirng for the body
|
|
17
44
|
*/
|
|
18
|
-
export type RawProxyResultV2 =
|
|
19
|
-
| {
|
|
20
|
-
statusCode: ErrorCode;
|
|
21
|
-
headers?:
|
|
22
|
-
| {
|
|
23
|
-
[header: string]: boolean | number | string;
|
|
24
|
-
}
|
|
25
|
-
| undefined;
|
|
26
|
-
body?: ErrorBody;
|
|
27
|
-
isBase64Encoded?: boolean | undefined;
|
|
28
|
-
cookies?: string[] | undefined;
|
|
29
|
-
}
|
|
30
|
-
| {
|
|
31
|
-
statusCode: SuccessCode;
|
|
32
|
-
headers?:
|
|
33
|
-
| {
|
|
34
|
-
[header: string]: boolean | number | string;
|
|
35
|
-
}
|
|
36
|
-
| undefined;
|
|
37
|
-
body?: object | undefined;
|
|
38
|
-
isBase64Encoded?: boolean | undefined;
|
|
39
|
-
cookies?: string[] | undefined;
|
|
40
|
-
};
|
|
45
|
+
export type RawProxyResultV2 = ErrorRawProxyResultV2 | OkRawProxyResultV2;
|
|
41
46
|
|
|
42
47
|
// The type of the handler returned from wrapHandler
|
|
43
48
|
export type APIGatewayHandler<E> = (event: E, context: Context) => Promise<APIGatewayProxyResultV2>;
|
|
@@ -64,7 +69,7 @@ export function wrapHandler<E>(handler: RawApiGatewayHandler<E>): APIGatewayHand
|
|
|
64
69
|
const result = await handler(event, context);
|
|
65
70
|
return {
|
|
66
71
|
...result,
|
|
67
|
-
body: result.body ?
|
|
72
|
+
body: result.body ? stringify(result.body) : undefined
|
|
68
73
|
};
|
|
69
74
|
};
|
|
70
75
|
}
|
package/src/lambda/index.ts
CHANGED
package/src/lambda/response.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { SafeParseResult } from 'valibot';
|
|
2
|
-
import {
|
|
1
|
+
import type { BaseIssue, SafeParseResult } from 'valibot';
|
|
2
|
+
import { error_lambda_badRequest, type type_error_lambda } from './errors.js';
|
|
3
|
+
import type { ErrorRawProxyResultV2, OkRawProxyResultV2 } from './handlerUtils.js';
|
|
3
4
|
|
|
4
5
|
function field(obj: { fieldName?: string; fieldValue?: string }) {
|
|
5
6
|
return obj.fieldName === undefined || obj.fieldValue === undefined
|
|
@@ -12,60 +13,104 @@ function field(obj: { fieldName?: string; fieldValue?: string }) {
|
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
export type type_error_response = Omit<ErrorRawProxyResultV2, 'headers' | 'body'> & {
|
|
17
|
+
headers: NonNullable<ErrorRawProxyResultV2['headers']>;
|
|
18
|
+
body: NonNullable<ErrorRawProxyResultV2['body']>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type LambdaErrorResponse<Type extends string = '', Extras extends object | never = never> =
|
|
22
|
+
| {
|
|
23
|
+
headers: Record<string, string>;
|
|
24
|
+
statusCode: 400;
|
|
25
|
+
body: { message: string; type: Type } & ReturnType<typeof field> & Extras;
|
|
26
|
+
}
|
|
27
|
+
| { headers: Record<string, string>; statusCode: 401; body: { message: string; type: Type } & Extras }
|
|
28
|
+
| { headers: Record<string, string>; statusCode: 403; body: { message: string; type: Type } & Extras }
|
|
29
|
+
| {
|
|
30
|
+
headers: Record<string, string>;
|
|
31
|
+
statusCode: 404;
|
|
32
|
+
body: { message: string; type: Type } & ReturnType<typeof field> & Extras;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
headers: Record<string, string>;
|
|
36
|
+
statusCode: 409;
|
|
37
|
+
body: { message: string; type: Type } & ReturnType<typeof field> & Extras;
|
|
38
|
+
}
|
|
39
|
+
| { headers: Record<string, string>; statusCode: 500; body: { message: string; type: Type } & Extras };
|
|
40
|
+
|
|
41
|
+
export function response_error<Type extends string, Extras extends object | never = never>(
|
|
42
|
+
e: type_error_lambda,
|
|
43
|
+
headers: Record<string, string>,
|
|
44
|
+
type: Type = '' as Type,
|
|
45
|
+
extras: Extras = {} as Extras
|
|
46
|
+
): LambdaErrorResponse<Type, Extras> {
|
|
19
47
|
switch (e.type) {
|
|
20
|
-
case '
|
|
48
|
+
case 'lambda_badRequest':
|
|
21
49
|
return {
|
|
22
50
|
headers,
|
|
23
|
-
statusCode: 400
|
|
51
|
+
statusCode: 400,
|
|
24
52
|
body: {
|
|
25
53
|
message: e.message,
|
|
26
|
-
|
|
54
|
+
type: type,
|
|
55
|
+
...field(e),
|
|
56
|
+
...extras
|
|
27
57
|
}
|
|
28
58
|
};
|
|
29
|
-
|
|
59
|
+
|
|
60
|
+
case 'lambda_unauthorized':
|
|
30
61
|
return {
|
|
31
62
|
headers,
|
|
32
|
-
statusCode: 401
|
|
63
|
+
statusCode: 401,
|
|
33
64
|
body: {
|
|
34
|
-
message: e.message
|
|
65
|
+
message: e.message,
|
|
66
|
+
type: type,
|
|
67
|
+
...extras
|
|
35
68
|
}
|
|
36
69
|
};
|
|
37
|
-
|
|
70
|
+
|
|
71
|
+
case 'lambda_forbidden':
|
|
38
72
|
return {
|
|
39
73
|
headers,
|
|
40
|
-
statusCode: 403
|
|
74
|
+
statusCode: 403,
|
|
41
75
|
body: {
|
|
42
|
-
message: e.message
|
|
76
|
+
message: e.message,
|
|
77
|
+
type: type,
|
|
78
|
+
...extras
|
|
43
79
|
}
|
|
44
80
|
};
|
|
45
|
-
|
|
81
|
+
|
|
82
|
+
case 'lambda_notFound':
|
|
46
83
|
return {
|
|
47
84
|
headers,
|
|
48
|
-
statusCode: 404
|
|
85
|
+
statusCode: 404,
|
|
49
86
|
body: {
|
|
50
87
|
message: e.message,
|
|
51
|
-
|
|
88
|
+
type: type,
|
|
89
|
+
...field(e),
|
|
90
|
+
...extras
|
|
52
91
|
}
|
|
53
92
|
};
|
|
54
|
-
|
|
93
|
+
|
|
94
|
+
case 'lambda_conflict':
|
|
55
95
|
return {
|
|
56
96
|
headers,
|
|
57
|
-
statusCode: 409
|
|
97
|
+
statusCode: 409,
|
|
58
98
|
body: {
|
|
59
99
|
message: e.message,
|
|
60
|
-
|
|
100
|
+
type: type,
|
|
101
|
+
...field(e),
|
|
102
|
+
...extras
|
|
61
103
|
}
|
|
62
104
|
};
|
|
105
|
+
|
|
63
106
|
default:
|
|
64
107
|
return {
|
|
65
108
|
headers,
|
|
66
|
-
statusCode: 500
|
|
109
|
+
statusCode: 500,
|
|
67
110
|
body: {
|
|
68
|
-
message: 'Unknown error'
|
|
111
|
+
message: 'Unknown error',
|
|
112
|
+
type: type,
|
|
113
|
+
...extras
|
|
69
114
|
}
|
|
70
115
|
};
|
|
71
116
|
}
|
|
@@ -73,9 +118,28 @@ export function errorResponse(e: LambdaError, headers: any) {
|
|
|
73
118
|
|
|
74
119
|
/**
|
|
75
120
|
* Helper function to get a reasonable default error response from a valibot parse result
|
|
121
|
+
* @param res - The output from calling safeParse on the input
|
|
122
|
+
* @param headers - The headers to return in the response
|
|
76
123
|
*/
|
|
77
|
-
export function
|
|
78
|
-
const issue = res.issues[0]
|
|
124
|
+
export function response_valibotError(res: Extract<SafeParseResult<any>, { success: false }>, headers: any) {
|
|
125
|
+
const issue = res.issues[0] as BaseIssue<any>;
|
|
126
|
+
|
|
127
|
+
if (issue.path && issue.path[0] && typeof issue.path[0].key === 'string') {
|
|
128
|
+
return response_error(error_lambda_badRequest('Invalid input', issue.path[0].key, issue.message), headers);
|
|
129
|
+
} else {
|
|
130
|
+
return response_error(error_lambda_badRequest(`Invalid input: ${issue.message}`), headers);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
79
133
|
|
|
80
|
-
|
|
134
|
+
export function response_ok<Body extends { message: string }>(
|
|
135
|
+
body: Body,
|
|
136
|
+
headers: any,
|
|
137
|
+
cookies?: string[] | undefined
|
|
138
|
+
) {
|
|
139
|
+
return {
|
|
140
|
+
headers,
|
|
141
|
+
cookies,
|
|
142
|
+
statusCode: 200 as const,
|
|
143
|
+
body
|
|
144
|
+
} satisfies OkRawProxyResultV2;
|
|
81
145
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { APIGatewayProxyEventV2WithLambdaAuthorizer, APIGatewayRequestAuthorizerEventV2 } from 'aws-lambda';
|
|
2
|
+
import { Result } from 'neverthrow';
|
|
3
|
+
import { parse } from 'cookie';
|
|
4
|
+
import { error_lambda_unauthorized } from '$lambda/errors.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wraps cookies parse along with the api gateway event with neverthrow
|
|
8
|
+
*/
|
|
9
|
+
export const getCookies = Result.fromThrowable(
|
|
10
|
+
(event: APIGatewayProxyEventV2WithLambdaAuthorizer<any> | APIGatewayRequestAuthorizerEventV2) => {
|
|
11
|
+
if (!('headers' in event) || !event.headers) {
|
|
12
|
+
throw new Error('No headers in event');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// First try to get cookies from the cookies array (API Gateway v2 format)
|
|
16
|
+
const cookieString =
|
|
17
|
+
Array.isArray(event.cookies) && event.cookies.length > 0
|
|
18
|
+
? event.cookies.join('; ')
|
|
19
|
+
: event.headers.Cookie || event.headers.cookie;
|
|
20
|
+
|
|
21
|
+
if (!cookieString) {
|
|
22
|
+
throw new Error('No cookies found in event');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const res = parse(cookieString);
|
|
26
|
+
return res;
|
|
27
|
+
},
|
|
28
|
+
(e) => {
|
|
29
|
+
if (e instanceof Error) return error_lambda_unauthorized(e.message);
|
|
30
|
+
return error_lambda_unauthorized('Invalid Cookies');
|
|
31
|
+
}
|
|
32
|
+
);
|
package/src/s3/client.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convenience function for S3Client when process.env.REGION is set
|
|
5
|
+
*/
|
|
6
|
+
export function getS3() {
|
|
7
|
+
return new S3Client({ endpoint: `https://s3.${process.env.REGION}.amazonaws.com`, region: process.env.REGION });
|
|
8
|
+
}
|
package/src/s3/errors.ts
ADDED
package/src/s3/index.ts
ADDED
package/src/s3/object.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ResultAsync } from 'neverthrow';
|
|
2
|
+
import { getS3 } from './client.js';
|
|
3
|
+
import { GetObjectCommand } from '@aws-sdk/client-s3';
|
|
4
|
+
import { error_s3_get } from './errors.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves an object from an S3 bucket.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} bucketName - The name of the S3 bucket.
|
|
10
|
+
* @param {string} key - The key of the object to retrieve.
|
|
11
|
+
* @returns {Promise<Buffer>} A promise that resolves to the object data as a Buffer.
|
|
12
|
+
*/
|
|
13
|
+
export const getObject = ResultAsync.fromThrowable(
|
|
14
|
+
async (bucketName: string, key: string) => {
|
|
15
|
+
const s3 = getS3();
|
|
16
|
+
const cmd = new GetObjectCommand({ Bucket: bucketName, Key: key });
|
|
17
|
+
const res = await s3.send(cmd);
|
|
18
|
+
const stream = res.Body as any;
|
|
19
|
+
return new Promise<Buffer>((resolve, reject) => {
|
|
20
|
+
const chunks: Buffer[] = [];
|
|
21
|
+
stream.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
22
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
23
|
+
stream.on('error', reject);
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
(e) => {
|
|
27
|
+
console.error(`Error getting object from S3: ${e}`);
|
|
28
|
+
return error_s3_get;
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convenience function to get an object from S3 and return it as a string.
|
|
34
|
+
*/
|
|
35
|
+
export function getObjectString(bucketName: string, key: string): ResultAsync<string, typeof error_s3_get> {
|
|
36
|
+
return getObject(bucketName, key).map((buffer) => buffer.toString('utf-8'));
|
|
37
|
+
}
|