@htlkg/astro 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +24 -8
  2. package/dist/chunk-2GML443T.js +273 -0
  3. package/dist/chunk-2GML443T.js.map +1 -0
  4. package/dist/{chunk-Z2ZAL7KX.js → chunk-UBF5F2RG.js} +1 -1
  5. package/dist/{chunk-Z2ZAL7KX.js.map → chunk-UBF5F2RG.js.map} +1 -1
  6. package/dist/chunk-XOY5BM3N.js +151 -0
  7. package/dist/chunk-XOY5BM3N.js.map +1 -0
  8. package/dist/htlkg/config.js +1 -1
  9. package/dist/htlkg/index.js +1 -1
  10. package/dist/index.js +126 -14
  11. package/dist/index.js.map +1 -1
  12. package/dist/middleware/index.js +27 -28
  13. package/dist/middleware/index.js.map +1 -1
  14. package/dist/utils/index.js +31 -12
  15. package/dist/vue-app-setup.js +47 -0
  16. package/dist/vue-app-setup.js.map +1 -0
  17. package/package.json +60 -26
  18. package/src/auth/auth.md +77 -0
  19. package/src/components/Island.astro +56 -0
  20. package/src/components/components.md +79 -0
  21. package/src/factories/createListPage.ts +290 -0
  22. package/src/factories/index.ts +16 -0
  23. package/src/htlkg/config.ts +10 -0
  24. package/src/htlkg/htlkg.md +63 -0
  25. package/src/htlkg/index.ts +49 -157
  26. package/src/index.ts +3 -0
  27. package/src/layouts/AdminLayout.astro +103 -92
  28. package/src/layouts/layouts.md +87 -0
  29. package/src/middleware/auth.ts +42 -0
  30. package/src/middleware/middleware.md +82 -0
  31. package/src/middleware/route-guards.ts +4 -28
  32. package/src/patterns/patterns.md +104 -0
  33. package/src/utils/filters.ts +320 -0
  34. package/src/utils/index.ts +8 -2
  35. package/src/utils/params.ts +260 -0
  36. package/src/utils/utils.md +86 -0
  37. package/src/vue-app-setup.ts +21 -28
  38. package/dist/chunk-WLOFOVCL.js +0 -210
  39. package/dist/chunk-WLOFOVCL.js.map +0 -1
  40. package/dist/chunk-ZQ4XMJH7.js +0 -1
  41. package/dist/chunk-ZQ4XMJH7.js.map +0 -1
@@ -0,0 +1,77 @@
1
+ # Auth Module
2
+
3
+ Authentication pages and components.
4
+
5
+ ## Components
6
+
7
+ ### LoginPage
8
+
9
+ Full login page with form and error handling.
10
+
11
+ ```astro
12
+ ---
13
+ import { LoginPage } from '@htlkg/astro/auth';
14
+ ---
15
+
16
+ <LoginPage
17
+ redirectTo="/admin"
18
+ logoUrl="/logo.svg"
19
+ />
20
+ ```
21
+
22
+ ### LoginForm
23
+
24
+ Vue component for login form (used within LoginPage).
25
+
26
+ ```vue
27
+ <script setup>
28
+ import { LoginForm } from '@htlkg/astro/auth';
29
+ </script>
30
+
31
+ <template>
32
+ <LoginForm
33
+ @success="handleSuccess"
34
+ @error="handleError"
35
+ />
36
+ </template>
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ The auth module is typically used with the htlkg integration which automatically injects the login page:
42
+
43
+ ```javascript
44
+ // astro.config.mjs
45
+ import { htlkg } from '@htlkg/astro';
46
+
47
+ export default defineConfig({
48
+ integrations: [
49
+ htlkg({
50
+ auth: {
51
+ enabled: true,
52
+ loginPage: '/login',
53
+ },
54
+ }),
55
+ ],
56
+ });
57
+ ```
58
+
59
+ ## Customization
60
+
61
+ Create a custom login page:
62
+
63
+ ```astro
64
+ ---
65
+ // src/pages/login.astro
66
+ import { AuthLayout } from '@htlkg/astro/layouts';
67
+ import { LoginForm } from '@htlkg/astro/auth';
68
+ ---
69
+
70
+ <AuthLayout title="Sign In">
71
+ <div class="custom-login">
72
+ <img src="/logo.svg" alt="Logo" />
73
+ <h1>Welcome Back</h1>
74
+ <LoginForm client:load />
75
+ </div>
76
+ </AuthLayout>
77
+ ```
@@ -0,0 +1,56 @@
1
+ ---
2
+ /**
3
+ * Island Component
4
+ *
5
+ * A wrapper component for Vue islands that handles:
6
+ * - Automatic nanostore synchronization from server to client
7
+ * - Hydration strategy selection
8
+ * - Props forwarding
9
+ *
10
+ * @example
11
+ * ```astro
12
+ * <Island
13
+ * component={AccountsPage}
14
+ * data={{ accounts }}
15
+ * stores={{ accounts: $accounts }}
16
+ * hydration="load"
17
+ * />
18
+ * ```
19
+ */
20
+
21
+ import type { Component } from "vue";
22
+ import type { WritableAtom } from "nanostores";
23
+
24
+ interface Props {
25
+ /** Vue component to render */
26
+ component: Component;
27
+ /** Data to sync to stores */
28
+ data?: Record<string, any>;
29
+ /** Store mapping { dataKey: storeAtom } */
30
+ stores?: Record<string, WritableAtom<any>>;
31
+ /** Hydration strategy */
32
+ hydration?: "load" | "idle" | "visible" | "only";
33
+ /** Additional props to pass to the component */
34
+ props?: Record<string, any>;
35
+ }
36
+
37
+ const {
38
+ component: Component,
39
+ data = {},
40
+ stores = {},
41
+ hydration = "load",
42
+ props = {},
43
+ } = Astro.props;
44
+
45
+ // Auto-sync data to stores on server-side
46
+ for (const [key, store] of Object.entries(stores)) {
47
+ if (data[key] !== undefined) {
48
+ store.set(data[key]);
49
+ }
50
+ }
51
+ ---
52
+
53
+ {hydration === "load" && <Component client:load {...props} />}
54
+ {hydration === "idle" && <Component client:idle {...props} />}
55
+ {hydration === "visible" && <Component client:visible {...props} />}
56
+ {hydration === "only" && <Component client:only="vue" {...props} />}
@@ -0,0 +1,79 @@
1
+ # Components Module
2
+
3
+ Astro components for page structure and navigation.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import { Sidebar } from '@htlkg/astro/components';
9
+ import { Topbar } from '@htlkg/astro/components';
10
+ import { PageHeader } from '@htlkg/astro/components';
11
+ import { Island } from '@htlkg/astro/components';
12
+ ```
13
+
14
+ ## Sidebar
15
+
16
+ Navigation sidebar for admin layouts.
17
+
18
+ ```astro
19
+ ---
20
+ import { Sidebar } from '@htlkg/astro/components';
21
+
22
+ const navItems = [
23
+ { label: 'Dashboard', href: '/admin', icon: 'home' },
24
+ { label: 'Brands', href: '/admin/brands', icon: 'building' },
25
+ { label: 'Users', href: '/admin/users', icon: 'users' },
26
+ ];
27
+ ---
28
+
29
+ <Sidebar items={navItems} currentPath={Astro.url.pathname} />
30
+ ```
31
+
32
+ ## Topbar
33
+
34
+ Top navigation bar with user menu.
35
+
36
+ ```astro
37
+ ---
38
+ import { Topbar } from '@htlkg/astro/components';
39
+
40
+ const user = Astro.locals.user;
41
+ ---
42
+
43
+ <Topbar user={user} title="Admin Portal" />
44
+ ```
45
+
46
+ ## PageHeader
47
+
48
+ Page header with title, breadcrumbs, and actions.
49
+
50
+ ```astro
51
+ ---
52
+ import { PageHeader } from '@htlkg/astro/components';
53
+
54
+ const breadcrumbs = [
55
+ { label: 'Admin', href: '/admin' },
56
+ { label: 'Brands', href: '/admin/brands' },
57
+ ];
58
+ ---
59
+
60
+ <PageHeader
61
+ title="Brands"
62
+ breadcrumbs={breadcrumbs}
63
+ >
64
+ <button slot="actions">Add Brand</button>
65
+ </PageHeader>
66
+ ```
67
+
68
+ ## Island
69
+
70
+ Wrapper for client-side Vue components with hydration control.
71
+
72
+ ```astro
73
+ ---
74
+ import { Island } from '@htlkg/astro/components';
75
+ import BrandList from '@/components/BrandList.vue';
76
+ ---
77
+
78
+ <Island component={BrandList} client:load props={{ brands }} />
79
+ ```
@@ -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";
@@ -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
+ ```