@htlkg/data 0.0.14 → 0.0.16
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 +72 -0
- package/dist/client/index.d.ts +123 -30
- package/dist/client/index.js +75 -1
- package/dist/client/index.js.map +1 -1
- package/dist/hooks/index.d.ts +76 -2
- package/dist/hooks/index.js +224 -6
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +550 -7
- package/dist/index.js.map +1 -1
- package/dist/mutations/index.d.ts +149 -5
- package/dist/mutations/index.js +397 -0
- package/dist/mutations/index.js.map +1 -1
- package/dist/productInstances-CzT3NZKU.d.ts +98 -0
- package/dist/queries/index.d.ts +54 -2
- package/dist/queries/index.js +60 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/server/index.d.ts +47 -0
- package/dist/server/index.js +59 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +5 -1
- package/src/client/index.ts +82 -3
- package/src/client/proxy.ts +170 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useProductInstances.ts +174 -0
- package/src/index.ts +11 -0
- package/src/mutations/accounts.ts +102 -1
- package/src/mutations/brands.ts +102 -1
- package/src/mutations/index.ts +23 -0
- package/src/mutations/productInstances/index.ts +14 -0
- package/src/mutations/productInstances/productInstances.integration.test.ts +621 -0
- package/src/mutations/productInstances/productInstances.test.ts +680 -0
- package/src/mutations/productInstances/productInstances.ts +280 -0
- package/src/mutations/systemSettings.ts +130 -0
- package/src/mutations/users.ts +102 -1
- package/src/queries/index.ts +9 -0
- package/src/queries/systemSettings.ts +115 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AstroGlobal } from 'astro';
|
|
2
|
+
import { ResourcesConfig } from 'aws-amplify';
|
|
3
|
+
import { CommonPublicClientOptions, DefaultCommonClientOptions, V6ClientSSRCookies } from 'aws-amplify/api/internals';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Server-side data client for Astro
|
|
7
|
+
*
|
|
8
|
+
* Provides a client generator similar to Next.js's generateServerClientUsingCookies
|
|
9
|
+
* using generateClientWithAmplifyInstance for proper server context integration.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface AstroCookiesClientParams {
|
|
13
|
+
cookies: AstroGlobal["cookies"];
|
|
14
|
+
request: AstroGlobal["request"];
|
|
15
|
+
config: ResourcesConfig;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generates a server-side data client for Astro (matches Next.js implementation)
|
|
19
|
+
*
|
|
20
|
+
* This function creates a client that automatically wraps all operations in the Amplify server context,
|
|
21
|
+
* ensuring that authentication tokens from cookies are properly used.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import type { Schema } from '../amplify/data/resource';
|
|
26
|
+
* import { generateServerClientUsingCookies } from '@htlkg/data/client';
|
|
27
|
+
* import { parseAmplifyConfig } from 'aws-amplify/utils';
|
|
28
|
+
* import outputs from '../amplify_outputs.json';
|
|
29
|
+
*
|
|
30
|
+
* const amplifyConfig = parseAmplifyConfig(outputs);
|
|
31
|
+
*
|
|
32
|
+
* const client = generateServerClientUsingCookies<Schema>({
|
|
33
|
+
* config: amplifyConfig,
|
|
34
|
+
* cookies: Astro.cookies,
|
|
35
|
+
* request: Astro.request,
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Use the client directly - operations are automatically wrapped
|
|
39
|
+
* const result = await client.models.User.list({
|
|
40
|
+
* selectionSet: ['id', 'email'],
|
|
41
|
+
* limit: 100,
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
declare function generateServerClientUsingCookies<T extends Record<any, any> = never, Options extends CommonPublicClientOptions & AstroCookiesClientParams = DefaultCommonClientOptions & AstroCookiesClientParams>(options: Options): V6ClientSSRCookies<T, Options>;
|
|
46
|
+
|
|
47
|
+
export { generateServerClientUsingCookies };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/client/server.ts
|
|
2
|
+
import {
|
|
3
|
+
generateClientWithAmplifyInstance
|
|
4
|
+
} from "aws-amplify/api/internals";
|
|
5
|
+
import { getAmplifyServerContext } from "aws-amplify/adapter-core/internals";
|
|
6
|
+
import { createRunWithAmplifyServerContext, createLogger } from "@htlkg/core/amplify-astro-adapter";
|
|
7
|
+
var log = createLogger("server-client");
|
|
8
|
+
function generateServerClientUsingCookies(options) {
|
|
9
|
+
const runWithAmplifyServerContext = createRunWithAmplifyServerContext({
|
|
10
|
+
config: options.config
|
|
11
|
+
});
|
|
12
|
+
const resourcesConfig = options.config;
|
|
13
|
+
const getAmplify = (fn) => {
|
|
14
|
+
return runWithAmplifyServerContext({
|
|
15
|
+
astroServerContext: {
|
|
16
|
+
cookies: options.cookies,
|
|
17
|
+
request: options.request
|
|
18
|
+
},
|
|
19
|
+
operation: async (contextSpec) => {
|
|
20
|
+
const amplifyInstance = getAmplifyServerContext(contextSpec).amplify;
|
|
21
|
+
try {
|
|
22
|
+
const config = amplifyInstance.getConfig();
|
|
23
|
+
log.debug("Amplify config from instance:", {
|
|
24
|
+
hasAPI: !!config.API,
|
|
25
|
+
hasGraphQL: !!config.API?.GraphQL,
|
|
26
|
+
endpoint: config.API?.GraphQL?.endpoint,
|
|
27
|
+
defaultAuthMode: config.API?.GraphQL?.defaultAuthMode,
|
|
28
|
+
region: config.API?.GraphQL?.region
|
|
29
|
+
});
|
|
30
|
+
const session = await amplifyInstance.Auth.fetchAuthSession();
|
|
31
|
+
log.debug("Auth session:", {
|
|
32
|
+
hasTokens: !!session.tokens,
|
|
33
|
+
hasAccessToken: !!session.tokens?.accessToken,
|
|
34
|
+
hasIdToken: !!session.tokens?.idToken,
|
|
35
|
+
hasCredentials: !!session.credentials
|
|
36
|
+
});
|
|
37
|
+
} catch (e) {
|
|
38
|
+
log.debug("Error fetching session:", e.message);
|
|
39
|
+
}
|
|
40
|
+
return fn(amplifyInstance);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const {
|
|
45
|
+
cookies: _cookies,
|
|
46
|
+
request: _request,
|
|
47
|
+
config: _config,
|
|
48
|
+
...params
|
|
49
|
+
} = options;
|
|
50
|
+
return generateClientWithAmplifyInstance({
|
|
51
|
+
amplify: getAmplify,
|
|
52
|
+
config: resourcesConfig,
|
|
53
|
+
...params
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
generateServerClientUsingCookies
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/server.ts"],"sourcesContent":["/**\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":";AASA;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;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@htlkg/data",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"import": "./dist/client/index.js",
|
|
12
12
|
"types": "./dist/client/index.d.ts"
|
|
13
13
|
},
|
|
14
|
+
"./server": {
|
|
15
|
+
"import": "./dist/server/index.js",
|
|
16
|
+
"types": "./dist/server/index.d.ts"
|
|
17
|
+
},
|
|
14
18
|
"./queries": {
|
|
15
19
|
"import": "./dist/queries/index.js",
|
|
16
20
|
"types": "./dist/queries/index.d.ts"
|
package/src/client/index.ts
CHANGED
|
@@ -3,14 +3,36 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides both client-side and server-side GraphQL capabilities using AWS Amplify Data.
|
|
5
5
|
* The server-side functions use the Amplify Astro adapter for proper SSR support.
|
|
6
|
+
*
|
|
7
|
+
* For server-side usage, use `Astro.locals.amplifyClient` (zero-config, injected by middleware).
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import { generateClient as generateDataClient } from "aws-amplify/data";
|
|
11
|
+
import { Amplify } from "aws-amplify";
|
|
9
12
|
import type { ResourcesConfig } from "aws-amplify";
|
|
13
|
+
import { generateServerClientUsingCookies } from "./server";
|
|
10
14
|
|
|
11
|
-
// Re-export server-side client generation
|
|
15
|
+
// Re-export server-side client generation (used internally by middleware)
|
|
12
16
|
export { generateServerClientUsingCookies } from "./server";
|
|
13
17
|
|
|
18
|
+
// Re-export proxy functions for authenticated client-side operations
|
|
19
|
+
export {
|
|
20
|
+
mutate,
|
|
21
|
+
query,
|
|
22
|
+
hasErrors,
|
|
23
|
+
getErrorMessage,
|
|
24
|
+
type Operation,
|
|
25
|
+
type GraphQLResponse,
|
|
26
|
+
type ProxyOptions,
|
|
27
|
+
} from "./proxy";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Type for the server-side Amplify client (for use in type declarations)
|
|
31
|
+
* This represents the client returned by generateServerClientUsingCookies
|
|
32
|
+
*/
|
|
33
|
+
export type AmplifyServerClient<TSchema extends Record<string, unknown> = Record<string, unknown>> =
|
|
34
|
+
ReturnType<typeof generateServerClientUsingCookies<TSchema>>;
|
|
35
|
+
|
|
14
36
|
// Singleton client instance for client-side fetching
|
|
15
37
|
let sharedClientInstance: any = null;
|
|
16
38
|
|
|
@@ -134,12 +156,69 @@ export interface GenerateServerClientOptions {
|
|
|
134
156
|
*/
|
|
135
157
|
export function generateServerClient<
|
|
136
158
|
TSchema extends Record<string, unknown> = Record<string, unknown>,
|
|
137
|
-
>(
|
|
159
|
+
>(_options?: GenerateServerClientOptions): ReturnType<typeof generateDataClient<TSchema>> {
|
|
138
160
|
// Generate the client without authMode parameter
|
|
139
161
|
// When called within runWithAmplifyServerContext, it will automatically use the token provider
|
|
140
162
|
// from the context (which reads JWT tokens from cookies)
|
|
141
163
|
// The authMode should be specified per-operation, not at client creation
|
|
142
164
|
const client = generateDataClient<TSchema>();
|
|
143
|
-
|
|
165
|
+
|
|
144
166
|
return client;
|
|
145
167
|
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Context required for getting a server client in API routes
|
|
171
|
+
*/
|
|
172
|
+
export interface ServerClientContext {
|
|
173
|
+
locals: { amplifyClient?: any; user?: any };
|
|
174
|
+
cookies: any;
|
|
175
|
+
request: Request;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get the server client from Astro context
|
|
180
|
+
*
|
|
181
|
+
* Uses locals.amplifyClient if available (set by middleware),
|
|
182
|
+
* otherwise creates a new client using Amplify's global config.
|
|
183
|
+
* No config parameter needed - uses the config set by the middleware.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* import { getServerClient } from '@htlkg/data/client';
|
|
188
|
+
*
|
|
189
|
+
* export const POST: APIRoute = async (context) => {
|
|
190
|
+
* const client = getServerClient(context);
|
|
191
|
+
* if (!client) return new Response('Not authenticated', { status: 401 });
|
|
192
|
+
*
|
|
193
|
+
* const result = await client.models.User.list();
|
|
194
|
+
* };
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export function getServerClient<TSchema extends Record<string, unknown> = Record<string, unknown>>(
|
|
198
|
+
context: ServerClientContext,
|
|
199
|
+
): ReturnType<typeof generateServerClientUsingCookies<TSchema>> | null {
|
|
200
|
+
// Try to use client from middleware first
|
|
201
|
+
if (context.locals.amplifyClient) {
|
|
202
|
+
return context.locals.amplifyClient as ReturnType<typeof generateServerClientUsingCookies<TSchema>>;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// If no client from middleware and user is authenticated, create one using global Amplify config
|
|
206
|
+
if (context.locals.user) {
|
|
207
|
+
try {
|
|
208
|
+
// Get config from Amplify (set by middleware)
|
|
209
|
+
const amplifyConfig = Amplify.getConfig();
|
|
210
|
+
if (amplifyConfig) {
|
|
211
|
+
return generateServerClientUsingCookies<TSchema>({
|
|
212
|
+
config: amplifyConfig,
|
|
213
|
+
cookies: context.cookies,
|
|
214
|
+
request: context.request,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
} catch (e) {
|
|
218
|
+
console.error('[getServerClient] Failed to get Amplify config:', e);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// No authentication available
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL Proxy Client
|
|
3
|
+
*
|
|
4
|
+
* Client-side helper for making authenticated GraphQL operations through
|
|
5
|
+
* the server-side proxy. This allows Vue components to perform mutations
|
|
6
|
+
* while keeping auth cookies httpOnly for security.
|
|
7
|
+
*
|
|
8
|
+
* @module @htlkg/data/proxy
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Valid GraphQL operations
|
|
13
|
+
*/
|
|
14
|
+
export type Operation = "create" | "update" | "delete" | "get" | "list";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Response type from GraphQL operations
|
|
18
|
+
*/
|
|
19
|
+
export interface GraphQLResponse<T = any> {
|
|
20
|
+
data: T | null;
|
|
21
|
+
errors?: Array<{ message: string; [key: string]: any }>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Options for proxy requests
|
|
26
|
+
*/
|
|
27
|
+
export interface ProxyOptions {
|
|
28
|
+
/** Custom API endpoint (default: /api/graphql) */
|
|
29
|
+
endpoint?: string;
|
|
30
|
+
/** Additional fetch options */
|
|
31
|
+
fetchOptions?: RequestInit;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Default proxy endpoint
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_ENDPOINT = "/api/graphql";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Execute a GraphQL mutation through the server proxy
|
|
41
|
+
*
|
|
42
|
+
* @param model - The model name (e.g., 'User', 'Brand', 'Account')
|
|
43
|
+
* @param operation - The operation type ('create', 'update', 'delete')
|
|
44
|
+
* @param data - The operation data/input
|
|
45
|
+
* @param options - Optional configuration
|
|
46
|
+
* @returns Promise with the operation result
|
|
47
|
+
*
|
|
48
|
+
* @example Create
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const result = await mutate('User', 'create', {
|
|
51
|
+
* email: 'user@example.com',
|
|
52
|
+
* accountId: '123',
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example Update
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const result = await mutate('User', 'update', {
|
|
59
|
+
* id: 'user-id',
|
|
60
|
+
* status: 'deleted',
|
|
61
|
+
* deletedAt: new Date().toISOString(),
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @example Delete
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const result = await mutate('User', 'delete', { id: 'user-id' });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export async function mutate<T = any>(
|
|
71
|
+
model: string,
|
|
72
|
+
operation: "create" | "update" | "delete",
|
|
73
|
+
data: Record<string, any>,
|
|
74
|
+
options?: ProxyOptions,
|
|
75
|
+
): Promise<GraphQLResponse<T>> {
|
|
76
|
+
return proxyRequest<T>(model, operation, data, options);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Execute a GraphQL query through the server proxy
|
|
81
|
+
*
|
|
82
|
+
* @param model - The model name (e.g., 'User', 'Brand', 'Account')
|
|
83
|
+
* @param operation - The operation type ('get', 'list')
|
|
84
|
+
* @param data - The query parameters (id for get, filter/limit for list)
|
|
85
|
+
* @param options - Optional configuration
|
|
86
|
+
* @returns Promise with the query result
|
|
87
|
+
*
|
|
88
|
+
* @example Get by ID
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const result = await query('User', 'get', { id: 'user-id' });
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @example List with filter
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const result = await query('User', 'list', {
|
|
96
|
+
* filter: { status: { eq: 'active' } },
|
|
97
|
+
* limit: 100,
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export async function query<T = any>(
|
|
102
|
+
model: string,
|
|
103
|
+
operation: "get" | "list",
|
|
104
|
+
data?: Record<string, any>,
|
|
105
|
+
options?: ProxyOptions,
|
|
106
|
+
): Promise<GraphQLResponse<T>> {
|
|
107
|
+
return proxyRequest<T>(model, operation, data, options);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Internal function to make proxy requests
|
|
112
|
+
*/
|
|
113
|
+
async function proxyRequest<T>(
|
|
114
|
+
model: string,
|
|
115
|
+
operation: Operation,
|
|
116
|
+
data?: Record<string, any>,
|
|
117
|
+
options?: ProxyOptions,
|
|
118
|
+
): Promise<GraphQLResponse<T>> {
|
|
119
|
+
const endpoint = options?.endpoint ?? DEFAULT_ENDPOINT;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const response = await fetch(endpoint, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
},
|
|
127
|
+
credentials: "include", // Important: include cookies
|
|
128
|
+
...options?.fetchOptions,
|
|
129
|
+
body: JSON.stringify({ model, operation, data }),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const result: GraphQLResponse<T> = await response.json();
|
|
133
|
+
|
|
134
|
+
// Handle auth errors
|
|
135
|
+
if (response.status === 401) {
|
|
136
|
+
console.error("[GraphQL Proxy] Unauthorized - session may have expired");
|
|
137
|
+
// Optionally trigger a redirect to login
|
|
138
|
+
// window.location.href = '/login';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error("[GraphQL Proxy] Request failed:", error);
|
|
144
|
+
return {
|
|
145
|
+
data: null,
|
|
146
|
+
errors: [
|
|
147
|
+
{
|
|
148
|
+
message: error instanceof Error ? error.message : "Network error",
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Helper to check if a response has errors
|
|
157
|
+
*/
|
|
158
|
+
export function hasErrors(response: GraphQLResponse): boolean {
|
|
159
|
+
return !!response.errors && response.errors.length > 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Helper to get the first error message from a response
|
|
164
|
+
*/
|
|
165
|
+
export function getErrorMessage(response: GraphQLResponse): string | null {
|
|
166
|
+
if (!response.errors || response.errors.length === 0) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return response.errors[0].message;
|
|
170
|
+
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -19,3 +19,4 @@ export { useBrands, type UseBrandsOptions, type UseBrandsReturn } from "./useBra
|
|
|
19
19
|
export { useAccounts, type UseAccountsOptions, type UseAccountsReturn } from "./useAccounts";
|
|
20
20
|
export { useUsers, type UseUsersOptions, type UseUsersReturn } from "./useUsers";
|
|
21
21
|
export { useProducts, type UseProductsOptions, type UseProductsReturn } from "./useProducts";
|
|
22
|
+
export { useProductInstances, type UseProductInstancesOptions, type UseProductInstancesReturn } from "./useProductInstances";
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useProductInstances Hook
|
|
3
|
+
*
|
|
4
|
+
* Vue composable for fetching and managing product instance data with reactive state.
|
|
5
|
+
* Provides loading states, error handling, refetch capabilities, and CRUD operations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Ref, ComputedRef } from "vue";
|
|
9
|
+
import type { ProductInstance } from "@htlkg/core/types";
|
|
10
|
+
import { createDataHook, type BaseHookOptions } from "./createDataHook";
|
|
11
|
+
import { getSharedClient } from "../client";
|
|
12
|
+
import {
|
|
13
|
+
createProductInstance,
|
|
14
|
+
updateProductInstance,
|
|
15
|
+
deleteProductInstance,
|
|
16
|
+
toggleProductInstanceEnabled,
|
|
17
|
+
type CreateProductInstanceInput,
|
|
18
|
+
type UpdateProductInstanceInput,
|
|
19
|
+
} from "../mutations/productInstances";
|
|
20
|
+
|
|
21
|
+
export interface UseProductInstancesOptions extends BaseHookOptions {
|
|
22
|
+
/** Filter criteria for product instances */
|
|
23
|
+
filter?: any;
|
|
24
|
+
/** Limit number of results */
|
|
25
|
+
limit?: number;
|
|
26
|
+
/** Auto-fetch on mount (default: true) */
|
|
27
|
+
autoFetch?: boolean;
|
|
28
|
+
/** Filter by brand ID */
|
|
29
|
+
brandId?: string;
|
|
30
|
+
/** Filter by account ID */
|
|
31
|
+
accountId?: string;
|
|
32
|
+
/** Filter by product ID */
|
|
33
|
+
productId?: string;
|
|
34
|
+
/** Only enabled instances */
|
|
35
|
+
enabledOnly?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UseProductInstancesReturn {
|
|
39
|
+
/** Reactive array of product instances */
|
|
40
|
+
instances: Ref<ProductInstance[]>;
|
|
41
|
+
/** Computed array of enabled instances only */
|
|
42
|
+
enabledInstances: ComputedRef<ProductInstance[]>;
|
|
43
|
+
/** Loading state */
|
|
44
|
+
loading: Ref<boolean>;
|
|
45
|
+
/** Error state */
|
|
46
|
+
error: Ref<Error | null>;
|
|
47
|
+
/** Refetch product instances */
|
|
48
|
+
refetch: () => Promise<void>;
|
|
49
|
+
/** Create a new product instance */
|
|
50
|
+
createInstance: (input: CreateProductInstanceInput) => Promise<ProductInstance>;
|
|
51
|
+
/** Update an existing product instance */
|
|
52
|
+
updateInstance: (input: UpdateProductInstanceInput) => Promise<ProductInstance>;
|
|
53
|
+
/** Delete a product instance */
|
|
54
|
+
deleteInstance: (id: string) => Promise<boolean>;
|
|
55
|
+
/** Toggle the enabled status of a product instance */
|
|
56
|
+
toggleEnabled: (id: string, enabled: boolean) => Promise<ProductInstance>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build filter from hook options
|
|
61
|
+
*/
|
|
62
|
+
function buildFilter(options: UseProductInstancesOptions): any {
|
|
63
|
+
let filter: any = options.filter || {};
|
|
64
|
+
|
|
65
|
+
if (options.brandId) {
|
|
66
|
+
filter = { ...filter, brandId: { eq: options.brandId } };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (options.accountId) {
|
|
70
|
+
filter = { ...filter, accountId: { eq: options.accountId } };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (options.productId) {
|
|
74
|
+
filter = { ...filter, productId: { eq: options.productId } };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (options.enabledOnly) {
|
|
78
|
+
filter = { ...filter, enabled: { eq: true } };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Internal hook created by factory
|
|
86
|
+
*/
|
|
87
|
+
const useProductInstancesInternal = createDataHook<
|
|
88
|
+
ProductInstance,
|
|
89
|
+
UseProductInstancesOptions,
|
|
90
|
+
{ enabledInstances: ProductInstance[] }
|
|
91
|
+
>({
|
|
92
|
+
model: "ProductInstance",
|
|
93
|
+
dataPropertyName: "instances",
|
|
94
|
+
buildFilter,
|
|
95
|
+
computedProperties: {
|
|
96
|
+
enabledInstances: (instances) => instances.filter((i) => i.enabled),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Composable for fetching and managing product instances
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* import { useProductInstances } from '@htlkg/data/hooks';
|
|
106
|
+
*
|
|
107
|
+
* const { instances, loading, error, refetch } = useProductInstances();
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @example With filters
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const { instances, enabledInstances, loading } = useProductInstances({
|
|
113
|
+
* brandId: 'brand-123',
|
|
114
|
+
* enabledOnly: true,
|
|
115
|
+
* limit: 50
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @example For a specific account
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const { instances, loading } = useProductInstances({
|
|
122
|
+
* accountId: 'account-456',
|
|
123
|
+
* autoFetch: true
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function useProductInstances(options: UseProductInstancesOptions = {}): UseProductInstancesReturn {
|
|
128
|
+
const result = useProductInstancesInternal(options);
|
|
129
|
+
|
|
130
|
+
// CRUD methods using mutations from @htlkg/data/mutations
|
|
131
|
+
async function createInstance(input: CreateProductInstanceInput): Promise<ProductInstance> {
|
|
132
|
+
const client = getSharedClient();
|
|
133
|
+
const instance = await createProductInstance(client, input);
|
|
134
|
+
if (!instance) {
|
|
135
|
+
throw new Error("Failed to create product instance");
|
|
136
|
+
}
|
|
137
|
+
return instance;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function updateInstance(input: UpdateProductInstanceInput): Promise<ProductInstance> {
|
|
141
|
+
const client = getSharedClient();
|
|
142
|
+
const instance = await updateProductInstance(client, input);
|
|
143
|
+
if (!instance) {
|
|
144
|
+
throw new Error("Failed to update product instance");
|
|
145
|
+
}
|
|
146
|
+
return instance;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function deleteInstance(id: string): Promise<boolean> {
|
|
150
|
+
const client = getSharedClient();
|
|
151
|
+
return deleteProductInstance(client, id);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function toggleEnabled(id: string, enabled: boolean): Promise<ProductInstance> {
|
|
155
|
+
const client = getSharedClient();
|
|
156
|
+
const instance = await toggleProductInstanceEnabled(client, id, enabled);
|
|
157
|
+
if (!instance) {
|
|
158
|
+
throw new Error("Failed to toggle product instance");
|
|
159
|
+
}
|
|
160
|
+
return instance;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
instances: result.instances as Ref<ProductInstance[]>,
|
|
165
|
+
enabledInstances: result.enabledInstances as ComputedRef<ProductInstance[]>,
|
|
166
|
+
loading: result.loading,
|
|
167
|
+
error: result.error,
|
|
168
|
+
refetch: result.refetch,
|
|
169
|
+
createInstance,
|
|
170
|
+
updateInstance,
|
|
171
|
+
deleteInstance,
|
|
172
|
+
toggleEnabled,
|
|
173
|
+
};
|
|
174
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,17 @@ export {
|
|
|
16
16
|
type AstroAmplifyConfig,
|
|
17
17
|
} from "./client";
|
|
18
18
|
|
|
19
|
+
// Proxy exports (for authenticated client-side operations)
|
|
20
|
+
export {
|
|
21
|
+
mutate,
|
|
22
|
+
query,
|
|
23
|
+
hasErrors,
|
|
24
|
+
getErrorMessage,
|
|
25
|
+
type Operation,
|
|
26
|
+
type GraphQLResponse,
|
|
27
|
+
type ProxyOptions,
|
|
28
|
+
} from "./client";
|
|
29
|
+
|
|
19
30
|
// Query exports
|
|
20
31
|
export * from "./queries";
|
|
21
32
|
|