@htlkg/data 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.
Files changed (52) hide show
  1. package/README.md +53 -0
  2. package/dist/client/index.d.ts +16 -1
  3. package/dist/client/index.js +13 -1
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/hooks/index.d.ts +113 -5
  6. package/dist/hooks/index.js +155 -164
  7. package/dist/hooks/index.js.map +1 -1
  8. package/dist/index.d.ts +4 -2
  9. package/dist/index.js +263 -161
  10. package/dist/index.js.map +1 -1
  11. package/dist/queries/index.js.map +1 -1
  12. package/dist/stores/index.d.ts +106 -0
  13. package/dist/stores/index.js +108 -0
  14. package/dist/stores/index.js.map +1 -0
  15. package/package.json +31 -8
  16. package/src/client/__tests__/server.test.ts +100 -0
  17. package/src/client/client.md +91 -0
  18. package/src/client/index.test.ts +232 -0
  19. package/src/client/index.ts +145 -0
  20. package/src/client/server.ts +118 -0
  21. package/src/content-collections/content-collections.md +87 -0
  22. package/src/content-collections/generator.ts +314 -0
  23. package/src/content-collections/index.ts +32 -0
  24. package/src/content-collections/schemas.ts +75 -0
  25. package/src/content-collections/sync.ts +139 -0
  26. package/src/hooks/README.md +293 -0
  27. package/src/hooks/createDataHook.ts +208 -0
  28. package/src/hooks/data-hook-errors.property.test.ts +270 -0
  29. package/src/hooks/data-hook-filters.property.test.ts +263 -0
  30. package/src/hooks/data-hooks.property.test.ts +190 -0
  31. package/src/hooks/hooks.test.ts +76 -0
  32. package/src/hooks/index.ts +21 -0
  33. package/src/hooks/useAccounts.ts +66 -0
  34. package/src/hooks/useBrands.ts +95 -0
  35. package/src/hooks/useProducts.ts +88 -0
  36. package/src/hooks/useUsers.ts +89 -0
  37. package/src/index.ts +32 -0
  38. package/src/mutations/accounts.ts +127 -0
  39. package/src/mutations/brands.ts +133 -0
  40. package/src/mutations/index.ts +32 -0
  41. package/src/mutations/mutations.md +96 -0
  42. package/src/mutations/users.ts +136 -0
  43. package/src/queries/accounts.ts +121 -0
  44. package/src/queries/brands.ts +176 -0
  45. package/src/queries/index.ts +45 -0
  46. package/src/queries/products.ts +282 -0
  47. package/src/queries/queries.md +88 -0
  48. package/src/queries/server-helpers.ts +114 -0
  49. package/src/queries/users.ts +199 -0
  50. package/src/stores/createStores.ts +148 -0
  51. package/src/stores/index.ts +15 -0
  52. package/src/stores/stores.md +104 -0
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Content Collections Generator
3
+ *
4
+ * Generates Astro Content Collections from AppSync data.
5
+ * Fetches brands, accounts, and product instances and writes them as JSON files.
6
+ */
7
+
8
+ import { writeFileSync, mkdirSync, existsSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import {
11
+ fetchBrands,
12
+ fetchAccounts,
13
+ fetchProductInstances,
14
+ fetchProducts,
15
+ type SyncOptions,
16
+ } from "./sync";
17
+ import {
18
+ brandSchema,
19
+ accountSchema,
20
+ productInstanceSchema,
21
+ productSchema,
22
+ type BrandData,
23
+ type AccountData,
24
+ type ProductInstanceData,
25
+ type ProductData,
26
+ } from "./schemas";
27
+
28
+ const log = {
29
+ info: (msg: string) => console.info(`[content-collections] ${msg}`),
30
+ error: (msg: string) => console.error(`[content-collections] ${msg}`),
31
+ };
32
+
33
+ export interface GeneratorOptions extends SyncOptions {
34
+ /** Output directory for content collections (default: 'src/content') */
35
+ outputDir?: string;
36
+ /** Whether to validate data against schemas (default: true) */
37
+ validate?: boolean;
38
+ /** Whether to continue on errors (default: true) */
39
+ continueOnError?: boolean;
40
+ }
41
+
42
+ export interface GeneratorResult {
43
+ brands: {
44
+ count: number;
45
+ errors: string[];
46
+ };
47
+ accounts: {
48
+ count: number;
49
+ errors: string[];
50
+ };
51
+ productInstances: {
52
+ count: number;
53
+ errors: string[];
54
+ };
55
+ products: {
56
+ count: number;
57
+ errors: string[];
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Generate Content Collections from AppSync
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * import { generateContentCollections } from '@htlkg/data/content-collections';
67
+ *
68
+ * const result = await generateContentCollections({
69
+ * apiConfig: {
70
+ * endpoint: process.env.APPSYNC_ENDPOINT,
71
+ * region: 'us-east-1',
72
+ * apiKey: process.env.APPSYNC_API_KEY
73
+ * },
74
+ * outputDir: 'src/content'
75
+ * });
76
+ *
77
+ * console.log(`Generated ${result.brands.count} brands`);
78
+ * ```
79
+ */
80
+ export async function generateContentCollections(
81
+ options: GeneratorOptions,
82
+ ): Promise<GeneratorResult> {
83
+ const {
84
+ outputDir = "src/content",
85
+ validate = true,
86
+ continueOnError = true,
87
+ } = options;
88
+
89
+ const result: GeneratorResult = {
90
+ brands: { count: 0, errors: [] },
91
+ accounts: { count: 0, errors: [] },
92
+ productInstances: { count: 0, errors: [] },
93
+ products: { count: 0, errors: [] },
94
+ };
95
+
96
+ // Ensure output directories exist
97
+ const brandsDir = join(outputDir, "brands");
98
+ const accountsDir = join(outputDir, "accounts");
99
+ const productInstancesDir = join(outputDir, "product-instances");
100
+ const productsDir = join(outputDir, "products");
101
+
102
+ for (const dir of [brandsDir, accountsDir, productInstancesDir, productsDir]) {
103
+ if (!existsSync(dir)) {
104
+ mkdirSync(dir, { recursive: true });
105
+ }
106
+ }
107
+
108
+ // Fetch and generate brands
109
+ try {
110
+ log.info("Fetching brands...");
111
+ const brandsResult = await fetchBrands(options);
112
+
113
+ if (brandsResult.errors && !continueOnError) {
114
+ throw new Error(
115
+ `Failed to fetch brands: ${brandsResult.errors[0]?.message}`,
116
+ );
117
+ }
118
+
119
+ if (brandsResult.errors) {
120
+ result.brands.errors.push(
121
+ ...brandsResult.errors.map((e) => e.message),
122
+ );
123
+ }
124
+
125
+ for (const brand of brandsResult.data) {
126
+ try {
127
+ // Validate if enabled
128
+ if (validate) {
129
+ brandSchema.parse(brand);
130
+ }
131
+
132
+ // Write JSON file
133
+ const filename = join(brandsDir, `${brand.id}.json`);
134
+ writeFileSync(filename, JSON.stringify(brand, null, 2));
135
+ result.brands.count++;
136
+ } catch (error) {
137
+ const errorMsg = `Failed to process brand ${brand.id}: ${(error as Error).message}`;
138
+ log.error(errorMsg);
139
+ result.brands.errors.push(errorMsg);
140
+
141
+ if (!continueOnError) {
142
+ throw error;
143
+ }
144
+ }
145
+ }
146
+
147
+ log.info(`Generated ${result.brands.count} brands`);
148
+ } catch (error) {
149
+ const errorMsg = `Failed to generate brands: ${(error as Error).message}`;
150
+ log.error(errorMsg);
151
+ result.brands.errors.push(errorMsg);
152
+
153
+ if (!continueOnError) {
154
+ throw error;
155
+ }
156
+ }
157
+
158
+ // Fetch and generate accounts
159
+ try {
160
+ log.info("Fetching accounts...");
161
+ const accountsResult = await fetchAccounts(options);
162
+
163
+ if (accountsResult.errors && !continueOnError) {
164
+ throw new Error(
165
+ `Failed to fetch accounts: ${accountsResult.errors[0]?.message}`,
166
+ );
167
+ }
168
+
169
+ if (accountsResult.errors) {
170
+ result.accounts.errors.push(
171
+ ...accountsResult.errors.map((e) => e.message),
172
+ );
173
+ }
174
+
175
+ for (const account of accountsResult.data) {
176
+ try {
177
+ // Validate if enabled
178
+ if (validate) {
179
+ accountSchema.parse(account);
180
+ }
181
+
182
+ // Write JSON file
183
+ const filename = join(accountsDir, `${account.id}.json`);
184
+ writeFileSync(filename, JSON.stringify(account, null, 2));
185
+ result.accounts.count++;
186
+ } catch (error) {
187
+ const errorMsg = `Failed to process account ${account.id}: ${(error as Error).message}`;
188
+ log.error(errorMsg);
189
+ result.accounts.errors.push(errorMsg);
190
+
191
+ if (!continueOnError) {
192
+ throw error;
193
+ }
194
+ }
195
+ }
196
+
197
+ log.info(`Generated ${result.accounts.count} accounts`);
198
+ } catch (error) {
199
+ const errorMsg = `Failed to generate accounts: ${(error as Error).message}`;
200
+ log.error(errorMsg);
201
+ result.accounts.errors.push(errorMsg);
202
+
203
+ if (!continueOnError) {
204
+ throw error;
205
+ }
206
+ }
207
+
208
+ // Fetch and generate product instances
209
+ try {
210
+ log.info("Fetching product instances...");
211
+ const productInstancesResult = await fetchProductInstances(options);
212
+
213
+ if (productInstancesResult.errors && !continueOnError) {
214
+ throw new Error(
215
+ `Failed to fetch product instances: ${productInstancesResult.errors[0]?.message}`,
216
+ );
217
+ }
218
+
219
+ if (productInstancesResult.errors) {
220
+ result.productInstances.errors.push(
221
+ ...productInstancesResult.errors.map((e) => e.message),
222
+ );
223
+ }
224
+
225
+ for (const instance of productInstancesResult.data) {
226
+ try {
227
+ // Validate if enabled
228
+ if (validate) {
229
+ productInstanceSchema.parse(instance);
230
+ }
231
+
232
+ // Write JSON file
233
+ const filename = join(productInstancesDir, `${instance.id}.json`);
234
+ writeFileSync(filename, JSON.stringify(instance, null, 2));
235
+ result.productInstances.count++;
236
+ } catch (error) {
237
+ const errorMsg = `Failed to process product instance ${instance.id}: ${(error as Error).message}`;
238
+ log.error(errorMsg);
239
+ result.productInstances.errors.push(errorMsg);
240
+
241
+ if (!continueOnError) {
242
+ throw error;
243
+ }
244
+ }
245
+ }
246
+
247
+ log.info(`Generated ${result.productInstances.count} product instances`);
248
+ } catch (error) {
249
+ const errorMsg = `Failed to generate product instances: ${(error as Error).message}`;
250
+ log.error(errorMsg);
251
+ result.productInstances.errors.push(errorMsg);
252
+
253
+ if (!continueOnError) {
254
+ throw error;
255
+ }
256
+ }
257
+
258
+ // Fetch and generate products
259
+ try {
260
+ log.info("Fetching products...");
261
+ const productsResult = await fetchProducts(options);
262
+
263
+ if (productsResult.errors && !continueOnError) {
264
+ throw new Error(
265
+ `Failed to fetch products: ${productsResult.errors[0]?.message}`,
266
+ );
267
+ }
268
+
269
+ if (productsResult.errors) {
270
+ result.products.errors.push(
271
+ ...productsResult.errors.map((e) => e.message),
272
+ );
273
+ }
274
+
275
+ for (const product of productsResult.data) {
276
+ try {
277
+ // Validate if enabled
278
+ if (validate) {
279
+ productSchema.parse(product);
280
+ }
281
+
282
+ // Write JSON file
283
+ const filename = join(productsDir, `${product.id}.json`);
284
+ writeFileSync(filename, JSON.stringify(product, null, 2));
285
+ result.products.count++;
286
+ } catch (error) {
287
+ const errorMsg = `Failed to process product ${product.id}: ${(error as Error).message}`;
288
+ log.error(errorMsg);
289
+ result.products.errors.push(errorMsg);
290
+
291
+ if (!continueOnError) {
292
+ throw error;
293
+ }
294
+ }
295
+ }
296
+
297
+ log.info(`Generated ${result.products.count} products`);
298
+ } catch (error) {
299
+ const errorMsg = `Failed to generate products: ${(error as Error).message}`;
300
+ log.error(errorMsg);
301
+ result.products.errors.push(errorMsg);
302
+
303
+ if (!continueOnError) {
304
+ throw error;
305
+ }
306
+ }
307
+
308
+ return result;
309
+ }
310
+
311
+ /**
312
+ * Sync data from AppSync (alias for generateContentCollections)
313
+ */
314
+ export const syncFromAppSync = generateContentCollections;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Content Collections for @htlkg/data
3
+ *
4
+ * Provides functionality to generate Astro Content Collections from AppSync data.
5
+ */
6
+
7
+ export {
8
+ generateContentCollections,
9
+ syncFromAppSync,
10
+ type GeneratorOptions,
11
+ type GeneratorResult,
12
+ } from "./generator";
13
+
14
+ export {
15
+ fetchBrands,
16
+ fetchAccounts,
17
+ fetchProductInstances,
18
+ fetchProducts,
19
+ type SyncOptions,
20
+ type SyncResult,
21
+ } from "./sync";
22
+
23
+ export {
24
+ brandSchema,
25
+ accountSchema,
26
+ productInstanceSchema,
27
+ productSchema,
28
+ type BrandData,
29
+ type AccountData,
30
+ type ProductInstanceData,
31
+ type ProductData,
32
+ } from "./schemas";
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Zod Schemas for Content Collections
3
+ *
4
+ * Defines validation schemas for brands, accounts, and product instances
5
+ * that will be synced from AppSync to Astro Content Collections.
6
+ */
7
+
8
+ import { z } from "zod";
9
+
10
+ /**
11
+ * Brand schema for content collections
12
+ */
13
+ export const brandSchema = z.object({
14
+ id: z.string(),
15
+ name: z.string(),
16
+ accountId: z.string(),
17
+ logo: z.string().optional(),
18
+ timezone: z.string(),
19
+ status: z.enum(["active", "inactive", "maintenance", "suspended"]),
20
+ settings: z.record(z.any()).optional().default({}),
21
+ createdAt: z.string().optional(),
22
+ updatedAt: z.string().optional(),
23
+ });
24
+
25
+ export type BrandData = z.infer<typeof brandSchema>;
26
+
27
+ /**
28
+ * Account schema for content collections
29
+ */
30
+ export const accountSchema = z.object({
31
+ id: z.string(),
32
+ name: z.string(),
33
+ logo: z.string().optional(),
34
+ subscription: z.record(z.any()).optional().default({}),
35
+ settings: z.record(z.any()).optional().default({}),
36
+ createdAt: z.string().optional(),
37
+ updatedAt: z.string().optional(),
38
+ });
39
+
40
+ export type AccountData = z.infer<typeof accountSchema>;
41
+
42
+ /**
43
+ * Product Instance schema for content collections
44
+ */
45
+ export const productInstanceSchema = z.object({
46
+ id: z.string(),
47
+ productId: z.string(),
48
+ productName: z.string(),
49
+ brandId: z.string(),
50
+ accountId: z.string(),
51
+ enabled: z.boolean(),
52
+ config: z.record(z.any()).optional().default({}),
53
+ version: z.string(),
54
+ createdAt: z.string().optional(),
55
+ updatedAt: z.string().optional(),
56
+ });
57
+
58
+ export type ProductInstanceData = z.infer<typeof productInstanceSchema>;
59
+
60
+ /**
61
+ * Product schema for content collections
62
+ */
63
+ export const productSchema = z.object({
64
+ id: z.string(),
65
+ name: z.string(),
66
+ version: z.string(),
67
+ schema: z.record(z.any()).optional().default({}),
68
+ uiSchema: z.record(z.any()).optional().default({}),
69
+ defaultConfig: z.record(z.any()).optional().default({}),
70
+ isActive: z.boolean(),
71
+ createdAt: z.string().optional(),
72
+ updatedAt: z.string().optional(),
73
+ });
74
+
75
+ export type ProductData = z.infer<typeof productSchema>;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * AppSync Sync for Content Collections
3
+ *
4
+ * Fetches data from Admin's AppSync API for use in Content Collections.
5
+ */
6
+
7
+ import { generateClient } from "@aws-amplify/api";
8
+
9
+ export interface SyncOptions {
10
+ /** AppSync API configuration */
11
+ apiConfig: {
12
+ endpoint: string;
13
+ region: string;
14
+ apiKey?: string;
15
+ };
16
+ /** Authorization mode (default: 'apiKey') */
17
+ authMode?: "apiKey" | "userPool" | "iam";
18
+ /** Limit for list queries */
19
+ limit?: number;
20
+ }
21
+
22
+ export interface SyncResult<T> {
23
+ data: T[];
24
+ errors: Array<{ message: string }> | null;
25
+ }
26
+
27
+ /**
28
+ * Fetch brands from AppSync
29
+ */
30
+ export async function fetchBrands(
31
+ options: SyncOptions,
32
+ ): Promise<SyncResult<any>> {
33
+ try {
34
+ const client = generateClient({
35
+ authMode: options.authMode || "apiKey",
36
+ });
37
+
38
+ const { data, errors } = await (client as any).models.Brand.list({
39
+ limit: options.limit || 1000,
40
+ });
41
+
42
+ return {
43
+ data: data || [],
44
+ errors: errors || null,
45
+ };
46
+ } catch (error) {
47
+ console.error("[fetchBrands] Error:", error);
48
+ return {
49
+ data: [],
50
+ errors: [{ message: (error as Error).message }],
51
+ };
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Fetch accounts from AppSync
57
+ */
58
+ export async function fetchAccounts(
59
+ options: SyncOptions,
60
+ ): Promise<SyncResult<any>> {
61
+ try {
62
+ const client = generateClient({
63
+ authMode: options.authMode || "apiKey",
64
+ });
65
+
66
+ const { data, errors } = await (client as any).models.Account.list({
67
+ limit: options.limit || 1000,
68
+ });
69
+
70
+ return {
71
+ data: data || [],
72
+ errors: errors || null,
73
+ };
74
+ } catch (error) {
75
+ console.error("[fetchAccounts] Error:", error);
76
+ return {
77
+ data: [],
78
+ errors: [{ message: (error as Error).message }],
79
+ };
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Fetch product instances from AppSync
85
+ */
86
+ export async function fetchProductInstances(
87
+ options: SyncOptions,
88
+ ): Promise<SyncResult<any>> {
89
+ try {
90
+ const client = generateClient({
91
+ authMode: options.authMode || "apiKey",
92
+ });
93
+
94
+ const { data, errors } = await (client as any).models.ProductInstance.list(
95
+ {
96
+ limit: options.limit || 1000,
97
+ },
98
+ );
99
+
100
+ return {
101
+ data: data || [],
102
+ errors: errors || null,
103
+ };
104
+ } catch (error) {
105
+ console.error("[fetchProductInstances] Error:", error);
106
+ return {
107
+ data: [],
108
+ errors: [{ message: (error as Error).message }],
109
+ };
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Fetch products from AppSync
115
+ */
116
+ export async function fetchProducts(
117
+ options: SyncOptions,
118
+ ): Promise<SyncResult<any>> {
119
+ try {
120
+ const client = generateClient({
121
+ authMode: options.authMode || "apiKey",
122
+ });
123
+
124
+ const { data, errors } = await (client as any).models.Product.list({
125
+ limit: options.limit || 1000,
126
+ });
127
+
128
+ return {
129
+ data: data || [],
130
+ errors: errors || null,
131
+ };
132
+ } catch (error) {
133
+ console.error("[fetchProducts] Error:", error);
134
+ return {
135
+ data: [],
136
+ errors: [{ message: (error as Error).message }],
137
+ };
138
+ }
139
+ }