@htlkg/data 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/client/index.d.ts +16 -1
- package/dist/client/index.js +13 -1
- package/dist/client/index.js.map +1 -1
- package/dist/hooks/index.d.ts +113 -5
- package/dist/hooks/index.js +155 -164
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +263 -161
- package/dist/index.js.map +1 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/stores/index.d.ts +106 -0
- package/dist/stores/index.js +108 -0
- package/dist/stores/index.js.map +1 -0
- package/package.json +31 -8
- package/src/client/__tests__/server.test.ts +100 -0
- package/src/client/client.md +91 -0
- package/src/client/index.test.ts +232 -0
- package/src/client/index.ts +145 -0
- package/src/client/server.ts +118 -0
- package/src/content-collections/content-collections.md +87 -0
- package/src/content-collections/generator.ts +314 -0
- package/src/content-collections/index.ts +32 -0
- package/src/content-collections/schemas.ts +75 -0
- package/src/content-collections/sync.ts +139 -0
- package/src/hooks/README.md +293 -0
- package/src/hooks/createDataHook.ts +208 -0
- package/src/hooks/data-hook-errors.property.test.ts +270 -0
- package/src/hooks/data-hook-filters.property.test.ts +263 -0
- package/src/hooks/data-hooks.property.test.ts +190 -0
- package/src/hooks/hooks.test.ts +76 -0
- package/src/hooks/index.ts +21 -0
- package/src/hooks/useAccounts.ts +66 -0
- package/src/hooks/useBrands.ts +95 -0
- package/src/hooks/useProducts.ts +88 -0
- package/src/hooks/useUsers.ts +89 -0
- package/src/index.ts +32 -0
- package/src/mutations/accounts.ts +127 -0
- package/src/mutations/brands.ts +133 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/mutations.md +96 -0
- package/src/mutations/users.ts +136 -0
- package/src/queries/accounts.ts +121 -0
- package/src/queries/brands.ts +176 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/products.ts +282 -0
- package/src/queries/queries.md +88 -0
- package/src/queries/server-helpers.ts +114 -0
- package/src/queries/users.ts +199 -0
- package/src/stores/createStores.ts +148 -0
- package/src/stores/index.ts +15 -0
- package/src/stores/stores.md +104 -0
package/dist/hooks/index.js.map
CHANGED
|
@@ -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/
|
|
511
|
+
// src/hooks/createDataHook.ts
|
|
502
512
|
import { ref, computed, onMounted } from "vue";
|
|
503
|
-
function
|
|
513
|
+
function createDataHook(config) {
|
|
504
514
|
const {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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 (
|
|
545
|
-
|
|
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:
|
|
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
|
-
|
|
618
|
+
var useAccountsInternal = createDataHook({
|
|
619
|
+
model: "Account",
|
|
620
|
+
dataPropertyName: "accounts"
|
|
621
|
+
});
|
|
560
622
|
function useAccounts(options = {}) {
|
|
561
|
-
const
|
|
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:
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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 (
|
|
633
|
-
|
|
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:
|
|
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
|
-
|
|
647
|
-
|
|
648
|
-
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
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:
|
|
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,
|