@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
package/dist/index.js
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
2
|
htlkg
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-XOY5BM3N.js";
|
|
4
4
|
import {
|
|
5
5
|
isAuthenticatedUser
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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"]}
|
package/dist/utils/index.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
import
|
|
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.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
|
-
".":
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"./
|
|
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":
|
|
22
|
-
|
|
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.
|
|
40
|
-
"@htlkg/components": "0.0.
|
|
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",
|
package/src/auth/auth.md
ADDED
|
@@ -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
|
+
```
|