@htlkg/astro 0.0.1

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 (70) hide show
  1. package/README.md +265 -0
  2. package/dist/chunk-33R4URZV.js +59 -0
  3. package/dist/chunk-33R4URZV.js.map +1 -0
  4. package/dist/chunk-64USRLVP.js +85 -0
  5. package/dist/chunk-64USRLVP.js.map +1 -0
  6. package/dist/chunk-WLOFOVCL.js +210 -0
  7. package/dist/chunk-WLOFOVCL.js.map +1 -0
  8. package/dist/chunk-WNMPTDCR.js +73 -0
  9. package/dist/chunk-WNMPTDCR.js.map +1 -0
  10. package/dist/chunk-Z2ZAL7KX.js +9 -0
  11. package/dist/chunk-Z2ZAL7KX.js.map +1 -0
  12. package/dist/chunk-ZQ4XMJH7.js +1 -0
  13. package/dist/chunk-ZQ4XMJH7.js.map +1 -0
  14. package/dist/htlkg/config.js +7 -0
  15. package/dist/htlkg/config.js.map +1 -0
  16. package/dist/htlkg/index.js +7 -0
  17. package/dist/htlkg/index.js.map +1 -0
  18. package/dist/index.js +64 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/middleware/index.js +168 -0
  21. package/dist/middleware/index.js.map +1 -0
  22. package/dist/utils/hydration.js +21 -0
  23. package/dist/utils/hydration.js.map +1 -0
  24. package/dist/utils/index.js +56 -0
  25. package/dist/utils/index.js.map +1 -0
  26. package/dist/utils/ssr.js +21 -0
  27. package/dist/utils/ssr.js.map +1 -0
  28. package/dist/utils/static.js +19 -0
  29. package/dist/utils/static.js.map +1 -0
  30. package/package.json +53 -0
  31. package/src/__mocks__/astro-middleware.ts +19 -0
  32. package/src/__mocks__/virtual-htlkg-config.ts +14 -0
  33. package/src/auth/LoginForm.vue +482 -0
  34. package/src/auth/LoginPage.astro +70 -0
  35. package/src/components/PageHeader.astro +145 -0
  36. package/src/components/Sidebar.astro +157 -0
  37. package/src/components/Topbar.astro +167 -0
  38. package/src/components/index.ts +9 -0
  39. package/src/htlkg/config.test.ts +165 -0
  40. package/src/htlkg/config.ts +242 -0
  41. package/src/htlkg/index.ts +245 -0
  42. package/src/htlkg/virtual-modules.test.ts +158 -0
  43. package/src/htlkg/virtual-modules.ts +81 -0
  44. package/src/index.ts +37 -0
  45. package/src/layouts/AdminLayout.astro +184 -0
  46. package/src/layouts/AuthLayout.astro +164 -0
  47. package/src/layouts/BrandLayout.astro +309 -0
  48. package/src/layouts/DefaultLayout.astro +25 -0
  49. package/src/layouts/PublicLayout.astro +153 -0
  50. package/src/layouts/index.ts +10 -0
  51. package/src/middleware/auth.ts +53 -0
  52. package/src/middleware/index.ts +31 -0
  53. package/src/middleware/route-guards.test.ts +182 -0
  54. package/src/middleware/route-guards.ts +218 -0
  55. package/src/patterns/admin/DetailPage.astro +195 -0
  56. package/src/patterns/admin/FormPage.astro +203 -0
  57. package/src/patterns/admin/ListPage.astro +178 -0
  58. package/src/patterns/admin/index.ts +9 -0
  59. package/src/patterns/brand/ConfigPage.astro +128 -0
  60. package/src/patterns/brand/PortalPage.astro +161 -0
  61. package/src/patterns/brand/index.ts +8 -0
  62. package/src/patterns/index.ts +8 -0
  63. package/src/utils/hydration.test.ts +154 -0
  64. package/src/utils/hydration.ts +151 -0
  65. package/src/utils/index.ts +9 -0
  66. package/src/utils/ssr.test.ts +235 -0
  67. package/src/utils/ssr.ts +139 -0
  68. package/src/utils/static.test.ts +144 -0
  69. package/src/utils/static.ts +144 -0
  70. package/src/vue-app-setup.ts +88 -0
@@ -0,0 +1,73 @@
1
+ // src/utils/ssr.ts
2
+ async function getServerData(astro, fetcher, options = {}) {
3
+ try {
4
+ return await fetcher();
5
+ } catch (error) {
6
+ console.error("[SSR] Error fetching data:", error);
7
+ if (options.redirectOnError) {
8
+ return astro.redirect(options.redirectOnError);
9
+ }
10
+ return options.defaultValue ?? null;
11
+ }
12
+ }
13
+ function isServerSide(astro) {
14
+ return !astro.request.headers.get("x-astro-client");
15
+ }
16
+ function getRequestHeaders(astro) {
17
+ const headers = {};
18
+ astro.request.headers.forEach((value, key) => {
19
+ headers[key] = value;
20
+ });
21
+ return headers;
22
+ }
23
+ function getQueryParams(astro) {
24
+ const params = {};
25
+ const searchParams = new URL(astro.request.url).searchParams;
26
+ searchParams.forEach((value, key) => {
27
+ params[key] = value;
28
+ });
29
+ return params;
30
+ }
31
+ function setResponseHeaders(astro, headers) {
32
+ Object.entries(headers).forEach(([key, value]) => {
33
+ astro.response.headers.set(key, value);
34
+ });
35
+ }
36
+ function setCacheControl(astro, options) {
37
+ const directives = [];
38
+ if (options.public) {
39
+ directives.push("public");
40
+ } else {
41
+ directives.push("private");
42
+ }
43
+ if (options.maxAge !== void 0) {
44
+ directives.push(`max-age=${options.maxAge}`);
45
+ }
46
+ if (options.sMaxAge !== void 0) {
47
+ directives.push(`s-maxage=${options.sMaxAge}`);
48
+ }
49
+ if (options.staleWhileRevalidate !== void 0) {
50
+ directives.push(`stale-while-revalidate=${options.staleWhileRevalidate}`);
51
+ }
52
+ astro.response.headers.set("Cache-Control", directives.join(", "));
53
+ }
54
+ function getClientIP(astro) {
55
+ const headers = astro.request.headers;
56
+ return headers.get("x-forwarded-for")?.split(",")[0].trim() || headers.get("x-real-ip") || headers.get("cf-connecting-ip") || null;
57
+ }
58
+ function isMobileDevice(astro) {
59
+ const userAgent = astro.request.headers.get("user-agent") || "";
60
+ return /mobile|android|iphone|ipad|phone/i.test(userAgent);
61
+ }
62
+
63
+ export {
64
+ getServerData,
65
+ isServerSide,
66
+ getRequestHeaders,
67
+ getQueryParams,
68
+ setResponseHeaders,
69
+ setCacheControl,
70
+ getClientIP,
71
+ isMobileDevice
72
+ };
73
+ //# sourceMappingURL=chunk-WNMPTDCR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/ssr.ts"],"sourcesContent":["/**\n * SSR Utilities\n * \n * Helper functions for server-side rendering in Astro pages.\n */\n\nimport type { AstroGlobal } from 'astro';\n\n/**\n * Get server-side data with error handling\n * \n * @example\n * ```ts\n * const data = await getServerData(Astro, async () => {\n * return await fetchBrand(brandId);\n * });\n * ```\n */\nexport async function getServerData<T>(\n astro: AstroGlobal,\n fetcher: () => Promise<T>,\n options: {\n redirectOnError?: string;\n defaultValue?: T;\n } = {}\n): Promise<T | null> {\n try {\n return await fetcher();\n } catch (error) {\n console.error('[SSR] Error fetching data:', error);\n \n if (options.redirectOnError) {\n return astro.redirect(options.redirectOnError) as any;\n }\n \n return options.defaultValue ?? null;\n }\n}\n\n/**\n * Check if request is from server-side\n */\nexport function isServerSide(astro: AstroGlobal): boolean {\n return !astro.request.headers.get('x-astro-client');\n}\n\n/**\n * Get request headers as object\n */\nexport function getRequestHeaders(astro: AstroGlobal): Record<string, string> {\n const headers: Record<string, string> = {};\n astro.request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n return headers;\n}\n\n/**\n * Get query parameters from URL\n */\nexport function getQueryParams(astro: AstroGlobal): Record<string, string> {\n const params: Record<string, string> = {};\n const searchParams = new URL(astro.request.url).searchParams;\n \n searchParams.forEach((value, key) => {\n params[key] = value;\n });\n \n return params;\n}\n\n/**\n * Set response headers\n */\nexport function setResponseHeaders(\n astro: AstroGlobal,\n headers: Record<string, string>\n): void {\n Object.entries(headers).forEach(([key, value]) => {\n astro.response.headers.set(key, value);\n });\n}\n\n/**\n * Set cache control headers\n */\nexport function setCacheControl(\n astro: AstroGlobal,\n options: {\n maxAge?: number;\n sMaxAge?: number;\n staleWhileRevalidate?: number;\n public?: boolean;\n }\n): void {\n const directives: string[] = [];\n \n if (options.public) {\n directives.push('public');\n } else {\n directives.push('private');\n }\n \n if (options.maxAge !== undefined) {\n directives.push(`max-age=${options.maxAge}`);\n }\n \n if (options.sMaxAge !== undefined) {\n directives.push(`s-maxage=${options.sMaxAge}`);\n }\n \n if (options.staleWhileRevalidate !== undefined) {\n directives.push(`stale-while-revalidate=${options.staleWhileRevalidate}`);\n }\n \n astro.response.headers.set('Cache-Control', directives.join(', '));\n}\n\n/**\n * Get client IP address\n */\nexport function getClientIP(astro: AstroGlobal): string | null {\n const headers = astro.request.headers;\n \n return (\n headers.get('x-forwarded-for')?.split(',')[0].trim() ||\n headers.get('x-real-ip') ||\n headers.get('cf-connecting-ip') ||\n null\n );\n}\n\n/**\n * Check if request is from mobile device\n */\nexport function isMobileDevice(astro: AstroGlobal): boolean {\n const userAgent = astro.request.headers.get('user-agent') || '';\n return /mobile|android|iphone|ipad|phone/i.test(userAgent);\n}\n"],"mappings":";AAkBA,eAAsB,cACpB,OACA,SACA,UAGI,CAAC,GACc;AACnB,MAAI;AACF,WAAO,MAAM,QAAQ;AAAA,EACvB,SAAS,OAAO;AACd,YAAQ,MAAM,8BAA8B,KAAK;AAEjD,QAAI,QAAQ,iBAAiB;AAC3B,aAAO,MAAM,SAAS,QAAQ,eAAe;AAAA,IAC/C;AAEA,WAAO,QAAQ,gBAAgB;AAAA,EACjC;AACF;AAKO,SAAS,aAAa,OAA6B;AACxD,SAAO,CAAC,MAAM,QAAQ,QAAQ,IAAI,gBAAgB;AACpD;AAKO,SAAS,kBAAkB,OAA4C;AAC5E,QAAM,UAAkC,CAAC;AACzC,QAAM,QAAQ,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AAC5C,YAAQ,GAAG,IAAI;AAAA,EACjB,CAAC;AACD,SAAO;AACT;AAKO,SAAS,eAAe,OAA4C;AACzE,QAAM,SAAiC,CAAC;AACxC,QAAM,eAAe,IAAI,IAAI,MAAM,QAAQ,GAAG,EAAE;AAEhD,eAAa,QAAQ,CAAC,OAAO,QAAQ;AACnC,WAAO,GAAG,IAAI;AAAA,EAChB,CAAC;AAED,SAAO;AACT;AAKO,SAAS,mBACd,OACA,SACM;AACN,SAAO,QAAQ,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAChD,UAAM,SAAS,QAAQ,IAAI,KAAK,KAAK;AAAA,EACvC,CAAC;AACH;AAKO,SAAS,gBACd,OACA,SAMM;AACN,QAAM,aAAuB,CAAC;AAE9B,MAAI,QAAQ,QAAQ;AAClB,eAAW,KAAK,QAAQ;AAAA,EAC1B,OAAO;AACL,eAAW,KAAK,SAAS;AAAA,EAC3B;AAEA,MAAI,QAAQ,WAAW,QAAW;AAChC,eAAW,KAAK,WAAW,QAAQ,MAAM,EAAE;AAAA,EAC7C;AAEA,MAAI,QAAQ,YAAY,QAAW;AACjC,eAAW,KAAK,YAAY,QAAQ,OAAO,EAAE;AAAA,EAC/C;AAEA,MAAI,QAAQ,yBAAyB,QAAW;AAC9C,eAAW,KAAK,0BAA0B,QAAQ,oBAAoB,EAAE;AAAA,EAC1E;AAEA,QAAM,SAAS,QAAQ,IAAI,iBAAiB,WAAW,KAAK,IAAI,CAAC;AACnE;AAKO,SAAS,YAAY,OAAmC;AAC7D,QAAM,UAAU,MAAM,QAAQ;AAE9B,SACE,QAAQ,IAAI,iBAAiB,GAAG,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,KACnD,QAAQ,IAAI,WAAW,KACvB,QAAQ,IAAI,kBAAkB,KAC9B;AAEJ;AAKO,SAAS,eAAe,OAA6B;AAC1D,QAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,YAAY,KAAK;AAC7D,SAAO,oCAAoC,KAAK,SAAS;AAC3D;","names":[]}
@@ -0,0 +1,9 @@
1
+ // src/htlkg/config.ts
2
+ function isAuthenticatedUser(user) {
3
+ return user !== null && typeof user === "object" && "username" in user && "email" in user && "brandIds" in user && "accountIds" in user && "isAdmin" in user && typeof user.username === "string" && typeof user.email === "string" && Array.isArray(user.brandIds) && Array.isArray(user.accountIds) && typeof user.isAdmin === "boolean";
4
+ }
5
+
6
+ export {
7
+ isAuthenticatedUser
8
+ };
9
+ //# sourceMappingURL=chunk-Z2ZAL7KX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/htlkg/config.ts"],"sourcesContent":["/**\n * Configuration types for the htlkg Astro integration\n */\n\nimport type { AuthUser } from '@htlkg/core/types';\n\n// Re-export AuthUser for convenience\nexport type { AuthUser };\n\n/**\n * Route pattern that can be either a RegExp or a string.\n * - String patterns match exact paths or paths that start with the pattern\n * - RegExp patterns allow for more complex matching logic\n *\n * @example\n * // String pattern - matches /login and /login/*\n * '/login'\n *\n * @example\n * // RegExp pattern - matches /api/v1/* and /api/v2/*\n * /^\\/api\\/v[12]\\//\n */\nexport type RoutePattern = RegExp | string;\n\n/**\n * Configuration for brand-specific routes that require brand ID extraction.\n * The pattern should include a capture group for the brand ID.\n *\n * @example\n * {\n * pattern: /^\\/brands\\/(\\d+)/,\n * brandIdParam: 1 // First capture group\n * }\n */\nexport interface BrandRouteConfig {\n\t/**\n\t * Regular expression pattern with a capture group for the brand ID\n\t */\n\tpattern: RegExp;\n\n\t/**\n\t * Index of the capture group containing the brand ID (1-based)\n\t */\n\tbrandIdParam: number;\n}\n\n/**\n * Configuration for the default login page injected by the integration.\n */\nexport interface LoginPageConfig {\n\t/**\n\t * URL path where the login page should be accessible.\n\t * @default '/login'\n\t */\n\tpath?: string;\n\n\t/**\n\t * Logo URL to display on the login page.\n\t * @default undefined\n\t */\n\tlogo?: string;\n\n\t/**\n\t * Title text to display on the login page.\n\t * @default 'Sign In'\n\t */\n\ttitle?: string;\n\n\t/**\n\t * URL to redirect to after successful login.\n\t * @default '/admin'\n\t */\n\tredirectUrl?: string;\n}\n\n/**\n * Configuration for route guards that protect routes based on authentication\n * status and user permissions.\n *\n * @example\n * {\n * publicRoutes: ['/login', '/register', /^\\/public\\//],\n * authenticatedRoutes: ['/dashboard', /^\\/profile/],\n * adminRoutes: ['/admin', /^\\/admin\\//],\n * brandRoutes: [\n * { pattern: /^\\/brands\\/(\\d+)/, brandIdParam: 1 }\n * ],\n * loginUrl: '/login'\n * }\n */\nexport interface RouteGuardConfig {\n\t/**\n\t * Routes that are accessible without authentication.\n\t * These routes bypass all authentication checks.\n\t *\n\t * @default []\n\t */\n\tpublicRoutes?: RoutePattern[];\n\n\t/**\n\t * Routes that require any authenticated user.\n\t * Users must be logged in but no specific permissions are required.\n\t *\n\t * @default []\n\t */\n\tauthenticatedRoutes?: RoutePattern[];\n\n\t/**\n\t * Routes that require admin privileges.\n\t * Only users with isAdmin=true can access these routes.\n\t *\n\t * @default []\n\t */\n\tadminRoutes?: RoutePattern[];\n\n\t/**\n\t * Routes that require brand-specific access.\n\t * Users must have access to the specific brand ID extracted from the route,\n\t * or be an admin.\n\t *\n\t * @default []\n\t */\n\tbrandRoutes?: BrandRouteConfig[];\n\n\t/**\n\t * URL to redirect to when authentication is required.\n\t * Query parameters will be added for return URL and error messages.\n\t *\n\t * @default '/login'\n\t */\n\tloginUrl?: string;\n}\n\n/**\n * Options for configuring the htlkg Astro integration.\n *\n * @example\n * htlkg({\n * auth: {\n * publicRoutes: ['/login', '/'],\n * adminRoutes: [/^\\/admin/],\n * loginUrl: '/login'\n * },\n * loginPage: {\n * logo: 'https://example.com/logo.png',\n * title: 'Admin Portal',\n * redirectUrl: '/admin/dashboard'\n * },\n * validateEnv: true,\n * tailwind: { configFile: './tailwind.config.mjs' }\n * })\n */\nexport interface HtlkgIntegrationOptions {\n\t/**\n\t * Route guard configuration for authentication and authorization.\n\t * Defines which routes are public, require authentication, or require\n\t * specific permissions.\n\t *\n\t * @default {}\n\t */\n\tauth?: RouteGuardConfig;\n\n\t/**\n\t * Configuration for the default login page.\n\t * Set to false to disable automatic login page injection.\n\t *\n\t * @default { path: '/login', title: 'Sign In', redirectUrl: '/admin' }\n\t */\n\tloginPage?: LoginPageConfig | false;\n\n\n\n\t/**\n\t * Validate that required environment variables are present.\n\t * When enabled, the integration will check for required Amplify\n\t * environment variables and log warnings if any are missing.\n\t *\n\t * @default true\n\t */\n\tvalidateEnv?: boolean;\n\n\t/**\n\t * List of required environment variable names to validate.\n\t * Only used when validateEnv is true.\n\t *\n\t * @default ['PUBLIC_COGNITO_USER_POOL_ID', 'PUBLIC_COGNITO_USER_POOL_CLIENT_ID']\n\t */\n\trequiredEnvVars?: string[];\n\n\t/**\n\t * AWS Amplify configuration.\n\t * Pass the amplify_outputs.json content directly for automatic configuration.\n\t * This is the recommended approach as it ensures consistency with Amplify CLI.\n\t *\n\t * @example\n\t * import amplifyOutputs from './amplify_outputs.json';\n\t * \n\t * htlkg({\n\t * amplify: amplifyOutputs\n\t * })\n\t */\n\tamplify?: Record<string, unknown>;\n\n\t/**\n\t * Tailwind CSS integration configuration.\n\t * Set to false to disable automatic Tailwind integration.\n\t * Pass an object to configure Tailwind options (e.g., configFile).\n\t *\n\t * @default undefined (Tailwind enabled with default config)\n\t */\n\ttailwind?: Record<string, unknown> | false;\n}\n\n/**\n * Type guard to check if a value is an authenticated user.\n * Useful for narrowing types in TypeScript when checking user authentication.\n *\n * @param user - Value to check\n * @returns True if the value is a valid AuthUser object\n *\n * @example\n * if (isAuthenticatedUser(locals.user)) {\n * // TypeScript knows locals.user is AuthUser here\n * console.log(locals.user.email);\n * }\n */\nexport function isAuthenticatedUser(user: unknown): user is AuthUser {\n\treturn (\n\t\tuser !== null &&\n\t\ttypeof user === 'object' &&\n\t\t'username' in user &&\n\t\t'email' in user &&\n\t\t'brandIds' in user &&\n\t\t'accountIds' in user &&\n\t\t'isAdmin' in user &&\n\t\ttypeof (user as AuthUser).username === 'string' &&\n\t\ttypeof (user as AuthUser).email === 'string' &&\n\t\tArray.isArray((user as AuthUser).brandIds) &&\n\t\tArray.isArray((user as AuthUser).accountIds) &&\n\t\ttypeof (user as AuthUser).isAdmin === 'boolean'\n\t);\n}\n"],"mappings":";AAkOO,SAAS,oBAAoB,MAAiC;AACpE,SACC,SAAS,QACT,OAAO,SAAS,YAChB,cAAc,QACd,WAAW,QACX,cAAc,QACd,gBAAgB,QAChB,aAAa,QACb,OAAQ,KAAkB,aAAa,YACvC,OAAQ,KAAkB,UAAU,YACpC,MAAM,QAAS,KAAkB,QAAQ,KACzC,MAAM,QAAS,KAAkB,UAAU,KAC3C,OAAQ,KAAkB,YAAY;AAExC;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-ZQ4XMJH7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ isAuthenticatedUser
3
+ } from "../chunk-Z2ZAL7KX.js";
4
+ export {
5
+ isAuthenticatedUser
6
+ };
7
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ htlkg
3
+ } from "../chunk-WLOFOVCL.js";
4
+ export {
5
+ htlkg
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ import {
2
+ htlkg
3
+ } from "./chunk-WLOFOVCL.js";
4
+ import {
5
+ isAuthenticatedUser
6
+ } from "./chunk-Z2ZAL7KX.js";
7
+ import "./chunk-ZQ4XMJH7.js";
8
+ import {
9
+ createHydrationScript,
10
+ createHydrationScripts,
11
+ createIslandProps,
12
+ deserializeFromHydration,
13
+ getHydratedData,
14
+ mergeProps,
15
+ serializeForHydration,
16
+ shouldHydrate
17
+ } from "./chunk-64USRLVP.js";
18
+ import {
19
+ getClientIP,
20
+ getQueryParams,
21
+ getRequestHeaders,
22
+ getServerData,
23
+ isMobileDevice,
24
+ isServerSide,
25
+ setCacheControl,
26
+ setResponseHeaders
27
+ } from "./chunk-WNMPTDCR.js";
28
+ import {
29
+ chunkArray,
30
+ filterItems,
31
+ generateNestedPaths,
32
+ generatePaginatedPaths,
33
+ generateStaticPaths,
34
+ groupItems,
35
+ sortItems
36
+ } from "./chunk-33R4URZV.js";
37
+ export {
38
+ chunkArray,
39
+ createHydrationScript,
40
+ createHydrationScripts,
41
+ createIslandProps,
42
+ deserializeFromHydration,
43
+ filterItems,
44
+ generateNestedPaths,
45
+ generatePaginatedPaths,
46
+ generateStaticPaths,
47
+ getClientIP,
48
+ getHydratedData,
49
+ getQueryParams,
50
+ getRequestHeaders,
51
+ getServerData,
52
+ groupItems,
53
+ htlkg,
54
+ isAuthenticatedUser,
55
+ isMobileDevice,
56
+ isServerSide,
57
+ mergeProps,
58
+ serializeForHydration,
59
+ setCacheControl,
60
+ setResponseHeaders,
61
+ shouldHydrate,
62
+ sortItems
63
+ };
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,168 @@
1
+ // src/middleware/index.ts
2
+ import { sequence } from "astro:middleware";
3
+
4
+ // src/middleware/auth.ts
5
+ import { getUser } from "@htlkg/core/auth";
6
+ var authMiddleware = async (context, next) => {
7
+ const { locals } = context;
8
+ try {
9
+ const user = await getUser(context);
10
+ locals.user = user;
11
+ } catch (error) {
12
+ locals.user = null;
13
+ if (error instanceof Error) {
14
+ const safeMessage = error.message.replace(/token[=:]\s*[^\s,}]+/gi, "token=***").replace(/key[=:]\s*[^\s,}]+/gi, "key=***").replace(/secret[=:]\s*[^\s,}]+/gi, "secret=***").replace(/password[=:]\s*[^\s,}]+/gi, "password=***");
15
+ console.error("[htlkg Auth] Authentication failed:", safeMessage);
16
+ } else {
17
+ console.error("[htlkg Auth] Authentication failed: Unknown error");
18
+ }
19
+ }
20
+ return next();
21
+ };
22
+
23
+ // src/middleware/route-guards.ts
24
+ import { routeGuardConfig } from "virtual:htlkg-config";
25
+ var config = routeGuardConfig;
26
+ function matchesPattern(pathname, patterns) {
27
+ return patterns.some((pattern) => {
28
+ try {
29
+ if (typeof pattern === "string") {
30
+ if (pattern === "/") {
31
+ return pathname === "/";
32
+ }
33
+ return pathname === pattern || pathname.startsWith(pattern + "/");
34
+ }
35
+ return pattern.test(pathname);
36
+ } catch (error) {
37
+ console.error(
38
+ "[htlkg Route Guard] Error matching pattern:",
39
+ error instanceof Error ? error.message : "Unknown error"
40
+ );
41
+ return false;
42
+ }
43
+ });
44
+ }
45
+ var routeGuard = async (context, next) => {
46
+ const { locals, url, redirect } = context;
47
+ const pathname = url.pathname;
48
+ const {
49
+ publicRoutes = [],
50
+ authenticatedRoutes = [],
51
+ adminRoutes = [],
52
+ brandRoutes = [],
53
+ loginUrl = "/login"
54
+ } = config;
55
+ try {
56
+ if (matchesPattern(pathname, publicRoutes)) {
57
+ console.log(`[htlkg Route Guard] Public route: ${pathname}`);
58
+ return next();
59
+ }
60
+ const user = locals.user;
61
+ if (matchesPattern(pathname, adminRoutes)) {
62
+ if (!user || !user.isAdmin) {
63
+ console.log(
64
+ `[htlkg Route Guard] Admin access denied for ${pathname} - User: ${user ? "authenticated (non-admin)" : "not authenticated"}`
65
+ );
66
+ return redirect(`${loginUrl}?error=admin_required`);
67
+ }
68
+ console.log(
69
+ `[htlkg Route Guard] Admin access granted for ${pathname}`
70
+ );
71
+ return next();
72
+ }
73
+ for (const brandRoute of brandRoutes) {
74
+ try {
75
+ const match = pathname.match(brandRoute.pattern);
76
+ if (match) {
77
+ const brandId = Number.parseInt(match[brandRoute.brandIdParam], 10);
78
+ if (Number.isNaN(brandId)) {
79
+ console.warn(
80
+ `[htlkg Route Guard] Invalid brandId extracted from ${pathname}`
81
+ );
82
+ return redirect(`${loginUrl}?error=invalid_brand`);
83
+ }
84
+ if (!user || !user.isAdmin && !user.brandIds.includes(brandId)) {
85
+ console.log(
86
+ `[htlkg Route Guard] Brand access denied for ${pathname} (brandId: ${brandId}) - User: ${user ? `authenticated (brandIds: ${user.brandIds.join(",")})` : "not authenticated"}`
87
+ );
88
+ return redirect(`${loginUrl}?error=access_denied`);
89
+ }
90
+ console.log(
91
+ `[htlkg Route Guard] Brand access granted for ${pathname} (brandId: ${brandId})`
92
+ );
93
+ return next();
94
+ }
95
+ } catch (error) {
96
+ console.error(
97
+ `[htlkg Route Guard] Error processing brand route for ${pathname}:`,
98
+ error instanceof Error ? error.message : "Unknown error"
99
+ );
100
+ return redirect(`${loginUrl}?error=access_denied`);
101
+ }
102
+ }
103
+ if (matchesPattern(pathname, authenticatedRoutes)) {
104
+ if (!user) {
105
+ const returnUrl = encodeURIComponent(pathname + url.search);
106
+ console.log(
107
+ `[htlkg Route Guard] Authentication required for ${pathname}, redirecting to login`
108
+ );
109
+ return redirect(`${loginUrl}?redirect=${returnUrl}`);
110
+ }
111
+ console.log(
112
+ `[htlkg Route Guard] Authenticated access granted for ${pathname}`
113
+ );
114
+ return next();
115
+ }
116
+ console.log(
117
+ `[htlkg Route Guard] Default access granted for ${pathname}`
118
+ );
119
+ return next();
120
+ } catch (error) {
121
+ console.error(
122
+ "[htlkg Route Guard] Unexpected error in route guard:",
123
+ error instanceof Error ? error.message : "Unknown error"
124
+ );
125
+ return redirect(`${loginUrl}?error=server_error`);
126
+ }
127
+ };
128
+ async function requireAuth(context, loginUrl = "/login") {
129
+ const user = context.locals.user;
130
+ if (!user) {
131
+ const currentUrl = context.url.pathname + context.url.search;
132
+ const encodedReturnUrl = encodeURIComponent(currentUrl);
133
+ return context.redirect(`${loginUrl}?redirect=${encodedReturnUrl}`);
134
+ }
135
+ return user;
136
+ }
137
+ async function requireAdminAccess(context, loginUrl = "/login") {
138
+ const user = context.locals.user;
139
+ if (!user) {
140
+ return context.redirect(`${loginUrl}?error=not_authenticated`);
141
+ }
142
+ if (!user.isAdmin) {
143
+ return context.redirect(`${loginUrl}?error=admin_required`);
144
+ }
145
+ return user;
146
+ }
147
+ async function requireBrandAccess(context, brandId, loginUrl = "/login") {
148
+ const user = context.locals.user;
149
+ if (!user) {
150
+ return context.redirect(`${loginUrl}?error=not_authenticated`);
151
+ }
152
+ if (!user.isAdmin && !user.brandIds.includes(brandId)) {
153
+ return context.redirect(`${loginUrl}?error=access_denied`);
154
+ }
155
+ return user;
156
+ }
157
+
158
+ // src/middleware/index.ts
159
+ var onRequest = sequence(authMiddleware, routeGuard);
160
+ export {
161
+ authMiddleware,
162
+ onRequest,
163
+ requireAdminAccess,
164
+ requireAuth,
165
+ requireBrandAccess,
166
+ routeGuard
167
+ };
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/middleware/index.ts","../../src/middleware/auth.ts","../../src/middleware/route-guards.ts"],"sourcesContent":["/**\n * Middleware for @htlkg/astro\n * \n * This module exports:\n * - authMiddleware: Retrieves authenticated user and injects into locals\n * - routeGuard: Enforces access control based on route configuration\n * - onRequest: Composed middleware chain (auth + route guard)\n * - Helper functions: requireAuth, requireAdminAccess, requireBrandAccess\n */\n\nimport { sequence } from 'astro:middleware';\nimport { authMiddleware } from './auth.js';\nimport { routeGuard } from './route-guards.js';\n\n// Export individual middleware\nexport { authMiddleware } from './auth.js';\nexport {\n\trouteGuard,\n\trequireAuth,\n\trequireAdminAccess,\n\trequireBrandAccess,\n} from './route-guards.js';\n\n/**\n * Export composed middleware chain\n * Auth middleware runs first to populate locals.user\n * Route guard middleware runs second to enforce access control\n * \n * This is the default export that gets injected by the htlkg integration\n */\nexport const onRequest = sequence(authMiddleware, routeGuard);\n","/**\n * Authentication middleware for htlkg integration\n * \n * This middleware:\n * - Retrieves the authenticated user from AWS Amplify\n * - Injects the user into Astro.locals for use in pages and API routes\n * - Handles authentication errors gracefully\n */\n\nimport type { MiddlewareHandler } from 'astro';\nimport { getUser } from '@htlkg/core/auth';\n\n/**\n * Auth middleware - retrieves authenticated user and injects into locals\n * \n * This middleware runs on every request and attempts to get the current\n * authenticated user from AWS Amplify. If successful, the user is injected\n * into context.locals.user. If authentication fails, locals.user is set to null.\n * \n * @example\n * // In an Astro page\n * const { user } = Astro.locals;\n * if (user) {\n * console.log('Authenticated user:', user.email);\n * }\n */\nexport const authMiddleware: MiddlewareHandler = async (context, next) => {\n\tconst { locals } = context;\n\n\ttry {\n\t\tconst user = await getUser(context);\n\t\tlocals.user = user;\n\t} catch (error) {\n\t\t// Set user to null on any authentication error\n\t\tlocals.user = null;\n\n\t\t// Log error without exposing sensitive information\n\t\tif (error instanceof Error) {\n\t\t\t// Filter out sensitive data from error messages\n\t\t\tconst safeMessage = error.message\n\t\t\t\t.replace(/token[=:]\\s*[^\\s,}]+/gi, 'token=***')\n\t\t\t\t.replace(/key[=:]\\s*[^\\s,}]+/gi, 'key=***')\n\t\t\t\t.replace(/secret[=:]\\s*[^\\s,}]+/gi, 'secret=***')\n\t\t\t\t.replace(/password[=:]\\s*[^\\s,}]+/gi, 'password=***');\n\n\t\t\tconsole.error('[htlkg Auth] Authentication failed:', safeMessage);\n\t\t} else {\n\t\t\tconsole.error('[htlkg Auth] Authentication failed: Unknown error');\n\t\t}\n\t}\n\n\treturn next();\n};\n","/**\n * Route guard middleware for htlkg integration\n * \n * This middleware enforces access control based on route configuration:\n * - Public routes: accessible to everyone\n * - Authenticated routes: require any logged-in user\n * - Admin routes: require admin privileges\n * - Brand routes: require brand-specific access or admin privileges\n */\n\nimport type { MiddlewareHandler } from 'astro';\nimport type { RoutePattern, RouteGuardConfig } from '../htlkg/config.js';\n\n// Import configuration from virtual module\nimport { routeGuardConfig } from 'virtual:htlkg-config';\n\n// Type assertion for the imported config\nconst config = routeGuardConfig as RouteGuardConfig;\n\n/**\n * Helper: Check if pathname matches any of the provided patterns\n */\nfunction matchesPattern(pathname: string, patterns: RoutePattern[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\ttry {\n\t\t\tif (typeof pattern === 'string') {\n\t\t\t\t// Special case: \"/\" only matches exactly\n\t\t\t\tif (pattern === '/') {\n\t\t\t\t\treturn pathname === '/';\n\t\t\t\t}\n\t\t\t\t// String pattern: exact match or starts with (for sub-routes)\n\t\t\t\treturn pathname === pattern || pathname.startsWith(pattern + '/');\n\t\t\t}\n\t\t\t// RegExp pattern: test against pathname\n\t\t\treturn pattern.test(pathname);\n\t\t} catch (error) {\n\t\t\t// Log pattern matching errors but don't block the request\n\t\t\tconsole.error(\n\t\t\t\t'[htlkg Route Guard] Error matching pattern:',\n\t\t\t\terror instanceof Error ? error.message : 'Unknown error',\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\n/**\n * Route guard middleware - protects routes based on configuration\n * \n * This middleware runs after authMiddleware and enforces access control\n * based on the route configuration provided to the htlkg integration.\n * \n * @example\n * // In astro.config.mjs\n * htlkg({\n * auth: {\n * publicRoutes: ['/login', '/'],\n * adminRoutes: [/^\\/admin/],\n * brandRoutes: [\n * { pattern: /^\\/brands\\/(\\d+)/, brandIdParam: 1 }\n * ],\n * loginUrl: '/login'\n * }\n * })\n */\nexport const routeGuard: MiddlewareHandler = async (context, next) => {\n\tconst { locals, url, redirect } = context;\n\tconst pathname = url.pathname;\n\n\tconst {\n\t\tpublicRoutes = [],\n\t\tauthenticatedRoutes = [],\n\t\tadminRoutes = [],\n\t\tbrandRoutes = [],\n\t\tloginUrl = '/login',\n\t} = config;\n\n\ttry {\n\t\t// Public routes - no auth required\n\t\tif (matchesPattern(pathname, publicRoutes)) {\n\t\t\tconsole.log(`[htlkg Route Guard] Public route: ${pathname}`);\n\t\t\treturn next();\n\t\t}\n\n\t\tconst user = locals.user;\n\n\t\t// Admin routes - require admin role\n\t\tif (matchesPattern(pathname, adminRoutes)) {\n\t\t\tif (!user || !user.isAdmin) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`[htlkg Route Guard] Admin access denied for ${pathname} - User: ${user ? 'authenticated (non-admin)' : 'not authenticated'}`,\n\t\t\t\t);\n\t\t\t\treturn redirect(`${loginUrl}?error=admin_required`);\n\t\t\t}\n\t\t\tconsole.log(\n\t\t\t\t`[htlkg Route Guard] Admin access granted for ${pathname}`,\n\t\t\t);\n\t\t\treturn next();\n\t\t}\n\n\t\t// Brand routes - require brand access or admin\n\t\tfor (const brandRoute of brandRoutes) {\n\t\t\ttry {\n\t\t\t\tconst match = pathname.match(brandRoute.pattern);\n\t\t\t\tif (match) {\n\t\t\t\t\tconst brandId = Number.parseInt(match[brandRoute.brandIdParam], 10);\n\n\t\t\t\t\tif (Number.isNaN(brandId)) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[htlkg Route Guard] Invalid brandId extracted from ${pathname}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn redirect(`${loginUrl}?error=invalid_brand`);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!user || (!user.isAdmin && !user.brandIds.includes(brandId))) {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`[htlkg Route Guard] Brand access denied for ${pathname} (brandId: ${brandId}) - User: ${user ? `authenticated (brandIds: ${user.brandIds.join(',')})` : 'not authenticated'}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn redirect(`${loginUrl}?error=access_denied`);\n\t\t\t\t\t}\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[htlkg Route Guard] Brand access granted for ${pathname} (brandId: ${brandId})`,\n\t\t\t\t\t);\n\t\t\t\t\treturn next();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[htlkg Route Guard] Error processing brand route for ${pathname}:`,\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error',\n\t\t\t\t);\n\t\t\t\t// Fail-safe: deny access on error\n\t\t\t\treturn redirect(`${loginUrl}?error=access_denied`);\n\t\t\t}\n\t\t}\n\n\t\t// Authenticated routes - require any logged-in user\n\t\tif (matchesPattern(pathname, authenticatedRoutes)) {\n\t\t\tif (!user) {\n\t\t\t\tconst returnUrl = encodeURIComponent(pathname + url.search);\n\t\t\t\tconsole.log(\n\t\t\t\t\t`[htlkg Route Guard] Authentication required for ${pathname}, redirecting to login`,\n\t\t\t\t);\n\t\t\t\treturn redirect(`${loginUrl}?redirect=${returnUrl}`);\n\t\t\t}\n\t\t\tconsole.log(\n\t\t\t\t`[htlkg Route Guard] Authenticated access granted for ${pathname}`,\n\t\t\t);\n\t\t\treturn next();\n\t\t}\n\n\t\t// Default: allow access\n\t\tconsole.log(\n\t\t\t`[htlkg Route Guard] Default access granted for ${pathname}`,\n\t\t);\n\t\treturn next();\n\t} catch (error) {\n\t\t// Catch-all error handler for route guard\n\t\tconsole.error(\n\t\t\t'[htlkg Route Guard] Unexpected error in route guard:',\n\t\t\terror instanceof Error ? error.message : 'Unknown error',\n\t\t);\n\t\t// Fail-safe: deny access and redirect to login\n\t\treturn redirect(`${loginUrl}?error=server_error`);\n\t}\n};\n\n/**\n * Helper functions for programmatic route protection in pages\n * These can be used in Astro pages for more fine-grained control\n */\n\n/**\n * Require authentication for a page\n * Redirects to login if user is not authenticated\n */\nexport async function requireAuth(context: any, loginUrl = '/login') {\n\tconst user = context.locals.user;\n\tif (!user) {\n\t\tconst currentUrl = context.url.pathname + context.url.search;\n\t\tconst encodedReturnUrl = encodeURIComponent(currentUrl);\n\t\treturn context.redirect(`${loginUrl}?redirect=${encodedReturnUrl}`);\n\t}\n\treturn user;\n}\n\n/**\n * Require admin access for a page\n * Redirects to login if user is not an admin\n */\nexport async function requireAdminAccess(context: any, loginUrl = '/login') {\n\tconst user = context.locals.user;\n\tif (!user) {\n\t\treturn context.redirect(`${loginUrl}?error=not_authenticated`);\n\t}\n\tif (!user.isAdmin) {\n\t\treturn context.redirect(`${loginUrl}?error=admin_required`);\n\t}\n\treturn user;\n}\n\n/**\n * Require brand access for a page\n * Redirects to login if user doesn't have access to the specified brand\n */\nexport async function requireBrandAccess(\n\tcontext: any,\n\tbrandId: number,\n\tloginUrl = '/login'\n) {\n\tconst user = context.locals.user;\n\tif (!user) {\n\t\treturn context.redirect(`${loginUrl}?error=not_authenticated`);\n\t}\n\tif (!user.isAdmin && !user.brandIds.includes(brandId)) {\n\t\treturn context.redirect(`${loginUrl}?error=access_denied`);\n\t}\n\treturn user;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,21 @@
1
+ import {
2
+ createHydrationScript,
3
+ createHydrationScripts,
4
+ createIslandProps,
5
+ deserializeFromHydration,
6
+ getHydratedData,
7
+ mergeProps,
8
+ serializeForHydration,
9
+ shouldHydrate
10
+ } from "../chunk-64USRLVP.js";
11
+ export {
12
+ createHydrationScript,
13
+ createHydrationScripts,
14
+ createIslandProps,
15
+ deserializeFromHydration,
16
+ getHydratedData,
17
+ mergeProps,
18
+ serializeForHydration,
19
+ shouldHydrate
20
+ };
21
+ //# sourceMappingURL=hydration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,56 @@
1
+ import "../chunk-ZQ4XMJH7.js";
2
+ import {
3
+ createHydrationScript,
4
+ createHydrationScripts,
5
+ createIslandProps,
6
+ deserializeFromHydration,
7
+ getHydratedData,
8
+ mergeProps,
9
+ serializeForHydration,
10
+ shouldHydrate
11
+ } from "../chunk-64USRLVP.js";
12
+ import {
13
+ getClientIP,
14
+ getQueryParams,
15
+ getRequestHeaders,
16
+ getServerData,
17
+ isMobileDevice,
18
+ isServerSide,
19
+ setCacheControl,
20
+ setResponseHeaders
21
+ } from "../chunk-WNMPTDCR.js";
22
+ import {
23
+ chunkArray,
24
+ filterItems,
25
+ generateNestedPaths,
26
+ generatePaginatedPaths,
27
+ generateStaticPaths,
28
+ groupItems,
29
+ sortItems
30
+ } from "../chunk-33R4URZV.js";
31
+ export {
32
+ chunkArray,
33
+ createHydrationScript,
34
+ createHydrationScripts,
35
+ createIslandProps,
36
+ deserializeFromHydration,
37
+ filterItems,
38
+ generateNestedPaths,
39
+ generatePaginatedPaths,
40
+ generateStaticPaths,
41
+ getClientIP,
42
+ getHydratedData,
43
+ getQueryParams,
44
+ getRequestHeaders,
45
+ getServerData,
46
+ groupItems,
47
+ isMobileDevice,
48
+ isServerSide,
49
+ mergeProps,
50
+ serializeForHydration,
51
+ setCacheControl,
52
+ setResponseHeaders,
53
+ shouldHydrate,
54
+ sortItems
55
+ };
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,21 @@
1
+ import {
2
+ getClientIP,
3
+ getQueryParams,
4
+ getRequestHeaders,
5
+ getServerData,
6
+ isMobileDevice,
7
+ isServerSide,
8
+ setCacheControl,
9
+ setResponseHeaders
10
+ } from "../chunk-WNMPTDCR.js";
11
+ export {
12
+ getClientIP,
13
+ getQueryParams,
14
+ getRequestHeaders,
15
+ getServerData,
16
+ isMobileDevice,
17
+ isServerSide,
18
+ setCacheControl,
19
+ setResponseHeaders
20
+ };
21
+ //# sourceMappingURL=ssr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,19 @@
1
+ import {
2
+ chunkArray,
3
+ filterItems,
4
+ generateNestedPaths,
5
+ generatePaginatedPaths,
6
+ generateStaticPaths,
7
+ groupItems,
8
+ sortItems
9
+ } from "../chunk-33R4URZV.js";
10
+ export {
11
+ chunkArray,
12
+ filterItems,
13
+ generateNestedPaths,
14
+ generatePaginatedPaths,
15
+ generateStaticPaths,
16
+ groupItems,
17
+ sortItems
18
+ };
19
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@htlkg/astro",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dist/index.js",
7
+ "./htlkg": "./dist/htlkg/index.js",
8
+ "./htlkg/config": "./dist/htlkg/config.js",
9
+ "./middleware": "./dist/middleware/index.js",
10
+ "./vue-app-setup": "./src/vue-app-setup.ts",
11
+ "./auth/LoginPage.astro": "./src/auth/LoginPage.astro",
12
+ "./layouts": "./src/layouts/index.ts",
13
+ "./layouts/*": "./src/layouts/*.astro",
14
+ "./patterns": "./src/patterns/index.ts",
15
+ "./patterns/admin": "./src/patterns/admin/index.ts",
16
+ "./patterns/admin/*": "./src/patterns/admin/*.astro",
17
+ "./patterns/brand": "./src/patterns/brand/index.ts",
18
+ "./patterns/brand/*": "./src/patterns/brand/*.astro",
19
+ "./components": "./src/components/index.ts",
20
+ "./components/*": "./src/components/*.astro",
21
+ "./utils": "./dist/utils/index.js",
22
+ "./utils/*": "./dist/utils/*.js"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "src"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest"
33
+ },
34
+ "dependencies": {
35
+ "@astrojs/tailwind": "^6.0.2",
36
+ "@astrojs/vue": "^5.0.0",
37
+ "@htlkg/core": "^0.0.1",
38
+ "@htlkg/components": "^0.0.1",
39
+ "@hotelinking/ui": "^15.49.1",
40
+ "astro": "^5.14.7",
41
+ "aws-amplify": "^6.11.3",
42
+ "tailwindcss": "^3.4.18",
43
+ "vue": "^3.5.22"
44
+ },
45
+ "publishConfig": {
46
+ "access": "restricted"
47
+ },
48
+ "devDependencies": {
49
+ "tsup": "^8.0.0",
50
+ "typescript": "^5.9.2",
51
+ "vitest": "^3.2.4"
52
+ }
53
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Mock for astro:middleware module used in tests
3
+ */
4
+
5
+ export function sequence(...handlers: any[]) {
6
+ return async (context: any, next: any) => {
7
+ let index = 0;
8
+
9
+ async function executeNext() {
10
+ if (index >= handlers.length) {
11
+ return next();
12
+ }
13
+ const handler = handlers[index++];
14
+ return handler(context, executeNext);
15
+ }
16
+
17
+ return executeNext();
18
+ };
19
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Mock for virtual:htlkg-config module used in tests
3
+ */
4
+
5
+ export const routeGuardConfig = {
6
+ publicRoutes: [],
7
+ authenticatedRoutes: [],
8
+ adminRoutes: [],
9
+ brandRoutes: [],
10
+ loginUrl: '/login',
11
+ };
12
+
13
+ export const loginPageConfig = null;
14
+ export const amplifyConfig = null;