@graphql-hive/router-runtime 1.1.0-rc-924ffb15d84292a020a45655046fd359ea9f02d5 → 1.1.0-rc-aa2a330ec509cf750e8722741cb18aed3ad01793

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @graphql-hive/router-runtime
2
2
 
3
- ## 1.1.0-rc-924ffb15d84292a020a45655046fd359ea9f02d5
3
+ ## 1.1.0-rc-aa2a330ec509cf750e8722741cb18aed3ad01793
4
4
  ### Minor Changes
5
5
 
6
6
 
@@ -17,6 +17,11 @@
17
17
  - Updated dependency [`@graphql-hive/router-query-planner@^0.0.4` ↗︎](https://www.npmjs.com/package/@graphql-hive/router-query-planner/v/0.0.4) (from `^0.0.3`, in `dependencies`)
18
18
 
19
19
 
20
+ - [#1739](https://github.com/graphql-hive/gateway/pull/1739) [`8ff2e47`](https://github.com/graphql-hive/gateway/commit/8ff2e471f368d5e41f91a7fe1f1b0e494ef3e6ff) Thanks [@enisdenjo](https://github.com/enisdenjo)! - dependencies updates:
21
+
22
+ - Updated dependency [`@graphql-hive/router-query-planner@^0.0.6` ↗︎](https://www.npmjs.com/package/@graphql-hive/router-query-planner/v/0.0.6) (from `^0.0.4`, in `dependencies`)
23
+
24
+
20
25
  - [#1740](https://github.com/graphql-hive/gateway/pull/1740) [`9cfe2a5`](https://github.com/graphql-hive/gateway/commit/9cfe2a555fcbc9a70ba04b32d6844a7a795de624) Thanks [@dependabot](https://github.com/apps/dependabot)! - dependencies updates:
21
26
 
22
27
  - Updated dependency [`@graphql-hive/router-query-planner@^0.0.6` ↗︎](https://www.npmjs.com/package/@graphql-hive/router-query-planner/v/0.0.6) (from `^0.0.4`, in `dependencies`)
@@ -24,9 +29,13 @@
24
29
 
25
30
  - [#1708](https://github.com/graphql-hive/gateway/pull/1708) [`bc6cddd`](https://github.com/graphql-hive/gateway/commit/bc6cddd1c53a012dd02a1d8a7217a28e65cc6ae9) Thanks [@ardatan](https://github.com/ardatan)! - Handle listed enum values correctly
26
31
  Previously when a field like `[MyEnum!]!` is projected, it was projecting it like it is `MyEnum`.
32
+
33
+
34
+ - [#1739](https://github.com/graphql-hive/gateway/pull/1739) [`8ff2e47`](https://github.com/graphql-hive/gateway/commit/8ff2e471f368d5e41f91a7fe1f1b0e494ef3e6ff) Thanks [@enisdenjo](https://github.com/enisdenjo)! - Expose the query plan by using the `useQueryPlan` plugin
35
+
27
36
  - Updated dependencies [[`bc6cddd`](https://github.com/graphql-hive/gateway/commit/bc6cddd1c53a012dd02a1d8a7217a28e65cc6ae9)]:
28
- - @graphql-mesh/fusion-runtime@1.6.0-rc-924ffb15d84292a020a45655046fd359ea9f02d5
29
- - @graphql-tools/federation@4.2.4-rc-924ffb15d84292a020a45655046fd359ea9f02d5
37
+ - @graphql-mesh/fusion-runtime@1.6.0-rc-aa2a330ec509cf750e8722741cb18aed3ad01793
38
+ - @graphql-tools/federation@4.2.4-rc-aa2a330ec509cf750e8722741cb18aed3ad01793
30
39
 
31
40
  ## 1.0.1
32
41
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -1071,6 +1071,7 @@ function projectRequires(requiresSelections, entity, supergraphSchema) {
1071
1071
  return result;
1072
1072
  }
1073
1073
 
1074
+ const queryPlanForExecutionRequestContext = /* @__PURE__ */ new WeakMap();
1074
1075
  function getLazyValue(factory) {
1075
1076
  let _value;
1076
1077
  return function() {
@@ -1141,52 +1142,59 @@ function unifiedGraphHandler(opts) {
1141
1142
  executionRequest.document,
1142
1143
  executionRequest.operationName || null
1143
1144
  ),
1144
- (queryPlan) => executeQueryPlan({
1145
- supergraphSchema,
1146
- executionRequest,
1147
- onSubgraphExecute(subgraphName, executionRequest2) {
1148
- const subschema = getSubschema(subgraphName);
1149
- if (subschema.transforms?.length) {
1150
- const transforms = subschema.transforms;
1151
- const transformationContext = /* @__PURE__ */ Object.create(null);
1152
- for (const transform of transforms) {
1153
- if (transform.transformRequest) {
1154
- executionRequest2 = transform.transformRequest(
1155
- executionRequest2,
1156
- void 0,
1157
- transformationContext
1158
- );
1145
+ (queryPlan) => {
1146
+ queryPlanForExecutionRequestContext.set(
1147
+ // setter like getter
1148
+ executionRequest.context || executionRequest.document,
1149
+ queryPlan
1150
+ );
1151
+ return executeQueryPlan({
1152
+ supergraphSchema,
1153
+ executionRequest,
1154
+ onSubgraphExecute(subgraphName, executionRequest2) {
1155
+ const subschema = getSubschema(subgraphName);
1156
+ if (subschema.transforms?.length) {
1157
+ const transforms = subschema.transforms;
1158
+ const transformationContext = /* @__PURE__ */ Object.create(null);
1159
+ for (const transform of transforms) {
1160
+ if (transform.transformRequest) {
1161
+ executionRequest2 = transform.transformRequest(
1162
+ executionRequest2,
1163
+ void 0,
1164
+ transformationContext
1165
+ );
1166
+ }
1159
1167
  }
1160
- }
1161
- return promiseHelpers.handleMaybePromise(
1162
- () => opts.onSubgraphExecute(subgraphName, executionRequest2),
1163
- (executionResult) => {
1164
- function handleResult(executionResult2) {
1165
- for (const transform of transforms.toReversed()) {
1166
- if (transform.transformResult) {
1167
- executionResult2 = transform.transformResult(
1168
- executionResult2,
1169
- void 0,
1170
- transformationContext
1171
- );
1168
+ return promiseHelpers.handleMaybePromise(
1169
+ () => opts.onSubgraphExecute(subgraphName, executionRequest2),
1170
+ (executionResult) => {
1171
+ function handleResult(executionResult2) {
1172
+ for (const transform of transforms.toReversed()) {
1173
+ if (transform.transformResult) {
1174
+ executionResult2 = transform.transformResult(
1175
+ executionResult2,
1176
+ void 0,
1177
+ transformationContext
1178
+ );
1179
+ }
1172
1180
  }
1181
+ return executionResult2;
1173
1182
  }
1174
- return executionResult2;
1175
- }
1176
- if (utils.isAsyncIterable(executionResult)) {
1177
- return promiseHelpers.mapAsyncIterator(
1178
- executionResult,
1179
- (result) => handleResult(result)
1180
- );
1183
+ if (utils.isAsyncIterable(executionResult)) {
1184
+ return promiseHelpers.mapAsyncIterator(
1185
+ executionResult,
1186
+ (result) => handleResult(result)
1187
+ );
1188
+ }
1189
+ return handleResult(executionResult);
1181
1190
  }
1182
- return handleResult(executionResult);
1183
- }
1184
- );
1185
- }
1186
- return opts.onSubgraphExecute(subgraphName, executionRequest2);
1187
- },
1188
- queryPlan
1189
- })
1191
+ );
1192
+ }
1193
+ return opts.onSubgraphExecute(subgraphName, executionRequest2);
1194
+ },
1195
+ queryPlan
1196
+ });
1197
+ }
1190
1198
  );
1191
1199
  }
1192
1200
  };
@@ -1215,4 +1223,32 @@ function isIntrospection(document) {
1215
1223
  return containsIntrospectionField || onlyQueryTypenameFields;
1216
1224
  }
1217
1225
 
1226
+ function useQueryPlan(opts = {}) {
1227
+ const { expose, onQueryPlan } = opts;
1228
+ return {
1229
+ onExecute({ context, args }) {
1230
+ return {
1231
+ onExecuteDone({ result, setResult }) {
1232
+ const queryPlan = queryPlanForExecutionRequestContext.get(
1233
+ // getter like setter
1234
+ context || args.document
1235
+ );
1236
+ onQueryPlan?.(queryPlan);
1237
+ const shouldExpose = typeof expose === "function" ? expose(context.request) : expose;
1238
+ if (shouldExpose && !utils.isAsyncIterable(result)) {
1239
+ setResult({
1240
+ ...result,
1241
+ extensions: {
1242
+ ...result.extensions,
1243
+ queryPlan
1244
+ }
1245
+ });
1246
+ }
1247
+ }
1248
+ };
1249
+ }
1250
+ };
1251
+ }
1252
+
1218
1253
  exports.unifiedGraphHandler = unifiedGraphHandler;
1254
+ exports.useQueryPlan = useQueryPlan;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,262 @@
1
- import { UnifiedGraphHandlerOpts, UnifiedGraphHandlerResult } from '@graphql-mesh/fusion-runtime';
1
+ import { UnifiedGraphHandlerOpts, UnifiedGraphHandlerResult, UnifiedGraphPlugin, Instrumentation as Instrumentation$2 } from '@graphql-mesh/fusion-runtime';
2
+ import { DisposableSymbols } from '@whatwg-node/disposablestack';
3
+ import { MaybePromise } from '@whatwg-node/promise-helpers';
4
+ import { Plugin, YogaInitialContext, Instrumentation as Instrumentation$1 } from 'graphql-yoga';
5
+ import { MeshFetch, KeyValueCache, MeshFetchRequestInit, Logger as Logger$1 } from '@graphql-mesh/types';
6
+ import { FetchInstrumentation } from '@graphql-mesh/utils';
7
+ import { ExecutionRequest, MaybePromise as MaybePromise$1 } from '@graphql-tools/utils';
8
+ import { GraphQLResolveInfo } from 'graphql/type';
9
+ import { QueryPlan } from '@graphql-hive/router-query-planner';
2
10
 
3
11
  declare function unifiedGraphHandler(opts: UnifiedGraphHandlerOpts): UnifiedGraphHandlerResult;
4
12
 
5
- export { unifiedGraphHandler };
13
+ type MaybeLazy<T> = T | (() => T);
14
+ type AttributeValue = any;
15
+ type Attributes = AttributeValue[] | {
16
+ [key: string | number]: AttributeValue;
17
+ };
18
+
19
+ interface LogWriter {
20
+ write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void | Promise<void>;
21
+ flush?(): void | Promise<void>;
22
+ }
23
+
24
+ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
25
+ interface LoggerOptions {
26
+ /**
27
+ * The minimum log level to log.
28
+ *
29
+ * Providing `false` will disable all logging.
30
+ *
31
+ * Provided function will always be invoked to get the current log level.
32
+ *
33
+ * @default env.LOG_LEVEL || env.DEBUG ? 'debug' : 'info'
34
+ */
35
+ level?: MaybeLazy<LogLevel | false>;
36
+ /** A prefix to include in every log's message. */
37
+ prefix?: string;
38
+ /**
39
+ * The attributes to include in all logs. Is mainly used to pass the parent
40
+ * attributes when creating {@link Logger.child child loggers}.
41
+ */
42
+ attrs?: Attributes;
43
+ /**
44
+ * The log writers to use when writing logs.
45
+ *
46
+ * @default env.LOG_JSON ? [new JSONLogWriter()] : [new ConsoleLogWriter()]
47
+ */
48
+ writers?: [LogWriter, ...LogWriter[]];
49
+ }
50
+ declare class Logger implements AsyncDisposable {
51
+ #private;
52
+ constructor(opts?: LoggerOptions);
53
+ /** The prefix that's prepended to each log message. */
54
+ get prefix(): string | undefined;
55
+ /**
56
+ * The attributes that are added to each log. If the log itself contains
57
+ * attributes with keys existing in {@link attrs}, the log's attributes will
58
+ * override.
59
+ */
60
+ get attrs(): Attributes | undefined;
61
+ /** The current {@link LogLevel} of the logger. You can change the level using the {@link setLevel} method. */
62
+ get level(): false | LogLevel;
63
+ /**
64
+ * Sets the new {@link LogLevel} of the logger. All subsequent logs, and {@link child child loggers} whose
65
+ * level did not change, will respect the new level.
66
+ */
67
+ setLevel(level: MaybeLazy<LogLevel | false>): void;
68
+ write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void;
69
+ flush(): Promise<void> | undefined;
70
+ [DisposableSymbols.asyncDispose](): Promise<void | undefined>;
71
+ child(prefix: string): Logger;
72
+ child(attrs: Attributes, prefix?: string): Logger;
73
+ log(level: LogLevel): void;
74
+ log(level: LogLevel, attrs: MaybeLazy<Attributes>): void;
75
+ log(level: LogLevel, msg: string, ...interpol: unknown[]): void;
76
+ log(level: LogLevel, attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
77
+ trace(): void;
78
+ trace(attrs: MaybeLazy<Attributes>): void;
79
+ trace(msg: string, ...interpol: unknown[]): void;
80
+ trace(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
81
+ debug(): void;
82
+ debug(attrs: MaybeLazy<Attributes>): void;
83
+ debug(msg: string, ...interpol: unknown[]): void;
84
+ debug(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
85
+ info(): void;
86
+ info(attrs: MaybeLazy<Attributes>): void;
87
+ info(msg: string, ...interpol: unknown[]): void;
88
+ info(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
89
+ warn(): void;
90
+ warn(attrs: MaybeLazy<Attributes>): void;
91
+ warn(msg: string, ...interpol: unknown[]): void;
92
+ warn(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
93
+ error(): void;
94
+ error(attrs: MaybeLazy<Attributes>): void;
95
+ error(msg: string, ...interpol: unknown[]): void;
96
+ error(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
97
+ }
98
+
99
+ type TopicDataMap = {
100
+ [topic: string]: any;
101
+ };
102
+ type PubSubListener<Data extends TopicDataMap, Topic extends keyof Data> = (data: Data[Topic]) => void;
103
+ interface PubSub<M extends TopicDataMap = TopicDataMap> {
104
+ /**
105
+ * Publish {@link data} for a {@link topic}.
106
+ * @returns `void` or a `Promise` that resolves when the data has been successfully published
107
+ */
108
+ publish<Topic extends keyof M>(topic: Topic, data: M[Topic]): MaybePromise<void>;
109
+ /**
110
+ * A distinct list of all topics that are currently subscribed to.
111
+ * Can be a promise to accomodate distributed systems where subscribers exist on other
112
+ * locations and we need to know about all of them.
113
+ */
114
+ subscribedTopics(): MaybePromise<Iterable<keyof M>>;
115
+ /**
116
+ * Subscribe and listen to a {@link topic} receiving its data.
117
+ *
118
+ * If the {@link listener} is provided, it will be called whenever data is emitted for the {@link topic},
119
+ *
120
+ * @returns an unsubscribe function or a `Promise<unsubscribe function>` that resolves when the subscription is successfully established. the unsubscribe function returns `void` or a `Promise` that resolves on successful unsubscribe and subscription cleanup
121
+ *
122
+ * If the {@link listener} is not provided,
123
+ *
124
+ * @returns an `AsyncIterable` that yields data for the given {@link topic}
125
+ */
126
+ subscribe<Topic extends keyof M>(topic: Topic): AsyncIterable<M[Topic]>;
127
+ subscribe<Topic extends keyof M>(topic: Topic, listener: PubSubListener<M, Topic>): MaybePromise<() => MaybePromise<void>>;
128
+ /**
129
+ * Closes active subscriptions and disposes of all resources. Publishing and subscribing after disposal
130
+ * is not possible and will throw an error if attempted.
131
+ */
132
+ dispose(): MaybePromise<void>;
133
+ /** @see {@link dispose} */
134
+ [DisposableSymbols.asyncDispose](): Promise<void>;
135
+ }
136
+
137
+ interface GatewayConfigContext {
138
+ /**
139
+ * WHATWG compatible Fetch implementation.
140
+ */
141
+ fetch: MeshFetch;
142
+ /**
143
+ * The logger to use throught Hive and its plugins.
144
+ */
145
+ log: Logger;
146
+ /**
147
+ * Current working directory.
148
+ * Note that working directory does not exist in serverless environments and will therefore be empty.
149
+ */
150
+ cwd: string;
151
+ /**
152
+ * Event bus for pub/sub.
153
+ */
154
+ pubsub?: PubSub;
155
+ /**
156
+ * Cache Storage
157
+ */
158
+ cache?: KeyValueCache;
159
+ }
160
+ interface GatewayContext extends GatewayConfigContext, YogaInitialContext {
161
+ /**
162
+ * Environment agnostic HTTP headers provided with the request.
163
+ */
164
+ headers: Record<string, string>;
165
+ /**
166
+ * Runtime context available within WebSocket connections.
167
+ */
168
+ connectionParams?: Record<string, string>;
169
+ }
170
+ type GatewayPlugin<TPluginContext extends Record<string, any> = Record<string, any>, TContext extends Record<string, any> = Record<string, any>> = Plugin<Partial<TPluginContext> & GatewayContext & TContext, GatewayConfigContext> & UnifiedGraphPlugin<Partial<TPluginContext> & GatewayContext & TContext> & {
171
+ onFetch?: OnFetchHook<Partial<TPluginContext> & TContext>;
172
+ onCacheGet?: OnCacheGetHook;
173
+ onCacheSet?: OnCacheSetHook;
174
+ onCacheDelete?: OnCacheDeleteHook;
175
+ /**
176
+ * An Instrumentation instance that will wrap each phases of the request pipeline.
177
+ * This should be used primarily as an observability tool (for monitoring, tracing, etc...).
178
+ *
179
+ * Note: The wrapped functions in instrumentation should always be called. Use hooks to
180
+ * conditionally skip a phase.
181
+ */
182
+ instrumentation?: Instrumentation<TPluginContext & TContext & GatewayContext>;
183
+ };
184
+ interface OnFetchHookPayload<TContext> {
185
+ url: string;
186
+ setURL(url: URL | string): void;
187
+ options: MeshFetchRequestInit;
188
+ setOptions(options: MeshFetchRequestInit): void;
189
+ /**
190
+ * The context is not available in cases where "fetch" is done in
191
+ * order to pull a supergraph or do some internal work.
192
+ *
193
+ * The logger will be available in all cases.
194
+ */
195
+ context: (GatewayContext & TContext) | {
196
+ log: Logger;
197
+ };
198
+ /** @deprecated Please use `log` from the {@link context} instead. */
199
+ logger: Logger$1;
200
+ info: GraphQLResolveInfo;
201
+ fetchFn: MeshFetch;
202
+ setFetchFn: (fetchFn: MeshFetch) => void;
203
+ executionRequest?: ExecutionRequest;
204
+ endResponse: (response$: MaybePromise$1<Response>) => void;
205
+ }
206
+ interface OnFetchHookDonePayload {
207
+ response: Response;
208
+ setResponse: (response: Response) => void;
209
+ }
210
+ type OnFetchHookDone = (payload: OnFetchHookDonePayload) => MaybePromise$1<void>;
211
+ type OnFetchHook<TContext> = (payload: OnFetchHookPayload<TContext>) => MaybePromise$1<void | OnFetchHookDone>;
212
+ type OnCacheGetHook = (payload: OnCacheGetHookEventPayload) => MaybePromise$1<OnCacheGetHookResult | void>;
213
+ interface OnCacheGetHookEventPayload {
214
+ cache: KeyValueCache;
215
+ key: string;
216
+ ttl?: number;
217
+ }
218
+ interface OnCacheGetHookResult {
219
+ onCacheHit?: OnCacheHitHook;
220
+ onCacheMiss?: OnCacheMissHook;
221
+ onCacheGetError?: OnCacheErrorHook;
222
+ }
223
+ type OnCacheErrorHook = (payload: OnCacheErrorHookPayload) => void;
224
+ interface OnCacheErrorHookPayload {
225
+ error: Error;
226
+ }
227
+ type OnCacheHitHook = (payload: OnCacheHitHookEventPayload) => void;
228
+ interface OnCacheHitHookEventPayload {
229
+ value: any;
230
+ }
231
+ type OnCacheMissHook = () => void;
232
+ type OnCacheSetHook = (payload: OnCacheSetHookEventPayload) => MaybePromise$1<OnCacheSetHookResult | void>;
233
+ interface OnCacheSetHookResult {
234
+ onCacheSetDone?: () => void;
235
+ onCacheSetError?: OnCacheErrorHook;
236
+ }
237
+ interface OnCacheSetHookEventPayload {
238
+ cache: KeyValueCache;
239
+ key: string;
240
+ value: any;
241
+ ttl?: number;
242
+ }
243
+ type OnCacheDeleteHook = (payload: OnCacheDeleteHookEventPayload) => MaybePromise$1<OnCacheDeleteHookResult | void>;
244
+ interface OnCacheDeleteHookResult {
245
+ onCacheDeleteDone?: () => void;
246
+ onCacheDeleteError?: OnCacheErrorHook;
247
+ }
248
+ interface OnCacheDeleteHookEventPayload {
249
+ cache: KeyValueCache;
250
+ key: string;
251
+ }
252
+ type Instrumentation<TContext extends Record<string, any>> = Instrumentation$1<TContext> & Instrumentation$2 & FetchInstrumentation;
253
+
254
+ interface QueryPlanOptions {
255
+ /** Callback when the query plan has been successfuly generated. */
256
+ onQueryPlan?(queryPlan: QueryPlan): void;
257
+ /** Exposing the query plan inside the GraphQL result extensions. */
258
+ expose?: boolean | ((request: Request) => boolean);
259
+ }
260
+ declare function useQueryPlan(opts?: QueryPlanOptions): GatewayPlugin;
261
+
262
+ export { type QueryPlanOptions, unifiedGraphHandler, useQueryPlan };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,262 @@
1
- import { UnifiedGraphHandlerOpts, UnifiedGraphHandlerResult } from '@graphql-mesh/fusion-runtime';
1
+ import { UnifiedGraphHandlerOpts, UnifiedGraphHandlerResult, UnifiedGraphPlugin, Instrumentation as Instrumentation$2 } from '@graphql-mesh/fusion-runtime';
2
+ import { DisposableSymbols } from '@whatwg-node/disposablestack';
3
+ import { MaybePromise } from '@whatwg-node/promise-helpers';
4
+ import { Plugin, YogaInitialContext, Instrumentation as Instrumentation$1 } from 'graphql-yoga';
5
+ import { MeshFetch, KeyValueCache, MeshFetchRequestInit, Logger as Logger$1 } from '@graphql-mesh/types';
6
+ import { FetchInstrumentation } from '@graphql-mesh/utils';
7
+ import { ExecutionRequest, MaybePromise as MaybePromise$1 } from '@graphql-tools/utils';
8
+ import { GraphQLResolveInfo } from 'graphql/type';
9
+ import { QueryPlan } from '@graphql-hive/router-query-planner';
2
10
 
3
11
  declare function unifiedGraphHandler(opts: UnifiedGraphHandlerOpts): UnifiedGraphHandlerResult;
4
12
 
5
- export { unifiedGraphHandler };
13
+ type MaybeLazy<T> = T | (() => T);
14
+ type AttributeValue = any;
15
+ type Attributes = AttributeValue[] | {
16
+ [key: string | number]: AttributeValue;
17
+ };
18
+
19
+ interface LogWriter {
20
+ write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void | Promise<void>;
21
+ flush?(): void | Promise<void>;
22
+ }
23
+
24
+ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
25
+ interface LoggerOptions {
26
+ /**
27
+ * The minimum log level to log.
28
+ *
29
+ * Providing `false` will disable all logging.
30
+ *
31
+ * Provided function will always be invoked to get the current log level.
32
+ *
33
+ * @default env.LOG_LEVEL || env.DEBUG ? 'debug' : 'info'
34
+ */
35
+ level?: MaybeLazy<LogLevel | false>;
36
+ /** A prefix to include in every log's message. */
37
+ prefix?: string;
38
+ /**
39
+ * The attributes to include in all logs. Is mainly used to pass the parent
40
+ * attributes when creating {@link Logger.child child loggers}.
41
+ */
42
+ attrs?: Attributes;
43
+ /**
44
+ * The log writers to use when writing logs.
45
+ *
46
+ * @default env.LOG_JSON ? [new JSONLogWriter()] : [new ConsoleLogWriter()]
47
+ */
48
+ writers?: [LogWriter, ...LogWriter[]];
49
+ }
50
+ declare class Logger implements AsyncDisposable {
51
+ #private;
52
+ constructor(opts?: LoggerOptions);
53
+ /** The prefix that's prepended to each log message. */
54
+ get prefix(): string | undefined;
55
+ /**
56
+ * The attributes that are added to each log. If the log itself contains
57
+ * attributes with keys existing in {@link attrs}, the log's attributes will
58
+ * override.
59
+ */
60
+ get attrs(): Attributes | undefined;
61
+ /** The current {@link LogLevel} of the logger. You can change the level using the {@link setLevel} method. */
62
+ get level(): false | LogLevel;
63
+ /**
64
+ * Sets the new {@link LogLevel} of the logger. All subsequent logs, and {@link child child loggers} whose
65
+ * level did not change, will respect the new level.
66
+ */
67
+ setLevel(level: MaybeLazy<LogLevel | false>): void;
68
+ write(level: LogLevel, attrs: Attributes | null | undefined, msg: string | null | undefined): void;
69
+ flush(): Promise<void> | undefined;
70
+ [DisposableSymbols.asyncDispose](): Promise<void | undefined>;
71
+ child(prefix: string): Logger;
72
+ child(attrs: Attributes, prefix?: string): Logger;
73
+ log(level: LogLevel): void;
74
+ log(level: LogLevel, attrs: MaybeLazy<Attributes>): void;
75
+ log(level: LogLevel, msg: string, ...interpol: unknown[]): void;
76
+ log(level: LogLevel, attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
77
+ trace(): void;
78
+ trace(attrs: MaybeLazy<Attributes>): void;
79
+ trace(msg: string, ...interpol: unknown[]): void;
80
+ trace(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
81
+ debug(): void;
82
+ debug(attrs: MaybeLazy<Attributes>): void;
83
+ debug(msg: string, ...interpol: unknown[]): void;
84
+ debug(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
85
+ info(): void;
86
+ info(attrs: MaybeLazy<Attributes>): void;
87
+ info(msg: string, ...interpol: unknown[]): void;
88
+ info(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
89
+ warn(): void;
90
+ warn(attrs: MaybeLazy<Attributes>): void;
91
+ warn(msg: string, ...interpol: unknown[]): void;
92
+ warn(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
93
+ error(): void;
94
+ error(attrs: MaybeLazy<Attributes>): void;
95
+ error(msg: string, ...interpol: unknown[]): void;
96
+ error(attrs: MaybeLazy<Attributes>, msg: string, ...interpol: unknown[]): void;
97
+ }
98
+
99
+ type TopicDataMap = {
100
+ [topic: string]: any;
101
+ };
102
+ type PubSubListener<Data extends TopicDataMap, Topic extends keyof Data> = (data: Data[Topic]) => void;
103
+ interface PubSub<M extends TopicDataMap = TopicDataMap> {
104
+ /**
105
+ * Publish {@link data} for a {@link topic}.
106
+ * @returns `void` or a `Promise` that resolves when the data has been successfully published
107
+ */
108
+ publish<Topic extends keyof M>(topic: Topic, data: M[Topic]): MaybePromise<void>;
109
+ /**
110
+ * A distinct list of all topics that are currently subscribed to.
111
+ * Can be a promise to accomodate distributed systems where subscribers exist on other
112
+ * locations and we need to know about all of them.
113
+ */
114
+ subscribedTopics(): MaybePromise<Iterable<keyof M>>;
115
+ /**
116
+ * Subscribe and listen to a {@link topic} receiving its data.
117
+ *
118
+ * If the {@link listener} is provided, it will be called whenever data is emitted for the {@link topic},
119
+ *
120
+ * @returns an unsubscribe function or a `Promise<unsubscribe function>` that resolves when the subscription is successfully established. the unsubscribe function returns `void` or a `Promise` that resolves on successful unsubscribe and subscription cleanup
121
+ *
122
+ * If the {@link listener} is not provided,
123
+ *
124
+ * @returns an `AsyncIterable` that yields data for the given {@link topic}
125
+ */
126
+ subscribe<Topic extends keyof M>(topic: Topic): AsyncIterable<M[Topic]>;
127
+ subscribe<Topic extends keyof M>(topic: Topic, listener: PubSubListener<M, Topic>): MaybePromise<() => MaybePromise<void>>;
128
+ /**
129
+ * Closes active subscriptions and disposes of all resources. Publishing and subscribing after disposal
130
+ * is not possible and will throw an error if attempted.
131
+ */
132
+ dispose(): MaybePromise<void>;
133
+ /** @see {@link dispose} */
134
+ [DisposableSymbols.asyncDispose](): Promise<void>;
135
+ }
136
+
137
+ interface GatewayConfigContext {
138
+ /**
139
+ * WHATWG compatible Fetch implementation.
140
+ */
141
+ fetch: MeshFetch;
142
+ /**
143
+ * The logger to use throught Hive and its plugins.
144
+ */
145
+ log: Logger;
146
+ /**
147
+ * Current working directory.
148
+ * Note that working directory does not exist in serverless environments and will therefore be empty.
149
+ */
150
+ cwd: string;
151
+ /**
152
+ * Event bus for pub/sub.
153
+ */
154
+ pubsub?: PubSub;
155
+ /**
156
+ * Cache Storage
157
+ */
158
+ cache?: KeyValueCache;
159
+ }
160
+ interface GatewayContext extends GatewayConfigContext, YogaInitialContext {
161
+ /**
162
+ * Environment agnostic HTTP headers provided with the request.
163
+ */
164
+ headers: Record<string, string>;
165
+ /**
166
+ * Runtime context available within WebSocket connections.
167
+ */
168
+ connectionParams?: Record<string, string>;
169
+ }
170
+ type GatewayPlugin<TPluginContext extends Record<string, any> = Record<string, any>, TContext extends Record<string, any> = Record<string, any>> = Plugin<Partial<TPluginContext> & GatewayContext & TContext, GatewayConfigContext> & UnifiedGraphPlugin<Partial<TPluginContext> & GatewayContext & TContext> & {
171
+ onFetch?: OnFetchHook<Partial<TPluginContext> & TContext>;
172
+ onCacheGet?: OnCacheGetHook;
173
+ onCacheSet?: OnCacheSetHook;
174
+ onCacheDelete?: OnCacheDeleteHook;
175
+ /**
176
+ * An Instrumentation instance that will wrap each phases of the request pipeline.
177
+ * This should be used primarily as an observability tool (for monitoring, tracing, etc...).
178
+ *
179
+ * Note: The wrapped functions in instrumentation should always be called. Use hooks to
180
+ * conditionally skip a phase.
181
+ */
182
+ instrumentation?: Instrumentation<TPluginContext & TContext & GatewayContext>;
183
+ };
184
+ interface OnFetchHookPayload<TContext> {
185
+ url: string;
186
+ setURL(url: URL | string): void;
187
+ options: MeshFetchRequestInit;
188
+ setOptions(options: MeshFetchRequestInit): void;
189
+ /**
190
+ * The context is not available in cases where "fetch" is done in
191
+ * order to pull a supergraph or do some internal work.
192
+ *
193
+ * The logger will be available in all cases.
194
+ */
195
+ context: (GatewayContext & TContext) | {
196
+ log: Logger;
197
+ };
198
+ /** @deprecated Please use `log` from the {@link context} instead. */
199
+ logger: Logger$1;
200
+ info: GraphQLResolveInfo;
201
+ fetchFn: MeshFetch;
202
+ setFetchFn: (fetchFn: MeshFetch) => void;
203
+ executionRequest?: ExecutionRequest;
204
+ endResponse: (response$: MaybePromise$1<Response>) => void;
205
+ }
206
+ interface OnFetchHookDonePayload {
207
+ response: Response;
208
+ setResponse: (response: Response) => void;
209
+ }
210
+ type OnFetchHookDone = (payload: OnFetchHookDonePayload) => MaybePromise$1<void>;
211
+ type OnFetchHook<TContext> = (payload: OnFetchHookPayload<TContext>) => MaybePromise$1<void | OnFetchHookDone>;
212
+ type OnCacheGetHook = (payload: OnCacheGetHookEventPayload) => MaybePromise$1<OnCacheGetHookResult | void>;
213
+ interface OnCacheGetHookEventPayload {
214
+ cache: KeyValueCache;
215
+ key: string;
216
+ ttl?: number;
217
+ }
218
+ interface OnCacheGetHookResult {
219
+ onCacheHit?: OnCacheHitHook;
220
+ onCacheMiss?: OnCacheMissHook;
221
+ onCacheGetError?: OnCacheErrorHook;
222
+ }
223
+ type OnCacheErrorHook = (payload: OnCacheErrorHookPayload) => void;
224
+ interface OnCacheErrorHookPayload {
225
+ error: Error;
226
+ }
227
+ type OnCacheHitHook = (payload: OnCacheHitHookEventPayload) => void;
228
+ interface OnCacheHitHookEventPayload {
229
+ value: any;
230
+ }
231
+ type OnCacheMissHook = () => void;
232
+ type OnCacheSetHook = (payload: OnCacheSetHookEventPayload) => MaybePromise$1<OnCacheSetHookResult | void>;
233
+ interface OnCacheSetHookResult {
234
+ onCacheSetDone?: () => void;
235
+ onCacheSetError?: OnCacheErrorHook;
236
+ }
237
+ interface OnCacheSetHookEventPayload {
238
+ cache: KeyValueCache;
239
+ key: string;
240
+ value: any;
241
+ ttl?: number;
242
+ }
243
+ type OnCacheDeleteHook = (payload: OnCacheDeleteHookEventPayload) => MaybePromise$1<OnCacheDeleteHookResult | void>;
244
+ interface OnCacheDeleteHookResult {
245
+ onCacheDeleteDone?: () => void;
246
+ onCacheDeleteError?: OnCacheErrorHook;
247
+ }
248
+ interface OnCacheDeleteHookEventPayload {
249
+ cache: KeyValueCache;
250
+ key: string;
251
+ }
252
+ type Instrumentation<TContext extends Record<string, any>> = Instrumentation$1<TContext> & Instrumentation$2 & FetchInstrumentation;
253
+
254
+ interface QueryPlanOptions {
255
+ /** Callback when the query plan has been successfuly generated. */
256
+ onQueryPlan?(queryPlan: QueryPlan): void;
257
+ /** Exposing the query plan inside the GraphQL result extensions. */
258
+ expose?: boolean | ((request: Request) => boolean);
259
+ }
260
+ declare function useQueryPlan(opts?: QueryPlanOptions): GatewayPlugin;
261
+
262
+ export { type QueryPlanOptions, unifiedGraphHandler, useQueryPlan };
package/dist/index.js CHANGED
@@ -1069,6 +1069,7 @@ function projectRequires(requiresSelections, entity, supergraphSchema) {
1069
1069
  return result;
1070
1070
  }
1071
1071
 
1072
+ const queryPlanForExecutionRequestContext = /* @__PURE__ */ new WeakMap();
1072
1073
  function getLazyValue(factory) {
1073
1074
  let _value;
1074
1075
  return function() {
@@ -1139,52 +1140,59 @@ function unifiedGraphHandler(opts) {
1139
1140
  executionRequest.document,
1140
1141
  executionRequest.operationName || null
1141
1142
  ),
1142
- (queryPlan) => executeQueryPlan({
1143
- supergraphSchema,
1144
- executionRequest,
1145
- onSubgraphExecute(subgraphName, executionRequest2) {
1146
- const subschema = getSubschema(subgraphName);
1147
- if (subschema.transforms?.length) {
1148
- const transforms = subschema.transforms;
1149
- const transformationContext = /* @__PURE__ */ Object.create(null);
1150
- for (const transform of transforms) {
1151
- if (transform.transformRequest) {
1152
- executionRequest2 = transform.transformRequest(
1153
- executionRequest2,
1154
- void 0,
1155
- transformationContext
1156
- );
1143
+ (queryPlan) => {
1144
+ queryPlanForExecutionRequestContext.set(
1145
+ // setter like getter
1146
+ executionRequest.context || executionRequest.document,
1147
+ queryPlan
1148
+ );
1149
+ return executeQueryPlan({
1150
+ supergraphSchema,
1151
+ executionRequest,
1152
+ onSubgraphExecute(subgraphName, executionRequest2) {
1153
+ const subschema = getSubschema(subgraphName);
1154
+ if (subschema.transforms?.length) {
1155
+ const transforms = subschema.transforms;
1156
+ const transformationContext = /* @__PURE__ */ Object.create(null);
1157
+ for (const transform of transforms) {
1158
+ if (transform.transformRequest) {
1159
+ executionRequest2 = transform.transformRequest(
1160
+ executionRequest2,
1161
+ void 0,
1162
+ transformationContext
1163
+ );
1164
+ }
1157
1165
  }
1158
- }
1159
- return handleMaybePromise(
1160
- () => opts.onSubgraphExecute(subgraphName, executionRequest2),
1161
- (executionResult) => {
1162
- function handleResult(executionResult2) {
1163
- for (const transform of transforms.toReversed()) {
1164
- if (transform.transformResult) {
1165
- executionResult2 = transform.transformResult(
1166
- executionResult2,
1167
- void 0,
1168
- transformationContext
1169
- );
1166
+ return handleMaybePromise(
1167
+ () => opts.onSubgraphExecute(subgraphName, executionRequest2),
1168
+ (executionResult) => {
1169
+ function handleResult(executionResult2) {
1170
+ for (const transform of transforms.toReversed()) {
1171
+ if (transform.transformResult) {
1172
+ executionResult2 = transform.transformResult(
1173
+ executionResult2,
1174
+ void 0,
1175
+ transformationContext
1176
+ );
1177
+ }
1170
1178
  }
1179
+ return executionResult2;
1171
1180
  }
1172
- return executionResult2;
1173
- }
1174
- if (isAsyncIterable(executionResult)) {
1175
- return mapAsyncIterator(
1176
- executionResult,
1177
- (result) => handleResult(result)
1178
- );
1181
+ if (isAsyncIterable(executionResult)) {
1182
+ return mapAsyncIterator(
1183
+ executionResult,
1184
+ (result) => handleResult(result)
1185
+ );
1186
+ }
1187
+ return handleResult(executionResult);
1179
1188
  }
1180
- return handleResult(executionResult);
1181
- }
1182
- );
1183
- }
1184
- return opts.onSubgraphExecute(subgraphName, executionRequest2);
1185
- },
1186
- queryPlan
1187
- })
1189
+ );
1190
+ }
1191
+ return opts.onSubgraphExecute(subgraphName, executionRequest2);
1192
+ },
1193
+ queryPlan
1194
+ });
1195
+ }
1188
1196
  );
1189
1197
  }
1190
1198
  };
@@ -1213,4 +1221,31 @@ function isIntrospection(document) {
1213
1221
  return containsIntrospectionField || onlyQueryTypenameFields;
1214
1222
  }
1215
1223
 
1216
- export { unifiedGraphHandler };
1224
+ function useQueryPlan(opts = {}) {
1225
+ const { expose, onQueryPlan } = opts;
1226
+ return {
1227
+ onExecute({ context, args }) {
1228
+ return {
1229
+ onExecuteDone({ result, setResult }) {
1230
+ const queryPlan = queryPlanForExecutionRequestContext.get(
1231
+ // getter like setter
1232
+ context || args.document
1233
+ );
1234
+ onQueryPlan?.(queryPlan);
1235
+ const shouldExpose = typeof expose === "function" ? expose(context.request) : expose;
1236
+ if (shouldExpose && !isAsyncIterable(result)) {
1237
+ setResult({
1238
+ ...result,
1239
+ extensions: {
1240
+ ...result.extensions,
1241
+ queryPlan
1242
+ }
1243
+ });
1244
+ }
1245
+ }
1246
+ };
1247
+ }
1248
+ };
1249
+ }
1250
+
1251
+ export { unifiedGraphHandler, useQueryPlan };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-hive/router-runtime",
3
- "version": "1.1.0-rc-924ffb15d84292a020a45655046fd359ea9f02d5",
3
+ "version": "1.1.0-rc-aa2a330ec509cf750e8722741cb18aed3ad01793",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -44,11 +44,11 @@
44
44
  "dependencies": {
45
45
  "@envelop/core": "^5.4.0",
46
46
  "@graphql-hive/router-query-planner": "^0.0.6",
47
- "@graphql-mesh/fusion-runtime": "1.6.0-rc-924ffb15d84292a020a45655046fd359ea9f02d5",
47
+ "@graphql-mesh/fusion-runtime": "1.6.0-rc-aa2a330ec509cf750e8722741cb18aed3ad01793",
48
48
  "@graphql-mesh/transport-common": "^1.0.12",
49
49
  "@graphql-tools/executor": "^1.4.13",
50
50
  "@graphql-tools/executor-common": "^1.0.5",
51
- "@graphql-tools/federation": "4.2.4-rc-924ffb15d84292a020a45655046fd359ea9f02d5",
51
+ "@graphql-tools/federation": "4.2.4-rc-aa2a330ec509cf750e8722741cb18aed3ad01793",
52
52
  "@graphql-tools/utils": "^10.10.3",
53
53
  "@whatwg-node/promise-helpers": "^1.3.2"
54
54
  },