@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.
Files changed (45) hide show
  1. package/README.md +51 -0
  2. package/dist/amplify-astro-adapter/index.d.ts +109 -0
  3. package/dist/amplify-astro-adapter/index.js +295 -0
  4. package/dist/amplify-astro-adapter/index.js.map +1 -0
  5. package/dist/auth/index.d.ts +1 -1
  6. package/dist/auth/index.js +305 -1
  7. package/dist/auth/index.js.map +1 -1
  8. package/dist/index.d.ts +220 -0
  9. package/dist/index.js +426 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/logger-BTW3fOeM.d.ts +45 -0
  12. package/dist/utils/index.d.ts +3 -0
  13. package/dist/utils/index.js +55 -0
  14. package/dist/utils/index.js.map +1 -1
  15. package/package.json +56 -33
  16. package/src/amplify-astro-adapter/amplify-astro-adapter.md +167 -0
  17. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.test.ts +296 -0
  18. package/src/amplify-astro-adapter/createCookieStorageAdapterFromAstroContext.ts +97 -0
  19. package/src/amplify-astro-adapter/createRunWithAmplifyServerContext.ts +84 -0
  20. package/src/amplify-astro-adapter/errors.test.ts +115 -0
  21. package/src/amplify-astro-adapter/errors.ts +105 -0
  22. package/src/amplify-astro-adapter/globalSettings.test.ts +78 -0
  23. package/src/amplify-astro-adapter/globalSettings.ts +16 -0
  24. package/src/amplify-astro-adapter/index.ts +14 -0
  25. package/src/amplify-astro-adapter/types.ts +55 -0
  26. package/src/auth/auth.md +178 -0
  27. package/src/auth/index.test.ts +180 -0
  28. package/src/auth/index.ts +294 -0
  29. package/src/constants/constants.md +132 -0
  30. package/src/constants/index.test.ts +116 -0
  31. package/src/constants/index.ts +98 -0
  32. package/src/core-exports.property.test.ts +186 -0
  33. package/src/errors/errors.md +177 -0
  34. package/src/errors/index.test.ts +153 -0
  35. package/src/errors/index.ts +134 -0
  36. package/src/index.ts +65 -0
  37. package/src/routes/index.ts +225 -0
  38. package/src/routes/routes.md +189 -0
  39. package/src/types/index.ts +94 -0
  40. package/src/types/types.md +144 -0
  41. package/src/utils/index.test.ts +257 -0
  42. package/src/utils/index.ts +112 -0
  43. package/src/utils/logger.ts +88 -0
  44. package/src/utils/utils.md +199 -0
  45. package/src/workspace.property.test.ts +235 -0
@@ -1,7 +1,10 @@
1
+ export { c as createLogger, l as logger } from '../logger-BTW3fOeM.js';
2
+
1
3
  /**
2
4
  * @htlkg/core/utils
3
5
  * Core utility functions for Hotelinking applications
4
6
  */
7
+
5
8
  /**
6
9
  * Format a date to a localized string
7
10
  * @param date - Date to format
@@ -1,3 +1,56 @@
1
+ // src/utils/logger.ts
2
+ var isDebugEnabled = () => {
3
+ if (typeof process !== "undefined" && process.env) {
4
+ return process.env.DEBUG === "true" || process.env.HTLKG_DEBUG === "true" || process.env.DEBUG === "*";
5
+ }
6
+ try {
7
+ if (typeof import.meta !== "undefined" && import.meta.env) {
8
+ return import.meta.env.DEBUG === "true" || // @ts-ignore
9
+ import.meta.env.HTLKG_DEBUG === "true" || // @ts-ignore
10
+ import.meta.env.DEV === true;
11
+ }
12
+ } catch {
13
+ }
14
+ return false;
15
+ };
16
+ var formatMessage = (namespace, message) => {
17
+ return `[${namespace}] ${message}`;
18
+ };
19
+ var logger = {
20
+ /**
21
+ * Debug log - only shown when DEBUG=true
22
+ */
23
+ debug(namespace, message, ...args) {
24
+ if (isDebugEnabled()) {
25
+ console.log(formatMessage(namespace, message), ...args);
26
+ }
27
+ },
28
+ /**
29
+ * Info log - always shown
30
+ */
31
+ info(namespace, message, ...args) {
32
+ console.info(formatMessage(namespace, message), ...args);
33
+ },
34
+ /**
35
+ * Warning log - always shown
36
+ */
37
+ warn(namespace, message, ...args) {
38
+ console.warn(formatMessage(namespace, message), ...args);
39
+ },
40
+ /**
41
+ * Error log - always shown
42
+ */
43
+ error(namespace, message, ...args) {
44
+ console.error(formatMessage(namespace, message), ...args);
45
+ }
46
+ };
47
+ var createLogger = (namespace) => ({
48
+ debug: (message, ...args) => logger.debug(namespace, message, ...args),
49
+ info: (message, ...args) => logger.info(namespace, message, ...args),
50
+ warn: (message, ...args) => logger.warn(namespace, message, ...args),
51
+ error: (message, ...args) => logger.error(namespace, message, ...args)
52
+ });
53
+
1
54
  // src/utils/index.ts
2
55
  function formatDate(date, locale = "en-US", options = {
3
56
  year: "numeric",
@@ -49,9 +102,11 @@ function throttle(fn, limit) {
49
102
  };
50
103
  }
51
104
  export {
105
+ createLogger,
52
106
  debounce,
53
107
  formatDate,
54
108
  groupBy,
109
+ logger,
55
110
  throttle,
56
111
  truncateText
57
112
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["/**\n * @htlkg/core/utils\n * Core utility functions for Hotelinking applications\n */\n\n/**\n * Format a date to a localized string\n * @param date - Date to format\n * @param locale - Locale string (default: 'en-US')\n * @param options - Intl.DateTimeFormatOptions\n * @returns Formatted date string\n */\nexport function formatDate(\n\tdate: Date | string | number,\n\tlocale = \"en-US\",\n\toptions: Intl.DateTimeFormatOptions = {\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t},\n): string {\n\tconst dateObj = typeof date === \"string\" || typeof date === \"number\" ? new Date(date) : date;\n\treturn new Intl.DateTimeFormat(locale, options).format(dateObj);\n}\n\n/**\n * Truncate text to a maximum length with ellipsis\n * @param text - Text to truncate\n * @param maxLength - Maximum length before truncation\n * @param suffix - Suffix to append (default: '...')\n * @returns Truncated text\n */\nexport function truncateText(\n\ttext: string,\n\tmaxLength: number,\n\tsuffix = \"...\",\n): string {\n\tif (text.length <= maxLength) return text;\n\treturn text.slice(0, maxLength - suffix.length) + suffix;\n}\n\n/**\n * Group an array of items by a key\n * @param items - Array of items to group\n * @param keyFn - Function to extract the grouping key\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(\n\titems: T[],\n\tkeyFn: (item: T) => string | number,\n): Record<string | number, T[]> {\n\treturn items.reduce(\n\t\t(groups, item) => {\n\t\t\tconst key = keyFn(item);\n\t\t\tif (!groups[key]) {\n\t\t\t\tgroups[key] = [];\n\t\t\t}\n\t\t\tgroups[key].push(item);\n\t\t\treturn groups;\n\t\t},\n\t\t{} as Record<string | number, T[]>,\n\t);\n}\n\n/**\n * Debounce a function call\n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: any[]) => any>(\n\tfn: T,\n\tdelay: number,\n): (...args: Parameters<T>) => void {\n\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\treturn function debounced(...args: Parameters<T>) {\n\t\tif (timeoutId !== null) {\n\t\t\tclearTimeout(timeoutId);\n\t\t}\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tfn(...args);\n\t\t\ttimeoutId = null;\n\t\t}, delay);\n\t};\n}\n\n/**\n * Throttle a function call\n * @param fn - Function to throttle\n * @param limit - Minimum time between calls in milliseconds\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: any[]) => any>(\n\tfn: T,\n\tlimit: number,\n): (...args: Parameters<T>) => void {\n\tlet inThrottle = false;\n\n\treturn function throttled(...args: Parameters<T>) {\n\t\tif (!inThrottle) {\n\t\t\tfn(...args);\n\t\t\tinThrottle = true;\n\t\t\tsetTimeout(() => {\n\t\t\t\tinThrottle = false;\n\t\t\t}, limit);\n\t\t}\n\t};\n}\n"],"mappings":";AAYO,SAAS,WACf,MACA,SAAS,SACT,UAAsC;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AACN,GACS;AACT,QAAM,UAAU,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,IAAI;AACxF,SAAO,IAAI,KAAK,eAAe,QAAQ,OAAO,EAAE,OAAO,OAAO;AAC/D;AASO,SAAS,aACf,MACA,WACA,SAAS,OACA;AACT,MAAI,KAAK,UAAU,UAAW,QAAO;AACrC,SAAO,KAAK,MAAM,GAAG,YAAY,OAAO,MAAM,IAAI;AACnD;AAQO,SAAS,QACf,OACA,OAC+B;AAC/B,SAAO,MAAM;AAAA,IACZ,CAAC,QAAQ,SAAS;AACjB,YAAM,MAAM,MAAM,IAAI;AACtB,UAAI,CAAC,OAAO,GAAG,GAAG;AACjB,eAAO,GAAG,IAAI,CAAC;AAAA,MAChB;AACA,aAAO,GAAG,EAAE,KAAK,IAAI;AACrB,aAAO;AAAA,IACR;AAAA,IACA,CAAC;AAAA,EACF;AACD;AAQO,SAAS,SACf,IACA,OACmC;AACnC,MAAI,YAAkD;AAEtD,SAAO,SAAS,aAAa,MAAqB;AACjD,QAAI,cAAc,MAAM;AACvB,mBAAa,SAAS;AAAA,IACvB;AACA,gBAAY,WAAW,MAAM;AAC5B,SAAG,GAAG,IAAI;AACV,kBAAY;AAAA,IACb,GAAG,KAAK;AAAA,EACT;AACD;AAQO,SAAS,SACf,IACA,OACmC;AACnC,MAAI,aAAa;AAEjB,SAAO,SAAS,aAAa,MAAqB;AACjD,QAAI,CAAC,YAAY;AAChB,SAAG,GAAG,IAAI;AACV,mBAAa;AACb,iBAAW,MAAM;AAChB,qBAAa;AAAA,MACd,GAAG,KAAK;AAAA,IACT;AAAA,EACD;AACD;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/logger.ts","../../src/utils/index.ts"],"sourcesContent":["/**\n * Simple debug logger utility\n * \n * Debug logs are only shown when DEBUG=true or HTLKG_DEBUG=true is set in environment.\n * Info logs are always shown.\n * \n * @example\n * ```typescript\n * import { logger } from '@htlkg/core/utils/logger';\n * \n * logger.debug('server-client', 'Detailed debug info', { data });\n * logger.info('server-client', 'Important info message');\n * logger.warn('server-client', 'Warning message');\n * logger.error('server-client', 'Error occurred', error);\n * ```\n */\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst isDebugEnabled = (): boolean => {\n // Check Node.js environment variables\n if (typeof process !== 'undefined' && process.env) {\n return process.env.DEBUG === 'true' || \n process.env.HTLKG_DEBUG === 'true' ||\n process.env.DEBUG === '*';\n }\n // Browser/Vite environment - check for DEV mode\n try {\n // @ts-ignore - import.meta.env is Vite-specific\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n // @ts-ignore\n return import.meta.env.DEBUG === 'true' || \n // @ts-ignore\n import.meta.env.HTLKG_DEBUG === 'true' ||\n // @ts-ignore\n import.meta.env.DEV === true;\n }\n } catch {\n // Ignore errors accessing import.meta\n }\n return false;\n};\n\nconst formatMessage = (namespace: string, message: string): string => {\n return `[${namespace}] ${message}`;\n};\n\nexport const logger = {\n /**\n * Debug log - only shown when DEBUG=true\n */\n debug(namespace: string, message: string, ...args: unknown[]): void {\n if (isDebugEnabled()) {\n console.log(formatMessage(namespace, message), ...args);\n }\n },\n\n /**\n * Info log - always shown\n */\n info(namespace: string, message: string, ...args: unknown[]): void {\n console.info(formatMessage(namespace, message), ...args);\n },\n\n /**\n * Warning log - always shown\n */\n warn(namespace: string, message: string, ...args: unknown[]): void {\n console.warn(formatMessage(namespace, message), ...args);\n },\n\n /**\n * Error log - always shown\n */\n error(namespace: string, message: string, ...args: unknown[]): void {\n console.error(formatMessage(namespace, message), ...args);\n },\n};\n\n/**\n * Create a namespaced logger for a specific module\n */\nexport const createLogger = (namespace: string) => ({\n debug: (message: string, ...args: unknown[]) => logger.debug(namespace, message, ...args),\n info: (message: string, ...args: unknown[]) => logger.info(namespace, message, ...args),\n warn: (message: string, ...args: unknown[]) => logger.warn(namespace, message, ...args),\n error: (message: string, ...args: unknown[]) => logger.error(namespace, message, ...args),\n});\n","/**\n * @htlkg/core/utils\n * Core utility functions for Hotelinking applications\n */\n\n// Logger utility\nexport { logger, createLogger } from './logger';\n\n/**\n * Format a date to a localized string\n * @param date - Date to format\n * @param locale - Locale string (default: 'en-US')\n * @param options - Intl.DateTimeFormatOptions\n * @returns Formatted date string\n */\nexport function formatDate(\n\tdate: Date | string | number,\n\tlocale = \"en-US\",\n\toptions: Intl.DateTimeFormatOptions = {\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t},\n): string {\n\tconst dateObj = typeof date === \"string\" || typeof date === \"number\" ? new Date(date) : date;\n\treturn new Intl.DateTimeFormat(locale, options).format(dateObj);\n}\n\n/**\n * Truncate text to a maximum length with ellipsis\n * @param text - Text to truncate\n * @param maxLength - Maximum length before truncation\n * @param suffix - Suffix to append (default: '...')\n * @returns Truncated text\n */\nexport function truncateText(\n\ttext: string,\n\tmaxLength: number,\n\tsuffix = \"...\",\n): string {\n\tif (text.length <= maxLength) return text;\n\treturn text.slice(0, maxLength - suffix.length) + suffix;\n}\n\n/**\n * Group an array of items by a key\n * @param items - Array of items to group\n * @param keyFn - Function to extract the grouping key\n * @returns Object with keys as group names and values as arrays of items\n */\nexport function groupBy<T>(\n\titems: T[],\n\tkeyFn: (item: T) => string | number,\n): Record<string | number, T[]> {\n\treturn items.reduce(\n\t\t(groups, item) => {\n\t\t\tconst key = keyFn(item);\n\t\t\tif (!groups[key]) {\n\t\t\t\tgroups[key] = [];\n\t\t\t}\n\t\t\tgroups[key].push(item);\n\t\t\treturn groups;\n\t\t},\n\t\t{} as Record<string | number, T[]>,\n\t);\n}\n\n/**\n * Debounce a function call\n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced function\n */\nexport function debounce<T extends (...args: any[]) => any>(\n\tfn: T,\n\tdelay: number,\n): (...args: Parameters<T>) => void {\n\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\treturn function debounced(...args: Parameters<T>) {\n\t\tif (timeoutId !== null) {\n\t\t\tclearTimeout(timeoutId);\n\t\t}\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tfn(...args);\n\t\t\ttimeoutId = null;\n\t\t}, delay);\n\t};\n}\n\n/**\n * Throttle a function call\n * @param fn - Function to throttle\n * @param limit - Minimum time between calls in milliseconds\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: any[]) => any>(\n\tfn: T,\n\tlimit: number,\n): (...args: Parameters<T>) => void {\n\tlet inThrottle = false;\n\n\treturn function throttled(...args: Parameters<T>) {\n\t\tif (!inThrottle) {\n\t\t\tfn(...args);\n\t\t\tinThrottle = true;\n\t\t\tsetTimeout(() => {\n\t\t\t\tinThrottle = false;\n\t\t\t}, limit);\n\t\t}\n\t};\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,34 +1,57 @@
1
1
  {
2
- "name": "@htlkg/core",
3
- "version": "0.0.1",
4
- "type": "module",
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
- },
13
- "files": [
14
- "dist"
15
- ],
16
- "scripts": {
17
- "build": "tsup",
18
- "dev": "tsup --watch",
19
- "test": "vitest run",
20
- "test:watch": "vitest"
21
- },
22
- "dependencies": {
23
- "aws-amplify": "^6.15.7"
24
- },
25
- "devDependencies": {
26
- "astro": "^5.14.7",
27
- "tsup": "^8.0.0",
28
- "typescript": "^5.9.2",
29
- "vitest": "^3.2.4"
30
- },
31
- "publishConfig": {
32
- "access": "restricted"
33
- }
34
- }
2
+ "name": "@htlkg/core",
3
+ "version": "0.0.3",
4
+ "type": "module",
5
+ "exports": {
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
+ }
34
+ },
35
+ "files": [
36
+ "src",
37
+ "dist"
38
+ ],
39
+ "dependencies": {
40
+ "aws-amplify": "^6.15.7"
41
+ },
42
+ "devDependencies": {
43
+ "astro": "^5.14.7",
44
+ "tsup": "^8.0.0",
45
+ "typescript": "^5.9.2",
46
+ "vitest": "^3.2.4"
47
+ },
48
+ "publishConfig": {
49
+ "access": "restricted"
50
+ },
51
+ "scripts": {
52
+ "build": "tsup",
53
+ "dev": "tsup --watch",
54
+ "test": "vitest run",
55
+ "test:watch": "vitest"
56
+ }
57
+ }
@@ -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
+ });