@htlkg/astro 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -8
- package/dist/chunk-2GML443T.js +273 -0
- package/dist/chunk-2GML443T.js.map +1 -0
- package/dist/{chunk-Z2ZAL7KX.js → chunk-UBF5F2RG.js} +1 -1
- package/dist/{chunk-Z2ZAL7KX.js.map → chunk-UBF5F2RG.js.map} +1 -1
- package/dist/chunk-XOY5BM3N.js +151 -0
- package/dist/chunk-XOY5BM3N.js.map +1 -0
- package/dist/htlkg/config.js +1 -1
- package/dist/htlkg/index.js +1 -1
- package/dist/index.js +126 -14
- package/dist/index.js.map +1 -1
- package/dist/utils/index.js +31 -12
- package/dist/vue-app-setup.js +47 -0
- package/dist/vue-app-setup.js.map +1 -0
- package/package.json +39 -10
- package/src/auth/auth.md +77 -0
- package/src/components/Island.astro +56 -0
- package/src/components/components.md +79 -0
- package/src/factories/createListPage.ts +290 -0
- package/src/factories/index.ts +16 -0
- package/src/htlkg/config.ts +10 -0
- package/src/htlkg/htlkg.md +63 -0
- package/src/htlkg/index.ts +48 -165
- package/src/index.ts +3 -0
- package/src/layouts/layouts.md +87 -0
- package/src/middleware/middleware.md +82 -0
- package/src/patterns/patterns.md +104 -0
- package/src/utils/filters.ts +320 -0
- package/src/utils/index.ts +8 -2
- package/src/utils/params.ts +260 -0
- package/src/utils/utils.md +86 -0
- package/dist/chunk-IWK5QCVD.js +0 -216
- package/dist/chunk-IWK5QCVD.js.map +0 -1
- package/dist/chunk-ZQ4XMJH7.js +0 -1
- package/dist/chunk-ZQ4XMJH7.js.map +0 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List Page Factory
|
|
3
|
+
*
|
|
4
|
+
* Factory function for creating standardized list pages with automatic
|
|
5
|
+
* data fetching, filtering, sorting, and pagination.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AstroGlobal } from "astro";
|
|
9
|
+
import type { WritableAtom } from "nanostores";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
parseListParams,
|
|
13
|
+
type ListParams,
|
|
14
|
+
type ListParamConfig,
|
|
15
|
+
type FilterFieldConfig,
|
|
16
|
+
} from "../utils/params";
|
|
17
|
+
import {
|
|
18
|
+
buildGraphQLFilter,
|
|
19
|
+
applyClientFilters,
|
|
20
|
+
sortItems,
|
|
21
|
+
paginateItems,
|
|
22
|
+
} from "../utils/filters";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Breadcrumb item
|
|
26
|
+
*/
|
|
27
|
+
export interface BreadcrumbItem {
|
|
28
|
+
label: string;
|
|
29
|
+
href?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Layout props returned by the factory
|
|
34
|
+
*/
|
|
35
|
+
export interface LayoutProps {
|
|
36
|
+
title: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
currentPage: string;
|
|
39
|
+
breadcrumbs: BreadcrumbItem[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Table initial state
|
|
44
|
+
*/
|
|
45
|
+
export interface TableInitialState {
|
|
46
|
+
currentPage: number;
|
|
47
|
+
pageSize: number;
|
|
48
|
+
sortKey: string;
|
|
49
|
+
sortOrder: "asc" | "desc";
|
|
50
|
+
totalItems: number;
|
|
51
|
+
totalPages: number;
|
|
52
|
+
filters: Record<string, any>;
|
|
53
|
+
search?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Related store configuration
|
|
58
|
+
*/
|
|
59
|
+
export interface RelatedStoreConfig<T = any> {
|
|
60
|
+
store: WritableAtom<T[]>;
|
|
61
|
+
fetch: () => Promise<T[]>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Configuration for createListPage
|
|
66
|
+
*/
|
|
67
|
+
export interface ListPageConfig<T> {
|
|
68
|
+
// Page metadata
|
|
69
|
+
/** Page title */
|
|
70
|
+
title: string;
|
|
71
|
+
/** Page description */
|
|
72
|
+
description?: string;
|
|
73
|
+
/** Page identifier (for sidebar active state) */
|
|
74
|
+
pageId: string;
|
|
75
|
+
/** Custom breadcrumbs (auto-generated if not provided) */
|
|
76
|
+
breadcrumbs?: BreadcrumbItem[];
|
|
77
|
+
|
|
78
|
+
// Data fetching
|
|
79
|
+
/** Fetch function that retrieves data */
|
|
80
|
+
fetchFn: (params: {
|
|
81
|
+
filter: any;
|
|
82
|
+
limit?: number;
|
|
83
|
+
nextToken?: string;
|
|
84
|
+
}) => Promise<{ data: T[]; nextToken?: string | null }>;
|
|
85
|
+
|
|
86
|
+
// Data handling
|
|
87
|
+
/** Transform function to apply to fetched data */
|
|
88
|
+
transform?: (item: any) => T;
|
|
89
|
+
/** Main store to set with data */
|
|
90
|
+
store: WritableAtom<T[]>;
|
|
91
|
+
/** Related stores to populate */
|
|
92
|
+
relatedStores?: Record<string, RelatedStoreConfig>;
|
|
93
|
+
|
|
94
|
+
// Filtering/sorting config
|
|
95
|
+
/** Searchable field keys */
|
|
96
|
+
searchableFields?: string[];
|
|
97
|
+
/** Filterable field configurations */
|
|
98
|
+
filterableFields?: FilterFieldConfig[];
|
|
99
|
+
/** Sortable field keys (all by default) */
|
|
100
|
+
sortableFields?: string[];
|
|
101
|
+
/** Default sort configuration */
|
|
102
|
+
defaultSort?: { key: string; order: "asc" | "desc" };
|
|
103
|
+
/** Default page size */
|
|
104
|
+
defaultPageSize?: number;
|
|
105
|
+
/** Maximum items to fetch from API */
|
|
106
|
+
fetchLimit?: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Result from createListPage
|
|
111
|
+
*/
|
|
112
|
+
export interface ListPageResult<T> {
|
|
113
|
+
/** Props to spread to Layout component */
|
|
114
|
+
layoutProps: LayoutProps;
|
|
115
|
+
/** Initial state for table component */
|
|
116
|
+
initialState: TableInitialState;
|
|
117
|
+
/** Processed items (paginated) */
|
|
118
|
+
items: T[];
|
|
119
|
+
/** All items (pre-pagination, for client-side operations) */
|
|
120
|
+
allItems: T[];
|
|
121
|
+
/** Parsed URL parameters */
|
|
122
|
+
params: ListParams;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Default breadcrumb generator
|
|
127
|
+
*/
|
|
128
|
+
function generateBreadcrumbs(pageId: string, title: string): BreadcrumbItem[] {
|
|
129
|
+
return [
|
|
130
|
+
{ label: "Admin", href: "/admin" },
|
|
131
|
+
{ label: title },
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Create a list page with automatic data handling
|
|
137
|
+
*
|
|
138
|
+
* This factory handles:
|
|
139
|
+
* - URL parameter parsing (page, pageSize, sortKey, sortOrder, filters)
|
|
140
|
+
* - Data fetching with GraphQL filter building
|
|
141
|
+
* - Data transformation
|
|
142
|
+
* - Client-side filtering (for computed fields)
|
|
143
|
+
* - Sorting
|
|
144
|
+
* - Pagination
|
|
145
|
+
* - Store population
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```astro
|
|
149
|
+
* ---
|
|
150
|
+
* import { createListPage } from '@htlkg/astro/factories';
|
|
151
|
+
* import { $accounts } from '@/stores/accounts';
|
|
152
|
+
*
|
|
153
|
+
* const { layoutProps, initialState, items } = await createListPage(Astro, {
|
|
154
|
+
* title: 'Accounts',
|
|
155
|
+
* pageId: 'accounts',
|
|
156
|
+
* store: $accounts,
|
|
157
|
+
*
|
|
158
|
+
* fetchFn: async ({ filter }) => {
|
|
159
|
+
* const client = getServerClient(Astro);
|
|
160
|
+
* return await client.models.Account.list({ filter });
|
|
161
|
+
* },
|
|
162
|
+
*
|
|
163
|
+
* transform: (account) => ({
|
|
164
|
+
* id: account.id,
|
|
165
|
+
* name: account.name,
|
|
166
|
+
* brandCount: account.brands?.length ?? 0,
|
|
167
|
+
* }),
|
|
168
|
+
*
|
|
169
|
+
* filterableFields: [
|
|
170
|
+
* { key: 'name', type: 'text', graphql: true },
|
|
171
|
+
* { key: 'status', type: 'select', graphql: true, options: ['active', 'inactive'] },
|
|
172
|
+
* ],
|
|
173
|
+
*
|
|
174
|
+
* defaultSort: { key: 'name', order: 'asc' },
|
|
175
|
+
* });
|
|
176
|
+
* ---
|
|
177
|
+
*
|
|
178
|
+
* <Layout {...layoutProps}>
|
|
179
|
+
* <AccountsTable client:load initialState={initialState} />
|
|
180
|
+
* </Layout>
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export async function createListPage<T extends Record<string, any>>(
|
|
184
|
+
astro: AstroGlobal,
|
|
185
|
+
config: ListPageConfig<T>
|
|
186
|
+
): Promise<ListPageResult<T>> {
|
|
187
|
+
const {
|
|
188
|
+
title,
|
|
189
|
+
description,
|
|
190
|
+
pageId,
|
|
191
|
+
breadcrumbs,
|
|
192
|
+
fetchFn,
|
|
193
|
+
transform,
|
|
194
|
+
store,
|
|
195
|
+
relatedStores,
|
|
196
|
+
searchableFields,
|
|
197
|
+
filterableFields = [],
|
|
198
|
+
defaultSort,
|
|
199
|
+
defaultPageSize = 25,
|
|
200
|
+
fetchLimit = 1000,
|
|
201
|
+
} = config;
|
|
202
|
+
|
|
203
|
+
// 1. Parse URL parameters
|
|
204
|
+
const paramConfig: ListParamConfig = {
|
|
205
|
+
defaultPageSize,
|
|
206
|
+
defaultSort,
|
|
207
|
+
searchableFields,
|
|
208
|
+
filterableFields,
|
|
209
|
+
};
|
|
210
|
+
const params = parseListParams(astro.url, paramConfig);
|
|
211
|
+
|
|
212
|
+
// 2. Build GraphQL filter from URL params
|
|
213
|
+
const graphqlFilter = buildGraphQLFilter(params.filters, filterableFields, {
|
|
214
|
+
search: params.search,
|
|
215
|
+
searchFields: searchableFields,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// 3. Fetch data with pagination
|
|
219
|
+
let allItems: T[] = [];
|
|
220
|
+
let nextToken: string | null | undefined = undefined;
|
|
221
|
+
|
|
222
|
+
do {
|
|
223
|
+
const result = await fetchFn({
|
|
224
|
+
filter: graphqlFilter,
|
|
225
|
+
limit: fetchLimit,
|
|
226
|
+
nextToken: nextToken ?? undefined,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const data = result.data ?? [];
|
|
230
|
+
allItems = allItems.concat(data);
|
|
231
|
+
nextToken = result.nextToken;
|
|
232
|
+
} while (nextToken);
|
|
233
|
+
|
|
234
|
+
// 4. Transform data if needed
|
|
235
|
+
if (transform) {
|
|
236
|
+
allItems = allItems.map(transform);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 5. Apply client-side filtering (for computed fields not in GraphQL)
|
|
240
|
+
allItems = applyClientFilters(allItems, params.filters, filterableFields, {
|
|
241
|
+
search: params.search,
|
|
242
|
+
searchFields: searchableFields,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// 6. Sort
|
|
246
|
+
allItems = sortItems(allItems, params.sortKey, params.sortOrder);
|
|
247
|
+
|
|
248
|
+
// 7. Paginate
|
|
249
|
+
const { paginatedItems, totalItems, totalPages, currentPage, pageSize } = paginateItems(
|
|
250
|
+
allItems,
|
|
251
|
+
params.page,
|
|
252
|
+
params.pageSize
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// 8. Set main store
|
|
256
|
+
store.set(paginatedItems);
|
|
257
|
+
|
|
258
|
+
// 9. Fetch and set related stores
|
|
259
|
+
if (relatedStores) {
|
|
260
|
+
await Promise.all(
|
|
261
|
+
Object.entries(relatedStores).map(async ([, config]) => {
|
|
262
|
+
const data = await config.fetch();
|
|
263
|
+
config.store.set(data);
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 10. Build result
|
|
269
|
+
return {
|
|
270
|
+
layoutProps: {
|
|
271
|
+
title,
|
|
272
|
+
description,
|
|
273
|
+
currentPage: pageId,
|
|
274
|
+
breadcrumbs: breadcrumbs ?? generateBreadcrumbs(pageId, title),
|
|
275
|
+
},
|
|
276
|
+
initialState: {
|
|
277
|
+
currentPage,
|
|
278
|
+
pageSize,
|
|
279
|
+
sortKey: params.sortKey,
|
|
280
|
+
sortOrder: params.sortOrder,
|
|
281
|
+
totalItems,
|
|
282
|
+
totalPages,
|
|
283
|
+
filters: params.filters,
|
|
284
|
+
search: params.search,
|
|
285
|
+
},
|
|
286
|
+
items: paginatedItems,
|
|
287
|
+
allItems,
|
|
288
|
+
params,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Factories for @htlkg/astro
|
|
3
|
+
*
|
|
4
|
+
* Factory functions for creating standardized Astro pages with automatic
|
|
5
|
+
* data handling, filtering, sorting, and pagination.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
createListPage,
|
|
10
|
+
type ListPageConfig,
|
|
11
|
+
type ListPageResult,
|
|
12
|
+
type LayoutProps,
|
|
13
|
+
type TableInitialState,
|
|
14
|
+
type BreadcrumbItem,
|
|
15
|
+
type RelatedStoreConfig,
|
|
16
|
+
} from "./createListPage";
|
package/src/htlkg/config.ts
CHANGED
|
@@ -209,6 +209,16 @@ export interface HtlkgIntegrationOptions {
|
|
|
209
209
|
* @default undefined (Tailwind enabled with default config)
|
|
210
210
|
*/
|
|
211
211
|
tailwind?: Record<string, unknown> | false;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Vue app setup mode.
|
|
215
|
+
* - 'full': Use the Amplify-configured Vue app setup (requires SSR)
|
|
216
|
+
* - 'basic': Use basic Vue integration without Amplify app setup (works with static)
|
|
217
|
+
* - 'auto': Automatically detect based on output mode (default)
|
|
218
|
+
*
|
|
219
|
+
* @default 'auto'
|
|
220
|
+
*/
|
|
221
|
+
vueAppSetup?: 'full' | 'basic' | 'auto';
|
|
212
222
|
}
|
|
213
223
|
|
|
214
224
|
/**
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# htlkg Integration
|
|
2
|
+
|
|
3
|
+
Zero-config Astro integration for Hotelinking applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
// astro.config.mjs
|
|
9
|
+
import { defineConfig } from 'astro/config';
|
|
10
|
+
import { htlkg } from '@htlkg/astro';
|
|
11
|
+
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
integrations: [
|
|
14
|
+
htlkg({
|
|
15
|
+
auth: {
|
|
16
|
+
enabled: true,
|
|
17
|
+
loginPage: '/login',
|
|
18
|
+
publicRoutes: ['/login', '/public'],
|
|
19
|
+
},
|
|
20
|
+
brandRoutes: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
pattern: '/[brandId]',
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- Automatic middleware setup
|
|
32
|
+
- Route guard configuration
|
|
33
|
+
- Login page injection
|
|
34
|
+
- Brand route handling
|
|
35
|
+
- Virtual module configuration
|
|
36
|
+
|
|
37
|
+
## Options
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
interface HtlkgIntegrationOptions {
|
|
41
|
+
auth?: {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
loginPage?: string;
|
|
44
|
+
publicRoutes?: RoutePattern[];
|
|
45
|
+
protectedRoutes?: RoutePattern[];
|
|
46
|
+
};
|
|
47
|
+
brandRoutes?: {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
pattern?: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Virtual Modules
|
|
55
|
+
|
|
56
|
+
The integration provides virtual modules for configuration:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Access config in your code
|
|
60
|
+
import config from 'virtual:htlkg-config';
|
|
61
|
+
|
|
62
|
+
console.log(config.auth.loginPage);
|
|
63
|
+
```
|
package/src/htlkg/index.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* htlkg Astro Integration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - Tailwind CSS integration
|
|
6
|
-
* - Vue 3 integration with Amplify setup
|
|
7
|
-
* - Authentication middleware
|
|
8
|
-
* - Route guards
|
|
9
|
-
* - Login page generation
|
|
4
|
+
* Supports static, hybrid, and full SSR output modes.
|
|
10
5
|
*/
|
|
11
6
|
|
|
12
7
|
import tailwind from '@astrojs/tailwind';
|
|
@@ -16,46 +11,11 @@ import type { HtlkgIntegrationOptions } from './config.js';
|
|
|
16
11
|
import { createVirtualModulePlugin, virtualModuleTypes } from './virtual-modules.js';
|
|
17
12
|
import vueDevTools from 'vite-plugin-vue-devtools';
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Default environment variables required for AWS Amplify authentication
|
|
21
|
-
*/
|
|
22
14
|
const DEFAULT_ENV_VARS = [
|
|
23
15
|
'PUBLIC_COGNITO_USER_POOL_ID',
|
|
24
16
|
'PUBLIC_COGNITO_USER_POOL_CLIENT_ID'
|
|
25
17
|
];
|
|
26
18
|
|
|
27
|
-
/**
|
|
28
|
-
* htlkg Astro integration that provides zero-config authentication setup.
|
|
29
|
-
*
|
|
30
|
-
* This integration automatically:
|
|
31
|
-
* - Includes Tailwind CSS integration (can be disabled)
|
|
32
|
-
* - Includes Vue 3 integration with Amplify and Nanostores setup
|
|
33
|
-
* - Injects authentication middleware for AWS Amplify
|
|
34
|
-
* - Configures route guards based on declarative configuration
|
|
35
|
-
* - Validates required environment variables (optional)
|
|
36
|
-
* - Injects TypeScript types for Astro.locals.user
|
|
37
|
-
* - Provides a default login page (optional)
|
|
38
|
-
*
|
|
39
|
-
* @param options - Configuration options for the integration
|
|
40
|
-
* @returns Astro integration object or array of integrations
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* // astro.config.mjs
|
|
44
|
-
* import { htlkg } from '@htlkg/astro';
|
|
45
|
-
*
|
|
46
|
-
* export default defineConfig({
|
|
47
|
-
* integrations: [
|
|
48
|
-
* htlkg({
|
|
49
|
-
* tailwind: { configFile: './tailwind.config.mjs' },
|
|
50
|
-
* auth: {
|
|
51
|
-
* publicRoutes: ['/login', '/'],
|
|
52
|
-
* adminRoutes: [/^\/admin/],
|
|
53
|
-
* loginUrl: '/login'
|
|
54
|
-
* }
|
|
55
|
-
* })
|
|
56
|
-
* ]
|
|
57
|
-
* });
|
|
58
|
-
*/
|
|
59
19
|
export function htlkg(
|
|
60
20
|
options: HtlkgIntegrationOptions = {},
|
|
61
21
|
): AstroIntegration | AstroIntegration[] {
|
|
@@ -66,6 +26,7 @@ export function htlkg(
|
|
|
66
26
|
requiredEnvVars = DEFAULT_ENV_VARS,
|
|
67
27
|
tailwind: tailwindOptions,
|
|
68
28
|
amplify,
|
|
29
|
+
vueAppSetup = 'auto',
|
|
69
30
|
} = options;
|
|
70
31
|
|
|
71
32
|
const integrations: AstroIntegration[] = [];
|
|
@@ -79,154 +40,77 @@ export function htlkg(
|
|
|
79
40
|
);
|
|
80
41
|
}
|
|
81
42
|
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
43
|
+
// Determine Vue setup mode:
|
|
44
|
+
// - 'full': Use Amplify app entrypoint (requires SSR)
|
|
45
|
+
// - 'basic': Basic Vue without app entrypoint (works with static)
|
|
46
|
+
// - 'auto': Default to 'basic' for compatibility with static builds
|
|
47
|
+
const useFullVueSetup = vueAppSetup === 'full';
|
|
48
|
+
|
|
49
|
+
// Add Vue integration
|
|
50
|
+
if (useFullVueSetup) {
|
|
51
|
+
integrations.push(vue({ appEntrypoint: '@htlkg/astro/vue-app-setup' }));
|
|
52
|
+
} else {
|
|
53
|
+
integrations.push(vue());
|
|
54
|
+
}
|
|
89
55
|
|
|
90
56
|
// Add the main htlkg integration
|
|
91
57
|
integrations.push({
|
|
92
58
|
name: '@htlkg/astro',
|
|
93
59
|
hooks: {
|
|
94
|
-
'astro:config:setup': ({
|
|
95
|
-
config,
|
|
96
|
-
updateConfig,
|
|
97
|
-
addMiddleware,
|
|
98
|
-
injectRoute,
|
|
99
|
-
logger,
|
|
100
|
-
}) => {
|
|
60
|
+
'astro:config:setup': ({ updateConfig, addMiddleware, injectRoute, logger }) => {
|
|
101
61
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
if (hasVue) {
|
|
107
|
-
logger.info('Vue integration configured with Amplify app setup');
|
|
108
|
-
}
|
|
62
|
+
logger.info(useFullVueSetup
|
|
63
|
+
? 'Vue configured with Amplify app setup'
|
|
64
|
+
: 'Vue configured (basic mode)');
|
|
109
65
|
|
|
110
|
-
// 2. Amplify will be configured by the middleware on first request
|
|
111
66
|
if (amplify) {
|
|
112
|
-
logger.info('Amplify configuration provided
|
|
113
|
-
} else {
|
|
114
|
-
logger.info('No Amplify configuration provided - will use environment variables');
|
|
67
|
+
logger.info('Amplify configuration provided');
|
|
115
68
|
}
|
|
116
69
|
|
|
117
|
-
//
|
|
118
|
-
if (validateEnv && !amplify) {
|
|
119
|
-
const missing = requiredEnvVars.filter(
|
|
120
|
-
(varName) => !process.env[varName],
|
|
121
|
-
);
|
|
122
|
-
|
|
70
|
+
// Validate env vars (only for full setup)
|
|
71
|
+
if (validateEnv && !amplify && useFullVueSetup) {
|
|
72
|
+
const missing = requiredEnvVars.filter(v => !process.env[v]);
|
|
123
73
|
if (missing.length > 0) {
|
|
124
|
-
logger.warn(
|
|
125
|
-
`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')}`,
|
|
126
|
-
);
|
|
127
|
-
} else {
|
|
128
|
-
logger.info('All required environment variables are present');
|
|
74
|
+
logger.warn(`Missing env vars: ${missing.join(', ')}`);
|
|
129
75
|
}
|
|
130
76
|
}
|
|
131
77
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
const vitePlugins: any[] = [virtualModulePlugin];
|
|
141
|
-
|
|
142
|
-
// Add Vue DevTools plugin in development
|
|
143
|
-
if (import.meta.env?.DEV !== false) {
|
|
144
|
-
vitePlugins.push(vueDevTools());
|
|
145
|
-
logger.info('Vue DevTools plugin enabled for development');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
updateConfig({
|
|
149
|
-
vite: {
|
|
150
|
-
plugins: vitePlugins,
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
} catch (error) {
|
|
154
|
-
const errorMsg =
|
|
155
|
-
error instanceof Error ? error.message : 'Unknown error';
|
|
156
|
-
logger.error(
|
|
157
|
-
`Failed to create virtual module for route configuration: ${errorMsg}`,
|
|
158
|
-
);
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// 5. Inject middleware
|
|
163
|
-
try {
|
|
164
|
-
addMiddleware({
|
|
165
|
-
entrypoint: '@htlkg/astro/middleware',
|
|
166
|
-
order: 'pre',
|
|
167
|
-
});
|
|
168
|
-
logger.info('Authentication middleware injected successfully');
|
|
169
|
-
} catch (error) {
|
|
170
|
-
const errorMsg =
|
|
171
|
-
error instanceof Error ? error.message : 'Unknown error';
|
|
172
|
-
logger.error(`Failed to inject middleware: ${errorMsg}`);
|
|
173
|
-
throw error;
|
|
78
|
+
// Create Vite virtual module plugin
|
|
79
|
+
const virtualModulePlugin = createVirtualModulePlugin(auth, loginPage, amplify || null);
|
|
80
|
+
const vitePlugins: any[] = [virtualModulePlugin];
|
|
81
|
+
|
|
82
|
+
if (import.meta.env?.DEV !== false) {
|
|
83
|
+
vitePlugins.push(vueDevTools());
|
|
84
|
+
logger.info('Vue DevTools enabled');
|
|
174
85
|
}
|
|
175
86
|
|
|
176
|
-
|
|
177
|
-
const vueIntegrationIndex = config.integrations.findIndex(
|
|
178
|
-
(i) => i.name === '@astrojs/vue',
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
if (vueIntegrationIndex === -1) {
|
|
182
|
-
logger.warn(
|
|
183
|
-
'@astrojs/vue integration not found.\n' +
|
|
184
|
-
'The htlkg integration should have added it automatically.\n' +
|
|
185
|
-
'If you see this warning, there may be an integration ordering issue.',
|
|
186
|
-
);
|
|
187
|
-
} else {
|
|
188
|
-
logger.info('Vue app setup with Amplify configuration enabled');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// 7. Verify Tailwind integration
|
|
192
|
-
const hasTailwind = config.integrations.some(
|
|
193
|
-
(i) =>
|
|
194
|
-
i.name === '@astrojs/tailwind' || i.name === 'astro:tailwind',
|
|
195
|
-
);
|
|
87
|
+
updateConfig({ vite: { plugins: vitePlugins } });
|
|
196
88
|
|
|
197
|
-
|
|
198
|
-
|
|
89
|
+
// Inject middleware (only for full setup)
|
|
90
|
+
if (useFullVueSetup) {
|
|
91
|
+
addMiddleware({ entrypoint: '@htlkg/astro/middleware', order: 'pre' });
|
|
92
|
+
logger.info('Authentication middleware injected');
|
|
199
93
|
}
|
|
200
94
|
|
|
201
|
-
//
|
|
202
|
-
if (loginPage !== false) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
logger.info(`Injected default login page at ${loginPath}`);
|
|
211
|
-
} catch (error) {
|
|
212
|
-
const errorMsg =
|
|
213
|
-
error instanceof Error ? error.message : 'Unknown error';
|
|
214
|
-
logger.warn(`Failed to inject login page: ${errorMsg}`);
|
|
215
|
-
}
|
|
95
|
+
// Inject login page (only for full setup)
|
|
96
|
+
if (loginPage !== false && useFullVueSetup) {
|
|
97
|
+
const loginPath = loginPage.path || '/login';
|
|
98
|
+
injectRoute({
|
|
99
|
+
pattern: loginPath,
|
|
100
|
+
entrypoint: '@htlkg/astro/auth/LoginPage.astro',
|
|
101
|
+
prerender: false,
|
|
102
|
+
});
|
|
103
|
+
logger.info(`Login page injected at ${loginPath}`);
|
|
216
104
|
}
|
|
217
105
|
|
|
218
|
-
logger.info('htlkg integration configured
|
|
106
|
+
logger.info('htlkg integration configured');
|
|
219
107
|
} catch (error) {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
logger.error(
|
|
223
|
-
`Failed to configure htlkg integration: ${errorMsg}`,
|
|
224
|
-
);
|
|
108
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
109
|
+
logger.error(`htlkg configuration failed: ${msg}`);
|
|
225
110
|
throw error;
|
|
226
111
|
}
|
|
227
112
|
},
|
|
228
113
|
'astro:config:done': ({ injectTypes }) => {
|
|
229
|
-
// Inject TypeScript types for Astro.locals and virtual module
|
|
230
114
|
injectTypes({
|
|
231
115
|
filename: 'htlkg.d.ts',
|
|
232
116
|
content: `
|
|
@@ -249,6 +133,5 @@ export {};
|
|
|
249
133
|
},
|
|
250
134
|
});
|
|
251
135
|
|
|
252
|
-
|
|
253
|
-
return integrations.length === 1 ? integrations[0] : integrations;
|
|
136
|
+
return integrations;
|
|
254
137
|
}
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,9 @@ export { isAuthenticatedUser } from './htlkg/config.js';
|
|
|
32
32
|
// Utils
|
|
33
33
|
export * from './utils';
|
|
34
34
|
|
|
35
|
+
// Page Factories
|
|
36
|
+
export * from './factories';
|
|
37
|
+
|
|
35
38
|
// Middleware is not exported from main index to avoid bundling astro:middleware
|
|
36
39
|
// It's loaded by Astro at runtime via the entrypoint specified in addMiddleware
|
|
37
40
|
// Import from '@htlkg/astro/middleware' if needed
|