@htlkg/astro 0.0.2 → 0.0.4

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/dist/index.js CHANGED
@@ -1,10 +1,30 @@
1
1
  import {
2
2
  htlkg
3
- } from "./chunk-IWK5QCVD.js";
3
+ } from "./chunk-6CW4SVDI.js";
4
4
  import {
5
5
  isAuthenticatedUser
6
- } from "./chunk-Z2ZAL7KX.js";
7
- import "./chunk-ZQ4XMJH7.js";
6
+ } from "./chunk-UBF5F2RG.js";
7
+ import {
8
+ applyClientFilters,
9
+ buildGraphQLFilter,
10
+ buildListQueryString,
11
+ buildListUrl,
12
+ getFilterCount,
13
+ hasActiveFilters,
14
+ mergeListParams,
15
+ paginateItems,
16
+ parseListParams,
17
+ processListData,
18
+ sortItems
19
+ } from "./chunk-2GML443T.js";
20
+ import {
21
+ chunkArray,
22
+ filterItems,
23
+ generateNestedPaths,
24
+ generatePaginatedPaths,
25
+ generateStaticPaths,
26
+ groupItems
27
+ } from "./chunk-33R4URZV.js";
8
28
  import {
9
29
  createHydrationScript,
10
30
  createHydrationScripts,
@@ -25,40 +45,132 @@ import {
25
45
  setCacheControl,
26
46
  setResponseHeaders
27
47
  } 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";
48
+
49
+ // src/factories/createListPage.ts
50
+ function generateBreadcrumbs(pageId, title) {
51
+ return [
52
+ { label: "Admin", href: "/admin" },
53
+ { label: title }
54
+ ];
55
+ }
56
+ async function createListPage(astro, config) {
57
+ const {
58
+ title,
59
+ description,
60
+ pageId,
61
+ breadcrumbs,
62
+ fetchFn,
63
+ transform,
64
+ store,
65
+ relatedStores,
66
+ searchableFields,
67
+ filterableFields = [],
68
+ defaultSort,
69
+ defaultPageSize = 25,
70
+ fetchLimit = 1e3
71
+ } = config;
72
+ const paramConfig = {
73
+ defaultPageSize,
74
+ defaultSort,
75
+ searchableFields,
76
+ filterableFields
77
+ };
78
+ const params = parseListParams(astro.url, paramConfig);
79
+ const graphqlFilter = buildGraphQLFilter(params.filters, filterableFields, {
80
+ search: params.search,
81
+ searchFields: searchableFields
82
+ });
83
+ let allItems = [];
84
+ let nextToken = void 0;
85
+ do {
86
+ const result = await fetchFn({
87
+ filter: graphqlFilter,
88
+ limit: fetchLimit,
89
+ nextToken: nextToken ?? void 0
90
+ });
91
+ const data = result.data ?? [];
92
+ allItems = allItems.concat(data);
93
+ nextToken = result.nextToken;
94
+ } while (nextToken);
95
+ if (transform) {
96
+ allItems = allItems.map(transform);
97
+ }
98
+ allItems = applyClientFilters(allItems, params.filters, filterableFields, {
99
+ search: params.search,
100
+ searchFields: searchableFields
101
+ });
102
+ allItems = sortItems(allItems, params.sortKey, params.sortOrder);
103
+ const { paginatedItems, totalItems, totalPages, currentPage, pageSize } = paginateItems(
104
+ allItems,
105
+ params.page,
106
+ params.pageSize
107
+ );
108
+ store.set(paginatedItems);
109
+ if (relatedStores) {
110
+ await Promise.all(
111
+ Object.entries(relatedStores).map(async ([, config2]) => {
112
+ const data = await config2.fetch();
113
+ config2.store.set(data);
114
+ })
115
+ );
116
+ }
117
+ return {
118
+ layoutProps: {
119
+ title,
120
+ description,
121
+ currentPage: pageId,
122
+ breadcrumbs: breadcrumbs ?? generateBreadcrumbs(pageId, title)
123
+ },
124
+ initialState: {
125
+ currentPage,
126
+ pageSize,
127
+ sortKey: params.sortKey,
128
+ sortOrder: params.sortOrder,
129
+ totalItems,
130
+ totalPages,
131
+ filters: params.filters,
132
+ search: params.search
133
+ },
134
+ items: paginatedItems,
135
+ allItems,
136
+ params
137
+ };
138
+ }
37
139
  export {
140
+ applyClientFilters,
141
+ buildGraphQLFilter,
142
+ buildListQueryString,
143
+ buildListUrl,
38
144
  chunkArray,
39
145
  createHydrationScript,
40
146
  createHydrationScripts,
41
147
  createIslandProps,
148
+ createListPage,
42
149
  deserializeFromHydration,
43
150
  filterItems,
44
151
  generateNestedPaths,
45
152
  generatePaginatedPaths,
46
153
  generateStaticPaths,
47
154
  getClientIP,
155
+ getFilterCount,
48
156
  getHydratedData,
49
157
  getQueryParams,
50
158
  getRequestHeaders,
51
159
  getServerData,
52
160
  groupItems,
161
+ hasActiveFilters,
53
162
  htlkg,
54
163
  isAuthenticatedUser,
55
164
  isMobileDevice,
56
165
  isServerSide,
166
+ mergeListParams,
57
167
  mergeProps,
168
+ paginateItems,
169
+ parseListParams,
170
+ processListData,
58
171
  serializeForHydration,
59
172
  setCacheControl,
60
173
  setResponseHeaders,
61
- shouldHydrate,
62
- sortItems
174
+ shouldHydrate
63
175
  };
64
176
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/factories/createListPage.ts"],"sourcesContent":["/**\n * List Page Factory\n *\n * Factory function for creating standardized list pages with automatic\n * data fetching, filtering, sorting, and pagination.\n */\n\nimport type { AstroGlobal } from \"astro\";\nimport type { WritableAtom } from \"nanostores\";\n\nimport {\n\tparseListParams,\n\ttype ListParams,\n\ttype ListParamConfig,\n\ttype FilterFieldConfig,\n} from \"../utils/params\";\nimport {\n\tbuildGraphQLFilter,\n\tapplyClientFilters,\n\tsortItems,\n\tpaginateItems,\n} from \"../utils/filters\";\n\n/**\n * Breadcrumb item\n */\nexport interface BreadcrumbItem {\n\tlabel: string;\n\thref?: string;\n}\n\n/**\n * Layout props returned by the factory\n */\nexport interface LayoutProps {\n\ttitle: string;\n\tdescription?: string;\n\tcurrentPage: string;\n\tbreadcrumbs: BreadcrumbItem[];\n}\n\n/**\n * Table initial state\n */\nexport interface TableInitialState {\n\tcurrentPage: number;\n\tpageSize: number;\n\tsortKey: string;\n\tsortOrder: \"asc\" | \"desc\";\n\ttotalItems: number;\n\ttotalPages: number;\n\tfilters: Record<string, any>;\n\tsearch?: string;\n}\n\n/**\n * Related store configuration\n */\nexport interface RelatedStoreConfig<T = any> {\n\tstore: WritableAtom<T[]>;\n\tfetch: () => Promise<T[]>;\n}\n\n/**\n * Configuration for createListPage\n */\nexport interface ListPageConfig<T> {\n\t// Page metadata\n\t/** Page title */\n\ttitle: string;\n\t/** Page description */\n\tdescription?: string;\n\t/** Page identifier (for sidebar active state) */\n\tpageId: string;\n\t/** Custom breadcrumbs (auto-generated if not provided) */\n\tbreadcrumbs?: BreadcrumbItem[];\n\n\t// Data fetching\n\t/** Fetch function that retrieves data */\n\tfetchFn: (params: {\n\t\tfilter: any;\n\t\tlimit?: number;\n\t\tnextToken?: string;\n\t}) => Promise<{ data: T[]; nextToken?: string | null }>;\n\n\t// Data handling\n\t/** Transform function to apply to fetched data */\n\ttransform?: (item: any) => T;\n\t/** Main store to set with data */\n\tstore: WritableAtom<T[]>;\n\t/** Related stores to populate */\n\trelatedStores?: Record<string, RelatedStoreConfig>;\n\n\t// Filtering/sorting config\n\t/** Searchable field keys */\n\tsearchableFields?: string[];\n\t/** Filterable field configurations */\n\tfilterableFields?: FilterFieldConfig[];\n\t/** Sortable field keys (all by default) */\n\tsortableFields?: string[];\n\t/** Default sort configuration */\n\tdefaultSort?: { key: string; order: \"asc\" | \"desc\" };\n\t/** Default page size */\n\tdefaultPageSize?: number;\n\t/** Maximum items to fetch from API */\n\tfetchLimit?: number;\n}\n\n/**\n * Result from createListPage\n */\nexport interface ListPageResult<T> {\n\t/** Props to spread to Layout component */\n\tlayoutProps: LayoutProps;\n\t/** Initial state for table component */\n\tinitialState: TableInitialState;\n\t/** Processed items (paginated) */\n\titems: T[];\n\t/** All items (pre-pagination, for client-side operations) */\n\tallItems: T[];\n\t/** Parsed URL parameters */\n\tparams: ListParams;\n}\n\n/**\n * Default breadcrumb generator\n */\nfunction generateBreadcrumbs(pageId: string, title: string): BreadcrumbItem[] {\n\treturn [\n\t\t{ label: \"Admin\", href: \"/admin\" },\n\t\t{ label: title },\n\t];\n}\n\n/**\n * Create a list page with automatic data handling\n *\n * This factory handles:\n * - URL parameter parsing (page, pageSize, sortKey, sortOrder, filters)\n * - Data fetching with GraphQL filter building\n * - Data transformation\n * - Client-side filtering (for computed fields)\n * - Sorting\n * - Pagination\n * - Store population\n *\n * @example\n * ```astro\n * ---\n * import { createListPage } from '@htlkg/astro/factories';\n * import { $accounts } from '@/stores/accounts';\n *\n * const { layoutProps, initialState, items } = await createListPage(Astro, {\n * title: 'Accounts',\n * pageId: 'accounts',\n * store: $accounts,\n *\n * fetchFn: async ({ filter }) => {\n * const client = getServerClient(Astro);\n * return await client.models.Account.list({ filter });\n * },\n *\n * transform: (account) => ({\n * id: account.id,\n * name: account.name,\n * brandCount: account.brands?.length ?? 0,\n * }),\n *\n * filterableFields: [\n * { key: 'name', type: 'text', graphql: true },\n * { key: 'status', type: 'select', graphql: true, options: ['active', 'inactive'] },\n * ],\n *\n * defaultSort: { key: 'name', order: 'asc' },\n * });\n * ---\n *\n * <Layout {...layoutProps}>\n * <AccountsTable client:load initialState={initialState} />\n * </Layout>\n * ```\n */\nexport async function createListPage<T extends Record<string, any>>(\n\tastro: AstroGlobal,\n\tconfig: ListPageConfig<T>\n): Promise<ListPageResult<T>> {\n\tconst {\n\t\ttitle,\n\t\tdescription,\n\t\tpageId,\n\t\tbreadcrumbs,\n\t\tfetchFn,\n\t\ttransform,\n\t\tstore,\n\t\trelatedStores,\n\t\tsearchableFields,\n\t\tfilterableFields = [],\n\t\tdefaultSort,\n\t\tdefaultPageSize = 25,\n\t\tfetchLimit = 1000,\n\t} = config;\n\n\t// 1. Parse URL parameters\n\tconst paramConfig: ListParamConfig = {\n\t\tdefaultPageSize,\n\t\tdefaultSort,\n\t\tsearchableFields,\n\t\tfilterableFields,\n\t};\n\tconst params = parseListParams(astro.url, paramConfig);\n\n\t// 2. Build GraphQL filter from URL params\n\tconst graphqlFilter = buildGraphQLFilter(params.filters, filterableFields, {\n\t\tsearch: params.search,\n\t\tsearchFields: searchableFields,\n\t});\n\n\t// 3. Fetch data with pagination\n\tlet allItems: T[] = [];\n\tlet nextToken: string | null | undefined = undefined;\n\n\tdo {\n\t\tconst result = await fetchFn({\n\t\t\tfilter: graphqlFilter,\n\t\t\tlimit: fetchLimit,\n\t\t\tnextToken: nextToken ?? undefined,\n\t\t});\n\n\t\tconst data = result.data ?? [];\n\t\tallItems = allItems.concat(data);\n\t\tnextToken = result.nextToken;\n\t} while (nextToken);\n\n\t// 4. Transform data if needed\n\tif (transform) {\n\t\tallItems = allItems.map(transform);\n\t}\n\n\t// 5. Apply client-side filtering (for computed fields not in GraphQL)\n\tallItems = applyClientFilters(allItems, params.filters, filterableFields, {\n\t\tsearch: params.search,\n\t\tsearchFields: searchableFields,\n\t});\n\n\t// 6. Sort\n\tallItems = sortItems(allItems, params.sortKey, params.sortOrder);\n\n\t// 7. Paginate\n\tconst { paginatedItems, totalItems, totalPages, currentPage, pageSize } = paginateItems(\n\t\tallItems,\n\t\tparams.page,\n\t\tparams.pageSize\n\t);\n\n\t// 8. Set main store\n\tstore.set(paginatedItems);\n\n\t// 9. Fetch and set related stores\n\tif (relatedStores) {\n\t\tawait Promise.all(\n\t\t\tObject.entries(relatedStores).map(async ([, config]) => {\n\t\t\t\tconst data = await config.fetch();\n\t\t\t\tconfig.store.set(data);\n\t\t\t})\n\t\t);\n\t}\n\n\t// 10. Build result\n\treturn {\n\t\tlayoutProps: {\n\t\t\ttitle,\n\t\t\tdescription,\n\t\t\tcurrentPage: pageId,\n\t\t\tbreadcrumbs: breadcrumbs ?? generateBreadcrumbs(pageId, title),\n\t\t},\n\t\tinitialState: {\n\t\t\tcurrentPage,\n\t\t\tpageSize,\n\t\t\tsortKey: params.sortKey,\n\t\t\tsortOrder: params.sortOrder,\n\t\t\ttotalItems,\n\t\t\ttotalPages,\n\t\t\tfilters: params.filters,\n\t\t\tsearch: params.search,\n\t\t},\n\t\titems: paginatedItems,\n\t\tallItems,\n\t\tparams,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+HA,SAAS,oBAAoB,QAAgB,OAAiC;AAC7E,SAAO;AAAA,IACN,EAAE,OAAO,SAAS,MAAM,SAAS;AAAA,IACjC,EAAE,OAAO,MAAM;AAAA,EAChB;AACD;AAkDA,eAAsB,eACrB,OACA,QAC6B;AAC7B,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,CAAC;AAAA,IACpB;AAAA,IACA,kBAAkB;AAAA,IAClB,aAAa;AAAA,EACd,IAAI;AAGJ,QAAM,cAA+B;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACA,QAAM,SAAS,gBAAgB,MAAM,KAAK,WAAW;AAGrD,QAAM,gBAAgB,mBAAmB,OAAO,SAAS,kBAAkB;AAAA,IAC1E,QAAQ,OAAO;AAAA,IACf,cAAc;AAAA,EACf,CAAC;AAGD,MAAI,WAAgB,CAAC;AACrB,MAAI,YAAuC;AAE3C,KAAG;AACF,UAAM,SAAS,MAAM,QAAQ;AAAA,MAC5B,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,WAAW,aAAa;AAAA,IACzB,CAAC;AAED,UAAM,OAAO,OAAO,QAAQ,CAAC;AAC7B,eAAW,SAAS,OAAO,IAAI;AAC/B,gBAAY,OAAO;AAAA,EACpB,SAAS;AAGT,MAAI,WAAW;AACd,eAAW,SAAS,IAAI,SAAS;AAAA,EAClC;AAGA,aAAW,mBAAmB,UAAU,OAAO,SAAS,kBAAkB;AAAA,IACzE,QAAQ,OAAO;AAAA,IACf,cAAc;AAAA,EACf,CAAC;AAGD,aAAW,UAAU,UAAU,OAAO,SAAS,OAAO,SAAS;AAG/D,QAAM,EAAE,gBAAgB,YAAY,YAAY,aAAa,SAAS,IAAI;AAAA,IACzE;AAAA,IACA,OAAO;AAAA,IACP,OAAO;AAAA,EACR;AAGA,QAAM,IAAI,cAAc;AAGxB,MAAI,eAAe;AAClB,UAAM,QAAQ;AAAA,MACb,OAAO,QAAQ,aAAa,EAAE,IAAI,OAAO,CAAC,EAAEA,OAAM,MAAM;AACvD,cAAM,OAAO,MAAMA,QAAO,MAAM;AAChC,QAAAA,QAAO,MAAM,IAAI,IAAI;AAAA,MACtB,CAAC;AAAA,IACF;AAAA,EACD;AAGA,SAAO;AAAA,IACN,aAAa;AAAA,MACZ;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,aAAa,eAAe,oBAAoB,QAAQ,KAAK;AAAA,IAC9D;AAAA,IACA,cAAc;AAAA,MACb;AAAA,MACA;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,WAAW,OAAO;AAAA,MAClB;AAAA,MACA;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,QAAQ,OAAO;AAAA,IAChB;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,EACD;AACD;","names":["config"]}
@@ -1,4 +1,23 @@
1
- import "../chunk-ZQ4XMJH7.js";
1
+ import {
2
+ applyClientFilters,
3
+ buildGraphQLFilter,
4
+ buildListQueryString,
5
+ buildListUrl,
6
+ getFilterCount,
7
+ hasActiveFilters,
8
+ mergeListParams,
9
+ paginateItems,
10
+ parseListParams,
11
+ processListData
12
+ } from "../chunk-2GML443T.js";
13
+ import {
14
+ chunkArray,
15
+ filterItems,
16
+ generateNestedPaths,
17
+ generatePaginatedPaths,
18
+ generateStaticPaths,
19
+ groupItems
20
+ } from "../chunk-33R4URZV.js";
2
21
  import {
3
22
  createHydrationScript,
4
23
  createHydrationScripts,
@@ -19,16 +38,11 @@ import {
19
38
  setCacheControl,
20
39
  setResponseHeaders
21
40
  } 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
41
  export {
42
+ applyClientFilters,
43
+ buildGraphQLFilter,
44
+ buildListQueryString,
45
+ buildListUrl,
32
46
  chunkArray,
33
47
  createHydrationScript,
34
48
  createHydrationScripts,
@@ -39,18 +53,23 @@ export {
39
53
  generatePaginatedPaths,
40
54
  generateStaticPaths,
41
55
  getClientIP,
56
+ getFilterCount,
42
57
  getHydratedData,
43
58
  getQueryParams,
44
59
  getRequestHeaders,
45
60
  getServerData,
46
61
  groupItems,
62
+ hasActiveFilters,
47
63
  isMobileDevice,
48
64
  isServerSide,
65
+ mergeListParams,
49
66
  mergeProps,
67
+ paginateItems,
68
+ parseListParams,
69
+ processListData,
50
70
  serializeForHydration,
51
71
  setCacheControl,
52
72
  setResponseHeaders,
53
- shouldHydrate,
54
- sortItems
73
+ shouldHydrate
55
74
  };
56
75
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,47 @@
1
+ // src/vue-app-setup.ts
2
+ import { amplifyConfig } from "virtual:htlkg-config";
3
+ import { Amplify } from "aws-amplify";
4
+ function setupVueApp(app) {
5
+ if (import.meta.env.DEV && typeof window !== "undefined") {
6
+ try {
7
+ import("@nanostores/vue/devtools").then(({ devtools }) => {
8
+ app.use(devtools, {});
9
+ }).catch(() => {
10
+ });
11
+ } catch {
12
+ }
13
+ }
14
+ try {
15
+ if (!amplifyConfig) {
16
+ console.warn("[htlkg] No Amplify configuration provided");
17
+ return;
18
+ }
19
+ if ("auth" in amplifyConfig || "data" in amplifyConfig || "storage" in amplifyConfig) {
20
+ Amplify.configure(amplifyConfig, { ssr: true });
21
+ } else {
22
+ const { userPoolId, userPoolClientId, region } = amplifyConfig;
23
+ if (userPoolId && userPoolClientId) {
24
+ const config = {
25
+ Auth: {
26
+ Cognito: {
27
+ userPoolId,
28
+ userPoolClientId,
29
+ ...region && { region }
30
+ }
31
+ }
32
+ };
33
+ Amplify.configure(config, { ssr: true });
34
+ } else {
35
+ console.error(
36
+ "[htlkg] Missing required Amplify configuration (userPoolId, userPoolClientId)"
37
+ );
38
+ }
39
+ }
40
+ } catch (error) {
41
+ console.error("[htlkg] Failed to setup Vue app:", error);
42
+ }
43
+ }
44
+ export {
45
+ setupVueApp as default
46
+ };
47
+ //# sourceMappingURL=vue-app-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vue-app-setup.ts"],"sourcesContent":["/**\n * Vue App Setup for htlkg Integration\n * \n * This file is automatically loaded by the Vue integration when vueAppSetup is enabled.\n * It configures AWS Amplify for client-side authentication in all Vue components.\n * It also sets up nanostores devtools integration for Vue DevTools.\n */\n\n/// <reference types=\"vite/client\" />\n\nimport { amplifyConfig } from \"virtual:htlkg-config\";\nimport type { App } from \"vue\";\nimport type { ResourcesConfig } from \"aws-amplify\";\nimport { Amplify } from \"aws-amplify\";\n\n/**\n * Setup function called by Astro's Vue integration\n * Configures Amplify for client-side authentication and nanostores devtools\n */\nexport default function setupVueApp(app: App): void {\n\t// Setup nanostores devtools in development\n\t// The devtools plugin will automatically detect stores used in components\n\tif (import.meta.env.DEV && typeof window !== 'undefined') {\n\t\ttry {\n\t\t\t// Dynamically import devtools plugin (only in browser)\n\t\t\timport('@nanostores/vue/devtools').then(({ devtools }) => {\n\t\t\t\t// Install devtools plugin - it will detect stores automatically\n\t\t\t\tapp.use(devtools, {});\n\t\t\t}).catch(() => {\n\t\t\t\t// Silently ignore - devtools are optional\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently ignore - devtools are optional\n\t\t}\n\t}\n\t\n\t// Setup Amplify\n\ttry {\n\t\tif (!amplifyConfig) {\n\t\t\tconsole.warn(\"[htlkg] No Amplify configuration provided\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if this is a full amplify_outputs.json config\n\t\tif (\n\t\t\t\"auth\" in amplifyConfig ||\n\t\t\t\"data\" in amplifyConfig ||\n\t\t\t\"storage\" in amplifyConfig\n\t\t) {\n\t\t\tAmplify.configure(amplifyConfig as ResourcesConfig, { ssr: true });\n\t\t} else {\n\t\t\t// Legacy individual config properties\n\t\t\tconst { userPoolId, userPoolClientId, region } = amplifyConfig as {\n\t\t\t\tuserPoolId?: string;\n\t\t\t\tuserPoolClientId?: string;\n\t\t\t\tregion?: string;\n\t\t\t};\n\n\t\t\tif (userPoolId && userPoolClientId) {\n\t\t\t\tconst config: ResourcesConfig = {\n\t\t\t\t\tAuth: {\n\t\t\t\t\t\tCognito: {\n\t\t\t\t\t\t\tuserPoolId,\n\t\t\t\t\t\t\tuserPoolClientId,\n\t\t\t\t\t\t\t...(region && { region }),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tAmplify.configure(config, { ssr: true });\n\t\t\t} else {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[htlkg] Missing required Amplify configuration (userPoolId, userPoolClientId)\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\"[htlkg] Failed to setup Vue app:\", error);\n\t\t// Don't throw - allow app to continue even if Amplify setup fails\n\t}\n}\n"],"mappings":";AAUA,SAAS,qBAAqB;AAG9B,SAAS,eAAe;AAMT,SAAR,YAA6B,KAAgB;AAGnD,MAAI,YAAY,IAAI,OAAO,OAAO,WAAW,aAAa;AACzD,QAAI;AAEH,aAAO,0BAA0B,EAAE,KAAK,CAAC,EAAE,SAAS,MAAM;AAEzD,YAAI,IAAI,UAAU,CAAC,CAAC;AAAA,MACrB,CAAC,EAAE,MAAM,MAAM;AAAA,MAEf,CAAC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACD;AAGA,MAAI;AACH,QAAI,CAAC,eAAe;AACnB,cAAQ,KAAK,2CAA2C;AACxD;AAAA,IACD;AAGA,QACC,UAAU,iBACV,UAAU,iBACV,aAAa,eACZ;AACD,cAAQ,UAAU,eAAkC,EAAE,KAAK,KAAK,CAAC;AAAA,IAClE,OAAO;AAEN,YAAM,EAAE,YAAY,kBAAkB,OAAO,IAAI;AAMjD,UAAI,cAAc,kBAAkB;AACnC,cAAM,SAA0B;AAAA,UAC/B,MAAM;AAAA,YACL,SAAS;AAAA,cACR;AAAA,cACA;AAAA,cACA,GAAI,UAAU,EAAE,OAAO;AAAA,YACxB;AAAA,UACD;AAAA,QACD;AAEA,gBAAQ,UAAU,QAAQ,EAAE,KAAK,KAAK,CAAC;AAAA,MACxC,OAAO;AACN,gBAAQ;AAAA,UACP;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD,SAAS,OAAO;AACf,YAAQ,MAAM,oCAAoC,KAAK;AAAA,EAExD;AACD;","names":[]}
package/package.json CHANGED
@@ -1,13 +1,28 @@
1
1
  {
2
2
  "name": "@htlkg/astro",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
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",
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "types": "./src/index.ts"
9
+ },
10
+ "./htlkg": {
11
+ "import": "./dist/htlkg/index.js",
12
+ "types": "./src/htlkg/index.ts"
13
+ },
14
+ "./htlkg/config": {
15
+ "import": "./dist/htlkg/config.js",
16
+ "types": "./src/htlkg/config.ts"
17
+ },
18
+ "./middleware": {
19
+ "import": "./dist/middleware/index.js",
20
+ "types": "./src/middleware/index.ts"
21
+ },
22
+ "./vue-app-setup": {
23
+ "import": "./dist/vue-app-setup.js",
24
+ "types": "./src/vue-app-setup.ts"
25
+ },
11
26
  "./auth/LoginPage.astro": "./src/auth/LoginPage.astro",
12
27
  "./layouts": "./src/layouts/index.ts",
13
28
  "./layouts/*.astro": "./src/layouts/*.astro",
@@ -18,8 +33,22 @@
18
33
  "./patterns/brand/*.astro": "./src/patterns/brand/*.astro",
19
34
  "./components": "./src/components/index.ts",
20
35
  "./components/*.astro": "./src/components/*.astro",
21
- "./utils": "./dist/utils/index.js",
22
- "./utils/*": "./dist/utils/*.js"
36
+ "./utils": {
37
+ "import": "./dist/utils/index.js",
38
+ "types": "./src/utils/index.ts"
39
+ },
40
+ "./utils/hydration": {
41
+ "import": "./dist/utils/hydration.js",
42
+ "types": "./src/utils/hydration.ts"
43
+ },
44
+ "./utils/ssr": {
45
+ "import": "./dist/utils/ssr.js",
46
+ "types": "./src/utils/ssr.ts"
47
+ },
48
+ "./utils/static": {
49
+ "import": "./dist/utils/static.js",
50
+ "types": "./src/utils/static.ts"
51
+ }
23
52
  },
24
53
  "files": [
25
54
  "dist",
@@ -36,8 +65,8 @@
36
65
  "nanostores": "^0.11.3",
37
66
  "tailwindcss": "^3.4.18",
38
67
  "vue": "^3.5.22",
39
- "@htlkg/core": "0.0.2",
40
- "@htlkg/components": "0.0.2"
68
+ "@htlkg/core": "0.0.3",
69
+ "@htlkg/components": "0.0.3"
41
70
  },
42
71
  "devDependencies": {
43
72
  "@vue/devtools-api": "^6.6.4",
@@ -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
+ ```