@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/useBrands.ts","../../src/client/index.ts","../../src/client/server.ts","../../src/hooks/useAccounts.ts","../../src/hooks/useUsers.ts","../../src/hooks/useProducts.ts"],"sourcesContent":["/**\n * useBrands Hook\n *\n * Vue composable for fetching and managing brand data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport { ref, computed, onMounted, type Ref, type ComputedRef } from \"vue\";\nimport { generateClient } from \"../client\";\nimport type { Brand } from \"@htlkg/core/types\";\n\nexport interface UseBrandsOptions {\n\t/** Filter criteria for brands */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n\t/** Filter by account ID */\n\taccountId?: string;\n\t/** Only active brands */\n\tactiveOnly?: boolean;\n}\n\nexport interface UseBrandsReturn {\n\t/** Reactive array of brands */\n\tbrands: Ref<Brand[]>;\n\t/** Computed array of active brands only */\n\tactiveBrands: ComputedRef<Brand[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch brands */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Composable for fetching and managing brands\n *\n * @example\n * ```typescript\n * import { useBrands } from '@htlkg/data/hooks';\n *\n * const { brands, loading, error, refetch } = useBrands();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { brands, loading } = useBrands({\n * accountId: 'account-123',\n * activeOnly: true,\n * limit: 50\n * });\n * ```\n */\nexport function useBrands(\n\toptions: UseBrandsOptions = {},\n): UseBrandsReturn {\n\tconst {\n\t\tfilter: baseFilter,\n\t\tlimit,\n\t\tautoFetch = true,\n\t\taccountId,\n\t\tactiveOnly,\n\t} = options;\n\n\t// State\n\tconst brands = ref<Brand[]>([]);\n\tconst loading = ref(false);\n\tconst error = ref<Error | null>(null);\n\n\t// Build filter\n\tlet filter: any = baseFilter || {};\n\n\tif (accountId) {\n\t\tfilter = { ...filter, accountId: { eq: accountId } };\n\t}\n\n\tif (activeOnly) {\n\t\tfilter = { ...filter, status: { eq: \"active\" } };\n\t}\n\n\t// Computed\n\tconst activeBrands = computed(() =>\n\t\tbrands.value.filter((b) => b.status === \"active\"),\n\t);\n\n\t// Fetch function\n\tasync function fetch() {\n\t\tloading.value = true;\n\t\terror.value = null;\n\n\t\ttry {\n\t\t\tconst client = generateClient();\n\t\t\tconst { data, errors } = await (client as any).models.Brand.list({\n\t\t\t\tfilter: Object.keys(filter).length > 0 ? filter : undefined,\n\t\t\t\tlimit,\n\t\t\t});\n\n\t\t\tif (errors) {\n\t\t\t\tthrow new Error(errors[0]?.message || \"Failed to fetch brands\");\n\t\t\t}\n\n\t\t\tbrands.value = (data || []) as Brand[];\n\t\t} catch (e) {\n\t\t\terror.value = e as Error;\n\t\t\tconsole.error(\"[useBrands] Error fetching brands:\", e);\n\t\t} finally {\n\t\t\tloading.value = false;\n\t\t}\n\t}\n\n\t// Auto-fetch on mount if enabled\n\tif (autoFetch) {\n\t\tonMounted(() => {\n\t\t\tfetch();\n\t\t});\n\t}\n\n\treturn {\n\t\tbrands,\n\t\tactiveBrands,\n\t\tloading,\n\t\terror,\n\t\trefetch: fetch,\n\t};\n}\n","/**\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","/**\n * useAccounts Hook\n *\n * Vue composable for fetching and managing account data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport { ref, onMounted, type Ref } from \"vue\";\nimport { generateClient } from \"../client\";\nimport type { Account } from \"@htlkg/core/types\";\n\nexport interface UseAccountsOptions {\n\t/** Filter criteria for accounts */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n}\n\nexport interface UseAccountsReturn {\n\t/** Reactive array of accounts */\n\taccounts: Ref<Account[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch accounts */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Composable for fetching and managing accounts\n *\n * @example\n * ```typescript\n * import { useAccounts } from '@htlkg/data/hooks';\n *\n * const { accounts, loading, error, refetch } = useAccounts();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { accounts, loading } = useAccounts({\n * filter: { status: { eq: 'active' } },\n * limit: 50\n * });\n * ```\n */\nexport function useAccounts(\n\toptions: UseAccountsOptions = {},\n): UseAccountsReturn {\n\tconst { filter, limit, autoFetch = true } = options;\n\n\t// State\n\tconst accounts = ref<Account[]>([]);\n\tconst loading = ref(false);\n\tconst error = ref<Error | null>(null);\n\n\t// Fetch function\n\tasync function fetch() {\n\t\tloading.value = true;\n\t\terror.value = null;\n\n\t\ttry {\n\t\t\tconst client = generateClient();\n\t\t\tconst { data, errors } = await (client as any).models.Account.list({\n\t\t\t\tfilter,\n\t\t\t\tlimit,\n\t\t\t});\n\n\t\t\tif (errors) {\n\t\t\t\tthrow new Error(errors[0]?.message || \"Failed to fetch accounts\");\n\t\t\t}\n\n\t\t\taccounts.value = (data || []) as Account[];\n\t\t} catch (e) {\n\t\t\terror.value = e as Error;\n\t\t\tconsole.error(\"[useAccounts] Error fetching accounts:\", e);\n\t\t} finally {\n\t\t\tloading.value = false;\n\t\t}\n\t}\n\n\t// Auto-fetch on mount if enabled\n\tif (autoFetch) {\n\t\tonMounted(() => {\n\t\t\tfetch();\n\t\t});\n\t}\n\n\treturn {\n\t\taccounts,\n\t\tloading,\n\t\terror,\n\t\trefetch: fetch,\n\t};\n}\n","/**\n * useUsers Hook\n *\n * Vue composable for fetching and managing user data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport { ref, onMounted, type Ref } from \"vue\";\nimport { generateClient } from \"../client\";\nimport type { User } from \"@htlkg/core/types\";\n\nexport interface UseUsersOptions {\n\t/** Filter criteria for users */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n\t/** Filter by brand ID */\n\tbrandId?: string;\n\t/** Filter by account ID */\n\taccountId?: string;\n}\n\nexport interface UseUsersReturn {\n\t/** Reactive array of users */\n\tusers: Ref<User[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch users */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Composable for fetching and managing users\n *\n * @example\n * ```typescript\n * import { useUsers } from '@htlkg/data/hooks';\n *\n * const { users, loading, error, refetch } = useUsers();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { users, loading } = useUsers({\n * brandId: 'brand-123',\n * accountId: 'account-456',\n * limit: 50\n * });\n * ```\n */\nexport function useUsers(options: UseUsersOptions = {}): UseUsersReturn {\n\tconst { filter: baseFilter, limit, autoFetch = true, brandId, accountId } = options;\n\n\t// State\n\tconst users = ref<User[]>([]);\n\tconst loading = ref(false);\n\tconst error = ref<Error | null>(null);\n\n\t// Build filter\n\tlet filter: any = baseFilter || {};\n\n\tif (brandId) {\n\t\tfilter = { ...filter, brandIds: { contains: brandId } };\n\t}\n\n\tif (accountId) {\n\t\tfilter = { ...filter, accountIds: { contains: accountId } };\n\t}\n\n\t// Fetch function\n\tasync function fetch() {\n\t\tloading.value = true;\n\t\terror.value = null;\n\n\t\ttry {\n\t\t\tconst client = generateClient();\n\t\t\tconst { data, errors } = await (client as any).models.User.list({\n\t\t\t\tfilter: Object.keys(filter).length > 0 ? filter : undefined,\n\t\t\t\tlimit,\n\t\t\t});\n\n\t\t\tif (errors) {\n\t\t\t\tthrow new Error(errors[0]?.message || \"Failed to fetch users\");\n\t\t\t}\n\n\t\t\tusers.value = (data || []) as User[];\n\t\t} catch (e) {\n\t\t\terror.value = e as Error;\n\t\t\tconsole.error(\"[useUsers] Error fetching users:\", e);\n\t\t} finally {\n\t\t\tloading.value = false;\n\t\t}\n\t}\n\n\t// Auto-fetch on mount if enabled\n\tif (autoFetch) {\n\t\tonMounted(() => {\n\t\t\tfetch();\n\t\t});\n\t}\n\n\treturn {\n\t\tusers,\n\t\tloading,\n\t\terror,\n\t\trefetch: fetch,\n\t};\n}\n","/**\n * useProducts Hook\n *\n * Vue composable for fetching and managing product data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport { ref, computed, onMounted, type Ref, type ComputedRef } from \"vue\";\nimport { generateClient } from \"../client\";\nimport type { Product } from \"@htlkg/core/types\";\n\nexport interface UseProductsOptions {\n\t/** Filter criteria for products */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n\t/** Only active products */\n\tactiveOnly?: boolean;\n}\n\nexport interface UseProductsReturn {\n\t/** Reactive array of products */\n\tproducts: Ref<Product[]>;\n\t/** Computed array of active products only */\n\tactiveProducts: ComputedRef<Product[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch products */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Composable for fetching and managing products\n *\n * @example\n * ```typescript\n * import { useProducts } from '@htlkg/data/hooks';\n *\n * const { products, loading, error, refetch } = useProducts();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { products, loading } = useProducts({\n * activeOnly: true,\n * limit: 50\n * });\n * ```\n */\nexport function useProducts(\n\toptions: UseProductsOptions = {},\n): UseProductsReturn {\n\tconst { filter: baseFilter, limit, autoFetch = true, activeOnly } = options;\n\n\t// State\n\tconst products = ref<Product[]>([]);\n\tconst loading = ref(false);\n\tconst error = ref<Error | null>(null);\n\n\t// Build filter\n\tlet filter: any = baseFilter || {};\n\n\tif (activeOnly) {\n\t\tfilter = { ...filter, isActive: { eq: true } };\n\t}\n\n\t// Computed\n\tconst activeProducts = computed(() =>\n\t\tproducts.value.filter((p) => p.isActive === true),\n\t);\n\n\t// Fetch function\n\tasync function fetch() {\n\t\tloading.value = true;\n\t\terror.value = null;\n\n\t\ttry {\n\t\t\tconst client = generateClient();\n\t\t\tconst { data, errors } = await (client as any).models.Product.list({\n\t\t\t\tfilter: Object.keys(filter).length > 0 ? filter : undefined,\n\t\t\t\tlimit,\n\t\t\t});\n\n\t\t\tif (errors) {\n\t\t\t\tthrow new Error(errors[0]?.message || \"Failed to fetch products\");\n\t\t\t}\n\n\t\t\tproducts.value = (data || []) as Product[];\n\t\t} catch (e) {\n\t\t\terror.value = e as Error;\n\t\t\tconsole.error(\"[useProducts] Error fetching products:\", e);\n\t\t} finally {\n\t\t\tloading.value = false;\n\t\t}\n\t}\n\n\t// Auto-fetch on mount if enabled\n\tif (autoFetch) {\n\t\tonMounted(() => {\n\t\t\tfetch();\n\t\t});\n\t}\n\n\treturn {\n\t\tproducts,\n\t\tactiveProducts,\n\t\tloading,\n\t\terror,\n\t\trefetch: fetch,\n\t};\n}\n"],"mappings":"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","names":["ref","onMounted","ref","onMounted","ref","onMounted","ref","onMounted","ref","computed","onMounted","ref","computed","onMounted"]}
1
+ {"version":3,"sources":["../../src/hooks/createDataHook.ts","../../src/client/index.ts","../../src/client/server.ts","../../src/hooks/useBrands.ts","../../src/hooks/useAccounts.ts","../../src/hooks/useUsers.ts","../../src/hooks/useProducts.ts"],"sourcesContent":["/**\n * Data Hook Factory\n *\n * Creates reusable Vue composables for fetching data from GraphQL models.\n * Provides a DRY approach to data fetching with consistent patterns.\n */\n\nimport { ref, computed, onMounted, type Ref, type ComputedRef } from \"vue\";\nimport { getSharedClient, resetSharedClient } from \"../client\";\n\n/**\n * Configuration options for creating a data hook\n */\nexport interface CreateDataHookOptions<T, TOptions extends BaseHookOptions = BaseHookOptions> {\n\t/** The GraphQL model name (e.g., 'Account', 'User', 'Brand') */\n\tmodel: string;\n\t/** Default limit for queries */\n\tdefaultLimit?: number;\n\t/** Selection set for the query (fields to fetch) */\n\tselectionSet?: string[];\n\t/** Transform function to apply to fetched data */\n\ttransform?: (item: any) => T;\n\t/** Build filter from hook options */\n\tbuildFilter?: (options: TOptions) => any;\n\t/** Define computed properties based on the data */\n\tcomputedProperties?: Record<string, (data: T[]) => any>;\n\t/** Plural name for the data property (e.g., 'accounts', 'users') */\n\tdataPropertyName?: string;\n}\n\n/**\n * Base options available to all hooks\n */\nexport interface BaseHookOptions {\n\t/** Filter criteria */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n}\n\n/**\n * Return type for data hooks\n */\nexport interface DataHookReturn<T, TComputed extends Record<string, any> = Record<string, never>> {\n\t/** Reactive array of data */\n\tdata: Ref<T[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch data */\n\trefetch: () => Promise<void>;\n\t/** Computed properties */\n\tcomputed: { [K in keyof TComputed]: ComputedRef<TComputed[K]> };\n}\n\n// Re-export resetSharedClient for testing convenience\nexport { resetSharedClient as resetClientInstance };\n\n/**\n * Creates a reusable data hook for a specific model\n *\n * @example\n * ```typescript\n * // Create a simple hook\n * export const useAccounts = createDataHook<Account>({\n * model: 'Account',\n * dataPropertyName: 'accounts',\n * });\n *\n * // Usage\n * const { data: accounts, loading, error, refetch } = useAccounts();\n * ```\n *\n * @example\n * ```typescript\n * // Create a hook with custom filters and computed properties\n * interface UseBrandsOptions extends BaseHookOptions {\n * accountId?: string;\n * activeOnly?: boolean;\n * }\n *\n * export const useBrands = createDataHook<Brand, UseBrandsOptions>({\n * model: 'Brand',\n * dataPropertyName: 'brands',\n * buildFilter: (options) => {\n * const filter: any = options.filter || {};\n * if (options.accountId) filter.accountId = { eq: options.accountId };\n * if (options.activeOnly) filter.status = { eq: 'active' };\n * return Object.keys(filter).length > 0 ? filter : undefined;\n * },\n * computedProperties: {\n * activeBrands: (brands) => brands.filter(b => b.status === 'active'),\n * },\n * });\n * ```\n */\nexport function createDataHook<\n\tT,\n\tTOptions extends BaseHookOptions = BaseHookOptions,\n\tTComputed extends Record<string, any> = Record<string, never>,\n>(\n\tconfig: CreateDataHookOptions<T, TOptions> & {\n\t\tcomputedProperties?: { [K in keyof TComputed]: (data: T[]) => TComputed[K] };\n\t},\n) {\n\tconst {\n\t\tmodel,\n\t\tdefaultLimit,\n\t\tselectionSet,\n\t\ttransform,\n\t\tbuildFilter,\n\t\tcomputedProperties,\n\t\tdataPropertyName = \"data\",\n\t} = config;\n\n\treturn function useData(options: TOptions = {} as TOptions): DataHookReturn<T, TComputed> & Record<string, any> {\n\t\tconst { filter: baseFilter, limit = defaultLimit, autoFetch = true } = options;\n\n\t\t// State\n\t\tconst data = ref<T[]>([]) as Ref<T[]>;\n\t\tconst loading = ref(false);\n\t\tconst error = ref<Error | null>(null);\n\n\t\t// Build filter using custom builder or default\n\t\tconst getFilter = () => {\n\t\t\tif (buildFilter) {\n\t\t\t\treturn buildFilter(options);\n\t\t\t}\n\t\t\treturn baseFilter && Object.keys(baseFilter).length > 0 ? baseFilter : undefined;\n\t\t};\n\n\t\t// Fetch function\n\t\tasync function fetch() {\n\t\t\tloading.value = true;\n\t\t\terror.value = null;\n\n\t\t\ttry {\n\t\t\t\tconst client = getSharedClient();\n\t\t\t\tconst queryOptions: any = {\n\t\t\t\t\tfilter: getFilter(),\n\t\t\t\t\tlimit,\n\t\t\t\t};\n\n\t\t\t\tif (selectionSet) {\n\t\t\t\t\tqueryOptions.selectionSet = selectionSet;\n\t\t\t\t}\n\n\t\t\t\tconst { data: responseData, errors } = await (client as any).models[model].list(queryOptions);\n\n\t\t\t\tif (errors) {\n\t\t\t\t\tthrow new Error(errors[0]?.message || `Failed to fetch ${model}`);\n\t\t\t\t}\n\n\t\t\t\tconst items = responseData || [];\n\t\t\t\tdata.value = transform ? items.map(transform) : items;\n\t\t\t} catch (e) {\n\t\t\t\terror.value = e as Error;\n\t\t\t\tconsole.error(`[use${model}] Error fetching ${model}:`, e);\n\t\t\t} finally {\n\t\t\t\tloading.value = false;\n\t\t\t}\n\t\t}\n\n\t\t// Create computed properties\n\t\tconst computedRefs: Record<string, ComputedRef<any>> = {};\n\t\tif (computedProperties) {\n\t\t\tfor (const [key, fn] of Object.entries(computedProperties)) {\n\t\t\t\tcomputedRefs[key] = computed(() => fn(data.value));\n\t\t\t}\n\t\t}\n\n\t\t// Auto-fetch on mount if enabled\n\t\tif (autoFetch) {\n\t\t\tonMounted(() => {\n\t\t\t\tfetch();\n\t\t\t});\n\t\t}\n\n\t\t// Build return object\n\t\tconst result: DataHookReturn<T, TComputed> & Record<string, any> = {\n\t\t\tdata,\n\t\t\tloading,\n\t\t\terror,\n\t\t\trefetch: fetch,\n\t\t\tcomputed: computedRefs as { [K in keyof TComputed]: ComputedRef<TComputed[K]> },\n\t\t};\n\n\t\t// Add data property with custom name for backwards compatibility\n\t\tif (dataPropertyName !== \"data\") {\n\t\t\tresult[dataPropertyName] = data;\n\t\t}\n\n\t\t// Add computed properties at top level for backwards compatibility\n\t\tfor (const [key, computedRef] of Object.entries(computedRefs)) {\n\t\t\tresult[key] = computedRef;\n\t\t}\n\n\t\treturn result;\n\t};\n}\n\n/**\n * Type helper to extract the return type of a created hook\n */\nexport type InferHookReturn<THook extends (...args: any[]) => any> = ReturnType<THook>;\n","/**\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","/**\n * useBrands Hook\n *\n * Vue composable for fetching and managing brand data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport type { Ref, ComputedRef } from \"vue\";\nimport type { Brand } from \"@htlkg/core/types\";\nimport { createDataHook, type BaseHookOptions } from \"./createDataHook\";\n\nexport interface UseBrandsOptions extends BaseHookOptions {\n\t/** Filter criteria for brands */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n\t/** Filter by account ID */\n\taccountId?: string;\n\t/** Only active brands */\n\tactiveOnly?: boolean;\n}\n\nexport interface UseBrandsReturn {\n\t/** Reactive array of brands */\n\tbrands: Ref<Brand[]>;\n\t/** Computed array of active brands only */\n\tactiveBrands: ComputedRef<Brand[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch brands */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Build filter from hook options\n */\nfunction buildFilter(options: UseBrandsOptions): any {\n\tlet filter: any = options.filter || {};\n\n\tif (options.accountId) {\n\t\tfilter = { ...filter, accountId: { eq: options.accountId } };\n\t}\n\n\tif (options.activeOnly) {\n\t\tfilter = { ...filter, status: { eq: \"active\" } };\n\t}\n\n\treturn Object.keys(filter).length > 0 ? filter : undefined;\n}\n\n/**\n * Internal hook created by factory\n */\nconst useBrandsInternal = createDataHook<Brand, UseBrandsOptions, { activeBrands: Brand[] }>({\n\tmodel: \"Brand\",\n\tdataPropertyName: \"brands\",\n\tbuildFilter,\n\tcomputedProperties: {\n\t\tactiveBrands: (brands) => brands.filter((b) => b.status === \"active\"),\n\t},\n});\n\n/**\n * Composable for fetching and managing brands\n *\n * @example\n * ```typescript\n * import { useBrands } from '@htlkg/data/hooks';\n *\n * const { brands, loading, error, refetch } = useBrands();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { brands, loading } = useBrands({\n * accountId: 'account-123',\n * activeOnly: true,\n * limit: 50\n * });\n * ```\n */\nexport function useBrands(options: UseBrandsOptions = {}): UseBrandsReturn {\n\tconst result = useBrandsInternal(options);\n\treturn {\n\t\tbrands: result.brands as Ref<Brand[]>,\n\t\tactiveBrands: result.activeBrands as ComputedRef<Brand[]>,\n\t\tloading: result.loading,\n\t\terror: result.error,\n\t\trefetch: result.refetch,\n\t};\n}\n","/**\n * useAccounts Hook\n *\n * Vue composable for fetching and managing account data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport type { Ref } from \"vue\";\nimport type { Account } from \"@htlkg/core/types\";\nimport { createDataHook, type BaseHookOptions } from \"./createDataHook\";\n\nexport interface UseAccountsOptions extends BaseHookOptions {\n\t/** Filter criteria for accounts */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n}\n\nexport interface UseAccountsReturn {\n\t/** Reactive array of accounts */\n\taccounts: Ref<Account[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch accounts */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Internal hook created by factory\n */\nconst useAccountsInternal = createDataHook<Account, UseAccountsOptions>({\n\tmodel: \"Account\",\n\tdataPropertyName: \"accounts\",\n});\n\n/**\n * Composable for fetching and managing accounts\n *\n * @example\n * ```typescript\n * import { useAccounts } from '@htlkg/data/hooks';\n *\n * const { accounts, loading, error, refetch } = useAccounts();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { accounts, loading } = useAccounts({\n * filter: { status: { eq: 'active' } },\n * limit: 50\n * });\n * ```\n */\nexport function useAccounts(options: UseAccountsOptions = {}): UseAccountsReturn {\n\tconst result = useAccountsInternal(options);\n\treturn {\n\t\taccounts: result.accounts as Ref<Account[]>,\n\t\tloading: result.loading,\n\t\terror: result.error,\n\t\trefetch: result.refetch,\n\t};\n}\n","/**\n * useUsers Hook\n *\n * Vue composable for fetching and managing user data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport type { Ref } from \"vue\";\nimport type { User } from \"@htlkg/core/types\";\nimport { createDataHook, type BaseHookOptions } from \"./createDataHook\";\n\nexport interface UseUsersOptions extends BaseHookOptions {\n\t/** Filter criteria for users */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n\t/** Filter by brand ID */\n\tbrandId?: string;\n\t/** Filter by account ID */\n\taccountId?: string;\n}\n\nexport interface UseUsersReturn {\n\t/** Reactive array of users */\n\tusers: Ref<User[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch users */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Build filter from hook options\n */\nfunction buildFilter(options: UseUsersOptions): any {\n\tlet filter: any = options.filter || {};\n\n\tif (options.brandId) {\n\t\tfilter = { ...filter, brandIds: { contains: options.brandId } };\n\t}\n\n\tif (options.accountId) {\n\t\tfilter = { ...filter, accountIds: { contains: options.accountId } };\n\t}\n\n\treturn Object.keys(filter).length > 0 ? filter : undefined;\n}\n\n/**\n * Internal hook created by factory\n */\nconst useUsersInternal = createDataHook<User, UseUsersOptions>({\n\tmodel: \"User\",\n\tdataPropertyName: \"users\",\n\tbuildFilter,\n});\n\n/**\n * Composable for fetching and managing users\n *\n * @example\n * ```typescript\n * import { useUsers } from '@htlkg/data/hooks';\n *\n * const { users, loading, error, refetch } = useUsers();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { users, loading } = useUsers({\n * brandId: 'brand-123',\n * accountId: 'account-456',\n * limit: 50\n * });\n * ```\n */\nexport function useUsers(options: UseUsersOptions = {}): UseUsersReturn {\n\tconst result = useUsersInternal(options);\n\treturn {\n\t\tusers: result.users as Ref<User[]>,\n\t\tloading: result.loading,\n\t\terror: result.error,\n\t\trefetch: result.refetch,\n\t};\n}\n","/**\n * useProducts Hook\n *\n * Vue composable for fetching and managing product data with reactive state.\n * Provides loading states, error handling, and refetch capabilities.\n */\n\nimport type { Ref, ComputedRef } from \"vue\";\nimport type { Product } from \"@htlkg/core/types\";\nimport { createDataHook, type BaseHookOptions } from \"./createDataHook\";\n\nexport interface UseProductsOptions extends BaseHookOptions {\n\t/** Filter criteria for products */\n\tfilter?: any;\n\t/** Limit number of results */\n\tlimit?: number;\n\t/** Auto-fetch on mount (default: true) */\n\tautoFetch?: boolean;\n\t/** Only active products */\n\tactiveOnly?: boolean;\n}\n\nexport interface UseProductsReturn {\n\t/** Reactive array of products */\n\tproducts: Ref<Product[]>;\n\t/** Computed array of active products only */\n\tactiveProducts: ComputedRef<Product[]>;\n\t/** Loading state */\n\tloading: Ref<boolean>;\n\t/** Error state */\n\terror: Ref<Error | null>;\n\t/** Refetch products */\n\trefetch: () => Promise<void>;\n}\n\n/**\n * Build filter from hook options\n */\nfunction buildFilter(options: UseProductsOptions): any {\n\tlet filter: any = options.filter || {};\n\n\tif (options.activeOnly) {\n\t\tfilter = { ...filter, isActive: { eq: true } };\n\t}\n\n\treturn Object.keys(filter).length > 0 ? filter : undefined;\n}\n\n/**\n * Internal hook created by factory\n */\nconst useProductsInternal = createDataHook<Product, UseProductsOptions, { activeProducts: Product[] }>({\n\tmodel: \"Product\",\n\tdataPropertyName: \"products\",\n\tbuildFilter,\n\tcomputedProperties: {\n\t\tactiveProducts: (products) => products.filter((p) => p.isActive === true),\n\t},\n});\n\n/**\n * Composable for fetching and managing products\n *\n * @example\n * ```typescript\n * import { useProducts } from '@htlkg/data/hooks';\n *\n * const { products, loading, error, refetch } = useProducts();\n * ```\n *\n * @example With filters\n * ```typescript\n * const { products, loading } = useProducts({\n * activeOnly: true,\n * limit: 50\n * });\n * ```\n */\nexport function useProducts(options: UseProductsOptions = {}): UseProductsReturn {\n\tconst result = useProductsInternal(options);\n\treturn {\n\t\tproducts: result.products as Ref<Product[]>,\n\t\tactiveProducts: result.activeProducts as ComputedRef<Product[]>,\n\t\tloading: result.loading,\n\t\terror: result.error,\n\t\trefetch: result.refetch,\n\t};\n}\n"],"mappings":"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","names":["buildFilter","buildFilter","buildFilter"]}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- export { AstroAmplifyConfig, generateClient, generateServerClient } from './client/index.js';
1
+ export { AstroAmplifyConfig, generateClient, generateServerClient, getSharedClient, resetSharedClient as resetClientInstance, resetSharedClient } from './client/index.js';
2
2
  export { executePublicQuery, executeServerQuery, getAccount, getAccountWithBrands, getBrand, getBrandWithProducts, getProduct, getProductInstance, getUser, getUserByCognitoId, getUserByEmail, listAccounts, listActiveBrands, listActiveProducts, listActiveUsers, listBrands, listBrandsByAccount, listEnabledProductInstancesByBrand, listProductInstancesByAccount, listProductInstancesByBrand, listProducts, listUsers, listUsersByAccount } from './queries/index.js';
3
3
  export { CreateAccountInput, CreateBrandInput, CreateUserInput, UpdateAccountInput, UpdateBrandInput, UpdateUserInput, createAccount, createBrand, createUser, deleteAccount, deleteBrand, deleteUser, updateAccount, updateBrand, updateUser } from './mutations/index.js';
4
- export { UseAccountsOptions, UseAccountsReturn, UseBrandsOptions, UseBrandsReturn, UseProductsOptions, UseProductsReturn, UseUsersOptions, UseUsersReturn, useAccounts, useBrands, useProducts, useUsers } from './hooks/index.js';
4
+ export { BaseHookOptions, CreateDataHookOptions, DataHookReturn, InferHookReturn, UseAccountsOptions, UseAccountsReturn, UseBrandsOptions, UseBrandsReturn, UseProductsOptions, UseProductsReturn, UseUsersOptions, UseUsersReturn, createDataHook, useAccounts, useBrands, useProducts, useUsers } from './hooks/index.js';
5
+ export { InferStoreType, ResourceStores, StoreConfig, createResourceStores, createSingleStore, createStore } from './stores/index.js';
5
6
  import 'aws-amplify/data';
6
7
  import 'aws-amplify';
7
8
  import 'astro';
8
9
  import 'aws-amplify/api/internals';
9
10
  import '@htlkg/core/types';
10
11
  import 'vue';
12
+ import 'nanostores';
package/dist/index.js CHANGED
@@ -10,6 +10,16 @@ import { createRunWithAmplifyServerContext, createLogger } from "@htlkg/core/amp
10
10
  var log = createLogger("server-client");
11
11
 
12
12
  // src/client/index.ts
13
+ var sharedClientInstance = null;
14
+ function getSharedClient() {
15
+ if (!sharedClientInstance) {
16
+ sharedClientInstance = generateDataClient();
17
+ }
18
+ return sharedClientInstance;
19
+ }
20
+ function resetSharedClient() {
21
+ sharedClientInstance = null;
22
+ }
13
23
  function generateClient() {
14
24
  return generateDataClient();
15
25
  }
@@ -498,200 +508,289 @@ async function deleteUser(client, id) {
498
508
  }
499
509
  }
500
510
 
501
- // src/hooks/useBrands.ts
511
+ // src/hooks/createDataHook.ts
502
512
  import { ref, computed, onMounted } from "vue";
503
- function useBrands(options = {}) {
513
+ function createDataHook(config) {
504
514
  const {
505
- filter: baseFilter,
506
- limit,
507
- autoFetch = true,
508
- accountId,
509
- activeOnly
510
- } = options;
511
- const brands = ref([]);
512
- const loading = ref(false);
513
- const error = ref(null);
514
- let filter = baseFilter || {};
515
- if (accountId) {
516
- filter = { ...filter, accountId: { eq: accountId } };
517
- }
518
- if (activeOnly) {
519
- filter = { ...filter, status: { eq: "active" } };
520
- }
521
- const activeBrands = computed(
522
- () => brands.value.filter((b) => b.status === "active")
523
- );
524
- async function fetch() {
525
- loading.value = true;
526
- error.value = null;
527
- try {
528
- const client = generateClient();
529
- const { data, errors } = await client.models.Brand.list({
530
- filter: Object.keys(filter).length > 0 ? filter : void 0,
531
- limit
532
- });
533
- if (errors) {
534
- throw new Error(errors[0]?.message || "Failed to fetch brands");
515
+ model,
516
+ defaultLimit,
517
+ selectionSet,
518
+ transform,
519
+ buildFilter: buildFilter4,
520
+ computedProperties,
521
+ dataPropertyName = "data"
522
+ } = config;
523
+ return function useData(options = {}) {
524
+ const { filter: baseFilter, limit = defaultLimit, autoFetch = true } = options;
525
+ const data = ref([]);
526
+ const loading = ref(false);
527
+ const error = ref(null);
528
+ const getFilter = () => {
529
+ if (buildFilter4) {
530
+ return buildFilter4(options);
531
+ }
532
+ return baseFilter && Object.keys(baseFilter).length > 0 ? baseFilter : void 0;
533
+ };
534
+ async function fetch() {
535
+ loading.value = true;
536
+ error.value = null;
537
+ try {
538
+ const client = getSharedClient();
539
+ const queryOptions = {
540
+ filter: getFilter(),
541
+ limit
542
+ };
543
+ if (selectionSet) {
544
+ queryOptions.selectionSet = selectionSet;
545
+ }
546
+ const { data: responseData, errors } = await client.models[model].list(queryOptions);
547
+ if (errors) {
548
+ throw new Error(errors[0]?.message || `Failed to fetch ${model}`);
549
+ }
550
+ const items = responseData || [];
551
+ data.value = transform ? items.map(transform) : items;
552
+ } catch (e) {
553
+ error.value = e;
554
+ console.error(`[use${model}] Error fetching ${model}:`, e);
555
+ } finally {
556
+ loading.value = false;
557
+ }
558
+ }
559
+ const computedRefs = {};
560
+ if (computedProperties) {
561
+ for (const [key, fn] of Object.entries(computedProperties)) {
562
+ computedRefs[key] = computed(() => fn(data.value));
535
563
  }
536
- brands.value = data || [];
537
- } catch (e) {
538
- error.value = e;
539
- console.error("[useBrands] Error fetching brands:", e);
540
- } finally {
541
- loading.value = false;
542
564
  }
565
+ if (autoFetch) {
566
+ onMounted(() => {
567
+ fetch();
568
+ });
569
+ }
570
+ const result = {
571
+ data,
572
+ loading,
573
+ error,
574
+ refetch: fetch,
575
+ computed: computedRefs
576
+ };
577
+ if (dataPropertyName !== "data") {
578
+ result[dataPropertyName] = data;
579
+ }
580
+ for (const [key, computedRef] of Object.entries(computedRefs)) {
581
+ result[key] = computedRef;
582
+ }
583
+ return result;
584
+ };
585
+ }
586
+
587
+ // src/hooks/useBrands.ts
588
+ function buildFilter(options) {
589
+ let filter = options.filter || {};
590
+ if (options.accountId) {
591
+ filter = { ...filter, accountId: { eq: options.accountId } };
543
592
  }
544
- if (autoFetch) {
545
- onMounted(() => {
546
- fetch();
547
- });
593
+ if (options.activeOnly) {
594
+ filter = { ...filter, status: { eq: "active" } };
548
595
  }
596
+ return Object.keys(filter).length > 0 ? filter : void 0;
597
+ }
598
+ var useBrandsInternal = createDataHook({
599
+ model: "Brand",
600
+ dataPropertyName: "brands",
601
+ buildFilter,
602
+ computedProperties: {
603
+ activeBrands: (brands) => brands.filter((b) => b.status === "active")
604
+ }
605
+ });
606
+ function useBrands(options = {}) {
607
+ const result = useBrandsInternal(options);
549
608
  return {
550
- brands,
551
- activeBrands,
552
- loading,
553
- error,
554
- refetch: fetch
609
+ brands: result.brands,
610
+ activeBrands: result.activeBrands,
611
+ loading: result.loading,
612
+ error: result.error,
613
+ refetch: result.refetch
555
614
  };
556
615
  }
557
616
 
558
617
  // src/hooks/useAccounts.ts
559
- import { ref as ref2, onMounted as onMounted2 } from "vue";
618
+ var useAccountsInternal = createDataHook({
619
+ model: "Account",
620
+ dataPropertyName: "accounts"
621
+ });
560
622
  function useAccounts(options = {}) {
561
- const { filter, limit, autoFetch = true } = options;
562
- const accounts = ref2([]);
563
- const loading = ref2(false);
564
- const error = ref2(null);
565
- async function fetch() {
566
- loading.value = true;
567
- error.value = null;
568
- try {
569
- const client = generateClient();
570
- const { data, errors } = await client.models.Account.list({
571
- filter,
572
- limit
573
- });
574
- if (errors) {
575
- throw new Error(errors[0]?.message || "Failed to fetch accounts");
576
- }
577
- accounts.value = data || [];
578
- } catch (e) {
579
- error.value = e;
580
- console.error("[useAccounts] Error fetching accounts:", e);
581
- } finally {
582
- loading.value = false;
583
- }
584
- }
585
- if (autoFetch) {
586
- onMounted2(() => {
587
- fetch();
588
- });
589
- }
623
+ const result = useAccountsInternal(options);
590
624
  return {
591
- accounts,
592
- loading,
593
- error,
594
- refetch: fetch
625
+ accounts: result.accounts,
626
+ loading: result.loading,
627
+ error: result.error,
628
+ refetch: result.refetch
595
629
  };
596
630
  }
597
631
 
598
632
  // src/hooks/useUsers.ts
599
- import { ref as ref3, onMounted as onMounted3 } from "vue";
600
- function useUsers(options = {}) {
601
- const { filter: baseFilter, limit, autoFetch = true, brandId, accountId } = options;
602
- const users = ref3([]);
603
- const loading = ref3(false);
604
- const error = ref3(null);
605
- let filter = baseFilter || {};
606
- if (brandId) {
607
- filter = { ...filter, brandIds: { contains: brandId } };
608
- }
609
- if (accountId) {
610
- filter = { ...filter, accountIds: { contains: accountId } };
611
- }
612
- async function fetch() {
613
- loading.value = true;
614
- error.value = null;
615
- try {
616
- const client = generateClient();
617
- const { data, errors } = await client.models.User.list({
618
- filter: Object.keys(filter).length > 0 ? filter : void 0,
619
- limit
620
- });
621
- if (errors) {
622
- throw new Error(errors[0]?.message || "Failed to fetch users");
623
- }
624
- users.value = data || [];
625
- } catch (e) {
626
- error.value = e;
627
- console.error("[useUsers] Error fetching users:", e);
628
- } finally {
629
- loading.value = false;
630
- }
633
+ function buildFilter2(options) {
634
+ let filter = options.filter || {};
635
+ if (options.brandId) {
636
+ filter = { ...filter, brandIds: { contains: options.brandId } };
631
637
  }
632
- if (autoFetch) {
633
- onMounted3(() => {
634
- fetch();
635
- });
638
+ if (options.accountId) {
639
+ filter = { ...filter, accountIds: { contains: options.accountId } };
636
640
  }
641
+ return Object.keys(filter).length > 0 ? filter : void 0;
642
+ }
643
+ var useUsersInternal = createDataHook({
644
+ model: "User",
645
+ dataPropertyName: "users",
646
+ buildFilter: buildFilter2
647
+ });
648
+ function useUsers(options = {}) {
649
+ const result = useUsersInternal(options);
637
650
  return {
638
- users,
639
- loading,
640
- error,
641
- refetch: fetch
651
+ users: result.users,
652
+ loading: result.loading,
653
+ error: result.error,
654
+ refetch: result.refetch
642
655
  };
643
656
  }
644
657
 
645
658
  // src/hooks/useProducts.ts
646
- import { ref as ref4, computed as computed2, onMounted as onMounted4 } from "vue";
647
- function useProducts(options = {}) {
648
- const { filter: baseFilter, limit, autoFetch = true, activeOnly } = options;
649
- const products = ref4([]);
650
- const loading = ref4(false);
651
- const error = ref4(null);
652
- let filter = baseFilter || {};
653
- if (activeOnly) {
659
+ function buildFilter3(options) {
660
+ let filter = options.filter || {};
661
+ if (options.activeOnly) {
654
662
  filter = { ...filter, isActive: { eq: true } };
655
663
  }
656
- const activeProducts = computed2(
657
- () => products.value.filter((p) => p.isActive === true)
658
- );
659
- async function fetch() {
660
- loading.value = true;
661
- error.value = null;
662
- try {
663
- const client = generateClient();
664
- const { data, errors } = await client.models.Product.list({
665
- filter: Object.keys(filter).length > 0 ? filter : void 0,
666
- limit
667
- });
668
- if (errors) {
669
- throw new Error(errors[0]?.message || "Failed to fetch products");
670
- }
671
- products.value = data || [];
672
- } catch (e) {
673
- error.value = e;
674
- console.error("[useProducts] Error fetching products:", e);
675
- } finally {
676
- loading.value = false;
677
- }
678
- }
679
- if (autoFetch) {
680
- onMounted4(() => {
681
- fetch();
682
- });
664
+ return Object.keys(filter).length > 0 ? filter : void 0;
665
+ }
666
+ var useProductsInternal = createDataHook({
667
+ model: "Product",
668
+ dataPropertyName: "products",
669
+ buildFilter: buildFilter3,
670
+ computedProperties: {
671
+ activeProducts: (products) => products.filter((p) => p.isActive === true)
683
672
  }
673
+ });
674
+ function useProducts(options = {}) {
675
+ const result = useProductsInternal(options);
684
676
  return {
685
- products,
686
- activeProducts,
687
- loading,
688
- error,
689
- refetch: fetch
677
+ products: result.products,
678
+ activeProducts: result.activeProducts,
679
+ loading: result.loading,
680
+ error: result.error,
681
+ refetch: result.refetch
682
+ };
683
+ }
684
+
685
+ // ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/clean-stores/index.js
686
+ var clean = /* @__PURE__ */ Symbol("clean");
687
+
688
+ // ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/atom/index.js
689
+ var listenerQueue = [];
690
+ var lqIndex = 0;
691
+ var QUEUE_ITEMS_PER_LISTENER = 4;
692
+ var epoch = 0;
693
+ var atom = /* @__NO_SIDE_EFFECTS__ */ (initialValue) => {
694
+ let listeners = [];
695
+ let $atom = {
696
+ get() {
697
+ if (!$atom.lc) {
698
+ $atom.listen(() => {
699
+ })();
700
+ }
701
+ return $atom.value;
702
+ },
703
+ lc: 0,
704
+ listen(listener) {
705
+ $atom.lc = listeners.push(listener);
706
+ return () => {
707
+ for (let i = lqIndex + QUEUE_ITEMS_PER_LISTENER; i < listenerQueue.length; ) {
708
+ if (listenerQueue[i] === listener) {
709
+ listenerQueue.splice(i, QUEUE_ITEMS_PER_LISTENER);
710
+ } else {
711
+ i += QUEUE_ITEMS_PER_LISTENER;
712
+ }
713
+ }
714
+ let index = listeners.indexOf(listener);
715
+ if (~index) {
716
+ listeners.splice(index, 1);
717
+ if (!--$atom.lc) $atom.off();
718
+ }
719
+ };
720
+ },
721
+ notify(oldValue, changedKey) {
722
+ epoch++;
723
+ let runListenerQueue = !listenerQueue.length;
724
+ for (let listener of listeners) {
725
+ listenerQueue.push(listener, $atom.value, oldValue, changedKey);
726
+ }
727
+ if (runListenerQueue) {
728
+ for (lqIndex = 0; lqIndex < listenerQueue.length; lqIndex += QUEUE_ITEMS_PER_LISTENER) {
729
+ listenerQueue[lqIndex](
730
+ listenerQueue[lqIndex + 1],
731
+ listenerQueue[lqIndex + 2],
732
+ listenerQueue[lqIndex + 3]
733
+ );
734
+ }
735
+ listenerQueue.length = 0;
736
+ }
737
+ },
738
+ /* It will be called on last listener unsubscribing.
739
+ We will redefine it in onMount and onStop. */
740
+ off() {
741
+ },
742
+ set(newValue) {
743
+ let oldValue = $atom.value;
744
+ if (oldValue !== newValue) {
745
+ $atom.value = newValue;
746
+ $atom.notify(oldValue);
747
+ }
748
+ },
749
+ subscribe(listener) {
750
+ let unbind = $atom.listen(listener);
751
+ listener($atom.value);
752
+ return unbind;
753
+ },
754
+ value: initialValue
690
755
  };
756
+ if (process.env.NODE_ENV !== "production") {
757
+ $atom[clean] = () => {
758
+ listeners = [];
759
+ $atom.lc = 0;
760
+ $atom.off();
761
+ };
762
+ }
763
+ return $atom;
764
+ };
765
+
766
+ // src/stores/createStores.ts
767
+ function capitalize(str) {
768
+ return str.charAt(0).toUpperCase() + str.slice(1);
769
+ }
770
+ function createResourceStores(shared, config) {
771
+ const { name, relations = [] } = config;
772
+ const stores = {};
773
+ stores.$data = shared(name, atom([]));
774
+ for (const relation of relations) {
775
+ const storeKey = `$${relation}`;
776
+ const sharedKey = `${name}${capitalize(relation)}`;
777
+ stores[storeKey] = shared(sharedKey, atom([]));
778
+ }
779
+ return stores;
780
+ }
781
+ function createStore(shared, name, defaultValue = []) {
782
+ return shared(name, atom(defaultValue));
783
+ }
784
+ function createSingleStore(shared, name, defaultValue) {
785
+ return shared(name, atom(defaultValue));
691
786
  }
692
787
  export {
693
788
  createAccount,
694
789
  createBrand,
790
+ createDataHook,
791
+ createResourceStores,
792
+ createSingleStore,
793
+ createStore,
695
794
  createUser,
696
795
  deleteAccount,
697
796
  deleteBrand,
@@ -706,6 +805,7 @@ export {
706
805
  getBrandWithProducts,
707
806
  getProduct,
708
807
  getProductInstance,
808
+ getSharedClient,
709
809
  getUser,
710
810
  getUserByCognitoId,
711
811
  getUserByEmail,
@@ -721,6 +821,8 @@ export {
721
821
  listProducts,
722
822
  listUsers,
723
823
  listUsersByAccount,
824
+ resetSharedClient as resetClientInstance,
825
+ resetSharedClient,
724
826
  updateAccount,
725
827
  updateBrand,
726
828
  updateUser,