@htlkg/core 0.0.2 → 0.0.3

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 (35) hide show
  1. package/README.md +51 -0
  2. package/dist/index.d.ts +219 -0
  3. package/dist/index.js +121 -0
  4. package/dist/index.js.map +1 -1
  5. package/package.json +30 -8
  6. package/src/amplify-astro-adapter/amplify-astro-adapter.md +167 -0
  7. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.test.ts +296 -0
  8. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts +97 -0
  9. package/src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts +84 -0
  10. package/src/amplify-astro-adapter/errors.test.ts +115 -0
  11. package/src/amplify-astro-adapter/errors.ts +105 -0
  12. package/src/amplify-astro-adapter/globalSettings.test.ts +78 -0
  13. package/src/amplify-astro-adapter/globalSettings.ts +16 -0
  14. package/src/amplify-astro-adapter/index.ts +14 -0
  15. package/src/amplify-astro-adapter/types.ts +55 -0
  16. package/src/auth/auth.md +178 -0
  17. package/src/auth/index.test.ts +180 -0
  18. package/src/auth/index.ts +294 -0
  19. package/src/constants/constants.md +132 -0
  20. package/src/constants/index.test.ts +116 -0
  21. package/src/constants/index.ts +98 -0
  22. package/src/core-exports.property.test.ts +186 -0
  23. package/src/errors/errors.md +177 -0
  24. package/src/errors/index.test.ts +153 -0
  25. package/src/errors/index.ts +134 -0
  26. package/src/index.ts +65 -0
  27. package/src/routes/index.ts +225 -0
  28. package/src/routes/routes.md +189 -0
  29. package/src/types/index.ts +94 -0
  30. package/src/types/types.md +144 -0
  31. package/src/utils/index.test.ts +257 -0
  32. package/src/utils/index.ts +112 -0
  33. package/src/utils/logger.ts +88 -0
  34. package/src/utils/utils.md +199 -0
  35. package/src/workspace.property.test.ts +235 -0
package/package.json CHANGED
@@ -1,17 +1,39 @@
1
1
  {
2
2
  "name": "@htlkg/core",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "exports": {
6
- ".": "./dist/index.js",
7
- "./auth": "./dist/auth/index.js",
8
- "./types": "./dist/types/index.js",
9
- "./utils": "./dist/utils/index.js",
10
- "./constants": "./dist/constants/index.js",
11
- "./errors": "./dist/errors/index.js",
12
- "./amplify-astro-adapter": "./dist/amplify-astro-adapter/index.js"
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "types": "./dist/index.d.ts"
9
+ },
10
+ "./auth": {
11
+ "import": "./dist/auth/index.js",
12
+ "types": "./dist/auth/index.d.ts"
13
+ },
14
+ "./types": {
15
+ "import": "./dist/types/index.js",
16
+ "types": "./dist/types/index.d.ts"
17
+ },
18
+ "./utils": {
19
+ "import": "./dist/utils/index.js",
20
+ "types": "./dist/utils/index.d.ts"
21
+ },
22
+ "./constants": {
23
+ "import": "./dist/constants/index.js",
24
+ "types": "./dist/constants/index.d.ts"
25
+ },
26
+ "./errors": {
27
+ "import": "./dist/errors/index.js",
28
+ "types": "./dist/errors/index.d.ts"
29
+ },
30
+ "./amplify-astro-adapter": {
31
+ "import": "./dist/amplify-astro-adapter/index.js",
32
+ "types": "./dist/amplify-astro-adapter/index.d.ts"
33
+ }
13
34
  },
14
35
  "files": [
36
+ "src",
15
37
  "dist"
16
38
  ],
17
39
  "dependencies": {
@@ -0,0 +1,167 @@
1
+ # Amplify Astro Adapter
2
+
3
+ Server-side authentication support for Astro applications using AWS Amplify. Handles cookie-based authentication context for SSR.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import {
9
+ createRunWithAmplifyServerContext,
10
+ createCookieStorageAdapterFromAstroContext,
11
+ globalSettings
12
+ } from '@htlkg/core/amplify-astro-adapter';
13
+ ```
14
+
15
+ ## Core Functions
16
+
17
+ ### createRunWithAmplifyServerContext
18
+
19
+ Creates a function that runs operations within an authenticated Amplify server context.
20
+
21
+ ```typescript
22
+ import { Amplify } from 'aws-amplify';
23
+ import { createRunWithAmplifyServerContext, globalSettings } from '@htlkg/core/amplify-astro-adapter';
24
+
25
+ const amplifyConfig = Amplify.getConfig();
26
+
27
+ const runWithAmplifyServerContext = createRunWithAmplifyServerContext({
28
+ config: amplifyConfig,
29
+ globalSettings, // Pass explicitly to avoid module singleton issues
30
+ });
31
+
32
+ // Use in Astro page or API route
33
+ const result = await runWithAmplifyServerContext({
34
+ astroServerContext: {
35
+ cookies: Astro.cookies,
36
+ request: Astro.request,
37
+ },
38
+ operation: async (contextSpec) => {
39
+ // Perform authenticated operations
40
+ const user = await getCurrentUser(contextSpec);
41
+ return user;
42
+ },
43
+ });
44
+ ```
45
+
46
+ ### createCookieStorageAdapterFromAstroContext
47
+
48
+ Creates a cookie storage adapter compatible with Amplify's auth system.
49
+
50
+ ```typescript
51
+ import { createCookieStorageAdapterFromAstroContext } from '@htlkg/core/amplify-astro-adapter';
52
+
53
+ const cookieAdapter = await createCookieStorageAdapterFromAstroContext({
54
+ cookies: Astro.cookies,
55
+ request: Astro.request,
56
+ });
57
+
58
+ // Adapter methods
59
+ cookieAdapter.get('cookie-name');
60
+ cookieAdapter.getAll();
61
+ cookieAdapter.set('name', 'value', options);
62
+ cookieAdapter.delete('name');
63
+ ```
64
+
65
+ ### globalSettings
66
+
67
+ Runtime configuration for the adapter.
68
+
69
+ ```typescript
70
+ import { globalSettings } from '@htlkg/core/amplify-astro-adapter';
71
+
72
+ // Enable server-side auth (enabled by default)
73
+ globalSettings.enableServerSideAuth();
74
+
75
+ // Check if SSL origin (affects secure cookie flag)
76
+ globalSettings.setIsSSLOrigin(true);
77
+
78
+ // Set custom cookie options
79
+ globalSettings.setRuntimeOptions({
80
+ cookies: {
81
+ domain: '.example.com',
82
+ sameSite: 'lax',
83
+ },
84
+ });
85
+ ```
86
+
87
+ ## Error Handling
88
+
89
+ The adapter provides specific error classes for different failure scenarios:
90
+
91
+ ```typescript
92
+ import {
93
+ AmplifyAstroAdapterError,
94
+ ErrorCodes,
95
+ createAuthFailedError,
96
+ createCookieError,
97
+ createConfigMissingError,
98
+ } from '@htlkg/core/amplify-astro-adapter';
99
+
100
+ try {
101
+ // ... adapter operations
102
+ } catch (error) {
103
+ if (error instanceof AmplifyAstroAdapterError) {
104
+ console.error(`[${error.code}] ${error.message}`);
105
+ if (error.recoverySuggestion) {
106
+ console.log('Suggestion:', error.recoverySuggestion);
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### Error Codes
113
+
114
+ | Code | Description |
115
+ |------|-------------|
116
+ | `CONFIG_MISSING` | Amplify configuration not found |
117
+ | `AUTH_FAILED` | Authentication failed |
118
+ | `COOKIE_ERROR` | Cookie read/write error |
119
+ | `GRAPHQL_ERROR` | GraphQL operation failed |
120
+ | `CONTEXT_CREATION_FAILED` | Server context creation failed |
121
+ | `TOKEN_REFRESH_FAILED` | Token refresh failed |
122
+
123
+ ## Types
124
+
125
+ ```typescript
126
+ import type { AstroServer, AstroAmplifyConfig } from '@htlkg/core/amplify-astro-adapter';
127
+
128
+ // Server context types
129
+ type ServerContext = AstroServer.AstroComponentContext | AstroServer.APIEndpointContext | null;
130
+
131
+ // Runtime options
132
+ interface RuntimeOptions {
133
+ cookies?: RuntimeCookieOptions;
134
+ }
135
+
136
+ // Run operation signature
137
+ type RunOperationWithContext = <T>(input: {
138
+ astroServerContext: ServerContext;
139
+ operation: (contextSpec: unknown) => Promise<T>;
140
+ }) => Promise<T>;
141
+ ```
142
+
143
+ ## Cookie Handling
144
+
145
+ The adapter handles Cognito authentication cookies with:
146
+
147
+ - Automatic encoding compatible with js-cookie
148
+ - Secure defaults (`httpOnly`, `sameSite: 'lax'`, `secure` based on SSL)
149
+ - Support for both Astro component and API endpoint contexts
150
+
151
+ ## Debug Logging
152
+
153
+ Enable debug logs by setting environment variables:
154
+
155
+ ```bash
156
+ DEBUG=true
157
+ # or
158
+ HTLKG_DEBUG=true
159
+ ```
160
+
161
+ ```typescript
162
+ import { createLogger } from '@htlkg/core/amplify-astro-adapter';
163
+
164
+ const log = createLogger('my-module');
165
+ log.debug('Debug message'); // Only shown when DEBUG=true
166
+ log.info('Info message'); // Always shown
167
+ ```
@@ -0,0 +1,296 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { createCookieStorageAdapterFromAstroContext } from "./createCookieStorageAdapterFromAstroContext";
3
+ import type { AstroServer } from "./types";
4
+
5
+ describe("createCookieStorageAdapterFromAstroContext", () => {
6
+ describe("with null context", () => {
7
+ it("should return a no-op adapter", async () => {
8
+ const adapter = await createCookieStorageAdapterFromAstroContext(null as any);
9
+
10
+ expect(adapter.get("test")).toBeUndefined();
11
+ expect(adapter.getAll()).toEqual([]);
12
+
13
+ // These should not throw
14
+ adapter.set("test", "value");
15
+ adapter.delete("test");
16
+ });
17
+ });
18
+
19
+ describe("with valid context", () => {
20
+ it("should get cookie from Astro cookies", async () => {
21
+ const mockCookies = {
22
+ get: vi.fn((name: string) => ({ name, value: "cookie-value" })),
23
+ set: vi.fn(),
24
+ delete: vi.fn(),
25
+ };
26
+
27
+ const mockRequest = new Request("https://example.com", {
28
+ headers: { cookie: "" },
29
+ });
30
+
31
+ const context: AstroServer.ServerContext = {
32
+ cookies: mockCookies as any,
33
+ request: mockRequest,
34
+ };
35
+
36
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
37
+ const result = adapter.get("test");
38
+
39
+ expect(result).toEqual({ name: "test", value: "cookie-value" });
40
+ expect(mockCookies.get).toHaveBeenCalledWith("test");
41
+ });
42
+
43
+ it("should get cookie from request headers if not in Astro cookies", async () => {
44
+ // When cookies.get returns an object without value property, it falls back to headers
45
+ const mockCookies = {
46
+ get: vi.fn(() => ({ value: undefined })),
47
+ set: vi.fn(),
48
+ delete: vi.fn(),
49
+ };
50
+
51
+ const mockRequest = new Request("https://example.com", {
52
+ headers: { cookie: "test=header-value; other=value2" },
53
+ });
54
+
55
+ const context: AstroServer.ServerContext = {
56
+ cookies: mockCookies as any,
57
+ request: mockRequest,
58
+ };
59
+
60
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
61
+ const result = adapter.get("test");
62
+
63
+ expect(result).toEqual({ name: "test", value: "header-value" });
64
+ });
65
+
66
+ it("should handle URL-encoded cookie values", async () => {
67
+ const mockCookies = {
68
+ get: vi.fn(() => ({ value: undefined })),
69
+ set: vi.fn(),
70
+ delete: vi.fn(),
71
+ };
72
+
73
+ const mockRequest = new Request("https://example.com", {
74
+ headers: { cookie: "test=hello%20world" },
75
+ });
76
+
77
+ const context: AstroServer.ServerContext = {
78
+ cookies: mockCookies as any,
79
+ request: mockRequest,
80
+ };
81
+
82
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
83
+ const result = adapter.get("test");
84
+
85
+ expect(result).toEqual({ name: "test", value: "hello world" });
86
+ });
87
+
88
+ it("should get all cookies from getAll method", async () => {
89
+ const mockCookies = {
90
+ get: vi.fn(),
91
+ getAll: vi.fn(() => [
92
+ { name: "cookie1", value: "value1" },
93
+ { name: "cookie2", value: "value2" },
94
+ ]),
95
+ set: vi.fn(),
96
+ delete: vi.fn(),
97
+ };
98
+
99
+ const mockRequest = new Request("https://example.com", {
100
+ headers: { cookie: "" },
101
+ });
102
+
103
+ const context: AstroServer.ServerContext = {
104
+ cookies: mockCookies as any,
105
+ request: mockRequest,
106
+ };
107
+
108
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
109
+ const result = adapter.getAll();
110
+
111
+ expect(result).toEqual([
112
+ { name: "cookie1", value: "value1" },
113
+ { name: "cookie2", value: "value2" },
114
+ ]);
115
+ });
116
+
117
+ it("should get all cookies from headers when getAll is not available", async () => {
118
+ const mockCookies = {
119
+ get: vi.fn(),
120
+ set: vi.fn(),
121
+ delete: vi.fn(),
122
+ // No getAll method
123
+ };
124
+
125
+ const mockRequest = new Request("https://example.com", {
126
+ headers: { cookie: "cookie1=value1; cookie2=value2" },
127
+ });
128
+
129
+ const context: AstroServer.ServerContext = {
130
+ cookies: mockCookies as any,
131
+ request: mockRequest,
132
+ };
133
+
134
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
135
+ const result = adapter.getAll();
136
+
137
+ expect(result).toEqual([
138
+ { name: "cookie1", value: "value1" },
139
+ { name: "cookie2", value: "value2" },
140
+ ]);
141
+ });
142
+
143
+ it("should set cookie with options", async () => {
144
+ const mockCookies = {
145
+ get: vi.fn(),
146
+ set: vi.fn(),
147
+ delete: vi.fn(),
148
+ };
149
+
150
+ const mockRequest = new Request("https://example.com", {
151
+ headers: { cookie: "" },
152
+ });
153
+
154
+ const context: AstroServer.ServerContext = {
155
+ cookies: mockCookies as any,
156
+ request: mockRequest,
157
+ };
158
+
159
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
160
+ adapter.set("test", "value", {
161
+ httpOnly: true,
162
+ secure: true,
163
+ sameSite: "lax",
164
+ });
165
+
166
+ expect(mockCookies.set).toHaveBeenCalledWith("test", "value", {
167
+ httpOnly: true,
168
+ secure: true,
169
+ sameSite: "lax",
170
+ });
171
+ });
172
+
173
+ it("should map sameSite boolean values", async () => {
174
+ const mockCookies = {
175
+ get: vi.fn(),
176
+ set: vi.fn(),
177
+ delete: vi.fn(),
178
+ };
179
+
180
+ const mockRequest = new Request("https://example.com", {
181
+ headers: { cookie: "" },
182
+ });
183
+
184
+ const context: AstroServer.ServerContext = {
185
+ cookies: mockCookies as any,
186
+ request: mockRequest,
187
+ };
188
+
189
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
190
+
191
+ // sameSite: true -> 'strict'
192
+ adapter.set("test1", "value1", { sameSite: true as any });
193
+ expect(mockCookies.set).toHaveBeenCalledWith("test1", "value1", {
194
+ sameSite: "strict",
195
+ });
196
+
197
+ // sameSite: false -> 'lax'
198
+ adapter.set("test2", "value2", { sameSite: false as any });
199
+ expect(mockCookies.set).toHaveBeenCalledWith("test2", "value2", {
200
+ sameSite: "lax",
201
+ });
202
+ });
203
+
204
+ it("should delete cookie", async () => {
205
+ const mockCookies = {
206
+ get: vi.fn(),
207
+ set: vi.fn(),
208
+ delete: vi.fn(),
209
+ };
210
+
211
+ const mockRequest = new Request("https://example.com", {
212
+ headers: { cookie: "" },
213
+ });
214
+
215
+ const context: AstroServer.ServerContext = {
216
+ cookies: mockCookies as any,
217
+ request: mockRequest,
218
+ };
219
+
220
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
221
+ adapter.delete("test");
222
+
223
+ expect(mockCookies.delete).toHaveBeenCalledWith("test");
224
+ });
225
+
226
+ it("should handle errors gracefully when setting cookies", async () => {
227
+ const mockCookies = {
228
+ get: vi.fn(),
229
+ set: vi.fn(() => {
230
+ throw new Error("Cookie error");
231
+ }),
232
+ delete: vi.fn(),
233
+ };
234
+
235
+ const mockRequest = new Request("https://example.com", {
236
+ headers: { cookie: "" },
237
+ });
238
+
239
+ const context: AstroServer.ServerContext = {
240
+ cookies: mockCookies as any,
241
+ request: mockRequest,
242
+ };
243
+
244
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
245
+
246
+ // Should not throw
247
+ expect(() => adapter.set("test", "value")).not.toThrow();
248
+ });
249
+
250
+ it("should handle errors gracefully when deleting cookies", async () => {
251
+ const mockCookies = {
252
+ get: vi.fn(),
253
+ set: vi.fn(),
254
+ delete: vi.fn(() => {
255
+ throw new Error("Delete error");
256
+ }),
257
+ };
258
+
259
+ const mockRequest = new Request("https://example.com", {
260
+ headers: { cookie: "" },
261
+ });
262
+
263
+ const context: AstroServer.ServerContext = {
264
+ cookies: mockCookies as any,
265
+ request: mockRequest,
266
+ };
267
+
268
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
269
+
270
+ // Should not throw
271
+ expect(() => adapter.delete("test")).not.toThrow();
272
+ });
273
+
274
+ it("should return undefined when cookie not found anywhere", async () => {
275
+ const mockCookies = {
276
+ get: vi.fn(() => ({ value: undefined })),
277
+ set: vi.fn(),
278
+ delete: vi.fn(),
279
+ };
280
+
281
+ const mockRequest = new Request("https://example.com", {
282
+ headers: { cookie: "" },
283
+ });
284
+
285
+ const context: AstroServer.ServerContext = {
286
+ cookies: mockCookies as any,
287
+ request: mockRequest,
288
+ };
289
+
290
+ const adapter = await createCookieStorageAdapterFromAstroContext(context);
291
+ const result = adapter.get("nonexistent");
292
+
293
+ expect(result).toBeUndefined();
294
+ });
295
+ });
296
+ });
@@ -0,0 +1,97 @@
1
+ import type { CookieStorage } from 'aws-amplify/adapter-core';
2
+ import type { AstroServer } from './types';
3
+ import { createLogger } from '../utils/logger';
4
+
5
+ const log = createLogger('cookie-storage-adapter');
6
+
7
+ // Ensures the cookie names are encoded in order to look up the cookie store
8
+ // that is manipulated by js-cookie on the client side.
9
+ // Details of the js-cookie encoding behavior see:
10
+ // https://github.com/js-cookie/js-cookie#encoding
11
+ // The implementation is borrowed from js-cookie without escaping `[()]` as
12
+ // we are not using those chars in the auth keys.
13
+ function ensureEncodedForJSCookie(name: string): string {
14
+ return encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent);
15
+ }
16
+
17
+ function parseCookieHeader(cookieHeader: string | null) {
18
+ const out: Record<string, string> = {};
19
+ if (!cookieHeader) return out;
20
+ for (const part of cookieHeader.split(';')) {
21
+ const [rawName, ...rest] = part.trim().split('=');
22
+ const name = decodeURIComponent(rawName || '');
23
+ const value = decodeURIComponent(rest.join('=') || '');
24
+ if (name) out[name] = value;
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function mapSameSite(
30
+ s: CookieStorage.SetCookieOptions['sameSite']
31
+ ): 'lax' | 'strict' | 'none' | undefined {
32
+ if (s === true) return 'strict';
33
+ if (s === false) return 'lax';
34
+ if (s === 'lax' || s === 'strict' || s === 'none') return s;
35
+ return undefined;
36
+ }
37
+
38
+ export async function createCookieStorageAdapterFromAstroContext(
39
+ astroServerContext: AstroServer.ServerContext
40
+ ): Promise<CookieStorage.Adapter> {
41
+ if (astroServerContext === null) {
42
+ log.debug('Context is null, returning no-op adapter');
43
+ return {
44
+ get: () => undefined,
45
+ getAll: () => [],
46
+ set: () => {},
47
+ delete: () => {},
48
+ };
49
+ }
50
+
51
+ const { cookies, request } = astroServerContext;
52
+ const cookieHeader = request?.headers?.get('cookie');
53
+ const headerCookies = parseCookieHeader(cookieHeader ?? null);
54
+
55
+ // Debug: Log available cookies (names only for security)
56
+ const cookieNames = Object.keys(headerCookies);
57
+ const cognitoCookies = cookieNames.filter(name => name.includes('CognitoIdentityServiceProvider'));
58
+ log.debug('Available cookies:', cookieNames.length);
59
+ log.debug('Cognito cookies found:', cognitoCookies.length);
60
+ if (cognitoCookies.length > 0) {
61
+ log.debug('Cognito cookie names:', cognitoCookies);
62
+ }
63
+
64
+ const adapter: CookieStorage.Adapter = {
65
+ get(name) {
66
+ const encodedName = ensureEncodedForJSCookie(name);
67
+ const v = cookies?.get(encodedName)?.value ?? headerCookies[encodedName] ?? headerCookies[name];
68
+ // Debug: Log token lookups (only for auth-related cookies)
69
+ if (name.includes('accessToken') || name.includes('idToken') || name.includes('refreshToken') || name.includes('LastAuthUser')) {
70
+ log.debug(`get('${name}'): ${v ? 'FOUND' : 'NOT FOUND'}`);
71
+ }
72
+ return v ? { name, value: v } : undefined;
73
+ },
74
+ getAll() {
75
+ const fromAPI =
76
+ (typeof (cookies as any)?.getAll === 'function'
77
+ ? (cookies as any).getAll().map((c: any) => ({ name: c.name, value: c.value }))
78
+ : Object.entries(headerCookies).map(([name, value]) => ({ name, value })));
79
+ return fromAPI;
80
+ },
81
+ set(name, value, options) {
82
+ try {
83
+ (cookies as any).set(name, value, {
84
+ ...(options ?? {}),
85
+ sameSite: mapSameSite(options?.sameSite),
86
+ });
87
+ } catch {}
88
+ },
89
+ delete(name: string) {
90
+ try {
91
+ (cookies as any).delete(name);
92
+ } catch {}
93
+ },
94
+ };
95
+
96
+ return adapter;
97
+ }
@@ -0,0 +1,84 @@
1
+ import type { ResourcesConfig } from 'aws-amplify';
2
+ import { sharedInMemoryStorage } from 'aws-amplify/utils';
3
+ import {
4
+ createAWSCredentialsAndIdentityIdProvider,
5
+ createKeyValueStorageFromCookieStorageAdapter,
6
+ createUserPoolsTokenProvider,
7
+ runWithAmplifyServerContext as coreRunWithContext,
8
+ } from 'aws-amplify/adapter-core';
9
+
10
+ import type { AstroServer } from './types';
11
+ import { globalSettings as defaultGlobalSettings } from './globalSettings';
12
+ import { createCookieStorageAdapterFromAstroContext } from './createCookieStorageAdapterFromAstroContext';
13
+ import { createLogger } from '../utils/logger';
14
+
15
+ const log = createLogger('amplify-server-context');
16
+
17
+ /**
18
+ * Creates a function that runs operations within the Amplify server context.
19
+ *
20
+ * IMPORTANT: Pass globalSettings explicitly to avoid module singleton issues
21
+ * when using linked packages or different bundling contexts.
22
+ */
23
+ export const createRunWithAmplifyServerContext = ({
24
+ config: resourcesConfig,
25
+ globalSettings = defaultGlobalSettings,
26
+ }: {
27
+ config: ResourcesConfig;
28
+ globalSettings?: AstroServer.GlobalSettings;
29
+ }): AstroServer.RunOperationWithContext => {
30
+ const isServerSideAuthEnabled = globalSettings.isServerSideAuthEnabled();
31
+ const isSSLOrigin = globalSettings.isSSLOrigin();
32
+ const setCookieOptions = globalSettings.getRuntimeOptions().cookies ?? {};
33
+
34
+ log.debug('Settings:', {
35
+ isServerSideAuthEnabled,
36
+ isSSLOrigin,
37
+ hasCookieOptions: Object.keys(setCookieOptions).length > 0,
38
+ });
39
+
40
+ const mergedSetCookieOptions = {
41
+ ...(isServerSideAuthEnabled && { httpOnly: true, sameSite: 'lax' as const }),
42
+ ...setCookieOptions,
43
+ ...(isServerSideAuthEnabled && { secure: isSSLOrigin }),
44
+ path: '/',
45
+ };
46
+
47
+ const runWithContext: AstroServer.RunOperationWithContext = async ({
48
+ astroServerContext,
49
+ operation,
50
+ }) => {
51
+ if (resourcesConfig.Auth) {
52
+ const cookieAdapter = await createCookieStorageAdapterFromAstroContext(astroServerContext);
53
+
54
+ const keyValueStorage =
55
+ astroServerContext === null
56
+ ? sharedInMemoryStorage
57
+ : createKeyValueStorageFromCookieStorageAdapter(
58
+ cookieAdapter,
59
+ undefined,
60
+ mergedSetCookieOptions
61
+ );
62
+
63
+ const credentialsProvider = createAWSCredentialsAndIdentityIdProvider(
64
+ resourcesConfig.Auth,
65
+ keyValueStorage
66
+ );
67
+
68
+ const tokenProvider = createUserPoolsTokenProvider(
69
+ resourcesConfig.Auth,
70
+ keyValueStorage
71
+ );
72
+
73
+ return coreRunWithContext(
74
+ resourcesConfig,
75
+ { Auth: { credentialsProvider, tokenProvider } },
76
+ operation
77
+ );
78
+ }
79
+
80
+ return coreRunWithContext(resourcesConfig, {}, operation);
81
+ };
82
+
83
+ return runWithContext;
84
+ };