@webiny/app 6.5.0-beta.1 → 6.5.0-beta.10
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/features/graphqlClient/BatchingGraphQLClient.js +1 -1
- package/features/graphqlClient/BatchingGraphQLClient.js.map +1 -1
- package/features/graphqlClient/FetchGraphQLClient.d.ts +3 -0
- package/features/graphqlClient/FetchGraphQLClient.js +22 -4
- package/features/graphqlClient/FetchGraphQLClient.js.map +1 -1
- package/features/graphqlClient/GraphQLExtensionsEvent.d.ts +18 -0
- package/features/graphqlClient/GraphQLExtensionsEvent.js +13 -0
- package/features/graphqlClient/GraphQLExtensionsEvent.js.map +1 -0
- package/features/graphqlClient/__tests__/GraphQLClient.test.js +78 -0
- package/features/graphqlClient/__tests__/GraphQLClient.test.js.map +1 -1
- package/features/graphqlClient/abstractions.d.ts +7 -0
- package/features/graphqlClient/abstractions.js +2 -1
- package/features/graphqlClient/abstractions.js.map +1 -1
- package/features/graphqlClient/index.d.ts +3 -0
- package/features/graphqlClient/index.js +2 -1
- package/package.json +16 -16
- package/plugins/AddQuerySelectionPlugin.js +1 -1
- package/plugins/AddQuerySelectionPlugin.js.map +1 -1
|
@@ -103,7 +103,7 @@ class BatchingGraphQLClientImpl {
|
|
|
103
103
|
}
|
|
104
104
|
if (!Array.isArray(json)) throw new Error("Expected batch response to be an array");
|
|
105
105
|
return json.map((result, index)=>{
|
|
106
|
-
if (result.errors && result.errors.length > 0) throw new Error(`GraphQL errors in operation ${index}: ${JSON.stringify(result.errors)}`);
|
|
106
|
+
if (result.errors && result.errors.length > 0 && !result.data) throw new Error(`GraphQL errors in operation ${index}: ${JSON.stringify(result.errors)}`);
|
|
107
107
|
return result.data;
|
|
108
108
|
});
|
|
109
109
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/graphqlClient/BatchingGraphQLClient.js","sources":["../../../src/features/graphqlClient/BatchingGraphQLClient.ts"],"sourcesContent":["import { createDecorator } from \"@webiny/di\";\nimport { GraphQLClient } from \"./abstractions.js\";\nimport { RequestValue } from \"~/features/graphqlClient/RequestValue.js\";\n\ninterface BatchedRequest {\n request: RequestValue;\n resolve: (value: any) => void;\n reject: (error: any) => void;\n}\n\ninterface BatchOperation {\n operationName: string | undefined;\n query: string;\n variables?: any;\n}\n\nclass BatchingGraphQLClientImpl implements GraphQLClient.Interface {\n private queue: BatchedRequest[] = [];\n private batchTimeout: NodeJS.Timeout | null = null;\n private readonly batchWindowMs: number;\n private readonly maxBatchSize: number;\n\n constructor(private decoratee: GraphQLClient.Interface) {\n // Default: 10ms window, max 10 operations per batch\n this.batchWindowMs = 10;\n this.maxBatchSize = 10;\n }\n\n async execute<TVariables = any, TResult = any>(\n params: GraphQLClient.Request<TVariables>\n ): Promise<TResult> {\n return new Promise((resolve, reject) => {\n this.queue.push({ request: RequestValue.from(params), resolve, reject });\n\n if (this.queue.length >= this.maxBatchSize) {\n this.flush();\n return;\n }\n\n if (!this.batchTimeout) {\n this.batchTimeout = setTimeout(() => {\n this.flush();\n }, this.batchWindowMs);\n }\n });\n }\n\n private async flush(): Promise<void> {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout);\n this.batchTimeout = null;\n }\n\n if (this.queue.length === 0) {\n return;\n }\n\n const batch = this.queue.splice(0, this.queue.length);\n\n // Single request - use decoratee directly (no batching needed)\n if (batch.length === 1) {\n const { request, resolve, reject } = batch[0];\n try {\n const result = await this.decoratee.execute(request.request);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n return;\n }\n\n // Group requests by endpoint + headers (only batch requests with identical target)\n const grouped = new Map<string, BatchedRequest[]>();\n for (const item of batch) {\n const groupKey =\n item.request.endpoint + \"|\" + this.serializeHeaders(item.request.headers);\n if (!grouped.has(groupKey)) {\n grouped.set(groupKey, []);\n }\n grouped.get(groupKey)!.push(item);\n }\n\n // Process each group separately\n const batchPromises = Array.from(grouped.entries()).map(requests => {\n const group = requests[1];\n\n if (group.length === 1) {\n // Single request in this group - use decoratee directly\n const { request, resolve, reject } = group[0];\n return this.decoratee.execute(request.request).then(resolve).catch(reject);\n }\n\n // Multiple requests with same headers - batch them\n return this.executeBatchGroup(group);\n });\n\n await Promise.all(batchPromises);\n }\n\n private async executeBatchGroup(group: BatchedRequest[]): Promise<void> {\n const batchedOperations = group.map(({ request }): BatchOperation => {\n return {\n query: request.queryAsString,\n operationName: request.operationName,\n variables: request.variables\n };\n });\n\n const endpoint = group[0].request.endpoint;\n const headers = group[0].request.headers;\n\n try {\n const results = await this.executeBatch(endpoint, batchedOperations, headers);\n group.forEach(({ resolve }, index) => {\n resolve(results[index]);\n });\n } catch (error) {\n group.forEach(({ reject }) => {\n reject(error);\n });\n }\n }\n\n private serializeHeaders(headers?: GraphQLClient.Headers): string {\n if (!headers) {\n return \"{}\";\n }\n // Sort keys for consistent serialization\n const sortedKeys = Object.keys(headers).sort();\n const sortedHeaders: Record<string, string> = {};\n for (const key of sortedKeys) {\n sortedHeaders[key] = String(headers[key]);\n }\n return JSON.stringify(sortedHeaders);\n }\n\n private async executeBatch(\n endpoint: string,\n operations: BatchOperation[],\n headers?: GraphQLClient.Headers\n ): Promise<any[]> {\n let response: Response;\n\n try {\n response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(headers || {})\n },\n body: JSON.stringify(operations)\n });\n } catch (err) {\n throw new Error(`Network error: ${(err as Error).message}`);\n }\n\n let json: any;\n try {\n json = await response.json();\n } catch {\n throw new Error(\"Failed to parse GraphQL batch response as JSON.\");\n }\n\n if (!Array.isArray(json)) {\n throw new Error(\"Expected batch response to be an array\");\n }\n\n // Map each result, handling individual errors\n return json.map((result: any, index: number) => {\n if (result.errors && result.errors.length > 0) {\n throw new Error(\n `GraphQL errors in operation ${index}: ${JSON.stringify(result.errors)}`\n );\n }\n return result.data;\n });\n }\n}\n\nexport const BatchingGraphQLClient = createDecorator({\n abstraction: GraphQLClient,\n decorator: BatchingGraphQLClientImpl,\n dependencies: []\n});\n"],"names":["BatchingGraphQLClientImpl","decoratee","params","Promise","resolve","reject","RequestValue","setTimeout","clearTimeout","batch","request","result","error","grouped","Map","item","groupKey","batchPromises","Array","requests","group","batchedOperations","endpoint","headers","results","index","sortedKeys","Object","sortedHeaders","key","String","JSON","operations","response","fetch","err","Error","json","BatchingGraphQLClient","createDecorator","GraphQLClient"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features/graphqlClient/BatchingGraphQLClient.js","sources":["../../../src/features/graphqlClient/BatchingGraphQLClient.ts"],"sourcesContent":["import { createDecorator } from \"@webiny/di\";\nimport { GraphQLClient } from \"./abstractions.js\";\nimport { RequestValue } from \"~/features/graphqlClient/RequestValue.js\";\n\ninterface BatchedRequest {\n request: RequestValue;\n resolve: (value: any) => void;\n reject: (error: any) => void;\n}\n\ninterface BatchOperation {\n operationName: string | undefined;\n query: string;\n variables?: any;\n}\n\nclass BatchingGraphQLClientImpl implements GraphQLClient.Interface {\n private queue: BatchedRequest[] = [];\n private batchTimeout: NodeJS.Timeout | null = null;\n private readonly batchWindowMs: number;\n private readonly maxBatchSize: number;\n\n constructor(private decoratee: GraphQLClient.Interface) {\n // Default: 10ms window, max 10 operations per batch\n this.batchWindowMs = 10;\n this.maxBatchSize = 10;\n }\n\n async execute<TVariables = any, TResult = any>(\n params: GraphQLClient.Request<TVariables>\n ): Promise<TResult> {\n return new Promise((resolve, reject) => {\n this.queue.push({ request: RequestValue.from(params), resolve, reject });\n\n if (this.queue.length >= this.maxBatchSize) {\n this.flush();\n return;\n }\n\n if (!this.batchTimeout) {\n this.batchTimeout = setTimeout(() => {\n this.flush();\n }, this.batchWindowMs);\n }\n });\n }\n\n private async flush(): Promise<void> {\n if (this.batchTimeout) {\n clearTimeout(this.batchTimeout);\n this.batchTimeout = null;\n }\n\n if (this.queue.length === 0) {\n return;\n }\n\n const batch = this.queue.splice(0, this.queue.length);\n\n // Single request - use decoratee directly (no batching needed)\n if (batch.length === 1) {\n const { request, resolve, reject } = batch[0];\n try {\n const result = await this.decoratee.execute(request.request);\n resolve(result);\n } catch (error) {\n reject(error);\n }\n return;\n }\n\n // Group requests by endpoint + headers (only batch requests with identical target)\n const grouped = new Map<string, BatchedRequest[]>();\n for (const item of batch) {\n const groupKey =\n item.request.endpoint + \"|\" + this.serializeHeaders(item.request.headers);\n if (!grouped.has(groupKey)) {\n grouped.set(groupKey, []);\n }\n grouped.get(groupKey)!.push(item);\n }\n\n // Process each group separately\n const batchPromises = Array.from(grouped.entries()).map(requests => {\n const group = requests[1];\n\n if (group.length === 1) {\n // Single request in this group - use decoratee directly\n const { request, resolve, reject } = group[0];\n return this.decoratee.execute(request.request).then(resolve).catch(reject);\n }\n\n // Multiple requests with same headers - batch them\n return this.executeBatchGroup(group);\n });\n\n await Promise.all(batchPromises);\n }\n\n private async executeBatchGroup(group: BatchedRequest[]): Promise<void> {\n const batchedOperations = group.map(({ request }): BatchOperation => {\n return {\n query: request.queryAsString,\n operationName: request.operationName,\n variables: request.variables\n };\n });\n\n const endpoint = group[0].request.endpoint;\n const headers = group[0].request.headers;\n\n try {\n const results = await this.executeBatch(endpoint, batchedOperations, headers);\n group.forEach(({ resolve }, index) => {\n resolve(results[index]);\n });\n } catch (error) {\n group.forEach(({ reject }) => {\n reject(error);\n });\n }\n }\n\n private serializeHeaders(headers?: GraphQLClient.Headers): string {\n if (!headers) {\n return \"{}\";\n }\n // Sort keys for consistent serialization\n const sortedKeys = Object.keys(headers).sort();\n const sortedHeaders: Record<string, string> = {};\n for (const key of sortedKeys) {\n sortedHeaders[key] = String(headers[key]);\n }\n return JSON.stringify(sortedHeaders);\n }\n\n private async executeBatch(\n endpoint: string,\n operations: BatchOperation[],\n headers?: GraphQLClient.Headers\n ): Promise<any[]> {\n let response: Response;\n\n try {\n response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(headers || {})\n },\n body: JSON.stringify(operations)\n });\n } catch (err) {\n throw new Error(`Network error: ${(err as Error).message}`);\n }\n\n let json: any;\n try {\n json = await response.json();\n } catch {\n throw new Error(\"Failed to parse GraphQL batch response as JSON.\");\n }\n\n if (!Array.isArray(json)) {\n throw new Error(\"Expected batch response to be an array\");\n }\n\n // Map each result, handling individual errors. As with the non-batched client, a result\n // may carry both `data` and `errors` (partial success); we only fail an operation when it\n // produced no data at all, otherwise the partial payload is handed back to its caller.\n return json.map((result: any, index: number) => {\n if (result.errors && result.errors.length > 0 && !result.data) {\n throw new Error(\n `GraphQL errors in operation ${index}: ${JSON.stringify(result.errors)}`\n );\n }\n return result.data;\n });\n }\n}\n\nexport const BatchingGraphQLClient = createDecorator({\n abstraction: GraphQLClient,\n decorator: BatchingGraphQLClientImpl,\n dependencies: []\n});\n"],"names":["BatchingGraphQLClientImpl","decoratee","params","Promise","resolve","reject","RequestValue","setTimeout","clearTimeout","batch","request","result","error","grouped","Map","item","groupKey","batchPromises","Array","requests","group","batchedOperations","endpoint","headers","results","index","sortedKeys","Object","sortedHeaders","key","String","JSON","operations","response","fetch","err","Error","json","BatchingGraphQLClient","createDecorator","GraphQLClient"],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { GraphQLClient } from "./abstractions.js";
|
|
2
|
+
import { EventPublisher } from "../../features/eventPublisher/index.js";
|
|
2
3
|
declare class GraphQLClientImpl implements GraphQLClient.Interface {
|
|
4
|
+
private eventPublisher;
|
|
5
|
+
constructor(eventPublisher: EventPublisher.Interface);
|
|
3
6
|
execute<TResult = any, TVariables = any>(params: GraphQLClient.Request<TVariables>): Promise<TResult>;
|
|
4
7
|
private fetch;
|
|
5
8
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { createImplementation } from "@webiny/di";
|
|
2
2
|
import { GraphQLClient } from "./abstractions.js";
|
|
3
3
|
import { RequestValue } from "./RequestValue.js";
|
|
4
|
+
import { EventPublisher } from "../eventPublisher/index.js";
|
|
5
|
+
import { GraphQLExtensionsEvent } from "./GraphQLExtensionsEvent.js";
|
|
4
6
|
class GraphQLClientImpl {
|
|
7
|
+
constructor(eventPublisher){
|
|
8
|
+
this.eventPublisher = eventPublisher;
|
|
9
|
+
}
|
|
5
10
|
async execute(params) {
|
|
6
11
|
const request = RequestValue.from(params);
|
|
7
12
|
const body = JSON.stringify({
|
|
@@ -9,7 +14,15 @@ class GraphQLClientImpl {
|
|
|
9
14
|
variables: request.variables,
|
|
10
15
|
operationName: request.operationName
|
|
11
16
|
});
|
|
12
|
-
|
|
17
|
+
const { data, extensions } = await this.fetch(request.endpoint, body, request.headers);
|
|
18
|
+
if (extensions) try {
|
|
19
|
+
await this.eventPublisher.publish(new GraphQLExtensionsEvent({
|
|
20
|
+
endpoint: request.endpoint,
|
|
21
|
+
operationName: request.operationName,
|
|
22
|
+
extensions
|
|
23
|
+
}));
|
|
24
|
+
} catch {}
|
|
25
|
+
return data;
|
|
13
26
|
}
|
|
14
27
|
async fetch(endpoint, body, headers = {}) {
|
|
15
28
|
let response;
|
|
@@ -35,16 +48,21 @@ class GraphQLClientImpl {
|
|
|
35
48
|
message: json.message,
|
|
36
49
|
code: json.code
|
|
37
50
|
};
|
|
38
|
-
if (json.errors && json.errors.length > 0) throw new Error("GraphQL errors", {
|
|
51
|
+
if (json.errors && json.errors.length > 0 && !json.data) throw new Error("GraphQL errors", {
|
|
39
52
|
cause: json.errors
|
|
40
53
|
});
|
|
41
|
-
return
|
|
54
|
+
return {
|
|
55
|
+
data: json.data,
|
|
56
|
+
extensions: json.extensions
|
|
57
|
+
};
|
|
42
58
|
}
|
|
43
59
|
}
|
|
44
60
|
const FetchGraphQLClient = createImplementation({
|
|
45
61
|
abstraction: GraphQLClient,
|
|
46
62
|
implementation: GraphQLClientImpl,
|
|
47
|
-
dependencies: [
|
|
63
|
+
dependencies: [
|
|
64
|
+
EventPublisher
|
|
65
|
+
]
|
|
48
66
|
});
|
|
49
67
|
export { FetchGraphQLClient };
|
|
50
68
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/graphqlClient/FetchGraphQLClient.js","sources":["../../../src/features/graphqlClient/FetchGraphQLClient.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/di\";\nimport { GraphQLClient } from \"./abstractions.js\";\nimport { RequestValue } from \"~/features/graphqlClient/RequestValue.js\";\n\nclass GraphQLClientImpl implements GraphQLClient.Interface {\n async execute<TResult = any, TVariables = any>(\n params: GraphQLClient.Request<TVariables>\n ): Promise<TResult> {\n const request = RequestValue.from(params);\n\n const body = JSON.stringify({\n query: request.queryAsString,\n variables: request.variables,\n operationName: request.operationName\n });\n\n
|
|
1
|
+
{"version":3,"file":"features/graphqlClient/FetchGraphQLClient.js","sources":["../../../src/features/graphqlClient/FetchGraphQLClient.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/di\";\nimport { GraphQLClient } from \"./abstractions.js\";\nimport { RequestValue } from \"~/features/graphqlClient/RequestValue.js\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\nimport { GraphQLExtensionsEvent } from \"./GraphQLExtensionsEvent.js\";\n\ninterface IFetchResult<TResult> {\n data: TResult;\n extensions?: Record<string, any>;\n}\n\nclass GraphQLClientImpl implements GraphQLClient.Interface {\n constructor(private eventPublisher: EventPublisher.Interface) {}\n\n async execute<TResult = any, TVariables = any>(\n params: GraphQLClient.Request<TVariables>\n ): Promise<TResult> {\n const request = RequestValue.from(params);\n\n const body = JSON.stringify({\n query: request.queryAsString,\n variables: request.variables,\n operationName: request.operationName\n });\n\n const { data, extensions } = await this.fetch<TResult>(\n request.endpoint,\n body,\n request.headers\n );\n\n /**\n * Only when the response actually carries extensions, so ordinary traffic pays nothing.\n * Failing to announce them must not fail the request that produced them.\n */\n if (extensions) {\n try {\n await this.eventPublisher.publish(\n new GraphQLExtensionsEvent({\n endpoint: request.endpoint,\n operationName: request.operationName,\n extensions\n })\n );\n } catch {\n // An observer's problem is not the caller's problem.\n }\n }\n\n return data;\n }\n\n private async fetch<TResult = any>(\n endpoint: string,\n body: string,\n headers: GraphQLClient.Headers = {}\n ): Promise<IFetchResult<TResult>> {\n let response: Response;\n try {\n response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(headers || {})\n },\n body\n });\n } catch (err) {\n throw new Error(`Network error: ${(err as Error).message}`);\n }\n let json: any;\n try {\n json = await response.json();\n } catch {\n throw new Error(\"Failed to parse GraphQL response as JSON.\");\n }\n\n // Check for generic API errors\n if (response.status !== 200) {\n throw { message: json.message, code: json.code };\n }\n\n /**\n * A GraphQL response can carry BOTH `data` and `errors` (partial success): a non-nullable\n * field error nulls that field and bubbles up to the nearest nullable ancestor, while the\n * rest of `data` stays valid and usable. We only treat errors as fatal when there is no\n * data to return — otherwise callers are free to consume the partial payload.\n */\n if (json.errors && json.errors.length > 0 && !json.data) {\n throw new Error(`GraphQL errors`, { cause: json.errors });\n }\n return { data: json.data as TResult, extensions: json.extensions };\n }\n}\n\nexport const FetchGraphQLClient = createImplementation({\n abstraction: GraphQLClient,\n implementation: GraphQLClientImpl,\n dependencies: [EventPublisher]\n});\n"],"names":["GraphQLClientImpl","eventPublisher","params","request","RequestValue","body","JSON","data","extensions","GraphQLExtensionsEvent","endpoint","headers","response","fetch","err","Error","json","FetchGraphQLClient","createImplementation","GraphQLClient","EventPublisher"],"mappings":";;;;;AAWA,MAAMA;IACF,YAAoBC,cAAwC,CAAE;aAA1CA,cAAc,GAAdA;IAA2C;IAE/D,MAAM,QACFC,MAAyC,EACzB;QAChB,MAAMC,UAAUC,aAAa,IAAI,CAACF;QAElC,MAAMG,OAAOC,KAAK,SAAS,CAAC;YACxB,OAAOH,QAAQ,aAAa;YAC5B,WAAWA,QAAQ,SAAS;YAC5B,eAAeA,QAAQ,aAAa;QACxC;QAEA,MAAM,EAAEI,IAAI,EAAEC,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CACzCL,QAAQ,QAAQ,EAChBE,MACAF,QAAQ,OAAO;QAOnB,IAAIK,YACA,IAAI;YACA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,uBAAuB;gBACvB,UAAUN,QAAQ,QAAQ;gBAC1B,eAAeA,QAAQ,aAAa;gBACpCK;YACJ;QAER,EAAE,OAAM,CAER;QAGJ,OAAOD;IACX;IAEA,MAAc,MACVG,QAAgB,EAChBL,IAAY,EACZM,UAAiC,CAAC,CAAC,EACL;QAC9B,IAAIC;QACJ,IAAI;YACAA,WAAW,MAAMC,MAAMH,UAAU;gBAC7B,QAAQ;gBACR,SAAS;oBACL,gBAAgB;oBAChB,GAAIC,WAAW,CAAC,CAAC;gBACrB;gBACAN;YACJ;QACJ,EAAE,OAAOS,KAAK;YACV,MAAM,IAAIC,MAAM,CAAC,eAAe,EAAGD,IAAc,OAAO,EAAE;QAC9D;QACA,IAAIE;QACJ,IAAI;YACAA,OAAO,MAAMJ,SAAS,IAAI;QAC9B,EAAE,OAAM;YACJ,MAAM,IAAIG,MAAM;QACpB;QAGA,IAAIH,AAAoB,QAApBA,SAAS,MAAM,EACf,MAAM;YAAE,SAASI,KAAK,OAAO;YAAE,MAAMA,KAAK,IAAI;QAAC;QASnD,IAAIA,KAAK,MAAM,IAAIA,KAAK,MAAM,CAAC,MAAM,GAAG,KAAK,CAACA,KAAK,IAAI,EACnD,MAAM,IAAID,MAAM,kBAAkB;YAAE,OAAOC,KAAK,MAAM;QAAC;QAE3D,OAAO;YAAE,MAAMA,KAAK,IAAI;YAAa,YAAYA,KAAK,UAAU;QAAC;IACrE;AACJ;AAEO,MAAMC,qBAAqBC,qBAAqB;IACnD,aAAaC;IACb,gBAAgBnB;IAChB,cAAc;QAACoB;KAAe;AAClC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseEvent } from "../../features/eventPublisher/index.js";
|
|
2
|
+
export interface GraphQLExtensionsPayload {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
operationName?: string;
|
|
5
|
+
extensions: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Published when a GraphQL response carries `extensions`.
|
|
9
|
+
*
|
|
10
|
+
* `execute` returns only `data`, so extensions would otherwise be unreachable to anything outside
|
|
11
|
+
* this client - a decorator sees the return value, which is already past them. Publishing keeps that
|
|
12
|
+
* contract intact while letting an interested feature observe them, without this package needing to
|
|
13
|
+
* know who is listening.
|
|
14
|
+
*/
|
|
15
|
+
export declare class GraphQLExtensionsEvent extends BaseEvent<GraphQLExtensionsPayload> {
|
|
16
|
+
eventType: "graphql.extensions";
|
|
17
|
+
getHandlerAbstraction(): import("@webiny/di").Abstraction<import("~/features/eventPublisher/index.js").IEventHandler<GraphQLExtensionsEvent>>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseEvent } from "../eventPublisher/index.js";
|
|
2
|
+
import { GraphQLExtensionsEventHandler } from "./abstractions.js";
|
|
3
|
+
class GraphQLExtensionsEvent extends BaseEvent {
|
|
4
|
+
getHandlerAbstraction() {
|
|
5
|
+
return GraphQLExtensionsEventHandler;
|
|
6
|
+
}
|
|
7
|
+
constructor(...args){
|
|
8
|
+
super(...args), this.eventType = "graphql.extensions";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export { GraphQLExtensionsEvent };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=GraphQLExtensionsEvent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/graphqlClient/GraphQLExtensionsEvent.js","sources":["../../../src/features/graphqlClient/GraphQLExtensionsEvent.ts"],"sourcesContent":["import { BaseEvent } from \"~/features/eventPublisher/index.js\";\nimport { GraphQLExtensionsEventHandler } from \"./abstractions.js\";\n\nexport interface GraphQLExtensionsPayload {\n endpoint: string;\n operationName?: string;\n extensions: Record<string, any>;\n}\n\n/**\n * Published when a GraphQL response carries `extensions`.\n *\n * `execute` returns only `data`, so extensions would otherwise be unreachable to anything outside\n * this client - a decorator sees the return value, which is already past them. Publishing keeps that\n * contract intact while letting an interested feature observe them, without this package needing to\n * know who is listening.\n */\nexport class GraphQLExtensionsEvent extends BaseEvent<GraphQLExtensionsPayload> {\n eventType = \"graphql.extensions\" as const;\n\n getHandlerAbstraction() {\n return GraphQLExtensionsEventHandler;\n }\n}\n"],"names":["GraphQLExtensionsEvent","BaseEvent","GraphQLExtensionsEventHandler"],"mappings":";;AAiBO,MAAMA,+BAA+BC;IAGxC,wBAAwB;QACpB,OAAOC;IACX;;QALG,qBACH,SAAS,GAAG;;AAKhB"}
|
|
@@ -5,6 +5,7 @@ import { FetchGraphQLClient } from "../FetchGraphQLClient.js";
|
|
|
5
5
|
import { BatchingGraphQLClient } from "../BatchingGraphQLClient.js";
|
|
6
6
|
import { RetryGraphQLClient } from "../RetryGraphQLClient.js";
|
|
7
7
|
import { EnvConfig } from "../../envConfig/index.js";
|
|
8
|
+
import { EventPublisher } from "../../eventPublisher/index.js";
|
|
8
9
|
import { __webpack_require__ } from "../../../rslib-runtime.js";
|
|
9
10
|
describe("GraphQLClient Feature", ()=>{
|
|
10
11
|
let container;
|
|
@@ -17,6 +18,9 @@ describe("GraphQLClient Feature", ()=>{
|
|
|
17
18
|
})
|
|
18
19
|
};
|
|
19
20
|
container.registerInstance(EnvConfig, mockEnvConfig);
|
|
21
|
+
container.registerInstance(EventPublisher, {
|
|
22
|
+
publish: vi.fn(async ()=>void 0)
|
|
23
|
+
});
|
|
20
24
|
});
|
|
21
25
|
describe("FetchGraphQLClient", ()=>{
|
|
22
26
|
beforeEach(()=>{
|
|
@@ -127,6 +131,37 @@ describe("GraphQLClient Feature", ()=>{
|
|
|
127
131
|
query: "query { user { id } }"
|
|
128
132
|
})).rejects.toThrow("GraphQL errors");
|
|
129
133
|
});
|
|
134
|
+
it("should return partial data when the response carries both data and errors", async ()=>{
|
|
135
|
+
const mockResponse = {
|
|
136
|
+
data: {
|
|
137
|
+
content: {
|
|
138
|
+
data: [
|
|
139
|
+
{
|
|
140
|
+
id: "1"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "2"
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
errors: [
|
|
149
|
+
{
|
|
150
|
+
message: "Cannot return null for non-nullable field RefField.id."
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
};
|
|
154
|
+
__webpack_require__.g.fetch = vi.fn().mockResolvedValue({
|
|
155
|
+
status: 200,
|
|
156
|
+
json: async ()=>mockResponse
|
|
157
|
+
});
|
|
158
|
+
const client = container.resolve(GraphQLClient);
|
|
159
|
+
const result = await client.execute({
|
|
160
|
+
endpoint: "https://api.example.com/graphql",
|
|
161
|
+
query: "query { content { data { id } } }"
|
|
162
|
+
});
|
|
163
|
+
expect(result).toEqual(mockResponse.data);
|
|
164
|
+
});
|
|
130
165
|
it("should throw on invalid JSON response", async ()=>{
|
|
131
166
|
__webpack_require__.g.fetch = vi.fn().mockResolvedValue({
|
|
132
167
|
json: async ()=>{
|
|
@@ -293,6 +328,49 @@ describe("GraphQLClient Feature", ()=>{
|
|
|
293
328
|
];
|
|
294
329
|
await expect(Promise.all(promises)).rejects.toThrow("GraphQL errors in operation 1");
|
|
295
330
|
});
|
|
331
|
+
it("should return partial data for a batched operation that has data and errors", async ()=>{
|
|
332
|
+
const mockResponse = [
|
|
333
|
+
{
|
|
334
|
+
data: {
|
|
335
|
+
user: {
|
|
336
|
+
id: "1"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
data: {
|
|
342
|
+
content: {
|
|
343
|
+
data: [
|
|
344
|
+
{
|
|
345
|
+
id: "2"
|
|
346
|
+
}
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
errors: [
|
|
351
|
+
{
|
|
352
|
+
message: "Cannot return null for non-nullable field RefField.id."
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
];
|
|
357
|
+
__webpack_require__.g.fetch = vi.fn().mockResolvedValue({
|
|
358
|
+
json: async ()=>mockResponse
|
|
359
|
+
});
|
|
360
|
+
const client = container.resolve(GraphQLClient);
|
|
361
|
+
const [result1, result2] = await Promise.all([
|
|
362
|
+
client.execute({
|
|
363
|
+
endpoint: "https://api.example.com/graphql",
|
|
364
|
+
query: "query { user { id } }"
|
|
365
|
+
}),
|
|
366
|
+
client.execute({
|
|
367
|
+
endpoint: "https://api.example.com/graphql",
|
|
368
|
+
query: "query { content { data { id } } }"
|
|
369
|
+
})
|
|
370
|
+
]);
|
|
371
|
+
expect(result1).toEqual(mockResponse[0].data);
|
|
372
|
+
expect(result2).toEqual(mockResponse[1].data);
|
|
373
|
+
});
|
|
296
374
|
});
|
|
297
375
|
describe("RetryGraphQLClient", ()=>{
|
|
298
376
|
beforeEach(()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/graphqlClient/__tests__/GraphQLClient.test.js","sources":["../../../../src/features/graphqlClient/__tests__/GraphQLClient.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GraphQLClient } from \"../abstractions.js\";\nimport { FetchGraphQLClient } from \"../FetchGraphQLClient.js\";\nimport { BatchingGraphQLClient } from \"../BatchingGraphQLClient.js\";\nimport { RetryGraphQLClient } from \"../RetryGraphQLClient.js\";\nimport { EnvConfig } from \"~/features/envConfig/index.js\";\n\ndescribe(\"GraphQLClient Feature\", () => {\n let container: Container;\n let mockEnvConfig: EnvConfig.Interface;\n\n beforeEach(() => {\n container = new Container();\n\n mockEnvConfig = {\n get: vi.fn((key: string) => {\n if (key === \"graphqlApiUrl\") {\n return \"https://api.example.com/graphql\";\n }\n return undefined;\n })\n } as any;\n\n container.registerInstance(EnvConfig, mockEnvConfig);\n });\n\n describe(\"FetchGraphQLClient\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n });\n\n it(\"should execute a query successfully\", async () => {\n const mockResponse = {\n data: { user: { id: \"1\", name: \"John\" } }\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id name } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n expect(global.fetch).toHaveBeenCalledWith(\n \"https://api.example.com/graphql\",\n expect.objectContaining({\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" }\n })\n );\n });\n\n it(\"should execute a mutation successfully\", async () => {\n const mockResponse = {\n data: { createUser: { id: \"2\", name: \"Jane\" } }\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"mutation CreateUser($name: String!) { createUser(name: $name) { id name } }\",\n variables: { name: \"Jane\" }\n });\n\n expect(result).toEqual(mockResponse.data);\n });\n\n it(\"should include custom headers\", async () => {\n const mockResponse = { data: { user: { id: \"1\" } } };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id } }\",\n headers: { \"x-tenant\": \"root\", Authorization: \"Bearer token\" }\n });\n\n expect(global.fetch).toHaveBeenCalledWith(\n \"https://api.example.com/graphql\",\n expect.objectContaining({\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-tenant\": \"root\",\n Authorization: \"Bearer token\"\n }\n })\n );\n });\n\n it(\"should throw on network error\", async () => {\n global.fetch = vi.fn().mockRejectedValue(new Error(\"Network failure\"));\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"Network error: Network failure\");\n });\n\n it(\"should throw on GraphQL errors\", async () => {\n const mockResponse = {\n errors: [{ message: \"User not found\" }],\n data: null\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"GraphQL errors\");\n });\n\n it(\"should throw on invalid JSON response\", async () => {\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => {\n throw new Error(\"Invalid JSON\");\n }\n });\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"Failed to parse GraphQL response as JSON\");\n });\n });\n\n describe(\"BatchingGraphQLClient\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n container.registerDecorator(BatchingGraphQLClient);\n });\n\n it(\"should batch multiple requests within the batch window\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\", name: \"John\" } } },\n { data: { post: { id: \"2\", title: \"Hello\" } } }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const [result1, result2] = await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id name } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetPost { post { id title } }\"\n })\n ]);\n\n expect(result1).toEqual(mockResponse[0].data);\n expect(result2).toEqual(mockResponse[1].data);\n expect(global.fetch).toHaveBeenCalledTimes(1);\n\n const callBody = JSON.parse((global.fetch as any).mock.calls[0][1].body);\n expect(callBody).toHaveLength(2);\n });\n\n it(\"should execute single request without batching\", async () => {\n const mockResponse = {\n data: { user: { id: \"1\", name: \"John\" } }\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id name } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n\n // Should use decoratee directly for single request\n const callBody = JSON.parse((global.fetch as any).mock.calls[0][1].body);\n expect(callBody).toHaveProperty(\"query\");\n expect(callBody).not.toBeInstanceOf(Array);\n });\n\n it(\"should handle mixed queries and mutations in batch\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\" } } },\n { data: { createPost: { id: \"2\" } } }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"mutation { createPost { id } }\"\n })\n ]);\n\n expect(global.fetch).toHaveBeenCalledTimes(1);\n const callBody = JSON.parse((global.fetch as any).mock.calls[0][1].body);\n expect(callBody[0]).toHaveProperty(\"query\");\n expect(callBody[1]).toHaveProperty(\"query\");\n });\n\n it(\"should reject all requests if batch fails\", async () => {\n global.fetch = vi.fn().mockRejectedValue(new Error(\"Network error\"));\n\n const client = container.resolve(GraphQLClient);\n\n const promises = [\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { post { id } }\"\n })\n ];\n\n await expect(Promise.all(promises)).rejects.toThrow(\"Network error\");\n });\n\n it(\"should handle GraphQL errors in batched operations\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\" } } },\n { errors: [{ message: \"Post not found\" }], data: null }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const promises = [\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { post { id } }\"\n })\n ];\n\n await expect(Promise.all(promises)).rejects.toThrow(\"GraphQL errors in operation 1\");\n });\n });\n\n describe(\"RetryGraphQLClient\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n container.registerDecorator(RetryGraphQLClient);\n });\n\n it(\"should retry on network errors\", async () => {\n const mockResponse = { data: { user: { id: \"1\" } } };\n\n global.fetch = vi\n .fn()\n .mockRejectedValueOnce(new Error(\"Network timeout\"))\n .mockRejectedValueOnce(new Error(\"Network timeout\"))\n .mockResolvedValueOnce({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n expect(global.fetch).toHaveBeenCalledTimes(3);\n });\n\n it(\"should not retry on GraphQL errors\", async () => {\n const mockResponse = {\n errors: [{ message: \"Unauthorized\" }],\n data: null\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"GraphQL errors\");\n\n expect(global.fetch).toHaveBeenCalledTimes(1);\n });\n\n it(\"should give up after max retries\", async () => {\n global.fetch = vi.fn().mockRejectedValue(new Error(\"Network error\"));\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"Network error\");\n\n // Should try 4 times: initial + 3 retries\n expect(global.fetch).toHaveBeenCalledTimes(4);\n });\n });\n\n describe(\"Combined decorators (Retry + Batching)\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n container.registerDecorator(BatchingGraphQLClient);\n container.registerDecorator(RetryGraphQLClient);\n });\n\n it(\"should retry batched requests on failure\", async () => {\n const mockResponse = [{ data: { user: { id: \"1\" } } }, { data: { post: { id: \"2\" } } }];\n\n global.fetch = vi\n .fn()\n .mockRejectedValueOnce(new Error(\"Network error\"))\n .mockResolvedValueOnce({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const [result1, result2] = await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { post { id } }\"\n })\n ]);\n\n expect(result1).toEqual(mockResponse[0].data);\n expect(result2).toEqual(mockResponse[1].data);\n expect(global.fetch).toHaveBeenCalledTimes(2);\n });\n });\n});\n"],"names":["describe","container","mockEnvConfig","beforeEach","Container","vi","key","EnvConfig","FetchGraphQLClient","it","mockResponse","global","client","GraphQLClient","result","expect","Error","BatchingGraphQLClient","result1","result2","Promise","callBody","JSON","Array","promises","RetryGraphQLClient"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features/graphqlClient/__tests__/GraphQLClient.test.js","sources":["../../../../src/features/graphqlClient/__tests__/GraphQLClient.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GraphQLClient } from \"../abstractions.js\";\nimport { FetchGraphQLClient } from \"../FetchGraphQLClient.js\";\nimport { BatchingGraphQLClient } from \"../BatchingGraphQLClient.js\";\nimport { RetryGraphQLClient } from \"../RetryGraphQLClient.js\";\nimport { EnvConfig } from \"~/features/envConfig/index.js\";\nimport { EventPublisher } from \"~/features/eventPublisher/index.js\";\n\ndescribe(\"GraphQLClient Feature\", () => {\n let container: Container;\n let mockEnvConfig: EnvConfig.Interface;\n\n beforeEach(() => {\n container = new Container();\n\n mockEnvConfig = {\n get: vi.fn((key: string) => {\n if (key === \"graphqlApiUrl\") {\n return \"https://api.example.com/graphql\";\n }\n return undefined;\n })\n } as any;\n\n container.registerInstance(EnvConfig, mockEnvConfig);\n\n /**\n * The client announces response extensions through the publisher, so it has to resolve.\n */\n container.registerInstance(EventPublisher, { publish: vi.fn(async () => undefined) });\n });\n\n describe(\"FetchGraphQLClient\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n });\n\n it(\"should execute a query successfully\", async () => {\n const mockResponse = {\n data: { user: { id: \"1\", name: \"John\" } }\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id name } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n expect(global.fetch).toHaveBeenCalledWith(\n \"https://api.example.com/graphql\",\n expect.objectContaining({\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" }\n })\n );\n });\n\n it(\"should execute a mutation successfully\", async () => {\n const mockResponse = {\n data: { createUser: { id: \"2\", name: \"Jane\" } }\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"mutation CreateUser($name: String!) { createUser(name: $name) { id name } }\",\n variables: { name: \"Jane\" }\n });\n\n expect(result).toEqual(mockResponse.data);\n });\n\n it(\"should include custom headers\", async () => {\n const mockResponse = { data: { user: { id: \"1\" } } };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id } }\",\n headers: { \"x-tenant\": \"root\", Authorization: \"Bearer token\" }\n });\n\n expect(global.fetch).toHaveBeenCalledWith(\n \"https://api.example.com/graphql\",\n expect.objectContaining({\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-tenant\": \"root\",\n Authorization: \"Bearer token\"\n }\n })\n );\n });\n\n it(\"should throw on network error\", async () => {\n global.fetch = vi.fn().mockRejectedValue(new Error(\"Network failure\"));\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"Network error: Network failure\");\n });\n\n it(\"should throw on GraphQL errors\", async () => {\n const mockResponse = {\n errors: [{ message: \"User not found\" }],\n data: null\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"GraphQL errors\");\n });\n\n it(\"should return partial data when the response carries both data and errors\", async () => {\n const mockResponse = {\n data: { content: { data: [{ id: \"1\" }, { id: \"2\" }] } },\n errors: [{ message: \"Cannot return null for non-nullable field RefField.id.\" }]\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { content { data { id } } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n });\n\n it(\"should throw on invalid JSON response\", async () => {\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => {\n throw new Error(\"Invalid JSON\");\n }\n });\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"Failed to parse GraphQL response as JSON\");\n });\n });\n\n describe(\"BatchingGraphQLClient\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n container.registerDecorator(BatchingGraphQLClient);\n });\n\n it(\"should batch multiple requests within the batch window\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\", name: \"John\" } } },\n { data: { post: { id: \"2\", title: \"Hello\" } } }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const [result1, result2] = await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id name } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetPost { post { id title } }\"\n })\n ]);\n\n expect(result1).toEqual(mockResponse[0].data);\n expect(result2).toEqual(mockResponse[1].data);\n expect(global.fetch).toHaveBeenCalledTimes(1);\n\n const callBody = JSON.parse((global.fetch as any).mock.calls[0][1].body);\n expect(callBody).toHaveLength(2);\n });\n\n it(\"should execute single request without batching\", async () => {\n const mockResponse = {\n data: { user: { id: \"1\", name: \"John\" } }\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query GetUser { user { id name } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n\n // Should use decoratee directly for single request\n const callBody = JSON.parse((global.fetch as any).mock.calls[0][1].body);\n expect(callBody).toHaveProperty(\"query\");\n expect(callBody).not.toBeInstanceOf(Array);\n });\n\n it(\"should handle mixed queries and mutations in batch\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\" } } },\n { data: { createPost: { id: \"2\" } } }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"mutation { createPost { id } }\"\n })\n ]);\n\n expect(global.fetch).toHaveBeenCalledTimes(1);\n const callBody = JSON.parse((global.fetch as any).mock.calls[0][1].body);\n expect(callBody[0]).toHaveProperty(\"query\");\n expect(callBody[1]).toHaveProperty(\"query\");\n });\n\n it(\"should reject all requests if batch fails\", async () => {\n global.fetch = vi.fn().mockRejectedValue(new Error(\"Network error\"));\n\n const client = container.resolve(GraphQLClient);\n\n const promises = [\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { post { id } }\"\n })\n ];\n\n await expect(Promise.all(promises)).rejects.toThrow(\"Network error\");\n });\n\n it(\"should handle GraphQL errors in batched operations\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\" } } },\n { errors: [{ message: \"Post not found\" }], data: null }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const promises = [\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { post { id } }\"\n })\n ];\n\n await expect(Promise.all(promises)).rejects.toThrow(\"GraphQL errors in operation 1\");\n });\n\n it(\"should return partial data for a batched operation that has data and errors\", async () => {\n const mockResponse = [\n { data: { user: { id: \"1\" } } },\n {\n data: { content: { data: [{ id: \"2\" }] } },\n errors: [{ message: \"Cannot return null for non-nullable field RefField.id.\" }]\n }\n ];\n\n global.fetch = vi.fn().mockResolvedValue({\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const [result1, result2] = await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { content { data { id } } }\"\n })\n ]);\n\n expect(result1).toEqual(mockResponse[0].data);\n expect(result2).toEqual(mockResponse[1].data);\n });\n });\n\n describe(\"RetryGraphQLClient\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n container.registerDecorator(RetryGraphQLClient);\n });\n\n it(\"should retry on network errors\", async () => {\n const mockResponse = { data: { user: { id: \"1\" } } };\n\n global.fetch = vi\n .fn()\n .mockRejectedValueOnce(new Error(\"Network timeout\"))\n .mockRejectedValueOnce(new Error(\"Network timeout\"))\n .mockResolvedValueOnce({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n const result = await client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n });\n\n expect(result).toEqual(mockResponse.data);\n expect(global.fetch).toHaveBeenCalledTimes(3);\n });\n\n it(\"should not retry on GraphQL errors\", async () => {\n const mockResponse = {\n errors: [{ message: \"Unauthorized\" }],\n data: null\n };\n\n global.fetch = vi.fn().mockResolvedValue({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"GraphQL errors\");\n\n expect(global.fetch).toHaveBeenCalledTimes(1);\n });\n\n it(\"should give up after max retries\", async () => {\n global.fetch = vi.fn().mockRejectedValue(new Error(\"Network error\"));\n\n const client = container.resolve(GraphQLClient);\n\n await expect(\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n })\n ).rejects.toThrow(\"Network error\");\n\n // Should try 4 times: initial + 3 retries\n expect(global.fetch).toHaveBeenCalledTimes(4);\n });\n });\n\n describe(\"Combined decorators (Retry + Batching)\", () => {\n beforeEach(() => {\n container.register(FetchGraphQLClient).inSingletonScope();\n container.registerDecorator(BatchingGraphQLClient);\n container.registerDecorator(RetryGraphQLClient);\n });\n\n it(\"should retry batched requests on failure\", async () => {\n const mockResponse = [{ data: { user: { id: \"1\" } } }, { data: { post: { id: \"2\" } } }];\n\n global.fetch = vi\n .fn()\n .mockRejectedValueOnce(new Error(\"Network error\"))\n .mockResolvedValueOnce({\n status: 200,\n json: async () => mockResponse\n });\n\n const client = container.resolve(GraphQLClient);\n\n const [result1, result2] = await Promise.all([\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { user { id } }\"\n }),\n client.execute({\n endpoint: \"https://api.example.com/graphql\",\n query: \"query { post { id } }\"\n })\n ]);\n\n expect(result1).toEqual(mockResponse[0].data);\n expect(result2).toEqual(mockResponse[1].data);\n expect(global.fetch).toHaveBeenCalledTimes(2);\n });\n });\n});\n"],"names":["describe","container","mockEnvConfig","beforeEach","Container","vi","key","EnvConfig","EventPublisher","undefined","FetchGraphQLClient","it","mockResponse","global","client","GraphQLClient","result","expect","Error","BatchingGraphQLClient","result1","result2","Promise","callBody","JSON","Array","promises","RetryGraphQLClient"],"mappings":";;;;;;;;;AASAA,SAAS,yBAAyB;IAC9B,IAAIC;IACJ,IAAIC;IAEJC,WAAW;QACPF,YAAY,IAAIG;QAEhBF,gBAAgB;YACZ,KAAKG,GAAG,EAAE,CAAC,CAACC;gBACR,IAAIA,AAAQ,oBAARA,KACA,OAAO;YAGf;QACJ;QAEAL,UAAU,gBAAgB,CAACM,WAAWL;QAKtCD,UAAU,gBAAgB,CAACO,gBAAgB;YAAE,SAASH,GAAG,EAAE,CAAC,UAAYI;QAAW;IACvF;IAEAT,SAAS,sBAAsB;QAC3BG,WAAW;YACPF,UAAU,QAAQ,CAACS,oBAAoB,gBAAgB;QAC3D;QAEAC,GAAG,uCAAuC;YACtC,MAAMC,eAAe;gBACjB,MAAM;oBAAE,MAAM;wBAAE,IAAI;wBAAK,MAAM;oBAAO;gBAAE;YAC5C;YAEAC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YACjC,MAAMC,SAAS,MAAMF,OAAO,OAAO,CAAC;gBAChC,UAAU;gBACV,OAAO;YACX;YAEAG,OAAOD,QAAQ,OAAO,CAACJ,aAAa,IAAI;YACxCK,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,oBAAoB,CACrC,mCACAI,OAAO,gBAAgB,CAAC;gBACpB,QAAQ;gBACR,SAAS;oBAAE,gBAAgB;gBAAmB;YAClD;QAER;QAEAN,GAAG,0CAA0C;YACzC,MAAMC,eAAe;gBACjB,MAAM;oBAAE,YAAY;wBAAE,IAAI;wBAAK,MAAM;oBAAO;gBAAE;YAClD;YAEAC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YACjC,MAAMC,SAAS,MAAMF,OAAO,OAAO,CAAC;gBAChC,UAAU;gBACV,OAAO;gBACP,WAAW;oBAAE,MAAM;gBAAO;YAC9B;YAEAG,OAAOD,QAAQ,OAAO,CAACJ,aAAa,IAAI;QAC5C;QAEAD,GAAG,iCAAiC;YAChC,MAAMC,eAAe;gBAAE,MAAM;oBAAE,MAAM;wBAAE,IAAI;oBAAI;gBAAE;YAAE;YAEnDC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YACjC,MAAMD,OAAO,OAAO,CAAC;gBACjB,UAAU;gBACV,OAAO;gBACP,SAAS;oBAAE,YAAY;oBAAQ,eAAe;gBAAe;YACjE;YAEAG,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,oBAAoB,CACrC,mCACAI,OAAO,gBAAgB,CAAC;gBACpB,SAAS;oBACL,gBAAgB;oBAChB,YAAY;oBACZ,eAAe;gBACnB;YACJ;QAER;QAEAN,GAAG,iCAAiC;YAChCE,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC,IAAIa,MAAM;YAEnD,MAAMJ,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAME,OACFH,OAAO,OAAO,CAAC;gBACX,UAAU;gBACV,OAAO;YACX,IACF,OAAO,CAAC,OAAO,CAAC;QACtB;QAEAH,GAAG,kCAAkC;YACjC,MAAMC,eAAe;gBACjB,QAAQ;oBAAC;wBAAE,SAAS;oBAAiB;iBAAE;gBACvC,MAAM;YACV;YAEAC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAME,OACFH,OAAO,OAAO,CAAC;gBACX,UAAU;gBACV,OAAO;YACX,IACF,OAAO,CAAC,OAAO,CAAC;QACtB;QAEAH,GAAG,6EAA6E;YAC5E,MAAMC,eAAe;gBACjB,MAAM;oBAAE,SAAS;wBAAE,MAAM;4BAAC;gCAAE,IAAI;4BAAI;4BAAG;gCAAE,IAAI;4BAAI;yBAAE;oBAAC;gBAAE;gBACtD,QAAQ;oBAAC;wBAAE,SAAS;oBAAyD;iBAAE;YACnF;YAEAC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YACjC,MAAMC,SAAS,MAAMF,OAAO,OAAO,CAAC;gBAChC,UAAU;gBACV,OAAO;YACX;YAEAG,OAAOD,QAAQ,OAAO,CAACJ,aAAa,IAAI;QAC5C;QAEAD,GAAG,yCAAyC;YACxCE,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,MAAM;oBACF,MAAM,IAAIa,MAAM;gBACpB;YACJ;YAEA,MAAMJ,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAME,OACFH,OAAO,OAAO,CAAC;gBACX,UAAU;gBACV,OAAO;YACX,IACF,OAAO,CAAC,OAAO,CAAC;QACtB;IACJ;IAEAd,SAAS,yBAAyB;QAC9BG,WAAW;YACPF,UAAU,QAAQ,CAACS,oBAAoB,gBAAgB;YACvDT,UAAU,iBAAiB,CAACkB;QAChC;QAEAR,GAAG,0DAA0D;YACzD,MAAMC,eAAe;gBACjB;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;4BAAK,MAAM;wBAAO;oBAAE;gBAAE;gBAC5C;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;4BAAK,OAAO;wBAAQ;oBAAE;gBAAE;aACjD;YAEDC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAM,CAACK,SAASC,QAAQ,GAAG,MAAMC,QAAQ,GAAG,CAAC;gBACzCR,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;gBACAA,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;aACH;YAEDG,OAAOG,SAAS,OAAO,CAACR,YAAY,CAAC,EAAE,CAAC,IAAI;YAC5CK,OAAOI,SAAS,OAAO,CAACT,YAAY,CAAC,EAAE,CAAC,IAAI;YAC5CK,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,qBAAqB,CAAC;YAE3C,MAAMU,WAAWC,KAAK,KAAK,CAAEX,oBAAAA,CAAMA,CAAC,KAAK,CAAS,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI;YACvEI,OAAOM,UAAU,YAAY,CAAC;QAClC;QAEAZ,GAAG,kDAAkD;YACjD,MAAMC,eAAe;gBACjB,MAAM;oBAAE,MAAM;wBAAE,IAAI;wBAAK,MAAM;oBAAO;gBAAE;YAC5C;YAEAC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YACjC,MAAMC,SAAS,MAAMF,OAAO,OAAO,CAAC;gBAChC,UAAU;gBACV,OAAO;YACX;YAEAG,OAAOD,QAAQ,OAAO,CAACJ,aAAa,IAAI;YAGxC,MAAMW,WAAWC,KAAK,KAAK,CAAEX,oBAAAA,CAAMA,CAAC,KAAK,CAAS,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI;YACvEI,OAAOM,UAAU,cAAc,CAAC;YAChCN,OAAOM,UAAU,GAAG,CAAC,cAAc,CAACE;QACxC;QAEAd,GAAG,sDAAsD;YACrD,MAAMC,eAAe;gBACjB;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;wBAAI;oBAAE;gBAAE;gBAC9B;oBAAE,MAAM;wBAAE,YAAY;4BAAE,IAAI;wBAAI;oBAAE;gBAAE;aACvC;YAEDC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAMO,QAAQ,GAAG,CAAC;gBACdR,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;gBACAA,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;aACH;YAEDG,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,qBAAqB,CAAC;YAC3C,MAAMU,WAAWC,KAAK,KAAK,CAAEX,oBAAAA,CAAMA,CAAC,KAAK,CAAS,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI;YACvEI,OAAOM,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;YACnCN,OAAOM,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;QACvC;QAEAZ,GAAG,6CAA6C;YAC5CE,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC,IAAIa,MAAM;YAEnD,MAAMJ,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAMW,WAAW;gBACbZ,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;gBACAA,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;aACH;YAED,MAAMG,OAAOK,QAAQ,GAAG,CAACI,WAAW,OAAO,CAAC,OAAO,CAAC;QACxD;QAEAf,GAAG,sDAAsD;YACrD,MAAMC,eAAe;gBACjB;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;wBAAI;oBAAE;gBAAE;gBAC9B;oBAAE,QAAQ;wBAAC;4BAAE,SAAS;wBAAiB;qBAAE;oBAAE,MAAM;gBAAK;aACzD;YAEDC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAMW,WAAW;gBACbZ,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;gBACAA,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;aACH;YAED,MAAMG,OAAOK,QAAQ,GAAG,CAACI,WAAW,OAAO,CAAC,OAAO,CAAC;QACxD;QAEAf,GAAG,+EAA+E;YAC9E,MAAMC,eAAe;gBACjB;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;wBAAI;oBAAE;gBAAE;gBAC9B;oBACI,MAAM;wBAAE,SAAS;4BAAE,MAAM;gCAAC;oCAAE,IAAI;gCAAI;6BAAE;wBAAC;oBAAE;oBACzC,QAAQ;wBAAC;4BAAE,SAAS;wBAAyD;qBAAE;gBACnF;aACH;YAEDC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAM,CAACK,SAASC,QAAQ,GAAG,MAAMC,QAAQ,GAAG,CAAC;gBACzCR,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;gBACAA,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;aACH;YAEDG,OAAOG,SAAS,OAAO,CAACR,YAAY,CAAC,EAAE,CAAC,IAAI;YAC5CK,OAAOI,SAAS,OAAO,CAACT,YAAY,CAAC,EAAE,CAAC,IAAI;QAChD;IACJ;IAEAZ,SAAS,sBAAsB;QAC3BG,WAAW;YACPF,UAAU,QAAQ,CAACS,oBAAoB,gBAAgB;YACvDT,UAAU,iBAAiB,CAAC0B;QAChC;QAEAhB,GAAG,kCAAkC;YACjC,MAAMC,eAAe;gBAAE,MAAM;oBAAE,MAAM;wBAAE,IAAI;oBAAI;gBAAE;YAAE;YAEnDC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAAA,EACR,GACF,qBAAqB,CAAC,IAAIa,MAAM,oBAChC,qBAAqB,CAAC,IAAIA,MAAM,oBAChC,qBAAqB,CAAC;gBACnB,QAAQ;gBACR,MAAM,UAAYN;YACtB;YAEJ,MAAME,SAASb,UAAU,OAAO,CAACc;YACjC,MAAMC,SAAS,MAAMF,OAAO,OAAO,CAAC;gBAChC,UAAU;gBACV,OAAO;YACX;YAEAG,OAAOD,QAAQ,OAAO,CAACJ,aAAa,IAAI;YACxCK,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,qBAAqB,CAAC;QAC/C;QAEAF,GAAG,sCAAsC;YACrC,MAAMC,eAAe;gBACjB,QAAQ;oBAAC;wBAAE,SAAS;oBAAe;iBAAE;gBACrC,MAAM;YACV;YAEAC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,MAAM,UAAYO;YACtB;YAEA,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAME,OACFH,OAAO,OAAO,CAAC;gBACX,UAAU;gBACV,OAAO;YACX,IACF,OAAO,CAAC,OAAO,CAAC;YAElBG,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,qBAAqB,CAAC;QAC/C;QAEAF,GAAG,oCAAoC;YACnCE,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAG,EAAE,GAAG,iBAAiB,CAAC,IAAIa,MAAM;YAEnD,MAAMJ,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAME,OACFH,OAAO,OAAO,CAAC;gBACX,UAAU;gBACV,OAAO;YACX,IACF,OAAO,CAAC,OAAO,CAAC;YAGlBG,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,qBAAqB,CAAC;QAC/C;IACJ;IAEAb,SAAS,0CAA0C;QAC/CG,WAAW;YACPF,UAAU,QAAQ,CAACS,oBAAoB,gBAAgB;YACvDT,UAAU,iBAAiB,CAACkB;YAC5BlB,UAAU,iBAAiB,CAAC0B;QAChC;QAEAhB,GAAG,4CAA4C;YAC3C,MAAMC,eAAe;gBAAC;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;wBAAI;oBAAE;gBAAE;gBAAG;oBAAE,MAAM;wBAAE,MAAM;4BAAE,IAAI;wBAAI;oBAAE;gBAAE;aAAE;YAEvFC,oBAAAA,CAAMA,CAAC,KAAK,GAAGR,GAAAA,EACR,GACF,qBAAqB,CAAC,IAAIa,MAAM,kBAChC,qBAAqB,CAAC;gBACnB,QAAQ;gBACR,MAAM,UAAYN;YACtB;YAEJ,MAAME,SAASb,UAAU,OAAO,CAACc;YAEjC,MAAM,CAACK,SAASC,QAAQ,GAAG,MAAMC,QAAQ,GAAG,CAAC;gBACzCR,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;gBACAA,OAAO,OAAO,CAAC;oBACX,UAAU;oBACV,OAAO;gBACX;aACH;YAEDG,OAAOG,SAAS,OAAO,CAACR,YAAY,CAAC,EAAE,CAAC,IAAI;YAC5CK,OAAOI,SAAS,OAAO,CAACT,YAAY,CAAC,EAAE,CAAC,IAAI;YAC5CK,OAAOJ,oBAAAA,CAAMA,CAAC,KAAK,EAAE,qBAAqB,CAAC;QAC/C;IACJ;AACJ"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DocumentNode } from "graphql";
|
|
2
|
+
import type { IEventHandler } from "../../features/eventPublisher/index.js";
|
|
3
|
+
import type { GraphQLExtensionsEvent } from "./GraphQLExtensionsEvent.js";
|
|
2
4
|
type IHeaders = Record<string, string | number | undefined>;
|
|
3
5
|
type GraphQLRequest<TVariables = any> = {
|
|
4
6
|
endpoint: string;
|
|
@@ -15,4 +17,9 @@ export declare namespace GraphQLClient {
|
|
|
15
17
|
type Interface = IGraphQLClient;
|
|
16
18
|
type Request<TVariables = any> = GraphQLRequest<TVariables>;
|
|
17
19
|
}
|
|
20
|
+
export declare const GraphQLExtensionsEventHandler: import("@webiny/di").Abstraction<IEventHandler<GraphQLExtensionsEvent>>;
|
|
21
|
+
export declare namespace GraphQLExtensionsEventHandler {
|
|
22
|
+
type Interface = IEventHandler<GraphQLExtensionsEvent>;
|
|
23
|
+
type Event = GraphQLExtensionsEvent;
|
|
24
|
+
}
|
|
18
25
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createAbstraction } from "@webiny/feature/admin";
|
|
2
2
|
const GraphQLClient = createAbstraction("GraphQLClient");
|
|
3
|
-
|
|
3
|
+
const GraphQLExtensionsEventHandler = createAbstraction("GraphQLExtensionsEventHandler");
|
|
4
|
+
export { GraphQLClient, GraphQLExtensionsEventHandler };
|
|
4
5
|
|
|
5
6
|
//# sourceMappingURL=abstractions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/graphqlClient/abstractions.js","sources":["../../../src/features/graphqlClient/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { DocumentNode } from \"graphql\";\n\ntype IHeaders = Record<string, string | number | undefined>;\n\ntype GraphQLRequest<TVariables = any> = {\n endpoint: string;\n query: DocumentNode | string;\n variables?: TVariables;\n headers?: IHeaders;\n};\n\nexport interface IGraphQLClient {\n execute<TResult = any, TVariables = any>(params: GraphQLRequest<TVariables>): Promise<TResult>;\n}\nexport const GraphQLClient = createAbstraction<IGraphQLClient>(\"GraphQLClient\");\n\nexport namespace GraphQLClient {\n export type Headers = IHeaders;\n export type Interface = IGraphQLClient;\n export type Request<TVariables = any> = GraphQLRequest<TVariables>;\n}\n"],"names":["GraphQLClient","createAbstraction"],"mappings":";
|
|
1
|
+
{"version":3,"file":"features/graphqlClient/abstractions.js","sources":["../../../src/features/graphqlClient/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { DocumentNode } from \"graphql\";\nimport type { IEventHandler } from \"~/features/eventPublisher/index.js\";\nimport type { GraphQLExtensionsEvent } from \"./GraphQLExtensionsEvent.js\";\n\ntype IHeaders = Record<string, string | number | undefined>;\n\ntype GraphQLRequest<TVariables = any> = {\n endpoint: string;\n query: DocumentNode | string;\n variables?: TVariables;\n headers?: IHeaders;\n};\n\nexport interface IGraphQLClient {\n execute<TResult = any, TVariables = any>(params: GraphQLRequest<TVariables>): Promise<TResult>;\n}\nexport const GraphQLClient = createAbstraction<IGraphQLClient>(\"GraphQLClient\");\n\nexport namespace GraphQLClient {\n export type Headers = IHeaders;\n export type Interface = IGraphQLClient;\n export type Request<TVariables = any> = GraphQLRequest<TVariables>;\n}\n\nexport const GraphQLExtensionsEventHandler = createAbstraction<\n IEventHandler<GraphQLExtensionsEvent>\n>(\"GraphQLExtensionsEventHandler\");\n\nexport namespace GraphQLExtensionsEventHandler {\n export type Interface = IEventHandler<GraphQLExtensionsEvent>;\n export type Event = GraphQLExtensionsEvent;\n}\n"],"names":["GraphQLClient","createAbstraction","GraphQLExtensionsEventHandler"],"mappings":";AAiBO,MAAMA,gBAAgBC,kBAAkC;AAQxD,MAAMC,gCAAgCD,kBAE3C"}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { GraphQLClient } from "./abstractions.js";
|
|
1
|
+
export { GraphQLClient, GraphQLExtensionsEventHandler } from "./abstractions.js";
|
|
2
|
+
export { GraphQLExtensionsEvent } from "./GraphQLExtensionsEvent.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/app",
|
|
3
|
-
"version": "6.5.0-beta.
|
|
3
|
+
"version": "6.5.0-beta.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@emotion/styled": "11.14.1",
|
|
22
22
|
"@types/react": "18.3.31",
|
|
23
|
-
"@webiny/di": "1.0
|
|
24
|
-
"@webiny/feature": "6.5.0-beta.
|
|
25
|
-
"@webiny/i18n": "6.5.0-beta.
|
|
26
|
-
"@webiny/i18n-react": "6.5.0-beta.
|
|
27
|
-
"@webiny/plugins": "6.5.0-beta.
|
|
28
|
-
"@webiny/react-composition": "6.5.0-beta.
|
|
29
|
-
"@webiny/react-properties": "6.5.0-beta.
|
|
30
|
-
"@webiny/stdlib": "0.0.
|
|
23
|
+
"@webiny/di": "1.1.0",
|
|
24
|
+
"@webiny/feature": "6.5.0-beta.10",
|
|
25
|
+
"@webiny/i18n": "6.5.0-beta.10",
|
|
26
|
+
"@webiny/i18n-react": "6.5.0-beta.10",
|
|
27
|
+
"@webiny/plugins": "6.5.0-beta.10",
|
|
28
|
+
"@webiny/react-composition": "6.5.0-beta.10",
|
|
29
|
+
"@webiny/react-properties": "6.5.0-beta.10",
|
|
30
|
+
"@webiny/stdlib": "0.0.19",
|
|
31
31
|
"apollo-cache": "1.3.5",
|
|
32
32
|
"apollo-cache-inmemory": "1.6.6",
|
|
33
33
|
"apollo-client": "2.6.10",
|
|
@@ -40,22 +40,22 @@
|
|
|
40
40
|
"history": "5.3.0",
|
|
41
41
|
"invariant": "2.2.4",
|
|
42
42
|
"lodash": "4.18.1",
|
|
43
|
-
"mobx": "
|
|
44
|
-
"nanoid": "6.0.
|
|
43
|
+
"mobx": "7.0.6",
|
|
44
|
+
"nanoid": "6.0.1",
|
|
45
45
|
"react": "18.3.1",
|
|
46
46
|
"react-dom": "18.3.1",
|
|
47
47
|
"ts-invariant": "0.10.3",
|
|
48
48
|
"warning": "4.0.3",
|
|
49
|
-
"zod": "4.
|
|
49
|
+
"zod": "4.6.5"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@types/lodash": "4.17.
|
|
52
|
+
"@types/lodash": "4.17.25",
|
|
53
53
|
"@types/warning": "3.0.4",
|
|
54
|
-
"@webiny/build-tools": "6.5.0-beta.
|
|
54
|
+
"@webiny/build-tools": "6.5.0-beta.10",
|
|
55
55
|
"rimraf": "6.1.3",
|
|
56
|
-
"type-fest": "5.
|
|
56
|
+
"type-fest": "5.10.0",
|
|
57
57
|
"typescript": "7.0.2",
|
|
58
|
-
"vitest": "
|
|
58
|
+
"vitest": "5.0.3"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
@@ -19,7 +19,7 @@ class AddQuerySelectionPlugin extends ApolloLinkPlugin {
|
|
|
19
19
|
const { addSelection, selectionPath } = this.config;
|
|
20
20
|
const firstQueryDefinition = document.definitions[0];
|
|
21
21
|
if (!firstQueryDefinition) return;
|
|
22
|
-
if (!firstQueryDefinition.selectionSet) return;
|
|
22
|
+
else if (!firstQueryDefinition.selectionSet) return;
|
|
23
23
|
let tree = firstQueryDefinition.selectionSet.selections;
|
|
24
24
|
const fields = selectionPath.split(".");
|
|
25
25
|
fieldLoop: for (const field of fields){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins/AddQuerySelectionPlugin.js","sources":["../../src/plugins/AddQuerySelectionPlugin.ts"],"sourcesContent":["import { ApolloLink } from \"apollo-link\";\nimport type { DocumentNode } from \"graphql\";\nimport { ApolloLinkPlugin } from \"./ApolloLinkPlugin.js\";\nimport type { ExecutableDefinitionNode, FieldNode } from \"graphql/language/ast.js\";\n\ndeclare module \"graphql\" {\n interface DocumentNode {\n __webiny__: Set<string>;\n }\n}\n\ninterface Config {\n operationName: string;\n selectionPath: string;\n addSelection: DocumentNode;\n}\n\nexport class AddQuerySelectionPlugin extends ApolloLinkPlugin {\n private readonly config: Config;\n\n constructor(config: Config) {\n super();\n this.config = config;\n }\n\n public override createLink(): ApolloLink {\n return new ApolloLink((operation, forward) => {\n if (operation.operationName !== this.config.operationName) {\n return forward(operation);\n }\n\n this.addSelectionToQuery(operation.operationName, operation.query);\n\n return forward(operation);\n });\n }\n\n public addSelectionToQuery(operationName: string, document: DocumentNode): void {\n if (operationName !== this.config.operationName) {\n return;\n }\n\n // If this plugin already processed the given document (documents are always passed by reference),\n // then we don't want to apply the selection again, to avoid adding duplicate selections.\n if (this.isProcessed(document)) {\n return;\n }\n\n this.markProcessed(document);\n\n const { addSelection, selectionPath } = this.config;\n\n const firstQueryDefinition = document.definitions[0] as ExecutableDefinitionNode;\n if (!firstQueryDefinition) {\n return;\n } else if (!firstQueryDefinition.selectionSet) {\n return;\n }\n\n let tree = firstQueryDefinition.selectionSet.selections as FieldNode[];\n const fields = selectionPath.split(\".\");\n\n fieldLoop: for (const field of fields) {\n for (const selection of tree) {\n if (!selection.selectionSet) {\n continue;\n }\n if (selection.name.value === field) {\n tree = selection.selectionSet.selections as FieldNode[];\n continue fieldLoop;\n }\n }\n // If we get here, it means we didn't find the necessary selection\n return;\n }\n /**\n * We must cast because there are a lot of types that are not intertwined and TS is complaining\n */\n tree.push(\n ...((addSelection.definitions[0] as ExecutableDefinitionNode).selectionSet\n .selections as FieldNode[])\n );\n }\n\n private isProcessed(document: DocumentNode) {\n if (!document.hasOwnProperty(\"__webiny__\")) {\n document.__webiny__ = new Set();\n }\n\n return document.__webiny__.has(this.cacheKey);\n }\n\n private markProcessed(document: DocumentNode) {\n if (!document.hasOwnProperty(\"__webiny__\")) {\n document.__webiny__ = new Set();\n }\n\n document.__webiny__.add(this.cacheKey);\n }\n}\n"],"names":["AddQuerySelectionPlugin","ApolloLinkPlugin","config","ApolloLink","operation","forward","operationName","document","addSelection","selectionPath","firstQueryDefinition","tree","fields","fieldLoop","field","selection","Set"],"mappings":";;AAiBO,MAAMA,gCAAgCC;IAGzC,YAAYC,MAAc,CAAE;QACxB,KAAK;QACL,IAAI,CAAC,MAAM,GAAGA;IAClB;IAEgB,aAAyB;QACrC,OAAO,IAAIC,WAAW,CAACC,WAAWC;YAC9B,IAAID,UAAU,aAAa,KAAK,IAAI,CAAC,MAAM,CAAC,aAAa,EACrD,OAAOC,QAAQD;YAGnB,IAAI,CAAC,mBAAmB,CAACA,UAAU,aAAa,EAAEA,UAAU,KAAK;YAEjE,OAAOC,QAAQD;QACnB;IACJ;IAEO,oBAAoBE,aAAqB,EAAEC,QAAsB,EAAQ;QAC5E,IAAID,kBAAkB,IAAI,CAAC,MAAM,CAAC,aAAa,EAC3C;QAKJ,IAAI,IAAI,CAAC,WAAW,CAACC,WACjB;QAGJ,IAAI,CAAC,aAAa,CAACA;QAEnB,MAAM,EAAEC,YAAY,EAAEC,aAAa,EAAE,GAAG,IAAI,CAAC,MAAM;QAEnD,MAAMC,uBAAuBH,SAAS,WAAW,CAAC,EAAE;QACpD,IAAI,CAACG,sBACD;
|
|
1
|
+
{"version":3,"file":"plugins/AddQuerySelectionPlugin.js","sources":["../../src/plugins/AddQuerySelectionPlugin.ts"],"sourcesContent":["import { ApolloLink } from \"apollo-link\";\nimport type { DocumentNode } from \"graphql\";\nimport { ApolloLinkPlugin } from \"./ApolloLinkPlugin.js\";\nimport type { ExecutableDefinitionNode, FieldNode } from \"graphql/language/ast.js\";\n\ndeclare module \"graphql\" {\n interface DocumentNode {\n __webiny__: Set<string>;\n }\n}\n\ninterface Config {\n operationName: string;\n selectionPath: string;\n addSelection: DocumentNode;\n}\n\nexport class AddQuerySelectionPlugin extends ApolloLinkPlugin {\n private readonly config: Config;\n\n constructor(config: Config) {\n super();\n this.config = config;\n }\n\n public override createLink(): ApolloLink {\n return new ApolloLink((operation, forward) => {\n if (operation.operationName !== this.config.operationName) {\n return forward(operation);\n }\n\n this.addSelectionToQuery(operation.operationName, operation.query);\n\n return forward(operation);\n });\n }\n\n public addSelectionToQuery(operationName: string, document: DocumentNode): void {\n if (operationName !== this.config.operationName) {\n return;\n }\n\n // If this plugin already processed the given document (documents are always passed by reference),\n // then we don't want to apply the selection again, to avoid adding duplicate selections.\n if (this.isProcessed(document)) {\n return;\n }\n\n this.markProcessed(document);\n\n const { addSelection, selectionPath } = this.config;\n\n const firstQueryDefinition = document.definitions[0] as ExecutableDefinitionNode;\n if (!firstQueryDefinition) {\n return;\n } else if (!firstQueryDefinition.selectionSet) {\n return;\n }\n\n let tree = firstQueryDefinition.selectionSet.selections as FieldNode[];\n const fields = selectionPath.split(\".\");\n\n fieldLoop: for (const field of fields) {\n for (const selection of tree) {\n if (!selection.selectionSet) {\n continue;\n }\n if (selection.name.value === field) {\n tree = selection.selectionSet.selections as FieldNode[];\n continue fieldLoop;\n }\n }\n // If we get here, it means we didn't find the necessary selection\n return;\n }\n /**\n * We must cast because there are a lot of types that are not intertwined and TS is complaining\n */\n tree.push(\n ...((addSelection.definitions[0] as ExecutableDefinitionNode).selectionSet\n .selections as FieldNode[])\n );\n }\n\n private isProcessed(document: DocumentNode) {\n if (!document.hasOwnProperty(\"__webiny__\")) {\n document.__webiny__ = new Set();\n }\n\n return document.__webiny__.has(this.cacheKey);\n }\n\n private markProcessed(document: DocumentNode) {\n if (!document.hasOwnProperty(\"__webiny__\")) {\n document.__webiny__ = new Set();\n }\n\n document.__webiny__.add(this.cacheKey);\n }\n}\n"],"names":["AddQuerySelectionPlugin","ApolloLinkPlugin","config","ApolloLink","operation","forward","operationName","document","addSelection","selectionPath","firstQueryDefinition","tree","fields","fieldLoop","field","selection","Set"],"mappings":";;AAiBO,MAAMA,gCAAgCC;IAGzC,YAAYC,MAAc,CAAE;QACxB,KAAK;QACL,IAAI,CAAC,MAAM,GAAGA;IAClB;IAEgB,aAAyB;QACrC,OAAO,IAAIC,WAAW,CAACC,WAAWC;YAC9B,IAAID,UAAU,aAAa,KAAK,IAAI,CAAC,MAAM,CAAC,aAAa,EACrD,OAAOC,QAAQD;YAGnB,IAAI,CAAC,mBAAmB,CAACA,UAAU,aAAa,EAAEA,UAAU,KAAK;YAEjE,OAAOC,QAAQD;QACnB;IACJ;IAEO,oBAAoBE,aAAqB,EAAEC,QAAsB,EAAQ;QAC5E,IAAID,kBAAkB,IAAI,CAAC,MAAM,CAAC,aAAa,EAC3C;QAKJ,IAAI,IAAI,CAAC,WAAW,CAACC,WACjB;QAGJ,IAAI,CAAC,aAAa,CAACA;QAEnB,MAAM,EAAEC,YAAY,EAAEC,aAAa,EAAE,GAAG,IAAI,CAAC,MAAM;QAEnD,MAAMC,uBAAuBH,SAAS,WAAW,CAAC,EAAE;QACpD,IAAI,CAACG,sBACD;aACG,IAAI,CAACA,qBAAqB,YAAY,EACzC;QAGJ,IAAIC,OAAOD,qBAAqB,YAAY,CAAC,UAAU;QACvD,MAAME,SAASH,cAAc,KAAK,CAAC;QAEnCI,WAAW,KAAK,MAAMC,SAASF,OAAQ;YACnC,KAAK,MAAMG,aAAaJ,KACpB,IAAKI,UAAU,YAAY,EAG3B;gBAAA,IAAIA,UAAU,IAAI,CAAC,KAAK,KAAKD,OAAO;oBAChCH,OAAOI,UAAU,YAAY,CAAC,UAAU;oBACxC,SAASF;gBACb;YAAA;YAGJ;QACJ;QAIAF,KAAK,IAAI,IACAH,aAAa,WAAW,CAAC,EAAE,CAA8B,YAAY,CACrE,UAAU;IAEvB;IAEQ,YAAYD,QAAsB,EAAE;QACxC,IAAI,CAACA,SAAS,cAAc,CAAC,eACzBA,SAAS,UAAU,GAAG,IAAIS;QAG9B,OAAOT,SAAS,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ;IAChD;IAEQ,cAAcA,QAAsB,EAAE;QAC1C,IAAI,CAACA,SAAS,cAAc,CAAC,eACzBA,SAAS,UAAU,GAAG,IAAIS;QAG9BT,SAAS,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ;IACzC;AACJ"}
|