@htlkg/core 0.0.1 → 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.
- package/README.md +51 -0
- package/dist/amplify-astro-adapter/index.d.ts +109 -0
- package/dist/amplify-astro-adapter/index.js +295 -0
- package/dist/amplify-astro-adapter/index.js.map +1 -0
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.js +305 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/index.d.ts +220 -0
- package/dist/index.js +426 -1
- package/dist/index.js.map +1 -1
- package/dist/logger-BTW3fOeM.d.ts +45 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +55 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +56 -33
- package/src/amplify-astro-adapter/amplify-astro-adapter.md +167 -0
- package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.test.ts +296 -0
- package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts +97 -0
- package/src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts +84 -0
- package/src/amplify-astro-adapter/errors.test.ts +115 -0
- package/src/amplify-astro-adapter/errors.ts +105 -0
- package/src/amplify-astro-adapter/globalSettings.test.ts +78 -0
- package/src/amplify-astro-adapter/globalSettings.ts +16 -0
- package/src/amplify-astro-adapter/index.ts +14 -0
- package/src/amplify-astro-adapter/types.ts +55 -0
- package/src/auth/auth.md +178 -0
- package/src/auth/index.test.ts +180 -0
- package/src/auth/index.ts +294 -0
- package/src/constants/constants.md +132 -0
- package/src/constants/index.test.ts +116 -0
- package/src/constants/index.ts +98 -0
- package/src/core-exports.property.test.ts +186 -0
- package/src/errors/errors.md +177 -0
- package/src/errors/index.test.ts +153 -0
- package/src/errors/index.ts +134 -0
- package/src/index.ts +65 -0
- package/src/routes/index.ts +225 -0
- package/src/routes/routes.md +189 -0
- package/src/types/index.ts +94 -0
- package/src/types/types.md +144 -0
- package/src/utils/index.test.ts +257 -0
- package/src/utils/index.ts +112 -0
- package/src/utils/logger.ts +88 -0
- package/src/utils/utils.md +199 -0
- package/src/workspace.property.test.ts +235 -0
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
AmplifyAstroAdapterError,
|
|
4
|
+
ErrorCodes,
|
|
5
|
+
createConfigMissingError,
|
|
6
|
+
createAuthFailedError,
|
|
7
|
+
createCookieError,
|
|
8
|
+
createGraphQLError,
|
|
9
|
+
createContextCreationError,
|
|
10
|
+
createTokenRefreshError,
|
|
11
|
+
} from "./errors";
|
|
12
|
+
|
|
13
|
+
describe("AmplifyAstroAdapterError", () => {
|
|
14
|
+
it("should create error with message and code", () => {
|
|
15
|
+
const error = new AmplifyAstroAdapterError(
|
|
16
|
+
"Test error",
|
|
17
|
+
"TEST_CODE"
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(error.message).toBe("Test error");
|
|
21
|
+
expect(error.code).toBe("TEST_CODE");
|
|
22
|
+
expect(error.name).toBe("AmplifyAstroAdapterError");
|
|
23
|
+
expect(error.recoverySuggestion).toBeUndefined();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should create error with recovery suggestion", () => {
|
|
27
|
+
const error = new AmplifyAstroAdapterError(
|
|
28
|
+
"Test error",
|
|
29
|
+
"TEST_CODE",
|
|
30
|
+
"Try this fix"
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(error.recoverySuggestion).toBe("Try this fix");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should be instanceof Error", () => {
|
|
37
|
+
const error = new AmplifyAstroAdapterError("Test", "CODE");
|
|
38
|
+
expect(error).toBeInstanceOf(Error);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("Error factory functions", () => {
|
|
43
|
+
describe("createConfigMissingError", () => {
|
|
44
|
+
it("should create config missing error", () => {
|
|
45
|
+
const error = createConfigMissingError("userPoolId");
|
|
46
|
+
|
|
47
|
+
expect(error.message).toContain("userPoolId");
|
|
48
|
+
expect(error.code).toBe(ErrorCodes.CONFIG_MISSING);
|
|
49
|
+
expect(error.recoverySuggestion).toContain("amplify_outputs.json");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("createAuthFailedError", () => {
|
|
54
|
+
it("should create auth failed error", () => {
|
|
55
|
+
const error = createAuthFailedError("Invalid token");
|
|
56
|
+
|
|
57
|
+
expect(error.message).toContain("Invalid token");
|
|
58
|
+
expect(error.code).toBe(ErrorCodes.AUTH_FAILED);
|
|
59
|
+
expect(error.recoverySuggestion).toContain("tokens are valid");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("createCookieError", () => {
|
|
64
|
+
it("should create cookie error", () => {
|
|
65
|
+
const error = createCookieError("set", "Invalid format");
|
|
66
|
+
|
|
67
|
+
expect(error.message).toContain("set");
|
|
68
|
+
expect(error.message).toContain("Invalid format");
|
|
69
|
+
expect(error.code).toBe(ErrorCodes.COOKIE_ERROR);
|
|
70
|
+
expect(error.recoverySuggestion).toContain("cookie");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("createGraphQLError", () => {
|
|
75
|
+
it("should create GraphQL error", () => {
|
|
76
|
+
const error = createGraphQLError("query", "Network error");
|
|
77
|
+
|
|
78
|
+
expect(error.message).toContain("query");
|
|
79
|
+
expect(error.message).toContain("Network error");
|
|
80
|
+
expect(error.code).toBe(ErrorCodes.GRAPHQL_ERROR);
|
|
81
|
+
expect(error.recoverySuggestion).toContain("network");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("createContextCreationError", () => {
|
|
86
|
+
it("should create context creation error", () => {
|
|
87
|
+
const error = createContextCreationError("Missing config");
|
|
88
|
+
|
|
89
|
+
expect(error.message).toContain("Missing config");
|
|
90
|
+
expect(error.code).toBe(ErrorCodes.CONTEXT_CREATION_FAILED);
|
|
91
|
+
expect(error.recoverySuggestion).toContain("Amplify configuration");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("createTokenRefreshError", () => {
|
|
96
|
+
it("should create token refresh error", () => {
|
|
97
|
+
const error = createTokenRefreshError("Expired refresh token");
|
|
98
|
+
|
|
99
|
+
expect(error.message).toContain("Expired refresh token");
|
|
100
|
+
expect(error.code).toBe(ErrorCodes.TOKEN_REFRESH_FAILED);
|
|
101
|
+
expect(error.recoverySuggestion).toContain("re-authenticate");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("ErrorCodes", () => {
|
|
107
|
+
it("should have all expected error codes", () => {
|
|
108
|
+
expect(ErrorCodes.CONFIG_MISSING).toBe("CONFIG_MISSING");
|
|
109
|
+
expect(ErrorCodes.AUTH_FAILED).toBe("AUTH_FAILED");
|
|
110
|
+
expect(ErrorCodes.COOKIE_ERROR).toBe("COOKIE_ERROR");
|
|
111
|
+
expect(ErrorCodes.GRAPHQL_ERROR).toBe("GRAPHQL_ERROR");
|
|
112
|
+
expect(ErrorCodes.CONTEXT_CREATION_FAILED).toBe("CONTEXT_CREATION_FAILED");
|
|
113
|
+
expect(ErrorCodes.TOKEN_REFRESH_FAILED).toBe("TOKEN_REFRESH_FAILED");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error classes for adapter-specific errors
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for Amplify Astro Adapter errors
|
|
7
|
+
*/
|
|
8
|
+
export class AmplifyAstroAdapterError extends Error {
|
|
9
|
+
public readonly code: string;
|
|
10
|
+
public readonly recoverySuggestion?: string;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
message: string,
|
|
14
|
+
code: string,
|
|
15
|
+
recoverySuggestion?: string
|
|
16
|
+
) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'AmplifyAstroAdapterError';
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.recoverySuggestion = recoverySuggestion;
|
|
21
|
+
|
|
22
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
23
|
+
if (Error.captureStackTrace) {
|
|
24
|
+
Error.captureStackTrace(this, AmplifyAstroAdapterError);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Error codes for different types of adapter errors
|
|
31
|
+
*/
|
|
32
|
+
export const ErrorCodes = {
|
|
33
|
+
CONFIG_MISSING: 'CONFIG_MISSING',
|
|
34
|
+
AUTH_FAILED: 'AUTH_FAILED',
|
|
35
|
+
COOKIE_ERROR: 'COOKIE_ERROR',
|
|
36
|
+
GRAPHQL_ERROR: 'GRAPHQL_ERROR',
|
|
37
|
+
CONTEXT_CREATION_FAILED: 'CONTEXT_CREATION_FAILED',
|
|
38
|
+
TOKEN_REFRESH_FAILED: 'TOKEN_REFRESH_FAILED',
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Creates a configuration missing error
|
|
43
|
+
*/
|
|
44
|
+
export function createConfigMissingError(missingConfig: string): AmplifyAstroAdapterError {
|
|
45
|
+
return new AmplifyAstroAdapterError(
|
|
46
|
+
`Amplify configuration is missing: ${missingConfig}`,
|
|
47
|
+
ErrorCodes.CONFIG_MISSING,
|
|
48
|
+
'Ensure amplify_outputs.json is properly configured and accessible to the adapter'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates an authentication failed error
|
|
54
|
+
*/
|
|
55
|
+
export function createAuthFailedError(reason: string): AmplifyAstroAdapterError {
|
|
56
|
+
return new AmplifyAstroAdapterError(
|
|
57
|
+
`Authentication failed: ${reason}`,
|
|
58
|
+
ErrorCodes.AUTH_FAILED,
|
|
59
|
+
'Check that authentication tokens are valid and not expired'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a cookie error
|
|
65
|
+
*/
|
|
66
|
+
export function createCookieError(operation: string, reason: string): AmplifyAstroAdapterError {
|
|
67
|
+
return new AmplifyAstroAdapterError(
|
|
68
|
+
`Cookie ${operation} failed: ${reason}`,
|
|
69
|
+
ErrorCodes.COOKIE_ERROR,
|
|
70
|
+
'Verify cookie data format and ensure cookies are properly set in the response'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates a GraphQL error
|
|
76
|
+
*/
|
|
77
|
+
export function createGraphQLError(operation: string, reason: string): AmplifyAstroAdapterError {
|
|
78
|
+
return new AmplifyAstroAdapterError(
|
|
79
|
+
`GraphQL ${operation} failed: ${reason}`,
|
|
80
|
+
ErrorCodes.GRAPHQL_ERROR,
|
|
81
|
+
'Check network connectivity and GraphQL schema configuration'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a context creation failed error
|
|
87
|
+
*/
|
|
88
|
+
export function createContextCreationError(reason: string): AmplifyAstroAdapterError {
|
|
89
|
+
return new AmplifyAstroAdapterError(
|
|
90
|
+
`Server context creation failed: ${reason}`,
|
|
91
|
+
ErrorCodes.CONTEXT_CREATION_FAILED,
|
|
92
|
+
'Verify Amplify configuration and authentication setup'
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Creates a token refresh failed error
|
|
98
|
+
*/
|
|
99
|
+
export function createTokenRefreshError(reason: string): AmplifyAstroAdapterError {
|
|
100
|
+
return new AmplifyAstroAdapterError(
|
|
101
|
+
`Token refresh failed: ${reason}`,
|
|
102
|
+
ErrorCodes.TOKEN_REFRESH_FAILED,
|
|
103
|
+
'User may need to re-authenticate or check refresh token validity'
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { globalSettings } from "./globalSettings";
|
|
3
|
+
|
|
4
|
+
describe("globalSettings", () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
// Reset settings before each test
|
|
7
|
+
globalSettings.setRuntimeOptions({});
|
|
8
|
+
globalSettings.setIsSSLOrigin(false);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe("runtimeOptions", () => {
|
|
12
|
+
it("should set and get runtime options", () => {
|
|
13
|
+
const options = {
|
|
14
|
+
cookies: {
|
|
15
|
+
domain: "example.com",
|
|
16
|
+
httpOnly: true,
|
|
17
|
+
secure: true,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
globalSettings.setRuntimeOptions(options);
|
|
22
|
+
expect(globalSettings.getRuntimeOptions()).toEqual(options);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should handle empty options", () => {
|
|
26
|
+
globalSettings.setRuntimeOptions({});
|
|
27
|
+
expect(globalSettings.getRuntimeOptions()).toEqual({});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should handle undefined options", () => {
|
|
31
|
+
globalSettings.setRuntimeOptions(undefined as any);
|
|
32
|
+
expect(globalSettings.getRuntimeOptions()).toEqual({});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("serverSideAuth", () => {
|
|
37
|
+
it("should be enabled by default", () => {
|
|
38
|
+
// The implementation starts with serverSideAuthEnabled = true
|
|
39
|
+
expect(globalSettings.isServerSideAuthEnabled()).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should enable server-side auth", () => {
|
|
43
|
+
globalSettings.enableServerSideAuth();
|
|
44
|
+
expect(globalSettings.isServerSideAuthEnabled()).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should remain enabled after multiple calls", () => {
|
|
48
|
+
globalSettings.enableServerSideAuth();
|
|
49
|
+
globalSettings.enableServerSideAuth();
|
|
50
|
+
expect(globalSettings.isServerSideAuthEnabled()).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("SSL origin", () => {
|
|
55
|
+
it("should start as false", () => {
|
|
56
|
+
expect(globalSettings.isSSLOrigin()).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should set SSL origin to true", () => {
|
|
60
|
+
globalSettings.setIsSSLOrigin(true);
|
|
61
|
+
expect(globalSettings.isSSLOrigin()).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should set SSL origin to false", () => {
|
|
65
|
+
globalSettings.setIsSSLOrigin(true);
|
|
66
|
+
globalSettings.setIsSSLOrigin(false);
|
|
67
|
+
expect(globalSettings.isSSLOrigin()).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should toggle SSL origin", () => {
|
|
71
|
+
expect(globalSettings.isSSLOrigin()).toBe(false);
|
|
72
|
+
globalSettings.setIsSSLOrigin(true);
|
|
73
|
+
expect(globalSettings.isSSLOrigin()).toBe(true);
|
|
74
|
+
globalSettings.setIsSSLOrigin(false);
|
|
75
|
+
expect(globalSettings.isSSLOrigin()).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AstroServer } from './types';
|
|
2
|
+
|
|
3
|
+
export const globalSettings: AstroServer.GlobalSettings = (() => {
|
|
4
|
+
let runtimeOptions: AstroServer.RuntimeOptions = {};
|
|
5
|
+
let serverSideAuthEnabled = true;
|
|
6
|
+
let sslOrigin = false;
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
setRuntimeOptions(opts) { runtimeOptions = opts ?? {}; },
|
|
10
|
+
getRuntimeOptions() { return runtimeOptions; },
|
|
11
|
+
enableServerSideAuth() { serverSideAuthEnabled = true; },
|
|
12
|
+
isServerSideAuthEnabled() { return serverSideAuthEnabled; },
|
|
13
|
+
setIsSSLOrigin(v: boolean) { sslOrigin = v; },
|
|
14
|
+
isSSLOrigin() { return sslOrigin; },
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amplify Astro Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side authentication support for Astro with AWS Amplify
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { createRunWithAmplifyServerContext } from './createRunWithAmplifyServerContext';
|
|
8
|
+
export { createCookieStorageAdapterFromAstroContext } from './createCookieStorageAdapterFromAstroContext';
|
|
9
|
+
export { globalSettings } from './globalSettings';
|
|
10
|
+
export * from './types';
|
|
11
|
+
export * from './errors';
|
|
12
|
+
|
|
13
|
+
// Re-export logger for convenience
|
|
14
|
+
export { logger, createLogger } from '../utils/logger';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ResourcesConfig } from 'aws-amplify';
|
|
2
|
+
import type { CookieStorage } from 'aws-amplify/adapter-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Amplify configuration structure matching amplify_outputs.json format
|
|
6
|
+
*/
|
|
7
|
+
export type AstroAmplifyConfig = ResourcesConfig;
|
|
8
|
+
|
|
9
|
+
export namespace AstroServer {
|
|
10
|
+
export type RuntimeCookieOptions = CookieStorage.SetCookieOptions & {
|
|
11
|
+
domain?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface RuntimeOptions {
|
|
15
|
+
cookies?: RuntimeCookieOptions;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AstroComponentContext {
|
|
19
|
+
cookies: import('astro').AstroGlobal['cookies'];
|
|
20
|
+
request: Request;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface APIEndpointContext {
|
|
24
|
+
cookies: import('astro').APIContext['cookies'];
|
|
25
|
+
request: Request;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type ServerContext =
|
|
29
|
+
| AstroComponentContext
|
|
30
|
+
| APIEndpointContext
|
|
31
|
+
| null;
|
|
32
|
+
|
|
33
|
+
export interface GlobalSettings {
|
|
34
|
+
setRuntimeOptions(opts: RuntimeOptions): void;
|
|
35
|
+
getRuntimeOptions(): RuntimeOptions;
|
|
36
|
+
enableServerSideAuth(): void;
|
|
37
|
+
isServerSideAuthEnabled(): boolean;
|
|
38
|
+
setIsSSLOrigin(val: boolean): void;
|
|
39
|
+
isSSLOrigin(): boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type RunOperationWithContext = <T>(input: {
|
|
43
|
+
astroServerContext: ServerContext;
|
|
44
|
+
operation: (contextSpec: unknown) => Promise<T>;
|
|
45
|
+
}) => Promise<T>;
|
|
46
|
+
|
|
47
|
+
export interface CreateServerRunnerInput {
|
|
48
|
+
config: ResourcesConfig;
|
|
49
|
+
runtimeOptions?: RuntimeOptions;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface CreateServerRunnerOutput {
|
|
53
|
+
runWithAmplifyServerContext: RunOperationWithContext;
|
|
54
|
+
}
|
|
55
|
+
}
|