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.
Files changed (122) hide show
  1. package/dist/browser.d.ts +1 -0
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js +1 -0
  4. package/dist/cognito/client.d.ts +6 -0
  5. package/dist/cognito/client.d.ts.map +1 -0
  6. package/dist/cognito/client.js +10 -0
  7. package/dist/cognito/errors.d.ts +95 -0
  8. package/dist/cognito/errors.d.ts.map +1 -0
  9. package/dist/cognito/errors.js +128 -0
  10. package/dist/cognito/index.d.ts +5 -0
  11. package/dist/cognito/index.d.ts.map +1 -0
  12. package/dist/cognito/index.js +4 -0
  13. package/dist/cognito/password.d.ts +53 -0
  14. package/dist/cognito/password.d.ts.map +1 -0
  15. package/dist/cognito/password.js +161 -0
  16. package/dist/cognito/user.d.ts +14 -0
  17. package/dist/cognito/user.d.ts.map +1 -0
  18. package/dist/cognito/user.js +34 -0
  19. package/dist/dynamo/errors.d.ts +9 -0
  20. package/dist/dynamo/errors.d.ts.map +1 -0
  21. package/dist/dynamo/errors.js +7 -0
  22. package/dist/dynamo/index.d.ts +2 -0
  23. package/dist/dynamo/index.d.ts.map +1 -0
  24. package/dist/dynamo/index.js +1 -0
  25. package/dist/index.d.ts +5 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +6 -0
  28. package/dist/lambda/authentication.d.ts +2 -0
  29. package/dist/lambda/authentication.d.ts.map +1 -0
  30. package/dist/lambda/authentication.js +1 -0
  31. package/dist/lambda/browser.d.ts +2 -0
  32. package/dist/lambda/browser.d.ts.map +1 -1
  33. package/dist/lambda/browser.js +2 -0
  34. package/dist/lambda/client-types.d.ts +19 -19
  35. package/dist/lambda/client-types.d.ts.map +1 -1
  36. package/dist/lambda/client-types.js +9 -1
  37. package/dist/lambda/client.d.ts.map +1 -1
  38. package/dist/lambda/client.js +7 -2
  39. package/dist/lambda/errors.d.ts +19 -19
  40. package/dist/lambda/errors.d.ts.map +1 -1
  41. package/dist/lambda/errors.js +12 -12
  42. package/dist/lambda/handlerUtils.d.ts +8 -3
  43. package/dist/lambda/handlerUtils.d.ts.map +1 -1
  44. package/dist/lambda/handlerUtils.js +3 -2
  45. package/dist/lambda/index.d.ts +1 -0
  46. package/dist/lambda/index.d.ts.map +1 -1
  47. package/dist/lambda/index.js +2 -0
  48. package/dist/lambda/response.d.ts +48 -95
  49. package/dist/lambda/response.d.ts.map +1 -1
  50. package/dist/lambda/response.js +42 -18
  51. package/dist/lambda/server/authentication.d.ts +10 -0
  52. package/dist/lambda/server/authentication.d.ts.map +1 -0
  53. package/dist/lambda/server/authentication.js +24 -0
  54. package/dist/s3/client.d.ts +6 -0
  55. package/dist/s3/client.d.ts.map +1 -0
  56. package/dist/s3/client.js +7 -0
  57. package/dist/s3/errors.d.ts +7 -0
  58. package/dist/s3/errors.d.ts.map +1 -0
  59. package/dist/s3/errors.js +6 -0
  60. package/dist/s3/index.d.ts +5 -0
  61. package/dist/s3/index.d.ts.map +1 -0
  62. package/dist/s3/index.js +4 -0
  63. package/dist/s3/object.d.ts +17 -0
  64. package/dist/s3/object.d.ts.map +1 -0
  65. package/dist/s3/object.js +32 -0
  66. package/dist/s3/signedUrl.d.ts +3 -0
  67. package/dist/s3/signedUrl.d.ts.map +1 -0
  68. package/dist/s3/signedUrl.js +3 -0
  69. package/dist/scripts/clean.js +30 -59
  70. package/dist/scripts/ts-alias.d.ts +3 -0
  71. package/dist/scripts/ts-alias.d.ts.map +1 -0
  72. package/dist/scripts/ts-alias.js +167 -0
  73. package/dist/ses/errors.d.ts +4 -0
  74. package/dist/ses/errors.d.ts.map +1 -0
  75. package/dist/ses/errors.js +3 -0
  76. package/dist/ses/index.d.ts +2 -0
  77. package/dist/ses/index.d.ts.map +1 -0
  78. package/dist/ses/index.js +1 -0
  79. package/dist/ts-alias.js +0 -0
  80. package/dist/types/deep.d.ts +24 -0
  81. package/dist/types/deep.d.ts.map +1 -0
  82. package/dist/types/deep.js +1 -0
  83. package/dist/types/index.d.ts +3 -0
  84. package/dist/types/index.d.ts.map +1 -0
  85. package/dist/types/index.js +2 -0
  86. package/dist/types/safe.d.ts +2 -0
  87. package/dist/types/safe.d.ts.map +1 -0
  88. package/dist/types/safe.js +1 -0
  89. package/dist/utils/fs.d.ts +2 -1
  90. package/dist/utils/fs.d.ts.map +1 -1
  91. package/dist/utils/fs.js +3 -1
  92. package/package.json +31 -22
  93. package/src/browser.ts +3 -1
  94. package/src/cognito/client.ts +11 -0
  95. package/src/cognito/errors.ts +175 -0
  96. package/src/cognito/index.ts +5 -0
  97. package/src/cognito/password.ts +233 -0
  98. package/src/cognito/user.ts +46 -0
  99. package/src/dynamo/errors.ts +11 -0
  100. package/src/dynamo/index.ts +1 -0
  101. package/src/index.ts +8 -0
  102. package/src/lambda/browser.ts +2 -0
  103. package/src/lambda/client-types.ts +55 -29
  104. package/src/lambda/client.ts +7 -2
  105. package/src/lambda/errors.ts +25 -25
  106. package/src/lambda/handlerUtils.ts +30 -25
  107. package/src/lambda/index.ts +3 -0
  108. package/src/lambda/response.ts +90 -26
  109. package/src/lambda/server/authentication.ts +32 -0
  110. package/src/s3/client.ts +8 -0
  111. package/src/s3/errors.ts +6 -0
  112. package/src/s3/index.ts +6 -0
  113. package/src/s3/object.ts +37 -0
  114. package/src/s3/signedUrl.ts +4 -0
  115. package/src/scripts/clean.ts +34 -68
  116. package/src/ses/errors.ts +3 -0
  117. package/src/ses/index.ts +1 -0
  118. package/src/types/deep.ts +43 -0
  119. package/src/types/index.ts +2 -0
  120. package/src/types/safe.ts +1 -0
  121. package/src/utils/fs.ts +4 -2
  122. /package/src/{ts-alias.ts → scripts/ts-alias.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import type { ErrorBody, SuccessCode, ErrorCode } from './handlerUtils.js';
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<E extends ApiEndpoints, P extends E['path'], M extends E['method']> = Extract<
13
- E,
14
- { path: P; method: M }
15
- >['requestInput'];
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<E extends ApiEndpoints, P extends E['path'], M extends E['method']> = Extract<
24
- E,
25
- { path: P; method: M }
26
- >['requestOutput'];
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<E extends ApiEndpoints, P extends E['path'], M extends E['method']> = Extract<
35
- E,
36
- { path: P; method: M }
37
- >['response'];
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<E extends ApiEndpoints, P extends E['path'], M extends E['method']> = Extract<
46
- Extract<E, { path: P; method: M }>['response'],
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
- >['json'] extends () => Promise<infer R>
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<E extends ApiEndpoints, P extends E['path'], M extends E['method']> = Extract<
59
- Extract<E, { path: P; method: M }>['response'],
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
- >['json'] extends () => Promise<infer R>
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 { statusCode: number; body: object; headers: object }
79
+ type ConvertToFetch<T> = T extends {
80
+ statusCode: number;
81
+ body: object;
82
+ headers: object;
83
+ }
69
84
  ? {
70
- ok: T['statusCode'] extends SuccessCode ? true : false;
71
- json: () => Promise<T['body']>;
72
- status: T['statusCode'];
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, 'status' | 'ok' | 'json'>;
77
- export type FetchResponse<T extends (...args: any) => any> = ConvertToFetch<Awaited<ReturnType<T>>> & CleanResponse;
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 = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'] as const;
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 in the createFormFunction
117
+ // This means we get better types
92
118
  () => Promise<
93
119
  | {
94
120
  headers: object;
@@ -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
- return await deserialize(await response.text(), environment);
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
  }
@@ -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 badRequestError = (message: string, fieldName?: string, fieldValue?: string) => ({
11
- type: 'BadRequest' as const,
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 unauthorizedError = (message: string) => ({
17
- type: 'Unauthorized' as const,
16
+ export const error_lambda_unauthorized = (message: string) => ({
17
+ type: 'lambda_unauthorized' as const,
18
18
  message
19
19
  });
20
20
 
21
- export const forbiddenError = (message: string) => ({
22
- type: 'Forbidden' as const,
21
+ export const error_lambda_forbidden = (message: string) => ({
22
+ type: 'lambda_forbidden' as const,
23
23
  message
24
24
  });
25
25
 
26
- export const notFoundError = (message: string, fieldName?: string, fieldValue?: string) => ({
27
- type: 'NotFound' as const,
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 conflictError = (message: string, fieldName?: string, fieldValue?: string) => ({
34
- type: 'Conflict' as const,
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 internalServerError = (message: string) => ({
41
- type: 'InternalServerError' as const,
40
+ export const error_lambda_internal = (message: string) => ({
41
+ type: 'lambda_internal' as const,
42
42
  message
43
43
  });
44
44
 
45
- export type BadRequestError = ReturnType<typeof badRequestError>;
46
- export type UnauthorizedError = ReturnType<typeof unauthorizedError>;
47
- export type ForbiddenError = ReturnType<typeof forbiddenError>;
48
- export type NotFoundError = ReturnType<typeof notFoundError>;
49
- export type ConflictError = ReturnType<typeof conflictError>;
50
- export type InternalServerError = ReturnType<typeof internalServerError>;
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 LambdaError =
53
- | BadRequestError
54
- | UnauthorizedError
55
- | ForbiddenError
56
- | NotFoundError
57
- | ConflictError
58
- | InternalServerError;
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 ? await stringify(result.body) : undefined
72
+ body: result.body ? stringify(result.body) : undefined
68
73
  };
69
74
  };
70
75
  }
@@ -1,3 +1,6 @@
1
1
  export * from './browser.js';
2
2
  export * from './errors.js';
3
3
  export * from './response.js';
4
+
5
+ // Server
6
+ export * from './server/authentication.js';
@@ -1,5 +1,6 @@
1
- import type { SafeParseResult } from 'valibot';
2
- import { badRequestError, type LambdaError } from './errors.js';
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
- * Takes a lambda error and gives an error response suitable to be returned from the lambda handler
17
- */
18
- export function errorResponse(e: LambdaError, headers: any) {
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 'BadRequest':
48
+ case 'lambda_badRequest':
21
49
  return {
22
50
  headers,
23
- statusCode: 400 as const,
51
+ statusCode: 400,
24
52
  body: {
25
53
  message: e.message,
26
- ...field(e)
54
+ type: type,
55
+ ...field(e),
56
+ ...extras
27
57
  }
28
58
  };
29
- case 'Unauthorized':
59
+
60
+ case 'lambda_unauthorized':
30
61
  return {
31
62
  headers,
32
- statusCode: 401 as const,
63
+ statusCode: 401,
33
64
  body: {
34
- message: e.message
65
+ message: e.message,
66
+ type: type,
67
+ ...extras
35
68
  }
36
69
  };
37
- case 'Forbidden':
70
+
71
+ case 'lambda_forbidden':
38
72
  return {
39
73
  headers,
40
- statusCode: 403 as const,
74
+ statusCode: 403,
41
75
  body: {
42
- message: e.message
76
+ message: e.message,
77
+ type: type,
78
+ ...extras
43
79
  }
44
80
  };
45
- case 'NotFound':
81
+
82
+ case 'lambda_notFound':
46
83
  return {
47
84
  headers,
48
- statusCode: 404 as const,
85
+ statusCode: 404,
49
86
  body: {
50
87
  message: e.message,
51
- ...field(e)
88
+ type: type,
89
+ ...field(e),
90
+ ...extras
52
91
  }
53
92
  };
54
- case 'Conflict':
93
+
94
+ case 'lambda_conflict':
55
95
  return {
56
96
  headers,
57
- statusCode: 409 as const,
97
+ statusCode: 409,
58
98
  body: {
59
99
  message: e.message,
60
- ...field(e)
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 as const,
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 valibotErrorResponse(res: Extract<SafeParseResult<any>, { success: false }>, headers: any) {
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
- return errorResponse(badRequestError('Invalid parameters', issue.path[0].key, issue.message), headers);
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
+ );
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ export const error_s3_internal = {
2
+ type: 's3_internal' as const
3
+ };
4
+ export const error_s3_get = {
5
+ type: 's3_get' as const
6
+ };
@@ -0,0 +1,6 @@
1
+ export * from './client.js';
2
+
3
+ export * from './errors.js';
4
+
5
+ export * from './signedUrl.js';
6
+ export * from './object.js';
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ import { ResultAsync } from 'neverthrow';
2
+ import { getSignedUrl as baseGetSignedUrl } from '@aws-sdk/s3-request-presigner';
3
+
4
+ export const getSignedUrl = ResultAsync.fromThrowable(baseGetSignedUrl, (e) => e);