@htlkg/data 0.0.2 → 0.0.9

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 +64 -37
  16. package/src/client/__tests__/server.test.ts +106 -0
  17. package/src/client/client.md +91 -0
  18. package/src/client/index.test.ts +198 -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
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @htlkg/data
2
+
3
+ GraphQL client, queries, mutations, hooks, and stores for Hotelinking applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @htlkg/data
9
+ ```
10
+
11
+ ## Modules
12
+
13
+ ### [Client](src/client/client.md)
14
+ GraphQL client generation for client-side and server-side data fetching with Amplify.
15
+
16
+ ### [Hooks](src/hooks/README.md)
17
+ Vue composables for reactive data management: `useBrands`, `useAccounts`, `useUsers`, `useProducts`.
18
+
19
+ ### [Queries](src/queries/queries.md)
20
+ GraphQL query functions for accounts, brands, users, and products.
21
+
22
+ ### [Mutations](src/mutations/mutations.md)
23
+ GraphQL mutation functions for creating, updating, and deleting resources.
24
+
25
+ ### [Stores](src/stores/stores.md)
26
+ Nanostore factories for request-scoped state management in Astro SSR.
27
+
28
+ ### [Content Collections](src/content-collections/content-collections.md)
29
+ Astro content collection generators and schema definitions.
30
+
31
+ ## Quick Start
32
+
33
+ ```typescript
34
+ // Client-side data fetching
35
+ import { getSharedClient } from '@htlkg/data/client';
36
+ import type { Schema } from '@backend/data/resource';
37
+
38
+ const client = getSharedClient<Schema>();
39
+ const { data } = await client.models.Brand.list();
40
+
41
+ // Vue composables
42
+ import { useBrands, useAccounts } from '@htlkg/data/hooks';
43
+
44
+ const { brands, loading, error, refetch } = useBrands({
45
+ accountId: 'account-123',
46
+ activeOnly: true,
47
+ });
48
+
49
+ // Server-side queries
50
+ import { fetchBrands, fetchAccounts } from '@htlkg/data/queries';
51
+
52
+ const brands = await fetchBrands(client, { accountId });
53
+ ```
@@ -52,6 +52,21 @@ declare function generateServerClientUsingCookies<T extends Record<any, any> = n
52
52
  * The server-side functions use the Amplify Astro adapter for proper SSR support.
53
53
  */
54
54
 
55
+ /**
56
+ * Get or create the shared GraphQL client instance (singleton pattern)
57
+ * Use this for client-side fetching to avoid creating multiple client instances.
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const client = getSharedClient<Schema>();
62
+ * const { data } = await client.models.Account.list();
63
+ * ```
64
+ */
65
+ declare function getSharedClient<TSchema extends Record<string, unknown> = Record<string, unknown>>(): ReturnType<typeof generateClient$1<TSchema>>;
66
+ /**
67
+ * Reset the shared client instance (useful for testing or auth state changes)
68
+ */
69
+ declare function resetSharedClient(): void;
55
70
  /**
56
71
  * Generate a client-side GraphQL client for use in Vue components and browser contexts
57
72
  * This is SSR-safe and should be called within a component's setup function or after hydration
@@ -138,4 +153,4 @@ interface GenerateServerClientOptions {
138
153
  */
139
154
  declare function generateServerClient<TSchema extends Record<string, unknown> = Record<string, unknown>>(options?: GenerateServerClientOptions): ReturnType<typeof generateClient$1<TSchema>>;
140
155
 
141
- export { type AstroAmplifyConfig, type GenerateServerClientOptions, type ServerAuthMode, generateClient, generateServerClient, generateServerClientUsingCookies };
156
+ export { type AstroAmplifyConfig, type GenerateServerClientOptions, type ServerAuthMode, generateClient, generateServerClient, generateServerClientUsingCookies, getSharedClient, resetSharedClient };
@@ -58,6 +58,16 @@ function generateServerClientUsingCookies(options) {
58
58
  }
59
59
 
60
60
  // src/client/index.ts
61
+ var sharedClientInstance = null;
62
+ function getSharedClient() {
63
+ if (!sharedClientInstance) {
64
+ sharedClientInstance = generateDataClient();
65
+ }
66
+ return sharedClientInstance;
67
+ }
68
+ function resetSharedClient() {
69
+ sharedClientInstance = null;
70
+ }
61
71
  function generateClient() {
62
72
  return generateDataClient();
63
73
  }
@@ -68,6 +78,8 @@ function generateServerClient(options) {
68
78
  export {
69
79
  generateClient,
70
80
  generateServerClient,
71
- generateServerClientUsingCookies
81
+ generateServerClientUsingCookies,
82
+ getSharedClient,
83
+ resetSharedClient
72
84
  };
73
85
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts","../../src/client/server.ts"],"sourcesContent":["/**\n * GraphQL Client for @htlkg/data\n *\n * Provides both client-side and server-side GraphQL capabilities using AWS Amplify Data.\n * The server-side functions use the Amplify Astro adapter for proper SSR support.\n */\n\nimport { generateClient as generateDataClient } from \"aws-amplify/data\";\nimport type { ResourcesConfig } from \"aws-amplify\";\n\n// Re-export server-side client generation\nexport { generateServerClientUsingCookies } from \"./server\";\n\n/**\n * Generate a client-side GraphQL client for use in Vue components and browser contexts\n * This is SSR-safe and should be called within a component's setup function or after hydration\n *\n * @example\n * ```typescript\n * import type { Schema } from '@backend/data/resource';\n * import { generateClient } from '@htlkg/data/client';\n *\n * const client = generateClient<Schema>();\n * const { data: brands } = await client.models.Brand.list();\n * ```\n */\nexport function generateClient<\n\tTSchema extends Record<string, unknown> = Record<string, unknown>,\n>(): ReturnType<typeof generateDataClient<TSchema>> {\n\treturn generateDataClient<TSchema>();\n}\n\n/**\n * Configuration for Amplify (matches amplify_outputs.json format)\n */\nexport type AstroAmplifyConfig = ResourcesConfig;\n\n/**\n * Authentication mode for server-side GraphQL client\n */\nexport type ServerAuthMode = 'userPool' | 'apiKey';\n\n/**\n * Options for generating a server-side GraphQL client\n */\nexport interface GenerateServerClientOptions {\n\t/** Authentication mode - 'userPool' (default) uses JWT from cookies, 'apiKey' uses API key */\n\tauthMode?: ServerAuthMode;\n}\n\n/**\n * Generate a server-side GraphQL client for use within runWithAmplifyServerContext\n * \n * This function creates a GraphQL client that can be used for server-side data fetching in Astro.\n * It MUST be called within runWithAmplifyServerContext to access JWT tokens from cookies.\n * \n * The client supports two authentication modes:\n * - 'userPool' (default): Uses JWT tokens from cookies (requires runWithAmplifyServerContext)\n * - 'apiKey': Uses API key for public/unauthenticated requests\n *\n * **Important**: \n * - Amplify.configure() must be called once at app startup (e.g., in amplify-server.ts)\n * - This function must be called INSIDE the operation function of runWithAmplifyServerContext\n * - The context automatically provides the token provider that reads JWT tokens from cookies\n *\n * @example\n * ```typescript\n * // In your Astro page\n * import type { Schema } from '../amplify/data/resource';\n * import { generateServerClient } from '@htlkg/data/client';\n * import { createRunWithAmplifyServerContext } from '@htlkg/core/amplify-astro-adapter';\n * import outputs from '../amplify_outputs.json';\n *\n * const runWithAmplifyServerContext = createRunWithAmplifyServerContext({ config: outputs });\n *\n * // Fetch data with authentication\n * const result = await runWithAmplifyServerContext({\n * astroServerContext: {\n * cookies: Astro.cookies,\n * request: Astro.request\n * },\n * operation: async (contextSpec) => {\n * // Generate client INSIDE the operation\n * const client = generateServerClient<Schema>({ authMode: 'userPool' });\n * return await client.models.User.list();\n * }\n * });\n * \n * const users = result.data || [];\n * ```\n * \n * @example Using API key for public data\n * ```typescript\n * const result = await runWithAmplifyServerContext({\n * astroServerContext: {\n * cookies: Astro.cookies,\n * request: Astro.request\n * },\n * operation: async (contextSpec) => {\n * const client = generateServerClient<Schema>({ authMode: 'apiKey' });\n * return await client.models.Brand.list();\n * }\n * });\n * ```\n */\nexport function generateServerClient<\n\tTSchema extends Record<string, unknown> = Record<string, unknown>,\n>(options?: GenerateServerClientOptions): ReturnType<typeof generateDataClient<TSchema>> {\n\t// Generate the client without authMode parameter\n\t// When called within runWithAmplifyServerContext, it will automatically use the token provider\n\t// from the context (which reads JWT tokens from cookies)\n\t// The authMode should be specified per-operation, not at client creation\n\tconst client = generateDataClient<TSchema>();\n\t\n\treturn client;\n}\n","/**\n * Server-side data client for Astro\n *\n * Provides a client generator similar to Next.js's generateServerClientUsingCookies\n * using generateClientWithAmplifyInstance for proper server context integration.\n */\n\nimport type { AstroGlobal } from \"astro\";\nimport type { ResourcesConfig } from \"aws-amplify\";\nimport {\n\tCommonPublicClientOptions,\n\tDefaultCommonClientOptions,\n\tV6ClientSSRCookies,\n\tgenerateClientWithAmplifyInstance,\n} from \"aws-amplify/api/internals\";\nimport { getAmplifyServerContext } from \"aws-amplify/adapter-core/internals\";\nimport { createRunWithAmplifyServerContext, createLogger } from \"@htlkg/core/amplify-astro-adapter\";\n\nconst log = createLogger('server-client');\n\ninterface AstroCookiesClientParams {\n\tcookies: AstroGlobal[\"cookies\"];\n\trequest: AstroGlobal[\"request\"];\n\tconfig: ResourcesConfig;\n}\n\n/**\n * Generates a server-side data client for Astro (matches Next.js implementation)\n *\n * This function creates a client that automatically wraps all operations in the Amplify server context,\n * ensuring that authentication tokens from cookies are properly used.\n *\n * @example\n * ```typescript\n * import type { Schema } from '../amplify/data/resource';\n * import { generateServerClientUsingCookies } from '@htlkg/data/client';\n * import { parseAmplifyConfig } from 'aws-amplify/utils';\n * import outputs from '../amplify_outputs.json';\n *\n * const amplifyConfig = parseAmplifyConfig(outputs);\n *\n * const client = generateServerClientUsingCookies<Schema>({\n * config: amplifyConfig,\n * cookies: Astro.cookies,\n * request: Astro.request,\n * });\n *\n * // Use the client directly - operations are automatically wrapped\n * const result = await client.models.User.list({\n * selectionSet: ['id', 'email'],\n * limit: 100,\n * });\n * ```\n */\nexport function generateServerClientUsingCookies<\n\tT extends Record<any, any> = never,\n\tOptions extends CommonPublicClientOptions &\n\t\tAstroCookiesClientParams = DefaultCommonClientOptions &\n\t\tAstroCookiesClientParams,\n>(options: Options): V6ClientSSRCookies<T, Options> {\n\tconst runWithAmplifyServerContext = createRunWithAmplifyServerContext({\n\t\tconfig: options.config,\n\t});\n\n\tconst resourcesConfig = options.config;\n\n\t// This function reference gets passed down to InternalGraphQLAPI.ts.graphql\n\t// where this._graphql is passed in as the `fn` argument\n\t// causing it to always get invoked inside `runWithAmplifyServerContext`\n\tconst getAmplify = (fn: (amplify: any) => Promise<any>) => {\n\t\treturn runWithAmplifyServerContext({\n\t\t\tastroServerContext: {\n\t\t\t\tcookies: options.cookies,\n\t\t\t\trequest: options.request,\n\t\t\t},\n\t\t\toperation: async (contextSpec: any) => {\n\t\t\t\tconst amplifyInstance = getAmplifyServerContext(contextSpec).amplify;\n\t\t\t\t\n\t\t\t\t// Debug logging (only when DEBUG=true)\n\t\t\t\ttry {\n\t\t\t\t\tconst config = amplifyInstance.getConfig();\n\t\t\t\t\tlog.debug('Amplify config from instance:', {\n\t\t\t\t\t\thasAPI: !!config.API,\n\t\t\t\t\t\thasGraphQL: !!config.API?.GraphQL,\n\t\t\t\t\t\tendpoint: config.API?.GraphQL?.endpoint,\n\t\t\t\t\t\tdefaultAuthMode: config.API?.GraphQL?.defaultAuthMode,\n\t\t\t\t\t\tregion: config.API?.GraphQL?.region,\n\t\t\t\t\t});\n\t\t\t\t\t\n\t\t\t\t\tconst session = await amplifyInstance.Auth.fetchAuthSession();\n\t\t\t\t\tlog.debug('Auth session:', {\n\t\t\t\t\t\thasTokens: !!session.tokens,\n\t\t\t\t\t\thasAccessToken: !!session.tokens?.accessToken,\n\t\t\t\t\t\thasIdToken: !!session.tokens?.idToken,\n\t\t\t\t\t\thasCredentials: !!session.credentials,\n\t\t\t\t\t});\n\t\t\t\t} catch (e: any) {\n\t\t\t\t\tlog.debug('Error fetching session:', e.message);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn fn(amplifyInstance);\n\t\t\t},\n\t\t});\n\t};\n\n\tconst {\n\t\tcookies: _cookies,\n\t\trequest: _request,\n\t\tconfig: _config,\n\t\t...params\n\t} = options;\n\n\treturn generateClientWithAmplifyInstance<T, V6ClientSSRCookies<T, Options>>({\n\t\tamplify: getAmplify,\n\t\tconfig: resourcesConfig,\n\t\t...params,\n\t} as any);\n}\n"],"mappings":";AAOA,SAAS,kBAAkB,0BAA0B;;;ACErD;AAAA,EAIC;AAAA,OACM;AACP,SAAS,+BAA+B;AACxC,SAAS,mCAAmC,oBAAoB;AAEhE,IAAM,MAAM,aAAa,eAAe;AAoCjC,SAAS,iCAKd,SAAkD;AACnD,QAAM,8BAA8B,kCAAkC;AAAA,IACrE,QAAQ,QAAQ;AAAA,EACjB,CAAC;AAED,QAAM,kBAAkB,QAAQ;AAKhC,QAAM,aAAa,CAAC,OAAuC;AAC1D,WAAO,4BAA4B;AAAA,MAClC,oBAAoB;AAAA,QACnB,SAAS,QAAQ;AAAA,QACjB,SAAS,QAAQ;AAAA,MAClB;AAAA,MACA,WAAW,OAAO,gBAAqB;AACtC,cAAM,kBAAkB,wBAAwB,WAAW,EAAE;AAG7D,YAAI;AACH,gBAAM,SAAS,gBAAgB,UAAU;AACzC,cAAI,MAAM,iCAAiC;AAAA,YAC1C,QAAQ,CAAC,CAAC,OAAO;AAAA,YACjB,YAAY,CAAC,CAAC,OAAO,KAAK;AAAA,YAC1B,UAAU,OAAO,KAAK,SAAS;AAAA,YAC/B,iBAAiB,OAAO,KAAK,SAAS;AAAA,YACtC,QAAQ,OAAO,KAAK,SAAS;AAAA,UAC9B,CAAC;AAED,gBAAM,UAAU,MAAM,gBAAgB,KAAK,iBAAiB;AAC5D,cAAI,MAAM,iBAAiB;AAAA,YAC1B,WAAW,CAAC,CAAC,QAAQ;AAAA,YACrB,gBAAgB,CAAC,CAAC,QAAQ,QAAQ;AAAA,YAClC,YAAY,CAAC,CAAC,QAAQ,QAAQ;AAAA,YAC9B,gBAAgB,CAAC,CAAC,QAAQ;AAAA,UAC3B,CAAC;AAAA,QACF,SAAS,GAAQ;AAChB,cAAI,MAAM,2BAA2B,EAAE,OAAO;AAAA,QAC/C;AAEA,eAAO,GAAG,eAAe;AAAA,MAC1B;AAAA,IACD,CAAC;AAAA,EACF;AAEA,QAAM;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,GAAG;AAAA,EACJ,IAAI;AAEJ,SAAO,kCAAqE;AAAA,IAC3E,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,GAAG;AAAA,EACJ,CAAQ;AACT;;;AD3FO,SAAS,iBAEoC;AACnD,SAAO,mBAA4B;AACpC;AA2EO,SAAS,qBAEd,SAAuF;AAKxF,QAAM,SAAS,mBAA4B;AAE3C,SAAO;AACR;","names":[]}
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/server.ts"],"sourcesContent":["/**\n * GraphQL Client for @htlkg/data\n *\n * Provides both client-side and server-side GraphQL capabilities using AWS Amplify Data.\n * The server-side functions use the Amplify Astro adapter for proper SSR support.\n */\n\nimport { generateClient as generateDataClient } from \"aws-amplify/data\";\nimport type { ResourcesConfig } from \"aws-amplify\";\n\n// Re-export server-side client generation\nexport { generateServerClientUsingCookies } from \"./server\";\n\n// Singleton client instance for client-side fetching\nlet sharedClientInstance: any = null;\n\n/**\n * Get or create the shared GraphQL client instance (singleton pattern)\n * Use this for client-side fetching to avoid creating multiple client instances.\n *\n * @example\n * ```typescript\n * const client = getSharedClient<Schema>();\n * const { data } = await client.models.Account.list();\n * ```\n */\nexport function getSharedClient<\n\tTSchema extends Record<string, unknown> = Record<string, unknown>,\n>(): ReturnType<typeof generateDataClient<TSchema>> {\n\tif (!sharedClientInstance) {\n\t\tsharedClientInstance = generateDataClient<TSchema>();\n\t}\n\treturn sharedClientInstance;\n}\n\n/**\n * Reset the shared client instance (useful for testing or auth state changes)\n */\nexport function resetSharedClient(): void {\n\tsharedClientInstance = null;\n}\n\n/**\n * Generate a client-side GraphQL client for use in Vue components and browser contexts\n * This is SSR-safe and should be called within a component's setup function or after hydration\n *\n * @example\n * ```typescript\n * import type { Schema } from '@backend/data/resource';\n * import { generateClient } from '@htlkg/data/client';\n *\n * const client = generateClient<Schema>();\n * const { data: brands } = await client.models.Brand.list();\n * ```\n */\nexport function generateClient<\n\tTSchema extends Record<string, unknown> = Record<string, unknown>,\n>(): ReturnType<typeof generateDataClient<TSchema>> {\n\treturn generateDataClient<TSchema>();\n}\n\n/**\n * Configuration for Amplify (matches amplify_outputs.json format)\n */\nexport type AstroAmplifyConfig = ResourcesConfig;\n\n/**\n * Authentication mode for server-side GraphQL client\n */\nexport type ServerAuthMode = 'userPool' | 'apiKey';\n\n/**\n * Options for generating a server-side GraphQL client\n */\nexport interface GenerateServerClientOptions {\n\t/** Authentication mode - 'userPool' (default) uses JWT from cookies, 'apiKey' uses API key */\n\tauthMode?: ServerAuthMode;\n}\n\n/**\n * Generate a server-side GraphQL client for use within runWithAmplifyServerContext\n * \n * This function creates a GraphQL client that can be used for server-side data fetching in Astro.\n * It MUST be called within runWithAmplifyServerContext to access JWT tokens from cookies.\n * \n * The client supports two authentication modes:\n * - 'userPool' (default): Uses JWT tokens from cookies (requires runWithAmplifyServerContext)\n * - 'apiKey': Uses API key for public/unauthenticated requests\n *\n * **Important**: \n * - Amplify.configure() must be called once at app startup (e.g., in amplify-server.ts)\n * - This function must be called INSIDE the operation function of runWithAmplifyServerContext\n * - The context automatically provides the token provider that reads JWT tokens from cookies\n *\n * @example\n * ```typescript\n * // In your Astro page\n * import type { Schema } from '../amplify/data/resource';\n * import { generateServerClient } from '@htlkg/data/client';\n * import { createRunWithAmplifyServerContext } from '@htlkg/core/amplify-astro-adapter';\n * import outputs from '../amplify_outputs.json';\n *\n * const runWithAmplifyServerContext = createRunWithAmplifyServerContext({ config: outputs });\n *\n * // Fetch data with authentication\n * const result = await runWithAmplifyServerContext({\n * astroServerContext: {\n * cookies: Astro.cookies,\n * request: Astro.request\n * },\n * operation: async (contextSpec) => {\n * // Generate client INSIDE the operation\n * const client = generateServerClient<Schema>({ authMode: 'userPool' });\n * return await client.models.User.list();\n * }\n * });\n * \n * const users = result.data || [];\n * ```\n * \n * @example Using API key for public data\n * ```typescript\n * const result = await runWithAmplifyServerContext({\n * astroServerContext: {\n * cookies: Astro.cookies,\n * request: Astro.request\n * },\n * operation: async (contextSpec) => {\n * const client = generateServerClient<Schema>({ authMode: 'apiKey' });\n * return await client.models.Brand.list();\n * }\n * });\n * ```\n */\nexport function generateServerClient<\n\tTSchema extends Record<string, unknown> = Record<string, unknown>,\n>(options?: GenerateServerClientOptions): ReturnType<typeof generateDataClient<TSchema>> {\n\t// Generate the client without authMode parameter\n\t// When called within runWithAmplifyServerContext, it will automatically use the token provider\n\t// from the context (which reads JWT tokens from cookies)\n\t// The authMode should be specified per-operation, not at client creation\n\tconst client = generateDataClient<TSchema>();\n\t\n\treturn client;\n}\n","/**\n * Server-side data client for Astro\n *\n * Provides a client generator similar to Next.js's generateServerClientUsingCookies\n * using generateClientWithAmplifyInstance for proper server context integration.\n */\n\nimport type { AstroGlobal } from \"astro\";\nimport type { ResourcesConfig } from \"aws-amplify\";\nimport {\n\tCommonPublicClientOptions,\n\tDefaultCommonClientOptions,\n\tV6ClientSSRCookies,\n\tgenerateClientWithAmplifyInstance,\n} from \"aws-amplify/api/internals\";\nimport { getAmplifyServerContext } from \"aws-amplify/adapter-core/internals\";\nimport { createRunWithAmplifyServerContext, createLogger } from \"@htlkg/core/amplify-astro-adapter\";\n\nconst log = createLogger('server-client');\n\ninterface AstroCookiesClientParams {\n\tcookies: AstroGlobal[\"cookies\"];\n\trequest: AstroGlobal[\"request\"];\n\tconfig: ResourcesConfig;\n}\n\n/**\n * Generates a server-side data client for Astro (matches Next.js implementation)\n *\n * This function creates a client that automatically wraps all operations in the Amplify server context,\n * ensuring that authentication tokens from cookies are properly used.\n *\n * @example\n * ```typescript\n * import type { Schema } from '../amplify/data/resource';\n * import { generateServerClientUsingCookies } from '@htlkg/data/client';\n * import { parseAmplifyConfig } from 'aws-amplify/utils';\n * import outputs from '../amplify_outputs.json';\n *\n * const amplifyConfig = parseAmplifyConfig(outputs);\n *\n * const client = generateServerClientUsingCookies<Schema>({\n * config: amplifyConfig,\n * cookies: Astro.cookies,\n * request: Astro.request,\n * });\n *\n * // Use the client directly - operations are automatically wrapped\n * const result = await client.models.User.list({\n * selectionSet: ['id', 'email'],\n * limit: 100,\n * });\n * ```\n */\nexport function generateServerClientUsingCookies<\n\tT extends Record<any, any> = never,\n\tOptions extends CommonPublicClientOptions &\n\t\tAstroCookiesClientParams = DefaultCommonClientOptions &\n\t\tAstroCookiesClientParams,\n>(options: Options): V6ClientSSRCookies<T, Options> {\n\tconst runWithAmplifyServerContext = createRunWithAmplifyServerContext({\n\t\tconfig: options.config,\n\t});\n\n\tconst resourcesConfig = options.config;\n\n\t// This function reference gets passed down to InternalGraphQLAPI.ts.graphql\n\t// where this._graphql is passed in as the `fn` argument\n\t// causing it to always get invoked inside `runWithAmplifyServerContext`\n\tconst getAmplify = (fn: (amplify: any) => Promise<any>) => {\n\t\treturn runWithAmplifyServerContext({\n\t\t\tastroServerContext: {\n\t\t\t\tcookies: options.cookies,\n\t\t\t\trequest: options.request,\n\t\t\t},\n\t\t\toperation: async (contextSpec: any) => {\n\t\t\t\tconst amplifyInstance = getAmplifyServerContext(contextSpec).amplify;\n\t\t\t\t\n\t\t\t\t// Debug logging (only when DEBUG=true)\n\t\t\t\ttry {\n\t\t\t\t\tconst config = amplifyInstance.getConfig();\n\t\t\t\t\tlog.debug('Amplify config from instance:', {\n\t\t\t\t\t\thasAPI: !!config.API,\n\t\t\t\t\t\thasGraphQL: !!config.API?.GraphQL,\n\t\t\t\t\t\tendpoint: config.API?.GraphQL?.endpoint,\n\t\t\t\t\t\tdefaultAuthMode: config.API?.GraphQL?.defaultAuthMode,\n\t\t\t\t\t\tregion: config.API?.GraphQL?.region,\n\t\t\t\t\t});\n\t\t\t\t\t\n\t\t\t\t\tconst session = await amplifyInstance.Auth.fetchAuthSession();\n\t\t\t\t\tlog.debug('Auth session:', {\n\t\t\t\t\t\thasTokens: !!session.tokens,\n\t\t\t\t\t\thasAccessToken: !!session.tokens?.accessToken,\n\t\t\t\t\t\thasIdToken: !!session.tokens?.idToken,\n\t\t\t\t\t\thasCredentials: !!session.credentials,\n\t\t\t\t\t});\n\t\t\t\t} catch (e: any) {\n\t\t\t\t\tlog.debug('Error fetching session:', e.message);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn fn(amplifyInstance);\n\t\t\t},\n\t\t});\n\t};\n\n\tconst {\n\t\tcookies: _cookies,\n\t\trequest: _request,\n\t\tconfig: _config,\n\t\t...params\n\t} = options;\n\n\treturn generateClientWithAmplifyInstance<T, V6ClientSSRCookies<T, Options>>({\n\t\tamplify: getAmplify,\n\t\tconfig: resourcesConfig,\n\t\t...params,\n\t} as any);\n}\n"],"mappings":";AAOA,SAAS,kBAAkB,0BAA0B;;;ACErD;AAAA,EAIC;AAAA,OACM;AACP,SAAS,+BAA+B;AACxC,SAAS,mCAAmC,oBAAoB;AAEhE,IAAM,MAAM,aAAa,eAAe;AAoCjC,SAAS,iCAKd,SAAkD;AACnD,QAAM,8BAA8B,kCAAkC;AAAA,IACrE,QAAQ,QAAQ;AAAA,EACjB,CAAC;AAED,QAAM,kBAAkB,QAAQ;AAKhC,QAAM,aAAa,CAAC,OAAuC;AAC1D,WAAO,4BAA4B;AAAA,MAClC,oBAAoB;AAAA,QACnB,SAAS,QAAQ;AAAA,QACjB,SAAS,QAAQ;AAAA,MAClB;AAAA,MACA,WAAW,OAAO,gBAAqB;AACtC,cAAM,kBAAkB,wBAAwB,WAAW,EAAE;AAG7D,YAAI;AACH,gBAAM,SAAS,gBAAgB,UAAU;AACzC,cAAI,MAAM,iCAAiC;AAAA,YAC1C,QAAQ,CAAC,CAAC,OAAO;AAAA,YACjB,YAAY,CAAC,CAAC,OAAO,KAAK;AAAA,YAC1B,UAAU,OAAO,KAAK,SAAS;AAAA,YAC/B,iBAAiB,OAAO,KAAK,SAAS;AAAA,YACtC,QAAQ,OAAO,KAAK,SAAS;AAAA,UAC9B,CAAC;AAED,gBAAM,UAAU,MAAM,gBAAgB,KAAK,iBAAiB;AAC5D,cAAI,MAAM,iBAAiB;AAAA,YAC1B,WAAW,CAAC,CAAC,QAAQ;AAAA,YACrB,gBAAgB,CAAC,CAAC,QAAQ,QAAQ;AAAA,YAClC,YAAY,CAAC,CAAC,QAAQ,QAAQ;AAAA,YAC9B,gBAAgB,CAAC,CAAC,QAAQ;AAAA,UAC3B,CAAC;AAAA,QACF,SAAS,GAAQ;AAChB,cAAI,MAAM,2BAA2B,EAAE,OAAO;AAAA,QAC/C;AAEA,eAAO,GAAG,eAAe;AAAA,MAC1B;AAAA,IACD,CAAC;AAAA,EACF;AAEA,QAAM;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,GAAG;AAAA,EACJ,IAAI;AAEJ,SAAO,kCAAqE;AAAA,IAC3E,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,GAAG;AAAA,EACJ,CAAQ;AACT;;;ADvGA,IAAI,uBAA4B;AAYzB,SAAS,kBAEoC;AACnD,MAAI,CAAC,sBAAsB;AAC1B,2BAAuB,mBAA4B;AAAA,EACpD;AACA,SAAO;AACR;AAKO,SAAS,oBAA0B;AACzC,yBAAuB;AACxB;AAeO,SAAS,iBAEoC;AACnD,SAAO,mBAA4B;AACpC;AA2EO,SAAS,qBAEd,SAAuF;AAKxF,QAAM,SAAS,mBAA4B;AAE3C,SAAO;AACR;","names":[]}
@@ -1,5 +1,113 @@
1
1
  import { Ref, ComputedRef } from 'vue';
2
2
  import { Brand, Account, User, Product } from '@htlkg/core/types';
3
+ export { resetSharedClient as resetClientInstance } from '../client/index.js';
4
+ import 'aws-amplify/data';
5
+ import 'aws-amplify';
6
+ import 'astro';
7
+ import 'aws-amplify/api/internals';
8
+
9
+ /**
10
+ * Data Hook Factory
11
+ *
12
+ * Creates reusable Vue composables for fetching data from GraphQL models.
13
+ * Provides a DRY approach to data fetching with consistent patterns.
14
+ */
15
+
16
+ /**
17
+ * Configuration options for creating a data hook
18
+ */
19
+ interface CreateDataHookOptions<T, TOptions extends BaseHookOptions = BaseHookOptions> {
20
+ /** The GraphQL model name (e.g., 'Account', 'User', 'Brand') */
21
+ model: string;
22
+ /** Default limit for queries */
23
+ defaultLimit?: number;
24
+ /** Selection set for the query (fields to fetch) */
25
+ selectionSet?: string[];
26
+ /** Transform function to apply to fetched data */
27
+ transform?: (item: any) => T;
28
+ /** Build filter from hook options */
29
+ buildFilter?: (options: TOptions) => any;
30
+ /** Define computed properties based on the data */
31
+ computedProperties?: Record<string, (data: T[]) => any>;
32
+ /** Plural name for the data property (e.g., 'accounts', 'users') */
33
+ dataPropertyName?: string;
34
+ }
35
+ /**
36
+ * Base options available to all hooks
37
+ */
38
+ interface BaseHookOptions {
39
+ /** Filter criteria */
40
+ filter?: any;
41
+ /** Limit number of results */
42
+ limit?: number;
43
+ /** Auto-fetch on mount (default: true) */
44
+ autoFetch?: boolean;
45
+ }
46
+ /**
47
+ * Return type for data hooks
48
+ */
49
+ interface DataHookReturn<T, TComputed extends Record<string, any> = Record<string, never>> {
50
+ /** Reactive array of data */
51
+ data: Ref<T[]>;
52
+ /** Loading state */
53
+ loading: Ref<boolean>;
54
+ /** Error state */
55
+ error: Ref<Error | null>;
56
+ /** Refetch data */
57
+ refetch: () => Promise<void>;
58
+ /** Computed properties */
59
+ computed: {
60
+ [K in keyof TComputed]: ComputedRef<TComputed[K]>;
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Creates a reusable data hook for a specific model
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * // Create a simple hook
70
+ * export const useAccounts = createDataHook<Account>({
71
+ * model: 'Account',
72
+ * dataPropertyName: 'accounts',
73
+ * });
74
+ *
75
+ * // Usage
76
+ * const { data: accounts, loading, error, refetch } = useAccounts();
77
+ * ```
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Create a hook with custom filters and computed properties
82
+ * interface UseBrandsOptions extends BaseHookOptions {
83
+ * accountId?: string;
84
+ * activeOnly?: boolean;
85
+ * }
86
+ *
87
+ * export const useBrands = createDataHook<Brand, UseBrandsOptions>({
88
+ * model: 'Brand',
89
+ * dataPropertyName: 'brands',
90
+ * buildFilter: (options) => {
91
+ * const filter: any = options.filter || {};
92
+ * if (options.accountId) filter.accountId = { eq: options.accountId };
93
+ * if (options.activeOnly) filter.status = { eq: 'active' };
94
+ * return Object.keys(filter).length > 0 ? filter : undefined;
95
+ * },
96
+ * computedProperties: {
97
+ * activeBrands: (brands) => brands.filter(b => b.status === 'active'),
98
+ * },
99
+ * });
100
+ * ```
101
+ */
102
+ declare function createDataHook<T, TOptions extends BaseHookOptions = BaseHookOptions, TComputed extends Record<string, any> = Record<string, never>>(config: CreateDataHookOptions<T, TOptions> & {
103
+ computedProperties?: {
104
+ [K in keyof TComputed]: (data: T[]) => TComputed[K];
105
+ };
106
+ }): (options?: TOptions) => DataHookReturn<T, TComputed> & Record<string, any>;
107
+ /**
108
+ * Type helper to extract the return type of a created hook
109
+ */
110
+ type InferHookReturn<THook extends (...args: any[]) => any> = ReturnType<THook>;
3
111
 
4
112
  /**
5
113
  * useBrands Hook
@@ -8,7 +116,7 @@ import { Brand, Account, User, Product } from '@htlkg/core/types';
8
116
  * Provides loading states, error handling, and refetch capabilities.
9
117
  */
10
118
 
11
- interface UseBrandsOptions {
119
+ interface UseBrandsOptions extends BaseHookOptions {
12
120
  /** Filter criteria for brands */
13
121
  filter?: any;
14
122
  /** Limit number of results */
@@ -60,7 +168,7 @@ declare function useBrands(options?: UseBrandsOptions): UseBrandsReturn;
60
168
  * Provides loading states, error handling, and refetch capabilities.
61
169
  */
62
170
 
63
- interface UseAccountsOptions {
171
+ interface UseAccountsOptions extends BaseHookOptions {
64
172
  /** Filter criteria for accounts */
65
173
  filter?: any;
66
174
  /** Limit number of results */
@@ -105,7 +213,7 @@ declare function useAccounts(options?: UseAccountsOptions): UseAccountsReturn;
105
213
  * Provides loading states, error handling, and refetch capabilities.
106
214
  */
107
215
 
108
- interface UseUsersOptions {
216
+ interface UseUsersOptions extends BaseHookOptions {
109
217
  /** Filter criteria for users */
110
218
  filter?: any;
111
219
  /** Limit number of results */
@@ -155,7 +263,7 @@ declare function useUsers(options?: UseUsersOptions): UseUsersReturn;
155
263
  * Provides loading states, error handling, and refetch capabilities.
156
264
  */
157
265
 
158
- interface UseProductsOptions {
266
+ interface UseProductsOptions extends BaseHookOptions {
159
267
  /** Filter criteria for products */
160
268
  filter?: any;
161
269
  /** Limit number of results */
@@ -197,4 +305,4 @@ interface UseProductsReturn {
197
305
  */
198
306
  declare function useProducts(options?: UseProductsOptions): UseProductsReturn;
199
307
 
200
- export { type UseAccountsOptions, type UseAccountsReturn, type UseBrandsOptions, type UseBrandsReturn, type UseProductsOptions, type UseProductsReturn, type UseUsersOptions, type UseUsersReturn, useAccounts, useBrands, useProducts, useUsers };
308
+ export { type BaseHookOptions, type CreateDataHookOptions, type DataHookReturn, type InferHookReturn, type UseAccountsOptions, type UseAccountsReturn, type UseBrandsOptions, type UseBrandsReturn, type UseProductsOptions, type UseProductsReturn, type UseUsersOptions, type UseUsersReturn, createDataHook, useAccounts, useBrands, useProducts, useUsers };
@@ -1,4 +1,4 @@
1
- // src/hooks/useBrands.ts
1
+ // src/hooks/createDataHook.ts
2
2
  import { ref, computed, onMounted } from "vue";
3
3
 
4
4
  // src/client/index.ts
@@ -13,201 +13,192 @@ import { createRunWithAmplifyServerContext, createLogger } from "@htlkg/core/amp
13
13
  var log = createLogger("server-client");
14
14
 
15
15
  // src/client/index.ts
16
- function generateClient() {
17
- return generateDataClient();
16
+ var sharedClientInstance = null;
17
+ function getSharedClient() {
18
+ if (!sharedClientInstance) {
19
+ sharedClientInstance = generateDataClient();
20
+ }
21
+ return sharedClientInstance;
22
+ }
23
+ function resetSharedClient() {
24
+ sharedClientInstance = null;
18
25
  }
19
26
 
20
- // src/hooks/useBrands.ts
21
- function useBrands(options = {}) {
27
+ // src/hooks/createDataHook.ts
28
+ function createDataHook(config) {
22
29
  const {
23
- filter: baseFilter,
24
- limit,
25
- autoFetch = true,
26
- accountId,
27
- activeOnly
28
- } = options;
29
- const brands = ref([]);
30
- const loading = ref(false);
31
- const error = ref(null);
32
- let filter = baseFilter || {};
33
- if (accountId) {
34
- filter = { ...filter, accountId: { eq: accountId } };
35
- }
36
- if (activeOnly) {
37
- filter = { ...filter, status: { eq: "active" } };
38
- }
39
- const activeBrands = computed(
40
- () => brands.value.filter((b) => b.status === "active")
41
- );
42
- async function fetch() {
43
- loading.value = true;
44
- error.value = null;
45
- try {
46
- const client = generateClient();
47
- const { data, errors } = await client.models.Brand.list({
48
- filter: Object.keys(filter).length > 0 ? filter : void 0,
49
- limit
50
- });
51
- if (errors) {
52
- throw new Error(errors[0]?.message || "Failed to fetch brands");
30
+ model,
31
+ defaultLimit,
32
+ selectionSet,
33
+ transform,
34
+ buildFilter: buildFilter4,
35
+ computedProperties,
36
+ dataPropertyName = "data"
37
+ } = config;
38
+ return function useData(options = {}) {
39
+ const { filter: baseFilter, limit = defaultLimit, autoFetch = true } = options;
40
+ const data = ref([]);
41
+ const loading = ref(false);
42
+ const error = ref(null);
43
+ const getFilter = () => {
44
+ if (buildFilter4) {
45
+ return buildFilter4(options);
46
+ }
47
+ return baseFilter && Object.keys(baseFilter).length > 0 ? baseFilter : void 0;
48
+ };
49
+ async function fetch() {
50
+ loading.value = true;
51
+ error.value = null;
52
+ try {
53
+ const client = getSharedClient();
54
+ const queryOptions = {
55
+ filter: getFilter(),
56
+ limit
57
+ };
58
+ if (selectionSet) {
59
+ queryOptions.selectionSet = selectionSet;
60
+ }
61
+ const { data: responseData, errors } = await client.models[model].list(queryOptions);
62
+ if (errors) {
63
+ throw new Error(errors[0]?.message || `Failed to fetch ${model}`);
64
+ }
65
+ const items = responseData || [];
66
+ data.value = transform ? items.map(transform) : items;
67
+ } catch (e) {
68
+ error.value = e;
69
+ console.error(`[use${model}] Error fetching ${model}:`, e);
70
+ } finally {
71
+ loading.value = false;
72
+ }
73
+ }
74
+ const computedRefs = {};
75
+ if (computedProperties) {
76
+ for (const [key, fn] of Object.entries(computedProperties)) {
77
+ computedRefs[key] = computed(() => fn(data.value));
53
78
  }
54
- brands.value = data || [];
55
- } catch (e) {
56
- error.value = e;
57
- console.error("[useBrands] Error fetching brands:", e);
58
- } finally {
59
- loading.value = false;
60
79
  }
80
+ if (autoFetch) {
81
+ onMounted(() => {
82
+ fetch();
83
+ });
84
+ }
85
+ const result = {
86
+ data,
87
+ loading,
88
+ error,
89
+ refetch: fetch,
90
+ computed: computedRefs
91
+ };
92
+ if (dataPropertyName !== "data") {
93
+ result[dataPropertyName] = data;
94
+ }
95
+ for (const [key, computedRef] of Object.entries(computedRefs)) {
96
+ result[key] = computedRef;
97
+ }
98
+ return result;
99
+ };
100
+ }
101
+
102
+ // src/hooks/useBrands.ts
103
+ function buildFilter(options) {
104
+ let filter = options.filter || {};
105
+ if (options.accountId) {
106
+ filter = { ...filter, accountId: { eq: options.accountId } };
107
+ }
108
+ if (options.activeOnly) {
109
+ filter = { ...filter, status: { eq: "active" } };
61
110
  }
62
- if (autoFetch) {
63
- onMounted(() => {
64
- fetch();
65
- });
111
+ return Object.keys(filter).length > 0 ? filter : void 0;
112
+ }
113
+ var useBrandsInternal = createDataHook({
114
+ model: "Brand",
115
+ dataPropertyName: "brands",
116
+ buildFilter,
117
+ computedProperties: {
118
+ activeBrands: (brands) => brands.filter((b) => b.status === "active")
66
119
  }
120
+ });
121
+ function useBrands(options = {}) {
122
+ const result = useBrandsInternal(options);
67
123
  return {
68
- brands,
69
- activeBrands,
70
- loading,
71
- error,
72
- refetch: fetch
124
+ brands: result.brands,
125
+ activeBrands: result.activeBrands,
126
+ loading: result.loading,
127
+ error: result.error,
128
+ refetch: result.refetch
73
129
  };
74
130
  }
75
131
 
76
132
  // src/hooks/useAccounts.ts
77
- import { ref as ref2, onMounted as onMounted2 } from "vue";
133
+ var useAccountsInternal = createDataHook({
134
+ model: "Account",
135
+ dataPropertyName: "accounts"
136
+ });
78
137
  function useAccounts(options = {}) {
79
- const { filter, limit, autoFetch = true } = options;
80
- const accounts = ref2([]);
81
- const loading = ref2(false);
82
- const error = ref2(null);
83
- async function fetch() {
84
- loading.value = true;
85
- error.value = null;
86
- try {
87
- const client = generateClient();
88
- const { data, errors } = await client.models.Account.list({
89
- filter,
90
- limit
91
- });
92
- if (errors) {
93
- throw new Error(errors[0]?.message || "Failed to fetch accounts");
94
- }
95
- accounts.value = data || [];
96
- } catch (e) {
97
- error.value = e;
98
- console.error("[useAccounts] Error fetching accounts:", e);
99
- } finally {
100
- loading.value = false;
101
- }
102
- }
103
- if (autoFetch) {
104
- onMounted2(() => {
105
- fetch();
106
- });
107
- }
138
+ const result = useAccountsInternal(options);
108
139
  return {
109
- accounts,
110
- loading,
111
- error,
112
- refetch: fetch
140
+ accounts: result.accounts,
141
+ loading: result.loading,
142
+ error: result.error,
143
+ refetch: result.refetch
113
144
  };
114
145
  }
115
146
 
116
147
  // src/hooks/useUsers.ts
117
- import { ref as ref3, onMounted as onMounted3 } from "vue";
118
- function useUsers(options = {}) {
119
- const { filter: baseFilter, limit, autoFetch = true, brandId, accountId } = options;
120
- const users = ref3([]);
121
- const loading = ref3(false);
122
- const error = ref3(null);
123
- let filter = baseFilter || {};
124
- if (brandId) {
125
- filter = { ...filter, brandIds: { contains: brandId } };
126
- }
127
- if (accountId) {
128
- filter = { ...filter, accountIds: { contains: accountId } };
129
- }
130
- async function fetch() {
131
- loading.value = true;
132
- error.value = null;
133
- try {
134
- const client = generateClient();
135
- const { data, errors } = await client.models.User.list({
136
- filter: Object.keys(filter).length > 0 ? filter : void 0,
137
- limit
138
- });
139
- if (errors) {
140
- throw new Error(errors[0]?.message || "Failed to fetch users");
141
- }
142
- users.value = data || [];
143
- } catch (e) {
144
- error.value = e;
145
- console.error("[useUsers] Error fetching users:", e);
146
- } finally {
147
- loading.value = false;
148
- }
148
+ function buildFilter2(options) {
149
+ let filter = options.filter || {};
150
+ if (options.brandId) {
151
+ filter = { ...filter, brandIds: { contains: options.brandId } };
149
152
  }
150
- if (autoFetch) {
151
- onMounted3(() => {
152
- fetch();
153
- });
153
+ if (options.accountId) {
154
+ filter = { ...filter, accountIds: { contains: options.accountId } };
154
155
  }
156
+ return Object.keys(filter).length > 0 ? filter : void 0;
157
+ }
158
+ var useUsersInternal = createDataHook({
159
+ model: "User",
160
+ dataPropertyName: "users",
161
+ buildFilter: buildFilter2
162
+ });
163
+ function useUsers(options = {}) {
164
+ const result = useUsersInternal(options);
155
165
  return {
156
- users,
157
- loading,
158
- error,
159
- refetch: fetch
166
+ users: result.users,
167
+ loading: result.loading,
168
+ error: result.error,
169
+ refetch: result.refetch
160
170
  };
161
171
  }
162
172
 
163
173
  // src/hooks/useProducts.ts
164
- import { ref as ref4, computed as computed2, onMounted as onMounted4 } from "vue";
165
- function useProducts(options = {}) {
166
- const { filter: baseFilter, limit, autoFetch = true, activeOnly } = options;
167
- const products = ref4([]);
168
- const loading = ref4(false);
169
- const error = ref4(null);
170
- let filter = baseFilter || {};
171
- if (activeOnly) {
174
+ function buildFilter3(options) {
175
+ let filter = options.filter || {};
176
+ if (options.activeOnly) {
172
177
  filter = { ...filter, isActive: { eq: true } };
173
178
  }
174
- const activeProducts = computed2(
175
- () => products.value.filter((p) => p.isActive === true)
176
- );
177
- async function fetch() {
178
- loading.value = true;
179
- error.value = null;
180
- try {
181
- const client = generateClient();
182
- const { data, errors } = await client.models.Product.list({
183
- filter: Object.keys(filter).length > 0 ? filter : void 0,
184
- limit
185
- });
186
- if (errors) {
187
- throw new Error(errors[0]?.message || "Failed to fetch products");
188
- }
189
- products.value = data || [];
190
- } catch (e) {
191
- error.value = e;
192
- console.error("[useProducts] Error fetching products:", e);
193
- } finally {
194
- loading.value = false;
195
- }
196
- }
197
- if (autoFetch) {
198
- onMounted4(() => {
199
- fetch();
200
- });
179
+ return Object.keys(filter).length > 0 ? filter : void 0;
180
+ }
181
+ var useProductsInternal = createDataHook({
182
+ model: "Product",
183
+ dataPropertyName: "products",
184
+ buildFilter: buildFilter3,
185
+ computedProperties: {
186
+ activeProducts: (products) => products.filter((p) => p.isActive === true)
201
187
  }
188
+ });
189
+ function useProducts(options = {}) {
190
+ const result = useProductsInternal(options);
202
191
  return {
203
- products,
204
- activeProducts,
205
- loading,
206
- error,
207
- refetch: fetch
192
+ products: result.products,
193
+ activeProducts: result.activeProducts,
194
+ loading: result.loading,
195
+ error: result.error,
196
+ refetch: result.refetch
208
197
  };
209
198
  }
210
199
  export {
200
+ createDataHook,
201
+ resetSharedClient as resetClientInstance,
211
202
  useAccounts,
212
203
  useBrands,
213
204
  useProducts,