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