@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.
- package/LICENSE +201 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -0
- package/lib/runWithAmplifyServerContext.d.ts +2 -0
- package/lib/runWithAmplifyServerContext.js +74 -0
- package/lib/runWithAmplifyServerContext.js.map +1 -0
- package/lib/types/NextServer.d.ts +53 -0
- package/lib/types/NextServer.js +5 -0
- package/lib/types/NextServer.js.map +1 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +5 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/createCookieStorageAdapterFromNextServerContext.d.ts +4 -0
- package/lib/utils/createCookieStorageAdapterFromNextServerContext.js +152 -0
- package/lib/utils/createCookieStorageAdapterFromNextServerContext.js.map +1 -0
- package/lib/utils/getAmplifyConfig.d.ts +2 -0
- package/lib/utils/getAmplifyConfig.js +20 -0
- package/lib/utils/getAmplifyConfig.js.map +1 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +10 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/withAmplify.d.ts +9 -0
- package/lib/withAmplify.js +33 -0
- package/lib/withAmplify.js.map +1 -0
- package/lib-esm/index.d.ts +1 -0
- package/lib-esm/index.js +4 -0
- package/lib-esm/index.js.map +1 -0
- package/lib-esm/runWithAmplifyServerContext.d.ts +2 -0
- package/lib-esm/runWithAmplifyServerContext.js +70 -0
- package/lib-esm/runWithAmplifyServerContext.js.map +1 -0
- package/lib-esm/types/NextServer.d.ts +53 -0
- package/lib-esm/types/NextServer.js +4 -0
- package/lib-esm/types/NextServer.js.map +1 -0
- package/lib-esm/types/index.d.ts +1 -0
- package/lib-esm/types/index.js +4 -0
- package/lib-esm/types/index.js.map +1 -0
- package/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.d.ts +4 -0
- package/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.js +148 -0
- package/lib-esm/utils/createCookieStorageAdapterFromNextServerContext.js.map +1 -0
- package/lib-esm/utils/getAmplifyConfig.d.ts +2 -0
- package/lib-esm/utils/getAmplifyConfig.js +16 -0
- package/lib-esm/utils/getAmplifyConfig.js.map +1 -0
- package/lib-esm/utils/index.d.ts +2 -0
- package/lib-esm/utils/index.js +5 -0
- package/lib-esm/utils/index.js.map +1 -0
- package/lib-esm/withAmplify.d.ts +9 -0
- package/lib-esm/withAmplify.js +29 -0
- package/lib-esm/withAmplify.js.map +1 -0
- package/package.json +111 -0
- package/src/index.ts +4 -0
- package/src/runWithAmplifyServerContext.ts +60 -0
- package/src/types/NextServer.ts +72 -0
- package/src/types/index.ts +4 -0
- package/src/utils/createCookieStorageAdapterFromNextServerContext.ts +202 -0
- package/src/utils/getAmplifyConfig.ts +22 -0
- package/src/utils/index.ts +5 -0
- 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,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
|
+
};
|