@aws-amplify/adapter-nextjs 0.0.2-console-preview.e8aa063.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 (58) hide show
  1. package/LICENSE +201 -0
  2. package/lib/index.d.ts +1 -0
  3. package/lib/index.js +8 -0
  4. package/lib/index.js.map +1 -0
  5. package/lib/runWithAmplifyServerContext.d.ts +2 -0
  6. package/lib/runWithAmplifyServerContext.js +74 -0
  7. package/lib/runWithAmplifyServerContext.js.map +1 -0
  8. package/lib/types/NextServer.d.ts +53 -0
  9. package/lib/types/NextServer.js +5 -0
  10. package/lib/types/NextServer.js.map +1 -0
  11. package/lib/types/index.d.ts +1 -0
  12. package/lib/types/index.js +5 -0
  13. package/lib/types/index.js.map +1 -0
  14. package/lib/utils/createCookieStorageAdapterFromNextServerContext.d.ts +4 -0
  15. package/lib/utils/createCookieStorageAdapterFromNextServerContext.js +152 -0
  16. package/lib/utils/createCookieStorageAdapterFromNextServerContext.js.map +1 -0
  17. package/lib/utils/getAmplifyConfig.d.ts +2 -0
  18. package/lib/utils/getAmplifyConfig.js +20 -0
  19. package/lib/utils/getAmplifyConfig.js.map +1 -0
  20. package/lib/utils/index.d.ts +2 -0
  21. package/lib/utils/index.js +10 -0
  22. package/lib/utils/index.js.map +1 -0
  23. package/lib/withAmplify.d.ts +9 -0
  24. package/lib/withAmplify.js +33 -0
  25. package/lib/withAmplify.js.map +1 -0
  26. package/lib-esm/index.d.ts +1 -0
  27. package/lib-esm/index.js +4 -0
  28. package/lib-esm/index.js.map +1 -0
  29. package/lib-esm/runWithAmplifyServerContext.d.ts +2 -0
  30. package/lib-esm/runWithAmplifyServerContext.js +70 -0
  31. package/lib-esm/runWithAmplifyServerContext.js.map +1 -0
  32. package/lib-esm/types/NextServer.d.ts +53 -0
  33. package/lib-esm/types/NextServer.js +4 -0
  34. package/lib-esm/types/NextServer.js.map +1 -0
  35. package/lib-esm/types/index.d.ts +1 -0
  36. package/lib-esm/types/index.js +4 -0
  37. package/lib-esm/types/index.js.map +1 -0
  38. package/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.d.ts +4 -0
  39. package/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.js +148 -0
  40. package/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.js.map +1 -0
  41. package/lib-esm/utils/getAmplifyConfig.d.ts +2 -0
  42. package/lib-esm/utils/getAmplifyConfig.js +16 -0
  43. package/lib-esm/utils/getAmplifyConfig.js.map +1 -0
  44. package/lib-esm/utils/index.d.ts +2 -0
  45. package/lib-esm/utils/index.js +5 -0
  46. package/lib-esm/utils/index.js.map +1 -0
  47. package/lib-esm/withAmplify.d.ts +9 -0
  48. package/lib-esm/withAmplify.js +29 -0
  49. package/lib-esm/withAmplify.js.map +1 -0
  50. package/package.json +111 -0
  51. package/src/index.ts +4 -0
  52. package/src/runWithAmplifyServerContext.ts +60 -0
  53. package/src/types/NextServer.ts +72 -0
  54. package/src/types/index.ts +4 -0
  55. package/src/utils/createCookieStorageAdapterFromNextServerContext.ts +202 -0
  56. package/src/utils/getAmplifyConfig.ts +22 -0
  57. package/src/utils/index.ts +5 -0
  58. package/src/withAmplify.ts +28 -0
@@ -0,0 +1,72 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { GetServerSidePropsContext as NextGetServerSidePropsContext } from 'next';
5
+ import { NextRequest, NextResponse } from 'next/server';
6
+ import { cookies } from 'next/headers';
7
+ import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core';
8
+
9
+ export namespace NextServer {
10
+ /**
11
+ * This context is normally available in the following:
12
+ * - Next App Router [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)
13
+ * - Next App Router [route handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers)
14
+ * when using `NextResponse` to create the response of the route handler
15
+ */
16
+ export type NextRequestAndNextResponseContext = {
17
+ request: NextRequest;
18
+ response: NextResponse;
19
+ };
20
+
21
+ /**
22
+ * This context is normally available in the following:
23
+ * - Next App Router [route handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers)
24
+ * when using the Web API [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
25
+ * to create the response of the route handler
26
+ */
27
+ export type NextRequestAndResponseContext = {
28
+ request: NextRequest;
29
+ response: Response;
30
+ };
31
+
32
+ /**
33
+ * This context is normally available in the following:
34
+ * - Next [Server Component](https://nextjs.org/docs/getting-started/react-essentials#server-components)
35
+ * where the [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies)
36
+ * function can be imported and called
37
+ */
38
+ export type ServerComponentContext = {
39
+ cookies: typeof cookies;
40
+ };
41
+
42
+ export type ServerActionContext = ServerComponentContext;
43
+
44
+ /**
45
+ * This context is normally available in the
46
+ * [`getServerSideProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props)
47
+ * function of the Next Pages Router.
48
+ */
49
+ export type GetServerSidePropsContext = {
50
+ request: NextGetServerSidePropsContext['req'];
51
+ response: NextGetServerSidePropsContext['res'];
52
+ };
53
+
54
+ export type Context =
55
+ | NextRequestAndNextResponseContext
56
+ | NextRequestAndResponseContext
57
+ | ServerComponentContext
58
+ | GetServerSidePropsContext;
59
+
60
+ export interface RunWithContextInput<OperationResult> {
61
+ nextServerContext: Context | null;
62
+ operation: (
63
+ contextSpec: AmplifyServer.ContextSpec
64
+ ) => OperationResult | Promise<OperationResult>;
65
+ }
66
+
67
+ export interface RunOperationWithContext {
68
+ <OperationResult>(
69
+ input: RunWithContextInput<OperationResult>
70
+ ): Promise<OperationResult>;
71
+ }
72
+ }
@@ -0,0 +1,4 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { NextServer } from './NextServer';
@@ -0,0 +1,202 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { NextRequest, NextResponse } from 'next/server';
5
+ import { NextServer } from '../types';
6
+ import {
7
+ AmplifyServerContextError,
8
+ CookieStorage,
9
+ } from '@aws-amplify/core/internals/adapter-core';
10
+ import { IncomingMessage, ServerResponse } from 'http';
11
+
12
+ export const DATE_IN_THE_PAST = new Date(0);
13
+
14
+ export const createCookieStorageAdapterFromNextServerContext = (
15
+ context: NextServer.Context
16
+ ): CookieStorage.Adapter => {
17
+ const { request, response } = context as
18
+ | NextServer.NextRequestAndNextResponseContext
19
+ | NextServer.NextRequestAndResponseContext;
20
+
21
+ if (request instanceof NextRequest && response) {
22
+ if (response instanceof NextResponse) {
23
+ return createCookieStorageAdapterFromNextRequestAndNextResponse(
24
+ request,
25
+ response
26
+ );
27
+ } else {
28
+ return createCookieStorageAdapterFromNextRequestAndHttpResponse(
29
+ request,
30
+ response
31
+ );
32
+ }
33
+ }
34
+
35
+ const { cookies } = context as
36
+ | NextServer.ServerComponentContext
37
+ | NextServer.ServerActionContext;
38
+
39
+ if (typeof cookies === 'function') {
40
+ return createCookieStorageAdapterFromNextCookies(cookies);
41
+ }
42
+
43
+ const { request: req, response: res } =
44
+ context as NextServer.GetServerSidePropsContext;
45
+
46
+ if (req instanceof IncomingMessage && res instanceof ServerResponse) {
47
+ return createCookieStorageAdapterFromGetServerSidePropsContext(req, res);
48
+ }
49
+
50
+ // This should not happen normally.
51
+ throw new AmplifyServerContextError({
52
+ message:
53
+ 'Attempted to create cookie storage adapter from an unsupported Next.js server context.',
54
+ });
55
+ };
56
+
57
+ const createCookieStorageAdapterFromNextRequestAndNextResponse = (
58
+ request: NextRequest,
59
+ response: NextResponse
60
+ ): CookieStorage.Adapter => {
61
+ const readonlyCookieStore = request.cookies;
62
+ const mutableCookieStore = response.cookies;
63
+
64
+ return {
65
+ get: readonlyCookieStore.get.bind(readonlyCookieStore),
66
+ getAll: readonlyCookieStore.getAll.bind(readonlyCookieStore),
67
+ set: mutableCookieStore.set.bind(mutableCookieStore),
68
+ delete: mutableCookieStore.delete.bind(mutableCookieStore),
69
+ };
70
+ };
71
+
72
+ const createCookieStorageAdapterFromNextRequestAndHttpResponse = (
73
+ request: NextRequest,
74
+ response: Response
75
+ ): CookieStorage.Adapter => {
76
+ const readonlyCookieStore = request.cookies;
77
+ const mutableCookieStore = createMutableCookieStoreFromHeaders(
78
+ response.headers
79
+ );
80
+
81
+ return {
82
+ get: readonlyCookieStore.get.bind(readonlyCookieStore),
83
+ getAll: readonlyCookieStore.getAll.bind(readonlyCookieStore),
84
+ ...mutableCookieStore,
85
+ };
86
+ };
87
+
88
+ const createCookieStorageAdapterFromNextCookies = (
89
+ cookies: NextServer.ServerComponentContext['cookies']
90
+ ): CookieStorage.Adapter => {
91
+ const cookieStore = cookies();
92
+
93
+ // When Next cookies() is called in a server component, it returns a readonly
94
+ // cookie store. Hence calling set and delete throws an error. However,
95
+ // cookies() returns a mutable cookie store when called in a server action.
96
+ // We have no way to detect which one is returned, so we try to call set and delete
97
+ // and safely ignore the error if it is thrown.
98
+ const setFunc: CookieStorage.Adapter['set'] = (name, value, options) => {
99
+ try {
100
+ cookieStore.set(name, value, options);
101
+ } catch {
102
+ // no-op
103
+ }
104
+ };
105
+
106
+ const deleteFunc: CookieStorage.Adapter['delete'] = name => {
107
+ try {
108
+ cookieStore.delete(name);
109
+ } catch {
110
+ // no-op
111
+ }
112
+ };
113
+
114
+ return {
115
+ get: cookieStore.get.bind(cookieStore),
116
+ getAll: cookieStore.getAll.bind(cookieStore),
117
+ set: setFunc,
118
+ delete: deleteFunc,
119
+ };
120
+ };
121
+
122
+ const createCookieStorageAdapterFromGetServerSidePropsContext = (
123
+ request: NextServer.GetServerSidePropsContext['request'],
124
+ response: NextServer.GetServerSidePropsContext['response']
125
+ ): CookieStorage.Adapter => {
126
+ const cookiesMap = { ...request.cookies };
127
+ const allCookies = Object.entries(cookiesMap).map(([name, value]) => ({
128
+ name,
129
+ value,
130
+ }));
131
+
132
+ return {
133
+ get(name) {
134
+ const value = cookiesMap[name];
135
+ return value
136
+ ? {
137
+ name,
138
+ value,
139
+ }
140
+ : undefined;
141
+ },
142
+ getAll() {
143
+ return allCookies;
144
+ },
145
+ set(name, value, options) {
146
+ response.setHeader(
147
+ 'Set-Cookie',
148
+ `${name}=${value};${options ? serializeSetCookieOptions(options) : ''}`
149
+ );
150
+ },
151
+ delete(name) {
152
+ response.setHeader(
153
+ 'Set-Cookie',
154
+ `${name}=;Expires=${DATE_IN_THE_PAST.toUTCString()}`
155
+ );
156
+ },
157
+ };
158
+ };
159
+
160
+ const createMutableCookieStoreFromHeaders = (
161
+ headers: Headers
162
+ ): Pick<CookieStorage.Adapter, 'set' | 'delete'> => {
163
+ const setFunc: CookieStorage.Adapter['set'] = (name, value, options) => {
164
+ headers.append(
165
+ 'Set-Cookie',
166
+ `${name}=${value};${options ? serializeSetCookieOptions(options) : ''}`
167
+ );
168
+ };
169
+ const deleteFunc: CookieStorage.Adapter['delete'] = name => {
170
+ headers.append(
171
+ 'Set-Cookie',
172
+ `${name}=;Expires=${DATE_IN_THE_PAST.toUTCString()}`
173
+ );
174
+ };
175
+ return {
176
+ set: setFunc,
177
+ delete: deleteFunc,
178
+ };
179
+ };
180
+
181
+ const serializeSetCookieOptions = (
182
+ options: CookieStorage.SetCookieOptions
183
+ ): string => {
184
+ const { expires, maxAge, domain, httpOnly, sameSite, secure } = options;
185
+ const serializedOptions: string[] = [];
186
+ if (domain) {
187
+ serializedOptions.push(`Domain=${domain}`);
188
+ }
189
+ if (expires) {
190
+ serializedOptions.push(`Expires=${expires.toUTCString()}`);
191
+ }
192
+ if (httpOnly) {
193
+ serializedOptions.push(`HttpOnly`);
194
+ }
195
+ if (sameSite) {
196
+ serializedOptions.push(`SameSite=${sameSite}`);
197
+ }
198
+ if (secure) {
199
+ serializedOptions.push(`Secure`);
200
+ }
201
+ return serializedOptions.join(';');
202
+ };
@@ -0,0 +1,22 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { ResourcesConfig } from '@aws-amplify/core';
5
+ import { AmplifyServerContextError } from '@aws-amplify/core/internals/adapter-core';
6
+
7
+ export const getAmplifyConfig = (): ResourcesConfig => {
8
+ const configStr = process.env.amplifyConfig;
9
+
10
+ if (!configStr) {
11
+ throw new AmplifyServerContextError({
12
+ message: 'Amplify configuration is missing from `process.env`.',
13
+ recoverySuggestion:
14
+ 'Ensure to use `withAmplify` function in your `next.config.js`.',
15
+ });
16
+ }
17
+
18
+ const configObject = JSON.parse(configStr);
19
+
20
+ // TODO(HuiSF): adds ResourcesConfig validation when it has one.
21
+ return configObject;
22
+ };
@@ -0,0 +1,5 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { getAmplifyConfig } from './getAmplifyConfig';
5
+ export { createCookieStorageAdapterFromNextServerContext } from './createCookieStorageAdapterFromNextServerContext';
@@ -0,0 +1,28 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { ResourcesConfig } from '@aws-amplify/core';
5
+ import { NextConfig } from 'next';
6
+
7
+ // NOTE: this function is exported from the subpath `/with-amplify`.
8
+ // The reason is that this function is called in the `next.config.js` which
9
+ // is not being transpiled by Next.js.
10
+
11
+ /**
12
+ * Merges the `amplifyConfig` into the `nextConfig.env`.
13
+ * @param nextConfig The next config for a Next.js app.
14
+ * @param amplifyConfig
15
+ * @returns The updated `nextConfig`.
16
+ */
17
+ export const withAmplify = (
18
+ nextConfig: NextConfig,
19
+ amplifyConfig: ResourcesConfig
20
+ ) => {
21
+ nextConfig.env = {
22
+ ...nextConfig.env,
23
+ // TODO(Hui): follow up the validation of the amplifyConfig.
24
+ amplifyConfig: JSON.stringify(amplifyConfig),
25
+ };
26
+
27
+ return nextConfig;
28
+ };