@htlkg/data 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 (56) hide show
  1. package/README.md +53 -0
  2. package/dist/client/index.d.ts +117 -31
  3. package/dist/client/index.js +74 -22
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/content-collections/index.js +20 -24
  6. package/dist/content-collections/index.js.map +1 -1
  7. package/dist/hooks/index.d.ts +113 -5
  8. package/dist/hooks/index.js +165 -182
  9. package/dist/hooks/index.js.map +1 -1
  10. package/dist/index.d.ts +8 -4
  11. package/dist/index.js +305 -182
  12. package/dist/index.js.map +1 -1
  13. package/dist/queries/index.d.ts +78 -1
  14. package/dist/queries/index.js +47 -0
  15. package/dist/queries/index.js.map +1 -1
  16. package/dist/stores/index.d.ts +106 -0
  17. package/dist/stores/index.js +108 -0
  18. package/dist/stores/index.js.map +1 -0
  19. package/package.json +60 -37
  20. package/src/client/__tests__/server.test.ts +100 -0
  21. package/src/client/client.md +91 -0
  22. package/src/client/index.test.ts +232 -0
  23. package/src/client/index.ts +145 -0
  24. package/src/client/server.ts +118 -0
  25. package/src/content-collections/content-collections.md +87 -0
  26. package/src/content-collections/generator.ts +314 -0
  27. package/src/content-collections/index.ts +32 -0
  28. package/src/content-collections/schemas.ts +75 -0
  29. package/src/content-collections/sync.ts +139 -0
  30. package/src/hooks/README.md +293 -0
  31. package/src/hooks/createDataHook.ts +208 -0
  32. package/src/hooks/data-hook-errors.property.test.ts +270 -0
  33. package/src/hooks/data-hook-filters.property.test.ts +263 -0
  34. package/src/hooks/data-hooks.property.test.ts +190 -0
  35. package/src/hooks/hooks.test.ts +76 -0
  36. package/src/hooks/index.ts +21 -0
  37. package/src/hooks/useAccounts.ts +66 -0
  38. package/src/hooks/useBrands.ts +95 -0
  39. package/src/hooks/useProducts.ts +88 -0
  40. package/src/hooks/useUsers.ts +89 -0
  41. package/src/index.ts +32 -0
  42. package/src/mutations/accounts.ts +127 -0
  43. package/src/mutations/brands.ts +133 -0
  44. package/src/mutations/index.ts +32 -0
  45. package/src/mutations/mutations.md +96 -0
  46. package/src/mutations/users.ts +136 -0
  47. package/src/queries/accounts.ts +121 -0
  48. package/src/queries/brands.ts +176 -0
  49. package/src/queries/index.ts +45 -0
  50. package/src/queries/products.ts +282 -0
  51. package/src/queries/queries.md +88 -0
  52. package/src/queries/server-helpers.ts +114 -0
  53. package/src/queries/users.ts +199 -0
  54. package/src/stores/createStores.ts +148 -0
  55. package/src/stores/index.ts +15 -0
  56. package/src/stores/stores.md +104 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * User Query Functions
3
+ *
4
+ * Provides query functions for fetching user data from the GraphQL API.
5
+ */
6
+
7
+ import type { User } from "@htlkg/core/types";
8
+
9
+ /**
10
+ * Get a single user by ID
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { getUser } from '@htlkg/data/queries';
15
+ * import { generateClient } from '@htlkg/data/client';
16
+ *
17
+ * const client = generateClient<Schema>();
18
+ * const user = await getUser(client, 'user-123');
19
+ * ```
20
+ */
21
+ export async function getUser<TClient = any>(
22
+ client: TClient,
23
+ id: string,
24
+ ): Promise<User | null> {
25
+ try {
26
+ const { data, errors } = await (client as any).models.User.get({ id });
27
+
28
+ if (errors) {
29
+ console.error("[getUser] GraphQL errors:", errors);
30
+ return null;
31
+ }
32
+
33
+ return data as User;
34
+ } catch (error) {
35
+ console.error("[getUser] Error fetching user:", error);
36
+ throw error;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Get a user by Cognito ID
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { getUserByCognitoId } from '@htlkg/data/queries';
46
+ * import { generateClient } from '@htlkg/data/client';
47
+ *
48
+ * const client = generateClient<Schema>();
49
+ * const user = await getUserByCognitoId(client, 'cognito-id-123');
50
+ * ```
51
+ */
52
+ export async function getUserByCognitoId<TClient = any>(
53
+ client: TClient,
54
+ cognitoId: string,
55
+ ): Promise<User | null> {
56
+ try {
57
+ const { data, errors } = await (client as any).models.User.list({
58
+ filter: { cognitoId: { eq: cognitoId } },
59
+ limit: 1,
60
+ });
61
+
62
+ if (errors) {
63
+ console.error("[getUserByCognitoId] GraphQL errors:", errors);
64
+ return null;
65
+ }
66
+
67
+ return data?.[0] as User | null;
68
+ } catch (error) {
69
+ console.error("[getUserByCognitoId] Error fetching user:", error);
70
+ throw error;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Get a user by email
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * import { getUserByEmail } from '@htlkg/data/queries';
80
+ * import { generateClient } from '@htlkg/data/client';
81
+ *
82
+ * const client = generateClient<Schema>();
83
+ * const user = await getUserByEmail(client, 'user@example.com');
84
+ * ```
85
+ */
86
+ export async function getUserByEmail<TClient = any>(
87
+ client: TClient,
88
+ email: string,
89
+ ): Promise<User | null> {
90
+ try {
91
+ const { data, errors } = await (client as any).models.User.list({
92
+ filter: { email: { eq: email } },
93
+ limit: 1,
94
+ });
95
+
96
+ if (errors) {
97
+ console.error("[getUserByEmail] GraphQL errors:", errors);
98
+ return null;
99
+ }
100
+
101
+ return data?.[0] as User | null;
102
+ } catch (error) {
103
+ console.error("[getUserByEmail] Error fetching user:", error);
104
+ throw error;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * List all users with optional filtering
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * import { listUsers } from '@htlkg/data/queries';
114
+ * import { generateClient } from '@htlkg/data/client';
115
+ *
116
+ * const client = generateClient<Schema>();
117
+ * const users = await listUsers(client, {
118
+ * filter: { status: { eq: 'active' } }
119
+ * });
120
+ * ```
121
+ */
122
+ export async function listUsers<TClient = any>(
123
+ client: TClient,
124
+ options?: {
125
+ filter?: any;
126
+ limit?: number;
127
+ nextToken?: string;
128
+ },
129
+ ): Promise<{ items: User[]; nextToken?: string }> {
130
+ try {
131
+ const { data, errors, nextToken } = await (client as any).models.User.list(
132
+ options,
133
+ );
134
+
135
+ if (errors) {
136
+ console.error("[listUsers] GraphQL errors:", errors);
137
+ return { items: [], nextToken: undefined };
138
+ }
139
+
140
+ return {
141
+ items: (data || []) as User[],
142
+ nextToken,
143
+ };
144
+ } catch (error) {
145
+ console.error("[listUsers] Error fetching users:", error);
146
+ throw error;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * List users by account ID
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * import { listUsersByAccount } from '@htlkg/data/queries';
156
+ * import { generateClient } from '@htlkg/data/client';
157
+ *
158
+ * const client = generateClient<Schema>();
159
+ * const users = await listUsersByAccount(client, 'account-123');
160
+ * ```
161
+ */
162
+ export async function listUsersByAccount<TClient = any>(
163
+ client: TClient,
164
+ accountId: string,
165
+ options?: {
166
+ limit?: number;
167
+ nextToken?: string;
168
+ },
169
+ ): Promise<{ items: User[]; nextToken?: string }> {
170
+ return listUsers(client, {
171
+ filter: { accountId: { eq: accountId } },
172
+ ...options,
173
+ });
174
+ }
175
+
176
+ /**
177
+ * List active users
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * import { listActiveUsers } from '@htlkg/data/queries';
182
+ * import { generateClient } from '@htlkg/data/client';
183
+ *
184
+ * const client = generateClient<Schema>();
185
+ * const users = await listActiveUsers(client);
186
+ * ```
187
+ */
188
+ export async function listActiveUsers<TClient = any>(
189
+ client: TClient,
190
+ options?: {
191
+ limit?: number;
192
+ nextToken?: string;
193
+ },
194
+ ): Promise<{ items: User[]; nextToken?: string }> {
195
+ return listUsers(client, {
196
+ filter: { status: { eq: "active" } },
197
+ ...options,
198
+ });
199
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Store Factory
3
+ *
4
+ * Creates request-scoped nanostores for sharing data across Vue islands
5
+ * without props. Works with @inox-tools/request-nanostores for Astro integration.
6
+ */
7
+
8
+ import { atom, type WritableAtom } from "nanostores";
9
+
10
+ /**
11
+ * Type for the shared function from @it-astro:request-nanostores
12
+ */
13
+ type SharedFn = <T>(key: string, atomFactory: WritableAtom<T>) => WritableAtom<T>;
14
+
15
+ /**
16
+ * Configuration for a resource store
17
+ */
18
+ export interface StoreConfig {
19
+ /** Resource name (e.g., 'accounts', 'users') */
20
+ name: string;
21
+ /** Related resources to create stores for (e.g., ['brands', 'products']) */
22
+ relations?: string[];
23
+ }
24
+
25
+ /**
26
+ * Result type for created stores
27
+ */
28
+ export type ResourceStores<T, TRelations extends string = never> = {
29
+ /** Main resource store */
30
+ $data: WritableAtom<readonly T[]>;
31
+ } & {
32
+ [K in TRelations as `$${K}`]: WritableAtom<readonly any[]>;
33
+ };
34
+
35
+ /**
36
+ * Capitalize first letter of a string
37
+ */
38
+ function capitalize(str: string): string {
39
+ return str.charAt(0).toUpperCase() + str.slice(1);
40
+ }
41
+
42
+ /**
43
+ * Creates request-scoped stores for a resource and its relations.
44
+ *
45
+ * This factory is designed to work with Astro's request-nanostores integration,
46
+ * providing request-isolated state that syncs from server to client.
47
+ *
48
+ * @param shared - The shared function from @it-astro:request-nanostores
49
+ * @param config - Store configuration
50
+ * @returns Object containing the main store and relation stores
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // In your store file (e.g., stores/accounts.ts)
55
+ * import { shared } from '@it-astro:request-nanostores';
56
+ * import { createResourceStores } from '@htlkg/data/stores';
57
+ *
58
+ * export const accountStores = createResourceStores(shared, {
59
+ * name: 'accounts',
60
+ * relations: ['brands'],
61
+ * });
62
+ *
63
+ * // This creates:
64
+ * // - accountStores.$data (WritableAtom<any[]>) - main accounts store
65
+ * // - accountStores.$brands (WritableAtom<any[]>) - related brands store
66
+ *
67
+ * // For cleaner exports:
68
+ * export const $accounts = accountStores.$data;
69
+ * export const $accountsBrands = accountStores.$brands;
70
+ * ```
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // With typed stores
75
+ * interface Account { id: number; name: string; }
76
+ * interface Brand { id: number; name: string; }
77
+ *
78
+ * export const { $data: $accounts, $brands } = createResourceStores<Account, 'brands'>(
79
+ * shared,
80
+ * { name: 'accounts', relations: ['brands'] }
81
+ * );
82
+ * ```
83
+ */
84
+ export function createResourceStores<T = any, TRelations extends string = never>(
85
+ shared: SharedFn,
86
+ config: StoreConfig & { relations?: TRelations[] },
87
+ ): ResourceStores<T, TRelations> {
88
+ const { name, relations = [] } = config;
89
+
90
+ const stores: Record<string, any> = {};
91
+
92
+ // Main resource store
93
+ stores.$data = shared(name, atom<T[]>([]));
94
+
95
+ // Relation stores
96
+ for (const relation of relations) {
97
+ const storeKey = `$${relation}`;
98
+ const sharedKey = `${name}${capitalize(relation)}`;
99
+ stores[storeKey] = shared(sharedKey, atom<any[]>([]));
100
+ }
101
+
102
+ return stores as ResourceStores<T, TRelations>;
103
+ }
104
+
105
+ /**
106
+ * Creates a simple single store (non-factory version for simpler use cases)
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * import { shared } from '@it-astro:request-nanostores';
111
+ * import { createStore } from '@htlkg/data/stores';
112
+ *
113
+ * export const $currentUser = createStore<User>(shared, 'currentUser');
114
+ * ```
115
+ */
116
+ export function createStore<T>(
117
+ shared: SharedFn,
118
+ name: string,
119
+ defaultValue: T[] = [],
120
+ ): WritableAtom<readonly T[]> {
121
+ return shared(name, atom<T[]>(defaultValue));
122
+ }
123
+
124
+ /**
125
+ * Creates a single-value store (for non-array values like current user)
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * import { shared } from '@it-astro:request-nanostores';
130
+ * import { createSingleStore } from '@htlkg/data/stores';
131
+ *
132
+ * export const $currentUser = createSingleStore<User | null>(shared, 'currentUser', null);
133
+ * ```
134
+ */
135
+ export function createSingleStore<T>(
136
+ shared: SharedFn,
137
+ name: string,
138
+ defaultValue: T,
139
+ ): WritableAtom<T> {
140
+ return shared(name, atom<T>(defaultValue));
141
+ }
142
+
143
+ /**
144
+ * Helper to create typed store with proper inference
145
+ */
146
+ export type InferStoreType<TStore extends WritableAtom<any>> = TStore extends WritableAtom<infer T>
147
+ ? T
148
+ : never;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Store Factories for @htlkg/data
3
+ *
4
+ * Utilities for creating request-scoped nanostores that work with Astro's
5
+ * server-client state synchronization.
6
+ */
7
+
8
+ export {
9
+ createResourceStores,
10
+ createStore,
11
+ createSingleStore,
12
+ type StoreConfig,
13
+ type ResourceStores,
14
+ type InferStoreType,
15
+ } from "./createStores";
@@ -0,0 +1,104 @@
1
+ # Stores Module
2
+
3
+ Nanostore factories for request-scoped state management in Astro SSR.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import {
9
+ createResourceStores,
10
+ createStore,
11
+ createSingleStore,
12
+ } from '@htlkg/data/stores';
13
+ ```
14
+
15
+ ## createResourceStores
16
+
17
+ Create a set of stores for a resource type with list and single-item stores.
18
+
19
+ ```typescript
20
+ import { createResourceStores } from '@htlkg/data/stores';
21
+ import type { Brand } from '@htlkg/core/types';
22
+
23
+ const brandStores = createResourceStores<Brand>('brands');
24
+
25
+ // List store
26
+ brandStores.list.set([brand1, brand2]);
27
+ const brands = brandStores.list.get();
28
+
29
+ // Single item store
30
+ brandStores.current.set(selectedBrand);
31
+ const current = brandStores.current.get();
32
+
33
+ // Loading state
34
+ brandStores.loading.set(true);
35
+ ```
36
+
37
+ ## createStore
38
+
39
+ Create a simple list store.
40
+
41
+ ```typescript
42
+ import { createStore } from '@htlkg/data/stores';
43
+
44
+ const accountsStore = createStore<Account[]>('accounts', []);
45
+
46
+ accountsStore.set(accounts);
47
+ const value = accountsStore.get();
48
+ ```
49
+
50
+ ## createSingleStore
51
+
52
+ Create a store for a single item (nullable).
53
+
54
+ ```typescript
55
+ import { createSingleStore } from '@htlkg/data/stores';
56
+
57
+ const currentUserStore = createSingleStore<User>('currentUser');
58
+
59
+ currentUserStore.set(user);
60
+ const user = currentUserStore.get(); // User | null
61
+ ```
62
+
63
+ ## Astro SSR Integration
64
+
65
+ Stores work with Astro's server-client state sync:
66
+
67
+ ```astro
68
+ ---
69
+ // Server-side: populate store
70
+ import { brandStores } from '@/stores';
71
+ import { fetchBrands } from '@htlkg/data/queries';
72
+
73
+ const brands = await fetchBrands(client, { accountId });
74
+ brandStores.list.set(brands);
75
+ ---
76
+
77
+ <BrandList client:load />
78
+ ```
79
+
80
+ ```vue
81
+ <!-- Client-side: read from store -->
82
+ <script setup>
83
+ import { useStore } from '@nanostores/vue';
84
+ import { brandStores } from '@/stores';
85
+
86
+ const brands = useStore(brandStores.list);
87
+ </script>
88
+ ```
89
+
90
+ ## Types
91
+
92
+ ```typescript
93
+ interface StoreConfig<T> {
94
+ name: string;
95
+ initial?: T;
96
+ }
97
+
98
+ interface ResourceStores<T> {
99
+ list: WritableAtom<T[]>;
100
+ current: WritableAtom<T | null>;
101
+ loading: WritableAtom<boolean>;
102
+ error: WritableAtom<Error | null>;
103
+ }
104
+ ```