@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.
- package/README.md +265 -0
- package/dist/chunk-33R4URZV.js +59 -0
- package/dist/chunk-33R4URZV.js.map +1 -0
- package/dist/chunk-64USRLVP.js +85 -0
- package/dist/chunk-64USRLVP.js.map +1 -0
- package/dist/chunk-WLOFOVCL.js +210 -0
- package/dist/chunk-WLOFOVCL.js.map +1 -0
- package/dist/chunk-WNMPTDCR.js +73 -0
- package/dist/chunk-WNMPTDCR.js.map +1 -0
- package/dist/chunk-Z2ZAL7KX.js +9 -0
- package/dist/chunk-Z2ZAL7KX.js.map +1 -0
- package/dist/chunk-ZQ4XMJH7.js +1 -0
- package/dist/chunk-ZQ4XMJH7.js.map +1 -0
- package/dist/htlkg/config.js +7 -0
- package/dist/htlkg/config.js.map +1 -0
- package/dist/htlkg/index.js +7 -0
- package/dist/htlkg/index.js.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.js +168 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/utils/hydration.js +21 -0
- package/dist/utils/hydration.js.map +1 -0
- package/dist/utils/index.js +56 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/ssr.js +21 -0
- package/dist/utils/ssr.js.map +1 -0
- package/dist/utils/static.js +19 -0
- package/dist/utils/static.js.map +1 -0
- package/package.json +53 -0
- package/src/__mocks__/astro-middleware.ts +19 -0
- package/src/__mocks__/virtual-htlkg-config.ts +14 -0
- package/src/auth/LoginForm.vue +482 -0
- package/src/auth/LoginPage.astro +70 -0
- package/src/components/PageHeader.astro +145 -0
- package/src/components/Sidebar.astro +157 -0
- package/src/components/Topbar.astro +167 -0
- package/src/components/index.ts +9 -0
- package/src/htlkg/config.test.ts +165 -0
- package/src/htlkg/config.ts +242 -0
- package/src/htlkg/index.ts +245 -0
- package/src/htlkg/virtual-modules.test.ts +158 -0
- package/src/htlkg/virtual-modules.ts +81 -0
- package/src/index.ts +37 -0
- package/src/layouts/AdminLayout.astro +184 -0
- package/src/layouts/AuthLayout.astro +164 -0
- package/src/layouts/BrandLayout.astro +309 -0
- package/src/layouts/DefaultLayout.astro +25 -0
- package/src/layouts/PublicLayout.astro +153 -0
- package/src/layouts/index.ts +10 -0
- package/src/middleware/auth.ts +53 -0
- package/src/middleware/index.ts +31 -0
- package/src/middleware/route-guards.test.ts +182 -0
- package/src/middleware/route-guards.ts +218 -0
- package/src/patterns/admin/DetailPage.astro +195 -0
- package/src/patterns/admin/FormPage.astro +203 -0
- package/src/patterns/admin/ListPage.astro +178 -0
- package/src/patterns/admin/index.ts +9 -0
- package/src/patterns/brand/ConfigPage.astro +128 -0
- package/src/patterns/brand/PortalPage.astro +161 -0
- package/src/patterns/brand/index.ts +8 -0
- package/src/patterns/index.ts +8 -0
- package/src/utils/hydration.test.ts +154 -0
- package/src/utils/hydration.ts +151 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/ssr.test.ts +235 -0
- package/src/utils/ssr.ts +139 -0
- package/src/utils/static.test.ts +144 -0
- package/src/utils/static.ts +144 -0
- package/src/vue-app-setup.ts +88 -0
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# @htlkg/astro
|
|
2
|
+
|
|
3
|
+
Astro integration, layouts, page patterns, middleware, and utilities for Hotelinking applications.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@htlkg/astro` consolidates all Astro-specific functionality into a single package, including:
|
|
8
|
+
|
|
9
|
+
- **Astro Integration** - Zero-config setup with `htlkg()` integration
|
|
10
|
+
- **Layouts** - AdminLayout, BrandLayout, AuthLayout, PublicLayout
|
|
11
|
+
- **Page Patterns** - Reusable page templates for common scenarios
|
|
12
|
+
- **Middleware** - Authentication and route guards
|
|
13
|
+
- **Components** - Astro components (Sidebar, Topbar, PageHeader)
|
|
14
|
+
- **Utilities** - SSR, hydration, and static rendering helpers
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @htlkg/astro
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Configure Astro Integration
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
// astro.config.mjs
|
|
28
|
+
import { defineConfig } from 'astro/config';
|
|
29
|
+
import { htlkg } from '@htlkg/astro';
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
integrations: [
|
|
33
|
+
htlkg({
|
|
34
|
+
auth: {
|
|
35
|
+
enabled: true,
|
|
36
|
+
loginPage: '/login',
|
|
37
|
+
publicRoutes: ['/login', '/public'],
|
|
38
|
+
},
|
|
39
|
+
brandRoutes: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
pattern: '/[brandId]',
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Use Layouts
|
|
49
|
+
|
|
50
|
+
```astro
|
|
51
|
+
---
|
|
52
|
+
// src/pages/admin/brands.astro
|
|
53
|
+
import { AdminLayout } from '@htlkg/astro/layouts';
|
|
54
|
+
import { BrandsPage } from '@/components';
|
|
55
|
+
|
|
56
|
+
const user = Astro.locals.user;
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
<AdminLayout user={user} title="Brands">
|
|
60
|
+
<BrandsPage />
|
|
61
|
+
</AdminLayout>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Apply Middleware
|
|
65
|
+
|
|
66
|
+
```astro
|
|
67
|
+
---
|
|
68
|
+
// src/pages/admin/users.astro
|
|
69
|
+
import { requireAdminAccess } from '@htlkg/astro/middleware';
|
|
70
|
+
|
|
71
|
+
export const prerender = false;
|
|
72
|
+
|
|
73
|
+
// Require admin access
|
|
74
|
+
await requireAdminAccess(Astro);
|
|
75
|
+
---
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Package Structure
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
@htlkg/astro/
|
|
82
|
+
├── auth/ # Authentication pages
|
|
83
|
+
│ └── LoginPage.astro
|
|
84
|
+
├── components/ # Astro components
|
|
85
|
+
│ ├── PageHeader.astro
|
|
86
|
+
│ ├── Sidebar.astro
|
|
87
|
+
│ └── Topbar.astro
|
|
88
|
+
├── htlkg/ # Astro integration
|
|
89
|
+
│ ├── index.ts # Main integration
|
|
90
|
+
│ ├── config.ts # Configuration types
|
|
91
|
+
│ └── virtual-modules.ts
|
|
92
|
+
├── layouts/ # Page layouts
|
|
93
|
+
│ ├── AdminLayout.astro
|
|
94
|
+
│ ├── BrandLayout.astro
|
|
95
|
+
│ ├── AuthLayout.astro
|
|
96
|
+
│ └── PublicLayout.astro
|
|
97
|
+
├── middleware/ # Middleware & route guards
|
|
98
|
+
│ ├── auth.ts
|
|
99
|
+
│ ├── route-guards.ts
|
|
100
|
+
│ └── index.ts
|
|
101
|
+
├── patterns/ # Page patterns
|
|
102
|
+
│ ├── admin/
|
|
103
|
+
│ │ ├── ListPage.astro
|
|
104
|
+
│ │ ├── DetailPage.astro
|
|
105
|
+
│ │ └── FormPage.astro
|
|
106
|
+
│ └── brand/
|
|
107
|
+
│ ├── ConfigPage.astro
|
|
108
|
+
│ └── PortalPage.astro
|
|
109
|
+
└── utils/ # Utilities
|
|
110
|
+
├── hydration.ts # Client hydration helpers
|
|
111
|
+
├── ssr.ts # Server-side rendering helpers
|
|
112
|
+
└── static.ts # Static generation helpers
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Exports
|
|
116
|
+
|
|
117
|
+
### Main Package (`@htlkg/astro`)
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { htlkg } from '@htlkg/astro';
|
|
121
|
+
import type { HtlkgIntegrationOptions, AuthUser } from '@htlkg/astro';
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Layouts
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { AdminLayout } from '@htlkg/astro/layouts';
|
|
128
|
+
import { BrandLayout } from '@htlkg/astro/layouts';
|
|
129
|
+
import { AuthLayout } from '@htlkg/astro/layouts';
|
|
130
|
+
import { PublicLayout } from '@htlkg/astro/layouts';
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Middleware
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import {
|
|
137
|
+
requireAdminAccess,
|
|
138
|
+
requireBrandAccess,
|
|
139
|
+
requireAuth,
|
|
140
|
+
} from '@htlkg/astro/middleware';
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Page Patterns
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { ListPage } from '@htlkg/astro/patterns/admin';
|
|
147
|
+
import { DetailPage } from '@htlkg/astro/patterns/admin';
|
|
148
|
+
import { FormPage } from '@htlkg/astro/patterns/admin';
|
|
149
|
+
import { ConfigPage } from '@htlkg/astro/patterns/brand';
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Components
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { Sidebar } from '@htlkg/astro/components';
|
|
156
|
+
import { Topbar } from '@htlkg/astro/components';
|
|
157
|
+
import { PageHeader } from '@htlkg/astro/components';
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Utilities
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { getHydrationStrategy } from '@htlkg/astro/utils/hydration';
|
|
164
|
+
import { getSSRConfig } from '@htlkg/astro/utils/ssr';
|
|
165
|
+
import { getStaticPaths } from '@htlkg/astro/utils/static';
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Features
|
|
169
|
+
|
|
170
|
+
### Astro Integration
|
|
171
|
+
|
|
172
|
+
The `htlkg()` integration provides:
|
|
173
|
+
|
|
174
|
+
- Automatic middleware setup
|
|
175
|
+
- Route guard configuration
|
|
176
|
+
- Login page injection
|
|
177
|
+
- Brand route handling
|
|
178
|
+
- Virtual module configuration
|
|
179
|
+
|
|
180
|
+
### Layouts
|
|
181
|
+
|
|
182
|
+
Pre-built layouts for common page types:
|
|
183
|
+
|
|
184
|
+
- **AdminLayout** - Admin portal pages with sidebar navigation
|
|
185
|
+
- **BrandLayout** - Brand-specific pages with brand context
|
|
186
|
+
- **AuthLayout** - Authentication pages (login, signup)
|
|
187
|
+
- **PublicLayout** - Public-facing pages
|
|
188
|
+
|
|
189
|
+
### Middleware
|
|
190
|
+
|
|
191
|
+
Authentication and authorization middleware:
|
|
192
|
+
|
|
193
|
+
- **requireAuth** - Require authenticated user
|
|
194
|
+
- **requireAdminAccess** - Require admin role
|
|
195
|
+
- **requireBrandAccess** - Require brand access
|
|
196
|
+
|
|
197
|
+
### Page Patterns
|
|
198
|
+
|
|
199
|
+
Reusable page templates:
|
|
200
|
+
|
|
201
|
+
- **ListPage** - List/table view with filters
|
|
202
|
+
- **DetailPage** - Detail view with tabs
|
|
203
|
+
- **FormPage** - Form view with validation
|
|
204
|
+
- **ConfigPage** - Configuration page
|
|
205
|
+
|
|
206
|
+
## Configuration
|
|
207
|
+
|
|
208
|
+
### Integration Options
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
interface HtlkgIntegrationOptions {
|
|
212
|
+
auth?: {
|
|
213
|
+
enabled: boolean;
|
|
214
|
+
loginPage?: string;
|
|
215
|
+
publicRoutes?: RoutePattern[];
|
|
216
|
+
protectedRoutes?: RoutePattern[];
|
|
217
|
+
};
|
|
218
|
+
brandRoutes?: {
|
|
219
|
+
enabled: boolean;
|
|
220
|
+
pattern?: string;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Route Guards
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
interface RouteGuardConfig {
|
|
229
|
+
publicRoutes: RoutePattern[];
|
|
230
|
+
protectedRoutes: RoutePattern[];
|
|
231
|
+
loginPage: string;
|
|
232
|
+
brandRoutePattern?: RegExp;
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Dependencies
|
|
237
|
+
|
|
238
|
+
- `astro` - Astro framework
|
|
239
|
+
- `@astrojs/vue` - Vue integration
|
|
240
|
+
- `@astrojs/tailwind` - Tailwind CSS integration
|
|
241
|
+
- `@htlkg/core` - Core utilities and types
|
|
242
|
+
- `@htlkg/components` - Vue components
|
|
243
|
+
- `vue` - Vue framework
|
|
244
|
+
- `tailwindcss` - Tailwind CSS
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Build package
|
|
250
|
+
pnpm build
|
|
251
|
+
|
|
252
|
+
# Watch mode
|
|
253
|
+
pnpm dev
|
|
254
|
+
|
|
255
|
+
# Run tests
|
|
256
|
+
pnpm test
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Migration
|
|
260
|
+
|
|
261
|
+
If migrating from `@htlkg/pages` and `@htlkg/integrations`, see [MIGRATION_ASTRO_PACKAGE.md](../../MIGRATION_ASTRO_PACKAGE.md).
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
Private - Hotelinking internal use only
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/utils/static.ts
|
|
2
|
+
function generateStaticPaths(items, mapper) {
|
|
3
|
+
return items.map(mapper);
|
|
4
|
+
}
|
|
5
|
+
function generatePaginatedPaths(items, pageSize, mapper) {
|
|
6
|
+
const totalPages = Math.ceil(items.length / pageSize);
|
|
7
|
+
const paths = [];
|
|
8
|
+
for (let page = 1; page <= totalPages; page++) {
|
|
9
|
+
const start = (page - 1) * pageSize;
|
|
10
|
+
const end = start + pageSize;
|
|
11
|
+
const pageItems = items.slice(start, end);
|
|
12
|
+
paths.push(mapper(page, pageItems, totalPages));
|
|
13
|
+
}
|
|
14
|
+
return paths;
|
|
15
|
+
}
|
|
16
|
+
async function generateNestedPaths(items, mapper) {
|
|
17
|
+
const nestedPaths = await Promise.all(items.map(mapper));
|
|
18
|
+
return nestedPaths.flat();
|
|
19
|
+
}
|
|
20
|
+
function chunkArray(array, size) {
|
|
21
|
+
const chunks = [];
|
|
22
|
+
for (let i = 0; i < array.length; i += size) {
|
|
23
|
+
chunks.push(array.slice(i, i + size));
|
|
24
|
+
}
|
|
25
|
+
return chunks;
|
|
26
|
+
}
|
|
27
|
+
function sortItems(items, key, order = "asc") {
|
|
28
|
+
return [...items].sort((a, b) => {
|
|
29
|
+
const aVal = a[key];
|
|
30
|
+
const bVal = b[key];
|
|
31
|
+
if (aVal < bVal) return order === "asc" ? -1 : 1;
|
|
32
|
+
if (aVal > bVal) return order === "asc" ? 1 : -1;
|
|
33
|
+
return 0;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function filterItems(items, predicate) {
|
|
37
|
+
return items.filter(predicate);
|
|
38
|
+
}
|
|
39
|
+
function groupItems(items, key) {
|
|
40
|
+
return items.reduce((groups, item) => {
|
|
41
|
+
const groupKey = String(item[key]);
|
|
42
|
+
if (!groups[groupKey]) {
|
|
43
|
+
groups[groupKey] = [];
|
|
44
|
+
}
|
|
45
|
+
groups[groupKey].push(item);
|
|
46
|
+
return groups;
|
|
47
|
+
}, {});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
generateStaticPaths,
|
|
52
|
+
generatePaginatedPaths,
|
|
53
|
+
generateNestedPaths,
|
|
54
|
+
chunkArray,
|
|
55
|
+
sortItems,
|
|
56
|
+
filterItems,
|
|
57
|
+
groupItems
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=chunk-33R4URZV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/static.ts"],"sourcesContent":["/**\n * Static Generation Utilities\n * \n * Helper functions for static site generation in Astro.\n */\n\n/**\n * Generate static paths from array of items\n * \n * @example\n * ```ts\n * export async function getStaticPaths() {\n * const brands = await getBrands();\n * return generateStaticPaths(brands, (brand) => ({\n * params: { brandId: brand.id },\n * props: { brand }\n * }));\n * }\n * ```\n */\nexport function generateStaticPaths<T, P = any>(\n items: T[],\n mapper: (item: T) => { params: Record<string, string | number>; props?: P }\n): Array<{ params: Record<string, string | number>; props?: P }> {\n return items.map(mapper);\n}\n\n/**\n * Generate paginated static paths\n * \n * @example\n * ```ts\n * export async function getStaticPaths() {\n * const items = await getItems();\n * return generatePaginatedPaths(items, 10, (page, items) => ({\n * params: { page: page.toString() },\n * props: { items, page }\n * }));\n * }\n * ```\n */\nexport function generatePaginatedPaths<T, P = any>(\n items: T[],\n pageSize: number,\n mapper: (\n page: number,\n pageItems: T[],\n totalPages: number\n ) => { params: Record<string, string | number>; props?: P }\n): Array<{ params: Record<string, string | number>; props?: P }> {\n const totalPages = Math.ceil(items.length / pageSize);\n const paths: Array<{ params: Record<string, string | number>; props?: P }> = [];\n \n for (let page = 1; page <= totalPages; page++) {\n const start = (page - 1) * pageSize;\n const end = start + pageSize;\n const pageItems = items.slice(start, end);\n \n paths.push(mapper(page, pageItems, totalPages));\n }\n \n return paths;\n}\n\n/**\n * Generate nested static paths\n * \n * @example\n * ```ts\n * export async function getStaticPaths() {\n * const brands = await getBrands();\n * return generateNestedPaths(brands, async (brand) => {\n * const products = await getProducts(brand.id);\n * return products.map(product => ({\n * params: { brandId: brand.id, productId: product.id },\n * props: { brand, product }\n * }));\n * });\n * }\n * ```\n */\nexport async function generateNestedPaths<T, P = any>(\n items: T[],\n mapper: (item: T) => Promise<Array<{ params: Record<string, string | number>; props?: P }>>\n): Promise<Array<{ params: Record<string, string | number>; props?: P }>> {\n const nestedPaths = await Promise.all(items.map(mapper));\n return nestedPaths.flat();\n}\n\n/**\n * Chunk array into smaller arrays\n */\nexport function chunkArray<T>(array: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n}\n\n/**\n * Sort items for static generation\n */\nexport function sortItems<T>(\n items: T[],\n key: keyof T,\n order: 'asc' | 'desc' = 'asc'\n): T[] {\n return [...items].sort((a, b) => {\n const aVal = a[key];\n const bVal = b[key];\n \n if (aVal < bVal) return order === 'asc' ? -1 : 1;\n if (aVal > bVal) return order === 'asc' ? 1 : -1;\n return 0;\n });\n}\n\n/**\n * Filter items for static generation\n */\nexport function filterItems<T>(\n items: T[],\n predicate: (item: T) => boolean\n): T[] {\n return items.filter(predicate);\n}\n\n/**\n * Group items by key\n */\nexport function groupItems<T>(\n items: T[],\n key: keyof T\n): Record<string, T[]> {\n return items.reduce((groups, item) => {\n const groupKey = String(item[key]);\n if (!groups[groupKey]) {\n groups[groupKey] = [];\n }\n groups[groupKey].push(item);\n return groups;\n }, {} as Record<string, T[]>);\n}\n"],"mappings":";AAoBO,SAAS,oBACd,OACA,QAC+D;AAC/D,SAAO,MAAM,IAAI,MAAM;AACzB;AAgBO,SAAS,uBACd,OACA,UACA,QAK+D;AAC/D,QAAM,aAAa,KAAK,KAAK,MAAM,SAAS,QAAQ;AACpD,QAAM,QAAuE,CAAC;AAE9E,WAAS,OAAO,GAAG,QAAQ,YAAY,QAAQ;AAC7C,UAAM,SAAS,OAAO,KAAK;AAC3B,UAAM,MAAM,QAAQ;AACpB,UAAM,YAAY,MAAM,MAAM,OAAO,GAAG;AAExC,UAAM,KAAK,OAAO,MAAM,WAAW,UAAU,CAAC;AAAA,EAChD;AAEA,SAAO;AACT;AAmBA,eAAsB,oBACpB,OACA,QACwE;AACxE,QAAM,cAAc,MAAM,QAAQ,IAAI,MAAM,IAAI,MAAM,CAAC;AACvD,SAAO,YAAY,KAAK;AAC1B;AAKO,SAAS,WAAc,OAAY,MAAqB;AAC7D,QAAM,SAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,MAAM;AAC3C,WAAO,KAAK,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAKO,SAAS,UACd,OACA,KACA,QAAwB,OACnB;AACL,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,UAAM,OAAO,EAAE,GAAG;AAClB,UAAM,OAAO,EAAE,GAAG;AAElB,QAAI,OAAO,KAAM,QAAO,UAAU,QAAQ,KAAK;AAC/C,QAAI,OAAO,KAAM,QAAO,UAAU,QAAQ,IAAI;AAC9C,WAAO;AAAA,EACT,CAAC;AACH;AAKO,SAAS,YACd,OACA,WACK;AACL,SAAO,MAAM,OAAO,SAAS;AAC/B;AAKO,SAAS,WACd,OACA,KACqB;AACrB,SAAO,MAAM,OAAO,CAAC,QAAQ,SAAS;AACpC,UAAM,WAAW,OAAO,KAAK,GAAG,CAAC;AACjC,QAAI,CAAC,OAAO,QAAQ,GAAG;AACrB,aAAO,QAAQ,IAAI,CAAC;AAAA,IACtB;AACA,WAAO,QAAQ,EAAE,KAAK,IAAI;AAC1B,WAAO;AAAA,EACT,GAAG,CAAC,CAAwB;AAC9B;","names":[]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// src/utils/hydration.ts
|
|
2
|
+
function serializeForHydration(data) {
|
|
3
|
+
return JSON.stringify(data, (key, value) => {
|
|
4
|
+
if (typeof value === "function") {
|
|
5
|
+
return void 0;
|
|
6
|
+
}
|
|
7
|
+
if (value === void 0) {
|
|
8
|
+
return { __type: "undefined" };
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function deserializeFromHydration(json) {
|
|
14
|
+
return JSON.parse(json, (key, value) => {
|
|
15
|
+
if (value && value.__type === "undefined") {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function createHydrationScript(variableName, data) {
|
|
22
|
+
const serialized = serializeForHydration(data);
|
|
23
|
+
return `window.${variableName} = ${serialized};`;
|
|
24
|
+
}
|
|
25
|
+
function createHydrationScripts(data) {
|
|
26
|
+
return Object.entries(data).map(([key, value]) => createHydrationScript(key, value)).join("\n");
|
|
27
|
+
}
|
|
28
|
+
function getHydratedData(variableName) {
|
|
29
|
+
if (typeof window === "undefined") {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const data = window[variableName];
|
|
33
|
+
return data ? deserializeFromHydration(JSON.stringify(data)) : null;
|
|
34
|
+
}
|
|
35
|
+
function shouldHydrate(strategy, options) {
|
|
36
|
+
if (typeof window === "undefined") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
switch (strategy) {
|
|
40
|
+
case "load":
|
|
41
|
+
return true;
|
|
42
|
+
case "idle":
|
|
43
|
+
return "requestIdleCallback" in window;
|
|
44
|
+
case "visible":
|
|
45
|
+
return "IntersectionObserver" in window;
|
|
46
|
+
case "media":
|
|
47
|
+
if (!options?.mediaQuery) return false;
|
|
48
|
+
return window.matchMedia(options.mediaQuery).matches;
|
|
49
|
+
case "only":
|
|
50
|
+
return false;
|
|
51
|
+
default:
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function createIslandProps(props) {
|
|
56
|
+
const cleanProps = {};
|
|
57
|
+
for (const [key, value] of Object.entries(props)) {
|
|
58
|
+
if (typeof value === "function") {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (value === void 0) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
cleanProps[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return cleanProps;
|
|
67
|
+
}
|
|
68
|
+
function mergeProps(serverProps, clientProps) {
|
|
69
|
+
return {
|
|
70
|
+
...serverProps,
|
|
71
|
+
...clientProps
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
serializeForHydration,
|
|
77
|
+
deserializeFromHydration,
|
|
78
|
+
createHydrationScript,
|
|
79
|
+
createHydrationScripts,
|
|
80
|
+
getHydratedData,
|
|
81
|
+
shouldHydrate,
|
|
82
|
+
createIslandProps,
|
|
83
|
+
mergeProps
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=chunk-64USRLVP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/hydration.ts"],"sourcesContent":["/**\n * Hydration Utilities\n * \n * Helper functions for Vue component hydration in Astro.\n */\n\n/**\n * Serialize data for client-side hydration\n * Safely handles dates, functions, and circular references\n */\nexport function serializeForHydration<T>(data: T): string {\n return JSON.stringify(data, (key, value) => {\n // Skip functions\n if (typeof value === 'function') {\n return undefined;\n }\n \n // Handle undefined\n if (value === undefined) {\n return { __type: 'undefined' };\n }\n \n return value;\n });\n}\n\n/**\n * Deserialize data on client-side\n */\nexport function deserializeFromHydration<T>(json: string): T {\n return JSON.parse(json, (key, value) => {\n // Handle undefined\n if (value && value.__type === 'undefined') {\n return undefined;\n }\n \n return value;\n });\n}\n\n/**\n * Create hydration script for Vue components\n * \n * @example\n * ```astro\n * <script set:html={createHydrationScript('userData', user)} />\n * ```\n */\nexport function createHydrationScript(\n variableName: string,\n data: any\n): string {\n const serialized = serializeForHydration(data);\n return `window.${variableName} = ${serialized};`;\n}\n\n/**\n * Create multiple hydration scripts\n */\nexport function createHydrationScripts(\n data: Record<string, any>\n): string {\n return Object.entries(data)\n .map(([key, value]) => createHydrationScript(key, value))\n .join('\\n');\n}\n\n/**\n * Get hydrated data on client-side\n */\nexport function getHydratedData<T>(variableName: string): T | null {\n if (typeof window === 'undefined') {\n return null;\n }\n \n const data = (window as any)[variableName];\n return data ? deserializeFromHydration(JSON.stringify(data)) : null;\n}\n\n/**\n * Check if component should hydrate\n */\nexport function shouldHydrate(\n strategy: 'load' | 'idle' | 'visible' | 'media' | 'only',\n options?: {\n mediaQuery?: string;\n }\n): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n \n switch (strategy) {\n case 'load':\n return true;\n \n case 'idle':\n return 'requestIdleCallback' in window;\n \n case 'visible':\n return 'IntersectionObserver' in window;\n \n case 'media':\n if (!options?.mediaQuery) return false;\n return window.matchMedia(options.mediaQuery).matches;\n \n case 'only':\n return false;\n \n default:\n return true;\n }\n}\n\n/**\n * Create Vue island props\n * Prepares props for Vue component islands\n */\nexport function createIslandProps<T extends Record<string, any>>(\n props: T\n): T {\n // Remove functions and non-serializable values\n const cleanProps: any = {};\n \n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'function') {\n continue;\n }\n \n if (value === undefined) {\n continue;\n }\n \n cleanProps[key] = value;\n }\n \n return cleanProps;\n}\n\n/**\n * Merge server and client props\n */\nexport function mergeProps<T extends Record<string, any>>(\n serverProps: T,\n clientProps: Partial<T>\n): T {\n return {\n ...serverProps,\n ...clientProps,\n };\n}\n"],"mappings":";AAUO,SAAS,sBAAyB,MAAiB;AACxD,SAAO,KAAK,UAAU,MAAM,CAAC,KAAK,UAAU;AAE1C,QAAI,OAAO,UAAU,YAAY;AAC/B,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,QAAW;AACvB,aAAO,EAAE,QAAQ,YAAY;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAKO,SAAS,yBAA4B,MAAiB;AAC3D,SAAO,KAAK,MAAM,MAAM,CAAC,KAAK,UAAU;AAEtC,QAAI,SAAS,MAAM,WAAW,aAAa;AACzC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAUO,SAAS,sBACd,cACA,MACQ;AACR,QAAM,aAAa,sBAAsB,IAAI;AAC7C,SAAO,UAAU,YAAY,MAAM,UAAU;AAC/C;AAKO,SAAS,uBACd,MACQ;AACR,SAAO,OAAO,QAAQ,IAAI,EACvB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,sBAAsB,KAAK,KAAK,CAAC,EACvD,KAAK,IAAI;AACd;AAKO,SAAS,gBAAmB,cAAgC;AACjE,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,OAAQ,OAAe,YAAY;AACzC,SAAO,OAAO,yBAAyB,KAAK,UAAU,IAAI,CAAC,IAAI;AACjE;AAKO,SAAS,cACd,UACA,SAGS;AACT,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAO,yBAAyB;AAAA,IAElC,KAAK;AACH,aAAO,0BAA0B;AAAA,IAEnC,KAAK;AACH,UAAI,CAAC,SAAS,WAAY,QAAO;AACjC,aAAO,OAAO,WAAW,QAAQ,UAAU,EAAE;AAAA,IAE/C,KAAK;AACH,aAAO;AAAA,IAET;AACE,aAAO;AAAA,EACX;AACF;AAMO,SAAS,kBACd,OACG;AAEH,QAAM,aAAkB,CAAC;AAEzB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,OAAO,UAAU,YAAY;AAC/B;AAAA,IACF;AAEA,QAAI,UAAU,QAAW;AACvB;AAAA,IACF;AAEA,eAAW,GAAG,IAAI;AAAA,EACpB;AAEA,SAAO;AACT;AAKO,SAAS,WACd,aACA,aACG;AACH,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;","names":[]}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// src/htlkg/index.ts
|
|
2
|
+
import tailwind from "@astrojs/tailwind";
|
|
3
|
+
import vue from "@astrojs/vue";
|
|
4
|
+
|
|
5
|
+
// src/htlkg/virtual-modules.ts
|
|
6
|
+
function serializePattern(pattern) {
|
|
7
|
+
if (pattern instanceof RegExp) {
|
|
8
|
+
return `new RegExp(${JSON.stringify(pattern.source)}, ${JSON.stringify(pattern.flags)})`;
|
|
9
|
+
}
|
|
10
|
+
return JSON.stringify(pattern);
|
|
11
|
+
}
|
|
12
|
+
function serializePatterns(patterns) {
|
|
13
|
+
if (!patterns || patterns.length === 0) return "[]";
|
|
14
|
+
return `[${patterns.map(serializePattern).join(", ")}]`;
|
|
15
|
+
}
|
|
16
|
+
function createVirtualModulePlugin(authConfig, loginPageConfig, amplifyConfig) {
|
|
17
|
+
return {
|
|
18
|
+
name: "htlkg-config",
|
|
19
|
+
resolveId(id) {
|
|
20
|
+
if (id === "virtual:htlkg-config") {
|
|
21
|
+
return "\0virtual:htlkg-config";
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
load(id) {
|
|
25
|
+
if (id === "\0virtual:htlkg-config") {
|
|
26
|
+
const serializedAuthConfig = `{
|
|
27
|
+
publicRoutes: ${serializePatterns(authConfig.publicRoutes || [])},
|
|
28
|
+
authenticatedRoutes: ${serializePatterns(authConfig.authenticatedRoutes || [])},
|
|
29
|
+
adminRoutes: ${serializePatterns(authConfig.adminRoutes || [])},
|
|
30
|
+
brandRoutes: ${JSON.stringify(authConfig.brandRoutes || [])},
|
|
31
|
+
loginUrl: ${JSON.stringify(authConfig.loginUrl || "/login")}
|
|
32
|
+
}`;
|
|
33
|
+
const serializedAmplifyConfig = amplifyConfig ? JSON.stringify(amplifyConfig) : "null";
|
|
34
|
+
const serializedLoginPageConfig = loginPageConfig !== false && loginPageConfig !== null ? JSON.stringify(loginPageConfig) : "null";
|
|
35
|
+
return `export const routeGuardConfig = ${serializedAuthConfig};
|
|
36
|
+
export const loginPageConfig = ${serializedLoginPageConfig};
|
|
37
|
+
export const amplifyConfig = ${serializedAmplifyConfig};`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
var virtualModuleTypes = `
|
|
43
|
+
declare module 'virtual:htlkg-config' {
|
|
44
|
+
import type { RouteGuardConfig, LoginPageConfig } from '@htlkg/astro/htlkg/config';
|
|
45
|
+
|
|
46
|
+
export const routeGuardConfig: RouteGuardConfig;
|
|
47
|
+
export const loginPageConfig: LoginPageConfig | null;
|
|
48
|
+
export const amplifyConfig: Record<string, unknown> | null;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
// src/htlkg/index.ts
|
|
53
|
+
var DEFAULT_ENV_VARS = [
|
|
54
|
+
"PUBLIC_COGNITO_USER_POOL_ID",
|
|
55
|
+
"PUBLIC_COGNITO_USER_POOL_CLIENT_ID"
|
|
56
|
+
];
|
|
57
|
+
function htlkg(options = {}) {
|
|
58
|
+
const {
|
|
59
|
+
auth = {},
|
|
60
|
+
loginPage = { path: "/login", title: "Sign In", redirectUrl: "/admin" },
|
|
61
|
+
validateEnv = true,
|
|
62
|
+
requiredEnvVars = DEFAULT_ENV_VARS,
|
|
63
|
+
tailwind: tailwindOptions,
|
|
64
|
+
amplify
|
|
65
|
+
} = options;
|
|
66
|
+
const integrations = [];
|
|
67
|
+
if (tailwindOptions !== false) {
|
|
68
|
+
const tailwindConfig = typeof tailwindOptions === "object" ? tailwindOptions : void 0;
|
|
69
|
+
integrations.push(
|
|
70
|
+
tailwind(tailwindConfig)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
integrations.push(
|
|
74
|
+
vue({
|
|
75
|
+
appEntrypoint: "@htlkg/astro/vue-app-setup"
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
integrations.push({
|
|
79
|
+
name: "@htlkg/astro",
|
|
80
|
+
hooks: {
|
|
81
|
+
"astro:config:setup": ({
|
|
82
|
+
config,
|
|
83
|
+
updateConfig,
|
|
84
|
+
addMiddleware,
|
|
85
|
+
injectRoute,
|
|
86
|
+
logger
|
|
87
|
+
}) => {
|
|
88
|
+
try {
|
|
89
|
+
const hasVue = config.integrations.some(
|
|
90
|
+
(i) => i.name === "@astrojs/vue"
|
|
91
|
+
);
|
|
92
|
+
if (hasVue) {
|
|
93
|
+
logger.info("Vue integration configured with Amplify app setup");
|
|
94
|
+
}
|
|
95
|
+
if (amplify) {
|
|
96
|
+
logger.info("Amplify configuration provided - will be configured on first request");
|
|
97
|
+
} else {
|
|
98
|
+
logger.info("No Amplify configuration provided - will use environment variables");
|
|
99
|
+
}
|
|
100
|
+
if (validateEnv && !amplify) {
|
|
101
|
+
const missing = requiredEnvVars.filter(
|
|
102
|
+
(varName) => !process.env[varName]
|
|
103
|
+
);
|
|
104
|
+
if (missing.length > 0) {
|
|
105
|
+
logger.warn(
|
|
106
|
+
`Missing required environment variables: ${missing.join(", ")}
|
|
107
|
+
Authentication may not work correctly. Please set these in your .env file:
|
|
108
|
+
${missing.map((v) => ` - ${v}`).join("\n")}`
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
logger.info("All required environment variables are present");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const virtualModulePlugin = createVirtualModulePlugin(
|
|
116
|
+
auth,
|
|
117
|
+
loginPage,
|
|
118
|
+
amplify || null
|
|
119
|
+
);
|
|
120
|
+
updateConfig({
|
|
121
|
+
vite: {
|
|
122
|
+
plugins: [virtualModulePlugin]
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
} catch (error) {
|
|
126
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
127
|
+
logger.error(
|
|
128
|
+
`Failed to create virtual module for route configuration: ${errorMsg}`
|
|
129
|
+
);
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
addMiddleware({
|
|
134
|
+
entrypoint: "@htlkg/astro/middleware",
|
|
135
|
+
order: "pre"
|
|
136
|
+
});
|
|
137
|
+
logger.info("Authentication middleware injected successfully");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
140
|
+
logger.error(`Failed to inject middleware: ${errorMsg}`);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
const vueIntegrationIndex = config.integrations.findIndex(
|
|
144
|
+
(i) => i.name === "@astrojs/vue"
|
|
145
|
+
);
|
|
146
|
+
if (vueIntegrationIndex === -1) {
|
|
147
|
+
logger.warn(
|
|
148
|
+
"@astrojs/vue integration not found.\nThe htlkg integration should have added it automatically.\nIf you see this warning, there may be an integration ordering issue."
|
|
149
|
+
);
|
|
150
|
+
} else {
|
|
151
|
+
logger.info("Vue app setup with Amplify configuration enabled");
|
|
152
|
+
}
|
|
153
|
+
const hasTailwind = config.integrations.some(
|
|
154
|
+
(i) => i.name === "@astrojs/tailwind" || i.name === "astro:tailwind"
|
|
155
|
+
);
|
|
156
|
+
if (hasTailwind) {
|
|
157
|
+
logger.info("Tailwind CSS integration configured");
|
|
158
|
+
}
|
|
159
|
+
if (loginPage !== false) {
|
|
160
|
+
try {
|
|
161
|
+
const loginPath = loginPage.path || "/login";
|
|
162
|
+
injectRoute({
|
|
163
|
+
pattern: loginPath,
|
|
164
|
+
entrypoint: "@htlkg/astro/auth/LoginPage.astro",
|
|
165
|
+
prerender: false
|
|
166
|
+
});
|
|
167
|
+
logger.info(`Injected default login page at ${loginPath}`);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
170
|
+
logger.warn(`Failed to inject login page: ${errorMsg}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
logger.info("htlkg integration configured successfully");
|
|
174
|
+
} catch (error) {
|
|
175
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
176
|
+
logger.error(
|
|
177
|
+
`Failed to configure htlkg integration: ${errorMsg}`
|
|
178
|
+
);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"astro:config:done": ({ injectTypes }) => {
|
|
183
|
+
injectTypes({
|
|
184
|
+
filename: "htlkg.d.ts",
|
|
185
|
+
content: `
|
|
186
|
+
import type { AuthUser } from '@htlkg/core/types';
|
|
187
|
+
|
|
188
|
+
declare global {
|
|
189
|
+
namespace App {
|
|
190
|
+
interface Locals {
|
|
191
|
+
user: AuthUser | null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
${virtualModuleTypes}
|
|
197
|
+
|
|
198
|
+
export {};
|
|
199
|
+
`
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return integrations.length === 1 ? integrations[0] : integrations;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export {
|
|
208
|
+
htlkg
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=chunk-WLOFOVCL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/htlkg/index.ts","../src/htlkg/virtual-modules.ts"],"sourcesContent":["/**\n * htlkg Astro Integration\n * \n * Provides zero-config setup for Hotelinking applications with:\n * - Tailwind CSS integration\n * - Vue 3 integration with Amplify setup\n * - Authentication middleware\n * - Route guards\n * - Login page generation\n */\n\nimport tailwind from '@astrojs/tailwind';\nimport vue from '@astrojs/vue';\nimport type { AstroIntegration } from 'astro';\nimport type { HtlkgIntegrationOptions } from './config.js';\nimport { createVirtualModulePlugin, virtualModuleTypes } from './virtual-modules.js';\n\n/**\n * Default environment variables required for AWS Amplify authentication\n */\nconst DEFAULT_ENV_VARS = [\n\t'PUBLIC_COGNITO_USER_POOL_ID',\n\t'PUBLIC_COGNITO_USER_POOL_CLIENT_ID'\n];\n\n/**\n * htlkg Astro integration that provides zero-config authentication setup.\n *\n * This integration automatically:\n * - Includes Tailwind CSS integration (can be disabled)\n * - Includes Vue 3 integration with Amplify and Nanostores setup\n * - Injects authentication middleware for AWS Amplify\n * - Configures route guards based on declarative configuration\n * - Validates required environment variables (optional)\n * - Injects TypeScript types for Astro.locals.user\n * - Provides a default login page (optional)\n *\n * @param options - Configuration options for the integration\n * @returns Astro integration object or array of integrations\n *\n * @example\n * // astro.config.mjs\n * import { htlkg } from '@htlkg/astro';\n *\n * export default defineConfig({\n * integrations: [\n * htlkg({\n * tailwind: { configFile: './tailwind.config.mjs' },\n * auth: {\n * publicRoutes: ['/login', '/'],\n * adminRoutes: [/^\\/admin/],\n * loginUrl: '/login'\n * }\n * })\n * ]\n * });\n */\nexport function htlkg(\n\toptions: HtlkgIntegrationOptions = {},\n): AstroIntegration | AstroIntegration[] {\n\tconst {\n\t\tauth = {},\n\t\tloginPage = { path: '/login', title: 'Sign In', redirectUrl: '/admin' },\n\t\tvalidateEnv = true,\n\t\trequiredEnvVars = DEFAULT_ENV_VARS,\n\t\ttailwind: tailwindOptions,\n\t\tamplify,\n\t} = options;\n\n\tconst integrations: AstroIntegration[] = [];\n\n\t// Add Tailwind integration (enabled by default)\n\tif (tailwindOptions !== false) {\n\t\tconst tailwindConfig =\n\t\t\ttypeof tailwindOptions === 'object' ? tailwindOptions : undefined;\n\t\tintegrations.push(\n\t\t\ttailwind(tailwindConfig as Parameters<typeof tailwind>[0]),\n\t\t);\n\t}\n\n\t// Add Vue integration with Amplify setup entrypoint\n\t// Use package import specifier that Vite can resolve\n\tintegrations.push(\n\t\tvue({\n\t\t\tappEntrypoint: '@htlkg/astro/vue-app-setup',\n\t\t}),\n\t);\n\n\t// Add the main htlkg integration\n\tintegrations.push({\n\t\tname: '@htlkg/astro',\n\t\thooks: {\n\t\t\t'astro:config:setup': ({\n\t\t\t\tconfig,\n\t\t\t\tupdateConfig,\n\t\t\t\taddMiddleware,\n\t\t\t\tinjectRoute,\n\t\t\t\tlogger,\n\t\t\t}) => {\n\t\t\t\ttry {\n\t\t\t\t\t// 1. Verify Vue integration is present\n\t\t\t\t\tconst hasVue = config.integrations.some(\n\t\t\t\t\t\t(i) => i.name === '@astrojs/vue',\n\t\t\t\t\t);\n\t\t\t\t\tif (hasVue) {\n\t\t\t\t\t\tlogger.info('Vue integration configured with Amplify app setup');\n\t\t\t\t\t}\n\n\t\t\t\t\t// 2. Amplify will be configured by the middleware on first request\n\t\t\t\t\tif (amplify) {\n\t\t\t\t\t\tlogger.info('Amplify configuration provided - will be configured on first request');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.info('No Amplify configuration provided - will use environment variables');\n\t\t\t\t\t}\n\n\t\t\t\t\t// 3. Validate environment variables (only if not using amplify_outputs.json)\n\t\t\t\t\tif (validateEnv && !amplify) {\n\t\t\t\t\t\tconst missing = requiredEnvVars.filter(\n\t\t\t\t\t\t\t(varName) => !process.env[varName],\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (missing.length > 0) {\n\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t`Missing required environment variables: ${missing.join(', ')}\\nAuthentication may not work correctly. Please set these in your .env file:\\n${missing.map((v) => ` - ${v}`).join('\\n')}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlogger.info('All required environment variables are present');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// 4. Create Vite virtual module plugin to pass configuration to middleware and pages\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst virtualModulePlugin = createVirtualModulePlugin(\n\t\t\t\t\t\t\tauth,\n\t\t\t\t\t\t\tloginPage,\n\t\t\t\t\t\t\tamplify || null\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tupdateConfig({\n\t\t\t\t\t\t\tvite: {\n\t\t\t\t\t\t\t\tplugins: [virtualModulePlugin],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst errorMsg =\n\t\t\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t`Failed to create virtual module for route configuration: ${errorMsg}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\t// 5. Inject middleware\n\t\t\t\t\ttry {\n\t\t\t\t\t\taddMiddleware({\n\t\t\t\t\t\t\tentrypoint: '@htlkg/astro/middleware',\n\t\t\t\t\t\t\torder: 'pre',\n\t\t\t\t\t\t});\n\t\t\t\t\t\tlogger.info('Authentication middleware injected successfully');\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst errorMsg =\n\t\t\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\t\t\tlogger.error(`Failed to inject middleware: ${errorMsg}`);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\n\t\t\t\t\t// 6. Verify Vue app entrypoint is configured\n\t\t\t\t\tconst vueIntegrationIndex = config.integrations.findIndex(\n\t\t\t\t\t\t(i) => i.name === '@astrojs/vue',\n\t\t\t\t\t);\n\n\t\t\t\t\tif (vueIntegrationIndex === -1) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t'@astrojs/vue integration not found.\\n' +\n\t\t\t\t\t\t\t\t'The htlkg integration should have added it automatically.\\n' +\n\t\t\t\t\t\t\t\t'If you see this warning, there may be an integration ordering issue.',\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogger.info('Vue app setup with Amplify configuration enabled');\n\t\t\t\t\t}\n\n\t\t\t\t\t// 7. Verify Tailwind integration\n\t\t\t\t\tconst hasTailwind = config.integrations.some(\n\t\t\t\t\t\t(i) =>\n\t\t\t\t\t\t\ti.name === '@astrojs/tailwind' || i.name === 'astro:tailwind',\n\t\t\t\t\t);\n\n\t\t\t\t\tif (hasTailwind) {\n\t\t\t\t\t\tlogger.info('Tailwind CSS integration configured');\n\t\t\t\t\t}\n\n\t\t\t\t\t// 8. Inject login page route if enabled\n\t\t\t\t\tif (loginPage !== false) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst loginPath = loginPage.path || '/login';\n\t\t\t\t\t\t\tinjectRoute({\n\t\t\t\t\t\t\t\tpattern: loginPath,\n\t\t\t\t\t\t\t\tentrypoint: '@htlkg/astro/auth/LoginPage.astro',\n\t\t\t\t\t\t\t\tprerender: false,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tlogger.info(`Injected default login page at ${loginPath}`);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst errorMsg =\n\t\t\t\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\t\t\t\tlogger.warn(`Failed to inject login page: ${errorMsg}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlogger.info('htlkg integration configured successfully');\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst errorMsg =\n\t\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error';\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t`Failed to configure htlkg integration: ${errorMsg}`,\n\t\t\t\t\t);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t},\n\t\t\t'astro:config:done': ({ injectTypes }) => {\n\t\t\t\t// Inject TypeScript types for Astro.locals and virtual module\n\t\t\t\tinjectTypes({\n\t\t\t\t\tfilename: 'htlkg.d.ts',\n\t\t\t\t\tcontent: `\nimport type { AuthUser } from '@htlkg/core/types';\n\ndeclare global {\n namespace App {\n interface Locals {\n user: AuthUser | null;\n }\n }\n}\n\n${virtualModuleTypes}\n\nexport {};\n`,\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t});\n\n\t// Return single integration or array based on what was added\n\treturn integrations.length === 1 ? integrations[0] : integrations;\n}\n","/**\n * Virtual module setup for htlkg integration\n * Creates Vite virtual modules to pass configuration to middleware and pages\n */\n\nimport type { RouteGuardConfig, LoginPageConfig, RoutePattern } from './config.js';\n\n/**\n * Serialize a route pattern (RegExp or string) to JavaScript code\n */\nfunction serializePattern(pattern: RegExp | string): string {\n\tif (pattern instanceof RegExp) {\n\t\treturn `new RegExp(${JSON.stringify(pattern.source)}, ${JSON.stringify(pattern.flags)})`;\n\t}\n\treturn JSON.stringify(pattern);\n}\n\n/**\n * Serialize an array of route patterns to JavaScript code\n */\nfunction serializePatterns(patterns: RoutePattern[]): string {\n\tif (!patterns || patterns.length === 0) return '[]';\n\treturn `[${patterns.map(serializePattern).join(', ')}]`;\n}\n\n/**\n * Create the virtual module plugin for Vite\n * This plugin provides configuration to middleware and pages at runtime\n */\nexport function createVirtualModulePlugin(\n\tauthConfig: RouteGuardConfig,\n\tloginPageConfig: LoginPageConfig | false | null,\n\tamplifyConfig: Record<string, unknown> | null\n) {\n\treturn {\n\t\tname: 'htlkg-config',\n\t\tresolveId(id: string) {\n\t\t\tif (id === 'virtual:htlkg-config') {\n\t\t\t\treturn '\\0virtual:htlkg-config';\n\t\t\t}\n\t\t},\n\t\tload(id: string) {\n\t\t\tif (id === '\\0virtual:htlkg-config') {\n\t\t\t\t// Serialize auth configuration with RegExp support\n\t\t\t\tconst serializedAuthConfig = `{\n\t\t\t\t\tpublicRoutes: ${serializePatterns(authConfig.publicRoutes || [])},\n\t\t\t\t\tauthenticatedRoutes: ${serializePatterns(authConfig.authenticatedRoutes || [])},\n\t\t\t\t\tadminRoutes: ${serializePatterns(authConfig.adminRoutes || [])},\n\t\t\t\t\tbrandRoutes: ${JSON.stringify(authConfig.brandRoutes || [])},\n\t\t\t\t\tloginUrl: ${JSON.stringify(authConfig.loginUrl || '/login')}\n\t\t\t\t}`;\n\n\t\t\t\tconst serializedAmplifyConfig = amplifyConfig\n\t\t\t\t\t? JSON.stringify(amplifyConfig)\n\t\t\t\t\t: 'null';\n\n\t\t\t\tconst serializedLoginPageConfig = loginPageConfig !== false && loginPageConfig !== null\n\t\t\t\t\t? JSON.stringify(loginPageConfig)\n\t\t\t\t\t: 'null';\n\n\t\t\t\treturn `export const routeGuardConfig = ${serializedAuthConfig};\nexport const loginPageConfig = ${serializedLoginPageConfig};\nexport const amplifyConfig = ${serializedAmplifyConfig};`;\n\t\t\t}\n\t\t},\n\t};\n}\n\n/**\n * Type definitions for the virtual module\n * This should be injected into the project's type definitions\n */\nexport const virtualModuleTypes = `\ndeclare module 'virtual:htlkg-config' {\n import type { RouteGuardConfig, LoginPageConfig } from '@htlkg/astro/htlkg/config';\n \n export const routeGuardConfig: RouteGuardConfig;\n export const loginPageConfig: LoginPageConfig | null;\n export const amplifyConfig: Record<string, unknown> | null;\n}\n`;\n"],"mappings":"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","names":[]}
|